@planningcenter/chat-react-native 3.24.0-rc.6 → 3.24.0-rc.8
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.
- package/build/navigation/index.d.ts +82 -0
- package/build/navigation/index.d.ts.map +1 -1
- package/build/navigation/index.js +32 -0
- package/build/navigation/index.js.map +1 -1
- package/build/screens/conversation_select_type_screen.d.ts.map +1 -1
- package/build/screens/conversation_select_type_screen.js +12 -6
- package/build/screens/conversation_select_type_screen.js.map +1 -1
- package/build/screens/index.d.ts +2 -0
- package/build/screens/index.d.ts.map +1 -1
- package/build/screens/index.js +2 -0
- package/build/screens/index.js.map +1 -1
- package/build/screens/notification_settings_screen.d.ts +5 -0
- package/build/screens/notification_settings_screen.d.ts.map +1 -0
- package/build/screens/notification_settings_screen.js +216 -0
- package/build/screens/notification_settings_screen.js.map +1 -0
- package/build/screens/preferred_app/hooks/use_chat_types.d.ts +39 -0
- package/build/screens/preferred_app/hooks/use_chat_types.d.ts.map +1 -0
- package/build/screens/preferred_app/hooks/use_chat_types.js +12 -0
- package/build/screens/preferred_app/hooks/use_chat_types.js.map +1 -0
- package/build/screens/preferred_app_selection_screen.d.ts +10 -0
- package/build/screens/preferred_app_selection_screen.d.ts.map +1 -0
- package/build/screens/preferred_app_selection_screen.js +128 -0
- package/build/screens/preferred_app_selection_screen.js.map +1 -0
- package/package.json +2 -2
- package/src/navigation/index.tsx +35 -0
- package/src/screens/conversation_select_type_screen.tsx +12 -6
- package/src/screens/index.ts +2 -0
- package/src/screens/notification_settings_screen.tsx +356 -0
- package/src/screens/preferred_app/hooks/use_chat_types.ts +25 -0
- package/src/screens/preferred_app_selection_screen.tsx +169 -0
- package/src/types/images.d.ts +14 -0
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
import { PlatformPressable } from '@react-navigation/elements'
|
|
2
|
+
import { StaticScreenProps, useNavigation } from '@react-navigation/native'
|
|
3
|
+
import type { NativeStackNavigationProp } from '@react-navigation/native-stack'
|
|
4
|
+
import React, { useCallback, useEffect, type ReactNode } from 'react'
|
|
5
|
+
import { FlatList, Platform, StyleSheet, View, type ViewProps, type ViewStyle } from 'react-native'
|
|
6
|
+
import { Heading, Icon, Text } from '../components'
|
|
7
|
+
import { HeaderTextButton } from '../components/display/platform_modal_header_buttons'
|
|
8
|
+
import { useTheme } from '../hooks'
|
|
9
|
+
import { isDefined } from '../types'
|
|
10
|
+
import { useChatTypes } from './preferred_app/hooks/use_chat_types'
|
|
11
|
+
import type { PreferredAppSelectionScreenProps } from './preferred_app_selection_screen'
|
|
12
|
+
|
|
13
|
+
// =========================================
|
|
14
|
+
// ====== Factory Constants & Types ========
|
|
15
|
+
// =========================================
|
|
16
|
+
|
|
17
|
+
type NotificationSettingsStackParamList = {
|
|
18
|
+
NotificationSettings: {}
|
|
19
|
+
PreferredAppSelection: PreferredAppSelectionScreenProps['route']['params']
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
enum SectionTypes {
|
|
23
|
+
header,
|
|
24
|
+
hidden,
|
|
25
|
+
setting,
|
|
26
|
+
view,
|
|
27
|
+
link,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type SectionListData = Array<
|
|
31
|
+
| DataItem<{ title: string }, SectionTypes.header>
|
|
32
|
+
| DataItem<ViewProps, SectionTypes.view>
|
|
33
|
+
| DataItem<SettingRowProps, SectionTypes.setting>
|
|
34
|
+
| DataItem<LinkRowProps, SectionTypes.link>
|
|
35
|
+
| DataItem<any, SectionTypes.hidden>
|
|
36
|
+
>
|
|
37
|
+
|
|
38
|
+
interface DataItem<T, TName extends SectionTypes> {
|
|
39
|
+
type: TName
|
|
40
|
+
data: T
|
|
41
|
+
sectionOuterStyle?: ViewStyle
|
|
42
|
+
sectionInnerStyle?: ViewStyle
|
|
43
|
+
showBottomBorder?: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// =================================
|
|
47
|
+
// ====== Components ===============
|
|
48
|
+
// =================================
|
|
49
|
+
|
|
50
|
+
export type NotificationSettingsScreenProps = StaticScreenProps<{}>
|
|
51
|
+
|
|
52
|
+
export function NotificationSettingsScreen({}: NotificationSettingsScreenProps) {
|
|
53
|
+
const navigation = useNavigation<NativeStackNavigationProp<NotificationSettingsStackParamList>>()
|
|
54
|
+
const styles = useStyles()
|
|
55
|
+
const { data: chatTypes } = useChatTypes()
|
|
56
|
+
|
|
57
|
+
// Header configuration
|
|
58
|
+
const HeaderRight = useCallback(() => {
|
|
59
|
+
return (
|
|
60
|
+
<HeaderTextButton
|
|
61
|
+
onPress={() => {
|
|
62
|
+
// Save settings logic would go here
|
|
63
|
+
navigation.goBack()
|
|
64
|
+
}}
|
|
65
|
+
title="Done"
|
|
66
|
+
/>
|
|
67
|
+
)
|
|
68
|
+
}, [navigation])
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
navigation.setOptions({
|
|
72
|
+
headerRight: HeaderRight,
|
|
73
|
+
})
|
|
74
|
+
}, [HeaderRight, navigation])
|
|
75
|
+
|
|
76
|
+
// Build section data
|
|
77
|
+
const listData = [
|
|
78
|
+
{
|
|
79
|
+
type: SectionTypes.header,
|
|
80
|
+
data: { title: 'Preferred app' },
|
|
81
|
+
sectionInnerStyle: styles.sectionInnerHeader,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
type: SectionTypes.view,
|
|
85
|
+
data: {
|
|
86
|
+
children: (
|
|
87
|
+
<Text variant="footnote" style={styles.sectionDescription}>
|
|
88
|
+
Choose which app receives each type of chat notification
|
|
89
|
+
</Text>
|
|
90
|
+
),
|
|
91
|
+
},
|
|
92
|
+
showBottomBorder: true,
|
|
93
|
+
},
|
|
94
|
+
...chatTypes.map(type => ({
|
|
95
|
+
type: SectionTypes.link,
|
|
96
|
+
data: {
|
|
97
|
+
title: `${type.title} Conversations`,
|
|
98
|
+
subtitle: type.preferredApp,
|
|
99
|
+
onPress: () =>
|
|
100
|
+
navigation.navigate('PreferredAppSelection', {
|
|
101
|
+
chatTypeId: type.id,
|
|
102
|
+
}),
|
|
103
|
+
},
|
|
104
|
+
showBottomBorder: true,
|
|
105
|
+
})),
|
|
106
|
+
// {
|
|
107
|
+
// type: SectionTypes.header,
|
|
108
|
+
// data: {
|
|
109
|
+
// title: 'Manage chat settings',
|
|
110
|
+
// },
|
|
111
|
+
// sectionInnerStyle: styles.sectionInnerHeader,
|
|
112
|
+
// },
|
|
113
|
+
// {
|
|
114
|
+
// type: SectionTypes.view,
|
|
115
|
+
// data: {
|
|
116
|
+
// children: (
|
|
117
|
+
// <Text variant="footnote" style={styles.sectionDescription}>
|
|
118
|
+
// Notification settings for all of your group, team and event related conversations
|
|
119
|
+
// </Text>
|
|
120
|
+
// ),
|
|
121
|
+
// },
|
|
122
|
+
// showBottomBorder: true,
|
|
123
|
+
// },
|
|
124
|
+
].filter(item => item.type !== SectionTypes.hidden)
|
|
125
|
+
|
|
126
|
+
const headerIndices = listData
|
|
127
|
+
.map(({ type }, index) => (type === SectionTypes.header ? index : undefined))
|
|
128
|
+
.filter(isDefined)
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<View style={styles.listContainer}>
|
|
132
|
+
<FlatList
|
|
133
|
+
data={listData as SectionListData}
|
|
134
|
+
contentContainerStyle={styles.contentContainer}
|
|
135
|
+
renderItem={({ item, index }) => {
|
|
136
|
+
const [isStart, isEnd] = [
|
|
137
|
+
index === 0 || headerIndices.includes(index),
|
|
138
|
+
index === listData.length - 1 || headerIndices.includes(index + 1),
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
switch (item.type) {
|
|
142
|
+
case SectionTypes.header:
|
|
143
|
+
return (
|
|
144
|
+
<ListSection
|
|
145
|
+
isStart={isStart}
|
|
146
|
+
isEnd={isEnd}
|
|
147
|
+
showBottomBorder={item?.showBottomBorder}
|
|
148
|
+
outerStyle={item?.sectionOuterStyle}
|
|
149
|
+
innerStyle={item?.sectionInnerStyle}
|
|
150
|
+
>
|
|
151
|
+
<Heading variant="h3">{item.data.title}</Heading>
|
|
152
|
+
</ListSection>
|
|
153
|
+
)
|
|
154
|
+
case SectionTypes.setting:
|
|
155
|
+
return (
|
|
156
|
+
<ListSection
|
|
157
|
+
isStart={isStart}
|
|
158
|
+
isEnd={isEnd}
|
|
159
|
+
showBottomBorder={item?.showBottomBorder}
|
|
160
|
+
outerStyle={item?.sectionOuterStyle}
|
|
161
|
+
innerStyle={item?.sectionInnerStyle}
|
|
162
|
+
>
|
|
163
|
+
<SettingRow {...item.data} />
|
|
164
|
+
</ListSection>
|
|
165
|
+
)
|
|
166
|
+
case SectionTypes.view:
|
|
167
|
+
return (
|
|
168
|
+
<ListSection
|
|
169
|
+
isStart={isStart}
|
|
170
|
+
isEnd={isEnd}
|
|
171
|
+
showBottomBorder={item?.showBottomBorder}
|
|
172
|
+
outerStyle={item?.sectionOuterStyle}
|
|
173
|
+
innerStyle={item?.sectionInnerStyle}
|
|
174
|
+
>
|
|
175
|
+
<View {...item.data} style={item.data.style} />
|
|
176
|
+
</ListSection>
|
|
177
|
+
)
|
|
178
|
+
case SectionTypes.link:
|
|
179
|
+
return (
|
|
180
|
+
<ListSection
|
|
181
|
+
isStart={isStart}
|
|
182
|
+
isEnd={isEnd}
|
|
183
|
+
showBottomBorder={item?.showBottomBorder}
|
|
184
|
+
outerStyle={item?.sectionOuterStyle}
|
|
185
|
+
innerStyle={item?.sectionInnerStyle}
|
|
186
|
+
>
|
|
187
|
+
<LinkRow {...item.data} />
|
|
188
|
+
</ListSection>
|
|
189
|
+
)
|
|
190
|
+
default:
|
|
191
|
+
return null
|
|
192
|
+
}
|
|
193
|
+
}}
|
|
194
|
+
/>
|
|
195
|
+
</View>
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
interface ListSectionProps {
|
|
200
|
+
isStart?: boolean
|
|
201
|
+
isEnd?: boolean
|
|
202
|
+
showBottomBorder?: boolean
|
|
203
|
+
outerStyle?: ViewStyle
|
|
204
|
+
innerStyle?: ViewStyle
|
|
205
|
+
children: ReactNode
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function ListSection({
|
|
209
|
+
isStart,
|
|
210
|
+
isEnd,
|
|
211
|
+
showBottomBorder,
|
|
212
|
+
outerStyle,
|
|
213
|
+
innerStyle,
|
|
214
|
+
children,
|
|
215
|
+
}: ListSectionProps) {
|
|
216
|
+
const styles = useStyles({ isStart, isEnd })
|
|
217
|
+
const bottomBorder = showBottomBorder ? styles.sectionInnerBottomBorder : {}
|
|
218
|
+
|
|
219
|
+
return (
|
|
220
|
+
<View style={[styles.sectionOuterBase, outerStyle]}>
|
|
221
|
+
<View style={[styles.sectionInnerBase, bottomBorder, innerStyle]}>{children}</View>
|
|
222
|
+
</View>
|
|
223
|
+
)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
interface SettingRowProps {
|
|
227
|
+
title: string
|
|
228
|
+
style?: ViewStyle
|
|
229
|
+
rightItem?: ReactNode
|
|
230
|
+
subtitle?: string
|
|
231
|
+
rightItemStyle?: ViewStyle
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function SettingRow({ title, style, subtitle, rightItem, rightItemStyle = {} }: SettingRowProps) {
|
|
235
|
+
const styles = useStyles()
|
|
236
|
+
|
|
237
|
+
return (
|
|
238
|
+
<View style={[styles.settingRow, style]}>
|
|
239
|
+
<View style={styles.settingRowContent}>
|
|
240
|
+
<Text variant="plain" style={styles.settingRowText}>
|
|
241
|
+
{title}
|
|
242
|
+
</Text>
|
|
243
|
+
{Boolean(subtitle) && <Text variant="footnote">{subtitle}</Text>}
|
|
244
|
+
</View>
|
|
245
|
+
{Boolean(rightItem) && <View style={rightItemStyle}>{rightItem}</View>}
|
|
246
|
+
</View>
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
interface LinkRowProps {
|
|
251
|
+
title: string
|
|
252
|
+
subtitle: string
|
|
253
|
+
onPress: () => void
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function LinkRow({ title, subtitle, onPress }: LinkRowProps) {
|
|
257
|
+
const styles = useLinkRowStyles()
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<PlatformPressable
|
|
261
|
+
style={styles.row}
|
|
262
|
+
onPress={onPress}
|
|
263
|
+
accessibilityRole="link"
|
|
264
|
+
accessibilityLabel={title}
|
|
265
|
+
accessibilityHint={`Navigate to ${title} settings`}
|
|
266
|
+
>
|
|
267
|
+
<View style={styles.innerContainer}>
|
|
268
|
+
<Text style={styles.title} numberOfLines={2}>
|
|
269
|
+
{title}
|
|
270
|
+
</Text>
|
|
271
|
+
<View style={styles.rightContent}>
|
|
272
|
+
<Text numberOfLines={1}>{subtitle}</Text>
|
|
273
|
+
{Platform.OS === 'ios' && (
|
|
274
|
+
<Icon name="general.rightChevron" size={16} style={styles.icon} />
|
|
275
|
+
)}
|
|
276
|
+
</View>
|
|
277
|
+
</View>
|
|
278
|
+
</PlatformPressable>
|
|
279
|
+
)
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// =================================
|
|
283
|
+
// ====== Styles ===================
|
|
284
|
+
// =================================
|
|
285
|
+
|
|
286
|
+
const useStyles = ({ isStart, isEnd }: { isStart?: boolean; isEnd?: boolean } = {}) => {
|
|
287
|
+
const { colors } = useTheme()
|
|
288
|
+
const headerBottomPadding = 0
|
|
289
|
+
|
|
290
|
+
return StyleSheet.create({
|
|
291
|
+
listContainer: {
|
|
292
|
+
flex: 1,
|
|
293
|
+
backgroundColor: colors.surfaceColor080,
|
|
294
|
+
},
|
|
295
|
+
contentContainer: {},
|
|
296
|
+
sectionOuterBase: {
|
|
297
|
+
paddingLeft: 16,
|
|
298
|
+
backgroundColor: colors.surfaceColor100,
|
|
299
|
+
},
|
|
300
|
+
sectionInnerBase: {
|
|
301
|
+
paddingRight: 16,
|
|
302
|
+
paddingTop: isStart ? 16 : 12,
|
|
303
|
+
paddingBottom: isEnd ? 16 : 12,
|
|
304
|
+
},
|
|
305
|
+
sectionInnerHeader: {
|
|
306
|
+
paddingBottom: headerBottomPadding,
|
|
307
|
+
},
|
|
308
|
+
sectionInnerBottomBorder: {
|
|
309
|
+
borderBottomColor: colors.borderColorDefaultBase,
|
|
310
|
+
borderBottomWidth: 1,
|
|
311
|
+
},
|
|
312
|
+
sectionDescription: {
|
|
313
|
+
color: colors.textColorDefaultSecondary,
|
|
314
|
+
},
|
|
315
|
+
settingRow: {
|
|
316
|
+
flexDirection: 'row',
|
|
317
|
+
justifyContent: 'space-between',
|
|
318
|
+
alignItems: 'center',
|
|
319
|
+
gap: 8,
|
|
320
|
+
},
|
|
321
|
+
settingRowContent: {
|
|
322
|
+
flex: 1,
|
|
323
|
+
},
|
|
324
|
+
settingRowText: {
|
|
325
|
+
lineHeight: 20,
|
|
326
|
+
},
|
|
327
|
+
})
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const useLinkRowStyles = () => {
|
|
331
|
+
const theme = useTheme()
|
|
332
|
+
|
|
333
|
+
return StyleSheet.create({
|
|
334
|
+
row: {
|
|
335
|
+
paddingLeft: 0,
|
|
336
|
+
},
|
|
337
|
+
innerContainer: {
|
|
338
|
+
flexDirection: 'row',
|
|
339
|
+
alignItems: 'center',
|
|
340
|
+
justifyContent: 'space-between',
|
|
341
|
+
gap: 12,
|
|
342
|
+
paddingVertical: 4,
|
|
343
|
+
},
|
|
344
|
+
rightContent: {
|
|
345
|
+
flexDirection: 'row',
|
|
346
|
+
alignItems: 'center',
|
|
347
|
+
gap: 8,
|
|
348
|
+
},
|
|
349
|
+
title: {
|
|
350
|
+
flexShrink: 1,
|
|
351
|
+
},
|
|
352
|
+
icon: {
|
|
353
|
+
color: theme.colors.iconColorDefaultDisabled,
|
|
354
|
+
},
|
|
355
|
+
})
|
|
356
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useSuspenseGet } from '../../../hooks'
|
|
2
|
+
import { ResourceObject } from '../../../types'
|
|
3
|
+
|
|
4
|
+
export type SourceAppName = 'Services' | 'Church Center'
|
|
5
|
+
export type PreferredApp = 'Services' | 'Church Center' | 'Chat' | 'None'
|
|
6
|
+
|
|
7
|
+
export interface ChatTypeResource extends ResourceObject {
|
|
8
|
+
type: 'ChatType'
|
|
9
|
+
defaultPreferredApp: PreferredApp
|
|
10
|
+
preferredApp: PreferredApp
|
|
11
|
+
preferredAppOptions: PreferredApp[]
|
|
12
|
+
sourceAppName: SourceAppName
|
|
13
|
+
title: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const useChatTypes = () => {
|
|
17
|
+
return useSuspenseGet<ChatTypeResource[]>({
|
|
18
|
+
url: '/me/chat_types',
|
|
19
|
+
data: {
|
|
20
|
+
fields: {
|
|
21
|
+
ChatType: [],
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
})
|
|
25
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { PlatformPressable } from '@react-navigation/elements'
|
|
2
|
+
import { StaticScreenProps } from '@react-navigation/native'
|
|
3
|
+
import { useMutation } from '@tanstack/react-query'
|
|
4
|
+
import React, { type PropsWithChildren } from 'react'
|
|
5
|
+
import { Image, StyleSheet, View, type ViewStyle } from 'react-native'
|
|
6
|
+
import churchCenterLogo from '../../assets/churchCenter.png'
|
|
7
|
+
import servicesLogo from '../../assets/servicesApp.png'
|
|
8
|
+
import chatLogo from '../../assets/chatLogo.png'
|
|
9
|
+
import { Heading, Icon, Text } from '../components'
|
|
10
|
+
import { useApiClient, useTheme } from '../hooks'
|
|
11
|
+
import { PreferredApp, useChatTypes } from './preferred_app/hooks/use_chat_types'
|
|
12
|
+
import { useAppName } from '../hooks/use_app_name'
|
|
13
|
+
import { throwResponseError } from '../utils/response_error'
|
|
14
|
+
|
|
15
|
+
export type PreferredAppSelectionScreenProps = StaticScreenProps<{
|
|
16
|
+
chatTypeId: number | string
|
|
17
|
+
conversationType?: 'group' | 'team'
|
|
18
|
+
currentSelection?: PreferredApp
|
|
19
|
+
}>
|
|
20
|
+
|
|
21
|
+
export function PreferredAppSelectionScreen({ route }: PreferredAppSelectionScreenProps) {
|
|
22
|
+
const { chatTypeId } = route.params
|
|
23
|
+
const { data: chatTypes, refetch: refetchChatTypes } = useChatTypes()
|
|
24
|
+
const appName = useAppName()
|
|
25
|
+
|
|
26
|
+
const apiClient = useApiClient()
|
|
27
|
+
|
|
28
|
+
const { mutate: updateChatType } = useMutation({
|
|
29
|
+
throwOnError: true,
|
|
30
|
+
onSuccess: () => refetchChatTypes(),
|
|
31
|
+
mutationFn: (app: PreferredApp) => {
|
|
32
|
+
return apiClient.chat
|
|
33
|
+
.post({
|
|
34
|
+
url: `/me/chat_types/${chatTypeId}/set_preferred_appz`,
|
|
35
|
+
data: { data: { type: 'ChatType', attributes: { app } } },
|
|
36
|
+
})
|
|
37
|
+
.catch(throwResponseError)
|
|
38
|
+
},
|
|
39
|
+
})
|
|
40
|
+
const chatType = chatTypes.find(t => t.id === chatTypeId)
|
|
41
|
+
const { preferredAppOptions } = chatType || {}
|
|
42
|
+
|
|
43
|
+
const handleSelection = (app: PreferredApp) => {
|
|
44
|
+
updateChatType(app)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const sectionTitle = `${chatType?.title} Conversations`
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<View style={styles.container}>
|
|
51
|
+
<View style={styles.section}>
|
|
52
|
+
<Heading variant="h3" style={styles.sectionHeading}>
|
|
53
|
+
{sectionTitle}
|
|
54
|
+
</Heading>
|
|
55
|
+
{preferredAppOptions?.sort(sortPreferredApp('asc')).map((option, key) => (
|
|
56
|
+
<PressableRow
|
|
57
|
+
key={`${key}-${option}`}
|
|
58
|
+
isActive={chatType?.preferredApp === option}
|
|
59
|
+
onPress={() => handleSelection(option)}
|
|
60
|
+
>
|
|
61
|
+
<Image source={getAppImage(option)} style={styles.logo} />
|
|
62
|
+
<Text>
|
|
63
|
+
{option}
|
|
64
|
+
{appName.includes(option.replace(/\s/, '').toLowerCase()) ? ' ( current )' : ''}
|
|
65
|
+
</Text>
|
|
66
|
+
</PressableRow>
|
|
67
|
+
))}
|
|
68
|
+
</View>
|
|
69
|
+
</View>
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const sortPreferredApp = (dir?: 'asc' | 'desc') => (a: PreferredApp, b: PreferredApp) => {
|
|
74
|
+
if (a === 'None') return 1
|
|
75
|
+
if (a > b) return dir === 'asc' ? 1 : -1
|
|
76
|
+
if (b > a) return dir === 'asc' ? -1 : 1
|
|
77
|
+
|
|
78
|
+
return 0
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const getAppImage = (option: PreferredApp) => {
|
|
82
|
+
switch (option) {
|
|
83
|
+
case 'Church Center':
|
|
84
|
+
return churchCenterLogo
|
|
85
|
+
case 'Services':
|
|
86
|
+
return servicesLogo
|
|
87
|
+
case 'Chat':
|
|
88
|
+
return chatLogo
|
|
89
|
+
default:
|
|
90
|
+
return undefined
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const PressableRow = ({
|
|
95
|
+
children,
|
|
96
|
+
isActive,
|
|
97
|
+
onPress,
|
|
98
|
+
style,
|
|
99
|
+
}: PropsWithChildren<{ isActive: boolean; onPress: () => void; style?: ViewStyle }>) => {
|
|
100
|
+
const styles = useRowStyles({ isActive })
|
|
101
|
+
return (
|
|
102
|
+
<PlatformPressable
|
|
103
|
+
style={styles.pressable}
|
|
104
|
+
onPress={onPress}
|
|
105
|
+
accessibilityRole="radio"
|
|
106
|
+
accessibilityState={{ selected: isActive }}
|
|
107
|
+
>
|
|
108
|
+
<View style={styles.row}>
|
|
109
|
+
<View style={[styles.rowInner, style]}>{children}</View>
|
|
110
|
+
<Icon
|
|
111
|
+
name="general.check"
|
|
112
|
+
style={styles.rowIconRight}
|
|
113
|
+
accessibilityElementsHidden
|
|
114
|
+
maxFontSizeMultiplier={2.5}
|
|
115
|
+
/>
|
|
116
|
+
</View>
|
|
117
|
+
</PlatformPressable>
|
|
118
|
+
)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const styles = StyleSheet.create({
|
|
122
|
+
container: {
|
|
123
|
+
flex: 1,
|
|
124
|
+
},
|
|
125
|
+
section: {
|
|
126
|
+
paddingTop: 16,
|
|
127
|
+
},
|
|
128
|
+
sectionHeading: {
|
|
129
|
+
paddingHorizontal: 16,
|
|
130
|
+
paddingBottom: 4,
|
|
131
|
+
},
|
|
132
|
+
logo: {
|
|
133
|
+
width: 32,
|
|
134
|
+
height: 32,
|
|
135
|
+
borderRadius: 8,
|
|
136
|
+
},
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
const useRowStyles = ({ isActive = false }: { isActive?: boolean } = {}) => {
|
|
140
|
+
const theme = useTheme()
|
|
141
|
+
|
|
142
|
+
return StyleSheet.create({
|
|
143
|
+
pressable: {
|
|
144
|
+
paddingLeft: 16,
|
|
145
|
+
backgroundColor: theme.colors.surfaceColor100,
|
|
146
|
+
},
|
|
147
|
+
row: {
|
|
148
|
+
flexDirection: 'row',
|
|
149
|
+
alignItems: 'center',
|
|
150
|
+
justifyContent: 'space-between',
|
|
151
|
+
gap: 12,
|
|
152
|
+
borderBottomWidth: 1,
|
|
153
|
+
borderBottomColor: theme.colors.borderColorDefaultBase,
|
|
154
|
+
paddingVertical: 12,
|
|
155
|
+
paddingRight: 16,
|
|
156
|
+
},
|
|
157
|
+
rowInner: {
|
|
158
|
+
flexDirection: 'row',
|
|
159
|
+
alignItems: 'center',
|
|
160
|
+
gap: 12,
|
|
161
|
+
flexShrink: 1,
|
|
162
|
+
},
|
|
163
|
+
rowIconRight: {
|
|
164
|
+
fontSize: 16,
|
|
165
|
+
color: theme.colors.statusSuccessIcon,
|
|
166
|
+
opacity: isActive ? 1 : 0,
|
|
167
|
+
},
|
|
168
|
+
})
|
|
169
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
declare module '*.png' {
|
|
2
|
+
const value: import('react-native').ImageSourcePropType
|
|
3
|
+
export default value
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
declare module '*.jpg' {
|
|
7
|
+
const value: import('react-native').ImageSourcePropType
|
|
8
|
+
export default value
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare module '*.jpeg' {
|
|
12
|
+
const value: import('react-native').ImageSourcePropType
|
|
13
|
+
export default value
|
|
14
|
+
}
|