@linktr.ee/messaging-react 3.9.2-rc-1783581484 → 3.9.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/dist/{Card-B4Rgburk.js → Card-B83PIVO4.js} +2 -2
- package/dist/{Card-B4Rgburk.js.map → Card-B83PIVO4.js.map} +1 -1
- package/dist/{Card-BTlJZXuO.cjs → Card-BcTjQXjg.cjs} +2 -2
- package/dist/{Card-BTlJZXuO.cjs.map → Card-BcTjQXjg.cjs.map} +1 -1
- package/dist/{Card-DQSjcIX_.cjs → Card-CVxFApFQ.cjs} +2 -2
- package/dist/{Card-DQSjcIX_.cjs.map → Card-CVxFApFQ.cjs.map} +1 -1
- package/dist/{Card-DBsTWA91.js → Card-Cblg_OP2.js} +3 -3
- package/dist/{Card-DBsTWA91.js.map → Card-Cblg_OP2.js.map} +1 -1
- package/dist/{Card-QNGgvs9y.cjs → Card-DyFTYx54.cjs} +2 -2
- package/dist/{Card-QNGgvs9y.cjs.map → Card-DyFTYx54.cjs.map} +1 -1
- package/dist/{Card-0f6UozSj.js → Card-ckN-pJAf.js} +2 -2
- package/dist/{Card-0f6UozSj.js.map → Card-ckN-pJAf.js.map} +1 -1
- package/dist/{LockedThumbnail-NlYrnJWD.cjs → LockedThumbnail-C-MNksZm.cjs} +2 -2
- package/dist/{LockedThumbnail-NlYrnJWD.cjs.map → LockedThumbnail-C-MNksZm.cjs.map} +1 -1
- package/dist/{LockedThumbnail-CYzXhG5H.js → LockedThumbnail-CZ8hj3VY.js} +2 -2
- package/dist/{LockedThumbnail-CYzXhG5H.js.map → LockedThumbnail-CZ8hj3VY.js.map} +1 -1
- package/dist/index-C2hIrXzf.cjs +2 -0
- package/dist/index-C2hIrXzf.cjs.map +1 -0
- package/dist/{index-DYp35Tv3.js → index-CPbf44kW.js} +1143 -1125
- package/dist/index-CPbf44kW.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/src/components/ChannelHeaderRedesign.tsx +8 -1
- package/src/components/ChannelView.tsx +12 -9
- package/src/components/CustomMessage/StreamAttachmentMessage.stories.tsx +19 -12
- package/src/components/CustomMessage/StreamAttachmentMessage.test.tsx +44 -26
- package/src/components/CustomMessage/StreamAttachmentMessage.tsx +37 -43
- package/src/components/CustomMessage/index.tsx +13 -21
- package/dist/index-D0lSJc9F.cjs +0 -2
- package/dist/index-D0lSJc9F.cjs.map +0 -1
- package/dist/index-DYp35Tv3.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./index-C2hIrXzf.cjs");exports.ActionButton=e.ActionButton;exports.Avatar=e.Avatar;exports.ChannelEmptyState=e.ChannelEmptyState;exports.ChannelList=e.ChannelList;exports.ChannelView=e.ChannelView;exports.CustomMessageProvider=e.CustomMessageProvider;exports.FaqList=e.FaqList;exports.FaqListItem=e.FaqListItem;exports.LinkAttachment=e.LinkAttachment;exports.LockedAttachment=e.LockedAttachment;exports.MediaMessage=e.MediaMessage;exports.MessageAttachment=e.MessageAttachment;exports.MessageVoteButtons=e.MessageVoteButtons;exports.MessagingProvider=e.MessagingProvider;exports.MessagingShell=e.MessagingShell;exports.buildCompactMetaLabel=e.buildCompactMetaLabel;exports.formatFileSize=e.formatFileSize;exports.formatRelativeTime=e.formatRelativeTime;exports.getFileExtensionLabel=e.getFileExtensionLabel;exports.getMessageDisplayText=e.getMessageDisplayText;exports.isLinkAttachment=e.isLinkAttachment;exports.isUuidLike=e.isUuidLike;exports.messageAttachmentGroupPositionFromStream=e.bubbleGroupPositionFromStream;exports.normalizeLanguageCode=e.normalizeLanguageCode;exports.resolveConversationParticipant=e.resolveConversationParticipant;exports.resolveLinkAttachment=e.resolveLinkAttachment;exports.resolveMediaFromMessage=e.resolveMediaFromMessage;exports.resolveParticipantDisplayName=e.resolveParticipantDisplayName;exports.useComposerLocked=e.useComposerLocked;exports.useCustomMessage=e.useCustomMessage;exports.useMessageVote=e.useMessageVote;exports.useMessaging=e.useMessaging;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as e, b as t, C as i, c as o, d as n, e as m, F as r, f as g, L as l, h as M, M as c, i as u, j as L, k as h, l as d, m as p, n as v, o as C, p as A, q as k, s as F, t as P, u as b, v as f, w as x, x as y, y as S, z as q, B as z, D as B, E as D, G as E } from "./index-
|
|
1
|
+
import { a as e, b as t, C as i, c as o, d as n, e as m, F as r, f as g, L as l, h as M, M as c, i as u, j as L, k as h, l as d, m as p, n as v, o as C, p as A, q as k, s as F, t as P, u as b, v as f, w as x, x as y, y as S, z as q, B as z, D as B, E as D, G as E } from "./index-CPbf44kW.js";
|
|
2
2
|
export {
|
|
3
3
|
e as ActionButton,
|
|
4
4
|
t as Avatar,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linktr.ee/messaging-react",
|
|
3
|
-
"version": "3.9.2
|
|
3
|
+
"version": "3.9.2",
|
|
4
4
|
"description": "React messaging components built on messaging-core for web applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@linktr.ee/component-library": "11.8.6",
|
|
53
|
-
"@linktr.ee/messaging-core": "2.
|
|
53
|
+
"@linktr.ee/messaging-core": "^2.2.0",
|
|
54
54
|
"@phosphor-icons/react": "^2.1.10"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
@@ -42,6 +42,7 @@ const hasCustomerPaidTag = (participant?: ChannelMemberResponse) => {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
interface ChannelHeaderRedesignProps {
|
|
45
|
+
className?: string
|
|
45
46
|
onBack?: () => void
|
|
46
47
|
showBackButton: boolean
|
|
47
48
|
showStarButton?: boolean
|
|
@@ -61,6 +62,7 @@ interface ChannelHeaderRedesignProps {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
export const ChannelHeaderRedesign: React.FC<ChannelHeaderRedesignProps> = ({
|
|
65
|
+
className,
|
|
64
66
|
onBack,
|
|
65
67
|
showBackButton,
|
|
66
68
|
showStarButton = false,
|
|
@@ -112,7 +114,12 @@ export const ChannelHeaderRedesign: React.FC<ChannelHeaderRedesignProps> = ({
|
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
return (
|
|
115
|
-
<div
|
|
117
|
+
<div
|
|
118
|
+
className={classNames(
|
|
119
|
+
'@container bg-[linear-gradient(180deg,#FBFAF9_0%,rgba(251,250,249,0.92)_58%,rgba(251,250,249,0)_100%)]',
|
|
120
|
+
className
|
|
121
|
+
)}
|
|
122
|
+
>
|
|
116
123
|
<div className="grid min-h-[100px] grid-cols-[minmax(40px,1fr)_auto_minmax(40px,1fr)] items-start pb-2 pt-5 @lg:min-h-[104px] @lg:pt-6">
|
|
117
124
|
<div className="flex items-start">
|
|
118
125
|
{showBackButton && (
|
|
@@ -475,6 +475,7 @@ const ChannelViewInner: React.FC<{
|
|
|
475
475
|
) : null
|
|
476
476
|
const redesignedHeader = onParticipantNameClick ? (
|
|
477
477
|
<ChannelHeaderRedesign
|
|
478
|
+
className="-mx-[var(--str-chat\_\_spacing-2)] px-[var(--str-chat\_\_spacing-2)] md:-mx-[min(var(--str-chat\_\_spacing-10),4%)] md:px-[min(var(--str-chat\_\_spacing-10),4%)]"
|
|
478
479
|
onBack={onBack}
|
|
479
480
|
showBackButton={showBackButton}
|
|
480
481
|
showChannelInfo={showChannelInfo}
|
|
@@ -552,20 +553,22 @@ const ChannelViewInner: React.FC<{
|
|
|
552
553
|
</>
|
|
553
554
|
)}
|
|
554
555
|
|
|
555
|
-
{/* Stream only renders MessageList.head when there are messages, so
|
|
556
|
-
empty redesigned channels need the header and banner in normal flow. */}
|
|
557
|
-
{onParticipantNameClick && !hasVisibleMessages && (
|
|
558
|
-
<div className="px-4 @lg:px-6">
|
|
559
|
-
{redesignedHeader}
|
|
560
|
-
{channelBanner}
|
|
561
|
-
</div>
|
|
562
|
-
)}
|
|
563
|
-
|
|
564
556
|
{/* Message List */}
|
|
565
557
|
<div
|
|
566
558
|
key="lt-channel-message-list"
|
|
567
559
|
className="flex-1 overflow-hidden relative"
|
|
568
560
|
>
|
|
561
|
+
{/* Stream only renders MessageList.head when there are messages, so
|
|
562
|
+
empty redesigned channels need the header and banner in normal flow. */}
|
|
563
|
+
{onParticipantNameClick && !hasVisibleMessages && (
|
|
564
|
+
<div className="str-chat__list">
|
|
565
|
+
<div className="str-chat__message-list-scroll">
|
|
566
|
+
{redesignedHeader}
|
|
567
|
+
{channelBanner}
|
|
568
|
+
</div>
|
|
569
|
+
</div>
|
|
570
|
+
)}
|
|
571
|
+
|
|
569
572
|
<MessageList
|
|
570
573
|
head={
|
|
571
574
|
onParticipantNameClick && hasVisibleMessages ? (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Meta, StoryFn } from '@storybook/react'
|
|
2
2
|
import classNames from 'classnames'
|
|
3
3
|
import React from 'react'
|
|
4
|
-
import type { Attachment } from 'stream-chat'
|
|
4
|
+
import type { Attachment, LocalMessage } from 'stream-chat'
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
currentUserArgType,
|
|
@@ -23,22 +23,29 @@ import StreamAttachmentMessage from './StreamAttachmentMessage'
|
|
|
23
23
|
* Stream-attachment → `LinkAttachment` prop mapping, caption
|
|
24
24
|
* de-duplication (including emoji preservation), and media clustering.
|
|
25
25
|
*
|
|
26
|
-
* The component
|
|
27
|
-
* the
|
|
26
|
+
* The component owns the Stream bubble-wrapper markup. Stories only supply
|
|
27
|
+
* the `.str-chat` / `.str-chat__message--me|other` / `.str-chat__li--single`
|
|
28
|
+
* ancestry that Stream bubble chrome and the MES-1031 tails are scoped under.
|
|
28
29
|
* ────────────────────────────────────────────────────────────────── */
|
|
29
30
|
|
|
30
31
|
const Bubbles: React.FC<{ isMyMessage: boolean; children: React.ReactNode }> = ({
|
|
31
32
|
isMyMessage,
|
|
32
33
|
children,
|
|
33
34
|
}) => (
|
|
34
|
-
<div className="mx-auto max-w-2xl p-12">
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
<div className="str-chat mx-auto max-w-2xl p-12">
|
|
36
|
+
{/* Tail CSS is scoped to `.str-chat__li--single|--bottom`; bubble surface
|
|
37
|
+
styles need `.str-chat__message--me|other`. */}
|
|
38
|
+
<div className="str-chat__li str-chat__li--single">
|
|
39
|
+
<div
|
|
40
|
+
className={classNames(
|
|
41
|
+
'str-chat__message str-chat__message-simple',
|
|
42
|
+
isMyMessage
|
|
43
|
+
? 'str-chat__message--me str-chat__message-simple--me'
|
|
44
|
+
: 'str-chat__message--other'
|
|
45
|
+
)}
|
|
46
|
+
>
|
|
47
|
+
<div className="str-chat__message-inner">{children}</div>
|
|
48
|
+
</div>
|
|
42
49
|
</div>
|
|
43
50
|
</div>
|
|
44
51
|
)
|
|
@@ -51,7 +58,7 @@ const Render = (attachments: Attachment[], text?: string): StoryFn<StoryArgs> =>
|
|
|
51
58
|
<StreamAttachmentMessage
|
|
52
59
|
groupPosition="single"
|
|
53
60
|
isMyMessage={isMyMessage}
|
|
54
|
-
message={{ attachments, text }}
|
|
61
|
+
message={{ attachments, text } as LocalMessage}
|
|
55
62
|
/>
|
|
56
63
|
</Bubbles>
|
|
57
64
|
)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
+
import type { Attachment, LocalMessage } from 'stream-chat'
|
|
2
3
|
import { describe, expect, it } from 'vitest'
|
|
3
4
|
|
|
4
5
|
import { renderWithProviders, screen } from '../../test/utils'
|
|
@@ -8,6 +9,11 @@ import StreamAttachmentMessage, {
|
|
|
8
9
|
linkCardPropsFromStreamAttachment,
|
|
9
10
|
} from './StreamAttachmentMessage'
|
|
10
11
|
|
|
12
|
+
const message = (
|
|
13
|
+
attachments: Attachment[],
|
|
14
|
+
text?: string
|
|
15
|
+
): LocalMessage => ({ attachments, text }) as LocalMessage
|
|
16
|
+
|
|
11
17
|
describe('linkCardPropsFromStreamAttachment', () => {
|
|
12
18
|
it('uses the featured layout when a thumbnail is present', () => {
|
|
13
19
|
expect(
|
|
@@ -66,8 +72,8 @@ describe('StreamAttachmentMessage', () => {
|
|
|
66
72
|
<StreamAttachmentMessage
|
|
67
73
|
groupPosition="single"
|
|
68
74
|
isMyMessage={true}
|
|
69
|
-
message={
|
|
70
|
-
|
|
75
|
+
message={message(
|
|
76
|
+
[
|
|
71
77
|
{
|
|
72
78
|
type: 'link',
|
|
73
79
|
og_scrape_url: 'https://linktr.ee/someone',
|
|
@@ -76,8 +82,8 @@ describe('StreamAttachmentMessage', () => {
|
|
|
76
82
|
image_url: 'https://cdn.example.com/thumb.jpg',
|
|
77
83
|
},
|
|
78
84
|
],
|
|
79
|
-
|
|
80
|
-
}
|
|
85
|
+
'https://linktr.ee/someone !'
|
|
86
|
+
)}
|
|
81
87
|
/>
|
|
82
88
|
)
|
|
83
89
|
|
|
@@ -88,24 +94,30 @@ describe('StreamAttachmentMessage', () => {
|
|
|
88
94
|
})
|
|
89
95
|
|
|
90
96
|
it('keeps an emoji-only caption instead of dropping it', () => {
|
|
91
|
-
renderWithProviders(
|
|
97
|
+
const { container } = renderWithProviders(
|
|
92
98
|
<StreamAttachmentMessage
|
|
93
99
|
groupPosition="single"
|
|
94
100
|
isMyMessage={false}
|
|
95
|
-
message={
|
|
96
|
-
|
|
101
|
+
message={message(
|
|
102
|
+
[
|
|
97
103
|
{
|
|
98
104
|
type: 'link',
|
|
99
105
|
og_scrape_url: 'https://linktr.ee/someone',
|
|
100
106
|
title: 'My Linktree',
|
|
101
107
|
},
|
|
102
108
|
],
|
|
103
|
-
|
|
104
|
-
}
|
|
109
|
+
'https://linktr.ee/someone 😎'
|
|
110
|
+
)}
|
|
105
111
|
/>
|
|
106
112
|
)
|
|
107
113
|
|
|
108
|
-
|
|
114
|
+
// MessageText linkifies the URL (display text drops the scheme), so the
|
|
115
|
+
// caption is split across a link node and trailing emoji text.
|
|
116
|
+
const captionBubble = container.querySelector(
|
|
117
|
+
'.str-chat__message-bubble:not([data-attachment-bubble])'
|
|
118
|
+
)
|
|
119
|
+
expect(captionBubble).toHaveTextContent('linktr.ee/someone')
|
|
120
|
+
expect(captionBubble).toHaveTextContent('😎')
|
|
109
121
|
})
|
|
110
122
|
|
|
111
123
|
it('renders mixed link and media attachments in order', () => {
|
|
@@ -113,8 +125,8 @@ describe('StreamAttachmentMessage', () => {
|
|
|
113
125
|
<StreamAttachmentMessage
|
|
114
126
|
groupPosition="single"
|
|
115
127
|
isMyMessage={true}
|
|
116
|
-
message={
|
|
117
|
-
|
|
128
|
+
message={message(
|
|
129
|
+
[
|
|
118
130
|
{
|
|
119
131
|
type: 'link',
|
|
120
132
|
og_scrape_url: 'https://linktr.ee/someone',
|
|
@@ -126,8 +138,8 @@ describe('StreamAttachmentMessage', () => {
|
|
|
126
138
|
title: 'Vacation',
|
|
127
139
|
},
|
|
128
140
|
],
|
|
129
|
-
|
|
130
|
-
}
|
|
141
|
+
'caption'
|
|
142
|
+
)}
|
|
131
143
|
/>
|
|
132
144
|
)
|
|
133
145
|
|
|
@@ -140,27 +152,28 @@ describe('StreamAttachmentMessage', () => {
|
|
|
140
152
|
expect(container).toHaveTextContent('caption')
|
|
141
153
|
})
|
|
142
154
|
|
|
143
|
-
// The message text must never be dropped when a message carries an
|
|
144
|
-
// attachment — only genuine URL-only captions are suppressed.
|
|
145
155
|
it('renders the accompanying message text next to a link attachment', () => {
|
|
146
|
-
renderWithProviders(
|
|
156
|
+
const { container } = renderWithProviders(
|
|
147
157
|
<StreamAttachmentMessage
|
|
148
158
|
groupPosition="single"
|
|
149
159
|
isMyMessage={true}
|
|
150
|
-
message={
|
|
151
|
-
|
|
160
|
+
message={message(
|
|
161
|
+
[
|
|
152
162
|
{
|
|
153
163
|
type: 'link',
|
|
154
164
|
og_scrape_url: 'https://linktr.ee/someone',
|
|
155
165
|
title: 'My Linktree',
|
|
156
166
|
},
|
|
157
167
|
],
|
|
158
|
-
|
|
159
|
-
}
|
|
168
|
+
'you have to see this'
|
|
169
|
+
)}
|
|
160
170
|
/>
|
|
161
171
|
)
|
|
162
172
|
|
|
163
|
-
|
|
173
|
+
const bubbles = container.querySelectorAll('.str-chat__message-bubble')
|
|
174
|
+
expect(bubbles).toHaveLength(2)
|
|
175
|
+
expect(bubbles[0]).toHaveAttribute('data-attachment-bubble', 'true')
|
|
176
|
+
expect(bubbles[1]).toHaveTextContent('you have to see this')
|
|
164
177
|
})
|
|
165
178
|
|
|
166
179
|
it('renders the accompanying message text next to a media attachment', () => {
|
|
@@ -168,19 +181,24 @@ describe('StreamAttachmentMessage', () => {
|
|
|
168
181
|
<StreamAttachmentMessage
|
|
169
182
|
groupPosition="single"
|
|
170
183
|
isMyMessage={false}
|
|
171
|
-
message={
|
|
172
|
-
|
|
184
|
+
message={message(
|
|
185
|
+
[
|
|
173
186
|
{
|
|
174
187
|
type: 'image',
|
|
175
188
|
image_url: 'https://cdn.example.com/photo.jpg',
|
|
176
189
|
title: 'Vacation',
|
|
177
190
|
},
|
|
178
191
|
],
|
|
179
|
-
|
|
180
|
-
}
|
|
192
|
+
'from the trip'
|
|
193
|
+
)}
|
|
181
194
|
/>
|
|
182
195
|
)
|
|
183
196
|
|
|
184
197
|
expect(container).toHaveTextContent('from the trip')
|
|
198
|
+
expect(
|
|
199
|
+
container.querySelector(
|
|
200
|
+
'.str-chat__message-bubble:not([data-attachment-bubble])'
|
|
201
|
+
)
|
|
202
|
+
).toHaveTextContent('from the trip')
|
|
185
203
|
})
|
|
186
204
|
})
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import React from 'react'
|
|
2
|
-
import type { Attachment } from 'stream-chat'
|
|
2
|
+
import type { Attachment, LocalMessage } from 'stream-chat'
|
|
3
|
+
import {
|
|
4
|
+
MessageErrorIcon,
|
|
5
|
+
MessageText,
|
|
6
|
+
type MessageContextValue,
|
|
7
|
+
} from 'stream-chat-react'
|
|
3
8
|
|
|
4
9
|
import LinkAttachment from '../LinkAttachment'
|
|
5
10
|
import { isPlayableMedia } from '../LinkAttachment/components/_shared/CardThumbnail'
|
|
@@ -12,16 +17,6 @@ export type AttachmentSegment =
|
|
|
12
17
|
| { type: 'link'; attachments: Attachment[] }
|
|
13
18
|
| { type: 'media'; kind: StreamMediaKind; attachments: Attachment[] }
|
|
14
19
|
|
|
15
|
-
type StreamThreadMessage = {
|
|
16
|
-
attachments?: Attachment[]
|
|
17
|
-
text?: string
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const SENT_CAPTION_CLASS =
|
|
21
|
-
'mt-[2px] max-w-[280px] whitespace-pre-wrap break-words rounded-[18px] bg-[#121110] px-4 py-2 text-white'
|
|
22
|
-
const RECEIVED_CAPTION_CLASS =
|
|
23
|
-
'mt-[2px] max-w-[280px] whitespace-pre-wrap break-words rounded-[18px] bg-[#e9eaed] px-4 py-2 text-[#080707]'
|
|
24
|
-
|
|
25
20
|
function trimToUndefined(value?: string | null): string | undefined {
|
|
26
21
|
const trimmed = value?.trim()
|
|
27
22
|
return trimmed ? trimmed : undefined
|
|
@@ -152,19 +147,9 @@ export function buildOrderedAttachmentSegments(
|
|
|
152
147
|
return segments
|
|
153
148
|
}
|
|
154
149
|
|
|
155
|
-
export function
|
|
150
|
+
export function countMediaSegmentBubbles(
|
|
156
151
|
segments: AttachmentSegment[]
|
|
157
152
|
): number {
|
|
158
|
-
let count = 0
|
|
159
|
-
|
|
160
|
-
for (const segment of segments) {
|
|
161
|
-
count += segment.type === 'link' ? segment.attachments.length : 1
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return count
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
export function countMediaSegmentBubbles(segments: AttachmentSegment[]): number {
|
|
168
153
|
return segments.filter((segment) => segment.type === 'media').length
|
|
169
154
|
}
|
|
170
155
|
|
|
@@ -304,18 +289,15 @@ function MediaClusterBubble({
|
|
|
304
289
|
groupPosition,
|
|
305
290
|
isMyMessage,
|
|
306
291
|
segment,
|
|
307
|
-
text,
|
|
308
292
|
}: {
|
|
309
293
|
groupPosition: BubbleGroupPosition
|
|
310
294
|
isMyMessage: boolean
|
|
311
295
|
segment: Extract<AttachmentSegment, { type: 'media' }>
|
|
312
|
-
text?: string
|
|
313
296
|
}) {
|
|
314
297
|
const TypeNamespace = messageAttachmentNamespaceForKind(segment.kind)
|
|
315
298
|
const props = {
|
|
316
299
|
...buildMediaClusterRenderProps(segment.kind, segment.attachments),
|
|
317
300
|
groupPosition,
|
|
318
|
-
...(text ? { text } : {}),
|
|
319
301
|
}
|
|
320
302
|
|
|
321
303
|
if (isMyMessage) {
|
|
@@ -328,18 +310,19 @@ function MediaClusterBubble({
|
|
|
328
310
|
export interface StreamAttachmentMessageProps {
|
|
329
311
|
groupPosition: BubbleGroupPosition
|
|
330
312
|
isMyMessage: boolean
|
|
331
|
-
message:
|
|
313
|
+
message: LocalMessage
|
|
314
|
+
renderText?: MessageContextValue['renderText']
|
|
332
315
|
}
|
|
333
316
|
|
|
334
317
|
const StreamAttachmentMessage: React.FC<StreamAttachmentMessageProps> = ({
|
|
335
318
|
groupPosition,
|
|
336
319
|
isMyMessage,
|
|
337
320
|
message,
|
|
321
|
+
renderText,
|
|
338
322
|
}) => {
|
|
339
323
|
const segments = buildOrderedAttachmentSegments(message.attachments)
|
|
340
324
|
if (segments.length === 0) return null
|
|
341
325
|
|
|
342
|
-
const totalBubbles = countAttachmentSegmentBubbles(segments)
|
|
343
326
|
const totalMediaBubbles = countMediaSegmentBubbles(segments)
|
|
344
327
|
const rawCaption = message.text?.trim()
|
|
345
328
|
const linkSegment = segments.find((segment) => segment.type === 'link')
|
|
@@ -353,25 +336,14 @@ const StreamAttachmentMessage: React.FC<StreamAttachmentMessageProps> = ({
|
|
|
353
336
|
? undefined
|
|
354
337
|
: rawCaption
|
|
355
338
|
|
|
356
|
-
let bubbleIndex = 0
|
|
357
339
|
let mediaBubbleIndex = 0
|
|
358
340
|
const rows: React.ReactNode[] = []
|
|
359
341
|
|
|
360
342
|
const pushLinkRow = (attachment: Attachment, key: string) => {
|
|
361
|
-
const isLastBubble = bubbleIndex === totalBubbles - 1
|
|
362
|
-
bubbleIndex += 1
|
|
363
|
-
|
|
364
343
|
const Card = isMyMessage ? LinkAttachment.Sent : LinkAttachment.Received
|
|
365
344
|
|
|
366
345
|
rows.push(
|
|
367
|
-
<
|
|
368
|
-
<Card {...linkCardPropsFromStreamAttachment(attachment)} />
|
|
369
|
-
{isLastBubble && caption ? (
|
|
370
|
-
<div className={isMyMessage ? SENT_CAPTION_CLASS : RECEIVED_CAPTION_CLASS}>
|
|
371
|
-
{caption}
|
|
372
|
-
</div>
|
|
373
|
-
) : null}
|
|
374
|
-
</React.Fragment>
|
|
346
|
+
<Card key={key} {...linkCardPropsFromStreamAttachment(attachment)} />
|
|
375
347
|
)
|
|
376
348
|
}
|
|
377
349
|
|
|
@@ -379,14 +351,12 @@ const StreamAttachmentMessage: React.FC<StreamAttachmentMessageProps> = ({
|
|
|
379
351
|
segment: Extract<AttachmentSegment, { type: 'media' }>,
|
|
380
352
|
key: string
|
|
381
353
|
) => {
|
|
382
|
-
const isLastBubble = bubbleIndex === totalBubbles - 1
|
|
383
354
|
const position = bubblePositionInRun(
|
|
384
355
|
mediaBubbleIndex,
|
|
385
356
|
totalMediaBubbles,
|
|
386
357
|
groupPosition
|
|
387
358
|
)
|
|
388
359
|
|
|
389
|
-
bubbleIndex += 1
|
|
390
360
|
mediaBubbleIndex += 1
|
|
391
361
|
|
|
392
362
|
rows.push(
|
|
@@ -395,7 +365,6 @@ const StreamAttachmentMessage: React.FC<StreamAttachmentMessageProps> = ({
|
|
|
395
365
|
groupPosition={position}
|
|
396
366
|
isMyMessage={isMyMessage}
|
|
397
367
|
segment={segment}
|
|
398
|
-
text={isLastBubble ? caption : undefined}
|
|
399
368
|
/>
|
|
400
369
|
)
|
|
401
370
|
}
|
|
@@ -411,7 +380,32 @@ const StreamAttachmentMessage: React.FC<StreamAttachmentMessageProps> = ({
|
|
|
411
380
|
pushMediaCluster(segment, `media-${segmentIndex}`)
|
|
412
381
|
})
|
|
413
382
|
|
|
414
|
-
return
|
|
383
|
+
return (
|
|
384
|
+
<div className="str-chat__message-bubble-wrapper">
|
|
385
|
+
<div
|
|
386
|
+
className="str-chat__message-bubble"
|
|
387
|
+
data-attachment-bubble="true"
|
|
388
|
+
style={{
|
|
389
|
+
background: 'transparent',
|
|
390
|
+
borderRadius: 0,
|
|
391
|
+
overflow: 'visible',
|
|
392
|
+
padding: 0,
|
|
393
|
+
}}
|
|
394
|
+
>
|
|
395
|
+
{rows}
|
|
396
|
+
{!caption && <MessageErrorIcon />}
|
|
397
|
+
</div>
|
|
398
|
+
{caption ? (
|
|
399
|
+
<div className="str-chat__message-bubble">
|
|
400
|
+
<MessageText
|
|
401
|
+
message={message}
|
|
402
|
+
renderText={renderText}
|
|
403
|
+
/>
|
|
404
|
+
<MessageErrorIcon />
|
|
405
|
+
</div>
|
|
406
|
+
) : null}
|
|
407
|
+
</div>
|
|
408
|
+
)
|
|
415
409
|
}
|
|
416
410
|
|
|
417
411
|
export default StreamAttachmentMessage
|
|
@@ -326,27 +326,19 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
326
326
|
/* Tip-only messages render as a standalone bubble */
|
|
327
327
|
<MessageTag message={message} standalone />
|
|
328
328
|
) : canRenderAttachmentsInToolkit ? (
|
|
329
|
-
<
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
message={{
|
|
343
|
-
attachments: finalAttachments,
|
|
344
|
-
text: displayMessage.text,
|
|
345
|
-
}}
|
|
346
|
-
/>
|
|
347
|
-
<MessageErrorIcon />
|
|
348
|
-
</div>
|
|
349
|
-
</div>
|
|
329
|
+
<StreamAttachmentMessage
|
|
330
|
+
groupPosition={streamAttachmentGroupPosition}
|
|
331
|
+
isMyMessage={isMine}
|
|
332
|
+
message={{
|
|
333
|
+
...displayMessage,
|
|
334
|
+
attachments: finalAttachments,
|
|
335
|
+
// MessageText prefers message.i18n[userLanguage_text] from
|
|
336
|
+
// Stream's TranslationContext over message.text. Drop i18n so
|
|
337
|
+
// the caption uses displayMessage.text from viewerLanguage.
|
|
338
|
+
i18n: undefined,
|
|
339
|
+
}}
|
|
340
|
+
renderText={renderText}
|
|
341
|
+
/>
|
|
350
342
|
) : (
|
|
351
343
|
<div className="str-chat__message-bubble-wrapper">
|
|
352
344
|
<div className="str-chat__message-bubble">
|