@planningcenter/chat-react-native 3.24.0-rc.7 → 3.24.0-rc.9
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/components/display/platform_modal_header_buttons.d.ts +8 -0
- package/build/components/display/platform_modal_header_buttons.d.ts.map +1 -1
- package/build/components/display/platform_modal_header_buttons.js +8 -0
- package/build/components/display/platform_modal_header_buttons.js.map +1 -1
- package/build/navigation/index.d.ts +34 -2
- package/build/navigation/index.d.ts.map +1 -1
- package/build/navigation/index.js +25 -1
- package/build/navigation/index.js.map +1 -1
- package/build/screens/group_notification_settings_screen.d.ts +8 -0
- package/build/screens/group_notification_settings_screen.d.ts.map +1 -0
- package/build/screens/group_notification_settings_screen.js +79 -0
- package/build/screens/group_notification_settings_screen.js.map +1 -0
- package/build/screens/index.d.ts +3 -0
- package/build/screens/index.d.ts.map +1 -1
- package/build/screens/index.js +3 -0
- package/build/screens/index.js.map +1 -1
- package/build/screens/notification_settings/hooks/groups.d.ts +94 -0
- package/build/screens/notification_settings/hooks/groups.d.ts.map +1 -0
- package/build/screens/notification_settings/hooks/groups.js +92 -0
- package/build/screens/notification_settings/hooks/groups.js.map +1 -0
- 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 +234 -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/build/types/resources/group_membership.d.ts +6 -0
- package/build/types/resources/group_membership.d.ts.map +1 -0
- package/build/types/resources/group_membership.js +2 -0
- package/build/types/resources/group_membership.js.map +1 -0
- package/build/types/resources/group_resource.d.ts +4 -0
- package/build/types/resources/group_resource.d.ts.map +1 -1
- package/build/types/resources/group_resource.js.map +1 -1
- package/build/types/resources/index.d.ts +2 -0
- package/build/types/resources/index.d.ts.map +1 -1
- package/build/types/resources/index.js +2 -0
- package/build/types/resources/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/display/platform_modal_header_buttons.tsx +16 -0
- package/src/navigation/index.tsx +32 -1
- package/src/screens/group_notification_settings_screen.tsx +92 -0
- package/src/screens/index.ts +3 -0
- package/src/screens/notification_settings/hooks/groups.ts +101 -0
- package/src/screens/notification_settings_screen.tsx +383 -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
- package/src/types/resources/group_membership.ts +6 -0
- package/src/types/resources/group_resource.ts +5 -0
- package/src/types/resources/index.ts +2 -0
|
@@ -3,6 +3,7 @@ import { TextButton } from './text_button'
|
|
|
3
3
|
import { HeaderButton } from '@react-navigation/elements'
|
|
4
4
|
import { Icon } from './icon'
|
|
5
5
|
import { useTheme } from '../../hooks'
|
|
6
|
+
import type { NativeStackHeaderRightProps } from '@react-navigation/native-stack'
|
|
6
7
|
|
|
7
8
|
interface HeaderTextButtonProps {
|
|
8
9
|
onPress: () => void
|
|
@@ -62,6 +63,21 @@ export const HeaderDismissButton = ({
|
|
|
62
63
|
})
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
interface HeaderDoneButtonProps extends NativeStackHeaderRightProps {
|
|
67
|
+
navigation: { goBack: () => void; getState: () => any }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export const HeaderDoneButton = ({ navigation, ...props }: HeaderDoneButtonProps) => {
|
|
71
|
+
const state = navigation.getState()
|
|
72
|
+
const isFirstScreen = state.index === 0
|
|
73
|
+
|
|
74
|
+
if (!isFirstScreen) {
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return <HeaderTextButton {...props} onPress={navigation.goBack} title="Done" />
|
|
79
|
+
}
|
|
80
|
+
|
|
65
81
|
const useStyles = () => {
|
|
66
82
|
const { colors } = useTheme()
|
|
67
83
|
|
package/src/navigation/index.tsx
CHANGED
|
@@ -8,7 +8,10 @@ import { CardStyleInterpolators } from '@react-navigation/stack'
|
|
|
8
8
|
import React from 'react'
|
|
9
9
|
import { Platform } from 'react-native'
|
|
10
10
|
import { Icon } from '../components'
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
HeaderDoneButton,
|
|
13
|
+
HeaderTextButton,
|
|
14
|
+
} from '../components/display/platform_modal_header_buttons'
|
|
12
15
|
import {
|
|
13
16
|
AttachmentActionsScreen,
|
|
14
17
|
AttachmentActionsScreenOptions,
|
|
@@ -49,6 +52,9 @@ import {
|
|
|
49
52
|
} from '../screens/message_actions_screen'
|
|
50
53
|
import { MessageReportScreen, MessageReportScreenOptions } from '../screens/message_report_screen'
|
|
51
54
|
import { NotFound } from '../screens/not_found'
|
|
55
|
+
import { NotificationSettingsScreen } from '../screens/notification_settings_screen'
|
|
56
|
+
import { PreferredAppSelectionScreen } from '../screens/preferred_app_selection_screen'
|
|
57
|
+
import { GroupNotificationSettingsScreen } from '../screens/group_notification_settings_screen'
|
|
52
58
|
import { ReactionsScreen, ReactionsScreenOptions } from '../screens/reactions_screen'
|
|
53
59
|
import { ScreenLayoutWithChatAccessGate } from './screenLayout'
|
|
54
60
|
import { SendGiphyScreen, SendGiphyScreenOptions } from '../screens/send_giphy_screen'
|
|
@@ -223,6 +229,31 @@ export const ChatStack = createNativeStackNavigator({
|
|
|
223
229
|
),
|
|
224
230
|
}),
|
|
225
231
|
},
|
|
232
|
+
NotificationSettings: {
|
|
233
|
+
screen: NotificationSettingsScreen,
|
|
234
|
+
options: ({ navigation }) => ({
|
|
235
|
+
title: 'Chat',
|
|
236
|
+
headerBackVisible: false,
|
|
237
|
+
headerRight: (props: NativeStackHeaderRightProps) => (
|
|
238
|
+
<HeaderTextButton {...props} onPress={navigation.goBack} title="Done" />
|
|
239
|
+
),
|
|
240
|
+
}),
|
|
241
|
+
},
|
|
242
|
+
PreferredAppSelection: {
|
|
243
|
+
screen: PreferredAppSelectionScreen,
|
|
244
|
+
options: {
|
|
245
|
+
title: 'Preferred app',
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
GroupNotificationSettings: {
|
|
249
|
+
screen: GroupNotificationSettingsScreen,
|
|
250
|
+
options: ({ route, navigation }) => ({
|
|
251
|
+
title: (route.params as { title?: string })?.title || 'Group Settings',
|
|
252
|
+
headerRight: (props: NativeStackHeaderRightProps) => (
|
|
253
|
+
<HeaderDoneButton {...props} navigation={navigation} />
|
|
254
|
+
),
|
|
255
|
+
}),
|
|
256
|
+
},
|
|
226
257
|
New: {
|
|
227
258
|
screen: NewConversationStack,
|
|
228
259
|
if: useQualifiedByAge,
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { StaticScreenProps, useNavigation } from '@react-navigation/native'
|
|
2
|
+
import React, { useEffect } from 'react'
|
|
3
|
+
import { StyleSheet, View } from 'react-native'
|
|
4
|
+
import { Heading, Switch, Text } from '../components'
|
|
5
|
+
import { useTheme } from '../hooks'
|
|
6
|
+
import { platformFontWeightBold } from '../utils/styles'
|
|
7
|
+
import { useGroup, useGroupMembershipUpdate } from './notification_settings/hooks/groups'
|
|
8
|
+
|
|
9
|
+
export type GroupNotificationSettingsScreenProps = StaticScreenProps<{
|
|
10
|
+
groupId: number | string
|
|
11
|
+
title: string
|
|
12
|
+
}>
|
|
13
|
+
|
|
14
|
+
export function GroupNotificationSettingsScreen({ route }: GroupNotificationSettingsScreenProps) {
|
|
15
|
+
const { groupId, title } = route.params
|
|
16
|
+
const navigation = useNavigation()
|
|
17
|
+
const styles = useStyles()
|
|
18
|
+
const { data: group } = useGroup({ groupId })
|
|
19
|
+
const { mutate: updateNotificationLevel } = useGroupMembershipUpdate({ groupId })
|
|
20
|
+
|
|
21
|
+
const notificationsEnabled = group.myGroupMembership?.notificationLevel === 'everything'
|
|
22
|
+
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
if (!group.name || title === group.name) return
|
|
25
|
+
|
|
26
|
+
navigation.setOptions({ title: group.name })
|
|
27
|
+
}, [group.name, title, navigation])
|
|
28
|
+
|
|
29
|
+
const handleToggle = (value: boolean) => {
|
|
30
|
+
const notificationLevel = value ? 'everything' : 'nothing'
|
|
31
|
+
updateNotificationLevel(notificationLevel)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<View style={styles.container}>
|
|
36
|
+
<View style={styles.sectionOuter}>
|
|
37
|
+
<View style={styles.sectionInner}>
|
|
38
|
+
<Heading variant="h3" style={styles.sectionHeading}>
|
|
39
|
+
Group notification settings
|
|
40
|
+
</Heading>
|
|
41
|
+
<Text variant="tertiary" style={styles.sectionSubtitle}>
|
|
42
|
+
The settings are applied to all conversations in{' '}
|
|
43
|
+
<Text style={styles.groupNameBold}>{group.name || title}</Text>
|
|
44
|
+
</Text>
|
|
45
|
+
</View>
|
|
46
|
+
</View>
|
|
47
|
+
<View style={styles.settingRow}>
|
|
48
|
+
<Text>Enable notifications</Text>
|
|
49
|
+
<Switch value={notificationsEnabled} onValueChange={handleToggle} />
|
|
50
|
+
</View>
|
|
51
|
+
</View>
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const useStyles = () => {
|
|
56
|
+
const { colors } = useTheme()
|
|
57
|
+
|
|
58
|
+
return StyleSheet.create({
|
|
59
|
+
container: {
|
|
60
|
+
flex: 1,
|
|
61
|
+
backgroundColor: colors.surfaceColor100,
|
|
62
|
+
},
|
|
63
|
+
sectionOuter: {
|
|
64
|
+
paddingLeft: 16,
|
|
65
|
+
backgroundColor: colors.surfaceColor100,
|
|
66
|
+
},
|
|
67
|
+
sectionInner: {
|
|
68
|
+
paddingRight: 16,
|
|
69
|
+
paddingTop: 16,
|
|
70
|
+
paddingBottom: 12,
|
|
71
|
+
borderBottomWidth: 1,
|
|
72
|
+
borderBottomColor: colors.borderColorDefaultBase,
|
|
73
|
+
},
|
|
74
|
+
sectionHeading: {
|
|
75
|
+
paddingBottom: 4,
|
|
76
|
+
},
|
|
77
|
+
sectionSubtitle: {
|
|
78
|
+
color: colors.textColorDefaultSecondary,
|
|
79
|
+
},
|
|
80
|
+
groupNameBold: {
|
|
81
|
+
fontWeight: platformFontWeightBold,
|
|
82
|
+
},
|
|
83
|
+
settingRow: {
|
|
84
|
+
flexDirection: 'row',
|
|
85
|
+
justifyContent: 'space-between',
|
|
86
|
+
alignItems: 'center',
|
|
87
|
+
paddingHorizontal: 16,
|
|
88
|
+
paddingVertical: 12,
|
|
89
|
+
backgroundColor: colors.surfaceColor100,
|
|
90
|
+
},
|
|
91
|
+
})
|
|
92
|
+
}
|
package/src/screens/index.ts
CHANGED
|
@@ -2,6 +2,9 @@ export * from './design_system_screen'
|
|
|
2
2
|
export * from './conversation_details_screen'
|
|
3
3
|
export * from './conversation_screen'
|
|
4
4
|
export * from './conversation_new/conversation_new_screen'
|
|
5
|
+
export * from './notification_settings_screen'
|
|
6
|
+
export * from './preferred_app_selection_screen'
|
|
7
|
+
export * from './group_notification_settings_screen'
|
|
5
8
|
export * from './conversation_filter_recipients/conversation_filter_recipients_screen'
|
|
6
9
|
export * from './message_actions_screen'
|
|
7
10
|
export * from './message_report_screen'
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { useMutation, useQueryClient } from '@tanstack/react-query'
|
|
2
|
+
import { getRequestQueryKey, useApiClient } from '../../../hooks'
|
|
3
|
+
import { useSuspenseGet, useSuspensePaginator } from '../../../hooks'
|
|
4
|
+
import { ApiResource } from '../../../types'
|
|
5
|
+
import { GroupMembership, GroupResourceWithMembership } from '../../../types/resources'
|
|
6
|
+
import { throwResponseError } from '../../../utils/response_error'
|
|
7
|
+
|
|
8
|
+
export const getGroupsRequestArgs = () => ({
|
|
9
|
+
url: '/me/groups',
|
|
10
|
+
data: {
|
|
11
|
+
perPage: 100,
|
|
12
|
+
include: ['my_group_membership'],
|
|
13
|
+
filter: 'user_settable_notification_level',
|
|
14
|
+
fields: {
|
|
15
|
+
Group: [],
|
|
16
|
+
GroupMembership: ['notification_level', 'notification_level_description'],
|
|
17
|
+
},
|
|
18
|
+
order: 'name',
|
|
19
|
+
},
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
export const getGroupRequestArgs = ({ groupId }: { groupId: number | string }) => ({
|
|
23
|
+
url: `/me/groups/${groupId}`,
|
|
24
|
+
data: {
|
|
25
|
+
include: ['my_group_membership'],
|
|
26
|
+
fields: {
|
|
27
|
+
Group: ['name', 'source_app_name', 'source_type', 'my_group_membership'],
|
|
28
|
+
GroupMembership: ['notification_level', 'notification_level_description'],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
export const useGroups = () => {
|
|
34
|
+
const args = getGroupsRequestArgs()
|
|
35
|
+
|
|
36
|
+
return useSuspensePaginator<GroupResourceWithMembership>(args)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const useGroup = ({ groupId }: { groupId: number | string }) => {
|
|
40
|
+
const args = getGroupRequestArgs({ groupId })
|
|
41
|
+
|
|
42
|
+
return useSuspenseGet<GroupResourceWithMembership>(args)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const useGroupMembershipUpdate = ({ groupId }: { groupId: number | string }) => {
|
|
46
|
+
const apiClient = useApiClient()
|
|
47
|
+
const queryClient = useQueryClient()
|
|
48
|
+
const requestArgs = getGroupRequestArgs({ groupId })
|
|
49
|
+
const queryKey = getRequestQueryKey(requestArgs)
|
|
50
|
+
|
|
51
|
+
return useMutation({
|
|
52
|
+
throwOnError: true,
|
|
53
|
+
onMutate: notificationLevel => {
|
|
54
|
+
queryClient.setQueryData<ApiResource<GroupResourceWithMembership>>(queryKey, groupData => {
|
|
55
|
+
if (!groupData?.data.myGroupMembership) return groupData
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
...groupData,
|
|
59
|
+
data: {
|
|
60
|
+
...groupData.data,
|
|
61
|
+
myGroupMembership: {
|
|
62
|
+
...groupData.data.myGroupMembership,
|
|
63
|
+
notificationLevel,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
},
|
|
69
|
+
onSuccess: response => {
|
|
70
|
+
queryClient.setQueryData<ApiResource<GroupResourceWithMembership>>(queryKey, groupData => {
|
|
71
|
+
if (!groupData?.data.myGroupMembership) return groupData
|
|
72
|
+
|
|
73
|
+
return {
|
|
74
|
+
...groupData,
|
|
75
|
+
data: {
|
|
76
|
+
...groupData.data,
|
|
77
|
+
myGroupMembership: {
|
|
78
|
+
...groupData.data.myGroupMembership,
|
|
79
|
+
...response.data,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
})
|
|
84
|
+
},
|
|
85
|
+
mutationFn: (notificationLevel: string) => {
|
|
86
|
+
return apiClient.chat
|
|
87
|
+
.patch<ApiResource<GroupMembership>>({
|
|
88
|
+
url: `/me/groups/${groupId}/my_group_membership`,
|
|
89
|
+
data: {
|
|
90
|
+
data: {
|
|
91
|
+
type: 'GroupMembership',
|
|
92
|
+
attributes: {
|
|
93
|
+
notification_level: notificationLevel,
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
})
|
|
98
|
+
.catch(throwResponseError)
|
|
99
|
+
},
|
|
100
|
+
})
|
|
101
|
+
}
|
|
@@ -0,0 +1,383 @@
|
|
|
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 { Badge, 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 type { GroupNotificationSettingsScreenProps } from './group_notification_settings_screen'
|
|
11
|
+
import { useGroups } from './notification_settings/hooks/groups'
|
|
12
|
+
import { useChatTypes } from './preferred_app/hooks/use_chat_types'
|
|
13
|
+
import type { PreferredAppSelectionScreenProps } from './preferred_app_selection_screen'
|
|
14
|
+
|
|
15
|
+
// =========================================
|
|
16
|
+
// ====== Factory Constants & Types ========
|
|
17
|
+
// =========================================
|
|
18
|
+
|
|
19
|
+
type NotificationSettingsStackParamList = {
|
|
20
|
+
NotificationSettings: {}
|
|
21
|
+
PreferredAppSelection: PreferredAppSelectionScreenProps['route']['params']
|
|
22
|
+
GroupNotificationSettings: GroupNotificationSettingsScreenProps['route']['params']
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
enum SectionTypes {
|
|
26
|
+
header,
|
|
27
|
+
hidden,
|
|
28
|
+
setting,
|
|
29
|
+
view,
|
|
30
|
+
link,
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type SectionListData = Array<
|
|
34
|
+
| DataItem<{ title: string }, SectionTypes.header>
|
|
35
|
+
| DataItem<ViewProps, SectionTypes.view>
|
|
36
|
+
| DataItem<SettingRowProps, SectionTypes.setting>
|
|
37
|
+
| DataItem<LinkRowProps, SectionTypes.link>
|
|
38
|
+
| DataItem<any, SectionTypes.hidden>
|
|
39
|
+
>
|
|
40
|
+
|
|
41
|
+
interface DataItem<T, TName extends SectionTypes> {
|
|
42
|
+
type: TName
|
|
43
|
+
data: T
|
|
44
|
+
sectionOuterStyle?: ViewStyle
|
|
45
|
+
sectionInnerStyle?: ViewStyle
|
|
46
|
+
showBottomBorder?: boolean
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// =================================
|
|
50
|
+
// ====== Components ===============
|
|
51
|
+
// =================================
|
|
52
|
+
|
|
53
|
+
export type NotificationSettingsScreenProps = StaticScreenProps<{}>
|
|
54
|
+
|
|
55
|
+
export function NotificationSettingsScreen({}: NotificationSettingsScreenProps) {
|
|
56
|
+
const navigation = useNavigation<NativeStackNavigationProp<NotificationSettingsStackParamList>>()
|
|
57
|
+
const styles = useStyles()
|
|
58
|
+
const { data: chatTypes } = useChatTypes()
|
|
59
|
+
const { data: groups } = useGroups()
|
|
60
|
+
|
|
61
|
+
// Header configuration
|
|
62
|
+
const HeaderRight = useCallback(() => {
|
|
63
|
+
return (
|
|
64
|
+
<HeaderTextButton
|
|
65
|
+
onPress={() => {
|
|
66
|
+
// Save settings logic would go here
|
|
67
|
+
navigation.goBack()
|
|
68
|
+
}}
|
|
69
|
+
title="Done"
|
|
70
|
+
/>
|
|
71
|
+
)
|
|
72
|
+
}, [navigation])
|
|
73
|
+
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
navigation.setOptions({
|
|
76
|
+
headerRight: HeaderRight,
|
|
77
|
+
})
|
|
78
|
+
}, [HeaderRight, navigation])
|
|
79
|
+
|
|
80
|
+
// Build section data
|
|
81
|
+
const listData = [
|
|
82
|
+
{
|
|
83
|
+
type: SectionTypes.header,
|
|
84
|
+
data: { title: 'Preferred app' },
|
|
85
|
+
sectionInnerStyle: styles.sectionInnerHeader,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
type: SectionTypes.view,
|
|
89
|
+
data: {
|
|
90
|
+
children: (
|
|
91
|
+
<Text variant="tertiary" style={styles.sectionDescription}>
|
|
92
|
+
Choose which app receives each type of chat notification
|
|
93
|
+
</Text>
|
|
94
|
+
),
|
|
95
|
+
},
|
|
96
|
+
showBottomBorder: true,
|
|
97
|
+
},
|
|
98
|
+
...chatTypes.map(type => ({
|
|
99
|
+
type: SectionTypes.link,
|
|
100
|
+
data: {
|
|
101
|
+
title: `${type.title} Conversations`,
|
|
102
|
+
rightLabel: type.preferredApp,
|
|
103
|
+
onPress: () =>
|
|
104
|
+
navigation.navigate('PreferredAppSelection', {
|
|
105
|
+
chatTypeId: type.id,
|
|
106
|
+
}),
|
|
107
|
+
},
|
|
108
|
+
sectionInnerStyle: { paddingVertical: 0 },
|
|
109
|
+
showBottomBorder: true,
|
|
110
|
+
})),
|
|
111
|
+
{
|
|
112
|
+
type: SectionTypes.header,
|
|
113
|
+
data: {
|
|
114
|
+
title: 'Manage chat settings',
|
|
115
|
+
},
|
|
116
|
+
sectionInnerStyle: styles.sectionInnerHeader,
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
type: SectionTypes.view,
|
|
120
|
+
data: {
|
|
121
|
+
children: (
|
|
122
|
+
<Text variant="tertiary" style={styles.sectionDescription}>
|
|
123
|
+
Notification settings for all of your group, team and event related conversations
|
|
124
|
+
</Text>
|
|
125
|
+
),
|
|
126
|
+
},
|
|
127
|
+
showBottomBorder: true,
|
|
128
|
+
},
|
|
129
|
+
...groups.map(group => ({
|
|
130
|
+
type: SectionTypes.link,
|
|
131
|
+
data: {
|
|
132
|
+
title: group.name,
|
|
133
|
+
rightLabel: group.sourceType,
|
|
134
|
+
onPress: () =>
|
|
135
|
+
navigation.navigate('GroupNotificationSettings', {
|
|
136
|
+
groupId: group.id,
|
|
137
|
+
title: group.name || 'Group',
|
|
138
|
+
}),
|
|
139
|
+
},
|
|
140
|
+
sectionInnerStyle: { paddingVertical: 0 },
|
|
141
|
+
showBottomBorder: true,
|
|
142
|
+
})),
|
|
143
|
+
].filter(item => item.type !== SectionTypes.hidden)
|
|
144
|
+
|
|
145
|
+
const headerIndices = listData
|
|
146
|
+
.map(({ type }, index) => (type === SectionTypes.header ? index : undefined))
|
|
147
|
+
.filter(isDefined)
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<View style={styles.listContainer}>
|
|
151
|
+
<FlatList
|
|
152
|
+
data={listData as SectionListData}
|
|
153
|
+
contentContainerStyle={styles.contentContainer}
|
|
154
|
+
renderItem={({ item, index }) => {
|
|
155
|
+
const [isStart, isEnd] = [
|
|
156
|
+
index === 0 || headerIndices.includes(index),
|
|
157
|
+
index === listData.length - 1 || headerIndices.includes(index + 1),
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
switch (item.type) {
|
|
161
|
+
case SectionTypes.header:
|
|
162
|
+
return (
|
|
163
|
+
<ListSection
|
|
164
|
+
isStart={isStart}
|
|
165
|
+
isEnd={isEnd}
|
|
166
|
+
showBottomBorder={item?.showBottomBorder}
|
|
167
|
+
outerStyle={item?.sectionOuterStyle}
|
|
168
|
+
innerStyle={item?.sectionInnerStyle}
|
|
169
|
+
>
|
|
170
|
+
<Heading variant="h2">{item.data.title}</Heading>
|
|
171
|
+
</ListSection>
|
|
172
|
+
)
|
|
173
|
+
case SectionTypes.setting:
|
|
174
|
+
return (
|
|
175
|
+
<ListSection
|
|
176
|
+
isStart={isStart}
|
|
177
|
+
isEnd={isEnd}
|
|
178
|
+
showBottomBorder={item?.showBottomBorder}
|
|
179
|
+
outerStyle={item?.sectionOuterStyle}
|
|
180
|
+
innerStyle={item?.sectionInnerStyle}
|
|
181
|
+
>
|
|
182
|
+
<SettingRow {...item.data} />
|
|
183
|
+
</ListSection>
|
|
184
|
+
)
|
|
185
|
+
case SectionTypes.view:
|
|
186
|
+
return (
|
|
187
|
+
<ListSection
|
|
188
|
+
isStart={isStart}
|
|
189
|
+
isEnd={isEnd}
|
|
190
|
+
showBottomBorder={item?.showBottomBorder}
|
|
191
|
+
outerStyle={item?.sectionOuterStyle}
|
|
192
|
+
innerStyle={item?.sectionInnerStyle}
|
|
193
|
+
>
|
|
194
|
+
<View {...item.data} style={item.data.style} />
|
|
195
|
+
</ListSection>
|
|
196
|
+
)
|
|
197
|
+
case SectionTypes.link:
|
|
198
|
+
return (
|
|
199
|
+
<ListSection
|
|
200
|
+
isStart={isStart}
|
|
201
|
+
isEnd={isEnd}
|
|
202
|
+
showBottomBorder={item?.showBottomBorder}
|
|
203
|
+
outerStyle={item?.sectionOuterStyle}
|
|
204
|
+
innerStyle={item?.sectionInnerStyle}
|
|
205
|
+
>
|
|
206
|
+
<LinkRow {...item.data} />
|
|
207
|
+
</ListSection>
|
|
208
|
+
)
|
|
209
|
+
default:
|
|
210
|
+
return null
|
|
211
|
+
}
|
|
212
|
+
}}
|
|
213
|
+
/>
|
|
214
|
+
</View>
|
|
215
|
+
)
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
interface ListSectionProps {
|
|
219
|
+
isStart?: boolean
|
|
220
|
+
isEnd?: boolean
|
|
221
|
+
showBottomBorder?: boolean
|
|
222
|
+
outerStyle?: ViewStyle
|
|
223
|
+
innerStyle?: ViewStyle
|
|
224
|
+
children: ReactNode
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function ListSection({
|
|
228
|
+
isStart,
|
|
229
|
+
isEnd,
|
|
230
|
+
showBottomBorder,
|
|
231
|
+
outerStyle,
|
|
232
|
+
innerStyle,
|
|
233
|
+
children,
|
|
234
|
+
}: ListSectionProps) {
|
|
235
|
+
const styles = useStyles({ isStart, isEnd })
|
|
236
|
+
const bottomBorder = showBottomBorder ? styles.sectionInnerBottomBorder : {}
|
|
237
|
+
|
|
238
|
+
return (
|
|
239
|
+
<View style={[styles.sectionOuterBase, outerStyle]}>
|
|
240
|
+
<View style={[styles.sectionInnerBase, bottomBorder, innerStyle]}>{children}</View>
|
|
241
|
+
</View>
|
|
242
|
+
)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
interface SettingRowProps {
|
|
246
|
+
title: string
|
|
247
|
+
style?: ViewStyle
|
|
248
|
+
rightItem?: ReactNode
|
|
249
|
+
rightLabel?: string
|
|
250
|
+
rightItemStyle?: ViewStyle
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function SettingRow({ title, style, rightLabel, rightItem, rightItemStyle = {} }: SettingRowProps) {
|
|
254
|
+
const styles = useStyles()
|
|
255
|
+
|
|
256
|
+
return (
|
|
257
|
+
<View style={[styles.settingRow, style]}>
|
|
258
|
+
<View style={styles.settingRowContent}>
|
|
259
|
+
<Text variant="plain" style={styles.settingRowText}>
|
|
260
|
+
{title}
|
|
261
|
+
</Text>
|
|
262
|
+
{Boolean(rightLabel) && <Text variant="footnote">{rightLabel}</Text>}
|
|
263
|
+
</View>
|
|
264
|
+
{Boolean(rightItem) && <View style={rightItemStyle}>{rightItem}</View>}
|
|
265
|
+
</View>
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
interface LinkRowProps {
|
|
270
|
+
title: string
|
|
271
|
+
rightLabel: string
|
|
272
|
+
onPress: () => void
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function LinkRow({ title, rightLabel, onPress }: LinkRowProps) {
|
|
276
|
+
const styles = useLinkRowStyles()
|
|
277
|
+
const isSourceType = rightLabel === 'Team' || rightLabel === 'Group' || rightLabel === 'PlanTeam'
|
|
278
|
+
|
|
279
|
+
return (
|
|
280
|
+
<PlatformPressable
|
|
281
|
+
style={styles.row}
|
|
282
|
+
onPress={onPress}
|
|
283
|
+
accessibilityRole="link"
|
|
284
|
+
accessibilityLabel={title}
|
|
285
|
+
accessibilityHint={`Navigate to ${title} settings`}
|
|
286
|
+
>
|
|
287
|
+
<View style={styles.innerContainer}>
|
|
288
|
+
<Text style={styles.title} numberOfLines={2}>
|
|
289
|
+
{title}
|
|
290
|
+
</Text>
|
|
291
|
+
<View style={styles.rightContent}>
|
|
292
|
+
{isSourceType ? (
|
|
293
|
+
<Badge label={rightLabel} appearance="neutral" variant="meta" />
|
|
294
|
+
) : (
|
|
295
|
+
<Text numberOfLines={1} style={styles.rightLabelText}>
|
|
296
|
+
{rightLabel}
|
|
297
|
+
</Text>
|
|
298
|
+
)}
|
|
299
|
+
{Platform.OS === 'ios' && (
|
|
300
|
+
<Icon name="general.rightChevron" size={16} style={styles.icon} />
|
|
301
|
+
)}
|
|
302
|
+
</View>
|
|
303
|
+
</View>
|
|
304
|
+
</PlatformPressable>
|
|
305
|
+
)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// =================================
|
|
309
|
+
// ====== Styles ===================
|
|
310
|
+
// =================================
|
|
311
|
+
|
|
312
|
+
const useStyles = ({}: { isStart?: boolean; isEnd?: boolean } = {}) => {
|
|
313
|
+
const { colors } = useTheme()
|
|
314
|
+
const headerBottomPadding = 0
|
|
315
|
+
|
|
316
|
+
return StyleSheet.create({
|
|
317
|
+
listContainer: {
|
|
318
|
+
flex: 1,
|
|
319
|
+
},
|
|
320
|
+
contentContainer: {},
|
|
321
|
+
sectionOuterBase: {
|
|
322
|
+
paddingLeft: 16,
|
|
323
|
+
},
|
|
324
|
+
sectionInnerBase: {
|
|
325
|
+
paddingRight: 16,
|
|
326
|
+
paddingVertical: 16,
|
|
327
|
+
},
|
|
328
|
+
sectionInnerHeader: {
|
|
329
|
+
paddingBottom: headerBottomPadding,
|
|
330
|
+
},
|
|
331
|
+
sectionInnerBottomBorder: {
|
|
332
|
+
borderBottomColor: colors.borderColorDefaultBase,
|
|
333
|
+
borderBottomWidth: 1,
|
|
334
|
+
},
|
|
335
|
+
sectionDescription: {
|
|
336
|
+
color: colors.textColorDefaultSecondary,
|
|
337
|
+
},
|
|
338
|
+
settingRow: {
|
|
339
|
+
flexDirection: 'row',
|
|
340
|
+
justifyContent: 'space-between',
|
|
341
|
+
alignItems: 'center',
|
|
342
|
+
gap: 8,
|
|
343
|
+
},
|
|
344
|
+
settingRowContent: {
|
|
345
|
+
flex: 1,
|
|
346
|
+
},
|
|
347
|
+
settingRowText: {
|
|
348
|
+
lineHeight: 20,
|
|
349
|
+
},
|
|
350
|
+
})
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const useLinkRowStyles = () => {
|
|
354
|
+
const theme = useTheme()
|
|
355
|
+
|
|
356
|
+
return StyleSheet.create({
|
|
357
|
+
row: {
|
|
358
|
+
paddingLeft: 0,
|
|
359
|
+
paddingVertical: 16,
|
|
360
|
+
},
|
|
361
|
+
innerContainer: {
|
|
362
|
+
flexDirection: 'row',
|
|
363
|
+
alignItems: 'center',
|
|
364
|
+
justifyContent: 'space-between',
|
|
365
|
+
gap: 12,
|
|
366
|
+
},
|
|
367
|
+
rightContent: {
|
|
368
|
+
flexDirection: 'row',
|
|
369
|
+
alignItems: 'center',
|
|
370
|
+
gap: 8,
|
|
371
|
+
},
|
|
372
|
+
title: {
|
|
373
|
+
flexShrink: 1,
|
|
374
|
+
alignSelf: 'center',
|
|
375
|
+
},
|
|
376
|
+
rightLabelText: {
|
|
377
|
+
color: theme.colors.textColorDefaultSecondary,
|
|
378
|
+
},
|
|
379
|
+
icon: {
|
|
380
|
+
color: theme.colors.iconColorDefaultDisabled,
|
|
381
|
+
},
|
|
382
|
+
})
|
|
383
|
+
}
|
|
@@ -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
|
+
}
|