Skip to content

Commit ec78d44

Browse files
authored
1 parent 96f2fff commit ec78d44

File tree

7 files changed

+145
-115
lines changed

7 files changed

+145
-115
lines changed

src/hooks/useHandleOnScrollCallback/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export interface UseHandleOnScrollCallbackProps {
1010
onScroll(callback: () => void): void;
1111
scrollRef: React.RefObject<HTMLDivElement>;
1212
setShowScrollDownButton?: React.Dispatch<React.SetStateAction<boolean>>;
13+
setIsScrolled?: React.Dispatch<React.SetStateAction<boolean>>;
1314
}
1415

1516
export function calcScrollBottom(scrollHeight: number, scrollTop: number): number {

src/modules/Channel/components/Message/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ const Message = ({
8989
onMessageAnimated,
9090
onMessageHighlighted,
9191
onScrollCallback,
92+
setIsScrolled,
9293
} = useChannelContext();
9394
const [showEdit, setShowEdit] = useState(false);
9495
const [showRemove, setShowRemove] = useState(false);
@@ -117,6 +118,7 @@ const Message = ({
117118
hasMore: false,
118119
onScroll: onScrollCallback,
119120
scrollRef: messageScrollRef,
121+
setIsScrolled,
120122
});
121123

122124
const mentionNodes = useDirtyGetMentions({ ref: editMessageInputRef }, { logger });

src/modules/Channel/components/MessageList/index.tsx

Lines changed: 123 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,11 @@ const MessageList: React.FC<MessageListProps> = ({
5555
filterMessageList,
5656
replyType,
5757
loading,
58+
isScrolled,
59+
setIsScrolled,
5860
unreadSince,
5961
} = useChannelContext();
62+
6063
const store = useSendbirdStateContext();
6164
const allMessagesFiltered = (typeof filterMessageList === 'function')
6265
? allMessages.filter((filterMessageList as (message: EveryMessage) => boolean))
@@ -160,121 +163,131 @@ const MessageList: React.FC<MessageListProps> = ({
160163
}
161164
return <PlaceHolder className="sendbird-conversation__no-messages" type={PlaceHolderTypes.NO_MESSAGES} />;
162165
}
166+
163167
return (
164-
<div className={`sendbird-conversation__messages ${className}`}>
165-
<div className="sendbird-conversation__scroll-container">
166-
<div className="sendbird-conversation__padding" />
167-
<div
168-
className="sendbird-conversation__messages-padding"
169-
ref={scrollRef}
170-
onScroll={(e) => {
171-
handleOnScroll();
172-
scrollToBottomHandler(e);
173-
}}
174-
>
175-
{allMessagesFiltered.map((m, idx) => {
176-
const {
177-
chainTop,
178-
chainBottom,
179-
hasSeparator,
180-
} = getMessagePartsInfo({
181-
allMessages: allMessagesFiltered,
182-
replyType,
183-
isMessageGroupingEnabled,
184-
currentIndex: idx,
185-
currentMessage: m,
186-
currentChannel: currentGroupChannel,
187-
});
188-
const isByMe = (m as UserMessage)?.sender?.userId === store?.config?.userId;
189-
return (
190-
<MessageProvider message={m} key={m?.messageId} isByMe={isByMe}>
191-
<Message
192-
handleScroll={moveScroll}
193-
renderMessage={renderMessage}
194-
message={m}
195-
hasSeparator={hasSeparator}
196-
chainTop={chainTop}
197-
chainBottom={chainBottom}
198-
renderCustomSeparator={renderCustomSeparator}
199-
/>
200-
</MessageProvider>
201-
);
202-
})}
203-
{localMessages.map((m, idx) => {
204-
const {
205-
chainTop,
206-
chainBottom,
207-
} = getMessagePartsInfo({
208-
allMessages: allMessagesFiltered,
209-
replyType,
210-
isMessageGroupingEnabled,
211-
currentIndex: idx,
212-
currentMessage: m,
213-
currentChannel: currentGroupChannel,
214-
});
215-
const isByMe = (m as UserMessage)?.sender?.userId === store?.config?.userId;
216-
return (
217-
<MessageProvider message={m} key={m?.messageId} isByMe={isByMe}>
218-
<Message
219-
handleScroll={moveScroll}
220-
renderMessage={renderMessage}
221-
message={m}
222-
chainTop={chainTop}
223-
chainBottom={chainBottom}
224-
renderCustomSeparator={renderCustomSeparator}
225-
/>
226-
</MessageProvider>
227-
);
228-
})}
229-
{/* show frozen notifications */}
230-
{/* show new message notifications */}
231-
</div>
232-
</div>
233-
{currentGroupChannel?.isFrozen && (
234-
<FrozenNotification className="sendbird-conversation__messages__notification" />
235-
)}
236-
{unreadSince && (
237-
<UnreadCount
238-
className="sendbird-conversation__messages__notification"
239-
count={currentGroupChannel?.unreadMessageCount}
240-
time={unreadSince}
241-
onClick={() => {
242-
if (scrollRef?.current?.scrollTop) {
243-
scrollRef.current.scrollTop = (scrollRef?.current?.scrollHeight ?? 0) - (scrollRef?.current?.offsetHeight ?? 0);
244-
}
245-
if (!disableMarkAsRead && !!currentGroupChannel) {
246-
markAsReadScheduler.push(currentGroupChannel);
247-
messagesDispatcher({
248-
type: messageActionTypes.MARK_AS_READ,
249-
payload: { channel: currentGroupChannel },
250-
});
251-
}
252-
setInitialTimeStamp(null);
253-
setAnimatedMessageId(null);
254-
setHighLightedMessageId(null);
255-
}}
256-
/>
257-
)}
168+
<>
258169
{
259-
// This flag is an unmatched variable
260-
scrollBottom > SCROLL_BOTTOM_PADDING && (
170+
!isScrolled && <PlaceHolder type={PlaceHolderTypes.LOADING} />
171+
}
172+
<div className={`sendbird-conversation__messages ${className}`}>
173+
<div className="sendbird-conversation__scroll-container">
174+
<div className="sendbird-conversation__padding" />
261175
<div
262-
className="sendbird-conversation__scroll-bottom-button"
263-
onClick={onClickScrollBot}
264-
onKeyDown={onClickScrollBot}
265-
tabIndex={0}
266-
role="button"
176+
className="sendbird-conversation__messages-padding"
177+
ref={scrollRef}
178+
onScroll={(e) => {
179+
handleOnScroll();
180+
scrollToBottomHandler(e);
181+
}}
267182
>
268-
<Icon
269-
width="24px"
270-
height="24px"
271-
type={IconTypes.CHEVRON_DOWN}
272-
fillColor={IconColors.PRIMARY}
273-
/>
183+
{
184+
allMessagesFiltered.map((m, idx) => {
185+
const {
186+
chainTop,
187+
chainBottom,
188+
hasSeparator,
189+
} = getMessagePartsInfo({
190+
allMessages: allMessagesFiltered,
191+
replyType,
192+
isMessageGroupingEnabled,
193+
currentIndex: idx,
194+
currentMessage: m,
195+
currentChannel: currentGroupChannel,
196+
});
197+
const isByMe = (m as UserMessage)?.sender?.userId === store?.config?.userId;
198+
return (
199+
<MessageProvider message={m} key={m?.messageId} isByMe={isByMe}>
200+
<Message
201+
handleScroll={moveScroll}
202+
renderMessage={renderMessage}
203+
message={m}
204+
hasSeparator={hasSeparator}
205+
chainTop={chainTop}
206+
chainBottom={chainBottom}
207+
renderCustomSeparator={renderCustomSeparator}
208+
/>
209+
</MessageProvider>
210+
);
211+
})
212+
}
213+
{
214+
localMessages.map((m, idx) => {
215+
const {
216+
chainTop,
217+
chainBottom,
218+
} = getMessagePartsInfo({
219+
allMessages: allMessagesFiltered,
220+
replyType,
221+
isMessageGroupingEnabled,
222+
currentIndex: idx,
223+
currentMessage: m,
224+
currentChannel: currentGroupChannel,
225+
});
226+
const isByMe = (m as UserMessage)?.sender?.userId === store?.config?.userId;
227+
return (
228+
<MessageProvider message={m} key={m?.messageId} isByMe={isByMe}>
229+
<Message
230+
handleScroll={moveScroll}
231+
renderMessage={renderMessage}
232+
message={m}
233+
chainTop={chainTop}
234+
chainBottom={chainBottom}
235+
renderCustomSeparator={renderCustomSeparator}
236+
/>
237+
</MessageProvider>
238+
);
239+
})
240+
}
241+
{/* show frozen notifications, */}
242+
{/* show new message notifications, */}
274243
</div>
275-
)
276-
}
277-
</div>
244+
</div>
245+
{currentGroupChannel?.isFrozen && (
246+
<FrozenNotification className="sendbird-conversation__messages__notification" />
247+
)}
248+
{unreadSince && (
249+
<UnreadCount
250+
className="sendbird-conversation__messages__notification"
251+
count={currentGroupChannel?.unreadMessageCount}
252+
time={unreadSince}
253+
onClick={() => {
254+
if (scrollRef?.current?.scrollTop) {
255+
scrollRef.current.scrollTop = (scrollRef?.current?.scrollHeight ?? 0) - (scrollRef?.current?.offsetHeight ?? 0);
256+
}
257+
if (!disableMarkAsRead && !!currentGroupChannel) {
258+
markAsReadScheduler.push(currentGroupChannel);
259+
messagesDispatcher({
260+
type: messageActionTypes.MARK_AS_READ,
261+
payload: { channel: currentGroupChannel },
262+
});
263+
}
264+
setInitialTimeStamp(null);
265+
setAnimatedMessageId(null);
266+
setHighLightedMessageId(null);
267+
}}
268+
/>
269+
)}
270+
{
271+
// This flag is an unmatched variable
272+
scrollBottom > SCROLL_BOTTOM_PADDING && (
273+
<div
274+
className="sendbird-conversation__scroll-bottom-button"
275+
onClick={onClickScrollBot}
276+
onKeyDown={onClickScrollBot}
277+
tabIndex={0}
278+
role="button"
279+
>
280+
<Icon
281+
width="24px"
282+
height="24px"
283+
type={IconTypes.CHEVRON_DOWN}
284+
fillColor={IconColors.PRIMARY}
285+
/>
286+
</div>
287+
)
288+
}
289+
</div>
290+
</>
278291
);
279292
};
280293

src/modules/Channel/context/ChannelProvider.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ interface UpdateMessageProps {
135135

136136
interface ChannelProviderInterface extends ChannelContextProps, MessageStoreInterface {
137137
scrollToMessage?(createdAt: number, messageId: number): void;
138+
isScrolled?: boolean;
139+
setIsScrolled?: React.Dispatch<React.SetStateAction<boolean>>;
138140
messageActionTypes: Record<string, string>;
139141
messagesDispatcher: CustomUseReducerDispatcher;
140142
quoteMessage: SendableMessageType;
@@ -229,6 +231,7 @@ const ChannelProvider: React.FC<ChannelContextProps> = (props: ChannelContextPro
229231
}, [highlightedMessage]);
230232
const userFilledMessageListQuery = queries?.messageListParams;
231233
const [quoteMessage, setQuoteMessage] = useState<SendableMessageType>(null);
234+
const [isScrolled, setIsScrolled] = useState(false);
232235

233236
const [messagesStore, messagesDispatcher] = useReducer(
234237
messagesReducer,
@@ -358,6 +361,7 @@ const ChannelProvider: React.FC<ChannelContextProps> = (props: ChannelContextPro
358361
latestMessageTimeStamp,
359362
replyType,
360363
isVoiceMessageEnabled,
364+
setIsScrolled,
361365
}, {
362366
logger,
363367
scrollRef,
@@ -516,6 +520,8 @@ const ChannelProvider: React.FC<ChannelContextProps> = (props: ChannelContextPro
516520
scrollRef,
517521
scrollBehavior,
518522
toggleReaction,
523+
isScrolled,
524+
setIsScrolled,
519525
}}>
520526
<UserProfileProvider
521527
disableUserProfile={props?.disableUserProfile ?? config?.disableUserProfile}

src/modules/Channel/context/dux/actionTypes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ export const RESET_MESSAGES = 'RESET_MESSAGES';
22

33
export const FETCH_INITIAL_MESSAGES_START = 'FETCH_INITIAL_MESSAGES_START';
44
export const FETCH_INITIAL_MESSAGES_SUCCESS = 'FETCH_INITIAL_MESSAGES_SUCCESS';
5+
56
export const FETCH_INITIAL_MESSAGES_FAILURE = 'FETCH_INITIAL_MESSAGES_FAILURE';
67
export const FETCH_PREV_MESSAGES_SUCCESS = 'FETCH_PREV_MESSAGES_SUCCESS';
78
export const FETCH_PREV_MESSAGES_FAILURE = 'FETCH_PREV_MESSAGES_FAILURE';

src/modules/Channel/context/hooks/useInitialMessagesFetch.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,17 @@ function useInitialMessagesFetch({
1010
userFilledMessageListQuery,
1111
initialTimeStamp,
1212
replyType,
13+
setIsScrolled,
1314
}, {
1415
logger,
1516
scrollRef,
1617
messagesDispatcher,
1718
}) {
1819
const channelUrl = currentGroupChannel?.url;
20+
1921
useEffect(() => {
2022
logger.info('Channel useInitialMessagesFetch: Setup started', currentGroupChannel);
23+
setIsScrolled(false);
2124
messagesDispatcher({
2225
type: messageActionTypes.RESET_MESSAGES,
2326
payload: null,
@@ -78,10 +81,10 @@ function useInitialMessagesFetch({
7881
})
7982
.finally(() => {
8083
if (!initialTimeStamp) {
81-
setTimeout(() => utils.scrollIntoLast(0, scrollRef));
84+
setTimeout(() => utils.scrollIntoLast(0, scrollRef, setIsScrolled));
8285
} else {
8386
setTimeout(() => {
84-
utils.scrollToRenderedMessage(scrollRef, initialTimeStamp);
87+
utils.scrollToRenderedMessage(scrollRef, initialTimeStamp, setIsScrolled);
8588
}, 500);
8689
}
8790
});

src/modules/Channel/context/utils.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { OutgoingMessageStates } from '../../../utils/exports/getOutgoingMessage
88
const UNDEFINED = 'undefined';
99
const { SUCCEEDED, FAILED, PENDING } = getSendingMessageStatus();
1010

11-
export const scrollToRenderedMessage = (scrollRef, initialTimeStamp) => {
11+
export const scrollToRenderedMessage = (scrollRef, initialTimeStamp, setIsScrolled) => {
1212
try {
1313
const container = scrollRef.current;
1414
// scroll into the message with initialTimeStamp
@@ -23,23 +23,27 @@ export const scrollToRenderedMessage = (scrollRef, initialTimeStamp) => {
2323
}
2424
} catch {
2525
// do nothing
26+
} finally {
27+
setIsScrolled?.(true);
2628
}
2729
};
2830

2931
/* eslint-disable default-param-last */
30-
export const scrollIntoLast = (initialTry = 0, scrollRef) => {
32+
export const scrollIntoLast = (initialTry = 0, scrollRef, setIsScrolled) => {
3133
const MAX_TRIES = 10;
3234
const currentTry = initialTry;
3335
if (currentTry > MAX_TRIES) {
36+
setIsScrolled?.(true);
3437
return;
3538
}
3639
try {
3740
const scrollDOM = scrollRef?.current || document.querySelector('.sendbird-conversation__messages-padding');
3841
// eslint-disable-next-line no-multi-assign
3942
scrollDOM.scrollTop = scrollDOM.scrollHeight;
43+
setIsScrolled?.(true);
4044
} catch (error) {
4145
setTimeout(() => {
42-
scrollIntoLast(currentTry + 1, scrollRef);
46+
scrollIntoLast(currentTry + 1, scrollRef, setIsScrolled);
4347
}, 500 * currentTry);
4448
}
4549
};

0 commit comments

Comments
 (0)