Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / applications / pass-extension / src / lib / context / extension-context.ts
blobc29c8527fb56bf687222ca847199ceefe1ac35cf
1 import { generatePortName } from 'proton-pass-extension/lib/utils/port';
2 import type { Runtime } from 'webextension-polyfill';
4 import { resolveMessageFactory, sendMessage } from '@proton/pass/lib/extension/message/send-message';
5 import browser from '@proton/pass/lib/globals/browser';
6 import type { ClientEndpoint, MaybeNull, TabId } from '@proton/pass/types';
7 import { WorkerMessageType } from '@proton/pass/types';
8 import { contextHandlerFactory } from '@proton/pass/utils/context';
9 import { pipe } from '@proton/pass/utils/fp/pipe';
10 import { safeCall } from '@proton/pass/utils/fp/safe-call';
11 import { logger, registerLoggerEffect } from '@proton/pass/utils/logger';
12 import type { ParsedUrl } from '@proton/pass/utils/url/types';
14 import.meta.webpackHot?.decline();
16 export type ExtensionContextType = {
17     endpoint: ClientEndpoint;
18     tabId: TabId;
19     port: Runtime.Port;
20     url: MaybeNull<ParsedUrl>;
21     destroy: () => void;
24 export type ExtensionContextOptions = {
25     endpoint: ClientEndpoint;
26     onDisconnect: (previousCtx?: ExtensionContextType) => { recycle: boolean };
27     onRecycle: (nextCtx: ExtensionContextType) => void;
30 export const ExtensionContext = contextHandlerFactory<ExtensionContextType>('extension');
32 export const setupExtensionContext = async (options: ExtensionContextOptions): Promise<ExtensionContextType> => {
33     const { endpoint, onDisconnect, onRecycle } = options;
34     const message = resolveMessageFactory(endpoint);
36     try {
37         const { tabId, url } = await sendMessage.on(
38             message({ type: WorkerMessageType.TABS_QUERY, payload: { current: endpoint === 'popup' } }),
39             (res) => {
40                 if (res.type === 'error') return { tabId: -1, url: null };
41                 return { tabId: res.tabId, url: res.url };
42             }
43         );
45         const name = generatePortName(endpoint, tabId);
46         const port = browser.runtime.connect(browser.runtime.id, { name });
47         const disconnectPort = safeCall(() => port.disconnect());
48         const destroy = pipe(disconnectPort, ExtensionContext.clear);
49         const ctx = ExtensionContext.set({ endpoint, port, tabId, url, destroy });
51         ctx.port.onDisconnect.addListener(async () => {
52             const { recycle } = onDisconnect?.(ExtensionContext.read());
53             logger.info(`[Context::Extension] port disconnected [reconnect=${recycle}]`);
54             return recycle && onRecycle(await setupExtensionContext(options));
55         });
57         registerLoggerEffect((...logs) =>
58             sendMessage(
59                 resolveMessageFactory(endpoint)({
60                     type: WorkerMessageType.LOG_EVENT,
61                     payload: { log: logs.join(' ') },
62                 })
63             )
64         );
66         logger.info('[Context::Extension] tabId resolved & port opened');
67         return ctx;
68     } catch (e) {
69         logger.warn('[Context::Extension]', e);
70         throw new Error('Initalization failed');
71     }