@linktr.ee/messaging-react 3.9.0-rc-1783580190 → 3.9.1-rc-1783576271
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/dist/Card-CyBRO0Ex.cjs +2 -0
- package/dist/Card-CyBRO0Ex.cjs.map +1 -0
- package/dist/Card-DYNxwKX6.js +132 -0
- package/dist/Card-DYNxwKX6.js.map +1 -0
- package/dist/Card-DmfP-4oF.cjs +2 -0
- package/dist/Card-DmfP-4oF.cjs.map +1 -0
- package/dist/Card-PBfzY620.cjs +2 -0
- package/dist/Card-PBfzY620.cjs.map +1 -0
- package/dist/Card-dZkL-om-.js +163 -0
- package/dist/Card-dZkL-om-.js.map +1 -0
- package/dist/Card-xR3OTXRB.js +107 -0
- package/dist/Card-xR3OTXRB.js.map +1 -0
- package/dist/LockedThumbnail-BblFdDJp.js +220 -0
- package/dist/LockedThumbnail-BblFdDJp.js.map +1 -0
- package/dist/LockedThumbnail-eE8ljauK.cjs +2 -0
- package/dist/LockedThumbnail-eE8ljauK.cjs.map +1 -0
- package/dist/assets/index.css +1 -1
- package/dist/index-Bxezkdl6.cjs +2 -0
- package/dist/index-Bxezkdl6.cjs.map +1 -0
- package/dist/{index-CmVOs9Jy.js → index-FY6hmphi.js} +1198 -1226
- package/dist/index-FY6hmphi.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +98 -161
- package/dist/index.js +1 -1
- package/dist/testing.d.ts +0 -1
- package/package.json +2 -2
- package/src/components/ChannelList/CustomChannelPreview.test.tsx +0 -95
- package/src/components/ChannelList/CustomChannelPreview.tsx +9 -21
- package/src/components/CustomMessage/CustomMessage.stories.tsx +1 -54
- package/src/components/CustomMessage/MessageTag.tsx +0 -8
- package/src/components/CustomMessage/SentMessageDeliveryStatus.test.tsx +29 -11
- package/src/components/CustomMessage/SentMessageDeliveryStatus.tsx +22 -26
- package/src/components/CustomMessage/context.tsx +1 -1
- package/src/components/CustomMessage/index.tsx +42 -76
- package/src/components/CustomMessageInput/CustomMessageInput.stories.tsx +3 -5
- package/src/components/LockedAttachment/LockedAttachment.stories.tsx +450 -0
- package/src/components/LockedAttachment/components/Composer/Card.tsx +221 -0
- package/src/components/LockedAttachment/components/Composer/index.ts +2 -0
- package/src/components/LockedAttachment/components/Received/Card.tsx +191 -0
- package/src/components/{CustomMessage/LockedAttachment/components/Media → LockedAttachment/components/Received}/CardActions.tsx +2 -9
- package/src/components/LockedAttachment/components/Received/index.ts +2 -0
- package/src/components/LockedAttachment/components/Sent/Card.tsx +177 -0
- package/src/components/LockedAttachment/components/Sent/index.ts +2 -0
- package/src/components/{CustomMessage/LockedAttachment → LockedAttachment}/components/_shared/CardBody.tsx +7 -8
- package/src/components/{CustomMessage/LockedAttachment → LockedAttachment}/components/_shared/GalleryThumbnail.tsx +39 -31
- package/src/components/LockedAttachment/components/_shared/LockBadge.tsx +39 -0
- package/src/components/{CustomMessage/LockedAttachment/components/_shared/MediaShell.tsx → LockedAttachment/components/_shared/LockedCardShell.tsx} +8 -8
- package/src/components/{CustomMessage/LockedAttachment/components/_shared/SingleThumbnail.tsx → LockedAttachment/components/_shared/LockedThumbnail.tsx} +7 -11
- package/src/components/LockedAttachment/index.tsx +63 -0
- package/src/components/LockedAttachment/types.ts +51 -0
- package/src/index.ts +8 -3
- package/src/stream-custom-data.ts +0 -1
- package/src/styles.css +0 -12
- package/src/types.ts +1 -1
- package/dist/Card-415OfRhw.cjs +0 -2
- package/dist/Card-415OfRhw.cjs.map +0 -1
- package/dist/Card-Q9e8pbMP.js +0 -694
- package/dist/Card-Q9e8pbMP.js.map +0 -1
- package/dist/Card-dg4NGPoC.cjs +0 -2
- package/dist/Card-dg4NGPoC.cjs.map +0 -1
- package/dist/Card-qS17qnN0.js +0 -103
- package/dist/Card-qS17qnN0.js.map +0 -1
- package/dist/LoadingDots-Ct3WmVlT.js +0 -59
- package/dist/LoadingDots-Ct3WmVlT.js.map +0 -1
- package/dist/LoadingDots-D9GGphgM.cjs +0 -2
- package/dist/LoadingDots-D9GGphgM.cjs.map +0 -1
- package/dist/index-ChRwg_g6.cjs +0 -2
- package/dist/index-ChRwg_g6.cjs.map +0 -1
- package/dist/index-CmVOs9Jy.js.map +0 -1
- package/src/components/CustomMessage/LockedAttachment/LockedAttachment.stories.tsx +0 -541
- package/src/components/CustomMessage/LockedAttachment/LockedAttachment.test.tsx +0 -117
- package/src/components/CustomMessage/LockedAttachment/components/Media/Card.tsx +0 -358
- package/src/components/CustomMessage/LockedAttachment/components/Media/types.ts +0 -79
- package/src/components/CustomMessage/LockedAttachment/components/Text/Card.tsx +0 -153
- package/src/components/CustomMessage/LockedAttachment/components/Text/types.ts +0 -58
- package/src/components/CustomMessage/LockedAttachment/components/_shared/CardThumbnail.tsx +0 -92
- package/src/components/CustomMessage/LockedAttachment/components/_shared/LoadingDots.tsx +0 -36
- package/src/components/CustomMessage/LockedAttachment/components/_shared/LockBadge.tsx +0 -63
- package/src/components/CustomMessage/LockedAttachment/components/_shared/PreviewToggle.tsx +0 -58
- package/src/components/CustomMessage/LockedAttachment/components/_shared/PriceLockRow.tsx +0 -21
- package/src/components/CustomMessage/LockedAttachment/components/_shared/PurchaseStatusBadge.tsx +0 -74
- package/src/components/CustomMessage/LockedAttachment/components/_shared/SkeletonTextLines.tsx +0 -29
- package/src/components/CustomMessage/LockedAttachment/components/_shared/TextShell.tsx +0 -28
- package/src/components/CustomMessage/LockedAttachment/components/_shared/usePreviewToggle.test.ts +0 -67
- package/src/components/CustomMessage/LockedAttachment/components/_shared/usePreviewToggle.ts +0 -89
- package/src/components/CustomMessage/LockedAttachment/components/_shared/useReceivedSource.ts +0 -83
- package/src/components/CustomMessage/LockedAttachment/index.tsx +0 -105
- package/src/components/CustomMessage/LockedAttachment/types.ts +0 -44
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
2
|
import {
|
|
3
3
|
useChannelStateContext,
|
|
4
|
-
useChatContext,
|
|
5
4
|
useMessageContext,
|
|
6
5
|
} from 'stream-chat-react'
|
|
7
6
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
@@ -13,14 +12,13 @@ import { SentMessageDeliveryStatus } from './SentMessageDeliveryStatus'
|
|
|
13
12
|
// stream-chat-react's `MessageStatus` owns the outer gating (own message, not
|
|
14
13
|
// error, not in a thread) and only exposes a terminal state once a message is
|
|
15
14
|
// stored — Stream's tested code. What this component owns is the "Delivered"
|
|
16
|
-
// label wired into the terminal slots (sent/delivered/read), its "
|
|
17
|
-
// message" gate derived from live channel state, and suppressing
|
|
18
|
-
// spinner. Mock `MessageStatus` to render the slots it was handed,
|
|
19
|
-
// context hooks the label reads.
|
|
15
|
+
// label wired into the terminal slots (sent/delivered/read), its "latest
|
|
16
|
+
// message in the thread" gate derived from live channel state, and suppressing
|
|
17
|
+
// the sending spinner. Mock `MessageStatus` to render the slots it was handed,
|
|
18
|
+
// and the context hooks the label reads.
|
|
20
19
|
vi.mock('stream-chat-react', () => ({
|
|
21
20
|
useMessageContext: vi.fn(),
|
|
22
21
|
useChannelStateContext: vi.fn(),
|
|
23
|
-
useChatContext: vi.fn(),
|
|
24
22
|
MessageStatus: ({
|
|
25
23
|
MessageSentStatus,
|
|
26
24
|
MessageDeliveredStatus,
|
|
@@ -46,10 +44,10 @@ vi.mock('stream-chat-react', () => ({
|
|
|
46
44
|
|
|
47
45
|
const mockedUseMessageContext = vi.mocked(useMessageContext)
|
|
48
46
|
const mockedUseChannelStateContext = vi.mocked(useChannelStateContext)
|
|
49
|
-
const mockedUseChatContext = vi.mocked(useChatContext)
|
|
50
47
|
|
|
51
48
|
// A run where the viewer ('me') sent the first and last messages, with an
|
|
52
|
-
// incoming message in between — the
|
|
49
|
+
// incoming message in between — the label must sit on the last message ('own-2')
|
|
50
|
+
// and clear from the earlier one.
|
|
53
51
|
const CHANNEL_MESSAGES = [
|
|
54
52
|
{ id: 'own-1', user: { id: 'me' } },
|
|
55
53
|
{ id: 'their-1', user: { id: 'them' } },
|
|
@@ -60,13 +58,12 @@ const TERMINAL_SLOTS = ['sent-slot', 'delivered-slot', 'read-slot'] as const
|
|
|
60
58
|
|
|
61
59
|
describe('SentMessageDeliveryStatus', () => {
|
|
62
60
|
beforeEach(() => {
|
|
63
|
-
mockedUseChatContext.mockReturnValue({ client: { user: { id: 'me' } } } as never)
|
|
64
61
|
mockedUseChannelStateContext.mockReturnValue({
|
|
65
62
|
messages: CHANNEL_MESSAGES,
|
|
66
63
|
} as never)
|
|
67
64
|
})
|
|
68
65
|
|
|
69
|
-
it('renders "Delivered" in every terminal slot for the last
|
|
66
|
+
it('renders "Delivered" in every terminal slot for the last message in the thread', () => {
|
|
70
67
|
// delivered/read fire ahead of sent in receipt channels, so all three
|
|
71
68
|
// must show the label — never Stream's default delivered icon / read
|
|
72
69
|
// avatars.
|
|
@@ -83,7 +80,7 @@ describe('SentMessageDeliveryStatus', () => {
|
|
|
83
80
|
}
|
|
84
81
|
})
|
|
85
82
|
|
|
86
|
-
it('renders nothing for an earlier own message
|
|
83
|
+
it('renders nothing for an earlier own message once a later message exists', () => {
|
|
87
84
|
mockedUseMessageContext.mockReturnValue({
|
|
88
85
|
message: { id: 'own-1' },
|
|
89
86
|
} as never)
|
|
@@ -95,6 +92,27 @@ describe('SentMessageDeliveryStatus', () => {
|
|
|
95
92
|
}
|
|
96
93
|
})
|
|
97
94
|
|
|
95
|
+
it('renders nothing for an own message once a reply arrives after it', () => {
|
|
96
|
+
// 'own-2' was the viewer's last sent message, but 'their-1' is now the
|
|
97
|
+
// latest message in the thread — the label must clear from the bubble.
|
|
98
|
+
mockedUseChannelStateContext.mockReturnValue({
|
|
99
|
+
messages: [
|
|
100
|
+
{ id: 'own-1', user: { id: 'me' } },
|
|
101
|
+
{ id: 'own-2', user: { id: 'me' } },
|
|
102
|
+
{ id: 'their-1', user: { id: 'them' } },
|
|
103
|
+
],
|
|
104
|
+
} as never)
|
|
105
|
+
mockedUseMessageContext.mockReturnValue({
|
|
106
|
+
message: { id: 'own-2' },
|
|
107
|
+
} as never)
|
|
108
|
+
|
|
109
|
+
renderWithProviders(<SentMessageDeliveryStatus />)
|
|
110
|
+
|
|
111
|
+
for (const slot of TERMINAL_SLOTS) {
|
|
112
|
+
expect(screen.getByTestId(slot)).toBeEmptyDOMElement()
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
98
116
|
it('always suppresses the sending spinner', () => {
|
|
99
117
|
mockedUseMessageContext.mockReturnValue({
|
|
100
118
|
message: { id: 'own-2' },
|
|
@@ -3,7 +3,6 @@ import { useMemo } from 'react'
|
|
|
3
3
|
import {
|
|
4
4
|
MessageStatus,
|
|
5
5
|
useChannelStateContext,
|
|
6
|
-
useChatContext,
|
|
7
6
|
useMessageContext,
|
|
8
7
|
} from 'stream-chat-react'
|
|
9
8
|
|
|
@@ -15,21 +14,23 @@ import {
|
|
|
15
14
|
*
|
|
16
15
|
* 1. `MessageStatus` picks its `delivered`/`read` branches ahead of `sent`
|
|
17
16
|
* whenever Stream has `deliveredTo`/`readBy`, and — unlike `sent` — those
|
|
18
|
-
* branches aren't gated to the last
|
|
17
|
+
* branches aren't gated to the last message. So we override all three
|
|
19
18
|
* terminal slots with this same label (never Stream's default delivered
|
|
20
|
-
* icon / read avatars — Read is out of scope) and re-apply the "
|
|
21
|
-
* message" gate ourselves
|
|
19
|
+
* icon / read avatars — Read is out of scope) and re-apply the "latest
|
|
20
|
+
* message in the thread" gate ourselves: the label only shows beneath the
|
|
21
|
+
* viewer's sent message while it is still the last message in the channel,
|
|
22
|
+
* and disappears as soon as a reply arrives after it.
|
|
22
23
|
*
|
|
23
|
-
* 2. We derive "
|
|
24
|
-
*
|
|
25
|
-
* `areMessagePropsEqual`, which does not compare
|
|
26
|
-
* previous own row whose message/grouping props are unchanged (e.g.
|
|
27
|
-
* an intervening incoming message) never re-renders, so
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
24
|
+
* 2. We derive "latest message" from **live channel state**, not the message
|
|
25
|
+
* context's `lastOwnMessage`/`lastReceivedId`. Stream's `Message` is
|
|
26
|
+
* memoised by `areMessagePropsEqual`, which does not compare those props;
|
|
27
|
+
* a previous own row whose message/grouping props are unchanged (e.g.
|
|
28
|
+
* after an intervening incoming message) never re-renders, so the old
|
|
29
|
+
* bubble would keep showing "Delivered" beside the new one.
|
|
30
|
+
* `useChannelStateContext` gets a fresh `messages` array on every new
|
|
31
|
+
* message and, being a context, re-renders this leaf straight through the
|
|
32
|
+
* stale `Message`/`MessageStatus` memos — so the label reliably clears
|
|
33
|
+
* once a reply lands.
|
|
33
34
|
*
|
|
34
35
|
* Rendered inside stream's `.str-chat__message-status` span, which supplies
|
|
35
36
|
* the flex layout; icon and copy are styled via that span in `styles.css`.
|
|
@@ -37,18 +38,13 @@ import {
|
|
|
37
38
|
const DeliveredStatus = () => {
|
|
38
39
|
const { message } = useMessageContext('SentMessageDeliveryStatus')
|
|
39
40
|
const { messages } = useChannelStateContext('SentMessageDeliveryStatus')
|
|
40
|
-
const { client } = useChatContext('SentMessageDeliveryStatus')
|
|
41
41
|
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (messages[i].user?.id === ownUserId) return messages[i].id
|
|
47
|
-
}
|
|
48
|
-
return undefined
|
|
49
|
-
}, [messages, ownUserId])
|
|
42
|
+
const lastMessageId = useMemo(
|
|
43
|
+
() => messages?.[messages.length - 1]?.id,
|
|
44
|
+
[messages]
|
|
45
|
+
)
|
|
50
46
|
|
|
51
|
-
if (message.id !==
|
|
47
|
+
if (message.id !== lastMessageId) {
|
|
52
48
|
return null
|
|
53
49
|
}
|
|
54
50
|
|
|
@@ -73,8 +69,8 @@ const NoStatus = () => null
|
|
|
73
69
|
* terminal (sending/sent/delivered/read) state once the message is stored.
|
|
74
70
|
* Scope is "Delivered" only: the sending spinner is suppressed and all
|
|
75
71
|
* terminal states render {@link DeliveredStatus}, which supplies the label
|
|
76
|
-
* and the
|
|
77
|
-
* live channel state rather than the context's
|
|
72
|
+
* and the latest-message-in-thread gate (see there for why the gate is
|
|
73
|
+
* computed from live channel state rather than the context's props).
|
|
78
74
|
*/
|
|
79
75
|
export const SentMessageDeliveryStatus = () => (
|
|
80
76
|
<MessageStatus
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createContext, useContext } from 'react'
|
|
2
2
|
|
|
3
|
-
import { defaultLockedAttachmentContextValue, type LockedAttachmentContextValue } from '
|
|
3
|
+
import { defaultLockedAttachmentContextValue, type LockedAttachmentContextValue } from '../LockedAttachment/types'
|
|
4
4
|
|
|
5
5
|
export interface CustomMessageRegistry {
|
|
6
6
|
LockedAttachment: LockedAttachmentContextValue
|
|
@@ -32,17 +32,17 @@ import {
|
|
|
32
32
|
import { useMessageVote } from '../../hooks/useMessageVote'
|
|
33
33
|
import { getMessageDisplayText } from '../../utils/getMessageDisplayText'
|
|
34
34
|
import { Avatar } from '../Avatar'
|
|
35
|
+
import LockedAttachment from '../LockedAttachment'
|
|
35
36
|
import { isLinkAttachment } from '../MediaMessage'
|
|
36
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
bubbleGroupPositionFromStream as messageAttachmentGroupPositionFromStream,
|
|
39
|
+
} from '../MessageAttachment'
|
|
37
40
|
|
|
38
41
|
import { useCustomMessage } from './context'
|
|
39
|
-
import LockedAttachment from './LockedAttachment'
|
|
40
42
|
import {
|
|
41
43
|
MessageTag,
|
|
42
44
|
isAttachmentMessage,
|
|
43
45
|
isChatbotMessage,
|
|
44
|
-
isMediaAttachmentMessage,
|
|
45
|
-
isTextAttachmentMessage,
|
|
46
46
|
isTipOnlyMessage,
|
|
47
47
|
} from './MessageTag'
|
|
48
48
|
import { MessageVoteButtons } from './MessageVoteButtons'
|
|
@@ -145,12 +145,13 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
145
145
|
(total, segment) => total + segment.attachments.length,
|
|
146
146
|
0
|
|
147
147
|
) === (finalAttachments?.length ?? 0)
|
|
148
|
-
const streamAttachmentGroupPosition =
|
|
149
|
-
|
|
148
|
+
const streamAttachmentGroupPosition = messageAttachmentGroupPositionFromStream(
|
|
149
|
+
{
|
|
150
150
|
endOfGroup,
|
|
151
151
|
firstOfGroup,
|
|
152
152
|
groupedByUser,
|
|
153
|
-
}
|
|
153
|
+
}
|
|
154
|
+
)
|
|
154
155
|
|
|
155
156
|
if (isDateSeparatorMessage(message)) {
|
|
156
157
|
return null
|
|
@@ -225,26 +226,6 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
225
226
|
!isAIGenerated &&
|
|
226
227
|
!isChatbot
|
|
227
228
|
|
|
228
|
-
const sharedLockedTextProps = {
|
|
229
|
-
contentType: 'text' as const,
|
|
230
|
-
amountText: message.metadata?.amount_text,
|
|
231
|
-
paymentStatus: message.metadata?.payment_status,
|
|
232
|
-
renderedText: message.text && (
|
|
233
|
-
<MessageText message={displayMessage} renderText={renderText} />
|
|
234
|
-
),
|
|
235
|
-
}
|
|
236
|
-
const sharedLockedMediaProps = {
|
|
237
|
-
contentType: 'media' as const,
|
|
238
|
-
title: message.metadata?.attachment_title,
|
|
239
|
-
mimeType: message.metadata?.attachment_mime_type,
|
|
240
|
-
thumbnailUrl: message.metadata?.attachment_thumbnail,
|
|
241
|
-
amountText: message.metadata?.amount_text,
|
|
242
|
-
detail: message.metadata?.attachment_detail,
|
|
243
|
-
paymentStatus: message.metadata?.payment_status,
|
|
244
|
-
onFetchSource: onFetchSource
|
|
245
|
-
? () => onFetchSource(message, channel)
|
|
246
|
-
: undefined,
|
|
247
|
-
}
|
|
248
229
|
return (
|
|
249
230
|
<>
|
|
250
231
|
{editing && (
|
|
@@ -295,61 +276,43 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
295
276
|
marginInlineStart: 0,
|
|
296
277
|
}}
|
|
297
278
|
>
|
|
298
|
-
{
|
|
299
|
-
<MessageTag message={message} standalone />
|
|
300
|
-
) : isTextAttachmentMessage(message) ? (
|
|
279
|
+
{isAttachment ? (
|
|
301
280
|
<div className="str-chat__message-bubble-wrapper">
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
<LockedAttachment
|
|
306
|
-
{
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
{
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
/>
|
|
316
|
-
)}
|
|
317
|
-
{!isMine && MessageActions && <MessageActions />}
|
|
318
|
-
</div>
|
|
319
|
-
</div>
|
|
320
|
-
) : isMediaAttachmentMessage(message) ? (
|
|
321
|
-
<div className="str-chat__message-bubble-wrapper">
|
|
322
|
-
<div className="flex items-center gap-2">
|
|
323
|
-
{isMine && MessageActions && <MessageActions />}
|
|
324
|
-
{isMine ? (
|
|
325
|
-
<LockedAttachment
|
|
326
|
-
{...sharedLockedMediaProps}
|
|
327
|
-
isMine={true}
|
|
328
|
-
onPreviewClick={() =>
|
|
329
|
-
onUnlockClick
|
|
330
|
-
? onUnlockClick(message, channel)
|
|
331
|
-
: undefined
|
|
281
|
+
{isMine ? (
|
|
282
|
+
<div className="flex items-center gap-2">
|
|
283
|
+
{MessageActions && <MessageActions />}
|
|
284
|
+
<LockedAttachment.Sent
|
|
285
|
+
title={message.metadata?.attachment_title}
|
|
286
|
+
mimeType={message.metadata?.attachment_mime_type}
|
|
287
|
+
thumbnailUrl={message.metadata?.attachment_thumbnail}
|
|
288
|
+
amountText={message.metadata?.amount_text}
|
|
289
|
+
detail={message.metadata?.attachment_detail}
|
|
290
|
+
paymentStatus={message.metadata?.payment_status}
|
|
291
|
+
onPreviewClick={() => onUnlockClick?.(message, channel)}
|
|
292
|
+
onFetchSource={async () =>
|
|
293
|
+
await onFetchSource?.(message, channel)
|
|
332
294
|
}
|
|
333
295
|
/>
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
296
|
+
</div>
|
|
297
|
+
) : (
|
|
298
|
+
<div className="flex items-center gap-2">
|
|
299
|
+
<LockedAttachment.Received
|
|
300
|
+
title={message.metadata?.attachment_title}
|
|
301
|
+
mimeType={message.metadata?.attachment_mime_type}
|
|
302
|
+
thumbnailUrl={message.metadata?.attachment_thumbnail}
|
|
303
|
+
amountText={message.metadata?.amount_text}
|
|
304
|
+
detail={message.metadata?.attachment_detail}
|
|
305
|
+
paymentStatus={message.metadata?.payment_status}
|
|
338
306
|
isUnlocking={isUnlocking(message.id)}
|
|
339
|
-
onUnlockClick={() =>
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
: undefined
|
|
343
|
-
}
|
|
344
|
-
onDownloadClick={() =>
|
|
345
|
-
onDownloadClick
|
|
346
|
-
? onDownloadClick(message, channel)
|
|
347
|
-
: undefined
|
|
307
|
+
onUnlockClick={() => onUnlockClick?.(message, channel)}
|
|
308
|
+
onFetchSource={async () =>
|
|
309
|
+
await onFetchSource?.(message, channel)
|
|
348
310
|
}
|
|
311
|
+
onDownloadClick={() => onDownloadClick?.(message, channel)}
|
|
349
312
|
/>
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
313
|
+
{MessageActions && <MessageActions />}
|
|
314
|
+
</div>
|
|
315
|
+
)}
|
|
353
316
|
{message.text && (
|
|
354
317
|
<div className="str-chat__message-bubble">
|
|
355
318
|
<MessageText
|
|
@@ -359,6 +322,9 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
359
322
|
</div>
|
|
360
323
|
)}
|
|
361
324
|
</div>
|
|
325
|
+
) : isTipOnly ? (
|
|
326
|
+
/* Tip-only messages render as a standalone bubble */
|
|
327
|
+
<MessageTag message={message} standalone />
|
|
362
328
|
) : canRenderAttachmentsInToolkit ? (
|
|
363
329
|
<div className="str-chat__message-bubble-wrapper">
|
|
364
330
|
<div
|
|
@@ -5,7 +5,7 @@ import { QueryChannelAPIResponse, StreamChat } from 'stream-chat'
|
|
|
5
5
|
import { Channel, Chat, type SendButtonProps } from 'stream-chat-react'
|
|
6
6
|
|
|
7
7
|
import { createMockStreamChatClient } from '../../testing/createMockStreamChatClient'
|
|
8
|
-
import LockedAttachment from '../
|
|
8
|
+
import LockedAttachment from '../LockedAttachment'
|
|
9
9
|
|
|
10
10
|
import { CustomMessageInput } from '.'
|
|
11
11
|
|
|
@@ -229,9 +229,7 @@ WithAttachments.parameters = {
|
|
|
229
229
|
export const WithPaidAttachment: StoryFn<WrapperProps> = (args) => <Wrapper {...args} />
|
|
230
230
|
WithPaidAttachment.args = {
|
|
231
231
|
attachmentPreviewList: () => (
|
|
232
|
-
<LockedAttachment
|
|
233
|
-
isPreview
|
|
234
|
-
contentType="media"
|
|
232
|
+
<LockedAttachment.Composer
|
|
235
233
|
title="Exclusive Content"
|
|
236
234
|
mimeType="image/jpeg"
|
|
237
235
|
thumbnailUrl="https://picsum.photos/seed/paid/320/180"
|
|
@@ -268,7 +266,7 @@ WithPaidAttachment.parameters = {
|
|
|
268
266
|
docs: {
|
|
269
267
|
description: {
|
|
270
268
|
story:
|
|
271
|
-
"Message input with a paid attachment staged inside the input bubble. Uses `Channel`'s `AttachmentPreviewList` override to render a `LockedAttachment
|
|
269
|
+
"Message input with a paid attachment staged inside the input bubble. Uses `Channel`'s `AttachmentPreviewList` override to render a `LockedAttachment.Composer` card at the top of the input. `onDismiss` removes it; `onEditClick` reopens the paid content wizard.",
|
|
272
270
|
},
|
|
273
271
|
},
|
|
274
272
|
}
|