mirror of
https://github.com/hzrd149/nostrudel.git
synced 2025-03-17 21:31:43 +01:00
cleanup lnurl code
This commit is contained in:
parent
a4d691a6cf
commit
01d7db3232
11
package.json
11
package.json
@ -28,9 +28,9 @@
|
||||
"@codemirror/autocomplete": "^6.18.3",
|
||||
"@codemirror/lang-json": "^6.0.1",
|
||||
"@codemirror/language": "^6.10.3",
|
||||
"@codemirror/view": "^6.34.3",
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@codemirror/view": "^6.35.0",
|
||||
"@emotion/react": "^11.13.5",
|
||||
"@emotion/styled": "^11.13.5",
|
||||
"@getalby/bitcoin-connect": "^3.6.2",
|
||||
"@getalby/bitcoin-connect-react": "^3.6.2",
|
||||
"@noble/ciphers": "^1.0.0",
|
||||
@ -56,7 +56,8 @@
|
||||
"chart.js": "^4.4.6",
|
||||
"cheerio": "^1.0.0",
|
||||
"chroma-js": "^2.6.0",
|
||||
"codemirror-json-schema": "^0.6.1",
|
||||
"codemirror": "^6.0.1",
|
||||
"codemirror-json-schema": "^0.7.9",
|
||||
"dayjs": "^1.11.13",
|
||||
"debug": "^4.3.7",
|
||||
"easymde": "^2.18.0",
|
||||
@ -117,7 +118,7 @@
|
||||
"zen-observable": "^0.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@changesets/cli": "^2.27.9",
|
||||
"@changesets/cli": "^2.27.10",
|
||||
"@types/chroma-js": "^2.4.4",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/dom-serial": "^1.0.6",
|
||||
|
814
pnpm-lock.yaml
generated
814
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -18,7 +18,7 @@ import Timestamp from "../../timestamp";
|
||||
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 { humanReadableSats } from "../../../helpers/lightning";
|
||||
import ZapReceiptMenu from "../../zap/zap-receipt-menu";
|
||||
import { EmbedEventPointer } from "../index";
|
||||
|
||||
@ -48,7 +48,7 @@ export default function EmbeddedZapRecept({ zap, ...props }: Omit<CardProps, "ch
|
||||
{payment.amount && (
|
||||
<>
|
||||
<LightningIcon color="yellow.500" boxSize={5} />
|
||||
<Text>{readablizeSats(payment.amount / 1000)}</Text>
|
||||
<Text>{humanReadableSats(payment.amount / 1000)}</Text>
|
||||
</>
|
||||
)}
|
||||
|
||||
|
@ -24,7 +24,7 @@ import lnurlMetadataService from "../../services/lnurl-metadata";
|
||||
import signingService from "../../services/signing";
|
||||
import accountService from "../../services/account";
|
||||
import PayStep from "./pay-step";
|
||||
import { getInvoiceFromCallbackUrl } from "../../helpers/lnurl";
|
||||
import { getInvoice } from "../../../../applesauce/packages/core/src/helpers/lnurl";
|
||||
import UserLink from "../user/user-link";
|
||||
import { getEventRelayHints } from "../../services/event-relay-hint";
|
||||
import { eventStore, queryStore } from "../../services/event-store";
|
||||
@ -57,7 +57,7 @@ async function getPayRequestForPubkey(
|
||||
callback.searchParams.append("amount", String(amount));
|
||||
if (comment) callback.searchParams.append("comment", comment);
|
||||
|
||||
const invoice = await getInvoiceFromCallbackUrl(callback);
|
||||
const invoice = await getInvoice(callback);
|
||||
|
||||
return { invoice, pubkey };
|
||||
}
|
||||
@ -101,7 +101,7 @@ async function getPayRequestForPubkey(
|
||||
callback.searchParams.append("amount", String(amount));
|
||||
callback.searchParams.append("nostr", JSON.stringify(signed));
|
||||
|
||||
const invoice = await getInvoiceFromCallbackUrl(callback);
|
||||
const invoice = await getInvoice(callback);
|
||||
|
||||
return { invoice, pubkey };
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { Box, Button, Flex, Input, Text } from "@chakra-ui/react";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import { NostrEvent } from "../../types/nostr-event";
|
||||
import { readablizeSats } from "../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../helpers/lightning";
|
||||
import { LightningIcon } from "../icons";
|
||||
import useUserLNURLMetadata from "../../hooks/use-user-lnurl-metadata";
|
||||
import { getZapSplits } from "../../helpers/nostr/zaps";
|
||||
@ -117,7 +117,7 @@ export default function InputStep({
|
||||
size="md"
|
||||
autoFocus
|
||||
>
|
||||
{actionName} {readablizeSats(watch("amount"))} sats
|
||||
{actionName} {humanReadableSats(watch("amount"))} sats
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
} from "@chakra-ui/react";
|
||||
import { parseBolt11 } from "applesauce-core/helpers";
|
||||
|
||||
import { readablizeSats } from "../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../helpers/lightning";
|
||||
import { CopyIconButton } from "../copy-icon-button";
|
||||
import QrCode02 from "../icons/qr-code-02";
|
||||
import QrCodeSvg from "../qr-code/qr-code-svg";
|
||||
@ -94,7 +94,7 @@ export default function InlineInvoiceCard({
|
||||
<CopyIconButton value={invoice.paymentRequest} aria-label="Copy Invoice" />
|
||||
<IconButton icon={<QrCode02 boxSize={6} />} onClick={more.onToggle} aria-label="Show QrCode" />
|
||||
<Button as="a" onClick={handleClick} isLoading={loading} href={`lightning:${paymentRequest}`}>
|
||||
⚡ Pay {invoice.amount ? readablizeSats(invoice.amount / 1000) + " sats" : ""}
|
||||
⚡ Pay {invoice.amount ? humanReadableSats(invoice.amount / 1000) + " sats" : ""}
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</Flex>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Button, ButtonProps, IconButton, useDisclosure } from "@chakra-ui/react";
|
||||
import { getZapSender } from "applesauce-core/helpers";
|
||||
|
||||
import { readablizeSats } from "../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../helpers/lightning";
|
||||
import { totalZaps } from "../../helpers/nostr/zaps";
|
||||
import useCurrentAccount from "../../hooks/use-current-account";
|
||||
import useEventZaps from "../../hooks/use-event-zaps";
|
||||
@ -48,7 +48,7 @@ export default function NoteZapButton({ event, allowComment, showEventPreview, .
|
||||
onClick={onOpen}
|
||||
isDisabled={!canZap}
|
||||
>
|
||||
{readablizeSats(total / 1000)}
|
||||
{humanReadableSats(total / 1000)}
|
||||
</Button>
|
||||
) : (
|
||||
<IconButton
|
||||
|
@ -6,7 +6,7 @@ import { getZapPayment, getZapRequest } from "applesauce-core/helpers";
|
||||
|
||||
import useEventZaps from "../../../../hooks/use-event-zaps";
|
||||
import UserAvatar from "../../../user/user-avatar";
|
||||
import { readablizeSats } from "../../../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../../../helpers/lightning";
|
||||
import { LightningIcon } from "../../../icons";
|
||||
|
||||
function ZapBubble({ zap }: { zap: NostrEvent }) {
|
||||
@ -18,7 +18,7 @@ function ZapBubble({ zap }: { zap: NostrEvent }) {
|
||||
return (
|
||||
<Tag key={zap.id} borderRadius="full" py="1" flexShrink={0} variant="outline">
|
||||
<LightningIcon mr="1" color="yellow.400" />
|
||||
<TagLabel fontWeight="bold">{readablizeSats((payment.amount ?? 0) / 1000)}</TagLabel>
|
||||
<TagLabel fontWeight="bold">{humanReadableSats((payment.amount ?? 0) / 1000)}</TagLabel>
|
||||
<UserAvatar pubkey={request.pubkey} size="xs" square={false} ml="2" />
|
||||
</Tag>
|
||||
);
|
||||
|
@ -1,11 +0,0 @@
|
||||
import { bech32 } from "bech32";
|
||||
|
||||
/** @deprecated */
|
||||
export function decodeText(encoded: string) {
|
||||
const decoded = bech32.decode(encoded, Infinity);
|
||||
const text = new TextDecoder().decode(new Uint8Array(bech32.fromWords(decoded.words)));
|
||||
return {
|
||||
text,
|
||||
prefix: decoded.prefix,
|
||||
};
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import { decode, Section, AmountSection, DescriptionSection, TimestampSection } from "light-bolt11-decoder";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
export type ParsedInvoice = {
|
||||
paymentRequest: string;
|
||||
description: string;
|
||||
amount?: number;
|
||||
timestamp: Date;
|
||||
expiry: Date;
|
||||
};
|
||||
|
||||
function isDescription(section: Section): section is DescriptionSection {
|
||||
return section.name === "description";
|
||||
}
|
||||
function isAmount(section: Section): section is AmountSection {
|
||||
return section.name === "amount";
|
||||
}
|
||||
function isTimestamp(section: Section): section is TimestampSection {
|
||||
return section.name === "timestamp";
|
||||
}
|
||||
|
||||
export function parsePaymentRequest(paymentRequest: string): ParsedInvoice {
|
||||
const decoded = decode(paymentRequest);
|
||||
const timestamp = decoded.sections.find(isTimestamp)?.value ?? 0;
|
||||
|
||||
return {
|
||||
paymentRequest: decoded.paymentRequest,
|
||||
description: decoded.sections.find(isDescription)?.value ?? "",
|
||||
amount: parseInt(decoded.sections.find(isAmount)?.value ?? "0"),
|
||||
timestamp: dayjs.unix(timestamp).toDate(),
|
||||
expiry: dayjs.unix(timestamp + decoded.expiry).toDate(),
|
||||
};
|
||||
}
|
||||
|
||||
// based on https://stackoverflow.com/a/10469752
|
||||
export function readablizeSats(sats: number) {
|
||||
if (sats === 0) return "0";
|
||||
var s = ["", "K", "M"];
|
||||
var e = Math.floor(Math.log(sats) / Math.log(1000));
|
||||
return Math.round((sats / Math.pow(1000, e)) * 100) / 100 + s[e];
|
||||
}
|
7
src/helpers/lightning.ts
Normal file
7
src/helpers/lightning.ts
Normal file
@ -0,0 +1,7 @@
|
||||
// based on https://stackoverflow.com/a/10469752
|
||||
export function humanReadableSats(sats: number) {
|
||||
if (sats === 0) return "0";
|
||||
var s = ["", "K", "M"];
|
||||
var e = Math.floor(Math.log(sats) / Math.log(1000));
|
||||
return Math.round((sats / Math.pow(1000, e)) * 100) / 100 + s[e];
|
||||
}
|
@ -1,46 +1,9 @@
|
||||
import { decodeText } from "./bech32";
|
||||
import { parsePaymentRequest } from "./bolt11";
|
||||
import { decodeLNURL } from "applesauce-core/helpers";
|
||||
|
||||
export function isLNURL(lnurl: string) {
|
||||
try {
|
||||
const parsed = decodeText(lnurl);
|
||||
return parsed.prefix.toLowerCase() === "lnurl";
|
||||
return !!decodeLNURL(lnurl);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function parseLub16Address(address: string) {
|
||||
let [name, domain] = address.split("@");
|
||||
if (!name || !domain) return;
|
||||
return `https://${domain}/.well-known/lnurlp/${name}`;
|
||||
}
|
||||
|
||||
export function parseLNURL(lnurl: string) {
|
||||
const { text, prefix } = decodeText(lnurl);
|
||||
|
||||
return prefix === "lnurl" ? text : undefined;
|
||||
}
|
||||
|
||||
export function getLudEndpoint(addressOrLNURL: string) {
|
||||
if (addressOrLNURL.includes("@")) {
|
||||
return parseLub16Address(addressOrLNURL);
|
||||
}
|
||||
try {
|
||||
return parseLNURL(addressOrLNURL);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
export async function getInvoiceFromCallbackUrl(callback: URL) {
|
||||
const amount = callback.searchParams.get("amount");
|
||||
if (!amount) throw new Error("Missing amount");
|
||||
|
||||
const { pr: payRequest } = await fetch(callback).then((res) => res.json());
|
||||
|
||||
if (payRequest as string) {
|
||||
const parsed = parsePaymentRequest(payRequest);
|
||||
if (parsed.amount !== parseInt(amount)) throw new Error("Incorrect amount");
|
||||
|
||||
return payRequest as string;
|
||||
} else throw new Error("Failed to get invoice");
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { parseLNURLOrAddress } from "applesauce-core/helpers/lnurl";
|
||||
import { fetchWithProxy } from "../helpers/request";
|
||||
import { getLudEndpoint } from "../helpers/lnurl";
|
||||
|
||||
type LNURLPMetadata = {
|
||||
callback: string;
|
||||
@ -21,7 +21,7 @@ class LNURLMetadataService {
|
||||
private pending = new Map<string, Promise<LNURLPMetadata | undefined>>();
|
||||
|
||||
private async fetchMetadata(addressOrLNURL: string) {
|
||||
const url = getLudEndpoint(addressOrLNURL);
|
||||
const url = parseLNURLOrAddress(addressOrLNURL);
|
||||
if (!url) return;
|
||||
try {
|
||||
const metadata = await fetchWithProxy(url).then((res) => res.json() as Promise<LNURLError | LNURLPMetadata>);
|
||||
|
@ -20,7 +20,7 @@ import { getCommunityImage, getCommunityName } from "../../../helpers/nostr/comm
|
||||
import UserAvatarLink from "../../../components/user/user-avatar-link";
|
||||
import UserLink from "../../../components/user/user-link";
|
||||
import useCountCommunityMembers from "../../../hooks/use-count-community-members";
|
||||
import { readablizeSats } from "../../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../../helpers/lightning";
|
||||
import User01 from "../../../components/icons/user-01";
|
||||
import useReplaceableEvent from "../../../hooks/use-replaceable-event";
|
||||
import { AddressPointer } from "nostr-tools/nip19";
|
||||
@ -67,7 +67,7 @@ function CommunityCard({ community, ...props }: Omit<CardProps, "children"> & {
|
||||
{countMembers !== undefined && countMembers > 0 && (
|
||||
<Tag variant="solid" ml="auto" alignSelf="flex-end" textShadow="none">
|
||||
<TagLeftIcon as={User01} boxSize={4} />
|
||||
<TagLabel>{readablizeSats(countMembers)}</TagLabel>
|
||||
<TagLabel>{humanReadableSats(countMembers)}</TagLabel>
|
||||
</Tag>
|
||||
)}
|
||||
|
||||
|
@ -28,7 +28,7 @@ import { NostrEvent } from "../../../types/nostr-event";
|
||||
import CommunityJoinButton from "../../communities/components/community-join-button";
|
||||
import CommunityMenu from "./community-menu";
|
||||
import useCountCommunityMembers from "../../../hooks/use-count-community-members";
|
||||
import { readablizeSats } from "../../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../../helpers/lightning";
|
||||
import CommunityMembersModal from "./community-members-modal";
|
||||
import { RelayFavicon } from "../../../components/relay-favicon";
|
||||
|
||||
@ -83,7 +83,7 @@ export default function HorizontalCommunityDetails({
|
||||
<Heading size="sm" mb="1">
|
||||
Members
|
||||
</Heading>
|
||||
<Text>{countMembers ? readablizeSats(countMembers) : "unknown"}</Text>
|
||||
<Text>{countMembers ? humanReadableSats(countMembers) : "unknown"}</Text>
|
||||
</Box>
|
||||
{rules && (
|
||||
<Box>
|
||||
|
@ -16,7 +16,7 @@ import CommunityJoinButton from "../../communities/components/community-join-but
|
||||
import CommunityMenu from "./community-menu";
|
||||
import useCountCommunityMembers from "../../../hooks/use-count-community-members";
|
||||
import CommunityMembersModal from "./community-members-modal";
|
||||
import { readablizeSats } from "../../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../../helpers/lightning";
|
||||
import { RelayFavicon } from "../../../components/relay-favicon";
|
||||
|
||||
export default function VerticalCommunityDetails({
|
||||
@ -65,7 +65,7 @@ export default function VerticalCommunityDetails({
|
||||
<Heading size="sm" mb="1">
|
||||
Members
|
||||
</Heading>
|
||||
<Text>{countMembers ? readablizeSats(countMembers) : "unknown"}</Text>
|
||||
<Text>{countMembers ? humanReadableSats(countMembers) : "unknown"}</Text>
|
||||
</Box>
|
||||
{rules && (
|
||||
<Box>
|
||||
|
@ -6,7 +6,7 @@ import { LightningIcon } from "../../../components/icons";
|
||||
import useEventZaps from "../../../hooks/use-event-zaps";
|
||||
import { getEventUID } from "../../../helpers/nostr/event";
|
||||
import { totalZaps } from "../../../helpers/nostr/zaps";
|
||||
import { readablizeSats } from "../../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../../helpers/lightning";
|
||||
|
||||
export default function GoalProgress({ goal }: { goal: NostrEvent }) {
|
||||
const amount = getGoalAmount(goal);
|
||||
@ -18,7 +18,8 @@ export default function GoalProgress({ goal }: { goal: NostrEvent }) {
|
||||
<LightningIcon />
|
||||
<Progress value={(raised / amount) * 100} colorScheme="yellow" flex={1} />
|
||||
<Text>
|
||||
{readablizeSats(raised / 1000)} / {readablizeSats(amount / 1000)} ({Math.round((raised / amount) * 1000) / 10}%)
|
||||
{humanReadableSats(raised / 1000)} / {humanReadableSats(amount / 1000)} (
|
||||
{Math.round((raised / amount) * 1000) / 10}%)
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
|
@ -6,7 +6,7 @@ import useEventZaps from "../../../hooks/use-event-zaps";
|
||||
import { NostrEvent } from "../../../types/nostr-event";
|
||||
import UserAvatarLink from "../../../components/user/user-avatar-link";
|
||||
import UserLink from "../../../components/user/user-link";
|
||||
import { readablizeSats } from "../../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../../helpers/lightning";
|
||||
import { LightningIcon } from "../../../components/icons";
|
||||
|
||||
export default function GoalTopZappers({
|
||||
@ -35,7 +35,7 @@ export default function GoalTopZappers({
|
||||
<Box whiteSpace="pre" isTruncated>
|
||||
<UserLink fontSize="lg" fontWeight="bold" pubkey={pubkey} mr="2" />
|
||||
<br />
|
||||
<LightningIcon /> {readablizeSats(amount / 1000)}
|
||||
<LightningIcon /> {humanReadableSats(amount / 1000)}
|
||||
</Box>
|
||||
</Flex>
|
||||
))}
|
||||
|
@ -6,7 +6,7 @@ import { getGoalRelays } from "../../../helpers/nostr/goal";
|
||||
import useEventZaps from "../../../hooks/use-event-zaps";
|
||||
import UserAvatarLink from "../../../components/user/user-avatar-link";
|
||||
import UserLink from "../../../components/user/user-link";
|
||||
import { readablizeSats } from "../../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../../helpers/lightning";
|
||||
import { LightningIcon } from "../../../components/icons";
|
||||
import Timestamp from "../../../components/timestamp";
|
||||
import TextNoteContents from "../../../components/note/timeline-note/text-note-contents";
|
||||
@ -29,7 +29,7 @@ function GoalZap({ zap }: { zap: NostrEvent }) {
|
||||
</Box>
|
||||
<Spacer />
|
||||
<Text>
|
||||
<LightningIcon /> {readablizeSats(payment.amount / 1000)}
|
||||
<LightningIcon /> {humanReadableSats(payment.amount / 1000)}
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
|
@ -10,7 +10,7 @@ import UserAvatar from "../../components/user/user-avatar";
|
||||
import UserLink from "../../components/user/user-link";
|
||||
import GoalContents from "./components/goal-contents";
|
||||
import GoalZapList from "./components/goal-zap-list";
|
||||
import { readablizeSats } from "../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../helpers/lightning";
|
||||
import GoalZapButton from "./components/goal-zap-button";
|
||||
import VerticalPageLayout from "../../components/vertical-page-layout";
|
||||
import useParamsEventPointer from "../../hooks/use-params-event-pointer";
|
||||
@ -30,7 +30,7 @@ export default function GoalDetailsView() {
|
||||
</Button>
|
||||
|
||||
<Heading size="md" isTruncated>
|
||||
{getGoalName(goal)} ({readablizeSats(getGoalAmount(goal) / 1000)})
|
||||
{getGoalName(goal)} ({humanReadableSats(getGoalAmount(goal) / 1000)})
|
||||
</Heading>
|
||||
|
||||
<Spacer />
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
} from "applesauce-core/helpers";
|
||||
import { NostrEvent } from "nostr-tools";
|
||||
|
||||
import { readablizeSats } from "../../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../../helpers/lightning";
|
||||
import { EmbedEventPointer } from "../../../components/embed-event";
|
||||
import UserAvatarLink from "../../../components/user/user-avatar-link";
|
||||
import { LightningIcon } from "../../../components/icons";
|
||||
@ -50,7 +50,7 @@ const ZapNotification = forwardRef<HTMLDivElement, { zap: NostrEvent; onClick?:
|
||||
timestamp={request.created_at}
|
||||
summary={
|
||||
<>
|
||||
{readablizeSats(payment.amount / 1000)} {request.content}
|
||||
{humanReadableSats(payment.amount / 1000)} {request.content}
|
||||
</>
|
||||
}
|
||||
onClick={onClick}
|
||||
@ -59,7 +59,7 @@ const ZapNotification = forwardRef<HTMLDivElement, { zap: NostrEvent; onClick?:
|
||||
<AvatarGroup size="sm">
|
||||
<UserAvatarLink pubkey={sender} />
|
||||
</AvatarGroup>
|
||||
<Text>zapped {readablizeSats(payment.amount / 1000)} sats</Text>
|
||||
<Text>zapped {humanReadableSats(payment.amount / 1000)} sats</Text>
|
||||
<ButtonGroup size="sm" variant="ghost" ml="auto">
|
||||
<ZapReceiptMenu zap={zap} aria-label="More Options" />
|
||||
</ButtonGroup>
|
||||
|
@ -7,7 +7,7 @@ import { Link as RouterLink } from "react-router-dom";
|
||||
import UserAvatar from "../../../components/user/user-avatar";
|
||||
import UserDnsIdentity from "../../../components/user/user-dns-identity";
|
||||
import trustedUserStatsService from "../../../services/trusted-user-stats";
|
||||
import { readablizeSats } from "../../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../../helpers/lightning";
|
||||
import replaceableEventsService from "../../../services/replaceable-events";
|
||||
import UserAboutContent from "../../../components/user/user-about";
|
||||
import UserName from "../../../components/user/user-name";
|
||||
@ -42,7 +42,7 @@ function ProfileResult({ profile }: { profile: NostrEvent }) {
|
||||
</Flex>
|
||||
<UserAboutContent pubkey={profile.pubkey} noOfLines={3} isTruncated />
|
||||
{stats && (
|
||||
<>{stats.followers_pubkey_count && <Text>Followers: {readablizeSats(stats.followers_pubkey_count)}</Text>}</>
|
||||
<>{stats.followers_pubkey_count && <Text>Followers: {humanReadableSats(stats.followers_pubkey_count)}</Text>}</>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
|
@ -19,9 +19,9 @@ import {
|
||||
Text,
|
||||
} from "@chakra-ui/react";
|
||||
import { useInterval } from "react-use";
|
||||
import { parseBolt11 } from "applesauce-core/helpers";
|
||||
|
||||
import useUserLNURLMetadata from "../../../hooks/use-user-lnurl-metadata";
|
||||
import { parsePaymentRequest } from "../../../helpers/bolt11";
|
||||
import { V4VStreamIcon, V4VStopIcon } from "../../../components/icons";
|
||||
|
||||
export default function StreamSatsPerMinute({ pubkey, ...props }: { pubkey: string } & FlexProps) {
|
||||
@ -49,7 +49,7 @@ export default function StreamSatsPerMinute({ pubkey, ...props }: { pubkey: stri
|
||||
const { pr: payRequest } = await fetch(callbackUrl).then((res) => res.json());
|
||||
|
||||
if (payRequest as string) {
|
||||
const parsed = parsePaymentRequest(payRequest);
|
||||
const parsed = parseBolt11(payRequest);
|
||||
if (parsed.amount !== amountMsats) throw new Error("incorrect amount");
|
||||
} else throw new Error("Failed to get invoice");
|
||||
|
||||
|
@ -4,7 +4,7 @@ import { getZapPayment, getZapSender } from "applesauce-core/helpers";
|
||||
|
||||
import UserLink from "../../../components/user/user-link";
|
||||
import { LightningIcon } from "../../../components/icons";
|
||||
import { readablizeSats } from "../../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../../helpers/lightning";
|
||||
import useStreamChatTimeline from "../stream/stream-chat/use-stream-chat-timeline";
|
||||
import { ParsedStream } from "../../../helpers/nostr/stream";
|
||||
import UserAvatarLink from "../../../components/user/user-avatar-link";
|
||||
@ -30,7 +30,7 @@ export default function TopZappers({ stream, ...props }: FlexProps & { stream: P
|
||||
<UserLink pubkey={pubkey} fontWeight="bold" />
|
||||
<br />
|
||||
<LightningIcon />
|
||||
{readablizeSats(total / 1000)}
|
||||
{humanReadableSats(total / 1000)}
|
||||
</Text>
|
||||
</Flex>
|
||||
))}
|
||||
|
@ -6,7 +6,7 @@ import UserAvatar from "../../../../components/user/user-avatar";
|
||||
import UserLink from "../../../../components/user/user-link";
|
||||
import { NostrEvent } from "../../../../types/nostr-event";
|
||||
import { LightningIcon } from "../../../../components/icons";
|
||||
import { readablizeSats } from "../../../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../../../helpers/lightning";
|
||||
import { TrustProvider } from "../../../../providers/local/trust-provider";
|
||||
import ChatMessageContent from "./chat-message-content";
|
||||
import useClientSideMuteFilter from "../../../../hooks/use-client-side-mute-filter";
|
||||
@ -31,7 +31,7 @@ function ZapMessage({ zap, stream }: { zap: NostrEvent; stream: ParsedStream })
|
||||
<LightningIcon color="yellow.400" />
|
||||
<UserAvatar pubkey={sender} size="xs" />
|
||||
<UserLink pubkey={sender} fontWeight="bold" color="yellow.400" />
|
||||
<Text>zapped {readablizeSats(payment.amount / 1000)} sats</Text>
|
||||
<Text>zapped {humanReadableSats(payment.amount / 1000)} sats</Text>
|
||||
</Flex>
|
||||
<Box>
|
||||
<ChatMessageContent event={request} />
|
||||
|
@ -8,7 +8,7 @@ import UserAvatarLink from "../../../../components/user/user-avatar-link";
|
||||
import UserLink from "../../../../components/user/user-link";
|
||||
import Timestamp from "../../../../components/timestamp";
|
||||
import { LightningIcon } from "../../../../components/icons";
|
||||
import { readablizeSats } from "../../../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../../../helpers/lightning";
|
||||
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";
|
||||
@ -24,7 +24,7 @@ const ZapEvent = memo(({ zap }: { zap: NostrEvent }) => {
|
||||
<Flex gap="2">
|
||||
<Flex direction="column" alignItems="center" minW="10">
|
||||
<LightningIcon color="yellow.500" boxSize={5} />
|
||||
<Text>{readablizeSats(payment.amount / 1000)}</Text>
|
||||
<Text>{humanReadableSats(payment.amount / 1000)}</Text>
|
||||
</Flex>
|
||||
|
||||
<UserAvatarLink pubkey={sender} size="sm" ml="2" />
|
||||
|
@ -5,7 +5,7 @@ import { NostrEvent } from "../../../../types/nostr-event";
|
||||
import UserAvatarLink from "../../../../components/user/user-avatar-link";
|
||||
import UserLink from "../../../../components/user/user-link";
|
||||
import { LightningIcon } from "../../../../components/icons";
|
||||
import { readablizeSats } from "../../../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../../../helpers/lightning";
|
||||
|
||||
export default function TextToSpeechStatus({ status }: { status: NostrEvent }) {
|
||||
const toast = useToast();
|
||||
@ -48,7 +48,7 @@ export default function TextToSpeechStatus({ status }: { status: NostrEvent }) {
|
||||
isLoading={paying || paid}
|
||||
isDisabled={!window.webln}
|
||||
>
|
||||
Pay {readablizeSats(amountMsat / 1000)} sats
|
||||
Pay {humanReadableSats(amountMsat / 1000)} sats
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
|
@ -5,7 +5,7 @@ import { NostrEvent } from "../../../../types/nostr-event";
|
||||
import UserAvatarLink from "../../../../components/user/user-avatar-link";
|
||||
import UserLink from "../../../../components/user/user-link";
|
||||
import { LightningIcon } from "../../../../components/icons";
|
||||
import { readablizeSats } from "../../../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../../../helpers/lightning";
|
||||
|
||||
export default function TranslationStatus({ status }: { status: NostrEvent }) {
|
||||
const toast = useToast();
|
||||
@ -48,7 +48,7 @@ export default function TranslationStatus({ status }: { status: NostrEvent }) {
|
||||
isLoading={paying || paid}
|
||||
isDisabled={!window.webln}
|
||||
>
|
||||
Pay {readablizeSats(amountMsat / 1000)} sats
|
||||
Pay {humanReadableSats(amountMsat / 1000)} sats
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
|
@ -17,10 +17,10 @@ import {
|
||||
Text,
|
||||
useDisclosure,
|
||||
} from "@chakra-ui/react";
|
||||
import { nip19, NostrEvent } from "nostr-tools";
|
||||
import { nip19 } from "nostr-tools";
|
||||
import { ChatIcon } from "@chakra-ui/icons";
|
||||
|
||||
import { getLudEndpoint } from "../../../helpers/lnurl";
|
||||
import { parseLNURLOrAddress } from "../../../../../applesauce/packages/core/src/helpers/lnurl";
|
||||
import { truncatedId } from "../../../helpers/nostr/event";
|
||||
import { parseAddress } from "../../../services/dns-identity";
|
||||
import { useAdditionalRelayContext } from "../../../providers/local/additional-relay-context";
|
||||
@ -49,10 +49,7 @@ import UserJoinedChanneled from "./user-joined-channels";
|
||||
import { getTextColor } from "../../../helpers/color";
|
||||
import UserName from "../../../components/user/user-name";
|
||||
import { useUserDNSIdentity } from "../../../hooks/use-user-dns-identity";
|
||||
import { renderGenericUrl } from "../../../components/content/links/common";
|
||||
import UserAboutContent from "../../../components/user/user-about";
|
||||
import { useStoreQuery } from "applesauce-react/hooks";
|
||||
import { TimelineQuery } from "applesauce-core/queries";
|
||||
import UserRecentEvents from "./user-recent-events";
|
||||
|
||||
function DNSIdentityWarning({ pubkey }: { pubkey: string }) {
|
||||
@ -186,7 +183,7 @@ export default function UserAboutTab() {
|
||||
{metadata?.lud16 && (
|
||||
<Flex gap="2">
|
||||
<LightningIcon boxSize="1.2em" />
|
||||
<Link href={getLudEndpoint(metadata.lud16)} isExternal>
|
||||
<Link href={parseLNURLOrAddress(metadata.lud16)?.toString()} isExternal>
|
||||
{metadata.lud16}
|
||||
</Link>
|
||||
</Flex>
|
||||
|
@ -16,7 +16,7 @@ import {
|
||||
import { useAsync } from "react-use";
|
||||
import { kinds } from "nostr-tools";
|
||||
|
||||
import { readablizeSats } from "../../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../../helpers/lightning";
|
||||
import trustedUserStatsService from "../../../services/trusted-user-stats";
|
||||
import { useAdditionalRelayContext } from "../../../providers/local/additional-relay-context";
|
||||
import useUserContactList from "../../../hooks/use-user-contact-list";
|
||||
@ -46,7 +46,7 @@ export default function UserStatsAccordion({ pubkey }: { pubkey: string }) {
|
||||
<StatGroup gap="4" whiteSpace="pre">
|
||||
<Stat>
|
||||
<StatLabel>Following</StatLabel>
|
||||
<StatNumber>{contacts ? readablizeSats(getPubkeysFromList(contacts).length) : "Unknown"}</StatNumber>
|
||||
<StatNumber>{contacts ? humanReadableSats(getPubkeysFromList(contacts).length) : "Unknown"}</StatNumber>
|
||||
{contacts && (
|
||||
<StatHelpText>
|
||||
Updated <Timestamp timestamp={contacts.created_at} />
|
||||
@ -58,17 +58,17 @@ export default function UserStatsAccordion({ pubkey }: { pubkey: string }) {
|
||||
<>
|
||||
<Stat>
|
||||
<StatLabel>Followers</StatLabel>
|
||||
<StatNumber>{readablizeSats(followerCount ?? 0) || 0}</StatNumber>
|
||||
<StatNumber>{humanReadableSats(followerCount ?? 0) || 0}</StatNumber>
|
||||
</Stat>
|
||||
|
||||
<Stat>
|
||||
<StatLabel>Notes & replies</StatLabel>
|
||||
<StatNumber>{readablizeSats(stats.pub_note_count) || 0}</StatNumber>
|
||||
<StatNumber>{humanReadableSats(stats.pub_note_count) || 0}</StatNumber>
|
||||
</Stat>
|
||||
|
||||
<Stat>
|
||||
<StatLabel>Reactions</StatLabel>
|
||||
<StatNumber>{readablizeSats(stats.pub_reaction_count) || 0}</StatNumber>
|
||||
<StatNumber>{humanReadableSats(stats.pub_reaction_count) || 0}</StatNumber>
|
||||
</Stat>
|
||||
</>
|
||||
)}
|
||||
@ -96,15 +96,15 @@ export default function UserStatsAccordion({ pubkey }: { pubkey: string }) {
|
||||
</Stat>
|
||||
<Stat>
|
||||
<StatLabel>Total Sats Sent</StatLabel>
|
||||
<StatNumber>{readablizeSats(stats.zaps_sent.msats / 1000)}</StatNumber>
|
||||
<StatNumber>{humanReadableSats(stats.zaps_sent.msats / 1000)}</StatNumber>
|
||||
</Stat>
|
||||
<Stat>
|
||||
<StatLabel>Avg Zap Sent</StatLabel>
|
||||
<StatNumber>{readablizeSats(stats.zaps_sent.avg_msats / 1000)}</StatNumber>
|
||||
<StatNumber>{humanReadableSats(stats.zaps_sent.avg_msats / 1000)}</StatNumber>
|
||||
</Stat>
|
||||
<Stat>
|
||||
<StatLabel>Biggest Zap Sent</StatLabel>
|
||||
<StatNumber>{readablizeSats(stats.zaps_sent.max_msats / 1000)}</StatNumber>
|
||||
<StatNumber>{humanReadableSats(stats.zaps_sent.max_msats / 1000)}</StatNumber>
|
||||
</Stat>
|
||||
</>
|
||||
)}
|
||||
@ -117,15 +117,15 @@ export default function UserStatsAccordion({ pubkey }: { pubkey: string }) {
|
||||
</Stat>
|
||||
<Stat>
|
||||
<StatLabel>Total Sats Received</StatLabel>
|
||||
<StatNumber>{readablizeSats(stats.zaps_received.msats / 1000)}</StatNumber>
|
||||
<StatNumber>{humanReadableSats(stats.zaps_received.msats / 1000)}</StatNumber>
|
||||
</Stat>
|
||||
<Stat>
|
||||
<StatLabel>Avg Zap Received</StatLabel>
|
||||
<StatNumber>{readablizeSats(stats.zaps_received.avg_msats / 1000)}</StatNumber>
|
||||
<StatNumber>{humanReadableSats(stats.zaps_received.avg_msats / 1000)}</StatNumber>
|
||||
</Stat>
|
||||
<Stat>
|
||||
<StatLabel>Biggest Zap Received</StatLabel>
|
||||
<StatNumber>{readablizeSats(stats.zaps_received.max_msats / 1000)}</StatNumber>
|
||||
<StatNumber>{humanReadableSats(stats.zaps_received.max_msats / 1000)}</StatNumber>
|
||||
</Stat>
|
||||
</>
|
||||
)}
|
||||
|
@ -9,7 +9,7 @@ import { ErrorBoundary } from "../../components/error-boundary";
|
||||
import { LightningIcon } from "../../components/icons";
|
||||
import UserAvatarLink from "../../components/user/user-avatar-link";
|
||||
import UserLink from "../../components/user/user-link";
|
||||
import { readablizeSats } from "../../helpers/bolt11";
|
||||
import { humanReadableSats } from "../../helpers/lightning";
|
||||
import { isProfileZap, isNoteZap, totalZaps } from "../../helpers/nostr/zaps";
|
||||
import useTimelineLoader from "../../hooks/use-timeline-loader";
|
||||
import { NostrEvent, isATag, isETag } from "../../types/nostr-event";
|
||||
@ -68,7 +68,7 @@ const Zap = ({ zap }: { zap: NostrEvent }) => {
|
||||
{payment?.amount && (
|
||||
<Flex gap="2">
|
||||
<LightningIcon color="yellow.400" />
|
||||
<Text>{readablizeSats(payment.amount / 1000)} sats</Text>
|
||||
<Text>{humanReadableSats(payment.amount / 1000)} sats</Text>
|
||||
</Flex>
|
||||
)}
|
||||
<Timestamp ml="auto" timestamp={request.created_at} />
|
||||
@ -120,7 +120,7 @@ const UserZapsTab = () => {
|
||||
<Flex gap="2">
|
||||
<LightningIcon color="yellow.400" />
|
||||
<Text>
|
||||
{readablizeSats(totalZaps(zaps) / 1000)} sats in the last{" "}
|
||||
{humanReadableSats(totalZaps(zaps) / 1000)} sats in the last{" "}
|
||||
{dayjs.unix(zaps[zaps.length - 1].created_at).fromNow(true)}
|
||||
</Text>
|
||||
</Flex>
|
||||
|
Loading…
x
Reference in New Issue
Block a user