@planningcenter/chat-react-native 3.43.1-qa-858.0 → 3.43.1-qa-staging.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 (46) hide show
  1. package/build/components/conversations/conversations.d.ts.map +1 -1
  2. package/build/components/conversations/conversations.js +16 -25
  3. package/build/components/conversations/conversations.js.map +1 -1
  4. package/build/components/display/platform_modal_header_buttons.d.ts +14 -42
  5. package/build/components/display/platform_modal_header_buttons.d.ts.map +1 -1
  6. package/build/components/display/platform_modal_header_buttons.js +27 -107
  7. package/build/components/display/platform_modal_header_buttons.js.map +1 -1
  8. package/build/components/primitive/form_sheet.js +2 -2
  9. package/build/components/primitive/form_sheet.js.map +1 -1
  10. package/build/navigation/index.d.ts +149 -76
  11. package/build/navigation/index.d.ts.map +1 -1
  12. package/build/navigation/index.js +76 -67
  13. package/build/navigation/index.js.map +1 -1
  14. package/build/screens/bug_report_screen.d.ts.map +1 -1
  15. package/build/screens/bug_report_screen.js +13 -10
  16. package/build/screens/bug_report_screen.js.map +1 -1
  17. package/build/screens/conversation_details_screen.d.ts.map +1 -1
  18. package/build/screens/conversation_details_screen.js +12 -11
  19. package/build/screens/conversation_details_screen.js.map +1 -1
  20. package/build/screens/conversation_filters_screen.d.ts.map +1 -1
  21. package/build/screens/conversation_filters_screen.js +2 -15
  22. package/build/screens/conversation_filters_screen.js.map +1 -1
  23. package/build/screens/conversation_new/components/groups_form.d.ts.map +1 -1
  24. package/build/screens/conversation_new/components/groups_form.js +3 -2
  25. package/build/screens/conversation_new/components/groups_form.js.map +1 -1
  26. package/build/screens/conversation_screen.d.ts.map +1 -1
  27. package/build/screens/conversation_screen.js +2 -6
  28. package/build/screens/conversation_screen.js.map +1 -1
  29. package/build/screens/message_report_screen.d.ts.map +1 -1
  30. package/build/screens/message_report_screen.js +20 -18
  31. package/build/screens/message_report_screen.js.map +1 -1
  32. package/build/screens/notification_settings_screen.d.ts.map +1 -1
  33. package/build/screens/notification_settings_screen.js +33 -27
  34. package/build/screens/notification_settings_screen.js.map +1 -1
  35. package/package.json +3 -4
  36. package/src/components/conversations/conversations.tsx +1 -17
  37. package/src/components/display/platform_modal_header_buttons.tsx +45 -196
  38. package/src/components/primitive/form_sheet.tsx +2 -2
  39. package/src/navigation/index.tsx +151 -147
  40. package/src/screens/bug_report_screen.tsx +15 -16
  41. package/src/screens/conversation_details_screen.tsx +17 -11
  42. package/src/screens/conversation_filters_screen.tsx +2 -22
  43. package/src/screens/conversation_new/components/groups_form.tsx +3 -2
  44. package/src/screens/conversation_screen.tsx +2 -7
  45. package/src/screens/message_report_screen.tsx +23 -24
  46. package/src/screens/notification_settings_screen.tsx +21 -16
@@ -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
 
@@ -35,7 +35,7 @@ export const getFormSheetScreenOptions = ({
35
35
  presentation: 'formSheet',
36
36
  headerShown: false,
37
37
  sheetAllowedDetents,
38
- sheetCornerRadius: isLiquidGlassEnabled() ? undefined : 16,
38
+ sheetCornerRadius: 16,
39
39
  sheetGrabberVisible: true,
40
40
  headerTitle,
41
41
  ...rest,