fix: resolve Arch/Wayland tray issues by enabling Ozone Wayland and hardening icon resolution paths

This commit is contained in:
2026-02-21 15:23:57 +01:00
parent 3f4b1abc4b
commit 4c77cb0a56

View File

@@ -16,6 +16,12 @@ const isDev = !app.isPackaged;
app.name = "Ultimate Ban Tracker"; app.name = "Ultimate Ban Tracker";
// Force Wayland/Ozone support if on Linux
if (process.platform === 'linux') {
app.commandLine.appendSwitch('enable-features', 'UseOzonePlatform');
app.commandLine.appendSwitch('ozone-platform', 'wayland');
}
// Load environment variables // Load environment variables
dotenv.config({ path: path.join(app.getAppPath(), '..', '.env') }); dotenv.config({ path: path.join(app.getAppPath(), '..', '.env') });
@@ -89,35 +95,64 @@ const initBackend = () => {
// --- System Tray --- // --- System Tray ---
const createTray = () => { const createTray = () => {
console.log('[Tray] Initializing...');
const possiblePaths = [ const possiblePaths = [
'/usr/share/pixmaps/ultimate-ban-tracker.png', '/usr/share/pixmaps/ultimate-ban-tracker.png', // Priority 1: System installed
path.join(process.resourcesPath, 'assets-build', 'icon.png'), path.join(process.resourcesPath, 'assets-build', 'icon.png'), // Priority 2: Unpacked resources
path.join(app.getAppPath(), 'assets-build', 'icon.png'), path.join(app.getAppPath(), 'assets-build', 'icon.png'), // Priority 3: Internal ASAR (Fallback)
path.join(__dirname, '..', 'assets-build', 'icon.png') path.join(__dirname, '..', 'assets-build', 'icon.png') // Priority 4: Dev
]; ];
let iconPath = ''; let iconPath = '';
for (const p of possiblePaths) { if (p && fs.existsSync(p)) { iconPath = p; break; } } for (const p of possiblePaths) {
console.log(`[Tray] Checking path: ${p}`);
if (p && fs.existsSync(p)) {
iconPath = p;
console.log(`[Tray] Found icon at: ${p}`);
break;
}
}
if (!iconPath) { try { tray = new Tray(nativeImage.createEmpty()); } catch (e) {} return; } if (!iconPath) {
console.warn('[Tray] FAILED: No icon file found on disk. Using empty fallback.');
try { tray = new Tray(nativeImage.createEmpty()); } catch (e) {}
} else {
try {
const icon = nativeImage.createFromPath(iconPath).resize({ width: 16, height: 16 });
tray = new Tray(icon);
console.log('[Tray] Tray object created successfully');
} catch (e: any) {
console.error(`[Tray] Failed to create Tray object: ${e.message}`);
return;
}
}
try { if (tray) {
const icon = nativeImage.createFromPath(iconPath).resize({ width: 16, height: 16 });
tray = new Tray(icon);
tray.setToolTip('Ultimate Ban Tracker'); tray.setToolTip('Ultimate Ban Tracker');
if (process.platform === 'linux') tray.setIgnoreMouseEvents(false); if (process.platform === 'linux') tray.setIgnoreMouseEvents(false);
tray.on('click', () => { if (mainWindow) { mainWindow.show(); mainWindow.focus(); } }); tray.on('click', () => { if (mainWindow) { mainWindow.show(); mainWindow.focus(); } });
// Initial menu build
updateTrayMenu(); updateTrayMenu();
const config = store.get('serverConfig'); const config = store.get('serverConfig');
if (config?.theme) setAppIcon(config.theme); if (config?.theme) setAppIcon(config.theme);
} catch (e: any) { console.error(`[Tray] Error: ${e.message}`); } }
}; };
const updateTrayMenu = () => { const updateTrayMenu = () => {
if (!tray) return; if (!tray) {
console.warn('[Tray] Cannot update menu: Tray is null');
return;
}
const accounts = store.get('accounts') as Account[]; const accounts = store.get('accounts') as Account[];
const config = store.get('serverConfig'); const config = store.get('serverConfig');
const contextMenu = Menu.buildFromTemplate([
console.log(`[Tray] Building menu with ${accounts.length} accounts...`);
const menuTemplate: any[] = [
{ label: `Ultimate Ban Tracker v${app.getVersion()}`, enabled: false }, { label: `Ultimate Ban Tracker v${app.getVersion()}`, enabled: false },
{ type: 'separator' }, { type: 'separator' },
{ {
@@ -125,34 +160,83 @@ const updateTrayMenu = () => {
submenu: accounts.length > 0 ? accounts.map(acc => ({ submenu: accounts.length > 0 ? accounts.map(acc => ({
label: `${acc.personaName} ${acc.loginName ? `(${acc.loginName})` : ''}`, label: `${acc.personaName} ${acc.loginName ? `(${acc.loginName})` : ''}`,
enabled: !!acc.loginName, enabled: !!acc.loginName,
click: () => handleSwitchAccount(acc.loginName) click: () => {
})) : [{ label: 'No accounts found', enabled: false }] console.log(`[Tray] Switching to account: ${acc.loginName}`);
handleSwitchAccount(acc.loginName);
}
})) : [{ label: 'No accounts tracked', enabled: false }]
},
{
label: 'Sync Now',
enabled: !!config?.enabled,
click: () => {
console.log('[Tray] Manual sync requested');
syncAccounts(true);
}
}, },
{ label: 'Sync Now', enabled: !!config?.enabled, click: () => syncAccounts(true) },
{ type: 'separator' }, { type: 'separator' },
{ label: 'Show Dashboard', click: () => { if (mainWindow) { mainWindow.show(); mainWindow.focus(); } } }, {
{ label: 'Quit', click: () => { label: 'Show Dashboard',
(app as any).isQuitting = true; click: () => {
if (tray) tray.destroy(); console.log('[Tray] Showing dashboard');
app.quit(); if (mainWindow) {
} } mainWindow.show();
]); mainWindow.focus();
tray.setContextMenu(contextMenu); }
}
},
{
label: 'Quit',
click: () => {
console.log('[Tray] Quitting application via menu');
(app as any).isQuitting = true;
if (tray) tray.destroy();
app.quit();
}
}
];
try {
const contextMenu = Menu.buildFromTemplate(menuTemplate);
tray.setContextMenu(contextMenu);
console.log('[Tray] Menu updated and attached');
} catch (e: any) {
console.error(`[Tray] Failed to build or set context menu: ${e.message}`);
}
}; };
const setAppIcon = (themeName: string = 'steam') => { const setAppIcon = (themeName: string = 'steam') => {
console.log(`[AppIcon] Setting icon for theme: ${themeName}`);
const possiblePaths = [ const possiblePaths = [
path.join(__dirname, '..', 'assets-build', 'icons', `${themeName}.svg`),
path.join(app.getAppPath(), 'assets-build', 'icons', `${themeName}.svg`), path.join(app.getAppPath(), 'assets-build', 'icons', `${themeName}.svg`),
path.join(__dirname, '..', 'assets-build', 'icons', `${themeName}.svg`),
path.join(process.resourcesPath, 'assets-build', 'icons', `${themeName}.svg`),
'/usr/share/pixmaps/ultimate-ban-tracker.png' '/usr/share/pixmaps/ultimate-ban-tracker.png'
]; ];
let iconPath = ''; let iconPath = '';
for (const p of possiblePaths) { if (fs.existsSync(p)) { iconPath = p; break; } } for (const p of possiblePaths) { if (p && fs.existsSync(p)) { iconPath = p; break; } }
if (!iconPath) return;
const icon = nativeImage.createFromPath(iconPath); if (!iconPath) {
if (tray) tray.setImage(icon.resize({ width: 16, height: 16 })); console.warn(`[AppIcon] No themed icon found for ${themeName}`);
if (mainWindow) mainWindow.setIcon(icon); return;
updateTrayMenu(); }
try {
const icon = nativeImage.createFromPath(iconPath);
if (tray) {
tray.setImage(icon.resize({ width: 16, height: 16 }));
console.log('[AppIcon] Tray icon updated');
}
if (mainWindow) {
mainWindow.setIcon(icon);
console.log('[AppIcon] Window icon updated');
}
// Re-build menu to ensure everything is consistent
updateTrayMenu();
} catch (e: any) {
console.error(`[AppIcon] Failed to apply themed icon: ${e.message}`);
}
}; };
// --- Steam Logic --- // --- Steam Logic ---