@linktr.ee/messaging-react 3.2.0 → 3.3.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/dist/{Card-bdnjL_4d.js → Card-BAc2cgtn.js} +3 -3
- package/dist/{Card-bdnjL_4d.js.map → Card-BAc2cgtn.js.map} +1 -1
- package/dist/{Card-CO089n1e.cjs → Card-Cn1cBVnr.cjs} +2 -2
- package/dist/{Card-CO089n1e.cjs.map → Card-Cn1cBVnr.cjs.map} +1 -1
- package/dist/{Card-B5TCecD6.cjs → Card-DAyszUxa.cjs} +2 -2
- package/dist/{Card-B5TCecD6.cjs.map → Card-DAyszUxa.cjs.map} +1 -1
- package/dist/{Card-DQYLHbDI.js → Card-D_2VQScd.js} +2 -2
- package/dist/{Card-DQYLHbDI.js.map → Card-D_2VQScd.js.map} +1 -1
- package/dist/{Card-DTaHgygz.js → Card-D_G8133I.js} +2 -2
- package/dist/{Card-DTaHgygz.js.map → Card-D_G8133I.js.map} +1 -1
- package/dist/{Card-aO1qZWDU.cjs → Card-gYxPXe_W.cjs} +2 -2
- package/dist/{Card-aO1qZWDU.cjs.map → Card-gYxPXe_W.cjs.map} +1 -1
- package/dist/{LockedThumbnail-nsFA3DjA.js → LockedThumbnail-C7tWpOQr.js} +2 -2
- package/dist/{LockedThumbnail-nsFA3DjA.js.map → LockedThumbnail-C7tWpOQr.js.map} +1 -1
- package/dist/{LockedThumbnail-CWVybsBb.cjs → LockedThumbnail-DtOTZl3l.cjs} +2 -2
- package/dist/{LockedThumbnail-CWVybsBb.cjs.map → LockedThumbnail-DtOTZl3l.cjs.map} +1 -1
- package/dist/{index-DJKFVBkP.js → index-C_NFzAB9.js} +1356 -1382
- 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 +39 -15
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/src/components/ChannelActionsMenu/ChannelActionsMenu.test.tsx +305 -0
- package/src/components/ChannelActionsMenu/index.tsx +221 -0
- package/src/components/ChannelView.stories.tsx +3 -73
- package/src/components/ChannelView.test.tsx +30 -57
- package/src/components/ChannelView.tsx +66 -115
- package/src/hooks/useChannelModerationActions.ts +227 -0
- package/src/stream-custom-data.ts +21 -1
- package/src/types.ts +20 -15
- package/dist/index-BO2VfA-M.cjs +0 -2
- package/dist/index-BO2VfA-M.cjs.map +0 -1
- package/dist/index-DJKFVBkP.js.map +0 -1
- package/src/components/ChannelInfoDialog/ChannelInfoDialog.test.tsx +0 -333
- package/src/components/ChannelInfoDialog/index.tsx +0 -336
|
@@ -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,63 +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} />)
|
|
191
|
-
|
|
192
|
-
expect(lastDialogProps().followerStatusLabel).toBe('Subscribed to you')
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
it('suppresses the follower status label when showFollowerStatus is false', () => {
|
|
196
|
-
const channel = createChannel()
|
|
197
|
-
;(channel as unknown as { data: Record<string, unknown> }).data = {
|
|
198
|
-
isFollower: true,
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
renderWithProviders(
|
|
202
|
-
<ChannelView channel={channel} showFollowerStatus={false} />
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
expect(lastDialogProps().followerStatusLabel).toBeUndefined()
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
it('renders the channel info trigger by default', () => {
|
|
209
|
-
const { container } = renderWithProviders(
|
|
210
|
-
<ChannelView channel={createChannel()} />
|
|
211
|
-
)
|
|
184
|
+
it('renders the participant name without an info trigger', () => {
|
|
185
|
+
renderWithProviders(<ChannelView channel={createChannel()} />)
|
|
212
186
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
expect(
|
|
217
|
-
|
|
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)
|
|
218
194
|
})
|
|
219
195
|
|
|
220
|
-
it('
|
|
221
|
-
|
|
222
|
-
<ChannelView channel={createChannel()} showChannelInfo={false} />
|
|
223
|
-
)
|
|
196
|
+
it('renders the actions menu by default', () => {
|
|
197
|
+
renderWithProviders(<ChannelView channel={createChannel()} />)
|
|
224
198
|
|
|
225
|
-
|
|
226
|
-
'
|
|
227
|
-
)
|
|
228
|
-
expect(infoTriggers.length).toBe(0)
|
|
199
|
+
expect(
|
|
200
|
+
screen.queryAllByTestId('channel-actions-menu').length
|
|
201
|
+
).toBeGreaterThan(0)
|
|
229
202
|
})
|
|
230
203
|
|
|
231
|
-
it('
|
|
204
|
+
it('hides the actions menu (both desktop and mobile) when showChannelInfo is false', () => {
|
|
232
205
|
renderWithProviders(
|
|
233
206
|
<ChannelView channel={createChannel()} showChannelInfo={false} />
|
|
234
207
|
)
|
|
235
208
|
|
|
236
|
-
expect(
|
|
209
|
+
expect(screen.queryAllByTestId('channel-actions-menu')).toHaveLength(0)
|
|
237
210
|
})
|
|
238
211
|
|
|
239
212
|
it('passes composer disabled state and reason to the message input', () => {
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ArrowLeftIcon,
|
|
3
|
-
DotsThreeIcon,
|
|
4
|
-
SparkleIcon,
|
|
5
|
-
StarIcon,
|
|
6
|
-
} from '@phosphor-icons/react'
|
|
1
|
+
import { ArrowLeftIcon, SparkleIcon, StarIcon } from '@phosphor-icons/react'
|
|
7
2
|
import classNames from 'classnames'
|
|
8
3
|
import React, { useCallback, useEffect, useRef } from 'react'
|
|
9
4
|
import { Channel as ChannelType } from 'stream-chat'
|
|
@@ -22,7 +17,7 @@ import type { ChannelViewProps } from '../types'
|
|
|
22
17
|
import { resolveParticipantDisplayName } from '../utils/resolveParticipantDisplayName'
|
|
23
18
|
|
|
24
19
|
import { Avatar } from './Avatar'
|
|
25
|
-
import {
|
|
20
|
+
import { ChannelActionsMenu } from './ChannelActionsMenu'
|
|
26
21
|
import { CustomDateSeparator } from './CustomDateSeparator'
|
|
27
22
|
import { CustomMessage } from './CustomMessage'
|
|
28
23
|
import { CustomMessageActions } from './CustomMessage/CustomMessageActions'
|
|
@@ -44,17 +39,33 @@ const DM_AGENT_HEADER_HELPER_TEXT = 'Replies instantly with AI assistant'
|
|
|
44
39
|
const CustomChannelHeader: React.FC<{
|
|
45
40
|
onBack?: () => void
|
|
46
41
|
showBackButton: boolean
|
|
47
|
-
onShowInfo: () => void
|
|
48
|
-
canShowInfo: boolean
|
|
49
42
|
showStarButton?: boolean
|
|
50
43
|
dmAgentEnabled?: boolean
|
|
44
|
+
onLeaveConversation?: (channel: ChannelType) => void
|
|
45
|
+
onBlockParticipant?: (participantId?: string) => void
|
|
46
|
+
showDeleteConversation?: boolean
|
|
47
|
+
showBlockParticipant?: boolean
|
|
48
|
+
showReportParticipant?: boolean
|
|
49
|
+
onDeleteConversationClick?: () => void
|
|
50
|
+
onBlockParticipantClick?: () => void
|
|
51
|
+
onReportParticipantClick?: () => void
|
|
52
|
+
customChannelActions?: React.ReactNode
|
|
53
|
+
showActionsMenu?: boolean
|
|
51
54
|
}> = ({
|
|
52
55
|
onBack,
|
|
53
56
|
showBackButton,
|
|
54
|
-
onShowInfo,
|
|
55
|
-
canShowInfo,
|
|
56
57
|
showStarButton = false,
|
|
57
58
|
dmAgentEnabled = false,
|
|
59
|
+
onLeaveConversation,
|
|
60
|
+
onBlockParticipant,
|
|
61
|
+
showDeleteConversation = true,
|
|
62
|
+
showBlockParticipant = true,
|
|
63
|
+
showReportParticipant = true,
|
|
64
|
+
onDeleteConversationClick,
|
|
65
|
+
onBlockParticipantClick,
|
|
66
|
+
onReportParticipantClick,
|
|
67
|
+
customChannelActions,
|
|
68
|
+
showActionsMenu = true,
|
|
58
69
|
}) => {
|
|
59
70
|
const { channel } = useChannelStateContext()
|
|
60
71
|
|
|
@@ -143,15 +154,21 @@ const CustomChannelHeader: React.FC<{
|
|
|
143
154
|
/>
|
|
144
155
|
</button>
|
|
145
156
|
)}
|
|
146
|
-
{
|
|
147
|
-
<
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
157
|
+
{showActionsMenu && (
|
|
158
|
+
<ChannelActionsMenu
|
|
159
|
+
channel={channel}
|
|
160
|
+
participant={participant}
|
|
161
|
+
showDeleteConversation={showDeleteConversation}
|
|
162
|
+
showBlockParticipant={showBlockParticipant}
|
|
163
|
+
showReportParticipant={showReportParticipant}
|
|
164
|
+
onLeaveConversation={onLeaveConversation}
|
|
165
|
+
onBlockParticipant={onBlockParticipant}
|
|
166
|
+
onDeleteConversationClick={onDeleteConversationClick}
|
|
167
|
+
onBlockParticipantClick={onBlockParticipantClick}
|
|
168
|
+
onReportParticipantClick={onReportParticipantClick}
|
|
169
|
+
customChannelActions={customChannelActions}
|
|
170
|
+
triggerClassName={classNames(ICON_BTN_CLASS, 'bg-[#F1F0EE]')}
|
|
171
|
+
/>
|
|
155
172
|
)}
|
|
156
173
|
</div>
|
|
157
174
|
</div>
|
|
@@ -180,20 +197,9 @@ const CustomChannelHeader: React.FC<{
|
|
|
180
197
|
size={48}
|
|
181
198
|
/>
|
|
182
199
|
<div className="min-w-0">
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
onClick={onShowInfo}
|
|
187
|
-
className="flex items-center gap-1 font-medium text-black/90 truncate hover:text-black/70 transition-colors"
|
|
188
|
-
aria-label={`View info for ${participantName}`}
|
|
189
|
-
>
|
|
190
|
-
<span className="truncate">{participantName}</span>
|
|
191
|
-
</button>
|
|
192
|
-
) : (
|
|
193
|
-
<h1 className="font-medium text-black/90 truncate">
|
|
194
|
-
{participantName}
|
|
195
|
-
</h1>
|
|
196
|
-
)}
|
|
200
|
+
<h1 className="font-medium text-black/90 truncate">
|
|
201
|
+
{participantName}
|
|
202
|
+
</h1>
|
|
197
203
|
{dmAgentEnabled && (
|
|
198
204
|
<div className="mt-0.5 flex items-center gap-1 text-[10px] leading-3 text-black/55">
|
|
199
205
|
<SparkleIcon className="size-3 shrink-0 text-black/55" />
|
|
@@ -221,15 +227,21 @@ const CustomChannelHeader: React.FC<{
|
|
|
221
227
|
/>
|
|
222
228
|
</button>
|
|
223
229
|
)}
|
|
224
|
-
{
|
|
225
|
-
<
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
230
|
+
{showActionsMenu && (
|
|
231
|
+
<ChannelActionsMenu
|
|
232
|
+
channel={channel}
|
|
233
|
+
participant={participant}
|
|
234
|
+
showDeleteConversation={showDeleteConversation}
|
|
235
|
+
showBlockParticipant={showBlockParticipant}
|
|
236
|
+
showReportParticipant={showReportParticipant}
|
|
237
|
+
onLeaveConversation={onLeaveConversation}
|
|
238
|
+
onBlockParticipant={onBlockParticipant}
|
|
239
|
+
onDeleteConversationClick={onDeleteConversationClick}
|
|
240
|
+
onBlockParticipantClick={onBlockParticipantClick}
|
|
241
|
+
onReportParticipantClick={onReportParticipantClick}
|
|
242
|
+
customChannelActions={customChannelActions}
|
|
243
|
+
triggerClassName={ICON_BTN_CLASS}
|
|
244
|
+
/>
|
|
233
245
|
)}
|
|
234
246
|
</div>
|
|
235
247
|
</div>
|
|
@@ -255,13 +267,11 @@ const ChannelViewInner: React.FC<{
|
|
|
255
267
|
onReportParticipantClick?: () => void
|
|
256
268
|
showBlockParticipant?: boolean
|
|
257
269
|
showReportParticipant?: boolean
|
|
258
|
-
showFollowerStatus?: boolean
|
|
259
270
|
composerDisabled?: boolean
|
|
260
271
|
composerDisabledReason?: string
|
|
261
272
|
showStarButton?: boolean
|
|
262
273
|
chatbotVotingEnabled?: boolean
|
|
263
274
|
renderChannelBanner?: () => React.ReactNode
|
|
264
|
-
customProfileContent?: React.ReactNode
|
|
265
275
|
customChannelActions?: React.ReactNode
|
|
266
276
|
renderMessage?: (
|
|
267
277
|
messageNode: React.ReactElement,
|
|
@@ -284,13 +294,11 @@ const ChannelViewInner: React.FC<{
|
|
|
284
294
|
onReportParticipantClick,
|
|
285
295
|
showBlockParticipant = true,
|
|
286
296
|
showReportParticipant = true,
|
|
287
|
-
showFollowerStatus = true,
|
|
288
297
|
composerDisabled = false,
|
|
289
298
|
composerDisabledReason,
|
|
290
299
|
showStarButton = false,
|
|
291
300
|
chatbotVotingEnabled = false,
|
|
292
301
|
renderChannelBanner,
|
|
293
|
-
customProfileContent,
|
|
294
302
|
customChannelActions,
|
|
295
303
|
renderMessage,
|
|
296
304
|
dmAgentEnabled = false,
|
|
@@ -298,9 +306,8 @@ const ChannelViewInner: React.FC<{
|
|
|
298
306
|
showChannelInfo = true,
|
|
299
307
|
}) => {
|
|
300
308
|
const { channel } = useChannelStateContext()
|
|
301
|
-
const infoDialogRef = useRef<HTMLDialogElement>(null)
|
|
302
309
|
|
|
303
|
-
// Get participant info
|
|
310
|
+
// Get participant info (the other channel member)
|
|
304
311
|
const participant = React.useMemo(() => {
|
|
305
312
|
const myUserId = channel._client?.userID
|
|
306
313
|
if (!myUserId) return undefined
|
|
@@ -329,39 +336,6 @@ const ChannelViewInner: React.FC<{
|
|
|
329
336
|
currentUserIsAccount === false &&
|
|
330
337
|
participantIsAccount === true
|
|
331
338
|
|
|
332
|
-
// Get follower status label from channel data.
|
|
333
|
-
// Suppressed entirely when showFollowerStatus is false (e.g. the Linktree
|
|
334
|
-
// official channel hides subscription status).
|
|
335
|
-
const followerStatusLabel = React.useMemo(() => {
|
|
336
|
-
if (!showFollowerStatus) return undefined
|
|
337
|
-
|
|
338
|
-
const channelExtraData = (channel.data ?? {}) as {
|
|
339
|
-
followerStatus?: string
|
|
340
|
-
isFollower?: boolean
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
// If explicit followerStatus is provided, use it
|
|
344
|
-
if (channelExtraData.followerStatus) {
|
|
345
|
-
return String(channelExtraData.followerStatus)
|
|
346
|
-
}
|
|
347
|
-
// If isFollower is explicitly defined, use it to determine status
|
|
348
|
-
if (channelExtraData.isFollower !== undefined) {
|
|
349
|
-
return channelExtraData.isFollower
|
|
350
|
-
? 'Subscribed to you'
|
|
351
|
-
: 'Not subscribed'
|
|
352
|
-
}
|
|
353
|
-
// Otherwise, don't show any status
|
|
354
|
-
return undefined
|
|
355
|
-
}, [channel.data, showFollowerStatus])
|
|
356
|
-
|
|
357
|
-
const handleShowInfo = useCallback(() => {
|
|
358
|
-
infoDialogRef.current?.showModal()
|
|
359
|
-
}, [])
|
|
360
|
-
|
|
361
|
-
const handleCloseInfo = useCallback(() => {
|
|
362
|
-
infoDialogRef.current?.close()
|
|
363
|
-
}, [])
|
|
364
|
-
|
|
365
339
|
// Prevents all message instances from unmounting when ChannelViewInner re-renders
|
|
366
340
|
const MessageOverride = useCallback(
|
|
367
341
|
(props: MessageUIComponentProps) => {
|
|
@@ -398,10 +372,18 @@ const ChannelViewInner: React.FC<{
|
|
|
398
372
|
<CustomChannelHeader
|
|
399
373
|
onBack={onBack}
|
|
400
374
|
showBackButton={showBackButton}
|
|
401
|
-
|
|
402
|
-
canShowInfo={showChannelInfo && Boolean(participant)}
|
|
375
|
+
showActionsMenu={showChannelInfo}
|
|
403
376
|
showStarButton={showStarButton}
|
|
404
377
|
dmAgentEnabled={showDmAgentHeader}
|
|
378
|
+
onLeaveConversation={onLeaveConversation}
|
|
379
|
+
onBlockParticipant={onBlockParticipant}
|
|
380
|
+
showDeleteConversation={showDeleteConversation}
|
|
381
|
+
showBlockParticipant={showBlockParticipant}
|
|
382
|
+
showReportParticipant={showReportParticipant}
|
|
383
|
+
onDeleteConversationClick={onDeleteConversationClick}
|
|
384
|
+
onBlockParticipantClick={onBlockParticipantClick}
|
|
385
|
+
onReportParticipantClick={onReportParticipantClick}
|
|
386
|
+
customChannelActions={customChannelActions}
|
|
405
387
|
/>
|
|
406
388
|
</div>
|
|
407
389
|
|
|
@@ -438,33 +420,6 @@ const ChannelViewInner: React.FC<{
|
|
|
438
420
|
/>
|
|
439
421
|
</Window>
|
|
440
422
|
</WithComponents>
|
|
441
|
-
|
|
442
|
-
{/* Channel Info Dialog — suppressed entirely when showChannelInfo is
|
|
443
|
-
false so restricted surfaces (e.g. anonymous visitor chat) cannot
|
|
444
|
-
surface the participant profile or block/report/delete actions even
|
|
445
|
-
if the dialog were opened programmatically. */}
|
|
446
|
-
{showChannelInfo && (
|
|
447
|
-
<ChannelInfoDialog
|
|
448
|
-
dialogRef={infoDialogRef}
|
|
449
|
-
onClose={handleCloseInfo}
|
|
450
|
-
participant={participant}
|
|
451
|
-
participantDisplayName={resolveParticipantDisplayName(
|
|
452
|
-
participant?.user
|
|
453
|
-
)}
|
|
454
|
-
channel={channel}
|
|
455
|
-
followerStatusLabel={followerStatusLabel}
|
|
456
|
-
onLeaveConversation={onLeaveConversation}
|
|
457
|
-
onBlockParticipant={onBlockParticipant}
|
|
458
|
-
showDeleteConversation={showDeleteConversation}
|
|
459
|
-
showBlockParticipant={showBlockParticipant}
|
|
460
|
-
showReportParticipant={showReportParticipant}
|
|
461
|
-
onDeleteConversationClick={onDeleteConversationClick}
|
|
462
|
-
onBlockParticipantClick={onBlockParticipantClick}
|
|
463
|
-
onReportParticipantClick={onReportParticipantClick}
|
|
464
|
-
customProfileContent={customProfileContent}
|
|
465
|
-
customChannelActions={customChannelActions}
|
|
466
|
-
/>
|
|
467
|
-
)}
|
|
468
423
|
</>
|
|
469
424
|
)
|
|
470
425
|
}
|
|
@@ -490,7 +445,6 @@ export const ChannelView = React.memo<ChannelViewProps>(
|
|
|
490
445
|
onReportParticipantClick,
|
|
491
446
|
showBlockParticipant = true,
|
|
492
447
|
showReportParticipant = true,
|
|
493
|
-
showFollowerStatus = true,
|
|
494
448
|
composerDisabled = false,
|
|
495
449
|
composerDisabledReason,
|
|
496
450
|
dmAgentEnabled,
|
|
@@ -499,7 +453,6 @@ export const ChannelView = React.memo<ChannelViewProps>(
|
|
|
499
453
|
showStarButton = false,
|
|
500
454
|
chatbotVotingEnabled = false,
|
|
501
455
|
renderChannelBanner,
|
|
502
|
-
customProfileContent,
|
|
503
456
|
customChannelActions,
|
|
504
457
|
renderMessage,
|
|
505
458
|
onMessageLinkClick,
|
|
@@ -610,14 +563,12 @@ export const ChannelView = React.memo<ChannelViewProps>(
|
|
|
610
563
|
onReportParticipantClick={onReportParticipantClick}
|
|
611
564
|
showBlockParticipant={showBlockParticipant}
|
|
612
565
|
showReportParticipant={showReportParticipant}
|
|
613
|
-
showFollowerStatus={showFollowerStatus}
|
|
614
566
|
composerDisabled={composerDisabled}
|
|
615
567
|
composerDisabledReason={composerDisabledReason}
|
|
616
568
|
showStarButton={showStarButton}
|
|
617
569
|
dmAgentEnabled={dmAgentEnabled}
|
|
618
570
|
chatbotVotingEnabled={chatbotVotingEnabled}
|
|
619
571
|
renderChannelBanner={renderChannelBanner}
|
|
620
|
-
customProfileContent={customProfileContent}
|
|
621
572
|
customChannelActions={customChannelActions}
|
|
622
573
|
renderMessage={renderMessage}
|
|
623
574
|
viewerLanguage={viewerLanguage}
|