Change how chat actions look
parent
c51c7c91b6
commit
96bf16484c
|
@ -37,6 +37,7 @@ body {
|
|||
|
||||
.App-heading small {
|
||||
opacity: 0.2;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.App-side {
|
||||
|
@ -122,4 +123,9 @@ lite-youtube {
|
|||
|
||||
.btn-secondary {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.btn-secondary:focus {
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
|
@ -10,8 +10,20 @@
|
|||
.ChatMessage {
|
||||
position: relative;
|
||||
animation: fading-in 0.3s ease-in-out forwards;
|
||||
padding: 1rem;
|
||||
padding-right: 3rem;
|
||||
padding-right: 1.5rem;
|
||||
min-height: 28px;
|
||||
}
|
||||
|
||||
.ChatMessage p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ChatMessage__showingUser:not(:first-child) {
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.ChatMessage:not(.ChatMessage__showingUser) {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.ChatMessage .btn {
|
||||
|
@ -25,6 +37,8 @@
|
|||
|
||||
.ChatMessage-timestamp {
|
||||
margin-left: 0.5rem;
|
||||
opacity: 0.5;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.ChatMessage-bottom {
|
||||
|
@ -41,20 +55,51 @@
|
|||
}
|
||||
|
||||
.ChatMessage-button {
|
||||
background: transparent !important;
|
||||
margin: 0 0.5rem;
|
||||
}
|
||||
|
||||
.ChatMessage-button__confirmed i {
|
||||
.ChatMessage-button i {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.ChatMessage-button__confirmed {
|
||||
color: red !important;
|
||||
}
|
||||
|
||||
.ChatMessage-delete {
|
||||
.ChatMessage-actions-button {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
top: 0;
|
||||
right: 0;
|
||||
cursor: pointer;
|
||||
z-index: 5;
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.ChatMessage-actions {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background: rgba(20, 20, 20, 0.85);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
padding: 1rem;
|
||||
padding-right: 3rem;
|
||||
animation: fading-in 0.3s ease-in-out forwards;
|
||||
}
|
||||
|
||||
.ChatMessage-actions button {
|
||||
font-size: 10px;
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
/* List */
|
||||
.ChatMessageList {
|
||||
flex: 1;
|
||||
padding-bottom: 2rem;
|
||||
}
|
|
@ -1,9 +1,4 @@
|
|||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import cx from "classnames";
|
||||
import key from "weak-key";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
|
@ -37,20 +32,16 @@ export function ChatMessage({
|
|||
time,
|
||||
quotes,
|
||||
} = message;
|
||||
const {
|
||||
username: loggedInUsername,
|
||||
admin,
|
||||
censored,
|
||||
themeColor,
|
||||
} = useRootContext();
|
||||
const { admin, censored } = useRootContext();
|
||||
const { messageLookup, deleteMessage, quoteMessage } = useChat();
|
||||
const quotedMessage = messageLookup[quotes];
|
||||
const content = censored ? text_censored : text_html;
|
||||
const hasMention = content.includes(loggedInUsername);
|
||||
const mentionStyle = hasMention
|
||||
? { backgroundColor: `#${themeColor}55` }
|
||||
: {};
|
||||
const [showingActions, setShowingActions] = useState(false);
|
||||
const [confirmedDelete, setConfirmedDelete] = useState(false);
|
||||
const timestamp = useMemo(
|
||||
() => formatTimeAgo(time),
|
||||
[time, timestampUpdates]
|
||||
);
|
||||
const handleDeleteMessage = useCallback(() => {
|
||||
if (confirmedDelete) {
|
||||
deleteMessage(text);
|
||||
|
@ -58,13 +49,63 @@ export function ChatMessage({
|
|||
setConfirmedDelete(true);
|
||||
}
|
||||
}, [text, confirmedDelete]);
|
||||
const timestamp = useMemo(
|
||||
() => formatTimeAgo(time),
|
||||
[time, timestampUpdates]
|
||||
const toggleMessageActions = useCallback(
|
||||
() => setShowingActions((prev) => !prev),
|
||||
[]
|
||||
);
|
||||
const handleQuoteMessage = useCallback(() => {
|
||||
quoteMessage(message);
|
||||
setShowingActions(false);
|
||||
}, [message]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!showingActions) {
|
||||
setConfirmedDelete(false);
|
||||
}
|
||||
}, [showingActions]);
|
||||
|
||||
return (
|
||||
<div className="ChatMessage" style={mentionStyle} id={id}>
|
||||
<div
|
||||
className={cx("ChatMessage", {
|
||||
ChatMessage__showingUser: showUser,
|
||||
})}
|
||||
id={id}
|
||||
>
|
||||
{!showingActions && (
|
||||
<button
|
||||
className="btn btn-secondary ChatMessage-actions-button"
|
||||
onClick={toggleMessageActions}
|
||||
>
|
||||
...
|
||||
</button>
|
||||
)}
|
||||
{showingActions && (
|
||||
<div className="ChatMessage-actions">
|
||||
<button
|
||||
className="btn btn-secondary ChatMessage-button"
|
||||
onClick={handleQuoteMessage}
|
||||
>
|
||||
<i className="fas fa-reply" /> Reply
|
||||
</button>
|
||||
{admin && (
|
||||
<button
|
||||
className={cx("btn btn-secondary ChatMessage-button", {
|
||||
"ChatMessage-button__confirmed": confirmedDelete,
|
||||
})}
|
||||
onClick={handleDeleteMessage}
|
||||
>
|
||||
<i className="fas fa-trash-alt" />{" "}
|
||||
{confirmedDelete ? "Are you sure?" : "Delete"}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
className="btn btn-secondary ChatMessage-button"
|
||||
onClick={toggleMessageActions}
|
||||
>
|
||||
<i>X</i> Close
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
{showUser && (
|
||||
<div className="ChatMessage-top">
|
||||
<Username
|
||||
|
@ -86,23 +127,7 @@ export function ChatMessage({
|
|||
__html: content,
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
className="ChatMessage-button quote btn"
|
||||
onClick={() => quoteMessage(message)}
|
||||
>
|
||||
<i className="fas fa-reply"></i>
|
||||
</button>
|
||||
</div>
|
||||
{admin && (
|
||||
<button
|
||||
className={cx("ChatMessage-button ChatMessage-delete btn", {
|
||||
"ChatMessage-button__confirmed": confirmedDelete,
|
||||
})}
|
||||
onClick={handleDeleteMessage}
|
||||
>
|
||||
<i className="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -138,12 +163,28 @@ export function ChatMessageList() {
|
|||
}
|
||||
|
||||
function formatTimeAgo(time: number) {
|
||||
const now = new Date().getTime();
|
||||
const humanized = `${humanizeDuration(time * 1000 - now, {
|
||||
const shortEnglishHumanizer = humanizeDuration.humanizer({
|
||||
language: "shortEn",
|
||||
languages: {
|
||||
shortEn: {
|
||||
y: () => "y",
|
||||
mo: () => "mo",
|
||||
w: () => "w",
|
||||
d: () => "d",
|
||||
h: () => "h",
|
||||
m: () => "m",
|
||||
s: () => "s",
|
||||
ms: () => "ms",
|
||||
},
|
||||
},
|
||||
round: true,
|
||||
units: ["h", "m", "s"],
|
||||
largest: 2,
|
||||
})} ago`;
|
||||
spacer: "",
|
||||
delimiter: ", ",
|
||||
});
|
||||
const now = new Date().getTime();
|
||||
const humanized = `${shortEnglishHumanizer(time * 1000 - now)} ago`;
|
||||
|
||||
return humanized === "0 seconds ago" ? "just now" : humanized;
|
||||
return humanized === "0s ago" ? "just now" : humanized;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
.QuotedMessageLink {
|
||||
font-size: 10px;
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import React, { useCallback, useMemo } from "react";
|
||||
import { useRootContext } from "../../hooks";
|
||||
import "./QuotedMessageLink.css";
|
||||
|
||||
const SCROLL_TO_QUOTED_OVERFLOW = 250;
|
||||
const QUOTED_MESSAGE_CONTEXTUAL_HIGHLIGHTING_DURATION = 2500;
|
||||
|
@ -40,7 +41,7 @@ export function QuotedMessageLink({ message }: { message: IChatMessage }) {
|
|||
}, [message, censored]);
|
||||
|
||||
return (
|
||||
<a href="#" onClick={handleLinkClick}>
|
||||
<a className="QuotedMessageLink" href="#" onClick={handleLinkClick}>
|
||||
Replying to @{message.username}:{" "}
|
||||
<em>"{replyText}"</em>
|
||||
</a>
|
||||
|
|
|
@ -47,7 +47,7 @@ export function UserInput() {
|
|||
);
|
||||
const handleKeyUp = useCallback(
|
||||
(event: KeyboardEvent<HTMLTextAreaElement>) => {
|
||||
if (event.key === "Enter") {
|
||||
if (event.key === "Enter" && !event.shiftKey) {
|
||||
handleSendMessage();
|
||||
}
|
||||
},
|
||||
|
|
Loading…
Reference in New Issue