Skip to content

Commit 23b0e7d

Browse files
authored
chore(demo): rename public channel overlay (#3161)
1 parent 76228d8 commit 23b0e7d

3 files changed

Lines changed: 181 additions & 24 deletions

File tree

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
.app-channel-preview-overlay {
2+
position: absolute;
3+
inset: 0;
4+
z-index: 3;
5+
display: flex;
6+
align-items: center;
7+
justify-content: center;
8+
backdrop-filter: blur(5px);
9+
background: rgba(255, 255, 255, 0.75);
10+
padding: 12px 0;
11+
12+
.str-chat__theme-dark & {
13+
background: rgba(0, 0, 0, 0.75);
14+
}
15+
}
16+
17+
.app-channel-preview-overlay__content {
18+
position: relative;
19+
display: flex;
20+
flex-direction: column;
21+
align-items: center;
22+
padding: 40px 48px;
23+
overscroll-behavior: contain;
24+
25+
&::before {
26+
content: '';
27+
position: absolute;
28+
inset: -120px;
29+
border-radius: 50%;
30+
background: radial-gradient(
31+
circle,
32+
rgba(255, 255, 255, 0.9) 0%,
33+
rgba(255, 255, 255, 0.85) 40%,
34+
rgba(255, 255, 255, 0) 70%
35+
);
36+
filter: blur(20px);
37+
z-index: -1;
38+
39+
.str-chat__theme-dark & {
40+
background: radial-gradient(
41+
circle,
42+
rgba(0, 0, 0, 0.6) 0%,
43+
rgba(0, 0, 0, 0.55) 40%,
44+
rgba(0, 0, 0, 0) 70%
45+
);
46+
}
47+
}
48+
49+
.str-chat__icon {
50+
width: 32px;
51+
height: 32px;
52+
color: var(--str-chat__text-tertiary);
53+
}
54+
}
55+
56+
.app-channel-preview-overlay__text {
57+
display: flex;
58+
flex-direction: column;
59+
align-items: center;
60+
text-align: center;
61+
gap: var(--str-chat__spacing-xxs);
62+
margin-block: var(--str-chat__spacing-sm) var(--str-chat__spacing-xl);
63+
64+
p {
65+
margin: 0;
66+
}
67+
}
68+
69+
.app-channel-preview-overlay__title {
70+
font: var(--str-chat__font-heading-xs);
71+
color: var(--str-chat__text-color);
72+
}
73+
74+
.app-channel-preview-overlay__description {
75+
font: var(--str-chat__font-caption-default);
76+
color: var(--str-chat__text-secondary);
77+
max-width: 200px;
78+
}
79+
80+
.app-channel-preview-overlay__join-button {
81+
width: 106px;
82+
83+
.str-chat__loading-indicator {
84+
height: 20px;
85+
width: 20px;
86+
}
87+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { useCallback, useState } from 'react';
2+
import type { ChannelMemberResponse } from 'stream-chat';
3+
import {
4+
Button,
5+
IconMessageBubbles,
6+
LoadingIndicator,
7+
useChannelMembersState,
8+
useChannelStateContext,
9+
useChatContext,
10+
useNotificationApi,
11+
} from 'stream-chat-react';
12+
13+
import './ChannelPreviewOverlay.scss';
14+
15+
export const useChannelMembershipState = () => {
16+
const { client } = useChatContext();
17+
const { channel } = useChannelStateContext();
18+
const members = useChannelMembersState(channel);
19+
const membership = members[client.userID!] as ChannelMemberResponse | undefined;
20+
21+
const isMember = typeof membership?.channel_role === 'string';
22+
const canJoin = channel.data?.own_capabilities?.includes('join-channel');
23+
24+
return { canJoin, channel, client, isMember };
25+
};
26+
27+
export const ChannelPreviewOverlay = () => {
28+
const { canJoin, channel, client, isMember } = useChannelMembershipState();
29+
const { addNotification } = useNotificationApi();
30+
const [joining, setJoining] = useState(false);
31+
32+
const handleJoin = useCallback(async () => {
33+
setJoining(true);
34+
try {
35+
await channel.addMembers([client.userID!]);
36+
} catch (error) {
37+
addNotification({
38+
emitter: 'ChannelPreviewOverlay',
39+
incident: {
40+
domain: 'api',
41+
entity: 'channel',
42+
operation: 'join',
43+
},
44+
message: 'Failed to join the group',
45+
severity: 'error',
46+
error: error instanceof Error ? error : new Error(String(error)),
47+
});
48+
} finally {
49+
setJoining(false);
50+
}
51+
}, [addNotification, channel, client.userID]);
52+
53+
if (isMember) return null;
54+
55+
return (
56+
<div className='app-channel-preview-overlay'>
57+
<div className='app-channel-preview-overlay__content'>
58+
<IconMessageBubbles />
59+
<div className='app-channel-preview-overlay__text'>
60+
<p className='app-channel-preview-overlay__title'>
61+
{canJoin ? "You're previewing this group" : 'This is a private group'}
62+
</p>
63+
<p className='app-channel-preview-overlay__description'>
64+
{canJoin
65+
? 'Join to send messages and follow the conversation'
66+
: 'It is not possible to join this group'}
67+
</p>
68+
</div>
69+
{canJoin && (
70+
<Button
71+
appearance='solid'
72+
className='app-channel-preview-overlay__join-button'
73+
disabled={joining}
74+
onClick={handleJoin}
75+
size='md'
76+
variant='primary'
77+
>
78+
{joining ? <LoadingIndicator /> : 'Join Group'}
79+
</Button>
80+
)}
81+
</div>
82+
</div>
83+
);
84+
};

examples/vite/src/ChatLayout/Panels.tsx

Lines changed: 10 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,7 @@ import { useAppSettingsSelector } from '../AppSettings/state';
2727
import { DESKTOP_LAYOUT_BREAKPOINT } from './constants.ts';
2828
import { SidebarResizeHandle, ThreadResizeHandle } from './Resize.tsx';
2929
import { ReturnToSkipNavigation } from '../AccessibilityNavigation/ReturnToSkipNavigation.tsx';
30-
import {
31-
PublicChannelComposerBanner,
32-
PublicChannelOverlay,
33-
usePublicChannelState,
34-
} from '../PublicChannelOverlay/PublicChannelOverlay.tsx';
30+
import { ChannelPreviewOverlay } from '../ChannelPreviewOverlay/ChannelPreviewOverlay.tsx';
3531
import { useSidebar } from './SidebarContext.tsx';
3632
import { ThreadStateSync } from './Sync.tsx';
3733

@@ -66,23 +62,6 @@ const ChannelThreadPanel = () => {
6662
);
6763
};
6864

69-
const MessageComposerOrBanner = () => {
70-
const { canJoin, isMember } = usePublicChannelState();
71-
72-
if (!isMember && !canJoin) return <PublicChannelComposerBanner />;
73-
74-
return (
75-
<MessageComposer
76-
additionalTextareaProps={{
77-
id: CHANNEL_MESSAGE_COMPOSER_TEXTAREA_TARGET_ID,
78-
}}
79-
audioRecordingEnabled
80-
maxRows={10}
81-
asyncMessagesMultiSendEnabled
82-
/>
83-
);
84-
};
85-
8665
const ResponsiveChannelPanels = () => {
8766
const { thread } = useChannelStateContext('ResponsiveChannelPanels');
8867
const isThreadOpen = !!thread;
@@ -105,8 +84,15 @@ const ResponsiveChannelPanels = () => {
10584
)}
10685
<ReturnToSkipNavigation />
10786
<AIStateIndicator />
108-
<MessageComposerOrBanner />
109-
<PublicChannelOverlay />
87+
<MessageComposer
88+
additionalTextareaProps={{
89+
id: CHANNEL_MESSAGE_COMPOSER_TEXTAREA_TARGET_ID,
90+
}}
91+
audioRecordingEnabled
92+
maxRows={10}
93+
asyncMessagesMultiSendEnabled
94+
/>
95+
<ChannelPreviewOverlay />
11096
</div>
11197
</Window>
11298
</WithDragAndDropUpload>

0 commit comments

Comments
 (0)