feat: add top zappers to the chat

This commit is contained in:
Alejandro Gomez 2023-06-24 12:41:21 +02:00
parent 4ecba2f0d7
commit 018022689e
No known key found for this signature in database
GPG Key ID: 4DF39E566658C817
2 changed files with 101 additions and 3 deletions

View File

@ -12,7 +12,7 @@
font-weight: 600;
font-size: 24px;
line-height: 30px;
padding: 0px 0px 16px;
padding: 0px 0px 4px;
}
.live-chat>.messages {
@ -75,3 +75,42 @@
align-items: center;
gap: 8px;
}
.top-zappers h3 {
margin: 0;
text-transform: uppercase;
font-size: 12px;
font-weight: 300;
color: #EEE;
}
.top-zappers-container {
display: flex;
justify-content: space-between;
}
.top-zapper {
display: flex;
flex-direction: row;
align-items: center;
}
.top-zapper .top-zapper-amount {
margin: 0 8px 0 4px;
}
.top-zapper .top-zapper-name {
font-size: 14px;
}
.zapper-gold-icon {
color: #FFD700;
}
.zapper-silver-icon {
color: #C0C0C0;
}
.zapper-bronze-icon {
color: #CD7F32;
}

View File

@ -4,9 +4,10 @@ import {
NostrLink,
TaggedRawEvent,
EventPublisher,
ParsedZap,
parseZap,
} from "@snort/system";
import { useState, type KeyboardEvent, type ChangeEvent } from "react";
import { useState, useMemo, type KeyboardEvent, type ChangeEvent } from "react";
import useEmoji from "hooks/emoji";
import { System } from "index";
@ -26,6 +27,54 @@ export interface LiveChatOptions {
showHeader?: boolean;
}
function totalZapped(pubkey: string, zaps: ParsedZap[]) {
return zaps
.filter((z) => (z.anonZap ? pubkey === "anon" : z.sender === pubkey))
.reduce((acc, z) => acc + z.amount, 0);
}
function TopZappers({ zaps }: { zaps: ParsedZap[] }) {
const zappers = zaps
.map((z) => (z.anonZap ? "anon" : z.sender))
.map((p) => p as string);
const sortedZappers = useMemo(() => {
const sorted = [...new Set([...zappers])];
sorted.sort((a, b) => totalZapped(b, zaps) - totalZapped(a, zaps));
return sorted.slice(0, 3);
}, [zappers]);
return (
<>
<h3>Top zappers</h3>
<div className="top-zappers-container">
{sortedZappers.map((pk, idx) => {
const total = totalZapped(pk, zaps);
const iconClass =
idx === 0
? "zapper-gold-icon"
: idx === 1
? "zapper-silver-icon"
: "zapper-bronze-icon";
return (
<div className="top-zapper" key={pk}>
<Icon name="zap" className={iconClass} />
<p className="top-zapper-amount">{formatSats(total)}</p>
<div className="top-zapper-name">
{pk === "anon" ? (
<p>Anon</p>
) : (
<Profile pubkey={pk} options={{ showName: false }} />
)}
</div>
</div>
);
})}
</div>
</>
);
}
export function LiveChat({
link,
options,
@ -35,11 +84,21 @@ export function LiveChat({
}) {
const messages = useLiveChatFeed(link);
const login = useLogin();
const events = messages.data ?? [];
const zaps = events
.filter((ev) => ev.kind === EventKind.ZapReceipt)
.map((ev) => parseZap(ev, System.ProfileLoader.Cache))
.filter((z) => z);
return (
<div className="live-chat">
{(options?.showHeader ?? true) && (
<div className="header">Stream Chat</div>
)}
{zaps.length > 0 && (
<div className="top-zappers">
<TopZappers zaps={zaps} />
</div>
)}
<div className="messages">
{[...(messages.data ?? [])]
.sort((a, b) => b.created_at - a.created_at)
@ -93,7 +152,7 @@ function ChatZap({ ev }: { ev: TaggedRawEvent }) {
pubkey={parsed.anonZap ? "" : parsed.sender ?? ""}
options={{
showAvatar: !parsed.anonZap,
overrideName: parsed.anonZap ? "Anonymous" : undefined,
overrideName: parsed.anonZap ? "Anon" : undefined,
}}
/>
zapped &nbsp;