Skip to content
Source
Sign inSign up

PullRequestCompletionSuggestions.tsx

105 lines3.6 KB

1import { Pressable, ScrollView, StyleSheet, View } from "react-native";
2​
3import { AppText as Text } from "../../components/ui/AppText";
4import { GlassSurface } from "../../components/ui/GlassSurface";
5import { PullRequestAvatar } from "./PullRequestPresentation";
6import type { PullRequestCompletion } from "@t3tools/shared/pullRequestComposerTrigger";
7import type { usePullRequestCompletions } from "./usePullRequestCompletions";
8​
9const COPY = {
10 mention: {
11 label: "Mention a user",
12 pending: "Searching users...",
13 failed: "Couldn't search users on the host",
14 none: "No matching users",
15 },
16 reference: {
17 label: "Reference an issue or change request",
18 pending: "Searching...",
19 failed: "Couldn't search the host",
20 none: "No matching issues or change requests",
21 },
22} as const;
23​
24const STATE_LABEL = { open: "Open", closed: "Closed", merged: "Merged" } as const;
25​
26function CompletionRow({ completion }: { readonly completion: PullRequestCompletion }) {
27 if (completion.kind === "mention") {
28 const { actor } = completion;
29 return (
30 <>
31 <PullRequestAvatar actor={actor} size={20} />
32 <Text className="shrink-0 text-base font-t3-medium text-foreground" numberOfLines={1}>
33 {actor.login}
34 </Text>
35 {actor.name && actor.name !== actor.login ? (
36 <Text className="min-w-0 flex-1 text-xs text-foreground-muted" numberOfLines={1}>
37 {actor.name}
38 </Text>
39 ) : null}
40 </>
41 );
42 }
43 const { candidate } = completion;
44 return (
45 <>
46 <Text className="shrink-0 text-base font-t3-medium text-foreground" numberOfLines={1}>
47 {candidate.reference}
48 </Text>
49 <Text className="min-w-0 flex-1 text-sm text-foreground-muted" numberOfLines={1}>
50 {candidate.title}
51 </Text>
52 <Text className="shrink-0 text-xs text-foreground-tertiary">
53 {STATE_LABEL[candidate.state]}
54 </Text>
55 </>
56 );
58​
59/** The `@`, `#` and `!` suggestions for a pull request text input, rendered just above it. */
60export function PullRequestCompletionSuggestions({
61 completions,
62}: {
63 readonly completions: ReturnType<typeof usePullRequestCompletions>;
64}) {
65 if (completions.kind === null) return null;
66 const copy = COPY[completions.kind];
67 return (
68 <GlassSurface
69 glassEffectStyle="clear"
70 tintColorClassName="accent-glass-surface"
71 style={{ borderRadius: 16, overflow: "hidden" }}
72 >
73 {completions.candidates.length > 0 ? (
74 <ScrollView
75 className="max-h-[180px]"
76 keyboardShouldPersistTaps="always"
77 showsVerticalScrollIndicator={false}
78 accessibilityLabel={copy.label}
79 >
80 {completions.candidates.map((completion, index) => (
81 <Pressable
82 key={completion.token}
83 accessibilityRole="button"
84 accessibilityLabel={`Insert ${completion.token}`}
85 onPress={() => completions.select(completion)}
86 className="flex-row items-center gap-2.5 border-border px-3.5 py-2.5 active:opacity-60"
87 style={{
88 borderBottomWidth:
89 index === completions.candidates.length - 1 ? 0 : StyleSheet.hairlineWidth,
90 }}
91 >
92 <CompletionRow completion={completion} />
93 </Pressable>
94 ))}
95 </ScrollView>
96 ) : (
97 <View className="px-3.5 py-2.5">
98 <Text className="text-xs text-foreground-tertiary">
99 {copy[completions.isLoading ? "pending" : completions.failed ? "failed" : "none"]}
100 </Text>
101 </View>
102 )}
103 </GlassSurface>
104 );