@linktr.ee/messaging-react 3.1.4-rc-1780636753 → 3.3.0
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/README.md +1 -18
- package/dist/{Card-jyXjZZ0u.js → Card-BAc2cgtn.js} +3 -3
- package/dist/{Card-jyXjZZ0u.js.map → Card-BAc2cgtn.js.map} +1 -1
- package/dist/{Card-D_XOj1eE.cjs → Card-Cn1cBVnr.cjs} +2 -2
- package/dist/{Card-D_XOj1eE.cjs.map → Card-Cn1cBVnr.cjs.map} +1 -1
- package/dist/{Card-BkgsPkp4.cjs → Card-DAyszUxa.cjs} +2 -2
- package/dist/{Card-BkgsPkp4.cjs.map → Card-DAyszUxa.cjs.map} +1 -1
- package/dist/{Card-BwFdJXYm.js → Card-D_2VQScd.js} +2 -2
- package/dist/{Card-BwFdJXYm.js.map → Card-D_2VQScd.js.map} +1 -1
- package/dist/{Card-B9atg4sP.js → Card-D_G8133I.js} +2 -2
- package/dist/{Card-B9atg4sP.js.map → Card-D_G8133I.js.map} +1 -1
- package/dist/{Card-1U2tLPcp.cjs → Card-gYxPXe_W.cjs} +2 -2
- package/dist/{Card-1U2tLPcp.cjs.map → Card-gYxPXe_W.cjs.map} +1 -1
- package/dist/{LockedThumbnail-Dwt_goCX.js → LockedThumbnail-C7tWpOQr.js} +2 -2
- package/dist/{LockedThumbnail-Dwt_goCX.js.map → LockedThumbnail-C7tWpOQr.js.map} +1 -1
- package/dist/{LockedThumbnail-oxtdpgut.cjs → LockedThumbnail-DtOTZl3l.cjs} +2 -2
- package/dist/{LockedThumbnail-oxtdpgut.cjs.map → LockedThumbnail-DtOTZl3l.cjs.map} +1 -1
- package/dist/{index-CO975B6P.js → index-C_NFzAB9.js} +1228 -1289
- package/dist/index-C_NFzAB9.js.map +1 -0
- package/dist/index-_Se6ovQm.cjs +2 -0
- package/dist/index-_Se6ovQm.cjs.map +1 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +24 -49
- package/dist/index.js +1 -1
- package/package.json +4 -3
- package/src/components/ChannelActionsMenu/ChannelActionsMenu.test.tsx +305 -0
- package/src/components/ChannelActionsMenu/index.tsx +221 -0
- package/src/components/ChannelList/index.test.tsx +3 -151
- package/src/components/ChannelList/index.tsx +4 -72
- package/src/components/ChannelView.stories.tsx +3 -73
- package/src/components/ChannelView.test.tsx +33 -29
- package/src/components/ChannelView.tsx +71 -109
- package/src/components/MessagingShell/index.tsx +2 -0
- package/src/hooks/useChannelModerationActions.ts +227 -0
- package/src/index.ts +0 -1
- package/src/types.ts +25 -48
- package/dist/index-CO975B6P.js.map +0 -1
- package/dist/index-D4Dse1Lu.cjs +0 -2
- package/dist/index-D4Dse1Lu.cjs.map +0 -1
- package/src/components/ChannelInfoDialog/ChannelInfoDialog.test.tsx +0 -333
- package/src/components/ChannelInfoDialog/index.tsx +0 -336
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { act, waitFor } from '@testing-library/react'
|
|
2
1
|
import React from 'react'
|
|
3
2
|
import type { Channel } from 'stream-chat'
|
|
4
3
|
import { describe, expect, it, vi, beforeEach } from 'vitest'
|
|
@@ -57,7 +56,7 @@ describe('ChannelList', () => {
|
|
|
57
56
|
})
|
|
58
57
|
})
|
|
59
58
|
|
|
60
|
-
it('wraps channelRenderFilterFn to restore pending messages and delegates to consumer filter',
|
|
59
|
+
it('wraps channelRenderFilterFn to restore pending messages and delegates to consumer filter', () => {
|
|
61
60
|
const filterFn = vi.fn((channels: Channel[]) => channels)
|
|
62
61
|
|
|
63
62
|
renderWithProviders(
|
|
@@ -78,156 +77,9 @@ describe('ChannelList', () => {
|
|
|
78
77
|
|
|
79
78
|
// When the wrapper is called, it should delegate to the consumer's filter
|
|
80
79
|
const mockChannels = [
|
|
81
|
-
{
|
|
82
|
-
cid: 'ch-1',
|
|
83
|
-
countUnread: () => 0,
|
|
84
|
-
state: { pending_messages: [], addMessageSorted: vi.fn() },
|
|
85
|
-
},
|
|
80
|
+
{ cid: 'ch-1', state: { pending_messages: [], addMessageSorted: vi.fn() } },
|
|
86
81
|
]
|
|
87
|
-
|
|
88
|
-
streamProps.channelRenderFilterFn!(mockChannels)
|
|
89
|
-
await Promise.resolve()
|
|
90
|
-
})
|
|
82
|
+
streamProps.channelRenderFilterFn!(mockChannels)
|
|
91
83
|
expect(filterFn).toHaveBeenCalledWith(mockChannels)
|
|
92
84
|
})
|
|
93
|
-
|
|
94
|
-
it('notifies hosts with derived visible-channel state', async () => {
|
|
95
|
-
const onStateChange = vi.fn()
|
|
96
|
-
const visibleChannel = {
|
|
97
|
-
cid: 'ch-visible',
|
|
98
|
-
countUnread: () => 2,
|
|
99
|
-
state: { pending_messages: [], addMessageSorted: vi.fn() },
|
|
100
|
-
}
|
|
101
|
-
const hiddenChannel = {
|
|
102
|
-
cid: 'ch-hidden',
|
|
103
|
-
countUnread: () => 7,
|
|
104
|
-
state: { pending_messages: [], addMessageSorted: vi.fn() },
|
|
105
|
-
}
|
|
106
|
-
const filterFn = vi.fn((channels: Channel[]) => channels.slice(0, 1))
|
|
107
|
-
|
|
108
|
-
renderWithProviders(
|
|
109
|
-
React.createElement(ChannelList, {
|
|
110
|
-
...defaultProps,
|
|
111
|
-
channelRenderFilterFn: filterFn,
|
|
112
|
-
onStateChange,
|
|
113
|
-
} as never)
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
const streamProps = streamChannelListMock.mock.calls[0][0] as {
|
|
117
|
-
channelRenderFilterFn?: (channels: Channel[]) => Channel[]
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
await act(async () => {
|
|
121
|
-
streamProps.channelRenderFilterFn!([
|
|
122
|
-
visibleChannel as unknown as Channel,
|
|
123
|
-
hiddenChannel as unknown as Channel,
|
|
124
|
-
])
|
|
125
|
-
await Promise.resolve()
|
|
126
|
-
})
|
|
127
|
-
|
|
128
|
-
await waitFor(() => {
|
|
129
|
-
expect(onStateChange).toHaveBeenLastCalledWith({
|
|
130
|
-
isInitialLoadSettled: true,
|
|
131
|
-
channels: [visibleChannel],
|
|
132
|
-
rawChannels: [visibleChannel, hiddenChannel],
|
|
133
|
-
hasChannels: true,
|
|
134
|
-
unreadCount: 1,
|
|
135
|
-
})
|
|
136
|
-
})
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
it('reports a settled empty state when no visible channels remain', async () => {
|
|
140
|
-
const onStateChange = vi.fn()
|
|
141
|
-
const filterFn = vi.fn(() => [])
|
|
142
|
-
const hiddenChannel = {
|
|
143
|
-
cid: 'ch-hidden',
|
|
144
|
-
countUnread: () => 3,
|
|
145
|
-
state: { pending_messages: [], addMessageSorted: vi.fn() },
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
renderWithProviders(
|
|
149
|
-
React.createElement(ChannelList, {
|
|
150
|
-
...defaultProps,
|
|
151
|
-
channelRenderFilterFn: filterFn,
|
|
152
|
-
onStateChange,
|
|
153
|
-
} as never)
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
const streamProps = streamChannelListMock.mock.calls[0][0] as {
|
|
157
|
-
channelRenderFilterFn?: (channels: Channel[]) => Channel[]
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
await act(async () => {
|
|
161
|
-
streamProps.channelRenderFilterFn!([hiddenChannel as unknown as Channel])
|
|
162
|
-
await Promise.resolve()
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
await waitFor(() => {
|
|
166
|
-
expect(onStateChange).toHaveBeenLastCalledWith({
|
|
167
|
-
isInitialLoadSettled: true,
|
|
168
|
-
channels: [],
|
|
169
|
-
rawChannels: [hiddenChannel],
|
|
170
|
-
hasChannels: false,
|
|
171
|
-
unreadCount: 0,
|
|
172
|
-
})
|
|
173
|
-
})
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
it('resets derived state when filters change before the new list resolves', async () => {
|
|
177
|
-
const onStateChange = vi.fn()
|
|
178
|
-
const firstChannel = {
|
|
179
|
-
cid: 'ch-first',
|
|
180
|
-
countUnread: () => 1,
|
|
181
|
-
state: { pending_messages: [], addMessageSorted: vi.fn() },
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const { rerender } = renderWithProviders(
|
|
185
|
-
React.createElement(ChannelList, {
|
|
186
|
-
...defaultProps,
|
|
187
|
-
onStateChange,
|
|
188
|
-
} as never)
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
let streamProps = streamChannelListMock.mock.calls.at(-1)?.[0] as {
|
|
192
|
-
channelRenderFilterFn?: (channels: Channel[]) => Channel[]
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
await act(async () => {
|
|
196
|
-
streamProps.channelRenderFilterFn!([firstChannel as unknown as Channel])
|
|
197
|
-
await Promise.resolve()
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
await waitFor(() => {
|
|
201
|
-
expect(onStateChange).toHaveBeenLastCalledWith({
|
|
202
|
-
isInitialLoadSettled: true,
|
|
203
|
-
channels: [firstChannel],
|
|
204
|
-
rawChannels: [firstChannel],
|
|
205
|
-
hasChannels: true,
|
|
206
|
-
unreadCount: 1,
|
|
207
|
-
})
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
rerender(
|
|
211
|
-
React.createElement(ChannelList, {
|
|
212
|
-
...defaultProps,
|
|
213
|
-
filters: { type: 'messaging', frozen: true },
|
|
214
|
-
onStateChange,
|
|
215
|
-
} as never)
|
|
216
|
-
)
|
|
217
|
-
|
|
218
|
-
streamProps = streamChannelListMock.mock.calls.at(-1)?.[0] as {
|
|
219
|
-
channelRenderFilterFn?: (channels: Channel[]) => Channel[]
|
|
220
|
-
}
|
|
221
|
-
expect(typeof streamProps.channelRenderFilterFn).toBe('function')
|
|
222
|
-
|
|
223
|
-
await waitFor(() => {
|
|
224
|
-
expect(onStateChange).toHaveBeenLastCalledWith({
|
|
225
|
-
isInitialLoadSettled: false,
|
|
226
|
-
channels: [],
|
|
227
|
-
rawChannels: [],
|
|
228
|
-
hasChannels: false,
|
|
229
|
-
unreadCount: 0,
|
|
230
|
-
})
|
|
231
|
-
})
|
|
232
|
-
})
|
|
233
85
|
})
|
|
@@ -5,21 +5,13 @@ import { ChannelList as StreamChannelList } from 'stream-chat-react'
|
|
|
5
5
|
|
|
6
6
|
import { restorePendingMessages } from '../../hooks/useRestorePendingMessages'
|
|
7
7
|
import { useMessagingContext } from '../../providers/MessagingProvider'
|
|
8
|
-
import type {
|
|
8
|
+
import type { ChannelListProps } from '../../types'
|
|
9
9
|
|
|
10
10
|
import { ChannelListProvider } from './ChannelListContext'
|
|
11
11
|
import CustomChannelPreview from './CustomChannelPreview'
|
|
12
12
|
|
|
13
13
|
const DEFAULT_SORT = { last_message_at: -1 } as const
|
|
14
14
|
|
|
15
|
-
const EMPTY_DERIVED_STATE: ChannelListDerivedState = {
|
|
16
|
-
isInitialLoadSettled: false,
|
|
17
|
-
channels: [],
|
|
18
|
-
rawChannels: [],
|
|
19
|
-
hasChannels: false,
|
|
20
|
-
unreadCount: 0,
|
|
21
|
-
}
|
|
22
|
-
|
|
23
15
|
/**
|
|
24
16
|
* Channel list component with customizable header and actions
|
|
25
17
|
*/
|
|
@@ -35,7 +27,6 @@ export const ChannelList = React.memo<ChannelListProps>(
|
|
|
35
27
|
customEmptyStateIndicator,
|
|
36
28
|
renderMessagePreview,
|
|
37
29
|
viewerLanguage,
|
|
38
|
-
onStateChange,
|
|
39
30
|
}) => {
|
|
40
31
|
// Track renders
|
|
41
32
|
const renderCountRef = React.useRef(0)
|
|
@@ -43,42 +34,6 @@ export const ChannelList = React.memo<ChannelListProps>(
|
|
|
43
34
|
|
|
44
35
|
// Get debug flag from context
|
|
45
36
|
const { debug = false } = useMessagingContext()
|
|
46
|
-
const [derivedState, setDerivedState] = React.useState<ChannelListDerivedState>(
|
|
47
|
-
EMPTY_DERIVED_STATE
|
|
48
|
-
)
|
|
49
|
-
const listIdentityKey = React.useMemo(
|
|
50
|
-
() => `${JSON.stringify(filters)}:${JSON.stringify(sort)}`,
|
|
51
|
-
[filters, sort]
|
|
52
|
-
)
|
|
53
|
-
const pendingDerivedStateRef = React.useRef<ChannelListDerivedState>(
|
|
54
|
-
EMPTY_DERIVED_STATE
|
|
55
|
-
)
|
|
56
|
-
const publishScheduledRef = React.useRef(false)
|
|
57
|
-
const listVersionRef = React.useRef(0)
|
|
58
|
-
|
|
59
|
-
const publishDerivedState = React.useCallback(
|
|
60
|
-
(nextState: ChannelListDerivedState) => {
|
|
61
|
-
const publishVersion = listVersionRef.current
|
|
62
|
-
pendingDerivedStateRef.current = nextState
|
|
63
|
-
if (publishScheduledRef.current) {
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
publishScheduledRef.current = true
|
|
68
|
-
queueMicrotask(() => {
|
|
69
|
-
if (publishVersion !== listVersionRef.current) {
|
|
70
|
-
publishScheduledRef.current = false
|
|
71
|
-
return
|
|
72
|
-
}
|
|
73
|
-
publishScheduledRef.current = false
|
|
74
|
-
setDerivedState((currentState) => {
|
|
75
|
-
const pendingState = pendingDerivedStateRef.current
|
|
76
|
-
return currentState === pendingState ? currentState : pendingState
|
|
77
|
-
})
|
|
78
|
-
})
|
|
79
|
-
},
|
|
80
|
-
[]
|
|
81
|
-
)
|
|
82
37
|
|
|
83
38
|
// Wrap channelRenderFilterFn to restore pending messages for all channels
|
|
84
39
|
// as soon as they are loaded, without waiting for the user to click into each one.
|
|
@@ -87,23 +42,11 @@ export const ChannelList = React.memo<ChannelListProps>(
|
|
|
87
42
|
for (const channel of channels) {
|
|
88
43
|
restorePendingMessages(channel)
|
|
89
44
|
}
|
|
90
|
-
|
|
91
|
-
const visibleChannels = channelRenderFilterFn
|
|
45
|
+
return channelRenderFilterFn
|
|
92
46
|
? channelRenderFilterFn(channels)
|
|
93
47
|
: channels
|
|
94
|
-
|
|
95
|
-
publishDerivedState({
|
|
96
|
-
isInitialLoadSettled: true,
|
|
97
|
-
channels: visibleChannels,
|
|
98
|
-
rawChannels: channels,
|
|
99
|
-
hasChannels: visibleChannels.length > 0,
|
|
100
|
-
unreadCount: visibleChannels.filter((channel) => channel.countUnread() > 0)
|
|
101
|
-
.length,
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
return visibleChannels
|
|
105
48
|
},
|
|
106
|
-
[channelRenderFilterFn
|
|
49
|
+
[channelRenderFilterFn]
|
|
107
50
|
)
|
|
108
51
|
|
|
109
52
|
if (debug) {
|
|
@@ -131,17 +74,6 @@ export const ChannelList = React.memo<ChannelListProps>(
|
|
|
131
74
|
]
|
|
132
75
|
)
|
|
133
76
|
|
|
134
|
-
React.useEffect(() => {
|
|
135
|
-
listVersionRef.current += 1
|
|
136
|
-
pendingDerivedStateRef.current = EMPTY_DERIVED_STATE
|
|
137
|
-
publishScheduledRef.current = false
|
|
138
|
-
setDerivedState(EMPTY_DERIVED_STATE)
|
|
139
|
-
}, [listIdentityKey])
|
|
140
|
-
|
|
141
|
-
React.useEffect(() => {
|
|
142
|
-
onStateChange?.(derivedState)
|
|
143
|
-
}, [derivedState, onStateChange])
|
|
144
|
-
|
|
145
77
|
return (
|
|
146
78
|
<div
|
|
147
79
|
className={classNames(
|
|
@@ -153,7 +85,7 @@ export const ChannelList = React.memo<ChannelListProps>(
|
|
|
153
85
|
<div className="flex-1 overflow-hidden min-w-0">
|
|
154
86
|
<ChannelListProvider value={contextValue}>
|
|
155
87
|
<StreamChannelList
|
|
156
|
-
key={
|
|
88
|
+
key={`${JSON.stringify(filters)}:${JSON.stringify(sort)}`}
|
|
157
89
|
filters={filters}
|
|
158
90
|
sort={sort}
|
|
159
91
|
options={{ limit: 30 }}
|
|
@@ -240,12 +240,12 @@ Default.args = {
|
|
|
240
240
|
console.log('Leave conversation:', channel.id),
|
|
241
241
|
onBlockParticipant: (participantId) =>
|
|
242
242
|
console.log('Block participant:', participantId),
|
|
243
|
-
followerStatus: true,
|
|
243
|
+
followerStatus: true,
|
|
244
244
|
}
|
|
245
245
|
Default.parameters = {
|
|
246
246
|
docs: {
|
|
247
247
|
description: {
|
|
248
|
-
story: 'Default channel view with messages and conversation header
|
|
248
|
+
story: 'Default channel view with messages and conversation header.',
|
|
249
249
|
},
|
|
250
250
|
},
|
|
251
251
|
}
|
|
@@ -277,10 +277,8 @@ RestrictedOfficialChannel.args = {
|
|
|
277
277
|
// Restricted surface for the Linktree official channel:
|
|
278
278
|
showBlockParticipant: false,
|
|
279
279
|
showReportParticipant: false,
|
|
280
|
-
showFollowerStatus: false,
|
|
281
280
|
composerDisabled: true,
|
|
282
281
|
composerDisabledReason: 'Only Linktree can send messages on this thread',
|
|
283
|
-
followerStatus: true, // would normally render "Subscribed to you" — suppressed here
|
|
284
282
|
onLeaveConversation: (channel) =>
|
|
285
283
|
console.log('Leave conversation:', channel.id),
|
|
286
284
|
}
|
|
@@ -288,7 +286,7 @@ RestrictedOfficialChannel.parameters = {
|
|
|
288
286
|
docs: {
|
|
289
287
|
description: {
|
|
290
288
|
story:
|
|
291
|
-
'Restricted action surface used by the Linktree official channel: block
|
|
289
|
+
'Restricted action surface used by the Linktree official channel: block and report are hidden, and the composer is replaced by a locked panel explaining the linker cannot send messages on this thread. Delete conversation and favorite remain available in the "..." actions popover.',
|
|
292
290
|
},
|
|
293
291
|
},
|
|
294
292
|
}
|
|
@@ -543,74 +541,6 @@ EmptyChannel.parameters = {
|
|
|
543
541
|
},
|
|
544
542
|
}
|
|
545
543
|
|
|
546
|
-
export const SubscriberStatus: StoryFn<TemplateProps> = Template.bind({})
|
|
547
|
-
SubscriberStatus.args = {
|
|
548
|
-
showBackButton: false,
|
|
549
|
-
followerStatus: true, // Shows "Subscribed to you" in green
|
|
550
|
-
onLeaveConversation: (channel) =>
|
|
551
|
-
console.log('Leave conversation:', channel.id),
|
|
552
|
-
onBlockParticipant: (participantId) =>
|
|
553
|
-
console.log('Block participant:', participantId),
|
|
554
|
-
}
|
|
555
|
-
SubscriberStatus.parameters = {
|
|
556
|
-
docs: {
|
|
557
|
-
description: {
|
|
558
|
-
story: 'Channel view showing "Subscribed to you" badge in green when isFollower is true.',
|
|
559
|
-
},
|
|
560
|
-
},
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
export const NotSubscribedStatus: StoryFn<TemplateProps> = Template.bind({})
|
|
564
|
-
NotSubscribedStatus.args = {
|
|
565
|
-
showBackButton: false,
|
|
566
|
-
followerStatus: false, // Shows "Not subscribed" in gray
|
|
567
|
-
onLeaveConversation: (channel) =>
|
|
568
|
-
console.log('Leave conversation:', channel.id),
|
|
569
|
-
onBlockParticipant: (participantId) =>
|
|
570
|
-
console.log('Block participant:', participantId),
|
|
571
|
-
}
|
|
572
|
-
NotSubscribedStatus.parameters = {
|
|
573
|
-
docs: {
|
|
574
|
-
description: {
|
|
575
|
-
story: 'Channel view showing "Not subscribed" badge in gray when isFollower is false.',
|
|
576
|
-
},
|
|
577
|
-
},
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
export const CustomFollowerStatus: StoryFn<TemplateProps> = Template.bind({})
|
|
581
|
-
CustomFollowerStatus.args = {
|
|
582
|
-
showBackButton: false,
|
|
583
|
-
followerStatus: 'Mutual subscribers', // Custom status text in gray
|
|
584
|
-
onLeaveConversation: (channel) =>
|
|
585
|
-
console.log('Leave conversation:', channel.id),
|
|
586
|
-
onBlockParticipant: (participantId) =>
|
|
587
|
-
console.log('Block participant:', participantId),
|
|
588
|
-
}
|
|
589
|
-
CustomFollowerStatus.parameters = {
|
|
590
|
-
docs: {
|
|
591
|
-
description: {
|
|
592
|
-
story: 'Channel view with a custom follower status text (shows in gray unless text is "Subscribed to you").',
|
|
593
|
-
},
|
|
594
|
-
},
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
export const NoFollowerStatus: StoryFn<TemplateProps> = Template.bind({})
|
|
598
|
-
NoFollowerStatus.args = {
|
|
599
|
-
showBackButton: false,
|
|
600
|
-
followerStatus: undefined, // No badge shown
|
|
601
|
-
onLeaveConversation: (channel) =>
|
|
602
|
-
console.log('Leave conversation:', channel.id),
|
|
603
|
-
onBlockParticipant: (participantId) =>
|
|
604
|
-
console.log('Block participant:', participantId),
|
|
605
|
-
}
|
|
606
|
-
NoFollowerStatus.parameters = {
|
|
607
|
-
docs: {
|
|
608
|
-
description: {
|
|
609
|
-
story: 'Channel view with no follower status badge displayed.',
|
|
610
|
-
},
|
|
611
|
-
},
|
|
612
|
-
}
|
|
613
|
-
|
|
614
544
|
export const FrozenChannel: StoryFn<TemplateProps> = Template.bind({})
|
|
615
545
|
FrozenChannel.args = {
|
|
616
546
|
showBackButton: false,
|
|
@@ -68,11 +68,11 @@ vi.mock('./CustomDateSeparator', () => ({
|
|
|
68
68
|
CustomDateSeparator: () => <div data-testid="custom-date-separator" />,
|
|
69
69
|
}))
|
|
70
70
|
|
|
71
|
-
const
|
|
72
|
-
vi.mock('./
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return <div data-testid="channel-
|
|
71
|
+
const channelActionsMenuProps: Array<Record<string, unknown>> = []
|
|
72
|
+
vi.mock('./ChannelActionsMenu', () => ({
|
|
73
|
+
ChannelActionsMenu: (props: Record<string, unknown>) => {
|
|
74
|
+
channelActionsMenuProps.push({ ...props })
|
|
75
|
+
return <div data-testid="channel-actions-menu" />
|
|
76
76
|
},
|
|
77
77
|
}))
|
|
78
78
|
|
|
@@ -151,24 +151,24 @@ describe('ChannelView', () => {
|
|
|
151
151
|
activeChannel = undefined
|
|
152
152
|
activeChannelProps = {}
|
|
153
153
|
avatarRenderCalls.length = 0
|
|
154
|
-
|
|
154
|
+
channelActionsMenuProps.length = 0
|
|
155
155
|
messageInputProps.length = 0
|
|
156
156
|
mockIsStarred = false
|
|
157
157
|
})
|
|
158
158
|
|
|
159
|
-
const
|
|
160
|
-
|
|
159
|
+
const lastActionsMenuProps = () =>
|
|
160
|
+
channelActionsMenuProps[channelActionsMenuProps.length - 1]
|
|
161
161
|
const lastInputProps = () =>
|
|
162
162
|
messageInputProps[messageInputProps.length - 1]
|
|
163
163
|
|
|
164
|
-
it('keeps block and report visible by default in the
|
|
164
|
+
it('keeps block and report visible by default in the actions menu', () => {
|
|
165
165
|
renderWithProviders(<ChannelView channel={createChannel()} />)
|
|
166
166
|
|
|
167
|
-
expect(
|
|
168
|
-
expect(
|
|
167
|
+
expect(lastActionsMenuProps().showBlockParticipant).not.toBe(false)
|
|
168
|
+
expect(lastActionsMenuProps().showReportParticipant).not.toBe(false)
|
|
169
169
|
})
|
|
170
170
|
|
|
171
|
-
it('hides block and report in the
|
|
171
|
+
it('hides block and report in the actions menu when restricted', () => {
|
|
172
172
|
renderWithProviders(
|
|
173
173
|
<ChannelView
|
|
174
174
|
channel={createChannel()}
|
|
@@ -177,32 +177,36 @@ describe('ChannelView', () => {
|
|
|
177
177
|
/>
|
|
178
178
|
)
|
|
179
179
|
|
|
180
|
-
expect(
|
|
181
|
-
expect(
|
|
180
|
+
expect(lastActionsMenuProps().showBlockParticipant).toBe(false)
|
|
181
|
+
expect(lastActionsMenuProps().showReportParticipant).toBe(false)
|
|
182
182
|
})
|
|
183
183
|
|
|
184
|
-
it('
|
|
185
|
-
|
|
186
|
-
;(channel as unknown as { data: Record<string, unknown> }).data = {
|
|
187
|
-
isFollower: true,
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
renderWithProviders(<ChannelView channel={channel} />)
|
|
184
|
+
it('renders the participant name without an info trigger', () => {
|
|
185
|
+
renderWithProviders(<ChannelView channel={createChannel()} />)
|
|
191
186
|
|
|
192
|
-
|
|
187
|
+
// The info sidebar was removed, so the participant name is plain text on
|
|
188
|
+
// both headers — there is no clickable affordance to open a profile
|
|
189
|
+
// dialog anymore.
|
|
190
|
+
expect(
|
|
191
|
+
screen.queryByRole('button', { name: /view info for/i })
|
|
192
|
+
).not.toBeInTheDocument()
|
|
193
|
+
expect(screen.getAllByText('Linker').length).toBeGreaterThanOrEqual(1)
|
|
193
194
|
})
|
|
194
195
|
|
|
195
|
-
it('
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
196
|
+
it('renders the actions menu by default', () => {
|
|
197
|
+
renderWithProviders(<ChannelView channel={createChannel()} />)
|
|
198
|
+
|
|
199
|
+
expect(
|
|
200
|
+
screen.queryAllByTestId('channel-actions-menu').length
|
|
201
|
+
).toBeGreaterThan(0)
|
|
202
|
+
})
|
|
200
203
|
|
|
204
|
+
it('hides the actions menu (both desktop and mobile) when showChannelInfo is false', () => {
|
|
201
205
|
renderWithProviders(
|
|
202
|
-
<ChannelView channel={
|
|
206
|
+
<ChannelView channel={createChannel()} showChannelInfo={false} />
|
|
203
207
|
)
|
|
204
208
|
|
|
205
|
-
expect(
|
|
209
|
+
expect(screen.queryAllByTestId('channel-actions-menu')).toHaveLength(0)
|
|
206
210
|
})
|
|
207
211
|
|
|
208
212
|
it('passes composer disabled state and reason to the message input', () => {
|