@planningcenter/chat-react-native 3.42.3-qa-staging.6 → 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.
- package/build/components/conversation/messages_disabled_banners.d.ts +3 -2
- package/build/components/conversation/messages_disabled_banners.d.ts.map +1 -1
- package/build/components/conversation/messages_disabled_banners.js +7 -2
- package/build/components/conversation/messages_disabled_banners.js.map +1 -1
- package/build/components/display/adult_requirement_notice.js +1 -1
- package/build/components/display/adult_requirement_notice.js.map +1 -1
- package/build/components/display/platform_modal_header_buttons.d.ts +7 -29
- package/build/components/display/platform_modal_header_buttons.d.ts.map +1 -1
- package/build/components/display/platform_modal_header_buttons.js +24 -76
- package/build/components/display/platform_modal_header_buttons.js.map +1 -1
- package/build/hooks/index.d.ts +1 -1
- package/build/hooks/index.d.ts.map +1 -1
- package/build/hooks/index.js +1 -1
- package/build/hooks/index.js.map +1 -1
- package/build/hooks/use_conversation.d.ts.map +1 -1
- package/build/hooks/use_conversation.js +0 -1
- package/build/hooks/use_conversation.js.map +1 -1
- package/build/hooks/use_conversation_jolt_events.d.ts.map +1 -1
- package/build/hooks/use_conversation_jolt_events.js +2 -10
- package/build/hooks/use_conversation_jolt_events.js.map +1 -1
- package/build/hooks/use_current_person.d.ts.map +1 -1
- package/build/hooks/use_current_person.js +0 -1
- package/build/hooks/use_current_person.js.map +1 -1
- package/build/hooks/use_direct_messages_enabled.d.ts +2 -0
- package/build/hooks/use_direct_messages_enabled.d.ts.map +1 -0
- package/build/hooks/use_direct_messages_enabled.js +16 -0
- package/build/hooks/use_direct_messages_enabled.js.map +1 -0
- package/build/hooks/use_features.d.ts +1 -0
- package/build/hooks/use_features.d.ts.map +1 -1
- package/build/hooks/use_features.js +1 -0
- package/build/hooks/use_features.js.map +1 -1
- package/build/navigation/index.d.ts +84 -50
- package/build/navigation/index.d.ts.map +1 -1
- package/build/navigation/index.js +36 -27
- package/build/navigation/index.js.map +1 -1
- package/build/screens/bug_report_screen.d.ts.map +1 -1
- package/build/screens/bug_report_screen.js +3 -9
- package/build/screens/bug_report_screen.js.map +1 -1
- package/build/screens/conversation_details_screen.d.ts.map +1 -1
- package/build/screens/conversation_details_screen.js +1 -2
- package/build/screens/conversation_details_screen.js.map +1 -1
- package/build/screens/conversation_filters/components/conversation_filters.js +3 -3
- package/build/screens/conversation_filters/components/conversation_filters.js.map +1 -1
- package/build/screens/conversation_filters/components/rows.js +1 -1
- package/build/screens/conversation_filters/components/rows.js.map +1 -1
- package/build/screens/conversation_filters_screen.d.ts.map +1 -1
- package/build/screens/conversation_filters_screen.js +14 -1
- package/build/screens/conversation_filters_screen.js.map +1 -1
- package/build/screens/conversation_screen.d.ts.map +1 -1
- package/build/screens/conversation_screen.js +3 -10
- package/build/screens/conversation_screen.js.map +1 -1
- package/build/screens/conversation_select_recipients/components/groups_recipient_row.js +1 -1
- package/build/screens/conversation_select_recipients/components/groups_recipient_row.js.map +1 -1
- package/build/screens/conversation_select_recipients/components/restricted_group_row.js +1 -1
- package/build/screens/conversation_select_recipients/components/restricted_group_row.js.map +1 -1
- package/build/screens/conversation_select_type_screen.d.ts.map +1 -1
- package/build/screens/conversation_select_type_screen.js +7 -4
- package/build/screens/conversation_select_type_screen.js.map +1 -1
- package/build/screens/conversations/components/list_header_component.js +3 -3
- package/build/screens/conversations/components/list_header_component.js.map +1 -1
- package/build/screens/message_report_screen.js +3 -5
- package/build/screens/message_report_screen.js.map +1 -1
- package/build/screens/notification_settings_screen.d.ts.map +1 -1
- package/build/screens/notification_settings_screen.js +2 -0
- package/build/screens/notification_settings_screen.js.map +1 -1
- package/build/screens/settings_screen.js +3 -3
- package/build/screens/settings_screen.js.map +1 -1
- package/build/types/jolt_events/conversation_events.d.ts +2 -2
- package/build/types/jolt_events/conversation_events.d.ts.map +1 -1
- package/build/types/jolt_events/conversation_events.js.map +1 -1
- package/build/types/resources/conversation.d.ts +2 -2
- package/build/types/resources/conversation.d.ts.map +1 -1
- package/build/types/resources/conversation.js.map +1 -1
- package/build/types/resources/person.d.ts +0 -1
- package/build/types/resources/person.d.ts.map +1 -1
- package/build/types/resources/person.js.map +1 -1
- package/build/utils/conversation_safety_constants.d.ts +1 -1
- package/build/utils/conversation_safety_constants.d.ts.map +1 -1
- package/build/utils/conversation_safety_constants.js +1 -1
- package/build/utils/conversation_safety_constants.js.map +1 -1
- package/build/utils/request/conversation.d.ts.map +1 -1
- package/build/utils/request/conversation.js +0 -1
- package/build/utils/request/conversation.js.map +1 -1
- package/package.json +4 -4
- package/src/components/conversation/messages_disabled_banners.tsx +11 -3
- package/src/components/display/adult_requirement_notice.tsx +1 -1
- package/src/components/display/platform_modal_header_buttons.tsx +50 -163
- package/src/hooks/index.ts +1 -1
- package/src/hooks/use_conversation.ts +0 -1
- package/src/hooks/use_conversation_jolt_events.ts +3 -16
- package/src/hooks/use_current_person.ts +0 -1
- package/src/hooks/use_direct_messages_enabled.ts +23 -0
- package/src/hooks/use_features.ts +1 -0
- package/src/navigation/index.tsx +42 -36
- package/src/screens/bug_report_screen.tsx +3 -9
- package/src/screens/conversation_details_screen.tsx +1 -2
- package/src/screens/conversation_filters/components/conversation_filters.tsx +3 -3
- package/src/screens/conversation_filters/components/rows.tsx +2 -2
- package/src/screens/conversation_filters_screen.tsx +21 -1
- package/src/screens/conversation_screen.tsx +3 -11
- package/src/screens/conversation_select_recipients/components/groups_recipient_row.tsx +1 -1
- package/src/screens/conversation_select_recipients/components/restricted_group_row.tsx +2 -2
- package/src/screens/conversation_select_type_screen.tsx +7 -4
- package/src/screens/conversations/components/list_header_component.tsx +3 -3
- package/src/screens/message_report_screen.tsx +3 -5
- package/src/screens/notification_settings_screen.tsx +4 -1
- package/src/screens/settings_screen.tsx +3 -3
- package/src/types/jolt_events/conversation_events.ts +2 -2
- package/src/types/resources/conversation.ts +3 -2
- package/src/types/resources/person.ts +0 -1
- package/src/utils/conversation_safety_constants.ts +1 -1
- package/src/utils/request/conversation.ts +0 -1
- package/build/hooks/use_direct_messages_eligible.d.ts +0 -2
- package/build/hooks/use_direct_messages_eligible.d.ts.map +0 -1
- package/build/hooks/use_direct_messages_eligible.js +0 -9
- package/build/hooks/use_direct_messages_eligible.js.map +0 -1
- package/src/__tests__/hooks/use_direct_messages_eligible.test.tsx +0 -77
- package/src/hooks/use_direct_messages_eligible.ts +0 -11
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { StyleSheet, View, type ViewStyle } from 'react-native'
|
|
2
2
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
3
3
|
import { useTheme } from '../../hooks'
|
|
4
|
+
import type { ConversationDisabledReason } from '../../types'
|
|
5
|
+
import { ADULT_REQUIREMENT_BODY } from '../../utils'
|
|
4
6
|
import { Button, Text } from '../display'
|
|
5
7
|
|
|
6
8
|
export const LeaderMessagesDisabledBanner = () => {
|
|
@@ -20,19 +22,25 @@ export const MemberMessagesDisabledBanner = () => {
|
|
|
20
22
|
)
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
const DISABLED_REASON_TEXT: Record<ConversationDisabledReason, string> = {
|
|
26
|
+
safety_check: ADULT_REQUIREMENT_BODY,
|
|
27
|
+
direct_messages_disabled: 'Direct messages have been turned off by someone in this conversation.',
|
|
28
|
+
}
|
|
29
|
+
|
|
23
30
|
const DEFAULT_DISABLED_TEXT = 'This conversation has been disabled.'
|
|
24
31
|
|
|
25
32
|
interface ConversationDisabledBannerProps {
|
|
26
|
-
|
|
33
|
+
disabledReason?: ConversationDisabledReason
|
|
27
34
|
messageLink?: { label: string; onPress: () => void }
|
|
28
35
|
}
|
|
29
36
|
|
|
30
37
|
export const ConversationDisabledBanner = ({
|
|
31
|
-
|
|
38
|
+
disabledReason,
|
|
32
39
|
messageLink,
|
|
33
40
|
}: ConversationDisabledBannerProps) => {
|
|
34
41
|
const styles = useStyles()
|
|
35
|
-
const description =
|
|
42
|
+
const description =
|
|
43
|
+
(disabledReason && DISABLED_REASON_TEXT[disabledReason]) ?? DEFAULT_DISABLED_TEXT
|
|
36
44
|
|
|
37
45
|
return (
|
|
38
46
|
<MessagesDisabledBanner
|
|
@@ -13,7 +13,7 @@ export function AdultRequirementNotice({ style }: AdultRequirementNoticeProps) {
|
|
|
13
13
|
<BannerCollapsible
|
|
14
14
|
appearance="warning"
|
|
15
15
|
iconName="general.shieldExclamation"
|
|
16
|
-
heading="
|
|
16
|
+
heading="2 adults required to chat"
|
|
17
17
|
style={style}
|
|
18
18
|
>
|
|
19
19
|
<BannerPrimitive.Text>{ADULT_REQUIREMENT_BODY}</BannerPrimitive.Text>
|
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
import { HeaderBackButton, HeaderButton
|
|
1
|
+
import { HeaderBackButton, HeaderButton } from '@react-navigation/elements'
|
|
2
2
|
import { useNavigation } from '@react-navigation/native'
|
|
3
3
|
import type {
|
|
4
|
-
|
|
4
|
+
NativeStackHeaderLeftProps,
|
|
5
|
+
NativeStackHeaderRightProps,
|
|
5
6
|
NativeStackNavigationOptions,
|
|
6
7
|
} from '@react-navigation/native-stack'
|
|
7
8
|
import { useLayoutEffect } from 'react'
|
|
8
9
|
import { ColorValue, Platform, StyleSheet } from 'react-native'
|
|
9
|
-
import
|
|
10
|
-
import { useFontScale, useTheme } from '../../hooks'
|
|
11
|
-
import { MAX_FONT_SIZE_MULTIPLIER_LANDMARK } from '../../utils'
|
|
10
|
+
import { useTheme } from '../../hooks'
|
|
12
11
|
import { isLiquidGlassEnabled } from '../../utils/liquid_glass'
|
|
13
|
-
import {
|
|
14
|
-
import { Icon, type IconString } from './icon'
|
|
12
|
+
import { Icon } from './icon'
|
|
15
13
|
import { TextButton } from './text_button'
|
|
16
|
-
import { getColorKey, useGradientColorMap } from './utils/button_colors'
|
|
17
14
|
|
|
18
15
|
interface HeaderTextButtonProps {
|
|
19
16
|
onPress: () => void
|
|
@@ -75,10 +72,14 @@ export const HeaderDismissButton = ({
|
|
|
75
72
|
|
|
76
73
|
const createTextHeaderRight =
|
|
77
74
|
({ onPress, title, disabled }: HeaderTextButtonProps) =>
|
|
78
|
-
(props:
|
|
75
|
+
(props: NativeStackHeaderRightProps) => (
|
|
79
76
|
<HeaderTextButton {...props} onPress={onPress} title={title} disabled={disabled} />
|
|
80
77
|
)
|
|
81
78
|
|
|
79
|
+
const createDismissHeaderLeft =
|
|
80
|
+
({ onPress, title }: { onPress: () => void; title: string }) =>
|
|
81
|
+
() => <HeaderDismissButton title={title} onPress={onPress} />
|
|
82
|
+
|
|
82
83
|
export type OptionsDecorator = (
|
|
83
84
|
options: NativeStackNavigationOptions
|
|
84
85
|
) => NativeStackNavigationOptions
|
|
@@ -105,149 +106,42 @@ export const withRightText =
|
|
|
105
106
|
unstable_headerRightItems: () => [{ type: 'button' as const, label: text, onPress, disabled }],
|
|
106
107
|
})
|
|
107
108
|
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
const HEADER_FILL_PADDING = 14
|
|
111
|
-
const HEADER_BAR_INSET = 16
|
|
112
|
-
const HEADER_SLOT_GAP = 12
|
|
113
|
-
|
|
114
|
-
const headerFillSize = (fontScale: number) => {
|
|
115
|
-
const icon = HEADER_FILL_ICON_SIZE * fontScale
|
|
116
|
-
|
|
117
|
-
return { icon, circle: icon + HEADER_FILL_PADDING * 2 }
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Liquid glass icons don't communicate
|
|
121
|
-
export const useHeaderSlotReserve = (): number => {
|
|
122
|
-
const fontScale = useFontScale({ maxFontSizeMultiplier: MAX_FONT_SIZE_MULTIPLIER_LANDMARK })
|
|
123
|
-
|
|
124
|
-
return HEADER_BAR_INSET + headerFillSize(fontScale).circle + HEADER_SLOT_GAP
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const HEADER_FALLBACK_HIT_SLOP = 14
|
|
128
|
-
|
|
129
|
-
const iconStyles = StyleSheet.create({
|
|
130
|
-
disabled: {
|
|
131
|
-
opacity: 0.4,
|
|
132
|
-
},
|
|
133
|
-
fallbackButton: {
|
|
134
|
-
alignItems: 'center',
|
|
135
|
-
justifyContent: 'center',
|
|
136
|
-
paddingHorizontal: 8,
|
|
137
|
-
},
|
|
138
|
-
fillCircle: {
|
|
139
|
-
alignItems: 'center',
|
|
140
|
-
justifyContent: 'center',
|
|
141
|
-
},
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
export interface HeaderIconProps {
|
|
145
|
-
icon: IconString
|
|
146
|
-
accessibilityLabel: string
|
|
147
|
-
onPress: () => void
|
|
148
|
-
disabled?: boolean
|
|
149
|
-
variant?: 'plain' | 'fill'
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const HeaderIcon = ({
|
|
153
|
-
icon,
|
|
154
|
-
disabled,
|
|
155
|
-
variant,
|
|
156
|
-
}: Pick<HeaderIconProps, 'icon' | 'disabled' | 'variant'>) => {
|
|
157
|
-
const { colors } = useTheme()
|
|
158
|
-
const gradients = useGradientColorMap()
|
|
159
|
-
const fontScale = useFontScale({ maxFontSizeMultiplier: MAX_FONT_SIZE_MULTIPLIER_LANDMARK })
|
|
160
|
-
|
|
161
|
-
if (variant !== 'fill') return <Icon name={icon} size={HEADER_ICON_SIZE} />
|
|
162
|
-
|
|
163
|
-
const { icon: iconSize, circle: circleSize } = headerFillSize(fontScale)
|
|
164
|
-
|
|
165
|
-
return (
|
|
166
|
-
<LinearGradient
|
|
167
|
-
start={{ x: 0.1, y: 0.1 }}
|
|
168
|
-
end={{ x: 0.9, y: 0.9 }}
|
|
169
|
-
colors={gradients[getColorKey({ disabled: Boolean(disabled), appearance: 'interaction' })]}
|
|
170
|
-
style={[
|
|
171
|
-
iconStyles.fillCircle,
|
|
172
|
-
{ borderRadius: circleSize, height: circleSize, width: circleSize },
|
|
173
|
-
]}
|
|
174
|
-
>
|
|
175
|
-
<Icon
|
|
176
|
-
name={icon}
|
|
177
|
-
size={iconSize}
|
|
178
|
-
color={disabled ? colors.iconColorDefaultDisabled : tokens.colorNeutral100White}
|
|
179
|
-
/>
|
|
180
|
-
</LinearGradient>
|
|
181
|
-
)
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Native bar items take SF Symbols, images, or any element, so both paths
|
|
185
|
-
// render the same icon: iOS 26 puts this one in the glass circle, and the
|
|
186
|
-
// element below covers Android and older iOS.
|
|
187
|
-
const createHeaderIconItem = ({
|
|
188
|
-
icon,
|
|
189
|
-
accessibilityLabel,
|
|
190
|
-
onPress,
|
|
191
|
-
disabled,
|
|
192
|
-
variant,
|
|
193
|
-
}: HeaderIconProps) => ({
|
|
194
|
-
type: 'custom' as const,
|
|
195
|
-
// The gradient circle replaces the glass one rather than sitting on it.
|
|
196
|
-
hidesSharedBackground: variant === 'fill',
|
|
197
|
-
element: (
|
|
198
|
-
<PlatformPressable
|
|
199
|
-
onPress={onPress}
|
|
200
|
-
disabled={disabled}
|
|
201
|
-
hitSlop={12}
|
|
202
|
-
accessibilityRole="button"
|
|
203
|
-
accessibilityLabel={accessibilityLabel}
|
|
204
|
-
style={disabled && variant !== 'fill' && iconStyles.disabled}
|
|
205
|
-
>
|
|
206
|
-
<HeaderIcon icon={icon} disabled={disabled} variant={variant} />
|
|
207
|
-
</PlatformPressable>
|
|
208
|
-
),
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
const createHeaderIconElement =
|
|
212
|
-
({ icon, accessibilityLabel, onPress, disabled, variant }: HeaderIconProps) =>
|
|
213
|
-
() => (
|
|
214
|
-
<PlatformPressable
|
|
215
|
-
onPress={onPress}
|
|
216
|
-
disabled={disabled}
|
|
217
|
-
hitSlop={HEADER_FALLBACK_HIT_SLOP}
|
|
218
|
-
accessibilityRole="button"
|
|
219
|
-
accessibilityLabel={accessibilityLabel}
|
|
220
|
-
style={[iconStyles.fallbackButton, disabled && variant !== 'fill' && iconStyles.disabled]}
|
|
221
|
-
>
|
|
222
|
-
<HeaderIcon icon={icon} disabled={disabled} variant={variant} />
|
|
223
|
-
</PlatformPressable>
|
|
224
|
-
)
|
|
225
|
-
|
|
226
|
-
export const withRightIcon =
|
|
227
|
-
(props: HeaderIconProps): OptionsDecorator =>
|
|
109
|
+
export const withLeftText =
|
|
110
|
+
({ text, onPress }: TextButtonDecoratorProps): OptionsDecorator =>
|
|
228
111
|
options => ({
|
|
229
112
|
...options,
|
|
230
|
-
|
|
231
|
-
|
|
113
|
+
headerLeft: createDismissHeaderLeft({ title: text, onPress }),
|
|
114
|
+
unstable_headerLeftItems: () => [{ type: 'button' as const, label: text, onPress }],
|
|
232
115
|
})
|
|
233
116
|
|
|
234
|
-
|
|
235
|
-
|
|
117
|
+
const renderCloseImage = ({ tintColor }: { tintColor: string }) => (
|
|
118
|
+
<Icon name="general.x" size={18} color={tintColor} />
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
const createCloseHeaderRight =
|
|
122
|
+
({ onPress }: { onPress: () => void }) =>
|
|
123
|
+
(props: NativeStackHeaderRightProps) => (
|
|
124
|
+
<HeaderBackButton backImage={renderCloseImage} onPress={onPress} {...props} />
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
// The native slot takes only SF Symbols or bundled rasters — we have no way
|
|
128
|
+
// to systematize rasterizing our SVG icon library, and xmark matches
|
|
129
|
+
// general.x closely. The fallback keeps our icon.
|
|
130
|
+
export const withRightClose =
|
|
131
|
+
({ onPress }: { onPress: () => void }): OptionsDecorator =>
|
|
236
132
|
options => ({
|
|
237
133
|
...options,
|
|
238
|
-
|
|
239
|
-
|
|
134
|
+
headerRight: createCloseHeaderRight({ onPress }),
|
|
135
|
+
unstable_headerRightItems: () => [
|
|
136
|
+
{
|
|
137
|
+
type: 'button' as const,
|
|
138
|
+
label: 'Close',
|
|
139
|
+
icon: { type: 'sfSymbol' as const, name: 'xmark' as const },
|
|
140
|
+
onPress,
|
|
141
|
+
},
|
|
142
|
+
],
|
|
240
143
|
})
|
|
241
144
|
|
|
242
|
-
export const withRightClose = ({ onPress }: { onPress: () => void }) =>
|
|
243
|
-
withRightIcon({ icon: 'general.x', accessibilityLabel: 'Close', onPress })
|
|
244
|
-
|
|
245
|
-
export const withLeftClose = ({ onPress }: { onPress: () => void }) =>
|
|
246
|
-
withLeftIcon({ icon: 'general.x', accessibilityLabel: 'Close', onPress })
|
|
247
|
-
|
|
248
|
-
export const withRightDone = ({ onPress }: { onPress: () => void }) =>
|
|
249
|
-
withRightIcon({ icon: 'general.check', accessibilityLabel: 'Done', onPress })
|
|
250
|
-
|
|
251
145
|
// The default back icon reserves trailing space for a label that
|
|
252
146
|
// displayMode="minimal" never shows; a custom image skips it so the glass
|
|
253
147
|
// capsule hugs the glyph.
|
|
@@ -259,7 +153,7 @@ const renderGlassBackImage = ({ tintColor }: { tintColor: string }) => (
|
|
|
259
153
|
// the button against the capsule edge.
|
|
260
154
|
export const createMinimalHeaderBackButton =
|
|
261
155
|
({ onPress }: { onPress: () => void }) =>
|
|
262
|
-
(props:
|
|
156
|
+
(props: NativeStackHeaderLeftProps) => (
|
|
263
157
|
<HeaderBackButton
|
|
264
158
|
style={
|
|
265
159
|
isLiquidGlassEnabled()
|
|
@@ -285,25 +179,22 @@ export const withMinimalBack =
|
|
|
285
179
|
...(isLiquidGlassEnabled() ? {} : { headerLeft: createMinimalHeaderBackButton({ onPress }) }),
|
|
286
180
|
})
|
|
287
181
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
type UseHeaderRightOptions = {
|
|
182
|
+
interface UseHeaderRightOptions {
|
|
183
|
+
title: string
|
|
291
184
|
onPress: () => void
|
|
292
185
|
disabled?: boolean
|
|
293
186
|
enabled?: boolean
|
|
294
|
-
}
|
|
295
|
-
| (HeaderIconProps & { title?: never })
|
|
296
|
-
| { title: string; icon?: never; accessibilityLabel?: never; variant?: never }
|
|
297
|
-
)
|
|
187
|
+
}
|
|
298
188
|
|
|
299
189
|
// For screens whose header button depends on live state (form validity,
|
|
300
190
|
// mutation callbacks). Static buttons belong in the navigator options.
|
|
301
|
-
export const useHeaderRight = (
|
|
191
|
+
export const useHeaderRight = ({
|
|
192
|
+
title,
|
|
193
|
+
onPress,
|
|
194
|
+
disabled,
|
|
195
|
+
enabled = true,
|
|
196
|
+
}: UseHeaderRightOptions) => {
|
|
302
197
|
const navigation = useNavigation()
|
|
303
|
-
const { onPress, disabled, enabled = true } = options
|
|
304
|
-
const icon = options.icon
|
|
305
|
-
const label = options.icon !== undefined ? options.accessibilityLabel : options.title
|
|
306
|
-
const variant = options.variant
|
|
307
198
|
|
|
308
199
|
useLayoutEffect(() => {
|
|
309
200
|
if (!enabled) {
|
|
@@ -311,12 +202,8 @@ export const useHeaderRight = (options: UseHeaderRightOptions) => {
|
|
|
311
202
|
return
|
|
312
203
|
}
|
|
313
204
|
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
: withRightText({ text: label, onPress, disabled })
|
|
317
|
-
|
|
318
|
-
navigation.setOptions(decorate({}))
|
|
319
|
-
}, [disabled, enabled, icon, label, navigation, onPress, variant])
|
|
205
|
+
navigation.setOptions(withRightText({ text: title, onPress, disabled })({}))
|
|
206
|
+
}, [disabled, enabled, navigation, onPress, title])
|
|
320
207
|
}
|
|
321
208
|
|
|
322
209
|
const useStyles = () => {
|
package/src/hooks/index.ts
CHANGED
|
@@ -8,7 +8,7 @@ export * from './use_chat_permissions'
|
|
|
8
8
|
export * from './use_create_android_ripple_color'
|
|
9
9
|
export * from './use_current_person'
|
|
10
10
|
export * from './use_deleting_ids'
|
|
11
|
-
export * from './
|
|
11
|
+
export * from './use_direct_messages_enabled'
|
|
12
12
|
export * from './use_font_scale'
|
|
13
13
|
export * from './use_has_direct_messages'
|
|
14
14
|
export * from './use_groups_groups'
|
|
@@ -26,13 +26,8 @@ export function useConversationJoltEvents({ conversationId }: Props) {
|
|
|
26
26
|
|
|
27
27
|
const handleUpdatedConversation = useCallback(
|
|
28
28
|
(e: JoltConversationEvent) => {
|
|
29
|
-
const {
|
|
30
|
-
|
|
31
|
-
last_message_author_id,
|
|
32
|
-
disabled,
|
|
33
|
-
disabled_reason,
|
|
34
|
-
disabled_message,
|
|
35
|
-
} = e.data.data
|
|
29
|
+
const { last_message_idempotent_key, last_message_author_id, disabled, disabled_reason } =
|
|
30
|
+
e.data.data
|
|
36
31
|
|
|
37
32
|
if (last_message_idempotent_key && last_message_author_id === currentPersonId) {
|
|
38
33
|
completeMessageCreationConversationTracking({
|
|
@@ -43,15 +38,7 @@ export function useConversationJoltEvents({ conversationId }: Props) {
|
|
|
43
38
|
|
|
44
39
|
queryClient.setQueryData<ApiResource<ConversationResource>>(queryKey, prev => {
|
|
45
40
|
if (!prev?.data) return prev
|
|
46
|
-
return {
|
|
47
|
-
...prev,
|
|
48
|
-
data: {
|
|
49
|
-
...prev.data,
|
|
50
|
-
disabled,
|
|
51
|
-
disabledReason: disabled_reason,
|
|
52
|
-
disabledMessage: disabled_message,
|
|
53
|
-
},
|
|
54
|
-
}
|
|
41
|
+
return { ...prev, data: { ...prev.data, disabled, disabledReason: disabled_reason } }
|
|
55
42
|
})
|
|
56
43
|
|
|
57
44
|
queryClient.invalidateQueries({ queryKey })
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useQuery } from '@tanstack/react-query'
|
|
2
|
+
import { ApiCollection } from '../types'
|
|
3
|
+
import type { FeatureResource } from '../types/resources/feature_resource'
|
|
4
|
+
import { getFeaturesRequestArgs, getFeaturesQueryKey } from '../utils/request/get_features'
|
|
5
|
+
import { useApiClient } from './use_api_client'
|
|
6
|
+
import { availableFeatures } from './use_features'
|
|
7
|
+
|
|
8
|
+
export function useDirectMessagesEnabled(): boolean {
|
|
9
|
+
const apiClient = useApiClient()
|
|
10
|
+
const { data, isFetched } = useQuery<ApiCollection<FeatureResource>>({
|
|
11
|
+
queryKey: getFeaturesQueryKey(),
|
|
12
|
+
queryFn: () => apiClient.chat.get<ApiCollection<FeatureResource>>(getFeaturesRequestArgs()),
|
|
13
|
+
staleTime: 1000 * 60 * 5,
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
if (!isFetched) return false
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
data?.data.some(
|
|
20
|
+
feature => feature.name === availableFeatures.direct_messages_eap && feature.enabled
|
|
21
|
+
) ?? false
|
|
22
|
+
)
|
|
23
|
+
}
|
|
@@ -43,6 +43,7 @@ export const availableFeatures = {
|
|
|
43
43
|
custom_conversation_avatars: 'ROLLOUT_custom_conversation_avatars',
|
|
44
44
|
conversation_safety_lock: 'ROLLOUT_conversation_safety_lock',
|
|
45
45
|
video_moderation: 'ROLLOUT_MOBILE_video_moderation',
|
|
46
|
+
direct_messages_eap: 'ROLLOUT_direct_messages_eap',
|
|
46
47
|
dm_search_clear_fix: 'ROLLOUT_MOBILE_dm_search_clear_fix',
|
|
47
48
|
} as const satisfies Record<string, `ROLLOUT_${string}`>
|
|
48
49
|
|
package/src/navigation/index.tsx
CHANGED
|
@@ -2,7 +2,6 @@ import { StaticParamList } from '@react-navigation/native'
|
|
|
2
2
|
import {
|
|
3
3
|
createNativeStackNavigator,
|
|
4
4
|
NativeStackHeaderRightProps,
|
|
5
|
-
NativeStackNavigationOptions,
|
|
6
5
|
} from '@react-navigation/native-stack'
|
|
7
6
|
import { CardStyleInterpolators } from '@react-navigation/stack'
|
|
8
7
|
import React from 'react'
|
|
@@ -11,9 +10,9 @@ import {
|
|
|
11
10
|
createMinimalHeaderBackButton,
|
|
12
11
|
withMinimalBack,
|
|
13
12
|
withRightClose,
|
|
14
|
-
|
|
13
|
+
withRightText,
|
|
15
14
|
} from '../components/display/platform_modal_header_buttons'
|
|
16
|
-
import {
|
|
15
|
+
import { useDirectMessagesEnabled, useQualifiedByAge } from '../hooks'
|
|
17
16
|
import {
|
|
18
17
|
AttachmentActionsScreen,
|
|
19
18
|
AttachmentActionsScreenOptions,
|
|
@@ -80,17 +79,13 @@ import {
|
|
|
80
79
|
SystemMessagePeopleScreenOptions,
|
|
81
80
|
} from '../screens/system_message_people_screen'
|
|
82
81
|
import { TeamConversationScreen } from '../screens/team_conversation_screen'
|
|
83
|
-
import { isLiquidGlassEnabled } from '../utils/liquid_glass'
|
|
84
82
|
import { ScreenLayoutWithChatAccessGate } from './screenLayout'
|
|
85
83
|
|
|
86
|
-
const globalScreenOptions: () => NativeStackNavigationOptions = () => ({
|
|
87
|
-
headerBackButtonDisplayMode: 'minimal',
|
|
88
|
-
...(isLiquidGlassEnabled() ? { headerShadowVisible: false } : {}),
|
|
89
|
-
})
|
|
90
|
-
|
|
91
84
|
export const NewConversationStack = createNativeStackNavigator({
|
|
92
85
|
initialRouteName: 'ConversationNewEntry',
|
|
93
|
-
screenOptions:
|
|
86
|
+
screenOptions: {
|
|
87
|
+
headerBackButtonDisplayMode: 'minimal',
|
|
88
|
+
},
|
|
94
89
|
screenLayout: ScreenLayoutWithChatAccessGate,
|
|
95
90
|
screens: {
|
|
96
91
|
ConversationNewEntry: {
|
|
@@ -101,7 +96,7 @@ export const NewConversationStack = createNativeStackNavigator({
|
|
|
101
96
|
title: 'New conversation',
|
|
102
97
|
headerBackVisible: false,
|
|
103
98
|
},
|
|
104
|
-
|
|
99
|
+
withRightText({ text: 'Cancel', onPress: navigation.goBack })
|
|
105
100
|
),
|
|
106
101
|
},
|
|
107
102
|
ConversationSelectGroupRecipients: {
|
|
@@ -111,7 +106,8 @@ export const NewConversationStack = createNativeStackNavigator({
|
|
|
111
106
|
{
|
|
112
107
|
title: 'New conversation',
|
|
113
108
|
},
|
|
114
|
-
|
|
109
|
+
withRightText({
|
|
110
|
+
text: 'Cancel',
|
|
115
111
|
onPress: () => navigation.popTo('Conversations', route.params),
|
|
116
112
|
})
|
|
117
113
|
),
|
|
@@ -123,20 +119,22 @@ export const NewConversationStack = createNativeStackNavigator({
|
|
|
123
119
|
{
|
|
124
120
|
title: 'New conversation',
|
|
125
121
|
},
|
|
126
|
-
|
|
122
|
+
withRightText({
|
|
123
|
+
text: 'Cancel',
|
|
127
124
|
onPress: () => navigation.popTo('Conversations', route.params),
|
|
128
125
|
})
|
|
129
126
|
),
|
|
130
127
|
},
|
|
131
128
|
ConversationSelectDirectMessageRecipients: {
|
|
132
129
|
screen: ConversationSelectDirectMessageRecipientsScreen,
|
|
133
|
-
if:
|
|
130
|
+
if: useDirectMessagesEnabled,
|
|
134
131
|
options: ({ navigation, route }) =>
|
|
135
132
|
composeOptions(
|
|
136
133
|
{
|
|
137
134
|
title: 'New conversation',
|
|
138
135
|
},
|
|
139
|
-
|
|
136
|
+
withRightText({
|
|
137
|
+
text: 'Cancel',
|
|
140
138
|
onPress: () => navigation.popTo('Conversations', route.params),
|
|
141
139
|
})
|
|
142
140
|
),
|
|
@@ -153,7 +151,8 @@ export const NewConversationStack = createNativeStackNavigator({
|
|
|
153
151
|
title: 'New conversation',
|
|
154
152
|
headerLeft: () => null,
|
|
155
153
|
},
|
|
156
|
-
|
|
154
|
+
withRightText({
|
|
155
|
+
text: 'Cancel',
|
|
157
156
|
onPress: () => {
|
|
158
157
|
const { chat_group_graph_id, group_source_app_name } =
|
|
159
158
|
route.params as ConversationSelectRecipientsParams
|
|
@@ -170,7 +169,9 @@ export const NewConversationStack = createNativeStackNavigator({
|
|
|
170
169
|
})
|
|
171
170
|
|
|
172
171
|
export const ChatStack = createNativeStackNavigator({
|
|
173
|
-
screenOptions:
|
|
172
|
+
screenOptions: {
|
|
173
|
+
headerBackButtonDisplayMode: 'minimal',
|
|
174
|
+
},
|
|
174
175
|
screenLayout: ScreenLayoutWithChatAccessGate,
|
|
175
176
|
screens: {
|
|
176
177
|
Conversations: {
|
|
@@ -250,7 +251,7 @@ export const ChatStack = createNativeStackNavigator({
|
|
|
250
251
|
presentation: 'modal',
|
|
251
252
|
title: 'Conversation details',
|
|
252
253
|
},
|
|
253
|
-
|
|
254
|
+
withRightText({ text: 'Done', onPress: navigation.goBack })
|
|
254
255
|
),
|
|
255
256
|
},
|
|
256
257
|
AvatarPicker: {
|
|
@@ -260,41 +261,46 @@ export const ChatStack = createNativeStackNavigator({
|
|
|
260
261
|
ChatSettings: {
|
|
261
262
|
screen: SettingsScreen,
|
|
262
263
|
options: ({ navigation }) =>
|
|
263
|
-
composeOptions(
|
|
264
|
+
composeOptions(
|
|
265
|
+
{
|
|
266
|
+
title: 'Chat settings',
|
|
267
|
+
headerBackVisible: false,
|
|
268
|
+
},
|
|
269
|
+
withRightText({ text: 'Done', onPress: navigation.goBack })
|
|
270
|
+
),
|
|
264
271
|
},
|
|
265
272
|
NotificationSettings: {
|
|
266
273
|
screen: NotificationSettingsScreen,
|
|
267
274
|
options: ({ navigation }) =>
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
:
|
|
274
|
-
|
|
275
|
-
withMinimalBack({ onPress: navigation.goBack })
|
|
276
|
-
),
|
|
275
|
+
composeOptions(
|
|
276
|
+
{
|
|
277
|
+
title: 'Chat',
|
|
278
|
+
headerBackVisible: false,
|
|
279
|
+
},
|
|
280
|
+
withRightText({ text: 'Done', onPress: navigation.goBack })
|
|
281
|
+
),
|
|
277
282
|
},
|
|
278
283
|
PreferredAppSelection: {
|
|
279
284
|
screen: PreferredAppSelectionScreen,
|
|
280
|
-
options:
|
|
281
|
-
|
|
285
|
+
options: {
|
|
286
|
+
title: 'Preferred app',
|
|
287
|
+
},
|
|
282
288
|
},
|
|
283
289
|
GroupNotificationSettings: {
|
|
284
290
|
screen: GroupNotificationSettingsScreen,
|
|
285
291
|
options: ({ route, navigation }) => {
|
|
286
292
|
const title = (route.params as { title?: string })?.title || 'Group Settings'
|
|
287
293
|
|
|
288
|
-
//
|
|
289
|
-
//
|
|
294
|
+
// Done only shows when this is the entry screen (e.g. deep links);
|
|
295
|
+
// deeper in the stack the back button covers dismissal.
|
|
290
296
|
return navigation.getState().index === 0
|
|
291
297
|
? composeOptions(
|
|
292
298
|
{
|
|
293
299
|
title,
|
|
294
300
|
},
|
|
295
|
-
|
|
301
|
+
withRightText({ text: 'Done', onPress: navigation.goBack })
|
|
296
302
|
)
|
|
297
|
-
:
|
|
303
|
+
: { title }
|
|
298
304
|
},
|
|
299
305
|
},
|
|
300
306
|
GroupNotificationLevelSelect: {
|
|
@@ -322,7 +328,7 @@ export const ChatStack = createNativeStackNavigator({
|
|
|
322
328
|
title: 'New conversation',
|
|
323
329
|
headerLeft: () => null,
|
|
324
330
|
},
|
|
325
|
-
|
|
331
|
+
withRightText({ text: 'Cancel', onPress: () => navigation.getParent()?.goBack() })
|
|
326
332
|
),
|
|
327
333
|
},
|
|
328
334
|
SendGiphy: {
|
|
@@ -371,7 +377,7 @@ export const ChatStack = createNativeStackNavigator({
|
|
|
371
377
|
headerBackVisible: false,
|
|
372
378
|
presentation: 'modal',
|
|
373
379
|
},
|
|
374
|
-
|
|
380
|
+
withRightText({ text: 'Close', onPress: navigation.goBack })
|
|
375
381
|
),
|
|
376
382
|
},
|
|
377
383
|
NotFound: {
|
|
@@ -28,7 +28,7 @@ import {
|
|
|
28
28
|
composeOptions,
|
|
29
29
|
HeaderDismissButton,
|
|
30
30
|
useHeaderRight,
|
|
31
|
-
|
|
31
|
+
withLeftText,
|
|
32
32
|
} from '../components/display/platform_modal_header_buttons'
|
|
33
33
|
import { VideoAttachmentPreview } from '../components/display/video_attachment_preview'
|
|
34
34
|
import { DefaultLoading } from '../components/page/loading'
|
|
@@ -60,7 +60,7 @@ export const BugReportScreenOptions = ({
|
|
|
60
60
|
}: NativeStackScreenProps<any>): NativeStackNavigationOptions =>
|
|
61
61
|
composeOptions(
|
|
62
62
|
{ presentation: 'modal', title: 'Report a bug' },
|
|
63
|
-
|
|
63
|
+
withLeftText({ text: 'Cancel', onPress: () => navigation.goBack() })
|
|
64
64
|
)
|
|
65
65
|
|
|
66
66
|
interface Attachment {
|
|
@@ -158,13 +158,7 @@ export function BugReportScreen() {
|
|
|
158
158
|
return ImagePicker.openImageLibraryAsync().then(result => uploadImagePickerResult(result))
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
useHeaderRight({
|
|
162
|
-
icon: 'general.message',
|
|
163
|
-
accessibilityLabel: 'Submit',
|
|
164
|
-
variant: 'fill',
|
|
165
|
-
onPress: handleSubmit,
|
|
166
|
-
disabled: !formValid,
|
|
167
|
-
})
|
|
161
|
+
useHeaderRight({ title: 'Submit', onPress: handleSubmit, disabled: !formValid })
|
|
168
162
|
|
|
169
163
|
if (status === 'pending') {
|
|
170
164
|
return (
|
|
@@ -223,8 +223,7 @@ export function ConversationDetailsScreen({ route }: ConversationDetailsScreenPr
|
|
|
223
223
|
)
|
|
224
224
|
|
|
225
225
|
useHeaderRight({
|
|
226
|
-
|
|
227
|
-
accessibilityLabel: 'Done',
|
|
226
|
+
title: 'Done',
|
|
228
227
|
onPress: () => {
|
|
229
228
|
saveTitle({ title: trimmedTitle || conversation.title })
|
|
230
229
|
navigation.goBack()
|
|
@@ -4,7 +4,7 @@ import { FlatList } from 'react-native-gesture-handler'
|
|
|
4
4
|
import { Heading } from '../../../components'
|
|
5
5
|
import {
|
|
6
6
|
useAtFontScaleBreakpoint,
|
|
7
|
-
|
|
7
|
+
useDirectMessagesEnabled,
|
|
8
8
|
useHasDirectMessages,
|
|
9
9
|
useTheme,
|
|
10
10
|
} from '../../../hooks'
|
|
@@ -59,9 +59,9 @@ export const ConversationFilters = () => {
|
|
|
59
59
|
const { setScrollOffset, params } = useContext(FilterContext)
|
|
60
60
|
const { chat_group_graph_id, group_source_app_name = '', show_direct_messages } = params
|
|
61
61
|
|
|
62
|
-
const
|
|
62
|
+
const directMessagesEnabled = useDirectMessagesEnabled()
|
|
63
63
|
const hasDirectMessages = useHasDirectMessages()
|
|
64
|
-
const canFilterByDMs =
|
|
64
|
+
const canFilterByDMs = directMessagesEnabled && hasDirectMessages
|
|
65
65
|
|
|
66
66
|
const activeFilter: FilterTypes = useMemo(() => {
|
|
67
67
|
if (chat_group_graph_id) {
|