9 } from '@proton/account';
10 import * as bootstrap from '@proton/account/bootstrap';
11 import { bootstrapEvent } from '@proton/account/bootstrap/action';
13 calendarSettingsThunk,
15 createCalendarModelEventManager,
16 holidaysDirectoryThunk,
17 } from '@proton/calendar';
18 import { setupGuestCrossStorage } from '@proton/cross-storage/account-impl/guestInstance';
19 import { FeatureCode, fetchFeatures } from '@proton/features';
20 import createApi from '@proton/shared/lib/api/createApi';
21 import { getSilentApi } from '@proton/shared/lib/api/helpers/customConfig';
22 import { loadAllowedTimeZones } from '@proton/shared/lib/date/timezone';
23 import { listenFreeTrialSessionExpiration } from '@proton/shared/lib/desktop/endOfTrialHelpers';
24 import { createDrawerApi } from '@proton/shared/lib/drawer/createDrawerApi';
25 import { getIsAuthorizedApp } from '@proton/shared/lib/drawer/helpers';
26 import { isElectronMail } from '@proton/shared/lib/helpers/desktop';
27 import { initElectronClassnames } from '@proton/shared/lib/helpers/initElectronClassnames';
28 import { initSafariFontFixClassnames } from '@proton/shared/lib/helpers/initSafariFontFixClassnames';
29 import { captureMessage } from '@proton/shared/lib/helpers/sentry';
30 import type { ProtonConfig } from '@proton/shared/lib/interfaces';
31 import initLogicalProperties from '@proton/shared/lib/logical/logical';
32 import noop from '@proton/utils/noop';
34 import { embeddedDrawerAppInfos } from './helpers/drawer';
35 import locales from './locales';
36 import { extendStore, setupStore } from './store/store';
38 const getAppContainer = () =>
39 import(/* webpackChunkName: "MainContainer" */ './containers/calendar/MainContainer').then(
40 (result) => result.default
43 const { isIframe, isDrawerApp, parentApp } = embeddedDrawerAppInfos;
45 export const bootstrapApp = async ({ config, signal }: { config: ProtonConfig; signal?: AbortSignal }) => {
46 const pathname = window.location.pathname;
47 const searchParams = new URLSearchParams(window.location.search);
49 const api = isDrawerApp ? createDrawerApi({ parentApp, appVersion: config.APP_VERSION }) : createApi({ config });
50 const silentApi = getSilentApi(api);
51 const authentication = bootstrap.createAuthentication();
52 bootstrap.init({ config, authentication, locales });
54 setupGuestCrossStorage();
55 initElectronClassnames();
56 initLogicalProperties();
57 initSafariFontFixClassnames();
59 const appName = config.APP_NAME;
62 listenFreeTrialSessionExpiration(appName, authentication, api);
65 // Temporary log for debugging
66 if (isIframe && !isDrawerApp) {
67 captureMessage('Drawer iframe bootstrap', {
72 isAuthorizedApp: getIsAuthorizedApp(parentApp || ''),
73 locationOrigin: window.location.origin,
74 locationHref: window.location.href,
79 const run = async () => {
80 const appContainerPromise = getAppContainer();
83 (isDrawerApp && parentApp
84 ? await bootstrap.loadDrawerSession({
90 : undefined) || (await bootstrap.loadSession({ authentication, api, pathname, searchParams }));
92 const history = bootstrap.createHistory({ sessionResult, pathname });
93 const unleashClient = bootstrap.createUnleash({ api: silentApi });
95 const user = sessionResult.session?.User;
96 extendStore({ config, api, authentication, unleashClient, history });
98 const store = setupStore();
99 const dispatch = store.dispatch;
102 dispatch(initEvent({ User: user }));
105 const loadUser = async () => {
106 const [user, userSettings, features] = await Promise.all([
107 dispatch(userThunk()),
108 dispatch(userSettingsThunk()),
109 dispatch(fetchFeatures([FeatureCode.EarlyAccessScope, FeatureCode.AutoAddHolidaysCalendars])),
112 dispatch(welcomeFlagsActions.initial(userSettings));
114 const [scopes] = await Promise.all([
115 bootstrap.initUser({ appName, user, userSettings }),
116 bootstrap.loadLocales({ userSettings, locales }),
119 return { user, userSettings, earlyAccessScope: features[FeatureCode.EarlyAccessScope], scopes };
122 const loadPreload = () => {
124 dispatch(addressesThunk()),
125 dispatch(calendarsThunk()),
126 dispatch(calendarSettingsThunk()),
130 const loadPreloadButIgnored = () => {
131 loadAllowedTimeZones(silentApi).catch(noop);
132 dispatch(holidaysDirectoryThunk()).catch(noop);
133 dispatch(organizationThunk()).catch(noop);
136 const userPromise = loadUser();
137 const preloadPromise = loadPreload();
138 const evPromise = bootstrap.eventManager({ api: silentApi });
139 const unleashPromise = bootstrap.unleashReady({ unleashClient }).catch(noop);
140 loadPreloadButIgnored();
142 await unleashPromise;
143 // Needs unleash to be loaded.
144 await bootstrap.loadCrypto({ appName, unleashClient });
145 const [MainContainer, userData, eventManager] = await Promise.all([
150 // Needs everything to be loaded.
151 await bootstrap.postLoad({ appName, authentication, ...userData, history });
152 // Preloaded models are not needed until the app starts, and also important do it postLoad as these requests might fail due to missing scopes.
153 await preloadPromise;
155 const calendarModelEventManager = createCalendarModelEventManager({ api: silentApi });
157 extendStore({ eventManager, calendarModelEventManager });
158 const unsubscribeEventManager = eventManager.subscribe((event) => {
159 dispatch(serverEvent(event));
161 eventManager.start();
163 bootstrap.onAbort(signal, () => {
164 unsubscribeEventManager();
165 eventManager.reset();
166 unleashClient.stop();
170 dispatch(bootstrapEvent({ type: 'complete' }));
182 return bootstrap.wrap({ appName, authentication }, run());