1 import type { ReactNode } from 'react';
2 import { memo, useCallback, useRef } from 'react';
3 import { useHistory } from 'react-router-dom';
5 import type { HotkeyTuple, IconName, IconSize } from '@proton/components';
8 SidebarListItemContent,
9 SidebarListItemContentIcon,
14 } from '@proton/components';
15 import { useLoading } from '@proton/hooks';
16 import { MAILBOX_LABEL_IDS } from '@proton/shared/lib/constants';
17 import { wait } from '@proton/shared/lib/helpers/promise';
18 import clsx from '@proton/utils/clsx';
19 import isTruthy from '@proton/utils/isTruthy';
20 import noop from '@proton/utils/noop';
22 import { useCheckAllRef } from 'proton-mail/containers/CheckAllRefProvider';
23 import useMailModel from 'proton-mail/hooks/useMailModel';
24 import { useSelectAll } from 'proton-mail/hooks/useSelectAll';
26 import { LABEL_IDS_TO_HUMAN } from '../../constants';
27 import { shouldDisplayTotal } from '../../helpers/labels';
28 import type { ApplyLabelsParams } from '../../hooks/actions/label/useApplyLabels';
29 import type { MoveParams } from '../../hooks/actions/move/useMoveToFolder';
30 import { useGetElementsFromIDs } from '../../hooks/mailbox/useElements';
31 import LocationAside from './LocationAside';
33 import './SidebarItem.scss';
35 const { ALL_MAIL, ALMOST_ALL_MAIL, ALL_DRAFTS, ALL_SENT, SCHEDULED, SNOOZED, OUTBOX } = MAILBOX_LABEL_IDS;
37 const NO_DROP_SET: Set<string> = new Set([ALL_MAIL, ALMOST_ALL_MAIL, ALL_DRAFTS, ALL_SENT, SCHEDULED, SNOOZED, OUTBOX]);
39 const defaultShortcutHandlers: HotkeyTuple[] = [];
42 currentLabelID: string;
46 hideCountOnHover?: boolean;
50 shortcutText?: string;
52 itemOptions?: ReactNode;
55 totalMessagesCount?: number;
56 shortcutHandlers?: HotkeyTuple[];
57 onFocus?: (id: string) => void;
58 isOptionDropdownOpened?: boolean;
61 moveToFolder: (params: MoveParams) => void;
62 applyLabels: (params: ApplyLabelsParams) => void;
65 const SidebarItem = ({
77 hideCountOnHover = true,
79 totalMessagesCount = 0,
80 shortcutHandlers = defaultShortcutHandlers,
83 isOptionDropdownOpened,
88 const { call } = useEventManager();
89 const history = useHistory();
90 const { Shortcuts } = useMailModel('MailSettings');
91 const getElementsFromIDs = useGetElementsFromIDs();
92 const { selectAll } = useSelectAll({ labelID });
93 const { checkAllRef } = useCheckAllRef();
95 const [refreshing, withRefreshing] = useLoading(false);
97 const humanID = LABEL_IDS_TO_HUMAN[labelID as MAILBOX_LABEL_IDS]
98 ? LABEL_IDS_TO_HUMAN[labelID as MAILBOX_LABEL_IDS]
100 const link = `/${humanID}`;
102 const needsTotalDisplay = shouldDisplayTotal(labelID);
104 const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
106 history.location.pathname.endsWith(link) &&
107 // No search, no paging, nothing
108 history.location.hash === '' &&
109 // Not already refreshing
112 event.preventDefault();
113 void withRefreshing(Promise.all([call(), wait(1000)]));
117 const handleRefresh = () => {
118 void withRefreshing(Promise.all([call(), wait(1000)]));
121 const dragFilter = useCallback(() => {
122 return !(labelID === currentLabelID) && !NO_DROP_SET.has(labelID);
123 }, [currentLabelID, labelID]);
125 const dropCallback = useCallback(
126 (itemIDs: string[]) => {
127 const elements = getElementsFromIDs(itemIDs);
133 sourceLabelID: currentLabelID,
134 destinationLabelID: labelID,
137 onCheckAll: checkAllRef?.current ? checkAllRef.current : undefined,
142 changes: { [labelID]: true },
143 labelID: currentLabelID,
145 onCheckAll: checkAllRef?.current ? checkAllRef.current : undefined,
150 [applyLabels, dragFilter, checkAllRef, currentLabelID, getElementsFromIDs, isFolder, labelID, selectAll]
153 const { dragOver, dragProps, handleDrop } = useItemsDroppable(dragFilter, isFolder ? 'move' : 'link', dropCallback);
155 const elementRef = useRef<HTMLAnchorElement>(null);
156 useHotkeys(elementRef, shortcutHandlers);
158 const tooltipText = text;
159 const titleText = shortcutText !== undefined && Shortcuts ? `${text} ${shortcutText}` : text;
164 dragOver && 'navigation__dragover',
165 'group-hover-hide-container group-hover-opacity-container',
167 data-testid={`sidebar-label:${text}`}
172 onClick={handleClick}
176 data-testid={`navigation-link:${humanID}`}
177 data-shortcut-target={['navigation-link', id].filter(isTruthy).join(' ')}
178 onFocus={() => onFocus(id || '')}
180 <SidebarListItemContent
181 collapsed={collapsed}
184 <SidebarListItemContentIcon
186 collapsed && 'flex mx-auto',
187 isLabel && 'navigation-icon--fixAliasing',
197 unreadCount={needsTotalDisplay ? totalMessagesCount : unreadCount}
198 weak={labelID !== MAILBOX_LABEL_IDS.INBOX}
199 refreshing={refreshing}
200 onRefresh={handleRefresh}
201 shouldDisplayTotal={needsTotalDisplay}
202 hideCountOnHover={hideCountOnHover}
203 itemOptions={itemOptions}
204 isOptionDropdownOpened={isOptionDropdownOpened}
205 collapsed={collapsed}
210 className={clsx('text-ellipsis', collapsed && 'sr-only')}
211 title={collapsed ? undefined : titleText}
215 </SidebarListItemContent>
216 </SidebarListItemLink>
221 export default memo(SidebarItem);