From 2282560481a66812ae3970d0820711f55fd33de3 Mon Sep 17 00:00:00 2001 From: Alexander Daichendt Date: Wed, 19 Feb 2025 23:29:14 +0100 Subject: [PATCH] fix: unclickable save button due to overlap with virtualized list item --- client/package.json | 3 - client/pnpm-lock.yaml | 46 ------------ .../ChatMessage/ChatMessageDisplay.tsx | 2 +- client/src/components/MessageDisplay.tsx | 72 ++++++++----------- 4 files changed, 31 insertions(+), 92 deletions(-) diff --git a/client/package.json b/client/package.json index 5d5001f..e77e460 100644 --- a/client/package.json +++ b/client/package.json @@ -22,8 +22,6 @@ "react": "^19.0.0", "react-dom": "^19.0.0", "react-router-dom": "^7.1.5", - "react-virtualized-auto-sizer": "^1.0.25", - "react-window": "^1.8.11", "tailwindcss": "^4.0.6" }, "devDependencies": { @@ -32,7 +30,6 @@ "@testing-library/react": "^16.2.0", "@types/react": "^19.0.8", "@types/react-dom": "^19.0.3", - "@types/react-window": "^1.8.8", "@vitejs/plugin-react": "^4.3.4", "eslint": "^9.19.0", "eslint-plugin-react-hooks": "^5.0.0", diff --git a/client/pnpm-lock.yaml b/client/pnpm-lock.yaml index 136efa1..2bd2006 100644 --- a/client/pnpm-lock.yaml +++ b/client/pnpm-lock.yaml @@ -23,12 +23,6 @@ importers: react-router-dom: specifier: ^7.1.5 version: 7.1.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react-virtualized-auto-sizer: - specifier: ^1.0.25 - version: 1.0.25(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react-window: - specifier: ^1.8.11 - version: 1.8.11(react-dom@19.0.0(react@19.0.0))(react@19.0.0) tailwindcss: specifier: ^4.0.6 version: 4.0.6 @@ -48,9 +42,6 @@ importers: '@types/react-dom': specifier: ^19.0.3 version: 19.0.3(@types/react@19.0.8) - '@types/react-window': - specifier: ^1.8.8 - version: 1.8.8 '@vitejs/plugin-react': specifier: ^4.3.4 version: 4.3.4(vite@6.1.0(jiti@2.4.2)(lightningcss@1.29.1)) @@ -668,9 +659,6 @@ packages: peerDependencies: '@types/react': ^19.0.0 - '@types/react-window@1.8.8': - resolution: {integrity: sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==} - '@types/react@19.0.8': resolution: {integrity: sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==} @@ -1280,9 +1268,6 @@ packages: magic-string@0.30.17: resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} - memoize-one@5.2.1: - resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} - merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -1416,19 +1401,6 @@ packages: react-dom: optional: true - react-virtualized-auto-sizer@1.0.25: - resolution: {integrity: sha512-YHsksEGDfsHbHuaBVDYwJmcktblcHGafz4ZVuYPQYuSHMUGjpwmUCrAOcvMSGMwwk1eFWj1M/1GwYpNPuyhaBg==} - peerDependencies: - react: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0 || ^19.0.0 - - react-window@1.8.11: - resolution: {integrity: sha512-+SRbUVT2scadgFSWx+R1P754xHPEqvcfSfVX10QYg6POOz+WNgkN48pS+BtZNIMGiL1HYrSEiCkwsMS15QogEQ==} - engines: {node: '>8.0.0'} - peerDependencies: - react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react@19.0.0: resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} engines: {node: '>=0.10.0'} @@ -2233,10 +2205,6 @@ snapshots: dependencies: '@types/react': 19.0.8 - '@types/react-window@1.8.8': - dependencies: - '@types/react': 19.0.8 - '@types/react@19.0.8': dependencies: csstype: 3.1.3 @@ -2890,8 +2858,6 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 - memoize-one@5.2.1: {} - merge2@1.4.1: {} micromatch@4.0.8: @@ -3009,18 +2975,6 @@ snapshots: optionalDependencies: react-dom: 19.0.0(react@19.0.0) - react-virtualized-auto-sizer@1.0.25(react-dom@19.0.0(react@19.0.0))(react@19.0.0): - dependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - - react-window@1.8.11(react-dom@19.0.0(react@19.0.0))(react@19.0.0): - dependencies: - '@babel/runtime': 7.26.7 - memoize-one: 5.2.1 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react@19.0.0: {} redent@3.0.0: diff --git a/client/src/components/ChatMessage/ChatMessageDisplay.tsx b/client/src/components/ChatMessage/ChatMessageDisplay.tsx index c926c80..0602ac3 100644 --- a/client/src/components/ChatMessage/ChatMessageDisplay.tsx +++ b/client/src/components/ChatMessage/ChatMessageDisplay.tsx @@ -59,7 +59,7 @@ function ChatMessageDisplay({ ) : ( <>
diff --git a/client/src/components/MessageDisplay.tsx b/client/src/components/MessageDisplay.tsx index 784db63..a92bfe4 100644 --- a/client/src/components/MessageDisplay.tsx +++ b/client/src/components/MessageDisplay.tsx @@ -1,10 +1,9 @@ import React, { useEffect, useRef } from "react"; -import { FixedSizeList as List } from "react-window"; -import AutoSizer from "react-virtualized-auto-sizer"; import { ClientDeleteMessage, ClientEditMessage, ClientMessage, + ServerMessage, } from "../../../shared"; import { useChatState } from "../contexts/ChatContext"; import ChatMessageDisplay from "./ChatMessage/ChatMessageDisplay"; @@ -16,7 +15,10 @@ interface MessageDisplayProps { function MessageDisplay({ sendMessage }: MessageDisplayProps) { const { state } = useChatState(); const userId = state.currentUser?.userId; - const listRef = useRef(null); + const messagesEndRef = useRef(null); // ref to an empty div at the end to scroll to + + // this counter is used to generate unique keys for join/leave messages. This is not ideal and with more time I'd introduce server-side message IDs for all messages, not just chat messages + let messageCounter = 0; const onDelete = (messageId: string) => { const deleteMessage: ClientDeleteMessage = { @@ -38,66 +40,52 @@ function MessageDisplay({ sendMessage }: MessageDisplayProps) { }; useEffect(() => { - if (listRef.current) { - // Scroll to bottom when messages change - listRef.current.scrollToItem(state.messages.length - 1); - } + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); + }; + + scrollToBottom(); }, [state.messages]); - const Row = ({ - index, - style, - }: { - index: number; - style: React.CSSProperties; - }) => { - const message = state.messages[index]; - + function renderMessage(message: ServerMessage) { switch (message.type) { case "CHAT_MESSAGE": return ( -
- -
+ ); case "USER_JOINED": return ( -
+
{message.payload.user.name} joined!
); case "USER_LEFT": return ( -
+
{message.payload.user.name} left!
); default: return null; } - }; + } return ( -
- - {({ height, width }) => ( - - {Row} - - )} - +
+ {state.messages.map(renderMessage)} +
); }