add manual update notice for macOS, disable autoinstall (#1725)

This commit is contained in:
jeffvli
2026-03-09 01:31:33 -07:00
parent 078d8068e0
commit 6a47e99680
7 changed files with 140 additions and 7 deletions
+2 -1
View File
@@ -160,7 +160,8 @@
"clean": "clean", "clean": "clean",
"gridRows": "grid rows", "gridRows": "grid rows",
"tableColumns": "table columns", "tableColumns": "table columns",
"itemsMore": "{{count}} more" "itemsMore": "{{count}} more",
"newVersionAvailable": "a new version is available"
}, },
"entity": { "entity": {
"album_one": "album", "album_one": "album",
+36 -5
View File
@@ -68,16 +68,40 @@ class AppUpdater {
const effectiveChannel = store.get('release_channel') as string; const effectiveChannel = store.get('release_channel') as string;
console.log('Effective update channel:', effectiveChannel); console.log('Effective update channel:', effectiveChannel);
if (effectiveChannel === 'alpha') { if (effectiveChannel === 'alpha') {
checkAllChannelsAndGetBest().then(({ updater: updaterInstance }) => { checkAllChannelsAndGetBest().then(({ result, updater: updaterInstance }) => {
updaterInstance.autoInstallOnAppQuit = true; updaterInstance.autoInstallOnAppQuit = true;
updaterInstance.autoRunAppAfterInstall = true; updaterInstance.autoRunAppAfterInstall = true;
updaterInstance.checkForUpdatesAndNotify(); if (isMacOS()) {
if (result?.isUpdateAvailable) {
getMainWindow()?.webContents.send(
'update-available',
result.updateInfo.version,
);
}
} else {
updaterInstance.checkForUpdatesAndNotify();
}
}); });
return; return;
} }
configureAndGetUpdater(); configureAndGetUpdater();
autoUpdater.checkForUpdatesAndNotify(); if (isMacOS()) {
autoUpdater.autoDownload = false;
autoUpdater
.checkForUpdates()
.then((result) => {
if (result?.isUpdateAvailable) {
getMainWindow()?.webContents.send(
'update-available',
result.updateInfo.version,
);
}
})
.catch((err) => console.error('Check for updates failed', err));
} else {
autoUpdater.checkForUpdatesAndNotify();
}
} }
} }
@@ -555,8 +579,15 @@ async function createWindow(first = true): Promise<void> {
const updateAvailable = result?.isUpdateAvailable ?? false; const updateAvailable = result?.isUpdateAvailable ?? false;
console.log('Update available:', updateAvailable); console.log('Update available:', updateAvailable);
if (updateAvailable && store.get('disable_auto_updates') !== true) { if (updateAvailable && store.get('disable_auto_updates') !== true) {
console.log('Downloading update'); if (isMacOS()) {
updater.downloadUpdate(); getMainWindow()?.webContents.send(
'update-available',
result?.updateInfo?.version,
);
} else {
console.log('Downloading update');
updater.downloadUpdate();
}
} }
return { return {
+10
View File
@@ -12,9 +12,19 @@ const invoke = (channel: string, ...args: any[]) => {
return ipcRenderer.invoke(channel, ...args); return ipcRenderer.invoke(channel, ...args);
}; };
const on = (channel: string, listener: (event: any, ...args: any[]) => void) => {
ipcRenderer.on(channel, listener);
};
const removeListener = (channel: string, listener: (event: any, ...args: any[]) => void) => {
ipcRenderer.removeListener(channel, listener);
};
export const ipc = { export const ipc = {
invoke, invoke,
on,
removeAllListeners, removeAllListeners,
removeListener,
send, send,
}; };
+7
View File
@@ -29,6 +29,12 @@ const ReleaseNotesModal = lazy(() =>
})), })),
); );
const UpdateAvailableDialog = lazy(() =>
import('./update-available-dialog').then((module) => ({
default: module.UpdateAvailableDialog,
})),
);
const ipc = isElectron() ? window.api.ipc : null; const ipc = isElectron() ? window.api.ipc : null;
export const App = () => { export const App = () => {
@@ -118,6 +124,7 @@ export const App = () => {
</WebAudioContext.Provider> </WebAudioContext.Provider>
<Suspense fallback={null}> <Suspense fallback={null}>
<ReleaseNotesModal /> <ReleaseNotesModal />
<UpdateAvailableDialog />
</Suspense> </Suspense>
</MantineProvider> </MantineProvider>
); );
+79
View File
@@ -0,0 +1,79 @@
import isElectron from 'is-electron';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Button } from '/@/shared/components/button/button';
import { Dialog } from '/@/shared/components/dialog/dialog';
import { Group } from '/@/shared/components/group/group';
import { Icon } from '/@/shared/components/icon/icon';
import { Stack } from '/@/shared/components/stack/stack';
import { Text } from '/@/shared/components/text/text';
import { useLocalStorage } from '/@/shared/hooks/use-local-storage';
export const UpdateAvailableDialog = () => {
const [opened, setOpened] = useState(false);
const [version, setVersion] = useState<string>('');
const { t } = useTranslation();
const [versionDismissed, setVersionDismissed] = useLocalStorage<string>({
key: 'version_dismissed',
});
useEffect(() => {
if (!isElectron()) return;
const handleUpdateAvailable = (_event: any, newVersion: string) => {
if (versionDismissed !== newVersion) {
setVersion(newVersion);
setOpened(true);
}
};
window.api.ipc.on('update-available', handleUpdateAvailable);
return () => {
window.api.ipc.removeListener?.('update-available', handleUpdateAvailable);
};
}, [versionDismissed]);
if (!opened) return null;
const handleDismiss = () => {
if (version) {
setVersionDismissed(version);
}
setOpened(false);
};
return (
<Dialog
onClose={handleDismiss}
opened={opened}
position={{ bottom: 100, right: 12 }}
radius="md"
size="lg"
withCloseButton
>
<Stack gap="md">
<Text fw={700} size="md">
{t('common.newVersionAvailable', { postProcess: 'sentenceCase' })} - {version}
</Text>
<Group justify="flex-end">
<Button onClick={handleDismiss} size="xs" variant="default">
{t('common.dismiss', { postProcess: 'titleCase' })}
</Button>
<Button
component="a"
href="https://github.com/jeffvli/feishin/releases/latest"
onClick={handleDismiss}
rightSection={<Icon icon="externalLink" size="sm" />}
size="xs"
target="_blank"
variant="filled"
>
{t('action.viewMore', { postProcess: 'titleCase' })}
</Button>
</Group>
</Stack>
</Dialog>
);
};
@@ -3,3 +3,8 @@
background: var(--theme-colors-surface); background: var(--theme-colors-surface);
box-shadow: 2px 2px 10px 2px rgb(0 0 0 / 40%); box-shadow: 2px 2px 10px 2px rgb(0 0 0 / 40%);
} }
.close-button {
display: none;
}
+1 -1
View File
@@ -9,7 +9,7 @@ interface DialogProps extends MantineDialogProps {}
export const Dialog = ({ classNames, style, ...props }: DialogProps) => { export const Dialog = ({ classNames, style, ...props }: DialogProps) => {
return ( return (
<MantineDialog <MantineDialog
classNames={{ root: styles.root, ...classNames }} classNames={{ closeButton: styles.closeButton, root: styles.root, ...classNames }}
style={{ style={{
...style, ...style,
}} }}