@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,16 +1,16 @@
1
- import { HeaderBackButton } from '@react-navigation/elements'
2
1
  import { StaticParamList } from '@react-navigation/native'
3
2
  import {
4
3
  createNativeStackNavigator,
5
4
  NativeStackHeaderRightProps,
5
+ NativeStackNavigationOptions,
6
6
  } from '@react-navigation/native-stack'
7
7
  import { CardStyleInterpolators } from '@react-navigation/stack'
8
8
  import React from 'react'
9
- import { Platform } from 'react-native'
10
- import { Icon } from '../components'
11
9
  import {
12
- HeaderDoneButton,
13
- HeaderTextButton,
10
+ composeOptions,
11
+ withMinimalBack,
12
+ withRightClose,
13
+ withRightDone,
14
14
  } from '../components/display/platform_modal_header_buttons'
15
15
  import { useDirectMessagesEnabled, useQualifiedByAge } from '../hooks'
16
16
  import {
@@ -79,69 +79,66 @@ import {
79
79
  SystemMessagePeopleScreenOptions,
80
80
  } from '../screens/system_message_people_screen'
81
81
  import { TeamConversationScreen } from '../screens/team_conversation_screen'
82
+ import { isLiquidGlassEnabled } from '../utils/liquid_glass'
82
83
  import { ScreenLayoutWithChatAccessGate } from './screenLayout'
83
84
 
84
- const HEADER_BACK_BUTTON_LAYOUT_RESET_STYLES = {
85
- marginLeft: Platform.select({ ios: -8, default: -3 }),
86
- marginRight: Platform.select({ ios: 0, default: 30 }),
87
- }
85
+ const globalScreenOptions: () => NativeStackNavigationOptions = () => ({
86
+ headerBackButtonDisplayMode: 'minimal',
87
+ ...(isLiquidGlassEnabled() ? { headerShadowVisible: false } : {}),
88
+ })
88
89
 
89
90
  export const NewConversationStack = createNativeStackNavigator({
90
91
  initialRouteName: 'ConversationNewEntry',
91
- screenOptions: {
92
- headerBackButtonDisplayMode: 'minimal',
93
- },
92
+ screenOptions: globalScreenOptions,
94
93
  screenLayout: ScreenLayoutWithChatAccessGate,
95
94
  screens: {
96
95
  ConversationNewEntry: {
97
96
  screen: ConversationNewEntryScreen,
98
- options: ({ navigation }) => ({
99
- title: 'New conversation',
100
- headerRight: (props: NativeStackHeaderRightProps) => (
101
- <HeaderTextButton {...props} onPress={navigation.goBack} title="Cancel" />
97
+ options: ({ navigation }) =>
98
+ composeOptions(
99
+ {
100
+ title: 'New conversation',
101
+ headerBackVisible: false,
102
+ },
103
+ withRightClose({ onPress: navigation.goBack })
102
104
  ),
103
- headerBackVisible: false,
104
- }),
105
105
  },
106
106
  ConversationSelectGroupRecipients: {
107
107
  screen: ConversationSelectGroupRecipientsScreen,
108
- options: ({ navigation, route }) => ({
109
- title: 'New conversation',
110
- headerRight: (props: NativeStackHeaderRightProps) => (
111
- <HeaderTextButton
112
- {...props}
113
- onPress={() => navigation.popTo('Conversations', route.params)}
114
- title="Cancel"
115
- />
108
+ options: ({ navigation, route }) =>
109
+ composeOptions(
110
+ {
111
+ title: 'New conversation',
112
+ },
113
+ withRightClose({
114
+ onPress: () => navigation.popTo('Conversations', route.params),
115
+ })
116
116
  ),
117
- }),
118
117
  },
119
118
  ConversationSelectTeamsILeadRecipients: {
120
119
  screen: ConversationSelectTeamsILeadRecipientsScreen,
121
- options: ({ navigation, route }) => ({
122
- title: 'New conversation',
123
- headerRight: (props: NativeStackHeaderRightProps) => (
124
- <HeaderTextButton
125
- {...props}
126
- onPress={() => navigation.popTo('Conversations', route.params)}
127
- title="Cancel"
128
- />
120
+ options: ({ navigation, route }) =>
121
+ composeOptions(
122
+ {
123
+ title: 'New conversation',
124
+ },
125
+ withRightClose({
126
+ onPress: () => navigation.popTo('Conversations', route.params),
127
+ })
129
128
  ),
130
- }),
131
129
  },
132
130
  ConversationSelectDirectMessageRecipients: {
133
131
  screen: ConversationSelectDirectMessageRecipientsScreen,
134
132
  if: useDirectMessagesEnabled,
135
- options: ({ navigation, route }) => ({
136
- title: 'New conversation',
137
- headerRight: (props: NativeStackHeaderRightProps) => (
138
- <HeaderTextButton
139
- {...props}
140
- onPress={() => navigation.popTo('Conversations', route.params)}
141
- title="Cancel"
142
- />
133
+ options: ({ navigation, route }) =>
134
+ composeOptions(
135
+ {
136
+ title: 'New conversation',
137
+ },
138
+ withRightClose({
139
+ onPress: () => navigation.popTo('Conversations', route.params),
140
+ })
143
141
  ),
144
- }),
145
142
  },
146
143
  ConversationFilterRecipients: {
147
144
  screen: ConversationFilterRecipientsScreen,
@@ -149,21 +146,20 @@ export const NewConversationStack = createNativeStackNavigator({
149
146
  },
150
147
  ConversationNew: {
151
148
  screen: ConversationNewScreen,
152
- options: ({ navigation, route }) => ({
153
- title: 'New conversation',
154
- headerLeft: () => null,
155
- headerRight: (props: NativeStackHeaderRightProps) => (
156
- <HeaderTextButton
157
- {...props}
158
- onPress={() => {
149
+ options: ({ navigation, route }) =>
150
+ composeOptions(
151
+ {
152
+ title: 'New conversation',
153
+ headerLeft: () => null,
154
+ },
155
+ withRightClose({
156
+ onPress: () => {
159
157
  const { chat_group_graph_id, group_source_app_name } =
160
158
  route.params as ConversationSelectRecipientsParams
161
159
  navigation.popTo('Conversations', { chat_group_graph_id, group_source_app_name })
162
- }}
163
- title="Cancel"
164
- />
160
+ },
161
+ })
165
162
  ),
166
- }),
167
163
  },
168
164
  AvatarPicker: {
169
165
  screen: AvatarPickerScreen,
@@ -173,24 +169,19 @@ export const NewConversationStack = createNativeStackNavigator({
173
169
  })
174
170
 
175
171
  export const ChatStack = createNativeStackNavigator({
176
- screenOptions: {
177
- headerBackButtonDisplayMode: 'minimal',
178
- },
172
+ screenOptions: globalScreenOptions,
179
173
  screenLayout: ScreenLayoutWithChatAccessGate,
180
174
  screens: {
181
175
  Conversations: {
182
176
  screen: ConversationsScreen,
183
- options: ({ route, navigation }) => ({
184
- headerTitle: (route.params as { title?: string })?.title ?? 'Chat',
185
- headerBackVisible: false,
186
- headerRight: (props: NativeStackHeaderRightProps) => (
187
- <HeaderBackButton
188
- backImage={() => <Icon name="general.x" size={18} color={props.tintColor} />}
189
- onPress={navigation.goBack}
190
- {...props}
191
- />
177
+ options: ({ route, navigation }) =>
178
+ composeOptions(
179
+ {
180
+ headerTitle: (route.params as { title?: string })?.title ?? 'Chat',
181
+ headerBackVisible: false,
182
+ },
183
+ withRightClose({ onPress: navigation.goBack })
192
184
  ),
193
- }),
194
185
  },
195
186
  ConversationFilters: {
196
187
  screen: ConversationFiltersScreen,
@@ -198,34 +189,28 @@ export const ChatStack = createNativeStackNavigator({
198
189
  },
199
190
  Conversation: {
200
191
  screen: ConversationScreen,
201
- options: ({ route, navigation }) => ({
202
- headerTitle: (props: NativeStackHeaderRightProps) => {
203
- const { conversation_id, title, badge, deleted, muted } =
204
- route.params as ConversationRouteProps
192
+ options: ({ route, navigation }) =>
193
+ composeOptions(
194
+ {
195
+ headerTitle: (props: NativeStackHeaderRightProps) => {
196
+ const { conversation_id, title, badge, deleted, muted } =
197
+ route.params as ConversationRouteProps
205
198
 
206
- return (
207
- <ConversationScreenTitle
208
- conversation_id={conversation_id}
209
- badge={badge}
210
- deleted={deleted}
211
- muted={muted}
212
- {...props}
213
- >
214
- {title ?? 'Conversation'}
215
- </ConversationScreenTitle>
216
- )
217
- },
218
- headerRight: (props: NativeStackHeaderRightProps) => (
219
- <HeaderBackButton
220
- backImage={() => <Icon name="general.x" size={18} color={props.tintColor} />}
221
- onPress={() => navigation.getParent()?.goBack()}
222
- />
223
- ),
224
- headerLeft: props => (
225
- <HeaderBackButton
226
- style={HEADER_BACK_BUTTON_LAYOUT_RESET_STYLES}
227
- displayMode="minimal"
228
- onPress={() => {
199
+ return (
200
+ <ConversationScreenTitle
201
+ conversation_id={conversation_id}
202
+ badge={badge}
203
+ deleted={deleted}
204
+ muted={muted}
205
+ {...props}
206
+ >
207
+ {title ?? 'Conversation'}
208
+ </ConversationScreenTitle>
209
+ )
210
+ },
211
+ },
212
+ withMinimalBack({
213
+ onPress: () => {
229
214
  const params = route.params as ConversationRouteProps
230
215
  if ((params as ConversationFiltersParams)?.chat_group_graph_id) {
231
216
  // Ensure that conversations with a graph id pass them back to the conversation list
@@ -234,25 +219,18 @@ export const ChatStack = createNativeStackNavigator({
234
219
  } else {
235
220
  navigation.goBack()
236
221
  }
237
- }}
238
- {...props}
239
- />
222
+ },
223
+ }),
224
+ withRightClose({ onPress: () => navigation.getParent()?.goBack() })
240
225
  ),
241
- }),
242
226
  },
243
227
  ConversationReply: {
244
228
  screen: ConversationScreen,
245
- options: ({ route, navigation }) => ({
246
- title: (route.params as ConversationRouteProps)?.title ?? 'Reply',
247
- headerLeft: props => (
248
- <HeaderBackButton
249
- style={HEADER_BACK_BUTTON_LAYOUT_RESET_STYLES}
250
- displayMode="minimal"
251
- onPress={() => navigation.goBack()}
252
- {...props}
253
- />
229
+ options: ({ route, navigation }) =>
230
+ composeOptions(
231
+ { title: (route.params as ConversationRouteProps)?.title ?? 'Reply' },
232
+ withMinimalBack({ onPress: navigation.goBack })
254
233
  ),
255
- }),
256
234
  },
257
235
  TeamConversation: {
258
236
  screen: TeamConversationScreen,
@@ -265,13 +243,14 @@ export const ChatStack = createNativeStackNavigator({
265
243
  },
266
244
  ConversationDetails: {
267
245
  screen: ConversationDetailsScreen,
268
- options: ({ navigation }) => ({
269
- presentation: 'modal',
270
- title: 'Conversation details',
271
- headerRight: (props: NativeStackHeaderRightProps) => (
272
- <HeaderTextButton {...props} onPress={navigation.goBack} title="Done" />
246
+ options: ({ navigation }) =>
247
+ composeOptions(
248
+ {
249
+ presentation: 'modal',
250
+ title: 'Conversation details',
251
+ },
252
+ withRightDone({ onPress: navigation.goBack })
273
253
  ),
274
- }),
275
254
  },
276
255
  AvatarPicker: {
277
256
  screen: AvatarPickerScreen,
@@ -279,38 +258,43 @@ export const ChatStack = createNativeStackNavigator({
279
258
  },
280
259
  ChatSettings: {
281
260
  screen: SettingsScreen,
282
- options: ({ navigation }) => ({
283
- title: 'Chat settings',
284
- headerBackVisible: false,
285
- headerRight: (props: NativeStackHeaderRightProps) => (
286
- <HeaderTextButton {...props} onPress={navigation.goBack} title="Done" />
287
- ),
288
- }),
261
+ options: ({ navigation }) =>
262
+ composeOptions({ title: 'Chat settings' }, withMinimalBack({ onPress: navigation.goBack })),
289
263
  },
290
264
  NotificationSettings: {
291
265
  screen: NotificationSettingsScreen,
292
- options: ({ navigation }) => ({
293
- title: 'Chat',
294
- headerBackVisible: false,
295
- headerRight: (props: NativeStackHeaderRightProps) => (
296
- <HeaderTextButton {...props} onPress={navigation.goBack} title="Done" />
297
- ),
298
- }),
266
+ options: ({ navigation }) =>
267
+ navigation.getState().index === 0
268
+ ? composeOptions(
269
+ { title: 'Chat notifications' },
270
+ withRightClose({ onPress: navigation.goBack })
271
+ )
272
+ : composeOptions(
273
+ { title: 'Chat notifications' },
274
+ withMinimalBack({ onPress: navigation.goBack })
275
+ ),
299
276
  },
300
277
  PreferredAppSelection: {
301
278
  screen: PreferredAppSelectionScreen,
302
- options: {
303
- title: 'Preferred app',
304
- },
279
+ options: ({ navigation }) =>
280
+ composeOptions({ title: 'Preferred app' }, withMinimalBack({ onPress: navigation.goBack })),
305
281
  },
306
282
  GroupNotificationSettings: {
307
283
  screen: GroupNotificationSettingsScreen,
308
- options: ({ route, navigation }) => ({
309
- title: (route.params as { title?: string })?.title || 'Group Settings',
310
- headerRight: (props: NativeStackHeaderRightProps) => (
311
- <HeaderDoneButton {...props} navigation={navigation} />
312
- ),
313
- }),
284
+ options: ({ route, navigation }) => {
285
+ const title = (route.params as { title?: string })?.title || 'Group Settings'
286
+
287
+ // The dismiss button only shows when this is the entry screen (e.g.
288
+ // deep links); deeper in the stack the back button covers dismissal.
289
+ return navigation.getState().index === 0
290
+ ? composeOptions(
291
+ {
292
+ title,
293
+ },
294
+ withRightClose({ onPress: navigation.goBack })
295
+ )
296
+ : composeOptions({ title }, withMinimalBack({ onPress: navigation.goBack }))
297
+ },
314
298
  },
315
299
  GroupNotificationLevelSelect: {
316
300
  screen: GroupNotificationLevelSelectScreen,
@@ -331,17 +315,14 @@ export const ChatStack = createNativeStackNavigator({
331
315
  GroupConversationNew: {
332
316
  screen: ConversationNewScreen,
333
317
  if: useQualifiedByAge,
334
- options: ({ navigation }) => ({
335
- title: 'New conversation',
336
- headerLeft: () => null,
337
- headerRight: props => (
338
- <HeaderTextButton
339
- {...props}
340
- onPress={() => navigation.getParent()?.goBack()}
341
- title="Cancel"
342
- />
318
+ options: ({ navigation }) =>
319
+ composeOptions(
320
+ {
321
+ title: 'New conversation',
322
+ headerLeft: () => null,
323
+ },
324
+ withRightClose({ onPress: () => navigation.getParent()?.goBack() })
343
325
  ),
344
- }),
345
326
  },
346
327
  SendGiphy: {
347
328
  screen: SendGiphyScreen,
@@ -382,14 +363,15 @@ export const ChatStack = createNativeStackNavigator({
382
363
  },
383
364
  GetHelp: {
384
365
  screen: GetHelpScreen,
385
- options: ({ navigation }) => ({
386
- headerTitle: 'Get help',
387
- headerBackVisible: false,
388
- presentation: 'modal',
389
- headerRight: props => (
390
- <HeaderTextButton {...props} onPress={navigation.goBack} title="Close" />
366
+ options: ({ navigation }) =>
367
+ composeOptions(
368
+ {
369
+ headerTitle: 'Get help',
370
+ headerBackVisible: false,
371
+ presentation: 'modal',
372
+ },
373
+ withRightClose({ onPress: navigation.goBack })
391
374
  ),
392
- }),
393
375
  },
394
376
  NotFound: {
395
377
  screen: NotFound,
@@ -4,7 +4,7 @@ import type {
4
4
  NativeStackScreenProps,
5
5
  } from '@react-navigation/native-stack'
6
6
  import { startsWith } from 'lodash'
7
- import React, { useCallback, useLayoutEffect, useState } from 'react'
7
+ import React, { useCallback, useState } from 'react'
8
8
  import {
9
9
  View,
10
10
  StyleSheet,
@@ -25,8 +25,10 @@ import {
25
25
  KeyboardView,
26
26
  } from '../components'
27
27
  import {
28
+ composeOptions,
28
29
  HeaderDismissButton,
29
- HeaderTextButton,
30
+ useHeaderRight,
31
+ withLeftClose,
30
32
  } from '../components/display/platform_modal_header_buttons'
31
33
  import { VideoAttachmentPreview } from '../components/display/video_attachment_preview'
32
34
  import { DefaultLoading } from '../components/page/loading'
@@ -55,11 +57,11 @@ enum BUG_TYPE_OPTIONS {
55
57
 
56
58
  export const BugReportScreenOptions = ({
57
59
  navigation,
58
- }: NativeStackScreenProps<any>): NativeStackNavigationOptions => ({
59
- presentation: 'modal',
60
- title: 'Report a bug',
61
- headerLeft: () => <HeaderDismissButton title="Cancel" onPress={() => navigation.goBack()} />,
62
- })
60
+ }: NativeStackScreenProps<any>): NativeStackNavigationOptions =>
61
+ composeOptions(
62
+ { presentation: 'modal', title: 'Report a bug' },
63
+ withLeftClose({ onPress: () => navigation.goBack() })
64
+ )
63
65
 
64
66
  interface Attachment {
65
67
  id: string
@@ -156,14 +158,13 @@ export function BugReportScreen() {
156
158
  return ImagePicker.openImageLibraryAsync().then(result => uploadImagePickerResult(result))
157
159
  }
158
160
 
159
- useLayoutEffect(() => {
160
- navigation.setOptions({
161
- // eslint-disable-next-line react/no-unstable-nested-components
162
- headerRight: () => (
163
- <HeaderTextButton title="Submit" onPress={handleSubmit} disabled={!formValid} />
164
- ),
165
- })
166
- }, [formValid, handleSubmit, navigation])
161
+ useHeaderRight({
162
+ icon: 'general.upArrow',
163
+ accessibilityLabel: 'Submit',
164
+ prominent: true,
165
+ onPress: handleSubmit,
166
+ disabled: !formValid,
167
+ })
167
168
 
168
169
  if (status === 'pending') {
169
170
  return (
@@ -38,7 +38,7 @@ import {
38
38
  type TextStyle,
39
39
  } from '../components'
40
40
  import { ConversationAvatar } from '../components/display/conversation_avatar'
41
- import { HeaderTextButton } from '../components/display/platform_modal_header_buttons'
41
+ import { useHeaderRight } from '../components/display/platform_modal_header_buttons'
42
42
  import { ButtonAppearanceUnion } from '../components/display/utils/button_colors'
43
43
  import { useSuspensePaginator, useTheme } from '../hooks'
44
44
  import {
@@ -222,24 +222,18 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
222
222
  [name, productName, resourceType, styles.headerTitleBadge, styles.headerTitleContainer]
223
223
  )
224
224
 
225
- const HeaderRight = useCallback(() => {
226
- return (
227
- <HeaderTextButton
228
- onPress={() => {
229
- saveTitle({ title: trimmedTitle || conversation.title })
230
- navigation.goBack()
231
- }}
232
- title="Done"
233
- />
234
- )
235
- }, [conversation.title, navigation, saveTitle, trimmedTitle])
225
+ useHeaderRight({
226
+ icon: 'general.check',
227
+ accessibilityLabel: 'Done',
228
+ onPress: () => {
229
+ saveTitle({ title: trimmedTitle || conversation.title })
230
+ navigation.goBack()
231
+ },
232
+ })
236
233
 
237
234
  useEffect(() => {
238
- navigation.setOptions({
239
- headerRight: HeaderRight,
240
- headerTitle: HeaderTitle,
241
- })
242
- }, [HeaderRight, HeaderTitle, navigation])
235
+ navigation.setOptions({ headerTitle: HeaderTitle })
236
+ }, [HeaderTitle, navigation])
243
237
 
244
238
  const listData = [
245
239
  {
@@ -59,9 +59,9 @@ export const GroupRow = ({ group, isActive }: GroupRowProps) => {
59
59
 
60
60
  return (
61
61
  <PressableRow onPress={handleFilterByGroup} isActive={isActive}>
62
- {headerImage?.medium && (
62
+ {headerImage?.thumbnail && (
63
63
  <Image
64
- source={{ uri: headerImage?.medium }}
64
+ source={{ uri: headerImage?.thumbnail }}
65
65
  resizeMode="cover"
66
66
  style={styles.rowImage}
67
67
  alt=""
@@ -1,5 +1,6 @@
1
1
  import { useHeaderHeight } from '@react-navigation/elements'
2
- import { createComponentForStaticNavigation, RouteProp, useRoute } from '@react-navigation/native'
2
+ import * as ReactNavigationNative from '@react-navigation/native'
3
+ import { RouteProp, useRoute } from '@react-navigation/native'
3
4
  import { createNativeStackNavigator } from '@react-navigation/native-stack'
4
5
  import React, { useCallback } from 'react'
5
6
  import { LayoutChangeEvent, Platform, StyleSheet, useWindowDimensions, View } from 'react-native'
@@ -58,7 +59,26 @@ const FilterNavigator = createNativeStackNavigator<ConversationFilterStackParamL
58
59
  },
59
60
  })
60
61
 
61
- const Filters = createComponentForStaticNavigation(FilterNavigator, 'Filters')
62
+ // tree.getComponent() doesn't exist on every host's installed
63
+ // react-navigation/core — hosts on an older version would throw here.
64
+ // Where it does exist, it's the real implementation (the "deprecated"
65
+ // createComponentForStaticNavigation is just a console.warn wrapper around
66
+ // the same call on current versions), so prefer it and only reach for the
67
+ // old function on cores where getComponent() genuinely isn't there yet.
68
+ function createStaticNavigationComponent<P extends object>(
69
+ tree: { getComponent?: () => React.ComponentType<P> },
70
+ name: string
71
+ ): React.ComponentType<P> {
72
+ if (typeof tree.getComponent === 'function') return tree.getComponent()
73
+
74
+ const { createComponentForStaticNavigation } = ReactNavigationNative as {
75
+ createComponentForStaticNavigation: (tree: unknown, name: string) => React.ComponentType<P>
76
+ }
77
+
78
+ return createComponentForStaticNavigation(tree, name)
79
+ }
80
+
81
+ const Filters = createStaticNavigationComponent(FilterNavigator, 'Filters')
62
82
 
63
83
  export const ConversationFiltersScreen = (_props: ConversationFiltersScreenProps) => {
64
84
  const styles = useStyles()
@@ -9,7 +9,7 @@ import {
9
9
  useRoute,
10
10
  } from '@react-navigation/native'
11
11
  import React, { useCallback, useEffect, useRef, useState } from 'react'
12
- import { FlatList, Platform, StyleSheet, View } from 'react-native'
12
+ import { FlatList, Platform, StyleSheet, useWindowDimensions, View } from 'react-native'
13
13
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
14
14
  import { Badge, Icon, Text } from '../components'
15
15
  import { ConversationStartBanner } from '../components/conversation/conversation_start_banner'
@@ -25,6 +25,7 @@ import { ReplyShadowMessage } from '../components/conversation/reply_shadow_mess
25
25
  import { SystemMessage } from '../components/conversation/system_message'
26
26
  import { TypingIndicator } from '../components/conversation/typing_indicator'
27
27
  import { KeyboardView } from '../components/display/keyboard_view'
28
+ import { useHeaderSlotReserve } from '../components/display/platform_modal_header_buttons'
28
29
  import BlankState from '../components/primitive/blank_state_primitive'
29
30
  import { ConversationContextProvider } from '../contexts/conversation_context'
30
31
  import { useTheme } from '../hooks'
@@ -257,7 +258,7 @@ function ConversationBottomBar({
257
258
  if (conversation.disabled)
258
259
  return (
259
260
  <ConversationDisabledBanner
260
- message={conversation.disabledMessage ?? null}
261
+ disabledReason={conversation.disabledReason}
261
262
  messageLink={messageLink}
262
263
  />
263
264
  )
@@ -374,11 +375,15 @@ export const ConversationScreenTitle = ({
374
375
  }
375
376
 
376
377
  const usePressableHeaderStyle = () => {
378
+ const { width } = useWindowDimensions()
379
+ const slotReserve = useHeaderSlotReserve()
380
+ const headerContainerStyle = { maxWidth: width - slotReserve * 2 }
381
+
377
382
  return StyleSheet.create({
378
383
  container: {
379
384
  alignItems: Platform.select({ android: 'flex-start', default: 'center' }),
380
- marginRight: Platform.select({ ios: 20, default: 16 }),
381
385
  flex: 1,
386
+ ...headerContainerStyle,
382
387
  },
383
388
  titleWrapper: {
384
389
  alignItems: 'center',
@@ -12,7 +12,7 @@ export const GroupsRecipientRow = ({ group, onPress }: GroupsRecipientRowProps)
12
12
 
13
13
  return (
14
14
  <RecipientLinkRow
15
- imageUri={group.headerImage?.medium}
15
+ imageUri={group.headerImage?.thumbnail}
16
16
  title={group.name}
17
17
  subtitle={`${group.membershipsCount} members`}
18
18
  onPress={() => onPress(group)}
@@ -29,9 +29,9 @@ export const RestrictedGroupRow = ({ group }: RestrictedGroupRowProps) => {
29
29
  accessibilityLabel={`${group.name} — ${reasonText(group.canCreateConversationReasonCode)}`}
30
30
  >
31
31
  <View style={styles.innerContainer}>
32
- {group.headerImage?.medium && (
32
+ {group.headerImage?.thumbnail && (
33
33
  <Image
34
- source={{ uri: group.headerImage.medium }}
34
+ source={{ uri: group.headerImage.thumbnail }}
35
35
  resizeMode="cover"
36
36
  style={styles.image}
37
37
  alt=""