diff --git a/frontend/taipy-gui/base/src/app.ts b/frontend/taipy-gui/base/src/app.ts index ba2409f91e..1ed6578267 100644 --- a/frontend/taipy-gui/base/src/app.ts +++ b/frontend/taipy-gui/base/src/app.ts @@ -12,7 +12,15 @@ export type OnInitHandler = (taipyApp: TaipyApp) => void; export type OnChangeHandler = (taipyApp: TaipyApp, encodedName: string, value: unknown) => void; export type OnNotifyHandler = (taipyApp: TaipyApp, type: string, message: string) => void; export type OnReloadHandler = (taipyApp: TaipyApp, removedChanges: ModuleData) => void; +export type OnWsMessage = (taipyApp: TaipyApp, event: string, payload: unknown) => void; export type OnWsStatusUpdate = (taipyApp: TaipyApp, messageQueue: string[]) => void; +export type OnEvent = + | OnInitHandler + | OnChangeHandler + | OnNotifyHandler + | OnReloadHandler + | OnWsMessage + | OnWsStatusUpdate; type Route = [string, string]; export class TaipyApp { @@ -21,6 +29,7 @@ export class TaipyApp { _onChange: OnChangeHandler | undefined; _onNotify: OnNotifyHandler | undefined; _onReload: OnReloadHandler | undefined; + _onWsMessage: OnWsMessage | undefined; _onWsStatusUpdate: OnWsStatusUpdate | undefined; _ackList: string[]; variableData: DataManager | undefined; @@ -69,6 +78,10 @@ export class TaipyApp { this._onInit = handler; } + onInitEvent() { + this.onInit && this.onInit(this); + } + get onChange() { return this._onChange; } @@ -80,6 +93,10 @@ export class TaipyApp { this._onChange = handler; } + onChangeEvent(encodedName: string, value: unknown) { + this.onChange && this.onChange(this, encodedName, value); + } + get onNotify() { return this._onNotify; } @@ -91,6 +108,10 @@ export class TaipyApp { this._onNotify = handler; } + onNotifyEvent(type: string, message: string) { + this.onNotify && this.onNotify(this, type, message); + } + get onReload() { return this._onReload; } @@ -101,6 +122,24 @@ export class TaipyApp { this._onReload = handler; } + onReloadEvent(removedChanges: ModuleData) { + this.onReload && this.onReload(this, removedChanges); + } + + get onWsMessage() { + return this._onWsMessage; + } + set onWsMessage(handler: OnWsMessage | undefined) { + if (handler !== undefined && handler?.length !== 3) { + throw new Error("onWsMessage() requires three parameters"); + } + this._onWsMessage = handler; + } + + onWsMessageEvent(event: string, payload: unknown) { + this.onWsMessage && this.onWsMessage(this, event, payload); + } + get onWsStatusUpdate() { return this._onWsStatusUpdate; } @@ -111,6 +150,10 @@ export class TaipyApp { this._onWsStatusUpdate = handler; } + onWsStatusUpdateEvent(messageQueue: string[]) { + this.onWsStatusUpdate && this.onWsStatusUpdate(this, messageQueue); + } + // Utility methods init() { this.clientId = ""; @@ -134,7 +177,7 @@ export class TaipyApp { const ackId = sendWsMessage(this.socket, type, id, payload, this.clientId, context); if (ackId) { this._ackList.push(ackId); - this.onWsStatusUpdate && this.onWsStatusUpdate(this, this._ackList); + this.onWsStatusUpdateEvent(this._ackList); } } diff --git a/frontend/taipy-gui/base/src/packaging/taipy-gui-base.d.ts b/frontend/taipy-gui/base/src/packaging/taipy-gui-base.d.ts index e6c9a0c509..a94617a56a 100644 --- a/frontend/taipy-gui/base/src/packaging/taipy-gui-base.d.ts +++ b/frontend/taipy-gui/base/src/packaging/taipy-gui-base.d.ts @@ -37,11 +37,22 @@ export type WsMessageType = | "GMC" | "GDT" | "AID" - | "GR"; + | "GR" + | "FV"; +export interface WsMessage { + type: WsMessageType | string; + name: string; + payload: Record | unknown; + propagate: boolean; + client_id: string; + module_context: string; + ack_id?: string; +} export type OnInitHandler = (taipyApp: TaipyApp) => void; export type OnChangeHandler = (taipyApp: TaipyApp, encodedName: string, value: unknown) => void; export type OnNotifyHandler = (taipyApp: TaipyApp, type: string, message: string) => void; export type OnReloadHandler = (taipyApp: TaipyApp, removedChanges: ModuleData) => void; +export type OnWsMessage = (taipyApp: TaipyApp, event: string, payload: unknown) => void; export type OnWsStatusUpdate = (taipyApp: TaipyApp, messageQueue: string[]) => void; export type Route = [string, string]; export declare class TaipyApp { @@ -50,6 +61,7 @@ export declare class TaipyApp { _onChange: OnChangeHandler | undefined; _onNotify: OnNotifyHandler | undefined; _onReload: OnReloadHandler | undefined; + _onWsMessage: OnWsMessage | undefined; _onWsStatusUpdate: OnWsStatusUpdate | undefined; _ackList: string[]; variableData: DataManager | undefined; @@ -69,14 +81,22 @@ export declare class TaipyApp { ); get onInit(): OnInitHandler | undefined; set onInit(handler: OnInitHandler | undefined); + onInitEvent(): void; get onChange(): OnChangeHandler | undefined; set onChange(handler: OnChangeHandler | undefined); + onChangeEvent(encodedName: string, value: unknown): void; get onNotify(): OnNotifyHandler | undefined; set onNotify(handler: OnNotifyHandler | undefined); + onNotifyEvent(type: string, message: string): void; get onReload(): OnReloadHandler | undefined; set onReload(handler: OnReloadHandler | undefined); + onReloadEvent(removedChanges: ModuleData): void; + get onWsMessage(): OnWsMessage | undefined; + set onWsMessage(handler: OnWsMessage | undefined); + onWsMessageEvent(event: string, payload: unknown): void; get onWsStatusUpdate(): OnWsStatusUpdate | undefined; set onWsStatusUpdate(handler: OnWsStatusUpdate | undefined); + onWsStatusUpdateEvent(messageQueue: string[]): void; init(): void; sendWsMessage(type: WsMessageType | string, id: string, payload: unknown, context?: string | undefined): void; registerWsAdapter(wsAdapter: WsAdapter): void; @@ -96,15 +116,6 @@ export declare class TaipyApp { getPageMetadata(): Record; getWsStatus(): string[]; } -export interface WsMessage { - type: WsMessageType | string; - name: string; - payload: Record | unknown; - propagate: boolean; - client_id: string; - module_context: string; - ack_id?: string; -} export declare abstract class WsAdapter { abstract supportedMessageTypes: string[]; abstract handleWsMessage(message: WsMessage, app: TaipyApp): boolean; diff --git a/frontend/taipy-gui/base/src/socket.ts b/frontend/taipy-gui/base/src/socket.ts index f6aff46f4e..8c6e5f9062 100644 --- a/frontend/taipy-gui/base/src/socket.ts +++ b/frontend/taipy-gui/base/src/socket.ts @@ -4,17 +4,20 @@ import { TaipyApp } from "./app"; export const initSocket = (socket: Socket, taipyApp: TaipyApp) => { socket.on("connect", () => { + taipyApp.onWsMessageEvent("connect", null); if (taipyApp.clientId === "" || taipyApp.appId === "") { taipyApp.init(); } }); // Send a request to get App ID to verify that the app has not been reloaded socket.io.on("reconnect", () => { + taipyApp.onWsMessageEvent("reconnect", null); console.log("WebSocket reconnected"); taipyApp.sendWsMessage("AID", "reconnect", taipyApp.appId); }); // try to reconnect on connect_error socket.on("connect_error", (err) => { + taipyApp.onWsMessageEvent("connect_error", { err }); console.log("Error connecting WebSocket: ", err); setTimeout(() => { socket && socket.connect(); @@ -22,6 +25,7 @@ export const initSocket = (socket: Socket, taipyApp: TaipyApp) => { }); // try to reconnect on server disconnection socket.on("disconnect", (reason, details) => { + taipyApp.onWsMessageEvent("disconnect", { reason, details }); console.log("WebSocket disconnected due to: ", reason, details); if (reason === "io server disconnect") { socket && socket.connect(); @@ -29,6 +33,7 @@ export const initSocket = (socket: Socket, taipyApp: TaipyApp) => { }); // handle message data from backend socket.on("message", (message: WsMessage) => { + taipyApp.onWsMessageEvent("message", message); // handle messages with registered websocket adapters for (const adapter of taipyApp.wsAdapters) { if (adapter.supportedMessageTypes.includes(message.type)) { diff --git a/frontend/taipy-gui/base/src/wsAdapter.ts b/frontend/taipy-gui/base/src/wsAdapter.ts index 3ecf3a44c1..9db40c9460 100644 --- a/frontend/taipy-gui/base/src/wsAdapter.ts +++ b/frontend/taipy-gui/base/src/wsAdapter.ts @@ -35,7 +35,7 @@ export class TaipyWsAdapter extends WsAdapter { const encodedName = muPayload.name; const { value } = muPayload.payload; taipyApp.variableData?.update(encodedName, value); - taipyApp.onChange && taipyApp.onChange(taipyApp, encodedName, value); + taipyApp.onChangeEvent(encodedName, value); } } else if (message.type === "ID") { const { id } = message as unknown as IdMessage; @@ -61,12 +61,12 @@ export class TaipyWsAdapter extends WsAdapter { const functionChanges = taipyApp.functionData.init(functionData); const changes = merge(varChanges, functionChanges); if (varChanges || functionChanges) { - taipyApp.onReload && taipyApp.onReload(taipyApp, changes); + taipyApp.onReloadEvent(changes); } } else { taipyApp.variableData = new DataManager(variableData); taipyApp.functionData = new DataManager(functionData); - taipyApp.onInit && taipyApp.onInit(taipyApp); + taipyApp.onInitEvent(); } } else if (message.type === "AID") { const payload = message.payload as Record; @@ -78,13 +78,13 @@ export class TaipyWsAdapter extends WsAdapter { } else if (message.type === "GR") { const payload = message.payload as [string, string][]; taipyApp.routes = payload; - } else if (message.type === "AL" && taipyApp.onNotify) { + } else if (message.type === "AL") { const payload = message as AlertMessage; - taipyApp.onNotify(taipyApp, payload.atype, payload.message); + taipyApp.onNotifyEvent(payload.atype, payload.message); } else if (message.type === "ACK") { - const {id} = message as unknown as Record; + const { id } = message as unknown as Record; taipyApp._ackList = taipyApp._ackList.filter((v) => v !== id); - taipyApp.onWsStatusUpdate && taipyApp.onWsStatusUpdate(taipyApp, taipyApp._ackList); + taipyApp.onWsStatusUpdateEvent(taipyApp._ackList); } this.postWsMessageProcessing(message, taipyApp); return true;