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:
Pierre Jacquier
2025-06-05 17:12:14 -04:00
committed by GitHub
parent 3c23cada8e
commit 11d8179368
9 changed files with 94 additions and 21 deletions

2
interface.d.ts vendored
View File

@ -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

View File

@ -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={() => {

View File

@ -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',

View File

@ -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)

View File

@ -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)

View File

@ -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',

View File

@ -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'

View File

@ -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
View 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
}