From bb0cc6944fc812b43342ad292000aaf1630f852e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Tur=C3=B3ci?= <64769322+mturoci@users.noreply.github.com> Date: Mon, 4 Sep 2023 08:23:56 +0200 Subject: [PATCH] fix: Clear dirty UI state across clients on broadcast/multicast user interaction for consistency. #1705 (#2123) --- client.go | 18 ++++++++++++++---- protocol.go | 1 + ts/index.ts | 8 ++++++++ ui/src/ui.ts | 5 ++++- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/client.go b/client.go index e35bbaec76c..3c4dae8449e 100644 --- a/client.go +++ b/client.go @@ -39,9 +39,11 @@ const ( ) var ( - newline = []byte{'\n'} - notFoundMsg = []byte(`{"e":"not_found"}`) - upgrader = websocket.Upgrader{ + newline = []byte{'\n'} + notFoundMsg = []byte(`{"e":"not_found"}`) + disconnectMsg = []byte(`{"data": {"":{"@system":{"client_disconnect":true}}}}`) + clearStateMsg = []byte(`{"c":1}`) + upgrader = websocket.Upgrader{ ReadBufferSize: 1024, // TODO review WriteBufferSize: 1024, // TODO review } @@ -94,7 +96,7 @@ func (c *Client) listen() { defer func() { app := c.broker.getApp(c.appPath) if app != nil { - app.forward(c.id, c.session, []byte(`{"data": {"":{"@system":{"client_disconnect":true}}}}`)) + app.forward(c.id, c.session, disconnectMsg) if err := app.disconnect(c.id); err != nil { echo(Log{"t": "disconnect", "client": c.addr, "route": c.appPath, "err": err.Error()}) } @@ -148,6 +150,14 @@ func (c *Client) listen() { } app.forward(c.id, c.session, []byte("{\"data\":"+string(m.data)+"}")) + + // Remove any dirty UI state if broadcast or multicast. + if app.mode == multicastMode { + c.broker.sendAll(c.broker.clients["/"+c.session.subject], clearStateMsg) + } + if app.mode == broadcastMode { + c.broker.sendAll(c.broker.clients[app.route], clearStateMsg) + } case watchMsgT: c.subscribe(m.addr) // subscribe even if page is currently NA diff --git a/protocol.go b/protocol.go index 982ba2792f0..2bfe537def4 100644 --- a/protocol.go +++ b/protocol.go @@ -22,6 +22,7 @@ type OpsD struct { U string `json:"u,omitempty"` // redirect E string `json:"e,omitempty"` // error M *Meta `json:"m,omitempty"` // metadata + C int `json:"c,omitempty"` // clear UI state } // Meta represents metadata unrelated to commands diff --git a/ts/index.ts b/ts/index.ts index b892bb9788e..ff55027744b 100644 --- a/ts/index.ts +++ b/ts/index.ts @@ -194,6 +194,7 @@ interface OpsD { u: S // active user's username e: B // can the user edit pages? } + c?: U // clear UI state } interface OpD { k?: S @@ -327,6 +328,8 @@ export enum WaveEventType { Page, /** Daemon sent some data. */ Data, + /** Daemon asked to clear any dirty state. */ + Clear, } /** */ @@ -348,10 +351,13 @@ export type WaveEvent = { t: WaveEventType.Disconnect, retry: U } | { t: WaveEventType.Data +} | { + t: WaveEventType.Clear } const connectEvent: WaveEvent = { t: WaveEventType.Connect }, resetEvent: WaveEvent = { t: WaveEventType.Reset }, + clearEvent: WaveEvent = { t: WaveEventType.Clear }, dataEvent: WaveEvent = { t: WaveEventType.Data } type WaveEventHandler = (e: WaveEvent) => void @@ -976,6 +982,8 @@ export const handle({ t: WaveEventType.Page, page }) } else if (msg.e) { handle({ t: WaveEventType.Error, code: errorCodes[msg.e] || WaveErrorCode.Unknown }) + } else if (msg.c) { + handle(clearEvent) } else if (msg.r) { handle(resetEvent) } else if (msg.u) { diff --git a/ui/src/ui.ts b/ui/src/ui.ts index f81a8b2f01c..bd86c5f0414 100644 --- a/ui/src/ui.ts +++ b/ui/src/ui.ts @@ -141,6 +141,9 @@ export const case WaveEventType.Data: busyB(false) break + case WaveEventType.Clear: + clearRec(args) + break } }) }, @@ -149,7 +152,7 @@ export const // Unconditionally set location hash so that the app doesn't have to track changes. const h = window.location.hash - if (h?.length > 1) args['#'] = h.substr(1) + if (h?.length > 1) args['#'] = h.substring(1) const d: Dict = { ...args } // shallow clone clearRec(args) // clear