2024-08-06 23:25:30 +02:00
import { isDev } from "@/util/isdev" ;
2024-08-06 20:05:26 +02:00
import * as electron from "electron" ;
import { autoUpdater } from "electron-updater" ;
import * as services from "../frontend/app/store/services" ;
import { fireAndForget } from "../frontend/util/util" ;
export let updater : Updater ;
export class Updater {
interval : NodeJS.Timeout | null ;
availableUpdateReleaseName : string | null ;
availableUpdateReleaseNotes : string | null ;
2024-08-07 00:13:59 +02:00
private _status : UpdaterStatus ;
2024-08-06 20:05:26 +02:00
lastUpdateCheck : Date ;
constructor ( ) {
autoUpdater . removeAllListeners ( ) ;
autoUpdater . on ( "error" , ( err ) = > {
console . log ( "updater error" ) ;
console . log ( err ) ;
this . status = "error" ;
} ) ;
autoUpdater . on ( "checking-for-update" , ( ) = > {
console . log ( "checking-for-update" ) ;
this . status = "checking" ;
} ) ;
autoUpdater . on ( "update-available" , ( ) = > {
console . log ( "update-available; downloading..." ) ;
} ) ;
autoUpdater . on ( "update-not-available" , ( ) = > {
console . log ( "update-not-available" ) ;
} ) ;
autoUpdater . on ( "update-downloaded" , ( event ) = > {
console . log ( "update-downloaded" , [ event ] ) ;
this . availableUpdateReleaseName = event . releaseName ;
this . availableUpdateReleaseNotes = event . releaseNotes as string | null ;
// Display the update banner and create a system notification
this . status = "ready" ;
const updateNotification = new electron . Notification ( {
title : "Wave Terminal" ,
body : "A new version of Wave Terminal is ready to install." ,
} ) ;
updateNotification . on ( "click" , ( ) = > {
2024-08-07 01:19:55 +02:00
fireAndForget ( ( ) = > this . promptToInstallUpdate ( ) ) ;
2024-08-06 20:05:26 +02:00
} ) ;
updateNotification . show ( ) ;
} ) ;
this . _status = "up-to-date" ;
this . lastUpdateCheck = new Date ( 0 ) ;
this . interval = null ;
this . availableUpdateReleaseName = null ;
}
get status ( ) : UpdaterStatus {
return this . _status ;
}
/ * *
* Sets the app update status and sends it to the main window
2024-08-06 23:51:06 +02:00
* @param value The status to set
2024-08-06 20:05:26 +02:00
* /
private set status ( value : UpdaterStatus ) {
this . _status = value ;
electron . BrowserWindow . getAllWindows ( ) . forEach ( ( window ) = > {
window . webContents . send ( "app-update-status" , value ) ;
} ) ;
}
/ * *
* Starts the auto update check interval .
2024-08-07 01:13:14 +02:00
* @param [ runCheckNow = true ] Determines whether the update check should run immediately . Defaults to true .
2024-08-06 20:05:26 +02:00
* @returns The timeout object for the auto update checker .
* /
2024-08-07 01:13:14 +02:00
startInterval ( runCheckNow = true ) : NodeJS . Timeout {
2024-08-06 20:05:26 +02:00
// check for updates right away and keep checking later
2024-08-07 01:13:14 +02:00
if ( runCheckNow ) this . checkForUpdates ( false ) ;
2024-08-06 20:05:26 +02:00
return setInterval ( ( ) = > {
this . checkForUpdates ( false ) ;
2024-08-07 01:13:14 +02:00
} , 600000 ) ; // intervals are unreliable when an app is suspended so we will check every 10 mins if the interval has passed.
2024-08-06 20:05:26 +02:00
}
/ * *
2024-08-07 01:09:24 +02:00
* Checks if the configured interval time has passed since the last update check , and if so , checks for updates using the ` autoUpdater ` object
2024-08-06 20:05:26 +02:00
* @param userInput Whether the user is requesting this . If so , an alert will report the result of the check .
* /
async checkForUpdates ( userInput : boolean ) {
2024-08-07 23:10:14 +02:00
console . log ( "checkForUpdates" ) ;
2024-08-06 20:05:26 +02:00
const autoUpdateOpts = ( await services . FileService . GetSettingsConfig ( ) ) . autoupdate ;
2024-08-07 01:08:49 +02:00
// If there's an active update check interval, check that the user still has auto update checks enabled. If not, remove the interval.
if ( this . interval && ! autoUpdateOpts . enabled ) {
2024-08-06 20:05:26 +02:00
console . log ( "Auto update is disabled in settings. Removing the auto update interval." ) ;
clearInterval ( this . interval ) ;
this . interval = null ;
2024-08-07 01:13:14 +02:00
} else if ( ! this . interval && autoUpdateOpts . enabled ) {
this . startInterval ( false ) ;
2024-08-06 20:05:26 +02:00
}
2024-08-07 01:08:49 +02:00
2024-08-06 20:05:26 +02:00
const now = new Date ( ) ;
2024-08-07 01:08:49 +02:00
// Run an update check always if the user requests it, otherwise only if there's an active update check interval and enough time has elapsed.
2024-08-06 20:05:26 +02:00
if (
2024-08-07 00:13:59 +02:00
userInput ||
2024-08-07 01:08:49 +02:00
( this . interval &&
( ! this . lastUpdateCheck ||
Math . abs ( now . getTime ( ) - this . lastUpdateCheck . getTime ( ) ) > autoUpdateOpts . intervalms ) )
2024-08-06 20:05:26 +02:00
) {
2024-08-06 23:51:06 +02:00
const result = await autoUpdater . checkForUpdates ( ) ;
2024-08-07 01:08:49 +02:00
// If the user requested this check and we do not have an available update, let them know with a popup dialog. No need to tell them if there is an update, because we show a banner once the update is ready to install.
2024-08-06 23:51:06 +02:00
if ( userInput && ! result . downloadPromise ) {
const dialogOpts : Electron.MessageBoxOptions = {
type : "info" ,
message : "There are currently no updates available." ,
} ;
electron . dialog . showMessageBox ( electron . BrowserWindow . getFocusedWindow ( ) , dialogOpts ) ;
}
2024-08-07 01:08:49 +02:00
// Only update the last check time if this is an automatic check. This ensures the interval remains consistent.
2024-08-07 00:36:21 +02:00
if ( ! userInput ) this . lastUpdateCheck = now ;
2024-08-06 20:05:26 +02:00
}
}
/ * *
* Prompts the user to install the downloaded application update and restarts the application
* /
2024-08-07 01:19:55 +02:00
async promptToInstallUpdate() {
2024-08-06 20:05:26 +02:00
const dialogOpts : Electron.MessageBoxOptions = {
type : "info" ,
buttons : [ "Restart" , "Later" ] ,
title : "Application Update" ,
message : process.platform === "win32" ? this . availableUpdateReleaseNotes : this.availableUpdateReleaseName ,
detail : "A new version has been downloaded. Restart the application to apply the updates." ,
} ;
const allWindows = electron . BrowserWindow . getAllWindows ( ) ;
if ( allWindows . length > 0 ) {
await electron . dialog
. showMessageBox ( electron . BrowserWindow . getFocusedWindow ( ) ? ? allWindows [ 0 ] , dialogOpts )
. then ( ( { response } ) = > {
2024-08-07 00:13:59 +02:00
if ( response === 0 ) {
2024-08-07 01:19:55 +02:00
this . installUpdate ( ) ;
2024-08-07 00:13:59 +02:00
}
2024-08-06 20:05:26 +02:00
} ) ;
}
}
2024-08-07 01:19:55 +02:00
/ * *
* Restarts the app and installs an update if it is available .
* /
installUpdate() {
if ( this . status == "ready" ) {
this . status = "installing" ;
autoUpdater . quitAndInstall ( ) ;
}
}
2024-08-06 20:05:26 +02:00
}
2024-08-07 01:19:55 +02:00
electron . ipcMain . on ( "install-app-update" , ( ) = > fireAndForget ( ( ) = > updater ? . promptToInstallUpdate ( ) ) ) ;
2024-08-06 20:05:26 +02:00
electron . ipcMain . on ( "get-app-update-status" , ( event ) = > {
event . returnValue = updater ? . status ;
} ) ;
2024-08-07 00:13:59 +02:00
let autoUpdateLock = false ;
2024-08-06 20:05:26 +02:00
/ * *
* Configures the auto - updater based on the user ' s preference
* @param enabled Whether the auto - updater should be enabled
* /
export async function configureAutoUpdater() {
2024-08-06 23:25:30 +02:00
if ( isDev ( ) ) {
console . log ( "skipping auto-updater in dev mode" ) ;
2024-08-07 00:13:59 +02:00
return ;
2024-08-06 23:25:30 +02:00
}
2024-08-06 20:05:26 +02:00
// simple lock to prevent multiple auto-update configuration attempts, this should be very rare
if ( autoUpdateLock ) {
console . log ( "auto-update configuration already in progress, skipping" ) ;
return ;
}
2024-08-07 23:10:14 +02:00
console . log ( "configureAutoUpdater" ) ;
2024-08-06 20:05:26 +02:00
autoUpdateLock = true ;
2024-08-07 01:30:42 +02:00
const autoUpdateOpts = ( await services . FileService . GetSettingsConfig ( ) ) . autoupdate ;
2024-08-06 20:05:26 +02:00
try {
console . log ( "Configuring updater" ) ;
updater = new Updater ( ) ;
2024-08-07 01:30:42 +02:00
autoUpdater . autoInstallOnAppQuit = autoUpdateOpts . installonquit ;
2024-08-06 20:05:26 +02:00
} catch ( e ) {
console . warn ( "error configuring updater" , e . toString ( ) ) ;
}
2024-08-07 01:30:42 +02:00
if ( autoUpdateOpts . enabled && updater ? . interval == null ) {
2024-08-06 20:05:26 +02:00
try {
console . log ( "configuring auto update interval" ) ;
2024-08-07 01:13:14 +02:00
updater ? . startInterval ( ) ;
2024-08-06 20:05:26 +02:00
} catch ( e ) {
console . log ( "error configuring auto update interval" , e . toString ( ) ) ;
}
2024-08-07 01:30:42 +02:00
} else if ( ! autoUpdateOpts . enabled && updater ? . interval != null ) {
2024-08-06 20:05:26 +02:00
console . log ( "disabling auto updater" ) ;
clearInterval ( updater . interval ) ;
updater = null ;
}
autoUpdateLock = false ;
}