@planningcenter/chat-react-native 3.39.0-rc.1 → 3.39.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"use_direct_message_candidates.d.ts","sourceRoot":"","sources":["../../../src/hooks/direct_messages/use_direct_message_candidates.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,8BAA8B,EAAE,MAAM,gDAAgD,CAAA;AAG/F,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,wGAwBtD"}
1
+ {"version":3,"file":"use_direct_message_candidates.d.ts","sourceRoot":"","sources":["../../../src/hooks/direct_messages/use_direct_message_candidates.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,8BAA8B,EAAE,MAAM,gDAAgD,CAAA;AAG/F,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,wGAyBtD"}
@@ -22,6 +22,7 @@ export function useDirectMessageCandidates(term) {
22
22
  }),
23
23
  enabled: term.length >= 1,
24
24
  staleTime: 1000 * 60,
25
+ throwOnError: true,
25
26
  });
26
27
  }
27
28
  //# sourceMappingURL=use_direct_message_candidates.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use_direct_message_candidates.js","sourceRoot":"","sources":["../../../src/hooks/direct_messages/use_direct_message_candidates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAGhD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,MAAM,UAAU,0BAA0B,CAAC,IAAY;IACrD,MAAM,SAAS,GAAG,YAAY,EAAE,CAAA;IAChC,OAAO,QAAQ,CAAC;QACd,QAAQ,EAAE,CAAC,MAAM,EAAE,+BAA+B,EAAE,IAAI,CAAC;QACzD,OAAO,EAAE,GAAG,EAAE,CACZ,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;YACjB,GAAG,EAAE,+BAA+B;YACpC,IAAI,EAAE;gBACJ,CAAC,EAAE,IAAI;gBACP,MAAM,EAAE;oBACN,sBAAsB,EAAE;wBACtB,MAAM;wBACN,QAAQ;wBACR,QAAQ;wBACR,OAAO;wBACP,UAAU;wBACV,0BAA0B;qBAC3B;iBACF;aACF;SACF,CAA2D;QAC9D,OAAO,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC;QACzB,SAAS,EAAE,IAAI,GAAG,EAAE;KACrB,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import { useQuery } from '@tanstack/react-query'\nimport { ApiCollection } from '../../types'\nimport { DirectMessageCandidateResource } from '../../types/resources/direct_message_candidate'\nimport { useApiClient } from '../use_api_client'\n\nexport function useDirectMessageCandidates(term: string) {\n const apiClient = useApiClient()\n return useQuery({\n queryKey: ['chat', '/me/direct_message_candidates', term],\n queryFn: () =>\n apiClient.chat.get({\n url: '/me/direct_message_candidates',\n data: {\n q: term,\n fields: {\n DirectMessageCandidate: [\n 'name',\n 'avatar',\n 'groups',\n 'teams',\n 'under_18',\n 'direct_messages_disabled',\n ],\n },\n },\n }) as Promise<ApiCollection<DirectMessageCandidateResource>>,\n enabled: term.length >= 1,\n staleTime: 1000 * 60,\n })\n}\n"]}
1
+ {"version":3,"file":"use_direct_message_candidates.js","sourceRoot":"","sources":["../../../src/hooks/direct_messages/use_direct_message_candidates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAGhD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAEhD,MAAM,UAAU,0BAA0B,CAAC,IAAY;IACrD,MAAM,SAAS,GAAG,YAAY,EAAE,CAAA;IAChC,OAAO,QAAQ,CAAC;QACd,QAAQ,EAAE,CAAC,MAAM,EAAE,+BAA+B,EAAE,IAAI,CAAC;QACzD,OAAO,EAAE,GAAG,EAAE,CACZ,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC;YACjB,GAAG,EAAE,+BAA+B;YACpC,IAAI,EAAE;gBACJ,CAAC,EAAE,IAAI;gBACP,MAAM,EAAE;oBACN,sBAAsB,EAAE;wBACtB,MAAM;wBACN,QAAQ;wBACR,QAAQ;wBACR,OAAO;wBACP,UAAU;wBACV,0BAA0B;qBAC3B;iBACF;aACF;SACF,CAA2D;QAC9D,OAAO,EAAE,IAAI,CAAC,MAAM,IAAI,CAAC;QACzB,SAAS,EAAE,IAAI,GAAG,EAAE;QACpB,YAAY,EAAE,IAAI;KACnB,CAAC,CAAA;AACJ,CAAC","sourcesContent":["import { useQuery } from '@tanstack/react-query'\nimport { ApiCollection } from '../../types'\nimport { DirectMessageCandidateResource } from '../../types/resources/direct_message_candidate'\nimport { useApiClient } from '../use_api_client'\n\nexport function useDirectMessageCandidates(term: string) {\n const apiClient = useApiClient()\n return useQuery({\n queryKey: ['chat', '/me/direct_message_candidates', term],\n queryFn: () =>\n apiClient.chat.get({\n url: '/me/direct_message_candidates',\n data: {\n q: term,\n fields: {\n DirectMessageCandidate: [\n 'name',\n 'avatar',\n 'groups',\n 'teams',\n 'under_18',\n 'direct_messages_disabled',\n ],\n },\n },\n }) as Promise<ApiCollection<DirectMessageCandidateResource>>,\n enabled: term.length >= 1,\n staleTime: 1000 * 60,\n throwOnError: true,\n })\n}\n"]}
@@ -1,4 +1,3 @@
1
- import React from 'react';
2
1
  import { ConversationSelectRecipientsScreenProps } from './types/screen_props';
3
- export declare function ConversationSelectDirectMessageRecipientsScreen(_props: ConversationSelectRecipientsScreenProps): React.JSX.Element;
2
+ export declare function ConversationSelectDirectMessageRecipientsScreen(_props: ConversationSelectRecipientsScreenProps): import("react").JSX.Element;
4
3
  //# sourceMappingURL=conversation_select_direct_message_recipients_screen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation_select_direct_message_recipients_screen.d.ts","sourceRoot":"","sources":["../../../src/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,EAAE,uCAAuC,EAAE,MAAM,sBAAsB,CAAA;AAE9E,wBAAgB,+CAA+C,CAC7D,MAAM,EAAE,uCAAuC,qBAUhD"}
1
+ {"version":3,"file":"conversation_select_direct_message_recipients_screen.d.ts","sourceRoot":"","sources":["../../../src/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.tsx"],"names":[],"mappings":"AASA,OAAO,EAAE,uCAAuC,EAAE,MAAM,sBAAsB,CAAA;AAI9E,wBAAgB,+CAA+C,CAC7D,MAAM,EAAE,uCAAuC,+BAwDhD"}
@@ -1,25 +1,261 @@
1
- import React from 'react';
2
- import { StyleSheet, View } from 'react-native';
1
+ import { PlatformPressable } from '@react-navigation/elements';
2
+ import { useEffect, useState } from 'react';
3
+ import { FlatList, Pressable, StyleSheet, TextInput, View } from 'react-native';
3
4
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
4
- import { Heading, Text } from '../../components';
5
+ import { Avatar, Banner, Icon, Spinner, Text } from '../../components';
6
+ import { ComponentErrorBoundary } from '../../components/page/component_error_boundary';
7
+ import { useTheme } from '../../hooks';
8
+ import { useDirectMessageCandidates } from '../../hooks/direct_messages/use_direct_message_candidates';
9
+ const MAX_RECIPIENTS = 9;
5
10
  export function ConversationSelectDirectMessageRecipientsScreen(_props) {
6
11
  const styles = useStyles();
12
+ const [searchTerm, setSearchTerm] = useState('');
13
+ const [debouncedTerm, setDebouncedTerm] = useState('');
14
+ const [selectedPeople, setSelectedPeople] = useState(new Map());
15
+ useEffect(() => {
16
+ const timer = setTimeout(() => setDebouncedTerm(searchTerm), 200);
17
+ return () => clearTimeout(timer);
18
+ }, [searchTerm]);
19
+ const selectPerson = (person) => {
20
+ if (selectedPeople.size >= MAX_RECIPIENTS)
21
+ return;
22
+ setSelectedPeople(prev => new Map(prev).set(person.id, person));
23
+ setSearchTerm('');
24
+ setDebouncedTerm('');
25
+ };
26
+ const removePerson = (id) => {
27
+ setSelectedPeople(prev => {
28
+ const next = new Map(prev);
29
+ next.delete(id);
30
+ return next;
31
+ });
32
+ };
7
33
  return (<View style={styles.container}>
8
- <Heading variant="h2">New direct message</Heading>
9
- <Text variant="secondary">Coming soon</Text>
34
+ <ToField selectedPeople={selectedPeople} searchTerm={searchTerm} onSearchChange={setSearchTerm} onRemove={removePerson} onClear={() => setSelectedPeople(new Map())}/>
35
+ <ComponentErrorBoundary key={debouncedTerm} fallback={<Banner appearance="error" description="Couldn't load people right now. Try again." style={styles.errorBanner}/>}>
36
+ <CandidateResults term={debouncedTerm} selectedPeople={selectedPeople} onSelect={selectPerson}/>
37
+ </ComponentErrorBoundary>
10
38
  </View>);
11
39
  }
40
+ function ToField({ selectedPeople, searchTerm, onSearchChange, onRemove, onClear, }) {
41
+ const styles = useStyles();
42
+ const { colors } = useTheme();
43
+ const hasSelections = selectedPeople.size > 0;
44
+ return (<View style={styles.toField}>
45
+ <View style={styles.toFieldHeader}>
46
+ <Text style={styles.toLabel}>To</Text>
47
+ {hasSelections && (<Pressable onPress={onClear} hitSlop={8}>
48
+ <Text style={styles.clearButton}>Clear</Text>
49
+ </Pressable>)}
50
+ </View>
51
+ <View style={styles.toInputArea}>
52
+ {[...selectedPeople.values()].map(person => (<PersonChip key={person.id} person={person} onRemove={onRemove}/>))}
53
+ <TextInput autoFocus placeholder="Search" placeholderTextColor={colors.textColorDefaultPlaceholder} value={searchTerm} onChangeText={onSearchChange} style={styles.toTextInput} returnKeyType="search"/>
54
+ </View>
55
+ </View>);
56
+ }
57
+ function PersonChip({ person, onRemove, }) {
58
+ const styles = useStyles();
59
+ return (<View style={styles.chip}>
60
+ <Avatar sourceUri={person.avatar} size="sm" showFallback/>
61
+ <Text style={styles.chipName}>{person.name}</Text>
62
+ <Pressable onPress={() => onRemove(person.id)} hitSlop={8} accessibilityLabel={`Remove ${person.name}`}>
63
+ <Icon name="general.x" size={12} style={styles.chipRemoveIcon}/>
64
+ </Pressable>
65
+ </View>);
66
+ }
67
+ function CandidateResults({ term, selectedPeople, onSelect, }) {
68
+ const styles = useStyles();
69
+ const { data, isFetching } = useDirectMessageCandidates(term);
70
+ if (term.length < 1) {
71
+ if (selectedPeople.size > 0)
72
+ return null;
73
+ return (<View style={styles.emptyState}>
74
+ <Icon name="churchCenter.people" size={40} style={styles.emptyStateIcon}/>
75
+ <Text style={styles.emptyStateTitle}>Search for people to direct message</Text>
76
+ <Text variant="secondary" style={styles.emptyStateSubtitle}>
77
+ Start a conversation with people in your groups and teams.
78
+ </Text>
79
+ </View>);
80
+ }
81
+ if (selectedPeople.size >= MAX_RECIPIENTS) {
82
+ return (<View style={styles.emptyState}>
83
+ <Text variant="secondary" style={styles.emptyStateSubtitle}>
84
+ You can add up to {MAX_RECIPIENTS} people to a direct message.
85
+ </Text>
86
+ </View>);
87
+ }
88
+ const unselectedCandidates = (data?.data ?? []).filter(c => !selectedPeople.has(c.id));
89
+ if (isFetching && unselectedCandidates.length === 0) {
90
+ return (<View style={styles.emptyState}>
91
+ <Spinner />
92
+ </View>);
93
+ }
94
+ if (!isFetching && unselectedCandidates.length === 0) {
95
+ return (<View style={styles.emptyState}>
96
+ <Icon name="general.search" size={32} style={styles.emptyStateIcon}/>
97
+ <Text style={styles.emptyStateTitle}>No matches for "{term}"</Text>
98
+ <Text variant="secondary" style={styles.emptyStateSubtitle}>
99
+ Try a different name, or check that you share a group or team with them.
100
+ </Text>
101
+ </View>);
102
+ }
103
+ const hasTruncated = (data?.meta.totalCount ?? 0) > (data?.data.length ?? 0);
104
+ return (<CandidateList candidates={unselectedCandidates} hasTruncated={hasTruncated} onSelect={onSelect}/>);
105
+ }
106
+ function CandidateList({ candidates, hasTruncated, onSelect, }) {
107
+ const styles = useStyles();
108
+ const available = candidates.filter(c => !c.directMessagesDisabled);
109
+ const dmDisabled = candidates.filter(c => c.directMessagesDisabled);
110
+ return (<FlatList data={available} keyExtractor={item => item.id} renderItem={({ item }) => <CandidateItem candidate={item} onPress={() => onSelect(item)}/>} ListHeaderComponent={<View style={styles.resultsHeader}>
111
+ <Text style={styles.resultsHeading}>Results</Text>
112
+ </View>} ListFooterComponent={<>
113
+ {dmDisabled.length > 0 && (<>
114
+ <View style={styles.unavailableHeader}>
115
+ <Text variant="secondary" style={styles.unavailableLabel}>
116
+ Unavailable for direct messages
117
+ </Text>
118
+ </View>
119
+ {dmDisabled.map(candidate => (<CandidateItem key={candidate.id} candidate={candidate} disabled/>))}
120
+ </>)}
121
+ {hasTruncated && (<View style={styles.truncatedFooter}>
122
+ <Text variant="secondary" style={styles.truncatedText}>
123
+ Keep typing to narrow your results.
124
+ </Text>
125
+ </View>)}
126
+ </>}/>);
127
+ }
128
+ function CandidateItem({ candidate, onPress, disabled = false, }) {
129
+ const styles = useStyles();
130
+ return (<PlatformPressable onPress={disabled ? undefined : onPress} accessibilityRole="button" accessibilityLabel={`Select ${candidate.name}`} accessibilityState={{ disabled }}>
131
+ <View style={[styles.row, disabled && styles.candidateDisabled]}>
132
+ <Avatar sourceUri={candidate.avatar} size="lg" showFallback/>
133
+ <Text style={styles.rowName}>{candidate.name}</Text>
134
+ </View>
135
+ </PlatformPressable>);
136
+ }
12
137
  const useStyles = () => {
13
138
  const { bottom } = useSafeAreaInsets();
139
+ const { colors } = useTheme();
14
140
  return StyleSheet.create({
15
141
  container: {
16
142
  flex: 1,
17
- padding: 16,
18
- paddingBottom: 16 + bottom,
143
+ paddingBottom: bottom,
144
+ },
145
+ toField: {
146
+ paddingHorizontal: 16,
147
+ paddingTop: 16,
148
+ paddingBottom: 8,
149
+ },
150
+ toFieldHeader: {
151
+ flexDirection: 'row',
152
+ justifyContent: 'space-between',
153
+ alignItems: 'center',
154
+ marginBottom: 8,
155
+ },
156
+ toLabel: {
157
+ fontWeight: '600',
158
+ color: colors.textColorDefaultSecondary,
159
+ },
160
+ clearButton: {
161
+ color: colors.interaction,
162
+ },
163
+ toInputArea: {
164
+ flexDirection: 'row',
165
+ flexWrap: 'wrap',
19
166
  alignItems: 'center',
20
- justifyContent: 'center',
167
+ gap: 6,
168
+ borderWidth: 1,
169
+ borderColor: colors.borderColorDefaultBase,
170
+ borderRadius: 8,
171
+ paddingHorizontal: 10,
172
+ paddingVertical: 8,
173
+ },
174
+ toTextInput: {
175
+ flex: 1,
176
+ minWidth: 80,
177
+ color: colors.textColorDefaultPrimary,
178
+ fontSize: 16,
179
+ paddingVertical: 2,
180
+ },
181
+ chip: {
182
+ flexDirection: 'row',
183
+ alignItems: 'center',
184
+ gap: 4,
185
+ borderWidth: 1,
186
+ borderColor: colors.borderColorDefaultBase,
187
+ borderRadius: 100,
188
+ paddingLeft: 4,
189
+ paddingRight: 6,
190
+ paddingVertical: 3,
191
+ },
192
+ chipName: {
193
+ fontSize: 14,
194
+ },
195
+ chipRemoveIcon: {
196
+ color: colors.iconColorDefaultSecondary,
197
+ },
198
+ errorBanner: {
199
+ margin: 16,
200
+ },
201
+ emptyState: {
202
+ alignItems: 'center',
203
+ paddingTop: 32,
204
+ paddingHorizontal: 32,
21
205
  gap: 8,
22
206
  },
207
+ emptyStateIcon: {
208
+ color: colors.iconColorDefaultSecondary,
209
+ marginBottom: 4,
210
+ },
211
+ emptyStateTitle: {
212
+ fontWeight: '600',
213
+ textAlign: 'center',
214
+ },
215
+ emptyStateSubtitle: {
216
+ textAlign: 'center',
217
+ fontSize: 14,
218
+ },
219
+ row: {
220
+ flexDirection: 'row',
221
+ alignItems: 'center',
222
+ gap: 12,
223
+ paddingHorizontal: 16,
224
+ paddingVertical: 12,
225
+ borderBottomWidth: 1,
226
+ borderColor: colors.fillColorNeutral050Base,
227
+ },
228
+ rowName: {
229
+ flex: 1,
230
+ },
231
+ candidateDisabled: {
232
+ opacity: 0.5,
233
+ },
234
+ resultsHeader: {
235
+ paddingHorizontal: 16,
236
+ paddingTop: 16,
237
+ paddingBottom: 8,
238
+ },
239
+ resultsHeading: {
240
+ fontWeight: '600',
241
+ },
242
+ unavailableHeader: {
243
+ paddingHorizontal: 16,
244
+ paddingTop: 16,
245
+ paddingBottom: 8,
246
+ },
247
+ unavailableLabel: {
248
+ fontSize: 13,
249
+ },
250
+ truncatedFooter: {
251
+ paddingHorizontal: 16,
252
+ paddingTop: 12,
253
+ paddingBottom: 8,
254
+ },
255
+ truncatedText: {
256
+ fontSize: 13,
257
+ textAlign: 'center',
258
+ },
23
259
  });
24
260
  };
25
261
  //# sourceMappingURL=conversation_select_direct_message_recipients_screen.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation_select_direct_message_recipients_screen.js","sourceRoot":"","sources":["../../../src/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAGhD,MAAM,UAAU,+CAA+C,CAC7D,MAA+C;IAE/C,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAE1B,OAAO,CACL,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAC5B;MAAA,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,OAAO,CACjD;MAAA,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,WAAW,EAAE,IAAI,CAC7C;IAAA,EAAE,IAAI,CAAC,CACR,CAAA;AACH,CAAC;AAED,MAAM,SAAS,GAAG,GAAG,EAAE;IACrB,MAAM,EAAE,MAAM,EAAE,GAAG,iBAAiB,EAAE,CAAA;IAEtC,OAAO,UAAU,CAAC,MAAM,CAAC;QACvB,SAAS,EAAE;YACT,IAAI,EAAE,CAAC;YACP,OAAO,EAAE,EAAE;YACX,aAAa,EAAE,EAAE,GAAG,MAAM;YAC1B,UAAU,EAAE,QAAQ;YACpB,cAAc,EAAE,QAAQ;YACxB,GAAG,EAAE,CAAC;SACP;KACF,CAAC,CAAA;AACJ,CAAC,CAAA","sourcesContent":["import React from 'react'\nimport { StyleSheet, View } from 'react-native'\nimport { useSafeAreaInsets } from 'react-native-safe-area-context'\nimport { Heading, Text } from '../../components'\nimport { ConversationSelectRecipientsScreenProps } from './types/screen_props'\n\nexport function ConversationSelectDirectMessageRecipientsScreen(\n _props: ConversationSelectRecipientsScreenProps\n) {\n const styles = useStyles()\n\n return (\n <View style={styles.container}>\n <Heading variant=\"h2\">New direct message</Heading>\n <Text variant=\"secondary\">Coming soon</Text>\n </View>\n )\n}\n\nconst useStyles = () => {\n const { bottom } = useSafeAreaInsets()\n\n return StyleSheet.create({\n container: {\n flex: 1,\n padding: 16,\n paddingBottom: 16 + bottom,\n alignItems: 'center',\n justifyContent: 'center',\n gap: 8,\n },\n })\n}\n"]}
1
+ {"version":3,"file":"conversation_select_direct_message_recipients_screen.js","sourceRoot":"","sources":["../../../src/screens/conversation_select_recipients/conversation_select_direct_message_recipients_screen.tsx"],"names":[],"mappings":"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","sourcesContent":["import { PlatformPressable } from '@react-navigation/elements'\nimport { useEffect, useState } from 'react'\nimport { FlatList, Pressable, StyleSheet, TextInput, View } from 'react-native'\nimport { useSafeAreaInsets } from 'react-native-safe-area-context'\nimport { Avatar, Banner, Icon, Spinner, Text } from '../../components'\nimport { ComponentErrorBoundary } from '../../components/page/component_error_boundary'\nimport { useTheme } from '../../hooks'\nimport { useDirectMessageCandidates } from '../../hooks/direct_messages/use_direct_message_candidates'\nimport { DirectMessageCandidateResource } from '../../types/resources/direct_message_candidate'\nimport { ConversationSelectRecipientsScreenProps } from './types/screen_props'\n\nconst MAX_RECIPIENTS = 9\n\nexport function ConversationSelectDirectMessageRecipientsScreen(\n _props: ConversationSelectRecipientsScreenProps\n) {\n const styles = useStyles()\n const [searchTerm, setSearchTerm] = useState('')\n const [debouncedTerm, setDebouncedTerm] = useState('')\n const [selectedPeople, setSelectedPeople] = useState<Map<string, DirectMessageCandidateResource>>(\n new Map()\n )\n\n useEffect(() => {\n const timer = setTimeout(() => setDebouncedTerm(searchTerm), 200)\n return () => clearTimeout(timer)\n }, [searchTerm])\n\n const selectPerson = (person: DirectMessageCandidateResource) => {\n if (selectedPeople.size >= MAX_RECIPIENTS) return\n setSelectedPeople(prev => new Map(prev).set(person.id, person))\n setSearchTerm('')\n setDebouncedTerm('')\n }\n\n const removePerson = (id: string) => {\n setSelectedPeople(prev => {\n const next = new Map(prev)\n next.delete(id)\n return next\n })\n }\n\n return (\n <View style={styles.container}>\n <ToField\n selectedPeople={selectedPeople}\n searchTerm={searchTerm}\n onSearchChange={setSearchTerm}\n onRemove={removePerson}\n onClear={() => setSelectedPeople(new Map())}\n />\n <ComponentErrorBoundary\n key={debouncedTerm}\n fallback={\n <Banner\n appearance=\"error\"\n description=\"Couldn't load people right now. Try again.\"\n style={styles.errorBanner}\n />\n }\n >\n <CandidateResults\n term={debouncedTerm}\n selectedPeople={selectedPeople}\n onSelect={selectPerson}\n />\n </ComponentErrorBoundary>\n </View>\n )\n}\n\nfunction ToField({\n selectedPeople,\n searchTerm,\n onSearchChange,\n onRemove,\n onClear,\n}: {\n selectedPeople: Map<string, DirectMessageCandidateResource>\n searchTerm: string\n onSearchChange: (term: string) => void\n onRemove: (id: string) => void\n onClear: () => void\n}) {\n const styles = useStyles()\n const { colors } = useTheme()\n const hasSelections = selectedPeople.size > 0\n\n return (\n <View style={styles.toField}>\n <View style={styles.toFieldHeader}>\n <Text style={styles.toLabel}>To</Text>\n {hasSelections && (\n <Pressable onPress={onClear} hitSlop={8}>\n <Text style={styles.clearButton}>Clear</Text>\n </Pressable>\n )}\n </View>\n <View style={styles.toInputArea}>\n {[...selectedPeople.values()].map(person => (\n <PersonChip key={person.id} person={person} onRemove={onRemove} />\n ))}\n <TextInput\n autoFocus\n placeholder=\"Search\"\n placeholderTextColor={colors.textColorDefaultPlaceholder}\n value={searchTerm}\n onChangeText={onSearchChange}\n style={styles.toTextInput}\n returnKeyType=\"search\"\n />\n </View>\n </View>\n )\n}\n\nfunction PersonChip({\n person,\n onRemove,\n}: {\n person: DirectMessageCandidateResource\n onRemove: (id: string) => void\n}) {\n const styles = useStyles()\n\n return (\n <View style={styles.chip}>\n <Avatar sourceUri={person.avatar} size=\"sm\" showFallback />\n <Text style={styles.chipName}>{person.name}</Text>\n <Pressable\n onPress={() => onRemove(person.id)}\n hitSlop={8}\n accessibilityLabel={`Remove ${person.name}`}\n >\n <Icon name=\"general.x\" size={12} style={styles.chipRemoveIcon} />\n </Pressable>\n </View>\n )\n}\n\nfunction CandidateResults({\n term,\n selectedPeople,\n onSelect,\n}: {\n term: string\n selectedPeople: Map<string, DirectMessageCandidateResource>\n onSelect: (candidate: DirectMessageCandidateResource) => void\n}) {\n const styles = useStyles()\n const { data, isFetching } = useDirectMessageCandidates(term)\n\n if (term.length < 1) {\n if (selectedPeople.size > 0) return null\n\n return (\n <View style={styles.emptyState}>\n <Icon name=\"churchCenter.people\" size={40} style={styles.emptyStateIcon} />\n <Text style={styles.emptyStateTitle}>Search for people to direct message</Text>\n <Text variant=\"secondary\" style={styles.emptyStateSubtitle}>\n Start a conversation with people in your groups and teams.\n </Text>\n </View>\n )\n }\n\n if (selectedPeople.size >= MAX_RECIPIENTS) {\n return (\n <View style={styles.emptyState}>\n <Text variant=\"secondary\" style={styles.emptyStateSubtitle}>\n You can add up to {MAX_RECIPIENTS} people to a direct message.\n </Text>\n </View>\n )\n }\n\n const unselectedCandidates = (data?.data ?? []).filter(c => !selectedPeople.has(c.id))\n\n if (isFetching && unselectedCandidates.length === 0) {\n return (\n <View style={styles.emptyState}>\n <Spinner />\n </View>\n )\n }\n\n if (!isFetching && unselectedCandidates.length === 0) {\n return (\n <View style={styles.emptyState}>\n <Icon name=\"general.search\" size={32} style={styles.emptyStateIcon} />\n <Text style={styles.emptyStateTitle}>No matches for \"{term}\"</Text>\n <Text variant=\"secondary\" style={styles.emptyStateSubtitle}>\n Try a different name, or check that you share a group or team with them.\n </Text>\n </View>\n )\n }\n\n const hasTruncated = (data?.meta.totalCount ?? 0) > (data?.data.length ?? 0)\n\n return (\n <CandidateList\n candidates={unselectedCandidates}\n hasTruncated={hasTruncated}\n onSelect={onSelect}\n />\n )\n}\n\nfunction CandidateList({\n candidates,\n hasTruncated,\n onSelect,\n}: {\n candidates: DirectMessageCandidateResource[]\n hasTruncated: boolean\n onSelect: (candidate: DirectMessageCandidateResource) => void\n}) {\n const styles = useStyles()\n const available = candidates.filter(c => !c.directMessagesDisabled)\n const dmDisabled = candidates.filter(c => c.directMessagesDisabled)\n\n return (\n <FlatList<DirectMessageCandidateResource>\n data={available}\n keyExtractor={item => item.id}\n renderItem={({ item }) => <CandidateItem candidate={item} onPress={() => onSelect(item)} />}\n ListHeaderComponent={\n <View style={styles.resultsHeader}>\n <Text style={styles.resultsHeading}>Results</Text>\n </View>\n }\n ListFooterComponent={\n <>\n {dmDisabled.length > 0 && (\n <>\n <View style={styles.unavailableHeader}>\n <Text variant=\"secondary\" style={styles.unavailableLabel}>\n Unavailable for direct messages\n </Text>\n </View>\n {dmDisabled.map(candidate => (\n <CandidateItem key={candidate.id} candidate={candidate} disabled />\n ))}\n </>\n )}\n {hasTruncated && (\n <View style={styles.truncatedFooter}>\n <Text variant=\"secondary\" style={styles.truncatedText}>\n Keep typing to narrow your results.\n </Text>\n </View>\n )}\n </>\n }\n />\n )\n}\n\nfunction CandidateItem({\n candidate,\n onPress,\n disabled = false,\n}: {\n candidate: DirectMessageCandidateResource\n onPress?: () => void\n disabled?: boolean\n}) {\n const styles = useStyles()\n\n return (\n <PlatformPressable\n onPress={disabled ? undefined : onPress}\n accessibilityRole=\"button\"\n accessibilityLabel={`Select ${candidate.name}`}\n accessibilityState={{ disabled }}\n >\n <View style={[styles.row, disabled && styles.candidateDisabled]}>\n <Avatar sourceUri={candidate.avatar} size=\"lg\" showFallback />\n <Text style={styles.rowName}>{candidate.name}</Text>\n </View>\n </PlatformPressable>\n )\n}\n\nconst useStyles = () => {\n const { bottom } = useSafeAreaInsets()\n const { colors } = useTheme()\n\n return StyleSheet.create({\n container: {\n flex: 1,\n paddingBottom: bottom,\n },\n toField: {\n paddingHorizontal: 16,\n paddingTop: 16,\n paddingBottom: 8,\n },\n toFieldHeader: {\n flexDirection: 'row',\n justifyContent: 'space-between',\n alignItems: 'center',\n marginBottom: 8,\n },\n toLabel: {\n fontWeight: '600',\n color: colors.textColorDefaultSecondary,\n },\n clearButton: {\n color: colors.interaction,\n },\n toInputArea: {\n flexDirection: 'row',\n flexWrap: 'wrap',\n alignItems: 'center',\n gap: 6,\n borderWidth: 1,\n borderColor: colors.borderColorDefaultBase,\n borderRadius: 8,\n paddingHorizontal: 10,\n paddingVertical: 8,\n },\n toTextInput: {\n flex: 1,\n minWidth: 80,\n color: colors.textColorDefaultPrimary,\n fontSize: 16,\n paddingVertical: 2,\n },\n chip: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 4,\n borderWidth: 1,\n borderColor: colors.borderColorDefaultBase,\n borderRadius: 100,\n paddingLeft: 4,\n paddingRight: 6,\n paddingVertical: 3,\n },\n chipName: {\n fontSize: 14,\n },\n chipRemoveIcon: {\n color: colors.iconColorDefaultSecondary,\n },\n errorBanner: {\n margin: 16,\n },\n emptyState: {\n alignItems: 'center',\n paddingTop: 32,\n paddingHorizontal: 32,\n gap: 8,\n },\n emptyStateIcon: {\n color: colors.iconColorDefaultSecondary,\n marginBottom: 4,\n },\n emptyStateTitle: {\n fontWeight: '600',\n textAlign: 'center',\n },\n emptyStateSubtitle: {\n textAlign: 'center',\n fontSize: 14,\n },\n row: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 12,\n paddingHorizontal: 16,\n paddingVertical: 12,\n borderBottomWidth: 1,\n borderColor: colors.fillColorNeutral050Base,\n },\n rowName: {\n flex: 1,\n },\n candidateDisabled: {\n opacity: 0.5,\n },\n resultsHeader: {\n paddingHorizontal: 16,\n paddingTop: 16,\n paddingBottom: 8,\n },\n resultsHeading: {\n fontWeight: '600',\n },\n unavailableHeader: {\n paddingHorizontal: 16,\n paddingTop: 16,\n paddingBottom: 8,\n },\n unavailableLabel: {\n fontSize: 13,\n },\n truncatedFooter: {\n paddingHorizontal: 16,\n paddingTop: 12,\n paddingBottom: 8,\n },\n truncatedText: {\n fontSize: 13,\n textAlign: 'center',\n },\n })\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@planningcenter/chat-react-native",
3
- "version": "3.39.0-rc.1",
3
+ "version": "3.39.0-rc.2",
4
4
  "description": "",
5
5
  "main": "build/index.js",
6
6
  "react-native": "./src/index.tsx",
@@ -72,5 +72,5 @@
72
72
  "react-native-url-polyfill": "^2.0.0",
73
73
  "typescript": "~5.9.2"
74
74
  },
75
- "gitHead": "03a375a045ff2ae572627318c41bfc6cdcf1bc74"
75
+ "gitHead": "2008d809d6e5be72841b9c1b015ee18c84c66a05"
76
76
  }
@@ -26,5 +26,6 @@ export function useDirectMessageCandidates(term: string) {
26
26
  }) as Promise<ApiCollection<DirectMessageCandidateResource>>,
27
27
  enabled: term.length >= 1,
28
28
  staleTime: 1000 * 60,
29
+ throwOnError: true,
29
30
  })
30
31
  }
@@ -1,33 +1,411 @@
1
- import React from 'react'
2
- import { StyleSheet, View } from 'react-native'
1
+ import { PlatformPressable } from '@react-navigation/elements'
2
+ import { useEffect, useState } from 'react'
3
+ import { FlatList, Pressable, StyleSheet, TextInput, View } from 'react-native'
3
4
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
4
- import { Heading, Text } from '../../components'
5
+ import { Avatar, Banner, Icon, Spinner, Text } from '../../components'
6
+ import { ComponentErrorBoundary } from '../../components/page/component_error_boundary'
7
+ import { useTheme } from '../../hooks'
8
+ import { useDirectMessageCandidates } from '../../hooks/direct_messages/use_direct_message_candidates'
9
+ import { DirectMessageCandidateResource } from '../../types/resources/direct_message_candidate'
5
10
  import { ConversationSelectRecipientsScreenProps } from './types/screen_props'
6
11
 
12
+ const MAX_RECIPIENTS = 9
13
+
7
14
  export function ConversationSelectDirectMessageRecipientsScreen(
8
15
  _props: ConversationSelectRecipientsScreenProps
9
16
  ) {
10
17
  const styles = useStyles()
18
+ const [searchTerm, setSearchTerm] = useState('')
19
+ const [debouncedTerm, setDebouncedTerm] = useState('')
20
+ const [selectedPeople, setSelectedPeople] = useState<Map<string, DirectMessageCandidateResource>>(
21
+ new Map()
22
+ )
23
+
24
+ useEffect(() => {
25
+ const timer = setTimeout(() => setDebouncedTerm(searchTerm), 200)
26
+ return () => clearTimeout(timer)
27
+ }, [searchTerm])
28
+
29
+ const selectPerson = (person: DirectMessageCandidateResource) => {
30
+ if (selectedPeople.size >= MAX_RECIPIENTS) return
31
+ setSelectedPeople(prev => new Map(prev).set(person.id, person))
32
+ setSearchTerm('')
33
+ setDebouncedTerm('')
34
+ }
35
+
36
+ const removePerson = (id: string) => {
37
+ setSelectedPeople(prev => {
38
+ const next = new Map(prev)
39
+ next.delete(id)
40
+ return next
41
+ })
42
+ }
11
43
 
12
44
  return (
13
45
  <View style={styles.container}>
14
- <Heading variant="h2">New direct message</Heading>
15
- <Text variant="secondary">Coming soon</Text>
46
+ <ToField
47
+ selectedPeople={selectedPeople}
48
+ searchTerm={searchTerm}
49
+ onSearchChange={setSearchTerm}
50
+ onRemove={removePerson}
51
+ onClear={() => setSelectedPeople(new Map())}
52
+ />
53
+ <ComponentErrorBoundary
54
+ key={debouncedTerm}
55
+ fallback={
56
+ <Banner
57
+ appearance="error"
58
+ description="Couldn't load people right now. Try again."
59
+ style={styles.errorBanner}
60
+ />
61
+ }
62
+ >
63
+ <CandidateResults
64
+ term={debouncedTerm}
65
+ selectedPeople={selectedPeople}
66
+ onSelect={selectPerson}
67
+ />
68
+ </ComponentErrorBoundary>
69
+ </View>
70
+ )
71
+ }
72
+
73
+ function ToField({
74
+ selectedPeople,
75
+ searchTerm,
76
+ onSearchChange,
77
+ onRemove,
78
+ onClear,
79
+ }: {
80
+ selectedPeople: Map<string, DirectMessageCandidateResource>
81
+ searchTerm: string
82
+ onSearchChange: (term: string) => void
83
+ onRemove: (id: string) => void
84
+ onClear: () => void
85
+ }) {
86
+ const styles = useStyles()
87
+ const { colors } = useTheme()
88
+ const hasSelections = selectedPeople.size > 0
89
+
90
+ return (
91
+ <View style={styles.toField}>
92
+ <View style={styles.toFieldHeader}>
93
+ <Text style={styles.toLabel}>To</Text>
94
+ {hasSelections && (
95
+ <Pressable onPress={onClear} hitSlop={8}>
96
+ <Text style={styles.clearButton}>Clear</Text>
97
+ </Pressable>
98
+ )}
99
+ </View>
100
+ <View style={styles.toInputArea}>
101
+ {[...selectedPeople.values()].map(person => (
102
+ <PersonChip key={person.id} person={person} onRemove={onRemove} />
103
+ ))}
104
+ <TextInput
105
+ autoFocus
106
+ placeholder="Search"
107
+ placeholderTextColor={colors.textColorDefaultPlaceholder}
108
+ value={searchTerm}
109
+ onChangeText={onSearchChange}
110
+ style={styles.toTextInput}
111
+ returnKeyType="search"
112
+ />
113
+ </View>
114
+ </View>
115
+ )
116
+ }
117
+
118
+ function PersonChip({
119
+ person,
120
+ onRemove,
121
+ }: {
122
+ person: DirectMessageCandidateResource
123
+ onRemove: (id: string) => void
124
+ }) {
125
+ const styles = useStyles()
126
+
127
+ return (
128
+ <View style={styles.chip}>
129
+ <Avatar sourceUri={person.avatar} size="sm" showFallback />
130
+ <Text style={styles.chipName}>{person.name}</Text>
131
+ <Pressable
132
+ onPress={() => onRemove(person.id)}
133
+ hitSlop={8}
134
+ accessibilityLabel={`Remove ${person.name}`}
135
+ >
136
+ <Icon name="general.x" size={12} style={styles.chipRemoveIcon} />
137
+ </Pressable>
16
138
  </View>
17
139
  )
18
140
  }
19
141
 
142
+ function CandidateResults({
143
+ term,
144
+ selectedPeople,
145
+ onSelect,
146
+ }: {
147
+ term: string
148
+ selectedPeople: Map<string, DirectMessageCandidateResource>
149
+ onSelect: (candidate: DirectMessageCandidateResource) => void
150
+ }) {
151
+ const styles = useStyles()
152
+ const { data, isFetching } = useDirectMessageCandidates(term)
153
+
154
+ if (term.length < 1) {
155
+ if (selectedPeople.size > 0) return null
156
+
157
+ return (
158
+ <View style={styles.emptyState}>
159
+ <Icon name="churchCenter.people" size={40} style={styles.emptyStateIcon} />
160
+ <Text style={styles.emptyStateTitle}>Search for people to direct message</Text>
161
+ <Text variant="secondary" style={styles.emptyStateSubtitle}>
162
+ Start a conversation with people in your groups and teams.
163
+ </Text>
164
+ </View>
165
+ )
166
+ }
167
+
168
+ if (selectedPeople.size >= MAX_RECIPIENTS) {
169
+ return (
170
+ <View style={styles.emptyState}>
171
+ <Text variant="secondary" style={styles.emptyStateSubtitle}>
172
+ You can add up to {MAX_RECIPIENTS} people to a direct message.
173
+ </Text>
174
+ </View>
175
+ )
176
+ }
177
+
178
+ const unselectedCandidates = (data?.data ?? []).filter(c => !selectedPeople.has(c.id))
179
+
180
+ if (isFetching && unselectedCandidates.length === 0) {
181
+ return (
182
+ <View style={styles.emptyState}>
183
+ <Spinner />
184
+ </View>
185
+ )
186
+ }
187
+
188
+ if (!isFetching && unselectedCandidates.length === 0) {
189
+ return (
190
+ <View style={styles.emptyState}>
191
+ <Icon name="general.search" size={32} style={styles.emptyStateIcon} />
192
+ <Text style={styles.emptyStateTitle}>No matches for "{term}"</Text>
193
+ <Text variant="secondary" style={styles.emptyStateSubtitle}>
194
+ Try a different name, or check that you share a group or team with them.
195
+ </Text>
196
+ </View>
197
+ )
198
+ }
199
+
200
+ const hasTruncated = (data?.meta.totalCount ?? 0) > (data?.data.length ?? 0)
201
+
202
+ return (
203
+ <CandidateList
204
+ candidates={unselectedCandidates}
205
+ hasTruncated={hasTruncated}
206
+ onSelect={onSelect}
207
+ />
208
+ )
209
+ }
210
+
211
+ function CandidateList({
212
+ candidates,
213
+ hasTruncated,
214
+ onSelect,
215
+ }: {
216
+ candidates: DirectMessageCandidateResource[]
217
+ hasTruncated: boolean
218
+ onSelect: (candidate: DirectMessageCandidateResource) => void
219
+ }) {
220
+ const styles = useStyles()
221
+ const available = candidates.filter(c => !c.directMessagesDisabled)
222
+ const dmDisabled = candidates.filter(c => c.directMessagesDisabled)
223
+
224
+ return (
225
+ <FlatList<DirectMessageCandidateResource>
226
+ data={available}
227
+ keyExtractor={item => item.id}
228
+ renderItem={({ item }) => <CandidateItem candidate={item} onPress={() => onSelect(item)} />}
229
+ ListHeaderComponent={
230
+ <View style={styles.resultsHeader}>
231
+ <Text style={styles.resultsHeading}>Results</Text>
232
+ </View>
233
+ }
234
+ ListFooterComponent={
235
+ <>
236
+ {dmDisabled.length > 0 && (
237
+ <>
238
+ <View style={styles.unavailableHeader}>
239
+ <Text variant="secondary" style={styles.unavailableLabel}>
240
+ Unavailable for direct messages
241
+ </Text>
242
+ </View>
243
+ {dmDisabled.map(candidate => (
244
+ <CandidateItem key={candidate.id} candidate={candidate} disabled />
245
+ ))}
246
+ </>
247
+ )}
248
+ {hasTruncated && (
249
+ <View style={styles.truncatedFooter}>
250
+ <Text variant="secondary" style={styles.truncatedText}>
251
+ Keep typing to narrow your results.
252
+ </Text>
253
+ </View>
254
+ )}
255
+ </>
256
+ }
257
+ />
258
+ )
259
+ }
260
+
261
+ function CandidateItem({
262
+ candidate,
263
+ onPress,
264
+ disabled = false,
265
+ }: {
266
+ candidate: DirectMessageCandidateResource
267
+ onPress?: () => void
268
+ disabled?: boolean
269
+ }) {
270
+ const styles = useStyles()
271
+
272
+ return (
273
+ <PlatformPressable
274
+ onPress={disabled ? undefined : onPress}
275
+ accessibilityRole="button"
276
+ accessibilityLabel={`Select ${candidate.name}`}
277
+ accessibilityState={{ disabled }}
278
+ >
279
+ <View style={[styles.row, disabled && styles.candidateDisabled]}>
280
+ <Avatar sourceUri={candidate.avatar} size="lg" showFallback />
281
+ <Text style={styles.rowName}>{candidate.name}</Text>
282
+ </View>
283
+ </PlatformPressable>
284
+ )
285
+ }
286
+
20
287
  const useStyles = () => {
21
288
  const { bottom } = useSafeAreaInsets()
289
+ const { colors } = useTheme()
22
290
 
23
291
  return StyleSheet.create({
24
292
  container: {
25
293
  flex: 1,
26
- padding: 16,
27
- paddingBottom: 16 + bottom,
294
+ paddingBottom: bottom,
295
+ },
296
+ toField: {
297
+ paddingHorizontal: 16,
298
+ paddingTop: 16,
299
+ paddingBottom: 8,
300
+ },
301
+ toFieldHeader: {
302
+ flexDirection: 'row',
303
+ justifyContent: 'space-between',
304
+ alignItems: 'center',
305
+ marginBottom: 8,
306
+ },
307
+ toLabel: {
308
+ fontWeight: '600',
309
+ color: colors.textColorDefaultSecondary,
310
+ },
311
+ clearButton: {
312
+ color: colors.interaction,
313
+ },
314
+ toInputArea: {
315
+ flexDirection: 'row',
316
+ flexWrap: 'wrap',
317
+ alignItems: 'center',
318
+ gap: 6,
319
+ borderWidth: 1,
320
+ borderColor: colors.borderColorDefaultBase,
321
+ borderRadius: 8,
322
+ paddingHorizontal: 10,
323
+ paddingVertical: 8,
324
+ },
325
+ toTextInput: {
326
+ flex: 1,
327
+ minWidth: 80,
328
+ color: colors.textColorDefaultPrimary,
329
+ fontSize: 16,
330
+ paddingVertical: 2,
331
+ },
332
+ chip: {
333
+ flexDirection: 'row',
334
+ alignItems: 'center',
335
+ gap: 4,
336
+ borderWidth: 1,
337
+ borderColor: colors.borderColorDefaultBase,
338
+ borderRadius: 100,
339
+ paddingLeft: 4,
340
+ paddingRight: 6,
341
+ paddingVertical: 3,
342
+ },
343
+ chipName: {
344
+ fontSize: 14,
345
+ },
346
+ chipRemoveIcon: {
347
+ color: colors.iconColorDefaultSecondary,
348
+ },
349
+ errorBanner: {
350
+ margin: 16,
351
+ },
352
+ emptyState: {
28
353
  alignItems: 'center',
29
- justifyContent: 'center',
354
+ paddingTop: 32,
355
+ paddingHorizontal: 32,
30
356
  gap: 8,
31
357
  },
358
+ emptyStateIcon: {
359
+ color: colors.iconColorDefaultSecondary,
360
+ marginBottom: 4,
361
+ },
362
+ emptyStateTitle: {
363
+ fontWeight: '600',
364
+ textAlign: 'center',
365
+ },
366
+ emptyStateSubtitle: {
367
+ textAlign: 'center',
368
+ fontSize: 14,
369
+ },
370
+ row: {
371
+ flexDirection: 'row',
372
+ alignItems: 'center',
373
+ gap: 12,
374
+ paddingHorizontal: 16,
375
+ paddingVertical: 12,
376
+ borderBottomWidth: 1,
377
+ borderColor: colors.fillColorNeutral050Base,
378
+ },
379
+ rowName: {
380
+ flex: 1,
381
+ },
382
+ candidateDisabled: {
383
+ opacity: 0.5,
384
+ },
385
+ resultsHeader: {
386
+ paddingHorizontal: 16,
387
+ paddingTop: 16,
388
+ paddingBottom: 8,
389
+ },
390
+ resultsHeading: {
391
+ fontWeight: '600',
392
+ },
393
+ unavailableHeader: {
394
+ paddingHorizontal: 16,
395
+ paddingTop: 16,
396
+ paddingBottom: 8,
397
+ },
398
+ unavailableLabel: {
399
+ fontSize: 13,
400
+ },
401
+ truncatedFooter: {
402
+ paddingHorizontal: 16,
403
+ paddingTop: 12,
404
+ paddingBottom: 8,
405
+ },
406
+ truncatedText: {
407
+ fontSize: 13,
408
+ textAlign: 'center',
409
+ },
32
410
  })
33
411
  }