@linktr.ee/messaging-react 1.35.0 → 1.36.0-rc-1777522205
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-DVzWMbDc.js → Card-BHrnmHeu.js} +2 -2
- package/dist/{Card-DVzWMbDc.js.map → Card-BHrnmHeu.js.map} +1 -1
- package/dist/{Card-lN2Z0Z5p.js → Card-D4vEgqWt.js} +2 -2
- package/dist/{Card-lN2Z0Z5p.js.map → Card-D4vEgqWt.js.map} +1 -1
- package/dist/{index-BsSqs61X.js → index-DOsC03ZN.js} +1266 -1240
- package/dist/index-DOsC03ZN.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/components/Avatar/Avatar.stories.tsx +2 -2
- package/src/components/Avatar/index.tsx +13 -8
- package/src/components/ChannelView.stories.tsx +31 -2
- package/src/components/ChannelView.test.tsx +75 -4
- package/src/components/ChannelView.tsx +39 -0
- package/src/types.ts +2 -0
- package/dist/index-BsSqs61X.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export declare type AttachmentSourceType = 'image' | 'audio' | 'video' | 'docume
|
|
|
27
27
|
/**
|
|
28
28
|
* Avatar component that displays a user image or colored initial fallback
|
|
29
29
|
*/
|
|
30
|
-
export declare const Avatar: ({ id, image, size, className, starred, shape,
|
|
30
|
+
export declare const Avatar: ({ id, image, size, className, starred, shape, dmAgentEnabled, }: AvatarProps) => JSX_2.Element;
|
|
31
31
|
|
|
32
32
|
export declare interface AvatarProps {
|
|
33
33
|
id: string;
|
|
@@ -37,7 +37,7 @@ export declare interface AvatarProps {
|
|
|
37
37
|
className?: string;
|
|
38
38
|
starred?: boolean;
|
|
39
39
|
shape?: 'squircle' | 'circle';
|
|
40
|
-
|
|
40
|
+
dmAgentEnabled?: boolean;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -151,6 +151,8 @@ export declare interface ChannelViewProps {
|
|
|
151
151
|
* messages will be sent with skip_push and silent flags to suppress
|
|
152
152
|
* notifications to the creator until the agent responds.
|
|
153
153
|
* The library reads chatbot_paused from channel.data internally.
|
|
154
|
+
*
|
|
155
|
+
* In header UI, this treatment is shown only for visitor view.
|
|
154
156
|
*/
|
|
155
157
|
dmAgentEnabled?: boolean;
|
|
156
158
|
/**
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as e, a as t, C as i, b as n, c as o, d as g, F as r, e as m, L as u, f as M, h as c, i as l, j as h, P as C, k as P, u as d, l as L, m as p, n as v } from "./index-
|
|
1
|
+
import { A as e, a as t, C as i, b as n, c as o, d as g, F as r, e as m, L as u, f as M, h as c, i as l, j as h, P as C, k as P, u as d, l as L, m as p, n as v } from "./index-DOsC03ZN.js";
|
|
2
2
|
export {
|
|
3
3
|
e as ActionButton,
|
|
4
4
|
t as Avatar,
|
package/package.json
CHANGED
|
@@ -34,7 +34,7 @@ AIActive.args = {
|
|
|
34
34
|
id: 'ai-agent',
|
|
35
35
|
name: 'AI Assistant',
|
|
36
36
|
image: 'https://i.pravatar.cc/150?img=12',
|
|
37
|
-
|
|
37
|
+
dmAgentEnabled: true,
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export const WithoutImage: StoryFn<ComponentProps> = Template.bind({})
|
|
@@ -170,7 +170,7 @@ export const AIActiveVariousSizes: StoryFn = () => {
|
|
|
170
170
|
id="ai-user-consistent"
|
|
171
171
|
name="AI Assistant"
|
|
172
172
|
size={size}
|
|
173
|
-
|
|
173
|
+
dmAgentEnabled
|
|
174
174
|
/>
|
|
175
175
|
<span className="text-xs text-stone">{size}px</span>
|
|
176
176
|
</div>
|
|
@@ -12,7 +12,7 @@ export interface AvatarProps {
|
|
|
12
12
|
className?: string
|
|
13
13
|
starred?: boolean
|
|
14
14
|
shape?: 'squircle' | 'circle'
|
|
15
|
-
|
|
15
|
+
dmAgentEnabled?: boolean
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
@@ -25,7 +25,7 @@ export const Avatar = ({
|
|
|
25
25
|
className,
|
|
26
26
|
starred = false,
|
|
27
27
|
shape = 'squircle',
|
|
28
|
-
|
|
28
|
+
dmAgentEnabled = false,
|
|
29
29
|
}: AvatarProps) => {
|
|
30
30
|
const emoji = getAvatarEmoji(id)
|
|
31
31
|
|
|
@@ -37,14 +37,15 @@ export const Avatar = ({
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
const fontSizeClass = getFontSizeClass()
|
|
40
|
-
|
|
40
|
+
// Match design treatment: 2px AI border at 40px avatar size.
|
|
41
|
+
const aiBorderWidth = size >= 40 ? 2 : 1
|
|
41
42
|
|
|
42
43
|
const borderStyle =
|
|
43
44
|
shape === 'circle'
|
|
44
45
|
? { borderRadius: '50%' }
|
|
45
46
|
: {
|
|
46
47
|
borderRadius: '33%',
|
|
47
|
-
|
|
48
|
+
cornerShape: 'superellipse(1.3)',
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
const avatarInner = (
|
|
@@ -89,15 +90,19 @@ export const Avatar = ({
|
|
|
89
90
|
</div>
|
|
90
91
|
)}
|
|
91
92
|
<div
|
|
93
|
+
data-testid="avatar-ring"
|
|
92
94
|
className={classNames(
|
|
93
95
|
'h-full w-full',
|
|
94
|
-
|
|
95
|
-
? 'bg-[linear-gradient(135deg,#A855F7_0%,#8B5CF6_45%,#6D28D9_100%)]'
|
|
96
|
-
: 'bg-transparent'
|
|
96
|
+
'bg-transparent'
|
|
97
97
|
)}
|
|
98
98
|
style={{
|
|
99
99
|
...borderStyle,
|
|
100
|
-
...(
|
|
100
|
+
...(dmAgentEnabled && {
|
|
101
|
+
borderWidth: `${aiBorderWidth}px`,
|
|
102
|
+
borderStyle: 'solid',
|
|
103
|
+
borderColor: 'var(--AI-Gradient, #7F22FE)',
|
|
104
|
+
boxShadow: 'inset 0 1px 2px 0 rgba(255, 255, 255, 0.5)',
|
|
105
|
+
}),
|
|
101
106
|
}}
|
|
102
107
|
>
|
|
103
108
|
{avatarInner}
|
|
@@ -125,12 +125,22 @@ const createMockChannel = async (
|
|
|
125
125
|
channel.state.messages = mockMessages as unknown as any[]
|
|
126
126
|
channel.state.members = {
|
|
127
127
|
[mockUser.id]: {
|
|
128
|
-
user:
|
|
128
|
+
user: {
|
|
129
|
+
...mockUser,
|
|
130
|
+
is_account: false,
|
|
131
|
+
},
|
|
129
132
|
user_id: mockUser.id,
|
|
133
|
+
role: 'owner',
|
|
134
|
+
is_account: false,
|
|
130
135
|
},
|
|
131
136
|
[participant.id]: {
|
|
132
|
-
user:
|
|
137
|
+
user: {
|
|
138
|
+
...participant,
|
|
139
|
+
is_account: true,
|
|
140
|
+
},
|
|
133
141
|
user_id: participant.id,
|
|
142
|
+
role: 'member',
|
|
143
|
+
is_account: true,
|
|
134
144
|
},
|
|
135
145
|
}
|
|
136
146
|
return {
|
|
@@ -240,6 +250,25 @@ Default.parameters = {
|
|
|
240
250
|
},
|
|
241
251
|
}
|
|
242
252
|
|
|
253
|
+
export const DmAgentHeader: StoryFn<TemplateProps> = Template.bind({})
|
|
254
|
+
DmAgentHeader.args = {
|
|
255
|
+
showBackButton: false,
|
|
256
|
+
dmAgentEnabled: true,
|
|
257
|
+
onBack: () => console.log('Back clicked'),
|
|
258
|
+
onLeaveConversation: (channel) =>
|
|
259
|
+
console.log('Leave conversation:', channel.id),
|
|
260
|
+
onBlockParticipant: (participantId) =>
|
|
261
|
+
console.log('Block participant:', participantId),
|
|
262
|
+
}
|
|
263
|
+
DmAgentHeader.parameters = {
|
|
264
|
+
docs: {
|
|
265
|
+
description: {
|
|
266
|
+
story:
|
|
267
|
+
'Channel view with DM agent header treatment enabled (AI avatar border and "Replies instantly with AI assistant" helper text).',
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
}
|
|
271
|
+
|
|
243
272
|
export const WithBackButton: StoryFn<TemplateProps> = Template.bind({})
|
|
244
273
|
WithBackButton.args = {
|
|
245
274
|
showBackButton: true,
|
|
@@ -71,16 +71,36 @@ vi.mock('../hooks/useChannelStar', () => ({
|
|
|
71
71
|
useChannelStar: () => mockIsStarred,
|
|
72
72
|
}))
|
|
73
73
|
|
|
74
|
-
const createChannel = (
|
|
74
|
+
const createChannel = ({
|
|
75
|
+
currentUserId = 'visitor-1',
|
|
76
|
+
currentUserRole = 'owner',
|
|
77
|
+
}: {
|
|
78
|
+
currentUserId?: 'visitor-1' | 'linker-1'
|
|
79
|
+
currentUserRole?: 'owner' | 'member'
|
|
80
|
+
} = {}) =>
|
|
75
81
|
({
|
|
76
82
|
id: 'channel-1',
|
|
77
83
|
cid: 'messaging:channel-1',
|
|
78
84
|
data: {},
|
|
79
|
-
_client: { userID:
|
|
85
|
+
_client: { userID: currentUserId },
|
|
80
86
|
state: {
|
|
81
87
|
members: {
|
|
82
|
-
|
|
83
|
-
|
|
88
|
+
current: {
|
|
89
|
+
user: {
|
|
90
|
+
id: currentUserId,
|
|
91
|
+
name: currentUserId === 'visitor-1' ? 'Visitor' : 'Linker',
|
|
92
|
+
is_account: currentUserId !== 'visitor-1',
|
|
93
|
+
},
|
|
94
|
+
role: currentUserRole,
|
|
95
|
+
},
|
|
96
|
+
other: {
|
|
97
|
+
user: {
|
|
98
|
+
id: currentUserId === 'visitor-1' ? 'linker-1' : 'visitor-1',
|
|
99
|
+
name: currentUserId === 'visitor-1' ? 'Linker' : 'Visitor',
|
|
100
|
+
is_account: currentUserId === 'visitor-1',
|
|
101
|
+
},
|
|
102
|
+
role: currentUserRole === 'owner' ? 'member' : 'owner',
|
|
103
|
+
},
|
|
84
104
|
},
|
|
85
105
|
membership: {},
|
|
86
106
|
messages: [],
|
|
@@ -172,6 +192,57 @@ describe('ChannelView', () => {
|
|
|
172
192
|
expect(avatarRenderCalls.every((call) => call.starred === false)).toBe(true)
|
|
173
193
|
})
|
|
174
194
|
|
|
195
|
+
it('passes dmAgentEnabled to avatar when dmAgentEnabled is true for visitor view', () => {
|
|
196
|
+
const channel = createChannel({
|
|
197
|
+
currentUserId: 'visitor-1',
|
|
198
|
+
currentUserRole: 'owner',
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
renderWithProviders(
|
|
202
|
+
<ChannelView channel={channel} dmAgentEnabled={true} />
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
expect(avatarRenderCalls.length).toBeGreaterThan(0)
|
|
206
|
+
expect(
|
|
207
|
+
avatarRenderCalls.every((call) => call.dmAgentEnabled === true)
|
|
208
|
+
).toBe(
|
|
209
|
+
true
|
|
210
|
+
)
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
it('renders dm agent helper text when dmAgentEnabled is true for visitor view', () => {
|
|
214
|
+
const channel = createChannel({
|
|
215
|
+
currentUserId: 'visitor-1',
|
|
216
|
+
currentUserRole: 'owner',
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
renderWithProviders(
|
|
220
|
+
<ChannelView channel={channel} dmAgentEnabled={true} />
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
expect(
|
|
224
|
+
screen.getAllByText('Replies instantly with AI assistant').length
|
|
225
|
+
).toBeGreaterThan(0)
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
it('does not render dm agent helper text for linker view even when dmAgentEnabled is true', () => {
|
|
229
|
+
const channel = createChannel({
|
|
230
|
+
currentUserId: 'linker-1',
|
|
231
|
+
currentUserRole: 'member',
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
renderWithProviders(
|
|
235
|
+
<ChannelView channel={channel} dmAgentEnabled={true} />
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
expect(
|
|
239
|
+
screen.queryByText('Replies instantly with AI assistant')
|
|
240
|
+
).not.toBeInTheDocument()
|
|
241
|
+
expect(
|
|
242
|
+
avatarRenderCalls.every((call) => call.dmAgentEnabled === false)
|
|
243
|
+
).toBe(true)
|
|
244
|
+
})
|
|
245
|
+
|
|
175
246
|
it('keeps channel banner and input action renderers working', () => {
|
|
176
247
|
const channel = createChannel()
|
|
177
248
|
const renderMessageInputActions = vi.fn((currentChannel: Channel) => (
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
ArrowLeftIcon,
|
|
3
3
|
CaretRightIcon,
|
|
4
4
|
DotsThreeIcon,
|
|
5
|
+
SparkleIcon,
|
|
5
6
|
StarIcon,
|
|
6
7
|
} from '@phosphor-icons/react'
|
|
7
8
|
import classNames from 'classnames'
|
|
@@ -32,6 +33,7 @@ import { LoadingState } from './MessagingShell/LoadingState'
|
|
|
32
33
|
|
|
33
34
|
const ICON_BTN_CLASS =
|
|
34
35
|
'size-10 rounded-full bg-[#F1F0EE] hover:bg-[#E5E4E1] flex items-center justify-center transition-colors duration-150 focus-ring'
|
|
36
|
+
const DM_AGENT_HEADER_HELPER_TEXT = 'Replies instantly with AI assistant'
|
|
35
37
|
|
|
36
38
|
/**
|
|
37
39
|
* Custom channel header component
|
|
@@ -42,12 +44,14 @@ const CustomChannelHeader: React.FC<{
|
|
|
42
44
|
onShowInfo: () => void
|
|
43
45
|
canShowInfo: boolean
|
|
44
46
|
showStarButton?: boolean
|
|
47
|
+
dmAgentEnabled?: boolean
|
|
45
48
|
}> = ({
|
|
46
49
|
onBack,
|
|
47
50
|
showBackButton,
|
|
48
51
|
onShowInfo,
|
|
49
52
|
canShowInfo,
|
|
50
53
|
showStarButton = false,
|
|
54
|
+
dmAgentEnabled = false,
|
|
51
55
|
}) => {
|
|
52
56
|
const { channel } = useChannelStateContext()
|
|
53
57
|
|
|
@@ -100,6 +104,7 @@ const CustomChannelHeader: React.FC<{
|
|
|
100
104
|
name={participantName}
|
|
101
105
|
image={participantImage}
|
|
102
106
|
starred={showStarButton && isStarred}
|
|
107
|
+
dmAgentEnabled={dmAgentEnabled}
|
|
103
108
|
size={40}
|
|
104
109
|
/>
|
|
105
110
|
<button
|
|
@@ -111,6 +116,12 @@ const CustomChannelHeader: React.FC<{
|
|
|
111
116
|
{participantName}
|
|
112
117
|
<CaretRightIcon className="size-3 shrink-0" />
|
|
113
118
|
</button>
|
|
119
|
+
{dmAgentEnabled && (
|
|
120
|
+
<div className="flex items-center gap-1 text-[10px] leading-3 text-black/55">
|
|
121
|
+
<SparkleIcon className="size-3 shrink-0 text-black/55" />
|
|
122
|
+
<span>{DM_AGENT_HEADER_HELPER_TEXT}</span>
|
|
123
|
+
</div>
|
|
124
|
+
)}
|
|
114
125
|
</div>
|
|
115
126
|
<div className="flex justify-end items-center gap-2">
|
|
116
127
|
{showStarButton && (
|
|
@@ -159,6 +170,7 @@ const CustomChannelHeader: React.FC<{
|
|
|
159
170
|
name={participantName}
|
|
160
171
|
image={participantImage}
|
|
161
172
|
starred={showStarButton && isStarred}
|
|
173
|
+
dmAgentEnabled={dmAgentEnabled}
|
|
162
174
|
size={40}
|
|
163
175
|
/>
|
|
164
176
|
<div className="min-w-0">
|
|
@@ -177,6 +189,12 @@ const CustomChannelHeader: React.FC<{
|
|
|
177
189
|
{participantName}
|
|
178
190
|
</h1>
|
|
179
191
|
)}
|
|
192
|
+
{dmAgentEnabled && (
|
|
193
|
+
<div className="mt-0.5 flex items-center gap-1 text-[10px] leading-3 text-black/55">
|
|
194
|
+
<SparkleIcon className="size-3 shrink-0 text-black/55" />
|
|
195
|
+
<span className="truncate">{DM_AGENT_HEADER_HELPER_TEXT}</span>
|
|
196
|
+
</div>
|
|
197
|
+
)}
|
|
180
198
|
</div>
|
|
181
199
|
</div>
|
|
182
200
|
<div className="flex items-center gap-2">
|
|
@@ -238,6 +256,7 @@ const ChannelViewInner: React.FC<{
|
|
|
238
256
|
messageNode: React.ReactElement,
|
|
239
257
|
message: NonNullable<MessageUIComponentProps['message']>
|
|
240
258
|
) => React.ReactNode
|
|
259
|
+
dmAgentEnabled?: boolean
|
|
241
260
|
}> = ({
|
|
242
261
|
onBack,
|
|
243
262
|
showBackButton,
|
|
@@ -255,6 +274,7 @@ const ChannelViewInner: React.FC<{
|
|
|
255
274
|
customProfileContent,
|
|
256
275
|
customChannelActions,
|
|
257
276
|
renderMessage,
|
|
277
|
+
dmAgentEnabled = false,
|
|
258
278
|
}) => {
|
|
259
279
|
const { channel } = useChannelStateContext()
|
|
260
280
|
const infoDialogRef = useRef<HTMLDialogElement>(null)
|
|
@@ -267,6 +287,23 @@ const ChannelViewInner: React.FC<{
|
|
|
267
287
|
)
|
|
268
288
|
}, [channel._client.userID, channel.state.members])
|
|
269
289
|
|
|
290
|
+
const currentMember = React.useMemo(() => {
|
|
291
|
+
const members = Object.values(channel.state.members || {})
|
|
292
|
+
return members.find((member) => member.user?.id === channel._client.userID)
|
|
293
|
+
}, [channel._client.userID, channel.state.members])
|
|
294
|
+
|
|
295
|
+
const currentUserIsAccount =
|
|
296
|
+
(currentMember?.user as { is_account?: boolean } | undefined)?.is_account ??
|
|
297
|
+
(currentMember as { is_account?: boolean } | undefined)?.is_account
|
|
298
|
+
const participantIsAccount =
|
|
299
|
+
(participant?.user as { is_account?: boolean } | undefined)?.is_account ??
|
|
300
|
+
(participant as { is_account?: boolean } | undefined)?.is_account
|
|
301
|
+
|
|
302
|
+
const showDmAgentHeader =
|
|
303
|
+
dmAgentEnabled &&
|
|
304
|
+
currentUserIsAccount === false &&
|
|
305
|
+
participantIsAccount === true
|
|
306
|
+
|
|
270
307
|
// Get follower status label from channel data
|
|
271
308
|
const followerStatusLabel = React.useMemo(() => {
|
|
272
309
|
const channelExtraData = (channel.data ?? {}) as {
|
|
@@ -326,6 +363,7 @@ const ChannelViewInner: React.FC<{
|
|
|
326
363
|
onShowInfo={handleShowInfo}
|
|
327
364
|
canShowInfo={Boolean(participant)}
|
|
328
365
|
showStarButton={showStarButton}
|
|
366
|
+
dmAgentEnabled={showDmAgentHeader}
|
|
329
367
|
/>
|
|
330
368
|
</div>
|
|
331
369
|
|
|
@@ -473,6 +511,7 @@ export const ChannelView = React.memo<ChannelViewProps>(
|
|
|
473
511
|
onBlockParticipantClick={onBlockParticipantClick}
|
|
474
512
|
onReportParticipantClick={onReportParticipantClick}
|
|
475
513
|
showStarButton={showStarButton}
|
|
514
|
+
dmAgentEnabled={dmAgentEnabled}
|
|
476
515
|
chatbotVotingEnabled={chatbotVotingEnabled}
|
|
477
516
|
renderChannelBanner={renderChannelBanner}
|
|
478
517
|
customProfileContent={customProfileContent}
|
package/src/types.ts
CHANGED
|
@@ -156,6 +156,8 @@ export interface ChannelViewProps {
|
|
|
156
156
|
* messages will be sent with skip_push and silent flags to suppress
|
|
157
157
|
* notifications to the creator until the agent responds.
|
|
158
158
|
* The library reads chatbot_paused from channel.data internally.
|
|
159
|
+
*
|
|
160
|
+
* In header UI, this treatment is shown only for visitor view.
|
|
159
161
|
*/
|
|
160
162
|
dmAgentEnabled?: boolean
|
|
161
163
|
|