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)
33 const report = getWebpackChunkFailedToLoadError(e, 'MainContainer');
35 sendErrorReport(report);
36 return Promise.reject(report);
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');
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;
72 dispatch(initEvent({ User: user }));
75 const loadUser = async () => {
76 const [user, userSettings, features] = await Promise.all([
77 dispatch(userThunk()),
78 dispatch(userSettingsThunk()),
79 dispatch(fetchFeatures([FeatureCode.EarlyAccessScope])),
82 dispatch(welcomeFlagsActions.initial(userSettings));
84 const [scopes] = await Promise.all([
85 bootstrap.initUser({ appName, user, userSettings }),
86 bootstrap.loadLocales({ userSettings, locales }),
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 };
94 const loadPreload = () => {
95 return Promise.all([api<UserSettingsResponse>(queryUserSettings()), dispatch(addressesThunk())]);
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([
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));
120 eventManager.start();
122 bootstrap.onAbort(signal, () => {
123 unsubscribeEventManager();
124 eventManager.reset();
125 unleashClient.stop();
129 dispatch(bootstrapEvent({ type: 'complete' }));
142 return bootstrap.wrap({ appName, authentication }, run());