Don't autoscroll chat when viewing old messages

This commit is contained in:
Kalle
2024-06-16 12:37:03 +03:00
parent b968233aa5
commit ab34e5daea
4 changed files with 105 additions and 5 deletions

View File

@@ -0,0 +1,71 @@
import React from "react";
import type { ChatMessage } from "./chat-types";
import { useUser } from "../auth/core/user";
// increasing this = scrolling happens even when scrolled more upwards
const THRESHOLD = 100;
export function useChatAutoScroll(
messages: ChatMessage[],
ref: React.RefObject<HTMLOListElement>,
) {
const user = useUser();
const [firstLoadHandled, setFirstLoadHandled] = React.useState(false);
const [unseenMessages, setUnseenMessages] = React.useState(false);
React.useEffect(() => {
const messagesContainer = ref.current!;
const isScrolledToBottom =
Math.abs(
messagesContainer.scrollHeight -
messagesContainer.clientHeight -
messagesContainer.scrollTop,
) <= THRESHOLD;
const latestMessageIsOwn =
messages[messages.length - 1]?.userId === user?.id;
// lets wait for messages to load first
if (!firstLoadHandled && messages.length === 0) return;
if (isScrolledToBottom || latestMessageIsOwn || !firstLoadHandled) {
setFirstLoadHandled(true);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
} else if (!isScrolledToBottom) {
setUnseenMessages(true);
}
}, [messages, ref, user, firstLoadHandled]);
React.useEffect(() => {
const messagesContainer = ref.current!;
function handleScroll() {
if (
messagesContainer.scrollTop + messagesContainer.clientHeight >=
messagesContainer.scrollHeight - THRESHOLD
) {
setUnseenMessages(false);
}
}
messagesContainer.addEventListener("scroll", handleScroll);
return () => {
messagesContainer.removeEventListener("scroll", handleScroll);
};
}, [ref]);
const scrollToBottom = () => {
ref.current!.scrollTop = ref.current!.scrollHeight;
};
const reset = () => {
setFirstLoadHandled(false);
setUnseenMessages(false);
};
return {
unseenMessagesInTheRoom: unseenMessages,
resetScroller: reset,
scrollToBottom,
};
}

View File

@@ -16,6 +16,7 @@ import { messageTypeToSound, soundEnabled, soundVolume } from "../chat-utils";
import { soundPath } from "~/utils/urls";
import { useTranslation } from "react-i18next";
import { logger } from "~/utils/logger";
import { useChatAutoScroll } from "../chat-hooks";
type ChatUser = Pick<User, "username" | "discordId" | "discordAvatar"> & {
chatNameColor: string | null;
@@ -83,10 +84,8 @@ export function Chat({
[send],
);
React.useEffect(() => {
const messagesContainer = messagesContainerRef.current!;
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}, [messages]);
const { unseenMessagesInTheRoom, scrollToBottom, resetScroller } =
useChatAutoScroll(messages, messagesContainerRef);
React.useEffect(() => {
onMount?.();
@@ -139,7 +138,10 @@ export function Chat({
className={clsx("chat__room-button", {
current: currentRoom === room.code,
})}
onClick={() => setCurrentRoom(room.code)}
onClick={() => {
setCurrentRoom(room.code);
resetScroller();
}}
>
<span className="chat__room-button__unseen invisible" />
{room.label}
@@ -183,6 +185,11 @@ export function Chat({
);
})}
</ol>
{unseenMessagesInTheRoom ? (
<Button className="chat__unseen-messages" onClick={scrollToBottom}>
{t("common:chat.newMessages")}
</Button>
) : null}
<form onSubmit={handleSubmit} className="mt-4">
<input
className="w-full"

View File

@@ -1567,6 +1567,7 @@ html[dir="rtl"] .fix-rtl {
.chat__input-container {
margin-top: auto;
position: relative;
--input-width: 100%;
}
@@ -1610,6 +1611,26 @@ html[dir="rtl"] .fix-rtl {
height: 20px;
}
.chat__unseen-messages {
position: absolute;
font-size: var(--fonts-xxs);
font-weight: var(--bold);
border-radius: var(--rounded-sm);
background-color: var(--bg-lightest-solid);
border: none;
color: var(--text);
bottom: 60px;
right: 50%;
left: 50%;
transform: translate(-50%, -50%);
height: 25px;
width: max-content;
}
.chat__unseen-messages:active {
transform: translate(-50%, -50%);
}
.winner__container {
height: 125px;
width: 125px;

View File

@@ -219,6 +219,7 @@
"chat.systemMsg.cancelReported": "{{name}} requested canceling the match",
"chat.systemMsg.cancelConfirmed": "{{name}} confirmed canceling the match. Match is now locked",
"chat.systemMsg.userLeft": "{{name}} left the group",
"chat.newMessages": "New messages",
"fc.title": "Friend code",