Add auto-update support (tauri-plugin-updater) and Gitea release pipeline
Ed25519-signed updates, checked via a fixed-tag latest.json manifest on Gitea Releases; scripts/release.sh automates build+sign+publish. Adds a manual "Nach Updates suchen" trigger plus a silent startup check.
This commit is contained in:
+26
@@ -45,6 +45,7 @@ import { api } from './lib/api'
|
||||
import { parseAddressList } from './lib/addresses'
|
||||
import { escapeHtml } from './lib/richtext'
|
||||
import { loadAppSettings, saveAppSettings, type AppSettings } from './lib/settings'
|
||||
import { checkForUpdate, installUpdate } from './lib/updater'
|
||||
import {
|
||||
applyTheme,
|
||||
loadConversationsEnabled,
|
||||
@@ -253,6 +254,11 @@ export default function App() {
|
||||
setTimeout(() => setToast(null), 3500)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
handleCheckForUpdates(true)
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
applyTheme(theme)
|
||||
if (theme !== 'system') return
|
||||
@@ -443,6 +449,25 @@ export default function App() {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [view, selectedMessage])
|
||||
|
||||
async function handleCheckForUpdates(silent = false) {
|
||||
const update = await checkForUpdate()
|
||||
if (!update) {
|
||||
if (!silent) showToast('Du hast schon die neueste Version')
|
||||
return
|
||||
}
|
||||
if (!window.confirm(`Version ${update.version} ist verfügbar (aktuell: ${update.currentVersion}).\n\n${update.body ?? ''}\n\nJetzt installieren? Die App startet danach neu.`)) {
|
||||
return
|
||||
}
|
||||
showToast('Update wird heruntergeladen…')
|
||||
try {
|
||||
await installUpdate(update, (pct) => {
|
||||
if (pct !== null) showToast(`Update wird installiert… ${pct}%`)
|
||||
})
|
||||
} catch (e) {
|
||||
showToast(`Update fehlgeschlagen: ${e}`)
|
||||
}
|
||||
}
|
||||
|
||||
function handleRefresh() {
|
||||
if (offlineMode) {
|
||||
showToast('Offline-Modus aktiv')
|
||||
@@ -1818,6 +1843,7 @@ export default function App() {
|
||||
onSyncGoogleCalendar={handleSyncGoogleCalendar}
|
||||
onReconnectGoogleAccount={handleReconnectGoogleAccount}
|
||||
onOpenCaldav={() => setCaldavModalOpen(true)}
|
||||
onCheckForUpdates={handleCheckForUpdates}
|
||||
theme={theme}
|
||||
onThemeChange={handleThemeChange}
|
||||
onOpenSettings={() => setSettingsModalOpen(true)}
|
||||
|
||||
@@ -273,6 +273,7 @@ export function Ribbon({
|
||||
onSyncGoogleCalendar,
|
||||
onReconnectGoogleAccount,
|
||||
onOpenCaldav,
|
||||
onCheckForUpdates,
|
||||
theme,
|
||||
onThemeChange,
|
||||
onOpenSettings,
|
||||
@@ -337,6 +338,7 @@ export function Ribbon({
|
||||
onSyncGoogleCalendar?: (accountId: string) => Promise<void>
|
||||
onReconnectGoogleAccount?: (accountId: string) => Promise<void>
|
||||
onOpenCaldav?: () => void
|
||||
onCheckForUpdates: () => void
|
||||
theme: Theme
|
||||
onThemeChange: (t: Theme) => void
|
||||
onOpenSettings: () => void
|
||||
@@ -556,6 +558,7 @@ export function Ribbon({
|
||||
<ClassicRibbonGroup caption="Support">
|
||||
<HelpMenu onShowShortcuts={onShowShortcuts} />
|
||||
<AboutMenu />
|
||||
<ClassicButton icon={RefreshCw} label="Nach Updates suchen" onClick={onCheckForUpdates} />
|
||||
</ClassicRibbonGroup>
|
||||
)}
|
||||
</ClassicRibbonBody>
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { check, type Update } from '@tauri-apps/plugin-updater'
|
||||
import { relaunch } from '@tauri-apps/plugin-process'
|
||||
|
||||
export async function checkForUpdate(): Promise<Update | null> {
|
||||
try {
|
||||
return await check()
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export async function installUpdate(update: Update, onProgress: (pct: number | null) => void): Promise<void> {
|
||||
let total = 0
|
||||
let downloaded = 0
|
||||
await update.downloadAndInstall((event) => {
|
||||
if (event.event === 'Started') {
|
||||
total = event.data.contentLength ?? 0
|
||||
onProgress(total > 0 ? 0 : null)
|
||||
} else if (event.event === 'Progress') {
|
||||
downloaded += event.data.chunkLength
|
||||
onProgress(total > 0 ? Math.min(100, Math.round((downloaded / total) * 100)) : null)
|
||||
} else if (event.event === 'Finished') {
|
||||
onProgress(100)
|
||||
}
|
||||
})
|
||||
await relaunch()
|
||||
}
|
||||
Reference in New Issue
Block a user