@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.
- package/build/components/conversation/jump_to_bottom_button.d.ts +1 -2
- package/build/components/conversation/jump_to_bottom_button.d.ts.map +1 -1
- package/build/components/conversation/jump_to_bottom_button.js +7 -39
- package/build/components/conversation/jump_to_bottom_button.js.map +1 -1
- package/build/components/conversation/message_form.d.ts.map +1 -1
- package/build/components/conversation/message_form.js +7 -19
- package/build/components/conversation/message_form.js.map +1 -1
- package/build/components/conversation/reply_shadow_message.d.ts +2 -1
- package/build/components/conversation/reply_shadow_message.d.ts.map +1 -1
- package/build/components/conversation/reply_shadow_message.js.map +1 -1
- package/build/components/conversations/conversations.d.ts.map +1 -1
- package/build/components/conversations/conversations.js +16 -6
- package/build/components/conversations/conversations.js.map +1 -1
- package/build/contexts/conversation_context.d.ts +1 -8
- package/build/contexts/conversation_context.d.ts.map +1 -1
- package/build/contexts/conversation_context.js +3 -21
- package/build/contexts/conversation_context.js.map +1 -1
- package/build/hooks/use_attachment_uploader.d.ts.map +1 -1
- package/build/hooks/use_attachment_uploader.js +0 -9
- package/build/hooks/use_attachment_uploader.js.map +1 -1
- package/build/hooks/use_conversation_messages.d.ts +6 -15
- package/build/hooks/use_conversation_messages.d.ts.map +1 -1
- package/build/hooks/use_conversation_messages.js +9 -62
- package/build/hooks/use_conversation_messages.js.map +1 -1
- package/build/hooks/use_conversation_messages_jolt_events.d.ts.map +1 -1
- package/build/hooks/use_conversation_messages_jolt_events.js +4 -4
- package/build/hooks/use_conversation_messages_jolt_events.js.map +1 -1
- package/build/hooks/use_conversations_actions.d.ts +0 -5
- package/build/hooks/use_conversations_actions.d.ts.map +1 -1
- package/build/hooks/use_conversations_actions.js +0 -12
- package/build/hooks/use_conversations_actions.js.map +1 -1
- package/build/hooks/use_features.d.ts +0 -2
- package/build/hooks/use_features.d.ts.map +1 -1
- package/build/hooks/use_features.js +0 -2
- package/build/hooks/use_features.js.map +1 -1
- package/build/hooks/use_mark_latest_message_read.d.ts +1 -1
- package/build/hooks/use_mark_latest_message_read.d.ts.map +1 -1
- package/build/hooks/use_mark_latest_message_read.js +1 -17
- package/build/hooks/use_mark_latest_message_read.js.map +1 -1
- package/build/hooks/use_suspense_api.d.ts +0 -1
- package/build/hooks/use_suspense_api.d.ts.map +1 -1
- package/build/hooks/use_suspense_api.js +1 -1
- package/build/hooks/use_suspense_api.js.map +1 -1
- package/build/hooks/use_upload_client.d.ts +0 -4
- package/build/hooks/use_upload_client.d.ts.map +1 -1
- package/build/hooks/use_upload_client.js +1 -13
- package/build/hooks/use_upload_client.js.map +1 -1
- package/build/jest.js +1 -1
- package/build/jest.js.map +1 -1
- package/build/screens/age_check/age_check_underage_screen.js +1 -1
- package/build/screens/age_check/age_check_underage_screen.js.map +1 -1
- package/build/screens/avatar_picker/emoji_tab.d.ts.map +1 -1
- package/build/screens/avatar_picker/emoji_tab.js +6 -2
- package/build/screens/avatar_picker/emoji_tab.js.map +1 -1
- package/build/screens/conversation_screen.d.ts +0 -1
- package/build/screens/conversation_screen.d.ts.map +1 -1
- package/build/screens/conversation_screen.js +48 -95
- package/build/screens/conversation_screen.js.map +1 -1
- package/build/types/jolt_events/index.d.ts +1 -3
- package/build/types/jolt_events/index.d.ts.map +1 -1
- package/build/types/jolt_events/index.js.map +1 -1
- package/build/utils/cache/messages_cache.d.ts +0 -1
- package/build/utils/cache/messages_cache.d.ts.map +1 -1
- package/build/utils/cache/messages_cache.js +0 -4
- package/build/utils/cache/messages_cache.js.map +1 -1
- package/build/utils/group_messages.d.ts +2 -9
- package/build/utils/group_messages.d.ts.map +1 -1
- package/build/utils/group_messages.js +1 -20
- package/build/utils/group_messages.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/hooks/use_attachment_uploader.test.tsx +0 -36
- package/src/__tests__/jest.ts +1 -1
- package/src/components/conversation/jump_to_bottom_button.tsx +8 -57
- package/src/components/conversation/message_form.tsx +7 -21
- package/src/components/conversation/reply_shadow_message.tsx +1 -1
- package/src/components/conversations/conversations.tsx +16 -9
- package/src/contexts/conversation_context.tsx +2 -30
- package/src/hooks/use_attachment_uploader.ts +1 -14
- package/src/hooks/use_conversation_messages.ts +20 -120
- package/src/hooks/use_conversation_messages_jolt_events.ts +3 -4
- package/src/hooks/use_conversations_actions.ts +0 -15
- package/src/hooks/use_features.ts +0 -2
- package/src/hooks/use_mark_latest_message_read.ts +2 -16
- package/src/hooks/use_suspense_api.ts +1 -1
- package/src/hooks/use_upload_client.ts +1 -19
- package/src/jest.ts +1 -1
- package/src/screens/age_check/age_check_underage_screen.tsx +1 -1
- package/src/screens/avatar_picker/emoji_tab.tsx +6 -2
- package/src/screens/conversation_screen.tsx +76 -184
- package/src/types/jolt_events/index.ts +0 -3
- package/src/utils/__tests__/group_messages.test.ts +0 -71
- package/src/utils/cache/messages_cache.ts +0 -5
- package/src/utils/group_messages.ts +2 -42
- package/build/components/conversation/message_list.d.ts +0 -10
- package/build/components/conversation/message_list.d.ts.map +0 -1
- package/build/components/conversation/message_list.js +0 -13
- package/build/components/conversation/message_list.js.map +0 -1
- package/build/components/conversation/unread_divider.d.ts +0 -6
- package/build/components/conversation/unread_divider.d.ts.map +0 -1
- package/build/components/conversation/unread_divider.js +0 -59
- package/build/components/conversation/unread_divider.js.map +0 -1
- package/build/components/conversations/conversations_blank_state.d.ts +0 -8
- package/build/components/conversations/conversations_blank_state.d.ts.map +0 -1
- package/build/components/conversations/conversations_blank_state.js +0 -25
- package/build/components/conversations/conversations_blank_state.js.map +0 -1
- package/build/hooks/use_flat_list_viewability.d.ts +0 -20
- package/build/hooks/use_flat_list_viewability.d.ts.map +0 -1
- package/build/hooks/use_flat_list_viewability.js +0 -30
- package/build/hooks/use_flat_list_viewability.js.map +0 -1
- package/build/hooks/use_jump_to_bottom_action.d.ts +0 -9
- package/build/hooks/use_jump_to_bottom_action.d.ts.map +0 -1
- package/build/hooks/use_jump_to_bottom_action.js +0 -62
- package/build/hooks/use_jump_to_bottom_action.js.map +0 -1
- package/build/hooks/use_jump_to_unread_anchor.d.ts +0 -20
- package/build/hooks/use_jump_to_unread_anchor.d.ts.map +0 -1
- package/build/hooks/use_jump_to_unread_anchor.js +0 -53
- package/build/hooks/use_jump_to_unread_anchor.js.map +0 -1
- package/build/hooks/use_jump_to_unread_gates.d.ts +0 -5
- package/build/hooks/use_jump_to_unread_gates.d.ts.map +0 -1
- package/build/hooks/use_jump_to_unread_gates.js +0 -10
- package/build/hooks/use_jump_to_unread_gates.js.map +0 -1
- package/build/hooks/use_scroll_tracking.d.ts +0 -13
- package/build/hooks/use_scroll_tracking.d.ts.map +0 -1
- package/build/hooks/use_scroll_tracking.js +0 -45
- package/build/hooks/use_scroll_tracking.js.map +0 -1
- package/build/hooks/use_track_highest_seen_message.d.ts +0 -4
- package/build/hooks/use_track_highest_seen_message.d.ts.map +0 -1
- package/build/hooks/use_track_highest_seen_message.js +0 -35
- package/build/hooks/use_track_highest_seen_message.js.map +0 -1
- package/build/types/jolt_events/attachment_events.d.ts +0 -14
- package/build/types/jolt_events/attachment_events.d.ts.map +0 -1
- package/build/types/jolt_events/attachment_events.js +0 -2
- package/build/types/jolt_events/attachment_events.js.map +0 -1
- package/build/utils/conversation_messages.d.ts +0 -10
- package/build/utils/conversation_messages.d.ts.map +0 -1
- package/build/utils/conversation_messages.js +0 -22
- package/build/utils/conversation_messages.js.map +0 -1
- package/build/utils/highest_seen_tracker.d.ts +0 -12
- package/build/utils/highest_seen_tracker.d.ts.map +0 -1
- package/build/utils/highest_seen_tracker.js +0 -37
- package/build/utils/highest_seen_tracker.js.map +0 -1
- package/build/utils/message_viewability.d.ts +0 -24
- package/build/utils/message_viewability.d.ts.map +0 -1
- package/build/utils/message_viewability.js +0 -29
- package/build/utils/message_viewability.js.map +0 -1
- package/build/utils/unread_divider_helpers.d.ts +0 -18
- package/build/utils/unread_divider_helpers.d.ts.map +0 -1
- package/build/utils/unread_divider_helpers.js +0 -13
- package/build/utils/unread_divider_helpers.js.map +0 -1
- package/src/__tests__/hooks/use_conversation_messages.test.tsx +0 -109
- package/src/__tests__/hooks/use_mark_latest_message_read.test.tsx +0 -154
- package/src/__tests__/utils/cache/messages_cache.test.ts +0 -54
- package/src/components/conversation/__tests__/message_list.test.tsx +0 -14
- package/src/components/conversation/message_list.tsx +0 -42
- package/src/components/conversation/unread_divider.tsx +0 -90
- package/src/components/conversations/conversations_blank_state.tsx +0 -42
- package/src/hooks/use_flat_list_viewability.ts +0 -50
- package/src/hooks/use_jump_to_bottom_action.ts +0 -75
- package/src/hooks/use_jump_to_unread_anchor.ts +0 -68
- package/src/hooks/use_jump_to_unread_gates.ts +0 -10
- package/src/hooks/use_scroll_tracking.ts +0 -64
- package/src/hooks/use_track_highest_seen_message.ts +0 -43
- package/src/types/jolt_events/attachment_events.ts +0 -14
- package/src/utils/__tests__/conversation_messages.test.ts +0 -105
- package/src/utils/__tests__/highest_seen_tracker.test.ts +0 -82
- package/src/utils/__tests__/message_viewability.test.ts +0 -168
- package/src/utils/__tests__/unread_divider_helpers.test.ts +0 -85
- package/src/utils/conversation_messages.ts +0 -37
- package/src/utils/highest_seen_tracker.ts +0 -42
- package/src/utils/message_viewability.ts +0 -49
- 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
|
|
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
|
|
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}
|
|
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
|
|
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
|
|
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
|
|
415
|
-
|
|
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
|
|
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
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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 {
|
|
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 }:
|
|
36
|
-
opts?:
|
|
7
|
+
{ conversation_id, reply_root_id }: { conversation_id: number; reply_root_id?: string | null },
|
|
8
|
+
opts?: SuspensePaginatorOptions
|
|
37
9
|
) => {
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
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 =
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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
|
-
|
|
57
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
@@ -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
|
-
|
|
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 =
|
|
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
|
}))
|