Add Apps/Store tab, template WebUI/URL import, container RO volumes/device passthrough/run-as-user, and in-app confirm dialogs
Replaces every native confirm() with a shared P.confirm() modal (a hung native dialog was found live to block the whole tab, including auto-refresh, and once even double-confirmed an unrelated deletion). Also fixes Edit Container silently resetting to Bridge/blanking the Static IP for any container on a custom network, and a context menu losing its anchor to a mid-read auto-refresh. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -129,9 +129,9 @@ window.Podman = (function () {
|
||||
// --- Toast notifications ----------------------------------------------------
|
||||
//
|
||||
// Replaces alert() for one-way feedback ("Saved.", "Removed 3 image(s)",
|
||||
// "Save failed: ..."). Confirmations stay native confirm() — a toast is
|
||||
// for telling the user something happened, not for asking them a
|
||||
// yes/no question. Command output that can run to hundreds of lines
|
||||
// "Save failed: ..."). Confirmations use confirmModal() below instead —
|
||||
// a toast is for telling the user something happened, not for asking
|
||||
// them a yes/no question. Command output that can run to hundreds of lines
|
||||
// (e.g. `podman compose up`) stays in the existing openLogModal()
|
||||
// pattern instead — a toast has to stay short and auto-dismiss, which
|
||||
// doesn't fit a scrolling log.
|
||||
@@ -180,6 +180,60 @@ window.Podman = (function () {
|
||||
setTimeout(dismiss, TOAST_DURATION_MS[kind]);
|
||||
}
|
||||
|
||||
// --- Confirm dialog ----------------------------------------------------
|
||||
|
||||
/**
|
||||
* In-app replacement for browser-native confirm() — a native confirm()
|
||||
* blocks the entire tab (including this plugin's own ~2s auto-refresh,
|
||||
* and any browser automation driving the page) until dismissed, can't
|
||||
* be styled/themed, and — found live — is easy to lose track of: a
|
||||
* hung dialog blocked screenshots/JS entirely, and a follow-up
|
||||
* keypress meant to dismiss just one of them ended up confirming a
|
||||
* second, unrelated one too. Returns a Promise<boolean> (true =
|
||||
* confirmed) instead of blocking synchronously.
|
||||
*
|
||||
* @param {string} message
|
||||
* @param {object} [opts]
|
||||
* @param {string} [opts.title='Confirm']
|
||||
* @param {string} [opts.confirmLabel='Confirm']
|
||||
* @param {string} [opts.cancelLabel='Cancel']
|
||||
* @param {boolean} [opts.danger=false] solid red confirm button, for
|
||||
* destructive/data-losing actions (delete, remove, format, ...).
|
||||
*/
|
||||
function confirmModal(message, opts) {
|
||||
opts = opts || {};
|
||||
return new Promise(function (resolve) {
|
||||
const backdrop = document.createElement('div');
|
||||
backdrop.className = 'podman-modal-backdrop';
|
||||
backdrop.innerHTML = '' +
|
||||
'<div class="podman-modal" role="alertdialog" aria-modal="true">' +
|
||||
'<div class="podman-modal-head"><h3>' + escapeHtml(opts.title || 'Confirm') + '</h3></div>' +
|
||||
'<div class="podman-modal-body"><p class="podman-confirm-message"></p></div>' +
|
||||
'<div class="podman-modal-actions">' +
|
||||
'<button type="button" class="podman-btn podman-btn-ghost" data-role="cancel">' + escapeHtml(opts.cancelLabel || 'Cancel') + '</button>' +
|
||||
'<button type="button" class="podman-btn ' + (opts.danger ? 'podman-btn-ghost podman-btn-danger' : 'podman-btn-primary') + '" data-role="confirm">' +
|
||||
escapeHtml(opts.confirmLabel || 'Confirm') + '</button>' +
|
||||
'</div></div>';
|
||||
backdrop.querySelector('.podman-confirm-message').textContent = message;
|
||||
|
||||
(document.querySelector('.podman-plugin') || document.body).appendChild(backdrop);
|
||||
backdrop.querySelector('[data-role="confirm"]').focus();
|
||||
|
||||
function settle(result) {
|
||||
backdrop.remove();
|
||||
document.removeEventListener('keydown', onKey);
|
||||
resolve(result);
|
||||
}
|
||||
function onKey(e) {
|
||||
if (e.key === 'Escape') settle(false);
|
||||
}
|
||||
backdrop.querySelector('[data-role="cancel"]').addEventListener('click', function () { settle(false); });
|
||||
backdrop.querySelector('[data-role="confirm"]').addEventListener('click', function () { settle(true); });
|
||||
backdrop.addEventListener('click', function (e) { if (e.target === backdrop) settle(false); });
|
||||
document.addEventListener('keydown', onKey);
|
||||
});
|
||||
}
|
||||
|
||||
// --- Modal form dialog -----------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -480,14 +534,19 @@ window.Podman = (function () {
|
||||
// Only panels that opt in via `autoRefresh: true` (Dashboard, Containers)
|
||||
// get polled — most panels (Settings, Compose, Terminal, ...) have
|
||||
// in-progress forms or connections an unexpected refresh would disrupt.
|
||||
// Paused while the tab is hidden (nothing to look at) and while any
|
||||
// modal is open (a full-panel re-render mid-edit would be jarring),
|
||||
// rather than fighting those cases with more state.
|
||||
// Paused while the tab is hidden (nothing to look at), while any modal
|
||||
// is open (a full-panel re-render mid-edit would be jarring), and while
|
||||
// a context menu is open — found live: a re-render replaces every row's
|
||||
// DOM node wholesale, so a menu opened from a row (its anchor button)
|
||||
// still LOOKS open but is now anchored to a detached element; opening a
|
||||
// submenu from it (e.g. "Move to Folder") then positions itself
|
||||
// relative to that stale anchor instead of anywhere sensible.
|
||||
const AUTO_REFRESH_INTERVAL_MS = 2000;
|
||||
|
||||
function autoRefreshTick() {
|
||||
if (document.hidden) return;
|
||||
if (document.querySelector('.podman-modal-backdrop')) return;
|
||||
if (document.querySelector('.podman-context-menu')) return;
|
||||
|
||||
const activeBtn = document.querySelector('.podman-subnav button.active');
|
||||
if (!activeBtn) return;
|
||||
@@ -512,6 +571,7 @@ window.Podman = (function () {
|
||||
loadingRow: loadingRow,
|
||||
errorRow: errorRow,
|
||||
toast: toast,
|
||||
confirm: confirmModal,
|
||||
openFormModal: openFormModal,
|
||||
openLogModal: openLogModal,
|
||||
openContextMenu: openContextMenu,
|
||||
|
||||
Reference in New Issue
Block a user