History

Expose V2 thread workflows on mobile

Julius Marminge committed 4 months ago

Commit beab58b1 parent 2fde64e16 files · +1024 −217Browse files

Showing the changes to apps/mobile/src/features/threads/ThreadQueueControl.tsx (1 of 16 files). Show all

Unified · Split

apps/mobile/src/features/threads/ThreadQueueControl.tsx+115−0
@@ -0,0 +1,115 @@
1+import * as Haptics from "expo-haptics";
2+import { SymbolView } from "expo-symbols";
3+import { deriveThreadQueueWorkflowState } from "@t3tools/client-runtime/state/thread-workflows";
4+import type { EnvironmentId, RunId, ThreadId } from "@t3tools/contracts";
5+import { useMemo, useState } from "react";
6+import { Pressable, ScrollView, View } from "react-native";
7+
8+import { AppText as Text } from "../../components/AppText";
9+import { useThemeColor } from "../../lib/useThemeColor";
10+import { threadEnvironment } from "../../state/threads";
11+import { useAtomCommand } from "../../state/use-atom-command";
12+import { useThreadProjection } from "../../state/use-thread-detail";
13+
14+export function ThreadQueueControl(props: {
15+ readonly environmentId: EnvironmentId;
16+ readonly threadId: ThreadId;
17+}) {
18+ const scoped = useThreadProjection(props);
19+ const workflow = useMemo(
20+ () => (scoped ? deriveThreadQueueWorkflowState(scoped.projection) : null),
21+ [scoped],
22+ );
23+ const reorder = useAtomCommand(threadEnvironment.reorderQueuedRun, "reorder queued message");
24+ const promote = useAtomCommand(threadEnvironment.promoteQueuedRun, "promote queued message");
25+ const [busyRunId, setBusyRunId] = useState<RunId | null>(null);
26+ const iconColor = useThemeColor("--color-icon-subtle");
27+
28+ if (!workflow || workflow.queuedRuns.length === 0) return null;
29+
30+ const move = async (runId: RunId, beforeRunId: RunId | null) => {
31+ setBusyRunId(runId);
32+ void Haptics.selectionAsync();
33+ await reorder({
34+ environmentId: props.environmentId,
35+ input: { threadId: props.threadId, runId, beforeRunId },
36+ });
37+ setBusyRunId(null);
38+ };
39+
40+ const steer = async (queuedRunId: RunId) => {
41+ if (!workflow.activeRun || !workflow.canPromoteToSteer) return;
42+ setBusyRunId(queuedRunId);
43+ void Haptics.selectionAsync();
44+ await promote({
45+ environmentId: props.environmentId,
46+ input: {
47+ threadId: props.threadId,
48+ queuedRunId,
49+ targetRunId: workflow.activeRun.id,
50+ },
51+ });
52+ setBusyRunId(null);
53+ };
54+
55+ return (
56+ <View className="mx-4 mb-3 overflow-hidden rounded-2xl border border-neutral-300/60 bg-card dark:border-white/[0.1]">
57+ <View className="flex-row items-center gap-2 border-b border-neutral-300/50 px-3 py-2 dark:border-white/[0.08]">
58+ <SymbolView name="list.number" size={13} tintColor={iconColor} type="monochrome" />
59+ <Text className="font-t3-medium text-xs text-foreground">Queue</Text>
60+ <Text className="ml-auto text-2xs tabular-nums text-foreground-muted">
61+ {workflow.queuedRuns.length}
62+ </Text>
63+ </View>
64+ <ScrollView style={{ maxHeight: 156 }} contentContainerStyle={{ paddingVertical: 4 }}>
65+ {workflow.queuedRuns.map(({ run, text }, index) => (
66+ <View key={run.id} className="min-h-11 flex-row items-center gap-1.5 px-2">
67+ <Text className="w-5 text-right text-3xs tabular-nums text-foreground-muted">
68+ {index + 1}
69+ </Text>
70+ <Text className="min-w-0 flex-1 text-xs text-foreground" numberOfLines={1}>
71+ {text}
72+ </Text>
73+ <Pressable
74+ accessibilityRole="button"
75+ accessibilityLabel="Move queued message up"
76+ disabled={busyRunId !== null || !workflow.canReorder || index === 0}
77+ onPress={() => void move(run.id, workflow.queuedRuns[index - 1]?.run.id ?? null)}
78+ className="h-9 w-9 items-center justify-center disabled:opacity-30"
79+ >
80+ <SymbolView name="arrow.up" size={13} tintColor={iconColor} type="monochrome" />
81+ </Pressable>
82+ <Pressable
83+ accessibilityRole="button"
84+ accessibilityLabel="Move queued message down"
85+ disabled={
86+ busyRunId !== null ||
87+ !workflow.canReorder ||
88+ index === workflow.queuedRuns.length - 1
89+ }
90+ onPress={() => void move(run.id, workflow.queuedRuns[index + 2]?.run.id ?? null)}
91+ className="h-9 w-9 items-center justify-center disabled:opacity-30"
92+ >
93+ <SymbolView name="arrow.down" size={13} tintColor={iconColor} type="monochrome" />
94+ </Pressable>
95+ <Pressable
96+ accessibilityRole="button"
97+ accessibilityLabel="Promote queued message to steer"
98+ disabled={busyRunId !== null || !workflow.canPromoteToSteer}
99+ onPress={() => void steer(run.id)}
100+ className="min-h-8 flex-row items-center gap-1 rounded-lg border border-neutral-300/60 px-2 disabled:opacity-30 dark:border-white/[0.1]"
101+ >
102+ <SymbolView
103+ name="arrow.turn.left.up"
104+ size={12}
105+ tintColor={iconColor}
106+ type="monochrome"
107+ />
108+ <Text className="font-t3-medium text-2xs text-foreground">Steer</Text>
109+ </Pressable>
110+ </View>
111+ ))}
112+ </ScrollView>
113+ </View>
114+ );
115+}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t