Skip to content

Commit 43b1f0d

Browse files
committed
Add feature to scroll to top of new message instead of bottom
1 parent 935a230 commit 43b1f0d

File tree

15 files changed

+95
-1
lines changed

15 files changed

+95
-1
lines changed

apps/testing/src/pages/PlaygroundPage.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,6 @@ export function PlaygroundPage() {
1010
replyType: 'thread',
1111
}
1212
}}
13+
// isFocusOnLastMessage={true}
1314
/>;
1415
}

src/lib/Sendbird/context/SendbirdProvider.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ const SendbirdContextManager = ({
7272
sdkInitParams,
7373
customExtensionParams,
7474
isMultipleFilesMessageEnabled = false,
75+
isFocusOnLastMessage = false,
7576
eventHandlers,
7677
htmlTextDirection = 'ltr',
7778
forceLeftToRightMessageLayout = false,
@@ -288,6 +289,7 @@ const SendbirdContextManager = ({
288289
setCurrentTheme,
289290
setCurrenttheme: setCurrentTheme, // deprecated: typo
290291
isMultipleFilesMessageEnabled,
292+
isFocusOnLastMessage,
291293
uikitMultipleFilesMessageLimit,
292294
logger,
293295
pubSub,
@@ -316,6 +318,7 @@ const SendbirdContextManager = ({
316318
currentTheme,
317319
setCurrentTheme,
318320
isMultipleFilesMessageEnabled,
321+
isFocusOnLastMessage,
319322
uikitMultipleFilesMessageLimit,
320323
logger,
321324
pubSub,
@@ -395,6 +398,7 @@ const InternalSendbirdProvider = (props: SendbirdProviderProps & { logger: Logge
395398
},
396399
disableMarkAsDelivered: props?.disableMarkAsDelivered,
397400
isMultipleFilesMessageEnabled: props?.isMultipleFilesMessageEnabled,
401+
isFocusOnLastMessage: props?.isFocusOnLastMessage,
398402
},
399403
eventHandlers: props?.eventHandlers,
400404
});

src/lib/Sendbird/context/initialState.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const config: SendbirdStateConfig = {
3333
forceLeftToRightMessageLayout: false,
3434
disableMarkAsDelivered: false,
3535
isMultipleFilesMessageEnabled: false,
36+
isFocusOnLastMessage: false,
3637
htmlTextDirection: 'ltr',
3738
uikitUploadSizeLimit: DEFAULT_UPLOAD_SIZE_LIMIT,
3839
uikitMultipleFilesMessageLimit: DEFAULT_MULTIPLE_FILES_MESSAGE_LIMIT,

src/lib/Sendbird/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ export interface SendbirdProviderProps extends CommonUIKitConfigProps, React.Pro
218218
sdkInitParams?: SendbirdChatInitParams;
219219
customExtensionParams?: CustomExtensionParams;
220220
isMultipleFilesMessageEnabled?: boolean;
221+
isFocusOnLastMessage?: boolean;
221222
// UserProfile
222223
renderUserProfile?: (props: RenderUserProfileProps) => React.ReactElement;
223224
onStartDirectMessage?: (channel: GroupChannel) => void;
@@ -260,6 +261,7 @@ export interface SendbirdStateConfig {
260261
markAsDeliveredScheduler: MarkAsDeliveredSchedulerType;
261262
disableMarkAsDelivered: boolean;
262263
isMultipleFilesMessageEnabled: boolean;
264+
isFocusOnLastMessage: boolean;
263265
// Remote configs set from dashboard by UIKit feature configuration
264266
common: {
265267
enableUsingDefaultUserProfile: SBUConfig['common']['enableUsingDefaultUserProfile'];

src/modules/App/AppLayout.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import useSendbird from '../../lib/Sendbird/context/hooks/useSendbird';
1313
export const AppLayout = (props: AppLayoutProps) => {
1414
const {
1515
isMessageGroupingEnabled,
16+
isFocusOnLastMessage,
1617
allowProfileEdit,
1718
onProfileEditSuccess,
1819
disableAutoSelect,
@@ -50,6 +51,7 @@ export const AppLayout = (props: AppLayoutProps) => {
5051
showSearchIcon={showSearchIcon}
5152
isReactionEnabled={isReactionEnabled}
5253
isMessageGroupingEnabled={isMessageGroupingEnabled}
54+
isFocusOnLastMessage={isFocusOnLastMessage}
5355
allowProfileEdit={allowProfileEdit}
5456
onProfileEditSuccess={onProfileEditSuccess}
5557
currentChannel={currentChannel}
@@ -69,6 +71,7 @@ export const AppLayout = (props: AppLayoutProps) => {
6971
isReactionEnabled={isReactionEnabled}
7072
showSearchIcon={showSearchIcon}
7173
isMessageGroupingEnabled={isMessageGroupingEnabled}
74+
isFocusOnLastMessage={isFocusOnLastMessage}
7275
allowProfileEdit={allowProfileEdit}
7376
onProfileEditSuccess={onProfileEditSuccess}
7477
disableAutoSelect={disableAutoSelect}

src/modules/App/DesktopLayout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export const DesktopLayout: React.FC<DesktopLayoutProps> = (props: DesktopLayout
2121
replyType,
2222
isMessageGroupingEnabled,
2323
isMultipleFilesMessageEnabled,
24+
isFocusOnLastMessage,
2425
allowProfileEdit,
2526
showSearchIcon,
2627
onProfileEditSuccess,
@@ -104,6 +105,7 @@ export const DesktopLayout: React.FC<DesktopLayoutProps> = (props: DesktopLayout
104105
replyType: replyType,
105106
isMessageGroupingEnabled: isMessageGroupingEnabled,
106107
isMultipleFilesMessageEnabled: isMultipleFilesMessageEnabled,
108+
isFocusOnLastMessage: isFocusOnLastMessage,
107109
// for GroupChannel
108110
animatedMessageId: highlightedMessage,
109111
onReplyInThreadClick: onClickThreadReply,

src/modules/App/MobileLayout.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const MobileLayout: React.FC<MobileLayoutProps> = (props: MobileLayoutPro
3232
replyType,
3333
isMessageGroupingEnabled,
3434
isMultipleFilesMessageEnabled,
35+
isFocusOnLastMessage,
3536
allowProfileEdit,
3637
isReactionEnabled,
3738
showSearchIcon,
@@ -155,6 +156,7 @@ export const MobileLayout: React.FC<MobileLayoutProps> = (props: MobileLayoutPro
155156
replyType,
156157
isMessageGroupingEnabled,
157158
isMultipleFilesMessageEnabled,
159+
isFocusOnLastMessage,
158160
// for GroupChannel
159161
animatedMessageId: highlightedMessage,
160162
onReplyInThreadClick: ({ message }) => {

src/modules/App/index.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export interface AppProps {
2929
config?: SendbirdProviderProps['config'];
3030
voiceRecord?: SendbirdProviderProps['voiceRecord'];
3131
isMultipleFilesMessageEnabled?: SendbirdProviderProps['isMultipleFilesMessageEnabled'];
32+
isFocusOnLastMessage?: SendbirdProviderProps['isFocusOnLastMessage'];
3233
colorSet?: SendbirdProviderProps['colorSet'];
3334
stringSet?: SendbirdProviderProps['stringSet'];
3435
allowProfileEdit?: SendbirdProviderProps['allowProfileEdit'];
@@ -99,6 +100,7 @@ export default function App(props: AppProps) {
99100
customExtensionParams,
100101
eventHandlers,
101102
isMultipleFilesMessageEnabled,
103+
isFocusOnLastMessage = false,
102104
isUserIdUsedForNickname = true,
103105
enableLegacyChannelModules = false,
104106
uikitOptions,
@@ -138,6 +140,7 @@ export default function App(props: AppProps) {
138140
renderUserProfile={renderUserProfile}
139141
imageCompression={imageCompression}
140142
isMultipleFilesMessageEnabled={isMultipleFilesMessageEnabled}
143+
isFocusOnLastMessage={isFocusOnLastMessage}
141144
voiceRecord={voiceRecord}
142145
onStartDirectMessage={(channel) => {
143146
setCurrentChannel(channel);
@@ -159,6 +162,7 @@ export default function App(props: AppProps) {
159162
forceLeftToRightMessageLayout={forceLeftToRightMessageLayout}
160163
>
161164
<AppLayout
165+
isFocusOnLastMessage={isFocusOnLastMessage}
162166
isMessageGroupingEnabled={isMessageGroupingEnabled}
163167
allowProfileEdit={allowProfileEdit}
164168
onProfileEditSuccess={onProfileEditSuccess}

src/modules/App/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export interface AppLayoutProps {
1818
forceLeftToRightMessageLayout?: boolean;
1919
isMessageGroupingEnabled?: boolean;
2020
isMultipleFilesMessageEnabled?: boolean;
21+
isFocusOnLastMessage?: boolean;
2122
allowProfileEdit?: boolean;
2223
showSearchIcon?: boolean;
2324
onProfileEditSuccess?(user: User): void; // TODO: Unused props, deprecate it

src/modules/GroupChannel/components/Message/MessageView.tsx

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ export interface MessageProps {
5555
* A function that is called when the new message separator visibility changes.
5656
*/
5757
onNewMessageSeparatorVisibilityChange?: (isVisible: boolean) => void;
58+
/**
59+
* A function that forces scroll to message when new message is received.
60+
*/
61+
forceScrollToMessage?: (ref: React.MutableRefObject<any>, message: CoreMessageType) => void;
5862
/**
5963
* @deprecated Please use `children` instead
6064
* @description Customizes all child components of the message.
@@ -97,6 +101,10 @@ export interface MessageViewProps extends MessageProps {
97101

98102
animatedMessageId: number | null;
99103
setAnimatedMessageId: React.Dispatch<React.SetStateAction<number | null>>;
104+
105+
newMessageIds?: number[] | null;
106+
setNewMessageIds?: (ids: number[]) => void;
107+
100108
onMessageAnimated?: () => void;
101109
/** @deprecated * */
102110
highLightedMessageId?: number | null;
@@ -119,6 +127,7 @@ const MessageView = (props: MessageViewProps) => {
119127
chainBottom,
120128
handleScroll,
121129
onNewMessageSeparatorVisibilityChange,
130+
forceScrollToMessage,
122131

123132
// MessageViewProps
124133
channel,
@@ -147,6 +156,9 @@ const MessageView = (props: MessageViewProps) => {
147156
animatedMessageId,
148157
onMessageAnimated,
149158
usedInLegacy = true,
159+
160+
newMessageIds,
161+
setNewMessageIds,
150162
} = props;
151163

152164
const {
@@ -257,6 +269,23 @@ const MessageView = (props: MessageViewProps) => {
257269
};
258270
}, [animatedMessageId, messageScrollRef.current, message.messageId]);
259271

272+
useLayoutEffect(() => {
273+
if (newMessageIds?.length > 0 && newMessageIds.includes(message.messageId)) {
274+
let rafId: number | null = null;
275+
276+
rafId = requestAnimationFrame(() => {
277+
forceScrollToMessage(messageScrollRef, message);
278+
setNewMessageIds([]);
279+
});
280+
281+
return () => {
282+
if (rafId !== null) {
283+
cancelAnimationFrame(rafId);
284+
}
285+
};
286+
}
287+
}, [newMessageIds]);
288+
260289
const renderedCustomSeparator = useMemo(() => renderCustomSeparator?.({ message }) ?? null, [message, renderCustomSeparator]);
261290

262291
const renderChildren = () => {

0 commit comments

Comments
 (0)