From 1de2435b375a02c2c7c2c524b40706ee2e5811de Mon Sep 17 00:00:00 2001 From: Thomas Belin Date: Wed, 17 Jan 2024 11:38:34 +0100 Subject: [PATCH 1/3] test: improve api of useAppSoftLock hook --- .../components/AppContainer/AppContainer.tsx | 19 +-- src/script/components/AppLoader/AppLoader.tsx | 4 +- src/script/hooks/useAppSoflLock.test.ts | 101 +++++++++++++ src/script/hooks/useAppSoftLock.ts | 40 +++-- src/script/page/AppMain.tsx | 141 +++++++++--------- 5 files changed, 194 insertions(+), 111 deletions(-) create mode 100644 src/script/hooks/useAppSoflLock.test.ts diff --git a/src/script/components/AppContainer/AppContainer.tsx b/src/script/components/AppContainer/AppContainer.tsx index 8ac37de7507..2c1ad9d155b 100644 --- a/src/script/components/AppContainer/AppContainer.tsx +++ b/src/script/components/AppContainer/AppContainer.tsx @@ -77,10 +77,7 @@ export const AppContainer: FC = ({config, clientType}) => { const {repository: repositories} = app; - const {isFreshMLSSelfClient, softLockLoaded = false} = useAppSoftLock( - repositories.calling, - repositories.notification, - ); + const {softLockEnabled, softLockLoaded} = useAppSoftLock(repositories.calling, repositories.notification); if (hasOtherInstance) { app.redirectToLogin(SIGN_OUT_REASON.MULTIPLE_TABS); @@ -90,15 +87,11 @@ export const AppContainer: FC = ({config, clientType}) => { return ( <> app.initApp(clientType, onProgress)}> - {selfUser => ( - - )} + {selfUser => { + return softLockLoaded ? ( + + ) : null; + }} diff --git a/src/script/components/AppLoader/AppLoader.tsx b/src/script/components/AppLoader/AppLoader.tsx index 8a99f84420a..fce8f22e445 100644 --- a/src/script/components/AppLoader/AppLoader.tsx +++ b/src/script/components/AppLoader/AppLoader.tsx @@ -17,7 +17,7 @@ * */ -import {FC, ReactElement, useEffect, useRef, useState} from 'react'; +import {FC, ReactNode, useEffect, useRef, useState} from 'react'; import {LoadingBar} from 'Components/LoadingBar/LoadingBar'; @@ -27,7 +27,7 @@ import {User} from '../../entity/User'; interface AppLoaderProps { init: (onProgress: (progress: number, message?: string) => void) => Promise; - children: (selfUser: User) => ReactElement; + children: (selfUser: User) => ReactNode; } interface LoadingProgress { diff --git a/src/script/hooks/useAppSoflLock.test.ts b/src/script/hooks/useAppSoflLock.test.ts new file mode 100644 index 00000000000..7513c9a337e --- /dev/null +++ b/src/script/hooks/useAppSoflLock.test.ts @@ -0,0 +1,101 @@ +/* + * Wire + * Copyright (C) 2023 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + * + */ + +import {renderHook} from '@testing-library/react'; + +import {useAppSoftLock} from './useAppSoftLock'; + +import {CallingRepository} from '../calling/CallingRepository'; +import {isE2EIEnabled, E2EIHandler} from '../E2EIdentity'; +import {NotificationRepository} from '../notification/NotificationRepository'; + +const isE2EIEnabledMock = isE2EIEnabled as jest.MockedFn; +const E2EIHandlerMock = E2EIHandler as jest.Mocked; + +jest.mock('../E2EIdentity', () => ({ + isE2EIEnabled: jest.fn(), + E2EIHandler: { + getInstance: jest.fn(), + }, +})); + +describe('useAppSoftLock', () => { + const callingRepository = {setSoftLock: jest.fn()} as unknown as CallingRepository; + const notificationRepository = {setSoftLock: jest.fn()} as unknown as NotificationRepository; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('should not do anything if e2ei is not enabled', () => { + const {result} = renderHook(() => useAppSoftLock(callingRepository, notificationRepository)); + expect(result.current).toEqual({softLockEnabled: false, softLockLoaded: true}); + expect(callingRepository.setSoftLock).not.toHaveBeenCalledWith(true); + expect(notificationRepository.setSoftLock).not.toHaveBeenCalledWith(true); + }); + + it('should set soft lock to true if the user has used up the entire grace period', async () => { + isE2EIEnabledMock.mockReturnValue(true); + E2EIHandlerMock.getInstance.mockReturnValue({ + on: jest.fn((eventName, callback) => callback({enrollmentConfig: {timer: {isSnoozeTimeAvailable: () => false}}})), + off: jest.fn(), + } as any); + + const {result} = renderHook(() => useAppSoftLock(callingRepository, notificationRepository)); + + expect(result.current.softLockEnabled).toBe(true); + expect(result.current.softLockLoaded).toBe(true); + expect(callingRepository.setSoftLock).toHaveBeenCalledWith(true); + expect(notificationRepository.setSoftLock).toHaveBeenCalledWith(true); + }); + + it('should set softLock if the device is a fresh new device', async () => { + isE2EIEnabledMock.mockReturnValue(true); + E2EIHandlerMock.getInstance.mockReturnValue({ + on: jest.fn((eventName, callback) => + callback({enrollmentConfig: {timer: {isSnoozeTimeAvailable: () => true}, isFreshMLSSelfClient: true}}), + ), + off: jest.fn(), + } as any); + + const {result} = renderHook(() => useAppSoftLock(callingRepository, notificationRepository)); + + expect(result.current.softLockEnabled).toBe(true); + expect(result.current.softLockLoaded).toBe(true); + expect(callingRepository.setSoftLock).toHaveBeenCalledWith(true); + expect(notificationRepository.setSoftLock).toHaveBeenCalledWith(true); + }); + + it('should not set softLock if the device is an old device and the grace period is not expireds', async () => { + isE2EIEnabledMock.mockReturnValue(true); + E2EIHandlerMock.getInstance.mockReturnValue({ + on: jest.fn((eventName, callback) => + callback({enrollmentConfig: {timer: {isSnoozeTimeAvailable: () => true}, isFreshMLSSelfClient: false}}), + ), + off: jest.fn(), + } as any); + + const {result} = renderHook(() => useAppSoftLock(callingRepository, notificationRepository)); + + expect(result.current.softLockEnabled).toBe(false); + expect(result.current.softLockLoaded).toBe(true); + expect(callingRepository.setSoftLock).not.toHaveBeenCalledWith(true); + expect(notificationRepository.setSoftLock).not.toHaveBeenCalledWith(true); + }); +}); diff --git a/src/script/hooks/useAppSoftLock.ts b/src/script/hooks/useAppSoftLock.ts index 6274a50bf86..4524653a799 100644 --- a/src/script/hooks/useAppSoftLock.ts +++ b/src/script/hooks/useAppSoftLock.ts @@ -17,7 +17,7 @@ * */ -import {useEffect, useState} from 'react'; +import {useCallback, useEffect, useState} from 'react'; import {CallingRepository} from '../calling/CallingRepository'; import {E2EIHandler, EnrollmentConfig, isE2EIEnabled, WireIdentity} from '../E2EIdentity'; @@ -25,28 +25,22 @@ import {shouldEnableSoftLock} from '../E2EIdentity/DelayTimer/delay'; import {NotificationRepository} from '../notification/NotificationRepository'; export function useAppSoftLock(callingRepository: CallingRepository, notificationRepository: NotificationRepository) { - const [freshMLSSelfClient, setFreshMLSSelfClient] = useState(false); - const [softLockLoaded, setSoftLockLoaded] = useState(false); - const e2eiEnabled = isE2EIEnabled(); - const setAppSoftLock = (isLocked: boolean) => { - setFreshMLSSelfClient(isLocked); - setSoftLockLoaded(true); - callingRepository.setSoftLock(isLocked); - notificationRepository.setSoftLock(isLocked); - }; - - const handleSoftLockActivation = ({ - enrollmentConfig, - identity, - }: { - enrollmentConfig: EnrollmentConfig; - identity: WireIdentity; - }) => { - const isSoftLockEnabled = shouldEnableSoftLock(enrollmentConfig, identity); - setAppSoftLock(isSoftLockEnabled); - }; + const [softLockEnabled, setSoftLockEnabled] = useState(false); + const [softLockLoaded, setSoftLockLoaded] = useState(!e2eiEnabled); + + const handleSoftLockActivation = useCallback( + ({enrollmentConfig, identity}: {enrollmentConfig: EnrollmentConfig; identity?: WireIdentity}) => { + const isSoftLockEnabled = shouldEnableSoftLock(enrollmentConfig, identity); + + setSoftLockEnabled(isSoftLockEnabled); + setSoftLockLoaded(true); + callingRepository.setSoftLock(isSoftLockEnabled); + notificationRepository.setSoftLock(isSoftLockEnabled); + }, + [callingRepository, notificationRepository], + ); useEffect(() => { if (!e2eiEnabled) { @@ -57,7 +51,7 @@ export function useAppSoftLock(callingRepository: CallingRepository, notificatio return () => { E2EIHandler.getInstance().off('identityUpdated', handleSoftLockActivation); }; - }, [e2eiEnabled]); + }, [e2eiEnabled, handleSoftLockActivation]); - return {isFreshMLSSelfClient: freshMLSSelfClient, softLockLoaded: e2eiEnabled ? softLockLoaded : true}; + return {softLockEnabled, softLockLoaded}; } diff --git a/src/script/page/AppMain.tsx b/src/script/page/AppMain.tsx index 74329e9f446..9e7ad5ed13a 100644 --- a/src/script/page/AppMain.tsx +++ b/src/script/page/AppMain.tsx @@ -69,8 +69,8 @@ interface AppMainProps { selfUser: User; mainView: MainViewModel; conversationState?: ConversationState; - isFreshMLSSelfClient: boolean; - softLockLoaded: boolean; + /** will block the user from being able to interact with the application (no notifications and no messages will be shown) */ + locked: boolean; } export const AppMain: FC = ({ @@ -78,8 +78,7 @@ export const AppMain: FC = ({ mainView, selfUser, conversationState = container.resolve(ConversationState), - isFreshMLSSelfClient, - softLockLoaded = false, + locked, }) => { const apiContext = app.getAPIContext(); @@ -212,10 +211,10 @@ export const AppMain: FC = ({ }, []); useLayoutEffect(() => { - if (!isFreshMLSSelfClient) { + if (!locked) { initializeApp(); } - }, [isFreshMLSSelfClient]); + }, [locked]); return ( = ({ data-uie-name="status-webapp" data-uie-value="is-loaded" > - {softLockLoaded && ( - <> - {!isFreshMLSSelfClient && } - - - {!isFreshMLSSelfClient && ( -
- {(!smBreakpoint || isLeftSidebarVisible) && ( - - )} - - {(!smBreakpoint || !isLeftSidebarVisible) && ( - - )} - - {currentState && ( - - )} -
+ {!locked && } + + + {!locked && ( +
+ {(!smBreakpoint || isLeftSidebarVisible) && ( + )} - - - - {!isFreshMLSSelfClient && ( - <> - - - - - - + {(!smBreakpoint || !isLeftSidebarVisible) && ( + )} - {/*The order of these elements matter to show proper modals stack upon each other*/} - - - - - - )} + {currentState && ( + + )} +
+ )} + + + + + {!locked && ( + <> + + + + + + + )} + + {/*The order of these elements matter to show proper modals stack upon each other*/} + + +
+
); }; From a0e71bcbede44de35228604034a7f2881693b01a Mon Sep 17 00:00:00 2001 From: Thomas Belin Date: Wed, 17 Jan 2024 11:46:41 +0100 Subject: [PATCH 2/3] remove deprecated loaded state --- src/script/components/AppContainer/AppContainer.tsx | 6 ++---- src/script/hooks/useAppSoflLock.test.ts | 5 +---- src/script/hooks/useAppSoftLock.ts | 4 +--- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/script/components/AppContainer/AppContainer.tsx b/src/script/components/AppContainer/AppContainer.tsx index 2c1ad9d155b..cf688d00351 100644 --- a/src/script/components/AppContainer/AppContainer.tsx +++ b/src/script/components/AppContainer/AppContainer.tsx @@ -77,7 +77,7 @@ export const AppContainer: FC = ({config, clientType}) => { const {repository: repositories} = app; - const {softLockEnabled, softLockLoaded} = useAppSoftLock(repositories.calling, repositories.notification); + const {softLockEnabled} = useAppSoftLock(repositories.calling, repositories.notification); if (hasOtherInstance) { app.redirectToLogin(SIGN_OUT_REASON.MULTIPLE_TABS); @@ -88,9 +88,7 @@ export const AppContainer: FC = ({config, clientType}) => { <> app.initApp(clientType, onProgress)}> {selfUser => { - return softLockLoaded ? ( - - ) : null; + return ; }} diff --git a/src/script/hooks/useAppSoflLock.test.ts b/src/script/hooks/useAppSoflLock.test.ts index 7513c9a337e..67c18e0ad7d 100644 --- a/src/script/hooks/useAppSoflLock.test.ts +++ b/src/script/hooks/useAppSoflLock.test.ts @@ -45,7 +45,7 @@ describe('useAppSoftLock', () => { it('should not do anything if e2ei is not enabled', () => { const {result} = renderHook(() => useAppSoftLock(callingRepository, notificationRepository)); - expect(result.current).toEqual({softLockEnabled: false, softLockLoaded: true}); + expect(result.current).toEqual({softLockEnabled: false}); expect(callingRepository.setSoftLock).not.toHaveBeenCalledWith(true); expect(notificationRepository.setSoftLock).not.toHaveBeenCalledWith(true); }); @@ -60,7 +60,6 @@ describe('useAppSoftLock', () => { const {result} = renderHook(() => useAppSoftLock(callingRepository, notificationRepository)); expect(result.current.softLockEnabled).toBe(true); - expect(result.current.softLockLoaded).toBe(true); expect(callingRepository.setSoftLock).toHaveBeenCalledWith(true); expect(notificationRepository.setSoftLock).toHaveBeenCalledWith(true); }); @@ -77,7 +76,6 @@ describe('useAppSoftLock', () => { const {result} = renderHook(() => useAppSoftLock(callingRepository, notificationRepository)); expect(result.current.softLockEnabled).toBe(true); - expect(result.current.softLockLoaded).toBe(true); expect(callingRepository.setSoftLock).toHaveBeenCalledWith(true); expect(notificationRepository.setSoftLock).toHaveBeenCalledWith(true); }); @@ -94,7 +92,6 @@ describe('useAppSoftLock', () => { const {result} = renderHook(() => useAppSoftLock(callingRepository, notificationRepository)); expect(result.current.softLockEnabled).toBe(false); - expect(result.current.softLockLoaded).toBe(true); expect(callingRepository.setSoftLock).not.toHaveBeenCalledWith(true); expect(notificationRepository.setSoftLock).not.toHaveBeenCalledWith(true); }); diff --git a/src/script/hooks/useAppSoftLock.ts b/src/script/hooks/useAppSoftLock.ts index 4524653a799..51ee49e77d7 100644 --- a/src/script/hooks/useAppSoftLock.ts +++ b/src/script/hooks/useAppSoftLock.ts @@ -28,14 +28,12 @@ export function useAppSoftLock(callingRepository: CallingRepository, notificatio const e2eiEnabled = isE2EIEnabled(); const [softLockEnabled, setSoftLockEnabled] = useState(false); - const [softLockLoaded, setSoftLockLoaded] = useState(!e2eiEnabled); const handleSoftLockActivation = useCallback( ({enrollmentConfig, identity}: {enrollmentConfig: EnrollmentConfig; identity?: WireIdentity}) => { const isSoftLockEnabled = shouldEnableSoftLock(enrollmentConfig, identity); setSoftLockEnabled(isSoftLockEnabled); - setSoftLockLoaded(true); callingRepository.setSoftLock(isSoftLockEnabled); notificationRepository.setSoftLock(isSoftLockEnabled); }, @@ -53,5 +51,5 @@ export function useAppSoftLock(callingRepository: CallingRepository, notificatio }; }, [e2eiEnabled, handleSoftLockActivation]); - return {softLockEnabled, softLockLoaded}; + return {softLockEnabled}; } From aa40f154311ff75fc0951cfe859dbcd4ba71af31 Mon Sep 17 00:00:00 2001 From: Thomas Belin Date: Wed, 17 Jan 2024 11:52:28 +0100 Subject: [PATCH 3/3] renaming --- .../hooks/{useAppSoflLock.test.ts => useAppSoftLock.test.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/script/hooks/{useAppSoflLock.test.ts => useAppSoftLock.test.ts} (100%) diff --git a/src/script/hooks/useAppSoflLock.test.ts b/src/script/hooks/useAppSoftLock.test.ts similarity index 100% rename from src/script/hooks/useAppSoflLock.test.ts rename to src/script/hooks/useAppSoftLock.test.ts