Browse Source

Add single-instance enforcement to Electron app

- Implement app.requestSingleInstanceLock() to prevent multiple instances
- Add user-friendly dialog when second instance is attempted
- Focus and restore existing window instead of creating new instance
- Prevent database corruption and resource conflicts
- Update documentation with single-instance behavior details
web-serve-fix
Matthew Raymer 2 weeks ago
parent
commit
3ba33cea55
  1. 18
      BUILDING.md
  2. 19
      electron/README-BUILDING.md
  3. 37
      electron/src/index.ts

18
BUILDING.md

@ -246,6 +246,24 @@ npm run build:electron:deb:prod
npm run build:electron:dmg:prod
```
### Single Instance Enforcement
The Electron app enforces single-instance operation to prevent:
- Database corruption from multiple instances
- Resource conflicts and performance issues
- User confusion from multiple windows
**Behavior:**
- Only one instance can run at a time
- Attempting to launch a second instance shows a user-friendly dialog
- The existing window is focused and restored if minimized
- Second instance exits gracefully
**Implementation:**
- Uses Electron's `requestSingleInstanceLock()` API
- Handles `second-instance` events to focus existing window
- Shows informative dialog explaining why only one instance is allowed
#### Direct Script Usage
```bash

19
electron/README-BUILDING.md

@ -117,6 +117,25 @@ dist/
└── linux-unpacked/ # Unpacked directory
```
## Features
### Single Instance Enforcement
TimeSafari Electron enforces single-instance operation to prevent:
- Database corruption from multiple instances
- Resource conflicts and performance issues
- User confusion from multiple windows
**Behavior:**
- Only one instance can run at a time
- Attempting to launch a second instance shows a user-friendly dialog
- The existing window is focused and restored if minimized
- Second instance exits gracefully
**Implementation:**
- Uses Electron's `requestSingleInstanceLock()` API
- Handles `second-instance` events to focus existing window
- Shows informative dialog explaining why only one instance is allowed
## Configuration
### App Metadata

37
electron/src/index.ts

@ -1,7 +1,7 @@
import type { CapacitorElectronConfig } from '@capacitor-community/electron';
import { getCapacitorElectronConfig, setupElectronDeepLinking } from '@capacitor-community/electron';
import type { MenuItemConstructorOptions } from 'electron';
import { app, MenuItem, ipcMain } from 'electron';
import { app, MenuItem, ipcMain, dialog } from 'electron';
import electronIsDev from 'electron-is-dev';
import unhandled from 'electron-unhandled';
import { autoUpdater } from 'electron-updater';
@ -10,6 +10,27 @@ import { join } from 'path';
import { ElectronCapacitorApp, setupContentSecurityPolicy, setupReloadWatcher } from './setup';
// Single instance enforcement
const gotTheLock = app.requestSingleInstanceLock();
if (!gotTheLock) {
console.log('[Electron] Another instance is already running. Exiting...');
// Show user-friendly dialog before quitting
dialog.showMessageBox({
type: 'info',
title: 'TimeSafari Already Running',
message: 'TimeSafari is already running in another window.',
detail: 'Please use the existing TimeSafari window instead of opening a new one.',
buttons: ['OK']
}).then(() => {
app.quit();
});
// Exit immediately
process.exit(0);
}
// Graceful handling of unhandled errors.
unhandled({
logger: (error) => {
@ -79,6 +100,20 @@ autoUpdater.on('error', (error) => {
// Initialize our app, build windows, and load content.
await myCapacitorApp.init();
// Handle second instance launch (focus existing window)
app.on('second-instance', (event, commandLine, workingDirectory) => {
console.log('[Electron] Second instance attempted to launch');
// Someone tried to run a second instance, we should focus our window instead
const mainWindow = myCapacitorApp.getMainWindow();
if (mainWindow) {
if (mainWindow.isMinimized()) {
mainWindow.restore();
}
mainWindow.focus();
}
});
// Only check for updates in production builds, not in development or AppImage
if (!electronIsDev && !process.env.APPIMAGE) {
try {

Loading…
Cancel
Save