@planningcenter/chat-react-native 3.39.1-rc.0 → 3.39.1-rc.2
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_form.d.ts +2 -1
- package/build/components/conversation/message_form.d.ts.map +1 -1
- package/build/components/conversation/message_form.js +2 -1
- package/build/components/conversation/message_form.js.map +1 -1
- package/build/hooks/use_attachment_uploader.d.ts +2 -1
- package/build/hooks/use_attachment_uploader.d.ts.map +1 -1
- package/build/hooks/use_attachment_uploader.js +9 -2
- package/build/hooks/use_attachment_uploader.js.map +1 -1
- package/build/hooks/use_video_moderation_jolt_events.d.ts +4 -0
- package/build/hooks/use_video_moderation_jolt_events.d.ts.map +1 -0
- package/build/hooks/use_video_moderation_jolt_events.js +33 -0
- package/build/hooks/use_video_moderation_jolt_events.js.map +1 -0
- package/build/screens/conversation_screen.d.ts.map +1 -1
- package/build/screens/conversation_screen.js +5 -3
- package/build/screens/conversation_screen.js.map +1 -1
- package/build/screens/notification_settings_screen.d.ts.map +1 -1
- package/build/screens/notification_settings_screen.js +2 -1
- package/build/screens/notification_settings_screen.js.map +1 -1
- package/build/screens/preferred_app_selection_screen.d.ts.map +1 -1
- package/build/screens/preferred_app_selection_screen.js +2 -1
- package/build/screens/preferred_app_selection_screen.js.map +1 -1
- package/build/utils/chat_type_label.d.ts +2 -0
- package/build/utils/chat_type_label.d.ts.map +1 -0
- package/build/utils/chat_type_label.js +6 -0
- package/build/utils/chat_type_label.js.map +1 -0
- package/build/utils/index.d.ts +1 -0
- package/build/utils/index.d.ts.map +1 -1
- package/build/utils/index.js +1 -0
- package/build/utils/index.js.map +1 -1
- package/package.json +2 -2
- package/src/__tests__/hooks/use_attachment_uploader.test.tsx +35 -11
- package/src/__tests__/hooks/use_video_moderation_jolt_events.test.tsx +81 -0
- package/src/components/conversation/message_form.tsx +3 -0
- package/src/hooks/use_attachment_uploader.ts +13 -3
- package/src/hooks/use_video_moderation_jolt_events.ts +46 -0
- package/src/screens/conversation_screen.tsx +6 -0
- package/src/screens/notification_settings_screen.tsx +2 -1
- package/src/screens/preferred_app_selection_screen.tsx +2 -1
- package/src/utils/chat_type_label.ts +4 -0
- package/src/utils/index.ts +1 -0
|
@@ -21,9 +21,11 @@ export interface FileError {
|
|
|
21
21
|
export function useAttachmentUploader({
|
|
22
22
|
conversationId,
|
|
23
23
|
draftAttachments,
|
|
24
|
+
onFlaggedInMessageForm,
|
|
24
25
|
}: {
|
|
25
26
|
conversationId: number
|
|
26
27
|
draftAttachments?: FileAttachment[]
|
|
28
|
+
onFlaggedInMessageForm?: (id: string) => void
|
|
27
29
|
}) {
|
|
28
30
|
const apiClient = useApiClient()
|
|
29
31
|
const uploadApi = useUploadClient()
|
|
@@ -31,18 +33,26 @@ export function useAttachmentUploader({
|
|
|
31
33
|
useChatConfiguration()
|
|
32
34
|
const maxFileSizeInMb = Number((maxFileSizeInBytes / (1024 * 1024)).toFixed(1))
|
|
33
35
|
const [attachments, setAttachments] = useState<FileAttachment[]>(() => draftAttachments || [])
|
|
36
|
+
const attachmentsRef = useRef<FileAttachment[]>(draftAttachments ?? [])
|
|
34
37
|
const uploadState = useRef<FileUploadState>({})
|
|
35
38
|
const [lastUploadId, setLastUploadId] = useState<string>()
|
|
36
39
|
const numberOfAttachments = attachments.length
|
|
37
40
|
const [errorMessage, setErrorMessage] = useState<string | null>(null)
|
|
38
41
|
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
attachmentsRef.current = attachments
|
|
44
|
+
}, [attachments])
|
|
45
|
+
|
|
39
46
|
const { data: currentPerson } = useApiGet<CurrentPersonResource>(currentPersonRequestArgs)
|
|
40
47
|
const joltChannel = useJoltChannel(`chat.people.${currentPerson?.id}`, Boolean(currentPerson?.id))
|
|
41
48
|
useJoltEvent(joltChannel, 'attachment.flagged', (e: AttachmentFlaggedEvent) => {
|
|
42
49
|
const flaggedId = e.data.data.attachment_id
|
|
43
|
-
|
|
44
|
-
prev
|
|
45
|
-
|
|
50
|
+
if (attachmentsRef.current.some(a => a.id === flaggedId)) {
|
|
51
|
+
setAttachments(prev =>
|
|
52
|
+
prev.map(a => (a.id === flaggedId ? { ...a, flagged: true, status: 'error' } : a))
|
|
53
|
+
)
|
|
54
|
+
onFlaggedInMessageForm?.(flaggedId)
|
|
55
|
+
}
|
|
46
56
|
})
|
|
47
57
|
|
|
48
58
|
const handleFilesAttached = useCallback(
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import debounce from 'lodash/debounce'
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef } from 'react'
|
|
3
|
+
import { Alert } from 'react-native'
|
|
4
|
+
import type { CurrentPersonResource } from '../types'
|
|
5
|
+
import type { AttachmentFlaggedEvent } from '../types/jolt_events/attachment_events'
|
|
6
|
+
import { useApiGet } from './use_api'
|
|
7
|
+
import { currentPersonRequestArgs } from './use_current_person'
|
|
8
|
+
import { useJoltChannel, useJoltEvent } from './use_jolt'
|
|
9
|
+
|
|
10
|
+
const DEBOUNCE_MS = 500
|
|
11
|
+
|
|
12
|
+
export function useVideoModerationJoltEvents() {
|
|
13
|
+
const { data: currentPerson } = useApiGet<CurrentPersonResource>(currentPersonRequestArgs)
|
|
14
|
+
const joltChannel = useJoltChannel(`chat.people.${currentPerson?.id}`, Boolean(currentPerson?.id))
|
|
15
|
+
const pendingIdsRef = useRef<Set<string>>(new Set())
|
|
16
|
+
|
|
17
|
+
const flushAlert = useMemo(
|
|
18
|
+
() =>
|
|
19
|
+
debounce(() => {
|
|
20
|
+
const count = pendingIdsRef.current.size
|
|
21
|
+
if (count === 0) return
|
|
22
|
+
pendingIdsRef.current.clear()
|
|
23
|
+
Alert.alert(
|
|
24
|
+
'Video removed',
|
|
25
|
+
count === 1
|
|
26
|
+
? "A video you uploaded was flagged because it doesn't meet our content guidelines."
|
|
27
|
+
: "Some videos you uploaded were flagged because they don't meet our content guidelines."
|
|
28
|
+
)
|
|
29
|
+
}, DEBOUNCE_MS),
|
|
30
|
+
[]
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
useEffect(() => () => flushAlert.cancel(), [flushAlert])
|
|
34
|
+
|
|
35
|
+
const markHandledByMessageForm = useCallback((id: string) => {
|
|
36
|
+
pendingIdsRef.current.delete(id)
|
|
37
|
+
}, [])
|
|
38
|
+
|
|
39
|
+
useJoltEvent(joltChannel, 'attachment.flagged', (e: AttachmentFlaggedEvent) => {
|
|
40
|
+
if (!e.data.data.content_type.startsWith('video/')) return
|
|
41
|
+
pendingIdsRef.current.add(e.data.data.attachment_id)
|
|
42
|
+
flushAlert()
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
return { markHandledByMessageForm }
|
|
46
|
+
}
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
normalizeAnalyticsMetadata,
|
|
39
39
|
usePublishProductAnalyticsEvent,
|
|
40
40
|
} from '../hooks/use_product_analytics'
|
|
41
|
+
import { useVideoModerationJoltEvents } from '../hooks/use_video_moderation_jolt_events'
|
|
41
42
|
import { ConversationResource } from '../types/resources/conversation'
|
|
42
43
|
import { ConversationBadgeResource } from '../types/resources/conversation_badge'
|
|
43
44
|
import { MessageResource } from '../types/resources/message'
|
|
@@ -94,6 +95,7 @@ function ConversationScreenContent({ route }: ConversationScreenProps) {
|
|
|
94
95
|
})
|
|
95
96
|
useConversationJoltEvents({ conversationId: conversation_id })
|
|
96
97
|
useConversationMessagesJoltEvents({ conversationId: conversation_id })
|
|
98
|
+
const { markHandledByMessageForm } = useVideoModerationJoltEvents()
|
|
97
99
|
useEnsureConversationsRouteExists()
|
|
98
100
|
useMarkLatestMessageRead({ conversation, messages })
|
|
99
101
|
const messagesWithSeparators = groupMessages({
|
|
@@ -223,6 +225,7 @@ function ConversationScreenContent({ route }: ConversationScreenProps) {
|
|
|
223
225
|
replyRootAuthorFirstName={replyRootAuthorFirstName}
|
|
224
226
|
replyRootId={reply_root_id}
|
|
225
227
|
currentlyEditingMessage={currentlyEditingMessage}
|
|
228
|
+
onFlaggedInMessageForm={markHandledByMessageForm}
|
|
226
229
|
/>
|
|
227
230
|
</KeyboardView>
|
|
228
231
|
</View>
|
|
@@ -235,6 +238,7 @@ interface ConversationBottomBarProps {
|
|
|
235
238
|
replyRootAuthorFirstName: string | undefined
|
|
236
239
|
replyRootId: string | null | undefined
|
|
237
240
|
currentlyEditingMessage: MessageResource | undefined
|
|
241
|
+
onFlaggedInMessageForm?: (id: string) => void
|
|
238
242
|
}
|
|
239
243
|
|
|
240
244
|
function ConversationBottomBar({
|
|
@@ -243,6 +247,7 @@ function ConversationBottomBar({
|
|
|
243
247
|
replyRootAuthorFirstName,
|
|
244
248
|
replyRootId,
|
|
245
249
|
currentlyEditingMessage,
|
|
250
|
+
onFlaggedInMessageForm,
|
|
246
251
|
}: ConversationBottomBarProps) {
|
|
247
252
|
if (conversation.disabled)
|
|
248
253
|
return <ConversationDisabledBanner disabledReason={conversation.disabledReason} />
|
|
@@ -253,6 +258,7 @@ function ConversationBottomBar({
|
|
|
253
258
|
conversation={conversation}
|
|
254
259
|
replyRootId={replyRootId}
|
|
255
260
|
currentlyEditingMessage={currentlyEditingMessage}
|
|
261
|
+
onFlaggedInMessageForm={onFlaggedInMessageForm}
|
|
256
262
|
key={
|
|
257
263
|
currentlyEditingMessage
|
|
258
264
|
? `edit-message-form-${currentlyEditingMessage.id}`
|
|
@@ -7,6 +7,7 @@ import { Badge, Heading, Icon, Text } from '../components'
|
|
|
7
7
|
import { HeaderTextButton } from '../components/display/platform_modal_header_buttons'
|
|
8
8
|
import { useTheme } from '../hooks'
|
|
9
9
|
import { isDefined } from '../types'
|
|
10
|
+
import { chatTypeLabel } from '../utils'
|
|
10
11
|
import { useGroups } from './notification_settings/hooks/groups'
|
|
11
12
|
import { useChatTypes } from './preferred_app/hooks/use_chat_types'
|
|
12
13
|
|
|
@@ -90,7 +91,7 @@ export function NotificationSettingsScreen({}: NotificationSettingsScreenProps)
|
|
|
90
91
|
...chatTypes.map(type => ({
|
|
91
92
|
type: SectionTypes.link,
|
|
92
93
|
data: {
|
|
93
|
-
title:
|
|
94
|
+
title: chatTypeLabel(type.title),
|
|
94
95
|
rightLabel: type.preferredApp,
|
|
95
96
|
onPress: () =>
|
|
96
97
|
navigation.navigate('PreferredAppSelection', {
|
|
@@ -6,6 +6,7 @@ import { Image, StyleSheet, View, type ViewStyle } from 'react-native'
|
|
|
6
6
|
import { Heading, Icon, Text } from '../components'
|
|
7
7
|
import { useApiClient, useTheme } from '../hooks'
|
|
8
8
|
import { useAppName } from '../hooks/use_app_name'
|
|
9
|
+
import { chatTypeLabel } from '../utils'
|
|
9
10
|
import { throwResponseError } from '../utils/response_error'
|
|
10
11
|
import { PreferredApp, useChatTypes } from './preferred_app/hooks/use_chat_types'
|
|
11
12
|
|
|
@@ -41,7 +42,7 @@ export function PreferredAppSelectionScreen({ route }: PreferredAppSelectionScre
|
|
|
41
42
|
updateChatType(app)
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
const sectionTitle =
|
|
45
|
+
const sectionTitle = chatType ? chatTypeLabel(chatType.title) : undefined
|
|
45
46
|
|
|
46
47
|
return (
|
|
47
48
|
<View style={styles.container}>
|