@planningcenter/chat-react-native 3.24.4 → 3.25.0-rc.1

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.
Files changed (86) hide show
  1. package/build/components/conversations/conversation_actions.js +3 -3
  2. package/build/components/conversations/conversation_actions.js.map +1 -1
  3. package/build/components/conversations/conversations.d.ts.map +1 -1
  4. package/build/components/conversations/conversations.js +1 -0
  5. package/build/components/conversations/conversations.js.map +1 -1
  6. package/build/hooks/use_conversation.d.ts.map +1 -1
  7. package/build/hooks/use_conversation.js +13 -1
  8. package/build/hooks/use_conversation.js.map +1 -1
  9. package/build/hooks/use_conversation_membership.d.ts +5 -0
  10. package/build/hooks/use_conversation_membership.d.ts.map +1 -0
  11. package/build/hooks/use_conversation_membership.js +63 -0
  12. package/build/hooks/use_conversation_membership.js.map +1 -0
  13. package/build/hooks/use_conversations_actions.d.ts.map +1 -1
  14. package/build/hooks/use_conversations_actions.js +4 -0
  15. package/build/hooks/use_conversations_actions.js.map +1 -1
  16. package/build/hooks/use_features.d.ts +1 -0
  17. package/build/hooks/use_features.d.ts.map +1 -1
  18. package/build/hooks/use_features.js +1 -0
  19. package/build/hooks/use_features.js.map +1 -1
  20. package/build/navigation/index.d.ts +10 -0
  21. package/build/navigation/index.d.ts.map +1 -1
  22. package/build/navigation/index.js +12 -2
  23. package/build/navigation/index.js.map +1 -1
  24. package/build/screens/conversation_details_screen.d.ts.map +1 -1
  25. package/build/screens/conversation_details_screen.js +50 -16
  26. package/build/screens/conversation_details_screen.js.map +1 -1
  27. package/build/screens/conversation_notification_level_select_screen.d.ts +8 -0
  28. package/build/screens/conversation_notification_level_select_screen.d.ts.map +1 -0
  29. package/build/screens/conversation_notification_level_select_screen.js +49 -0
  30. package/build/screens/conversation_notification_level_select_screen.js.map +1 -0
  31. package/build/screens/conversation_screen.d.ts +3 -1
  32. package/build/screens/conversation_screen.d.ts.map +1 -1
  33. package/build/screens/conversation_screen.js +16 -5
  34. package/build/screens/conversation_screen.js.map +1 -1
  35. package/build/screens/group_notification_level_select_screen.d.ts +8 -0
  36. package/build/screens/group_notification_level_select_screen.d.ts.map +1 -0
  37. package/build/screens/group_notification_level_select_screen.js +40 -0
  38. package/build/screens/group_notification_level_select_screen.js.map +1 -0
  39. package/build/screens/group_notification_settings_screen.d.ts.map +1 -1
  40. package/build/screens/group_notification_settings_screen.js +30 -17
  41. package/build/screens/group_notification_settings_screen.js.map +1 -1
  42. package/build/screens/notification_settings/hooks/groups.d.ts +6 -5
  43. package/build/screens/notification_settings/hooks/groups.d.ts.map +1 -1
  44. package/build/screens/notification_settings/hooks/groups.js +63 -36
  45. package/build/screens/notification_settings/hooks/groups.js.map +1 -1
  46. package/build/screens/notification_settings_screen.d.ts.map +1 -1
  47. package/build/screens/notification_settings_screen.js +25 -11
  48. package/build/screens/notification_settings_screen.js.map +1 -1
  49. package/build/types/resources/conversation.d.ts +2 -3
  50. package/build/types/resources/conversation.d.ts.map +1 -1
  51. package/build/types/resources/conversation.js.map +1 -1
  52. package/build/types/resources/conversation_membership.d.ts +16 -0
  53. package/build/types/resources/conversation_membership.d.ts.map +1 -0
  54. package/build/types/resources/conversation_membership.js +2 -0
  55. package/build/types/resources/conversation_membership.js.map +1 -0
  56. package/build/types/resources/group_membership.d.ts +7 -0
  57. package/build/types/resources/group_membership.d.ts.map +1 -1
  58. package/build/types/resources/group_membership.js.map +1 -1
  59. package/build/types/resources/index.d.ts +1 -0
  60. package/build/types/resources/index.d.ts.map +1 -1
  61. package/build/types/resources/index.js +1 -0
  62. package/build/types/resources/index.js.map +1 -1
  63. package/build/utils/deep_snake_case_keys.d.ts +4 -0
  64. package/build/utils/deep_snake_case_keys.d.ts.map +1 -0
  65. package/build/utils/deep_snake_case_keys.js +13 -0
  66. package/build/utils/deep_snake_case_keys.js.map +1 -0
  67. package/package.json +2 -2
  68. package/src/components/conversations/conversation_actions.tsx +3 -3
  69. package/src/components/conversations/conversations.tsx +1 -0
  70. package/src/hooks/use_conversation.ts +13 -1
  71. package/src/hooks/use_conversation_membership.ts +91 -0
  72. package/src/hooks/use_conversations_actions.ts +4 -0
  73. package/src/hooks/use_features.ts +1 -0
  74. package/src/navigation/index.tsx +19 -1
  75. package/src/screens/conversation_details_screen.tsx +87 -22
  76. package/src/screens/conversation_notification_level_select_screen.tsx +73 -0
  77. package/src/screens/conversation_screen.tsx +18 -4
  78. package/src/screens/group_notification_level_select_screen.tsx +62 -0
  79. package/src/screens/group_notification_settings_screen.tsx +53 -18
  80. package/src/screens/notification_settings/hooks/groups.ts +76 -37
  81. package/src/screens/notification_settings_screen.tsx +24 -21
  82. package/src/types/resources/conversation.ts +2 -3
  83. package/src/types/resources/conversation_membership.ts +17 -0
  84. package/src/types/resources/group_membership.ts +7 -0
  85. package/src/types/resources/index.ts +1 -0
  86. package/src/utils/deep_snake_case_keys.ts +16 -0
@@ -1,25 +1,29 @@
1
+ import {
2
+ HeaderTitle as ElementsHeaderTitle,
3
+ HeaderTitleProps,
4
+ PlatformPressable,
5
+ } from '@react-navigation/elements'
1
6
  import { StackActions, StaticScreenProps, useNavigation } from '@react-navigation/native'
2
7
  import React, {
3
8
  useCallback,
4
9
  useEffect,
10
+ useRef,
5
11
  useState,
6
- type SetStateAction,
7
12
  type Dispatch,
8
13
  type ReactNode,
9
- useRef,
14
+ type SetStateAction,
10
15
  } from 'react'
11
16
  import {
17
+ Alert,
12
18
  FlatList,
19
+ Platform,
20
+ Pressable,
13
21
  StyleSheet,
14
22
  TextInput,
15
23
  View,
16
- type ViewStyle,
17
24
  type ViewProps,
18
- Pressable,
19
- Alert,
20
- Platform,
25
+ type ViewStyle,
21
26
  } from 'react-native'
22
- import { HeaderTitle as ElementsHeaderTitle, HeaderTitleProps } from '@react-navigation/elements'
23
27
  import {
24
28
  Badge,
25
29
  ChildNotice,
@@ -31,6 +35,8 @@ import {
31
35
  TextButton,
32
36
  type TextStyle,
33
37
  } from '../components'
38
+ import { HeaderTextButton } from '../components/display/platform_modal_header_buttons'
39
+ import { ButtonAppearanceUnion } from '../components/display/utils/button_colors'
34
40
  import { useSuspensePaginator, useTheme } from '../hooks'
35
41
  import {
36
42
  useConversation,
@@ -39,14 +45,11 @@ import {
39
45
  useConversationMute,
40
46
  useConversationUpdate,
41
47
  } from '../hooks/use_conversation'
48
+
49
+ import { availableFeatures, useFeatures } from '../hooks/use_features'
42
50
  import { MemberResource, isDefined } from '../types'
43
- import { HeaderTextButton } from '../components/display/platform_modal_header_buttons'
44
- import { tokens } from '../vendor/tapestry/tokens'
45
- import { ButtonAppearanceUnion } from '../components/display/utils/button_colors'
46
51
  import { GroupResource } from '../types/resources/group_resource'
47
- import { useFeatures } from '../hooks/use_features'
48
- import { availableFeatures } from '../hooks/use_features'
49
-
52
+ import { tokens } from '../vendor/tapestry/tokens'
50
53
  // =========================================
51
54
  // ====== Factory Constants & Types ========
52
55
  // =========================================
@@ -56,6 +59,7 @@ enum SectionTypes {
56
59
  hidden,
57
60
  members,
58
61
  loadingMembers,
62
+ navigableSetting,
59
63
  setting,
60
64
  view,
61
65
  }
@@ -64,6 +68,7 @@ type SectionListData = Array<
64
68
  | DataItem<{ title: string }, SectionTypes.header>
65
69
  | DataItem<MemberResource, SectionTypes.members>
66
70
  | DataItem<any, SectionTypes.loadingMembers>
71
+ | DataItem<NavigableSettingRowProps, SectionTypes.navigableSetting>
67
72
  | DataItem<ViewProps, SectionTypes.view>
68
73
  | DataItem<SettingRowProps, SectionTypes.setting>
69
74
  | DataItem<any, SectionTypes.hidden>
@@ -91,16 +96,20 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
91
96
 
92
97
  const { data: conversation } = useConversation(route.params)
93
98
  const [title, setTitle] = useState(conversation.title)
94
- const { muted, setMuted } = useConversationMute(route.params)
99
+ const { conversation_id } = route.params
95
100
  const { repliesDisabled, setRepliesDisabled } = useConversationDisableReplies(route.params)
96
101
  const { mutate: saveTitle } = useConversationUpdate(route.params)
97
102
  const { mutate: deleteConversation } = useConversationDelete(route.params)
98
103
  const { featureEnabled } = useFeatures()
99
104
  const repliesEnabled = featureEnabled(availableFeatures.threaded_replies)
105
+ const granularNotificationsEnabled = featureEnabled(availableFeatures.granular_notifications_ui)
106
+ const { muted, setMuted } = useConversationMute({ conversation_id })
100
107
 
101
108
  const trimmedTitle = title.trim()
102
109
  const emptyTitle = trimmedTitle === '' || title === null
103
110
 
111
+ const notificationLevelDescription =
112
+ conversation?.conversationMembership?.notificationLevelDescription
104
113
  const canUpdate = conversation.memberAbility?.canUpdate || false
105
114
  const canDelete = conversation.memberAbility?.canDelete || false
106
115
  const isLeader = conversation.memberAbility?.leader || false
@@ -243,14 +252,25 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
243
252
  showBottomBorder: true,
244
253
  sectionInnerStyle: styles.sectionInnerHeaderWithBottomBorder,
245
254
  },
246
- {
247
- type: SectionTypes.setting,
248
- data: {
249
- title: 'Mute',
250
- rightItem: <Switch value={muted} onChange={() => setMuted(!muted)} />,
251
- },
252
- showBottomBorder: true,
253
- },
255
+ granularNotificationsEnabled
256
+ ? {
257
+ type: SectionTypes.navigableSetting,
258
+ data: {
259
+ title: 'Notifications',
260
+ subtitle: notificationLevelDescription,
261
+ onPress: () =>
262
+ navigation.navigate('ConversationNotificationLevelSelect', { conversation_id }),
263
+ },
264
+ showBottomBorder: true,
265
+ }
266
+ : {
267
+ type: SectionTypes.setting,
268
+ data: {
269
+ title: 'Mute',
270
+ rightItem: <Switch value={muted} onValueChange={value => setMuted(value)} />,
271
+ },
272
+ showBottomBorder: true,
273
+ },
254
274
  {
255
275
  type: canUpdate ? SectionTypes.setting : SectionTypes.hidden,
256
276
  data: {
@@ -350,6 +370,18 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
350
370
  <Text>Loading more...</Text>
351
371
  </ListSection>
352
372
  )
373
+ case SectionTypes.navigableSetting:
374
+ return (
375
+ <ListSection
376
+ isStart={isStart}
377
+ isEnd={isEnd}
378
+ showBottomBorder={item?.showBottomBorder}
379
+ outerStyle={item?.sectionOuterStyle}
380
+ innerStyle={item?.sectionInnerStyle}
381
+ >
382
+ <NavigableSettingRow {...item.data} />
383
+ </ListSection>
384
+ )
353
385
  case SectionTypes.setting:
354
386
  return (
355
387
  <ListSection
@@ -459,6 +491,12 @@ function TitleInput({ canUpdate, title, setTitle, style, isEmpty }: InputProps)
459
491
  )
460
492
  }
461
493
 
494
+ interface NavigableSettingRowProps {
495
+ title: string
496
+ subtitle?: string
497
+ onPress: () => void
498
+ }
499
+
462
500
  interface SettingRowProps {
463
501
  title: string
464
502
  style?: ViewStyle
@@ -510,6 +548,30 @@ function SettingRow({
510
548
  )
511
549
  }
512
550
 
551
+ function NavigableSettingRow({ title, subtitle, onPress }: NavigableSettingRowProps) {
552
+ const styles = useStyles()
553
+ return (
554
+ <PlatformPressable onPress={onPress} accessibilityRole="button">
555
+ <View style={styles.settingRow}>
556
+ <View style={styles.settingRowContent}>
557
+ <Text variant="plain" style={styles.settingRowText}>
558
+ {title}
559
+ </Text>
560
+ {Boolean(subtitle) && <Text variant="footnote">{subtitle}</Text>}
561
+ </View>
562
+ {Platform.OS === 'ios' && (
563
+ <Icon
564
+ name="general.rightChevron"
565
+ size={16}
566
+ style={styles.navigableSettingChevron}
567
+ accessibilityElementsHidden
568
+ />
569
+ )}
570
+ </View>
571
+ </PlatformPressable>
572
+ )
573
+ }
574
+
513
575
  function TeamsGroup({ teams }: { teams: GroupResource[] }) {
514
576
  const styles = useStyles()
515
577
 
@@ -628,6 +690,9 @@ const useStyles = ({ isStart, isEnd }: { isStart?: boolean; isEnd?: boolean } =
628
690
  settingRowText: {
629
691
  lineHeight: 20,
630
692
  },
693
+ navigableSettingChevron: {
694
+ color: colors.iconColorDefaultDisabled,
695
+ },
631
696
  teamGroup: {
632
697
  flexDirection: 'row',
633
698
  gap: 4,
@@ -0,0 +1,73 @@
1
+ import { StaticScreenProps, useNavigation } from '@react-navigation/native'
2
+ import React from 'react'
3
+ import { StyleSheet } from 'react-native'
4
+ import FormSheet, { getFormSheetScreenOptions } from '../components/primitive/form_sheet'
5
+ import { PressableRow } from '../components'
6
+ import { useTheme } from '../hooks'
7
+ import { useConversation } from '../hooks/use_conversation'
8
+ import { useConversationMembershipUpdate } from '../hooks/use_conversation_membership'
9
+ import { NotificationLevelValue } from '../types'
10
+
11
+ export const ConversationNotificationLevelSelectScreenOptions = getFormSheetScreenOptions({
12
+ headerTitle: 'Notification level',
13
+ sheetAllowedDetents: [0.35],
14
+ })
15
+
16
+ export type ConversationNotificationLevelSelectScreenProps = StaticScreenProps<{
17
+ conversation_id: number
18
+ }>
19
+
20
+ export function ConversationNotificationLevelSelectScreen({
21
+ route,
22
+ }: ConversationNotificationLevelSelectScreenProps) {
23
+ const { conversation_id } = route.params
24
+ const navigation = useNavigation()
25
+ const styles = useStyles()
26
+ const { colors } = useTheme()
27
+ const { data: conversation } = useConversation({ conversation_id })
28
+ const { mutate: updateNotificationLevel } = useConversationMembershipUpdate({ conversation_id })
29
+
30
+ const notificationLevel = conversation.conversationMembership?.notificationLevel
31
+ const notificationLevelOptions =
32
+ conversation.conversationMembership?.notificationLevelOptions ?? []
33
+
34
+ const handleSelect = (value: NotificationLevelValue, isActive: boolean) => {
35
+ if (!isActive) {
36
+ const muted = value === 'nothing'
37
+ updateNotificationLevel({
38
+ muted,
39
+ notificationLevel: value as typeof notificationLevel,
40
+ })
41
+ }
42
+ navigation.goBack()
43
+ }
44
+
45
+ return (
46
+ <FormSheet.Root contentStyle={styles.content}>
47
+ {notificationLevelOptions.map(option => {
48
+ const isActive = option.enabled
49
+ return (
50
+ <PressableRow
51
+ key={option.value}
52
+ text={option.description}
53
+ isActive={isActive}
54
+ onPress={() => handleSelect(option.value, isActive)}
55
+ iconColor={colors.statusSuccessIcon}
56
+ style={styles.row}
57
+ />
58
+ )
59
+ })}
60
+ </FormSheet.Root>
61
+ )
62
+ }
63
+
64
+ const useStyles = () => {
65
+ return StyleSheet.create({
66
+ content: {
67
+ paddingTop: 20,
68
+ },
69
+ row: {
70
+ borderBottomWidth: 0,
71
+ },
72
+ })
73
+ }
@@ -53,6 +53,7 @@ export type ConversationRouteProps = {
53
53
  subtitle?: string
54
54
  badge?: ConversationBadgeResource
55
55
  deleted?: boolean
56
+ muted?: boolean
56
57
  }
57
58
 
58
59
  export type ConversationScreenProps = StaticScreenProps<ConversationRouteProps>
@@ -109,6 +110,8 @@ function ConversationScreenContent({ route }: ConversationScreenProps) {
109
110
  const replyHeaderTitle = replyRootAuthorFirstName
110
111
  ? `Reply to ${replyRootAuthorFirstName}`
111
112
  : 'Reply'
113
+ // Prefer the membership for optimistic updates.
114
+ const muted = conversation.conversationMembership?.muted ?? conversation.muted
112
115
 
113
116
  const listRef = useRef<FlatList>(null)
114
117
  const [showJumpToBottomButton, setShowJumpToBottomButton] = useState(false)
@@ -134,9 +137,10 @@ function ConversationScreenContent({ route }: ConversationScreenProps) {
134
137
  title: title,
135
138
  badge: badges?.[0],
136
139
  deleted: conversation?.deleted,
140
+ muted,
137
141
  })
138
142
  }
139
- }, [navigation, title, badges, conversation?.deleted, reply_root_id, replyHeaderTitle])
143
+ }, [navigation, title, badges, conversation?.deleted, reply_root_id, replyHeaderTitle, muted])
140
144
 
141
145
  if (!conversation || conversation.deleted) {
142
146
  return (
@@ -390,6 +394,7 @@ interface ConversationScreenTitleProps extends HeaderTitleProps {
390
394
  conversation_id: number
391
395
  badge?: ConversationBadgeResource
392
396
  deleted?: boolean
397
+ muted?: boolean
393
398
  }
394
399
 
395
400
  export const ConversationScreenTitle = ({
@@ -398,6 +403,7 @@ export const ConversationScreenTitle = ({
398
403
  children,
399
404
  style,
400
405
  deleted,
406
+ muted,
401
407
  }: ConversationScreenTitleProps) => {
402
408
  const styles = usePressableHeaderStyle()
403
409
  const navigation = useNavigation()
@@ -416,9 +422,12 @@ export const ConversationScreenTitle = ({
416
422
  }}
417
423
  >
418
424
  <View style={styles.titleWrapper}>
419
- <HeaderTitle maxFontSizeMultiplier={1} style={style}>
420
- {children}
421
- </HeaderTitle>
425
+ <View style={styles.titleTextContainer}>
426
+ <HeaderTitle maxFontSizeMultiplier={1} style={style}>
427
+ {children}
428
+ </HeaderTitle>
429
+ </View>
430
+ {muted && <Icon name="general.bellMuted" size={12} />}
422
431
  {!deleted && <Icon name="general.downChevron" size={12} />}
423
432
  </View>
424
433
  <Badge
@@ -438,6 +447,7 @@ const usePressableHeaderStyle = () => {
438
447
  container: {
439
448
  alignItems: Platform.select({ android: 'flex-start', default: 'center' }),
440
449
  marginRight: Platform.select({ ios: 20, default: 16 }),
450
+ flex: 1,
441
451
  },
442
452
  titleWrapper: {
443
453
  alignItems: 'center',
@@ -445,6 +455,10 @@ const usePressableHeaderStyle = () => {
445
455
  flexDirection: 'row',
446
456
  flexShrink: 1,
447
457
  },
458
+ titleTextContainer: {
459
+ flexShrink: 1,
460
+ minWidth: 0,
461
+ },
448
462
  badge: {
449
463
  alignSelf: Platform.select({ android: 'flex-start', default: 'center' }),
450
464
  marginTop: 2,
@@ -0,0 +1,62 @@
1
+ import { StaticScreenProps, useNavigation } from '@react-navigation/native'
2
+ import React from 'react'
3
+ import { StyleSheet } from 'react-native'
4
+ import { PressableRow } from '../components'
5
+ import FormSheet, { getFormSheetScreenOptions } from '../components/primitive/form_sheet'
6
+ import { useTheme } from '../hooks'
7
+ import { useGroup, useGroupMembershipUpdate } from './notification_settings/hooks/groups'
8
+
9
+ export const GroupNotificationLevelSelectScreenOptions = getFormSheetScreenOptions({
10
+ headerTitle: 'Notification level',
11
+ sheetAllowedDetents: [0.35],
12
+ })
13
+
14
+ export type GroupNotificationLevelSelectScreenProps = StaticScreenProps<{
15
+ groupId: number | string
16
+ }>
17
+
18
+ export function GroupNotificationLevelSelectScreen({
19
+ route,
20
+ }: GroupNotificationLevelSelectScreenProps) {
21
+ const { groupId } = route.params
22
+ const navigation = useNavigation()
23
+ const styles = useStyles()
24
+ const { colors } = useTheme()
25
+ const { data: group } = useGroup({ groupId })
26
+ const { mutate: updateNotificationLevel } = useGroupMembershipUpdate({ groupId })
27
+
28
+ const notificationLevelOptions = group.myGroupMembership?.notificationLevelOptions ?? []
29
+
30
+ const handleSelect = (value: string, isActive: boolean) => {
31
+ if (!isActive) {
32
+ updateNotificationLevel({ notificationLevel: value })
33
+ }
34
+ navigation.goBack()
35
+ }
36
+
37
+ return (
38
+ <FormSheet.Root contentStyle={styles.content}>
39
+ {notificationLevelOptions.map(option => (
40
+ <PressableRow
41
+ key={option.value}
42
+ text={option.description}
43
+ isActive={option.enabled}
44
+ onPress={() => handleSelect(option.value, option.enabled)}
45
+ iconColor={colors.statusSuccessIcon}
46
+ style={styles.row}
47
+ />
48
+ ))}
49
+ </FormSheet.Root>
50
+ )
51
+ }
52
+
53
+ const useStyles = () => {
54
+ return StyleSheet.create({
55
+ content: {
56
+ paddingTop: 20,
57
+ },
58
+ row: {
59
+ borderBottomWidth: 0,
60
+ },
61
+ })
62
+ }
@@ -1,10 +1,17 @@
1
+ import { PlatformPressable } from '@react-navigation/elements'
1
2
  import { StaticScreenProps, useNavigation } from '@react-navigation/native'
3
+ import type { NativeStackNavigationProp } from '@react-navigation/native-stack'
2
4
  import React, { useEffect } from 'react'
3
- import { StyleSheet, View } from 'react-native'
4
- import { Heading, Switch, Text } from '../components'
5
+ import { Platform, StyleSheet, View } from 'react-native'
6
+ import { Heading, Icon, Text } from '../components'
5
7
  import { useTheme } from '../hooks'
6
8
  import { platformFontWeightBold } from '../utils/styles'
7
- import { useGroup, useGroupMembershipUpdate } from './notification_settings/hooks/groups'
9
+ import { useGroup } from './notification_settings/hooks/groups'
10
+ import type { GroupNotificationLevelSelectScreenProps } from './group_notification_level_select_screen'
11
+
12
+ type GroupNotificationSettingsStackParamList = {
13
+ GroupNotificationLevelSelect: GroupNotificationLevelSelectScreenProps['route']['params']
14
+ }
8
15
 
9
16
  export type GroupNotificationSettingsScreenProps = StaticScreenProps<{
10
17
  groupId: number | string
@@ -13,12 +20,12 @@ export type GroupNotificationSettingsScreenProps = StaticScreenProps<{
13
20
 
14
21
  export function GroupNotificationSettingsScreen({ route }: GroupNotificationSettingsScreenProps) {
15
22
  const { groupId, title } = route.params
16
- const navigation = useNavigation()
23
+ const navigation =
24
+ useNavigation<NativeStackNavigationProp<GroupNotificationSettingsStackParamList>>()
17
25
  const styles = useStyles()
18
26
  const { data: group } = useGroup({ groupId })
19
- const { mutate: updateNotificationLevel } = useGroupMembershipUpdate({ groupId })
20
27
 
21
- const notificationsEnabled = group.myGroupMembership?.notificationLevel === 'everything'
28
+ const notificationLevelDescription = group.myGroupMembership?.notificationLevelDescription
22
29
 
23
30
  useEffect(() => {
24
31
  if (!group.name || title === group.name) return
@@ -26,9 +33,8 @@ export function GroupNotificationSettingsScreen({ route }: GroupNotificationSett
26
33
  navigation.setOptions({ title: group.name })
27
34
  }, [group.name, title, navigation])
28
35
 
29
- const handleToggle = (value: boolean) => {
30
- const notificationLevel = value ? 'everything' : 'nothing'
31
- updateNotificationLevel(notificationLevel)
36
+ const handleOpenSelector = () => {
37
+ navigation.navigate('GroupNotificationLevelSelect', { groupId })
32
38
  }
33
39
 
34
40
  return (
@@ -44,10 +50,30 @@ export function GroupNotificationSettingsScreen({ route }: GroupNotificationSett
44
50
  </Text>
45
51
  </View>
46
52
  </View>
47
- <View style={styles.settingRow}>
48
- <Text>Enable notifications</Text>
49
- <Switch value={notificationsEnabled} onValueChange={handleToggle} />
50
- </View>
53
+ <PlatformPressable
54
+ style={styles.settingRowPressable}
55
+ onPress={handleOpenSelector}
56
+ accessibilityRole="button"
57
+ accessibilityLabel="Notify me for"
58
+ accessibilityHint={`Currently set to ${notificationLevelDescription}. Tap to change.`}
59
+ >
60
+ <View style={styles.settingRowInner}>
61
+ <View style={styles.settingRowText}>
62
+ <Text>Notify me for</Text>
63
+ {Boolean(notificationLevelDescription) && (
64
+ <Text variant="tertiary">{notificationLevelDescription}</Text>
65
+ )}
66
+ </View>
67
+ {Platform.OS === 'ios' && (
68
+ <Icon
69
+ name="general.rightChevron"
70
+ size={16}
71
+ style={styles.chevron}
72
+ accessibilityElementsHidden
73
+ />
74
+ )}
75
+ </View>
76
+ </PlatformPressable>
51
77
  </View>
52
78
  )
53
79
  }
@@ -66,7 +92,7 @@ const useStyles = () => {
66
92
  },
67
93
  sectionInner: {
68
94
  paddingRight: 16,
69
- paddingTop: 16,
95
+ paddingTop: 24,
70
96
  paddingBottom: 12,
71
97
  borderBottomWidth: 1,
72
98
  borderBottomColor: colors.borderColorDefaultBase,
@@ -80,13 +106,22 @@ const useStyles = () => {
80
106
  groupNameBold: {
81
107
  fontWeight: platformFontWeightBold,
82
108
  },
83
- settingRow: {
109
+ settingRowPressable: {
110
+ paddingLeft: 16,
111
+ },
112
+ settingRowInner: {
84
113
  flexDirection: 'row',
85
- justifyContent: 'space-between',
86
114
  alignItems: 'center',
87
- paddingHorizontal: 16,
115
+ justifyContent: 'space-between',
116
+ paddingRight: 16,
88
117
  paddingVertical: 12,
89
- backgroundColor: colors.surfaceColor100,
118
+ },
119
+ settingRowText: {
120
+ flex: 1,
121
+ gap: 2,
122
+ },
123
+ chevron: {
124
+ color: colors.iconColorDefaultDisabled,
90
125
  },
91
126
  })
92
127
  }