mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-04-12 21:59:16 +02:00
small improvements to users zap tab
This commit is contained in:
parent
fdf18c8a5f
commit
4e89806203
@ -67,7 +67,9 @@ I would recomend you use a browser extension like [Alby](https://getalby.com/) o
|
||||
- [ ] [NIP-42](https://github.com/nostr-protocol/nips/blob/master/42.md): Authentication of clients to relays
|
||||
- [ ] [NIP-50](https://github.com/nostr-protocol/nips/blob/master/50.md): Keywords filter
|
||||
- [ ] [NIP-56](https://github.com/nostr-protocol/nips/blob/master/56.md): Reporting
|
||||
- [ ] [NIP-51](https://github.com/nostr-protocol/nips/blob/master/51.md): Lists (Mute, Pin, People List, and Bookmark list)
|
||||
- [x] [NIP-57](https://github.com/nostr-protocol/nips/blob/master/57.md): Lightning Zaps
|
||||
- [ ] [NIP-57](https://github.com/nostr-protocol/nips/blob/master/57.md): Badges
|
||||
- [x] [NIP-65](https://github.com/nostr-protocol/nips/blob/master/65.md): Relay List Metadata
|
||||
|
||||
## TODO
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
CardBody,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardProps,
|
||||
Flex,
|
||||
Heading,
|
||||
IconButton,
|
||||
@ -37,8 +38,9 @@ import { ExpandProvider } from "./expanded";
|
||||
export type NoteProps = {
|
||||
event: NostrEvent;
|
||||
maxHeight?: number;
|
||||
variant?: CardProps["variant"];
|
||||
};
|
||||
export const Note = React.memo(({ event, maxHeight }: NoteProps) => {
|
||||
export const Note = React.memo(({ event, maxHeight, variant = "outline" }: NoteProps) => {
|
||||
const isMobile = useIsMobile();
|
||||
const account = useCurrentAccount();
|
||||
const { openModal } = useContext(PostModalContext);
|
||||
@ -51,7 +53,7 @@ export const Note = React.memo(({ event, maxHeight }: NoteProps) => {
|
||||
|
||||
return (
|
||||
<ExpandProvider>
|
||||
<Card variant="outline">
|
||||
<Card variant={variant}>
|
||||
<CardHeader padding="2">
|
||||
<Flex flex="1" gap="2" alignItems="center" wrap="wrap">
|
||||
<UserAvatarLink pubkey={event.pubkey} size={isMobile ? "xs" : "sm"} />
|
||||
|
@ -15,7 +15,7 @@ const NoteView = () => {
|
||||
const isRoot = rootId === focusId;
|
||||
const rootPost = thread.get(rootId);
|
||||
if (isRoot && rootPost) {
|
||||
pageContent = <ThreadPost post={rootPost} initShowReplies />;
|
||||
pageContent = <ThreadPost post={rootPost} initShowReplies focusId={focusId} />;
|
||||
}
|
||||
|
||||
const post = thread.get(focusId);
|
||||
@ -34,11 +34,11 @@ const NoteView = () => {
|
||||
{parentPosts.map((parent) => (
|
||||
<Note key={parent.event.id + "-rely"} event={parent.event} maxHeight={200} />
|
||||
))}
|
||||
<ThreadPost key={post.event.id} post={post} initShowReplies />
|
||||
<ThreadPost key={post.event.id} post={post} initShowReplies focusId={focusId} />
|
||||
</>
|
||||
);
|
||||
} else if (events[focusId]) {
|
||||
pageContent = <Note event={events[focusId]} />;
|
||||
pageContent = <Note event={events[focusId]} variant="filled" />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -8,9 +8,10 @@ import { useIsMobile } from "../../hooks/use-is-mobile";
|
||||
export type ThreadItemProps = {
|
||||
post: ThreadItemData;
|
||||
initShowReplies?: boolean;
|
||||
focusId?: string;
|
||||
};
|
||||
|
||||
export const ThreadPost = ({ post, initShowReplies }: ThreadItemProps) => {
|
||||
export const ThreadPost = ({ post, initShowReplies, focusId }: ThreadItemProps) => {
|
||||
const isMobile = useIsMobile();
|
||||
const [showReplies, setShowReplies] = useState(initShowReplies ?? post.replies.length === 1);
|
||||
const toggle = () => setShowReplies((v) => !v);
|
||||
@ -19,7 +20,7 @@ export const ThreadPost = ({ post, initShowReplies }: ThreadItemProps) => {
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="2">
|
||||
<Note event={post.event} />
|
||||
<Note event={post.event} variant={focusId === post.event.id ? "filled" : "outline"} />
|
||||
{post.replies.length > 0 && (
|
||||
<>
|
||||
<Button variant="link" size="sm" alignSelf="flex-start" onClick={toggle}>
|
||||
@ -29,7 +30,7 @@ export const ThreadPost = ({ post, initShowReplies }: ThreadItemProps) => {
|
||||
{showReplies && (
|
||||
<Flex direction="column" gap="2" pl={isMobile ? 2 : 4} borderLeftColor="gray.500" borderLeftWidth="1px">
|
||||
{post.replies.map((child) => (
|
||||
<ThreadPost key={child.event.id} post={child} />
|
||||
<ThreadPost key={child.event.id} post={child} focusId={focusId} />
|
||||
))}
|
||||
</Flex>
|
||||
)}
|
||||
|
@ -21,7 +21,7 @@ import { useOutletContext } from "react-router-dom";
|
||||
import { RelayMode } from "../../classes/relay";
|
||||
import { RelayIcon } from "../../components/icons";
|
||||
import { Note } from "../../components/note";
|
||||
import { isNote } from "../../helpers/nostr-event";
|
||||
import { isNote, truncatedId } from "../../helpers/nostr-event";
|
||||
import { useReadRelayUrls } from "../../hooks/use-client-relays";
|
||||
import useFallbackUserRelays from "../../hooks/use-fallback-user-relays";
|
||||
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
|
||||
@ -41,10 +41,10 @@ const UserNotesTab = () => {
|
||||
const { isOpen: showReplies, onToggle: toggleReplies } = useDisclosure();
|
||||
|
||||
const { events, loading, loadMore } = useTimelineLoader(
|
||||
`${pubkey}-notes`,
|
||||
`${truncatedId(pubkey)}-notes`,
|
||||
relays,
|
||||
{ authors: [pubkey], kinds: [1] },
|
||||
{ pageSize: moment.duration(1, "day").asSeconds() }
|
||||
{ pageSize: moment.duration(1, "day").asSeconds(), startLimit: 20 }
|
||||
);
|
||||
const timeline = showReplies ? events : events.filter(isNote);
|
||||
|
||||
|
@ -1,18 +1,23 @@
|
||||
import { Box, Button, Flex, Spinner, Text, useDisclosure } from "@chakra-ui/react";
|
||||
import { Box, Button, Flex, Select, Spinner, Text, useDisclosure } from "@chakra-ui/react";
|
||||
import moment from "moment";
|
||||
import { useState } from "react";
|
||||
import { useOutletContext } from "react-router-dom";
|
||||
import { RelayMode } from "../../classes/relay";
|
||||
import { ErrorBoundary, ErrorFallback } from "../../components/error-boundary";
|
||||
import QuoteNote from "../../components/note/quote-note";
|
||||
import { LightningIcon } from "../../components/icons";
|
||||
import { NoteLink } from "../../components/note-link";
|
||||
import { UserAvatarLink } from "../../components/user-avatar-link";
|
||||
import { UserLink } from "../../components/user-link";
|
||||
import { readablizeSats } from "../../helpers/bolt11";
|
||||
import { convertTimestampToDate } from "../../helpers/date";
|
||||
import { isProfileZap, parseZapNote } from "../../helpers/zaps";
|
||||
import { truncatedId } from "../../helpers/nostr-event";
|
||||
import { isProfileZap, isNoteZap, parseZapNote, totalZaps } from "../../helpers/zaps";
|
||||
import { useReadRelayUrls } from "../../hooks/use-client-relays";
|
||||
import useFallbackUserRelays from "../../hooks/use-fallback-user-relays";
|
||||
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
|
||||
const ZapNote = ({ zapEvent }: { zapEvent: NostrEvent }) => {
|
||||
const Zap = ({ zapEvent }: { zapEvent: NostrEvent }) => {
|
||||
const { isOpen, onToggle } = useDisclosure();
|
||||
try {
|
||||
const { request, payment, eventId } = parseZapNote(zapEvent);
|
||||
@ -30,7 +35,14 @@ const ZapNote = ({ zapEvent }: { zapEvent: NostrEvent }) => {
|
||||
<Flex gap="2" alignItems="center" wrap="wrap">
|
||||
<UserAvatarLink pubkey={request.pubkey} size="xs" />
|
||||
<UserLink pubkey={request.pubkey} />
|
||||
{payment.amount && <Text>{readablizeSats(payment.amount / 1000)}</Text>}
|
||||
<Text>Zapped</Text>
|
||||
{eventId && <NoteLink noteId={eventId} />}
|
||||
{payment.amount && (
|
||||
<>
|
||||
<LightningIcon color="yellow.400" />
|
||||
<Text>{readablizeSats(payment.amount / 1000)} sats</Text>
|
||||
</>
|
||||
)}
|
||||
{request.content && (
|
||||
<Button variant="link" onClick={onToggle}>
|
||||
Show message
|
||||
@ -39,7 +51,6 @@ const ZapNote = ({ zapEvent }: { zapEvent: NostrEvent }) => {
|
||||
<Text ml="auto">{moment(convertTimestampToDate(request.created_at)).fromNow()}</Text>
|
||||
</Flex>
|
||||
{request.content && isOpen && <Text>{request.content}</Text>}
|
||||
{eventId && <QuoteNote noteId={eventId} />}
|
||||
</Box>
|
||||
);
|
||||
} catch (e) {
|
||||
@ -54,21 +65,44 @@ const ZapNote = ({ zapEvent }: { zapEvent: NostrEvent }) => {
|
||||
|
||||
const UserZapsTab = () => {
|
||||
const { pubkey } = useOutletContext() as { pubkey: string };
|
||||
const readRelays = useReadRelayUrls();
|
||||
const [filter, setFilter] = useState("both");
|
||||
// get user relays
|
||||
const userRelays = useFallbackUserRelays(pubkey)
|
||||
.filter((r) => r.mode & RelayMode.WRITE)
|
||||
.map((r) => r.url);
|
||||
const relays = useReadRelayUrls(userRelays);
|
||||
|
||||
const { events, loading, loadMore } = useTimelineLoader(
|
||||
`${pubkey}-zaps`,
|
||||
readRelays,
|
||||
{ "#p": [pubkey], kinds: [9735], since: moment().subtract(1, "day").unix() },
|
||||
{ pageSize: moment.duration(1, "day").asSeconds() }
|
||||
`${truncatedId(pubkey)}-zaps`,
|
||||
relays,
|
||||
{ "#p": [pubkey], kinds: [9735] },
|
||||
{ pageSize: moment.duration(1, "week").asSeconds() }
|
||||
);
|
||||
const timeline = events.filter(isProfileZap);
|
||||
|
||||
const timeline =
|
||||
filter === "note" ? events.filter(isNoteZap) : filter === "profile" ? events.filter(isProfileZap) : events;
|
||||
|
||||
return (
|
||||
<Flex direction="column" gap="2" pr="2" pl="2">
|
||||
<Flex gap="2" alignItems="center">
|
||||
<Select value={filter} onChange={(e) => setFilter(e.target.value)} maxW="lg">
|
||||
<option value="both">Note & Profile Zaps</option>
|
||||
<option value="note">Note Zaps</option>
|
||||
<option value="profile">Profile Zaps</option>
|
||||
</Select>
|
||||
{timeline.length && (
|
||||
<>
|
||||
<LightningIcon color="yellow.400" />
|
||||
<Text>
|
||||
{readablizeSats(totalZaps(timeline) / 1000)} sats in the last{" "}
|
||||
{moment(convertTimestampToDate(timeline[timeline.length - 1].created_at)).fromNow(true)}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
{timeline.map((event) => (
|
||||
<ErrorBoundary key={event.id}>
|
||||
<ZapNote zapEvent={event} />
|
||||
<Zap zapEvent={event} />
|
||||
</ErrorBoundary>
|
||||
))}
|
||||
{loading ? <Spinner ml="auto" mr="auto" mt="8" mb="8" /> : <Button onClick={() => loadMore()}>Load More</Button>}
|
||||
|
Loading…
x
Reference in New Issue
Block a user