diff --git a/src/script/components/AppContainer/AppContainer.tsx b/src/script/components/AppContainer/AppContainer.tsx index 8ac37de7507..cf688d00351 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} = useAppSoftLock(repositories.calling, repositories.notification); if (hasOtherInstance) { app.redirectToLogin(SIGN_OUT_REASON.MULTIPLE_TABS); @@ -90,15 +87,9 @@ export const AppContainer: FC = ({config, clientType}) => { return ( <> app.initApp(clientType, onProgress)}> - {selfUser => ( - - )} + {selfUser => { + return ; + }} 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/useAppSoftLock.test.ts b/src/script/hooks/useAppSoftLock.test.ts new file mode 100644 index 00000000000..67c18e0ad7d --- /dev/null +++ b/src/script/hooks/useAppSoftLock.test.ts @@ -0,0 +1,98 @@ +/* + * 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}); + 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(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(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(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..51ee49e77d7 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,20 @@ 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 handleSoftLockActivation = useCallback( + ({enrollmentConfig, identity}: {enrollmentConfig: EnrollmentConfig; identity?: WireIdentity}) => { + const isSoftLockEnabled = shouldEnableSoftLock(enrollmentConfig, identity); + + setSoftLockEnabled(isSoftLockEnabled); + callingRepository.setSoftLock(isSoftLockEnabled); + notificationRepository.setSoftLock(isSoftLockEnabled); + }, + [callingRepository, notificationRepository], + ); useEffect(() => { if (!e2eiEnabled) { @@ -57,7 +49,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}; } 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*/} + + +
+
); };