Only show Puter Dialog when needed

This commit is contained in:
jelveh
2025-05-23 19:50:47 -07:00
parent dbdebec635
commit 951bc0ea7a
4 changed files with 110 additions and 10 deletions
+26 -3
View File
@@ -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 <a href="https://puter.com" target="_blank">Puter</a> 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)
+14
View File
@@ -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, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
document.body.innerHTML += arg;
}
}
+64 -3
View File
@@ -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() {
+6 -4
View File
@@ -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 = `
<div class="puter-loading-spinner"></div>
<div class="puter-loading-text">Working...</div>
<div class="puter-loading-text">${html ?? 'Working...'}</div>
`;
overlay.appendChild(container);