From 068e620249b534f7732176237fa218102eebdab1 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Tue, 9 Apr 2024 10:39:06 +0100 Subject: [PATCH 1/3] Add a message broadcasting service A broadcast is a message sent to every running app that uses Puter.js. Broadcasts have a name and a data payload, and are sent as a 'broadcast' message. Send a broadcast using: `globalThis.services.get('broadcast').sendBroadcast(...)` When doing so, you have the option to keep the broadcast message around, so that it can be sent to any newly-launched apps. Sending another broadcast with the same name will overwrite the previous one, so you don't have to worry about flooding a new app with duplicates. --- src/IPC.js | 3 ++ src/initgui.js | 2 ++ src/services/BroadcastService.js | 61 ++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) create mode 100644 src/services/BroadcastService.js diff --git a/src/IPC.js b/src/IPC.js index de771e447..56e257876 100644 --- a/src/IPC.js +++ b/src/IPC.js @@ -105,6 +105,9 @@ window.addEventListener('message', async (event) => { }, '*'); delete window.child_launch_callbacks[event.data.appInstanceID]; } + + // Send any saved broadcasts to the new app + globalThis.services.get('broadcast').sendSavedBroadcastsTo(event.data.appInstanceID); } //------------------------------------------------- // windowFocused diff --git a/src/initgui.js b/src/initgui.js index b5d2afb37..dfb1e3b04 100644 --- a/src/initgui.js +++ b/src/initgui.js @@ -36,6 +36,7 @@ import PuterDialog from './UI/PuterDialog.js'; import determine_active_container_parent from './helpers/determine_active_container_parent.js'; import { ThemeService } from './services/ThemeService.js'; import UIWindowThemeDialog from './UI/UIWindowThemeDialog.js'; +import { BroadcastService } from './services/BroadcastService.js'; const launch_services = async function () { const services_l_ = []; @@ -49,6 +50,7 @@ const launch_services = async function () { services_m_[name] = instance; } + register('broadcast', new BroadcastService()); register('theme', new ThemeService()); for (const [_, instance] of services_l_) { diff --git a/src/services/BroadcastService.js b/src/services/BroadcastService.js new file mode 100644 index 000000000..e0e6a470d --- /dev/null +++ b/src/services/BroadcastService.js @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2024 Puter Technologies Inc. + * + * This file is part of Puter. + * + * Puter is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +import { Service } from "../definitions.js"; + +export class BroadcastService extends Service { + // After a new app is launched, it will receive these broadcasts + #broadcastsToSendToNewAppInstances = new Map(); // name -> data + + async _init() { + // Nothing + } + + // Send a 'broadcast' message to all open apps, with the given name and data. + // If sendToNewAppInstances is true, the message will be saved, and sent to any apps that are launched later. + // A new saved broadcast will replace an earlier one with the same name. + sendBroadcast(name, data, { sendToNewAppInstances = false } = {}) { + $('.window-app-iframe[data-appUsesSDK=true]').each((_, iframe) => { + iframe.contentWindow.postMessage({ + msg: 'broadcast', + name: name, + data: data, + }, '*'); + }); + + if (sendToNewAppInstances) { + this.#broadcastsToSendToNewAppInstances.set(name, data); + } + } + + // Send all saved broadcast messages to the given app instance. + sendSavedBroadcastsTo(appInstanceID) { + const iframe = $(`.window[data-element_uuid="${appInstanceID}"] .window-app-iframe[data-appUsesSDK=true]`).get(0); + if (!iframe) { + console.error(`Attempted to send saved broadcasts to app instance ${appInstanceID}, which is not using the Puter SDK`); + return; + } + for (const [name, data] of this.#broadcastsToSendToNewAppInstances) { + iframe.contentWindow.postMessage({ + msg: 'broadcast', + name: name, + data: data, + }, '*'); + } + } +} From 59cdb6e8c6bd65468cffc3e6fc0936a0e93d1364 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Tue, 9 Apr 2024 10:40:06 +0100 Subject: [PATCH 2/3] Broadcast when theme changes --- src/services/ThemeService.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/services/ThemeService.js b/src/services/ThemeService.js index afd6c8414..10921b3ef 100644 --- a/src/services/ThemeService.js +++ b/src/services/ThemeService.js @@ -31,7 +31,11 @@ const default_values = { }; export class ThemeService extends Service { + #broadcastService; + async _init () { + this.#broadcastService = globalThis.services.get('broadcast'); + this.state = { sat: 41.18, hue: 210, @@ -113,6 +117,16 @@ export class ThemeService extends Service { this.root.style.setProperty('--primary-saturation', s.sat + '%'); this.root.style.setProperty('--primary-lightness', s.lig + '%'); this.root.style.setProperty('--primary-alpha', s.alpha); + + // TODO: Should we debounce this to reduce traffic? + this.#broadcastService.sendBroadcast('themeChanged', { + palette: { + primaryHue: s.hue, + primarySaturation: s.sat + '%', + primaryLightness: s.lig + '%', + primaryAlpha: s.alpha, + }, + }, { sendToNewAppInstances: true }); } save_ () { From 84a31b3520dee9a67d4d24bbbd256f2de3ee48f6 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Mon, 8 Apr 2024 14:04:44 +0100 Subject: [PATCH 3/3] Broadcast when the language changes This is left open to future additions, by naming it 'locale', and having the language just be an object field. Side note, maybe we should have a LocaleService for this? --- src/helpers.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/helpers.js b/src/helpers.js index a8486d11e..7a8731b85 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -722,7 +722,14 @@ window.mutate_user_preferences = function(user_preferences_delta) { window.update_user_preferences = function(user_preferences) { window.user_preferences = user_preferences; localStorage.setItem('user_preferences', JSON.stringify(user_preferences)); - window.locale = user_preferences.language ?? 'en'; + const language = user_preferences.language ?? 'en'; + window.locale = language; + + // Broadcast locale change to apps + const broadcastService = globalThis.services.get('broadcast'); + broadcastService.sendBroadcast('localeChanged', { + language: language, + }, { sendToNewAppInstances: true }); } window.sendWindowWillCloseMsg = function(iframe_element) {