mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-10 12:49:29 +02:00
show top zappers on stream
This commit is contained in:
parent
85d6a2a098
commit
677bf684e7
@ -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}
|
||||
|
@ -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"
|
||||
|
@ -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}
|
||||
>
|
||||
|
35
src/views/streams/stream/stream-chat/top-zappers.tsx
Normal file
35
src/views/streams/stream/stream-chat/top-zappers.tsx
Normal 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>
|
||||
);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user