const { app, BrowserWindow, session, desktopCapturer, ipcMain, clipboard } = require('electron'); const { autoUpdater } = require('electron-updater'); const path = require('path'); const url = require('url'); function createWindow() { // Create a persistent session for chattz to keep the user logged in const sess = session.fromPartition('persist:chattz'); const win = new BrowserWindow({ width: 1200, height: 800, title: "Chattz Desktop", webPreferences: { nodeIntegration: false, contextIsolation: true, webSecurity: false, // Required for cross-origin fetch/ws from file:// with cookies session: sess, preload: path.join(__dirname, 'desktop', 'preload.js') } }); win.setAutoHideMenuBar(true); win.setMenuBarVisibility(false); ipcMain.handle('get-config', () => { return { backendUrl: (process.env.CHATTZ_URL || 'https://discord.flegr.me').replace(/\/$/, '') }; }); ipcMain.handle('clipboard-write', (event, text) => { clipboard.writeText(text); return true; }); // Auto-approve media permissions (camera, microphone) sess.setPermissionCheckHandler((webContents, permission) => { if (permission === 'media' || permission === 'clipboard-read' || permission === 'clipboard-write') { return true; } return false; }); sess.setPermissionRequestHandler((webContents, permission, callback) => { if (permission === 'media' || permission === 'clipboard-read' || permission === 'clipboard-write') { callback(true); } else { callback(false); } }); // Handle screen share requests sess.setDisplayMediaRequestHandler((request, callback) => { desktopCapturer.getSources({ types: ['screen', 'window'] }).then((sources) => { // Provide the first screen source by default, or implement a picker window here if (sources && sources.length > 0) { // We prefer a screen over a window if available, simple heuristic const screenSource = sources.find(s => s.id.startsWith('screen')) || sources[0]; callback({ video: screenSource, audio: 'loopback' }); } else { callback(null); // Reject safely } }).catch(err => { console.error("Failed to get desktop sources for screen share", err); callback(null); }); }); const backendUrl = (process.env.CHATTZ_URL || 'https://discord.flegr.me').replace(/\/$/, ''); const indexPath = path.join(__dirname, 'desktop', 'index.html'); const loadDesktopApp = (queryParams = '') => { const options = {}; if (queryParams) { try { options.query = Object.fromEntries(new URLSearchParams(queryParams)); } catch (e) { console.error("Failed to parse query params", e); } } // Use setImmediate to ensure the current navigation tick is cleared, // which prevents ERR_ABORTED (-3) on some platforms when interrupting a redirect. setImmediate(() => { if (win.isDestroyed()) return; win.loadFile(indexPath, options).catch((err) => { // Ignore aborted errors as they often happen during fast redirects if (err.toString().includes('-3') || err.code === -3) return; console.error(`Failed to load desktop file: ${err}`); }); }); }; // Use a navigation listener to detect when the remote login is complete win.webContents.on('will-navigate', (event, navigatedUrl) => { try { const urlObj = new URL(navigatedUrl); const backendObj = new URL(backendUrl); // Detect the redirect back to the home page with a token if (urlObj.origin === backendObj.origin && urlObj.pathname === '/') { if (urlObj.searchParams.has('token')) { console.log("Detected login success redirect, returning to desktop UI..."); event.preventDefault(); loadDesktopApp(urlObj.search.slice(1)); } } } catch (e) { console.error(e); } }); loadDesktopApp(); // Uncomment to debug // win.webContents.openDevTools(); } app.commandLine.appendSwitch('disable-webrtc-hw-encoding'); // Sometime helps resolve codec mismatch behavior app.commandLine.appendSwitch('log-level', '3'); // Silences standard Chromium warning logs (like SRTP transport unprotect logs which are benign timing issues) app.whenReady().then(() => { createWindow(); // Configure Auto-Updater autoUpdater.autoDownload = false; autoUpdater.logger = console; autoUpdater.on('update-available', (info) => { const wins = BrowserWindow.getAllWindows(); if (wins.length > 0) wins[0].webContents.send('update-available', info); }); autoUpdater.on('update-downloaded', (info) => { const wins = BrowserWindow.getAllWindows(); if (wins.length > 0) wins[0].webContents.send('update-downloaded', info); }); autoUpdater.on('error', (err) => { const wins = BrowserWindow.getAllWindows(); if (wins.length > 0) wins[0].webContents.send('update-error', err.message); }); ipcMain.on('check-for-updates', () => { autoUpdater.checkForUpdatesAndNotify().catch(err => { console.error("Manual update check failed", err); }); }); ipcMain.on('download-update', () => { autoUpdater.downloadUpdate(); }); ipcMain.on('quit-and-install', () => { autoUpdater.quitAndInstall(); }); // Check once on startup setTimeout(() => { autoUpdater.checkForUpdatesAndNotify().catch(() => { }); }, 5000); app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) { createWindow(); } }); }); app.on('window-all-closed', () => { if (process.platform !== 'darwin') { app.quit(); } });