Add support for launching child apps

Calling `puter.ui.launchApp()` now treats the new app as a child of the
one that launched it.

A child app is given a `puter.parent_instance_id` URL param, containing
its parent's instance ID.

Previously, `launchApp()` would resolve as soon as the app was launched.
This commit changes that, so that it is notified after the child app
sends its READY event to Puter. This means that as soon as `launchApp()`
has completed, the child app is ready to receive messages. The downside
is that launching an app that does not include Puter.js will now not
cause a notification, so `await puter.ui.launchApp()` will not resolve
in that case.
This commit is contained in:
Sam Atkins 2024-04-05 16:55:49 +01:00
parent c78927d5db
commit 725cbf2e20
3 changed files with 32 additions and 7 deletions

View File

@ -92,6 +92,19 @@ window.addEventListener('message', async (event) => {
//-------------------------------------------------
if(event.data.msg === 'READY'){
$(target_iframe).attr('data-appUsesSDK', 'true');
// If we were waiting to launch this as a child app, report to the parent that it succeeded.
const child_launch_callback = window.child_launch_callbacks[event.data.appInstanceID];
if (child_launch_callback) {
const parent_iframe = iframe_for_app_instance(child_launch_callback.parent_instance_id);
// send confirmation to requester window
parent_iframe.contentWindow.postMessage({
msg: 'childAppLaunched',
original_msg_id: child_launch_callback.launch_msg_id,
child_instance_id: event.data.appInstanceID,
}, '*');
delete window.child_launch_callbacks[event.data.appInstanceID];
}
}
//-------------------------------------------------
// windowFocused
@ -500,16 +513,20 @@ window.addEventListener('message', async (event) => {
// launchApp
//--------------------------------------------------------
else if(event.data.msg === 'launchApp'){
// launch app
// TODO: Determine if the app is allowed to launch child apps? We may want to limit this to prevent abuse.
// remember app for launch callback later
const child_instance_id = uuidv4();
window.child_launch_callbacks[child_instance_id] = {
parent_instance_id: event.data.appInstanceID,
launch_msg_id: msg_id,
};
// launch child app
launch_app({
name: event.data.app_name ?? app_name,
args: event.data.args ?? {},
parent_instance_id: event.data.appInstanceID,
uuid: child_instance_id,
});
// send confirmation to requester window
target_iframe.contentWindow.postMessage({
original_msg_id: msg_id,
}, '*');
}
//--------------------------------------------------------
// readAppDataFile

View File

@ -130,6 +130,9 @@ window.launch_apps = [];
window.launch_apps.recent = []
window.launch_apps.recommended = []
// Map of { child_instance_id -> { parent_instance_id, launch_msg_id } }
window.child_launch_callbacks = {};
// Is puter being loaded inside an iframe?
if (window.location !== window.parent.location) {
window.is_embedded = true;

View File

@ -1849,7 +1849,7 @@ window.trigger_download = (paths)=>{
* @param {*} options
*/
window.launch_app = async (options)=>{
const uuid = uuidv4();
const uuid = options.uuid ?? uuidv4();
let icon, title, file_signature;
const window_options = options.window_options ?? {};
@ -1945,6 +1945,11 @@ window.launch_app = async (options)=>{
// add app_id to URL
iframe_url.searchParams.append('puter.app.id', app_info.uuid);
// add parent_app_instance_id to URL
if (options.parent_instance_id) {
iframe_url.searchParams.append('puter.parent_instance_id', options.parent_instance_id);
}
if(file_signature){
iframe_url.searchParams.append('puter.item.uid', file_signature.uid);
iframe_url.searchParams.append('puter.item.path', options.file_path ? `~/` + options.file_path.split('/').slice(1).join('/') : file_signature.path);