Cleanup - unused files / unused exports / duplicate exports
[ProtonMail-WebClient.git] / packages / components / containers / labels / LabelsSection.tsx
blobe6644a64fc339aa91474dea8c0d2dfaa447b5182
1 import { useEffect, useState } from 'react';
2 import { arrayMove } from 'react-sortable-hoc';
4 import { c } from 'ttag';
6 import { useUser } from '@proton/account/user/hooks';
7 import { Button } from '@proton/atoms';
8 import useDebounceInput from '@proton/components/components/input/useDebounceInput';
9 import Loader from '@proton/components/components/loader/Loader';
10 import useModalState from '@proton/components/components/modalTwo/useModalState';
11 import MailUpsellButton from '@proton/components/components/upsell/MailUpsellButton';
12 import LabelsUpsellModal from '@proton/components/components/upsell/modal/types/LabelsUpsellModal';
13 import SettingsSection from '@proton/components/containers/account/SettingsSection';
14 import useApi from '@proton/components/hooks/useApi';
15 import useEventManager from '@proton/components/hooks/useEventManager';
16 import useNotifications from '@proton/components/hooks/useNotifications';
17 import { useLoading } from '@proton/hooks';
18 import { useLabels } from '@proton/mail';
19 import { orderLabels } from '@proton/shared/lib/api/labels';
20 import { MAIL_UPSELL_PATHS } from '@proton/shared/lib/constants';
21 import { hasReachedLabelLimit } from '@proton/shared/lib/helpers/folder';
22 import isDeepEqual from '@proton/shared/lib/helpers/isDeepEqual';
23 import type { Label } from '@proton/shared/lib/interfaces';
25 import LabelSortableList from './LabelSortableList';
26 import EditLabelModal from './modals/EditLabelModal';
28 const DEBOUNCE_VALUE = 1600;
30 const toLabelIDs = (labels: Label[]) => labels.map(({ ID }) => ID).join(',');
32 function LabelsSection() {
33     const [user] = useUser();
34     const [labels = [], loadingLabels] = useLabels();
35     const { call } = useEventManager();
36     const api = useApi();
37     const { createNotification } = useNotifications();
38     const [loading, withLoading] = useLoading();
40     const [localLabels, setLocalLabels] = useState(labels);
41     const debouncedLabels = useDebounceInput(localLabels, DEBOUNCE_VALUE);
43     const labelsOrder = toLabelIDs(labels);
44     const debouncedLabelOrder = toLabelIDs(debouncedLabels);
46     const canCreateLabel = !hasReachedLabelLimit(user, labels);
48     const [editLabelProps, setEditLabelModalOpen, renderEditLabelModal] = useModalState();
49     const [upsellModalProps, handleUpsellModalDisplay, renderUpsellModal] = useModalState();
51     /**
52      * Refresh the list + update API and call event, it can be slow.
53      * We want a responsive UI, if it fails the item will go back to its previous index
54      * @param  {Number} oldIndex cf https://github.com/clauderic/react-sortable-hoc#basic-example
55      * @param  {Number} newIndex
56      */
57     const onSortEnd = async ({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) => {
58         const newLabels = arrayMove(localLabels, oldIndex, newIndex);
59         setLocalLabels(newLabels);
60     };
62     const handleSortLabel = async () => {
63         const newLabels = [...localLabels].sort((a, b) => a.Name.localeCompare(b.Name, undefined, { numeric: true }));
64         setLocalLabels(newLabels);
65         createNotification({ text: c('Success message after sorting labels').t`Labels sorted` });
66     };
68     useEffect(() => {
69         if (!debouncedLabelOrder || debouncedLabelOrder === labelsOrder) {
70             return;
71         }
73         const sync = async () => {
74             await api(orderLabels({ LabelIDs: debouncedLabels.map(({ ID }) => ID) }));
75             await call();
76         };
78         void sync();
79     }, [debouncedLabels]);
81     useEffect(() => {
82         if (isDeepEqual(debouncedLabels, labels)) {
83             return;
84         }
85         setLocalLabels(labels);
86     }, [labels]);
88     return (
89         <SettingsSection>
90             {loadingLabels ? (
91                 <Loader />
92             ) : (
93                 <>
94                     <div className="flex gap-4 mb-7 labels-action">
95                         {canCreateLabel ? (
96                             <Button color="norm" onClick={() => setEditLabelModalOpen(true)}>
97                                 {c('Action').t`Add label`}
98                             </Button>
99                         ) : (
100                             <MailUpsellButton
101                                 onClick={() => handleUpsellModalDisplay(true)}
102                                 text={c('Action').t`Get more labels`}
103                             />
104                         )}
105                         {localLabels.length ? (
106                             <Button
107                                 shape="outline"
108                                 title={c('Title').t`Sort labels alphabetically`}
109                                 loading={loading}
110                                 onClick={() => withLoading(handleSortLabel())}
111                             >
112                                 {c('Action').t`Sort`}
113                             </Button>
114                         ) : null}
115                     </div>
116                     {localLabels.length ? <LabelSortableList items={localLabels} onSortEnd={onSortEnd} /> : null}
118                     {renderEditLabelModal && <EditLabelModal {...editLabelProps} type="label" />}
120                     {renderUpsellModal && (
121                         <LabelsUpsellModal
122                             modalProps={upsellModalProps}
123                             feature={MAIL_UPSELL_PATHS.UNLIMITED_LABELS}
124                             isSettings
125                         />
126                     )}
127                 </>
128             )}
129         </SettingsSection>
130     );
133 export default LabelsSection;