@planningcenter/chat-react-native 3.18.0-rc.3 → 3.18.0-rc.5
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/message.d.ts +2 -1
- package/build/components/conversation/message.d.ts.map +1 -1
- package/build/components/conversation/message.js +5 -5
- package/build/components/conversation/message.js.map +1 -1
- package/build/components/conversation/message_form.js +5 -2
- package/build/components/conversation/message_form.js.map +1 -1
- package/build/components/conversation/reply_connectors.d.ts.map +1 -1
- package/build/components/conversation/reply_connectors.js +0 -5
- package/build/components/conversation/reply_connectors.js.map +1 -1
- package/build/components/conversations/conversation_actions.d.ts.map +1 -1
- package/build/components/conversations/conversation_actions.js +1 -2
- package/build/components/conversations/conversation_actions.js.map +1 -1
- package/build/hooks/use_features.d.ts +9 -0
- package/build/hooks/use_features.d.ts.map +1 -0
- package/build/hooks/use_features.js +35 -0
- package/build/hooks/use_features.js.map +1 -0
- package/build/screens/conversation_screen.d.ts +2 -1
- package/build/screens/conversation_screen.d.ts.map +1 -1
- package/build/screens/conversation_screen.js +12 -6
- package/build/screens/conversation_screen.js.map +1 -1
- package/build/screens/message_actions_screen.js +4 -2
- package/build/screens/message_actions_screen.js.map +1 -1
- package/build/types/resources/feature_resource.d.ts +7 -0
- package/build/types/resources/feature_resource.d.ts.map +1 -0
- package/build/types/resources/feature_resource.js +2 -0
- package/build/types/resources/feature_resource.js.map +1 -0
- package/build/utils/index.d.ts +0 -1
- package/build/utils/index.d.ts.map +1 -1
- package/build/utils/index.js +0 -1
- package/build/utils/index.js.map +1 -1
- package/build/utils/request/get_features.d.ts +11 -0
- package/build/utils/request/get_features.d.ts.map +1 -0
- package/build/utils/request/get_features.js +18 -0
- package/build/utils/request/get_features.js.map +1 -0
- package/package.json +2 -2
- package/src/components/conversation/message.tsx +8 -4
- package/src/components/conversation/message_form.tsx +4 -2
- package/src/components/conversation/reply_connectors.tsx +0 -3
- package/src/components/conversations/conversation_actions.tsx +1 -1
- package/src/hooks/use_features.ts +47 -0
- package/src/screens/conversation_screen.tsx +17 -5
- package/src/screens/message_actions_screen.tsx +4 -2
- package/src/types/resources/feature_resource.ts +6 -0
- package/src/utils/index.ts +0 -1
- package/src/utils/request/get_features.ts +20 -0
- package/build/utils/replies_local_feature_flag.d.ts +0 -2
- package/build/utils/replies_local_feature_flag.d.ts.map +0 -1
- package/build/utils/replies_local_feature_flag.js +0 -3
- package/build/utils/replies_local_feature_flag.js.map +0 -1
- package/src/utils/replies_local_feature_flag.ts +0 -2
|
@@ -25,7 +25,7 @@ import { MessageReadReceipts } from './message_read_receipts'
|
|
|
25
25
|
import { isNewMessage, useMessageCreateOrUpdate } from '../../hooks/use_message_create_or_update'
|
|
26
26
|
import { Haptic } from '../../utils/native_adapters'
|
|
27
27
|
import { TheirReplyConnector, MyReplyConnector, AVATAR_CONNECTOR_SPACING } from './reply_connectors'
|
|
28
|
-
import { pluralize
|
|
28
|
+
import { pluralize } from '../../utils'
|
|
29
29
|
import { useConversationMessage } from '../../hooks/use_conversation_message'
|
|
30
30
|
|
|
31
31
|
/** Message
|
|
@@ -36,6 +36,7 @@ interface MessageProps extends MessageResource {
|
|
|
36
36
|
conversation_id: number
|
|
37
37
|
latestReadMessageSortKey?: string
|
|
38
38
|
inReplyScreen?: boolean
|
|
39
|
+
repliesEnabled?: boolean
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
export function Message({
|
|
@@ -43,6 +44,7 @@ export function Message({
|
|
|
43
44
|
conversation_id,
|
|
44
45
|
latestReadMessageSortKey,
|
|
45
46
|
inReplyScreen,
|
|
47
|
+
repliesEnabled,
|
|
46
48
|
...message
|
|
47
49
|
}: MessageProps) {
|
|
48
50
|
const { text, reactionCounts, pending, error } = message
|
|
@@ -73,7 +75,7 @@ export function Message({
|
|
|
73
75
|
|
|
74
76
|
const renderAuthor = (!message.mine && message.renderAuthor) || false
|
|
75
77
|
const showReplyCountButton =
|
|
76
|
-
!inReplyScreen && message.replyRootId === message.id &&
|
|
78
|
+
!inReplyScreen && message.replyRootId === message.id && repliesEnabled
|
|
77
79
|
const isReplyRootMessage = message.replyRootId === message.id
|
|
78
80
|
const isDeletedReplyRootMessage = isReplyRootMessage && !!message.deletedAt
|
|
79
81
|
|
|
@@ -181,7 +183,9 @@ export function Message({
|
|
|
181
183
|
) : (
|
|
182
184
|
<View style={styles.avatarPlaceholder} />
|
|
183
185
|
)}
|
|
184
|
-
|
|
186
|
+
{repliesEnabled && (
|
|
187
|
+
<TheirReplyConnector message={message} messageBubbleHeight={messageBubbleHeight} />
|
|
188
|
+
)}
|
|
185
189
|
</View>
|
|
186
190
|
)}
|
|
187
191
|
<View style={[styles.messageContent, { marginBottom: messageBottomMargin }]}>
|
|
@@ -282,7 +286,7 @@ export function Message({
|
|
|
282
286
|
</View>
|
|
283
287
|
)}
|
|
284
288
|
</View>
|
|
285
|
-
{message.mine && (
|
|
289
|
+
{repliesEnabled && message.mine && (
|
|
286
290
|
<MyReplyConnector message={message} messageBubbleHeight={messageBubbleHeight} />
|
|
287
291
|
)}
|
|
288
292
|
</Animated.View>
|
|
@@ -30,10 +30,10 @@ import { ChatContext } from '../../contexts/chat_context'
|
|
|
30
30
|
import { Haptic, ImagePicker, ImagePickerResult } from '../../utils/native_adapters'
|
|
31
31
|
import {
|
|
32
32
|
MAX_FONT_SIZE_MULTIPLIER_LANDMARK,
|
|
33
|
-
REPLIES_FEATURE_ENABLED,
|
|
34
33
|
platformFontWeightMedium,
|
|
35
34
|
platformPressedOpacityStyle,
|
|
36
35
|
} from '../../utils'
|
|
36
|
+
import { availableFeatures, useFeatures } from '../../hooks/use_features'
|
|
37
37
|
import { useAttachmentUploader } from '../../hooks/use_attachment_uploader'
|
|
38
38
|
import { useMessageDraft } from '../../hooks/use_message_draft'
|
|
39
39
|
import {
|
|
@@ -566,9 +566,11 @@ function EditingIndicator() {
|
|
|
566
566
|
function ReplyIndicator() {
|
|
567
567
|
const { replyRootId, replyRootAuthorFirstName } = React.useContext(MessageFormContext)
|
|
568
568
|
const navigation = useNavigation()
|
|
569
|
+
const { featureEnabled } = useFeatures()
|
|
570
|
+
const repliesEnabled = featureEnabled(availableFeatures.threaded_replies)
|
|
569
571
|
const title = replyRootAuthorFirstName ? `Reply to ${replyRootAuthorFirstName}` : 'Reply'
|
|
570
572
|
|
|
571
|
-
if (!
|
|
573
|
+
if (!repliesEnabled || !replyRootId) return null
|
|
572
574
|
|
|
573
575
|
return (
|
|
574
576
|
<FormIndicatorRow
|
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL,
|
|
7
7
|
MESSAGE_AUTHOR_AVATAR_COLUMN_WIDTH,
|
|
8
8
|
} from '../../utils/styles'
|
|
9
|
-
import { REPLIES_FEATURE_ENABLED } from '../../utils'
|
|
10
9
|
|
|
11
10
|
const MY_REPLY_CONNECTOR_WIDTH = 38
|
|
12
11
|
const CONNECTOR_BORDER_WIDTH = 4
|
|
@@ -22,7 +21,6 @@ export function TheirReplyConnector({ message, messageBubbleHeight }: ReplyConne
|
|
|
22
21
|
const styles = useStyles()
|
|
23
22
|
const { nextRendersAuthor, threadPosition, renderAuthor, isReplyShadowMessage } = message
|
|
24
23
|
|
|
25
|
-
if (!REPLIES_FEATURE_ENABLED) return null
|
|
26
24
|
if (messageBubbleHeight === 0) return null // Prevents UI shifting
|
|
27
25
|
|
|
28
26
|
const connectorMap: Record<string, React.ReactNode | null> = {
|
|
@@ -59,7 +57,6 @@ export function MyReplyConnector({ message, messageBubbleHeight }: ReplyConnecto
|
|
|
59
57
|
const { nextRendersAuthor, threadPosition, prevIsMyReply, nextIsMyReply, isReplyShadowMessage } =
|
|
60
58
|
message
|
|
61
59
|
|
|
62
|
-
if (!REPLIES_FEATURE_ENABLED) return null
|
|
63
60
|
if (messageBubbleHeight === 0) return null // Prevents UI shifting
|
|
64
61
|
|
|
65
62
|
const connectorMap: Record<string, React.ReactNode | null> = {
|
|
@@ -2,6 +2,7 @@ import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'reac
|
|
|
2
2
|
import {
|
|
3
3
|
AccessibilityActionEvent,
|
|
4
4
|
Platform,
|
|
5
|
+
Pressable,
|
|
5
6
|
StyleProp,
|
|
6
7
|
StyleSheet,
|
|
7
8
|
View,
|
|
@@ -10,7 +11,6 @@ import {
|
|
|
10
11
|
import ReanimatedSwipeable, {
|
|
11
12
|
SwipeableMethods,
|
|
12
13
|
} from 'react-native-gesture-handler/ReanimatedSwipeable'
|
|
13
|
-
import { Pressable } from 'react-native-gesture-handler'
|
|
14
14
|
import { useConversationsContext } from '../../contexts/conversations_context'
|
|
15
15
|
import { useTheme, useCreateAndroidRippleColor } from '../../hooks'
|
|
16
16
|
import {
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { useSuspenseQuery } from '@tanstack/react-query'
|
|
2
|
+
import { useCallback } from 'react'
|
|
3
|
+
import { getFeaturesRequestArgs, getFeaturesQueryKey } from '../utils/request/get_features'
|
|
4
|
+
import { useApiClient } from './use_api_client'
|
|
5
|
+
import type { FeatureResource } from '../types/resources/feature_resource'
|
|
6
|
+
import { ApiCollection } from '../types'
|
|
7
|
+
|
|
8
|
+
export function useFeatures() {
|
|
9
|
+
const apiClient = useApiClient()
|
|
10
|
+
const requestArgs = getFeaturesRequestArgs()
|
|
11
|
+
|
|
12
|
+
const { data } = useSuspenseQuery({
|
|
13
|
+
queryKey: getFeaturesQueryKey(),
|
|
14
|
+
queryFn: () => {
|
|
15
|
+
return apiClient.chat
|
|
16
|
+
.get<ApiCollection<FeatureResource>>(requestArgs)
|
|
17
|
+
.catch(() => stableEmptyFeatures)
|
|
18
|
+
},
|
|
19
|
+
staleTime: 1000 * 60 * 5, // 5 minutes
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
const features = data.data
|
|
23
|
+
|
|
24
|
+
const featureEnabled = useCallback(
|
|
25
|
+
(featureName: string) =>
|
|
26
|
+
features.some(feature => feature.name === featureName && feature.enabled),
|
|
27
|
+
[features]
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
features,
|
|
32
|
+
featureEnabled,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const availableFeatures = {
|
|
37
|
+
threaded_replies: 'QA_MOBILE_threaded_replies',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const stableEmptyFeatures: ApiCollection<FeatureResource> = {
|
|
41
|
+
data: [],
|
|
42
|
+
links: {},
|
|
43
|
+
meta: {
|
|
44
|
+
count: 0,
|
|
45
|
+
totalCount: 0,
|
|
46
|
+
},
|
|
47
|
+
}
|
|
@@ -35,7 +35,7 @@ import { CONVERSATION_MESSAGE_LIST_PADDING_HORIZONTAL } from '../utils/styles'
|
|
|
35
35
|
import { useConversationJoltEvents } from '../hooks/use_conversation_jolt_events'
|
|
36
36
|
import { JumpToBottomButton } from '../components/conversation/jump_to_bottom_button'
|
|
37
37
|
import { ReplyShadowMessage } from '../components/conversation/reply_shadow_message'
|
|
38
|
-
import {
|
|
38
|
+
import { availableFeatures, useFeatures } from '../hooks/use_features'
|
|
39
39
|
|
|
40
40
|
export type ConversationRouteProps = {
|
|
41
41
|
conversation_id: number
|
|
@@ -66,7 +66,13 @@ export function ConversationScreen({ route }: ConversationScreenProps) {
|
|
|
66
66
|
useConversationMessagesJoltEvents({ conversationId: conversation_id })
|
|
67
67
|
useEnsureConversationsRouteExists()
|
|
68
68
|
useMarkLatestMessageRead({ conversation, messages })
|
|
69
|
-
const
|
|
69
|
+
const { featureEnabled } = useFeatures()
|
|
70
|
+
const repliesEnabled = featureEnabled(availableFeatures.threaded_replies)
|
|
71
|
+
const messagesWithSeparators = groupMessages({
|
|
72
|
+
ms: messages,
|
|
73
|
+
inReplyScreen: !!reply_root_id,
|
|
74
|
+
repliesEnabled,
|
|
75
|
+
})
|
|
70
76
|
const noMessages = messagesWithSeparators.length === 0
|
|
71
77
|
|
|
72
78
|
const { repliesDisabled, memberAbility, badges, title } = conversation
|
|
@@ -164,6 +170,7 @@ export function ConversationScreen({ route }: ConversationScreenProps) {
|
|
|
164
170
|
conversation_id={conversation_id}
|
|
165
171
|
latestReadMessageSortKey={conversation?.latestReadMessageSortKey}
|
|
166
172
|
inReplyScreen={!!reply_root_id}
|
|
173
|
+
repliesEnabled={repliesEnabled}
|
|
167
174
|
/>
|
|
168
175
|
)
|
|
169
176
|
}}
|
|
@@ -252,9 +259,14 @@ type ReplyShadowMessage = {
|
|
|
252
259
|
interface GroupMessagesProps {
|
|
253
260
|
ms: MessageResource[]
|
|
254
261
|
inReplyScreen?: boolean
|
|
262
|
+
repliesEnabled?: boolean
|
|
255
263
|
}
|
|
256
264
|
|
|
257
|
-
export const groupMessages = ({
|
|
265
|
+
export const groupMessages = ({
|
|
266
|
+
ms,
|
|
267
|
+
inReplyScreen,
|
|
268
|
+
repliesEnabled = false,
|
|
269
|
+
}: GroupMessagesProps) => {
|
|
258
270
|
let enrichedMessages: (MessageResource | DateSeparator | ReplyShadowMessage)[] = []
|
|
259
271
|
let encounteredOneOfMyMessages = false
|
|
260
272
|
|
|
@@ -298,7 +310,7 @@ export const groupMessages = ({ ms, inReplyScreen }: GroupMessagesProps) => {
|
|
|
298
310
|
message.nextRendersAuthor = nextMessage?.renderAuthor
|
|
299
311
|
message.isReplyShadowMessage = false
|
|
300
312
|
message.nextIsReplyShadowMessage =
|
|
301
|
-
|
|
313
|
+
repliesEnabled &&
|
|
302
314
|
nextMessageInThread &&
|
|
303
315
|
!nextMessageThreadRoot &&
|
|
304
316
|
(nextMessageDifferentThread || nextMessageIsDateSeparator)
|
|
@@ -319,7 +331,7 @@ export const groupMessages = ({ ms, inReplyScreen }: GroupMessagesProps) => {
|
|
|
319
331
|
|
|
320
332
|
enrichedMessages.push(message)
|
|
321
333
|
|
|
322
|
-
if (insertReplyShadowMessage &&
|
|
334
|
+
if (insertReplyShadowMessage && repliesEnabled) {
|
|
323
335
|
enrichedMessages.push({
|
|
324
336
|
type: 'ReplyShadowMessage',
|
|
325
337
|
id: `${message.id}-${message.replyRootId}`,
|
|
@@ -14,7 +14,7 @@ import { useMessageReactionToggle } from '../hooks/use_message_reaction_toggle'
|
|
|
14
14
|
import { ReactionCountResource } from '../types/resources/reaction'
|
|
15
15
|
import { Clipboard, Haptic } from '../utils/native_adapters'
|
|
16
16
|
import { MessageResource } from '../types'
|
|
17
|
-
import {
|
|
17
|
+
import { availableFeatures, useFeatures } from '../hooks/use_features'
|
|
18
18
|
|
|
19
19
|
export const MessageActionsScreenOptions = getFormSheetScreenOptions({
|
|
20
20
|
sheetAllowedDetents: [0.5],
|
|
@@ -76,6 +76,8 @@ function MessageActionsScreenContent({
|
|
|
76
76
|
const navigation = useNavigation()
|
|
77
77
|
const apiClient = useApiClient()
|
|
78
78
|
const styles = useStyles()
|
|
79
|
+
const { featureEnabled } = useFeatures()
|
|
80
|
+
const repliesEnabled = featureEnabled(availableFeatures.threaded_replies)
|
|
79
81
|
|
|
80
82
|
const myReactions = message?.reactionCounts
|
|
81
83
|
.filter(reaction => reaction.mine)
|
|
@@ -190,7 +192,7 @@ function MessageActionsScreenContent({
|
|
|
190
192
|
))}
|
|
191
193
|
</View>
|
|
192
194
|
<View style={styles.actions}>
|
|
193
|
-
{
|
|
195
|
+
{repliesEnabled && !inReplyScreen && (
|
|
194
196
|
<FormSheet.Action
|
|
195
197
|
onPress={handleReplyPress}
|
|
196
198
|
title="Reply to message"
|
package/src/utils/index.ts
CHANGED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { getRequestQueryKey } from '../../hooks/use_suspense_api'
|
|
2
|
+
|
|
3
|
+
export const getFeaturesRequestArgs = () => {
|
|
4
|
+
const url = '/me/features'
|
|
5
|
+
|
|
6
|
+
return {
|
|
7
|
+
url,
|
|
8
|
+
data: {
|
|
9
|
+
perPage: 100,
|
|
10
|
+
fields: {
|
|
11
|
+
Feature: ['name', 'enabled'],
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const getFeaturesQueryKey = () => {
|
|
18
|
+
const requestArgs = getFeaturesRequestArgs()
|
|
19
|
+
return getRequestQueryKey(requestArgs)
|
|
20
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"replies_local_feature_flag.d.ts","sourceRoot":"","sources":["../../src/utils/replies_local_feature_flag.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,uBAAuB,QAAQ,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"replies_local_feature_flag.js","sourceRoot":"","sources":["../../src/utils/replies_local_feature_flag.ts"],"names":[],"mappings":"AAAA,sDAAsD;AACtD,MAAM,CAAC,MAAM,uBAAuB,GAAG,KAAK,CAAA","sourcesContent":["// TODO: Replace this whole file with the flipper flag\nexport const REPLIES_FEATURE_ENABLED = false\n"]}
|