diff --git a/app/src/electron.js b/app/src/electron.js index 921ce7937..e41571049 100644 --- a/app/src/electron.js +++ b/app/src/electron.js @@ -28,6 +28,7 @@ let apiLoaded = false; let mainModule; // let getLogger; // let loadLogsContent; +let appUpdateStatus = ''; process.on('uncaughtException', function (error) { console.error('uncaughtException', error); @@ -201,6 +202,9 @@ ipcMain.on('app-started', async (event, arg) => { if (initialConfig['winIsMaximized']) { mainWindow.webContents.send('setIsMaximized', true); } + if (autoUpdater.isUpdaterActive()) { + mainWindow.webContents.send('setAppUpdaterActive'); + } }); ipcMain.on('window-action', async (event, arg) => { if (!mainWindow) { @@ -274,6 +278,15 @@ ipcMain.handle('showItemInFolder', async (event, path) => { ipcMain.handle('openExternal', async (event, url) => { electron.shell.openExternal(url); }); +ipcMain.handle('downloadUpdate', async (event, url) => { + autoUpdater.downloadUpdate(); +}); +ipcMain.on('applyUpdate', async (event, url) => { + autoUpdater.quitAndInstall(false, true); +}); +ipcMain.on('check-for-updates', async (event, url) => { + autoUpdater.checkForUpdates(); +}); function fillMissingSettings(value) { const res = { @@ -335,7 +348,7 @@ function createWindow() { titleBarStyle: useNativeMenu ? undefined : 'hidden', ...bounds, icon: os.platform() == 'win32' ? 'icon.ico' : path.resolve(__dirname, '../icon.png'), - partition: 'persist:dbgate', + partition: isProApp() ? 'persist:dbgate-premium' : 'persist:dbgate', webPreferences: { nodeIntegration: true, contextIsolation: false, @@ -438,6 +451,38 @@ function createWindow() { }); } +function changeAppUpdateStatus(message) { + appUpdateStatus = message; + mainWindow.webContents.send('app-update-status', appUpdateStatus); +} + +autoUpdater.on('checking-for-update', () => { + console.log('Checking for updates'); + changeAppUpdateStatus('Checking for updates...'); +}); + +autoUpdater.on('update-available', info => { + console.log('Update available', info); + changeAppUpdateStatus(`New version ${info.version} available`); + mainWindow.webContents.send('update-available', info.version); +}); + +autoUpdater.on('update-not-available', info => { + console.log('Update not available', info); + changeAppUpdateStatus(`No new updates`); +}); + +autoUpdater.on('update-downloaded', info => { + console.log('Update downloaded from', info); + changeAppUpdateStatus(`Downloaded new version ${info.version}`); + mainWindow.webContents.send('downloaded-new-version', info.version); +}); + +autoUpdater.on('error', error => { + changeAppUpdateStatus(`Autoupdate error`); + console.error('Update error', error); +}); + function onAppReady() { if (!process.env.DEVMODE) { autoUpdater.autoDownload = false; diff --git a/app/src/mainMenuDefinition.js b/app/src/mainMenuDefinition.js index 5d5cd1188..40cc485dc 100644 --- a/app/src/mainMenuDefinition.js +++ b/app/src/mainMenuDefinition.js @@ -105,6 +105,8 @@ module.exports = ({ editMenu }) => [ { command: 'settings.commands', hideDisabled: true }, { command: 'tabs.changelog', hideDisabled: true }, { command: 'about.show', hideDisabled: true }, + { divider: true }, + { command: 'file.checkForUpdates', hideDisabled: true }, ], }, ]; diff --git a/packages/web/src/App.svelte b/packages/web/src/App.svelte index 0c0c8c08b..4669f628a 100644 --- a/packages/web/src/App.svelte +++ b/packages/web/src/App.svelte @@ -21,6 +21,7 @@ import AppStartInfo from './widgets/AppStartInfo.svelte'; import SettingsListener from './utility/SettingsListener.svelte'; import { handleAuthOnStartup } from './clientAuth'; + import { initializeAppUpdates } from './utility/appUpdate'; export let isAdminPage = false; @@ -49,6 +50,7 @@ subscribeConnectionPingers(); subscribePermissionCompiler(); installNewVolatileConnectionListener(); + initializeAppUpdates(); } loadedApi = loadedApiValue; diff --git a/packages/web/src/commands/stdCommands.ts b/packages/web/src/commands/stdCommands.ts index d1b23a4dd..34f2f7bef 100644 --- a/packages/web/src/commands/stdCommands.ts +++ b/packages/web/src/commands/stdCommands.ts @@ -3,6 +3,7 @@ import { currentTheme, emptyConnectionGroupNames, extensions, + getAppUpdaterActive, getExtensions, getVisibleToolbar, visibleToolbar, @@ -585,6 +586,17 @@ registerCommand({ onClick: () => disconnectServerConnection(getCurrentConfig()?.singleConnection?._id), }); +registerCommand({ + id: 'file.checkForUpdates', + category: 'App', + name: 'Check for updates', + // testEnabled: () => true, + testEnabled: () => getAppUpdaterActive(), + onClick: () => getElectron().send('check-for-updates'), +}); + + + export function registerFileCommands({ idPrefix, category, diff --git a/packages/web/src/stores.ts b/packages/web/src/stores.ts index b275d216d..27e3d6fe4 100644 --- a/packages/web/src/stores.ts +++ b/packages/web/src/stores.ts @@ -123,6 +123,8 @@ export const commandsCustomized = derived([commands, commandsSettings], ([$comma ...$commandsSettings[k], })) ); +export const appUpdateStatus = writable(''); +export const appUpdaterActive = writable(false); export const draggingTab = writable(null); export const draggingTabTarget = writable(null); @@ -303,3 +305,9 @@ currentArchive.subscribe(value => { currentArchiveValue = value; }); export const getCurrentArchive = () => currentArchiveValue; + +let appUpdaterActiveValue = false; +appUpdaterActive.subscribe(value => { + appUpdaterActiveValue = value; +}); +export const getAppUpdaterActive = () => appUpdaterActiveValue; \ No newline at end of file diff --git a/packages/web/src/utility/appUpdate.ts b/packages/web/src/utility/appUpdate.ts new file mode 100644 index 000000000..3e0083139 --- /dev/null +++ b/packages/web/src/utility/appUpdate.ts @@ -0,0 +1,48 @@ +import { appUpdaterActive, appUpdateStatus } from '../stores'; +import getElectron from './getElectron'; +import { showSnackbar } from './snackbar'; + +export function initializeAppUpdates() { + const electron = getElectron(); + if (!electron) { + return; + } + + electron.addEventListener('update-available', (e, version) => { + showSnackbar({ + message: `Update available: ${version}`, + allowClose: true, + buttons: [ + { + label: 'Download', + onClick: () => { + electron.send('downloadUpdate'); + }, + }, + ], + }); + }); + + electron.addEventListener('app-update-status', (e, text) => { + appUpdateStatus.set(text); + }); + + electron.addEventListener('downloaded-new-version', (e, version) => { + showSnackbar({ + message: `New version ${version} downloaded`, + allowClose: true, + buttons: [ + { + label: 'Restart', + onClick: () => { + electron.send('applyUpdate'); + }, + }, + ], + }); + }); + + electron.addEventListener('setAppUpdaterActive', (e, error) => { + appUpdaterActive.set(true); + }); +} diff --git a/packages/web/src/widgets/StatusBar.svelte b/packages/web/src/widgets/StatusBar.svelte index 338854879..4cd8ddaae 100644 --- a/packages/web/src/widgets/StatusBar.svelte +++ b/packages/web/src/widgets/StatusBar.svelte @@ -8,6 +8,7 @@ import { activeTabId, + appUpdateStatus, currentArchive, currentDatabase, currentThemeDefinition, @@ -169,6 +170,12 @@ {item.text} {/each} + + {#if $appUpdateStatus} +
+ {$appUpdateStatus} +
+ {/if}