mirror of
https://github.com/suitenumerique/messages.git
synced 2026-09-28 04:34:56 +02:00
🐛(frontend) refresh thread view on new messages (#453)
If the message count of the selected thread has changed the view should be refreshed to display the new message.
This commit is contained in:
+1
-5
@@ -38,7 +38,7 @@ export const ThreadMessage = forwardRef<HTMLElement, ThreadMessageProps>(
|
||||
const replyFormRef = useRef<HTMLDivElement>(null);
|
||||
const threadViewContext = useThreadViewContext()
|
||||
const isMessageReady = threadViewContext.isMessageReady(message.id);
|
||||
const [isMessageBodyLoaded, setIsMessageBodyLoaded] = useState(false);
|
||||
const [isMessageBodyLoaded, setIsMessageBodyLoaded] = useState(isMessageReady);
|
||||
const [isFolded, setIsFolded] = useState(!isLatest && !message.is_unread && !draftMessage?.is_draft);
|
||||
const [replyFormMode, setReplyFormMode] = useState<MessageFormMode | null>(getReplyFormMode)
|
||||
const previousReplyFormMode = usePrevious<MessageFormMode | null>(replyFormMode);
|
||||
@@ -97,10 +97,6 @@ export const ThreadMessage = forwardRef<HTMLElement, ThreadMessageProps>(
|
||||
setReplyFormMode(getReplyFormMode())
|
||||
}, [message, draftMessage])
|
||||
|
||||
useEffect(() => {
|
||||
setIsFolded(!isLatest && !message.is_unread && !draftMessage?.is_draft);
|
||||
}, [isLatest, draftMessage?.is_draft])
|
||||
|
||||
// Smooth scroll to the reply form when it is opened by the user
|
||||
useEffect(() => {
|
||||
if (!threadViewContext.isReady) return;
|
||||
|
||||
@@ -42,7 +42,7 @@ const ThreadViewComponent = ({ messages, mailboxId, thread, showTrashedMessages,
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
const { markAsRead } = useRead();
|
||||
const isAISummaryEnabled = useFeatureFlag(FEATURE_KEYS.AI_SUMMARY);
|
||||
const { isReady, reset } = useThreadViewContext();
|
||||
const { isReady, reset, hasBeenInitialized, setHasBeenInitialized } = useThreadViewContext();
|
||||
// Refs for all unread messages
|
||||
const unreadRefs = useRef<Record<string, HTMLElement | null>>({});
|
||||
// Find all unread message IDs
|
||||
@@ -95,7 +95,7 @@ const ThreadViewComponent = ({ messages, mailboxId, thread, showTrashedMessages,
|
||||
}, [isReady, unreadMessageIds.join(","), thread.id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isReady) {
|
||||
if (isReady && !hasBeenInitialized) {
|
||||
let messageToScroll = latestMessage?.id;
|
||||
let selector = `#thread-message-${messageToScroll}`;
|
||||
if (draftMessageIds.length > 0) {
|
||||
@@ -109,15 +109,15 @@ const ThreadViewComponent = ({ messages, mailboxId, thread, showTrashedMessages,
|
||||
const el = document.querySelector<HTMLElement>(selector);
|
||||
if (el) {
|
||||
rootRef.current?.scrollTo({ top: el.offsetTop - 225, behavior: 'instant' });
|
||||
setHasBeenInitialized(true);
|
||||
}
|
||||
}
|
||||
}, [isReady]);
|
||||
|
||||
useEffect(() => {
|
||||
useEffect(() => () => {
|
||||
reset();
|
||||
}, [thread.id]);
|
||||
|
||||
|
||||
return (
|
||||
<div className={clsx("thread-view", { "thread-view--talk": isThreadSender })} ref={rootRef}>
|
||||
<div className="thread-view__sticky-container" ref={stickyContainerRef}>
|
||||
|
||||
@@ -9,6 +9,8 @@ type ThreadViewContextType = {
|
||||
isMessageReady: (messageId: string) => boolean | undefined;
|
||||
setMessageReadiness: (messageId: string, isReady: boolean) => void;
|
||||
reset: (messageId?: string) => void;
|
||||
hasBeenInitialized: boolean;
|
||||
setHasBeenInitialized: (hasBeenInitialized: boolean) => void;
|
||||
}
|
||||
|
||||
const ThreadViewContext = createContext<ThreadViewContextType | undefined>(undefined);
|
||||
@@ -19,6 +21,7 @@ const ThreadViewContext = createContext<ThreadViewContextType | undefined>(undef
|
||||
*/
|
||||
const ThreadViewProvider = ({ messageIds, children }: ThreadViewProviderProps) => {
|
||||
const [messagesReadiness, setMessagesReadiness] = useState(new Map(messageIds.map((id) => [id, false])));
|
||||
const [hasBeenInitialized, setHasBeenInitialized] = useState(false);
|
||||
|
||||
const isReady = useMemo(() => {
|
||||
return Array.from(messagesReadiness.values()).every((isReady) => isReady === true);
|
||||
@@ -48,6 +51,7 @@ const ThreadViewProvider = ({ messageIds, children }: ThreadViewProviderProps) =
|
||||
setMessageReadiness(messageId, false);
|
||||
} else {
|
||||
setMessagesReadiness(new Map(messageIds.map((id) => [id, false])));
|
||||
setHasBeenInitialized(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,8 +60,10 @@ const ThreadViewProvider = ({ messageIds, children }: ThreadViewProviderProps) =
|
||||
isMessageReady,
|
||||
setMessageReadiness,
|
||||
reset,
|
||||
hasBeenInitialized,
|
||||
setHasBeenInitialized,
|
||||
messagesReadiness,
|
||||
}), [isReady, setMessageReadiness, isMessageReady, reset, messagesReadiness]);
|
||||
}), [isReady, setMessageReadiness, isMessageReady, reset, messagesReadiness, hasBeenInitialized, setHasBeenInitialized]);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ export const MailboxProvider = ({ children }: PropsWithChildren) => {
|
||||
?? mailboxQuery.data.data[mailboxQuery.data.data.length - 1]
|
||||
}, [router.query.mailboxId, mailboxQuery.data])
|
||||
|
||||
const previousUnreadMessagesCount = usePrevious(selectedMailbox?.count_unread_messages || 0);
|
||||
const previousUnreadMessagesCount = usePrevious(selectedMailbox?.count_unread_messages);
|
||||
const threadQueryKey = useMemo(() => {
|
||||
const queryKey = ['threads', selectedMailbox?.id];
|
||||
if (searchParams.get('search')) {
|
||||
@@ -167,6 +167,7 @@ export const MailboxProvider = ({ children }: PropsWithChildren) => {
|
||||
const threadId = router.query.threadId;
|
||||
return threadsQuery.data?.pages.flatMap((page) => page.data.results).find((thread) => thread.id === threadId) ?? null;
|
||||
}, [router.query.threadId, flattenThreads])
|
||||
const previousSelectedThreadMessagesCount = usePrevious(selectedThread?.messages.length);
|
||||
|
||||
const messagesQuery = useMessagesList({
|
||||
query: {
|
||||
@@ -335,13 +336,21 @@ export const MailboxProvider = ({ children }: PropsWithChildren) => {
|
||||
|
||||
// Invalidate the threads query to refresh the threads list when the unread messages count changes
|
||||
useEffect(() => {
|
||||
if (!selectedMailbox) return;
|
||||
if ((previousUnreadMessagesCount ?? 0) !== (selectedMailbox.count_unread_messages)) {
|
||||
if (!selectedMailbox || previousUnreadMessagesCount === undefined) return;
|
||||
if (previousUnreadMessagesCount !== selectedMailbox.count_unread_messages) {
|
||||
invalidateThreadsStats();
|
||||
queryClient.invalidateQueries({ queryKey: ['threads', selectedMailbox?.id] });
|
||||
}
|
||||
}, [selectedMailbox?.count_unread_messages]);
|
||||
|
||||
// Invalidate the thread messages query to refresh the thread messages when there is a new message
|
||||
useEffect(() => {
|
||||
if (!selectedThread || previousSelectedThreadMessagesCount === undefined) return;
|
||||
if (previousSelectedThreadMessagesCount < (selectedThread?.messages.length ?? 0)) {
|
||||
invalidateThreadMessages();
|
||||
}
|
||||
}, [selectedThread?.messages.length]);
|
||||
|
||||
useEffect(() => {
|
||||
if (searchParams.get('search') !== previousSearchParams?.get('search')) {
|
||||
resetSearchQueryDebounced();
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useEffect, useRef } from 'react';
|
||||
* @param value The value to track
|
||||
* @returns The previous value of the tracked variable
|
||||
*/
|
||||
function usePrevious<T>(value: T): T | undefined {
|
||||
function usePrevious<T>(value: T): T {
|
||||
const ref = useRef<T>(value);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -15,4 +15,4 @@ function usePrevious<T>(value: T): T | undefined {
|
||||
return ref.current;
|
||||
}
|
||||
|
||||
export default usePrevious;
|
||||
export default usePrevious;
|
||||
|
||||
Reference in New Issue
Block a user