@linktr.ee/messaging-react 3.37.1 → 3.38.0-rc-1786342246
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-DVMzYquF.js → Card-C40UQcWN.js} +2 -2
- package/dist/{Card-DVMzYquF.js.map → Card-C40UQcWN.js.map} +1 -1
- package/dist/{Card-Dxk_Ggxw.cjs → Card-C8glaNwz.cjs} +2 -2
- package/dist/{Card-Dxk_Ggxw.cjs.map → Card-C8glaNwz.cjs.map} +1 -1
- package/dist/assets/index.css +1 -1
- package/dist/index-Byt_hX2r.cjs +3 -0
- package/dist/index-Byt_hX2r.cjs.map +1 -0
- package/dist/{index-BOBIiAuD.js → index-fk5ihkQY.js} +2240 -2277
- package/dist/index-fk5ihkQY.js.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -34
- package/dist/index.js +27 -29
- package/package.json +4 -3
- package/src/components/ChannelView.tsx +1 -6
- package/src/components/CustomMessage/CustomMessage.test.tsx +0 -12
- package/src/components/CustomMessage/MessageTag.test.tsx +9 -18
- package/src/components/CustomMessage/MessageTag.tsx +16 -17
- package/src/components/CustomMessage/MessageTail.tsx +45 -0
- package/src/components/CustomMessage/SentMessageDeliveryStatus.tsx +9 -2
- package/src/components/CustomMessage/StreamAttachmentMessage.tsx +18 -4
- package/src/components/CustomMessage/index.tsx +23 -23
- package/src/components/LinkAttachment/components/_shared/BubbleTail.tsx +5 -6
- package/src/components/MessageAttachment/_shared/CarouselNav.tsx +5 -6
- package/src/components/MessageAttachment/_shared/DownloadAction.tsx +5 -8
- package/src/components/MessageAttachment/_shared/ImageViewer.tsx +8 -12
- package/src/components/MessageAttachment/_shared/PdfViewer.tsx +1 -1
- package/src/components/MessageAttachment/_shared/VideoViewer.tsx +1 -1
- package/src/components/MessageAttachment/_shared/ViewerShell.tsx +16 -11
- package/src/components/MessageAttachment/_shared/viewerChromeClasses.ts +16 -0
- package/src/components/MessagingShell/index.tsx +0 -2
- package/src/index.ts +0 -3
- package/src/styles.css +56 -377
- package/src/types.ts +0 -8
- package/dist/index-BOBIiAuD.js.map +0 -1
- package/dist/index-D7A8U8GQ.cjs +0 -3
- package/dist/index-D7A8U8GQ.cjs.map +0 -1
- package/src/components/CustomMessage/MessageVoteButtons.stories.tsx +0 -95
- package/src/components/CustomMessage/MessageVoteButtons.tsx +0 -47
- package/src/hooks/useMessageVote.ts +0 -77
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-Byt_hX2r.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.MessageBubble=e.MessageBubble;exports.MessagingProvider=e.MessagingProvider;exports.MessagingShell=e.MessagingShell;exports.RichCard=e.RichCard;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.optimizeMessagingAttachmentUrl=e.optimizeMessagingAttachmentUrl;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.useMessaging=e.useMessaging;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.d.ts
CHANGED
|
@@ -215,7 +215,7 @@ export declare const ChannelView: default_2.NamedExoticComponent<ChannelViewProp
|
|
|
215
215
|
* Props that MessagingShell passes through to ChannelView.
|
|
216
216
|
* ChannelViewProps is the source of truth for these props.
|
|
217
217
|
*/
|
|
218
|
-
declare type ChannelViewPassthroughProps = Pick<ChannelViewProps, 'renderMessageInputActions' | 'renderConversationFooter' | 'CustomChannelEmptyState' | 'onBlockParticipantClick' | 'onReportParticipantClick' | 'dmAgentEnabled' | 'onMessageSent' | '
|
|
218
|
+
declare type ChannelViewPassthroughProps = Pick<ChannelViewProps, 'renderMessageInputActions' | 'renderConversationFooter' | 'CustomChannelEmptyState' | 'onBlockParticipantClick' | 'onReportParticipantClick' | 'dmAgentEnabled' | 'onMessageSent' | 'viewerLanguage' | 'renderChannelBanner' | 'customChannelActions' | 'renderChannelActions' | 'onParticipantNameClick' | 'renderMessage' | 'onMessageLinkClick' | 'onBroadcastClick' | 'resolveBroadcastName' | 'showChannelInfo' | 'composerInput'>;
|
|
219
219
|
|
|
220
220
|
/**
|
|
221
221
|
* ChannelView component props
|
|
@@ -337,13 +337,6 @@ export declare interface ChannelViewProps {
|
|
|
337
337
|
* participant name; it now solely gates the actions menu.
|
|
338
338
|
*/
|
|
339
339
|
showChannelInfo?: boolean;
|
|
340
|
-
/**
|
|
341
|
-
* Enable thumbs up/down voting on chatbot messages.
|
|
342
|
-
* When true, vote buttons render below chatbot (DM Agent) messages.
|
|
343
|
-
* Votes are persisted as Stream Chat reactions (vote_up / vote_down).
|
|
344
|
-
* Defaults to false.
|
|
345
|
-
*/
|
|
346
|
-
chatbotVotingEnabled?: boolean;
|
|
347
340
|
/**
|
|
348
341
|
* Language code used to pick translated message text from Stream Chat i18n.
|
|
349
342
|
* Falls back to message.text when no matching translation exists.
|
|
@@ -1420,14 +1413,6 @@ export declare interface MessageMetadata {
|
|
|
1420
1413
|
broadcast_name?: string;
|
|
1421
1414
|
}
|
|
1422
1415
|
|
|
1423
|
-
export declare const MessageVoteButtons: default_2.FC<MessageVoteButtonsProps>;
|
|
1424
|
-
|
|
1425
|
-
declare interface MessageVoteButtonsProps {
|
|
1426
|
-
selected: VoteSelection;
|
|
1427
|
-
onVoteUp: () => void;
|
|
1428
|
-
onVoteDown: () => void;
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
1416
|
declare type MessageWithI18n = Pick<LocalMessage, 'text'> & {
|
|
1432
1417
|
i18n?: Record<string, string> | null;
|
|
1433
1418
|
};
|
|
@@ -1820,22 +1805,6 @@ export declare const useComposerLocked: () => boolean;
|
|
|
1820
1805
|
|
|
1821
1806
|
export declare function useCustomMessage<K extends keyof CustomMessageRegistry>(key: K): CustomMessageRegistry[K];
|
|
1822
1807
|
|
|
1823
|
-
/**
|
|
1824
|
-
* Hook that wraps Stream Chat reactions to provide toggle-style
|
|
1825
|
-
* upvote/downvote behavior on a message.
|
|
1826
|
-
*
|
|
1827
|
-
* Uses enforce_unique so sending a new reaction type automatically
|
|
1828
|
-
* removes the previous one. Uses skip_push to avoid notifying the
|
|
1829
|
-
* Linker on every vote.
|
|
1830
|
-
*/
|
|
1831
|
-
export declare function useMessageVote(message: LocalMessage): UseMessageVoteResult;
|
|
1832
|
-
|
|
1833
|
-
declare interface UseMessageVoteResult {
|
|
1834
|
-
selected: VoteSelection;
|
|
1835
|
-
voteUp: () => void;
|
|
1836
|
-
voteDown: () => void;
|
|
1837
|
-
}
|
|
1838
|
-
|
|
1839
1808
|
/**
|
|
1840
1809
|
* Hook to access messaging service and state
|
|
1841
1810
|
*/
|
|
@@ -1878,8 +1847,6 @@ declare interface VideoAttachmentSharedProps extends MessageAttachmentBaseProps
|
|
|
1878
1847
|
onClick?: (index: number) => boolean | void;
|
|
1879
1848
|
}
|
|
1880
1849
|
|
|
1881
|
-
export declare type VoteSelection = 'up' | 'down' | null;
|
|
1882
|
-
|
|
1883
1850
|
export { }
|
|
1884
1851
|
|
|
1885
1852
|
|
package/dist/index.js
CHANGED
|
@@ -1,39 +1,37 @@
|
|
|
1
|
-
import { a as e, b as t, C as i, c as
|
|
1
|
+
import { a as e, b as t, C as i, c as n, d as o, e as m, F as r, f as l, L as g, h as c, M, i as h, j as L, k as u, l as d, R as p, m as C, n as v, p as A, q as k, s as F, t as b, u as P, v as f, w as x, o as y, x as z, y as S, z as q, B, D, E, G as R } from "./index-fk5ihkQY.js";
|
|
2
2
|
export {
|
|
3
3
|
e as ActionButton,
|
|
4
4
|
t as Avatar,
|
|
5
5
|
i as ChannelEmptyState,
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
n as ChannelList,
|
|
7
|
+
o as ChannelView,
|
|
8
8
|
m as CustomMessageProvider,
|
|
9
9
|
r as FaqList,
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
d as
|
|
18
|
-
p as
|
|
19
|
-
C as
|
|
20
|
-
v as
|
|
21
|
-
A as
|
|
22
|
-
k as
|
|
23
|
-
F as
|
|
24
|
-
b as
|
|
25
|
-
P as
|
|
26
|
-
f as
|
|
27
|
-
x as
|
|
28
|
-
y as
|
|
29
|
-
z as
|
|
30
|
-
B as resolveConversationParticipant,
|
|
10
|
+
l as FaqListItem,
|
|
11
|
+
g as LinkAttachment,
|
|
12
|
+
c as LockedAttachment,
|
|
13
|
+
M as MediaMessage,
|
|
14
|
+
h as MessageAttachment,
|
|
15
|
+
L as MessageBubble,
|
|
16
|
+
u as MessagingProvider,
|
|
17
|
+
d as MessagingShell,
|
|
18
|
+
p as RichCard,
|
|
19
|
+
C as buildCompactMetaLabel,
|
|
20
|
+
v as formatFileSize,
|
|
21
|
+
A as formatRelativeTime,
|
|
22
|
+
k as getFileExtensionLabel,
|
|
23
|
+
F as getMessageDisplayText,
|
|
24
|
+
b as isLinkAttachment,
|
|
25
|
+
P as isUuidLike,
|
|
26
|
+
f as messageAttachmentGroupPositionFromStream,
|
|
27
|
+
x as normalizeLanguageCode,
|
|
28
|
+
y as optimizeMessagingAttachmentUrl,
|
|
29
|
+
z as resolveConversationParticipant,
|
|
31
30
|
S as resolveLinkAttachment,
|
|
32
31
|
q as resolveMediaFromMessage,
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
w as useMessaging
|
|
32
|
+
B as resolveParticipantDisplayName,
|
|
33
|
+
D as useComposerLocked,
|
|
34
|
+
E as useCustomMessage,
|
|
35
|
+
R as useMessaging
|
|
38
36
|
};
|
|
39
37
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linktr.ee/messaging-react",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.38.0-rc-1786342246",
|
|
4
4
|
"description": "React messaging components built on messaging-core for web applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -46,10 +46,11 @@
|
|
|
46
46
|
"storybook": "storybook dev -p 6006",
|
|
47
47
|
"storybook:build": "storybook build --stats-json",
|
|
48
48
|
"chromatic:ci:feature": "chromatic --ci --exit-zero-on-changes --exit-once-uploaded",
|
|
49
|
-
"chromatic:ci:main": "chromatic --ci --auto-accept-changes"
|
|
49
|
+
"chromatic:ci:main": "chromatic --ci --auto-accept-changes",
|
|
50
|
+
"chromatic:local": "yarn storybook:build && chromatic"
|
|
50
51
|
},
|
|
51
52
|
"dependencies": {
|
|
52
|
-
"@linktr.ee/messaging-core": "
|
|
53
|
+
"@linktr.ee/messaging-core": "2.4.0-rc-1786342246",
|
|
53
54
|
"@linktr.ee/messaging-taxonomy": "^0.9.0",
|
|
54
55
|
"@phosphor-icons/react": "^2.1.10"
|
|
55
56
|
},
|
|
@@ -49,7 +49,6 @@ const ChannelViewInner: React.FC<{
|
|
|
49
49
|
composerDisabled?: boolean
|
|
50
50
|
composerDisabledReason?: string
|
|
51
51
|
showStarButton?: boolean
|
|
52
|
-
chatbotVotingEnabled?: boolean
|
|
53
52
|
renderChannelBanner?: () => React.ReactNode
|
|
54
53
|
customChannelActions?: React.ReactNode
|
|
55
54
|
renderChannelActions?: React.ReactNode
|
|
@@ -80,7 +79,6 @@ const ChannelViewInner: React.FC<{
|
|
|
80
79
|
composerDisabled = false,
|
|
81
80
|
composerDisabledReason,
|
|
82
81
|
showStarButton = false,
|
|
83
|
-
chatbotVotingEnabled = false,
|
|
84
82
|
renderChannelBanner,
|
|
85
83
|
customChannelActions,
|
|
86
84
|
renderChannelActions,
|
|
@@ -149,7 +147,6 @@ const ChannelViewInner: React.FC<{
|
|
|
149
147
|
const messageNode = (
|
|
150
148
|
<CustomMessage
|
|
151
149
|
{...props}
|
|
152
|
-
chatbotVotingEnabled={chatbotVotingEnabled}
|
|
153
150
|
viewerLanguage={viewerLanguage}
|
|
154
151
|
onBroadcastClick={messageBroadcastClick}
|
|
155
152
|
/>
|
|
@@ -161,7 +158,7 @@ const ChannelViewInner: React.FC<{
|
|
|
161
158
|
|
|
162
159
|
return renderMessage(messageNode, message)
|
|
163
160
|
},
|
|
164
|
-
[
|
|
161
|
+
[messageBroadcastClick, renderMessage, viewerLanguage]
|
|
165
162
|
)
|
|
166
163
|
|
|
167
164
|
return (
|
|
@@ -308,7 +305,6 @@ export const ChannelView = React.memo<ChannelViewProps>(
|
|
|
308
305
|
messageMetadata,
|
|
309
306
|
onMessageSent,
|
|
310
307
|
showStarButton = false,
|
|
311
|
-
chatbotVotingEnabled = false,
|
|
312
308
|
renderChannelBanner,
|
|
313
309
|
customChannelActions,
|
|
314
310
|
renderChannelActions,
|
|
@@ -426,7 +422,6 @@ export const ChannelView = React.memo<ChannelViewProps>(
|
|
|
426
422
|
composerDisabled={composerDisabled}
|
|
427
423
|
composerDisabledReason={composerDisabledReason}
|
|
428
424
|
showStarButton={showStarButton}
|
|
429
|
-
chatbotVotingEnabled={chatbotVotingEnabled}
|
|
430
425
|
renderChannelBanner={renderChannelBanner}
|
|
431
426
|
customChannelActions={customChannelActions}
|
|
432
427
|
renderChannelActions={renderChannelActions}
|
|
@@ -43,14 +43,6 @@ vi.mock('stream-chat-react', () => ({
|
|
|
43
43
|
useMessageReminder: () => undefined,
|
|
44
44
|
}))
|
|
45
45
|
|
|
46
|
-
vi.mock('../../hooks/useMessageVote', () => ({
|
|
47
|
-
useMessageVote: () => ({
|
|
48
|
-
selected: undefined,
|
|
49
|
-
voteUp: vi.fn(),
|
|
50
|
-
voteDown: vi.fn(),
|
|
51
|
-
}),
|
|
52
|
-
}))
|
|
53
|
-
|
|
54
46
|
vi.mock('./context', () => ({
|
|
55
47
|
useCustomMessage: () => ({
|
|
56
48
|
isUnlocking: () => false,
|
|
@@ -70,10 +62,6 @@ vi.mock('./MessageTag', () => ({
|
|
|
70
62
|
isTipOnlyMessage: () => false,
|
|
71
63
|
}))
|
|
72
64
|
|
|
73
|
-
vi.mock('./MessageVoteButtons', () => ({
|
|
74
|
-
MessageVoteButtons: () => null,
|
|
75
|
-
}))
|
|
76
|
-
|
|
77
65
|
vi.mock('./LockedAttachment', () => ({
|
|
78
66
|
default: () => null,
|
|
79
67
|
}))
|
|
@@ -35,13 +35,10 @@ describe('MessageTag', () => {
|
|
|
35
35
|
|
|
36
36
|
const indicator = screen.getByTestId('message-chatbot-indicator')
|
|
37
37
|
expect(indicator).toHaveTextContent('Sent by AI Assistant')
|
|
38
|
-
expect(indicator).toHaveClass('message-chatbot-indicator--receiver')
|
|
39
|
-
expect(indicator).toHaveClass('message-chatbot-indicator--text')
|
|
40
38
|
|
|
41
|
-
|
|
42
|
-
expect(indicator.children[
|
|
43
|
-
|
|
44
|
-
)
|
|
39
|
+
// icon first, then label
|
|
40
|
+
expect(indicator.children[0].querySelector('svg')).not.toBeNull()
|
|
41
|
+
expect(indicator.children[1]).toHaveTextContent('Sent by AI Assistant')
|
|
45
42
|
})
|
|
46
43
|
|
|
47
44
|
it('renders sender chatbot text variant with mirrored order', () => {
|
|
@@ -56,13 +53,10 @@ describe('MessageTag', () => {
|
|
|
56
53
|
|
|
57
54
|
const indicator = screen.getByTestId('message-chatbot-indicator')
|
|
58
55
|
expect(indicator).toHaveTextContent('Sent by AI Assistant')
|
|
59
|
-
expect(indicator).toHaveClass('message-chatbot-indicator--sender')
|
|
60
|
-
expect(indicator).toHaveClass('message-chatbot-indicator--text')
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
)
|
|
65
|
-
expect(indicator.children[1]).toHaveClass('message-chatbot-indicator__icon')
|
|
57
|
+
// label first, then icon (mirrored)
|
|
58
|
+
expect(indicator.children[0]).toHaveTextContent('Sent by AI Assistant')
|
|
59
|
+
expect(indicator.children[1].querySelector('svg')).not.toBeNull()
|
|
66
60
|
})
|
|
67
61
|
|
|
68
62
|
it('renders sender attachment chatbot variant outside-footer copy', () => {
|
|
@@ -78,13 +72,10 @@ describe('MessageTag', () => {
|
|
|
78
72
|
|
|
79
73
|
const indicator = screen.getByTestId('message-chatbot-indicator')
|
|
80
74
|
expect(indicator).toHaveTextContent('Sent with AI')
|
|
81
|
-
expect(indicator).toHaveClass('message-chatbot-indicator--sender')
|
|
82
|
-
expect(indicator).toHaveClass('message-chatbot-indicator--attachment')
|
|
83
75
|
|
|
84
|
-
|
|
85
|
-
expect(indicator.children[
|
|
86
|
-
|
|
87
|
-
)
|
|
76
|
+
// icon first, then label
|
|
77
|
+
expect(indicator.children[0].querySelector('svg')).not.toBeNull()
|
|
78
|
+
expect(indicator.children[1]).toHaveTextContent('Sent with AI')
|
|
88
79
|
})
|
|
89
80
|
|
|
90
81
|
it('keeps tip tag behavior unchanged', () => {
|
|
@@ -90,8 +90,8 @@ export const MessageTag = ({
|
|
|
90
90
|
if (!amountText) return null
|
|
91
91
|
|
|
92
92
|
const className = standalone
|
|
93
|
-
? 'message-tip-standalone'
|
|
94
|
-
: 'message-tag
|
|
93
|
+
? 'message-tip-standalone inline-flex items-center gap-1.5 px-3.5 py-2.5 rounded-[18px] text-[14px] font-medium text-[#016e1a] bg-[#dbf0e0]'
|
|
94
|
+
: 'message-tag--tip inline-flex items-center gap-1 text-[12px] leading-[2] font-medium text-[#016630]'
|
|
95
95
|
|
|
96
96
|
const amountLabel = isPaidMessage(message) ? 'message' : 'tip'
|
|
97
97
|
const label = standalone
|
|
@@ -111,28 +111,27 @@ export const MessageTag = ({
|
|
|
111
111
|
? 'Sent with AI'
|
|
112
112
|
: 'Sent by AI Assistant'
|
|
113
113
|
|
|
114
|
-
const
|
|
115
|
-
'
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const label = (
|
|
125
|
-
<span className="message-chatbot-indicator__label">{chatbotLabel}</span>
|
|
126
|
-
)
|
|
114
|
+
const chatbotBase =
|
|
115
|
+
'inline-flex items-center text-[10px] font-normal leading-4 tracking-[0.2px]'
|
|
116
|
+
const chatbotVariant = isSenderAttachmentVariant
|
|
117
|
+
? 'justify-end gap-2 text-black/30'
|
|
118
|
+
: isMyMessage
|
|
119
|
+
? 'w-full justify-start gap-1.5 mt-3 px-4 text-white/55'
|
|
120
|
+
: 'w-full justify-start gap-1.5 mt-3 px-4 text-black/30'
|
|
121
|
+
|
|
122
|
+
const label = <span className="text-left">{chatbotLabel}</span>
|
|
127
123
|
const icon = (
|
|
128
|
-
<span className="
|
|
124
|
+
<span className="inline-flex items-center leading-none">
|
|
129
125
|
<SparkleIcon size={isSenderAttachmentVariant ? 12 : 15} />
|
|
130
126
|
</span>
|
|
131
127
|
)
|
|
132
128
|
|
|
133
129
|
// Chatbot indicator variant
|
|
134
130
|
return (
|
|
135
|
-
<div
|
|
131
|
+
<div
|
|
132
|
+
className={`${chatbotBase} ${chatbotVariant}`}
|
|
133
|
+
data-testid="message-chatbot-indicator"
|
|
134
|
+
>
|
|
136
135
|
{isMyMessage && !isSenderAttachmentVariant ? (
|
|
137
136
|
<>
|
|
138
137
|
{label}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* In-channel chat-bubble tail (20×23), rendered as a real element at the last
|
|
5
|
+
* bubble's bottom corner. Painted behind the bubble body (`z-index: -1`), so the
|
|
6
|
+
* host bubble must be `relative` with `isolation: isolate`.
|
|
7
|
+
*
|
|
8
|
+
* The silhouette is the same 20×23 mask path shared by the card tails
|
|
9
|
+
* (`.messaging-link-card-tail` in styles.css) and the standalone `MessageBubble`
|
|
10
|
+
* preview — one source of truth for the shape. It stays a CSS `mask` because the
|
|
11
|
+
* fill comes from the bubble-surface colour (so a plain `background-color` shows
|
|
12
|
+
* through the shape), and the data-URI path isn't expressible in Tailwind.
|
|
13
|
+
*/
|
|
14
|
+
const TAIL_MASK =
|
|
15
|
+
"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='23' fill='none'%3E%3Cpath fill='black' d='M0 0H15C15 10.1934 15.859 15.2345 19.168 21.4893C19.527 22.168 19.039 22.9917 18.274 22.9178C5.176 21.6506 0.32 6.0737 0 0Z'/%3E%3C/svg%3E\") no-repeat center / 100% 100%"
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Bubble surface colours, reused from the toolkit's Stream theme vars so the tail
|
|
19
|
+
* is tinted the same as its bubble. Those vars live on `.str-chat`; the `var()`
|
|
20
|
+
* fallbacks keep the tail correct if it ever renders outside a channel.
|
|
21
|
+
*/
|
|
22
|
+
const BG_BY_SIDE = {
|
|
23
|
+
me: 'var(--str-chat__own-message-bubble-background-color, #1e2330)',
|
|
24
|
+
other: 'var(--str-chat__message-bubble-background-color, #f1f0ee)',
|
|
25
|
+
} as const
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* `me` tails hang off the right edge; `other` tails mirror to the left with
|
|
29
|
+
* `scaleX(-1)`, matching the in-channel convention.
|
|
30
|
+
*/
|
|
31
|
+
export const MessageTail = ({ side }: { side: 'me' | 'other' }) => (
|
|
32
|
+
<span
|
|
33
|
+
aria-hidden="true"
|
|
34
|
+
className="pointer-events-none absolute bottom-0 z-[-1] h-[1.4375rem] w-5"
|
|
35
|
+
style={{
|
|
36
|
+
insetInlineEnd: side === 'me' ? 'calc(-1 * 0.3125rem)' : undefined,
|
|
37
|
+
insetInlineStart: side === 'other' ? 'calc(-1 * 0.3125rem)' : undefined,
|
|
38
|
+
transform: side === 'other' ? 'scaleX(-1)' : undefined,
|
|
39
|
+
transformOrigin: 'center',
|
|
40
|
+
backgroundColor: BG_BY_SIDE[side],
|
|
41
|
+
WebkitMask: TAIL_MASK,
|
|
42
|
+
mask: TAIL_MASK,
|
|
43
|
+
}}
|
|
44
|
+
/>
|
|
45
|
+
)
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
MegaphoneIcon,
|
|
5
5
|
WarningCircleIcon,
|
|
6
6
|
} from '@phosphor-icons/react'
|
|
7
|
+
import classNames from 'classnames'
|
|
7
8
|
import {
|
|
8
9
|
MessageStatus,
|
|
9
10
|
useChannelStateContext,
|
|
@@ -107,13 +108,19 @@ const BroadcastStatus = ({
|
|
|
107
108
|
}) => {
|
|
108
109
|
const campaignName = resolvedBroadcastName || broadcastName
|
|
109
110
|
const name = campaignName || 'Broadcast'
|
|
111
|
+
// Underline the name only in the clickable variant (it reads as a link).
|
|
112
|
+
// The name's colour still comes from the `.str-chat`-scoped grid rule in
|
|
113
|
+
// styles.css; we only add the underline here.
|
|
114
|
+
const isLink = Boolean(onBroadcastClick)
|
|
110
115
|
const content = (
|
|
111
116
|
<>
|
|
112
117
|
<span className="sent-message-broadcast-badge" aria-hidden="true">
|
|
113
118
|
<MegaphoneIcon weight="fill" />
|
|
114
119
|
</span>
|
|
115
120
|
<span
|
|
116
|
-
className=
|
|
121
|
+
className={classNames('sent-message-broadcast-name', {
|
|
122
|
+
'underline [text-decoration-skip-ink:auto]': isLink,
|
|
123
|
+
})}
|
|
117
124
|
data-testid="sent-message-broadcast-status"
|
|
118
125
|
>
|
|
119
126
|
{name}
|
|
@@ -124,7 +131,7 @@ const BroadcastStatus = ({
|
|
|
124
131
|
return onBroadcastClick ? (
|
|
125
132
|
<button
|
|
126
133
|
type="button"
|
|
127
|
-
className="
|
|
134
|
+
className="inline-flex cursor-pointer items-center gap-1 border-0 bg-transparent p-0 text-inherit [font:inherit]"
|
|
128
135
|
aria-label={`View broadcast ${name}`}
|
|
129
136
|
onClick={() =>
|
|
130
137
|
onBroadcastClick({
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import classNames from 'classnames'
|
|
1
2
|
import React from 'react'
|
|
2
3
|
import type { Attachment, LocalMessage } from 'stream-chat'
|
|
3
4
|
import {
|
|
@@ -12,6 +13,8 @@ import { isPlayableMedia } from '../LinkAttachment/components/_shared/CardThumbn
|
|
|
12
13
|
import MessageAttachment from '../MessageAttachment'
|
|
13
14
|
import type { BubbleGroupPosition } from '../MessageAttachment/types'
|
|
14
15
|
|
|
16
|
+
import { MessageTail } from './MessageTail'
|
|
17
|
+
|
|
15
18
|
type StreamMediaKind = 'image' | 'video' | 'audio' | 'pdf' | 'file'
|
|
16
19
|
|
|
17
20
|
export type AttachmentSegment =
|
|
@@ -344,12 +347,16 @@ const StreamAttachmentMessage: React.FC<StreamAttachmentMessageProps> = ({
|
|
|
344
347
|
|
|
345
348
|
// A link card paints its own accent-coloured tail only when it is the very
|
|
346
349
|
// last thing this message renders. A caption bubble follows when there is
|
|
347
|
-
// one, and that bubble
|
|
348
|
-
// mid-run has its tail on a later message. Anything else is a mid-run
|
|
349
|
-
// which must stay tail-less.
|
|
350
|
+
// one, and that bubble owns the tail (rendered below via `MessageTail`); and a
|
|
351
|
+
// message mid-run has its tail on a later message. Anything else is a mid-run
|
|
352
|
+
// card, which must stay tail-less.
|
|
350
353
|
const linkTailPosition: BubbleGroupPosition = caption
|
|
351
354
|
? 'middle'
|
|
352
355
|
: groupPosition
|
|
356
|
+
// The trailing caption bubble carries the tail on single / last-in-run
|
|
357
|
+
// messages, matching the in-channel text bubbles.
|
|
358
|
+
const captionShowsTail =
|
|
359
|
+
groupPosition === 'single' || groupPosition === 'end'
|
|
353
360
|
|
|
354
361
|
const pushLinkRow = (
|
|
355
362
|
attachment: Attachment,
|
|
@@ -422,9 +429,16 @@ const StreamAttachmentMessage: React.FC<StreamAttachmentMessageProps> = ({
|
|
|
422
429
|
{!caption && <MessageErrorIcon />}
|
|
423
430
|
</div>
|
|
424
431
|
{caption ? (
|
|
425
|
-
<div
|
|
432
|
+
<div
|
|
433
|
+
className={classNames('str-chat__message-bubble', {
|
|
434
|
+
'relative isolate': captionShowsTail,
|
|
435
|
+
})}
|
|
436
|
+
>
|
|
426
437
|
<MessageText message={message} renderText={renderText} />
|
|
427
438
|
<MessageErrorIcon />
|
|
439
|
+
{captionShowsTail && (
|
|
440
|
+
<MessageTail side={isMyMessage ? 'me' : 'other'} />
|
|
441
|
+
)}
|
|
428
442
|
</div>
|
|
429
443
|
) : null}
|
|
430
444
|
</div>
|
|
@@ -29,7 +29,6 @@ import {
|
|
|
29
29
|
type MessageUIComponentProps,
|
|
30
30
|
} from 'stream-chat-react'
|
|
31
31
|
|
|
32
|
-
import { useMessageVote } from '../../hooks/useMessageVote'
|
|
33
32
|
import { getMessageDisplayText } from '../../utils/getMessageDisplayText'
|
|
34
33
|
import { Avatar } from '../Avatar'
|
|
35
34
|
import { isLinkAttachment } from '../MediaMessage'
|
|
@@ -46,20 +45,18 @@ import {
|
|
|
46
45
|
isTextAttachmentMessage,
|
|
47
46
|
isTipOnlyMessage,
|
|
48
47
|
} from './MessageTag'
|
|
49
|
-
import {
|
|
48
|
+
import { MessageTail } from './MessageTail'
|
|
50
49
|
import { SentMessageDeliveryStatus } from './SentMessageDeliveryStatus'
|
|
51
50
|
import StreamAttachmentMessage, {
|
|
52
51
|
buildOrderedAttachmentSegments,
|
|
53
52
|
} from './StreamAttachmentMessage'
|
|
54
53
|
|
|
55
54
|
type CustomMessageUIComponentProps = MessageUIComponentProps & {
|
|
56
|
-
chatbotVotingEnabled?: boolean
|
|
57
55
|
viewerLanguage?: string
|
|
58
56
|
onBroadcastClick?: (broadcast: { id: string; name?: string }) => void
|
|
59
57
|
}
|
|
60
58
|
|
|
61
59
|
type CustomMessageWithContextProps = MessageContextValue & {
|
|
62
|
-
chatbotVotingEnabled?: boolean
|
|
63
60
|
viewerLanguage?: string
|
|
64
61
|
onBroadcastClick?: (broadcast: { id: string; name?: string }) => void
|
|
65
62
|
resolvedBroadcastName?: string
|
|
@@ -68,7 +65,6 @@ type CustomMessageWithContextProps = MessageContextValue & {
|
|
|
68
65
|
const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
69
66
|
const {
|
|
70
67
|
additionalMessageInputProps,
|
|
71
|
-
chatbotVotingEnabled,
|
|
72
68
|
editing,
|
|
73
69
|
endOfGroup,
|
|
74
70
|
firstOfGroup,
|
|
@@ -93,7 +89,6 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
93
89
|
useCustomMessage('LockedAttachment')
|
|
94
90
|
const [isBounceDialogOpen, setIsBounceDialogOpen] = useState(false)
|
|
95
91
|
const reminder = useMessageReminder(message.id)
|
|
96
|
-
const { selected: voteState, voteUp, voteDown } = useMessageVote(message)
|
|
97
92
|
|
|
98
93
|
const {
|
|
99
94
|
Attachment = DefaultAttachment,
|
|
@@ -151,12 +146,15 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
151
146
|
(total, segment) => total + segment.attachments.length,
|
|
152
147
|
0
|
|
153
148
|
) === (finalAttachments?.length ?? 0)
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
149
|
+
const bubbleGroupPosition = messageAttachmentGroupPositionFromStream({
|
|
150
|
+
endOfGroup,
|
|
151
|
+
firstOfGroup,
|
|
152
|
+
groupedByUser,
|
|
153
|
+
})
|
|
154
|
+
// The trailing bubble of a same-author run carries the tail (matches Stream's
|
|
155
|
+
// `--single`/`--bottom` grouping the card tails already key off).
|
|
156
|
+
const showTail =
|
|
157
|
+
bubbleGroupPosition === 'single' || bubbleGroupPosition === 'end'
|
|
160
158
|
|
|
161
159
|
if (isDateSeparatorMessage(message)) {
|
|
162
160
|
return null
|
|
@@ -353,17 +351,22 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
353
351
|
{!isMine && MessageActions && <MessageActions />}
|
|
354
352
|
</div>
|
|
355
353
|
{message.text && (
|
|
356
|
-
<div
|
|
354
|
+
<div
|
|
355
|
+
className={classNames('str-chat__message-bubble', {
|
|
356
|
+
'relative isolate': showTail,
|
|
357
|
+
})}
|
|
358
|
+
>
|
|
357
359
|
<MessageText
|
|
358
360
|
message={displayMessage}
|
|
359
361
|
renderText={renderText}
|
|
360
362
|
/>
|
|
363
|
+
{showTail && <MessageTail side={isMine ? 'me' : 'other'} />}
|
|
361
364
|
</div>
|
|
362
365
|
)}
|
|
363
366
|
</div>
|
|
364
367
|
) : canRenderAttachmentsInToolkit ? (
|
|
365
368
|
<StreamAttachmentMessage
|
|
366
|
-
groupPosition={
|
|
369
|
+
groupPosition={bubbleGroupPosition}
|
|
367
370
|
isMyMessage={isMine}
|
|
368
371
|
message={{
|
|
369
372
|
...displayMessage,
|
|
@@ -377,7 +380,11 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
377
380
|
/>
|
|
378
381
|
) : (
|
|
379
382
|
<div className="str-chat__message-bubble-wrapper">
|
|
380
|
-
<div
|
|
383
|
+
<div
|
|
384
|
+
className={classNames('str-chat__message-bubble', {
|
|
385
|
+
'relative isolate': showTail,
|
|
386
|
+
})}
|
|
387
|
+
>
|
|
381
388
|
{isChatbot && !useAttachmentFooterChatbotTag && (
|
|
382
389
|
<MessageTag
|
|
383
390
|
message={message}
|
|
@@ -404,6 +411,7 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
404
411
|
/>
|
|
405
412
|
)}
|
|
406
413
|
<MessageErrorIcon />
|
|
414
|
+
{showTail && <MessageTail side={isMine ? 'me' : 'other'} />}
|
|
407
415
|
</div>
|
|
408
416
|
</div>
|
|
409
417
|
)}
|
|
@@ -417,13 +425,6 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
417
425
|
isMyMessage={isMine}
|
|
418
426
|
/>
|
|
419
427
|
)}
|
|
420
|
-
{chatbotVotingEnabled && isChatbot && (
|
|
421
|
-
<MessageVoteButtons
|
|
422
|
-
selected={voteState}
|
|
423
|
-
onVoteUp={voteUp}
|
|
424
|
-
onVoteDown={voteDown}
|
|
425
|
-
/>
|
|
426
|
-
)}
|
|
427
428
|
</div>
|
|
428
429
|
)}
|
|
429
430
|
<SentMessageDeliveryStatus
|
|
@@ -447,7 +448,6 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
447
448
|
const MemoizedCustomMessage = React.memo(
|
|
448
449
|
CustomMessageWithContext,
|
|
449
450
|
(prev, next) => {
|
|
450
|
-
if (prev.chatbotVotingEnabled !== next.chatbotVotingEnabled) return false
|
|
451
451
|
if (prev.viewerLanguage !== next.viewerLanguage) return false
|
|
452
452
|
if (prev.onBroadcastClick !== next.onBroadcastClick) return false
|
|
453
453
|
if (prev.resolvedBroadcastName !== next.resolvedBroadcastName) return false
|
|
@@ -24,12 +24,11 @@ export interface BubbleTailProps {
|
|
|
24
24
|
* Wraps a `LinkAttachment` card in the chat bubble tail from the messaging
|
|
25
25
|
* design system, painted to match the card's own bottom edge.
|
|
26
26
|
*
|
|
27
|
-
* The geometry and mask live in `styles.css`
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* tinted from the text-bubble surface would clash with a coloured card.
|
|
27
|
+
* The geometry and mask live in `styles.css` (`.messaging-link-card-tail`),
|
|
28
|
+
* mirroring the text-bubble tail (`MessageTail`) they share a silhouette with.
|
|
29
|
+
* A card carries its own accent-tinted tail rather than the surface-tinted
|
|
30
|
+
* `MessageTail`, so `StreamAttachmentMessage` renders `MessageTail` only on the
|
|
31
|
+
* trailing caption bubble, never on the card bubble itself.
|
|
33
32
|
*/
|
|
34
33
|
const BubbleTail: React.FC<BubbleTailProps> = ({
|
|
35
34
|
accentHex,
|