mirror of
https://github.com/HeyPuter/puter.git
synced 2026-08-23 22:47:19 +00:00
fix(gui): launch from the add-an-app modal with the tile morph, not a fade
Choosing App Center or the AI builder opened the window with the plain fade while every other launch in the Apps tab grows out of the icon that was clicked — the one launch that most needs to say where it came from read as the app appearing from nowhere. The morph resolved its anchor by app name, which cannot work here: the whole point of these two options is installing an app you do not have, and an app with no tile has no name to find. So the launcher may name its anchor element (dashboard_tile_el), and TabApps resolves it once before launching so the click flourish and the window's half can never disagree about which tile the launch came from: the app's own tile when it already has one on screen — minimize flies the window back there, so that is where opening should come from — and otherwise the + tile itself. dashboard_tile_in_view's visibility test is now a named helper it shares with the new path, so an anchor on an offscreen pager page is still rejected and still falls through to the fade.
This commit is contained in:
@@ -2,7 +2,7 @@ import UIContextMenu from '../UIContextMenu.js';
|
||||
import UIAlert from '../UIAlert.js';
|
||||
import launch_app from '../../helpers/launch_app.js';
|
||||
import revokeAppSessions from '../../helpers/revoke_app_sessions.js';
|
||||
import { begin_dashboard_tile_launch, settle_dashboard_tile_launch } from '../UIWindow.js';
|
||||
import { begin_dashboard_tile_launch, dashboard_tile_in_view, settle_dashboard_tile_launch } from '../UIWindow.js';
|
||||
import { isTouchPrimaryDevice } from './ContextMenu/ContextMenu.js';
|
||||
import { reconcileAppOrder, serializeAppOrder, mergeSavedOrder, APPS_ORDER_KV_KEY } from './appOrder.js';
|
||||
import { parseRemovedApps, serializeRemovedApps, REMOVED_APPS_KV_KEY } from './removedApps.js';
|
||||
@@ -654,14 +654,33 @@ function showAddAppModal ({ $el_window }) {
|
||||
else close();
|
||||
});
|
||||
|
||||
// Maximized like a tile launch, and without a morph: the add tile is not
|
||||
// the app's own icon, so there is nothing for the window to grow out of.
|
||||
// Opened exactly as a tile click opens it, click flourish and
|
||||
// grow-out-of-the-icon morph included. The anchor is resolved HERE, once,
|
||||
// so the icon half and the window half can never disagree about which
|
||||
// tile this launch came from: the app's own tile when it already has one
|
||||
// on screen (minimize will fly the window back to it, so that is where
|
||||
// opening should come from), and otherwise the + tile the user clicked —
|
||||
// which is the whole point for an app they don't have yet, since
|
||||
// UIWindow's by-app-name lookup can't find a tile that doesn't exist.
|
||||
const openApp = appName => {
|
||||
if ( focusExistingAppWindow(appName) ) return;
|
||||
launch_app({ name: appName, maximized: true }).catch(err => {
|
||||
console.error(`Failed to launch ${appName}:`, err);
|
||||
UIAlert(i18n('something_went_wrong'));
|
||||
});
|
||||
const tile = dashboard_tile_in_view(appName)
|
||||
|| $el_window.find('.myapps-add-tile')[0]
|
||||
|| null;
|
||||
begin_dashboard_tile_launch(tile);
|
||||
launch_app({
|
||||
name: appName,
|
||||
maximized: true,
|
||||
window_options: {
|
||||
morph_from_dashboard_tile: true,
|
||||
dashboard_tile_el: tile,
|
||||
},
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(`Failed to launch ${appName}:`, err);
|
||||
UIAlert(i18n('something_went_wrong'));
|
||||
})
|
||||
.finally(() => settle_dashboard_tile_launch(tile));
|
||||
};
|
||||
|
||||
$overlay.on('click', '.myapps-add-option', function () {
|
||||
|
||||
+29
-13
@@ -716,7 +716,14 @@ async function UIWindow (options) {
|
||||
if ( options.morph_from_dashboard_tile && window.animate_window_opening
|
||||
&& options.is_visible && ! options.fadeIn
|
||||
&& ! (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) ) {
|
||||
const tile = dashboard_tile_in_view(options.app);
|
||||
// A named tile wins over the by-app-name lookup: the launcher knows
|
||||
// which icon the user actually clicked, and it is the icon whose
|
||||
// click feedback has already played. The grid's add-an-app tile uses
|
||||
// this to open apps that have no tile of their own — an app you
|
||||
// don't have yet can't be found by name (see showAddAppModal).
|
||||
const tile = (options.dashboard_tile_el && dashboard_tile_visible(options.dashboard_tile_el))
|
||||
? options.dashboard_tile_el
|
||||
: dashboard_tile_in_view(options.app);
|
||||
if ( tile ) {
|
||||
// .window is display:none from the stylesheet until the show()
|
||||
// further down; the morph needs the window laid out to measure
|
||||
@@ -4154,23 +4161,14 @@ $.fn.focusWindow = function (event) {
|
||||
* the user will actually look for the app (and where opening it from will put
|
||||
* it back). See buildGroupTileHtml for the data-group-apps this reads.
|
||||
*/
|
||||
function dashboard_tile_in_view (app_name) {
|
||||
export function dashboard_tile_in_view (app_name) {
|
||||
if ( ! app_name || typeof CSS === 'undefined' || ! CSS.escape ) return null;
|
||||
const in_view = tile => {
|
||||
const rect = tile.getBoundingClientRect();
|
||||
if ( rect.width <= 0 || rect.height <= 0 ) return false;
|
||||
const scroller = tile.closest('.myapps-pager-scroller');
|
||||
const clip = (scroller || tile.parentElement).getBoundingClientRect();
|
||||
const cx = rect.left + rect.width / 2;
|
||||
const cy = rect.top + rect.height / 2;
|
||||
return cx >= clip.left && cx <= clip.right && cy >= clip.top && cy <= clip.bottom;
|
||||
};
|
||||
|
||||
const tiles = document.querySelectorAll(
|
||||
`.dashboard-section-apps.active .myapps-tile[data-app-name="${CSS.escape(app_name)}"]`,
|
||||
);
|
||||
for ( const tile of tiles ) {
|
||||
if ( in_view(tile) ) return tile;
|
||||
if ( dashboard_tile_visible(tile) ) return tile;
|
||||
}
|
||||
|
||||
const folders = document.querySelectorAll('.dashboard-section-apps.active .myapps-group-tile');
|
||||
@@ -4181,11 +4179,29 @@ function dashboard_tile_in_view (app_name) {
|
||||
} catch ( _e ) {
|
||||
continue;
|
||||
}
|
||||
if ( Array.isArray(names) && names.includes(app_name) && in_view(folder) ) return folder;
|
||||
if ( Array.isArray(names) && names.includes(app_name) && dashboard_tile_visible(folder) ) return folder;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a tile is rendered AND on the pager page currently in view. The
|
||||
* page test is the point: pages sit side by side in a horizontal scroller,
|
||||
* so an off-page tile has a real rendered box just past the scroller's clip
|
||||
* edge, and a window morphing out of one would grow from nowhere the user
|
||||
* is looking.
|
||||
*/
|
||||
function dashboard_tile_visible (tile) {
|
||||
if ( ! tile || ! tile.getBoundingClientRect ) return false;
|
||||
const rect = tile.getBoundingClientRect();
|
||||
if ( rect.width <= 0 || rect.height <= 0 ) return false;
|
||||
const scroller = tile.closest('.myapps-pager-scroller');
|
||||
const clip = (scroller || tile.parentElement).getBoundingClientRect();
|
||||
const cx = rect.left + rect.width / 2;
|
||||
const cy = rect.top + rect.height / 2;
|
||||
return cx >= clip.left && cx <= clip.right && cy >= clip.top && cy <= clip.bottom;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Dashboard app URL ownership
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user