Check for updates button in moar menus & toasts (#7369)
* Check for update button in more menus Fixes #7368 * Add menubar item * Another one * Add Checking for updates... and No new update toasts * Lint * Trigger CI * Update src/main.ts Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> * Update electron-builder.yml Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> * Update electron-builder.yml * Moar clean up --------- Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
This commit is contained in:
2
interface.d.ts
vendored
2
interface.d.ts
vendored
@ -92,6 +92,8 @@ export interface IElectronAPI {
|
||||
kittycad: (access: string, args: any) => any
|
||||
listMachines: (machineApiIp: string) => Promise<MachinesListing>
|
||||
getMachineApiIp: () => Promise<string | null>
|
||||
onUpdateChecking: (callback: () => void) => Electron.IpcRenderer
|
||||
onUpdateNotAvailable: (callback: () => void) => Electron.IpcRenderer
|
||||
onUpdateDownloadStart: (
|
||||
callback: (value: { version: string }) => void
|
||||
) => Electron.IpcRenderer
|
||||
|
@ -14,6 +14,8 @@ import {
|
||||
catchOnboardingWarnError,
|
||||
} from '@src/routes/Onboarding/utils'
|
||||
import { onboardingStartPath } from '@src/lib/onboardingPaths'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
import { isDesktop } from '@src/lib/isDesktop'
|
||||
|
||||
const HelpMenuDivider = () => (
|
||||
<div className="h-[1px] bg-chalkboard-110 dark:bg-chalkboard-80" />
|
||||
@ -117,6 +119,17 @@ export function HelpMenu({
|
||||
>
|
||||
Release notes
|
||||
</HelpMenuItem>
|
||||
{isDesktop() && (
|
||||
<HelpMenuItem
|
||||
as="button"
|
||||
onClick={() => {
|
||||
close()
|
||||
window.electron.appCheckForUpdates().catch(reportRejection)
|
||||
}}
|
||||
>
|
||||
Check for updates
|
||||
</HelpMenuItem>
|
||||
)}
|
||||
<HelpMenuItem
|
||||
as="button"
|
||||
onClick={() => {
|
||||
|
@ -12,6 +12,7 @@ import usePlatform from '@src/hooks/usePlatform'
|
||||
import { isDesktop } from '@src/lib/isDesktop'
|
||||
import { PATHS } from '@src/lib/paths'
|
||||
import { authActor } from '@src/lib/singletons'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
|
||||
type User = Models['User_type']
|
||||
|
||||
@ -129,6 +130,15 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'check-for-updates',
|
||||
Element: 'button',
|
||||
hide: !isDesktop(),
|
||||
onClick: () => {
|
||||
window.electron.appCheckForUpdates().catch(reportRejection)
|
||||
},
|
||||
children: <span className="flex-1">Check for updates</span>,
|
||||
},
|
||||
'break',
|
||||
{
|
||||
id: 'sign-out',
|
||||
|
@ -81,21 +81,31 @@ root.render(
|
||||
reportWebVitals()
|
||||
|
||||
if (isDesktop()) {
|
||||
// Listen for update download progress to begin
|
||||
// to show a loading toast.
|
||||
window.electron.onUpdateChecking(() => {
|
||||
const message = `Checking for updates...`
|
||||
console.log(message)
|
||||
toast.loading(message, { id: AUTO_UPDATER_TOAST_ID })
|
||||
})
|
||||
|
||||
window.electron.onUpdateNotAvailable(() => {
|
||||
const message = `You're already using the latest version of the app.`
|
||||
console.log(message)
|
||||
toast.success(message, { id: AUTO_UPDATER_TOAST_ID })
|
||||
})
|
||||
|
||||
window.electron.onUpdateDownloadStart(() => {
|
||||
const message = `Downloading app update...`
|
||||
console.log(message)
|
||||
toast.loading(message, { id: AUTO_UPDATER_TOAST_ID })
|
||||
})
|
||||
// Listen for update download errors to show
|
||||
// an error toast and clear the loading toast.
|
||||
|
||||
window.electron.onUpdateError(({ error }) => {
|
||||
console.error(error)
|
||||
toast.error('An error occurred while downloading the update.', {
|
||||
id: AUTO_UPDATER_TOAST_ID,
|
||||
})
|
||||
})
|
||||
|
||||
window.electron.onUpdateDownloaded(({ version, releaseNotes }) => {
|
||||
const message = `A new update (${version}) was downloaded and will be available next time you open the app.`
|
||||
console.log(message)
|
||||
|
47
src/main.ts
47
src/main.ts
@ -19,9 +19,9 @@ import {
|
||||
shell,
|
||||
systemPreferences,
|
||||
} from 'electron'
|
||||
import electronUpdater, { type AppUpdater } from 'electron-updater'
|
||||
import { Issuer } from 'openid-client'
|
||||
|
||||
import { getAutoUpdater } from '@src/updater'
|
||||
import {
|
||||
argvFromYargs,
|
||||
getPathOrUrlFromArgs,
|
||||
@ -442,16 +442,6 @@ ipcMain.handle('disable-menu', (event, data) => {
|
||||
disableMenu(menuId)
|
||||
})
|
||||
|
||||
export function getAutoUpdater(): AppUpdater {
|
||||
// Using destructuring to access autoUpdater due to the CommonJS module of 'electron-updater'.
|
||||
// It is a workaround for ESM compatibility issues, see https://github.com/electron-userland/electron-builder/issues/7976.
|
||||
const { autoUpdater } = electronUpdater
|
||||
// Allows us to rollback to a previous version if needed.
|
||||
// See https://github.com/electron-userland/electron-builder/blob/7dbc6c77c340c869d1e7effa22135fc740003a0f/packages/electron-updater/src/AppUpdater.ts#L450-L451
|
||||
autoUpdater.allowDowngrade = true
|
||||
return autoUpdater
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
// Disable auto updater on non-versioned builds
|
||||
if (packageJSON.version === '0.0.0' && viteEnv.MODE !== 'production') {
|
||||
@ -462,13 +452,36 @@ app.on('ready', () => {
|
||||
// TODO: we're getting `Error: Response ends without calling any handlers` with our setup,
|
||||
// so at the moment this isn't worth enabling
|
||||
autoUpdater.disableDifferentialDownload = true
|
||||
setTimeout(() => {
|
||||
autoUpdater.checkForUpdates().catch(reportRejection)
|
||||
}, 1000)
|
||||
|
||||
// Check for updates in the background at startup and then every 15 minutes
|
||||
let backgroundCheckingForUpdates = false
|
||||
const checkForUpdatesBackground = () => {
|
||||
backgroundCheckingForUpdates = true
|
||||
autoUpdater
|
||||
.checkForUpdates()
|
||||
.catch(reportRejection)
|
||||
.finally(() => {
|
||||
backgroundCheckingForUpdates = false
|
||||
})
|
||||
}
|
||||
const oneSecond = 1000
|
||||
const fifteenMinutes = 15 * 60 * 1000
|
||||
setInterval(() => {
|
||||
autoUpdater.checkForUpdates().catch(reportRejection)
|
||||
}, fifteenMinutes)
|
||||
setTimeout(checkForUpdatesBackground, oneSecond)
|
||||
setInterval(checkForUpdatesBackground, fifteenMinutes)
|
||||
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
console.log('checking-for-update')
|
||||
if (!backgroundCheckingForUpdates) {
|
||||
mainWindow?.webContents.send('update-checking')
|
||||
}
|
||||
})
|
||||
|
||||
autoUpdater.on('update-not-available', (info) => {
|
||||
console.log('update-not-available', info)
|
||||
if (!backgroundCheckingForUpdates) {
|
||||
mainWindow?.webContents.send('update-not-available')
|
||||
}
|
||||
})
|
||||
|
||||
autoUpdater.on('error', (error) => {
|
||||
console.error('update-error', error)
|
||||
|
@ -4,6 +4,7 @@ import { shell } from 'electron'
|
||||
import { reportRejection } from '@src/lib/trap'
|
||||
import { typeSafeWebContentsSend } from '@src/menu/channels'
|
||||
import type { ZooMenuItemConstructorOptions } from '@src/menu/roles'
|
||||
import { getAutoUpdater } from '@src/updater'
|
||||
|
||||
export const helpRole = (
|
||||
mainWindow: BrowserWindow
|
||||
@ -105,6 +106,12 @@ export const helpRole = (
|
||||
.catch(reportRejection)
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'Check for updates',
|
||||
click: () => {
|
||||
getAutoUpdater().checkForUpdates().catch(reportRejection)
|
||||
},
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Manage account',
|
||||
|
@ -47,6 +47,7 @@ type HelpRoleLabel =
|
||||
| 'KCL docs'
|
||||
| 'Replay onboarding tutorial'
|
||||
| 'Show release notes'
|
||||
| 'Check for updates'
|
||||
| 'Manage account'
|
||||
| 'Get started with Text-to-CAD'
|
||||
| 'Show all commands'
|
||||
|
@ -45,6 +45,10 @@ const onUpdateDownloadStart = (
|
||||
ipcRenderer.on('update-download-start', (_event: any, value) =>
|
||||
callback(value)
|
||||
)
|
||||
const onUpdateChecking = (callback: () => void) =>
|
||||
ipcRenderer.on('update-checking', (_event: any) => callback())
|
||||
const onUpdateNotAvailable = (callback: () => void) =>
|
||||
ipcRenderer.on('update-not-available', (_event: any) => callback())
|
||||
const onUpdateError = (callback: (value: Error) => void) =>
|
||||
ipcRenderer.on('update-error', (_event: any, value) => callback(value))
|
||||
const appRestart = () => ipcRenderer.invoke('app.restart')
|
||||
@ -305,6 +309,8 @@ contextBridge.exposeInMainWorld('electron', {
|
||||
kittycad,
|
||||
listMachines,
|
||||
getMachineApiIp,
|
||||
onUpdateChecking,
|
||||
onUpdateNotAvailable,
|
||||
onUpdateDownloadStart,
|
||||
onUpdateDownloaded,
|
||||
onUpdateError,
|
||||
|
11
src/updater.ts
Normal file
11
src/updater.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import electronUpdater, { type AppUpdater } from 'electron-updater'
|
||||
|
||||
export function getAutoUpdater(): AppUpdater {
|
||||
// Using destructuring to access autoUpdater due to the CommonJS module of 'electron-updater'.
|
||||
// It is a workaround for ESM compatibility issues, see https://github.com/electron-userland/electron-builder/issues/7976.
|
||||
const { autoUpdater } = electronUpdater
|
||||
// Allows us to rollback to a previous version if needed.
|
||||
// See https://github.com/electron-userland/electron-builder/blob/7dbc6c77c340c869d1e7effa22135fc740003a0f/packages/electron-updater/src/AppUpdater.ts#L450-L451
|
||||
autoUpdater.allowDowngrade = true
|
||||
return autoUpdater
|
||||
}
|
Reference in New Issue
Block a user