@linktr.ee/messaging-react 3.5.6-rc-1783064039 → 3.5.6-rc-1783097662
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-BY5GWs2L.cjs → Card-4eHS-HQB.cjs} +2 -2
- package/dist/{Card-BY5GWs2L.cjs.map → Card-4eHS-HQB.cjs.map} +1 -1
- package/dist/{Card-DdUewLf6.cjs → Card-Bxu71IQM.cjs} +2 -2
- package/dist/{Card-DdUewLf6.cjs.map → Card-Bxu71IQM.cjs.map} +1 -1
- package/dist/{Card-CsQmLvWE.cjs → Card-By-eDX0U.cjs} +2 -2
- package/dist/{Card-CsQmLvWE.cjs.map → Card-By-eDX0U.cjs.map} +1 -1
- package/dist/{Card-CuWniEOV.js → Card-CmLNgIdO.js} +2 -2
- package/dist/{Card-CuWniEOV.js.map → Card-CmLNgIdO.js.map} +1 -1
- package/dist/{Card-CpgkVykU.js → Card-XSDNvOZf.js} +3 -3
- package/dist/{Card-CpgkVykU.js.map → Card-XSDNvOZf.js.map} +1 -1
- package/dist/{Card-DQdw5-A_.js → Card-mDB7sZiu.js} +2 -2
- package/dist/{Card-DQdw5-A_.js.map → Card-mDB7sZiu.js.map} +1 -1
- package/dist/{LockedThumbnail-D5hLQ6Kz.js → LockedThumbnail-C44Wkle9.js} +3 -3
- package/dist/{LockedThumbnail-D5hLQ6Kz.js.map → LockedThumbnail-C44Wkle9.js.map} +1 -1
- package/dist/{LockedThumbnail-s_LZkGm4.cjs → LockedThumbnail-CWhzO3wx.cjs} +2 -2
- package/dist/{LockedThumbnail-s_LZkGm4.cjs.map → LockedThumbnail-CWhzO3wx.cjs.map} +1 -1
- package/dist/assets/index.css +1 -1
- package/dist/{index-3vM1UOPb.js → index-BRV621wo.js} +1601 -1794
- package/dist/index-BRV621wo.js.map +1 -0
- package/dist/index-DBVgTHrA.cjs +2 -0
- package/dist/index-DBVgTHrA.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/src/components/AttachmentCard/index.tsx +1 -1
- package/src/components/ChannelView.stories.tsx +11 -65
- package/src/components/ChannelView.test.tsx +12 -30
- package/src/components/ChannelView.tsx +22 -50
- package/src/components/CustomLinkPreviewList/CustomLinkPreviewCard.tsx +1 -1
- package/src/components/CustomLinkPreviewList/index.tsx +6 -12
- package/src/components/CustomMessage/CustomMessage.stories.tsx +27 -0
- package/src/components/CustomMessage/MessageAttachmentConversations.stories.tsx +2 -2
- package/src/components/CustomMessage/MessageTag.stories.tsx +2 -2
- package/src/components/CustomMessageInput/index.tsx +4 -12
- package/src/components/LinkAttachment/components/_shared/CardShell.tsx +1 -1
- package/src/components/LockedAttachment/components/_shared/LockedCardShell.tsx +1 -1
- package/src/components/MediaMessage/index.tsx +8 -1
- package/src/components/MessageAttachment/_shared/Bubble.tsx +7 -7
- package/src/components/MessageAttachment/_shared/CompactDocumentRow.tsx +3 -3
- package/src/styles.css +104 -9
- package/dist/index-3vM1UOPb.js.map +0 -1
- package/dist/index-BIJpO0jL.cjs +0 -2
- package/dist/index-BIJpO0jL.cjs.map +0 -1
- package/src/components/ChannelHeaderRedesign.tsx +0 -231
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-DBVgTHrA.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-BRV621wo.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.5.6-rc-
|
|
3
|
+
"version": "3.5.6-rc-1783097662",
|
|
4
4
|
"description": "React messaging components built on messaging-core for web applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@linktr.ee/component-library": "11.8.6",
|
|
41
|
-
"@linktr.ee/messaging-core": "2.3.0-rc-
|
|
41
|
+
"@linktr.ee/messaging-core": "2.3.0-rc-1783097662",
|
|
42
42
|
"@phosphor-icons/react": "^2.1.10"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
@@ -57,7 +57,7 @@ const AttachmentCard: React.FC<AttachmentCardProps> = ({
|
|
|
57
57
|
data-testid={dataTestId}
|
|
58
58
|
className={classNames(
|
|
59
59
|
'relative w-[280px] select-none overflow-hidden rounded-[24px] shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_4px_8px_rgba(0,0,0,0.06)]',
|
|
60
|
-
isDark ? 'bg-[#
|
|
60
|
+
isDark ? 'bg-[#1e2330]' : 'bg-white'
|
|
61
61
|
)}
|
|
62
62
|
>
|
|
63
63
|
{topLeft ? (
|
|
@@ -16,7 +16,6 @@ import { ChannelView } from './ChannelView'
|
|
|
16
16
|
import { ChannelEmptyState } from './MessagingShell/ChannelEmptyState'
|
|
17
17
|
|
|
18
18
|
type ComponentProps = React.ComponentProps<typeof ChannelView>
|
|
19
|
-
const SCROLLABLE_MESSAGE_COUNT = 10
|
|
20
19
|
|
|
21
20
|
const meta: Meta<ComponentProps> = {
|
|
22
21
|
title: 'ChannelView',
|
|
@@ -42,9 +41,7 @@ const createMockChannel = async (
|
|
|
42
41
|
client: StreamChat,
|
|
43
42
|
hasMessages = true,
|
|
44
43
|
followerStatus?: string | boolean,
|
|
45
|
-
isFrozen = false
|
|
46
|
-
participantCustomerTag?: string,
|
|
47
|
-
hasScrollableMessages = false
|
|
44
|
+
isFrozen = false
|
|
48
45
|
) => {
|
|
49
46
|
const participant = mockParticipants[0]
|
|
50
47
|
|
|
@@ -104,34 +101,6 @@ const createMockChannel = async (
|
|
|
104
101
|
cid: 'messaging:storybook-channel-1',
|
|
105
102
|
mentioned_users: [],
|
|
106
103
|
},
|
|
107
|
-
...(hasScrollableMessages
|
|
108
|
-
? Array.from({ length: SCROLLABLE_MESSAGE_COUNT }, (_, index) => {
|
|
109
|
-
const isParticipantMessage = index % 2 === 0
|
|
110
|
-
const user = isParticipantMessage ? participant : mockUser
|
|
111
|
-
const text = isParticipantMessage
|
|
112
|
-
? `Here is another message to show how the redesigned header floats above the thread (${index + 1}).`
|
|
113
|
-
: `Replying so the conversation has enough content to scroll under the header (${index + 1}).`
|
|
114
|
-
|
|
115
|
-
return {
|
|
116
|
-
id: `msg-extra-${index + 1}`,
|
|
117
|
-
text,
|
|
118
|
-
type: 'regular' as const,
|
|
119
|
-
created_at: minutesAgo(Math.max(1, 25 - index)),
|
|
120
|
-
updated_at: minutesAgo(Math.max(1, 25 - index)),
|
|
121
|
-
user,
|
|
122
|
-
html: `<p>${text}</p>`,
|
|
123
|
-
attachments: [],
|
|
124
|
-
latest_reactions: [],
|
|
125
|
-
own_reactions: [],
|
|
126
|
-
reaction_counts: {},
|
|
127
|
-
reaction_scores: {},
|
|
128
|
-
reply_count: 0,
|
|
129
|
-
status: 'received',
|
|
130
|
-
cid: 'messaging:storybook-channel-1',
|
|
131
|
-
mentioned_users: [],
|
|
132
|
-
}
|
|
133
|
-
})
|
|
134
|
-
: []),
|
|
135
104
|
]
|
|
136
105
|
: []
|
|
137
106
|
|
|
@@ -149,11 +118,7 @@ const createMockChannel = async (
|
|
|
149
118
|
}
|
|
150
119
|
|
|
151
120
|
// Create a real channel using the client
|
|
152
|
-
const channel = client.channel(
|
|
153
|
-
'messaging',
|
|
154
|
-
'storybook-channel-1',
|
|
155
|
-
channelData
|
|
156
|
-
)
|
|
121
|
+
const channel = client.channel('messaging', 'storybook-channel-1', channelData)
|
|
157
122
|
|
|
158
123
|
// Mock the watch method to return mocked data
|
|
159
124
|
channel.watch = async () => {
|
|
@@ -177,7 +142,6 @@ const createMockChannel = async (
|
|
|
177
142
|
user_id: participant.id,
|
|
178
143
|
role: 'member',
|
|
179
144
|
is_account: true,
|
|
180
|
-
customerTag: participantCustomerTag,
|
|
181
145
|
},
|
|
182
146
|
}
|
|
183
147
|
return {
|
|
@@ -212,16 +176,12 @@ type TemplateProps = ComponentProps & {
|
|
|
212
176
|
name?: string
|
|
213
177
|
image?: string
|
|
214
178
|
}
|
|
215
|
-
participantCustomerTag?: string
|
|
216
|
-
hasScrollableMessages?: boolean
|
|
217
179
|
}
|
|
218
180
|
|
|
219
181
|
const Template: StoryFn<TemplateProps> = (args) => {
|
|
220
182
|
const {
|
|
221
183
|
followerStatus,
|
|
222
184
|
isFrozen = false,
|
|
223
|
-
participantCustomerTag,
|
|
224
|
-
hasScrollableMessages,
|
|
225
185
|
typingUser,
|
|
226
186
|
...channelViewProps
|
|
227
187
|
} = args
|
|
@@ -233,27 +193,16 @@ const Template: StoryFn<TemplateProps> = (args) => {
|
|
|
233
193
|
client.user = mockUser
|
|
234
194
|
return client
|
|
235
195
|
})
|
|
236
|
-
|
|
196
|
+
|
|
237
197
|
const [channel, setChannel] = React.useState<ChannelType | null>(null)
|
|
238
198
|
|
|
239
199
|
useEffect(() => {
|
|
240
|
-
createMockChannel(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
hasScrollableMessages
|
|
247
|
-
).then((mockChannel) => {
|
|
248
|
-
setChannel(mockChannel)
|
|
249
|
-
})
|
|
250
|
-
}, [
|
|
251
|
-
client,
|
|
252
|
-
followerStatus,
|
|
253
|
-
isFrozen,
|
|
254
|
-
participantCustomerTag,
|
|
255
|
-
hasScrollableMessages,
|
|
256
|
-
])
|
|
200
|
+
createMockChannel(client, true, followerStatus, isFrozen).then(
|
|
201
|
+
(mockChannel) => {
|
|
202
|
+
setChannel(mockChannel)
|
|
203
|
+
}
|
|
204
|
+
)
|
|
205
|
+
}, [client, followerStatus, isFrozen])
|
|
257
206
|
|
|
258
207
|
useEffect(() => {
|
|
259
208
|
if (!channel || !typingUser) {
|
|
@@ -307,9 +256,6 @@ export const ParticipantIdentityTrigger: StoryFn<TemplateProps> = Template.bind(
|
|
|
307
256
|
)
|
|
308
257
|
ParticipantIdentityTrigger.args = {
|
|
309
258
|
showBackButton: false,
|
|
310
|
-
showStarButton: true,
|
|
311
|
-
participantCustomerTag: 'CUSTOMER_PAID',
|
|
312
|
-
hasScrollableMessages: true,
|
|
313
259
|
onParticipantNameClick: () =>
|
|
314
260
|
// eslint-disable-next-line no-alert
|
|
315
261
|
window.alert('Participant details clicked — open Thread content'),
|
|
@@ -318,7 +264,7 @@ ParticipantIdentityTrigger.parameters = {
|
|
|
318
264
|
docs: {
|
|
319
265
|
description: {
|
|
320
266
|
story:
|
|
321
|
-
'Header with `onParticipantNameClick` set: the
|
|
267
|
+
'Header with `onParticipantNameClick` set: the participant avatar and name render as one button with a trailing caret in both the mobile and desktop header layouts. Click the identity block to fire the callback (shows an alert here). With the prop omitted (see Default) the header remains non-interactive.',
|
|
322
268
|
},
|
|
323
269
|
},
|
|
324
270
|
}
|
|
@@ -596,7 +542,7 @@ const EmptyTemplate: StoryFn<ComponentProps> = (args) => {
|
|
|
596
542
|
client.user = mockUser
|
|
597
543
|
return client
|
|
598
544
|
})
|
|
599
|
-
|
|
545
|
+
|
|
600
546
|
const [channel, setChannel] = React.useState<ChannelType | null>(null)
|
|
601
547
|
|
|
602
548
|
useEffect(() => {
|
|
@@ -22,9 +22,7 @@ vi.mock('stream-chat-react', () => ({
|
|
|
22
22
|
Window: ({ children }: { children: React.ReactNode }) => (
|
|
23
23
|
<div data-testid="window">{children}</div>
|
|
24
24
|
),
|
|
25
|
-
MessageList: (
|
|
26
|
-
<div data-testid="message-list">{head}</div>
|
|
27
|
-
),
|
|
25
|
+
MessageList: () => <div data-testid="message-list" />,
|
|
28
26
|
WithComponents: ({ children }: { children: React.ReactNode }) => (
|
|
29
27
|
<>{children}</>
|
|
30
28
|
),
|
|
@@ -102,11 +100,9 @@ const createChannel = ({
|
|
|
102
100
|
username: undefined as string | undefined,
|
|
103
101
|
is_account: true,
|
|
104
102
|
},
|
|
105
|
-
otherMemberCustomerTag,
|
|
106
103
|
}: {
|
|
107
104
|
currentUserId?: 'visitor-1' | 'linker-1'
|
|
108
105
|
currentUserRole?: 'owner' | 'member'
|
|
109
|
-
otherMemberCustomerTag?: string
|
|
110
106
|
otherUser?: {
|
|
111
107
|
id: string
|
|
112
108
|
name?: string
|
|
@@ -138,7 +134,6 @@ const createChannel = ({
|
|
|
138
134
|
is_account:
|
|
139
135
|
otherUser.is_account ?? currentUserId === 'visitor-1',
|
|
140
136
|
},
|
|
141
|
-
customerTag: otherMemberCustomerTag,
|
|
142
137
|
role: currentUserRole === 'owner' ? 'member' : 'owner',
|
|
143
138
|
},
|
|
144
139
|
},
|
|
@@ -208,7 +203,7 @@ describe('ChannelView', () => {
|
|
|
208
203
|
)
|
|
209
204
|
})
|
|
210
205
|
|
|
211
|
-
it('renders the
|
|
206
|
+
it('renders the participant name as a button that fires onParticipantNameClick', () => {
|
|
212
207
|
const onParticipantNameClick = vi.fn()
|
|
213
208
|
renderWithProviders(
|
|
214
209
|
<ChannelView
|
|
@@ -217,42 +212,29 @@ describe('ChannelView', () => {
|
|
|
217
212
|
/>
|
|
218
213
|
)
|
|
219
214
|
|
|
220
|
-
|
|
215
|
+
// Both the mobile and desktop header layouts render the button (one is
|
|
216
|
+
// hidden via CSS), so there is at least one match.
|
|
217
|
+
const buttons = screen.getAllByRole('button', {
|
|
221
218
|
name: /view details for linker/i,
|
|
222
219
|
})
|
|
223
|
-
expect(
|
|
220
|
+
expect(buttons.length).toBeGreaterThanOrEqual(1)
|
|
221
|
+
expect(buttons[0]).toHaveClass(
|
|
224
222
|
'appearance-none',
|
|
225
223
|
'border-0',
|
|
226
224
|
'bg-transparent',
|
|
227
|
-
'
|
|
225
|
+
'rounded-[12px]',
|
|
226
|
+
'px-1',
|
|
227
|
+
'py-0.5'
|
|
228
228
|
)
|
|
229
229
|
|
|
230
230
|
const avatars = screen.getAllByTestId('avatar')
|
|
231
231
|
expect(avatars.length).toBeGreaterThanOrEqual(1)
|
|
232
|
-
expect(avatars[0].closest('button')).toBe(
|
|
233
|
-
expect(screen.getByTestId('channel-actions-menu')).toBeInTheDocument()
|
|
232
|
+
expect(avatars[0].closest('button')).toBe(buttons[0])
|
|
234
233
|
|
|
235
|
-
|
|
234
|
+
buttons[0].click()
|
|
236
235
|
expect(onParticipantNameClick).toHaveBeenCalledTimes(1)
|
|
237
236
|
})
|
|
238
237
|
|
|
239
|
-
it('renders redesigned header badges for paid and starred conversations', () => {
|
|
240
|
-
mockIsStarred = true
|
|
241
|
-
|
|
242
|
-
renderWithProviders(
|
|
243
|
-
<ChannelView
|
|
244
|
-
channel={createChannel({
|
|
245
|
-
otherMemberCustomerTag: 'CUSTOMER_PAID',
|
|
246
|
-
})}
|
|
247
|
-
onParticipantNameClick={vi.fn()}
|
|
248
|
-
showStarButton={true}
|
|
249
|
-
/>
|
|
250
|
-
)
|
|
251
|
-
|
|
252
|
-
expect(screen.getByLabelText('Paid customer')).toBeInTheDocument()
|
|
253
|
-
expect(screen.getByLabelText('Starred conversation')).toBeInTheDocument()
|
|
254
|
-
})
|
|
255
|
-
|
|
256
238
|
it('renders the actions menu by default', () => {
|
|
257
239
|
renderWithProviders(<ChannelView channel={createChannel()} />)
|
|
258
240
|
|
|
@@ -25,7 +25,6 @@ import { resolveParticipantDisplayName } from '../utils/resolveParticipantDispla
|
|
|
25
25
|
|
|
26
26
|
import { Avatar } from './Avatar'
|
|
27
27
|
import { ChannelActionsMenu } from './ChannelActionsMenu'
|
|
28
|
-
import { ChannelHeaderRedesign } from './ChannelHeaderRedesign'
|
|
29
28
|
import { CustomDateSeparator } from './CustomDateSeparator'
|
|
30
29
|
import { CustomMessage } from './CustomMessage'
|
|
31
30
|
import { CustomMessageActions } from './CustomMessage/CustomMessageActions'
|
|
@@ -498,28 +497,27 @@ const ChannelViewInner: React.FC<{
|
|
|
498
497
|
>
|
|
499
498
|
<Window>
|
|
500
499
|
{/* Custom Channel Header */}
|
|
501
|
-
|
|
502
|
-
<
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
)}
|
|
500
|
+
<div key="lt-channel-header">
|
|
501
|
+
<CustomChannelHeader
|
|
502
|
+
onBack={onBack}
|
|
503
|
+
showBackButton={showBackButton}
|
|
504
|
+
showChannelInfo={showChannelInfo}
|
|
505
|
+
showStarButton={showStarButton}
|
|
506
|
+
dmAgentEnabled={showDmAgentHeader}
|
|
507
|
+
onLeaveConversation={onLeaveConversation}
|
|
508
|
+
onBlockParticipant={onBlockParticipant}
|
|
509
|
+
showDeleteConversation={showDeleteConversation}
|
|
510
|
+
showBlockParticipant={showBlockParticipant}
|
|
511
|
+
showReportParticipant={showReportParticipant}
|
|
512
|
+
onDeleteConversationClick={onDeleteConversationClick}
|
|
513
|
+
onBlockParticipantClick={onBlockParticipantClick}
|
|
514
|
+
onReportParticipantClick={onReportParticipantClick}
|
|
515
|
+
customChannelActions={customChannelActions}
|
|
516
|
+
renderChannelActions={renderChannelActions}
|
|
517
|
+
onParticipantNameClick={onParticipantNameClick}
|
|
518
|
+
renderHeaderTitleBadges={renderHeaderTitleBadges}
|
|
519
|
+
/>
|
|
520
|
+
</div>
|
|
523
521
|
|
|
524
522
|
{/* Custom Banner/Summary */}
|
|
525
523
|
{renderChannelBanner ? (
|
|
@@ -533,33 +531,7 @@ const ChannelViewInner: React.FC<{
|
|
|
533
531
|
key="lt-channel-message-list"
|
|
534
532
|
className="flex-1 overflow-hidden relative"
|
|
535
533
|
>
|
|
536
|
-
<MessageList
|
|
537
|
-
head={
|
|
538
|
-
onParticipantNameClick ? (
|
|
539
|
-
<ChannelHeaderRedesign
|
|
540
|
-
onBack={onBack}
|
|
541
|
-
showBackButton={showBackButton}
|
|
542
|
-
showChannelInfo={showChannelInfo}
|
|
543
|
-
showStarButton={showStarButton}
|
|
544
|
-
dmAgentEnabled={showDmAgentHeader}
|
|
545
|
-
onLeaveConversation={onLeaveConversation}
|
|
546
|
-
onBlockParticipant={onBlockParticipant}
|
|
547
|
-
showDeleteConversation={showDeleteConversation}
|
|
548
|
-
showBlockParticipant={showBlockParticipant}
|
|
549
|
-
showReportParticipant={showReportParticipant}
|
|
550
|
-
onDeleteConversationClick={onDeleteConversationClick}
|
|
551
|
-
onBlockParticipantClick={onBlockParticipantClick}
|
|
552
|
-
onReportParticipantClick={onReportParticipantClick}
|
|
553
|
-
customChannelActions={customChannelActions}
|
|
554
|
-
renderChannelActions={renderChannelActions}
|
|
555
|
-
onParticipantNameClick={onParticipantNameClick}
|
|
556
|
-
renderHeaderTitleBadges={renderHeaderTitleBadges}
|
|
557
|
-
/>
|
|
558
|
-
) : undefined
|
|
559
|
-
}
|
|
560
|
-
hideDeletedMessages
|
|
561
|
-
hideNewMessageSeparator={false}
|
|
562
|
-
/>
|
|
534
|
+
<MessageList hideDeletedMessages hideNewMessageSeparator={false} />
|
|
563
535
|
</div>
|
|
564
536
|
|
|
565
537
|
{renderConversationFooter ? (
|
|
@@ -23,7 +23,7 @@ const CustomLinkPreviewCard: React.FC<CustomLinkPreviewCardProps> = ({
|
|
|
23
23
|
href={og_scrape_url}
|
|
24
24
|
target="_blank"
|
|
25
25
|
rel="noopener noreferrer"
|
|
26
|
-
className="relative block w-full rounded-[24px] bg-[#
|
|
26
|
+
className="relative block w-full rounded-[24px] bg-[#1e2330] p-2 no-underline transition-opacity hover:opacity-90"
|
|
27
27
|
>
|
|
28
28
|
{image_url && (
|
|
29
29
|
<img
|
|
@@ -32,19 +32,13 @@ export const CustomLinkPreviewList = () => {
|
|
|
32
32
|
if (!showLinkPreviews) return null
|
|
33
33
|
|
|
34
34
|
return (
|
|
35
|
-
|
|
36
|
-
// mobile visitor surface sizes itself to the keyboard-shrunk
|
|
37
|
-
// visualViewport, so when vertical space runs out this container gives
|
|
38
|
-
// way (min-h-0) and scrolls, while the textarea + send row stays visible
|
|
39
|
-
// (shrink-0 in CustomMessageInput). Each card is wrapped in a shrink-0
|
|
40
|
-
// block so it keeps its intrinsic height and the container scrolls — a
|
|
41
|
-
// bare flex-column child would be shrunk on the column axis and its
|
|
42
|
-
// fixed-height hero would spill out of the collapsed card box.
|
|
43
|
-
<div className="messaging-link-preview-list flex min-h-0 flex-col items-stretch w-full gap-2 mb-4 overflow-y-auto overscroll-contain">
|
|
35
|
+
<div className="flex flex-col items-stretch w-full gap-2 mb-4">
|
|
44
36
|
{stateLinkPreviews.map((linkPreview) => (
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
<CustomLinkPreviewCard
|
|
38
|
+
key={linkPreview.og_scrape_url}
|
|
39
|
+
link={linkPreview}
|
|
40
|
+
onDismiss={handleDismiss}
|
|
41
|
+
/>
|
|
48
42
|
))}
|
|
49
43
|
</div>
|
|
50
44
|
)
|
|
@@ -247,6 +247,33 @@ WithTipTag.args = {
|
|
|
247
247
|
],
|
|
248
248
|
}
|
|
249
249
|
|
|
250
|
+
export const ThreeConsecutiveSentMessages: StoryFn<TemplateProps> =
|
|
251
|
+
Template.bind({})
|
|
252
|
+
ThreeConsecutiveSentMessages.args = {
|
|
253
|
+
messages: [
|
|
254
|
+
{
|
|
255
|
+
id: 'msg-1',
|
|
256
|
+
text: 'why is it that the sun',
|
|
257
|
+
user: storyUsers.visitor,
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
id: 'msg-2',
|
|
261
|
+
text: 'has rays',
|
|
262
|
+
user: storyUsers.creator,
|
|
263
|
+
},
|
|
264
|
+
{
|
|
265
|
+
id: 'msg-3',
|
|
266
|
+
text: 'how how how',
|
|
267
|
+
user: storyUsers.creator,
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: 'msg-4',
|
|
271
|
+
text: 'thanks for the note',
|
|
272
|
+
user: storyUsers.visitor,
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
}
|
|
276
|
+
|
|
250
277
|
export const TipOnly: StoryFn<TemplateProps> = Template.bind({})
|
|
251
278
|
TipOnly.args = {
|
|
252
279
|
messages: [
|
|
@@ -42,8 +42,8 @@ interface ChatNode {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
const TEXT_BG_BY_ROLE: Record<ChatRole, string> = {
|
|
45
|
-
sender: 'bg-[#
|
|
46
|
-
receiver: 'bg-[#
|
|
45
|
+
sender: 'bg-[#1e2330] text-white',
|
|
46
|
+
receiver: 'bg-[#f2f1ef] text-[#000000]',
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
const TextBubble: React.FC<{ role: ChatRole; children: React.ReactNode }> = ({
|
|
@@ -83,7 +83,7 @@ ChatbotReceiverText.parameters = skipInChromatic
|
|
|
83
83
|
export const ChatbotSenderText: StoryFn<ComponentProps> = (args) => {
|
|
84
84
|
return (
|
|
85
85
|
<div className="p-12">
|
|
86
|
-
<div className="bg-[#
|
|
86
|
+
<div className="bg-[#1e2330] rounded-[24px] px-4 py-3 w-[280px]">
|
|
87
87
|
<MessageTag {...args} />
|
|
88
88
|
</div>
|
|
89
89
|
</div>
|
|
@@ -150,7 +150,7 @@ export const AllVariants: StoryFn = () => {
|
|
|
150
150
|
</div>
|
|
151
151
|
<div className="flex items-center gap-4">
|
|
152
152
|
<span className="text-sm w-32">Chatbot (sender):</span>
|
|
153
|
-
<div className="bg-[#
|
|
153
|
+
<div className="bg-[#1e2330] rounded-[24px] px-4 py-3 w-[280px]">
|
|
154
154
|
<MessageTag
|
|
155
155
|
message={createMockMessage({
|
|
156
156
|
metadata: { custom_type: 'MESSAGE_CHATBOT' },
|
|
@@ -61,22 +61,14 @@ const CustomMessageInputInner: React.FC = () => {
|
|
|
61
61
|
} = useComponentContext('CustomMessageInput')
|
|
62
62
|
|
|
63
63
|
return (
|
|
64
|
-
// min-h-0 lets the composer shrink under keyboard squeeze (the link
|
|
65
|
-
// preview list inside is the scrollable region); the textarea + send row
|
|
66
|
-
// is shrink-0 so it can never be pushed out of view. self-stretch matters
|
|
67
|
-
// because the parent `.message-input` row uses items-end, which suppresses
|
|
68
|
-
// the default cross-axis stretch — without it this container keeps its
|
|
69
|
-
// content height and the squeeze never reaches the preview list. Stream's
|
|
70
|
-
// <MessageInput> renders its Input directly (context providers only, no
|
|
71
|
-
// wrapper element), so this is a direct flex item of that row.
|
|
72
64
|
<div
|
|
73
|
-
className="central-container flex
|
|
65
|
+
className="central-container flex flex-col gap-2 min-w-0 w-full p-2 bg-white rounded-[1.5rem] shadow-[0_4px_16px_0_rgba(0,0,0,0.08),0_1px_2px_0_rgba(0,0,0,0.04),0_0_0_1px_rgba(0,0,0,0.04)]"
|
|
74
66
|
data-dd-privacy="mask"
|
|
75
67
|
>
|
|
76
68
|
<QuotedMessagePreview />
|
|
77
69
|
<CustomLinkPreviewList />
|
|
78
70
|
<AttachmentPreviewList />
|
|
79
|
-
<div className="flex
|
|
71
|
+
<div className="flex">
|
|
80
72
|
<div className="w-full ml-2 mr-4 self-center leading-[0]">
|
|
81
73
|
<TextareaComposer
|
|
82
74
|
aria-disabled={disabled || undefined}
|
|
@@ -163,12 +155,12 @@ export const CustomMessageInput: React.FC<CustomMessageInputProps> = ({
|
|
|
163
155
|
}
|
|
164
156
|
|
|
165
157
|
return (
|
|
166
|
-
<div className="flex
|
|
158
|
+
<div className="flex flex-col gap-4 p-4">
|
|
167
159
|
<div
|
|
168
160
|
// @ts-expect-error Only React 19 onwards has `inert` in its types.
|
|
169
161
|
inert={isFrozen ? '' : undefined}
|
|
170
162
|
aria-disabled={isFrozen || undefined}
|
|
171
|
-
className="message-input flex
|
|
163
|
+
className="message-input flex items-end gap-4 aria-disabled:opacity-40"
|
|
172
164
|
>
|
|
173
165
|
{renderActions && (
|
|
174
166
|
<div className="flex h-12 shrink-0 items-center justify-center">
|
|
@@ -64,7 +64,7 @@ const CardShell: React.FC<CardShellProps> = ({
|
|
|
64
64
|
const isInteractive = href != null || onClick != null
|
|
65
65
|
const className = classNames(
|
|
66
66
|
SHELL_CLASS,
|
|
67
|
-
bgClassName ?? (variant === 'dark' ? 'bg-[#
|
|
67
|
+
bgClassName ?? (variant === 'dark' ? 'bg-[#1e2330]' : 'bg-white'),
|
|
68
68
|
// `focus-ring` is a design-system utility from the component-library
|
|
69
69
|
// tailwind preset — outline-none + a black 2px focus-visible ring
|
|
70
70
|
// with offset, so keyboard users can see the focused card.
|
|
@@ -26,7 +26,7 @@ const LockedCardShell: React.FC<LockedCardShellProps> = ({
|
|
|
26
26
|
className={classNames(
|
|
27
27
|
'relative w-[280px] select-none overflow-hidden rounded-md',
|
|
28
28
|
'shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_1px_2px_rgba(0,0,0,0.04),0_8px_32px_rgba(0,0,0,0.1)]',
|
|
29
|
-
variant === 'dark' ? 'bg-[#
|
|
29
|
+
variant === 'dark' ? 'bg-[#1e2330]' : 'bg-white'
|
|
30
30
|
)}
|
|
31
31
|
>
|
|
32
32
|
{children}
|
|
@@ -15,7 +15,7 @@ function formatBytes(bytes: number): string {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function linkCardShellClass(isMyMessage: boolean) {
|
|
18
|
-
const bg = isMyMessage ? 'bg-[#
|
|
18
|
+
const bg = isMyMessage ? 'bg-[#1e2330]' : 'bg-[#F3F3F1]'
|
|
19
19
|
return `w-[280px] select-none overflow-hidden rounded-[24px] ${bg} shadow-[0_0_0_1px_rgba(0,0,0,0.04),0_4px_8px_rgba(0,0,0,0.06)]`
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -318,6 +318,13 @@ const MediaMessageRoot: React.FC<MediaMessageProps> = ({
|
|
|
318
318
|
<div className="str-chat__message-bubble-wrapper">
|
|
319
319
|
<div
|
|
320
320
|
className="str-chat__message-bubble"
|
|
321
|
+
// The visible child is a self-contained AttachmentCard (white /
|
|
322
|
+
// #1e2330 card with its own shadow + radius), not a speech
|
|
323
|
+
// bubble, and this wrapper is transparent. Mark it so the
|
|
324
|
+
// received/sent tail in `styles.css` skips it — otherwise a
|
|
325
|
+
// tail tinted from the text-bubble surface (#f2f1ef) hangs off
|
|
326
|
+
// a white card and reads as a detached grey nub.
|
|
327
|
+
data-attachment-bubble="true"
|
|
321
328
|
style={{ padding: 0, borderRadius: 0, overflow: 'visible', background: 'transparent' }}
|
|
322
329
|
>
|
|
323
330
|
{linkAttachment ? (
|
|
@@ -29,8 +29,8 @@ export interface BubbleProps {
|
|
|
29
29
|
// Colors and metrics tracked from `stream-chat-react`'s default
|
|
30
30
|
// `.str-chat__message-bubble` token set so attachments visually drop
|
|
31
31
|
// into a normal `CustomMessage` thread without any seams:
|
|
32
|
-
// - light → `--str-
|
|
33
|
-
// - dark → `.str-chat__message--me` override in `styles.css` (#
|
|
32
|
+
// - light → `--str-chat__message-bubble-background-color` in `styles.css` (#f2f1ef)
|
|
33
|
+
// - dark → `.str-chat__message--me` override in `styles.css` (#1e2330)
|
|
34
34
|
// - text padding → `--str-chat__spacing-2 --str-chat__spacing-4` = 8px 16px
|
|
35
35
|
// - border-radius → `--str-chat__border-radius-md` = 18px
|
|
36
36
|
//
|
|
@@ -41,13 +41,13 @@ export interface BubbleProps {
|
|
|
41
41
|
// rebrand would need to touch every site. A central token would
|
|
42
42
|
// collapse the migration to one definition.
|
|
43
43
|
const BUBBLE_BG_BY_VARIANT: Record<BubbleVariant, string> = {
|
|
44
|
-
dark: 'bg-[#
|
|
45
|
-
light: 'bg-[#
|
|
44
|
+
dark: 'bg-[#1e2330]',
|
|
45
|
+
light: 'bg-[#f2f1ef]',
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
const BUBBLE_TEXT_BY_VARIANT: Record<BubbleVariant, string> = {
|
|
49
49
|
dark: 'text-white',
|
|
50
|
-
light: 'text-[#
|
|
50
|
+
light: 'text-[#000000]',
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
const BUBBLE_BORDER_BY_VARIANT: Record<BubbleVariant, string> = {
|
|
@@ -104,8 +104,8 @@ const CORNER_CLASSES_BY_SIDE_AND_POSITION: Record<
|
|
|
104
104
|
* is the file', 'Here is the image').
|
|
105
105
|
*
|
|
106
106
|
* Two visual variants:
|
|
107
|
-
* - `dark` — sender-side (Composer / Sent), white text on `#
|
|
108
|
-
* - `light` — recipient-side (Received), dark text on `#
|
|
107
|
+
* - `dark` — sender-side (Composer / Sent), white text on `#1e2330`.
|
|
108
|
+
* - `light` — recipient-side (Received), dark text on `#f2f1ef`.
|
|
109
109
|
*
|
|
110
110
|
* Background colors, padding, border-radius, and inherited font come
|
|
111
111
|
* from the stream-chat-react `.str-chat__message-bubble` token set so
|
|
@@ -41,11 +41,11 @@ export interface CompactDocumentRowProps {
|
|
|
41
41
|
// so they stay variant-aware. The title inherits the bubble's text
|
|
42
42
|
// color (set on the parent `Bubble`).
|
|
43
43
|
//
|
|
44
|
-
// `text-black/55` on the `#
|
|
44
|
+
// `text-black/55` on the `#f2f1ef` light bubble lands right at the
|
|
45
45
|
// WCAG AA 4.5:1 small-text threshold (≈4.5:1) — bump to `/65` so
|
|
46
46
|
// the meta line has a comfortable margin on the light bubble. The
|
|
47
|
-
// dark variant (`text-white/55` on `#
|
|
48
|
-
//
|
|
47
|
+
// dark variant (`text-white/55` on `#1e2330`) already passes AA,
|
|
48
|
+
// so we leave it alone.
|
|
49
49
|
const META_CLASS_BY_VARIANT: Record<BubbleVariant, string> = {
|
|
50
50
|
dark: 'text-white/55',
|
|
51
51
|
light: 'text-black/65',
|