mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-09-30 23:27:55 -05:00
Don't autoscroll chat when viewing old messages
This commit is contained in:
71
app/features/chat/chat-hooks.ts
Normal file
71
app/features/chat/chat-hooks.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
|
||||
Reference in New Issue
Block a user