From 01a125db5560a39d21b07d535c1e9f33bdc43625 Mon Sep 17 00:00:00 2001 From: librelad Date: Wed, 27 May 2026 01:16:43 +0100 Subject: [PATCH] style(notif): unify task notifications + drop the App:/System: prefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Started, completed, and failed task toasts were rendered by three different code paths producing three different layouts: • task-actions.js executeTask "App: AdGuard\n…task started!" (with type emoji) • task-actions.js executeTaskMonitoring "App: AdGuard\n…task started!" (without type emoji) — dead code • tasks-manager.js createAndExecuteTask "Task created: install adguard" (raw shape) — dead code • tasks-manager.js complete/fail notif "App: AdGuard\n…task completed!" (with type emoji) …plus the system-task path was reading the literal `'system'` slug into the toast: "App: System / Config_update task started!" with a 404'd /icons/apps/system.svg (the same bug renderTaskIcons had on the row itself, fixed in 59ee92b). Three changes: 1. Drop the "App: " / "System: " label prefix on every toast. The bold line is now just the subject name (the row's title still carries the semantic with its leading App-or-LibrePortal icon). Three tasks of the same app no longer read like a column heading repeated. 2. Treat `appName === 'system'` as the LibrePortal sentinel everywhere the toast renders — displayName resolves to "LibrePortal" and the app-icon slot loads /icons/libreportal.svg. Mirrors the row-icon fix in 59ee92b. The completion-path `isSystemTask` check now also accepts `appName === 'system'` in addition to `setup-*` types. 3. Delete the dead code that produced the inconsistent shapes: - executeTaskMonitoring in task-actions.js (no callers anywhere) - window.createAndExecuteTask in tasks-manager.js (no callers; only surviving reference was a stale comment in app-tabbed-manager.js, updated to point at executeTask instead) Net: every task toast in the WebUI now follows the same three-slot layout — [type emoji] [app/LibrePortal logo] Name + "Action task started/completed/failed/cancelled!". Co-Authored-By: Claude Opus 4.7 Signed-off-by: librelad --- .../js/components/app/app-tabbed-manager.js | 2 +- .../js/components/task/task-actions.js | 73 +++---------------- .../js/components/tasks/tasks-manager.js | 58 +++------------ 3 files changed, 23 insertions(+), 110 deletions(-) diff --git a/containers/libreportal/frontend/js/components/app/app-tabbed-manager.js b/containers/libreportal/frontend/js/components/app/app-tabbed-manager.js index a630c98..8cff938 100755 --- a/containers/libreportal/frontend/js/components/app/app-tabbed-manager.js +++ b/containers/libreportal/frontend/js/components/app/app-tabbed-manager.js @@ -774,7 +774,7 @@ class AppTabbedManager { if (this.runningTasks.has(key)) { const existing = this.runningTasks.get(key); - // Same task firing twice — `createAndExecuteTask` dispatches taskCreated + // Same task firing twice — executeTask dispatches taskCreated // synchronously, and the SSE bus also dispatches it when the new task // file shows up. Both events carry the same taskId; treat as a no-op. if (existing && existing.taskId === taskId) return; diff --git a/containers/libreportal/frontend/js/components/task/task-actions.js b/containers/libreportal/frontend/js/components/task/task-actions.js index 31ebe53..c7f03d0 100755 --- a/containers/libreportal/frontend/js/components/task/task-actions.js +++ b/containers/libreportal/frontend/js/components/task/task-actions.js @@ -302,7 +302,15 @@ async configUpdate(changes) { } if (window.notificationSystem) { - const displayName = window.getAppDisplayName ? window.getAppDisplayName(appName) : appName; + // `appName === 'system'` is a category sentinel (config_update, + // system_update — no real app). Resolve it to the LibrePortal + // logo + name so the toast doesn't read "App: System" with a + // broken /icons/apps/system.svg. + const isSystem = appName === 'system'; + const displayName = isSystem + ? 'LibrePortal' + : (window.getAppDisplayName ? window.getAppDisplayName(appName) : appName); + const notifIcon = isSystem ? '/icons/libreportal.svg' : appIcon; const typeIcon = (window.tasksManager && window.tasksManager.getTaskTypeIcon ? window.tasksManager.getTaskTypeIcon({ type: action }) : null)?.icon || ''; @@ -311,11 +319,11 @@ async configUpdate(changes) { ? `${displayLabel} task started!` : `${action.charAt(0).toUpperCase() + action.slice(1)} task started!`; window.notificationSystem.show( - `App: ${displayName}
${headline}`, + `${displayName}
${headline}`, 'success', appName, taskUrl, - appIcon, + notifIcon, customIcon ); } @@ -330,64 +338,7 @@ async configUpdate(changes) { } } - /** - * Execute task monitoring (separated to avoid duplicate task creation) - */ - async executeTaskMonitoring(task, appName, action) { - // Emit task creation event for AppTabbedManager - window.dispatchEvent(new CustomEvent('taskCreated', { - detail: { - taskId: task.id, - appName: appName, - action: action, - timestamp: Date.now() - } - })); - - // console.log(`✅ Task created: ${task.id} for ${action} on ${appName}`); - - // Set up monitoring for this specific task - this.tasksManager.monitorTask(task.id, appName, action); - - // Try to get app data for better notifications - let appData = null; - try { - appData = this.commands.getAppData ? this.commands.getAppData(appName) : null; - } catch (error) { - console.warn('Could not get app data:', error); - } - const appIcon = appData?.icon || `/icons/apps/${task.app}.svg`; - - // Smart URL generation - always include app name - let taskUrl; - const currentUrl = window.location.href; - // console.log('🔍 TaskActions: Current URL:', currentUrl); - - // Always generate URL with app name for proper navigation - if (currentUrl.includes('/app/') && appName) { - // We're on an app page, maintain app context - taskUrl = `/app/${appName}?tab=tasks&task=${task.id}`; - } else { - // We're on main tasks page, use normal URL - taskUrl = `/tasks/all?task=${task.id}`; - } - - // Show success notification with app icon and direct link - if (window.notificationSystem) { - const displayName = window.getAppDisplayName ? window.getAppDisplayName(appName) : appName; - window.notificationSystem.show( - `App: ${displayName}
- ${action.charAt(0).toUpperCase() + action.slice(1)} task started!`, - 'success', - appName, - taskUrl, - appIcon - ); - } - - return task; - } - + /** * Update task in the manager */ diff --git a/containers/libreportal/frontend/js/components/tasks/tasks-manager.js b/containers/libreportal/frontend/js/components/tasks/tasks-manager.js index ee66658..12e7503 100755 --- a/containers/libreportal/frontend/js/components/tasks/tasks-manager.js +++ b/containers/libreportal/frontend/js/components/tasks/tasks-manager.js @@ -1320,48 +1320,6 @@ class TasksManager { } }; - window.createAndExecuteTask = async (action, appName, config = '') => { - try { - // Create task using NEW signature - const task = await this.taskManager.createTask(`libreportal app ${action} ${appName}`, action, appName, config); - - // Emit task creation event for AppTabbedManager - window.dispatchEvent(new CustomEvent('taskCreated', { - detail: { - taskId: task.id, - appName: appName, - action: action, - timestamp: Date.now() - } - })); - - // Set up monitoring for this specific task - this.monitorTask(task.id, appName, action); - - // Show success notification with app icon and direct link - if (window.notificationSystem) { - const taskUrl = `/tasks/all?task=${task.id}`; - const typeIcon = this.getTaskTypeIcon ? this.getTaskTypeIcon({ type: action })?.icon : ''; - const customIcon = typeIcon ? `${typeIcon}` : null; - window.notificationSystem.show( - `Task created: ${action} ${appName}`, - 'info', - appName, - taskUrl, - `/icons/apps/${appName}.svg`, - customIcon - ); - } - - return task; - } catch (error) { - console.error(`❌ Failed to create ${action} task for ${appName}:`, error); - if (window.notificationSystem) { - window.notificationSystem.error(`Failed to start ${action} task for ${appName}: ${error.message}`); - } - throw error; - } - }; } // Single page-wide subscription to the TaskEventBus. This complements @@ -1452,18 +1410,22 @@ class TasksManager { } actionTitle = toolLabel; } - const isSystemTask = action.startsWith('setup-'); + // System-level tasks resolve to LibrePortal as the subject + use the + // LibrePortal logo as the app-icon. Covers `app: 'system'` (config + // update, system update) and `setup-*` types (setup wizard phases). + const isSystemTask = action.startsWith('setup-') || appName === 'system'; const displayName = isSystemTask ? 'LibrePortal' : ((appName && window.getAppDisplayName) ? window.getAppDisplayName(appName) : (appName || (task.command || `Task ${taskId}`))); - const subjectLabel = isSystemTask ? 'System' : 'App'; const onAppPage = window.location.pathname.startsWith('/app') && !window.location.pathname.startsWith('/apps'); const url = (onAppPage && appName) ? `/app/${appName}?tab=tasks&task=${taskId}` : `/tasks/all?task=${taskId}`; - const icon = appName ? `/icons/apps/${appName}.svg` : null; + const icon = isSystemTask + ? '/icons/libreportal.svg' + : (appName ? `/icons/apps/${appName}.svg` : null); // Match the per-action emoji used in the task list rows (see // `getTaskTypeIcon`). Passed as the 6th `customIcon` arg so the @@ -1475,13 +1437,13 @@ class TasksManager { let body; let level; if (task.status === 'completed') { - body = `${subjectLabel}: ${displayName}
${actionTitle} task completed!`; + body = `${displayName}
${actionTitle} task completed!`; level = 'success'; } else if (task.status === 'failed') { - body = `${subjectLabel}: ${displayName}
${actionTitle} task failed.`; + body = `${displayName}
${actionTitle} task failed.`; level = 'error'; } else if (task.status === 'cancelled') { - body = `${subjectLabel}: ${displayName}
${actionTitle} task cancelled.`; + body = `${displayName}
${actionTitle} task cancelled.`; level = 'warning'; } if (body) window.notificationSystem.show(body, level, appName, url, icon, customIcon);