init code for the trainer app
This commit is contained in:
292
electron/main.cjs
Normal file
292
electron/main.cjs
Normal file
@@ -0,0 +1,292 @@
|
||||
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();
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user