From 43fb420d76de33737cef58443c28318bf4bc4c7c Mon Sep 17 00:00:00 2001 From: Tim Bradgate Date: Sat, 6 Jun 2026 21:29:26 +0100 Subject: [PATCH 1/2] Broadcast SCRIPT_PAGE_CHANGED after script saves so other clients update in real-time When one client saved script changes, other connected clients did not see the updates without a manual refresh. The backend was missing a WebSocket broadcast after the save commit in both the POST and PATCH handlers. Adds ws_send_to_all("NOOP", "SCRIPT_PAGE_CHANGED", {"page": page}) to both handlers, and adds matching action handlers in the Vue 2 Vuex store (SCRIPT_PAGE_CHANGED) and Vue 3 Pinia store (scriptPageChanged) that reload the changed page into both the script store and tmpScript (the data source the editor template renders from) when the page is loaded. Fixes #1149. Co-Authored-By: Claude Sonnet 4.6 --- client-v3/src/stores/script.ts | 12 ++++++++++++ client/src/store/modules/script.ts | 7 +++++++ server/controllers/api/show/script/script.py | 7 +++++++ 3 files changed, 26 insertions(+) diff --git a/client-v3/src/stores/script.ts b/client-v3/src/stores/script.ts index e16c3f19..adf710bb 100644 --- a/client-v3/src/stores/script.ts +++ b/client-v3/src/stores/script.ts @@ -2,6 +2,7 @@ import { defineStore } from 'pinia'; import log from 'loglevel'; import { makeURL } from '@/js/utils'; import { toast } from '@/js/toast'; +import { useScriptConfigStore } from '@/stores/scriptConfig'; import type { ScriptLine, StageDirectionStyle, @@ -47,6 +48,17 @@ export const useScriptStore = defineStore('script', { } }, + async scriptPageChanged(data: { page: number }): Promise { + const pageStr = String(data.page); + if (Object.prototype.hasOwnProperty.call(this.script, pageStr)) { + await this.loadScriptPage(data.page); + const scriptConfigStore = useScriptConfigStore(); + if (Object.prototype.hasOwnProperty.call(scriptConfigStore.tmpScript, pageStr)) { + scriptConfigStore.addPage(data.page, this.getScriptPage(data.page)); + } + } + }, + async saveNewPage(page: number, lines: ScriptLine[]): Promise { const params = new URLSearchParams({ page: String(page) }); const response = await fetch(`${makeURL('/api/v1/show/script')}?${params}`, { diff --git a/client/src/store/modules/script.ts b/client/src/store/modules/script.ts index cb7e453d..c5e6c5e0 100644 --- a/client/src/store/modules/script.ts +++ b/client/src/store/modules/script.ts @@ -145,6 +145,13 @@ const module: Module = { await context.dispatch('LOAD_CUES'); await context.dispatch('GET_CUTS'); }, + async SCRIPT_PAGE_CHANGED(context, msg: { DATA: { page: number } }) { + const page = String(msg.DATA.page); + if (Object.prototype.hasOwnProperty.call(context.state.script, page)) { + await context.dispatch('LOAD_SCRIPT_PAGE', page); + await context.dispatch('ADD_BLANK_PAGE', page); + } + }, async LOAD_SCRIPT_PAGE(context, page: string | number) { const searchParams = new URLSearchParams({ page: String(page) }); const response = await fetch(`${makeURL('/api/v1/show/script')}?${searchParams}`, { diff --git a/server/controllers/api/show/script/script.py b/server/controllers/api/show/script/script.py index cd61959a..cb9b6212 100644 --- a/server/controllers/api/show/script/script.py +++ b/server/controllers/api/show/script/script.py @@ -269,6 +269,9 @@ async def post(self): CompiledScript.compile_script, self.application, revision.id ) ) + await self.application.ws_send_to_all( + "NOOP", "SCRIPT_PAGE_CHANGED", {"page": page} + ) else: self.set_status(404) await self.finish({"message": ERROR_SHOW_NOT_FOUND}) @@ -663,11 +666,15 @@ async def patch(self): (revision.id, line["id"]), ) # Spawn a callback to create a compiled version of the script + session.commit() IOLoop.current().add_callback( partial( CompiledScript.compile_script, self.application, revision.id ) ) + await self.application.ws_send_to_all( + "NOOP", "SCRIPT_PAGE_CHANGED", {"page": page} + ) else: self.set_status(404) await self.finish({"message": ERROR_SHOW_NOT_FOUND}) From 3dbc914d992f2a52a12a28b546110e79655a6259 Mon Sep 17 00:00:00 2001 From: Tim Bradgate Date: Sat, 6 Jun 2026 21:36:42 +0100 Subject: [PATCH 2/2] Use Object.hasOwn() instead of Object.prototype.hasOwnProperty.call() Addresses SonarCloud warnings on PR #1151. Co-Authored-By: Claude Sonnet 4.6 --- client-v3/src/stores/script.ts | 4 ++-- client/src/store/modules/script.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/client-v3/src/stores/script.ts b/client-v3/src/stores/script.ts index adf710bb..1e8fb89a 100644 --- a/client-v3/src/stores/script.ts +++ b/client-v3/src/stores/script.ts @@ -50,10 +50,10 @@ export const useScriptStore = defineStore('script', { async scriptPageChanged(data: { page: number }): Promise { const pageStr = String(data.page); - if (Object.prototype.hasOwnProperty.call(this.script, pageStr)) { + if (Object.hasOwn(this.script, pageStr)) { await this.loadScriptPage(data.page); const scriptConfigStore = useScriptConfigStore(); - if (Object.prototype.hasOwnProperty.call(scriptConfigStore.tmpScript, pageStr)) { + if (Object.hasOwn(scriptConfigStore.tmpScript, pageStr)) { scriptConfigStore.addPage(data.page, this.getScriptPage(data.page)); } } diff --git a/client/src/store/modules/script.ts b/client/src/store/modules/script.ts index c5e6c5e0..740f9c9a 100644 --- a/client/src/store/modules/script.ts +++ b/client/src/store/modules/script.ts @@ -147,7 +147,7 @@ const module: Module = { }, async SCRIPT_PAGE_CHANGED(context, msg: { DATA: { page: number } }) { const page = String(msg.DATA.page); - if (Object.prototype.hasOwnProperty.call(context.state.script, page)) { + if (Object.hasOwn(context.state.script, page)) { await context.dispatch('LOAD_SCRIPT_PAGE', page); await context.dispatch('ADD_BLANK_PAGE', page); }