From c4e57776ca39687e197dc030318afcb3e18f87be Mon Sep 17 00:00:00 2001 From: Ishan Karmakar Date: Mon, 1 Jun 2026 14:07:08 -0500 Subject: [PATCH 1/5] feat: Add Quit item to context menu --- src/main.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main.ts b/src/main.ts index c270a22..e4ee6ac 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,4 @@ -import { app, BrowserWindow, ipcMain, nativeImage, shell, Tray } from 'electron'; +import { app, BrowserWindow, ipcMain, Menu, nativeImage, shell, Tray } from 'electron'; import path from 'node:path'; import started from 'electron-squirrel-startup'; import { APP_NAME, DEFAULT_STATUS, GATEWAY_HOST, MANAGEMENT_URL, POLL_INTERVAL_MS, WINDOW_HEIGHT, WINDOW_WIDTH } from './config'; @@ -187,6 +187,10 @@ function showWindow() { function createTray() { const icon = nativeImage.createFromPath(resourcePath('VynteIcon.png')).resize({ width: 18, height: 18 }); tray = new Tray(icon); + const contextMenu = Menu.buildFromTemplate([ + { role: 'quit' } + ]); + tray.setContextMenu(contextMenu); tray.setToolTip(APP_NAME); tray.on('click', showWindow); } From 2ed1128607d8d609e6bfd76eb2c3b2e52a26df84 Mon Sep 17 00:00:00 2001 From: Ishan Karmakar Date: Mon, 1 Jun 2026 14:14:45 -0500 Subject: [PATCH 2/5] feat: Add connect, disconnect, and logout to context menu --- src/main.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main.ts b/src/main.ts index e4ee6ac..aef77f4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -188,6 +188,18 @@ function createTray() { const icon = nativeImage.createFromPath(resourcePath('VynteIcon.png')).resize({ width: 18, height: 18 }); tray = new Tray(icon); const contextMenu = Menu.buildFromTemplate([ + { + label: 'Connect', + click: connect, + }, + { + label: 'Disconnect', + click: disconnect, + }, + { + label: 'Logout', + click: logout + }, { role: 'quit' } ]); tray.setContextMenu(contextMenu); From a7d40d293cd1c62292b5a6a4cf64b8dfa500f775 Mon Sep 17 00:00:00 2001 From: Ishan Karmakar Date: Mon, 1 Jun 2026 14:15:47 -0500 Subject: [PATCH 3/5] feat: Disconnect on app quit --- src/main.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.ts b/src/main.ts index aef77f4..49dd365 100644 --- a/src/main.ts +++ b/src/main.ts @@ -24,6 +24,8 @@ app.on('window-all-closed', () => { } }); +app.on('before-quit', disconnect); + app.on('activate', () => { // On OS X it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. From 00dca3ba9b547e5813a16010f2815c2ba4db7b5e Mon Sep 17 00:00:00 2001 From: Ishan Karmakar Date: Mon, 1 Jun 2026 14:50:12 -0500 Subject: [PATCH 4/5] feat: Use single instance lock to avoid multiple instances of Vynte Connect --- src/main.ts | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/main.ts b/src/main.ts index 49dd365..6934f6c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -10,10 +10,29 @@ if (started) { app.quit(); } -// This method will be called when Electron has finished -// initialization and is ready to create browser windows. -// Some APIs can only be used after this event occurs. -app.on('ready', createWindow); +const singleInstanceLock = app.requestSingleInstanceLock() + +if (!singleInstanceLock) app.quit(); +else { + app.on('second-instance', () => { + if (window) { + if (window.isMinimized()) window.restore(); + window.focus(); + } + }); + app.on('before-quit', disconnect); +} + +app.whenReady().then(() => { + createWindow(); + createTray(); + refreshStatus(); + pollTimer = setInterval(refreshStatus, POLL_INTERVAL_MS); +}); + +app.on('before-quit', () => { + if (pollTimer) clearInterval(pollTimer); +}); // Quit when all windows are closed, except on macOS. There, it's common // for applications and their menu bar to stay active until the user quits @@ -24,8 +43,6 @@ app.on('window-all-closed', () => { } }); -app.on('before-quit', disconnect); - app.on('activate', () => { // On OS X it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. @@ -209,17 +226,6 @@ function createTray() { tray.on('click', showWindow); } -app.whenReady().then(() => { - createWindow(); - createTray(); - refreshStatus(); - pollTimer = setInterval(refreshStatus, POLL_INTERVAL_MS); -}); - -app.on('before-quit', () => { - if (pollTimer) clearInterval(pollTimer); -}); - ipcMain.handle('status:get', () => withAutoConnect(cachedStatus)); ipcMain.handle('status:refresh', refreshStatus); ipcMain.handle('vpn:connect', connect); From 16c2dcde41f11f5a492c5b7cce1a99530c3e3d5a Mon Sep 17 00:00:00 2001 From: Ishan Karmakar Date: Mon, 1 Jun 2026 17:49:04 -0500 Subject: [PATCH 5/5] feat: Switch between connect/disconnect based on current state --- src/main.ts | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/src/main.ts b/src/main.ts index 6934f6c..de26315 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,4 @@ -import { app, BrowserWindow, ipcMain, Menu, nativeImage, shell, Tray } from 'electron'; +import { app, BrowserWindow, ipcMain, Menu, MenuItemConstructorOptions, nativeImage, shell, Tray } from 'electron'; import path from 'node:path'; import started from 'electron-squirrel-startup'; import { APP_NAME, DEFAULT_STATUS, GATEWAY_HOST, MANAGEMENT_URL, POLL_INTERVAL_MS, WINDOW_HEIGHT, WINDOW_WIDTH } from './config'; @@ -84,6 +84,25 @@ function updateTray() { if (!tray) return; const label = cachedStatus.state === 'connected' ? `${APP_NAME}: Connected` : APP_NAME; tray.setToolTip(label); + + const template: MenuItemConstructorOptions[] = []; + const connected = cachedStatus.state === "connected"; + const busy = ['connecting', 'disconnecting'].includes(cachedStatus.state); + + template.push({ + label: connected ? 'Disconnect' : 'Connect', + click: connected ? disconnect : connect, + enabled: !busy + }); + + template.push( + { + label: 'Logout', + click: logout + }, + { role: 'quit' } + ); + tray.setContextMenu(Menu.buildFromTemplate(template)); } function setStatus(status: Record) { @@ -206,23 +225,7 @@ function showWindow() { function createTray() { const icon = nativeImage.createFromPath(resourcePath('VynteIcon.png')).resize({ width: 18, height: 18 }); tray = new Tray(icon); - const contextMenu = Menu.buildFromTemplate([ - { - label: 'Connect', - click: connect, - }, - { - label: 'Disconnect', - click: disconnect, - }, - { - label: 'Logout', - click: logout - }, - { role: 'quit' } - ]); - tray.setContextMenu(contextMenu); - tray.setToolTip(APP_NAME); + updateTray(); tray.on('click', showWindow); }