@planningcenter/chat-react-native 3.42.3-qa-staging.7 → 3.42.3-qa-856.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 (76) 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 +25 -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 +66 -8
  10. package/build/components/display/platform_modal_header_buttons.js.map +1 -1
  11. package/build/hooks/use_conversation.d.ts.map +1 -1
  12. package/build/hooks/use_conversation.js +0 -1
  13. package/build/hooks/use_conversation.js.map +1 -1
  14. package/build/hooks/use_conversation_jolt_events.d.ts.map +1 -1
  15. package/build/hooks/use_conversation_jolt_events.js +2 -10
  16. package/build/hooks/use_conversation_jolt_events.js.map +1 -1
  17. package/build/navigation/index.d.ts +47 -117
  18. package/build/navigation/index.d.ts.map +1 -1
  19. package/build/navigation/index.js +55 -57
  20. package/build/navigation/index.js.map +1 -1
  21. package/build/screens/bug_report_screen.d.ts.map +1 -1
  22. package/build/screens/bug_report_screen.js +4 -13
  23. package/build/screens/bug_report_screen.js.map +1 -1
  24. package/build/screens/conversation_details_screen.d.ts.map +1 -1
  25. package/build/screens/conversation_details_screen.js +10 -12
  26. package/build/screens/conversation_details_screen.js.map +1 -1
  27. package/build/screens/conversation_filters/components/rows.js +1 -1
  28. package/build/screens/conversation_filters/components/rows.js.map +1 -1
  29. package/build/screens/conversation_filters_screen.d.ts.map +1 -1
  30. package/build/screens/conversation_filters_screen.js +15 -2
  31. package/build/screens/conversation_filters_screen.js.map +1 -1
  32. package/build/screens/conversation_screen.js +1 -1
  33. package/build/screens/conversation_screen.js.map +1 -1
  34. package/build/screens/conversation_select_recipients/components/groups_recipient_row.js +1 -1
  35. package/build/screens/conversation_select_recipients/components/groups_recipient_row.js.map +1 -1
  36. package/build/screens/conversation_select_recipients/components/restricted_group_row.js +1 -1
  37. package/build/screens/conversation_select_recipients/components/restricted_group_row.js.map +1 -1
  38. package/build/screens/message_report_screen.d.ts.map +1 -1
  39. package/build/screens/message_report_screen.js +12 -18
  40. package/build/screens/message_report_screen.js.map +1 -1
  41. package/build/screens/notification_settings_screen.d.ts.map +1 -1
  42. package/build/screens/notification_settings_screen.js +2 -14
  43. package/build/screens/notification_settings_screen.js.map +1 -1
  44. package/build/types/jolt_events/conversation_events.d.ts +2 -2
  45. package/build/types/jolt_events/conversation_events.d.ts.map +1 -1
  46. package/build/types/jolt_events/conversation_events.js.map +1 -1
  47. package/build/types/resources/conversation.d.ts +2 -2
  48. package/build/types/resources/conversation.d.ts.map +1 -1
  49. package/build/types/resources/conversation.js.map +1 -1
  50. package/build/utils/conversation_safety_constants.d.ts +1 -1
  51. package/build/utils/conversation_safety_constants.d.ts.map +1 -1
  52. package/build/utils/conversation_safety_constants.js +1 -1
  53. package/build/utils/conversation_safety_constants.js.map +1 -1
  54. package/build/utils/request/conversation.d.ts.map +1 -1
  55. package/build/utils/request/conversation.js +0 -1
  56. package/build/utils/request/conversation.js.map +1 -1
  57. package/package.json +3 -3
  58. package/src/components/conversation/messages_disabled_banners.tsx +11 -3
  59. package/src/components/display/adult_requirement_notice.tsx +1 -1
  60. package/src/components/display/platform_modal_header_buttons.tsx +139 -11
  61. package/src/hooks/use_conversation.ts +0 -1
  62. package/src/hooks/use_conversation_jolt_events.ts +3 -16
  63. package/src/navigation/index.tsx +143 -154
  64. package/src/screens/bug_report_screen.tsx +10 -15
  65. package/src/screens/conversation_details_screen.tsx +10 -17
  66. package/src/screens/conversation_filters/components/rows.tsx +2 -2
  67. package/src/screens/conversation_filters_screen.tsx +22 -2
  68. package/src/screens/conversation_screen.tsx +1 -1
  69. package/src/screens/conversation_select_recipients/components/groups_recipient_row.tsx +1 -1
  70. package/src/screens/conversation_select_recipients/components/restricted_group_row.tsx +2 -2
  71. package/src/screens/message_report_screen.tsx +18 -21
  72. package/src/screens/notification_settings_screen.tsx +3 -20
  73. package/src/types/jolt_events/conversation_events.ts +2 -2
  74. package/src/types/resources/conversation.ts +3 -2
  75. package/src/utils/conversation_safety_constants.ts +1 -1
  76. package/src/utils/request/conversation.ts +0 -1
@@ -1,4 +1,3 @@
1
- import { HeaderBackButton } from '@react-navigation/elements'
2
1
  import { StaticParamList } from '@react-navigation/native'
3
2
  import {
4
3
  createNativeStackNavigator,
@@ -6,11 +5,12 @@ import {
6
5
  } from '@react-navigation/native-stack'
7
6
  import { CardStyleInterpolators } from '@react-navigation/stack'
8
7
  import React from 'react'
9
- import { Platform } from 'react-native'
10
- import { Icon } from '../components'
11
8
  import {
12
- HeaderDoneButton,
13
- HeaderTextButton,
9
+ composeOptions,
10
+ createMinimalHeaderBackButton,
11
+ withMinimalBack,
12
+ withRightClose,
13
+ withRightText,
14
14
  } from '../components/display/platform_modal_header_buttons'
15
15
  import { useDirectMessagesEnabled, useQualifiedByAge } from '../hooks'
16
16
  import {
@@ -81,11 +81,6 @@ import {
81
81
  import { TeamConversationScreen } from '../screens/team_conversation_screen'
82
82
  import { ScreenLayoutWithChatAccessGate } from './screenLayout'
83
83
 
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
- }
88
-
89
84
  export const NewConversationStack = createNativeStackNavigator({
90
85
  initialRouteName: 'ConversationNewEntry',
91
86
  screenOptions: {
@@ -95,53 +90,54 @@ export const NewConversationStack = createNativeStackNavigator({
95
90
  screens: {
96
91
  ConversationNewEntry: {
97
92
  screen: ConversationNewEntryScreen,
98
- options: ({ navigation }) => ({
99
- title: 'New conversation',
100
- headerRight: (props: NativeStackHeaderRightProps) => (
101
- <HeaderTextButton {...props} onPress={navigation.goBack} title="Cancel" />
93
+ options: ({ navigation }) =>
94
+ composeOptions(
95
+ {
96
+ title: 'New conversation',
97
+ headerBackVisible: false,
98
+ },
99
+ withRightText({ text: 'Cancel', onPress: navigation.goBack })
102
100
  ),
103
- headerBackVisible: false,
104
- }),
105
101
  },
106
102
  ConversationSelectGroupRecipients: {
107
103
  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
- />
104
+ options: ({ navigation, route }) =>
105
+ composeOptions(
106
+ {
107
+ title: 'New conversation',
108
+ },
109
+ withRightText({
110
+ text: 'Cancel',
111
+ onPress: () => navigation.popTo('Conversations', route.params),
112
+ })
116
113
  ),
117
- }),
118
114
  },
119
115
  ConversationSelectTeamsILeadRecipients: {
120
116
  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
- />
117
+ options: ({ navigation, route }) =>
118
+ composeOptions(
119
+ {
120
+ title: 'New conversation',
121
+ },
122
+ withRightText({
123
+ text: 'Cancel',
124
+ onPress: () => navigation.popTo('Conversations', route.params),
125
+ })
129
126
  ),
130
- }),
131
127
  },
132
128
  ConversationSelectDirectMessageRecipients: {
133
129
  screen: ConversationSelectDirectMessageRecipientsScreen,
134
130
  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
- />
131
+ options: ({ navigation, route }) =>
132
+ composeOptions(
133
+ {
134
+ title: 'New conversation',
135
+ },
136
+ withRightText({
137
+ text: 'Cancel',
138
+ onPress: () => navigation.popTo('Conversations', route.params),
139
+ })
143
140
  ),
144
- }),
145
141
  },
146
142
  ConversationFilterRecipients: {
147
143
  screen: ConversationFilterRecipientsScreen,
@@ -149,21 +145,21 @@ export const NewConversationStack = createNativeStackNavigator({
149
145
  },
150
146
  ConversationNew: {
151
147
  screen: ConversationNewScreen,
152
- options: ({ navigation, route }) => ({
153
- title: 'New conversation',
154
- headerLeft: () => null,
155
- headerRight: (props: NativeStackHeaderRightProps) => (
156
- <HeaderTextButton
157
- {...props}
158
- onPress={() => {
148
+ options: ({ navigation, route }) =>
149
+ composeOptions(
150
+ {
151
+ title: 'New conversation',
152
+ headerLeft: () => null,
153
+ },
154
+ withRightText({
155
+ text: 'Cancel',
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,
@@ -180,17 +176,14 @@ export const ChatStack = createNativeStackNavigator({
180
176
  screens: {
181
177
  Conversations: {
182
178
  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
- />
179
+ options: ({ route, navigation }) =>
180
+ composeOptions(
181
+ {
182
+ headerTitle: (route.params as { title?: string })?.title ?? 'Chat',
183
+ headerBackVisible: false,
184
+ },
185
+ withRightClose({ onPress: navigation.goBack })
192
186
  ),
193
- }),
194
187
  },
195
188
  ConversationFilters: {
196
189
  screen: ConversationFiltersScreen,
@@ -198,61 +191,48 @@ export const ChatStack = createNativeStackNavigator({
198
191
  },
199
192
  Conversation: {
200
193
  screen: ConversationScreen,
201
- options: ({ route, navigation }) => ({
202
- headerTitle: (props: NativeStackHeaderRightProps) => {
203
- const { conversation_id, title, badge, deleted, muted } =
204
- route.params as ConversationRouteProps
194
+ options: ({ route, navigation }) =>
195
+ composeOptions(
196
+ {
197
+ headerTitle: (props: NativeStackHeaderRightProps) => {
198
+ const { conversation_id, title, badge, deleted, muted } =
199
+ route.params as ConversationRouteProps
205
200
 
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
- />
201
+ return (
202
+ <ConversationScreenTitle
203
+ conversation_id={conversation_id}
204
+ badge={badge}
205
+ deleted={deleted}
206
+ muted={muted}
207
+ {...props}
208
+ >
209
+ {title ?? 'Conversation'}
210
+ </ConversationScreenTitle>
211
+ )
212
+ },
213
+ headerLeft: createMinimalHeaderBackButton({
214
+ onPress: () => {
215
+ const params = route.params as ConversationRouteProps
216
+ if ((params as ConversationFiltersParams)?.chat_group_graph_id) {
217
+ // Ensure that conversations with a graph id pass them back to the conversation list
218
+ const { chat_group_graph_id } = params
219
+ navigation.popTo('Conversations', { chat_group_graph_id })
220
+ } else {
221
+ navigation.goBack()
222
+ }
223
+ },
224
+ }),
225
+ },
226
+ withRightClose({ onPress: () => navigation.getParent()?.goBack() })
223
227
  ),
224
- headerLeft: props => (
225
- <HeaderBackButton
226
- style={HEADER_BACK_BUTTON_LAYOUT_RESET_STYLES}
227
- displayMode="minimal"
228
- onPress={() => {
229
- const params = route.params as ConversationRouteProps
230
- if ((params as ConversationFiltersParams)?.chat_group_graph_id) {
231
- // Ensure that conversations with a graph id pass them back to the conversation list
232
- const { chat_group_graph_id } = params
233
- navigation.popTo('Conversations', { chat_group_graph_id })
234
- } else {
235
- navigation.goBack()
236
- }
237
- }}
238
- {...props}
239
- />
240
- ),
241
- }),
242
228
  },
243
229
  ConversationReply: {
244
230
  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
- />
231
+ options: ({ route, navigation }) =>
232
+ composeOptions(
233
+ { title: (route.params as ConversationRouteProps)?.title ?? 'Reply' },
234
+ withMinimalBack({ onPress: navigation.goBack })
254
235
  ),
255
- }),
256
236
  },
257
237
  TeamConversation: {
258
238
  screen: TeamConversationScreen,
@@ -265,13 +245,14 @@ export const ChatStack = createNativeStackNavigator({
265
245
  },
266
246
  ConversationDetails: {
267
247
  screen: ConversationDetailsScreen,
268
- options: ({ navigation }) => ({
269
- presentation: 'modal',
270
- title: 'Conversation details',
271
- headerRight: (props: NativeStackHeaderRightProps) => (
272
- <HeaderTextButton {...props} onPress={navigation.goBack} title="Done" />
248
+ options: ({ navigation }) =>
249
+ composeOptions(
250
+ {
251
+ presentation: 'modal',
252
+ title: 'Conversation details',
253
+ },
254
+ withRightText({ text: 'Done', onPress: navigation.goBack })
273
255
  ),
274
- }),
275
256
  },
276
257
  AvatarPicker: {
277
258
  screen: AvatarPickerScreen,
@@ -279,23 +260,25 @@ export const ChatStack = createNativeStackNavigator({
279
260
  },
280
261
  ChatSettings: {
281
262
  screen: SettingsScreen,
282
- options: ({ navigation }) => ({
283
- title: 'Chat settings',
284
- headerBackVisible: false,
285
- headerRight: (props: NativeStackHeaderRightProps) => (
286
- <HeaderTextButton {...props} onPress={navigation.goBack} title="Done" />
263
+ options: ({ navigation }) =>
264
+ composeOptions(
265
+ {
266
+ title: 'Chat settings',
267
+ headerBackVisible: false,
268
+ },
269
+ withRightText({ text: 'Done', onPress: navigation.goBack })
287
270
  ),
288
- }),
289
271
  },
290
272
  NotificationSettings: {
291
273
  screen: NotificationSettingsScreen,
292
- options: ({ navigation }) => ({
293
- title: 'Chat',
294
- headerBackVisible: false,
295
- headerRight: (props: NativeStackHeaderRightProps) => (
296
- <HeaderTextButton {...props} onPress={navigation.goBack} title="Done" />
274
+ options: ({ navigation }) =>
275
+ composeOptions(
276
+ {
277
+ title: 'Chat',
278
+ headerBackVisible: false,
279
+ },
280
+ withRightText({ text: 'Done', onPress: navigation.goBack })
297
281
  ),
298
- }),
299
282
  },
300
283
  PreferredAppSelection: {
301
284
  screen: PreferredAppSelectionScreen,
@@ -305,12 +288,20 @@ export const ChatStack = createNativeStackNavigator({
305
288
  },
306
289
  GroupNotificationSettings: {
307
290
  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
- }),
291
+ options: ({ route, navigation }) => {
292
+ const title = (route.params as { title?: string })?.title || 'Group Settings'
293
+
294
+ // Done only shows when this is the entry screen (e.g. deep links);
295
+ // deeper in the stack the back button covers dismissal.
296
+ return navigation.getState().index === 0
297
+ ? composeOptions(
298
+ {
299
+ title,
300
+ },
301
+ withRightText({ text: 'Done', onPress: navigation.goBack })
302
+ )
303
+ : { title }
304
+ },
314
305
  },
315
306
  GroupNotificationLevelSelect: {
316
307
  screen: GroupNotificationLevelSelectScreen,
@@ -331,17 +322,14 @@ export const ChatStack = createNativeStackNavigator({
331
322
  GroupConversationNew: {
332
323
  screen: ConversationNewScreen,
333
324
  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
- />
325
+ options: ({ navigation }) =>
326
+ composeOptions(
327
+ {
328
+ title: 'New conversation',
329
+ headerLeft: () => null,
330
+ },
331
+ withRightText({ text: 'Cancel', onPress: () => navigation.getParent()?.goBack() })
343
332
  ),
344
- }),
345
333
  },
346
334
  SendGiphy: {
347
335
  screen: SendGiphyScreen,
@@ -382,14 +370,15 @@ export const ChatStack = createNativeStackNavigator({
382
370
  },
383
371
  GetHelp: {
384
372
  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" />
373
+ options: ({ navigation }) =>
374
+ composeOptions(
375
+ {
376
+ headerTitle: 'Get help',
377
+ headerBackVisible: false,
378
+ presentation: 'modal',
379
+ },
380
+ withRightText({ text: 'Close', onPress: navigation.goBack })
391
381
  ),
392
- }),
393
382
  },
394
383
  NotFound: {
395
384
  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
+ withLeftText,
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
+ withLeftText({ text: 'Cancel', onPress: () => navigation.goBack() })
64
+ )
63
65
 
64
66
  interface Attachment {
65
67
  id: string
@@ -156,14 +158,7 @@ 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({ title: 'Submit', onPress: handleSubmit, disabled: !formValid })
167
162
 
168
163
  if (status === 'pending') {
169
164
  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,17 @@ 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
+ title: 'Done',
227
+ onPress: () => {
228
+ saveTitle({ title: trimmedTitle || conversation.title })
229
+ navigation.goBack()
230
+ },
231
+ })
236
232
 
237
233
  useEffect(() => {
238
- navigation.setOptions({
239
- headerRight: HeaderRight,
240
- headerTitle: HeaderTitle,
241
- })
242
- }, [HeaderRight, HeaderTitle, navigation])
234
+ navigation.setOptions({ headerTitle: HeaderTitle })
235
+ }, [HeaderTitle, navigation])
243
236
 
244
237
  const listData = [
245
238
  {
@@ -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()
@@ -257,7 +257,7 @@ function ConversationBottomBar({
257
257
  if (conversation.disabled)
258
258
  return (
259
259
  <ConversationDisabledBanner
260
- message={conversation.disabledMessage ?? null}
260
+ disabledReason={conversation.disabledReason}
261
261
  messageLink={messageLink}
262
262
  />
263
263
  )
@@ -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=""
@@ -8,8 +8,9 @@ import { View, StyleSheet, TextInput, ScrollView } from 'react-native'
8
8
  import { useSafeAreaInsets } from 'react-native-safe-area-context'
9
9
  import { Spinner, Text, KeyboardView } from '../components'
10
10
  import {
11
- HeaderDismissButton,
12
- HeaderTextButton,
11
+ composeOptions,
12
+ useHeaderRight,
13
+ withLeftText,
13
14
  } from '../components/display/platform_modal_header_buttons'
14
15
  import BlankState from '../components/primitive/blank_state_primitive'
15
16
  import { useTheme } from '../hooks'
@@ -34,11 +35,10 @@ export const MessageReportScreenOptions = ({
34
35
  })
35
36
  )
36
37
 
37
- return {
38
- presentation: 'modal',
39
- title: 'Report message',
40
- headerLeft: () => <HeaderDismissButton title="Cancel" onPress={handleClose} />,
41
- }
38
+ return composeOptions(
39
+ { presentation: 'modal', title: 'Report message' },
40
+ withLeftText({ text: 'Cancel', onPress: handleClose })
41
+ )
42
42
  }
43
43
 
44
44
  export type MessageReportScreenProps = StaticScreenProps<{
@@ -103,23 +103,20 @@ function MessageReportScreenContent({
103
103
  navigation.dispatch(StackActions.popTo('Conversation', { conversation_id }))
104
104
  }, [navigation, conversation_id])
105
105
 
106
- const HeaderRight = useCallback(
107
- () => <HeaderTextButton title="Submit" onPress={handleSubmit} disabled={!isFormValid} />,
108
- [handleSubmit, isFormValid]
109
- )
106
+ const formVisible = status !== 'pending' && status !== 'success'
107
+
108
+ useHeaderRight({
109
+ title: 'Submit',
110
+ onPress: handleSubmit,
111
+ disabled: !isFormValid,
112
+ enabled: formVisible,
113
+ })
110
114
 
111
115
  useLayoutEffect(() => {
112
- if (status === 'pending' || status === 'success') {
113
- navigation.setOptions({
114
- headerRight: () => null,
115
- })
116
- } else {
117
- navigation.setOptions({
118
- headerTitle: 'Report message',
119
- headerRight: HeaderRight,
120
- })
116
+ if (formVisible) {
117
+ navigation.setOptions({ headerTitle: 'Report message' })
121
118
  }
122
- }, [HeaderRight, navigation, status])
119
+ }, [formVisible, navigation])
123
120
 
124
121
  if (status === 'pending') {
125
122
  return (