feat: display message reactions

This commit is contained in:
Alejandro Gomez
2023-07-02 22:53:55 +02:00
parent a48991c13e
commit f40bcb0875
2 changed files with 49 additions and 12 deletions

View File

@ -222,7 +222,6 @@
.zap-pill { .zap-pill {
display: flex; display: flex;
align-items: center; align-items: center;
margin-top: 4px;
padding: 0 4px; padding: 0 4px;
justify-content: center; justify-content: center;
gap: 2px; gap: 2px;
@ -237,6 +236,18 @@
color: #FF8D2B; color: #FF8D2B;
} }
.message-reactions {
display: flex;
align-items: flex-end;
gap: 4px;
margin-top: 4px;
}
.message-reaction {
font-size: 16px;
line-height: 18px;
}
.zap-pill-amount { .zap-pill-amount {
color: #FFF; color: #FFF;
font-size: 12px; font-size: 12px;

View File

@ -33,6 +33,7 @@ import { useUserProfile } from "@snort/system-react";
import { formatSats } from "number"; import { formatSats } from "number";
import useTopZappers from "hooks/top-zappers"; import useTopZappers from "hooks/top-zappers";
import { LIVE_STREAM_CHAT } from "const"; import { LIVE_STREAM_CHAT } from "const";
import { findTag } from "utils";
export interface LiveChatOptions { export interface LiveChatOptions {
canWrite?: boolean; canWrite?: boolean;
@ -79,10 +80,6 @@ export function LiveChat({
.filter((ev) => ev.kind === EventKind.ZapReceipt) .filter((ev) => ev.kind === EventKind.ZapReceipt)
.map((ev) => parseZap(ev, System.ProfileLoader.Cache)) .map((ev) => parseZap(ev, System.ProfileLoader.Cache))
.filter((z) => z && z.valid); .filter((z) => z && z.valid);
const reactions = feed.reactions
.filter((ev) => ev.kind === EventKind.ZapReceipt)
.map((ev) => parseZap(ev, System.ProfileLoader.Cache))
.filter((z) => z && z.valid);
const events = useMemo(() => { const events = useMemo(() => {
return [...feed.messages, ...feed.zaps].sort( return [...feed.messages, ...feed.zaps].sort(
(a, b) => b.created_at - a.created_at (a, b) => b.created_at - a.created_at
@ -109,7 +106,7 @@ export function LiveChat({
ev={a} ev={a}
link={link} link={link}
key={a.id} key={a.id}
reactions={reactions} reactions={feed.reactions}
/> />
); );
} }
@ -134,6 +131,16 @@ export function LiveChat({
); );
} }
function emojifyReaction(reaction: string) {
if (reaction === "+") {
return "💜";
}
if (reaction === "-") {
return "👎";
}
return reaction;
}
function ChatMessage({ function ChatMessage({
streamer, streamer,
ev, ev,
@ -143,16 +150,28 @@ function ChatMessage({
streamer: string; streamer: string;
ev: TaggedRawEvent; ev: TaggedRawEvent;
link: NostrLink; link: NostrLink;
reactions: ParsedZap[]; reactions: readonly TaggedRawEvent[];
}) { }) {
const ref = useRef(null); const ref = useRef(null);
const isHovering = useHover(ref); const isHovering = useHover(ref);
const profile = useUserProfile(System, ev.pubkey); const profile = useUserProfile(System, ev.pubkey);
const zapTarget = profile?.lud16 ?? profile?.lud06; const zapTarget = profile?.lud16 ?? profile?.lud06;
const zaps = reactions
.filter((ev) => ev.kind === EventKind.ZapReceipt)
.map((ev) => parseZap(ev, System.ProfileLoader.Cache))
.filter((z) => z && z.valid);
const emojis = useMemo(() => {
const emojified = reactions
.filter((e) => e.kind === EventKind.Reaction && findTag(e, "e") === ev.id)
.map((ev) => emojifyReaction(ev.content));
return [...new Set(emojified)];
}, [ev, reactions]);
const hasReactions = emojis.length > 0;
const totalZaps = useMemo(() => { const totalZaps = useMemo(() => {
const messageZaps = reactions.filter((z) => z.event === ev.id); const messageZaps = zaps.filter((z) => z.event === ev.id);
return messageZaps.reduce((acc, z) => acc + z.amount, 0); return messageZaps.reduce((acc, z) => acc + z.amount, 0);
}, [reactions, ev]); }, [reactions, ev]);
const hasZaps = totalZaps > 0;
return ( return (
<> <>
<div <div
@ -188,12 +207,19 @@ function ChatMessage({
)} )}
<Profile pubkey={ev.pubkey} /> <Profile pubkey={ev.pubkey} />
<Text content={ev.content} tags={ev.tags} /> <Text content={ev.content} tags={ev.tags} />
{totalZaps !== 0 && ( {(hasReactions || hasZaps) && (
<div className="message-reactions">
{hasZaps && (
<div className="zap-pill"> <div className="zap-pill">
<Icon name="zap-filled" className="zap-pill-icon" /> <Icon name="zap-filled" className="zap-pill-icon" />
<span className="zap-pill-amount">{formatSats(totalZaps)}</span> <span className="zap-pill-amount">{formatSats(totalZaps)}</span>
</div> </div>
)} )}
{emojis.map((e) => (
<span className="message-reaction">{e}</span>
))}
</div>
)}
</div> </div>
</> </>
); );