ui(tasks): delete notification matches the started/completed format
"Task deleted successfully" was a plain single-line toast while every
other task notification (started, completed, failed, cancelled) renders
as <App>-in-bold on the first line + "<Action> task <verb>" on the
second, with the app icon on the left and the per-action emoji as
custom-icon. Inconsistent.
Now reads e.g.
[🗑️] Ipinfo
Install task deleted.
with the ipinfo logo as the row icon, matching the install/completion
toast format.
Also factored the three duplicate "task → identity (display name + app
icon + friendly action title + emoji)" blocks (taskCompleted listener,
delete-modal title, delete notification) into one helper —
_taskNotificationDescriptor(task) — so the four surfaces (started,
completed/failed/cancelled, delete modal, delete notification) always
agree on what to call a task. Net -20 lines.
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
parent
a1a896e59c
commit
a1315024c5
@ -78,6 +78,38 @@ class TasksManager {
|
||||
return action.split(/[_-]/).map(w => w ? w.charAt(0).toUpperCase() + w.slice(1) : '').join(' ');
|
||||
}
|
||||
|
||||
// Shared "task → notification payload" resolver. Every task surface
|
||||
// (started/completed toast, delete modal, delete confirmation) builds
|
||||
// the same identity (display name, app icon, friendly action title,
|
||||
// emoji type-icon) — keep it in one place so they read consistently.
|
||||
_taskNotificationDescriptor(task) {
|
||||
const appName = (task && task.app) || null;
|
||||
const action = (task && task.type) || '';
|
||||
const isSystemTask = action.startsWith('setup-') || appName === 'system';
|
||||
let actionTitle = this.formatActionTitle(action);
|
||||
// Tool tasks: prefer the catalog-defined label.
|
||||
const toolCmdMatch = ((task && task.command) || '').match(/libreportal app tool (\S+) (\S+)/);
|
||||
if (toolCmdMatch) {
|
||||
const toolId = toolCmdMatch[2];
|
||||
let toolLabel = null;
|
||||
const cat = window.toolsCatalog;
|
||||
if (cat && cat.apps && cat.apps[toolCmdMatch[1]] && Array.isArray(cat.apps[toolCmdMatch[1]].tools)) {
|
||||
const t = cat.apps[toolCmdMatch[1]].tools.find(x => x.id === toolId);
|
||||
if (t && t.label) toolLabel = t.label;
|
||||
}
|
||||
if (!toolLabel) toolLabel = toolId.split(/[_-]/).map(w => w ? w.charAt(0).toUpperCase() + w.slice(1) : '').join(' ');
|
||||
actionTitle = toolLabel;
|
||||
}
|
||||
const displayName = isSystemTask
|
||||
? 'LibrePortal'
|
||||
: ((appName && window.getAppDisplayName) ? window.getAppDisplayName(appName) : (appName || ''));
|
||||
const icon = isSystemTask
|
||||
? '/icons/libreportal.svg'
|
||||
: (appName ? `/icons/apps/${encodeURIComponent(appName)}.svg` : null);
|
||||
const typeIcon = (this.getTaskTypeIcon ? this.getTaskTypeIcon(task)?.icon : '') || '';
|
||||
return { appName, isSystemTask, actionTitle, displayName, icon, typeIcon };
|
||||
}
|
||||
|
||||
// Initialize task system after scripts are loaded
|
||||
initializeTaskSystem() {
|
||||
try {
|
||||
@ -1402,49 +1434,13 @@ class TasksManager {
|
||||
// Layout matches the "<App> task started!" format used by task-actions
|
||||
// and backup-manager so started/completed look like a matched pair.
|
||||
if (window.notificationSystem && task) {
|
||||
const appName = task.app || null;
|
||||
const action = task.type || 'task';
|
||||
let actionTitle = this.formatActionTitle(action);
|
||||
// Tool tasks: override the generic "Tool" label with the tool's
|
||||
// friendly name (e.g. "Manage Shortcuts") so completion toasts
|
||||
// match what the user clicked.
|
||||
const toolCmdMatch = (task.command || '').match(/libreportal app tool (\S+) (\S+)/);
|
||||
if (toolCmdMatch) {
|
||||
const toolApp = toolCmdMatch[1];
|
||||
const toolId = toolCmdMatch[2];
|
||||
let toolLabel = null;
|
||||
const cat = window.toolsCatalog;
|
||||
if (cat && cat.apps && cat.apps[toolApp] && Array.isArray(cat.apps[toolApp].tools)) {
|
||||
const t = cat.apps[toolApp].tools.find(x => x.id === toolId);
|
||||
if (t && t.label) toolLabel = t.label;
|
||||
}
|
||||
if (!toolLabel) {
|
||||
toolLabel = toolId.split('_').map(w => w ? w.charAt(0).toUpperCase() + w.slice(1) : '').join(' ');
|
||||
}
|
||||
actionTitle = toolLabel;
|
||||
}
|
||||
// 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 { appName, actionTitle, displayName, icon, typeIcon } = this._taskNotificationDescriptor(task);
|
||||
const onAppPage = window.location.pathname.startsWith('/app') && !window.location.pathname.startsWith('/apps');
|
||||
const url = (onAppPage && appName)
|
||||
? window.appPath(appName, 'tasks', null, taskId)
|
||||
: `/tasks/all?task=${taskId}`;
|
||||
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
|
||||
// notification's leftmost icon slot shows the task *type* (install
|
||||
// ✅, backup 💾, restore 📦, …) instead of the generic level tick.
|
||||
const typeIcon = (this.getTaskTypeIcon ? this.getTaskTypeIcon(task) : null)?.icon || '';
|
||||
// Per-action emoji (install ✅, backup 💾, restore 📦, …) in the
|
||||
// notification's leftmost icon slot, mirroring task-list rows.
|
||||
const customIcon = typeIcon ? `<span style="font-size:18px;line-height:1;">${typeIcon}</span>` : null;
|
||||
|
||||
let body;
|
||||
@ -2196,11 +2192,16 @@ class TasksManager {
|
||||
this.generateAppCategories();
|
||||
|
||||
if (window.notificationSystem) {
|
||||
// Type icon from whatever the task was, with a trash fallback
|
||||
// because deletion is conceptually a 🗑️ action.
|
||||
const typeIcon = (task && this.getTaskTypeIcon ? this.getTaskTypeIcon(task)?.icon : '') || '🗑️';
|
||||
const customIcon = `<span style="font-size:18px;line-height:1;">${typeIcon}</span>`;
|
||||
window.notificationSystem.show('Task deleted successfully', 'info', null, null, null, customIcon);
|
||||
// Match the "<App> task <verb>" two-line shape used by started/
|
||||
// completed/failed/cancelled so deletion reads as part of the same
|
||||
// family. Type emoji falls back to 🗑️ since deletion is a 🗑️ action.
|
||||
const { appName, actionTitle, displayName, icon, typeIcon } = this._taskNotificationDescriptor(task);
|
||||
const emoji = typeIcon || '🗑️';
|
||||
const customIcon = `<span style="font-size:18px;line-height:1;">${emoji}</span>`;
|
||||
const body = displayName
|
||||
? `<strong>${displayName}</strong><br>${actionTitle} task deleted.`
|
||||
: `${actionTitle || 'Task'} deleted.`;
|
||||
window.notificationSystem.show(body, 'info', appName, null, icon, customIcon);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting task:', error);
|
||||
@ -2219,36 +2220,15 @@ class TasksManager {
|
||||
const escHtml = (s) => String(s == null ? '' : s)
|
||||
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
||||
|
||||
// Build a friendly title + app icon so the modal reads "Install Ipinfo"
|
||||
// with the app's logo, not the raw "libreportal app install ipinfo"
|
||||
// command. Mirrors the completion-notification flow above so the
|
||||
// visual identity of a task is consistent across surfaces.
|
||||
const appName = (task && task.app) || null;
|
||||
const action = (task && task.type) || '';
|
||||
const isSystemTask = action.startsWith('setup-') || appName === 'system';
|
||||
let actionTitle = this.formatActionTitle(action);
|
||||
// Tool tasks: prefer the tool's curated label.
|
||||
const toolCmdMatch = (task && task.command || '').match(/libreportal app tool (\S+) (\S+)/);
|
||||
if (toolCmdMatch) {
|
||||
const toolId = toolCmdMatch[2];
|
||||
let toolLabel = null;
|
||||
const cat = window.toolsCatalog;
|
||||
if (cat && cat.apps && cat.apps[toolCmdMatch[1]] && Array.isArray(cat.apps[toolCmdMatch[1]].tools)) {
|
||||
const t = cat.apps[toolCmdMatch[1]].tools.find(x => x.id === toolId);
|
||||
if (t && t.label) toolLabel = t.label;
|
||||
}
|
||||
if (!toolLabel) toolLabel = toolId.split(/[_-]/).map(w => w ? w.charAt(0).toUpperCase() + w.slice(1) : '').join(' ');
|
||||
actionTitle = toolLabel;
|
||||
}
|
||||
const displayName = isSystemTask
|
||||
? ''
|
||||
: ((appName && window.getAppDisplayName) ? window.getAppDisplayName(appName) : (appName || ''));
|
||||
const taskLabel = displayName
|
||||
? `${actionTitle} ${displayName}`
|
||||
// Friendly title + app icon — mirrors the completion-toast format so
|
||||
// the modal's identity matches every other surface.
|
||||
const { isSystemTask, actionTitle, displayName, icon: taskIcon } = this._taskNotificationDescriptor(task);
|
||||
// For the modal we omit the "LibrePortal" subject (the eyebrow already
|
||||
// says "Delete Task") and just lead with the action: "Update Config".
|
||||
const modalSubject = isSystemTask ? '' : displayName;
|
||||
const taskLabel = modalSubject
|
||||
? `${actionTitle} ${modalSubject}`
|
||||
: (actionTitle || (task && task.id) || 'Unknown task');
|
||||
const taskIcon = isSystemTask
|
||||
? '/icons/libreportal.svg'
|
||||
: (appName ? `/icons/apps/${encodeURIComponent(appName)}.svg` : null);
|
||||
const taskStatus = (task && task.status) || 'unknown';
|
||||
|
||||
const warningTitle = isActive ? 'Active task' : 'This cannot be undone';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user