Don't allow multiple sets of actions open

remotes/1693176582716663532/tmp_refs/heads/watchparty
Outrun Colors 2022-09-25 14:52:45 -05:00
parent fbcd2e9f74
commit 249d510f23
No known key found for this signature in database
GPG Key ID: 0426976DCEFE6073
5 changed files with 89 additions and 30 deletions

View File

@ -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",

View File

@ -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);
}

View File

@ -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 (
<div
@ -71,15 +71,15 @@ export function ChatMessage({
})}
id={id}
>
{!showingActions && (
{!actionsOpen && (
<button
className="btn btn-secondary ChatMessage-actions-button"
onClick={toggleMessageActions}
onClick={() => onToggleActions(id)}
>
...
</button>
)}
{showingActions && (
{actionsOpen && (
<div className="ChatMessage-actions">
<button
className="btn btn-secondary ChatMessage-button"
@ -100,7 +100,7 @@ export function ChatMessage({
)}
<button
className="btn btn-secondary ChatMessage-button"
onClick={toggleMessageActions}
onClick={() => onToggleActions(id)}
>
<i>X</i> Close
</button>
@ -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 (
<div className="ChatMessageList">
{messages.map((message, index) => (
<ChatMessage
key={key(message)}
message={message}
timestampUpdates={timestampUpdates}
showUser={message.username !== messages[index - 1]?.username}
/>
{groupedMessages.map((group) => (
<div key={key(group)} className="ChatMessageList-group">
{group.map((message, index) => (
<ChatMessage
key={key(message)}
message={message}
timestampUpdates={timestampUpdates}
showUser={index === 0}
actionsOpen={actionsOpenForMessage === message.id}
onToggleActions={handleToggleActionsForMessage}
/>
))}
</div>
))}
</div>
);
@ -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;
}

View File

@ -42,7 +42,7 @@ export function QuotedMessageLink({ message }: { message: IChatMessage }) {
return (
<a className="QuotedMessageLink" href="#" onClick={handleLinkClick}>
Replying to @{message.username}:{" "}
<i className="fas fa-reply" /> @{message.username}:{" "}
<em>"{replyText}"</em>
</a>
);

View File

@ -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"