@planningcenter/chat-react-native 3.42.3-qa-staging.2 → 3.42.3-qa-staging.4

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/conversation/messages_disabled_banners.d.ts +2 -3
  2. package/build/components/conversation/messages_disabled_banners.d.ts.map +1 -1
  3. package/build/components/conversation/messages_disabled_banners.js +2 -7
  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 +47 -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 +119 -9
  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 +1 -0
  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 +10 -2
  16. package/build/hooks/use_conversation_jolt_events.js.map +1 -1
  17. package/build/navigation/index.d.ts +67 -170
  18. package/build/navigation/index.d.ts.map +1 -1
  19. package/build/navigation/index.js +59 -70
  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 +10 -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 +11 -12
  26. package/build/screens/conversation_details_screen.js.map +1 -1
  27. package/build/screens/conversation_filters_screen.js +2 -2
  28. package/build/screens/conversation_filters_screen.js.map +1 -1
  29. package/build/screens/conversation_screen.d.ts.map +1 -1
  30. package/build/screens/conversation_screen.js +10 -3
  31. package/build/screens/conversation_screen.js.map +1 -1
  32. package/build/screens/message_report_screen.d.ts.map +1 -1
  33. package/build/screens/message_report_screen.js +14 -18
  34. package/build/screens/message_report_screen.js.map +1 -1
  35. package/build/screens/notification_settings_screen.d.ts.map +1 -1
  36. package/build/screens/notification_settings_screen.js +0 -14
  37. package/build/screens/notification_settings_screen.js.map +1 -1
  38. package/build/types/jolt_events/conversation_events.d.ts +2 -2
  39. package/build/types/jolt_events/conversation_events.d.ts.map +1 -1
  40. package/build/types/jolt_events/conversation_events.js.map +1 -1
  41. package/build/types/resources/conversation.d.ts +2 -2
  42. package/build/types/resources/conversation.d.ts.map +1 -1
  43. package/build/types/resources/conversation.js.map +1 -1
  44. package/build/utils/conversation_safety_constants.d.ts +1 -1
  45. package/build/utils/conversation_safety_constants.d.ts.map +1 -1
  46. package/build/utils/conversation_safety_constants.js +1 -1
  47. package/build/utils/conversation_safety_constants.js.map +1 -1
  48. package/build/utils/request/conversation.d.ts.map +1 -1
  49. package/build/utils/request/conversation.js +1 -0
  50. package/build/utils/request/conversation.js.map +1 -1
  51. package/package.json +3 -3
  52. package/src/components/conversation/messages_disabled_banners.tsx +3 -11
  53. package/src/components/display/adult_requirement_notice.tsx +1 -1
  54. package/src/components/display/platform_modal_header_buttons.tsx +254 -13
  55. package/src/hooks/use_conversation.ts +1 -0
  56. package/src/hooks/use_conversation_jolt_events.ts +16 -3
  57. package/src/navigation/index.tsx +147 -164
  58. package/src/screens/bug_report_screen.tsx +16 -15
  59. package/src/screens/conversation_details_screen.tsx +11 -17
  60. package/src/screens/conversation_filters_screen.tsx +2 -2
  61. package/src/screens/conversation_screen.tsx +11 -3
  62. package/src/screens/message_report_screen.tsx +20 -21
  63. package/src/screens/notification_settings_screen.tsx +1 -21
  64. package/src/types/jolt_events/conversation_events.ts +2 -2
  65. package/src/types/resources/conversation.ts +2 -3
  66. package/src/utils/conversation_safety_constants.ts +1 -1
  67. package/src/utils/request/conversation.ts +1 -0
@@ -1,16 +1,17 @@
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
+ createMinimalHeaderBackButton,
12
+ withMinimalBack,
13
+ withRightClose,
14
+ withRightDone,
14
15
  } from '../components/display/platform_modal_header_buttons'
15
16
  import { useDirectMessagesEligible, useQualifiedByAge } from '../hooks'
16
17
  import {
@@ -79,69 +80,66 @@ import {
79
80
  SystemMessagePeopleScreenOptions,
80
81
  } from '../screens/system_message_people_screen'
81
82
  import { TeamConversationScreen } from '../screens/team_conversation_screen'
83
+ import { isLiquidGlassEnabled } from '../utils/liquid_glass'
82
84
  import { ScreenLayoutWithChatAccessGate } from './screenLayout'
83
85
 
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
- }
86
+ const globalScreenOptions: () => NativeStackNavigationOptions = () => ({
87
+ headerBackButtonDisplayMode: 'minimal',
88
+ ...(isLiquidGlassEnabled() ? { headerShadowVisible: false } : {}),
89
+ })
88
90
 
89
91
  export const NewConversationStack = createNativeStackNavigator({
90
92
  initialRouteName: 'ConversationNewEntry',
91
- screenOptions: {
92
- headerBackButtonDisplayMode: 'minimal',
93
- },
93
+ screenOptions: globalScreenOptions,
94
94
  screenLayout: ScreenLayoutWithChatAccessGate,
95
95
  screens: {
96
96
  ConversationNewEntry: {
97
97
  screen: ConversationNewEntryScreen,
98
- options: ({ navigation }) => ({
99
- title: 'New conversation',
100
- headerRight: (props: NativeStackHeaderRightProps) => (
101
- <HeaderTextButton {...props} onPress={navigation.goBack} title="Cancel" />
98
+ options: ({ navigation }) =>
99
+ composeOptions(
100
+ {
101
+ title: 'New conversation',
102
+ headerBackVisible: false,
103
+ },
104
+ withRightClose({ onPress: navigation.goBack })
102
105
  ),
103
- headerBackVisible: false,
104
- }),
105
106
  },
106
107
  ConversationSelectGroupRecipients: {
107
108
  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
- />
109
+ options: ({ navigation, route }) =>
110
+ composeOptions(
111
+ {
112
+ title: 'New conversation',
113
+ },
114
+ withRightClose({
115
+ onPress: () => navigation.popTo('Conversations', route.params),
116
+ })
116
117
  ),
117
- }),
118
118
  },
119
119
  ConversationSelectTeamsILeadRecipients: {
120
120
  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
- />
121
+ options: ({ navigation, route }) =>
122
+ composeOptions(
123
+ {
124
+ title: 'New conversation',
125
+ },
126
+ withRightClose({
127
+ onPress: () => navigation.popTo('Conversations', route.params),
128
+ })
129
129
  ),
130
- }),
131
130
  },
132
131
  ConversationSelectDirectMessageRecipients: {
133
132
  screen: ConversationSelectDirectMessageRecipientsScreen,
134
133
  if: useDirectMessagesEligible,
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
- />
134
+ options: ({ navigation, route }) =>
135
+ composeOptions(
136
+ {
137
+ title: 'New conversation',
138
+ },
139
+ withRightClose({
140
+ onPress: () => navigation.popTo('Conversations', route.params),
141
+ })
143
142
  ),
144
- }),
145
143
  },
146
144
  ConversationFilterRecipients: {
147
145
  screen: ConversationFilterRecipientsScreen,
@@ -149,21 +147,20 @@ export const NewConversationStack = createNativeStackNavigator({
149
147
  },
150
148
  ConversationNew: {
151
149
  screen: ConversationNewScreen,
152
- options: ({ navigation, route }) => ({
153
- title: 'New conversation',
154
- headerLeft: () => null,
155
- headerRight: (props: NativeStackHeaderRightProps) => (
156
- <HeaderTextButton
157
- {...props}
158
- onPress={() => {
150
+ options: ({ navigation, route }) =>
151
+ composeOptions(
152
+ {
153
+ title: 'New conversation',
154
+ headerLeft: () => null,
155
+ },
156
+ withRightClose({
157
+ onPress: () => {
159
158
  const { chat_group_graph_id, group_source_app_name } =
160
159
  route.params as ConversationSelectRecipientsParams
161
160
  navigation.popTo('Conversations', { chat_group_graph_id, group_source_app_name })
162
- }}
163
- title="Cancel"
164
- />
161
+ },
162
+ })
165
163
  ),
166
- }),
167
164
  },
168
165
  AvatarPicker: {
169
166
  screen: AvatarPickerScreen,
@@ -173,24 +170,19 @@ export const NewConversationStack = createNativeStackNavigator({
173
170
  })
174
171
 
175
172
  export const ChatStack = createNativeStackNavigator({
176
- screenOptions: {
177
- headerBackButtonDisplayMode: 'minimal',
178
- },
173
+ screenOptions: globalScreenOptions,
179
174
  screenLayout: ScreenLayoutWithChatAccessGate,
180
175
  screens: {
181
176
  Conversations: {
182
177
  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
- />
178
+ options: ({ route, navigation }) =>
179
+ composeOptions(
180
+ {
181
+ headerTitle: (route.params as { title?: string })?.title ?? 'Chat',
182
+ headerBackVisible: false,
183
+ },
184
+ withRightClose({ onPress: navigation.goBack })
192
185
  ),
193
- }),
194
186
  },
195
187
  ConversationFilters: {
196
188
  screen: ConversationFiltersScreen,
@@ -198,61 +190,48 @@ export const ChatStack = createNativeStackNavigator({
198
190
  },
199
191
  Conversation: {
200
192
  screen: ConversationScreen,
201
- options: ({ route, navigation }) => ({
202
- headerTitle: (props: NativeStackHeaderRightProps) => {
203
- const { conversation_id, title, badge, deleted, muted } =
204
- route.params as ConversationRouteProps
193
+ options: ({ route, navigation }) =>
194
+ composeOptions(
195
+ {
196
+ headerTitle: (props: NativeStackHeaderRightProps) => {
197
+ const { conversation_id, title, badge, deleted, muted } =
198
+ route.params as ConversationRouteProps
205
199
 
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
- />
200
+ return (
201
+ <ConversationScreenTitle
202
+ conversation_id={conversation_id}
203
+ badge={badge}
204
+ deleted={deleted}
205
+ muted={muted}
206
+ {...props}
207
+ >
208
+ {title ?? 'Conversation'}
209
+ </ConversationScreenTitle>
210
+ )
211
+ },
212
+ headerLeft: createMinimalHeaderBackButton({
213
+ onPress: () => {
214
+ const params = route.params as ConversationRouteProps
215
+ if ((params as ConversationFiltersParams)?.chat_group_graph_id) {
216
+ // Ensure that conversations with a graph id pass them back to the conversation list
217
+ const { chat_group_graph_id } = params
218
+ navigation.popTo('Conversations', { chat_group_graph_id })
219
+ } else {
220
+ navigation.goBack()
221
+ }
222
+ },
223
+ }),
224
+ },
225
+ withRightClose({ onPress: () => navigation.getParent()?.goBack() })
223
226
  ),
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
227
  },
243
228
  ConversationReply: {
244
229
  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
- />
230
+ options: ({ route, navigation }) =>
231
+ composeOptions(
232
+ { title: (route.params as ConversationRouteProps)?.title ?? 'Reply' },
233
+ withMinimalBack({ onPress: navigation.goBack })
254
234
  ),
255
- }),
256
235
  },
257
236
  TeamConversation: {
258
237
  screen: TeamConversationScreen,
@@ -265,13 +244,14 @@ export const ChatStack = createNativeStackNavigator({
265
244
  },
266
245
  ConversationDetails: {
267
246
  screen: ConversationDetailsScreen,
268
- options: ({ navigation }) => ({
269
- presentation: 'modal',
270
- title: 'Conversation details',
271
- headerRight: (props: NativeStackHeaderRightProps) => (
272
- <HeaderTextButton {...props} onPress={navigation.goBack} title="Done" />
247
+ options: ({ navigation }) =>
248
+ composeOptions(
249
+ {
250
+ presentation: 'modal',
251
+ title: 'Conversation details',
252
+ },
253
+ withRightDone({ onPress: navigation.goBack })
273
254
  ),
274
- }),
275
255
  },
276
256
  AvatarPicker: {
277
257
  screen: AvatarPickerScreen,
@@ -279,38 +259,43 @@ export const ChatStack = createNativeStackNavigator({
279
259
  },
280
260
  ChatSettings: {
281
261
  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
- }),
262
+ options: ({ navigation }) =>
263
+ composeOptions({ title: 'Chat settings' }, withMinimalBack({ onPress: navigation.goBack })),
289
264
  },
290
265
  NotificationSettings: {
291
266
  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
- }),
267
+ options: ({ navigation }) =>
268
+ navigation.getState().index === 0
269
+ ? composeOptions(
270
+ { title: 'Chat notifications' },
271
+ withRightClose({ onPress: navigation.goBack })
272
+ )
273
+ : composeOptions(
274
+ { title: 'Chat notifications' },
275
+ withMinimalBack({ onPress: navigation.goBack })
276
+ ),
299
277
  },
300
278
  PreferredAppSelection: {
301
279
  screen: PreferredAppSelectionScreen,
302
- options: {
303
- title: 'Preferred app',
304
- },
280
+ options: ({ navigation }) =>
281
+ composeOptions({ title: 'Preferred app' }, withMinimalBack({ onPress: navigation.goBack })),
305
282
  },
306
283
  GroupNotificationSettings: {
307
284
  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
- }),
285
+ options: ({ route, navigation }) => {
286
+ const title = (route.params as { title?: string })?.title || 'Group Settings'
287
+
288
+ // The dismiss button only shows when this is the entry screen (e.g.
289
+ // deep links); deeper in the stack the back button covers dismissal.
290
+ return navigation.getState().index === 0
291
+ ? composeOptions(
292
+ {
293
+ title,
294
+ },
295
+ withRightClose({ onPress: navigation.goBack })
296
+ )
297
+ : composeOptions({ title }, withMinimalBack({ onPress: navigation.goBack }))
298
+ },
314
299
  },
315
300
  GroupNotificationLevelSelect: {
316
301
  screen: GroupNotificationLevelSelectScreen,
@@ -331,17 +316,14 @@ export const ChatStack = createNativeStackNavigator({
331
316
  GroupConversationNew: {
332
317
  screen: ConversationNewScreen,
333
318
  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
- />
319
+ options: ({ navigation }) =>
320
+ composeOptions(
321
+ {
322
+ title: 'New conversation',
323
+ headerLeft: () => null,
324
+ },
325
+ withRightClose({ onPress: () => navigation.getParent()?.goBack() })
343
326
  ),
344
- }),
345
327
  },
346
328
  SendGiphy: {
347
329
  screen: SendGiphyScreen,
@@ -382,14 +364,15 @@ export const ChatStack = createNativeStackNavigator({
382
364
  },
383
365
  GetHelp: {
384
366
  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" />
367
+ options: ({ navigation }) =>
368
+ composeOptions(
369
+ {
370
+ headerTitle: 'Get help',
371
+ headerBackVisible: false,
372
+ presentation: 'modal',
373
+ },
374
+ withRightClose({ onPress: navigation.goBack })
391
375
  ),
392
- }),
393
376
  },
394
377
  NotFound: {
395
378
  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.message',
163
+ accessibilityLabel: 'Submit',
164
+ variant: 'fill',
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
  {
@@ -1,5 +1,5 @@
1
1
  import { useHeaderHeight } from '@react-navigation/elements'
2
- import { createComponentForStaticNavigation, RouteProp, useRoute } from '@react-navigation/native'
2
+ import { RouteProp, useRoute } from '@react-navigation/native'
3
3
  import { createNativeStackNavigator } from '@react-navigation/native-stack'
4
4
  import React, { useCallback } from 'react'
5
5
  import { LayoutChangeEvent, Platform, StyleSheet, useWindowDimensions, View } from 'react-native'
@@ -58,7 +58,7 @@ const FilterNavigator = createNativeStackNavigator<ConversationFilterStackParamL
58
58
  },
59
59
  })
60
60
 
61
- const Filters = createComponentForStaticNavigation(FilterNavigator, 'Filters')
61
+ const Filters = FilterNavigator.getComponent()
62
62
 
63
63
  export const ConversationFiltersScreen = (_props: ConversationFiltersScreenProps) => {
64
64
  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'
@@ -45,6 +46,7 @@ import { ConversationBadgeResource } from '../types/resources/conversation_badge
45
46
  import { MessageResource } from '../types/resources/message'
46
47
  import { getRelativeDateStatus } from '../utils/date'
47
48
  import { groupMessages, type DateSeparator } from '../utils/group_messages'
49
+ import { isLiquidGlassEnabled } from '../utils/liquid_glass'
48
50
  import { CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL } from '../utils/styles'
49
51
  import { isSystemMessage } from '../utils/system_messages'
50
52
 
@@ -257,7 +259,7 @@ function ConversationBottomBar({
257
259
  if (conversation.disabled)
258
260
  return (
259
261
  <ConversationDisabledBanner
260
- disabledReason={conversation.disabledReason}
262
+ message={conversation.disabledMessage ?? null}
261
263
  messageLink={messageLink}
262
264
  />
263
265
  )
@@ -374,11 +376,17 @@ export const ConversationScreenTitle = ({
374
376
  }
375
377
 
376
378
  const usePressableHeaderStyle = () => {
379
+ const { width } = useWindowDimensions()
380
+ const slotReserve = useHeaderSlotReserve()
381
+ const headerContainerStyle = isLiquidGlassEnabled()
382
+ ? { maxWidth: width - slotReserve * 2 }
383
+ : { marginRight: Platform.select({ default: 20, android: 16 }) }
384
+
377
385
  return StyleSheet.create({
378
386
  container: {
379
387
  alignItems: Platform.select({ android: 'flex-start', default: 'center' }),
380
- marginRight: Platform.select({ ios: 20, default: 16 }),
381
388
  flex: 1,
389
+ ...headerContainerStyle,
382
390
  },
383
391
  titleWrapper: {
384
392
  alignItems: 'center',
@@ -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
+ withLeftClose,
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
+ withLeftClose({ onPress: handleClose })
41
+ )
42
42
  }
43
43
 
44
44
  export type MessageReportScreenProps = StaticScreenProps<{
@@ -103,23 +103,22 @@ 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
+ icon: 'general.message',
110
+ accessibilityLabel: 'Submit',
111
+ variant: 'fill',
112
+ onPress: handleSubmit,
113
+ disabled: !isFormValid,
114
+ enabled: formVisible,
115
+ })
110
116
 
111
117
  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
- })
118
+ if (formVisible) {
119
+ navigation.setOptions({ headerTitle: 'Report message' })
121
120
  }
122
- }, [HeaderRight, navigation, status])
121
+ }, [formVisible, navigation])
123
122
 
124
123
  if (status === 'pending') {
125
124
  return (