@planningcenter/chat-react-native 3.43.1-qa-858.1 → 3.44.0-rc.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 (67) hide show
  1. package/build/components/display/platform_modal_header_buttons.d.ts +14 -42
  2. package/build/components/display/platform_modal_header_buttons.d.ts.map +1 -1
  3. package/build/components/display/platform_modal_header_buttons.js +27 -107
  4. package/build/components/display/platform_modal_header_buttons.js.map +1 -1
  5. package/build/components/primitive/form_sheet.d.ts +2 -8
  6. package/build/components/primitive/form_sheet.d.ts.map +1 -1
  7. package/build/components/primitive/form_sheet.js +4 -10
  8. package/build/components/primitive/form_sheet.js.map +1 -1
  9. package/build/navigation/index.d.ts +149 -76
  10. package/build/navigation/index.d.ts.map +1 -1
  11. package/build/navigation/index.js +76 -67
  12. package/build/navigation/index.js.map +1 -1
  13. package/build/screens/attachment_actions/attachment_actions_screen.d.ts +1 -1
  14. package/build/screens/attachment_actions/attachment_actions_screen.d.ts.map +1 -1
  15. package/build/screens/avatar_picker/avatar_picker_screen.d.ts +2 -2
  16. package/build/screens/avatar_picker/avatar_picker_screen.d.ts.map +1 -1
  17. package/build/screens/bug_report_screen.d.ts.map +1 -1
  18. package/build/screens/bug_report_screen.js +13 -10
  19. package/build/screens/bug_report_screen.js.map +1 -1
  20. package/build/screens/conversation/message_read_receipts_screen.d.ts +1 -1
  21. package/build/screens/conversation/message_read_receipts_screen.d.ts.map +1 -1
  22. package/build/screens/conversation_details_screen.d.ts.map +1 -1
  23. package/build/screens/conversation_details_screen.js +12 -11
  24. package/build/screens/conversation_details_screen.js.map +1 -1
  25. package/build/screens/conversation_filter_recipients/conversation_filter_recipients_screen.d.ts +1 -1
  26. package/build/screens/conversation_filter_recipients/conversation_filter_recipients_screen.d.ts.map +1 -1
  27. package/build/screens/conversation_filters_screen.d.ts +1 -1
  28. package/build/screens/conversation_filters_screen.d.ts.map +1 -1
  29. package/build/screens/conversation_filters_screen.js +2 -18
  30. package/build/screens/conversation_filters_screen.js.map +1 -1
  31. package/build/screens/conversation_new/components/groups_form.d.ts.map +1 -1
  32. package/build/screens/conversation_new/components/groups_form.js +3 -2
  33. package/build/screens/conversation_new/components/groups_form.js.map +1 -1
  34. package/build/screens/conversation_notification_level_select_screen.d.ts +1 -1
  35. package/build/screens/conversation_notification_level_select_screen.d.ts.map +1 -1
  36. package/build/screens/conversation_screen.d.ts.map +1 -1
  37. package/build/screens/conversation_screen.js +2 -6
  38. package/build/screens/conversation_screen.js.map +1 -1
  39. package/build/screens/conversation_select_type_screen.d.ts +1 -1
  40. package/build/screens/conversation_select_type_screen.d.ts.map +1 -1
  41. package/build/screens/group_notification_level_select_screen.d.ts +1 -1
  42. package/build/screens/group_notification_level_select_screen.d.ts.map +1 -1
  43. package/build/screens/message_actions_screen.d.ts +1 -1
  44. package/build/screens/message_actions_screen.d.ts.map +1 -1
  45. package/build/screens/message_report_screen.d.ts.map +1 -1
  46. package/build/screens/message_report_screen.js +18 -14
  47. package/build/screens/message_report_screen.js.map +1 -1
  48. package/build/screens/notification_settings_screen.d.ts.map +1 -1
  49. package/build/screens/notification_settings_screen.js +14 -0
  50. package/build/screens/notification_settings_screen.js.map +1 -1
  51. package/build/screens/reactions_screen.d.ts +1 -1
  52. package/build/screens/reactions_screen.d.ts.map +1 -1
  53. package/build/screens/send_giphy_screen.d.ts +1 -1
  54. package/build/screens/send_giphy_screen.d.ts.map +1 -1
  55. package/build/screens/system_message_people_screen.d.ts +1 -1
  56. package/build/screens/system_message_people_screen.d.ts.map +1 -1
  57. package/package.json +3 -4
  58. package/src/components/display/platform_modal_header_buttons.tsx +45 -196
  59. package/src/components/primitive/form_sheet.tsx +15 -19
  60. package/src/navigation/index.tsx +151 -147
  61. package/src/screens/bug_report_screen.tsx +15 -16
  62. package/src/screens/conversation_details_screen.tsx +17 -11
  63. package/src/screens/conversation_filters_screen.tsx +2 -25
  64. package/src/screens/conversation_new/components/groups_form.tsx +3 -2
  65. package/src/screens/conversation_screen.tsx +2 -7
  66. package/src/screens/message_report_screen.tsx +21 -20
  67. package/src/screens/notification_settings_screen.tsx +21 -1
@@ -1,15 +1,12 @@
1
- import { HeaderBackButton, HeaderButton, PlatformPressable } from '@react-navigation/elements'
2
- import { useNavigation } from '@react-navigation/native'
1
+ import { HeaderBackButton, HeaderButton } from '@react-navigation/elements'
3
2
  import type {
4
- NativeStackHeaderItemProps,
5
- NativeStackNavigationOptions,
3
+ NativeStackHeaderLeftProps,
4
+ NativeStackHeaderRightProps,
6
5
  } from '@react-navigation/native-stack'
7
- import { useLayoutEffect } from 'react'
8
6
  import { ColorValue, Platform, StyleSheet } from 'react-native'
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'
7
+ import { useTheme } from '../../hooks'
8
+ import { isLiquidGlassEnabled } from '../../utils/liquid_glass'
9
+ import { Icon } from './icon'
13
10
  import { TextButton } from './text_button'
14
11
 
15
12
  interface HeaderTextButtonProps {
@@ -70,209 +67,61 @@ export const HeaderDismissButton = ({
70
67
  })
71
68
  }
72
69
 
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
70
+ interface HeaderDoneButtonProps extends NativeStackHeaderRightProps {
71
+ navigation: { goBack: () => void; getState: () => any }
92
72
  }
93
73
 
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
- })
74
+ export const HeaderDoneButton = ({ navigation, ...props }: HeaderDoneButtonProps) => {
75
+ const state = navigation.getState()
76
+ const isFirstScreen = state.index === 0
101
77
 
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
- })
78
+ if (!isFirstScreen) {
79
+ return null
80
+ }
128
81
 
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',
82
+ return <HeaderTextButton {...props} onPress={navigation.goBack} title="Done" />
134
83
  }
135
84
 
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
142
- }
143
-
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 })
85
+ // The default back icon reserves trailing space for a label that
86
+ // displayMode="minimal" never shows; a custom image skips it so the glass
87
+ // capsule hugs the glyph.
88
+ const renderGlassBackImage = ({ tintColor }: { tintColor: string }) => (
89
+ <Icon name="general.leftChevron" size={18} color={tintColor} />
90
+ )
214
91
 
215
- const createMinimalHeaderBackElement =
92
+ // The iOS 26 glass capsule manages its own insets — negative margins clip
93
+ // the button against the capsule edge.
94
+ export const createMinimalHeaderBackButton =
216
95
  ({ onPress }: { onPress: () => void }) =>
217
- (props: NativeStackHeaderItemProps) => (
96
+ (props: NativeStackHeaderLeftProps) => (
218
97
  <HeaderBackButton
219
- style={Platform.select({
220
- ios: { marginLeft: -8 },
221
- default: { marginLeft: -3, marginRight: 30 },
222
- })}
98
+ style={
99
+ isLiquidGlassEnabled()
100
+ ? undefined
101
+ : {
102
+ marginLeft: Platform.select({ ios: -8, default: -3 }),
103
+ marginRight: Platform.select({ ios: 0, default: 30 }),
104
+ }
105
+ }
106
+ backImage={isLiquidGlassEnabled() ? renderGlassBackImage : undefined}
223
107
  displayMode="minimal"
224
108
  onPress={onPress}
225
109
  {...props}
226
110
  />
227
111
  )
228
112
 
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 = {
113
+ // Under glass the native minimal back button suffices; pre-26 needs the
114
+ // custom button and its layout nudges.
115
+ export const composeHeaderOptions = ({
116
+ title,
117
+ onPress,
118
+ }: {
119
+ title: string
240
120
  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
- }
261
-
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 })
272
-
273
- navigation.setOptions(decorate({}))
274
- }, [colors.interaction, disabled, enabled, icon, label, navigation, onPress, prominent])
275
- }
121
+ }) => ({
122
+ title,
123
+ ...(isLiquidGlassEnabled() ? {} : { headerLeft: createMinimalHeaderBackButton({ onPress }) }),
124
+ })
276
125
 
277
126
  const useStyles = () => {
278
127
  const { colors } = useTheme()
@@ -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 { isLiquidGlassEnabled, MAX_FONT_SIZE_MULTIPLIER_LANDMARK } from '../../utils'
15
+ import { 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
 
@@ -22,28 +22,24 @@ import { Button, Heading, Icon, IconString, Text, TextButton } from '../display'
22
22
 
23
23
  /**
24
24
  * Screen options for the formsheet's NativeStackNavigation route
25
- *
26
- * Returns a factory rather than an object because callers assign it at module
27
- * scope, which runs before ChatProvider reports the host's liquid glass
28
- * capability. React Navigation calls the factory per render, so the sheet
29
- * corner radius is resolved once that capability is known.
30
- *
31
25
  * @param {string} options.headerTitle - Doesn't show in UI but good to have for semantic reasons.
32
26
  * @param {number[]} [options.sheetAllowedDetents] - Controls the height increments the sheet grows to. To prevent bugs in Android, do not specify more then two values and use [0.94] instead of [1] to go full screen.
33
27
  * @param {NativeStackNavigationOptions} [options.rest] - Adds or overrides any valid NativeStackNavigationOptions
34
- * @returns {() => NativeStackNavigationOptions} Factory producing merged NativeStackNavigationOptions
28
+ * @returns {NativeStackNavigationOptions} Merged NativeStackNavigationOptions
35
29
  */
36
- export const getFormSheetScreenOptions =
37
- ({ headerTitle, sheetAllowedDetents = [0.25], ...rest }: NativeStackNavigationOptions = {}) =>
38
- (): NativeStackNavigationOptions => ({
39
- presentation: 'formSheet',
40
- headerShown: false,
41
- sheetAllowedDetents,
42
- sheetCornerRadius: isLiquidGlassEnabled() ? undefined : 16,
43
- sheetGrabberVisible: true,
44
- headerTitle,
45
- ...rest,
46
- })
30
+ export const getFormSheetScreenOptions = ({
31
+ headerTitle,
32
+ sheetAllowedDetents = [0.25],
33
+ ...rest
34
+ }: NativeStackNavigationOptions = {}): NativeStackNavigationOptions => ({
35
+ presentation: 'formSheet',
36
+ headerShown: false,
37
+ sheetAllowedDetents,
38
+ sheetCornerRadius: 16,
39
+ sheetGrabberVisible: true,
40
+ headerTitle,
41
+ ...rest,
42
+ })
47
43
 
48
44
  const FormSheet = {
49
45
  Root: FormSheetRoot,