Merge branch 'feat/inda-383-daily-stat' into 'main'
[ProtonMail-WebClient.git] / packages / docs-core / lib / Bridge / EditorInvoker.ts
blob3be029735ab9fea06a98fde808909909f9fdc3ca
1 import type {
2   EditorToClientReplyMessage,
3   ClientRequiresEditorMethods,
4   ParamsExcludingFunctions,
5   ClientToEditorInvokationMessage,
6   PendingMessage,
7   RtsMessagePayload,
8   DataTypesThatDocumentCanBeExportedAs,
9   DocumentRoleType,
10   EditorInitializationConfig,
11   YjsState,
12   SyncedEditorStateValues,
13 } from '@proton/docs-shared'
14 import { EditorBridgeMessageType, BridgeOriginProvider } from '@proton/docs-shared'
15 import type { LoggerInterface } from '@proton/utils/logs'
16 import { GenerateUUID } from '../Util/GenerateUuid'
17 import type { SerializedEditorState } from 'lexical'
18 import type { UserSettings } from '@proton/shared/lib/interfaces'
20 /** Allows the client to invoke methods on the editor */
21 export class EditorInvoker implements ClientRequiresEditorMethods {
22   private pendingMessages: PendingMessage[] = []
24   constructor(
25     private editorFrame: HTMLIFrameElement,
26     private readonly logger: LoggerInterface,
27   ) {}
29   async syncProperty(
30     property: keyof SyncedEditorStateValues,
31     value: SyncedEditorStateValues[keyof SyncedEditorStateValues],
32   ): Promise<void> {
33     return this.invokeEditorMethod('syncProperty', [property, value])
34   }
36   async loadUserSettings(settings: UserSettings): Promise<void> {
37     return this.invokeEditorMethod('loadUserSettings', [settings])
38   }
40   async getClientId(): Promise<number> {
41     return this.invokeEditorMethod('getClientId', [])
42   }
44   async showEditor(): Promise<void> {
45     return this.invokeEditorMethod('showEditor', [])
46   }
48   async performOpeningCeremony(): Promise<void> {
49     return this.invokeEditorMethod('performOpeningCeremony', [])
50   }
52   async performClosingCeremony(): Promise<void> {
53     return this.invokeEditorMethod('performClosingCeremony', [])
54   }
56   async receiveMessage(message: RtsMessagePayload): Promise<void> {
57     return this.invokeEditorMethod('receiveMessage', [message])
58   }
60   async getDocumentState(): Promise<YjsState> {
61     return this.invokeEditorMethod('getDocumentState', [])
62   }
64   async replaceEditorState(state: SerializedEditorState): Promise<void> {
65     return this.invokeEditorMethod('replaceEditorState', [state])
66   }
68   async handleCommentsChange(): Promise<void> {
69     return this.invokeEditorMethod('handleCommentsChange', [])
70   }
72   async handleTypingStatusChange(threadId: string): Promise<void> {
73     return this.invokeEditorMethod('handleTypingStatusChange', [threadId])
74   }
76   async handleCreateCommentMarkNode(markID: string): Promise<void> {
77     return this.invokeEditorMethod('handleCreateCommentMarkNode', [markID])
78   }
80   async handleRemoveCommentMarkNode(markID: string): Promise<void> {
81     return this.invokeEditorMethod('handleRemoveCommentMarkNode', [markID])
82   }
84   async handleResolveCommentMarkNode(markID: string): Promise<void> {
85     return this.invokeEditorMethod('handleResolveCommentMarkNode', [markID])
86   }
88   async handleUnresolveCommentMarkNode(markID: string): Promise<void> {
89     return this.invokeEditorMethod('handleUnresolveCommentMarkNode', [markID])
90   }
92   async changeLockedState(locked: boolean): Promise<void> {
93     return this.invokeEditorMethod('changeLockedState', [locked])
94   }
96   async broadcastPresenceState(): Promise<void> {
97     return this.invokeEditorMethod('broadcastPresenceState', [])
98   }
100   async showCommentsPanel(): Promise<void> {
101     return this.invokeEditorMethod('showCommentsPanel', [])
102   }
104   async exportData(format: DataTypesThatDocumentCanBeExportedAs): Promise<Uint8Array> {
105     return this.invokeEditorMethod('exportData', [format])
106   }
108   async printAsPDF(): Promise<void> {
109     return this.invokeEditorMethod('printAsPDF', [])
110   }
112   async getCurrentEditorState(): Promise<SerializedEditorState | undefined> {
113     return this.invokeEditorMethod('getCurrentEditorState', [])
114   }
116   async toggleDebugTreeView(): Promise<void> {
117     return this.invokeEditorMethod('toggleDebugTreeView', [])
118   }
120   async handleIsSuggestionsFeatureEnabled(enabled: boolean): Promise<void> {
121     return this.invokeEditorMethod('handleIsSuggestionsFeatureEnabled', [enabled])
122   }
124   async initializeEditor(
125     documentId: string,
126     userAddress: string,
127     documentRole: DocumentRoleType,
128     editorInitializationConfig?: EditorInitializationConfig,
129   ): Promise<void> {
130     return this.invokeEditorMethod('initializeEditor', [
131       documentId,
132       userAddress,
133       documentRole,
134       editorInitializationConfig,
135     ])
136   }
138   public handleReplyFromEditor(message: EditorToClientReplyMessage): void {
139     const pendingMessage = this.pendingMessages.find((m) => m.messageId === message.messageId)
140     if (pendingMessage) {
141       pendingMessage.resolve(message.returnValue)
142       this.pendingMessages = this.pendingMessages.filter((m) => m !== pendingMessage)
143     }
144   }
146   private async invokeEditorMethod<K extends keyof ClientRequiresEditorMethods>(
147     functionName: K,
148     args: ParamsExcludingFunctions<Parameters<ClientRequiresEditorMethods[K]>>,
149   ): Promise<ReturnType<ClientRequiresEditorMethods[K]>> {
150     if (!this.editorFrame.contentWindow) {
151       throw new Error('Editor frame contentWindow is not ready')
152     }
154     const messageId = GenerateUUID()
156     const message: ClientToEditorInvokationMessage<K> = {
157       type: EditorBridgeMessageType.ClientToEditorInvokation,
158       functionName,
159       args,
160       messageId,
161     }
163     this.logger.debug('Sending message to editor', message)
165     this.editorFrame.contentWindow.postMessage(message, BridgeOriginProvider.GetEditorOrigin())
167     return new Promise<ReturnType<ClientRequiresEditorMethods[K]>>((resolve) => {
168       this.pendingMessages.push({
169         messageId,
170         resolve,
171       })
172     })
173   }