From cce6531d45cf815e0cf80ba52f48bbcd865aaecd Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Tue, 9 Apr 2024 15:08:36 +0100 Subject: [PATCH] Notify parents/children when an app closes Send an appClosed message with the instance ID of the app that was closed. This will be picked up by Puter.js's AppConnection and reported as a 'close' event. To make this work, a `data-parent_instance_id` attribute is set on child app windows. This is very similar to the `data-parent_uuid` attribute, which tracks parent windows instead of parent app instances. (Dialogs have a parent window, but are not apps, so don't have a parent app instance.) The difference is subtle, and we may want to combine these in the future, but currently closing an app will close any child windows, which is not behaviour we want for child apps. --- src/UI/UIWindow.js | 28 +++++++++++++++++++++++++++- src/helpers.js | 4 ++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/UI/UIWindow.js b/src/UI/UIWindow.js index a922e403..3134c23e 100644 --- a/src/UI/UIWindow.js +++ b/src/UI/UIWindow.js @@ -185,6 +185,7 @@ async function UIWindow(options) { data-uid ="${options.uid}" data-element_uuid="${options.element_uuid}" data-parent_uuid="${options.parent_uuid}" + ${options.parent_instance_id ? `data-parent_instance_id="${options.parent_instance_id}"` : ''} data-id ="${win_id}" data-iframe_msg_uid ="${options.iframe_msg_uid}" data-is_dir ="${options.is_dir}" @@ -2745,8 +2746,9 @@ $.fn.close = async function(options) { options = options || {}; $(this).each(async function() { const el_iframe = $(this).find('.window-app-iframe'); + const app_uses_sdk = el_iframe.length > 0 && el_iframe.attr('data-appUsesSDK') === 'true'; // tell child app that this window is about to close, get its response - if(el_iframe.length > 0 && el_iframe.attr('data-appUsesSDK') === 'true'){ + if(app_uses_sdk){ if(!options.bypass_iframe_messaging){ const resp = await sendWindowWillCloseMsg(el_iframe.get(0)); if(!resp.msg){ @@ -2819,6 +2821,30 @@ $.fn.close = async function(options) { } // close child windows $(`.window[data-parent_uuid="${window_uuid}"]`).close(); + + // notify other apps that we're closing + if (app_uses_sdk) { + // notify parent app, if we have one, that we're closing + const parent_id = this.dataset['parent_instance_id']; + const parent = $(`.window[data-element_uuid="${parent_id}"] .window-app-iframe`).get(0); + if (parent) { + parent.contentWindow.postMessage({ + msg: 'appClosed', + appInstanceID: window_uuid, + }, '*'); + } + + // notify child apps, if we have them, that we're closing + const children = $(`.window[data-parent_instance_id="${window_uuid}"] .window-app-iframe`); + children.each((_, child) => { + child.contentWindow.postMessage({ + msg: 'appClosed', + appInstanceID: window_uuid, + }, '*'); + }); + // TODO: Once other AppConnections exist, those will need notifying too. + } + // remove backdrop $(this).closest('.window-backdrop').remove(); // remove DOM element diff --git a/src/helpers.js b/src/helpers.js index a8486d11..d977e1b2 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1853,6 +1853,10 @@ window.launch_app = async (options)=>{ let icon, title, file_signature; const window_options = options.window_options ?? {}; + if (options.parent_instance_id) { + window_options.parent_instance_id = options.parent_instance_id; + } + // try to get 3rd-party app info let app_info = options.app_obj ?? await get_apps(options.name);