style(notif): unify task notifications + drop the App:/System: prefix
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] <strong>Name</strong> +
"Action task started/completed/failed/cancelled!".
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
This commit is contained in:
parent
3e63dfbdbb
commit
01a125db55
@ -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;
|
||||
|
||||
@ -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(
|
||||
`<strong>App: ${displayName}</strong><br>${headline}`,
|
||||
`<strong>${displayName}</strong><br>${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(
|
||||
`<strong>App: ${displayName}</strong><br>
|
||||
${action.charAt(0).toUpperCase() + action.slice(1)} task started!`,
|
||||
'success',
|
||||
appName,
|
||||
taskUrl,
|
||||
appIcon
|
||||
);
|
||||
}
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update task in the manager
|
||||
*/
|
||||
|
||||
@ -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 ? `<span style="font-size:18px;line-height:1;">${typeIcon}</span>` : 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 = `<strong>${subjectLabel}: ${displayName}</strong><br>${actionTitle} task completed!`;
|
||||
body = `<strong>${displayName}</strong><br>${actionTitle} task completed!`;
|
||||
level = 'success';
|
||||
} else if (task.status === 'failed') {
|
||||
body = `<strong>${subjectLabel}: ${displayName}</strong><br>${actionTitle} task failed.`;
|
||||
body = `<strong>${displayName}</strong><br>${actionTitle} task failed.`;
|
||||
level = 'error';
|
||||
} else if (task.status === 'cancelled') {
|
||||
body = `<strong>${subjectLabel}: ${displayName}</strong><br>${actionTitle} task cancelled.`;
|
||||
body = `<strong>${displayName}</strong><br>${actionTitle} task cancelled.`;
|
||||
level = 'warning';
|
||||
}
|
||||
if (body) window.notificationSystem.show(body, level, appName, url, icon, customIcon);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user