show top zappers on stream

This commit is contained in:
hzrd149 2023-07-04 12:55:38 -05:00
parent 85d6a2a098
commit 677bf684e7
4 changed files with 86 additions and 20 deletions

View File

@ -1,4 +1,4 @@
import { Button, ButtonProps, useDisclosure } from "@chakra-ui/react";
import { Button, ButtonProps, IconButton, useDisclosure } from "@chakra-ui/react";
import { readablizeSats } from "../../helpers/bolt11";
import { totalZaps } from "../../helpers/zaps";
import { useCurrentAccount } from "../../hooks/use-current-account";
@ -31,19 +31,33 @@ export default function NoteZapButton({
eventZapsService.requestZaps(note.id, clientRelaysService.getReadUrls(), true);
};
const total = totalZaps(zaps);
return (
<>
<Button
leftIcon={<LightningIcon color="yellow.500" />}
aria-label="Zap Note"
title="Zap Note"
colorScheme={hasZapped ? "brand" : undefined}
{...props}
onClick={onOpen}
isDisabled={!metadata?.allowsNostr}
>
{readablizeSats(totalZaps(zaps) / 1000)}
</Button>
{total > 0 ? (
<Button
leftIcon={<LightningIcon />}
aria-label="Zap Note"
title="Zap Note"
colorScheme={hasZapped ? "brand" : undefined}
{...props}
onClick={onOpen}
isDisabled={!metadata?.allowsNostr}
>
{readablizeSats(total / 1000)}
</Button>
) : (
<IconButton
icon={<LightningIcon />}
aria-label="Zap Note"
title="Zap Note"
{...props}
onClick={onOpen}
isDisabled={!metadata?.allowsNostr}
/>
)}
{isOpen && (
<ZapModal
isOpen={isOpen}

View File

@ -44,12 +44,14 @@ function StreamPage({ stream, displayMode }: { stream: ParsedStream; displayMode
return (
<ButtonGroup>
{isMobile && toggleButton}
<CopyIconButton
text={location.href + "?displayMode=log&colorMode=dark"}
aria-label="Copy chat log URL"
title="Copy chat log URL"
size="sm"
/>
{!isMobile && (
<CopyIconButton
text={location.href + "?displayMode=log&colorMode=dark"}
aria-label="Copy chat log URL"
title="Copy chat log URL"
size="sm"
/>
)}
<Button
rightIcon={<ExternalLinkIcon />}
size="sm"

View File

@ -1,4 +1,4 @@
import { useRef } from "react";
import { useMemo, useRef } from "react";
import {
Box,
Button,
@ -36,6 +36,9 @@ import useSubject from "../../../../hooks/use-subject";
import { useTimelineLoader } from "../../../../hooks/use-timeline-loader";
import { truncatedId } from "../../../../helpers/nostr-event";
import { css } from "@emotion/react";
import TopZappers from "./top-zappers";
import { Kind } from "nostr-tools";
import { parseZapEvent } from "../../../../helpers/zaps";
const hideScrollbar = css`
scrollbar-width: 0;
@ -67,6 +70,16 @@ export default function StreamChat({
const events = useSubject(timeline.timeline).sort((a, b) => b.created_at - a.created_at);
const zaps = useMemo(() => {
const parsed = [];
for (const event of events) {
try {
parsed.push(parseZapEvent(event));
} catch (e) {}
}
return parsed;
}, [events]);
const scrollBox = useRef<HTMLDivElement | null>(null);
const callback = useTimelineCurserIntersectionCallback(timeline);
@ -104,7 +117,8 @@ export default function StreamChat({
{actions}
</CardHeader>
)}
<CardBody display="flex" flexDirection="column" gap="2" overflow="hidden" p={0}>
<CardBody display="flex" flexDirection="column" overflow="hidden" p={0}>
<TopZappers zaps={zaps} pt={!isPopup ? 0 : undefined} />
<Flex
overflowY="scroll"
overflowX="hidden"
@ -113,6 +127,7 @@ export default function StreamChat({
flex={1}
px="4"
py="2"
mb="2"
gap="2"
css={isChatLog && hideScrollbar}
>

View File

@ -0,0 +1,35 @@
import { Box, Flex, FlexProps, Text } from "@chakra-ui/react";
import { ParsedZap } from "../../../../helpers/zaps";
import { UserAvatar } from "../../../../components/user-avatar";
import { UserLink } from "../../../../components/user-link";
import { LightningIcon } from "../../../../components/icons";
import { readablizeSats } from "../../../../helpers/bolt11";
export default function TopZappers({ zaps, ...props }: FlexProps & { zaps: ParsedZap[] }) {
const totals: Record<string, number> = {};
for (const zap of zaps) {
const p = zap.request.pubkey;
if (zap.payment.amount) {
totals[p] = (totals[p] || 0) + zap.payment.amount;
}
}
const sortedTotals = Array.from(Object.entries(totals)).sort((a, b) => b[1] - a[1]);
return (
<Flex overflowX="auto" overflowY="hidden" gap="4" py="2" px="4" {...props}>
{sortedTotals.map(([pubkey, total]) => (
<Flex key={pubkey} gap="2" alignItems="center" maxW="sm">
<UserAvatar pubkey={pubkey} size="sm" noProxy />
<Box>
<UserLink pubkey={pubkey} isTruncated fontWeight="bold" />
<Text whiteSpace="nowrap">
<LightningIcon />
{readablizeSats(total / 1000)}
</Text>
</Box>
</Flex>
))}
</Flex>
);
}