@linktr.ee/messaging-react 1.33.3 → 1.35.0-rc-1777516745
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-BXKUE7JN.js +195 -0
- package/dist/Card-BXKUE7JN.js.map +1 -0
- package/dist/Card-J-rj8Q6w.js +167 -0
- package/dist/Card-J-rj8Q6w.js.map +1 -0
- package/dist/assets/index.css +1 -1
- package/dist/{index-Ydi1pTAi.js → index-B3H4GLek.js} +1067 -987
- package/dist/index-B3H4GLek.js.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/components/Avatar/Avatar.stories.tsx +29 -0
- package/src/components/Avatar/index.tsx +43 -20
- package/src/components/ChannelView.stories.tsx +45 -1
- package/src/components/ChannelView.tsx +9 -5
- package/src/components/CustomMessage/CustomMessage.stories.tsx +75 -15
- package/src/components/CustomMessage/MessageTag.stories.tsx +1 -1
- package/src/components/CustomMessage/index.tsx +8 -5
- package/src/components/CustomTypingIndicator/CustomTypingIndicator.stories.tsx +114 -0
- package/src/components/CustomTypingIndicator/index.tsx +101 -0
- package/src/components/LockedAttachment/LockedAttachment.stories.tsx +94 -66
- package/src/components/LockedAttachment/components/Creator/Card.tsx +55 -23
- package/src/components/LockedAttachment/components/Creator/CardThumbnail.tsx +2 -2
- package/src/components/LockedAttachment/components/Visitor/Card.tsx +9 -16
- package/src/components/LockedAttachment/components/Visitor/CardActions.tsx +3 -12
- package/src/components/LockedAttachment/components/Visitor/CardThumbnail.tsx +2 -2
- package/src/components/MediaMessage/MediaMessage.stories.tsx +77 -71
- package/src/components/MediaMessage/index.tsx +1 -1
- package/src/styles.css +27 -5
- package/dist/Card-BfA8wq8O.js +0 -181
- package/dist/Card-BfA8wq8O.js.map +0 -1
- package/dist/Card-Bpud_enW.js +0 -177
- package/dist/Card-Bpud_enW.js.map +0 -1
- package/dist/index-Ydi1pTAi.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:
|
|
30
|
+
export declare const Avatar: ({ id, image, size, className, starred, shape, aiActive, }: AvatarProps) => JSX_2.Element;
|
|
31
31
|
|
|
32
32
|
export declare interface AvatarProps {
|
|
33
33
|
id: string;
|
|
@@ -37,6 +37,7 @@ export declare interface AvatarProps {
|
|
|
37
37
|
className?: string;
|
|
38
38
|
starred?: boolean;
|
|
39
39
|
shape?: 'squircle' | 'circle';
|
|
40
|
+
aiActive?: boolean;
|
|
40
41
|
}
|
|
41
42
|
|
|
42
43
|
/**
|
|
@@ -233,8 +234,10 @@ export declare interface ChannelViewProps {
|
|
|
233
234
|
export declare interface CreatorCardProps extends LockedAttachmentBaseProps {
|
|
234
235
|
placeholderTitle?: string;
|
|
235
236
|
placeholderAmountText?: string;
|
|
237
|
+
isUnlocking?: boolean;
|
|
236
238
|
onDismiss?: () => void;
|
|
237
|
-
onPreviewClick?: () =>
|
|
239
|
+
onPreviewClick?: () => void;
|
|
240
|
+
onFetchSource?: () => Promise<LockedAttachmentSource | void>;
|
|
238
241
|
}
|
|
239
242
|
|
|
240
243
|
export declare const CustomMessageProvider: Provider<Partial<CustomMessageRegistry>>;
|
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-B3H4GLek.js";
|
|
2
2
|
export {
|
|
3
3
|
e as ActionButton,
|
|
4
4
|
t as Avatar,
|
package/package.json
CHANGED
|
@@ -29,6 +29,14 @@ Default.args = {
|
|
|
29
29
|
image: 'https://i.pravatar.cc/150?img=1',
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
export const AIActive: StoryFn<ComponentProps> = Template.bind({})
|
|
33
|
+
AIActive.args = {
|
|
34
|
+
id: 'ai-agent',
|
|
35
|
+
name: 'AI Assistant',
|
|
36
|
+
image: 'https://i.pravatar.cc/150?img=12',
|
|
37
|
+
aiActive: true,
|
|
38
|
+
}
|
|
39
|
+
|
|
32
40
|
export const WithoutImage: StoryFn<ComponentProps> = Template.bind({})
|
|
33
41
|
WithoutImage.args = {
|
|
34
42
|
id: 'user-2',
|
|
@@ -150,3 +158,24 @@ export const VariousSizes: StoryFn = () => {
|
|
|
150
158
|
)
|
|
151
159
|
}
|
|
152
160
|
|
|
161
|
+
export const AIActiveVariousSizes: StoryFn = () => {
|
|
162
|
+
const sizes = [20, 32, 40, 56, 80]
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<div className="p-12">
|
|
166
|
+
<div className="flex gap-6 items-end">
|
|
167
|
+
{sizes.map((size) => (
|
|
168
|
+
<div key={size} className="flex flex-col items-center gap-2">
|
|
169
|
+
<Avatar
|
|
170
|
+
id="ai-user-consistent"
|
|
171
|
+
name="AI Assistant"
|
|
172
|
+
size={size}
|
|
173
|
+
aiActive
|
|
174
|
+
/>
|
|
175
|
+
<span className="text-xs text-stone">{size}px</span>
|
|
176
|
+
</div>
|
|
177
|
+
))}
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
)
|
|
181
|
+
}
|
|
@@ -12,19 +12,21 @@ export interface AvatarProps {
|
|
|
12
12
|
className?: string
|
|
13
13
|
starred?: boolean
|
|
14
14
|
shape?: 'squircle' | 'circle'
|
|
15
|
+
aiActive?: boolean
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Avatar component that displays a user image or colored initial fallback
|
|
19
20
|
*/
|
|
20
|
-
export const Avatar
|
|
21
|
+
export const Avatar = ({
|
|
21
22
|
id,
|
|
22
23
|
image,
|
|
23
24
|
size = 40,
|
|
24
25
|
className,
|
|
25
26
|
starred = false,
|
|
26
27
|
shape = 'squircle',
|
|
27
|
-
|
|
28
|
+
aiActive = false,
|
|
29
|
+
}: AvatarProps) => {
|
|
28
30
|
const emoji = getAvatarEmoji(id)
|
|
29
31
|
|
|
30
32
|
const getFontSizeClass = () => {
|
|
@@ -35,6 +37,7 @@ export const Avatar: React.FC<AvatarProps> = ({
|
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
const fontSizeClass = getFontSizeClass()
|
|
40
|
+
const aiRingWidth = Math.max(1, Math.ceil(size / 40))
|
|
38
41
|
|
|
39
42
|
const borderStyle =
|
|
40
43
|
shape === 'circle'
|
|
@@ -44,9 +47,34 @@ export const Avatar: React.FC<AvatarProps> = ({
|
|
|
44
47
|
'corner-shape': 'superellipse(1.3)',
|
|
45
48
|
}
|
|
46
49
|
|
|
50
|
+
const avatarInner = (
|
|
51
|
+
<div className="h-full w-full overflow-hidden" style={borderStyle}>
|
|
52
|
+
{image ? (
|
|
53
|
+
<img
|
|
54
|
+
src={image}
|
|
55
|
+
alt=""
|
|
56
|
+
className="aspect-square h-full w-full object-cover"
|
|
57
|
+
/>
|
|
58
|
+
) : (
|
|
59
|
+
<div
|
|
60
|
+
aria-hidden="true"
|
|
61
|
+
className={classNames(
|
|
62
|
+
'avatar-fallback flex h-full w-full items-center justify-center bg-[#E6E5E3] font-semibold select-none transition-colors',
|
|
63
|
+
fontSizeClass
|
|
64
|
+
)}
|
|
65
|
+
>
|
|
66
|
+
{emoji}
|
|
67
|
+
</div>
|
|
68
|
+
)}
|
|
69
|
+
</div>
|
|
70
|
+
)
|
|
71
|
+
|
|
47
72
|
return (
|
|
48
73
|
<div
|
|
49
|
-
className={classNames(
|
|
74
|
+
className={classNames(
|
|
75
|
+
'relative flex-shrink-0 !bg-transparent',
|
|
76
|
+
className
|
|
77
|
+
)}
|
|
50
78
|
style={{
|
|
51
79
|
width: `${size}px`,
|
|
52
80
|
height: `${size}px`,
|
|
@@ -60,24 +88,19 @@ export const Avatar: React.FC<AvatarProps> = ({
|
|
|
60
88
|
<StarIcon className="size-3 text-yellow-600" weight="duotone" />
|
|
61
89
|
</div>
|
|
62
90
|
)}
|
|
63
|
-
<div
|
|
64
|
-
{
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
/>
|
|
70
|
-
) : (
|
|
71
|
-
<div
|
|
72
|
-
aria-hidden="true"
|
|
73
|
-
className={classNames(
|
|
74
|
-
'avatar-fallback flex h-full w-full items-center justify-center bg-[#E6E5E3] font-semibold select-none transition-colors',
|
|
75
|
-
fontSizeClass
|
|
76
|
-
)}
|
|
77
|
-
>
|
|
78
|
-
{emoji}
|
|
79
|
-
</div>
|
|
91
|
+
<div
|
|
92
|
+
className={classNames(
|
|
93
|
+
'h-full w-full',
|
|
94
|
+
aiActive
|
|
95
|
+
? 'bg-[linear-gradient(135deg,#A855F7_0%,#8B5CF6_45%,#6D28D9_100%)]'
|
|
96
|
+
: 'bg-transparent'
|
|
80
97
|
)}
|
|
98
|
+
style={{
|
|
99
|
+
...borderStyle,
|
|
100
|
+
padding: `${aiRingWidth}px`,
|
|
101
|
+
}}
|
|
102
|
+
>
|
|
103
|
+
{avatarInner}
|
|
81
104
|
</div>
|
|
82
105
|
</div>
|
|
83
106
|
)
|
|
@@ -160,10 +160,20 @@ const createMockChannel = async (
|
|
|
160
160
|
type TemplateProps = ComponentProps & {
|
|
161
161
|
followerStatus?: string | boolean
|
|
162
162
|
isFrozen?: boolean
|
|
163
|
+
typingUser?: {
|
|
164
|
+
id: string
|
|
165
|
+
name?: string
|
|
166
|
+
image?: string
|
|
167
|
+
}
|
|
163
168
|
}
|
|
164
169
|
|
|
165
170
|
const Template: StoryFn<TemplateProps> = (args) => {
|
|
166
|
-
const {
|
|
171
|
+
const {
|
|
172
|
+
followerStatus,
|
|
173
|
+
isFrozen = false,
|
|
174
|
+
typingUser,
|
|
175
|
+
...channelViewProps
|
|
176
|
+
} = args
|
|
167
177
|
const [client] = React.useState(() => {
|
|
168
178
|
const client = new StreamChat('mock-api-key', {
|
|
169
179
|
allowServerSideConnect: true,
|
|
@@ -183,6 +193,22 @@ const Template: StoryFn<TemplateProps> = (args) => {
|
|
|
183
193
|
)
|
|
184
194
|
}, [client, followerStatus, isFrozen])
|
|
185
195
|
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
if (!channel || !typingUser) {
|
|
198
|
+
return
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const timer = setTimeout(() => {
|
|
202
|
+
client.dispatchEvent({
|
|
203
|
+
type: 'typing.start',
|
|
204
|
+
cid: channel.cid,
|
|
205
|
+
user: typingUser,
|
|
206
|
+
})
|
|
207
|
+
}, 0)
|
|
208
|
+
|
|
209
|
+
return () => clearTimeout(timer)
|
|
210
|
+
}, [channel, client, typingUser])
|
|
211
|
+
|
|
186
212
|
if (!channel) {
|
|
187
213
|
return <div>Loading...</div>
|
|
188
214
|
}
|
|
@@ -556,6 +582,24 @@ FrozenChannel.parameters = {
|
|
|
556
582
|
},
|
|
557
583
|
}
|
|
558
584
|
|
|
585
|
+
export const WithTypingIndicator: StoryFn<TemplateProps> = Template.bind({})
|
|
586
|
+
WithTypingIndicator.args = {
|
|
587
|
+
showBackButton: false,
|
|
588
|
+
typingUser: {
|
|
589
|
+
id: mockParticipants[0].id,
|
|
590
|
+
name: mockParticipants[0].name,
|
|
591
|
+
image: mockParticipants[0].image,
|
|
592
|
+
},
|
|
593
|
+
}
|
|
594
|
+
WithTypingIndicator.parameters = {
|
|
595
|
+
docs: {
|
|
596
|
+
description: {
|
|
597
|
+
story:
|
|
598
|
+
'Channel view with the typing indicator visible, driven by a mocked typing.start event from the other participant.',
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
}
|
|
602
|
+
|
|
559
603
|
// ---------------------------------------------------------------------------
|
|
560
604
|
// Custom SendButton stories
|
|
561
605
|
// ---------------------------------------------------------------------------
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
ArrowLeftIcon,
|
|
3
|
+
CaretRightIcon,
|
|
4
|
+
DotsThreeIcon,
|
|
5
|
+
StarIcon,
|
|
6
|
+
} from '@phosphor-icons/react'
|
|
2
7
|
import classNames from 'classnames'
|
|
3
8
|
import React, { useCallback, useRef } from 'react'
|
|
4
9
|
import { Channel as ChannelType } from 'stream-chat'
|
|
@@ -21,6 +26,7 @@ import { CustomDateSeparator } from './CustomDateSeparator'
|
|
|
21
26
|
import { CustomMessage } from './CustomMessage'
|
|
22
27
|
import { CustomMessageInput } from './CustomMessageInput'
|
|
23
28
|
import { CustomSystemMessage } from './CustomSystemMessage'
|
|
29
|
+
import CustomTypingIndicator from './CustomTypingIndicator'
|
|
24
30
|
import { ChannelEmptyState } from './MessagingShell/ChannelEmptyState'
|
|
25
31
|
import { LoadingState } from './MessagingShell/LoadingState'
|
|
26
32
|
|
|
@@ -296,10 +302,7 @@ const ChannelViewInner: React.FC<{
|
|
|
296
302
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
297
303
|
const { message } = useMessageContext('ChannelView')
|
|
298
304
|
const messageNode = (
|
|
299
|
-
<CustomMessage
|
|
300
|
-
{...props}
|
|
301
|
-
chatbotVotingEnabled={chatbotVotingEnabled}
|
|
302
|
-
/>
|
|
305
|
+
<CustomMessage {...props} chatbotVotingEnabled={chatbotVotingEnabled} />
|
|
303
306
|
)
|
|
304
307
|
|
|
305
308
|
if (!renderMessage || !message) {
|
|
@@ -453,6 +456,7 @@ export const ChannelView = React.memo<ChannelViewProps>(
|
|
|
453
456
|
EmptyStateIndicator={CustomChannelEmptyState}
|
|
454
457
|
LoadingIndicator={LoadingState}
|
|
455
458
|
DateSeparator={CustomDateSeparator}
|
|
459
|
+
TypingIndicator={CustomTypingIndicator}
|
|
456
460
|
doSendMessageRequest={doSendMessageRequest}
|
|
457
461
|
{...(sendButton ? { SendButton: sendButton } : {})}
|
|
458
462
|
>
|
|
@@ -2,19 +2,30 @@ import '../../stream-custom-data'
|
|
|
2
2
|
|
|
3
3
|
import type { Meta, StoryFn } from '@storybook/react'
|
|
4
4
|
import React, { useEffect } from 'react'
|
|
5
|
-
import {
|
|
6
|
-
|
|
5
|
+
import {
|
|
6
|
+
Channel as ChannelType,
|
|
7
|
+
QueryChannelAPIResponse,
|
|
8
|
+
StreamChat,
|
|
9
|
+
} from 'stream-chat'
|
|
10
|
+
import {
|
|
11
|
+
Channel,
|
|
12
|
+
Chat,
|
|
13
|
+
MessageList,
|
|
14
|
+
MessageUIComponentProps,
|
|
15
|
+
Window,
|
|
16
|
+
} from 'stream-chat-react'
|
|
7
17
|
|
|
8
18
|
import {
|
|
9
19
|
currentUserArgType,
|
|
10
20
|
StoryUser,
|
|
11
21
|
storyUsers,
|
|
12
22
|
} from '../../stories/decorators/storyUser'
|
|
23
|
+
import CustomTypingIndicator from '../CustomTypingIndicator'
|
|
13
24
|
|
|
14
25
|
import { CustomMessage } from './index'
|
|
15
26
|
|
|
16
27
|
const meta: Meta = {
|
|
17
|
-
title: '
|
|
28
|
+
title: 'CustomMessage',
|
|
18
29
|
component: CustomMessage,
|
|
19
30
|
argTypes: currentUserArgType,
|
|
20
31
|
args: { currentUser: storyUsers.creator },
|
|
@@ -89,6 +100,7 @@ const createMockChannel = async (
|
|
|
89
100
|
|
|
90
101
|
interface TemplateProps {
|
|
91
102
|
currentUser: StoryUser
|
|
103
|
+
typingUser?: StoryUser
|
|
92
104
|
messages: Array<{
|
|
93
105
|
id: string
|
|
94
106
|
text: string
|
|
@@ -101,11 +113,7 @@ interface TemplateProps {
|
|
|
101
113
|
| 'MESSAGE_PAID'
|
|
102
114
|
| 'MESSAGE_CHATBOT'
|
|
103
115
|
| 'MESSAGE_ATTACHMENT'
|
|
104
|
-
payment_status?:
|
|
105
|
-
| 'pending'
|
|
106
|
-
| 'paid'
|
|
107
|
-
| 'failed'
|
|
108
|
-
| 'refunded'
|
|
116
|
+
payment_status?: 'pending' | 'paid' | 'failed' | 'refunded'
|
|
109
117
|
amount_text?: string
|
|
110
118
|
attachment_title?: string
|
|
111
119
|
attachment_mime_type?: string
|
|
@@ -117,8 +125,9 @@ interface TemplateProps {
|
|
|
117
125
|
|
|
118
126
|
const TemplateInner: React.FC<{
|
|
119
127
|
currentUser: StoryUser
|
|
128
|
+
typingUser?: StoryUser
|
|
120
129
|
messages: TemplateProps['messages']
|
|
121
|
-
}> = ({ currentUser, messages }) => {
|
|
130
|
+
}> = ({ currentUser, typingUser, messages }) => {
|
|
122
131
|
const [client] = React.useState(() => {
|
|
123
132
|
const c = new StreamChat('mock-api-key', { allowServerSideConnect: true })
|
|
124
133
|
c.userID = currentUser.id
|
|
@@ -132,13 +141,26 @@ const TemplateInner: React.FC<{
|
|
|
132
141
|
createMockChannel(client, messages).then(setChannel)
|
|
133
142
|
}, [client, messages])
|
|
134
143
|
|
|
144
|
+
useEffect(() => {
|
|
145
|
+
if (!channel || !typingUser) {
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Stream Channel UI only updates typing context from typing.start/typing.stop events.
|
|
150
|
+
const timer = setTimeout(() => {
|
|
151
|
+
client.dispatchEvent({
|
|
152
|
+
type: 'typing.start',
|
|
153
|
+
cid: channel.cid,
|
|
154
|
+
user: typingUser,
|
|
155
|
+
})
|
|
156
|
+
}, 0)
|
|
157
|
+
|
|
158
|
+
return () => clearTimeout(timer)
|
|
159
|
+
}, [channel, client, typingUser])
|
|
160
|
+
|
|
135
161
|
const MessageComponent = React.useMemo(() => {
|
|
136
162
|
return function CustomMessageComponent(props: MessageUIComponentProps) {
|
|
137
|
-
return
|
|
138
|
-
<CustomMessage
|
|
139
|
-
{...props}
|
|
140
|
-
/>
|
|
141
|
-
)
|
|
163
|
+
return <CustomMessage {...props} />
|
|
142
164
|
}
|
|
143
165
|
}, [])
|
|
144
166
|
|
|
@@ -149,7 +171,11 @@ const TemplateInner: React.FC<{
|
|
|
149
171
|
return (
|
|
150
172
|
<Chat client={client}>
|
|
151
173
|
<div className="h-screen w-full bg-white">
|
|
152
|
-
<Channel
|
|
174
|
+
<Channel
|
|
175
|
+
channel={channel}
|
|
176
|
+
Message={MessageComponent}
|
|
177
|
+
TypingIndicator={CustomTypingIndicator}
|
|
178
|
+
>
|
|
153
179
|
<Window>
|
|
154
180
|
<MessageList />
|
|
155
181
|
</Window>
|
|
@@ -161,11 +187,13 @@ const TemplateInner: React.FC<{
|
|
|
161
187
|
|
|
162
188
|
const Template: StoryFn<TemplateProps> = ({
|
|
163
189
|
currentUser = storyUsers.creator,
|
|
190
|
+
typingUser,
|
|
164
191
|
messages,
|
|
165
192
|
}) => (
|
|
166
193
|
<TemplateInner
|
|
167
194
|
key={currentUser.id}
|
|
168
195
|
currentUser={currentUser}
|
|
196
|
+
typingUser={typingUser}
|
|
169
197
|
messages={messages}
|
|
170
198
|
/>
|
|
171
199
|
)
|
|
@@ -357,3 +385,35 @@ ChatbotVariants.args = {
|
|
|
357
385
|
},
|
|
358
386
|
],
|
|
359
387
|
}
|
|
388
|
+
|
|
389
|
+
export const WithTypingIndicatorComparison: StoryFn<TemplateProps> =
|
|
390
|
+
Template.bind({})
|
|
391
|
+
WithTypingIndicatorComparison.args = {
|
|
392
|
+
currentUser: storyUsers.creator,
|
|
393
|
+
typingUser: storyUsers.visitor,
|
|
394
|
+
messages: [
|
|
395
|
+
{
|
|
396
|
+
id: 'msg-1',
|
|
397
|
+
text: 'Hi, is this still available?',
|
|
398
|
+
user: storyUsers.visitor,
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
id: 'msg-2',
|
|
402
|
+
text: 'Yes, absolutely.',
|
|
403
|
+
user: storyUsers.creator,
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
id: 'msg-3',
|
|
407
|
+
text: 'Great! I have another question…',
|
|
408
|
+
user: storyUsers.visitor,
|
|
409
|
+
},
|
|
410
|
+
],
|
|
411
|
+
}
|
|
412
|
+
WithTypingIndicatorComparison.parameters = {
|
|
413
|
+
docs: {
|
|
414
|
+
description: {
|
|
415
|
+
story:
|
|
416
|
+
'Renders a standard incoming message and the custom typing indicator in the same list for visual comparison.',
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
}
|
|
@@ -7,7 +7,7 @@ import { MessageTag } from './MessageTag'
|
|
|
7
7
|
type ComponentProps = React.ComponentProps<typeof MessageTag>
|
|
8
8
|
|
|
9
9
|
const meta: Meta<ComponentProps> = {
|
|
10
|
-
title: '
|
|
10
|
+
title: 'MessageTag',
|
|
11
11
|
component: MessageTag,
|
|
12
12
|
parameters: {
|
|
13
13
|
layout: 'centered',
|
|
@@ -208,7 +208,7 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
208
208
|
}}
|
|
209
209
|
>
|
|
210
210
|
{isAttachment ? (
|
|
211
|
-
<div className=
|
|
211
|
+
<div className="str-chat__message-bubble-wrapper">
|
|
212
212
|
{isMine ? (
|
|
213
213
|
<LockedAttachment.Creator
|
|
214
214
|
title={message.metadata?.attachment_title}
|
|
@@ -217,6 +217,11 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
217
217
|
amountText={message.metadata?.amount_text}
|
|
218
218
|
detail={message.metadata?.attachment_detail}
|
|
219
219
|
paymentStatus={message.metadata?.payment_status}
|
|
220
|
+
isUnlocking={isUnlocking(message.id)}
|
|
221
|
+
onPreviewClick={() => onUnlockClick?.(message, channel)}
|
|
222
|
+
onFetchSource={async () =>
|
|
223
|
+
await onFetchSource?.(message, channel)
|
|
224
|
+
}
|
|
220
225
|
/>
|
|
221
226
|
) : (
|
|
222
227
|
<LockedAttachment.Visitor
|
|
@@ -235,10 +240,8 @@ const CustomMessageWithContext = (props: CustomMessageWithContextProps) => {
|
|
|
235
240
|
/>
|
|
236
241
|
)}
|
|
237
242
|
{message.text && (
|
|
238
|
-
<div className="str-chat__message-bubble
|
|
239
|
-
<
|
|
240
|
-
<MessageText message={message} renderText={renderText} />
|
|
241
|
-
</div>
|
|
243
|
+
<div className="str-chat__message-bubble">
|
|
244
|
+
<MessageText message={message} renderText={renderText} />
|
|
242
245
|
</div>
|
|
243
246
|
)}
|
|
244
247
|
</div>
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { Meta, StoryFn } from '@storybook/react'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import type { Event } from 'stream-chat'
|
|
4
|
+
import {
|
|
5
|
+
ChannelStateProvider,
|
|
6
|
+
ChatProvider,
|
|
7
|
+
TypingProvider,
|
|
8
|
+
} from 'stream-chat-react'
|
|
9
|
+
|
|
10
|
+
import CustomTypingIndicator from '.'
|
|
11
|
+
|
|
12
|
+
type StoryProps = {
|
|
13
|
+
typingEventsEnabled?: boolean
|
|
14
|
+
typing?: Record<string, Event>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const currentUser = {
|
|
18
|
+
id: 'visitor-1',
|
|
19
|
+
name: 'Visitor',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const typingUser = {
|
|
23
|
+
id: 'creator-1',
|
|
24
|
+
name: 'Creator',
|
|
25
|
+
image: 'https://i.pravatar.cc/48?img=5',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const defaultTyping: Record<string, Event> = {
|
|
29
|
+
[typingUser.id]: {
|
|
30
|
+
type: 'typing.start',
|
|
31
|
+
user: typingUser,
|
|
32
|
+
parent_id: undefined,
|
|
33
|
+
} as Event,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const StoryWrapper: React.FC<StoryProps> = ({
|
|
37
|
+
typingEventsEnabled = true,
|
|
38
|
+
typing = defaultTyping,
|
|
39
|
+
}) => {
|
|
40
|
+
const chatContextValue = {
|
|
41
|
+
client: {
|
|
42
|
+
user: currentUser,
|
|
43
|
+
},
|
|
44
|
+
theme: 'str-chat__theme-light',
|
|
45
|
+
channelsQueryState: {
|
|
46
|
+
error: null,
|
|
47
|
+
queryInProgress: null,
|
|
48
|
+
},
|
|
49
|
+
latestMessageDatesByChannels: {},
|
|
50
|
+
mutes: [],
|
|
51
|
+
closeMobileNav: () => {},
|
|
52
|
+
openMobileNav: () => {},
|
|
53
|
+
getAppSettings: () => null,
|
|
54
|
+
setActiveChannel: () => {},
|
|
55
|
+
themeVersion: '2',
|
|
56
|
+
useImageFlagEmojisOnWindows: false,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const channelStateValue = {
|
|
60
|
+
channel: {
|
|
61
|
+
state: {
|
|
62
|
+
members: {
|
|
63
|
+
[currentUser.id]: {
|
|
64
|
+
user: currentUser,
|
|
65
|
+
},
|
|
66
|
+
[typingUser.id]: {
|
|
67
|
+
user: typingUser,
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
channelCapabilities: {},
|
|
73
|
+
channelConfig: {
|
|
74
|
+
typing_events: typingEventsEnabled,
|
|
75
|
+
},
|
|
76
|
+
imageAttachmentSizeHandler: () => ({}),
|
|
77
|
+
multipleUploads: false,
|
|
78
|
+
notifications: [],
|
|
79
|
+
shouldGenerateVideoThumbnail: false,
|
|
80
|
+
suppressAutoscroll: false,
|
|
81
|
+
videoAttachmentSizeHandler: () => ({}),
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<ChatProvider value={chatContextValue as never}>
|
|
86
|
+
<ChannelStateProvider value={channelStateValue as never}>
|
|
87
|
+
<TypingProvider value={{ typing }}>
|
|
88
|
+
<div className="relative h-20 w-[200px] bg-[#f4f4f4] p-3">
|
|
89
|
+
<CustomTypingIndicator />
|
|
90
|
+
</div>
|
|
91
|
+
</TypingProvider>
|
|
92
|
+
</ChannelStateProvider>
|
|
93
|
+
</ChatProvider>
|
|
94
|
+
)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const meta: Meta<StoryProps> = {
|
|
98
|
+
title: 'CustomTypingIndicator',
|
|
99
|
+
component: CustomTypingIndicator,
|
|
100
|
+
parameters: {
|
|
101
|
+
layout: 'centered',
|
|
102
|
+
},
|
|
103
|
+
}
|
|
104
|
+
export default meta
|
|
105
|
+
|
|
106
|
+
export const Default: StoryFn<StoryProps> = (args) => <StoryWrapper {...args} />
|
|
107
|
+
Default.args = {}
|
|
108
|
+
|
|
109
|
+
export const HiddenWhenNoTyping: StoryFn<StoryProps> = (args) => (
|
|
110
|
+
<StoryWrapper {...args} />
|
|
111
|
+
)
|
|
112
|
+
HiddenWhenNoTyping.args = {
|
|
113
|
+
typing: {},
|
|
114
|
+
}
|