From 951bc0ea7a677a05e0db2dfd3a33930324d1b02d Mon Sep 17 00:00:00 2001 From: jelveh Date: Fri, 23 May 2025 19:50:47 -0700 Subject: [PATCH] Only show Puter Dialog when needed --- src/gui/src/initgui.js | 29 +++++++++-- src/puter-js/src/index.js | 14 ++++++ src/puter-js/src/modules/PuterDialog.js | 67 +++++++++++++++++++++++-- src/puter-js/src/modules/UI.js | 10 ++-- 4 files changed, 110 insertions(+), 10 deletions(-) diff --git a/src/gui/src/initgui.js b/src/gui/src/initgui.js index 7d238ab06..dea490278 100644 --- a/src/gui/src/initgui.js +++ b/src/gui/src/initgui.js @@ -829,6 +829,13 @@ window.initgui = async function(options){ let headers = {}; if(window.custom_headers) headers = window.custom_headers; + + // if this is a popup, show a spinner + let spinner_init_ts = Date.now(); + if(window.embedded_in_popup){ + puter.ui.showSpinner('Setting up your Puter account for secure AI and Cloud features...'); + } + $.ajax({ url: window.gui_origin + "/signup", type: 'POST', @@ -841,18 +848,34 @@ window.initgui = async function(options){ is_temp: true, }), success: async function (data){ - window.update_auth_data(data.token, data.user); - document.dispatchEvent(new Event("login", { bubbles: true})); + // if this is a popup, hide the spinner, make sure it was visible for at least 2 seconds + if(window.embedded_in_popup){ + let spinner_duration = (Date.now() - spinner_init_ts); + setTimeout(() => { + window.update_auth_data(data.token, data.user); + document.dispatchEvent(new Event("login", { bubbles: true})); + puter.ui.hideSpinner(); + }, spinner_duration > 2000 ? 10 : 2000 - spinner_duration); + + return; + }else{ + window.update_auth_data(data.token, data.user); + document.dispatchEvent(new Event("login", { bubbles: true})); + } }, error: async (err) => { UIAlert({ message: html_encode(err.responseText), }); + }, + complete: function(){ + } }); + } - // if there is at least one window open (only non-Explorer windows), ask user for confirmation when navigating away + // if there is at least one window open (only non-Explorer windows), ask user for confirmation when navigating away from puter if(window.feature_flags.prompt_user_when_navigation_away_from_puter){ window.onbeforeunload = function(){ if($(`.window:not(.window[data-app="explorer"])`).length > 0) diff --git a/src/puter-js/src/index.js b/src/puter-js/src/index.js index a9c0ad2d0..8b7570403 100644 --- a/src/puter-js/src/index.js +++ b/src/puter-js/src/index.js @@ -520,7 +520,21 @@ export default window.puter = (function() { } print = function(...args){ + // Check if the last argument is an options object with escapeHTML property + let options = {}; + if(args.length > 0 && typeof args[args.length - 1] === 'object' && args[args.length - 1] !== null && 'escapeHTML' in args[args.length - 1]) { + options = args.pop(); + } + for(let arg of args){ + // Escape HTML if the option is set to true + if(options.escapeHTML === true && typeof arg === 'string') { + arg = arg.replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); + } document.body.innerHTML += arg; } } diff --git a/src/puter-js/src/modules/PuterDialog.js b/src/puter-js/src/modules/PuterDialog.js index 9b355290e..5bc9d9933 100644 --- a/src/puter-js/src/modules/PuterDialog.js +++ b/src/puter-js/src/modules/PuterDialog.js @@ -10,10 +10,58 @@ class PuterDialog extends HTMLElement { constructor(resolve, reject) { super(); - this.attachShadow({ mode: 'open' }); - this.reject = reject; this.resolve = resolve; + this.popupLaunched = false; // Track if popup was successfully launched + + /** + * Detects if there's a recent user activation that would allow popup opening + * @returns {boolean} True if user activation is available, false otherwise. + */ + this.hasUserActivation = () => { + // Modern browsers support navigator.userActivation + if (navigator.userActivation) { + return navigator.userActivation.hasBeenActive && navigator.userActivation.isActive; + } + + // Fallback: try to detect user activation by attempting to open a popup + // This is a bit hacky but works as a fallback + try { + const testPopup = window.open('', '_blank', 'width=1,height=1,left=-1000,top=-1000'); + if (testPopup) { + testPopup.close(); + return true; + } + return false; + } catch (e) { + return false; + } + } + + /** + * Launches the authentication popup window + * @returns {Window|null} The popup window reference or null if failed + */ + this.launchPopup = () => { + try { + let w = 600; + let h = 400; + let title = 'Puter'; + var left = (screen.width/2)-(w/2); + var top = (screen.height/2)-(h/2); + const popup = window.open( + puter.defaultGUIOrigin + '/?embedded_in_popup=true&request_auth=true' + (window.crossOriginIsolated ? '&cross_origin_isolated=true' : ''), + title, + 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width='+w+', height='+h+', top='+top+', left='+left + ); + return popup; + } catch (e) { + console.error('Failed to open popup:', e); + return null; + } + } + + this.attachShadow({ mode: 'open' }); let h; // Dialog @@ -408,7 +456,20 @@ class PuterDialog extends HTMLElement { } open() { - this.shadowRoot.querySelector('dialog').showModal(); + console.log(this.hasUserActivation()); + if(this.hasUserActivation()){ + let w = 600; + let h = 400; + let title = 'Puter'; + var left = (screen.width/2)-(w/2); + var top = (screen.height/2)-(h/2); + window.open(puter.defaultGUIOrigin + '/?embedded_in_popup=true&request_auth=true' + (window.crossOriginIsolated ? '&cross_origin_isolated=true' : ''), + title, + 'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width='+w+', height='+h+', top='+top+', left='+left); + } + else{ + this.shadowRoot.querySelector('dialog').showModal(); + } } close() { diff --git a/src/puter-js/src/modules/UI.js b/src/puter-js/src/modules/UI.js index 5e25f40d2..3daf67dc7 100644 --- a/src/puter-js/src/modules/UI.js +++ b/src/puter-js/src/modules/UI.js @@ -1307,7 +1307,7 @@ class UI extends EventListener { #showTime = null; #hideTimeout = null; - showSpinner() { + showSpinner(html) { if (this.#overlayActive) return; // Create and add stylesheet for spinner if it doesn't exist @@ -1330,6 +1330,7 @@ class UI extends EventListener { font-size: 16px; margin-top: 10px; text-align: center; + width: 100%; } @keyframes spin { @@ -1342,10 +1343,11 @@ class UI extends EventListener { flex-direction: column; align-items: center; justify-content: center; - width: 120px; - height: 120px; + min-height: 120px; background: #ffffff; border-radius: 10px; + padding: 20px; + min-width: 120px; } `; document.head.appendChild(styleSheet); @@ -1377,7 +1379,7 @@ class UI extends EventListener { // Add spinner and text container.innerHTML = `
-
Working...
+
${html ?? 'Working...'}
`; overlay.appendChild(container);