pl-fe: Replace some redux stores with zustand
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
parent
b801acffe9
commit
afdaa61a06
22 changed files with 167 additions and 365 deletions
|
@ -1,27 +0,0 @@
|
|||
const PROFILE_HOVER_CARD_OPEN = 'PROFILE_HOVER_CARD_OPEN';
|
||||
const PROFILE_HOVER_CARD_UPDATE = 'PROFILE_HOVER_CARD_UPDATE';
|
||||
const PROFILE_HOVER_CARD_CLOSE = 'PROFILE_HOVER_CARD_CLOSE';
|
||||
|
||||
const openProfileHoverCard = (ref: React.MutableRefObject<HTMLDivElement>, accountId: string) => ({
|
||||
type: PROFILE_HOVER_CARD_OPEN,
|
||||
ref,
|
||||
accountId,
|
||||
});
|
||||
|
||||
const updateProfileHoverCard = () => ({
|
||||
type: PROFILE_HOVER_CARD_UPDATE,
|
||||
});
|
||||
|
||||
const closeProfileHoverCard = (force = false) => ({
|
||||
type: PROFILE_HOVER_CARD_CLOSE,
|
||||
force,
|
||||
});
|
||||
|
||||
export {
|
||||
PROFILE_HOVER_CARD_OPEN,
|
||||
PROFILE_HOVER_CARD_UPDATE,
|
||||
PROFILE_HOVER_CARD_CLOSE,
|
||||
openProfileHoverCard,
|
||||
updateProfileHoverCard,
|
||||
closeProfileHoverCard,
|
||||
};
|
|
@ -1,17 +0,0 @@
|
|||
const SIDEBAR_OPEN = 'SIDEBAR_OPEN';
|
||||
const SIDEBAR_CLOSE = 'SIDEBAR_CLOSE';
|
||||
|
||||
const openSidebar = () => ({
|
||||
type: SIDEBAR_OPEN,
|
||||
});
|
||||
|
||||
const closeSidebar = () => ({
|
||||
type: SIDEBAR_CLOSE,
|
||||
});
|
||||
|
||||
export {
|
||||
SIDEBAR_OPEN,
|
||||
SIDEBAR_CLOSE,
|
||||
openSidebar,
|
||||
closeSidebar,
|
||||
};
|
|
@ -1,27 +0,0 @@
|
|||
const STATUS_HOVER_CARD_OPEN = 'STATUS_HOVER_CARD_OPEN';
|
||||
const STATUS_HOVER_CARD_UPDATE = 'STATUS_HOVER_CARD_UPDATE';
|
||||
const STATUS_HOVER_CARD_CLOSE = 'STATUS_HOVER_CARD_CLOSE';
|
||||
|
||||
const openStatusHoverCard = (ref: React.MutableRefObject<HTMLDivElement>, statusId: string) => ({
|
||||
type: STATUS_HOVER_CARD_OPEN,
|
||||
ref,
|
||||
statusId,
|
||||
});
|
||||
|
||||
const updateStatusHoverCard = () => ({
|
||||
type: STATUS_HOVER_CARD_UPDATE,
|
||||
});
|
||||
|
||||
const closeStatusHoverCard = (force = false) => ({
|
||||
type: STATUS_HOVER_CARD_CLOSE,
|
||||
force,
|
||||
});
|
||||
|
||||
export {
|
||||
STATUS_HOVER_CARD_OPEN,
|
||||
STATUS_HOVER_CARD_UPDATE,
|
||||
STATUS_HOVER_CARD_CLOSE,
|
||||
openStatusHoverCard,
|
||||
updateStatusHoverCard,
|
||||
closeStatusHoverCard,
|
||||
};
|
|
@ -5,7 +5,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|||
import ReactSwipeableViews from 'react-swipeable-views';
|
||||
|
||||
import { userTouching } from 'pl-fe/is-mobile';
|
||||
import { useDropdownMenuStore, useModalsStore } from 'pl-fe/stores';
|
||||
import { useUiStore, useModalsStore } from 'pl-fe/stores';
|
||||
|
||||
import { HStack, IconButton, Portal } from '../ui';
|
||||
|
||||
|
@ -187,7 +187,7 @@ const DropdownMenu = (props: IDropdownMenu) => {
|
|||
title = 'Menu',
|
||||
} = props;
|
||||
|
||||
const { openDropdownMenu, closeDropdownMenu } = useDropdownMenuStore();
|
||||
const { openDropdownMenu, closeDropdownMenu } = useUiStore();
|
||||
const { openModal, closeModal } = useModalsStore();
|
||||
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||
|
|
|
@ -3,12 +3,12 @@ import debounce from 'lodash/debounce';
|
|||
import React, { useRef } from 'react';
|
||||
|
||||
import { fetchAccount } from 'pl-fe/actions/accounts';
|
||||
import { openProfileHoverCard, closeProfileHoverCard } from 'pl-fe/actions/profile-hover-card';
|
||||
import { useAppDispatch } from 'pl-fe/hooks';
|
||||
import { isMobile } from 'pl-fe/is-mobile';
|
||||
import { useAccountHoverCardStore } from 'pl-fe/stores';
|
||||
|
||||
const showProfileHoverCard = debounce((dispatch, ref, accountId) => {
|
||||
dispatch(openProfileHoverCard(ref, accountId));
|
||||
const showProfileHoverCard = debounce((openAccountHoverCard, ref, accountId) => {
|
||||
openAccountHoverCard(ref, accountId);
|
||||
}, 600);
|
||||
|
||||
interface IHoverRefWrapper {
|
||||
|
@ -21,24 +21,27 @@ interface IHoverRefWrapper {
|
|||
/** Makes a profile hover card appear when the wrapped element is hovered. */
|
||||
const HoverRefWrapper: React.FC<IHoverRefWrapper> = ({ accountId, children, inline = false, className }) => {
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { openAccountHoverCard, closeAccountHoverCard } = useAccountHoverCardStore();
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const Elem: keyof JSX.IntrinsicElements = inline ? 'span' : 'div';
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
if (!isMobile(window.innerWidth)) {
|
||||
dispatch(fetchAccount(accountId));
|
||||
showProfileHoverCard(dispatch, ref, accountId);
|
||||
showProfileHoverCard(openAccountHoverCard, ref, accountId);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
showProfileHoverCard.cancel();
|
||||
setTimeout(() => dispatch(closeProfileHoverCard()), 300);
|
||||
setTimeout(() => closeAccountHoverCard(), 300);
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
showProfileHoverCard.cancel();
|
||||
dispatch(closeProfileHoverCard(true));
|
||||
closeAccountHoverCard(true);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import clsx from 'clsx';
|
||||
import debounce from 'lodash/debounce';
|
||||
import React, { useRef } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { openStatusHoverCard, closeStatusHoverCard } from 'pl-fe/actions/status-hover-card';
|
||||
import { isMobile } from 'pl-fe/is-mobile';
|
||||
import { useStatusHoverCardStore } from 'pl-fe/stores';
|
||||
|
||||
const showStatusHoverCard = debounce((dispatch, ref, statusId) => {
|
||||
dispatch(openStatusHoverCard(ref, statusId));
|
||||
const showStatusHoverCard = debounce((openStatusHoverCard, ref, statusId) => {
|
||||
openStatusHoverCard(ref, statusId);
|
||||
}, 300);
|
||||
|
||||
interface IHoverStatusWrapper {
|
||||
|
@ -19,24 +18,25 @@ interface IHoverStatusWrapper {
|
|||
|
||||
/** Makes a status hover card appear when the wrapped element is hovered. */
|
||||
const HoverStatusWrapper: React.FC<IHoverStatusWrapper> = ({ statusId, children, inline = false, className }) => {
|
||||
const dispatch = useDispatch();
|
||||
const { openStatusHoverCard, closeStatusHoverCard } = useStatusHoverCardStore();
|
||||
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const Elem: keyof JSX.IntrinsicElements = inline ? 'span' : 'div';
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
if (!isMobile(window.innerWidth)) {
|
||||
showStatusHoverCard(dispatch, ref, statusId);
|
||||
showStatusHoverCard(openStatusHoverCard, ref, statusId);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
showStatusHoverCard.cancel();
|
||||
setTimeout(() => dispatch(closeStatusHoverCard()), 200);
|
||||
setTimeout(() => closeStatusHoverCard(), 200);
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
showStatusHoverCard.cancel();
|
||||
dispatch(closeStatusHoverCard(true));
|
||||
closeStatusHoverCard(true);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -5,12 +5,12 @@ import { useIntl, FormattedMessage } from 'react-intl';
|
|||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { fetchRelationships } from 'pl-fe/actions/accounts';
|
||||
import { closeProfileHoverCard, updateProfileHoverCard } from 'pl-fe/actions/profile-hover-card';
|
||||
import { useAccount } from 'pl-fe/api/hooks';
|
||||
import Badge from 'pl-fe/components/badge';
|
||||
import ActionButton from 'pl-fe/features/ui/components/action-button';
|
||||
import { UserPanel } from 'pl-fe/features/ui/util/async-components';
|
||||
import { useAppSelector, useAppDispatch } from 'pl-fe/hooks';
|
||||
import { useAccountHoverCardStore } from 'pl-fe/stores';
|
||||
|
||||
import { showProfileHoverCard } from './hover-ref-wrapper';
|
||||
import { dateFormatOptions } from './relative-timestamp';
|
||||
|
@ -18,7 +18,6 @@ import Scrobble from './scrobble';
|
|||
import { Card, CardBody, HStack, Icon, Stack, Text } from './ui';
|
||||
|
||||
import type { Account } from 'pl-fe/normalizers';
|
||||
import type { AppDispatch } from 'pl-fe/store';
|
||||
|
||||
const getBadges = (
|
||||
account?: Pick<Account, 'is_admin' | 'is_moderator'>,
|
||||
|
@ -34,14 +33,6 @@ const getBadges = (
|
|||
return badges;
|
||||
};
|
||||
|
||||
const handleMouseEnter = (dispatch: AppDispatch): React.MouseEventHandler => () => {
|
||||
dispatch(updateProfileHoverCard());
|
||||
};
|
||||
|
||||
const handleMouseLeave = (dispatch: AppDispatch): React.MouseEventHandler => () => {
|
||||
dispatch(closeProfileHoverCard(true));
|
||||
};
|
||||
|
||||
interface IProfileHoverCard {
|
||||
visible?: boolean;
|
||||
}
|
||||
|
@ -52,10 +43,10 @@ const ProfileHoverCard: React.FC<IProfileHoverCard> = ({ visible = true }) => {
|
|||
const history = useHistory();
|
||||
const intl = useIntl();
|
||||
|
||||
const { accountId, ref, updateAccountHoverCard, closeAccountHoverCard } = useAccountHoverCardStore();
|
||||
|
||||
const me = useAppSelector(state => state.me);
|
||||
const accountId: string | undefined = useAppSelector(state => state.profile_hover_card.accountId || undefined);
|
||||
const { account } = useAccount(accountId, { withRelationship: true, withScrobble: true });
|
||||
const targetRef = useAppSelector(state => state.profile_hover_card.ref?.current);
|
||||
const { account } = useAccount(accountId || undefined, { withRelationship: true, withScrobble: true });
|
||||
const badges = getBadges(account);
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -65,7 +56,7 @@ const ProfileHoverCard: React.FC<IProfileHoverCard> = ({ visible = true }) => {
|
|||
useEffect(() => {
|
||||
const unlisten = history.listen(() => {
|
||||
showProfileHoverCard.cancel();
|
||||
dispatch(closeProfileHoverCard());
|
||||
closeAccountHoverCard();
|
||||
});
|
||||
|
||||
return () => {
|
||||
|
@ -76,7 +67,7 @@ const ProfileHoverCard: React.FC<IProfileHoverCard> = ({ visible = true }) => {
|
|||
const { x, y, strategy, refs, context, placement } = useFloating({
|
||||
open: !!account,
|
||||
elements: {
|
||||
reference: targetRef,
|
||||
reference: ref?.current,
|
||||
},
|
||||
middleware: [
|
||||
shift({
|
||||
|
@ -116,8 +107,8 @@ const ProfileHoverCard: React.FC<IProfileHoverCard> = ({ visible = true }) => {
|
|||
left: x ?? 0,
|
||||
...styles,
|
||||
}}
|
||||
onMouseEnter={handleMouseEnter(dispatch)}
|
||||
onMouseLeave={handleMouseLeave(dispatch)}
|
||||
onMouseEnter={() => updateAccountHoverCard()}
|
||||
onMouseLeave={() => closeAccountHoverCard()}
|
||||
>
|
||||
<Card variant='rounded' className='relative isolate overflow-hidden black:rounded-xl black:border black:border-gray-800'>
|
||||
<CardBody>
|
||||
|
|
|
@ -6,13 +6,13 @@ import { Link, NavLink } from 'react-router-dom';
|
|||
|
||||
import { fetchOwnAccounts, logOut, switchAccount } from 'pl-fe/actions/auth';
|
||||
import { getSettings } from 'pl-fe/actions/settings';
|
||||
import { closeSidebar } from 'pl-fe/actions/sidebar';
|
||||
import { useAccount } from 'pl-fe/api/hooks';
|
||||
import Account from 'pl-fe/components/account';
|
||||
import { Stack, Divider, HStack, Icon, Text } from 'pl-fe/components/ui';
|
||||
import ProfileStats from 'pl-fe/features/ui/components/profile-stats';
|
||||
import { useAppDispatch, useAppSelector, useFeatures, useInstance, useRegistrationStatus } from 'pl-fe/hooks';
|
||||
import { makeGetOtherAccounts } from 'pl-fe/selectors';
|
||||
import { useUiStore } from 'pl-fe/stores';
|
||||
import sourceCode from 'pl-fe/utils/code';
|
||||
|
||||
import type { List as ImmutableList } from 'immutable';
|
||||
|
@ -79,18 +79,19 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
const intl = useIntl();
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { isSidebarOpen, closeSidebar } = useUiStore();
|
||||
|
||||
const getOtherAccounts = useCallback(makeGetOtherAccounts(), []);
|
||||
const features = useFeatures();
|
||||
const me = useAppSelector((state) => state.me);
|
||||
const { account } = useAccount(me || undefined);
|
||||
const otherAccounts: ImmutableList<AccountEntity> = useAppSelector((state) => getOtherAccounts(state));
|
||||
const sidebarOpen = useAppSelector((state) => state.sidebar.sidebarOpen);
|
||||
const settings = useAppSelector((state) => getSettings(state));
|
||||
const followRequestsCount = useAppSelector((state) => state.user_lists.follow_requests.items.count());
|
||||
const scheduledStatusCount = useAppSelector((state) => state.scheduled_statuses.size);
|
||||
const draftCount = useAppSelector((state) => state.draft_statuses.size);
|
||||
// const dashboardCount = useAppSelector((state) => state.admin.openReports.count() + state.admin.awaitingApproval.count());
|
||||
const [sidebarVisible, setSidebarVisible] = useState(sidebarOpen);
|
||||
const [sidebarVisible, setSidebarVisible] = useState(isSidebarOpen);
|
||||
const touchStart = useRef(0);
|
||||
const touchEnd = useRef<number | null>(null);
|
||||
const { isOpen } = useRegistrationStatus();
|
||||
|
@ -102,11 +103,9 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
|
||||
const [switcher, setSwitcher] = React.useState(false);
|
||||
|
||||
const onClose = () => dispatch(closeSidebar());
|
||||
|
||||
const handleClose = () => {
|
||||
setSwitcher(false);
|
||||
onClose();
|
||||
closeSidebar();
|
||||
};
|
||||
|
||||
const handleSwitchAccount = (account: AccountEntity): React.MouseEventHandler => (e) => {
|
||||
|
@ -157,17 +156,17 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (sidebarOpen) containerRef.current?.querySelector('a')?.focus();
|
||||
setTimeout(() => setSidebarVisible(sidebarOpen), sidebarOpen ? 0 : 150);
|
||||
}, [sidebarOpen]);
|
||||
if (isSidebarOpen) containerRef.current?.querySelector('a')?.focus();
|
||||
setTimeout(() => setSidebarVisible(isSidebarOpen), isSidebarOpen ? 0 : 150);
|
||||
}, [isSidebarOpen]);
|
||||
|
||||
return (
|
||||
<div
|
||||
aria-expanded={sidebarOpen}
|
||||
aria-expanded={isSidebarOpen}
|
||||
className={
|
||||
clsx({
|
||||
'z-[1000]': sidebarOpen || sidebarVisible,
|
||||
hidden: !(sidebarOpen || sidebarVisible),
|
||||
'z-[1000]': isSidebarOpen || sidebarVisible,
|
||||
hidden: !(isSidebarOpen || sidebarVisible),
|
||||
})
|
||||
}
|
||||
ref={containerRef}
|
||||
|
@ -178,8 +177,8 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
>
|
||||
<div
|
||||
className={clsx('fixed inset-0 cursor-default bg-gray-500 black:bg-gray-900 no-reduce-motion:transition-opacity dark:bg-gray-700', {
|
||||
'no-reduce-motion:opacity-0': !(sidebarVisible && sidebarOpen),
|
||||
'opacity-40': (sidebarVisible && sidebarOpen),
|
||||
'no-reduce-motion:opacity-0': !(sidebarVisible && isSidebarOpen),
|
||||
'opacity-40': (sidebarVisible && isSidebarOpen),
|
||||
})}
|
||||
role='button'
|
||||
onClick={handleClose}
|
||||
|
@ -188,8 +187,8 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
<div
|
||||
className={
|
||||
clsx('fixed bottom-[60px] left-2 z-[1000] flex max-h-[calc(100dvh-68px)] w-full max-w-xs flex-1 origin-bottom-left flex-col overflow-hidden rounded-xl bg-white shadow-lg ease-in-out black:bg-black no-reduce-motion:transition-transform dark:border dark:border-gray-800 dark:bg-primary-900 dark:shadow-none rtl:right-2 rtl:origin-bottom-right', {
|
||||
'scale-100': sidebarVisible && sidebarOpen,
|
||||
'no-reduce-motion:scale-0': !(sidebarVisible && sidebarOpen),
|
||||
'scale-100': sidebarVisible && isSidebarOpen,
|
||||
'no-reduce-motion:scale-0': !(sidebarVisible && isSidebarOpen),
|
||||
})
|
||||
}
|
||||
>
|
||||
|
@ -197,7 +196,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
<div className='p-4'>
|
||||
{account ? (
|
||||
<Stack space={4}>
|
||||
<Link to={`/@${account.acct}`} onClick={onClose}>
|
||||
<Link to={`/@${account.acct}`} onClick={closeSidebar}>
|
||||
<Account account={account} showProfileHoverCard={false} withLinkToProfile={false} />
|
||||
</Link>
|
||||
|
||||
|
@ -213,7 +212,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to={`/@${account.acct}`}
|
||||
icon={require('@tabler/icons/outline/user.svg')}
|
||||
text={intl.formatMessage(messages.profile)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
|
||||
{(account.locked || followRequestsCount > 0) && (
|
||||
|
@ -221,7 +220,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/follow_requests'
|
||||
icon={require('@tabler/icons/outline/user-plus.svg')}
|
||||
text={intl.formatMessage(messages.followRequests)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -230,7 +229,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/conversations'
|
||||
icon={require('@tabler/icons/outline/mail.svg')}
|
||||
text={intl.formatMessage(messages.conversations)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -239,7 +238,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/bookmarks'
|
||||
icon={require('@tabler/icons/outline/bookmark.svg')}
|
||||
text={intl.formatMessage(messages.bookmarks)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -248,7 +247,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/groups'
|
||||
icon={require('@tabler/icons/outline/circles.svg')}
|
||||
text={intl.formatMessage(messages.groups)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -257,7 +256,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/lists'
|
||||
icon={require('@tabler/icons/outline/list.svg')}
|
||||
text={intl.formatMessage(messages.lists)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -266,7 +265,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/events'
|
||||
icon={require('@tabler/icons/outline/calendar-event.svg')}
|
||||
text={intl.formatMessage(messages.events)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -275,7 +274,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/directory'
|
||||
icon={require('@tabler/icons/outline/address-book.svg')}
|
||||
text={intl.formatMessage(messages.profileDirectory)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -284,7 +283,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/scheduled_statuses'
|
||||
icon={require('@tabler/icons/outline/calendar-stats.svg')}
|
||||
text={intl.formatMessage(messages.scheduledStatuses)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -293,7 +292,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/draft_statuses'
|
||||
icon={require('@tabler/icons/outline/notes.svg')}
|
||||
text={intl.formatMessage(messages.drafts)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -304,7 +303,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/timeline/local'
|
||||
icon={features.federating ? require('@tabler/icons/outline/affiliate.svg') : require('@tabler/icons/outline/world.svg')}
|
||||
text={features.federating ? <FormattedMessage id='tabs_bar.local' defaultMessage='Local' /> : <FormattedMessage id='tabs_bar.all' defaultMessage='All' />}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
|
||||
{features.bubbleTimeline && (
|
||||
|
@ -312,7 +311,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/timeline/bubble'
|
||||
icon={require('@tabler/icons/outline/chart-bubble.svg')}
|
||||
text={<FormattedMessage id='tabs_bar.bubble' defaultMessage='Bubble' />}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -321,7 +320,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/timeline/fediverse'
|
||||
icon={require('@tabler/icons/outline/topology-star-ring-3.svg')}
|
||||
text={<FormattedMessage id='tabs_bar.fediverse' defaultMessage='Fediverse' />}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
</>}
|
||||
|
@ -332,7 +331,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/settings/preferences'
|
||||
icon={require('@tabler/icons/outline/settings.svg')}
|
||||
text={intl.formatMessage(messages.preferences)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
|
||||
{features.followedHashtagsList && (
|
||||
|
@ -340,7 +339,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/followed_tags'
|
||||
icon={require('@tabler/icons/outline/hash.svg')}
|
||||
text={intl.formatMessage(messages.followedTags)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -349,7 +348,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/developers'
|
||||
icon={require('@tabler/icons/outline/code.svg')}
|
||||
text={intl.formatMessage(messages.developers)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -358,7 +357,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/admin'
|
||||
icon={require('@tabler/icons/outline/dashboard.svg')}
|
||||
text={intl.formatMessage(messages.dashboard)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
// count={dashboardCount} WIP
|
||||
/>
|
||||
)}
|
||||
|
@ -378,7 +377,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
href={sourceCode.url}
|
||||
icon={require('@tabler/icons/outline/code.svg')}
|
||||
text={intl.formatMessage(messages.sourceCode)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
|
||||
<Divider />
|
||||
|
@ -419,7 +418,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/timeline/local'
|
||||
icon={features.federating ? require('@tabler/icons/outline/affiliate.svg') : require('@tabler/icons/outline/world.svg')}
|
||||
text={features.federating ? <FormattedMessage id='tabs_bar.local' defaultMessage='Local' /> : <FormattedMessage id='tabs_bar.all' defaultMessage='All' />}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
|
||||
{features.bubbleTimeline && !restrictUnauth.timelines.bubble && (
|
||||
|
@ -427,7 +426,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/timeline/bubble'
|
||||
icon={require('@tabler/icons/outline/chart-bubble.svg')}
|
||||
text={<FormattedMessage id='tabs_bar.bubble' defaultMessage='Bubble' />}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -436,7 +435,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/timeline/fediverse'
|
||||
icon={require('@tabler/icons/outline/topology-star-ring-3.svg')}
|
||||
text={<FormattedMessage id='tabs_bar.fediverse' defaultMessage='Fediverse' />}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -447,7 +446,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/login'
|
||||
icon={require('@tabler/icons/outline/login.svg')}
|
||||
text={intl.formatMessage(messages.login)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
|
||||
{isOpen && (
|
||||
|
@ -455,7 +454,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
to='/signup'
|
||||
icon={require('@tabler/icons/outline/user-plus.svg')}
|
||||
text={intl.formatMessage(messages.register)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -465,7 +464,7 @@ const SidebarMenu: React.FC = (): JSX.Element | null => {
|
|||
href={sourceCode.url}
|
||||
icon={require('@tabler/icons/outline/code.svg')}
|
||||
text={intl.formatMessage(messages.sourceCode)}
|
||||
onClick={onClose}
|
||||
onClick={closeSidebar}
|
||||
/>
|
||||
</Stack>
|
||||
)}
|
||||
|
|
|
@ -4,10 +4,10 @@ import React, { useEffect, useCallback } from 'react';
|
|||
import { useIntl } from 'react-intl';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { closeStatusHoverCard, updateStatusHoverCard } from 'pl-fe/actions/status-hover-card';
|
||||
import { fetchStatus } from 'pl-fe/actions/statuses';
|
||||
import StatusContainer from 'pl-fe/containers/status-container';
|
||||
import { useAppSelector, useAppDispatch } from 'pl-fe/hooks';
|
||||
import { useStatusHoverCardStore } from 'pl-fe/stores';
|
||||
|
||||
import { showStatusHoverCard } from './hover-status-wrapper';
|
||||
import { Card, CardBody } from './ui';
|
||||
|
@ -22,9 +22,9 @@ const StatusHoverCard: React.FC<IStatusHoverCard> = ({ visible = true }) => {
|
|||
const intl = useIntl();
|
||||
const history = useHistory();
|
||||
|
||||
const statusId: string | undefined = useAppSelector(state => state.status_hover_card.statusId || undefined);
|
||||
const { statusId, ref, closeStatusHoverCard, updateStatusHoverCard } = useStatusHoverCardStore();
|
||||
|
||||
const status = useAppSelector(state => state.statuses.get(statusId!));
|
||||
const targetRef = useAppSelector(state => state.status_hover_card.ref?.current);
|
||||
|
||||
useEffect(() => {
|
||||
if (statusId && !status) {
|
||||
|
@ -35,7 +35,7 @@ const StatusHoverCard: React.FC<IStatusHoverCard> = ({ visible = true }) => {
|
|||
useEffect(() => {
|
||||
const unlisten = history.listen(() => {
|
||||
showStatusHoverCard.cancel();
|
||||
dispatch(closeStatusHoverCard());
|
||||
closeStatusHoverCard();
|
||||
});
|
||||
|
||||
return () => {
|
||||
|
@ -46,7 +46,7 @@ const StatusHoverCard: React.FC<IStatusHoverCard> = ({ visible = true }) => {
|
|||
const { x, y, strategy, refs, context, placement } = useFloating({
|
||||
open: !!statusId,
|
||||
elements: {
|
||||
reference: targetRef,
|
||||
reference: ref?.current,
|
||||
},
|
||||
placement: 'top',
|
||||
middleware: [
|
||||
|
@ -69,11 +69,11 @@ const StatusHoverCard: React.FC<IStatusHoverCard> = ({ visible = true }) => {
|
|||
});
|
||||
|
||||
const handleMouseEnter = useCallback((): React.MouseEventHandler => () => {
|
||||
dispatch(updateStatusHoverCard());
|
||||
updateStatusHoverCard();
|
||||
}, []);
|
||||
|
||||
const handleMouseLeave = useCallback((): React.MouseEventHandler => () => {
|
||||
dispatch(closeStatusHoverCard(true));
|
||||
closeStatusHoverCard(true);
|
||||
}, []);
|
||||
|
||||
if (!statusId) return null;
|
||||
|
|
|
@ -3,12 +3,11 @@ import { defineMessages, useIntl } from 'react-intl';
|
|||
import { useRouteMatch } from 'react-router-dom';
|
||||
|
||||
import { groupComposeModal } from 'pl-fe/actions/compose';
|
||||
import { openSidebar } from 'pl-fe/actions/sidebar';
|
||||
import ThumbNavigationLink from 'pl-fe/components/thumb-navigation-link';
|
||||
import { useStatContext } from 'pl-fe/contexts/stat-context';
|
||||
import { Entities } from 'pl-fe/entity-store/entities';
|
||||
import { useAppDispatch, useAppSelector, useFeatures, useOwnAccount } from 'pl-fe/hooks';
|
||||
import { useModalsStore } from 'pl-fe/stores';
|
||||
import { useModalsStore, useUiStore } from 'pl-fe/stores';
|
||||
import { isStandalone } from 'pl-fe/utils/state';
|
||||
|
||||
import { Icon } from './ui';
|
||||
|
@ -30,14 +29,13 @@ const ThumbNavigation: React.FC = (): JSX.Element => {
|
|||
|
||||
const match = useRouteMatch<{ groupId: string }>('/groups/:groupId');
|
||||
|
||||
const { openSidebar } = useUiStore();
|
||||
const { openModal } = useModalsStore();
|
||||
const { unreadChatsCount } = useStatContext();
|
||||
|
||||
const standalone = useAppSelector(isStandalone);
|
||||
const notificationCount = useAppSelector((state) => state.notifications.unread);
|
||||
|
||||
const handleOpenSidebar = () => dispatch(openSidebar());
|
||||
|
||||
const handleOpenComposeModal = () => {
|
||||
if (match?.params.groupId) {
|
||||
dispatch((_, getState) => {
|
||||
|
@ -66,7 +64,7 @@ const ThumbNavigation: React.FC = (): JSX.Element => {
|
|||
<div className='fixed inset-x-0 bottom-0 z-50 flex w-full overflow-x-auto border-t border-solid border-gray-200 bg-white/90 shadow-2xl backdrop-blur-md black:bg-black/80 dark:border-gray-800 dark:bg-primary-900/90 lg:hidden'>
|
||||
<button
|
||||
className='flex flex-1 flex-col items-center px-2 py-4 text-lg text-gray-600'
|
||||
onClick={handleOpenSidebar}
|
||||
onClick={openSidebar}
|
||||
title={intl.formatMessage(messages.sidebar)}
|
||||
>
|
||||
<Icon
|
||||
|
|
|
@ -34,7 +34,7 @@ import ProfileLayout from 'pl-fe/layouts/profile-layout';
|
|||
import RemoteInstanceLayout from 'pl-fe/layouts/remote-instance-layout';
|
||||
import SearchLayout from 'pl-fe/layouts/search-layout';
|
||||
import StatusLayout from 'pl-fe/layouts/status-layout';
|
||||
import { useDropdownMenuStore } from 'pl-fe/stores';
|
||||
import { useUiStore } from 'pl-fe/stores';
|
||||
import { getVapidKey } from 'pl-fe/utils/auth';
|
||||
import { isStandalone } from 'pl-fe/utils/state';
|
||||
|
||||
|
@ -352,7 +352,7 @@ const UI: React.FC<IUI> = ({ children }) => {
|
|||
const features = useFeatures();
|
||||
const vapidKey = useAppSelector(state => getVapidKey(state));
|
||||
|
||||
const { isOpen: dropdownMenuIsOpen } = useDropdownMenuStore();
|
||||
const { isDropdownMenuOpen } = useUiStore();
|
||||
const standalone = useAppSelector(isStandalone);
|
||||
|
||||
const { isDragging } = useDraggedFiles(node);
|
||||
|
@ -445,7 +445,7 @@ const UI: React.FC<IUI> = ({ children }) => {
|
|||
if (me === null) return null;
|
||||
|
||||
const style: React.CSSProperties = {
|
||||
pointerEvents: dropdownMenuIsOpen ? 'none' : undefined,
|
||||
pointerEvents: isDropdownMenuOpen ? 'none' : undefined,
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -33,14 +33,11 @@ import onboarding from './onboarding';
|
|||
import pending_statuses from './pending-statuses';
|
||||
import plfe from './pl-fe';
|
||||
import polls from './polls';
|
||||
import profile_hover_card from './profile-hover-card';
|
||||
import push_notifications from './push-notifications';
|
||||
import scheduled_statuses from './scheduled-statuses';
|
||||
import search from './search';
|
||||
import security from './security';
|
||||
import settings from './settings';
|
||||
import sidebar from './sidebar';
|
||||
import status_hover_card from './status-hover-card';
|
||||
import status_lists from './status-lists';
|
||||
import statuses from './statuses';
|
||||
import suggestions from './suggestions';
|
||||
|
@ -80,14 +77,11 @@ const reducers = {
|
|||
pending_statuses,
|
||||
plfe,
|
||||
polls,
|
||||
profile_hover_card,
|
||||
push_notifications,
|
||||
scheduled_statuses,
|
||||
search,
|
||||
security,
|
||||
settings,
|
||||
sidebar,
|
||||
status_hover_card,
|
||||
status_lists,
|
||||
statuses,
|
||||
suggestions,
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
import { Record as ImmutableRecord } from 'immutable';
|
||||
|
||||
import {
|
||||
PROFILE_HOVER_CARD_OPEN,
|
||||
PROFILE_HOVER_CARD_CLOSE,
|
||||
PROFILE_HOVER_CARD_UPDATE,
|
||||
} from 'pl-fe/actions/profile-hover-card';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
|
||||
const ReducerRecord = ImmutableRecord({
|
||||
ref: null as React.MutableRefObject<HTMLDivElement> | null,
|
||||
accountId: '',
|
||||
hovered: false,
|
||||
});
|
||||
|
||||
type State = ReturnType<typeof ReducerRecord>;
|
||||
|
||||
const profileHoverCard = (state: State = ReducerRecord(), action: AnyAction) => {
|
||||
switch (action.type) {
|
||||
case PROFILE_HOVER_CARD_OPEN:
|
||||
return state.withMutations((state) => {
|
||||
state.set('ref', action.ref);
|
||||
state.set('accountId', action.accountId);
|
||||
});
|
||||
case PROFILE_HOVER_CARD_UPDATE:
|
||||
return state.set('hovered', true);
|
||||
case PROFILE_HOVER_CARD_CLOSE:
|
||||
if (state.get('hovered') === true && !action.force)
|
||||
return state;
|
||||
else
|
||||
return ReducerRecord();
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export { profileHoverCard as default };
|
|
@ -1,7 +0,0 @@
|
|||
import reducer from './sidebar';
|
||||
|
||||
describe('sidebar reducer', () => {
|
||||
it('should return the initial state', () => {
|
||||
expect(reducer(undefined, {} as any)).toEqual({ sidebarOpen: false });
|
||||
});
|
||||
});
|
|
@ -1,24 +0,0 @@
|
|||
import { SIDEBAR_OPEN, SIDEBAR_CLOSE } from '../actions/sidebar';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
|
||||
type State = {
|
||||
sidebarOpen: boolean;
|
||||
};
|
||||
|
||||
const initialState: State = {
|
||||
sidebarOpen: false,
|
||||
};
|
||||
|
||||
const sidebar = (state: State = initialState, action: AnyAction): State => {
|
||||
switch (action.type) {
|
||||
case SIDEBAR_OPEN:
|
||||
return { sidebarOpen: true };
|
||||
case SIDEBAR_CLOSE:
|
||||
return { sidebarOpen: false };
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export { sidebar as default };
|
|
@ -1,72 +0,0 @@
|
|||
import {
|
||||
STATUS_HOVER_CARD_OPEN,
|
||||
STATUS_HOVER_CARD_CLOSE,
|
||||
STATUS_HOVER_CARD_UPDATE,
|
||||
} from 'pl-fe/actions/status-hover-card';
|
||||
|
||||
import reducer, { ReducerRecord } from './status-hover-card';
|
||||
|
||||
describe(STATUS_HOVER_CARD_OPEN, () => {
|
||||
it('sets the ref and statusId', () => {
|
||||
const ref = { current: document.createElement('div') };
|
||||
|
||||
const action = {
|
||||
type: STATUS_HOVER_CARD_OPEN,
|
||||
ref,
|
||||
statusId: '1234',
|
||||
};
|
||||
|
||||
const result = reducer(undefined, action);
|
||||
expect(result.ref).toBe(ref);
|
||||
expect(result.statusId).toBe('1234');
|
||||
});
|
||||
});
|
||||
|
||||
describe(STATUS_HOVER_CARD_CLOSE, () => {
|
||||
it('flushes the state', () => {
|
||||
const state = ReducerRecord({
|
||||
ref: { current: document.createElement('div') },
|
||||
statusId: '1234',
|
||||
});
|
||||
|
||||
const action = { type: STATUS_HOVER_CARD_CLOSE };
|
||||
|
||||
const result = reducer(state, action);
|
||||
expect(result.ref).toBe(null);
|
||||
expect(result.statusId).toBe('');
|
||||
});
|
||||
|
||||
it('leaves the state alone if hovered', () => {
|
||||
const state = ReducerRecord({
|
||||
ref: { current: document.createElement('div') },
|
||||
statusId: '1234',
|
||||
hovered: true,
|
||||
});
|
||||
|
||||
const action = { type: STATUS_HOVER_CARD_CLOSE };
|
||||
const result = reducer(state, action);
|
||||
expect(result).toEqual(state);
|
||||
});
|
||||
|
||||
it('action.force flushes the state even if hovered', () => {
|
||||
const state = ReducerRecord({
|
||||
ref: { current: document.createElement('div') },
|
||||
statusId: '1234',
|
||||
hovered: true,
|
||||
});
|
||||
|
||||
const action = { type: STATUS_HOVER_CARD_CLOSE, force: true };
|
||||
const result = reducer(state, action);
|
||||
expect(result.ref).toBe(null);
|
||||
expect(result.statusId).toBe('');
|
||||
});
|
||||
});
|
||||
|
||||
describe(STATUS_HOVER_CARD_UPDATE, () => {
|
||||
it('sets hovered', () => {
|
||||
const state = ReducerRecord();
|
||||
const action = { type: STATUS_HOVER_CARD_UPDATE };
|
||||
const result = reducer(state, action);
|
||||
expect(result.hovered).toBe(true);
|
||||
});
|
||||
});
|
|
@ -1,41 +0,0 @@
|
|||
import { Record as ImmutableRecord } from 'immutable';
|
||||
|
||||
import {
|
||||
STATUS_HOVER_CARD_OPEN,
|
||||
STATUS_HOVER_CARD_CLOSE,
|
||||
STATUS_HOVER_CARD_UPDATE,
|
||||
} from 'pl-fe/actions/status-hover-card';
|
||||
|
||||
import type { AnyAction } from 'redux';
|
||||
|
||||
const ReducerRecord = ImmutableRecord({
|
||||
ref: null as React.MutableRefObject<HTMLDivElement> | null,
|
||||
statusId: '',
|
||||
hovered: false,
|
||||
});
|
||||
|
||||
type State = ReturnType<typeof ReducerRecord>;
|
||||
|
||||
const statusHoverCard = (state: State = ReducerRecord(), action: AnyAction) => {
|
||||
switch (action.type) {
|
||||
case STATUS_HOVER_CARD_OPEN:
|
||||
return state.withMutations((state) => {
|
||||
state.set('ref', action.ref);
|
||||
state.set('statusId', action.statusId);
|
||||
});
|
||||
case STATUS_HOVER_CARD_UPDATE:
|
||||
return state.set('hovered', true);
|
||||
case STATUS_HOVER_CARD_CLOSE:
|
||||
if (state.hovered === true && !action.force)
|
||||
return state;
|
||||
else
|
||||
return ReducerRecord();
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
ReducerRecord,
|
||||
statusHoverCard as default,
|
||||
};
|
31
packages/pl-fe/src/stores/account-hover-card.ts
Normal file
31
packages/pl-fe/src/stores/account-hover-card.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { create } from 'zustand';
|
||||
|
||||
type State = {
|
||||
ref: React.MutableRefObject<HTMLDivElement> | null;
|
||||
accountId: string | null;
|
||||
hovered: boolean;
|
||||
openAccountHoverCard: (ref: React.MutableRefObject<HTMLDivElement>, accountId: string) => void;
|
||||
updateAccountHoverCard: () => void;
|
||||
closeAccountHoverCard: (force?: boolean) => void;
|
||||
}
|
||||
|
||||
const useAccountHoverCardStore = create<State>((set) => ({
|
||||
ref: null,
|
||||
accountId: null,
|
||||
hovered: false,
|
||||
openAccountHoverCard: (ref, accountId) => set({
|
||||
ref,
|
||||
accountId,
|
||||
}),
|
||||
updateAccountHoverCard: () => set({
|
||||
hovered: true,
|
||||
}),
|
||||
closeAccountHoverCard: (force = false) => set((state) => state.hovered && !force ? {} : {
|
||||
ref: null,
|
||||
accountId: null,
|
||||
hovered: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
export { useAccountHoverCardStore };
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
import { create } from 'zustand';
|
||||
|
||||
type State = {
|
||||
isOpen: boolean;
|
||||
openDropdownMenu: () => void;
|
||||
closeDropdownMenu: () => void;
|
||||
}
|
||||
|
||||
const useDropdownMenuStore = create<State>((set) => ({
|
||||
isOpen: false,
|
||||
openDropdownMenu: () => set({ isOpen: true }),
|
||||
closeDropdownMenu: () => set({ isOpen: false }),
|
||||
}));
|
||||
|
||||
export { useDropdownMenuStore };
|
||||
|
|
@ -1,2 +1,4 @@
|
|||
export { useDropdownMenuStore } from './dropdown-menu';
|
||||
export { useAccountHoverCardStore } from './account-hover-card';
|
||||
export { useModalsStore } from './modals';
|
||||
export { useStatusHoverCardStore } from './status-hover-card';
|
||||
export { useUiStore } from './ui';
|
||||
|
|
31
packages/pl-fe/src/stores/status-hover-card.ts
Normal file
31
packages/pl-fe/src/stores/status-hover-card.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { create } from 'zustand';
|
||||
|
||||
type State = {
|
||||
ref: React.MutableRefObject<HTMLDivElement> | null;
|
||||
statusId: string | null;
|
||||
hovered: boolean;
|
||||
openStatusHoverCard: (ref: React.MutableRefObject<HTMLDivElement>, statusId: string) => void;
|
||||
updateStatusHoverCard: () => void;
|
||||
closeStatusHoverCard: (force?: boolean) => void;
|
||||
}
|
||||
|
||||
const useStatusHoverCardStore = create<State>((set) => ({
|
||||
ref: null,
|
||||
statusId: null,
|
||||
hovered: false,
|
||||
openStatusHoverCard: (ref, statusId) => set({
|
||||
ref,
|
||||
statusId,
|
||||
}),
|
||||
updateStatusHoverCard: () => set({
|
||||
hovered: true,
|
||||
}),
|
||||
closeStatusHoverCard: (force = false) => set((state) => state.hovered && !force ? {} : {
|
||||
ref: null,
|
||||
statusId: null,
|
||||
hovered: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
export { useStatusHoverCardStore };
|
||||
|
22
packages/pl-fe/src/stores/ui.ts
Normal file
22
packages/pl-fe/src/stores/ui.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
import { create } from 'zustand';
|
||||
|
||||
type State = {
|
||||
isDropdownMenuOpen: boolean;
|
||||
openDropdownMenu: () => void;
|
||||
closeDropdownMenu: () => void;
|
||||
isSidebarOpen: boolean;
|
||||
openSidebar: () => void;
|
||||
closeSidebar: () => void;
|
||||
}
|
||||
|
||||
const useUiStore = create<State>((set) => ({
|
||||
isDropdownMenuOpen: false,
|
||||
openDropdownMenu: () => set({ isDropdownMenuOpen: true }),
|
||||
closeDropdownMenu: () => set({ isDropdownMenuOpen: false }),
|
||||
isSidebarOpen: false,
|
||||
openSidebar: () => set({ isSidebarOpen: true }),
|
||||
closeSidebar: () => set({ isSidebarOpen: false }),
|
||||
}));
|
||||
|
||||
export { useUiStore };
|
||||
|
Loading…
Reference in a new issue