@planningcenter/chat-react-native 3.37.1-qa-736.0 → 3.37.1-qa-747.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (171) hide show
  1. package/build/components/conversation/jump_to_bottom_button.d.ts +1 -2
  2. package/build/components/conversation/jump_to_bottom_button.d.ts.map +1 -1
  3. package/build/components/conversation/jump_to_bottom_button.js +7 -39
  4. package/build/components/conversation/jump_to_bottom_button.js.map +1 -1
  5. package/build/components/conversation/message_form.d.ts.map +1 -1
  6. package/build/components/conversation/message_form.js +7 -19
  7. package/build/components/conversation/message_form.js.map +1 -1
  8. package/build/components/conversation/reply_shadow_message.d.ts +2 -1
  9. package/build/components/conversation/reply_shadow_message.d.ts.map +1 -1
  10. package/build/components/conversation/reply_shadow_message.js.map +1 -1
  11. package/build/components/conversations/conversations.d.ts.map +1 -1
  12. package/build/components/conversations/conversations.js +16 -6
  13. package/build/components/conversations/conversations.js.map +1 -1
  14. package/build/contexts/conversation_context.d.ts +1 -8
  15. package/build/contexts/conversation_context.d.ts.map +1 -1
  16. package/build/contexts/conversation_context.js +3 -21
  17. package/build/contexts/conversation_context.js.map +1 -1
  18. package/build/hooks/use_attachment_uploader.d.ts.map +1 -1
  19. package/build/hooks/use_attachment_uploader.js +0 -9
  20. package/build/hooks/use_attachment_uploader.js.map +1 -1
  21. package/build/hooks/use_conversation_messages.d.ts +6 -15
  22. package/build/hooks/use_conversation_messages.d.ts.map +1 -1
  23. package/build/hooks/use_conversation_messages.js +9 -62
  24. package/build/hooks/use_conversation_messages.js.map +1 -1
  25. package/build/hooks/use_conversation_messages_jolt_events.d.ts.map +1 -1
  26. package/build/hooks/use_conversation_messages_jolt_events.js +4 -4
  27. package/build/hooks/use_conversation_messages_jolt_events.js.map +1 -1
  28. package/build/hooks/use_conversations_actions.d.ts +0 -5
  29. package/build/hooks/use_conversations_actions.d.ts.map +1 -1
  30. package/build/hooks/use_conversations_actions.js +0 -12
  31. package/build/hooks/use_conversations_actions.js.map +1 -1
  32. package/build/hooks/use_features.d.ts +0 -2
  33. package/build/hooks/use_features.d.ts.map +1 -1
  34. package/build/hooks/use_features.js +0 -2
  35. package/build/hooks/use_features.js.map +1 -1
  36. package/build/hooks/use_mark_latest_message_read.d.ts +1 -1
  37. package/build/hooks/use_mark_latest_message_read.d.ts.map +1 -1
  38. package/build/hooks/use_mark_latest_message_read.js +1 -17
  39. package/build/hooks/use_mark_latest_message_read.js.map +1 -1
  40. package/build/hooks/use_suspense_api.d.ts +0 -1
  41. package/build/hooks/use_suspense_api.d.ts.map +1 -1
  42. package/build/hooks/use_suspense_api.js +1 -1
  43. package/build/hooks/use_suspense_api.js.map +1 -1
  44. package/build/hooks/use_upload_client.d.ts +0 -4
  45. package/build/hooks/use_upload_client.d.ts.map +1 -1
  46. package/build/hooks/use_upload_client.js +1 -13
  47. package/build/hooks/use_upload_client.js.map +1 -1
  48. package/build/jest.js +1 -1
  49. package/build/jest.js.map +1 -1
  50. package/build/screens/age_check/age_check_underage_screen.js +1 -1
  51. package/build/screens/age_check/age_check_underage_screen.js.map +1 -1
  52. package/build/screens/avatar_picker/emoji_tab.d.ts.map +1 -1
  53. package/build/screens/avatar_picker/emoji_tab.js +6 -2
  54. package/build/screens/avatar_picker/emoji_tab.js.map +1 -1
  55. package/build/screens/conversation_screen.d.ts +0 -1
  56. package/build/screens/conversation_screen.d.ts.map +1 -1
  57. package/build/screens/conversation_screen.js +48 -95
  58. package/build/screens/conversation_screen.js.map +1 -1
  59. package/build/types/jolt_events/index.d.ts +1 -3
  60. package/build/types/jolt_events/index.d.ts.map +1 -1
  61. package/build/types/jolt_events/index.js.map +1 -1
  62. package/build/utils/cache/messages_cache.d.ts +0 -1
  63. package/build/utils/cache/messages_cache.d.ts.map +1 -1
  64. package/build/utils/cache/messages_cache.js +0 -4
  65. package/build/utils/cache/messages_cache.js.map +1 -1
  66. package/build/utils/group_messages.d.ts +2 -9
  67. package/build/utils/group_messages.d.ts.map +1 -1
  68. package/build/utils/group_messages.js +1 -20
  69. package/build/utils/group_messages.js.map +1 -1
  70. package/package.json +4 -4
  71. package/src/__tests__/hooks/use_attachment_uploader.test.tsx +0 -36
  72. package/src/__tests__/jest.ts +1 -1
  73. package/src/components/conversation/jump_to_bottom_button.tsx +8 -57
  74. package/src/components/conversation/message_form.tsx +7 -21
  75. package/src/components/conversation/reply_shadow_message.tsx +1 -1
  76. package/src/components/conversations/conversations.tsx +16 -9
  77. package/src/contexts/conversation_context.tsx +2 -30
  78. package/src/hooks/use_attachment_uploader.ts +1 -14
  79. package/src/hooks/use_conversation_messages.ts +20 -120
  80. package/src/hooks/use_conversation_messages_jolt_events.ts +3 -4
  81. package/src/hooks/use_conversations_actions.ts +0 -15
  82. package/src/hooks/use_features.ts +0 -2
  83. package/src/hooks/use_mark_latest_message_read.ts +2 -16
  84. package/src/hooks/use_suspense_api.ts +1 -1
  85. package/src/hooks/use_upload_client.ts +1 -19
  86. package/src/jest.ts +1 -1
  87. package/src/screens/age_check/age_check_underage_screen.tsx +1 -1
  88. package/src/screens/avatar_picker/emoji_tab.tsx +6 -2
  89. package/src/screens/conversation_screen.tsx +76 -184
  90. package/src/types/jolt_events/index.ts +0 -3
  91. package/src/utils/__tests__/group_messages.test.ts +0 -71
  92. package/src/utils/cache/messages_cache.ts +0 -5
  93. package/src/utils/group_messages.ts +2 -42
  94. package/build/components/conversation/message_list.d.ts +0 -10
  95. package/build/components/conversation/message_list.d.ts.map +0 -1
  96. package/build/components/conversation/message_list.js +0 -13
  97. package/build/components/conversation/message_list.js.map +0 -1
  98. package/build/components/conversation/unread_divider.d.ts +0 -6
  99. package/build/components/conversation/unread_divider.d.ts.map +0 -1
  100. package/build/components/conversation/unread_divider.js +0 -59
  101. package/build/components/conversation/unread_divider.js.map +0 -1
  102. package/build/components/conversations/conversations_blank_state.d.ts +0 -8
  103. package/build/components/conversations/conversations_blank_state.d.ts.map +0 -1
  104. package/build/components/conversations/conversations_blank_state.js +0 -25
  105. package/build/components/conversations/conversations_blank_state.js.map +0 -1
  106. package/build/hooks/use_flat_list_viewability.d.ts +0 -20
  107. package/build/hooks/use_flat_list_viewability.d.ts.map +0 -1
  108. package/build/hooks/use_flat_list_viewability.js +0 -30
  109. package/build/hooks/use_flat_list_viewability.js.map +0 -1
  110. package/build/hooks/use_jump_to_bottom_action.d.ts +0 -9
  111. package/build/hooks/use_jump_to_bottom_action.d.ts.map +0 -1
  112. package/build/hooks/use_jump_to_bottom_action.js +0 -62
  113. package/build/hooks/use_jump_to_bottom_action.js.map +0 -1
  114. package/build/hooks/use_jump_to_unread_anchor.d.ts +0 -20
  115. package/build/hooks/use_jump_to_unread_anchor.d.ts.map +0 -1
  116. package/build/hooks/use_jump_to_unread_anchor.js +0 -53
  117. package/build/hooks/use_jump_to_unread_anchor.js.map +0 -1
  118. package/build/hooks/use_jump_to_unread_gates.d.ts +0 -5
  119. package/build/hooks/use_jump_to_unread_gates.d.ts.map +0 -1
  120. package/build/hooks/use_jump_to_unread_gates.js +0 -10
  121. package/build/hooks/use_jump_to_unread_gates.js.map +0 -1
  122. package/build/hooks/use_scroll_tracking.d.ts +0 -13
  123. package/build/hooks/use_scroll_tracking.d.ts.map +0 -1
  124. package/build/hooks/use_scroll_tracking.js +0 -45
  125. package/build/hooks/use_scroll_tracking.js.map +0 -1
  126. package/build/hooks/use_track_highest_seen_message.d.ts +0 -4
  127. package/build/hooks/use_track_highest_seen_message.d.ts.map +0 -1
  128. package/build/hooks/use_track_highest_seen_message.js +0 -35
  129. package/build/hooks/use_track_highest_seen_message.js.map +0 -1
  130. package/build/types/jolt_events/attachment_events.d.ts +0 -14
  131. package/build/types/jolt_events/attachment_events.d.ts.map +0 -1
  132. package/build/types/jolt_events/attachment_events.js +0 -2
  133. package/build/types/jolt_events/attachment_events.js.map +0 -1
  134. package/build/utils/conversation_messages.d.ts +0 -10
  135. package/build/utils/conversation_messages.d.ts.map +0 -1
  136. package/build/utils/conversation_messages.js +0 -22
  137. package/build/utils/conversation_messages.js.map +0 -1
  138. package/build/utils/highest_seen_tracker.d.ts +0 -12
  139. package/build/utils/highest_seen_tracker.d.ts.map +0 -1
  140. package/build/utils/highest_seen_tracker.js +0 -37
  141. package/build/utils/highest_seen_tracker.js.map +0 -1
  142. package/build/utils/message_viewability.d.ts +0 -24
  143. package/build/utils/message_viewability.d.ts.map +0 -1
  144. package/build/utils/message_viewability.js +0 -29
  145. package/build/utils/message_viewability.js.map +0 -1
  146. package/build/utils/unread_divider_helpers.d.ts +0 -18
  147. package/build/utils/unread_divider_helpers.d.ts.map +0 -1
  148. package/build/utils/unread_divider_helpers.js +0 -13
  149. package/build/utils/unread_divider_helpers.js.map +0 -1
  150. package/src/__tests__/hooks/use_conversation_messages.test.tsx +0 -109
  151. package/src/__tests__/hooks/use_mark_latest_message_read.test.tsx +0 -154
  152. package/src/__tests__/utils/cache/messages_cache.test.ts +0 -54
  153. package/src/components/conversation/__tests__/message_list.test.tsx +0 -14
  154. package/src/components/conversation/message_list.tsx +0 -42
  155. package/src/components/conversation/unread_divider.tsx +0 -90
  156. package/src/components/conversations/conversations_blank_state.tsx +0 -42
  157. package/src/hooks/use_flat_list_viewability.ts +0 -50
  158. package/src/hooks/use_jump_to_bottom_action.ts +0 -75
  159. package/src/hooks/use_jump_to_unread_anchor.ts +0 -68
  160. package/src/hooks/use_jump_to_unread_gates.ts +0 -10
  161. package/src/hooks/use_scroll_tracking.ts +0 -64
  162. package/src/hooks/use_track_highest_seen_message.ts +0 -43
  163. package/src/types/jolt_events/attachment_events.ts +0 -14
  164. package/src/utils/__tests__/conversation_messages.test.ts +0 -105
  165. package/src/utils/__tests__/highest_seen_tracker.test.ts +0 -82
  166. package/src/utils/__tests__/message_viewability.test.ts +0 -168
  167. package/src/utils/__tests__/unread_divider_helpers.test.ts +0 -85
  168. package/src/utils/conversation_messages.ts +0 -37
  169. package/src/utils/highest_seen_tracker.ts +0 -42
  170. package/src/utils/message_viewability.ts +0 -49
  171. package/src/utils/unread_divider_helpers.ts +0 -25
@@ -31,7 +31,6 @@ import { ConversationResource, MessageResource } from '../../types'
31
31
  import {
32
32
  DenormalizedAttachmentResourceForCreate,
33
33
  DenormalizedMessageAttachmentResourceForCreate,
34
- FileAttachment,
35
34
  } from '../../types/resources/denormalized_attachment_resource_for_create'
36
35
  import {
37
36
  MAX_FONT_SIZE_MULTIPLIER_LANDMARK,
@@ -341,28 +340,15 @@ function MessageFormAttachments() {
341
340
  )
342
341
  }
343
342
 
344
- function getFileType(attachments: FileAttachment[]): 'image' | 'video' | 'file' {
345
- const kinds = new Set(
346
- attachments.map(({ file }) => {
347
- if (file.type.startsWith('image/')) return 'image'
348
- if (file.type.startsWith('video/')) return 'video'
349
- return 'file'
350
- })
351
- )
352
- return kinds.size === 1 ? [...kinds][0] : 'file'
353
- }
354
-
355
343
  function FlaggedContentBanner() {
356
344
  const styles = useMessageFormStyles()
357
345
  const { attachmentUploader } = React.useContext(MessageFormContext)
358
- const flaggedAttachments = attachmentUploader?.attachments.filter(a => a.flagged) ?? []
359
- const flaggedCount = flaggedAttachments.length
346
+ const flaggedCount = attachmentUploader?.flaggedAttachmentCount || 0
360
347
 
361
348
  if (flaggedCount === 0) return null
362
349
 
363
350
  const isSingular = flaggedCount === 1
364
- const fileType = getFileType(flaggedAttachments)
365
- const buttonTitle = isSingular ? `Remove flagged ${fileType}` : `Remove flagged ${fileType}s`
351
+ const buttonTitle = isSingular ? 'Remove flagged image' : 'Remove flagged images'
366
352
 
367
353
  return (
368
354
  <View style={styles.flaggedBannerContainer}>
@@ -370,7 +356,7 @@ function FlaggedContentBanner() {
370
356
  <BannerPrimitive.StaticLayout>
371
357
  <BannerPrimitive.StatusIcon />
372
358
  <BannerPrimitive.Content>
373
- <BannerMessage isSingular={isSingular} fileType={fileType} />
359
+ <BannerMessage isSingular={isSingular} />
374
360
  <View style={styles.flaggedBannerButtonRow}>
375
361
  <Button
376
362
  title={buttonTitle}
@@ -387,7 +373,7 @@ function FlaggedContentBanner() {
387
373
  )
388
374
  }
389
375
 
390
- function BannerMessage({ isSingular, fileType }: { isSingular: boolean; fileType: string }) {
376
+ function BannerMessage({ isSingular }: { isSingular: boolean }) {
391
377
  const styles = useMessageFormStyles()
392
378
 
393
379
  const contentGuidelinesLink = (
@@ -406,13 +392,13 @@ function BannerMessage({ isSingular, fileType }: { isSingular: boolean; fileType
406
392
 
407
393
  return isSingular ? (
408
394
  <Text style={styles.flaggedBannerText}>
409
- An uploaded {fileType} was flagged because it doesn't meet our {contentGuidelinesLink}. Please
395
+ An uploaded image was flagged because it doesn't meet our {contentGuidelinesLink}. Please
410
396
  remove before proceeding.
411
397
  </Text>
412
398
  ) : (
413
399
  <Text style={styles.flaggedBannerText}>
414
- Some uploaded {fileType}s were flagged because they don't meet our {contentGuidelinesLink}.
415
- Please remove them before proceeding.
400
+ Some uploaded images were flagged because they don't meet our {contentGuidelinesLink}. Please
401
+ remove them before proceeding.
416
402
  </Text>
417
403
  )
418
404
  }
@@ -28,7 +28,7 @@ import {
28
28
  import { Avatar, Icon, IconProps, Image, Text } from '../display'
29
29
  import { TheirReplyConnector, MyReplyConnector } from './reply_connectors'
30
30
 
31
- interface ReplyShadowMessageProps {
31
+ interface ReplyShadowMessageProps extends MessageResource {
32
32
  messageId: string
33
33
  conversation_id: number
34
34
  inReplyScreen?: boolean
@@ -3,12 +3,11 @@ import React, { useMemo } from 'react'
3
3
  import { FlatList, StyleSheet, View } from 'react-native'
4
4
  import { useConversationsContext } from '../../contexts/conversations_context'
5
5
  import { useTheme } from '../../hooks'
6
- import { useCanCreateConversations } from '../../hooks/use_chat_permissions'
7
6
  import { useConversationsJoltEvents } from '../../hooks/use_conversations_jolt_events'
8
7
  import { ConversationResource } from '../../types'
9
8
  import { throwResponseError } from '../../utils/response_error'
9
+ import BlankState from '../primitive/blank_state_primitive'
10
10
  import { ConversationPreview, ConversationPreviewSkeleton } from './conversation_preview'
11
- import { ConversationsBlankState } from './conversations_blank_state'
12
11
 
13
12
  interface ConversationsProps {
14
13
  ListHeaderComponent?:
@@ -29,11 +28,9 @@ export const Conversations = ({ ListHeaderComponent }: ConversationsProps) => {
29
28
  isFetched,
30
29
  isError,
31
30
  error,
32
- args: { chat_group_graph_id, group_source_app_name },
31
+ args: { chat_group_graph_id },
33
32
  } = useConversationsContext()
34
33
  const navigation = useNavigation()
35
- const canCreateConversations = useCanCreateConversations()
36
- const isFilterApplied = !!chat_group_graph_id || !!group_source_app_name
37
34
 
38
35
  const showBadges = !chat_group_graph_id
39
36
 
@@ -64,10 +61,14 @@ export const Conversations = ({ ListHeaderComponent }: ConversationsProps) => {
64
61
  refreshing={!isFetched && isRefetching}
65
62
  ListHeaderComponent={ListHeaderComponent}
66
63
  ListEmptyComponent={
67
- <ConversationsBlankState
68
- isFilterApplied={isFilterApplied}
69
- canCreateConversations={canCreateConversations}
70
- />
64
+ <View style={styles.listEmpty}>
65
+ <BlankState.Root>
66
+ <BlankState.Imagery name="general.outlinedTextMessage" />
67
+ <BlankState.Content>
68
+ <BlankState.Heading>No conversations</BlankState.Heading>
69
+ </BlankState.Content>
70
+ </BlankState.Root>
71
+ </View>
71
72
  }
72
73
  renderItem={({ item }) => {
73
74
  if (item.type === 'loading') {
@@ -103,6 +104,12 @@ const useStyles = () => {
103
104
  container: { flex: 1 },
104
105
  contentContainer: { paddingVertical: 16 },
105
106
  listItem: { color: colors.fillColorNeutral020 },
107
+ listEmpty: {
108
+ flex: 1,
109
+ justifyContent: 'center',
110
+ alignItems: 'center',
111
+ paddingVertical: 32,
112
+ },
106
113
  })
107
114
  }
108
115
 
@@ -1,59 +1,31 @@
1
- import React, { createContext, PropsWithChildren, useContext, useMemo, useState } from 'react'
1
+ import React, { createContext, PropsWithChildren, useContext, useMemo } from 'react'
2
2
 
3
3
  interface ConversationContextValue {
4
4
  conversationId: number
5
5
  currentPageReplyRootId: string | null
6
- initialMessageId: string | null
7
- setInitialMessageId: (id: string | null) => void
8
- initialMessageIdIsAnchor: boolean
9
- atEndOfMessageHistory: boolean
10
- setAtEndOfMessageHistory: (atEnd: boolean) => void
11
6
  }
12
7
 
13
8
  interface ConversationContextProviderProps extends PropsWithChildren {
14
9
  conversationId: number
15
10
  currentPageReplyRootId: string | null
16
- initialMessageId?: string | null
17
- initialMessageIdIsAnchor?: boolean
18
11
  }
19
12
 
20
13
  const ConversationContext = createContext<ConversationContextValue>({
21
14
  conversationId: 0,
22
15
  currentPageReplyRootId: null,
23
- initialMessageId: null,
24
- setInitialMessageId: () => {},
25
- initialMessageIdIsAnchor: false,
26
- atEndOfMessageHistory: false,
27
- setAtEndOfMessageHistory: () => {},
28
16
  })
29
17
 
30
18
  export const ConversationContextProvider = ({
31
19
  children,
32
20
  conversationId,
33
21
  currentPageReplyRootId,
34
- initialMessageId: initialMessageIdProp = null,
35
- initialMessageIdIsAnchor = false,
36
22
  }: ConversationContextProviderProps) => {
37
- const [initialMessageId, setInitialMessageId] = useState(initialMessageIdProp)
38
- const [atEndOfMessageHistory, setAtEndOfMessageHistory] = useState(false)
39
-
40
23
  const value = useMemo(
41
24
  () => ({
42
25
  conversationId,
43
26
  currentPageReplyRootId,
44
- initialMessageId,
45
- setInitialMessageId,
46
- initialMessageIdIsAnchor,
47
- atEndOfMessageHistory,
48
- setAtEndOfMessageHistory,
49
27
  }),
50
- [
51
- conversationId,
52
- currentPageReplyRootId,
53
- initialMessageId,
54
- initialMessageIdIsAnchor,
55
- atEndOfMessageHistory,
56
- ]
28
+ [conversationId, currentPageReplyRootId]
57
29
  )
58
30
 
59
31
  return <ConversationContext.Provider value={value}>{children}</ConversationContext.Provider>
@@ -1,16 +1,12 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
2
- import { ApiResource, CurrentPersonResource } from '../types'
3
- import { AttachmentFlaggedEvent } from '../types/jolt_events/attachment_events'
2
+ import { ApiResource } from '../types'
4
3
  import {
5
4
  FileAttachment,
6
5
  FileUploadState,
7
6
  NativeAttachmentFile,
8
7
  } from '../types/resources/denormalized_attachment_resource_for_create'
9
- import { useApiGet } from './use_api'
10
8
  import { useApiClient } from './use_api_client'
11
9
  import { useChatConfiguration } from './use_chat_configuration'
12
- import { currentPersonRequestArgs } from './use_current_person'
13
- import { useJoltChannel, useJoltEvent } from './use_jolt'
14
10
  import { useUploadClient } from './use_upload_client'
15
11
 
16
12
  export interface FileError {
@@ -36,15 +32,6 @@ export function useAttachmentUploader({
36
32
  const numberOfAttachments = attachments.length
37
33
  const [errorMessage, setErrorMessage] = useState<string | null>(null)
38
34
 
39
- const { data: currentPerson } = useApiGet<CurrentPersonResource>(currentPersonRequestArgs)
40
- const joltChannel = useJoltChannel(`chat.people.${currentPerson?.id}`, Boolean(currentPerson?.id))
41
- useJoltEvent(joltChannel, 'attachment.flagged', (e: AttachmentFlaggedEvent) => {
42
- const flaggedId = e.data.data.attachment_id
43
- setAttachments(prev =>
44
- prev.map(a => (a.id === flaggedId ? { ...a, flagged: true, status: 'error' } : a))
45
- )
46
- })
47
-
48
35
  const handleFilesAttached = useCallback(
49
36
  (files: NativeAttachmentFile[]) => {
50
37
  const fileErrors = {} as FileError
@@ -1,128 +1,28 @@
1
- import {
2
- AnyUseSuspenseInfiniteQueryOptions,
3
- InfiniteData,
4
- useQueryClient,
5
- useSuspenseInfiniteQuery,
6
- useSuspenseQueries,
7
- } from '@tanstack/react-query'
8
- import { useCallback, useMemo } from 'react'
9
- import { useConversationContext } from '../contexts/conversation_context'
10
- import { ApiCollection, MessageResource } from '../types'
11
- import {
12
- anchoredSeedPageParams,
13
- MessagesPageParam,
14
- newerPageParam,
15
- olderPageParam,
16
- sortAndFilterMessages,
17
- } from '../utils/conversation_messages'
1
+ import { useMemo } from 'react'
2
+ import { MessageResource } from '../types'
18
3
  import { getMessagesQueryKey, getMessagesRequestArgs } from '../utils/request/get_messages'
19
- import { useApiClient } from './use_api_client'
20
- import { throwResponseError } from './use_suspense_api'
21
-
22
- type Args = { conversation_id: number; reply_root_id?: string | null }
23
-
24
- export type ConversationMessagesOptions = Omit<
25
- AnyUseSuspenseInfiniteQueryOptions,
26
- | 'getNextPageParam'
27
- | 'getPreviousPageParam'
28
- | 'initialData'
29
- | 'initialPageParam'
30
- | 'queryFn'
31
- | 'queryKey'
32
- >
4
+ import { SuspensePaginatorOptions, useSuspensePaginator } from './use_suspense_api'
33
5
 
34
6
  export const useConversationMessages = (
35
- { conversation_id, reply_root_id }: Args,
36
- opts?: ConversationMessagesOptions
7
+ { conversation_id, reply_root_id }: { conversation_id: number; reply_root_id?: string | null },
8
+ opts?: SuspensePaginatorOptions
37
9
  ) => {
38
- const apiClient = useApiClient()
39
- const { initialMessageId } = useConversationContext()
40
- const anchored = !reply_root_id && !!initialMessageId
41
-
42
- const requestArgs = useMemo(
43
- () => getMessagesRequestArgs({ conversation_id, reply_root_id }),
44
- [conversation_id, reply_root_id]
10
+ const { data, refetch, isRefetching, fetchNextPage } = useSuspensePaginator<MessageResource>(
11
+ getMessagesRequestArgs({ conversation_id, reply_root_id }),
12
+ opts
45
13
  )
46
- const queryKey = useMemo(
47
- () => getMessagesQueryKey({ conversation_id, reply_root_id }),
48
- [conversation_id, reply_root_id]
49
- )
50
-
51
- const fetchPage = (pageParam: MessagesPageParam) => {
52
- const data = {
53
- ...requestArgs.data,
54
- ...(pageParam.where ? { where: pageParam.where } : {}),
55
- ...(pageParam.order ? { order: pageParam.order } : {}),
56
- }
57
- return apiClient.chat
58
- .get<ApiCollection<MessageResource>>({ url: requestArgs.url, data })
59
- .catch(throwResponseError)
60
- }
61
-
62
- const seedPageParams = anchored ? anchoredSeedPageParams(initialMessageId) : []
63
- const seedQueries = useSuspenseQueries({
64
- queries: seedPageParams.map((pageParam, index) => ({
65
- queryKey: [...queryKey, 'seed', index],
66
- queryFn: () => fetchPage(pageParam),
67
- staleTime: Infinity,
68
- gcTime: 0,
69
- })),
70
- })
71
-
72
- const initialData: InfiniteData<ApiCollection<MessageResource>, MessagesPageParam> | undefined =
73
- anchored
74
- ? {
75
- pages: seedQueries.map(q => q.data),
76
- pageParams: seedPageParams,
77
- }
78
- : undefined
79
-
80
- const initialPageParam: MessagesPageParam = anchored ? seedPageParams[0] : {}
81
-
82
- const {
83
- data,
84
- refetch,
85
- isRefetching,
86
- fetchNextPage,
87
- hasNextPage,
88
- fetchPreviousPage,
89
- hasPreviousPage,
90
- isFetchingPreviousPage,
91
- } = useSuspenseInfiniteQuery<
92
- ApiCollection<MessageResource>,
93
- Response,
94
- InfiniteData<ApiCollection<MessageResource>, MessagesPageParam>,
95
- typeof queryKey,
96
- MessagesPageParam
97
- >({
98
- queryKey,
99
- queryFn: ({ pageParam }) => fetchPage(pageParam),
100
- initialPageParam,
101
- initialData,
102
- getNextPageParam: olderPageParam,
103
- getPreviousPageParam: anchored ? newerPageParam : () => undefined,
104
- ...(opts || {}),
105
- ...(anchored ? { staleTime: Infinity, refetchOnMount: false } : {}),
106
- })
107
-
108
- const messages = useMemo(() => sortAndFilterMessages(data.pages), [data.pages])
109
-
110
- const queryClient = useQueryClient()
111
- const cancelFetchNewerMessages = useCallback(
112
- () => queryClient.cancelQueries({ queryKey }),
113
- [queryClient, queryKey]
14
+ const queryKey = getMessagesQueryKey({ conversation_id, reply_root_id })
15
+ const messages = useMemo(
16
+ () =>
17
+ data
18
+ .filter(
19
+ message =>
20
+ (!message.deletedAt || message.replyRootId) &&
21
+ (message.attachments?.length || message.text?.length)
22
+ )
23
+ .sort((a, b) => -a.id.localeCompare(b.id)),
24
+ [data]
114
25
  )
115
26
 
116
- return {
117
- messages,
118
- refetch,
119
- isRefetching,
120
- fetchOlderMessages: fetchNextPage,
121
- hasMoreOlderMessages: hasNextPage,
122
- fetchNewerMessages: fetchPreviousPage,
123
- hasMoreNewerMessages: hasPreviousPage,
124
- isFetchingNewerMessages: isFetchingPreviousPage,
125
- cancelFetchNewerMessages,
126
- queryKey,
127
- }
27
+ return { messages, refetch, isRefetching, fetchNextPage, queryKey }
128
28
  }
@@ -12,7 +12,6 @@ import {
12
12
  updateCacheWithIndividualMessage,
13
13
  updateCacheWithReaction,
14
14
  getThreadedMessagesQueryKey,
15
- hasUnloadedNewerPages,
16
15
  } from '../utils/cache/messages_cache'
17
16
  import { transformMessageEventDataToMessageResource } from '../utils/jolt/transform_message_event_data_to_message_resource'
18
17
  import { completeMessageCreationTracking } from '../utils/performance_tracking'
@@ -53,10 +52,10 @@ export function useConversationMessagesJoltEvents({ conversationId }: Props) {
53
52
  }
54
53
  }
55
54
 
56
- if (e.event === 'message.updated' || !hasUnloadedNewerPages(queryClient, messagesQueryKey)) {
57
- updateCacheWithMessage(queryClient, messagesQueryKey, message, e.event)
58
- }
55
+ // Update the main conversation cache
56
+ updateCacheWithMessage(queryClient, messagesQueryKey, message, e.event)
59
57
 
58
+ // If message has a reply_root_id, also update the threaded cache
60
59
  if (data.reply_root_id) {
61
60
  const threadedMessagesQueryKey = getThreadedMessagesQueryKey(
62
61
  conversationId,
@@ -98,21 +98,6 @@ export const useConversationsMute = ({ conversation }: { conversation: Conversat
98
98
  }
99
99
  }
100
100
 
101
- export const useConversationsMarkReadUpTo = ({ conversationId }: { conversationId: number }) => {
102
- const apiClient = useApiClient()
103
-
104
- return useMutation({
105
- mutationKey: ['markReadUpTo', conversationId],
106
- mutationFn: async ({ sortKey }: { sortKey: string }) =>
107
- apiClient.chat.post({
108
- url: `/me/conversations/${conversationId}/mark_read_up_to`,
109
- data: {
110
- data: { type: 'Conversation', attributes: { sort_key: sortKey } },
111
- },
112
- }),
113
- })
114
- }
115
-
116
101
  export const useMarkAllRead = () => {
117
102
  const apiClient = useApiClient()
118
103
  const { args } = useConversationsContext()
@@ -40,9 +40,7 @@ export const availableFeatures = {
40
40
  message_reporting: 'ROLLOUT_MOBILE_message_reporting',
41
41
  granular_notifications_ui: 'ROLLOUT_granular_notification_preferences_ui',
42
42
  custom_conversation_avatars: 'ROLLOUT_custom_conversation_avatars',
43
- jump_to_unread: 'ROLLOUT_jump_to_unread',
44
43
  conversation_safety_lock: 'ROLLOUT_conversation_safety_lock',
45
- video_moderation: 'ROLLOUT_MOBILE_video_moderation',
46
44
  } as const satisfies Record<string, `ROLLOUT_${string}`>
47
45
 
48
46
  const stableEmptyFeatures: ApiCollection<FeatureResource> = {
@@ -1,19 +1,15 @@
1
1
  import { debounce } from 'lodash'
2
2
  import { useEffect, useMemo, useRef } from 'react'
3
- import { useConversationContext } from '../contexts/conversation_context'
4
3
  import { ConversationResource, MessageResource } from '../types'
5
4
  import { useAppState } from './use_app_state'
6
5
  import { useConversationsMarkRead } from './use_conversations_actions'
7
- import { useJumpToUnreadGates } from './use_jump_to_unread_gates'
8
6
 
9
7
  interface Props {
10
8
  conversation: ConversationResource
11
- messages?: MessageResource[]
9
+ messages: MessageResource[]
12
10
  }
13
11
 
14
12
  export function useMarkLatestMessageRead({ conversation }: Props) {
15
- const { jumpToUnreadActive } = useJumpToUnreadGates()
16
- const { currentPageReplyRootId, atEndOfMessageHistory } = useConversationContext()
17
13
  const firedOnce = useRef<boolean>(false)
18
14
  const { markRead } = useConversationsMarkRead({ conversation })
19
15
  const debouncedMarkRead = useMemo(
@@ -29,20 +25,10 @@ export function useMarkLatestMessageRead({ conversation }: Props) {
29
25
 
30
26
  useEffect(() => {
31
27
  if (!isActive || !shouldMarkRead) return
32
- if (currentPageReplyRootId) return
33
- if (jumpToUnreadActive && !atEndOfMessageHistory) return
34
28
 
35
29
  firedOnce.current = true
36
30
 
37
31
  debouncedMarkRead(true)
38
32
  // keeping unreadReactionCount in the dependency array to watch for changes
39
- }, [
40
- debouncedMarkRead,
41
- isActive,
42
- shouldMarkRead,
43
- unreadReactionCount,
44
- currentPageReplyRootId,
45
- jumpToUnreadActive,
46
- atEndOfMessageHistory,
47
- ])
33
+ }, [debouncedMarkRead, isActive, shouldMarkRead, unreadReactionCount])
48
34
  }
@@ -90,7 +90,7 @@ export const useSuspensePaginator = <T extends ResourceObject>(
90
90
  return { ...query, data, totalCount }
91
91
  }
92
92
 
93
- export const throwResponseError = (error: unknown) => {
93
+ const throwResponseError = (error: unknown) => {
94
94
  if (error instanceof Response) {
95
95
  throw new ResponseError(error as FailedResponse)
96
96
  }
@@ -2,7 +2,6 @@ import { useContext, useMemo } from 'react'
2
2
  import { ChatContext } from '../contexts/chat_context'
3
3
  import { Client } from '../utils'
4
4
  import { UploadUri } from '../utils/upload_uri'
5
- import { availableFeatures, useFeatures } from './use_features'
6
5
 
7
6
  export interface FileForUploadClient {
8
7
  uri: string
@@ -12,8 +11,6 @@ export interface FileForUploadClient {
12
11
 
13
12
  export const useUploadClient = () => {
14
13
  const { session, onUnauthorizedResponse } = useContext(ChatContext)
15
- const { featureEnabled } = useFeatures()
16
- const videoModerationEnabled = featureEnabled(availableFeatures.video_moderation)
17
14
 
18
15
  const uri = useMemo(() => new UploadUri({ session }), [session])
19
16
 
@@ -24,25 +21,14 @@ export const useUploadClient = () => {
24
21
  root: uri.baseUrl,
25
22
  defaultHeaders: uri.headers,
26
23
  onUnauthorizedResponse,
27
- videoModerationEnabled,
28
24
  }),
29
- [uri, onUnauthorizedResponse, videoModerationEnabled]
25
+ [uri, onUnauthorizedResponse]
30
26
  )
31
27
 
32
28
  return api
33
29
  }
34
30
 
35
31
  class UploadClient extends Client {
36
- private videoModerationEnabled: boolean
37
-
38
- constructor({
39
- videoModerationEnabled,
40
- ...rest
41
- }: ConstructorParameters<typeof Client>[0] & { videoModerationEnabled: boolean }) {
42
- super(rest)
43
- this.videoModerationEnabled = videoModerationEnabled
44
- }
45
-
46
32
  async uploadFile(file: FileForUploadClient): Promise<UploadedResource> {
47
33
  const formData = new FormData()
48
34
  formData.append('file', {
@@ -54,10 +40,6 @@ class UploadClient extends Client {
54
40
  const headers: RequestInit['headers'] = { ...this.headers }
55
41
  delete headers['Content-Type']
56
42
 
57
- if (this.videoModerationEnabled && file.type.startsWith('video/')) {
58
- headers['X-PCO-Moderate-Video'] = 'true'
59
- }
60
-
61
43
  const response = await fetch(`${this.root}/v2/files`, {
62
44
  method: 'POST',
63
45
  headers,
package/src/jest.ts CHANGED
@@ -16,6 +16,6 @@
16
16
  */
17
17
  export const jestTransformPackages = [
18
18
  '@planningcenter/chat-react-native',
19
- '@planningcenter/emoji-keyboard',
20
19
  '@fortawesome',
20
+ 'rn-emoji-keyboard',
21
21
  ]
@@ -31,7 +31,7 @@ export function AgeCheckUnderageScreen({ contactEmail }: AgeCheckUnderageScreenP
31
31
 
32
32
  <View style={styles.content}>
33
33
  <Heading variant="h3" style={styles.baseText}>
34
- Chat is only available for users 13 and older.
34
+ Your age does not meet the minimum safety requirements to use chat.
35
35
  </Heading>
36
36
  <Text variant="tertiary" style={styles.baseText}>
37
37
  If you submitted the wrong birthdate by accident,{` `}
@@ -1,11 +1,15 @@
1
- import { EmojiKeyboard, emojisByCategory, type EmojiType } from '@planningcenter/emoji-keyboard'
2
1
  import React, { useCallback } from 'react'
3
2
  import { StyleSheet, View } from 'react-native'
3
+ import { EmojiKeyboard, type EmojiType, type EmojisByCategory } from 'rn-emoji-keyboard'
4
+ // rn-emoji-keyboard exposes no public exclusion API, so we reach into its
5
+ // internal src/ tree for the emoji JSON. Version is pinned in package.json
6
+ // — verify this path still resolves before bumping rn-emoji-keyboard.
7
+ import emojiData from 'rn-emoji-keyboard/src/assets/emojis.json'
4
8
  import { useTheme } from '../../hooks'
5
9
 
6
10
  const BLOCKED_EMOJIS = new Set(['🖕'])
7
11
 
8
- const filteredEmojis = emojisByCategory.map(category => ({
12
+ const filteredEmojis = (emojiData as EmojisByCategory[]).map(category => ({
9
13
  ...category,
10
14
  data: category.data.filter(e => !BLOCKED_EMOJIS.has(e.emoji)),
11
15
  }))