diff --git a/chat/package.json b/chat/package.json
index b83d3f6b60..0e9502ae3d 100644
--- a/chat/package.json
+++ b/chat/package.json
@@ -1,10 +1,11 @@
{
"name": "chat",
- "version": "0.0.24",
+ "version": "0.1.0",
"main": "./src/index.tsx",
"license": "MIT",
"dependencies": {
"@types/humanize-duration": "^3.27.1",
+ "@types/lodash.clonedeep": "^4.5.7",
"@types/lodash.debounce": "^4.0.7",
"@types/lozad": "^1.16.1",
"@types/react": "^18.0.20",
@@ -15,6 +16,7 @@
"dotenv": "^16.0.2",
"esbuild": "^0.15.7",
"humanize-duration": "^3.27.3",
+ "lodash.clonedeep": "^4.5.0",
"lodash.debounce": "^4.0.8",
"lozad": "^1.16.0",
"react": "^18.2.0",
diff --git a/chat/src/features/chat/ChatMessage.css b/chat/src/features/chat/ChatMessage.css
index d6c445d31a..55a112fdb7 100644
--- a/chat/src/features/chat/ChatMessage.css
+++ b/chat/src/features/chat/ChatMessage.css
@@ -18,14 +18,6 @@
margin: 0;
}
-.ChatMessage__showingUser:not(:first-child) {
- margin-top: 2rem;
-}
-
-.ChatMessage:not(.ChatMessage__showingUser) {
- padding-bottom: 0;
-}
-
.ChatMessage .btn {
border: none !important;
}
@@ -102,4 +94,14 @@
.ChatMessageList {
flex: 1;
padding-bottom: 2rem;
+}
+
+.ChatMessageList-group {
+ margin-bottom: 1rem;
+ padding: 0.3rem;
+ border-radius: 8px;
+}
+
+.ChatMessageList-group:nth-child(even) {
+ background: rgba(255, 255, 255, 0.025);
}
\ No newline at end of file
diff --git a/chat/src/features/chat/ChatMessage.tsx b/chat/src/features/chat/ChatMessage.tsx
index b4c6678584..135cfda277 100644
--- a/chat/src/features/chat/ChatMessage.tsx
+++ b/chat/src/features/chat/ChatMessage.tsx
@@ -2,6 +2,7 @@ import React, { useCallback, useEffect, useMemo, useState } from "react";
import cx from "classnames";
import key from "weak-key";
import humanizeDuration from "humanize-duration";
+import cloneDeep from "lodash.clonedeep";
import { Username } from "./Username";
import { useChat, useRootContext } from "../../hooks";
import { QuotedMessageLink } from "./QuotedMessageLink";
@@ -11,6 +12,8 @@ interface ChatMessageProps {
message: IChatMessage;
timestampUpdates: number;
showUser?: boolean;
+ actionsOpen: boolean;
+ onToggleActions(messageId: string): void;
}
const TIMESTAMP_UPDATE_INTERVAL = 20000;
@@ -19,6 +22,8 @@ export function ChatMessage({
message,
showUser = true,
timestampUpdates,
+ actionsOpen,
+ onToggleActions
}: ChatMessageProps) {
const {
id,
@@ -36,7 +41,6 @@ export function ChatMessage({
const { messageLookup, deleteMessage, quoteMessage } = useChat();
const quotedMessage = messageLookup[quotes];
const content = censored ? text_censored : text_html;
- const [showingActions, setShowingActions] = useState(false);
const [confirmedDelete, setConfirmedDelete] = useState(false);
const timestamp = useMemo(
() => formatTimeAgo(time),
@@ -49,20 +53,16 @@ export function ChatMessage({
setConfirmedDelete(true);
}
}, [text, confirmedDelete]);
- const toggleMessageActions = useCallback(
- () => setShowingActions((prev) => !prev),
- []
- );
const handleQuoteMessage = useCallback(() => {
quoteMessage(message);
- setShowingActions(false);
- }, [message]);
+ onToggleActions(message.id);
+ }, [message, onToggleActions]);
useEffect(() => {
- if (!showingActions) {
+ if (!actionsOpen) {
setConfirmedDelete(false);
}
- }, [showingActions]);
+ }, [actionsOpen]);
return (
- {!showingActions && (
+ {!actionsOpen && (
)}
- {showingActions && (
+ {actionsOpen && (
@@ -136,6 +136,17 @@ export function ChatMessage({
export function ChatMessageList() {
const { messages } = useChat();
const [timestampUpdates, setTimestampUpdates] = useState(0);
+ const groupedMessages = useMemo(() => groupMessages(messages), [messages]);
+ const [actionsOpenForMessage, setActionsOpenForMessage] = useState<
+ string | null
+ >(null);
+ const handleToggleActionsForMessage = useCallback(
+ (messageId: string) =>
+ setActionsOpenForMessage(
+ messageId === actionsOpenForMessage ? null : messageId
+ ),
+ [actionsOpenForMessage]
+ );
useEffect(() => {
const updatingTimestamps = setInterval(
@@ -150,13 +161,19 @@ export function ChatMessageList() {
return (
- {messages.map((message, index) => (
-
+ {groupedMessages.map((group) => (
+
+ {group.map((message, index) => (
+
+ ))}
+
))}
);
@@ -188,3 +205,29 @@ function formatTimeAgo(time: number) {
return humanized === "0s ago" ? "just now" : humanized;
}
+
+function groupMessages(messages: IChatMessage[]) {
+ const grouped: IChatMessage[][] = [];
+ let lastUsername = "";
+ let temp: IChatMessage[] = [];
+
+ for (const message of messages) {
+ if (!lastUsername) {
+ lastUsername = message.username;
+ }
+
+ if (message.username === lastUsername) {
+ temp.push(message);
+ } else {
+ grouped.push(cloneDeep(temp));
+ lastUsername = message.username;
+ temp = [message];
+ }
+ }
+
+ if (temp.length > 0) {
+ grouped.push(cloneDeep(temp));
+ }
+
+ return grouped;
+}
diff --git a/chat/src/features/chat/QuotedMessageLink.tsx b/chat/src/features/chat/QuotedMessageLink.tsx
index 46ee0f546d..1e598a4369 100644
--- a/chat/src/features/chat/QuotedMessageLink.tsx
+++ b/chat/src/features/chat/QuotedMessageLink.tsx
@@ -42,7 +42,7 @@ export function QuotedMessageLink({ message }: { message: IChatMessage }) {
return (
- Replying to @{message.username}:{" "}
+ @{message.username}:{" "}
"{replyText}"
);
diff --git a/chat/yarn.lock b/chat/yarn.lock
index 2555ae9355..98721ce974 100644
--- a/chat/yarn.lock
+++ b/chat/yarn.lock
@@ -39,6 +39,13 @@
resolved "https://registry.yarnpkg.com/@types/humanize-duration/-/humanize-duration-3.27.1.tgz#f14740d1f585a0a8e3f46359b62fda8b0eaa31e7"
integrity sha512-K3e+NZlpCKd6Bd/EIdqjFJRFHbrq5TzPPLwREk5Iv/YoIjQrs6ljdAUCo+Lb2xFlGNOjGSE0dqsVD19cZL137w==
+"@types/lodash.clonedeep@^4.5.7":
+ version "4.5.7"
+ resolved "https://registry.yarnpkg.com/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.7.tgz#0e119f582ed6f9e6b373c04a644651763214f197"
+ integrity sha512-ccNqkPptFIXrpVqUECi60/DFxjNKsfoQxSQsgcBJCX/fuX1wgyQieojkcWH/KpE3xzLoWN/2k+ZeGqIN3paSvw==
+ dependencies:
+ "@types/lodash" "*"
+
"@types/lodash.debounce@^4.0.7":
version "4.0.7"
resolved "https://registry.yarnpkg.com/@types/lodash.debounce/-/lodash.debounce-4.0.7.tgz#0285879defb7cdb156ae633cecd62d5680eded9f"
@@ -390,6 +397,11 @@ inherits@2:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+lodash.clonedeep@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
+ integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==
+
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"