Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / applications / drive / src / app / bootstrap.ts
blobd00daf38389081ff16f09ce245cec729b3cff489
1 import {
2     addressesThunk,
3     initEvent,
4     serverEvent,
5     userSettingsThunk,
6     userThunk,
7     welcomeFlagsActions,
8 } from '@proton/account';
9 import * as bootstrap from '@proton/account/bootstrap';
10 import { bootstrapEvent } from '@proton/account/bootstrap/action';
11 import { setupGuestCrossStorage } from '@proton/cross-storage/account-impl/guestInstance';
12 import { FeatureCode, fetchFeatures } from '@proton/features';
13 import createApi from '@proton/shared/lib/api/createApi';
14 import { queryUserSettings } from '@proton/shared/lib/api/drive/user';
15 import { getSilentApi } from '@proton/shared/lib/api/helpers/customConfig';
16 import { getClientID } from '@proton/shared/lib/apps/helper';
17 import { initSafariFontFixClassnames } from '@proton/shared/lib/helpers/initSafariFontFixClassnames';
18 import type { ProtonConfig } from '@proton/shared/lib/interfaces';
19 import type { UserSettingsResponse } from '@proton/shared/lib/interfaces/drive/userSettings';
20 import type { UnleashClient } from '@proton/unleash';
21 import noop from '@proton/utils/noop';
23 import locales from './locales';
24 import { extendStore, setupStore } from './redux-store/store';
25 import { sendErrorReport } from './utils/errorHandling';
26 import { getWebpackChunkFailedToLoadError } from './utils/errorHandling/WebpackChunkFailedToLoadError';
27 import { userSuccessMetrics } from './utils/metrics/userSuccessMetrics';
29 const getAppContainer = () =>
30     import(/* webpackChunkName: "MainContainer" */ './containers/MainContainer')
31         .then((result) => result.default)
32         .catch((e) => {
33             const report = getWebpackChunkFailedToLoadError(e, 'MainContainer');
34             console.warn(report);
35             sendErrorReport(report);
36             return Promise.reject(report);
37         });
39 /* TODO: To be removed after test DRVWEB-4375 */
40 const setIsSWForSafariEnabled = (unleashClient: UnleashClient) => {
41     if (typeof window !== 'undefined') {
42         (window as any).isSWForSafariEnabled = unleashClient.isEnabled('DriveWebDownloadSWModernBrowsers');
43     }
46 export const bootstrapApp = async ({ config, signal }: { config: ProtonConfig; signal?: AbortSignal }) => {
47     const pathname = window.location.pathname;
48     const searchParams = new URLSearchParams(window.location.search);
49     const api = createApi({ config });
50     const silentApi = getSilentApi(api);
51     const authentication = bootstrap.createAuthentication();
52     bootstrap.init({ config, authentication, locales });
53     await userSuccessMetrics.init();
54     setupGuestCrossStorage();
55     const appName = config.APP_NAME;
57     initSafariFontFixClassnames();
59     const run = async () => {
60         const appContainerPromise = getAppContainer();
61         const sessionResult = await bootstrap.loadSession({ authentication, api, pathname, searchParams });
62         const history = bootstrap.createHistory({ sessionResult, pathname });
63         const unleashClient = bootstrap.createUnleash({ api: silentApi });
64         /* TODO: To be removed after test DRVWEB-4375 */
65         setIsSWForSafariEnabled(unleashClient);
66         const user = sessionResult.session?.User;
67         extendStore({ config, api, authentication, unleashClient, history });
68         const store = setupStore();
69         const dispatch = store.dispatch;
71         if (user) {
72             dispatch(initEvent({ User: user }));
73         }
75         const loadUser = async () => {
76             const [user, userSettings, features] = await Promise.all([
77                 dispatch(userThunk()),
78                 dispatch(userSettingsThunk()),
79                 dispatch(fetchFeatures([FeatureCode.EarlyAccessScope])),
80             ]);
82             dispatch(welcomeFlagsActions.initial(userSettings));
84             const [scopes] = await Promise.all([
85                 bootstrap.initUser({ appName, user, userSettings }),
86                 bootstrap.loadLocales({ userSettings, locales }),
87             ]);
89             await userSuccessMetrics.setVersionHeaders(getClientID(config.APP_NAME), config.APP_VERSION);
90             await userSuccessMetrics.setLocalUser(authentication.getUID(), user.isPaid);
91             return { user, userSettings, earlyAccessScope: features[FeatureCode.EarlyAccessScope], scopes };
92         };
94         const loadPreload = () => {
95             return Promise.all([api<UserSettingsResponse>(queryUserSettings()), dispatch(addressesThunk())]);
96         };
98         const userPromise = loadUser();
99         const preloadPromise = loadPreload();
100         const evPromise = bootstrap.eventManager({ api: silentApi });
101         const unleashPromise = bootstrap.unleashReady({ unleashClient }).catch(noop);
103         await unleashPromise;
104         // Needs unleash to be loaded.
105         await bootstrap.loadCrypto({ appName, unleashClient });
106         const [MainContainer, userData, eventManager] = await Promise.all([
107             appContainerPromise,
108             userPromise,
109             evPromise,
110         ]);
111         // Needs everything to be loaded.
112         await bootstrap.postLoad({ appName, authentication, ...userData, history });
113         // Preloaded models are not needed until the app starts, and also important do it postLoad as these requests might fail due to missing scopes.
114         const [driveUserSettings] = await preloadPromise;
116         extendStore({ eventManager });
117         const unsubscribeEventManager = eventManager.subscribe((event) => {
118             dispatch(serverEvent(event));
119         });
120         eventManager.start();
122         bootstrap.onAbort(signal, () => {
123             unsubscribeEventManager();
124             eventManager.reset();
125             unleashClient.stop();
126             store.unsubscribe();
127         });
129         dispatch(bootstrapEvent({ type: 'complete' }));
131         return {
132             ...userData,
133             eventManager,
134             driveUserSettings,
135             unleashClient,
136             history,
137             store,
138             MainContainer,
139         };
140     };
142     return bootstrap.wrap({ appName, authentication }, run());