Skip to content

Commit f026518

Browse files
committed
Merge branch 'master' into chore/demo/public-channel-overlay
# Conflicts: # examples/vite/src/ChatLayout/Panels.tsx
2 parents ca1d53b + 76228d8 commit f026518

9 files changed

Lines changed: 236 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,28 @@
1+
## [14.1.0](https://github.com/GetStream/stream-chat-react/compare/v14.0.1...v14.1.0) (2026-05-04)
2+
3+
### Bug Fixes
4+
5+
* add ScrollToLatestMessageButton to ComponentContext ([#3159](https://github.com/GetStream/stream-chat-react/issues/3159)) ([952c125](https://github.com/GetStream/stream-chat-react/commit/952c125120c401258f4654d6ce1c3314ceef6964))
6+
* allow user blocking only in DM-type channels ([#3139](https://github.com/GetStream/stream-chat-react/issues/3139)) ([deda536](https://github.com/GetStream/stream-chat-react/commit/deda536d14da5ea108959e8144d29da0e3afbf25))
7+
* decouple msg bubble width from reaction list width ([#3142](https://github.com/GetStream/stream-chat-react/issues/3142)) ([980c233](https://github.com/GetStream/stream-chat-react/commit/980c233e5993ffff559ede3e8b8942066c0761f8))
8+
* export AttachmentSelectorContext from the SDK ([#3158](https://github.com/GetStream/stream-chat-react/issues/3158)) ([68efeb5](https://github.com/GetStream/stream-chat-react/commit/68efeb59542b1f849b04c0f8c09ac16f077c7d46))
9+
* font & box shadow fixes ([#3135](https://github.com/GetStream/stream-chat-react/issues/3135)) ([6d04cdf](https://github.com/GetStream/stream-chat-react/commit/6d04cdf6dc4f1a67685d0080fd6a530da336b4f7)), closes [#3134](https://github.com/GetStream/stream-chat-react/issues/3134)
10+
* limit reactions host width (segmented/bottom) ([#3154](https://github.com/GetStream/stream-chat-react/issues/3154)) ([be50105](https://github.com/GetStream/stream-chat-react/commit/be50105a8a4e2002ea1d0b13ccbd3bd3fe1fc779))
11+
* make search results scrollable ([#3152](https://github.com/GetStream/stream-chat-react/issues/3152)) ([ead6cb5](https://github.com/GetStream/stream-chat-react/commit/ead6cb55e855104b06774358c8fac24ff2c699b6))
12+
* **MessageList:** prevent message pagination too early on mount ([#3143](https://github.com/GetStream/stream-chat-react/issues/3143)) ([12e282f](https://github.com/GetStream/stream-chat-react/commit/12e282f36706745453b29e4852d799cd498c52ae))
13+
* prevent cutting off button outlines in ContextMenu components ([#3151](https://github.com/GetStream/stream-chat-react/issues/3151)) ([b3469f0](https://github.com/GetStream/stream-chat-react/commit/b3469f012623a1500090e7a8acd1af3403fccc8a))
14+
* remove scrollbar gutters from VML ([#3148](https://github.com/GetStream/stream-chat-react/issues/3148)) ([4a6a8ae](https://github.com/GetStream/stream-chat-react/commit/4a6a8aed3e62eb0da5c56ceeb46eb317205341f1))
15+
16+
### Features
17+
18+
* **a11y:** improve accessibility across dialogs, forms, menus, media, and focus flows ([#3146](https://github.com/GetStream/stream-chat-react/issues/3146)) ([917b7f5](https://github.com/GetStream/stream-chat-react/commit/917b7f5e214e51129e41cc24070b59c759359e3e))
19+
* change textarea default placeholder text ([#3150](https://github.com/GetStream/stream-chat-react/issues/3150)) ([45b1836](https://github.com/GetStream/stream-chat-react/commit/45b18361d54e36e964d3fa7b38814590fad037b4))
20+
* introduce `MessageUI` to `ComponentContext` ([#3140](https://github.com/GetStream/stream-chat-react/issues/3140)) ([16af18d](https://github.com/GetStream/stream-chat-react/commit/16af18de4a2891e47f36fff108ace1f290caf25d))
21+
22+
### Refactors
23+
24+
* message styling ([#3136](https://github.com/GetStream/stream-chat-react/issues/3136)) ([cd3a9c0](https://github.com/GetStream/stream-chat-react/commit/cd3a9c052c8a55a26b0415b1447dcd8f16f001a3))
25+
126
## [14.0.1](https://github.com/GetStream/stream-chat-react/compare/v14.0.0...v14.0.1) (2026-04-17)
227

328
### Bug Fixes
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
.app-public-channel-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-public-channel-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-public-channel-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-public-channel-overlay__title {
70+
font: var(--str-chat__font-heading-xs);
71+
color: var(--str-chat__text-color);
72+
}
73+
74+
.app-public-channel-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-public-channel-overlay__join-button {
81+
width: 106px;
82+
83+
.str-chat__loading-indicator {
84+
height: 20px;
85+
width: 20px;
86+
}
87+
}
88+
89+
.app-public-channel-composer-banner {
90+
display: flex;
91+
align-items: center;
92+
justify-content: center;
93+
width: 100%;
94+
min-height: 56px;
95+
padding: 16px;
96+
border-top: 1px solid var(--str-chat__border-color);
97+
}
98+
99+
.app-public-channel-composer-banner__text {
100+
margin: 0;
101+
font: var(--str-chat__font-caption-default);
102+
color: var(--str-chat__text-secondary);
103+
text-align: center;
104+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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 './PublicChannelOverlay.scss';
14+
15+
export const usePublicChannelState = () => {
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 PublicChannelOverlay = () => {
28+
const { canJoin, channel, client, isMember } = usePublicChannelState();
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: 'PublicChannelOverlay',
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 || !canJoin) return null;
54+
55+
return (
56+
<div className='app-public-channel-overlay'>
57+
<div className='app-public-channel-overlay__content'>
58+
<IconMessageBubbles />
59+
<div className='app-public-channel-overlay__text'>
60+
<p className='app-public-channel-overlay__title'>
61+
You're previewing this group
62+
</p>
63+
<p className='app-public-channel-overlay__description'>
64+
Join to send messages and follow the conversation
65+
</p>
66+
</div>
67+
<Button
68+
appearance='solid'
69+
className='app-public-channel-overlay__join-button'
70+
disabled={joining}
71+
onClick={handleJoin}
72+
size='md'
73+
variant='primary'
74+
>
75+
{joining ? <LoadingIndicator /> : 'Join Group'}
76+
</Button>
77+
</div>
78+
</div>
79+
);
80+
};
81+
82+
export const PublicChannelComposerBanner = () => {
83+
const { canJoin, isMember } = usePublicChannelState();
84+
85+
if (isMember || canJoin) return null;
86+
87+
return (
88+
<div className='app-public-channel-composer-banner'>
89+
<p className='app-public-channel-composer-banner__text'>
90+
You can only view this conversation
91+
</p>
92+
</div>
93+
);
94+
};

src/components/Message/styling/Message.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,10 @@ $message-bubble-padding: var(--str-chat__spacing-xs);
280280
display: flex;
281281
grid-area: reactions;
282282

283+
&:has(.str-chat__message-reactions--segmented.str-chat__message-reactions--bottom) {
284+
max-width: var(--str-chat__message-max-width);
285+
}
286+
283287
&:has(.str-chat__message-reactions--top) {
284288
margin-bottom: calc(var(--str-chat__spacing-xxs) * -1);
285289
}

src/components/MessageList/MessageList.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ import {
4343
} from '../../constants/limits';
4444
import { useLastOwnMessage } from './hooks/useLastOwnMessage';
4545
import { useReducedMotionPreference } from './hooks/useReducedMotionPreference';
46-
import { ScrollToLatestMessageButton } from './ScrollToLatestMessageButton';
46+
import { ScrollToLatestMessageButton as DefaultScrollToLatestMessageButton } from './ScrollToLatestMessageButton';
4747
import {
4848
NotificationList as DefaultNotificationList,
4949
useNotificationTarget,
@@ -128,6 +128,7 @@ const MessageListWithContext = (props: MessageListWithContextProps) => {
128128
MessageListWrapper = 'ul',
129129
NewMessageNotification = DefaultNewMessageNotification,
130130
NotificationList = DefaultNotificationList,
131+
ScrollToLatestMessageButton = DefaultScrollToLatestMessageButton,
131132
TypingIndicator = DefaultTypingIndicator,
132133
UnreadMessagesNotification = DefaultUnreadMessagesNotification,
133134
} = useComponentContext();

src/components/MessageList/ScrollToLatestMessageButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { Badge } from '../Badge';
1111
import { Button } from '../Button';
1212
import { IconArrowDown } from '../Icons';
1313

14-
type ScrollToLatestMessageButtonProps = {
14+
export type ScrollToLatestMessageButtonProps = {
1515
/** When true, user has jumped to an older message set and newer messages can be loaded */
1616
isNotAtLatestMessageSet?: boolean;
1717
isMessageListScrolledToBottom?: boolean;

src/components/MessageList/VirtualizedMessageList.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,8 @@ import {
4141
} from './VirtualizedMessageListComponents';
4242

4343
import {
44+
ScrollToLatestMessageButton as DefaultScrollToLatestMessageButton,
4445
UnreadMessagesSeparator as DefaultUnreadMessagesSeparator,
45-
ScrollToLatestMessageButton,
4646
} from '../MessageList';
4747
import { DateSeparator as DefaultDateSeparator } from '../DateSeparator';
4848
import { EventComponent as DefaultMessageSystem } from '../EventComponent';
@@ -253,6 +253,7 @@ const VirtualizedMessageListWithContext = (
253253
MessageSystem = DefaultMessageSystem,
254254
NewMessageNotification = DefaultNewMessageNotification,
255255
NotificationList = DefaultNotificationList,
256+
ScrollToLatestMessageButton = DefaultScrollToLatestMessageButton,
256257
TypingIndicator,
257258
UnreadMessagesNotification = DefaultUnreadMessagesNotification,
258259
UnreadMessagesSeparator = DefaultUnreadMessagesSeparator,

src/context/ComponentContext.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import {
4747
type ReactionSelectorProps,
4848
type RecordingPermissionDeniedNotificationProps,
4949
type ReminderNotificationProps,
50+
type ScrollToLatestMessageButtonProps,
5051
type SearchResultsPresearchProps,
5152
type SearchSourceResultListProps,
5253
type SendButtonProps,
@@ -185,6 +186,8 @@ export type ComponentContextValue = {
185186
NotificationList?: React.ComponentType<NotificationListProps>;
186187
/** Custom UI component to display a notification when scrolled up the list and new messages arrive, defaults to and accepts same props as [NewMessageNotification](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageList/NewMessageNotification.tsx) */
187188
NewMessageNotification?: React.ComponentType<NewMessageNotificationProps>;
189+
/** Custom UI component to display the scroll-to-latest-message button in a `MessageList`, defaults to and accepts same props as: [ScrollToLatestMessageButton](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageList/ScrollToLatestMessageButton.tsx) */
190+
ScrollToLatestMessageButton?: React.ComponentType<ScrollToLatestMessageButtonProps>;
188191
/** Custom UI component to display message replies, defaults to and accepts same props as: [MessageRepliesCountButton](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/MessageRepliesCountButton.tsx) */
189192
MessageRepliesCountButton?: React.ComponentType<MessageRepliesCountButtonProps>;
190193
/** Custom UI component to display message delivery status, defaults to and accepts same props as: [MessageStatus](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Message/MessageStatus.tsx) */

src/context/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
export * from './AttachmentSelectorContext';
12
export * from './ChannelActionContext';
23
export * from './ChannelListContext';
34
export * from './ChannelStateContext';

0 commit comments

Comments
 (0)