Skip to content

Commit

Permalink
refactor: Migrate App initialization from knockout to React (#14033)
Browse files Browse the repository at this point in the history
* refactor: Migrate App initialization from knockout to React

* Update tests and remove unecessary code

* Move WindowTitleViewModal to hook

* CR fixes

* CR Fixes
  • Loading branch information
przemvs authored Nov 7, 2022
1 parent 957c4a5 commit c228af4
Show file tree
Hide file tree
Showing 45 changed files with 905 additions and 948 deletions.
13 changes: 1 addition & 12 deletions src/page/index.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,7 @@
</head>

<body>
<app-container
id="wire-main"
class="off"
data-bind="css: mainClasses()"
params="root: $root"
data-uie-name="status-webapp"
data-uie-value="is-loading"
></app-container>

<div id="loading-screen" class="loading-screen">
<loading-bar params="message: loadingMessage, progress: loadingProgress"></loading-bar>
</div>
<main id="wire-app"></main>

<script src="./config.js?<%= VERSION %>"></script>
<script src="./min/dexie.js?<%= VERSION %>"></script>
Expand Down
47 changes: 47 additions & 0 deletions src/script/components/AppContainer/AppContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Wire
* Copyright (C) 2022 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 {FC} from 'react';

import {ClientType} from '@wireapp/api-client/lib/client/';
import {container} from 'tsyringe';

import {Configuration} from '../../Config';
import {App} from '../../main/app';
import {AppMain} from '../../page/AppMain';
import {APIClient} from '../../service/APIClientSingleton';
import {Core} from '../../service/CoreSingleton';
import {MainViewModel} from '../../view_model/MainViewModel';
import {AppLoader} from '../AppLoader';

interface AppProps {
config: Configuration;
clientType: ClientType;
}

export const AppContainer: FC<AppProps> = ({config, clientType}) => {
const app = new App(container.resolve(Core), container.resolve(APIClient));
const mainView = new MainViewModel(app.repository);

return (
<AppLoader init={onProgress => app.initApp(clientType, config, onProgress)}>
{selfUser => <AppMain app={app} selfUser={selfUser} mainView={mainView} />}
</AppLoader>
);
};
20 changes: 20 additions & 0 deletions src/script/components/AppContainer/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Wire
* Copyright (C) 2022 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/.
*
*/

export * from './AppContainer';
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Wire
* Copyright (C) 2019 Wire Swiss GmbH
* Copyright (C) 2022 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
Expand All @@ -17,23 +17,15 @@
*
*/

import {initRouterBindings} from 'src/script/router/routerBindings';
import {CSSObject} from '@emotion/react';

import {bindHtml} from '../../helper/knockoutHelpers';

describe('routerBindings', () => {
let mockRouter;
beforeEach(() => {
mockRouter = {navigate: () => {}};
initRouterBindings(mockRouter);
spyOn(mockRouter, 'navigate');
});

it('handles click and triggers router navigation', async () => {
const url = '/conversation/uuid';
const domElement = await bindHtml(`<a data-bind="link_to: '${url}'">click me</a>`);
domElement.querySelector('a').click();

expect(mockRouter.navigate).toHaveBeenCalledWith(url);
});
});
export const styles: CSSObject = {
alignItems: 'center',
backgroundColor: 'var(--app-bg)',
color: 'var(--foreground)',
display: 'flex',
height: '100%',
justifyContent: 'center',
position: 'absolute',
width: '100%',
};
47 changes: 47 additions & 0 deletions src/script/components/AppLoader/AppLoader.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Wire
* Copyright (C) 2021 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 {act, render, waitFor} from '@testing-library/react';

import {User} from 'src/script/entity/User';

import {AppLoader} from '.';

describe('AppLoader', () => {
it('triggers loading of the app once mounted', async () => {
jest.useFakeTimers();
let nextStep: (progress: number, message: string) => void = () => {};
let done: () => void = () => {};
const init = jest.fn(async (onProgress: (p: number, m: string) => void) => {
nextStep = (progress: number, message: string) => onProgress(progress, message);
return new Promise<User>(resolve => (done = () => resolve(new User())));
});

const {queryByText, getByText} = render(<AppLoader init={init}>{() => <div>LoadedApp</div>}</AppLoader>);

act(() => nextStep(1, 'first'));
expect(getByText('first')).not.toBe(null);
expect(queryByText('LoadedApp')).toBe(null);
act(() => nextStep(2, 'second'));
expect(getByText('second')).not.toBe(null);
expect(queryByText('LoadedApp')).toBe(null);
done();
await waitFor(() => expect(getByText('LoadedApp')).not.toBe(null));
});
});
68 changes: 68 additions & 0 deletions src/script/components/AppLoader/AppLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Wire
* Copyright (C) 2022 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 {FC, ReactElement, useEffect, useRef, useState} from 'react';

import {LoadingBar} from 'Components/LoadingBar/LoadingBar';

import {styles} from './AppLoader.styles';

import {User} from '../../entity/User';

interface AppLoaderProps {
init: (onProgress: (progress: number, message?: string) => void) => Promise<User | undefined>;
children: (selfUser: User) => ReactElement;
}

interface LoadingProgress {
progress: number;
message: string;
}

const defaultLoadingState: LoadingProgress = {
message: '',
progress: 0,
};

export const AppLoader: FC<AppLoaderProps> = ({init, children}) => {
const [loadingState, setLoadingState] = useState<LoadingProgress>(defaultLoadingState);
const [selfUser, setSelfUser] = useState<User>();
const isFirstRender = useRef(true);

useEffect(() => {
if (!isFirstRender.current) {
return;
}
isFirstRender.current = false;

init((progress, message) => {
setLoadingState(previousState => ({message: message ?? previousState?.message ?? '', progress}));
}).then(user => setSelfUser(user));
}, []);

if (selfUser) {
return children(selfUser);
}

return (
<div css={styles} data-uie-name="status-webapp" data-uie-value="is-loading">
<LoadingBar {...loadingState} />
</div>
);
};
20 changes: 20 additions & 0 deletions src/script/components/AppLoader/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Wire
* Copyright (C) 2022 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/.
*
*/

export * from './AppLoader';
4 changes: 2 additions & 2 deletions src/script/components/HistoryExport/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import {FC, useContext, useEffect, useState} from 'react';

import {container} from 'tsyringe';

import {LoadingBar} from 'Components/LoadingBar';
import {LoadingBar} from 'Components/LoadingBar/LoadingBar';
import {ContentState} from 'src/script/page/useAppState';
import {useKoSubscribableChildren} from 'Util/ComponentUtil';
import {t} from 'Util/LocalizerUtil';
import {getLogger} from 'Util/Logger';
Expand All @@ -32,7 +33,6 @@ import {CancelError} from '../../backup/Error';
import {Config} from '../../Config';
import {RootContext} from '../../page/RootProvider';
import {UserState} from '../../user/UserState';
import {ContentState} from '../../view_model/ContentViewModel';

enum ExportState {
COMPRESSING = 'ExportState.STATE.COMPRESSING',
Expand Down
4 changes: 2 additions & 2 deletions src/script/components/HistoryImport/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
import {FC, useEffect, useState} from 'react';

import {Icon} from 'Components/Icon';
import {LoadingBar} from 'Components/LoadingBar';
import {LoadingBar} from 'Components/LoadingBar/LoadingBar';
import {ContentState} from 'src/script/page/useAppState';
import {t} from 'Util/LocalizerUtil';
import {getLogger} from 'Util/Logger';
import {formatDuration} from 'Util/TimeUtil';
Expand All @@ -31,7 +32,6 @@ import {BackupRepository} from '../../backup/BackupRepository';
import {CancelError, DifferentAccountError, ImportError, IncompatibleBackupError} from '../../backup/Error';
import {Config} from '../../Config';
import {MotionDuration} from '../../motion/MotionDuration';
import {ContentState} from '../../view_model/ContentViewModel';

export enum HistoryImportState {
DONE = 'HistoryImportState.STATE.DONE',
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,17 @@
*
*/

import React from 'react';
import {FC} from 'react';

import cx from 'classnames';

import {registerReactComponent} from 'Util/ComponentUtil';

export interface LoadingBarProps {
message: string;
progress: number;
className?: string;
}

const LoadingBar: React.FC<LoadingBarProps> = ({progress, message, className = ''}) => (
const LoadingBar: FC<LoadingBarProps> = ({progress, message, className = ''}) => (
<div className={cx('loading-bar text-center', className)}>
<div className="progress-console">{message}</div>
<div className="progress-bar">
Expand All @@ -39,5 +37,3 @@ const LoadingBar: React.FC<LoadingBarProps> = ({progress, message, className = '
);

export {LoadingBar};

registerReactComponent('loading-bar', LoadingBar);
20 changes: 20 additions & 0 deletions src/script/components/LoadingBar/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Wire
* Copyright (C) 2022 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/.
*
*/

export * from './LoadingBar';
2 changes: 1 addition & 1 deletion src/script/components/TitleBar/TitleBar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import ko from 'knockout';

import {TitleBar} from 'Components/TitleBar';
import {withTheme} from 'src/script/auth/util/test/TestUtil';
import {ContentState} from 'src/script/page/useAppState';

import {TestFactory} from '../../../../test/helper/TestFactory';
import {CallingRepository} from '../../calling/CallingRepository';
Expand All @@ -37,7 +38,6 @@ import {User} from '../../entity/User';
import {PanelState} from '../../page/RightSidebar/RightSidebar';
import {TeamState} from '../../team/TeamState';
import {UserState} from '../../user/UserState';
import {ContentState} from '../../view_model/ContentViewModel';
import {ViewModelRepositories} from '../../view_model/MainViewModel';

jest.mock('@wireapp/react-ui-kit', () => ({
Expand Down
2 changes: 1 addition & 1 deletion src/script/components/TitleBar/TitleBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {container} from 'tsyringe';
import {Icon} from 'Components/Icon';
import {LegalHoldDot} from 'Components/LegalHoldDot';
import {useAppMainState, ViewType} from 'src/script/page/state';
import {ContentState} from 'src/script/page/useAppState';
import {useKoSubscribableChildren} from 'Util/ComponentUtil';
import {handleKeyDown} from 'Util/KeyboardUtil';
import {StringIdentifer, t} from 'Util/LocalizerUtil';
Expand All @@ -45,7 +46,6 @@ import {Shortcut} from '../../ui/Shortcut';
import {ShortcutType} from '../../ui/ShortcutType';
import {UserState} from '../../user/UserState';
import {CallActions} from '../../view_model/CallingViewModel';
import {ContentState} from '../../view_model/ContentViewModel';
import {ViewModelRepositories} from '../../view_model/MainViewModel';

export interface TitleBarProps {
Expand Down
Loading

0 comments on commit c228af4

Please sign in to comment.