1 import { z } from 'zod';
3 import type { HttpsProtonMeDesktopInboxHeartbeatTotalV1SchemaJson } from '@proton/metrics/types/desktop_inbox_heartbeat_total_v1.schema';
4 import type { Environment } from '@proton/shared/lib/interfaces';
6 import type { ColorScheme, ThemeSetting } from '../themes/themes';
7 import { type DailyStatsStored, zDailyStatsReport } from './DailyStats';
8 import { type DefaultProtocol, zDefaultProtocol } from './DefaultProtocol';
9 import type { AppVersion, DesktopVersion } from './DesktopVersion';
11 export type CHANGE_VIEW_TARGET = 'mail' | 'calendar' | 'account';
12 export type ElectronNotification = {
15 app: CHANGE_VIEW_TARGET;
20 export type ESUserChoice = boolean | null;
22 // This type must be updated in the Electron application as well
23 export type IPCInboxDesktopFeature =
28 | 'LatestVersionCheck'
36 export type IPCInboxGetInfoMessage =
37 | { type: 'theme'; result: ThemeSetting }
38 | { type: 'latestVersion'; result: DesktopVersion | null }
39 | { type: 'installSource'; result: string | null }
40 | { type: 'defaultMailto'; result: DefaultProtocol }
41 | { type: 'colorScheme'; result: ColorScheme }
42 | { type: 'getAllAppVersions'; result: string }
43 | { type: 'dailyStats'; result: DailyStatsStored };
44 export type IPCInboxGetUserInfoMessage = { type: 'esUserChoice'; result: ESUserChoice };
45 export type IPCInboxClientUpdateMessage =
46 | { type: 'updateNotification'; payload: number }
47 | { type: 'userLogin'; payload?: undefined }
48 | { type: 'userLogout'; payload?: undefined }
49 | { type: 'clearAppData'; payload?: undefined }
50 | { type: 'oauthPopupOpened'; payload: 'oauthPopupStarted' | 'oauthPopupFinished' }
51 | { type: 'subscriptionModalOpened'; payload: 'subscriptionModalStarted' | 'subscriptionModalFinished' }
52 | { type: 'openExternal'; payload: string }
53 | { type: 'changeView'; payload: CHANGE_VIEW_TARGET }
54 | { type: 'trialEnd'; payload?: 'trialEnded' | 'resetTrialEnded' | undefined }
55 | { type: 'showNotification'; payload: ElectronNotification }
56 | { type: 'updateLocale'; payload: string }
57 | { type: 'setTheme'; payload: ThemeSetting }
58 | { type: 'earlyAccess'; payload: Environment | undefined }
59 | { type: 'checkDefaultMailtoAndSignal'; payload?: undefined }
60 | { type: 'checkDailyStatsAndSignal'; payload?: undefined }
61 | { type: 'defaultMailtoTelemetryReported'; payload: number }
62 | { type: 'dailyStatsReported'; payload: number }
63 | { type: 'setESUserChoice'; payload: { userID: string; userChoice: boolean } }
64 | { type: 'storeAppVersion'; payload: AppVersion }
65 | { type: 'triggerCrash'; payload?: undefined }
66 | { type: 'metricsListenerChanged'; payload: 'ready' | 'removed' };
67 export type IPCInboxClientUpdateMessageType = IPCInboxClientUpdateMessage['type'];
69 export const IPCInboxHostUpdateMessageSchema = z.discriminatedUnion('type', [
71 type: z.literal('captureMessage'),
74 level: z.union([z.literal('error'), z.literal('warning')]),
75 tags: z.record(z.union([z.string(), z.number()])),
76 extra: z.record(z.union([z.string(), z.number()])),
80 type: z.literal('defaultMailtoChecked'),
81 payload: zDefaultProtocol,
84 type: z.literal('dailyStatsChecked'),
85 payload: zDailyStatsReport,
88 type: z.literal('sentHeartbeatMetrics'),
89 payload: z.custom<HttpsProtonMeDesktopInboxHeartbeatTotalV1SchemaJson>(),
93 export type IPCInboxHostUpdateMessage = z.infer<typeof IPCInboxHostUpdateMessageSchema>;
94 export type IPCInboxHostUpdateMessageType = IPCInboxHostUpdateMessage['type'];
95 export type IPCInboxHostUpdateMessagePayload = IPCInboxHostUpdateMessage['payload'];
96 export type IPCInboxHostUpdateListener = (payload: IPCInboxHostUpdateMessagePayload) => void;
97 export type IPCInboxHostUpdateListenerRemover = { removeListener: () => void };
99 // THIS NEEDS REFACTOR: inda-refactor-001
100 // The orininal idea for `on` function was that this type will be defined by
101 // generic T extends IPCInboxHostUpdateMessageType, but it makes it messy
102 // because generic doesn't pick one value but also union of all possible types
103 // and therefore payload and callback need to accpet all.
104 export type IPCInboxHostUpdateListenerAdder = (
105 eventType: IPCInboxHostUpdateMessageType,
106 callback: IPCInboxHostUpdateListener
107 ) => IPCInboxHostUpdateListenerRemover;
110 * Electron injects an object in the window object
111 * This object can then be used to communicate from the web app to the desktop app
112 * This type can be used in any file that needs to communicate with the desktop app.
114 * The object can be injected when used in specific clients to avoid adding it globally
116 export type IPCInboxMessageBroker = {
117 hasFeature?: (feature: IPCInboxDesktopFeature) => boolean;
118 getInfo?: <T extends IPCInboxGetInfoMessage['type']>(
120 ) => Extract<IPCInboxGetInfoMessage, { type: T }>['result'];
121 getUserInfo?: <T extends IPCInboxGetUserInfoMessage['type']>(
124 ) => Extract<IPCInboxGetUserInfoMessage, { type: T }>['result'];
125 on?: IPCInboxHostUpdateListenerAdder;
126 send?: <T extends IPCInboxClientUpdateMessageType>(
128 payload: Extract<IPCInboxClientUpdateMessage, { type: T }>['payload']
132 export type PayloadOfHostUpdateType<T extends IPCInboxHostUpdateMessageType> = Extract<
133 IPCInboxHostUpdateMessage,
137 export const END_OF_TRIAL_KEY = 'endOfTrial';
139 export interface InboxDesktopFreeTrialDates {
140 trialStartDate?: Date;
144 export interface InboxDesktopFreeTrialReminders {