293 lines
7.2 KiB
JavaScript
293 lines
7.2 KiB
JavaScript
const { app, BrowserWindow, protocol, ipcMain } = require('electron');
|
|
const path = require('path');
|
|
|
|
app.commandLine.appendSwitch('enable-experimental-web-platform-features');
|
|
app.commandLine.appendSwitch('enable-web-bluetooth');
|
|
app.commandLine.appendSwitch('enable-features', 'WebBluetooth');
|
|
|
|
if (process.platform === 'linux') {
|
|
app.commandLine.appendSwitch('disable-gpu-sandbox');
|
|
app.commandLine.appendSwitch('no-sandbox');
|
|
app.commandLine.appendSwitch('disable-seccomp-filter-sandbox');
|
|
app.commandLine.appendSwitch('enable-logging');
|
|
app.commandLine.appendSwitch('v', '1');
|
|
}
|
|
|
|
const isDev = process.env.ELECTRON_IS_DEV === '1';
|
|
|
|
let mainWindow;
|
|
let deepLinkUrl = null;
|
|
let bluetoothSelectCallback = null;
|
|
let discoveredDevices = new Map();
|
|
|
|
const gotTheLock = app.requestSingleInstanceLock();
|
|
|
|
if (!gotTheLock) {
|
|
app.quit();
|
|
} else {
|
|
app.on('second-instance', (event, commandLine) => {
|
|
if (mainWindow) {
|
|
if (mainWindow.isMinimized()) mainWindow.restore();
|
|
mainWindow.focus();
|
|
|
|
const url = commandLine.find(arg => arg.startsWith('rideaware://'));
|
|
if (url) {
|
|
handleDeepLink(url);
|
|
}
|
|
}
|
|
});
|
|
|
|
app.whenReady().then(() => {
|
|
if (!isDev) {
|
|
protocol.registerFileProtocol('rideaware', (request, callback) => {
|
|
callback({ path: '' });
|
|
});
|
|
}
|
|
|
|
createWindow();
|
|
|
|
app.on('open-url', (event, url) => {
|
|
event.preventDefault();
|
|
handleDeepLink(url);
|
|
});
|
|
|
|
if (deepLinkUrl) {
|
|
handleDeepLink(deepLinkUrl);
|
|
deepLinkUrl = null;
|
|
}
|
|
});
|
|
}
|
|
|
|
app.on('open-url', (event, url) => {
|
|
event.preventDefault();
|
|
if (mainWindow) {
|
|
handleDeepLink(url);
|
|
} else {
|
|
deepLinkUrl = url;
|
|
}
|
|
});
|
|
|
|
if (process.platform !== 'darwin') {
|
|
const url = process.argv.find(arg => arg.startsWith('rideaware://'));
|
|
if (url) {
|
|
deepLinkUrl = url;
|
|
}
|
|
}
|
|
|
|
function createWindow() {
|
|
mainWindow = new BrowserWindow({
|
|
width: 1200,
|
|
height: 900,
|
|
minWidth: 800,
|
|
minHeight: 600,
|
|
webPreferences: {
|
|
preload: path.join(__dirname, 'preload.cjs'),
|
|
nodeIntegration: false,
|
|
contextIsolation: true,
|
|
enableBlinkFeatures: 'WebBluetooth',
|
|
webSecurity: true,
|
|
sandbox: process.platform !== 'linux',
|
|
},
|
|
backgroundColor: '#0a0a0a',
|
|
show: false,
|
|
});
|
|
|
|
mainWindow.webContents.session.setPermissionCheckHandler((webContents, permission) => {
|
|
if (permission === 'bluetooth') {
|
|
return true;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
mainWindow.webContents.session.setPermissionRequestHandler((webContents, permission, callback) => {
|
|
if (permission === 'bluetooth') {
|
|
callback(true);
|
|
} else {
|
|
callback(false);
|
|
}
|
|
});
|
|
|
|
mainWindow.once('ready-to-show', () => {
|
|
mainWindow.show();
|
|
});
|
|
|
|
if (isDev) {
|
|
mainWindow.loadURL('http://localhost:5173');
|
|
mainWindow.webContents.openDevTools();
|
|
} else {
|
|
mainWindow.loadFile(path.join(__dirname, '../dist/index.html'));
|
|
}
|
|
|
|
mainWindow.on('closed', () => {
|
|
mainWindow = null;
|
|
});
|
|
|
|
mainWindow.webContents.setWindowOpenHandler(({ url }) => {
|
|
require('electron').shell.openExternal(url);
|
|
return { action: 'deny' };
|
|
});
|
|
|
|
mainWindow.webContents.on('select-bluetooth-device', (event, deviceList, callback) => {
|
|
event.preventDefault();
|
|
|
|
bluetoothSelectCallback = callback;
|
|
|
|
deviceList.forEach((device) => {
|
|
discoveredDevices.set(device.deviceId, {
|
|
id: device.deviceId,
|
|
name: device.deviceName || 'Unknown Device',
|
|
});
|
|
});
|
|
|
|
const devices = Array.from(discoveredDevices.values());
|
|
mainWindow.webContents.send('bluetooth:devices-updated', devices);
|
|
});
|
|
}
|
|
|
|
ipcMain.on('bluetooth:select-device', (event, deviceId) => {
|
|
if (bluetoothSelectCallback) {
|
|
bluetoothSelectCallback(deviceId);
|
|
bluetoothSelectCallback = null;
|
|
}
|
|
});
|
|
|
|
ipcMain.on('bluetooth:cancel-selection', () => {
|
|
if (bluetoothSelectCallback) {
|
|
bluetoothSelectCallback('');
|
|
bluetoothSelectCallback = null;
|
|
}
|
|
discoveredDevices.clear();
|
|
});
|
|
|
|
ipcMain.on('bluetooth:clear-devices', () => {
|
|
discoveredDevices.clear();
|
|
});
|
|
|
|
function handleDeepLink(urlString) {
|
|
if (!mainWindow) return;
|
|
|
|
try {
|
|
const parsedUrl = new URL(urlString);
|
|
|
|
if (parsedUrl.protocol === 'rideaware:') {
|
|
const action = parsedUrl.hostname;
|
|
const params = {};
|
|
|
|
parsedUrl.searchParams.forEach((value, key) => {
|
|
params[key] = value;
|
|
});
|
|
|
|
mainWindow.webContents.send('deep-link', {
|
|
action,
|
|
params,
|
|
});
|
|
|
|
if (mainWindow.isMinimized()) mainWindow.restore();
|
|
mainWindow.focus();
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to parse deep link:', error);
|
|
}
|
|
}
|
|
|
|
ipcMain.handle('get-version', () => {
|
|
return app.getVersion();
|
|
});
|
|
|
|
ipcMain.handle('get-platform', () => {
|
|
return process.platform;
|
|
});
|
|
|
|
ipcMain.handle('bluetooth:check-availability', async () => {
|
|
return {
|
|
available: true,
|
|
platform: process.platform,
|
|
flags: {
|
|
webBluetooth: app.commandLine.hasSwitch('enable-web-bluetooth'),
|
|
experimentalFeatures: app.commandLine.hasSwitch('enable-experimental-web-platform-features'),
|
|
}
|
|
};
|
|
});
|
|
|
|
ipcMain.handle('bluetooth:check-system', async () => {
|
|
const { exec } = require('child_process');
|
|
const { promisify } = require('util');
|
|
const execAsync = promisify(exec);
|
|
|
|
const result = {
|
|
platform: process.platform,
|
|
available: false,
|
|
adapter: null,
|
|
error: null,
|
|
guidance: []
|
|
};
|
|
|
|
try {
|
|
if (process.platform === 'linux') {
|
|
try {
|
|
const { stdout } = await execAsync('systemctl is-active bluetooth');
|
|
result.available = stdout.trim() === 'active';
|
|
|
|
if (result.available) {
|
|
try {
|
|
const { stdout: hciOutput } = await execAsync('hciconfig 2>&1');
|
|
result.adapter = hciOutput.includes('UP RUNNING') ? 'enabled' : 'disabled';
|
|
|
|
if (!hciOutput.includes('UP RUNNING')) {
|
|
result.guidance.push('Bluetooth adapter is not powered on. Try: sudo hciconfig hci0 up');
|
|
}
|
|
} catch {
|
|
result.adapter = 'unknown';
|
|
result.guidance.push('Unable to check Bluetooth adapter status. Install bluez-utils if not present.');
|
|
}
|
|
} else {
|
|
result.guidance.push('Bluetooth service is not running. Start it with: sudo systemctl start bluetooth');
|
|
}
|
|
} catch {
|
|
result.available = false;
|
|
result.guidance.push('Bluetooth service not found. Install bluez package.');
|
|
}
|
|
} else if (process.platform === 'darwin') {
|
|
result.available = true;
|
|
result.adapter = 'system';
|
|
} else if (process.platform === 'win32') {
|
|
result.available = true;
|
|
result.adapter = 'system';
|
|
}
|
|
} catch (error) {
|
|
result.error = error.message;
|
|
}
|
|
|
|
return result;
|
|
});
|
|
|
|
ipcMain.handle('minimize-window', () => {
|
|
if (mainWindow) mainWindow.minimize();
|
|
});
|
|
|
|
ipcMain.handle('maximize-window', () => {
|
|
if (mainWindow) {
|
|
if (mainWindow.isMaximized()) {
|
|
mainWindow.unmaximize();
|
|
} else {
|
|
mainWindow.maximize();
|
|
}
|
|
}
|
|
});
|
|
|
|
ipcMain.handle('close-window', () => {
|
|
if (mainWindow) mainWindow.close();
|
|
});
|
|
|
|
app.on('window-all-closed', () => {
|
|
if (process.platform !== 'darwin') {
|
|
app.quit();
|
|
}
|
|
});
|
|
|
|
app.on('activate', () => {
|
|
if (BrowserWindow.getAllWindows().length === 0) {
|
|
createWindow();
|
|
}
|
|
});
|