mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-03-26 17:52:18 +01:00
add option to quote zap receipts
This commit is contained in:
parent
5b842081a3
commit
7a486bbe4f
5
.changeset/silver-dogs-kick.md
Normal file
5
.changeset/silver-dogs-kick.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"nostrudel": minor
|
||||
---
|
||||
|
||||
Add menu to zap events
|
@ -3,10 +3,10 @@ import dayjs from "dayjs";
|
||||
|
||||
import { logger } from "../helpers/debug";
|
||||
import { safeRelayUrl, validateRelayURL } from "../helpers/relay";
|
||||
import { offlineMode } from "../services/offline-mode";
|
||||
import Subject, { PersistentSubject } from "./subject";
|
||||
import verifyEventMethod from "../services/verify-event";
|
||||
import SuperMap from "./super-map";
|
||||
import verifyEventMethod from "../services/verify-event";
|
||||
import { offlineMode } from "../services/offline-mode";
|
||||
import processManager from "../services/process-manager";
|
||||
import signingService from "../services/signing";
|
||||
import accountService from "../services/account";
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Observable from "zen-observable";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
import ControlledObservable from "./controlled-observable";
|
||||
|
||||
/** An observable that is always open and stores the last value */
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { MenuItem } from "@chakra-ui/react";
|
||||
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import { getSharableEventAddress } from "../../helpers/nip19";
|
||||
import { CopyToClipboardIcon } from "../icons";
|
||||
import relayHintService from "../../services/event-relay-hint";
|
||||
|
||||
export default function CopyEmbedCodeMenuItem({ event }: { event: NostrEvent }) {
|
||||
const address = getSharableEventAddress(event);
|
||||
const address = relayHintService.getSharableEventAddress(event);
|
||||
|
||||
return (
|
||||
address && (
|
||||
|
@ -1,13 +1,13 @@
|
||||
import { useCallback, useContext, useMemo } from "react";
|
||||
import { MenuItem } from "@chakra-ui/react";
|
||||
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import { ExternalLinkIcon } from "../icons";
|
||||
import { getSharableEventAddress } from "../../helpers/nip19";
|
||||
import { useCallback, useContext, useMemo } from "react";
|
||||
import { AppHandlerContext } from "../../providers/route/app-handler-provider";
|
||||
import relayHintService from "../../services/event-relay-hint";
|
||||
|
||||
export default function OpenInAppMenuItem({ event }: { event: NostrEvent }) {
|
||||
const address = useMemo(() => getSharableEventAddress(event), [event]);
|
||||
const address = useMemo(() => relayHintService.getSharableEventAddress(event), [event]);
|
||||
const { openAddress } = useContext(AppHandlerContext);
|
||||
const open = useCallback(() => address && openAddress(address), [address, openAddress]);
|
||||
|
||||
|
27
src/components/common-menu-items/quote-event.tsx
Normal file
27
src/components/common-menu-items/quote-event.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { useCallback, useContext, useMemo } from "react";
|
||||
import { MenuItem, useToast } from "@chakra-ui/react";
|
||||
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import { QuoteEventIcon } from "../icons";
|
||||
import useUserMetadata from "../../hooks/use-user-metadata";
|
||||
import { PostModalContext } from "../../providers/route/post-modal-provider";
|
||||
import relayHintService from "../../services/event-relay-hint";
|
||||
|
||||
export default function QuoteEventMenuItem({ event }: { event: NostrEvent }) {
|
||||
const toast = useToast();
|
||||
const address = useMemo(() => relayHintService.getSharableEventAddress(event), [event]);
|
||||
const metadata = useUserMetadata(event.pubkey);
|
||||
const { openModal } = useContext(PostModalContext);
|
||||
|
||||
const share = useCallback(async () => {
|
||||
openModal({ cacheFormKey: null, initContent: "\nnostr:" + address });
|
||||
}, [metadata, event, toast, address]);
|
||||
|
||||
return (
|
||||
address && (
|
||||
<MenuItem onClick={share} icon={<QuoteEventIcon />}>
|
||||
Quote Event
|
||||
</MenuItem>
|
||||
)
|
||||
);
|
||||
}
|
@ -1,22 +1,18 @@
|
||||
import { useCallback } from "react";
|
||||
import { MenuItem, useToast } from "@chakra-ui/react";
|
||||
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import { getSharableEventAddress } from "../../helpers/nip19";
|
||||
import { ShareIcon } from "../icons";
|
||||
import { Signature } from "@noble/secp256k1";
|
||||
import { descriptors } from "chart.js/dist/core/core.defaults";
|
||||
import useUserMetadata from "../../hooks/use-user-metadata";
|
||||
import { useCallback } from "react";
|
||||
import { getDisplayName } from "../../helpers/nostr/user-metadata";
|
||||
|
||||
let urlShareFailed = false;
|
||||
import useShareableEventAddress from "../../hooks/use-shareable-event-address";
|
||||
|
||||
export default function ShareLinkMenuItem({ event }: { event: NostrEvent }) {
|
||||
const toast = useToast();
|
||||
const address = getSharableEventAddress(event);
|
||||
const address = useShareableEventAddress(event);
|
||||
const metadata = useUserMetadata(event.pubkey);
|
||||
|
||||
const share = useCallback(async () => {
|
||||
const handleClick = useCallback(async () => {
|
||||
const data: ShareData = {
|
||||
url: "https://njump.me/" + address,
|
||||
title: event.tags.find((t) => t[0] === "title")?.[1] || "Nostr note by " + getDisplayName(metadata, event.pubkey),
|
||||
@ -40,7 +36,7 @@ export default function ShareLinkMenuItem({ event }: { event: NostrEvent }) {
|
||||
|
||||
return (
|
||||
address && (
|
||||
<MenuItem onClick={share} icon={<ShareIcon />}>
|
||||
<MenuItem onClick={handleClick} icon={<ShareIcon />}>
|
||||
Share Link
|
||||
</MenuItem>
|
||||
)
|
||||
|
@ -5,10 +5,7 @@ import {
|
||||
ModalContent,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
Flex,
|
||||
Button,
|
||||
Heading,
|
||||
Text,
|
||||
AccordionItem,
|
||||
Accordion,
|
||||
AccordionPanel,
|
||||
@ -18,21 +15,17 @@ import {
|
||||
ModalHeader,
|
||||
Code,
|
||||
AccordionPanelProps,
|
||||
Card,
|
||||
} from "@chakra-ui/react";
|
||||
import { ModalProps } from "@chakra-ui/react";
|
||||
import { nip19 } from "nostr-tools";
|
||||
|
||||
import { getContentTagRefs, getEventUID, getThreadReferences } from "../../helpers/nostr/event";
|
||||
import { getContentTagRefs, getThreadReferences } from "../../helpers/nostr/event";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import RawValue from "./raw-value";
|
||||
import { getSharableEventAddress } from "../../helpers/nip19";
|
||||
import { usePublishEvent } from "../../providers/global/publish-provider";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import { getEventRelays } from "../../services/event-relays";
|
||||
import { RelayFavicon } from "../relay-favicon";
|
||||
import { CopyIconButton } from "../copy-icon-button";
|
||||
import DebugEventTags from "./event-tags";
|
||||
import relayHintService from "../../services/event-relay-hint";
|
||||
|
||||
function Section({
|
||||
label,
|
||||
@ -75,8 +68,6 @@ export default function EventDebugModal({ event, ...props }: { event: NostrEvent
|
||||
setLoading(false);
|
||||
}, []);
|
||||
|
||||
const eventRelays = useSubject(getEventRelays(getEventUID(event)));
|
||||
|
||||
return (
|
||||
<Modal size="6xl" {...props}>
|
||||
<ModalOverlay />
|
||||
@ -88,7 +79,7 @@ export default function EventDebugModal({ event, ...props }: { event: NostrEvent
|
||||
<Section label="IDs">
|
||||
<RawValue heading="Event Id" value={event.id} />
|
||||
<RawValue heading="NIP-19 Encoded Id" value={nip19.noteEncode(event.id)} />
|
||||
<RawValue heading="NIP-19 Pointer" value={getSharableEventAddress(event)} />
|
||||
<RawValue heading="NIP-19 Pointer" value={relayHintService.getSharableEventAddress(event)} />
|
||||
</Section>
|
||||
|
||||
<Section
|
||||
@ -115,18 +106,6 @@ export default function EventDebugModal({ event, ...props }: { event: NostrEvent
|
||||
<Heading size="sm">Tags referenced in content</Heading>
|
||||
<JsonCode data={getContentTagRefs(event.content, event.tags)} />
|
||||
</Section>
|
||||
<Section label="Relays">
|
||||
<Heading size="sm">Seen on:</Heading>
|
||||
{eventRelays.map((url) => (
|
||||
<Flex gap="2" key={url} alignItems="center">
|
||||
<RelayFavicon size="sm" relay={url} />
|
||||
<Text fontWeight="bold">{url}</Text>
|
||||
</Flex>
|
||||
))}
|
||||
<Button onClick={broadcast} mr="auto" colorScheme="primary" isLoading={loading}>
|
||||
Broadcast
|
||||
</Button>
|
||||
</Section>
|
||||
</Accordion>
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
|
@ -8,11 +8,11 @@ import {
|
||||
getArticleTitle,
|
||||
} from "../../../helpers/nostr/long-form";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import UserAvatarLink from "../../user/user-avatar-link";
|
||||
import UserLink from "../../user/user-link";
|
||||
import Timestamp from "../../timestamp";
|
||||
import { AppHandlerContext } from "../../../providers/route/app-handler-provider";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export default function EmbeddedArticle({ article, ...props }: Omit<CardProps, "children"> & { article: NostrEvent }) {
|
||||
const toast = useToast();
|
||||
@ -23,7 +23,7 @@ export default function EmbeddedArticle({ article, ...props }: Omit<CardProps, "
|
||||
const { openAddress } = useContext(AppHandlerContext);
|
||||
|
||||
const open = () => {
|
||||
const naddr = getSharableEventAddress(article);
|
||||
const naddr = relayHintService.getSharableEventAddress(article);
|
||||
if (naddr) openAddress(naddr);
|
||||
else toast({ status: "error", description: "Failed to get address" });
|
||||
};
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { useMemo } from "react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
import {
|
||||
Card,
|
||||
@ -14,12 +15,12 @@ import {
|
||||
|
||||
import UserAvatarLink from "../../user/user-avatar-link";
|
||||
import UserLink from "../../user/user-link";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import { getBadgeDescription, getBadgeImage, getBadgeName } from "../../../helpers/nostr/badges";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export default function EmbeddedBadge({ badge, ...props }: Omit<CardProps, "children"> & { badge: NostrEvent }) {
|
||||
const naddr = getSharableEventAddress(badge);
|
||||
const naddr = useMemo(() => relayHintService.getSharableEventAddress(badge), [badge]);
|
||||
const image = getBadgeImage(badge);
|
||||
|
||||
return (
|
||||
|
@ -13,7 +13,6 @@ import {
|
||||
} from "@chakra-ui/react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import { getEmojisFromPack, getPackName } from "../../../helpers/nostr/emoji-packs";
|
||||
import UserAvatarLink from "../../user/user-avatar-link";
|
||||
import UserLink from "../../user/user-link";
|
||||
@ -21,10 +20,11 @@ import EmojiPackFavoriteButton from "../../../views/emoji-packs/components/emoji
|
||||
import EmojiPackMenu from "../../../views/emoji-packs/components/emoji-pack-menu";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import Timestamp from "../../timestamp";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export default function EmbeddedEmojiPack({ pack, ...props }: Omit<CardProps, "children"> & { pack: NostrEvent }) {
|
||||
const emojis = getEmojisFromPack(pack);
|
||||
const naddr = getSharableEventAddress(pack);
|
||||
const naddr = relayHintService.getSharableEventAddress(pack);
|
||||
|
||||
return (
|
||||
<Card {...props}>
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { useMemo } from "react";
|
||||
import { Card, CardBody, CardProps, Flex, Heading, Image, Link, Text } from "@chakra-ui/react";
|
||||
import { Link as RouterLink, useNavigate } from "react-router-dom";
|
||||
|
||||
@ -6,7 +7,7 @@ import UserLink from "../../user/user-link";
|
||||
import UserAvatar from "../../user/user-avatar";
|
||||
import { useBreakpointValue } from "../../../providers/global/breakpoint-provider";
|
||||
import { getVideoDuration, getVideoImages, getVideoSummary, getVideoTitle } from "../../../helpers/nostr/flare";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export default function EmbeddedFlareVideo({ video, ...props }: Omit<CardProps, "children"> & { video: NostrEvent }) {
|
||||
const navigate = useNavigate();
|
||||
@ -17,7 +18,7 @@ export default function EmbeddedFlareVideo({ video, ...props }: Omit<CardProps,
|
||||
const summary = getVideoSummary(video);
|
||||
|
||||
const isVertical = useBreakpointValue({ base: true, md: false });
|
||||
const naddr = getSharableEventAddress(video);
|
||||
const naddr = useMemo(() => relayHintService.getSharableEventAddress(video), [video]);
|
||||
|
||||
return (
|
||||
<Card {...props} position="relative">
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Card, CardBody, CardHeader, CardProps, Flex, Heading, Link, Text } from "@chakra-ui/react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import { getGoalName } from "../../../helpers/nostr/goal";
|
||||
import UserAvatarLink from "../../user/user-avatar-link";
|
||||
@ -9,6 +9,7 @@ import UserLink from "../../user/user-link";
|
||||
import GoalProgress from "../../../views/goals/components/goal-progress";
|
||||
import GoalZapButton from "../../../views/goals/components/goal-zap-button";
|
||||
import GoalTopZappers from "../../../views/goals/components/goal-top-zappers";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export type EmbeddedGoalOptions = {
|
||||
showActions?: boolean;
|
||||
@ -17,7 +18,7 @@ export type EmbeddedGoalOptions = {
|
||||
export type EmbeddedGoalProps = Omit<CardProps, "children"> & { goal: NostrEvent } & EmbeddedGoalOptions;
|
||||
|
||||
export default function EmbeddedGoal({ goal, showActions = true, ...props }: EmbeddedGoalProps) {
|
||||
const nevent = getSharableEventAddress(goal);
|
||||
const nevent = useMemo(() => relayHintService.getSharableEventAddress(goal), [goal]);
|
||||
|
||||
return (
|
||||
<Card {...props}>
|
||||
|
@ -3,15 +3,15 @@ import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import { getListDescription, getListName, isSpecialListKind } from "../../../helpers/nostr/lists";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import UserAvatarLink from "../../user/user-avatar-link";
|
||||
import UserLink from "../../user/user-link";
|
||||
import ListFeedButton from "../../../views/lists/components/list-feed-button";
|
||||
import { ListCardContent } from "../../../views/lists/components/list-card";
|
||||
import { createCoordinate } from "../../../classes/batch-kind-pubkey-loader";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export default function EmbeddedList({ list, ...props }: Omit<CardProps, "children"> & { list: NostrEvent }) {
|
||||
const link = isSpecialListKind(list.kind) ? createCoordinate(list.kind, list.pubkey) : getSharableEventAddress(list);
|
||||
const link = isSpecialListKind(list.kind) ? createCoordinate(list.kind, list.pubkey) : relayHintService.getSharableEventAddress(list);
|
||||
const description = getListDescription(list);
|
||||
|
||||
return (
|
||||
|
@ -11,16 +11,16 @@ import EventVerificationIcon from "../../common-event/event-verification-icon";
|
||||
import { TrustProvider } from "../../../providers/local/trust-provider";
|
||||
import { NoteLink } from "../../note/note-link";
|
||||
import Timestamp from "../../timestamp";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import { CompactNoteContent } from "../../compact-note-content";
|
||||
import { useNavigateInDrawer } from "../../../providers/drawer-sub-view-provider";
|
||||
import HoverLinkOverlay from "../../hover-link-overlay";
|
||||
import singleEventService from "../../../services/single-event";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export default function EmbeddedNote({ event, ...props }: Omit<CardProps, "children"> & { event: NostrEvent }) {
|
||||
const { showSignatureVerification } = useSubject(appSettings);
|
||||
const navigate = useNavigateInDrawer();
|
||||
const to = `/n/${getSharableEventAddress(event)}`;
|
||||
const to = `/n/${relayHintService.getSharableEventAddress(event)}`;
|
||||
|
||||
const handleClick = useCallback<MouseEventHandler>(
|
||||
(e) => {
|
||||
|
@ -21,7 +21,7 @@ import Timestamp from "../../timestamp";
|
||||
import TrackStemstrButton from "../../../views/tracks/components/track-stemstr-button";
|
||||
import TrackDownloadButton from "../../../views/tracks/components/track-download-button";
|
||||
import TrackPlayer from "../../../views/tracks/components/track-player";
|
||||
import QuoteRepostButton from "../../note/quote-repost-button";
|
||||
import QuoteEventButton from "../../note/quote-event-button";
|
||||
import NoteZapButton from "../../note/note-zap-button";
|
||||
|
||||
// example nevent1qqst32cnyhhs7jt578u7vp3y047dduuwjquztpvwqc43f3nvg8dh28gpzamhxue69uhhyetvv9ujuum5v4khxarj9eshquq4rxdxa
|
||||
@ -53,7 +53,7 @@ export default function EmbeddedStemstrTrack({ track, ...props }: Omit<CardProps
|
||||
Comment
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<QuoteRepostButton event={track} />
|
||||
<QuoteEventButton event={track} />
|
||||
<NoteZapButton event={track} />
|
||||
</ButtonGroup>
|
||||
<ButtonGroup size="sm" ml="auto">
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Box, Card, CardProps, Divider, Flex, Link, Text } from "@chakra-ui/react";
|
||||
import { Card, CardProps, Divider, Flex, Link } from "@chakra-ui/react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
import { NostrEvent, isATag } from "../../../types/nostr-event";
|
||||
@ -8,7 +8,7 @@ import ChatMessageContent from "../../../views/streams/stream/stream-chat/chat-m
|
||||
import useReplaceableEvent from "../../../hooks/use-replaceable-event";
|
||||
import { parseStreamEvent } from "../../../helpers/nostr/stream";
|
||||
import StreamStatusBadge from "../../../views/streams/components/status-badge";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export default function EmbeddedStreamMessage({
|
||||
message,
|
||||
@ -25,7 +25,7 @@ export default function EmbeddedStreamMessage({
|
||||
<Flex gap="2" alignItems="center">
|
||||
<Link
|
||||
as={RouterLink}
|
||||
to={`/streams/${getSharableEventAddress(streamEvent) ?? ""}`}
|
||||
to={`/streams/${relayHintService.getSharableEventAddress(streamEvent) ?? ""}`}
|
||||
fontWeight="bold"
|
||||
fontSize="lg"
|
||||
>
|
||||
|
@ -6,13 +6,13 @@ import { NostrEvent } from "../../../types/nostr-event";
|
||||
import StreamStatusBadge from "../../../views/streams/components/status-badge";
|
||||
import UserLink from "../../user/user-link";
|
||||
import UserAvatar from "../../user/user-avatar";
|
||||
import useEventNaddr from "../../../hooks/use-event-naddr";
|
||||
import useShareableEventAddress from "../../../hooks/use-shareable-event-address";
|
||||
import Timestamp from "../../timestamp";
|
||||
import { useBreakpointValue } from "../../../providers/global/breakpoint-provider";
|
||||
|
||||
export default function EmbeddedStream({ event, ...props }: Omit<CardProps, "children"> & { event: NostrEvent }) {
|
||||
const stream = parseStreamEvent(event);
|
||||
const naddr = useEventNaddr(stream.event, stream.relays);
|
||||
const naddr = useShareableEventAddress(stream.event, stream.relays);
|
||||
const isVertical = useBreakpointValue({ base: true, md: false });
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
@ -16,7 +16,6 @@ import {
|
||||
} from "@chakra-ui/react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import UserAvatarLink from "../../user/user-avatar-link";
|
||||
import UserLink from "../../user/user-link";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
@ -26,10 +25,11 @@ import { getTorrentMagnetLink, getTorrentSize, getTorrentTitle } from "../../../
|
||||
import { formatBytes } from "../../../helpers/number";
|
||||
import { useNavigateInDrawer } from "../../../providers/drawer-sub-view-provider";
|
||||
import HoverLinkOverlay from "../../hover-link-overlay";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export default function EmbeddedTorrent({ torrent, ...props }: Omit<CardProps, "children"> & { torrent: NostrEvent }) {
|
||||
const navigate = useNavigateInDrawer();
|
||||
const link = `/torrents/${getSharableEventAddress(torrent)}`;
|
||||
const link = `/torrents/${relayHintService.getSharableEventAddress(torrent)}`;
|
||||
|
||||
const handleClick = useCallback<MouseEventHandler>(
|
||||
(e) => {
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { useContext, useMemo } from "react";
|
||||
import { Box, Button, ButtonGroup, Card, CardBody, CardHeader, CardProps, Text } from "@chakra-ui/react";
|
||||
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import UserAvatarLink from "../../user/user-avatar-link";
|
||||
import UserLink from "../../user/user-link";
|
||||
@ -21,9 +20,10 @@ import { renderAudioUrl } from "../../external-embeds/types/audio";
|
||||
import DebugEventButton from "../../debug-modal/debug-event-button";
|
||||
import DebugEventTags from "../../debug-modal/event-tags";
|
||||
import { AppHandlerContext } from "../../../providers/route/app-handler-provider";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export default function EmbeddedUnknown({ event, ...props }: Omit<CardProps, "children"> & { event: NostrEvent }) {
|
||||
const address = getSharableEventAddress(event);
|
||||
const address = useMemo(()=> relayHintService.getSharableEventAddress(event), [event])
|
||||
const { openAddress } = useContext(AppHandlerContext);
|
||||
|
||||
const alt = event.tags.find((t) => t[0] === "alt")?.[1];
|
||||
|
@ -18,10 +18,10 @@ import { NostrEvent } from "../../../types/nostr-event";
|
||||
import UserLink from "../../user/user-link";
|
||||
import { getPageForks, getPageSummary, getPageTitle } from "../../../helpers/nostr/wiki";
|
||||
import HoverLinkOverlay from "../../hover-link-overlay";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import Timestamp from "../../timestamp";
|
||||
import GitBranch01 from "../../icons/git-branch-01";
|
||||
import UserName from "../../user/user-name";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export default function EmbeddedWikiPage({ page: page, ...props }: Omit<CardProps, "children"> & { page: NostrEvent }) {
|
||||
const { address } = useMemo(() => getPageForks(page), [page]);
|
||||
@ -31,7 +31,7 @@ export default function EmbeddedWikiPage({ page: page, ...props }: Omit<CardProp
|
||||
<Card as={LinkBox} {...props}>
|
||||
<CardHeader p="2" pb="0" display="flex" gap="2" alignItems="center">
|
||||
<Heading size="md">
|
||||
<HoverLinkOverlay as={RouterLink} to={`/wiki/page/${getSharableEventAddress(page)}`}>
|
||||
<HoverLinkOverlay as={RouterLink} to={`/wiki/page/${relayHintService.getSharableEventAddress(page)}`}>
|
||||
{getPageTitle(page)}
|
||||
</HoverLinkOverlay>
|
||||
</Heading>
|
||||
|
@ -0,0 +1,54 @@
|
||||
import { Box, ButtonGroup, Card, CardBody, CardHeader, CardProps, LinkBox, Text } from "@chakra-ui/react";
|
||||
import { useMemo } from "react";
|
||||
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import UserLink from "../../user/user-link";
|
||||
import Timestamp from "../../timestamp";
|
||||
import { getParsedZap, getZapRecipient } from "../../../helpers/nostr/zaps";
|
||||
import TextNoteContents from "../../note/timeline-note/text-note-contents";
|
||||
import UserAvatar from "../../user/user-avatar";
|
||||
import { LightningIcon } from "../../icons";
|
||||
import { readablizeSats } from "../../../helpers/bolt11";
|
||||
import ZapReceiptMenu from "../../zap/zap-receipt-menu";
|
||||
import { getPointerFromTag } from "../../../helpers/nip19";
|
||||
import { EmbedEventPointer } from "../index";
|
||||
|
||||
export default function EmbeddedZapRecept({ zap, ...props }: Omit<CardProps, "children"> & { zap: NostrEvent }) {
|
||||
const parsed = useMemo(() => getParsedZap(zap), [zap]);
|
||||
if (!parsed) return null;
|
||||
const recipient = getZapRecipient(parsed.request);
|
||||
if (!recipient) return null;
|
||||
|
||||
const eTag = parsed.request.tags.find((t) => t[0] === "e" && t[1]);
|
||||
const pointer = eTag && getPointerFromTag(eTag);
|
||||
|
||||
return (
|
||||
<Card as={LinkBox} {...props}>
|
||||
<CardHeader display="flex" p="2" gap="2" alignItems="center">
|
||||
<UserAvatar pubkey={parsed.request.pubkey} size="sm" />
|
||||
<UserLink pubkey={parsed.request.pubkey} fontWeight="bold" />
|
||||
<Text>Zapped</Text>
|
||||
<UserLink pubkey={recipient} fontWeight="bold" />
|
||||
|
||||
{parsed.payment.amount && (
|
||||
<>
|
||||
<LightningIcon color="yellow.500" boxSize={5} />
|
||||
<Text>{readablizeSats(parsed.payment.amount / 1000)}</Text>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Timestamp timestamp={parsed.event.created_at} ml="auto" />
|
||||
<ButtonGroup size="sm" variant="ghost">
|
||||
<ZapReceiptMenu zap={zap} aria-label="More Options" />
|
||||
</ButtonGroup>
|
||||
</CardHeader>
|
||||
<CardBody px="2" pb="2" pt="0" display="flex" flexDirection="column" gap="2">
|
||||
<Box>
|
||||
<TextNoteContents event={parsed.request} />
|
||||
</Box>
|
||||
|
||||
{pointer && <EmbedEventPointer pointer={pointer} />}
|
||||
</CardBody>
|
||||
</Card>
|
||||
);
|
||||
}
|
@ -43,6 +43,7 @@ import LoadingNostrLink from "../loading-nostr-link";
|
||||
import EmbeddedRepost from "./event-types/embedded-repost";
|
||||
import { WIKI_PAGE_KIND } from "../../helpers/nostr/wiki";
|
||||
import EmbeddedWikiPage from "./event-types/embedded-wiki-page";
|
||||
import EmbeddedZapRecept from "./event-types/embedded-zap-receipt";
|
||||
const EmbeddedStemstrTrack = lazy(() => import("./event-types/embedded-stemstr-track"));
|
||||
|
||||
export type EmbedProps = {
|
||||
@ -97,6 +98,8 @@ export function EmbedEvent({
|
||||
return <EmbeddedRepost repost={event} {...cardProps} />;
|
||||
case WIKI_PAGE_KIND:
|
||||
return <EmbeddedWikiPage page={event} {...cardProps} />;
|
||||
case kinds.Zap:
|
||||
return <EmbeddedZapRecept zap={event} {...cardProps} />;
|
||||
}
|
||||
|
||||
return <EmbeddedUnknown event={event} {...cardProps} />;
|
||||
|
@ -107,7 +107,7 @@ export const SearchIcon = SearchMd;
|
||||
|
||||
export const ReplyIcon = ReverseLeft;
|
||||
export const RepostIcon = Repeat01;
|
||||
export const QuoteRepostIcon = createIcon({
|
||||
export const QuoteEventIcon = createIcon({
|
||||
displayName: "QuoteRepostIcon",
|
||||
d: "M19.4167 6.67891C20.4469 7.77257 21.0001 9 21.0001 10.9897C21.0001 14.4891 18.5436 17.6263 14.9695 19.1768L14.0768 17.7992C17.4121 15.9946 18.0639 13.6539 18.3245 12.178C17.7875 12.4557 17.0845 12.5533 16.3954 12.4895C14.591 12.3222 13.1689 10.8409 13.1689 9C13.1689 7.067 14.7359 5.5 16.6689 5.5C17.742 5.5 18.7681 5.99045 19.4167 6.67891ZM9.41669 6.67891C10.4469 7.77257 11.0001 9 11.0001 10.9897C11.0001 14.4891 8.54359 17.6263 4.96951 19.1768L4.07682 17.7992C7.41206 15.9946 8.06392 13.6539 8.32447 12.178C7.78747 12.4557 7.08452 12.5533 6.39539 12.4895C4.59102 12.3222 3.16895 10.8409 3.16895 9C3.16895 7.067 4.73595 5.5 6.66895 5.5C7.742 5.5 8.76814 5.99045 9.41669 6.67891Z",
|
||||
defaultProps,
|
||||
|
@ -13,8 +13,8 @@ import { useTimelineCurserIntersectionCallback } from "../../../hooks/use-timeli
|
||||
import { getDMRecipient, getDMSender } from "../../../helpers/nostr/dms";
|
||||
import UserName from "../../user/user-name";
|
||||
import HoverLinkOverlay from "../../hover-link-overlay";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
const kindColors: Record<number, FlexProps["bg"]> = {
|
||||
[kinds.ShortTextNote]: "blue.500",
|
||||
@ -73,7 +73,12 @@ function TimelineItem({ event }: { event: NostrEvent }) {
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<HoverLinkOverlay as={RouterLink} to={`/l/${getSharableEventAddress(event)}`} noOfLines={1} isTruncated>
|
||||
<HoverLinkOverlay
|
||||
as={RouterLink}
|
||||
to={`/l/${relayHintService.getSharableEventAddress(event)}`}
|
||||
noOfLines={1}
|
||||
isTruncated
|
||||
>
|
||||
{event.content}
|
||||
</HoverLinkOverlay>
|
||||
);
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
import { Button, Flex, FlexProps, Spacer, useDisclosure } from "@chakra-ui/react";
|
||||
import { useUnmount } from "react-use";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
import styled from "@emotion/styled";
|
||||
|
||||
import Lightbox, { RenderSlideContainerProps, Slide } from "yet-another-react-lightbox";
|
||||
import Zoom from "yet-another-react-lightbox/plugins/zoom";
|
||||
@ -32,8 +33,7 @@ declare module "yet-another-react-lightbox" {
|
||||
import { NostrEvent } from "../types/nostr-event";
|
||||
import UserAvatarLink from "./user/user-avatar-link";
|
||||
import UserLink from "./user/user-link";
|
||||
import styled from "@emotion/styled";
|
||||
import { getSharableEventAddress } from "../helpers/nip19";
|
||||
import relayHintService from "../services/event-relay-hint";
|
||||
|
||||
type RefType = MutableRefObject<HTMLElement | null>;
|
||||
|
||||
@ -100,7 +100,7 @@ function getRefPath(ref: RefType) {
|
||||
}
|
||||
|
||||
function EventSlideHeader({ event, ...props }: { event: NostrEvent } & Omit<FlexProps, "children">) {
|
||||
const encoded = useMemo(() => getSharableEventAddress(event), [event]);
|
||||
const encoded = useMemo(() => relayHintService.getSharableEventAddress(event), [event]);
|
||||
|
||||
return (
|
||||
<Flex gap="2" alignItems="center" p="2" {...props}>
|
||||
|
@ -1,17 +0,0 @@
|
||||
import { memo } from "react";
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
|
||||
import { getEventRelays } from "../../services/event-relays";
|
||||
import useSubject from "../../hooks/use-subject";
|
||||
import { RelayIconStack, RelayIconStackProps } from "../relay-icon-stack";
|
||||
import { getEventUID } from "../../helpers/nostr/event";
|
||||
import { useBreakpointValue } from "../../providers/global/breakpoint-provider";
|
||||
|
||||
export const EventRelays = memo(
|
||||
({ event, ...props }: { event: NostrEvent } & Omit<RelayIconStackProps, "relays" | "maxRelays">) => {
|
||||
const maxRelays = useBreakpointValue({ base: 3, md: undefined });
|
||||
const eventRelays = useSubject(getEventRelays(getEventUID(event)));
|
||||
|
||||
return <RelayIconStack relays={eventRelays} direction="row-reverse" maxRelays={maxRelays} {...props} />;
|
||||
},
|
||||
);
|
@ -1,4 +1,4 @@
|
||||
import { useCallback } from "react";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { MenuItem, useDisclosure } from "@chakra-ui/react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
@ -13,15 +13,17 @@ import OpenInAppMenuItem from "../common-menu-items/open-in-app";
|
||||
import MuteUserMenuItem from "../common-menu-items/mute-user";
|
||||
import DeleteEventMenuItem from "../common-menu-items/delete-event";
|
||||
import CopyEmbedCodeMenuItem from "../common-menu-items/copy-embed-code";
|
||||
import { getSharableEventAddress } from "../../helpers/nip19";
|
||||
import Recording02 from "../icons/recording-02";
|
||||
import { usePublishEvent } from "../../providers/global/publish-provider";
|
||||
import DebugEventMenuItem from "../debug-modal/debug-event-menu-item";
|
||||
import relayHintService from "../../services/event-relay-hint";
|
||||
|
||||
export default function NoteMenu({ event, ...props }: { event: NostrEvent } & Omit<MenuIconButtonProps, "children">) {
|
||||
const translationsModal = useDisclosure();
|
||||
const publish = usePublishEvent();
|
||||
|
||||
const address = useMemo(() => relayHintService.getSharableEventAddress(event), [event])
|
||||
|
||||
const broadcast = useCallback(async () => {
|
||||
await publish("Broadcast", event);
|
||||
}, []);
|
||||
@ -38,14 +40,14 @@ export default function NoteMenu({ event, ...props }: { event: NostrEvent } & Om
|
||||
<MenuItem
|
||||
as={RouterLink}
|
||||
icon={<Recording02 />}
|
||||
to={`/tools/transform/${getSharableEventAddress(event)}?tab=tts`}
|
||||
to={`/tools/transform/${address}?tab=tts`}
|
||||
>
|
||||
Text to speech
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
as={RouterLink}
|
||||
icon={<Translate01 />}
|
||||
to={`/tools/transform/${getSharableEventAddress(event)}?tab=translation`}
|
||||
to={`/tools/transform/${address}?tab=translation`}
|
||||
>
|
||||
Translate
|
||||
</MenuItem>
|
||||
|
@ -2,30 +2,28 @@ import { useContext } from "react";
|
||||
import { ButtonProps, IconButton } from "@chakra-ui/react";
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
|
||||
import { QuoteRepostIcon } from "../icons";
|
||||
import { QuoteEventIcon } from "../icons";
|
||||
import { PostModalContext } from "../../providers/route/post-modal-provider";
|
||||
import { getSharableEventAddress } from "../../helpers/nip19";
|
||||
import relayHintService from "../../services/event-relay-hint";
|
||||
|
||||
export type QuoteRepostButtonProps = Omit<ButtonProps, "children" | "onClick"> & {
|
||||
event: NostrEvent;
|
||||
};
|
||||
|
||||
export default function QuoteRepostButton({
|
||||
export default function QuoteEventButton({
|
||||
event,
|
||||
"aria-label": ariaLabel,
|
||||
title = "Quote repost",
|
||||
title = "Quote Note",
|
||||
...props
|
||||
}: QuoteRepostButtonProps) {
|
||||
}: Omit<ButtonProps, "children" | "onClick"> & {
|
||||
event: NostrEvent;
|
||||
}) {
|
||||
const { openModal } = useContext(PostModalContext);
|
||||
|
||||
const handleClick = () => {
|
||||
const nevent = getSharableEventAddress(event);
|
||||
const nevent = relayHintService.getSharableEventAddress(event);
|
||||
openModal({ cacheFormKey: null, initContent: "\nnostr:" + nevent });
|
||||
};
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
icon={<QuoteRepostIcon />}
|
||||
icon={<QuoteEventIcon />}
|
||||
onClick={handleClick}
|
||||
aria-label={ariaLabel || title}
|
||||
title={title}
|
@ -25,7 +25,7 @@ import useSubject from "../../../hooks/use-subject";
|
||||
import appSettings from "../../../services/settings/app-settings";
|
||||
import EventVerificationIcon from "../../common-event/event-verification-icon";
|
||||
import RepostButton from "./components/repost-button";
|
||||
import QuoteRepostButton from "../quote-repost-button";
|
||||
import QuoteEventButton from "../quote-event-button";
|
||||
import { ReplyIcon } from "../../icons";
|
||||
import NoteContentWithWarning from "./note-content-with-warning";
|
||||
import { TrustProvider } from "../../../providers/local/trust-provider";
|
||||
@ -36,7 +36,6 @@ import ReplyForm from "../../../views/thread/components/reply-form";
|
||||
import { getThreadReferences } from "../../../helpers/nostr/event";
|
||||
import Timestamp from "../../timestamp";
|
||||
import OpenInDrawerButton from "../open-in-drawer-button";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import { useBreakpointValue } from "../../../providers/global/breakpoint-provider";
|
||||
import HoverLinkOverlay from "../../hover-link-overlay";
|
||||
import NoteCommunityMetadata from "./note-community-metadata";
|
||||
@ -46,6 +45,7 @@ import POWIcon from "../../pow/pow-icon";
|
||||
import ReplyContext from "./components/reply-context";
|
||||
import ZapBubbles from "./components/zap-bubbles";
|
||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export type TimelineNoteProps = Omit<CardProps, "children"> & {
|
||||
event: NostrEvent;
|
||||
@ -89,7 +89,7 @@ export function TimelineNote({
|
||||
{clickable && (
|
||||
<HoverLinkOverlay
|
||||
as={RouterLink}
|
||||
to={`/n/${getSharableEventAddress(event)}`}
|
||||
to={`/n/${relayHintService.getSharableEventAddress(event)}`}
|
||||
onClick={() => singleEventService.handleEvent(event)}
|
||||
/>
|
||||
)}
|
||||
@ -97,7 +97,12 @@ export function TimelineNote({
|
||||
<Flex flex="1" gap="2" alignItems="center">
|
||||
<UserAvatarLink pubkey={event.pubkey} size="sm" />
|
||||
<UserLink pubkey={event.pubkey} isTruncated fontWeight="bold" fontSize="lg" />
|
||||
<Link as={RouterLink} whiteSpace="nowrap" color="current" to={`/n/${getSharableEventAddress(event)}`}>
|
||||
<Link
|
||||
as={RouterLink}
|
||||
whiteSpace="nowrap"
|
||||
color="current"
|
||||
to={`/n/${relayHintService.getSharableEventAddress(event)}`}
|
||||
>
|
||||
<Timestamp timestamp={event.created_at} />
|
||||
</Link>
|
||||
<POWIcon event={event} boxSize={5} />
|
||||
@ -105,7 +110,7 @@ export function TimelineNote({
|
||||
{showSignatureVerification && <EventVerificationIcon event={event} />}
|
||||
{!hideDrawerButton && (
|
||||
<OpenInDrawerButton
|
||||
to={`/n/${getSharableEventAddress(event)}`}
|
||||
to={`/n/${relayHintService.getSharableEventAddress(event)}`}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={() => singleEventService.handleEvent(event)}
|
||||
@ -127,7 +132,7 @@ export function TimelineNote({
|
||||
<IconButton icon={<ReplyIcon />} aria-label="Reply" title="Reply" onClick={replyForm.onOpen} />
|
||||
)}
|
||||
<RepostButton event={event} />
|
||||
<QuoteRepostButton event={event} />
|
||||
<QuoteEventButton event={event} />
|
||||
<NoteZapButton event={event} />
|
||||
</ButtonGroup>
|
||||
{!showReactionsOnNewLine && reactionButtons}
|
||||
|
@ -17,20 +17,19 @@ import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import { parseStreamEvent } from "../../../helpers/nostr/stream";
|
||||
import useEventNaddr from "../../../hooks/use-event-naddr";
|
||||
import useShareableEventAddress from "../../../hooks/use-shareable-event-address";
|
||||
import UserAvatar from "../../user/user-avatar";
|
||||
import UserLink from "../../user/user-link";
|
||||
import StreamStatusBadge from "../../../views/streams/components/status-badge";
|
||||
import { useAsync } from "react-use";
|
||||
import Timestamp from "../../timestamp";
|
||||
import { EventRelays } from "../../note/event-relays";
|
||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||
|
||||
export default function StreamNote({ event, ...props }: CardProps & { event: NostrEvent }) {
|
||||
const { value: stream, error } = useAsync(async () => parseStreamEvent(event), [event]);
|
||||
const ref = useEventIntersectionRef(event);
|
||||
|
||||
const naddr = useEventNaddr(event);
|
||||
const naddr = useShareableEventAddress(event);
|
||||
|
||||
if (!stream || error) return null;
|
||||
|
||||
@ -68,8 +67,6 @@ export default function StreamNote({ event, ...props }: CardProps & { event: Nos
|
||||
<Divider />
|
||||
<CardFooter p="2" display="flex" gap="2" alignItems="center">
|
||||
<StreamStatusBadge stream={stream} />
|
||||
<Spacer />
|
||||
<EventRelays event={stream.event} />
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
|
@ -17,7 +17,6 @@ import {
|
||||
|
||||
import TimelineLoader from "../../../classes/timeline-loader";
|
||||
import useSubject from "../../../hooks/use-subject";
|
||||
import { getEventRelays } from "../../../services/event-relays";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import { RelayFavicon } from "../../relay-favicon";
|
||||
import { NoteLink } from "../../note/note-link";
|
||||
@ -31,8 +30,8 @@ function EventRow({
|
||||
relays,
|
||||
...props
|
||||
}: { event: NostrEvent; relays: string[] } & Omit<TableRowProps, "children">) {
|
||||
const sub = useMemo(() => getEventRelays(event.id), [event.id]);
|
||||
const seenRelays = useSubject(sub);
|
||||
// const sub = useMemo(() => getEventRelays(event.id), [event.id]);
|
||||
const seenRelays = true; //useSubject(sub);
|
||||
const publish = usePublishEvent();
|
||||
|
||||
const ref = useEventIntersectionRef(event);
|
||||
@ -65,7 +64,7 @@ function EventRow({
|
||||
{broadcasting ? <Spinner size="xs" /> : <BroadcastEventIcon />}
|
||||
</Td>
|
||||
{relays.map((relay) => (
|
||||
<Td key={relay} title={relay} p="2" backgroundColor={seenRelays.includes(relay) ? yes : no}>
|
||||
<Td key={relay} title={relay} p="2" backgroundColor={/*seenRelays.includes(relay)*/ true ? yes : no}>
|
||||
<RelayFavicon relay={relay} size="2xs" />
|
||||
</Td>
|
||||
))}
|
||||
|
20
src/components/zap/zap-receipt-menu.tsx
Normal file
20
src/components/zap/zap-receipt-menu.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import { DotsMenuButton, MenuIconButtonProps } from "../dots-menu-button";
|
||||
import OpenInAppMenuItem from "../common-menu-items/open-in-app";
|
||||
import CopyEmbedCodeMenuItem from "../common-menu-items/copy-embed-code";
|
||||
import DebugEventMenuItem from "../debug-modal/debug-event-menu-item";
|
||||
import QuoteEventMenuItem from "../common-menu-items/quote-event";
|
||||
|
||||
export default function ZapReceiptMenu({ zap, ...props }: { zap: NostrEvent } & Omit<MenuIconButtonProps, "children">) {
|
||||
return (
|
||||
<>
|
||||
<DotsMenuButton {...props}>
|
||||
<OpenInAppMenuItem event={zap} />
|
||||
<QuoteEventMenuItem event={zap} />
|
||||
<CopyEmbedCodeMenuItem event={zap} />
|
||||
|
||||
<DebugEventMenuItem event={zap} />
|
||||
</DotsMenuButton>
|
||||
</>
|
||||
);
|
||||
}
|
@ -9,3 +9,4 @@ export const SEARCH_RELAYS = safeRelayUrls([
|
||||
]);
|
||||
export const WIKI_RELAYS = safeRelayUrls(["wss://relay.wikifreedia.xyz/"]);
|
||||
export const COMMON_CONTACT_RELAY = safeRelayUrl("wss://purplepag.es") as string;
|
||||
export const COMMON_CONTACT_RELAYS = [COMMON_CONTACT_RELAY];
|
||||
|
@ -1,8 +1,6 @@
|
||||
import { getPublicKey, nip19 } from "nostr-tools";
|
||||
|
||||
import { NostrEvent, Tag, isATag, isDTag, isETag, isPTag } from "../types/nostr-event";
|
||||
import { isReplaceable } from "./nostr/event";
|
||||
import relayHintService from "../services/event-relay-hint";
|
||||
import { Tag, isATag, isETag, isPTag } from "../types/nostr-event";
|
||||
import { safeRelayUrls } from "./relay";
|
||||
|
||||
export function isHex(str?: string) {
|
||||
@ -43,18 +41,6 @@ export function normalizeToHexPubkey(hex: string) {
|
||||
return getPubkeyFromDecodeResult(decode) ?? null;
|
||||
}
|
||||
|
||||
export function getSharableEventAddress(event: NostrEvent) {
|
||||
const relays = relayHintService.getEventRelayHints(event, 2);
|
||||
|
||||
if (isReplaceable(event.kind)) {
|
||||
const d = event.tags.find(isDTag)?.[1];
|
||||
if (!d) return null;
|
||||
return nip19.naddrEncode({ kind: event.kind, identifier: d, pubkey: event.pubkey, relays });
|
||||
} else {
|
||||
return nip19.neventEncode({ id: event.id, kind: event.kind, relays, author: event.pubkey });
|
||||
}
|
||||
}
|
||||
|
||||
export function encodeDecodeResult(result: nip19.DecodeResult) {
|
||||
switch (result.type) {
|
||||
case "naddr":
|
||||
|
@ -42,6 +42,10 @@ export function isProfileZap(event: NostrEvent) {
|
||||
return !isNoteZap(event) && event.tags.some(isPTag);
|
||||
}
|
||||
|
||||
export function getZapRecipient(event: NostrEvent) {
|
||||
return event.tags.find((t) => t[0] === "p" && t[1])?.[1];
|
||||
}
|
||||
|
||||
export function totalZaps(zaps: ParsedZap[]) {
|
||||
return zaps.reduce((t, zap) => t + (zap.payment.amount || 0), 0);
|
||||
}
|
||||
|
@ -1,10 +1,5 @@
|
||||
import { SimpleRelay, SubscriptionOptions } from "nostr-idb";
|
||||
import { AbstractRelay, Filter, SubCloser, SubscribeManyParams, Subscription } from "nostr-tools";
|
||||
|
||||
import { NostrQuery, NostrRequestFilter } from "../types/nostr-relay";
|
||||
import { NostrEvent } from "../types/nostr-event";
|
||||
import relayPoolService from "../services/relay-pool";
|
||||
|
||||
// NOTE: only use this for equality checks and querying
|
||||
export function getRelayVariations(relay: string) {
|
||||
if (relay.endsWith("/")) {
|
||||
@ -59,11 +54,11 @@ export function safeRelayUrls(urls: Iterable<string>): string[] {
|
||||
}
|
||||
|
||||
export function splitNostrFilterByPubkeys(
|
||||
filter: NostrRequestFilter,
|
||||
filter: Filter | Filter[],
|
||||
relayPubkeyMap: Record<string, string[]>,
|
||||
): Record<string, NostrRequestFilter> {
|
||||
): Record<string, Filter | Filter[]> {
|
||||
if (Array.isArray(filter)) {
|
||||
const dir: Record<string, NostrQuery[]> = {};
|
||||
const dir: Record<string, Filter[]> = {};
|
||||
|
||||
for (const query of filter) {
|
||||
const split = splitQueryByPubkeys(query, relayPubkeyMap);
|
||||
@ -77,8 +72,8 @@ export function splitNostrFilterByPubkeys(
|
||||
} else return splitQueryByPubkeys(filter, relayPubkeyMap);
|
||||
}
|
||||
|
||||
export function splitQueryByPubkeys(query: NostrQuery, relayPubkeyMap: Record<string, string[]>) {
|
||||
const filtersByRelay: Record<string, NostrQuery> = {};
|
||||
export function splitQueryByPubkeys(query: Filter, relayPubkeyMap: Record<string, string[]>) {
|
||||
const filtersByRelay: Record<string, Filter> = {};
|
||||
|
||||
const allPubkeys = new Set(Object.values(relayPubkeyMap).flat());
|
||||
for (const [relay, pubkeys] of Object.entries(relayPubkeyMap)) {
|
||||
@ -142,6 +137,7 @@ export function subscribeMany(relays: string[], filters: Filter[], params: Subsc
|
||||
|
||||
let relay: AbstractRelay;
|
||||
try {
|
||||
const { default: relayPoolService } = await import("../services/relay-pool");
|
||||
relay = relayPoolService.requestRelay(url);
|
||||
await relayPoolService.requestConnect(relay);
|
||||
// changed from nostr-tools
|
||||
|
@ -2,12 +2,12 @@ import clientRelaysService from "../services/client-relays";
|
||||
import useSubject from "./use-subject";
|
||||
|
||||
export function useReadRelays(additional?: Iterable<string>) {
|
||||
const set = useSubject(clientRelaysService.readRelays);
|
||||
if (additional) return set.clone().merge(additional);
|
||||
return set;
|
||||
const readRelays = useSubject(clientRelaysService.readRelays);
|
||||
if (additional) return readRelays.clone().merge(additional);
|
||||
return readRelays;
|
||||
}
|
||||
export function useWriteRelays(additional?: Iterable<string>) {
|
||||
const set = useSubject(clientRelaysService.writeRelays);
|
||||
if (additional) return set.clone().merge(additional);
|
||||
return set;
|
||||
const writeRelays = useSubject(clientRelaysService.writeRelays);
|
||||
if (additional) return writeRelays.clone().merge(additional);
|
||||
return writeRelays;
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { useMemo } from "react";
|
||||
import { Filter } from "nostr-tools";
|
||||
import eventCountService from "../services/event-count";
|
||||
import { NostrRequestFilter } from "../types/nostr-relay";
|
||||
import useSubject from "./use-subject";
|
||||
|
||||
export default function useEventCount(filter?: NostrRequestFilter, alwaysRequest = false) {
|
||||
export default function useEventCount(filter?: Filter | Filter[], alwaysRequest = false) {
|
||||
const key = filter ? eventCountService.stringifyFilter(filter) : "empty";
|
||||
const subject = useMemo(() => filter && eventCountService.requestCount(filter, alwaysRequest), [key, alwaysRequest]);
|
||||
return useSubject(subject);
|
||||
|
@ -1,23 +0,0 @@
|
||||
import { useMemo } from "react";
|
||||
import { NostrEvent } from "../types/nostr-event";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { getEventRelays } from "../services/event-relays";
|
||||
import relayScoreboardService from "../services/relay-scoreboard";
|
||||
|
||||
export default function useEventNaddr(event: NostrEvent, overrideRelays?: string[]) {
|
||||
return useMemo(() => {
|
||||
const identifier = event.tags.find((t) => t[0] === "d" && t[1])?.[1];
|
||||
const relays = overrideRelays || getEventRelays(event.id).value;
|
||||
const ranked = relayScoreboardService.getRankedRelays(relays);
|
||||
const onlyTwo = ranked.slice(0, 2);
|
||||
|
||||
if (!identifier) return null;
|
||||
|
||||
return nip19.naddrEncode({
|
||||
identifier,
|
||||
relays: onlyTwo,
|
||||
pubkey: event.pubkey,
|
||||
kind: event.kind,
|
||||
});
|
||||
}, [event]);
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import { useMemo } from "react";
|
||||
import { getEventRelays } from "../services/event-relays";
|
||||
import useSubject from "./use-subject";
|
||||
|
||||
export default function useEventRelays(eventId?: string) {
|
||||
const sub = useMemo(() => (eventId ? getEventRelays(eventId) : undefined), [eventId]);
|
||||
return useSubject(sub) ?? [];
|
||||
}
|
12
src/hooks/use-localstorage-disclosure.ts
Normal file
12
src/hooks/use-localstorage-disclosure.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { useDisclosure } from "@chakra-ui/react";
|
||||
import { useLocalStorage } from "react-use";
|
||||
|
||||
export default function useLocalStorageDisclosure(name: string) {
|
||||
const [value, setValue] = useLocalStorage<boolean>(name);
|
||||
return useDisclosure({
|
||||
isOpen: value,
|
||||
onOpen: () => setValue(true),
|
||||
onClose: () => setValue(false),
|
||||
defaultIsOpen: value,
|
||||
});
|
||||
}
|
12
src/hooks/use-shareable-event-address.ts
Normal file
12
src/hooks/use-shareable-event-address.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { useMemo } from "react";
|
||||
import { NostrEvent } from "../types/nostr-event";
|
||||
import relayHintService from "../services/event-relay-hint";
|
||||
import useUserMailboxes from "./use-user-mailboxes";
|
||||
|
||||
export default function useShareableEventAddress(event: NostrEvent, overrideRelays?: string[]) {
|
||||
const mailboxes = useUserMailboxes(event.pubkey);
|
||||
|
||||
return useMemo(() => {
|
||||
return relayHintService.getSharableEventAddress(event, overrideRelays);
|
||||
}, [event]);
|
||||
}
|
@ -1,16 +1,15 @@
|
||||
import { useMemo } from "react";
|
||||
|
||||
import userMetadataService from "../services/user-metadata";
|
||||
import { useReadRelays } from "./use-client-relays";
|
||||
import useSubject from "./use-subject";
|
||||
import { RequestOptions } from "../services/replaceable-events";
|
||||
import { COMMON_CONTACT_RELAY } from "../const";
|
||||
import { COMMON_CONTACT_RELAYS } from "../const";
|
||||
|
||||
export default function useUserMetadata(
|
||||
pubkey?: string,
|
||||
additionalRelays: Iterable<string> = [],
|
||||
opts: RequestOptions = {},
|
||||
) {
|
||||
const readRelays = useReadRelays([...additionalRelays, COMMON_CONTACT_RELAY]);
|
||||
export default function useUserMetadata(pubkey?: string, additionalRelays?: Iterable<string>, opts?: RequestOptions) {
|
||||
const readRelays = useReadRelays(
|
||||
additionalRelays ? [...additionalRelays, ...COMMON_CONTACT_RELAYS] : COMMON_CONTACT_RELAYS,
|
||||
);
|
||||
|
||||
const subject = useMemo(
|
||||
() => (pubkey ? userMetadataService.requestMetadata(pubkey, readRelays, opts) : undefined),
|
||||
|
@ -2,6 +2,7 @@ import "./polyfill";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { App } from "./app";
|
||||
import { GlobalProviders } from "./providers/global";
|
||||
|
||||
import "./services/user-event-sync";
|
||||
import "./services/username-search";
|
||||
|
||||
|
@ -11,7 +11,6 @@ import { getAllRelayHints, isReplaceable } from "../../helpers/nostr/event";
|
||||
import replaceableEventsService from "../../services/replaceable-events";
|
||||
import eventReactionsService from "../../services/event-reactions";
|
||||
import { localRelay } from "../../services/local-relay";
|
||||
import { handleEventFromRelay } from "../../services/event-relays";
|
||||
import deleteEventService from "../../services/delete-events";
|
||||
import userMailboxesService from "../../services/user-mailboxes";
|
||||
|
||||
@ -86,10 +85,6 @@ export default function PublishProvider({ children }: PropsWithChildren) {
|
||||
const pub = new PublishAction(label, relays, signed);
|
||||
setLog((arr) => arr.concat(pub));
|
||||
|
||||
pub.onResult.subscribe(({ relay, success }) => {
|
||||
if (success) handleEventFromRelay(relay, signed);
|
||||
});
|
||||
|
||||
// send it to the local relay
|
||||
if (localRelay) localRelay.publish(signed);
|
||||
|
||||
|
@ -1,11 +1,10 @@
|
||||
import { PropsWithChildren, createContext, useCallback, useContext, useMemo } from "react";
|
||||
import { kinds } from "nostr-tools";
|
||||
import { Filter, kinds } from "nostr-tools";
|
||||
|
||||
import useCurrentAccount from "../../hooks/use-current-account";
|
||||
import { getPubkeysFromList } from "../../helpers/nostr/lists";
|
||||
import useReplaceableEvent from "../../hooks/use-replaceable-event";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import { NostrQuery } from "../../types/nostr-relay";
|
||||
import useRouteSearchValue from "../../hooks/use-route-search-value";
|
||||
|
||||
export type ListId = "following" | "global" | string;
|
||||
@ -17,7 +16,7 @@ export type PeopleListContextType = {
|
||||
listEvent?: NostrEvent;
|
||||
people: Person[] | undefined;
|
||||
setSelected: (list: ListId) => void;
|
||||
filter: NostrQuery | undefined;
|
||||
filter: Filter | undefined;
|
||||
};
|
||||
const PeopleListContext = createContext<PeopleListContextType>({
|
||||
setSelected: () => {},
|
||||
@ -60,7 +59,7 @@ export default function PeopleListProvider({ children, initList }: PeopleListPro
|
||||
|
||||
const people = listEvent && getPubkeysFromList(listEvent);
|
||||
|
||||
const filter = useMemo<NostrQuery | undefined>(() => {
|
||||
const filter = useMemo<Filter | undefined>(() => {
|
||||
if (selected === "global") return {};
|
||||
if (!people) return undefined;
|
||||
return { authors: people.map((p) => p.pubkey) };
|
||||
|
@ -23,10 +23,9 @@ import { Event, kinds } from "nostr-tools";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import createDefer, { Deferred } from "../../classes/deferred";
|
||||
import useEventRelays from "../../hooks/use-event-relays";
|
||||
import { RelayFavicon } from "../../components/relay-favicon";
|
||||
import { ExternalLinkIcon } from "../../components/icons";
|
||||
import { getEventCoordinate, getEventUID, isReplaceable } from "../../helpers/nostr/event";
|
||||
import { getEventCoordinate, isReplaceable } from "../../helpers/nostr/event";
|
||||
import { Tag } from "../../types/nostr-event";
|
||||
import { EmbedEvent } from "../../components/embed-event";
|
||||
import { useWriteRelays } from "../../hooks/use-client-relays";
|
||||
@ -53,8 +52,7 @@ export default function DeleteEventProvider({ children }: PropsWithChildren) {
|
||||
const [defer, setDefer] = useState<Deferred<void>>();
|
||||
const [reason, setReason] = useState("");
|
||||
|
||||
const eventRelays = useEventRelays(event && getEventUID(event));
|
||||
const writeRelays = useWriteRelays(eventRelays);
|
||||
const writeRelays = useWriteRelays();
|
||||
|
||||
const deleteEvent = useCallback((event: Event) => {
|
||||
setEvent(event);
|
||||
|
@ -1,13 +1,12 @@
|
||||
import dayjs from "dayjs";
|
||||
import debug, { Debugger } from "debug";
|
||||
import _throttle from "lodash.throttle";
|
||||
import { kinds } from "nostr-tools";
|
||||
import { Filter, kinds } from "nostr-tools";
|
||||
|
||||
import NostrSubscription from "../classes/nostr-subscription";
|
||||
import SuperMap from "../classes/super-map";
|
||||
import { NostrEvent } from "../types/nostr-event";
|
||||
import Subject from "../classes/subject";
|
||||
import { NostrQuery } from "../types/nostr-relay";
|
||||
import { logger } from "../helpers/debug";
|
||||
import db from "./db";
|
||||
import createDefer, { Deferred } from "../classes/deferred";
|
||||
@ -103,7 +102,7 @@ class ChannelMetadataRelayLoader {
|
||||
// update the subscription
|
||||
if (needsUpdate) {
|
||||
if (this.requested.size > 0) {
|
||||
const query: NostrQuery = {
|
||||
const query: Filter = {
|
||||
kinds: [kinds.ChannelMetadata],
|
||||
"#e": Array.from(this.requested.keys()),
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { NostrEvent, kinds } from "nostr-tools";
|
||||
import { getEventUID } from "nostr-idb";
|
||||
|
||||
import { getEventUID } from "../helpers/nostr/event";
|
||||
import ControlledObservable from "../classes/controlled-observable";
|
||||
|
||||
const deleteEventStream = new ControlledObservable<NostrEvent>();
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { Filter } from "nostr-tools";
|
||||
import stringify from "json-stringify-deterministic";
|
||||
|
||||
import Subject from "../classes/subject";
|
||||
import SuperMap from "../classes/super-map";
|
||||
import { NostrRequestFilter } from "../types/nostr-relay";
|
||||
import { localRelay } from "./local-relay";
|
||||
|
||||
class EventCountService {
|
||||
subjects = new SuperMap<string, Subject<number>>(() => new Subject<number>());
|
||||
|
||||
stringifyFilter(filter: NostrRequestFilter) {
|
||||
stringifyFilter(filter: Filter | Filter[]) {
|
||||
return stringify(filter);
|
||||
}
|
||||
|
||||
requestCount(filter: NostrRequestFilter, alwaysRequest = false) {
|
||||
requestCount(filter: Filter | Filter[], alwaysRequest = false) {
|
||||
const key = this.stringifyFilter(filter);
|
||||
const sub = this.subjects.get(key);
|
||||
|
||||
@ -26,7 +26,7 @@ class EventCountService {
|
||||
return sub;
|
||||
}
|
||||
|
||||
getCount(filter: NostrRequestFilter) {
|
||||
getCount(filter: Filter | Filter[]) {
|
||||
const key = this.stringifyFilter(filter);
|
||||
const sub = this.subjects.get(key);
|
||||
return sub;
|
||||
|
@ -1,25 +1,36 @@
|
||||
import { createCoordinate } from "../classes/batch-kind-pubkey-loader";
|
||||
import { NostrEvent } from "../types/nostr-event";
|
||||
import { getEventRelays } from "./event-relays";
|
||||
import relayScoreboardService from "./relay-scoreboard";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import type { AddressPointer, EventPointer } from "nostr-tools/lib/types/nip19";
|
||||
|
||||
function pickBestRelays(relays: string[]) {
|
||||
// ignore local relays
|
||||
relays = relays.filter((url) => !url.includes("://localhost") && !url.includes("://192.168"));
|
||||
import { NostrEvent, isDTag } from "../types/nostr-event";
|
||||
import relayScoreboardService from "./relay-scoreboard";
|
||||
import userMailboxesService from "./user-mailboxes";
|
||||
import singleEventService from "./single-event";
|
||||
import { isReplaceable } from "../helpers/nostr/event";
|
||||
|
||||
return relayScoreboardService.getRankedRelays(relays);
|
||||
function pickBestRelays(relays: Iterable<string>) {
|
||||
// ignore local relays
|
||||
const urls = Array.from(relays).filter((url) => !url.includes("://localhost") && !url.includes("://192.168"));
|
||||
return relayScoreboardService.getRankedRelays(urls);
|
||||
}
|
||||
|
||||
function getAddressPointerRelayHint(pointer: AddressPointer): string | undefined {
|
||||
let relays = getEventRelays(createCoordinate(pointer.kind, pointer.pubkey, pointer.identifier)).value;
|
||||
return pickBestRelays(relays)[0];
|
||||
const authorRelays = userMailboxesService.getMailboxes(pointer.pubkey).value;
|
||||
return pickBestRelays(authorRelays?.outbox || [])[0];
|
||||
}
|
||||
|
||||
function getEventPointerRelayHints(pointerOrId: string | EventPointer): string[] {
|
||||
let relays =
|
||||
typeof pointerOrId === "string" ? getEventRelays(pointerOrId).value : getEventRelays(pointerOrId.id).value;
|
||||
return pickBestRelays(relays);
|
||||
if (typeof pointerOrId === "string") {
|
||||
const event = singleEventService.getSubject(pointerOrId).value;
|
||||
if (event) {
|
||||
const authorRelays = userMailboxesService.getMailboxes(event.pubkey).value;
|
||||
return pickBestRelays(authorRelays?.outbox || []);
|
||||
}
|
||||
} else if (pointerOrId.author) {
|
||||
const authorRelays = userMailboxesService.getMailboxes(pointerOrId.author).value;
|
||||
return pickBestRelays(authorRelays?.outbox || []);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
function getEventPointerRelayHint(pointerOrId: string | EventPointer): string | undefined {
|
||||
return getEventPointerRelayHints(pointerOrId)[0];
|
||||
@ -30,11 +41,21 @@ function getEventRelayHint(event: NostrEvent): string | undefined {
|
||||
}
|
||||
|
||||
function getEventRelayHints(event: NostrEvent, count = 2): string[] {
|
||||
// NOTE: in the future try to use the events authors relays
|
||||
const authorRelays = userMailboxesService.getMailboxes(event.pubkey).value?.outbox || [];
|
||||
|
||||
let relays = getEventRelays(event.id).value;
|
||||
return pickBestRelays(authorRelays).slice(0, count);
|
||||
}
|
||||
|
||||
return pickBestRelays(relays).slice(0, count);
|
||||
function getSharableEventAddress(event: NostrEvent, relays?: Iterable<string>) {
|
||||
relays = relays || relayHintService.getEventRelayHints(event, 2);
|
||||
|
||||
if (isReplaceable(event.kind)) {
|
||||
const d = event.tags.find(isDTag)?.[1];
|
||||
if (!d) return null;
|
||||
return nip19.naddrEncode({ kind: event.kind, identifier: d, pubkey: event.pubkey, relays: Array.from(relays) });
|
||||
} else {
|
||||
return nip19.neventEncode({ id: event.id, kind: event.kind, relays: Array.from(relays), author: event.pubkey });
|
||||
}
|
||||
}
|
||||
|
||||
const relayHintService = {
|
||||
@ -42,6 +63,7 @@ const relayHintService = {
|
||||
getEventRelayHint,
|
||||
getEventPointerRelayHint,
|
||||
getEventPointerRelayHints,
|
||||
getSharableEventAddress,
|
||||
getAddressPointerRelayHint,
|
||||
pickBestRelays,
|
||||
};
|
||||
|
@ -1,49 +0,0 @@
|
||||
import { AbstractRelay } from "nostr-tools";
|
||||
import { PersistentSubject } from "../classes/subject";
|
||||
import { getEventUID } from "../helpers/nostr/event";
|
||||
import { NostrEvent } from "../types/nostr-event";
|
||||
|
||||
const eventRelays = new Map<string, PersistentSubject<string[]>>();
|
||||
|
||||
export function getEventRelays(id: string) {
|
||||
let relays = eventRelays.get(id);
|
||||
if (!relays) {
|
||||
relays = new PersistentSubject<string[]>([]);
|
||||
eventRelays.set(id, relays);
|
||||
}
|
||||
return relays;
|
||||
}
|
||||
|
||||
function addRelay(id: string, relay: string) {
|
||||
const relays = getEventRelays(id);
|
||||
|
||||
if (!relays.value.includes(relay)) {
|
||||
relays.next(relays.value.concat(relay));
|
||||
}
|
||||
}
|
||||
|
||||
export function handleEventFromRelay(relay: AbstractRelay, event: NostrEvent) {
|
||||
const uid = getEventUID(event);
|
||||
|
||||
addRelay(uid, relay.url);
|
||||
if (event.id !== uid) addRelay(event.id, relay.url);
|
||||
}
|
||||
|
||||
// TODO: track events from relays
|
||||
// relayPoolService.onRelayCreated.subscribe((relay) => {
|
||||
// relay.onEvent.subscribe((message) => {
|
||||
// handleEventFromRelay(relay, message[2]);
|
||||
// });
|
||||
// });
|
||||
|
||||
const eventRelaysService = {
|
||||
getEventRelays,
|
||||
handleEventFromRelay,
|
||||
};
|
||||
|
||||
if (import.meta.env.DEV) {
|
||||
//@ts-ignore
|
||||
window.eventRelaysService = eventRelaysService;
|
||||
}
|
||||
|
||||
export default eventRelaysService;
|
@ -3,14 +3,14 @@ import dayjs from "dayjs";
|
||||
import { nanoid } from "nanoid";
|
||||
import { bytesToHex, hexToBytes } from "@noble/hashes/utils";
|
||||
|
||||
import { DraftNostrEvent, NostrEvent, isPTag } from "../types/nostr-event";
|
||||
import createDefer, { Deferred } from "../classes/deferred";
|
||||
import MultiSubscription from "../classes/multi-subscription";
|
||||
import { getPubkeyFromDecodeResult, isHexKey, normalizeToHexPubkey } from "../helpers/nip19";
|
||||
import { logger } from "../helpers/debug";
|
||||
import { DraftNostrEvent, NostrEvent, isPTag } from "../types/nostr-event";
|
||||
import createDefer, { Deferred } from "../classes/deferred";
|
||||
import { alwaysVerify } from "./verify-event";
|
||||
import { NostrConnectAccount } from "./account";
|
||||
import { safeRelayUrl } from "../helpers/relay";
|
||||
import { alwaysVerify } from "./verify-event";
|
||||
import { truncateId } from "../helpers/string";
|
||||
|
||||
export function isErrorResponse(response: any): response is NostrConnectErrorResponse {
|
||||
|
@ -2,18 +2,18 @@ import { AbstractRelay, NostrEvent } from "nostr-tools";
|
||||
import _throttle from "lodash.throttle";
|
||||
|
||||
import SuperMap from "../classes/super-map";
|
||||
import { logger } from "../helpers/debug";
|
||||
import { getEventCoordinate } from "../helpers/nostr/event";
|
||||
import { localRelay } from "./local-relay";
|
||||
import EventStore from "../classes/event-store";
|
||||
import Subject from "../classes/subject";
|
||||
import BatchKindPubkeyLoader, { createCoordinate } from "../classes/batch-kind-pubkey-loader";
|
||||
import Process from "../classes/process";
|
||||
import { logger } from "../helpers/debug";
|
||||
import { getEventCoordinate } from "../helpers/nostr/event";
|
||||
import { localRelay } from "./local-relay";
|
||||
import relayPoolService from "./relay-pool";
|
||||
import { alwaysVerify } from "./verify-event";
|
||||
import { truncateId } from "../helpers/string";
|
||||
import UserSquare from "../components/icons/user-square";
|
||||
import Process from "../classes/process";
|
||||
import processManager from "./process-manager";
|
||||
import UserSquare from "../components/icons/user-square";
|
||||
|
||||
export type RequestOptions = {
|
||||
/** Always request the event from the relays */
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { nip04, getPublicKey, finalizeEvent } from "nostr-tools";
|
||||
import { hexToBytes } from "@noble/hashes/utils";
|
||||
|
||||
import { DraftNostrEvent, NostrEvent } from "../types/nostr-event";
|
||||
import { Account } from "./account";
|
||||
@ -6,7 +7,6 @@ import db from "./db";
|
||||
import serialPortService from "./serial-port";
|
||||
import amberSignerService from "./amber-signer";
|
||||
import nostrConnectService from "./nostr-connect";
|
||||
import { hexToBytes } from "@noble/hashes/utils";
|
||||
import { alwaysVerify } from "./verify-event";
|
||||
|
||||
const decryptedKeys = new Map<string, string | Promise<string>>();
|
||||
|
@ -1,6 +0,0 @@
|
||||
import { Filter } from "nostr-tools";
|
||||
|
||||
/** @deprecated use Filter instead */
|
||||
export type NostrQuery = Filter;
|
||||
|
||||
export type NostrRequestFilter = Filter | Filter[];
|
@ -35,7 +35,6 @@ import BadgeAwardCard from "./components/badge-award-card";
|
||||
import TimelineLoader from "../../classes/timeline-loader";
|
||||
import { ErrorBoundary } from "../../components/error-boundary";
|
||||
import useParamsAddressPointer from "../../hooks/use-params-address-pointer";
|
||||
import { EventRelays } from "../../components/note/event-relays";
|
||||
|
||||
function BadgeActivityTab({ timeline }: { timeline: TimelineLoader }) {
|
||||
const awards = useSubject(timeline.timeline);
|
||||
@ -108,8 +107,6 @@ function BadgeDetailsPage({ badge }: { badge: NostrEvent }) {
|
||||
|
||||
<Spacer />
|
||||
|
||||
<EventRelays event={badge} />
|
||||
|
||||
<BadgeMenu aria-label="More options" badge={badge} />
|
||||
</Flex>
|
||||
|
||||
|
@ -5,11 +5,11 @@ import { Link as RouterLink } from "react-router-dom";
|
||||
import { getBadgeAwardBadge, getBadgeAwardPubkeys, getBadgeImage, getBadgeName } from "../../../helpers/nostr/badges";
|
||||
import useReplaceableEvent from "../../../hooks/use-replaceable-event";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import UserLink from "../../../components/user/user-link";
|
||||
import Timestamp from "../../../components/timestamp";
|
||||
import UserAvatarLink from "../../../components/user/user-avatar-link";
|
||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||
import useShareableEventAddress from "../../../hooks/use-shareable-event-address";
|
||||
|
||||
const UserCard = memo(({ pubkey }: { pubkey: string }) => (
|
||||
<Flex gap="2" alignItems="center">
|
||||
@ -30,11 +30,11 @@ export default function BadgeAwardCard({ award, showImage = true }: { award: Nos
|
||||
const awards = getBadgeAwardPubkeys(award);
|
||||
const collapsed = !showAll.isOpen && awards.length > 10;
|
||||
|
||||
const naddr = getSharableEventAddress(badge);
|
||||
const address = useShareableEventAddress(badge);
|
||||
return (
|
||||
<Card as={LinkBox} p="2" variant="outline" gap="2" flexDirection={["column", null, "row"]} ref={ref}>
|
||||
{showImage && (
|
||||
<Flex as={RouterLink} to={`/badges/${naddr}`} direction="column" overflow="hidden" gap="2" w="40" mx="auto">
|
||||
<Flex as={RouterLink} to={`/badges/${address}`} direction="column" overflow="hidden" gap="2" w="40" mx="auto">
|
||||
<Image aspectRatio={1} src={getBadgeImage(badge)?.src ?? ""} w="40" />
|
||||
</Flex>
|
||||
)}
|
||||
@ -43,7 +43,7 @@ export default function BadgeAwardCard({ award, showImage = true }: { award: Nos
|
||||
<UserAvatarLink pubkey={award.pubkey} size="sm" />
|
||||
<UserLink pubkey={award.pubkey} fontWeight="bold" />
|
||||
<Text>Awarded</Text>
|
||||
<Link as={RouterLink} to={`/badges/${naddr}`} fontWeight="bold">
|
||||
<Link as={RouterLink} to={`/badges/${address}`} fontWeight="bold">
|
||||
{getBadgeName(badge)}
|
||||
</Link>
|
||||
<Text>To</Text>
|
||||
|
@ -5,7 +5,6 @@ import { kinds } from "nostr-tools";
|
||||
|
||||
import UserAvatarLink from "../../../components/user/user-avatar-link";
|
||||
import UserLink from "../../../components/user/user-link";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import { getEventCoordinate } from "../../../helpers/nostr/event";
|
||||
import BadgeMenu from "./badge-menu";
|
||||
@ -13,9 +12,10 @@ import { getBadgeImage, getBadgeName } from "../../../helpers/nostr/badges";
|
||||
import Timestamp from "../../../components/timestamp";
|
||||
import useEventCount from "../../../hooks/use-event-count";
|
||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||
import useShareableEventAddress from "../../../hooks/use-shareable-event-address";
|
||||
|
||||
function BadgeCard({ badge, ...props }: Omit<CardProps, "children"> & { badge: NostrEvent }) {
|
||||
const naddr = getSharableEventAddress(badge);
|
||||
const address = useShareableEventAddress(badge);
|
||||
const image = getBadgeImage(badge);
|
||||
const navigate = useNavigate();
|
||||
|
||||
@ -27,11 +27,11 @@ function BadgeCard({ badge, ...props }: Omit<CardProps, "children"> & { badge: N
|
||||
return (
|
||||
<Card ref={ref} variant="outline" {...props}>
|
||||
{image && (
|
||||
<Image src={image.src} cursor="pointer" onClick={() => navigate(`/badges/${naddr}`)} borderRadius="lg" />
|
||||
<Image src={image.src} cursor="pointer" onClick={() => navigate(`/badges/${address}`)} borderRadius="lg" />
|
||||
)}
|
||||
<CardHeader display="flex" alignItems="center" p="2" pb="0">
|
||||
<Heading size="md">
|
||||
<Link as={RouterLink} to={`/badges/${naddr}`}>
|
||||
<Link as={RouterLink} to={`/badges/${address}`}>
|
||||
{getBadgeName(badge)}
|
||||
</Link>
|
||||
</Heading>
|
||||
|
@ -19,7 +19,6 @@ import { kinds } from "nostr-tools";
|
||||
import { NostrEvent, isETag } from "../../../types/nostr-event";
|
||||
import { getEventCommunityPointer, getPostSubject } from "../../../helpers/nostr/communities";
|
||||
import { useNavigateInDrawer } from "../../../providers/drawer-sub-view-provider";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import HoverLinkOverlay from "../../../components/hover-link-overlay";
|
||||
import { CompactNoteContent } from "../../../components/compact-note-content";
|
||||
import { parseHardcodedNoteContent } from "../../../helpers/nostr/event";
|
||||
@ -30,6 +29,7 @@ import { useReadRelays } from "../../../hooks/use-client-relays";
|
||||
import useSingleEvent from "../../../hooks/use-single-event";
|
||||
import CommunityPostMenu from "./community-post-menu";
|
||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||
import useShareableEventAddress from "../../../hooks/use-shareable-event-address";
|
||||
|
||||
export function ApprovalIcon({ approval }: { approval: NostrEvent }) {
|
||||
const ref = useEventIntersectionRef<HTMLAnchorElement>(approval);
|
||||
@ -45,9 +45,10 @@ export type CommunityPostPropTypes = {
|
||||
|
||||
function PostSubject({ event }: { event: NostrEvent }) {
|
||||
const subject = getPostSubject(event);
|
||||
const address = useShareableEventAddress(event);
|
||||
|
||||
const navigate = useNavigateInDrawer();
|
||||
const to = `/n/${getSharableEventAddress(event)}`;
|
||||
const to = `/n/${address}`;
|
||||
const handleClick = useCallback<MouseEventHandler>(
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
|
@ -19,8 +19,8 @@ import dayjs from "dayjs";
|
||||
|
||||
import { EMOJI_PACK_KIND } from "../../../helpers/nostr/emoji-packs";
|
||||
import { DraftNostrEvent } from "../../../types/nostr-event";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import { usePublishEvent } from "../../../providers/global/publish-provider";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export default function EmojiPackCreateModal({ onClose, ...props }: Omit<ModalProps, "children">) {
|
||||
const publish = usePublishEvent();
|
||||
@ -40,7 +40,7 @@ export default function EmojiPackCreateModal({ onClose, ...props }: Omit<ModalPr
|
||||
};
|
||||
|
||||
const pub = await publish("Create emoji pack", draft);
|
||||
if (pub) navigate(`/emojis/${getSharableEventAddress(pub.event)}`);
|
||||
if (pub) navigate(`/emojis/${relayHintService.getSharableEventAddress(pub.event)}`);
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -14,7 +14,6 @@ import {
|
||||
|
||||
import UserAvatarLink from "../../../components/user/user-avatar-link";
|
||||
import UserLink from "../../../components/user/user-link";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import EmojiPackFavoriteButton from "./emoji-pack-favorite-button";
|
||||
import { getEmojisFromPack, getPackName } from "../../../helpers/nostr/emoji-packs";
|
||||
@ -22,10 +21,11 @@ import EmojiPackMenu from "./emoji-pack-menu";
|
||||
import NoteZapButton from "../../../components/note/note-zap-button";
|
||||
import HoverLinkOverlay from "../../../components/hover-link-overlay";
|
||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||
import useShareableEventAddress from "../../../hooks/use-shareable-event-address";
|
||||
|
||||
export default function EmojiPackCard({ pack, ...props }: Omit<CardProps, "children"> & { pack: NostrEvent }) {
|
||||
const emojis = getEmojisFromPack(pack);
|
||||
const naddr = getSharableEventAddress(pack);
|
||||
const address = useShareableEventAddress(pack);
|
||||
|
||||
// if there is a parent intersection observer, register this card
|
||||
const ref = useEventIntersectionRef(pack);
|
||||
@ -34,7 +34,7 @@ export default function EmojiPackCard({ pack, ...props }: Omit<CardProps, "child
|
||||
<Card ref={ref} variant="outline" {...props}>
|
||||
<CardHeader display="flex" gap="2" alignItems="center" p="2" pb="0" flexWrap="wrap">
|
||||
<Heading size="md">
|
||||
<HoverLinkOverlay as={RouterLink} to={`/emojis/${naddr}`}>
|
||||
<HoverLinkOverlay as={RouterLink} to={`/emojis/${address}`}>
|
||||
{getPackName(pack)}
|
||||
</HoverLinkOverlay>
|
||||
</Heading>
|
||||
|
@ -34,7 +34,7 @@ import Timestamp from "../../components/timestamp";
|
||||
import useParamsAddressPointer from "../../hooks/use-params-address-pointer";
|
||||
import { usePublishEvent } from "../../providers/global/publish-provider";
|
||||
import NoteZapButton from "../../components/note/note-zap-button";
|
||||
import QuoteRepostButton from "../../components/note/quote-repost-button";
|
||||
import QuoteEventButton from "../../components/note/quote-event-button";
|
||||
|
||||
function AddEmojiForm({ onAdd }: { onAdd: (values: { name: string; url: string }) => void }) {
|
||||
const { register, handleSubmit, watch, getValues, reset } = useForm({
|
||||
@ -175,7 +175,7 @@ function EmojiPackPage({ pack }: { pack: NostrEvent }) {
|
||||
|
||||
<ButtonGroup variant="ghost">
|
||||
<NoteZapButton event={pack} />
|
||||
<QuoteRepostButton event={pack} />
|
||||
<QuoteEventButton event={pack} />
|
||||
<EmojiPackFavoriteButton pack={pack} />
|
||||
</ButtonGroup>
|
||||
|
||||
|
@ -4,7 +4,6 @@ import { ButtonGroup, Card, CardBody, CardHeader, CardProps, Flex, Heading, Link
|
||||
|
||||
import UserAvatarLink from "../../../components/user/user-avatar-link";
|
||||
import UserLink from "../../../components/user/user-link";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import { getGoalClosedDate, getGoalName } from "../../../helpers/nostr/goal";
|
||||
import GoalMenu from "./goal-menu";
|
||||
@ -14,9 +13,10 @@ import GoalZapButton from "./goal-zap-button";
|
||||
import GoalTopZappers from "./goal-top-zappers";
|
||||
import Timestamp from "../../../components/timestamp";
|
||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||
import useShareableEventAddress from "../../../hooks/use-shareable-event-address";
|
||||
|
||||
function GoalCard({ goal, ...props }: Omit<CardProps, "children"> & { goal: NostrEvent }) {
|
||||
const nevent = getSharableEventAddress(goal);
|
||||
const address = useShareableEventAddress(goal);
|
||||
|
||||
// if there is a parent intersection observer, register this card
|
||||
const ref = useEventIntersectionRef(goal);
|
||||
@ -27,7 +27,7 @@ function GoalCard({ goal, ...props }: Omit<CardProps, "children"> & { goal: Nost
|
||||
<Card ref={ref} variant="outline" {...props}>
|
||||
<CardHeader display="flex" gap="2" alignItems="center" p="2" pb="0" flexWrap="wrap">
|
||||
<Heading size="md">
|
||||
<Link as={RouterLink} to={`/goals/${nevent}`}>
|
||||
<Link as={RouterLink} to={`/goals/${address}`}>
|
||||
{getGoalName(goal)}
|
||||
</Link>
|
||||
</Heading>
|
||||
|
@ -26,7 +26,6 @@ import {
|
||||
getReferencesFromList,
|
||||
isSpecialListKind,
|
||||
} from "../../../helpers/nostr/lists";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import useReplaceableEvent from "../../../hooks/use-replaceable-event";
|
||||
import ListFavoriteButton from "./list-favorite-button";
|
||||
@ -41,6 +40,7 @@ import File02 from "../../../components/icons/file-02";
|
||||
import SimpleLikeButton from "../../../components/event-reactions/simple-like-button";
|
||||
import { createCoordinate } from "../../../classes/batch-kind-pubkey-loader";
|
||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export function ListCardContent({ list, ...props }: Omit<CardProps, "children"> & { list: NostrEvent }) {
|
||||
const people = getPubkeysFromList(list);
|
||||
@ -83,7 +83,10 @@ export function ListCardContent({ list, ...props }: Omit<CardProps, "children">
|
||||
|
||||
export function createListLink(list: NostrEvent) {
|
||||
const isSpecialList = isSpecialListKind(list.kind);
|
||||
return "/lists/" + (isSpecialList ? createCoordinate(list.kind, list.pubkey) : getSharableEventAddress(list));
|
||||
return (
|
||||
"/lists/" +
|
||||
(isSpecialList ? createCoordinate(list.kind, list.pubkey) : relayHintService.getSharableEventAddress(list))
|
||||
);
|
||||
}
|
||||
|
||||
function ListCardRender({
|
||||
|
@ -2,15 +2,15 @@ import { Image, MenuItem } from "@chakra-ui/react";
|
||||
|
||||
import { NostrEvent, isPTag } from "../../../types/nostr-event";
|
||||
import { DotsMenuButton, MenuIconButtonProps } from "../../../components/dots-menu-button";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import DeleteEventMenuItem from "../../../components/common-menu-items/delete-event";
|
||||
import OpenInAppMenuItem from "../../../components/common-menu-items/open-in-app";
|
||||
import CopyEmbedCodeMenuItem from "../../../components/common-menu-items/copy-embed-code";
|
||||
import { isSpecialListKind } from "../../../helpers/nostr/lists";
|
||||
import DebugEventMenuItem from "../../../components/debug-modal/debug-event-menu-item";
|
||||
import useShareableEventAddress from "../../../hooks/use-shareable-event-address";
|
||||
|
||||
export default function ListMenu({ list, ...props }: { list: NostrEvent } & Omit<MenuIconButtonProps, "children">) {
|
||||
const naddr = getSharableEventAddress(list);
|
||||
const address = useShareableEventAddress(list);
|
||||
const isSpecial = isSpecialListKind(list.kind);
|
||||
|
||||
const hasPeople = list.tags.some(isPTag);
|
||||
@ -24,7 +24,7 @@ export default function ListMenu({ list, ...props }: { list: NostrEvent } & Omit
|
||||
{hasPeople && (
|
||||
<MenuItem
|
||||
icon={<Image w="4" h="4" src="https://framerusercontent.com/images/3S3Pyvkh2tEvvKyX47QrUq7XQLk.png" />}
|
||||
onClick={() => window.open(`https://www.makeprisms.com/create/${naddr}`, "_blank")}
|
||||
onClick={() => window.open(`https://www.makeprisms.com/create/${address}`, "_blank")}
|
||||
>
|
||||
Create $prism
|
||||
</MenuItem>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Button, Divider, Flex, Heading, Image, Link, SimpleGrid, Spacer, useDisclosure } from "@chakra-ui/react";
|
||||
import { Button, Flex, Heading, Image, Link, SimpleGrid, Spacer, useDisclosure } from "@chakra-ui/react";
|
||||
import { useNavigate, Link as RouterLink, Navigate } from "react-router-dom";
|
||||
import { kinds } from "nostr-tools";
|
||||
|
||||
@ -8,7 +8,6 @@ import ListCard from "./components/list-card";
|
||||
import { getEventUID } from "../../helpers/nostr/event";
|
||||
import useUserLists from "../../hooks/use-user-lists";
|
||||
import NewListModal from "./components/new-list-modal";
|
||||
import { getSharableEventAddress } from "../../helpers/nip19";
|
||||
import {
|
||||
BOOKMARK_LIST_KIND,
|
||||
COMMUNITIES_LIST_KIND,
|
||||
@ -19,6 +18,7 @@ import {
|
||||
} from "../../helpers/nostr/lists";
|
||||
import useFavoriteLists from "../../hooks/use-favorite-lists";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import relayHintService from "../../services/event-relay-hint";
|
||||
|
||||
function ListsHomePage() {
|
||||
const account = useCurrentAccount()!;
|
||||
@ -102,7 +102,7 @@ function ListsHomePage() {
|
||||
<NewListModal
|
||||
isOpen
|
||||
onClose={newList.onClose}
|
||||
onCreated={(list) => navigate(`/lists/${getSharableEventAddress(list)}`)}
|
||||
onCreated={(list) => navigate(`/lists/${relayHintService.getSharableEventAddress(list)}`)}
|
||||
/>
|
||||
)}
|
||||
</VerticalPageLayout>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { ReactNode, forwardRef, memo, useMemo } from "react";
|
||||
import { AvatarGroup, Flex, IconButton, IconButtonProps, Text, useDisclosure } from "@chakra-ui/react";
|
||||
import { AvatarGroup, ButtonGroup, Flex, IconButton, IconButtonProps, Text, useDisclosure } from "@chakra-ui/react";
|
||||
import { kinds, nip18, nip25 } from "nostr-tools";
|
||||
|
||||
import useCurrentAccount from "../../../hooks/use-current-account";
|
||||
@ -25,6 +25,7 @@ import useSingleEvent from "../../../hooks/use-single-event";
|
||||
import NotificationIconEntry from "./notification-icon-entry";
|
||||
import { CategorizedEvent, NotificationType, typeSymbol } from "../../../classes/notifications";
|
||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||
import ZapReceiptMenu from "../../../components/zap/zap-receipt-menu";
|
||||
|
||||
export const ExpandableToggleButton = ({
|
||||
toggle,
|
||||
@ -128,7 +129,10 @@ const ZapNotification = forwardRef<HTMLDivElement, { event: NostrEvent }>(({ eve
|
||||
</AvatarGroup>
|
||||
<Text>{readablizeSats(zap.payment.amount / 1000)} sats</Text>
|
||||
{zap.request.content && <Text>{zap.request.content}</Text>}
|
||||
{eventJSX !== null && <ExpandableToggleButton aria-label="Toggle event" ml="auto" toggle={expanded} />}
|
||||
<ButtonGroup size="sm" variant="ghost" ml="auto">
|
||||
{eventJSX !== null && <ExpandableToggleButton aria-label="Toggle event" toggle={expanded} />}
|
||||
<ZapReceiptMenu zap={zap.event} aria-label="More Options" />
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
{expanded.isOpen && eventJSX}
|
||||
</NotificationIconEntry>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { memo, useEffect, useMemo } from "react";
|
||||
import { Button, ButtonGroup, Flex, IconButton, Input, useDisclosure } from "@chakra-ui/react";
|
||||
import { memo, useMemo } from "react";
|
||||
import { Button, ButtonGroup, Flex, IconButton, Input } from "@chakra-ui/react";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
@ -15,6 +15,7 @@ import NotificationItem from "./components/notification-item";
|
||||
import NotificationTypeToggles from "./notification-type-toggles";
|
||||
import { ChevronLeftIcon, ChevronRightIcon } from "../../components/icons";
|
||||
import useRouteSearchValue from "../../hooks/use-route-search-value";
|
||||
import useLocalStorageDisclosure from "../../hooks/use-localstorage-disclosure";
|
||||
import { NotificationType, typeSymbol } from "../../classes/notifications";
|
||||
|
||||
const DATE_FORMAT = "YYYY-MM-DD";
|
||||
@ -94,15 +95,11 @@ const NotificationsTimeline = memo(
|
||||
function NotificationsPage() {
|
||||
const { timeline } = useNotifications();
|
||||
|
||||
const showReplies = useDisclosure({ defaultIsOpen: localStorage.getItem("notifications-show-replies") !== "false" });
|
||||
const showMentions = useDisclosure({
|
||||
defaultIsOpen: localStorage.getItem("notifications-show-mentions") !== "false",
|
||||
});
|
||||
const showZaps = useDisclosure({ defaultIsOpen: localStorage.getItem("notifications-show-zaps") !== "false" });
|
||||
const showReposts = useDisclosure({ defaultIsOpen: localStorage.getItem("notifications-show-reposts") !== "false" });
|
||||
const showReactions = useDisclosure({
|
||||
defaultIsOpen: localStorage.getItem("notifications-show-reactions") !== "false",
|
||||
});
|
||||
const showReplies = useLocalStorageDisclosure("notifications-show-replies");
|
||||
const showMentions = useLocalStorageDisclosure("notifications-show-mentions");
|
||||
const showZaps = useLocalStorageDisclosure("notifications-show-zaps");
|
||||
const showReposts = useLocalStorageDisclosure("notifications-show-reposts");
|
||||
const showReactions = useLocalStorageDisclosure("notifications-show-reactions");
|
||||
|
||||
const today = dayjs().format(DATE_FORMAT);
|
||||
const { value: day, setValue: setDay } = useRouteSearchValue(
|
||||
@ -142,15 +139,6 @@ function NotificationsPage() {
|
||||
});
|
||||
};
|
||||
|
||||
// save toggles to localStorage when changed
|
||||
useEffect(() => {
|
||||
localStorage.setItem("notifications-show-replies", String(showReplies.isOpen));
|
||||
localStorage.setItem("notifications-show-mentions", String(showMentions.isOpen));
|
||||
localStorage.setItem("notifications-show-zaps", String(showZaps.isOpen));
|
||||
localStorage.setItem("notifications-show-reposts", String(showReposts.isOpen));
|
||||
localStorage.setItem("notifications-show-reactions", String(showReactions.isOpen));
|
||||
}, [showReplies.isOpen, showMentions.isOpen, showZaps.isOpen, showReposts.isOpen, showReactions.isOpen]);
|
||||
|
||||
const callback = useTimelineCurserIntersectionCallback(timeline);
|
||||
|
||||
return (
|
||||
|
@ -21,23 +21,24 @@ import UserLink from "../../components/user/user-link";
|
||||
import { CompactNoteContent } from "../../components/compact-note-content";
|
||||
import Timestamp from "../../components/timestamp";
|
||||
import HoverLinkOverlay from "../../components/hover-link-overlay";
|
||||
import { getSharableEventAddress } from "../../helpers/nip19";
|
||||
import PeopleListSelection from "../../components/people-list-selection/people-list-selection";
|
||||
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
|
||||
import { useNavigateInDrawer } from "../../providers/drawer-sub-view-provider";
|
||||
import useEventIntersectionRef from "../../hooks/use-event-intersection-ref";
|
||||
import useShareableEventAddress from "../../hooks/use-shareable-event-address";
|
||||
|
||||
const THREAD_KINDS = [kinds.ShortTextNote, TORRENT_COMMENT_KIND];
|
||||
|
||||
function ReplyEntry({ event }: { event: NostrEvent }) {
|
||||
const navigate = useNavigateInDrawer();
|
||||
const address = useShareableEventAddress(event);
|
||||
const onClick = useCallback<MouseEventHandler>(
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
navigate(`/n/${getSharableEventAddress(event)}`);
|
||||
navigate(`/n/${address}`);
|
||||
},
|
||||
[navigate],
|
||||
[navigate, address],
|
||||
);
|
||||
|
||||
return (
|
||||
@ -47,7 +48,7 @@ function ReplyEntry({ event }: { event: NostrEvent }) {
|
||||
<Timestamp timestamp={event.created_at} />
|
||||
</Flex>
|
||||
<CompactNoteContent event={event} maxLength={100} />
|
||||
<HoverLinkOverlay as={RouterLink} to={`/n/${getSharableEventAddress(event)}`} onClick={onClick} />
|
||||
<HoverLinkOverlay as={RouterLink} to={`/n/${address}`} onClick={onClick} />
|
||||
</LinkBox>
|
||||
);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
LinearScale,
|
||||
CategoryScale,
|
||||
} from "chart.js";
|
||||
import { Filter } from "nostr-tools";
|
||||
import _throttle from "lodash.throttle";
|
||||
|
||||
import { useAppTitle } from "../../../hooks/use-app-title";
|
||||
@ -22,7 +23,6 @@ import { groupByTime } from "../../../helpers/notification";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import EventStore from "../../../classes/event-store";
|
||||
import { getSortedKinds, sortByDate } from "../../../helpers/nostr/event";
|
||||
import { NostrQuery } from "../../../types/nostr-relay";
|
||||
import relayPoolService from "../../../services/relay-pool";
|
||||
import EventKindsPieChart from "../../../components/charts/event-kinds-pie-chart";
|
||||
import EventKindsTable from "../../../components/charts/event-kinds-table";
|
||||
@ -107,7 +107,7 @@ export default function RelayDetailsTab({ relay }: { relay: string }) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const loadMore = useCallback(() => {
|
||||
setLoading(true);
|
||||
const query: NostrQuery = { limit: 500 };
|
||||
const query: Filter = { limit: 500 };
|
||||
const last = store.getLastEvent();
|
||||
if (last) query.until = last.created_at;
|
||||
|
||||
|
@ -6,7 +6,7 @@ import { Link as RouterLink } from "react-router-dom";
|
||||
import UserAvatar from "../../../components/user/user-avatar";
|
||||
import UserLink from "../../../components/user/user-link";
|
||||
import StreamStatusBadge from "./status-badge";
|
||||
import useEventNaddr from "../../../hooks/use-event-naddr";
|
||||
import useShareableEventAddress from "../../../hooks/use-shareable-event-address";
|
||||
import StreamHashtags from "./stream-hashtags";
|
||||
import Timestamp from "../../../components/timestamp";
|
||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||
@ -17,7 +17,7 @@ function StreamCard({ stream, ...props }: CardProps & { stream: ParsedStream })
|
||||
// if there is a parent intersection observer, register this card
|
||||
const ref = useEventIntersectionRef(stream.event);
|
||||
|
||||
const naddr = useEventNaddr(stream.event, stream.relays);
|
||||
const naddr = useShareableEventAddress(stream.event, stream.relays);
|
||||
|
||||
return (
|
||||
<Card {...props} ref={ref} position="relative">
|
||||
|
@ -4,22 +4,22 @@ import { Card, CardBody, CardHeader, CardProps, Flex, Heading, Link } from "@cha
|
||||
import { ParsedStream } from "../../../helpers/nostr/stream";
|
||||
import { getGoalName } from "../../../helpers/nostr/goal";
|
||||
import GoalProgress from "../../goals/components/goal-progress";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import GoalTopZappers from "../../goals/components/goal-top-zappers";
|
||||
import GoalZapButton from "../../goals/components/goal-zap-button";
|
||||
import useStreamGoal from "../../../hooks/use-stream-goal";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export default function StreamGoal({ stream, ...props }: Omit<CardProps, "children"> & { stream: ParsedStream }) {
|
||||
const goal = useStreamGoal(stream);
|
||||
|
||||
if (!goal) return null;
|
||||
const nevent = getSharableEventAddress(goal);
|
||||
const address = relayHintService.getSharableEventAddress(goal);
|
||||
|
||||
return (
|
||||
<Card direction="column" gap="1" {...props}>
|
||||
<CardHeader px="2" pt="2" pb="0">
|
||||
<Heading size="md">
|
||||
<Link as={RouterLink} to={`/goals/${nevent}`}>
|
||||
<Link as={RouterLink} to={`/goals/${address}`}>
|
||||
{getGoalName(goal)}
|
||||
</Link>
|
||||
</Heading>
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { useContext } from "react";
|
||||
import { Button, ButtonProps } from "@chakra-ui/react";
|
||||
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import { PostModalContext } from "../../../providers/route/post-modal-provider";
|
||||
import { RepostIcon } from "../../../components/icons";
|
||||
import { ParsedStream } from "../../../helpers/nostr/stream";
|
||||
import useShareableEventAddress from "../../../hooks/use-shareable-event-address";
|
||||
|
||||
export type StreamShareButtonProps = Omit<ButtonProps, "children" | "onClick"> & {
|
||||
stream: ParsedStream;
|
||||
@ -19,8 +19,8 @@ export default function StreamShareButton({
|
||||
const { openModal } = useContext(PostModalContext);
|
||||
|
||||
const handleClick = () => {
|
||||
const nevent = getSharableEventAddress(stream.event);
|
||||
openModal({ initContent: "\nnostr:" + nevent });
|
||||
const address = useShareableEventAddress(stream.event);
|
||||
openModal({ initContent: "\nnostr:" + address });
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { Flex, Heading, SimpleGrid, Switch } from "@chakra-ui/react";
|
||||
import { Filter } from "nostr-tools";
|
||||
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import IntersectionObserverProvider from "../../providers/local/intersection-observer";
|
||||
@ -12,7 +13,6 @@ import PeopleListSelection from "../../components/people-list-selection/people-l
|
||||
import PeopleListProvider, { usePeopleListContext } from "../../providers/local/people-list-provider";
|
||||
import TimelineActionAndStatus from "../../components/timeline/timeline-action-and-status";
|
||||
import useParsedStreams from "../../hooks/use-parsed-streams";
|
||||
import { NostrRequestFilter } from "../../types/nostr-relay";
|
||||
import { useAppTitle } from "../../hooks/use-app-title";
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
@ -36,7 +36,7 @@ function StreamsPage() {
|
||||
);
|
||||
|
||||
const { filter, listId } = usePeopleListContext();
|
||||
const query = useMemo<NostrRequestFilter | undefined>(() => {
|
||||
const query = useMemo<Filter | Filter[] | undefined>(() => {
|
||||
if (!filter) return undefined;
|
||||
return [
|
||||
{ authors: filter.authors, kinds: [STREAM_KIND] },
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { kinds } from "nostr-tools";
|
||||
import { Filter, kinds } from "nostr-tools";
|
||||
|
||||
import { getEventUID } from "../../../../helpers/nostr/event";
|
||||
import { ParsedStream, STREAM_CHAT_MESSAGE_KIND, getATag } from "../../../../helpers/nostr/stream";
|
||||
import useTimelineLoader from "../../../../hooks/use-timeline-loader";
|
||||
import { NostrEvent } from "../../../../types/nostr-event";
|
||||
import useStreamGoal from "../../../../hooks/use-stream-goal";
|
||||
import { NostrQuery } from "../../../../types/nostr-relay";
|
||||
import useUserMuteFilter from "../../../../hooks/use-user-mute-filter";
|
||||
import useClientSideMuteFilter from "../../../../hooks/use-client-side-mute-filter";
|
||||
import { useReadRelays } from "../../../../hooks/use-client-relays";
|
||||
@ -29,7 +28,7 @@ export default function useStreamChatTimeline(stream: ParsedStream) {
|
||||
|
||||
const goal = useStreamGoal(stream);
|
||||
const query = useMemo(() => {
|
||||
const streamQuery: NostrQuery = {
|
||||
const streamQuery: Filter = {
|
||||
"#a": [getATag(stream)],
|
||||
kinds: [STREAM_CHAT_MESSAGE_KIND, kinds.Zap],
|
||||
};
|
||||
|
@ -5,7 +5,6 @@ import { ParsedStream } from "../../../../helpers/nostr/stream";
|
||||
import UserAvatar from "../../../../components/user/user-avatar";
|
||||
import UserLink from "../../../../components/user/user-link";
|
||||
import { NostrEvent } from "../../../../types/nostr-event";
|
||||
import { useRegisterIntersectionEntity } from "../../../../providers/local/intersection-observer";
|
||||
import { LightningIcon } from "../../../../components/icons";
|
||||
import { getParsedZap } from "../../../../helpers/nostr/zaps";
|
||||
import { readablizeSats } from "../../../../helpers/bolt11";
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { memo } from "react";
|
||||
import { Box, Flex, Text } from "@chakra-ui/react";
|
||||
import { Box, ButtonGroup, Flex, Text } from "@chakra-ui/react";
|
||||
|
||||
import { ThreadItem } from "../../../../helpers/thread";
|
||||
import { ParsedZap } from "../../../../helpers/nostr/zaps";
|
||||
@ -10,6 +10,7 @@ import { LightningIcon } from "../../../../components/icons";
|
||||
import { readablizeSats } from "../../../../helpers/bolt11";
|
||||
import TextNoteContents from "../../../../components/note/timeline-note/text-note-contents";
|
||||
import { TrustProvider } from "../../../../providers/local/trust-provider";
|
||||
import ZapReceiptMenu from "../../../../components/zap/zap-receipt-menu";
|
||||
|
||||
const ZapEvent = memo(({ zap }: { zap: ParsedZap }) => {
|
||||
if (!zap.payment.amount) return null;
|
||||
@ -28,6 +29,10 @@ const ZapEvent = memo(({ zap }: { zap: ParsedZap }) => {
|
||||
<Timestamp timestamp={zap.event.created_at} ml="2" />
|
||||
<TextNoteContents event={zap.request} />
|
||||
</Box>
|
||||
|
||||
<ButtonGroup ml="auto" size="sm" variant="ghost">
|
||||
<ZapReceiptMenu zap={zap.event} aria-label="More Options" />
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
</TrustProvider>
|
||||
);
|
||||
|
@ -14,12 +14,11 @@ import Expand01 from "../../../components/icons/expand-01";
|
||||
import Minus from "../../../components/icons/minus";
|
||||
import { useBreakpointValue } from "../../../providers/global/breakpoint-provider";
|
||||
import UserDnsIdentity from "../../../components/user/user-dns-identity";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import useAppSettings from "../../../hooks/use-app-settings";
|
||||
import useThreadColorLevelProps from "../../../hooks/use-thread-color-level-props";
|
||||
import POWIcon from "../../../components/pow/pow-icon";
|
||||
import RepostButton from "../../../components/note/timeline-note/components/repost-button";
|
||||
import QuoteRepostButton from "../../../components/note/quote-repost-button";
|
||||
import QuoteEventButton from "../../../components/note/quote-event-button";
|
||||
import NoteZapButton from "../../../components/note/note-zap-button";
|
||||
import NoteProxyLink from "../../../components/note/timeline-note/components/note-proxy-link";
|
||||
import BookmarkButton from "../../../components/note/bookmark-button";
|
||||
@ -30,6 +29,7 @@ import NoteReactions from "../../../components/note/timeline-note/components/not
|
||||
import ZapBubbles from "../../../components/note/timeline-note/components/zap-bubbles";
|
||||
import DetailsTabs from "./details-tabs";
|
||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export type ThreadItemProps = {
|
||||
post: ThreadItem;
|
||||
@ -68,7 +68,12 @@ function ThreadPost({ post, initShowReplies, focusId, level = -1 }: ThreadItemPr
|
||||
<UserLink pubkey={post.event.pubkey} fontWeight="bold" isTruncated />
|
||||
<UserDnsIdentity pubkey={post.event.pubkey} onlyIcon />
|
||||
<POWIcon event={post.event} boxSize={5} />
|
||||
<Link as={RouterLink} whiteSpace="nowrap" color="current" to={`/n/${getSharableEventAddress(post.event)}`}>
|
||||
<Link
|
||||
as={RouterLink}
|
||||
whiteSpace="nowrap"
|
||||
color="current"
|
||||
to={`/n/${relayHintService.getSharableEventAddress(post.event)}`}
|
||||
>
|
||||
<Timestamp timestamp={post.event.created_at} />
|
||||
</Link>
|
||||
{replies.length > 0 ? (
|
||||
@ -109,7 +114,7 @@ function ThreadPost({ post, initShowReplies, focusId, level = -1 }: ThreadItemPr
|
||||
<ButtonGroup variant="ghost" size="sm">
|
||||
<IconButton aria-label="Reply" title="Reply" onClick={replyForm.onToggle} icon={<ReplyIcon />} />
|
||||
<RepostButton event={post.event} />
|
||||
<QuoteRepostButton event={post.event} />
|
||||
<QuoteEventButton event={post.event} />
|
||||
<NoteZapButton event={post.event} />
|
||||
</ButtonGroup>
|
||||
{!showReactionsOnNewLine && reactionButtons}
|
||||
|
@ -14,11 +14,10 @@ import useSingleEvent from "../../hooks/use-single-event";
|
||||
import useParamsEventPointer from "../../hooks/use-params-event-pointer";
|
||||
import LoadingNostrLink from "../../components/loading-nostr-link";
|
||||
import UserName from "../../components/user/user-name";
|
||||
import { getSharableEventAddress } from "../../helpers/nip19";
|
||||
import UserAvatarLink from "../../components/user/user-avatar-link";
|
||||
import { ReplyIcon } from "../../components/icons";
|
||||
import TimelineNote from "../../components/note/timeline-note";
|
||||
import TimelineLoader from "../../classes/timeline-loader";
|
||||
import relayHintService from "../../services/event-relay-hint";
|
||||
|
||||
function CollapsedReplies({
|
||||
pointer,
|
||||
@ -44,7 +43,7 @@ function CollapsedReplies({
|
||||
<UserAvatarLink pubkey={post.event.pubkey} size="xs" />
|
||||
<UserName pubkey={post.event.pubkey} fontWeight="bold" />
|
||||
{root.id !== pointer.id && <ReplyIcon />}
|
||||
<Link as={RouterLink} to={`/n/${getSharableEventAddress(post.event)}`} isTruncated>
|
||||
<Link as={RouterLink} to={`/n/${relayHintService.getSharableEventAddress(post.event)}`} isTruncated>
|
||||
{post.event.content}
|
||||
</Link>
|
||||
</Card>
|
||||
|
@ -2,7 +2,7 @@ import { useContext } from "react";
|
||||
import { SatelliteCDNFile } from "../../../helpers/satellite-cdn";
|
||||
import { PostModalContext } from "../../../providers/route/post-modal-provider";
|
||||
import { ButtonProps, IconButton } from "@chakra-ui/react";
|
||||
import { QuoteRepostIcon } from "../../../components/icons";
|
||||
import { QuoteEventIcon } from "../../../components/icons";
|
||||
|
||||
export type ShareFileButtonProps = Omit<ButtonProps, "children" | "onClick"> & {
|
||||
file: SatelliteCDNFile;
|
||||
@ -22,7 +22,7 @@ export default function ShareFileButton({
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
icon={<QuoteRepostIcon />}
|
||||
icon={<QuoteEventIcon />}
|
||||
onClick={handleClick}
|
||||
aria-label={ariaLabel || title}
|
||||
title={title}
|
||||
|
@ -9,6 +9,7 @@ import MuteUserMenuItem from "../../../components/common-menu-items/mute-user";
|
||||
import OpenInAppMenuItem from "../../../components/common-menu-items/open-in-app";
|
||||
import CopyEmbedCodeMenuItem from "../../../components/common-menu-items/copy-embed-code";
|
||||
import DebugEventMenuItem from "../../../components/debug-modal/debug-event-menu-item";
|
||||
import QuoteEventMenuItem from "../../../components/common-menu-items/quote-event";
|
||||
|
||||
export default function TorrentMenu({
|
||||
torrent,
|
||||
@ -20,6 +21,7 @@ export default function TorrentMenu({
|
||||
<>
|
||||
<DotsMenuButton {...props}>
|
||||
<OpenInAppMenuItem event={torrent} />
|
||||
<QuoteEventMenuItem event={torrent} />
|
||||
<CopyEmbedCodeMenuItem event={torrent} />
|
||||
<MuteUserMenuItem event={torrent} />
|
||||
<DeleteEventMenuItem event={torrent} />
|
||||
|
@ -7,11 +7,11 @@ import { NostrEvent } from "../../../types/nostr-event";
|
||||
import Timestamp from "../../../components/timestamp";
|
||||
import UserLink from "../../../components/user/user-link";
|
||||
import Magnet from "../../../components/icons/magnet";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import { formatBytes } from "../../../helpers/number";
|
||||
import TorrentMenu from "./torrent-menu";
|
||||
import NoteZapButton from "../../../components/note/note-zap-button";
|
||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||
import useShareableEventAddress from "../../../hooks/use-shareable-event-address";
|
||||
|
||||
type DisplayCategory = { name: string; tags: string[] };
|
||||
|
||||
@ -19,6 +19,7 @@ function TorrentTableRow({ torrent }: { torrent: NostrEvent }) {
|
||||
const ref = useEventIntersectionRef<HTMLTableRowElement>(torrent);
|
||||
|
||||
const magnetLink = useMemo(() => getTorrentMagnetLink(torrent), [torrent]);
|
||||
const address = useShareableEventAddress(torrent);
|
||||
|
||||
const categories: DisplayCategory[] = [];
|
||||
const chain: string[] = [];
|
||||
@ -56,7 +57,7 @@ function TorrentTableRow({ torrent }: { torrent: NostrEvent }) {
|
||||
))}
|
||||
</Td>
|
||||
<Td maxW="lg" overflow="hidden" isTruncated>
|
||||
<Link as={RouterLink} to={`/torrents/${getSharableEventAddress(torrent)}`}>
|
||||
<Link as={RouterLink} to={`/torrents/${address}`}>
|
||||
{getTorrentTitle(torrent)}
|
||||
</Link>
|
||||
</Td>
|
||||
|
@ -42,7 +42,7 @@ import { getThreadReferences } from "../../helpers/nostr/event";
|
||||
import MessageTextCircle01 from "../../components/icons/message-text-circle-01";
|
||||
import useParamsEventPointer from "../../hooks/use-params-event-pointer";
|
||||
import NoteZapButton from "../../components/note/note-zap-button";
|
||||
import QuoteRepostButton from "../../components/note/quote-repost-button";
|
||||
import QuoteEventButton from "../../components/note/quote-event-button";
|
||||
import { TextNoteContents } from "../../components/note/timeline-note/text-note-contents";
|
||||
|
||||
function TorrentDetailsPage({ torrent }: { torrent: NostrEvent }) {
|
||||
@ -75,7 +75,7 @@ function TorrentDetailsPage({ torrent }: { torrent: NostrEvent }) {
|
||||
</Flex>
|
||||
<ButtonGroup variant="ghost" size="sm">
|
||||
<NoteZapButton event={torrent} />
|
||||
<QuoteRepostButton event={torrent} />
|
||||
<QuoteEventButton event={torrent} />
|
||||
<Button as={Link} leftIcon={<Magnet boxSize={5} />} href={getTorrentMagnetLink(torrent)} isExternal>
|
||||
Download torrent
|
||||
</Button>
|
||||
|
@ -12,7 +12,7 @@ import TrackDownloadButton from "./track-download-button";
|
||||
import TrackPlayer from "./track-player";
|
||||
import UserDnsIdentity from "../../../components/user/user-dns-identity";
|
||||
import TrackMenu from "./track-menu";
|
||||
import QuoteRepostButton from "../../../components/note/quote-repost-button";
|
||||
import QuoteEventButton from "../../../components/note/quote-event-button";
|
||||
import NoteZapButton from "../../../components/note/note-zap-button";
|
||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||
|
||||
@ -45,7 +45,7 @@ export default function TrackCard({ track, ...props }: { track: NostrEvent } & O
|
||||
<Button leftIcon={<ReplyIcon />} isDisabled>
|
||||
Comment
|
||||
</Button>
|
||||
<QuoteRepostButton event={track} />
|
||||
<QuoteEventButton event={track} />
|
||||
<NoteZapButton event={track} />
|
||||
</ButtonGroup>
|
||||
<ButtonGroup size="sm" ml="auto">
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Button,
|
||||
Flex,
|
||||
@ -22,14 +23,13 @@ import useUserProfileBadges from "../../../hooks/use-user-profile-badges";
|
||||
import { getBadgeDescription, getBadgeImage, getBadgeName } from "../../../helpers/nostr/badges";
|
||||
import { getEventCoordinate } from "../../../helpers/nostr/event";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import UserAvatarLink from "../../../components/user/user-avatar-link";
|
||||
import UserLink from "../../../components/user/user-link";
|
||||
import Timestamp from "../../../components/timestamp";
|
||||
import { useState } from "react";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
function Badge({ pubkey, badge, award }: { pubkey: string; badge: NostrEvent; award: NostrEvent }) {
|
||||
const naddr = getSharableEventAddress(badge);
|
||||
const naddr = relayHintService.getSharableEventAddress(badge);
|
||||
const modal = useDisclosure();
|
||||
const description = getBadgeDescription(badge);
|
||||
|
||||
|
@ -4,8 +4,8 @@ import { Link as RouterLink } from "react-router-dom";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import { getVideoDuration, getVideoImages, getVideoSummary, getVideoTitle } from "../../../helpers/nostr/flare";
|
||||
import HoverLinkOverlay from "../../../components/hover-link-overlay";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import useEventIntersectionRef from "../../../hooks/use-event-intersection-ref";
|
||||
import useShareableEventAddress from "../../../hooks/use-shareable-event-address";
|
||||
|
||||
export default function VideoCard({ video, ...props }: Omit<CardProps, "children"> & { video: NostrEvent }) {
|
||||
const title = getVideoTitle(video);
|
||||
@ -14,6 +14,7 @@ export default function VideoCard({ video, ...props }: Omit<CardProps, "children
|
||||
const summary = getVideoSummary(video);
|
||||
|
||||
const ref = useEventIntersectionRef(video);
|
||||
const address = useShareableEventAddress(video);
|
||||
|
||||
return (
|
||||
<Card as={LinkBox} {...props}>
|
||||
@ -25,7 +26,7 @@ export default function VideoCard({ video, ...props }: Omit<CardProps, "children
|
||||
backgroundSize="cover"
|
||||
/>
|
||||
<CardHeader p="2">
|
||||
<HoverLinkOverlay as={RouterLink} to={`/videos/${getSharableEventAddress(video)}`}>
|
||||
<HoverLinkOverlay as={RouterLink} to={`/videos/${address}`}>
|
||||
<Heading size="sm" isTruncated>
|
||||
{title}
|
||||
</Heading>
|
||||
|
@ -28,7 +28,7 @@ import UserName from "../../components/user/user-name";
|
||||
import { useBreakpointValue } from "../../providers/global/breakpoint-provider";
|
||||
import SimpleBookmarkButton from "../../components/simple-bookmark-button";
|
||||
import NoteZapButton from "../../components/note/note-zap-button";
|
||||
import QuoteRepostButton from "../../components/note/quote-repost-button";
|
||||
import QuoteEventButton from "../../components/note/quote-event-button";
|
||||
|
||||
function VideoRecommendations({ video }: { video: NostrEvent }) {
|
||||
const readRelays = useReadRelays();
|
||||
@ -77,7 +77,7 @@ function VideoDetailsPage({ video }: { video: NostrEvent }) {
|
||||
<UserFollowButton pubkey={video.pubkey} size="sm" />
|
||||
<ButtonGroup ml="auto" size="sm" variant="ghost">
|
||||
<SimpleBookmarkButton event={video} aria-label="Bookmark video" title="Bookmark video" />
|
||||
<QuoteRepostButton event={video} />
|
||||
<QuoteEventButton event={video} />
|
||||
</ButtonGroup>
|
||||
<VideoMenu video={video} aria-label="More options" size="sm" />
|
||||
</Flex>
|
||||
|
@ -24,12 +24,14 @@ import MarkdownContent from "./components/markdown";
|
||||
import { WIKI_RELAYS } from "../../const";
|
||||
import UserName from "../../components/user/user-name";
|
||||
import WikiPageMenu from "./components/wiki-page-menu";
|
||||
import { getSharableEventAddress } from "../../helpers/nip19";
|
||||
import { ExternalLinkIcon } from "../../components/icons";
|
||||
import useShareableEventAddress from "../../hooks/use-shareable-event-address";
|
||||
|
||||
function WikiComparePage({ base, diff }: { base: NostrEvent; diff: NostrEvent }) {
|
||||
const vertical = useBreakpointValue({ base: true, lg: false }) ?? false;
|
||||
const identical = base.content.trim() === diff.content.trim();
|
||||
const baseAddress = useShareableEventAddress(base);
|
||||
const diffAddress = useShareableEventAddress(diff);
|
||||
|
||||
return (
|
||||
<VerticalPageLayout>
|
||||
@ -40,7 +42,7 @@ function WikiComparePage({ base, diff }: { base: NostrEvent; diff: NostrEvent })
|
||||
<ButtonGroup float="right" size="sm">
|
||||
<IconButton
|
||||
as={RouterLink}
|
||||
to={`/wiki/page/${getSharableEventAddress(base)}`}
|
||||
to={`/wiki/page/${baseAddress}`}
|
||||
icon={<ExternalLinkIcon />}
|
||||
aria-label="Open Page"
|
||||
/>
|
||||
@ -57,7 +59,7 @@ function WikiComparePage({ base, diff }: { base: NostrEvent; diff: NostrEvent })
|
||||
<ButtonGroup float="right" size="sm">
|
||||
<IconButton
|
||||
as={RouterLink}
|
||||
to={`/wiki/page/${getSharableEventAddress(diff)}`}
|
||||
to={`/wiki/page/${diffAddress}`}
|
||||
icon={<ExternalLinkIcon />}
|
||||
aria-label="Open Page"
|
||||
/>
|
||||
|
@ -10,11 +10,12 @@ import DebugEventMenuItem from "../../../components/debug-modal/debug-event-menu
|
||||
import useCurrentAccount from "../../../hooks/use-current-account";
|
||||
import { EditIcon } from "../../../components/icons";
|
||||
import { getPageTopic } from "../../../helpers/nostr/wiki";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import GitBranch02 from "../../../components/icons/git-branch-02";
|
||||
import useShareableEventAddress from "../../../hooks/use-shareable-event-address";
|
||||
|
||||
export default function WikiPageMenu({ page, ...props }: { page: NostrEvent } & Omit<MenuIconButtonProps, "children">) {
|
||||
const account = useCurrentAccount();
|
||||
const address = useShareableEventAddress(page);
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -26,7 +27,7 @@ export default function WikiPageMenu({ page, ...props }: { page: NostrEvent } &
|
||||
</MenuItem>
|
||||
)}
|
||||
{account?.pubkey !== page.pubkey && (
|
||||
<MenuItem as={RouterLink} to={`/wiki/create?fork=${getSharableEventAddress(page)}`} icon={<GitBranch02 />}>
|
||||
<MenuItem as={RouterLink} to={`/wiki/create?fork=${address}`} icon={<GitBranch02 />}>
|
||||
Fork Page
|
||||
</MenuItem>
|
||||
)}
|
||||
|
@ -4,13 +4,13 @@ import { NostrEvent, nip19 } from "nostr-tools";
|
||||
import { Link as RouterLink } from "react-router-dom";
|
||||
|
||||
import HoverLinkOverlay from "../../../components/hover-link-overlay";
|
||||
import { getSharableEventAddress } from "../../../helpers/nip19";
|
||||
import { getPageForks, getPageSummary, getPageTitle, getPageTopic } from "../../../helpers/nostr/wiki";
|
||||
import UserLink from "../../../components/user/user-link";
|
||||
import FileSearch01 from "../../../components/icons/file-search-01";
|
||||
import GitBranch01 from "../../../components/icons/git-branch-01";
|
||||
import UserName from "../../../components/user/user-name";
|
||||
import UserAvatar from "../../../components/user/user-avatar";
|
||||
import relayHintService from "../../../services/event-relay-hint";
|
||||
|
||||
export default function WikiPageResult({ page, compare }: { page: NostrEvent; compare?: NostrEvent }) {
|
||||
const topic = getPageTopic(page);
|
||||
@ -22,7 +22,7 @@ export default function WikiPageResult({ page, compare }: { page: NostrEvent; co
|
||||
<Box overflow="hidden">
|
||||
<Flex gap="2" wrap="wrap">
|
||||
<Heading size="md">
|
||||
<HoverLinkOverlay as={RouterLink} to={`/wiki/page/${getSharableEventAddress(page)}`}>
|
||||
<HoverLinkOverlay as={RouterLink} to={`/wiki/page/${relayHintService.getSharableEventAddress(page)}`}>
|
||||
{getPageTitle(page)}
|
||||
</HoverLinkOverlay>
|
||||
</Heading>
|
||||
|
@ -24,7 +24,6 @@ import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import { removeNonASCIIChar } from "../../helpers/string";
|
||||
import { usePublishEvent } from "../../providers/global/publish-provider";
|
||||
import { WIKI_PAGE_KIND, getPageSummary, getPageTitle, getPageTopic } from "../../helpers/nostr/wiki";
|
||||
import { getSharableEventAddress } from "../../helpers/nip19";
|
||||
import useCacheForm from "../../hooks/use-cache-form";
|
||||
import MarkdownEditor from "./components/markdown-editor";
|
||||
import useReplaceableEvent from "../../hooks/use-replaceable-event";
|
||||
@ -33,6 +32,7 @@ import UserName from "../../components/user/user-name";
|
||||
import { getEventCoordinate } from "../../helpers/nostr/event";
|
||||
import FormatButton from "./components/format-toolbar";
|
||||
import dictionaryService from "../../services/dictionary";
|
||||
import relayHintService from "../../services/event-relay-hint";
|
||||
|
||||
export default function CreateWikiPageView() {
|
||||
const toast = useToast();
|
||||
@ -111,7 +111,7 @@ export default function CreateWikiPageView() {
|
||||
const pub = await publish("Publish Page", draft, WIKI_RELAYS, false);
|
||||
dictionaryService.handleEvent(pub.event);
|
||||
clearFormCache();
|
||||
navigate(`/wiki/page/${getSharableEventAddress(pub.event)}`, { replace: true });
|
||||
navigate(`/wiki/page/${relayHintService.getSharableEventAddress(pub.event)}`, { replace: true });
|
||||
} catch (error) {
|
||||
if (error instanceof Error) toast({ description: error.message, status: "error" });
|
||||
}
|
||||
|
@ -19,7 +19,6 @@ import useCacheForm from "../../hooks/use-cache-form";
|
||||
import useReplaceableEvent from "../../hooks/use-replaceable-event";
|
||||
import useCurrentAccount from "../../hooks/use-current-account";
|
||||
import { WIKI_PAGE_KIND, getPageSummary, getPageTitle, getPageTopic } from "../../helpers/nostr/wiki";
|
||||
import { getSharableEventAddress } from "../../helpers/nip19";
|
||||
import { usePublishEvent } from "../../providers/global/publish-provider";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import MarkdownEditor from "./components/markdown-editor";
|
||||
@ -27,6 +26,7 @@ import { ErrorBoundary } from "../../components/error-boundary";
|
||||
import { cloneEvent, replaceOrAddSimpleTag } from "../../helpers/nostr/event";
|
||||
import FormatButton from "./components/format-toolbar";
|
||||
import dictionaryService from "../../services/dictionary";
|
||||
import relayHintService from "../../services/event-relay-hint";
|
||||
|
||||
function EditWikiPagePage({ page }: { page: NostrEvent }) {
|
||||
const toast = useToast();
|
||||
@ -64,7 +64,7 @@ function EditWikiPagePage({ page }: { page: NostrEvent }) {
|
||||
const pub = await publish("Publish Page", draft, WIKI_RELAYS, false);
|
||||
dictionaryService.handleEvent(pub.event);
|
||||
clearFormCache();
|
||||
navigate(`/wiki/page/${getSharableEventAddress(pub.event)}`, { replace: true });
|
||||
navigate(`/wiki/page/${relayHintService.getSharableEventAddress(pub.event)}`, { replace: true });
|
||||
} catch (error) {
|
||||
if (error instanceof Error) toast({ description: error.message, status: "error" });
|
||||
}
|
||||
|
@ -31,14 +31,14 @@ import { ExternalLinkIcon } from "../../components/icons";
|
||||
import FileSearch01 from "../../components/icons/file-search-01";
|
||||
import NoteZapButton from "../../components/note/note-zap-button";
|
||||
import ZapBubbles from "../../components/note/timeline-note/components/zap-bubbles";
|
||||
import QuoteRepostButton from "../../components/note/quote-repost-button";
|
||||
import QuoteEventButton from "../../components/note/quote-event-button";
|
||||
import WikiPageMenu from "./components/wiki-page-menu";
|
||||
import EventVoteButtons from "../../components/reactions/event-vote-buttions";
|
||||
import useCurrentAccount from "../../hooks/use-current-account";
|
||||
import dictionaryService from "../../services/dictionary";
|
||||
import { useReadRelays } from "../../hooks/use-client-relays";
|
||||
import { useWebOfTrust } from "../../providers/global/web-of-trust-provider";
|
||||
import { getSharableEventAddress } from "../../helpers/nip19";
|
||||
import relayHintService from "../../services/event-relay-hint";
|
||||
|
||||
function ForkAlert({ page, address }: { page: NostrEvent; address: nip19.AddressPointer }) {
|
||||
const topic = getPageTopic(page);
|
||||
@ -111,7 +111,11 @@ export function WikiPagePage({ page }: { page: NostrEvent }) {
|
||||
</Button>
|
||||
)}
|
||||
{page.pubkey !== account?.pubkey && (
|
||||
<Button as={RouterLink} colorScheme="primary" to={`/wiki/create?fork=${getSharableEventAddress(page)}`}>
|
||||
<Button
|
||||
as={RouterLink}
|
||||
colorScheme="primary"
|
||||
to={`/wiki/create?fork=${relayHintService.getSharableEventAddress(page)}`}
|
||||
>
|
||||
Fork
|
||||
</Button>
|
||||
)}
|
||||
@ -119,7 +123,7 @@ export function WikiPagePage({ page }: { page: NostrEvent }) {
|
||||
<Flex alignItems="flex-end" gap="2" ml="auto">
|
||||
<EventVoteButtons event={page} inline chevrons={false} />
|
||||
<ButtonGroup size="sm">
|
||||
<QuoteRepostButton event={page} />
|
||||
<QuoteEventButton event={page} />
|
||||
<NoteZapButton event={page} showEventPreview={false} />
|
||||
<WikiPageMenu page={page} aria-label="Page Options" />
|
||||
</ButtonGroup>
|
||||
|
Loading…
x
Reference in New Issue
Block a user