forked from trent_larson/crowd-funder-for-time-pwa
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
This commit is contained in:
18
BUILDING.md
18
BUILDING.md
@@ -246,6 +246,24 @@ npm run build:electron:deb:prod
|
|||||||
npm run build:electron:dmg: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
|
#### Direct Script Usage
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -117,6 +117,25 @@ dist/
|
|||||||
└── linux-unpacked/ # Unpacked directory
|
└── 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
|
## Configuration
|
||||||
|
|
||||||
### App Metadata
|
### App Metadata
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { CapacitorElectronConfig } from '@capacitor-community/electron';
|
import type { CapacitorElectronConfig } from '@capacitor-community/electron';
|
||||||
import { getCapacitorElectronConfig, setupElectronDeepLinking } from '@capacitor-community/electron';
|
import { getCapacitorElectronConfig, setupElectronDeepLinking } from '@capacitor-community/electron';
|
||||||
import type { MenuItemConstructorOptions } from '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 electronIsDev from 'electron-is-dev';
|
||||||
import unhandled from 'electron-unhandled';
|
import unhandled from 'electron-unhandled';
|
||||||
import { autoUpdater } from 'electron-updater';
|
import { autoUpdater } from 'electron-updater';
|
||||||
@@ -10,6 +10,27 @@ import { join } from 'path';
|
|||||||
|
|
||||||
import { ElectronCapacitorApp, setupContentSecurityPolicy, setupReloadWatcher } from './setup';
|
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.
|
// Graceful handling of unhandled errors.
|
||||||
unhandled({
|
unhandled({
|
||||||
logger: (error) => {
|
logger: (error) => {
|
||||||
@@ -79,6 +100,20 @@ autoUpdater.on('error', (error) => {
|
|||||||
// Initialize our app, build windows, and load content.
|
// Initialize our app, build windows, and load content.
|
||||||
await myCapacitorApp.init();
|
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
|
// Only check for updates in production builds, not in development or AppImage
|
||||||
if (!electronIsDev && !process.env.APPIMAGE) {
|
if (!electronIsDev && !process.env.APPIMAGE) {
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user