Report when a non-SDK app closes

puter.ui.launchApp() returns a Promise that needs to resolve whether the
app uses the Puter SDK or not. Non-SDK apps are tricky because they
don't send a READY message on startup, and we don't know in advance
whether an app will use the SDK or not.

This is a workaround to ensure that launchApp() always resolves. When an
app is closed, if it wasn't using the SDK, we send an artificial
notification that it launched, followed by an extra notification that
it has closed (because the original close notification was sent before
this point). This means any users of launchApp() can await it, and get
an AppConnection, and listen to the close event. They can't otherwise
interact with a non-SDK app because it will have closed already, but we
can improve this in the future without breaking the API.
This commit is contained in:
Sam Atkins 2024-04-17 11:47:30 +01:00
parent cce5cda640
commit 2890f19bfd
2 changed files with 37 additions and 22 deletions

View File

@ -2853,27 +2853,7 @@ $.fn.close = async function(options) {
$(`.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.
}
window.report_app_closed(window_uuid);
// remove backdrop
$(this).closest('.window-backdrop').remove();

View File

@ -1899,6 +1899,15 @@ window.launch_app = async (options)=>{
$(el).on('remove', () => {
const svc_process = globalThis.services.get('process');
svc_process.unregister(process.uuid);
// If it's a non-sdk app, report that it launched and closed.
// FIXME: This is awkward. Really, we want some way of knowing when it's launched and reporting that immediately instead.
const $app_iframe = $(el).find('.window-app-iframe');
if ($app_iframe.attr('data-appUsesSdk') !== 'true') {
window.report_app_launched(process.uuid, { uses_sdk: false });
// We also have to report an extra close event because the real one was sent already
window.report_app_closed(process.uuid);
}
});
process.references.el_win = el;
@ -3538,4 +3547,30 @@ window.report_app_launched = (instance_id, { uses_sdk = true }) => {
}, '*');
delete window.child_launch_callbacks[instance_id];
}
}
};
// Run any callbacks to say that the app has closed
window.report_app_closed = (instance_id) => {
const el_window = window_for_app_instance(instance_id);
// notify parent app, if we have one, that we're closing
const parent_id = el_window.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: instance_id,
}, '*');
}
// notify child apps, if we have them, that we're closing
const children = $(`.window[data-parent_instance_id="${instance_id}"] .window-app-iframe`);
children.each((_, child) => {
child.contentWindow.postMessage({
msg: 'appClosed',
appInstanceID: instance_id,
}, '*');
});
// TODO: Once other AppConnections exist, those will need notifying too.
};