@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.
Files changed (50) hide show
  1. package/build/components/conversation/message.d.ts +2 -1
  2. package/build/components/conversation/message.d.ts.map +1 -1
  3. package/build/components/conversation/message.js +5 -5
  4. package/build/components/conversation/message.js.map +1 -1
  5. package/build/components/conversation/message_form.js +5 -2
  6. package/build/components/conversation/message_form.js.map +1 -1
  7. package/build/components/conversation/reply_connectors.d.ts.map +1 -1
  8. package/build/components/conversation/reply_connectors.js +0 -5
  9. package/build/components/conversation/reply_connectors.js.map +1 -1
  10. package/build/components/conversations/conversation_actions.d.ts.map +1 -1
  11. package/build/components/conversations/conversation_actions.js +1 -2
  12. package/build/components/conversations/conversation_actions.js.map +1 -1
  13. package/build/hooks/use_features.d.ts +9 -0
  14. package/build/hooks/use_features.d.ts.map +1 -0
  15. package/build/hooks/use_features.js +35 -0
  16. package/build/hooks/use_features.js.map +1 -0
  17. package/build/screens/conversation_screen.d.ts +2 -1
  18. package/build/screens/conversation_screen.d.ts.map +1 -1
  19. package/build/screens/conversation_screen.js +12 -6
  20. package/build/screens/conversation_screen.js.map +1 -1
  21. package/build/screens/message_actions_screen.js +4 -2
  22. package/build/screens/message_actions_screen.js.map +1 -1
  23. package/build/types/resources/feature_resource.d.ts +7 -0
  24. package/build/types/resources/feature_resource.d.ts.map +1 -0
  25. package/build/types/resources/feature_resource.js +2 -0
  26. package/build/types/resources/feature_resource.js.map +1 -0
  27. package/build/utils/index.d.ts +0 -1
  28. package/build/utils/index.d.ts.map +1 -1
  29. package/build/utils/index.js +0 -1
  30. package/build/utils/index.js.map +1 -1
  31. package/build/utils/request/get_features.d.ts +11 -0
  32. package/build/utils/request/get_features.d.ts.map +1 -0
  33. package/build/utils/request/get_features.js +18 -0
  34. package/build/utils/request/get_features.js.map +1 -0
  35. package/package.json +2 -2
  36. package/src/components/conversation/message.tsx +8 -4
  37. package/src/components/conversation/message_form.tsx +4 -2
  38. package/src/components/conversation/reply_connectors.tsx +0 -3
  39. package/src/components/conversations/conversation_actions.tsx +1 -1
  40. package/src/hooks/use_features.ts +47 -0
  41. package/src/screens/conversation_screen.tsx +17 -5
  42. package/src/screens/message_actions_screen.tsx +4 -2
  43. package/src/types/resources/feature_resource.ts +6 -0
  44. package/src/utils/index.ts +0 -1
  45. package/src/utils/request/get_features.ts +20 -0
  46. package/build/utils/replies_local_feature_flag.d.ts +0 -2
  47. package/build/utils/replies_local_feature_flag.d.ts.map +0 -1
  48. package/build/utils/replies_local_feature_flag.js +0 -3
  49. package/build/utils/replies_local_feature_flag.js.map +0 -1
  50. 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, REPLIES_FEATURE_ENABLED } from '../../utils'
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 && REPLIES_FEATURE_ENABLED
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
- <TheirReplyConnector message={message} messageBubbleHeight={messageBubbleHeight} />
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 (!REPLIES_FEATURE_ENABLED || !replyRootId) return null
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 { REPLIES_FEATURE_ENABLED } from '../utils'
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 messagesWithSeparators = groupMessages({ ms: messages, inReplyScreen: !!reply_root_id })
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 = ({ ms, inReplyScreen }: GroupMessagesProps) => {
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
- REPLIES_FEATURE_ENABLED &&
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 && REPLIES_FEATURE_ENABLED) {
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 { REPLIES_FEATURE_ENABLED } from '../utils'
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
- {REPLIES_FEATURE_ENABLED && !inReplyScreen && (
195
+ {repliesEnabled && !inReplyScreen && (
194
196
  <FormSheet.Action
195
197
  onPress={handleReplyPress}
196
198
  title="Reply to message"
@@ -0,0 +1,6 @@
1
+ export interface FeatureResource {
2
+ type: 'Feature'
3
+ id: string
4
+ name: string
5
+ enabled: boolean
6
+ }
@@ -9,4 +9,3 @@ export * from './pluralize'
9
9
  export * from './destructure_chat_group_graph_id'
10
10
  export * from './convert_attachments_for_create'
11
11
  export * from './assert_keys_are_numbers'
12
- export * from './replies_local_feature_flag'
@@ -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,2 +0,0 @@
1
- export declare const REPLIES_FEATURE_ENABLED = false;
2
- //# sourceMappingURL=replies_local_feature_flag.d.ts.map
@@ -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,3 +0,0 @@
1
- // TODO: Replace this whole file with the flipper flag
2
- export const REPLIES_FEATURE_ENABLED = false;
3
- //# sourceMappingURL=replies_local_feature_flag.js.map
@@ -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"]}
@@ -1,2 +0,0 @@
1
- // TODO: Replace this whole file with the flipper flag
2
- export const REPLIES_FEATURE_ENABLED = false