authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-17 20:29:11-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-17 20:29:11-07:00
logcc39ec1008ce584e088e79210b69d3359357463b
tree7f02722753b106875c506841ca37cc9ce451012c
parentc7fa1e96943f380a8893e89460d03a362285ab83
signature Signed by PGP key B5690EEEBB952194

fix(mobile): share request action button defaults (#12366)


4 files changed, 64 insertions(+), 43 deletions(-)

apps/mobile/src/features/threads/PendingApprovalCard.tsx+10-20
......@@ -1,9 +1,10 @@
1import { RequestActionButton } from "./RequestActionButton";
12import type {
23 ApprovalRequestId,
34 ProviderApprovalDecision,
45 ProviderApprovalOption,
56} from "@t3tools/contracts";
6import { Pressable, View } from "react-native";
7import { View } from "react-native";
78
89import { AppText as Text } from "../../components/AppText";
910import type { PendingApproval } from "../../lib/threadActivity";
......@@ -47,30 +48,19 @@ export function PendingApprovalCard(props: PendingApprovalCardProps) {
4748 ) : null}
4849 <View className="flex-row flex-wrap gap-2.5">
4950 {options.map((option) => (
50 <Pressable
51 <RequestActionButton
5152 key={option.decision}
52 className={`items-center justify-center rounded-[14px] px-3.5 py-3 ${
53 label={option.label}
54 tone={
5355 option.decision === "accept"
54 ? "bg-primary"
56 ? "primary"
5557 : option.decision === "decline"
56 ? "bg-danger"
57 : "bg-subtle-strong"
58 }`}
58 ? "danger"
59 : "secondary"
60 }
5961 disabled={props.respondingApprovalId === props.approval.requestId}
6062 onPress={() => void props.onRespond(props.approval.requestId, option.decision)}
61 >
62 <Text
63 className={`text-sm ${
64 option.decision === "accept"
65 ? "font-t3-extrabold text-primary-foreground"
66 : option.decision === "decline"
67 ? "font-t3-bold text-danger-foreground"
68 : "font-t3-bold text-foreground"
69 }`}
70 >
71 {option.label}
72 </Text>
73 </Pressable>
63 />
7464 ))}
7565 </View>
7666 </View>
apps/mobile/src/features/threads/PendingUserInputCard.tsx+6-15
......@@ -1,3 +1,4 @@
1import { RequestActionButton } from "./RequestActionButton";
12import { QuestionAttachments } from "./QuestionAttachments";
23import type { ApprovalRequestId, UserInputQuestion } from "@t3tools/contracts";
34import { useCallback, useRef } from "react";
......@@ -321,25 +322,15 @@ export function PendingUserInputCard(props: PendingUserInputCardProps) {
321322 );
322323 })}
323324 </ScrollView>
324 <Pressable
325 className={cn(
326 "items-center justify-center rounded-2xl px-4 py-3.5",
327 props.answers ? "bg-primary" : "bg-subtle-strong",
328 )}
325 <RequestActionButton
326 label="Submit answers"
327 size="large"
328 tone={props.answers ? "primary" : "secondary"}
329329 disabled={
330330 props.answers === null || props.respondingUserInputId === props.pendingUserInput.requestId
331331 }
332332 onPress={() => void props.onSubmit()}
333 >
334 <Text
335 className={cn(
336 "font-t3-extrabold text-sm",
337 props.answers ? "text-primary-foreground" : "text-foreground-muted",
338 )}
339 >
340 Submit answers
341 </Text>
342 </Pressable>
333 />
343334 {props.pendingUserInput.dismissible ? (
344335 <Pressable
345336 accessibilityRole="button"
apps/mobile/src/features/threads/RequestActionButton.tsx created+45
......@@ -0,0 +1,45 @@
1import type { ComponentProps } from "react";
2import { Pressable } from "react-native";
3
4import { AppText as Text } from "../../components/AppText";
5import { cn } from "../../lib/cn";
6
7/** Primary actions in the cards that replace the thread composer. */
8export function RequestActionButton({
9 label,
10 tone = "primary",
11 size = "default",
12 disabled,
13 ...props
14}: Omit<ComponentProps<typeof Pressable>, "children" | "className" | "style"> & {
15 label: string;
16 tone?: "primary" | "secondary" | "danger";
17 size?: "default" | "large";
18}) {
19 return (
20 <Pressable
21 {...props}
22 accessibilityRole="button"
23 accessibilityState={{ ...props.accessibilityState, disabled: Boolean(disabled) }}
24 disabled={disabled}
25 className={cn(
26 "items-center justify-center active:opacity-70 disabled:opacity-50",
27 size === "large" ? "rounded-2xl px-4 py-3.5" : "rounded-[14px] px-3.5 py-3",
28 tone === "primary" ? "bg-primary" : tone === "danger" ? "bg-danger" : "bg-subtle-strong",
29 )}
30 >
31 <Text
32 className={cn(
33 "text-sm",
34 tone === "primary"
35 ? "font-t3-extrabold text-primary-foreground"
36 : tone === "danger"
37 ? "font-t3-bold text-danger-foreground"
38 : "font-t3-bold text-foreground",
39 )}
40 >
41 {label}
42 </Text>
43 </Pressable>
44 );
45}
apps/mobile/src/features/threads/ThreadCreationFailedCard.tsx+3-8
......@@ -1,4 +1,5 @@
1import { Pressable, View } from "react-native";
1import { RequestActionButton } from "./RequestActionButton";
2import { View } from "react-native";
23
34import { AppText as Text } from "../../components/AppText";
45
......@@ -23,13 +24,7 @@ export function ThreadCreationFailedCard(props: {
2324 Your prompt was kept in the project draft.
2425 </Text>
2526 <View className="flex-row">
26 <Pressable
27 accessibilityRole="button"
28 className="items-center justify-center rounded-[14px] bg-blue-500 px-3.5 py-3"
29 onPress={props.onEditTask}
30 >
31 <Text className="text-sm font-t3-extrabold text-white">Edit task</Text>
32 </Pressable>
27 <RequestActionButton label="Edit task" onPress={props.onEditTask} />
3328 </View>
3429 </View>
3530 );