mirror of
https://github.com/hoornet/vega.git
synced 2026-07-09 01:58:13 -07:00
Add auto-updater (Phase 1 #4)
- tauri-plugin-updater + tauri-plugin-process registered (Rust + npm) - Updater endpoint: github.com/hoornet/wrystr/releases/latest/download/latest.json - Ed25519 signing keypair generated; public key in tauri.conf.json; private key added to TAURI_SIGNING_PRIVATE_KEY GitHub secret - Release workflow passes TAURI_SIGNING_PRIVATE_KEY env var so tauri-action signs artifacts and generates latest.json manifest - useUpdater hook: checks for updates 5 s after startup (silent on error); exposes available, version, install(), dismiss() - UpdateBanner: dismissible top-of-app bar shown when an update is available — "Update & restart" downloads, installs, and relaunches Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+28
-1
@@ -13,6 +13,30 @@ import { AboutView } from "./components/shared/AboutView";
|
||||
import { ZapHistoryView } from "./components/zap/ZapHistoryView";
|
||||
import { DMView } from "./components/dm/DMView";
|
||||
import { useUIStore } from "./stores/ui";
|
||||
import { useUpdater } from "./hooks/useUpdater";
|
||||
|
||||
function UpdateBanner() {
|
||||
const { available, version, installing, error, install, dismiss } = useUpdater();
|
||||
if (!available) return null;
|
||||
return (
|
||||
<div className="flex items-center justify-between px-4 py-2 bg-accent/10 border-b border-accent/30 text-[12px] shrink-0">
|
||||
<span className="text-text">
|
||||
Wrystr {version} is available.{" "}
|
||||
{error && <span className="text-danger ml-1">{error}</span>}
|
||||
</span>
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={install}
|
||||
disabled={installing}
|
||||
className="text-accent hover:text-accent-hover transition-colors disabled:opacity-50"
|
||||
>
|
||||
{installing ? "Installing…" : "Update & restart"}
|
||||
</button>
|
||||
<button onClick={dismiss} className="text-text-dim hover:text-text transition-colors">×</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function App() {
|
||||
const currentView = useUIStore((s) => s.currentView);
|
||||
@@ -25,7 +49,9 @@ function App() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-screen bg-bg">
|
||||
<div className="flex flex-col h-screen w-screen bg-bg">
|
||||
<UpdateBanner />
|
||||
<div className="flex flex-1 min-h-0">
|
||||
<Sidebar />
|
||||
<main className="flex-1 min-w-0">
|
||||
{currentView === "feed" && <Feed />}
|
||||
@@ -40,6 +66,7 @@ function App() {
|
||||
{currentView === "zaps" && <ZapHistoryView />}
|
||||
{currentView === "dm" && <DMView />}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { check } from "@tauri-apps/plugin-updater";
|
||||
import { relaunch } from "@tauri-apps/plugin-process";
|
||||
|
||||
interface UpdateState {
|
||||
available: boolean;
|
||||
version: string | null;
|
||||
body: string | null;
|
||||
installing: boolean;
|
||||
error: string | null;
|
||||
install: () => Promise<void>;
|
||||
dismiss: () => void;
|
||||
}
|
||||
|
||||
export function useUpdater(): UpdateState {
|
||||
const [available, setAvailable] = useState(false);
|
||||
const [version, setVersion] = useState<string | null>(null);
|
||||
const [body, setBody] = useState<string | null>(null);
|
||||
const [installing, setInstalling] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [dismissed, setDismissed] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// Check for updates ~5 s after startup (non-blocking)
|
||||
const t = setTimeout(async () => {
|
||||
try {
|
||||
const update = await check();
|
||||
if (update?.available) {
|
||||
setAvailable(true);
|
||||
setVersion(update.version);
|
||||
setBody(update.body ?? null);
|
||||
}
|
||||
} catch {
|
||||
// Update check failure is silent — network may be unavailable
|
||||
}
|
||||
}, 5000);
|
||||
return () => clearTimeout(t);
|
||||
}, []);
|
||||
|
||||
const install = async () => {
|
||||
setInstalling(true);
|
||||
setError(null);
|
||||
try {
|
||||
const update = await check();
|
||||
if (update?.available) {
|
||||
await update.downloadAndInstall();
|
||||
await relaunch();
|
||||
}
|
||||
} catch (err) {
|
||||
setError(String(err));
|
||||
setInstalling(false);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
available: available && !dismissed,
|
||||
version,
|
||||
body,
|
||||
installing,
|
||||
error,
|
||||
install,
|
||||
dismiss: () => setDismissed(true),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user