evOo-local/apps/mobile/components/computer-maintenance-actions.tsx
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

90 lines
2.9 KiB
TypeScript

import type { ComputerStatus } from "@rakazo/contracts";
import { useState } from "react";
import { Alert, Pressable, Text, View } from "react-native";
import { rpc } from "../lib/api";
import { computerUpdates } from "../lib/computer-updates";
import { useI18n } from "../lib/i18n";
import { useMobileTokens } from "../lib/native";
type Action = "recover" | "reset" | "update";
export function ComputerMaintenanceActions({
botId,
computer,
onChanged,
}: {
botId: string;
computer: ComputerStatus | null;
onChanged: () => Promise<void>;
}) {
const { t } = useI18n();
const tokens = useMobileTokens();
const [pending, setPending] = useState<Action | null>(null);
const [error, setError] = useState<string | null>(null);
if (!computer) return null;
const busy = Boolean(computer.busyBotName) || computer.state === "booting";
async function run(action: Action) {
setPending(action);
setError(null);
try {
if (action === "recover") await computerUpdates.start(botId, "recover");
else if (action === "reset") await rpc("computer/reset", { botId });
else await computerUpdates.start(botId);
await onChanged();
} catch (err) {
setError(err instanceof Error ? err.message : t("Could not update computer"));
} finally {
setPending(null);
}
}
function confirmReset() {
Alert.alert(
t("Reset computer?"),
t("Restore the last saved workspace. Unsaved work on the computer is lost."),
[
{ text: t("Cancel"), style: "cancel" },
{ text: t("Reset"), style: "destructive", onPress: () => void run("reset") },
],
);
}
return (
<View style={{ marginTop: 16, gap: 10 }}>
<Pressable
disabled={busy || pending !== null}
onPress={() => void run("recover")}
style={{ opacity: busy || pending !== null ? 0.4 : 1 }}
>
<Text style={{ color: tokens.mutedForeground, fontSize: 14 }}>
{pending === "recover" ? t("Recovering…") : t("Recover computer")}
</Text>
</Pressable>
<Pressable
disabled={busy || pending !== null}
onPress={confirmReset}
style={{ opacity: busy || pending !== null ? 0.4 : 1 }}
>
<Text style={{ color: tokens.mutedForeground, fontSize: 14 }}>
{pending === "reset" ? t("Resetting…") : t("Reset computer")}
</Text>
</Pressable>
{computer.canUpdate ? (
<Pressable
disabled={busy || pending !== null}
onPress={() => void run("update")}
style={{ opacity: busy || pending !== null ? 0.4 : 1 }}
>
<Text style={{ color: tokens.mutedForeground, fontSize: 14 }}>
{pending === "update" ? t("Updating…") : t("Update computer")}
</Text>
</Pressable>
) : null}
{error ? <Text style={{ color: tokens.destructive, fontSize: 13 }}>{error}</Text> : null}
</View>
);
}