@planningcenter/chat-react-native 3.42.3-qa-staging.7 → 3.42.3-qa-858.0

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 (80) hide show
  1. package/build/components/conversation/messages_disabled_banners.d.ts +3 -2
  2. package/build/components/conversation/messages_disabled_banners.d.ts.map +1 -1
  3. package/build/components/conversation/messages_disabled_banners.js +7 -2
  4. package/build/components/conversation/messages_disabled_banners.js.map +1 -1
  5. package/build/components/display/adult_requirement_notice.js +1 -1
  6. package/build/components/display/adult_requirement_notice.js.map +1 -1
  7. package/build/components/display/platform_modal_header_buttons.d.ts +45 -7
  8. package/build/components/display/platform_modal_header_buttons.d.ts.map +1 -1
  9. package/build/components/display/platform_modal_header_buttons.js +108 -9
  10. package/build/components/display/platform_modal_header_buttons.js.map +1 -1
  11. package/build/components/primitive/form_sheet.js +2 -2
  12. package/build/components/primitive/form_sheet.js.map +1 -1
  13. package/build/hooks/use_conversation.d.ts.map +1 -1
  14. package/build/hooks/use_conversation.js +0 -1
  15. package/build/hooks/use_conversation.js.map +1 -1
  16. package/build/hooks/use_conversation_jolt_events.d.ts.map +1 -1
  17. package/build/hooks/use_conversation_jolt_events.js +2 -10
  18. package/build/hooks/use_conversation_jolt_events.js.map +1 -1
  19. package/build/navigation/index.d.ts +76 -149
  20. package/build/navigation/index.d.ts.map +1 -1
  21. package/build/navigation/index.js +67 -79
  22. package/build/navigation/index.js.map +1 -1
  23. package/build/screens/bug_report_screen.d.ts.map +1 -1
  24. package/build/screens/bug_report_screen.js +10 -13
  25. package/build/screens/bug_report_screen.js.map +1 -1
  26. package/build/screens/conversation_details_screen.d.ts.map +1 -1
  27. package/build/screens/conversation_details_screen.js +11 -12
  28. package/build/screens/conversation_details_screen.js.map +1 -1
  29. package/build/screens/conversation_filters/components/rows.js +1 -1
  30. package/build/screens/conversation_filters/components/rows.js.map +1 -1
  31. package/build/screens/conversation_filters_screen.d.ts.map +1 -1
  32. package/build/screens/conversation_filters_screen.js +15 -2
  33. package/build/screens/conversation_filters_screen.js.map +1 -1
  34. package/build/screens/conversation_screen.d.ts.map +1 -1
  35. package/build/screens/conversation_screen.js +7 -3
  36. package/build/screens/conversation_screen.js.map +1 -1
  37. package/build/screens/conversation_select_recipients/components/groups_recipient_row.js +1 -1
  38. package/build/screens/conversation_select_recipients/components/groups_recipient_row.js.map +1 -1
  39. package/build/screens/conversation_select_recipients/components/restricted_group_row.js +1 -1
  40. package/build/screens/conversation_select_recipients/components/restricted_group_row.js.map +1 -1
  41. package/build/screens/message_report_screen.d.ts.map +1 -1
  42. package/build/screens/message_report_screen.js +14 -18
  43. package/build/screens/message_report_screen.js.map +1 -1
  44. package/build/screens/notification_settings_screen.d.ts.map +1 -1
  45. package/build/screens/notification_settings_screen.js +0 -14
  46. package/build/screens/notification_settings_screen.js.map +1 -1
  47. package/build/types/jolt_events/conversation_events.d.ts +2 -2
  48. package/build/types/jolt_events/conversation_events.d.ts.map +1 -1
  49. package/build/types/jolt_events/conversation_events.js.map +1 -1
  50. package/build/types/resources/conversation.d.ts +2 -2
  51. package/build/types/resources/conversation.d.ts.map +1 -1
  52. package/build/types/resources/conversation.js.map +1 -1
  53. package/build/utils/conversation_safety_constants.d.ts +1 -1
  54. package/build/utils/conversation_safety_constants.d.ts.map +1 -1
  55. package/build/utils/conversation_safety_constants.js +1 -1
  56. package/build/utils/conversation_safety_constants.js.map +1 -1
  57. package/build/utils/request/conversation.d.ts.map +1 -1
  58. package/build/utils/request/conversation.js +0 -1
  59. package/build/utils/request/conversation.js.map +1 -1
  60. package/package.json +4 -3
  61. package/src/components/conversation/messages_disabled_banners.tsx +11 -3
  62. package/src/components/display/adult_requirement_notice.tsx +1 -1
  63. package/src/components/display/platform_modal_header_buttons.tsx +209 -13
  64. package/src/components/primitive/form_sheet.tsx +2 -2
  65. package/src/hooks/use_conversation.ts +0 -1
  66. package/src/hooks/use_conversation_jolt_events.ts +3 -16
  67. package/src/navigation/index.tsx +138 -156
  68. package/src/screens/bug_report_screen.tsx +16 -15
  69. package/src/screens/conversation_details_screen.tsx +11 -17
  70. package/src/screens/conversation_filters/components/rows.tsx +2 -2
  71. package/src/screens/conversation_filters_screen.tsx +22 -2
  72. package/src/screens/conversation_screen.tsx +8 -3
  73. package/src/screens/conversation_select_recipients/components/groups_recipient_row.tsx +1 -1
  74. package/src/screens/conversation_select_recipients/components/restricted_group_row.tsx +2 -2
  75. package/src/screens/message_report_screen.tsx +20 -21
  76. package/src/screens/notification_settings_screen.tsx +1 -21
  77. package/src/types/jolt_events/conversation_events.ts +2 -2
  78. package/src/types/resources/conversation.ts +3 -2
  79. package/src/utils/conversation_safety_constants.ts +1 -1
  80. package/src/utils/request/conversation.ts +0 -1
@@ -1,8 +1,15 @@
1
- import { HeaderButton } from '@react-navigation/elements'
2
- import type { NativeStackHeaderRightProps } from '@react-navigation/native-stack'
1
+ import { HeaderBackButton, HeaderButton, PlatformPressable } from '@react-navigation/elements'
2
+ import { useNavigation } from '@react-navigation/native'
3
+ import type {
4
+ NativeStackHeaderItemProps,
5
+ NativeStackNavigationOptions,
6
+ } from '@react-navigation/native-stack'
7
+ import { useLayoutEffect } from 'react'
3
8
  import { ColorValue, Platform, StyleSheet } from 'react-native'
4
- import { useTheme } from '../../hooks'
5
- import { Icon } from './icon'
9
+ import type { SFSymbol } from 'sf-symbols-typescript'
10
+ import { useFontScale, useTheme } from '../../hooks'
11
+ import { MAX_FONT_SIZE_MULTIPLIER_LANDMARK } from '../../utils'
12
+ import { Icon, type IconString } from './icon'
6
13
  import { TextButton } from './text_button'
7
14
 
8
15
  interface HeaderTextButtonProps {
@@ -63,19 +70,208 @@ export const HeaderDismissButton = ({
63
70
  })
64
71
  }
65
72
 
66
- interface HeaderDoneButtonProps extends NativeStackHeaderRightProps {
67
- navigation: { goBack: () => void; getState: () => any }
73
+ const createTextHeaderRight =
74
+ ({ onPress, title, disabled }: HeaderTextButtonProps) =>
75
+ (props: NativeStackHeaderItemProps) => (
76
+ <HeaderTextButton {...props} onPress={onPress} title={title} disabled={disabled} />
77
+ )
78
+
79
+ export type OptionsDecorator = (
80
+ options: NativeStackNavigationOptions
81
+ ) => NativeStackNavigationOptions
82
+
83
+ export const composeOptions = (
84
+ base: NativeStackNavigationOptions,
85
+ ...decorators: OptionsDecorator[]
86
+ ): NativeStackNavigationOptions => decorators.reduce((options, decorate) => decorate(options), base)
87
+
88
+ interface TextButtonDecoratorProps {
89
+ text: string
90
+ onPress: () => void
91
+ disabled?: boolean
92
+ }
93
+
94
+ export const withRightText =
95
+ ({ text, onPress, disabled }: TextButtonDecoratorProps): OptionsDecorator =>
96
+ options => ({
97
+ ...options,
98
+ headerRight: createTextHeaderRight({ onPress, title: text, disabled }),
99
+ unstable_headerRightItems: () => [{ type: 'button' as const, label: text, onPress, disabled }],
100
+ })
101
+
102
+ const HEADER_ICON_SIZE = 17
103
+ const HEADER_GLASS_BUTTON_SIZE = 44
104
+ const HEADER_BAR_INSET = 16
105
+ const HEADER_SLOT_GAP = 12
106
+
107
+ export const useHeaderSlotReserve = (): number => {
108
+ const fontScale = useFontScale({ maxFontSizeMultiplier: MAX_FONT_SIZE_MULTIPLIER_LANDMARK })
109
+
110
+ return HEADER_BAR_INSET + HEADER_GLASS_BUTTON_SIZE * fontScale + HEADER_SLOT_GAP
111
+ }
112
+
113
+ const HEADER_FALLBACK_HIT_SLOP = 14
114
+
115
+ const iconStyles = StyleSheet.create({
116
+ disabled: {
117
+ opacity: 0.4,
118
+ },
119
+ fallbackButton: {
120
+ alignItems: 'center',
121
+ justifyContent: 'center',
122
+ paddingHorizontal: 8,
123
+ },
124
+ fallbackButtonLeft: {
125
+ marginRight: Platform.select({ android: 16, default: 0 }),
126
+ },
127
+ })
128
+
129
+ const SF_SYMBOL_NAMES: Partial<Record<IconString, SFSymbol>> = {
130
+ 'general.x': 'xmark',
131
+ 'general.check': 'checkmark',
132
+ 'general.upArrow': 'arrow.up',
133
+ 'general.leftChevron': 'chevron.left',
134
+ }
135
+
136
+ const sfSymbolFor = (icon: IconString): SFSymbol => {
137
+ const name = SF_SYMBOL_NAMES[icon]
138
+
139
+ if (!name) throw new Error(`No SF Symbol mapped for icon "${icon}" — add one to SF_SYMBOL_NAMES.`)
140
+
141
+ return name
68
142
  }
69
143
 
70
- export const HeaderDoneButton = ({ navigation, ...props }: HeaderDoneButtonProps) => {
71
- const state = navigation.getState()
72
- const isFirstScreen = state.index === 0
144
+ export interface HeaderIconProps {
145
+ icon: IconString
146
+ accessibilityLabel: string
147
+ onPress: () => void
148
+ disabled?: boolean
149
+ prominent?: boolean
150
+ tintColor?: ColorValue
151
+ }
152
+
153
+ const createHeaderIconItem = ({
154
+ icon,
155
+ accessibilityLabel,
156
+ onPress,
157
+ disabled,
158
+ prominent,
159
+ tintColor,
160
+ }: HeaderIconProps) => ({
161
+ type: 'button' as const,
162
+ label: '',
163
+ accessibilityLabel,
164
+ onPress,
165
+ disabled,
166
+ icon: { type: 'sfSymbol' as const, name: sfSymbolFor(icon) },
167
+ ...(prominent ? { variant: 'prominent' as const, tintColor } : {}),
168
+ })
169
+
170
+ const createHeaderIconElement =
171
+ (position: 'left' | 'right') =>
172
+ ({ icon, accessibilityLabel, onPress, disabled, tintColor }: HeaderIconProps) =>
173
+ () => (
174
+ <PlatformPressable
175
+ onPress={onPress}
176
+ disabled={disabled}
177
+ hitSlop={HEADER_FALLBACK_HIT_SLOP}
178
+ accessibilityRole="button"
179
+ accessibilityLabel={accessibilityLabel}
180
+ style={[
181
+ iconStyles.fallbackButton,
182
+ position === 'left' && iconStyles.fallbackButtonLeft,
183
+ disabled && iconStyles.disabled,
184
+ ]}
185
+ >
186
+ <Icon name={icon} size={HEADER_ICON_SIZE} color={tintColor} />
187
+ </PlatformPressable>
188
+ )
189
+
190
+ export const withRightIcon =
191
+ (props: HeaderIconProps): OptionsDecorator =>
192
+ options => ({
193
+ ...options,
194
+ headerRight: createHeaderIconElement('right')(props),
195
+ unstable_headerRightItems: () => [createHeaderIconItem(props)],
196
+ })
197
+
198
+ export const withLeftIcon =
199
+ (props: HeaderIconProps): OptionsDecorator =>
200
+ options => ({
201
+ ...options,
202
+ headerLeft: createHeaderIconElement('left')(props),
203
+ unstable_headerLeftItems: () => [createHeaderIconItem(props)],
204
+ })
205
+
206
+ export const withRightClose = ({ onPress }: { onPress: () => void }) =>
207
+ withRightIcon({ icon: 'general.x', accessibilityLabel: 'Close', onPress })
208
+
209
+ export const withLeftClose = ({ onPress }: { onPress: () => void }) =>
210
+ withLeftIcon({ icon: 'general.x', accessibilityLabel: 'Close', onPress })
211
+
212
+ export const withRightDone = ({ onPress }: { onPress: () => void }) =>
213
+ withRightIcon({ icon: 'general.check', accessibilityLabel: 'Done', onPress })
214
+
215
+ const createMinimalHeaderBackElement =
216
+ ({ onPress }: { onPress: () => void }) =>
217
+ (props: NativeStackHeaderItemProps) => (
218
+ <HeaderBackButton
219
+ style={Platform.select({
220
+ ios: { marginLeft: -8 },
221
+ default: { marginLeft: -3, marginRight: 30 },
222
+ })}
223
+ displayMode="minimal"
224
+ onPress={onPress}
225
+ {...props}
226
+ />
227
+ )
228
+
229
+ export const withMinimalBack =
230
+ ({ onPress }: { onPress: () => void }): OptionsDecorator =>
231
+ options => ({
232
+ ...options,
233
+ headerLeft: createMinimalHeaderBackElement({ onPress }),
234
+ unstable_headerLeftItems: () => [
235
+ createHeaderIconItem({ icon: 'general.leftChevron', accessibilityLabel: 'Back', onPress }),
236
+ ],
237
+ })
238
+
239
+ type UseHeaderRightOptions = {
240
+ onPress: () => void
241
+ disabled?: boolean
242
+ enabled?: boolean
243
+ } & (
244
+ | (Omit<HeaderIconProps, 'tintColor'> & { title?: never })
245
+ | { title: string; icon?: never; accessibilityLabel?: never; prominent?: never }
246
+ )
247
+
248
+ export const useHeaderRight = (options: UseHeaderRightOptions) => {
249
+ const navigation = useNavigation()
250
+ const { colors } = useTheme()
251
+ const { onPress, disabled, enabled = true } = options
252
+ const icon = options.icon
253
+ const label = options.icon !== undefined ? options.accessibilityLabel : options.title
254
+ const prominent = options.icon !== undefined ? options.prominent : undefined
255
+
256
+ useLayoutEffect(() => {
257
+ if (!enabled) {
258
+ navigation.setOptions({ headerRight: () => null, unstable_headerRightItems: undefined })
259
+ return
260
+ }
73
261
 
74
- if (!isFirstScreen) {
75
- return null
76
- }
262
+ const decorate = icon
263
+ ? withRightIcon({
264
+ icon,
265
+ accessibilityLabel: label,
266
+ onPress,
267
+ disabled,
268
+ prominent,
269
+ tintColor: prominent ? colors.interaction : undefined,
270
+ })
271
+ : withRightText({ text: label, onPress, disabled })
77
272
 
78
- return <HeaderTextButton {...props} onPress={navigation.goBack} title="Done" />
273
+ navigation.setOptions(decorate({}))
274
+ }, [colors.interaction, disabled, enabled, icon, label, navigation, onPress, prominent])
79
275
  }
80
276
 
81
277
  const useStyles = () => {
@@ -12,7 +12,7 @@ import {
12
12
  } from 'react-native'
13
13
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
14
14
  import { useTheme } from '../../hooks'
15
- import { MAX_FONT_SIZE_MULTIPLIER_LANDMARK } from '../../utils'
15
+ import { isLiquidGlassEnabled, MAX_FONT_SIZE_MULTIPLIER_LANDMARK } from '../../utils'
16
16
  import type { ButtonProps, TextButtonProps } from '../display'
17
17
  import { Button, Heading, Icon, IconString, Text, TextButton } from '../display'
18
18
 
@@ -35,7 +35,7 @@ export const getFormSheetScreenOptions = ({
35
35
  presentation: 'formSheet',
36
36
  headerShown: false,
37
37
  sheetAllowedDetents,
38
- sheetCornerRadius: 16,
38
+ sheetCornerRadius: isLiquidGlassEnabled() ? undefined : 16,
39
39
  sheetGrabberVisible: true,
40
40
  headerTitle,
41
41
  ...rest,
@@ -32,7 +32,6 @@ export const getConversationRequestArgs = ({ conversation_id }: { conversation_i
32
32
  'direct_message',
33
33
  'disabled',
34
34
  'disabled_reason',
35
- 'disabled_message',
36
35
  'member_ability',
37
36
  'muted',
38
37
  'replies_disabled',
@@ -26,13 +26,8 @@ export function useConversationJoltEvents({ conversationId }: Props) {
26
26
 
27
27
  const handleUpdatedConversation = useCallback(
28
28
  (e: JoltConversationEvent) => {
29
- const {
30
- last_message_idempotent_key,
31
- last_message_author_id,
32
- disabled,
33
- disabled_reason,
34
- disabled_message,
35
- } = e.data.data
29
+ const { last_message_idempotent_key, last_message_author_id, disabled, disabled_reason } =
30
+ e.data.data
36
31
 
37
32
  if (last_message_idempotent_key && last_message_author_id === currentPersonId) {
38
33
  completeMessageCreationConversationTracking({
@@ -43,15 +38,7 @@ export function useConversationJoltEvents({ conversationId }: Props) {
43
38
 
44
39
  queryClient.setQueryData<ApiResource<ConversationResource>>(queryKey, prev => {
45
40
  if (!prev?.data) return prev
46
- return {
47
- ...prev,
48
- data: {
49
- ...prev.data,
50
- disabled,
51
- disabledReason: disabled_reason,
52
- disabledMessage: disabled_message,
53
- },
54
- }
41
+ return { ...prev, data: { ...prev.data, disabled, disabledReason: disabled_reason } }
55
42
  })
56
43
 
57
44
  queryClient.invalidateQueries({ queryKey })