@linktr.ee/messaging-react 1.14.1 → 1.15.1
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Meta, StoryFn } from '@storybook/react'
|
|
2
2
|
import React from 'react'
|
|
3
|
-
import { Channel, LocalMessage, StreamChat } from 'stream-chat'
|
|
3
|
+
import { Channel, CustomMessageData, LocalMessage, StreamChat } from 'stream-chat'
|
|
4
4
|
|
|
5
5
|
import CustomChannelPreview from './CustomChannelPreview'
|
|
6
6
|
|
|
@@ -41,6 +41,7 @@ const createMockChannel = (options: {
|
|
|
41
41
|
og_scrape_url?: string
|
|
42
42
|
thumb_url?: string
|
|
43
43
|
}>
|
|
44
|
+
lastMessageMetadata?: CustomMessageData['metadata']
|
|
44
45
|
}): Channel => {
|
|
45
46
|
const {
|
|
46
47
|
id,
|
|
@@ -51,6 +52,7 @@ const createMockChannel = (options: {
|
|
|
51
52
|
lastMessageTime = new Date(),
|
|
52
53
|
unreadCount = 0,
|
|
53
54
|
lastMessageAttachments,
|
|
55
|
+
lastMessageMetadata,
|
|
54
56
|
} = options
|
|
55
57
|
|
|
56
58
|
return {
|
|
@@ -86,6 +88,7 @@ const createMockChannel = (options: {
|
|
|
86
88
|
name: participantName,
|
|
87
89
|
},
|
|
88
90
|
attachments: lastMessageAttachments,
|
|
91
|
+
metadata: lastMessageMetadata,
|
|
89
92
|
},
|
|
90
93
|
] as unknown as LocalMessage[])
|
|
91
94
|
: ([] as LocalMessage[]),
|
|
@@ -204,6 +207,19 @@ LongName.args = {
|
|
|
204
207
|
}),
|
|
205
208
|
}
|
|
206
209
|
|
|
210
|
+
export const ChatbotMessage: StoryFn<ComponentProps> = Template.bind({})
|
|
211
|
+
ChatbotMessage.args = {
|
|
212
|
+
channel: createMockChannel({
|
|
213
|
+
id: 'channel-chatbot',
|
|
214
|
+
participantName: 'Emily Watson',
|
|
215
|
+
participantId: 'participant-chatbot',
|
|
216
|
+
participantImage: 'https://i.pravatar.cc/150?img=9',
|
|
217
|
+
lastMessageText: 'Thanks for reaching out! How can I help you today?',
|
|
218
|
+
lastMessageTime: new Date(Date.now() - 1000 * 60 * 2), // 2 minutes ago
|
|
219
|
+
lastMessageMetadata: { custom_type: 'MESSAGE_CHATBOT' },
|
|
220
|
+
}),
|
|
221
|
+
}
|
|
222
|
+
|
|
207
223
|
export const SelectedWithUnread: StoryFn<ComponentProps> = Template.bind({})
|
|
208
224
|
SelectedWithUnread.args = {
|
|
209
225
|
channel: createMockChannel({
|
|
@@ -5,6 +5,7 @@ import { ChannelPreviewUIComponentProps } from 'stream-chat-react'
|
|
|
5
5
|
|
|
6
6
|
import { formatRelativeTime } from '../../utils/formatRelativeTime'
|
|
7
7
|
import { Avatar } from '../Avatar'
|
|
8
|
+
import { isChatbotMessage } from '../CustomMessage/MessageTag'
|
|
8
9
|
|
|
9
10
|
type CustomChannelPreviewProps = ChannelPreviewUIComponentProps & {
|
|
10
11
|
selectedChannel?: Channel | null
|
|
@@ -62,6 +63,9 @@ const CustomChannelPreview = React.memo<CustomChannelPreviewProps>(
|
|
|
62
63
|
const lastMessageTime = lastMessage?.created_at
|
|
63
64
|
? formatRelativeTime(new Date(lastMessage.created_at))
|
|
64
65
|
: ''
|
|
66
|
+
const isLastMessageFromChatbot = lastMessage
|
|
67
|
+
? isChatbotMessage(lastMessage)
|
|
68
|
+
: false
|
|
65
69
|
|
|
66
70
|
// Use the unread prop passed by Stream Chat (reactive and updates automatically)
|
|
67
71
|
const unreadCount = unread ?? 0
|
|
@@ -120,6 +124,7 @@ const CustomChannelPreview = React.memo<CustomChannelPreviewProps>(
|
|
|
120
124
|
{/* Message and unread badge row */}
|
|
121
125
|
<div className="flex items-center justify-between gap-2 min-w-0">
|
|
122
126
|
<p className="text-xs text-stone mr-2 flex-1 line-clamp-2">
|
|
127
|
+
{isLastMessageFromChatbot && '✨ '}
|
|
123
128
|
{lastMessageText}
|
|
124
129
|
</p>
|
|
125
130
|
{unreadCount > 0 && (
|
|
@@ -5,10 +5,11 @@ import {
|
|
|
5
5
|
ProhibitInsetIcon,
|
|
6
6
|
SignOutIcon,
|
|
7
7
|
SpinnerGapIcon,
|
|
8
|
+
StarIcon,
|
|
8
9
|
} from '@phosphor-icons/react'
|
|
9
10
|
import classNames from 'classnames'
|
|
10
11
|
import React, { useState, useCallback, useRef, useEffect } from 'react'
|
|
11
|
-
import { Channel as ChannelType, ChannelMemberResponse } from 'stream-chat'
|
|
12
|
+
import { Channel as ChannelType, ChannelMemberResponse, Event } from 'stream-chat'
|
|
12
13
|
import {
|
|
13
14
|
Channel,
|
|
14
15
|
Window,
|
|
@@ -65,20 +66,49 @@ const CustomChannelHeader: React.FC<{
|
|
|
65
66
|
participant?.user?.name || participant?.user?.id || 'Unknown member'
|
|
66
67
|
const participantImage = participant?.user?.image
|
|
67
68
|
|
|
69
|
+
const [isStarred, setIsStarred] = useState(!!channel.state.membership?.pinned_at)
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
const handleMemberUpdate = (event: Event) => {
|
|
73
|
+
setIsStarred(event?.member ? !!event.member.pinned_at : !!channel.state.membership?.pinned_at)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
channel.on('member.updated', handleMemberUpdate)
|
|
77
|
+
|
|
78
|
+
return () => {
|
|
79
|
+
channel.off('member.updated', handleMemberUpdate)
|
|
80
|
+
}
|
|
81
|
+
}, [channel])
|
|
82
|
+
|
|
83
|
+
const handleStarClick = async () => {
|
|
84
|
+
try {
|
|
85
|
+
if (isStarred) {
|
|
86
|
+
await channel.unpin()
|
|
87
|
+
} else {
|
|
88
|
+
await channel.pin()
|
|
89
|
+
}
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error('[CustomChannelHeader] Failed to update pinned status:', error)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
68
95
|
return (
|
|
69
96
|
<div className="@container">
|
|
70
97
|
<div className="flex justify-between items-center @lg:hidden">
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
98
|
+
<div className="flex items-center gap-2">
|
|
99
|
+
<button
|
|
100
|
+
className={classNames(
|
|
101
|
+
'size-10 rounded-full bg-[#F1F0EE] flex items-center justify-center',
|
|
102
|
+
!showBackButton && 'invisible'
|
|
103
|
+
)}
|
|
104
|
+
onClick={onBack || (() => {})}
|
|
105
|
+
type="button"
|
|
106
|
+
aria-label="Back to conversations"
|
|
107
|
+
>
|
|
108
|
+
<ArrowLeftIcon className="size-5 text-black/90" />
|
|
109
|
+
</button>
|
|
110
|
+
<div className="size-10" />
|
|
111
|
+
</div>
|
|
82
112
|
<div className="flex flex-col gap-1 items-center">
|
|
83
113
|
<Avatar
|
|
84
114
|
id={participant?.user?.id || channel.id || 'unknown'}
|
|
@@ -90,16 +120,27 @@ const CustomChannelHeader: React.FC<{
|
|
|
90
120
|
{participantName}
|
|
91
121
|
</h1>
|
|
92
122
|
</div>
|
|
93
|
-
<
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
123
|
+
<div className="flex items-center gap-2">
|
|
124
|
+
<button
|
|
125
|
+
className="size-10 rounded-full bg-[#F1F0EE] flex items-center justify-center"
|
|
126
|
+
onClick={handleStarClick}
|
|
127
|
+
type="button"
|
|
128
|
+
aria-label={isStarred ? 'Unstar conversation' : 'Star conversation'}
|
|
129
|
+
>
|
|
130
|
+
<StarIcon
|
|
131
|
+
className="size-5 text-black/90"
|
|
132
|
+
weight={isStarred ? 'fill' : 'regular'}
|
|
133
|
+
/>
|
|
134
|
+
</button>
|
|
135
|
+
<button
|
|
136
|
+
className="size-10 rounded-full bg-[#F1F0EE] flex items-center justify-center"
|
|
137
|
+
onClick={onShowInfo}
|
|
138
|
+
type="button"
|
|
139
|
+
aria-label="Show info"
|
|
140
|
+
>
|
|
141
|
+
<DotsThreeIcon className="size-5 text-black/90" />
|
|
142
|
+
</button>
|
|
143
|
+
</div>
|
|
103
144
|
</div>
|
|
104
145
|
<div className="hidden @lg:flex items-center justify-between gap-3 min-h-12">
|
|
105
146
|
<div className="flex items-center gap-4 min-w-0">
|
|
@@ -126,16 +167,29 @@ const CustomChannelHeader: React.FC<{
|
|
|
126
167
|
</h1>
|
|
127
168
|
</div>
|
|
128
169
|
</div>
|
|
129
|
-
|
|
170
|
+
<div className="flex items-center gap-2">
|
|
130
171
|
<button
|
|
131
|
-
className=
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
172
|
+
className="size-10 rounded-full bg-[#F1F0EE] flex items-center justify-center"
|
|
173
|
+
onClick={handleStarClick}
|
|
174
|
+
type="button"
|
|
175
|
+
aria-label={isStarred ? 'Unstar conversation' : 'Star conversation'}
|
|
135
176
|
>
|
|
136
|
-
<
|
|
177
|
+
<StarIcon
|
|
178
|
+
className="size-5 text-black/90"
|
|
179
|
+
weight={isStarred ? 'fill' : 'regular'}
|
|
180
|
+
/>
|
|
137
181
|
</button>
|
|
138
|
-
|
|
182
|
+
{canShowInfo && onShowInfo && (
|
|
183
|
+
<button
|
|
184
|
+
className="size-10 rounded-full bg-[#F1F0EE] flex items-center justify-center"
|
|
185
|
+
onClick={onShowInfo}
|
|
186
|
+
type="button"
|
|
187
|
+
aria-label="Show info"
|
|
188
|
+
>
|
|
189
|
+
<DotsThreeIcon className="size-5 text-black/90" />
|
|
190
|
+
</button>
|
|
191
|
+
)}
|
|
192
|
+
</div>
|
|
139
193
|
</div>
|
|
140
194
|
</div>
|
|
141
195
|
)
|