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.
141 lines
4.5 KiB
TypeScript
141 lines
4.5 KiB
TypeScript
import type { Routine } from "@rakazo/contracts";
|
|
import { Stack, useLocalSearchParams, useRouter } from "expo-router";
|
|
import { useEffect, useState } from "react";
|
|
import { ActivityIndicator, Pressable, ScrollView, Text, View } from "react-native";
|
|
import { rpc } from "../lib/api";
|
|
import { useI18n } from "../lib/i18n";
|
|
import { useMobileTokens } from "../lib/native";
|
|
|
|
export default function RoutineDetail() {
|
|
const tokens = useMobileTokens();
|
|
const { t } = useI18n();
|
|
const { botId, botName, routineId } = useLocalSearchParams<{
|
|
botId?: string;
|
|
botName?: string;
|
|
routineId?: string;
|
|
}>();
|
|
const router = useRouter();
|
|
const [routine, setRoutine] = useState<Routine | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!botId || !routineId) {
|
|
setError(t("Routine link is incomplete"));
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
let cancelled = false;
|
|
setLoading(true);
|
|
void rpc<Routine[]>("routines/list", { botId })
|
|
.then((routines) => {
|
|
if (cancelled) return;
|
|
const match = routines.find((item) => item.id === routineId);
|
|
if (match) setRoutine(match);
|
|
else setError(t("This routine no longer exists"));
|
|
})
|
|
.catch((loadError) => {
|
|
if (!cancelled) {
|
|
setError(loadError instanceof Error ? loadError.message : t("Could not load routine"));
|
|
}
|
|
})
|
|
.finally(() => {
|
|
if (!cancelled) setLoading(false);
|
|
});
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [botId, routineId]);
|
|
|
|
return (
|
|
<ScrollView
|
|
style={{ flex: 1, backgroundColor: tokens.background }}
|
|
contentContainerStyle={{ padding: 24, gap: 18 }}
|
|
>
|
|
<Stack.Screen options={{ title: routine?.name ?? t("Routine") }} />
|
|
{loading ? <ActivityIndicator color={tokens.mutedForeground} /> : null}
|
|
{error ? <Text style={{ color: tokens.destructive, fontSize: 15 }}>{error}</Text> : null}
|
|
{routine ? (
|
|
<>
|
|
<View
|
|
style={{
|
|
borderRadius: 16,
|
|
borderWidth: 1,
|
|
borderColor: tokens.border,
|
|
backgroundColor: tokens.card,
|
|
padding: 18,
|
|
gap: 8,
|
|
}}
|
|
>
|
|
<Text style={{ color: tokens.foreground, fontSize: 20, fontWeight: "600" }}>
|
|
{routine.name}
|
|
</Text>
|
|
<Text
|
|
style={{
|
|
color: routine.active ? tokens.success : tokens.mutedForeground,
|
|
fontSize: 14,
|
|
}}
|
|
>
|
|
{[
|
|
routine.active ? t("Active") : t("Paused"),
|
|
[
|
|
...routine.crons,
|
|
...(routine.webhookEnabled ? [t("Webhook")] : []),
|
|
...(routine.githubEnabled ? [t("Git event")] : []),
|
|
...(routine.messageProvider === "slack"
|
|
? [t("Slack message")]
|
|
: routine.messageProvider === "teams"
|
|
? [t("Teams message")]
|
|
: routine.messageProvider
|
|
? [t("Message event")]
|
|
: []),
|
|
].join(", "),
|
|
routine.timezone,
|
|
].join(" · ")}
|
|
</Text>
|
|
</View>
|
|
<View style={{ gap: 8 }}>
|
|
<Text
|
|
style={{ color: tokens.mutedForeground, fontSize: 13, textTransform: "uppercase" }}
|
|
>
|
|
{t("Prompt")}
|
|
</Text>
|
|
<Text
|
|
selectable
|
|
style={{
|
|
color: tokens.foreground,
|
|
fontSize: 15,
|
|
lineHeight: 23,
|
|
borderRadius: 16,
|
|
backgroundColor: tokens.card,
|
|
padding: 18,
|
|
}}
|
|
>
|
|
{routine.prompt}
|
|
</Text>
|
|
</View>
|
|
<Pressable
|
|
accessibilityRole="button"
|
|
onPress={() =>
|
|
router.push({
|
|
pathname: "/thread",
|
|
params: { botId: botId ?? "", name: botName ?? t("Bot") },
|
|
})
|
|
}
|
|
style={{
|
|
alignItems: "center",
|
|
borderRadius: 12,
|
|
backgroundColor: tokens.primary,
|
|
padding: 14,
|
|
}}
|
|
>
|
|
<Text style={{ color: tokens.primaryForeground, fontSize: 15, fontWeight: "600" }}>
|
|
{t("Open conversation")}
|
|
</Text>
|
|
</Pressable>
|
|
</>
|
|
) : null}
|
|
</ScrollView>
|
|
);
|
|
}
|