In-app toasts for electron-updater notifications (#3902)
* Add custom updater model back after electron migration Fixes #3872 * Enable release builds (temp) * Lint & clean up * Change approach to no user input, heads up with toast * Re-enable prod builds * Working toasts * Only one toast * Add missing type * Clean up before review * New toast design test * Clean up * Use theme colors, add link to changelog --------- Co-authored-by: Frank Noirot <frank@kittycad.io>
This commit is contained in:
4
interface.d.ts
vendored
4
interface.d.ts
vendored
@ -63,6 +63,10 @@ export interface IElectronAPI {
|
||||
kittycad: (access: string, args: any) => any
|
||||
listMachines: () => Promise<MachinesListing>
|
||||
getMachineApiIp: () => Promise<string | null>
|
||||
onUpdateDownloaded: (
|
||||
callback: (value: string) => void
|
||||
) => Electron.IpcRenderer
|
||||
appRestart: () => void
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
64
src/components/ToastUpdate.tsx
Normal file
64
src/components/ToastUpdate.tsx
Normal file
@ -0,0 +1,64 @@
|
||||
import toast from 'react-hot-toast'
|
||||
import { ActionButton } from './ActionButton'
|
||||
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
||||
|
||||
export function ToastUpdate({
|
||||
version,
|
||||
onRestart,
|
||||
}: {
|
||||
version: string
|
||||
onRestart: () => void
|
||||
}) {
|
||||
return (
|
||||
<div className="inset-0 z-50 grid place-content-center rounded bg-chalkboard-110/50 shadow-md">
|
||||
<div className="max-w-3xl min-w-[35rem] p-8 rounded bg-chalkboard-10 dark:bg-chalkboard-90">
|
||||
<div className="my-4 flex items-baseline">
|
||||
<span
|
||||
className="px-3 py-1 text-xl rounded-full bg-primary text-chalkboard-10"
|
||||
data-testid="update-version"
|
||||
>
|
||||
v{version}
|
||||
</span>
|
||||
<span className="ml-4 text-md text-bold">
|
||||
A new update has downloaded and will be available next time you
|
||||
start the app. You can view the release notes{' '}
|
||||
<a
|
||||
onClick={openExternalBrowserIfDesktop(
|
||||
`https://github.com/KittyCAD/modeling-app/releases/tag/v${version}`
|
||||
)}
|
||||
href={`https://github.com/KittyCAD/modeling-app/releases/tag/v${version}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
here on GitHub.
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex justify-between gap-8">
|
||||
<ActionButton
|
||||
Element="button"
|
||||
iconStart={{
|
||||
icon: 'arrowRotateRight',
|
||||
}}
|
||||
name="Restart app now"
|
||||
onClick={onRestart}
|
||||
>
|
||||
Restart app now
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
Element="button"
|
||||
iconStart={{
|
||||
icon: 'checkmark',
|
||||
}}
|
||||
name="Got it"
|
||||
onClick={() => {
|
||||
toast.dismiss()
|
||||
}}
|
||||
>
|
||||
Got it
|
||||
</ActionButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
import ReactDOM from 'react-dom/client'
|
||||
import './index.css'
|
||||
import reportWebVitals from './reportWebVitals'
|
||||
import { Toaster } from 'react-hot-toast'
|
||||
import toast, { Toaster } from 'react-hot-toast'
|
||||
import { Router } from './Router'
|
||||
import { HotkeysProvider } from 'react-hotkeys-hook'
|
||||
import ModalContainer from 'react-modal-promise'
|
||||
import { isDesktop } from 'lib/isDesktop'
|
||||
import { AppStreamProvider } from 'AppState'
|
||||
import { ToastUpdate } from 'components/ToastUpdate'
|
||||
|
||||
// uncomment for xstate inspector
|
||||
// import { DEV } from 'env'
|
||||
@ -52,4 +53,17 @@ root.render(
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
reportWebVitals()
|
||||
|
||||
isDesktop()
|
||||
isDesktop() &&
|
||||
window.electron.onUpdateDownloaded((version: string) => {
|
||||
const message = `A new update (${version}) was downloaded and will be available next time you open the app.`
|
||||
console.log(message)
|
||||
toast.custom(
|
||||
ToastUpdate({
|
||||
version,
|
||||
onRestart: () => {
|
||||
window.electron.appRestart()
|
||||
},
|
||||
}),
|
||||
{ duration: 30000 }
|
||||
)
|
||||
})
|
||||
|
17
src/main.ts
17
src/main.ts
@ -251,18 +251,14 @@ export function getAutoUpdater(): AppUpdater {
|
||||
return autoUpdater
|
||||
}
|
||||
|
||||
export async function checkForUpdates(autoUpdater: AppUpdater) {
|
||||
// TODO: figure out how to get the update modal back
|
||||
const result = await autoUpdater.checkForUpdatesAndNotify()
|
||||
console.log(result)
|
||||
}
|
||||
|
||||
app.on('ready', () => {
|
||||
const autoUpdater = getAutoUpdater()
|
||||
checkForUpdates(autoUpdater).catch(reportRejection)
|
||||
setTimeout(() => {
|
||||
autoUpdater.checkForUpdates().catch(reportRejection)
|
||||
}, 1000)
|
||||
const fifteenMinutes = 15 * 60 * 1000
|
||||
setInterval(() => {
|
||||
checkForUpdates(autoUpdater).catch(reportRejection)
|
||||
autoUpdater.checkForUpdates().catch(reportRejection)
|
||||
}, fifteenMinutes)
|
||||
|
||||
autoUpdater.on('update-available', (info) => {
|
||||
@ -271,6 +267,11 @@ app.on('ready', () => {
|
||||
|
||||
autoUpdater.on('update-downloaded', (info) => {
|
||||
console.log('update-downloaded', info)
|
||||
mainWindow?.webContents.send('update-downloaded', info.version)
|
||||
})
|
||||
|
||||
ipcMain.handle('app.restart', () => {
|
||||
autoUpdater.quitAndInstall()
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -15,6 +15,9 @@ const startDeviceFlow = (host: string): Promise<string> =>
|
||||
ipcRenderer.invoke('startDeviceFlow', host)
|
||||
const loginWithDeviceFlow = (): Promise<string> =>
|
||||
ipcRenderer.invoke('loginWithDeviceFlow')
|
||||
const onUpdateDownloaded = (callback: (value: string) => void) =>
|
||||
ipcRenderer.on('update-downloaded', (_event, value) => callback(value))
|
||||
const appRestart = () => ipcRenderer.invoke('app.restart')
|
||||
|
||||
const isMac = os.platform() === 'darwin'
|
||||
const isWindows = os.platform() === 'win32'
|
||||
@ -123,4 +126,6 @@ contextBridge.exposeInMainWorld('electron', {
|
||||
kittycad,
|
||||
listMachines,
|
||||
getMachineApiIp,
|
||||
onUpdateDownloaded,
|
||||
appRestart,
|
||||
})
|
||||
|
Reference in New Issue
Block a user