@linktr.ee/messaging-react 1.0.2 → 1.1.0-rc-1760927977
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/assets/index.css +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +836 -1079
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/src/components/ActionButton/ActionButton.stories.tsx +2 -1
- package/src/components/ActionButton/ActionButton.test.tsx +2 -0
- package/src/components/Avatar/Avatar.stories.tsx +2 -1
- package/src/components/Avatar/index.tsx +2 -1
- package/src/components/ChannelList/ChannelList.stories.tsx +4 -2
- package/src/components/ChannelList/CustomChannelPreview.stories.tsx +31 -27
- package/src/components/ChannelList/CustomChannelPreview.tsx +5 -11
- package/src/components/ChannelList/index.tsx +43 -35
- package/src/components/ChannelView.tsx +150 -127
- package/src/components/CloseButton/index.tsx +4 -5
- package/src/components/IconButton/IconButton.stories.tsx +3 -3
- package/src/components/Loading/Loading.stories.tsx +2 -1
- package/src/components/Loading/index.tsx +7 -9
- package/src/components/MessagingShell/EmptyState.stories.tsx +2 -1
- package/src/components/MessagingShell/ErrorState.stories.tsx +2 -1
- package/src/components/MessagingShell/LoadingState.stories.tsx +2 -1
- package/src/components/MessagingShell/LoadingState.tsx +3 -5
- package/src/components/MessagingShell/index.tsx +159 -135
- package/src/components/ParticipantPicker/ParticipantItem.stories.tsx +4 -2
- package/src/components/ParticipantPicker/ParticipantItem.tsx +25 -21
- package/src/components/ParticipantPicker/ParticipantPicker.stories.tsx +4 -2
- package/src/components/ParticipantPicker/ParticipantPicker.tsx +104 -76
- package/src/components/ParticipantPicker/index.tsx +93 -72
- package/src/components/SearchInput/SearchInput.stories.tsx +2 -1
- package/src/components/SearchInput/SearchInput.test.tsx +4 -2
- package/src/components/SearchInput/index.tsx +14 -15
- package/src/hooks/useParticipants.ts +1 -0
- package/src/index.ts +3 -0
- package/src/providers/MessagingProvider.tsx +213 -135
- package/src/stories/mocks.tsx +18 -19
- package/src/styles.css +75 -0
- package/src/test/setup.ts +11 -12
- package/src/test/utils.tsx +6 -7
- package/src/types.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linktr.ee/messaging-react",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0-rc-1760927977",
|
|
4
4
|
"description": "React messaging components built on messaging-core for web applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
".": {
|
|
11
11
|
"types": "./dist/index.d.ts",
|
|
12
12
|
"import": "./dist/index.js"
|
|
13
|
-
}
|
|
13
|
+
},
|
|
14
|
+
"./styles.css": "./dist/assets/index.css"
|
|
14
15
|
},
|
|
15
16
|
"license": "UNLICENSED",
|
|
16
17
|
"files": [
|
|
@@ -33,7 +34,7 @@
|
|
|
33
34
|
},
|
|
34
35
|
"dependencies": {
|
|
35
36
|
"@linktr.ee/component-library": "*",
|
|
36
|
-
"@linktr.ee/messaging-core": "
|
|
37
|
+
"@linktr.ee/messaging-core": "^1.0.1",
|
|
37
38
|
"@phosphor-icons/react": "^2.1.9",
|
|
38
39
|
"stream-chat-react": "^13.9.0"
|
|
39
40
|
},
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { Meta, StoryFn } from '@storybook/react'
|
|
2
|
-
import
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
3
4
|
import { MockChatProvider } from '../../stories/mocks'
|
|
4
5
|
|
|
5
|
-
import
|
|
6
|
+
import { ChannelList } from '.'
|
|
7
|
+
|
|
6
8
|
|
|
7
9
|
type ComponentProps = React.ComponentProps<typeof ChannelList>
|
|
8
10
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Meta, StoryFn } from '@storybook/react'
|
|
2
|
-
import CustomChannelPreview from './CustomChannelPreview'
|
|
3
2
|
import React from 'react'
|
|
3
|
+
import { Channel, LocalMessage, StreamChat } from 'stream-chat'
|
|
4
|
+
|
|
5
|
+
import CustomChannelPreview from './CustomChannelPreview'
|
|
4
6
|
|
|
5
7
|
type ComponentProps = React.ComponentProps<typeof CustomChannelPreview>
|
|
6
8
|
|
|
@@ -8,13 +10,9 @@ const meta: Meta<ComponentProps> = {
|
|
|
8
10
|
title: 'ChannelList/CustomChannelPreview',
|
|
9
11
|
component: CustomChannelPreview,
|
|
10
12
|
parameters: {
|
|
11
|
-
layout: 'centered'
|
|
13
|
+
layout: 'centered',
|
|
12
14
|
},
|
|
13
|
-
decorators: [
|
|
14
|
-
(Story) => (
|
|
15
|
-
<Story />
|
|
16
|
-
)
|
|
17
|
-
]
|
|
15
|
+
decorators: [(Story) => <Story />],
|
|
18
16
|
}
|
|
19
17
|
export default meta
|
|
20
18
|
|
|
@@ -34,7 +32,7 @@ const createMockChannel = (options: {
|
|
|
34
32
|
lastMessageText?: string
|
|
35
33
|
lastMessageTime?: Date
|
|
36
34
|
unreadCount?: number
|
|
37
|
-
}):
|
|
35
|
+
}): Channel => {
|
|
38
36
|
const {
|
|
39
37
|
id,
|
|
40
38
|
participantName,
|
|
@@ -50,7 +48,7 @@ const createMockChannel = (options: {
|
|
|
50
48
|
cid: `messaging:${id}`,
|
|
51
49
|
_client: {
|
|
52
50
|
userID: mockUser.id,
|
|
53
|
-
},
|
|
51
|
+
} as unknown as StreamChat,
|
|
54
52
|
state: {
|
|
55
53
|
members: {
|
|
56
54
|
[mockUser.id]: {
|
|
@@ -66,20 +64,22 @@ const createMockChannel = (options: {
|
|
|
66
64
|
user_id: participantId,
|
|
67
65
|
},
|
|
68
66
|
},
|
|
69
|
-
messages: lastMessageText
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
67
|
+
messages: lastMessageText
|
|
68
|
+
? ([
|
|
69
|
+
{
|
|
70
|
+
id: `msg-${id}-1`,
|
|
71
|
+
text: lastMessageText,
|
|
72
|
+
created_at: lastMessageTime.toISOString(),
|
|
73
|
+
user: {
|
|
74
|
+
id: participantId,
|
|
75
|
+
name: participantName,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
] as unknown as LocalMessage[])
|
|
79
|
+
: ([] as LocalMessage[]),
|
|
80
80
|
unreadCount,
|
|
81
81
|
},
|
|
82
|
-
}
|
|
82
|
+
} as unknown as Channel
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
const Template: StoryFn<ComponentProps> = (args) => {
|
|
@@ -181,7 +181,8 @@ LongMessage.args = {
|
|
|
181
181
|
participantName: 'Grace Lee',
|
|
182
182
|
participantId: 'participant-7',
|
|
183
183
|
participantImage: 'https://i.pravatar.cc/150?img=7',
|
|
184
|
-
lastMessageText:
|
|
184
|
+
lastMessageText:
|
|
185
|
+
'This is a very long message that should be truncated because it contains way too much text to display in the preview. We want to make sure the component handles this gracefully.',
|
|
185
186
|
lastMessageTime: new Date(Date.now() - 1000 * 60 * 60 * 2), // 2 hours ago
|
|
186
187
|
}),
|
|
187
188
|
onChannelSelect: (channel) => console.log('Channel selected:', channel.id),
|
|
@@ -219,7 +220,9 @@ SelectedWithUnread.args = {
|
|
|
219
220
|
}
|
|
220
221
|
|
|
221
222
|
export const MultipleChannels: StoryFn = () => {
|
|
222
|
-
const [selectedChannelId,
|
|
223
|
+
const [selectedChannelId, _setSelectedChannelId] = React.useState<
|
|
224
|
+
string | null
|
|
225
|
+
>('channel-2')
|
|
223
226
|
|
|
224
227
|
const channels = [
|
|
225
228
|
createMockChannel({
|
|
@@ -257,11 +260,12 @@ export const MultipleChannels: StoryFn = () => {
|
|
|
257
260
|
}),
|
|
258
261
|
]
|
|
259
262
|
|
|
260
|
-
const selectedChannel =
|
|
263
|
+
const selectedChannel =
|
|
264
|
+
channels.find((c) => c.id === selectedChannelId) || null
|
|
261
265
|
|
|
262
266
|
return (
|
|
263
267
|
<div className="w-[360px] bg-chalk border border-sand rounded-lg overflow-hidden">
|
|
264
|
-
{channels.map(channel => (
|
|
268
|
+
{channels.map((channel) => (
|
|
265
269
|
<CustomChannelPreview
|
|
266
270
|
key={channel.id}
|
|
267
271
|
channel={channel}
|
|
@@ -295,9 +299,9 @@ WithVeryLongUrl.args = {
|
|
|
295
299
|
participantName: 'Julia Martinez',
|
|
296
300
|
participantId: 'participant-long-url',
|
|
297
301
|
participantImage: 'https://i.pravatar.cc/150?img=11',
|
|
298
|
-
lastMessageText:
|
|
302
|
+
lastMessageText:
|
|
303
|
+
'https://example.com/very/long/path/with/many/segments/and/query/parameters?param1=value1¶m2=value2¶m3=value3¶m4=value4¶m5=very-long-value-that-makes-the-url-extremely-long',
|
|
299
304
|
lastMessageTime: new Date(Date.now() - 1000 * 60 * 20), // 20 minutes ago
|
|
300
305
|
}),
|
|
301
306
|
onChannelSelect: (channel) => console.log('Channel selected:', channel.id),
|
|
302
307
|
}
|
|
303
|
-
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import classNames from 'classnames'
|
|
1
2
|
import React from 'react'
|
|
2
|
-
import { ChannelPreviewUIComponentProps } from 'stream-chat-react'
|
|
3
3
|
import { Channel } from 'stream-chat'
|
|
4
|
-
import
|
|
4
|
+
import { ChannelPreviewUIComponentProps } from 'stream-chat-react'
|
|
5
|
+
|
|
5
6
|
import { Avatar } from '../Avatar'
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -9,17 +10,11 @@ import { Avatar } from '../Avatar'
|
|
|
9
10
|
*/
|
|
10
11
|
const CustomChannelPreview: React.FC<
|
|
11
12
|
ChannelPreviewUIComponentProps & {
|
|
12
|
-
selectedChannel?: Channel
|
|
13
|
+
selectedChannel?: Channel | null
|
|
13
14
|
onChannelSelect: (channel: Channel) => void
|
|
14
15
|
debug?: boolean
|
|
15
16
|
}
|
|
16
|
-
> = ({
|
|
17
|
-
channel,
|
|
18
|
-
selectedChannel,
|
|
19
|
-
onChannelSelect,
|
|
20
|
-
debug = false,
|
|
21
|
-
...props
|
|
22
|
-
}) => {
|
|
17
|
+
> = ({ channel, selectedChannel, onChannelSelect, debug = false }) => {
|
|
23
18
|
const isSelected = selectedChannel?.id === channel?.id
|
|
24
19
|
|
|
25
20
|
const handleClick = () => {
|
|
@@ -35,7 +30,6 @@ const CustomChannelPreview: React.FC<
|
|
|
35
30
|
)
|
|
36
31
|
const participantName = participant?.user?.name || 'Conversation'
|
|
37
32
|
const participantImage = participant?.user?.image
|
|
38
|
-
const participantInitial = participantName.charAt(0).toUpperCase()
|
|
39
33
|
|
|
40
34
|
// Get last message and format timestamp
|
|
41
35
|
const lastMessage =
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { NotePencilIcon } from '@phosphor-icons/react'
|
|
2
|
+
import classNames from 'classnames'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import {
|
|
5
|
+
ChannelList as StreamChannelList,
|
|
6
|
+
useChatContext,
|
|
7
|
+
} from 'stream-chat-react'
|
|
3
8
|
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import { IconButton } from '../IconButton';
|
|
8
|
-
import { useMessagingContext } from '../../providers/MessagingProvider';
|
|
9
|
-
import CustomChannelPreview from './CustomChannelPreview';
|
|
9
|
+
import { useMessagingContext } from '../../providers/MessagingProvider'
|
|
10
|
+
import type { ChannelListProps } from '../../types'
|
|
11
|
+
import { IconButton } from '../IconButton'
|
|
10
12
|
|
|
13
|
+
import CustomChannelPreview from './CustomChannelPreview'
|
|
11
14
|
|
|
12
15
|
/**
|
|
13
16
|
* Channel list component with customizable header and actions
|
|
@@ -21,52 +24,57 @@ export const ChannelList: React.FC<ChannelListProps> = ({
|
|
|
21
24
|
className,
|
|
22
25
|
}) => {
|
|
23
26
|
// Track renders
|
|
24
|
-
const renderCountRef = React.useRef(0)
|
|
25
|
-
renderCountRef.current
|
|
27
|
+
const renderCountRef = React.useRef(0)
|
|
28
|
+
renderCountRef.current++
|
|
26
29
|
|
|
27
30
|
// Get debug flag from context
|
|
28
|
-
const { debug = false } = useMessagingContext()
|
|
31
|
+
const { debug = false } = useMessagingContext()
|
|
29
32
|
|
|
30
33
|
if (debug) {
|
|
31
34
|
console.log('📺 [ChannelList] 🔄 RENDER START', {
|
|
32
35
|
renderCount: renderCountRef.current,
|
|
33
36
|
selectedChannelId: selectedChannel?.id,
|
|
34
37
|
showStartConversation,
|
|
35
|
-
participantLabel
|
|
36
|
-
})
|
|
38
|
+
participantLabel,
|
|
39
|
+
})
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
const { client } = useChatContext()
|
|
42
|
+
const { client } = useChatContext()
|
|
40
43
|
|
|
41
44
|
if (debug) {
|
|
42
45
|
console.log('📺 [ChannelList] 📡 CHAT CONTEXT', {
|
|
43
46
|
renderCount: renderCountRef.current,
|
|
44
47
|
hasClient: !!client,
|
|
45
48
|
clientUserId: client?.userID,
|
|
46
|
-
clientConnected: client?.wsConnection?.isHealthy
|
|
47
|
-
})
|
|
49
|
+
clientConnected: client?.wsConnection?.isHealthy,
|
|
50
|
+
})
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
// Filter for messaging channels
|
|
51
54
|
const filters = React.useMemo(() => {
|
|
52
|
-
const userId = client.userID
|
|
55
|
+
const userId = client.userID
|
|
53
56
|
const newFilters = userId
|
|
54
57
|
? { type: 'messaging', members: { $in: [userId] }, hidden: false }
|
|
55
|
-
: { type: 'messaging' }
|
|
56
|
-
|
|
58
|
+
: { type: 'messaging' }
|
|
59
|
+
|
|
57
60
|
if (debug) {
|
|
58
61
|
console.log('📺 [ChannelList] 🔍 FILTERS MEMOIZED', {
|
|
59
62
|
renderCount: renderCountRef.current,
|
|
60
63
|
userId,
|
|
61
|
-
filters: newFilters
|
|
62
|
-
})
|
|
64
|
+
filters: newFilters,
|
|
65
|
+
})
|
|
63
66
|
}
|
|
64
|
-
|
|
65
|
-
return newFilters
|
|
66
|
-
}, [client.userID, debug])
|
|
67
|
+
|
|
68
|
+
return newFilters
|
|
69
|
+
}, [client.userID, debug])
|
|
67
70
|
|
|
68
71
|
return (
|
|
69
|
-
<div
|
|
72
|
+
<div
|
|
73
|
+
className={classNames(
|
|
74
|
+
'h-full flex flex-col min-w-0 overflow-hidden',
|
|
75
|
+
className
|
|
76
|
+
)}
|
|
77
|
+
>
|
|
70
78
|
{/* Header */}
|
|
71
79
|
<div className="px-4 py-4 border-b border-sand bg-chalk">
|
|
72
80
|
<div className="flex items-center justify-between gap-3 min-h-10 min-w-0">
|
|
@@ -93,10 +101,10 @@ export const ChannelList: React.FC<ChannelListProps> = ({
|
|
|
93
101
|
renderCount: renderCountRef.current,
|
|
94
102
|
filters,
|
|
95
103
|
hasClient: !!client,
|
|
96
|
-
clientUserId: client?.userID
|
|
97
|
-
})
|
|
104
|
+
clientUserId: client?.userID,
|
|
105
|
+
})
|
|
98
106
|
}
|
|
99
|
-
|
|
107
|
+
|
|
100
108
|
return (
|
|
101
109
|
<StreamChannelList
|
|
102
110
|
filters={filters}
|
|
@@ -107,10 +115,10 @@ export const ChannelList: React.FC<ChannelListProps> = ({
|
|
|
107
115
|
console.log('📺 [ChannelList] 📋 CHANNEL PREVIEW RENDER', {
|
|
108
116
|
channelId: props.channel?.id,
|
|
109
117
|
selectedChannelId: selectedChannel?.id,
|
|
110
|
-
isSelected: selectedChannel?.id === props.channel?.id
|
|
111
|
-
})
|
|
118
|
+
isSelected: selectedChannel?.id === props.channel?.id,
|
|
119
|
+
})
|
|
112
120
|
}
|
|
113
|
-
|
|
121
|
+
|
|
114
122
|
return (
|
|
115
123
|
<CustomChannelPreview
|
|
116
124
|
{...props}
|
|
@@ -118,12 +126,12 @@ export const ChannelList: React.FC<ChannelListProps> = ({
|
|
|
118
126
|
onChannelSelect={onChannelSelect}
|
|
119
127
|
debug={debug}
|
|
120
128
|
/>
|
|
121
|
-
)
|
|
129
|
+
)
|
|
122
130
|
}}
|
|
123
131
|
/>
|
|
124
|
-
)
|
|
132
|
+
)
|
|
125
133
|
})()}
|
|
126
134
|
</div>
|
|
127
135
|
</div>
|
|
128
|
-
)
|
|
129
|
-
}
|
|
136
|
+
)
|
|
137
|
+
}
|