Skip to content

Commit 9700a80

Browse files
author
MargeBot
committed
Merge branch 'INDA-457-fix-missing-notification' into 'main'
Add missing hooks in refactoring and improve test coverage See merge request web/clients!16669
2 parents 9a8c024 + 2e45499 commit 9700a80

13 files changed

+669
-94
lines changed

applications/mail/src/app/containers/PageContainer.tsx

+4-26
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,19 @@ import { Redirect, useRouteMatch } from 'react-router-dom';
44

55
import { useUserSettings } from '@proton/account/userSettings/hooks';
66
import type { Breakpoints } from '@proton/components';
7-
import { MailShortcutsModal, useModalState, useOpenDrawerOnLoad } from '@proton/components';
7+
import { MailShortcutsModal, useModalState } from '@proton/components';
88
import { useFolders, useLabels } from '@proton/mail';
99
import type { UserSettings } from '@proton/shared/lib/interfaces';
1010
import type { Label } from '@proton/shared/lib/interfaces/Label';
1111
import { HUMAN_TO_LABEL_IDS } from '@proton/shared/lib/mail/constants';
1212

1313
import AssistantIframe from 'proton-mail/components/assistant/AssistantIframe';
14-
import useComposerEvent from 'proton-mail/hooks/useComposerEvent';
1514
import useMailModel from 'proton-mail/hooks/useMailModel';
16-
import { useMailPTTMetric } from 'proton-mail/metrics/useMailPTTMetric';
15+
import { useAppShellSideEffects } from 'proton-mail/router/sideEffects/useAppShellSideEffects';
1716

1817
import PrivateLayout from '../components/layout/PrivateLayout';
1918
import { LabelActionsContextProvider } from '../components/sidebar/EditLabelContext';
2019
import type { MailUrlParams } from '../helpers/mailboxUrl';
21-
import { useContactsListener } from '../hooks/contact/useContactsListener';
22-
import { useConversationsEvent } from '../hooks/events/useConversationsEvents';
23-
import { useMessagesEvents } from '../hooks/events/useMessagesEvents';
24-
import useIncomingDefaultsEvents from '../hooks/incomingDefaults/useIncomingDefaultsEvents';
25-
import useIncomingDefaultsLoad from '../hooks/incomingDefaults/useIncomingDefaultsLoad';
26-
import { usePageHotkeys } from '../hooks/mailbox/usePageHotkeys';
2720
import { useDeepMemo } from '../hooks/useDeepMemo';
2821
import MailStartupModals from './MailStartupModals';
2922
import MailboxContainer from './mailbox/MailboxContainer';
@@ -38,25 +31,10 @@ const PageContainer = ({ params: { elementID, labelID, messageID }, breakpoints
3831
const mailSettings = useMailModel('MailSettings');
3932
const [mailShortcutsProps, setMailShortcutsModalOpen, renderMailShortcutsModal] = useModalState();
4033

41-
useOpenDrawerOnLoad();
42-
43-
useContactsListener();
44-
useConversationsEvent();
45-
useMessagesEvents();
46-
47-
useMailPTTMetric();
48-
4934
/**
50-
* Incoming defaults
51-
* - cache loading
52-
* - events subscription
35+
* Temporary: Page container side effects
5336
*/
54-
useIncomingDefaultsLoad();
55-
useIncomingDefaultsEvents();
56-
57-
useComposerEvent();
58-
59-
usePageHotkeys({ onOpenShortcutsModal: () => setMailShortcutsModalOpen(true) });
37+
useAppShellSideEffects({ openShortcutsModal: (value: boolean) => setMailShortcutsModalOpen(value) });
6038

6139
if (!labelID) {
6240
return <Redirect to="/inbox" />;

applications/mail/src/app/containers/mailbox/MailboxContainer.tsx

+7-30
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import { useHistory, useLocation } from 'react-router-dom';
44

55
import { c } from 'ttag';
66

7-
import { useCalendarUserSettings } from '@proton/calendar/calendarUserSettings/hooks';
8-
import { useCalendars } from '@proton/calendar/calendars/hooks';
97
import type { Breakpoints, CommanderItemInterface } from '@proton/components';
108
import {
119
Commander,
@@ -14,7 +12,6 @@ import {
1412
ErrorBoundary,
1513
InboxQuickSettingsAppButton,
1614
PrivateMainArea,
17-
useInboxDesktopBadgeCount,
1815
useItemsSelection,
1916
useModalState,
2017
} from '@proton/components';
@@ -33,10 +30,9 @@ import clsx from '@proton/utils/clsx';
3330

3431
import { useCheckAllRef } from 'proton-mail/containers/CheckAllRefProvider';
3532
import useMailDrawer from 'proton-mail/hooks/drawer/useMailDrawer';
36-
import useInboxDesktopElementId from 'proton-mail/hooks/useInboxDesktopElementId';
37-
import useMailtoHash from 'proton-mail/hooks/useMailtoHash';
3833
import { useSelectAll } from 'proton-mail/hooks/useSelectAll';
3934
import { useMailECRTMetric } from 'proton-mail/metrics/useMailECRTMetric';
35+
import { useMailboxContainerSideEffects } from 'proton-mail/router/sideEffects/useMailboxContainerSideEffects';
4036
import { useMailSelector } from 'proton-mail/store/hooks';
4137

4238
import ConversationView from '../../components/conversation/ConversationView';
@@ -63,14 +59,9 @@ import {
6359
import { usePermanentDelete } from '../../hooks/actions/delete/usePermanentDelete';
6460
import { useMarkAs } from '../../hooks/actions/markAs/useMarkAs';
6561
import { ComposeTypes } from '../../hooks/composer/useCompose';
66-
import useNewEmailNotification from '../../hooks/mailbox/notifications/useNewEmailNotification';
67-
import { useApplyEncryptedSearch } from '../../hooks/mailbox/useApplyEncryptedSearch';
6862
import { useElements, useGetElementsFromIDs } from '../../hooks/mailbox/useElements';
69-
import { useMailboxFavicon } from '../../hooks/mailbox/useMailboxFavicon';
7063
import { useMailboxFocus } from '../../hooks/mailbox/useMailboxFocus';
7164
import { useMailboxHotkeys } from '../../hooks/mailbox/useMailboxHotkeys';
72-
import { useMailboxPageTitle } from '../../hooks/mailbox/useMailboxPageTitle';
73-
import usePreLoadElements from '../../hooks/mailbox/usePreLoadElements';
7465
import { useWelcomeFlag } from '../../hooks/mailbox/useWelcomeFlag';
7566
import { useDeepMemo } from '../../hooks/useDeepMemo';
7667
import { useResizeMessageView } from '../../hooks/useResizeMessageView';
@@ -141,12 +132,6 @@ const MailboxContainer = ({
141132
history.push(`/${LABEL_IDS_TO_HUMAN[labelID]}`);
142133
};
143134

144-
// Open a composer when the url contains a mailto query
145-
useMailtoHash({ isSearch });
146-
147-
// Opens the email details when the url contains a elementID query
148-
useInboxDesktopElementId({ isSearch });
149-
150135
const handlePage = useCallback(
151136
(pageNumber: number) => {
152137
history.push(setPageInUrl(history.location, pageNumber));
@@ -176,7 +161,6 @@ const MailboxContainer = ({
176161
const { labelID, elements, elementIDs, loading, placeholderCount, total } = useElements(elementsParams);
177162

178163
const { handleDelete: permanentDelete, deleteSelectionModal, deleteAllModal } = usePermanentDelete(labelID);
179-
useApplyEncryptedSearch(elementsParams);
180164

181165
const handleBack = useCallback(
182166
() => history.push(setParamsInLocation(history.location, { labelID })),
@@ -187,11 +171,6 @@ const MailboxContainer = ({
187171

188172
const onCompose = useOnCompose();
189173

190-
useMailboxPageTitle(labelID);
191-
useMailboxFavicon(labelID);
192-
useInboxDesktopBadgeCount();
193-
useScrollToTop(listRef as RefObject<HTMLElement>, [page, labelID, sort, filter, searchParameters]);
194-
195174
const onCheck = (checked: boolean) => {
196175
// Reset select all state when interacting with checkboxes in the list
197176
if (selectAll && !checked) {
@@ -220,12 +199,6 @@ const MailboxContainer = ({
220199
onCheck,
221200
});
222201

223-
useNewEmailNotification(() => handleCheckAll(false));
224-
225-
// Launch two calendar-specific API calls here to boost calendar widget performance
226-
useCalendars();
227-
useCalendarUserSettings();
228-
229202
const elementsLength = loading ? placeholderCount : elements.length;
230203
const showList = columnMode || !elementID;
231204
const showContentPanel = (columnMode && !!elementsLength) || !!elementID;
@@ -367,8 +340,6 @@ const MailboxContainer = ({
367340
[selectedIDs, permanentDelete, selectAll]
368341
);
369342

370-
usePreLoadElements({ elements, labelID, loading });
371-
372343
const {
373344
elementRef,
374345
moveScheduledModal,
@@ -569,6 +540,12 @@ const MailboxContainer = ({
569540

570541
const canShowDrawer = drawerSidebarButtons.length > 0;
571542

543+
/**
544+
* Temporary: Mailbox container side effects
545+
*/
546+
useMailboxContainerSideEffects({ labelID, isSearch, elementsParams, handleCheckAll, elements, loading });
547+
useScrollToTop(listRef as RefObject<HTMLElement>, [page, labelID, sort, filter, searchParameters]);
548+
572549
return (
573550
<MailboxContainerContextProvider
574551
isResizing={isResizing}

applications/mail/src/app/hooks/mailbox/useApplyEncryptedSearch.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import {
2727
} from '../../store/elements/elementsSelectors';
2828
import type { MailState } from '../../store/store';
2929

30-
interface EncryptedSearchParams {
30+
export interface EncryptedSearchParams {
3131
conversationMode: boolean;
3232
labelID: string;
3333
page: number;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import type { ReactNode } from 'react';
2+
import { MemoryRouter } from 'react-router-dom';
3+
4+
import { renderHook } from '@testing-library/react-hooks';
5+
6+
import { useDynamicFavicon } from '@proton/components';
7+
import { useConversationCounts } from '@proton/mail/counts/conversationCounts';
8+
import { useMessageCounts } from '@proton/mail/counts/messageCounts';
9+
import { useMailSettings } from '@proton/mail/mailSettings/hooks';
10+
import { MAILBOX_LABEL_IDS } from '@proton/shared/lib/constants';
11+
import { DEFAULT_MAILSETTINGS, UNREAD_FAVICON, VIEW_MODE } from '@proton/shared/lib/mail/mailSettings';
12+
13+
import { useMailboxFavicon } from './useMailboxFavicon';
14+
15+
jest.mock('@proton/components', () => ({
16+
useDynamicFavicon: jest.fn(),
17+
}));
18+
19+
jest.mock('@proton/mail/mailSettings/hooks');
20+
const mockUseMailSettings = useMailSettings as jest.Mock;
21+
22+
jest.mock('@proton/mail/counts/conversationCounts');
23+
const mockUseConversationCounts = useConversationCounts as jest.Mock;
24+
25+
jest.mock('@proton/mail/counts/messageCounts');
26+
const mockUseMessageCounts = useMessageCounts as jest.Mock;
27+
28+
const mockBaseFavicon = 'mock-base-favicon';
29+
const mockFavicons = {
30+
1: 'favicon-1',
31+
2: 'favicon-2',
32+
10: 'favicon-10',
33+
50: 'favicon-50',
34+
99: 'favicon-99',
35+
100: 'favicon-100+',
36+
};
37+
38+
jest.mock('../../../assets/favicons', () => {
39+
return {
40+
__esModule: true,
41+
default: {
42+
1: 'favicon-1',
43+
2: 'favicon-2',
44+
10: 'favicon-10',
45+
50: 'favicon-50',
46+
99: 'favicon-99',
47+
100: 'favicon-100+',
48+
},
49+
baseFavicon: 'mock-base-favicon',
50+
};
51+
});
52+
53+
const wrapper = ({ children }: { children: ReactNode }) => <MemoryRouter>{children}</MemoryRouter>;
54+
55+
describe('useMailboxFavicon', () => {
56+
beforeEach(() => {
57+
jest.clearAllMocks();
58+
});
59+
60+
it('should use baseFavicon when there are no unread messages', () => {
61+
mockUseConversationCounts.mockReturnValue([[{ LabelID: MAILBOX_LABEL_IDS.INBOX, Unread: 0 }]]);
62+
mockUseMessageCounts.mockReturnValue([[{ LabelID: MAILBOX_LABEL_IDS.INBOX, Unread: 0 }]]);
63+
mockUseMailSettings.mockReturnValue([
64+
{ ...DEFAULT_MAILSETTINGS, ViewMode: VIEW_MODE.GROUP, UnreadFavicon: UNREAD_FAVICON.ENABLED },
65+
]);
66+
67+
renderHook(() => useMailboxFavicon(MAILBOX_LABEL_IDS.INBOX), { wrapper });
68+
69+
expect(useDynamicFavicon).toHaveBeenCalledWith(mockBaseFavicon);
70+
});
71+
72+
it('should use the correct favicon when there are 10 unread messages in conversation mode', () => {
73+
mockUseConversationCounts.mockReturnValue([[{ LabelID: MAILBOX_LABEL_IDS.INBOX, Unread: 10 }]]);
74+
mockUseMessageCounts.mockReturnValue([[{ LabelID: MAILBOX_LABEL_IDS.INBOX, Unread: 0 }]]);
75+
mockUseMailSettings.mockReturnValue([
76+
{ ...DEFAULT_MAILSETTINGS, ViewMode: VIEW_MODE.GROUP, UnreadFavicon: UNREAD_FAVICON.ENABLED },
77+
]);
78+
79+
renderHook(() => useMailboxFavicon(MAILBOX_LABEL_IDS.INBOX), { wrapper });
80+
81+
expect(useDynamicFavicon).toHaveBeenCalledWith(mockFavicons[10]);
82+
});
83+
84+
it('should use the correct favicon when there are 10 unread messages in message mode', () => {
85+
mockUseConversationCounts.mockReturnValue([[{ LabelID: MAILBOX_LABEL_IDS.INBOX, Unread: 0 }]]);
86+
mockUseMessageCounts.mockReturnValue([[{ LabelID: MAILBOX_LABEL_IDS.INBOX, Unread: 10 }]]);
87+
mockUseMailSettings.mockReturnValue([
88+
{ ...DEFAULT_MAILSETTINGS, ViewMode: VIEW_MODE.SINGLE, UnreadFavicon: UNREAD_FAVICON.ENABLED },
89+
]);
90+
91+
renderHook(() => useMailboxFavicon(MAILBOX_LABEL_IDS.INBOX), { wrapper });
92+
93+
expect(useDynamicFavicon).toHaveBeenCalledWith(mockFavicons[10]);
94+
});
95+
96+
it('should use the last favicon when there are more than 100 unread messages in conversation mode', () => {
97+
mockUseConversationCounts.mockReturnValue([[{ LabelID: MAILBOX_LABEL_IDS.INBOX, Unread: 200 }]]);
98+
mockUseMessageCounts.mockReturnValue([[{ LabelID: MAILBOX_LABEL_IDS.INBOX, Unread: 0 }]]);
99+
mockUseMailSettings.mockReturnValue([
100+
{ ...DEFAULT_MAILSETTINGS, ViewMode: VIEW_MODE.GROUP, UnreadFavicon: UNREAD_FAVICON.ENABLED },
101+
]);
102+
103+
renderHook(() => useMailboxFavicon(MAILBOX_LABEL_IDS.INBOX), { wrapper });
104+
105+
expect(useDynamicFavicon).toHaveBeenCalledWith(mockFavicons[100]);
106+
});
107+
108+
it('should use the last favicon when there are more than 100 unread messages in message mode', () => {
109+
mockUseConversationCounts.mockReturnValue([[{ LabelID: MAILBOX_LABEL_IDS.INBOX, Unread: 0 }]]);
110+
mockUseMessageCounts.mockReturnValue([[{ LabelID: MAILBOX_LABEL_IDS.INBOX, Unread: 200 }]]);
111+
mockUseMailSettings.mockReturnValue([
112+
{ ...DEFAULT_MAILSETTINGS, ViewMode: VIEW_MODE.SINGLE, UnreadFavicon: UNREAD_FAVICON.ENABLED },
113+
]);
114+
115+
renderHook(() => useMailboxFavicon(MAILBOX_LABEL_IDS.INBOX), { wrapper });
116+
117+
expect(useDynamicFavicon).toHaveBeenCalledWith(mockFavicons[100]);
118+
});
119+
120+
it('should use baseFavicon when the favicon is disabled in conversation mode', () => {
121+
mockUseConversationCounts.mockReturnValue([[{ LabelID: MAILBOX_LABEL_IDS.INBOX, Unread: 10 }]]);
122+
mockUseMessageCounts.mockReturnValue([[{ LabelID: MAILBOX_LABEL_IDS.INBOX, Unread: 0 }]]);
123+
mockUseMailSettings.mockReturnValue([
124+
{ ...DEFAULT_MAILSETTINGS, ViewMode: VIEW_MODE.GROUP, UnreadFavicon: UNREAD_FAVICON.DISABLED },
125+
]);
126+
127+
renderHook(() => useMailboxFavicon(MAILBOX_LABEL_IDS.INBOX), { wrapper });
128+
129+
expect(useDynamicFavicon).toHaveBeenCalledWith(mockBaseFavicon);
130+
});
131+
132+
it('should use baseFavicon when the favicon is disabled in message mode', () => {
133+
mockUseConversationCounts.mockReturnValue([[{ LabelID: MAILBOX_LABEL_IDS.INBOX, Unread: 0 }]]);
134+
mockUseMessageCounts.mockReturnValue([[{ LabelID: MAILBOX_LABEL_IDS.INBOX, Unread: 10 }]]);
135+
mockUseMailSettings.mockReturnValue([
136+
{ ...DEFAULT_MAILSETTINGS, ViewMode: VIEW_MODE.SINGLE, UnreadFavicon: UNREAD_FAVICON.DISABLED },
137+
]);
138+
139+
renderHook(() => useMailboxFavicon(MAILBOX_LABEL_IDS.INBOX), { wrapper });
140+
141+
expect(useDynamicFavicon).toHaveBeenCalledWith(mockBaseFavicon);
142+
});
143+
});

applications/mail/src/app/hooks/mailbox/usePreLoadElements.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const usePreLoadElements = ({ elements, labelID, loading }: Props) => {
2525
const { feature: preloadedConversations } = useFeature<number>(FeatureCode.NumberOfPreloadedConversations);
2626
const { feature: electronPreloadAmount } = useFeature<number>(FeatureCode.ElectronConvPreloadAmount);
2727

28-
// We ensure that there is a value and that it does't impacts API calls
28+
// We ensure that there is a value and that it doesn't impacts API calls
2929
const defaultPreload = preloadedConversations?.Value || 0;
3030
const electronPreload = electronPreloadAmount?.Value || 0;
3131

@@ -48,7 +48,7 @@ const usePreLoadElements = ({ elements, labelID, loading }: Props) => {
4848
const resultAction = await dispatch(
4949
load({ silentFetch: true, conversationID: ID, messageID: undefined })
5050
);
51-
const conversationResult = await unwrapResult(resultAction);
51+
const conversationResult = unwrapResult(resultAction);
5252
const { Messages } = conversationResult;
5353
const messageToExpand = findMessageToExpand(labelID, Messages);
5454

applications/mail/src/app/router/MailAppShell.tsx

+6-20
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,25 @@
11
import { type Ref, forwardRef } from 'react';
22

3-
import { MailShortcutsModal, useModalStateObject, useOpenDrawerOnLoad } from '@proton/components';
3+
import { MailShortcutsModal, useModalStateObject } from '@proton/components';
44

55
import AssistantIframe from 'proton-mail/components/assistant/AssistantIframe';
66
import PrivateLayout from 'proton-mail/components/layout/PrivateLayout';
77
import { LabelActionsContextProvider } from 'proton-mail/components/sidebar/EditLabelContext';
88
import MailStartupModals from 'proton-mail/containers/MailStartupModals';
9-
import { useContactsListener } from 'proton-mail/hooks/contact/useContactsListener';
10-
import { useConversationsEvent } from 'proton-mail/hooks/events/useConversationsEvents';
11-
import { useMessagesEvents } from 'proton-mail/hooks/events/useMessagesEvents';
12-
import useIncomingDefaultsEvents from 'proton-mail/hooks/incomingDefaults/useIncomingDefaultsEvents';
13-
import useIncomingDefaultsLoad from 'proton-mail/hooks/incomingDefaults/useIncomingDefaultsLoad';
14-
import { usePageHotkeys } from 'proton-mail/hooks/mailbox/usePageHotkeys';
15-
import { useMailPTTMetric } from 'proton-mail/metrics/useMailPTTMetric';
169
import { paramsSelector } from 'proton-mail/store/elements/elementsSelectors';
1710
import { useMailSelector } from 'proton-mail/store/hooks';
1811

1912
import { RouterMailboxContainer } from './RouterMailboxContainer';
2013
import { MailboxLayoutProvider } from './components/MailboxLayoutContext';
14+
import { useAppShellSideEffects } from './sideEffects/useAppShellSideEffects';
2115

2216
const MailAppShell = (_props: {}, ref: Ref<HTMLDivElement>) => {
2317
const mailShortcut = useModalStateObject();
2418

25-
useOpenDrawerOnLoad();
26-
27-
useContactsListener();
28-
useConversationsEvent();
29-
useMessagesEvents();
30-
31-
useMailPTTMetric();
32-
33-
useIncomingDefaultsLoad();
34-
useIncomingDefaultsEvents();
35-
36-
usePageHotkeys({ onOpenShortcutsModal: () => mailShortcut.openModal(true) });
19+
/**
20+
* Temporary: App side effects
21+
*/
22+
useAppShellSideEffects({ openShortcutsModal: (value: boolean) => mailShortcut.openModal(value) });
3723

3824
const { labelID } = useMailSelector(paramsSelector);
3925

0 commit comments

Comments
 (0)