feat: in-app update checker + Spinner import for General settings
- Add useTauriUpdater hook (check_for_update / install_update commands) - Add AppUpdates section to General settings (Tauri-only, hidden on web) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -26,6 +26,7 @@ import {
|
||||
PopOut,
|
||||
RectCords,
|
||||
Scroll,
|
||||
Spinner,
|
||||
Switch,
|
||||
Text,
|
||||
toRem,
|
||||
@@ -63,6 +64,7 @@ import { useMessageLayoutItems } from '../../../hooks/useMessageLayout';
|
||||
import { useMessageSpacingItems } from '../../../hooks/useMessageSpacing';
|
||||
import { useDateFormatItems } from '../../../hooks/useDateFormat';
|
||||
import { SequenceCardStyle } from '../styles.css';
|
||||
import { useTauriUpdater } from '../../../hooks/useTauriUpdater';
|
||||
|
||||
type ThemeSelectorProps = {
|
||||
themeNames: Record<string, string>;
|
||||
@@ -1476,6 +1478,46 @@ function Messages() {
|
||||
);
|
||||
}
|
||||
|
||||
function AppUpdates() {
|
||||
const { isTauri, status, check, install } = useTauriUpdater();
|
||||
if (!isTauri) return null;
|
||||
|
||||
const description =
|
||||
status.state === 'checking'
|
||||
? 'Checking for updates...'
|
||||
: status.state === 'up-to-date'
|
||||
? 'Lotus Chat is up to date.'
|
||||
: status.state === 'available'
|
||||
? `Update available: v${status.version}`
|
||||
: status.state === 'installing'
|
||||
? 'Installing update, the app will restart shortly...'
|
||||
: status.state === 'error'
|
||||
? `Update check failed: ${status.message}`
|
||||
: 'Check for a new version of Lotus Chat.';
|
||||
|
||||
const after =
|
||||
status.state === 'available' ? (
|
||||
<Button size="300" radii="300" onClick={install}>
|
||||
<Text size="B300">Install & Restart</Text>
|
||||
</Button>
|
||||
) : status.state === 'checking' || status.state === 'installing' ? (
|
||||
<Spinner variant="Secondary" size="200" />
|
||||
) : (
|
||||
<Button size="300" radii="300" variant="Secondary" onClick={check}>
|
||||
<Text size="B300">Check</Text>
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box direction="Column" gap="100">
|
||||
<Text size="L400">App Updates</Text>
|
||||
<SequenceCard className={SequenceCardStyle} variant="SurfaceVariant" direction="Column">
|
||||
<SettingTile title="Check for Updates" description={description} after={after} />
|
||||
</SequenceCard>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
type GeneralProps = {
|
||||
requestClose: () => void;
|
||||
};
|
||||
@@ -1506,6 +1548,7 @@ export function General({ requestClose }: GeneralProps) {
|
||||
<Messages />
|
||||
<Privacy />
|
||||
<Calls />
|
||||
<AppUpdates />
|
||||
</Box>
|
||||
</PageContent>
|
||||
</Scroll>
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
|
||||
type TauriInternals = { invoke: (cmd: string, args?: Record<string, unknown>) => Promise<unknown> };
|
||||
const tauriInvoke = (): TauriInternals['invoke'] | undefined =>
|
||||
(window as unknown as { __TAURI_INTERNALS__?: TauriInternals }).__TAURI_INTERNALS__?.invoke;
|
||||
|
||||
type UpdateStatus =
|
||||
| { state: 'idle' }
|
||||
| { state: 'checking' }
|
||||
| { state: 'up-to-date' }
|
||||
| { state: 'available'; version: string }
|
||||
| { state: 'installing' }
|
||||
| { state: 'error'; message: string };
|
||||
|
||||
export function useTauriUpdater() {
|
||||
const isTauri = !!tauriInvoke();
|
||||
const [status, setStatus] = useState<UpdateStatus>({ state: 'idle' });
|
||||
|
||||
const check = useCallback(async () => {
|
||||
const invoke = tauriInvoke();
|
||||
if (!invoke) return;
|
||||
setStatus({ state: 'checking' });
|
||||
try {
|
||||
const result = (await invoke('check_for_update')) as { available: boolean; version?: string };
|
||||
setStatus(
|
||||
result.available && result.version
|
||||
? { state: 'available', version: result.version }
|
||||
: { state: 'up-to-date' },
|
||||
);
|
||||
} catch (e) {
|
||||
setStatus({ state: 'error', message: String(e) });
|
||||
}
|
||||
}, []);
|
||||
|
||||
const install = useCallback(async () => {
|
||||
const invoke = tauriInvoke();
|
||||
if (!invoke) return;
|
||||
setStatus({ state: 'installing' });
|
||||
try {
|
||||
await invoke('install_update');
|
||||
} catch (e) {
|
||||
setStatus({ state: 'error', message: String(e) });
|
||||
}
|
||||
}, []);
|
||||
|
||||
return { isTauri, status, check, install };
|
||||
}
|
||||
Reference in New Issue
Block a user