1import type { EnvironmentId, ThreadId } from "@t3tools/contracts";
2import { ScrollView, View } from "react-native";
3
4import { AppText as Text } from "../../components/AppText";
5import { scriptProgressEnvironment } from "../../state/scriptProgress";
6import { useEnvironmentQuery } from "../../state/query";
7
8export function ScriptProgress({
9 environmentId,
10 threadId,
11}: {
12 environmentId: EnvironmentId;
13 threadId: ThreadId;
14}) {
15 const { data, isSuccess } = useEnvironmentQuery(
16 scriptProgressEnvironment.watch({ environmentId, input: { threadId } }),
17 );
18 const streams = isSuccess
19 ? (data?.streams.filter((stream) => stream.summary.length > 0) ?? [])
20 : [];
21 if (streams.length === 0) return null;
22
23 return (
24 <View accessibilityLabel="Script progress" className="mb-2 rounded-2xl bg-card px-4 py-3">
25 <ScrollView style={{ maxHeight: 180 }}>
26 {streams.map((stream) => (
27 <View key={stream.id} className="gap-2">
28 {stream.status === "disconnected" ? (
29 <Text className="text-xs text-foreground-muted">{stream.label} · Disconnected</Text>
30 ) : null}
31 {stream.summary.map((node, index) => {
32 const fraction =
33 node.total > 0 ? Math.max(0, Math.min(1, node.value / node.total)) : 0;
34 return (
35 // Summary rows are ordered by their progress root; labels may repeat.
36 // oxlint-disable-next-line react/no-array-index-key
37 <View key={index} className="gap-1">
38 <View className="flex-row items-center gap-2">
39 <Text className="min-w-0 flex-1 text-sm text-foreground">{node.text}</Text>
40 <Text className="text-xs text-foreground-muted">{node.detail}</Text>
41 </View>
42 {node.total > 0 ? (
43 <View
44 accessibilityRole="progressbar"
45 accessibilityLabel={node.text}
46 accessibilityValue={{
47 min: 0,
48 max: node.total,
49 now: fraction * node.total,
50 text: node.detail,
51 }}
52 className="h-1 overflow-hidden rounded-full bg-border-subtle"
53 >
54 <View className="h-full bg-primary" style={{ width: `${fraction * 100}%` }} />
55 </View>
56 ) : null}
57 </View>
58 );
59 })}
60 </View>
61 ))}
62 </ScrollView>
63 </View>
64 );
65}