This commit is contained in:
hzrd149 2023-03-02 20:17:59 -06:00
parent 9221309831
commit 4b0d8a4073
32 changed files with 51 additions and 59 deletions

View File

@ -10,7 +10,7 @@ import accountService from "./services/account";
import { FollowingTab } from "./views/home/following-tab";
import { DiscoverTab } from "./views/home/discover-tab";
import { GlobalTab } from "./views/home/global-tab";
import { normalizeToHex } from "./helpers/nip-19";
import { normalizeToHex } from "./helpers/nip19";
import UserView from "./views/user";
import UserNotesTab from "./views/user/notes";
import UserFollowersTab from "./views/user/followers";

View File

@ -9,7 +9,7 @@ import { NostrEvent } from "../types/nostr-event";
import { UserAvatarLink } from "./user-avatar-link";
import { UserLink } from "./user-link";
import { UserDnsIdentityIcon } from "./user-dns-identity";
import { Bech32Prefix, normalizeToBech32 } from "../helpers/nip-19";
import { Bech32Prefix, normalizeToBech32 } from "../helpers/nip19";
import { convertTimestampToDate } from "../helpers/date";
const EmbeddedNote = ({ note }: { note: NostrEvent }) => {

View File

@ -1,6 +1,6 @@
import { Box, Button, Flex, SkeletonText } from "@chakra-ui/react";
import { Link } from "react-router-dom";
import { Bech32Prefix, normalizeToBech32 } from "../helpers/nip-19";
import { Bech32Prefix, normalizeToBech32 } from "../helpers/nip19";
import { getUserDisplayName } from "../helpers/user-metadata";
import useSubject from "../hooks/use-subject";
import { useUserMetadata } from "../hooks/use-user-metadata";

View File

@ -1,7 +1,7 @@
import React, { useState } from "react";
import { Box, Button, ButtonGroup, IconButton, Text } from "@chakra-ui/react";
import { requestProvider } from "webln";
import { readableAmountInSats, parsePaymentRequest } from "../helpers/bolt11";
import { parsePaymentRequest, readablizeSats } from "../helpers/bolt11";
import { useAsync } from "react-use";
import { ClipboardIcon } from "./icons";
import moment from "moment";
@ -62,7 +62,7 @@ export const InlineInvoiceCard = ({ paymentRequest }: InvoiceButtonProps) => {
<ButtonGroup>
<IconButton icon={<ClipboardIcon />} title="Copy to clipboard" aria-label="copy invoice" variant="outline" />
<Button as="a" variant="outline" onClick={handleClick} isLoading={loading} href={`lightning:${paymentRequest}`}>
Pay {invoice.amount ? readableAmountInSats(invoice.amount) : ""}
Pay {invoice.amount ? readablizeSats(invoice.amount / 1000) + " sats" : ""}
</Button>
</ButtonGroup>
</Box>

View File

@ -1,6 +1,6 @@
import { Link, LinkProps } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import { Bech32Prefix, normalizeToBech32 } from "../helpers/nip-19";
import { Bech32Prefix, normalizeToBech32 } from "../helpers/nip19";
import { truncatedId } from "../helpers/nostr-event";
export type NoteLinkProps = LinkProps & {

View File

@ -16,7 +16,7 @@ import {
} from "@chakra-ui/react";
import { NostrEvent } from "../../types/nostr-event";
import { UserAvatarLink } from "../user-avatar-link";
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip-19";
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip19";
import { NoteContents } from "./note-contents";
import { NoteMenu } from "./note-menu";

View File

@ -13,7 +13,7 @@ import {
import { InlineInvoiceCard } from "../inline-invoice-card";
import { TweetEmbed } from "../tweet-embed";
import { UserLink } from "../user-link";
import { normalizeToHex } from "../../helpers/nip-19";
import { normalizeToHex } from "../../helpers/nip19";
import { DraftNostrEvent, NostrEvent } from "../../types/nostr-event";
import { NoteLink } from "../note-link";
import settings from "../../services/settings";

View File

@ -10,7 +10,7 @@ import {
} from "@chakra-ui/react";
import { useCopyToClipboard } from "react-use";
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip-19";
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip19";
import { NostrEvent } from "../../types/nostr-event";
import { MenuIconButton, MenuIconButtonProps } from "../menu-icon-button";

View File

@ -17,8 +17,8 @@ import { UserLink } from "../user-link";
import moment from "moment";
import { convertTimestampToDate } from "../../helpers/date";
import { DislikeIcon, LikeIcon } from "../icons";
import { parseZapNote } from "../../helpers/nip-57";
import { readableAmountInSats } from "../../helpers/bolt11";
import { parseZapNote } from "../../helpers/zaps";
import { readablizeSats } from "../../helpers/bolt11";
import useEventReactions from "../../hooks/use-event-reactions";
import useEventZaps from "../../hooks/use-event-zaps";
@ -54,7 +54,7 @@ const ZapEvent = React.memo(({ event }: { event: NostrEvent }) => {
return (
<Flex gap="2">
<Text>{readableAmountInSats(payment.amount)}</Text>
<Text>{readablizeSats(payment.amount / 1000)}</Text>
<Flex overflow="hidden" gap="2">
<UserAvatarLink pubkey={request.pubkey} size="xs" />
<UserLink pubkey={request.pubkey} />

View File

@ -1,7 +1,7 @@
import { Button, ButtonProps, useDisclosure } from "@chakra-ui/react";
import { useMemo } from "react";
import { readableAmountInSats } from "../../helpers/bolt11";
import { parseZapNote, totalZaps } from "../../helpers/nip-57";
import { readablizeSats } from "../../helpers/bolt11";
import { parseZapNote, totalZaps } from "../../helpers/zaps";
import { useCurrentAccount } from "../../hooks/use-current-account";
import useEventZaps from "../../hooks/use-event-zaps";
import { useUserMetadata } from "../../hooks/use-user-metadata";
@ -42,7 +42,7 @@ export default function NoteZapButton({ note, ...props }: { note: NostrEvent } &
onClick={onOpen}
isDisabled={!tipAddress}
>
{readableAmountInSats(totalZaps(zaps), false)}
{readablizeSats(totalZaps(zaps) / 1000)}
</Button>
{isOpen && <ZapModal isOpen={isOpen} onClose={onClose} event={note} onPaid={invoicePaid} pubkey={note.pubkey} />}
</>

View File

@ -2,7 +2,7 @@ import { Box, LinkBox, Text } from "@chakra-ui/react";
import { Link } from "react-router-dom";
import { UserAvatar } from "./user-avatar";
import { useUserMetadata } from "../hooks/use-user-metadata";
import { normalizeToBech32 } from "../helpers/nip-19";
import { normalizeToBech32 } from "../helpers/nip19";
import { truncatedId } from "../helpers/nostr-event";
import { useCurrentAccount } from "../hooks/use-current-account";

View File

@ -1,6 +1,6 @@
import React from "react";
import { Link } from "react-router-dom";
import { Bech32Prefix, normalizeToBech32 } from "../helpers/nip-19";
import { Bech32Prefix, normalizeToBech32 } from "../helpers/nip19";
import { UserAvatar, UserAvatarProps } from "./user-avatar";
export const UserAvatarLink = React.memo(({ pubkey, ...props }: UserAvatarProps) => (

View File

@ -1,6 +1,6 @@
import { Link, LinkProps } from "@chakra-ui/react";
import { Link as RouterLink } from "react-router-dom";
import { Bech32Prefix, normalizeToBech32 } from "../helpers/nip-19";
import { Bech32Prefix, normalizeToBech32 } from "../helpers/nip19";
import { getUserDisplayName } from "../helpers/user-metadata";
import { useUserMetadata } from "../hooks/use-user-metadata";

View File

@ -22,11 +22,11 @@ import { SubmitHandler, useForm } from "react-hook-form";
import { UserAvatar } from "./user-avatar";
import { useUserMetadata } from "../hooks/use-user-metadata";
import { UserLink } from "./user-link";
import { parsePaymentRequest, readableAmountInSats } from "../helpers/bolt11";
import { parsePaymentRequest, readablizeSats } from "../helpers/bolt11";
import { ExternalLinkIcon, LightningIcon, QrCodeIcon } from "./icons";
import lnurlMetadataService from "../services/lnurl-metadata";
import { useAsync } from "react-use";
import { makeZapRequest } from "nostr-tools/nip57";
import { nip57 } from "nostr-tools";
import clientRelaysService from "../services/client-relays";
import { getEventRelays } from "../services/event-relays";
import { useSigningContext } from "../providers/signing-provider";
@ -87,7 +87,7 @@ export default function ZapModal({
const otherRelays = event ? getEventRelays(event.id).value : [];
const readRelays = clientRelaysService.getReadUrls();
const zapRequest = makeZapRequest({
const zapRequest = nip57.makeZapRequest({
profile: pubkey,
event: event?.id ?? null,
relays: [...otherRelays, ...readRelays],
@ -225,7 +225,7 @@ export default function ZapModal({
/>
)}
<Button leftIcon={<LightningIcon />} type="submit" isLoading={isSubmitting} variant="solid" size="md">
{actionName} {getUserDisplayName(metadata, pubkey)} {readableAmountInSats(watch("amount") * 1000)}
{actionName} {getUserDisplayName(metadata, pubkey)} {readablizeSats(watch("amount"))} sats
</Button>
</Flex>
</form>

View File

@ -32,12 +32,10 @@ export function parsePaymentRequest(paymentRequest: string): ParsedInvoice {
};
}
export function readableAmountInSats(amount: number, includeSats = true) {
const amountInSats = Math.round(amount / 1000);
const end = includeSats ? " sats" : "";
if (amountInSats > 1000000) {
return `${amountInSats / 1000000}M` + end;
} else if (amountInSats > 1000) {
return `${amountInSats / 1000}K` + end;
} else return amountInSats + end;
// 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];
}

View File

@ -1,5 +1,5 @@
import { NostrEvent } from "../types/nostr-event";
import { Bech32Prefix, normalizeToBech32 } from "./nip-19";
import { Bech32Prefix, normalizeToBech32 } from "./nip19";
import { truncatedId } from "./nostr-event";
import { safeJson } from "./parse";

View File

@ -1,11 +1,9 @@
import { utf8Decoder } from "nostr-tools/utils";
import { bech32 } from "@scure/base";
import { isETag, isPTag, NostrEvent } from "../types/nostr-event";
import { parsePaymentRequest } from "./bolt11";
import { Kind0ParsedContent } from "./user-metadata";
import { validateZapRequest } from "nostr-tools/nip57";
import { nip57, utils } from "nostr-tools";
// based on https://github.com/nbd-wtf/nostr-tools/blob/master/nip57.ts
export async function getZapEndpoint(metadata: Kind0ParsedContent): Promise<null | string> {
@ -15,7 +13,7 @@ export async function getZapEndpoint(metadata: Kind0ParsedContent): Promise<null
if (lud06) {
let { words } = bech32.decode(lud06, 1000);
let data = bech32.fromWords(words);
lnurl = utf8Decoder.decode(data);
lnurl = utils.utf8Decoder.decode(data);
} else if (lud16) {
let [name, domain] = lud16.split("@");
lnurl = `https://${domain}/.well-known/lnurlp/${name}`;
@ -64,7 +62,7 @@ export function parseZapNote(event: NostrEvent) {
const bolt11 = event.tags.find((t) => t[0] === "bolt11")?.[1];
if (!bolt11) throw new Error("missing bolt11 invoice");
const error = validateZapRequest(zapRequestStr);
const error = nip57.validateZapRequest(zapRequestStr);
if (error) throw new Error(error);
const zapRequest = JSON.parse(zapRequestStr) as NostrEvent;

View File

@ -1,8 +1,5 @@
import moment from "moment";
import { createRoot } from "react-dom/client";
// import the nostr-tools index to apply patch to secp256k1
// https://github.com/nbd-wtf/nostr-tools/blob/a330b975903758737ae2b455d5cfd7b99d33ad35/index.ts#L17
import "nostr-tools";
import { App } from "./app";
import { Providers } from "./providers";

View File

@ -2,7 +2,7 @@ import moment, { MomentInput } from "moment";
import { NostrMultiSubscription } from "../classes/nostr-multi-subscription";
import { NostrEvent } from "../types/nostr-event";
import clientRelaysService from "./client-relays";
import { insertEventIntoDescendingList } from "nostr-tools/utils";
import { utils } from "nostr-tools";
import { SuperMap } from "../classes/super-map";
import { PersistentSubject } from "../classes/subject";
import accountService from "./account";
@ -77,7 +77,7 @@ class DirectMessagesService {
const conversation = from === pubkey ? to : from;
const subject = this.messages.get(conversation);
subject.next(insertEventIntoDescendingList(subject.value, event));
subject.next(utils.insertEventIntoDescendingList(subject.value, event));
if (!this.conversations.value.includes(conversation)) {
this.conversations.next([...this.conversations.value, conversation]);

View File

@ -1,9 +1,7 @@
import { DraftNostrEvent, NostrEvent } from "../types/nostr-event";
import { Account } from "./account";
import { getPublicKey } from "nostr-tools/keys";
import { signEvent, getEventHash } from "nostr-tools/event";
import db from "./db";
import { decrypt, encrypt } from "nostr-tools/nip04";
import { nip04, signEvent, getEventHash, getPublicKey } from "nostr-tools";
class SigningService {
private async getSalt() {
@ -98,7 +96,7 @@ class SigningService {
} else throw new Error("Missing nostr extension");
} else if (account?.secKey) {
const secKey = await this.decryptSecKey(account);
return await decrypt(secKey, pubkey, data);
return await nip04.decrypt(secKey, pubkey, data);
} else throw new Error("No decryption method");
}
@ -112,7 +110,7 @@ class SigningService {
} else throw new Error("Missing nostr extension");
} else if (account?.secKey) {
const secKey = await this.decryptSecKey(account);
return await encrypt(secKey, pubkey, text);
return await nip04.encrypt(secKey, pubkey, text);
} else throw new Error("No encryption method");
}
}

View File

@ -8,7 +8,7 @@ import { ArrowLeftSIcon } from "../../components/icons";
import { UserAvatar } from "../../components/user-avatar";
import { UserLink } from "../../components/user-link";
import { convertTimestampToDate } from "../../helpers/date";
import { normalizeToHex } from "../../helpers/nip-19";
import { normalizeToHex } from "../../helpers/nip19";
import { useCurrentAccount } from "../../hooks/use-current-account";
import { useIsMobile } from "../../hooks/use-is-mobile";
import useSubject from "../../hooks/use-subject";

View File

@ -17,7 +17,7 @@ import { useEffect, useMemo, useState } from "react";
import { Link } from "react-router-dom";
import { UserAvatar } from "../../components/user-avatar";
import { convertTimestampToDate } from "../../helpers/date";
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip-19";
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip19";
import { getUserDisplayName } from "../../helpers/user-metadata";
import useSubject from "../../hooks/use-subject";
import { useUserMetadata } from "../../hooks/use-user-metadata";

View File

@ -6,7 +6,7 @@ import { useAppTitle } from "../../hooks/use-app-title";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useThrottle } from "react-use";
import { Kind } from "nostr-tools";
import { parseZapNote } from "../../helpers/nip-57";
import { parseZapNote } from "../../helpers/zaps";
import { NoteLink } from "../../components/note-link";
export default function PopularTab() {

View File

@ -2,7 +2,7 @@ import { useState } from "react";
import { Button, Flex, FormControl, FormHelperText, FormLabel, Input, Link, useToast } from "@chakra-ui/react";
import { useNavigate } from "react-router-dom";
import { RelayUrlInput } from "../../components/relay-url-input";
import { normalizeToHex } from "../../helpers/nip-19";
import { normalizeToHex } from "../../helpers/nip19";
import accountService from "../../services/account";
import clientRelaysService from "../../services/client-relays";

View File

@ -18,10 +18,10 @@ import {
} from "@chakra-ui/react";
import { useNavigate } from "react-router-dom";
import { RelayUrlInput } from "../../components/relay-url-input";
import { Bech32Prefix, normalizeToBech32, normalizeToHex } from "../../helpers/nip-19";
import { Bech32Prefix, normalizeToBech32, normalizeToHex } from "../../helpers/nip19";
import accountService from "../../services/account";
import clientRelaysService from "../../services/client-relays";
import { generatePrivateKey, getPublicKey } from "nostr-tools/keys";
import { generatePrivateKey, getPublicKey } from "nostr-tools";
import signingService from "../../services/signing";
export const LoginNsecView = () => {

View File

@ -7,7 +7,7 @@ import { UserAvatar } from "../../../components/user-avatar";
import { UserDnsIdentityIcon } from "../../../components/user-dns-identity";
import { UserFollowButton } from "../../../components/user-follow-button";
import { UserTipButton } from "../../../components/user-tip-button";
import { Bech32Prefix, normalizeToBech32 } from "../../../helpers/nip-19";
import { Bech32Prefix, normalizeToBech32 } from "../../../helpers/nip19";
import { truncatedId } from "../../../helpers/nostr-event";
import { fixWebsiteUrl, getUserDisplayName } from "../../../helpers/user-metadata";
import { useCurrentAccount } from "../../../hooks/use-current-account";

View File

@ -4,7 +4,7 @@ import { Link as ReactRouterLink } from "react-router-dom";
import { useUserMetadata } from "../../../hooks/use-user-metadata";
import { getUserDisplayName } from "../../../helpers/user-metadata";
import { UserAvatar } from "../../../components/user-avatar";
import { Bech32Prefix, normalizeToBech32 } from "../../../helpers/nip-19";
import { Bech32Prefix, normalizeToBech32 } from "../../../helpers/nip19";
import { UserDnsIdentityIcon } from "../../../components/user-dns-identity";
export const UserCard = ({ pubkey, relay }: { pubkey: string; relay?: string }) => {

View File

@ -2,7 +2,7 @@ import { Avatar, MenuItem } from "@chakra-ui/react";
import { MenuIconButton, MenuIconButtonProps } from "../../../components/menu-icon-button";
import { IMAGE_ICONS, SpyIcon } from "../../../components/icons";
import { Bech32Prefix, normalizeToBech32 } from "../../../helpers/nip-19";
import { Bech32Prefix, normalizeToBech32 } from "../../../helpers/nip19";
import accountService from "../../../services/account";
import { useUserMetadata } from "../../../hooks/use-user-metadata";
import { getUserDisplayName } from "../../../helpers/user-metadata";

View File

@ -3,7 +3,7 @@ import { Outlet, useLoaderData, useMatches, useNavigate } from "react-router-dom
import { useUserMetadata } from "../../hooks/use-user-metadata";
import { getUserDisplayName } from "../../helpers/user-metadata";
import { useIsMobile } from "../../hooks/use-is-mobile";
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip-19";
import { Bech32Prefix, normalizeToBech32 } from "../../helpers/nip19";
import { useAppTitle } from "../../hooks/use-app-title";
import Header from "./components/header";

View File

@ -5,9 +5,9 @@ import { ErrorBoundary, ErrorFallback } from "../../components/error-boundary";
import QuoteNote from "../../components/note/quote-note";
import { UserAvatarLink } from "../../components/user-avatar-link";
import { UserLink } from "../../components/user-link";
import { readableAmountInSats } from "../../helpers/bolt11";
import { readablizeSats } from "../../helpers/bolt11";
import { convertTimestampToDate } from "../../helpers/date";
import { isProfileZap, parseZapNote } from "../../helpers/nip-57";
import { isProfileZap, parseZapNote } from "../../helpers/zaps";
import { useReadRelayUrls } from "../../hooks/use-client-relays";
import { useTimelineLoader } from "../../hooks/use-timeline-loader";
import { NostrEvent } from "../../types/nostr-event";
@ -30,7 +30,7 @@ const ZapNote = ({ zapEvent }: { zapEvent: NostrEvent }) => {
<Flex gap="2" alignItems="center" wrap="wrap">
<UserAvatarLink pubkey={request.pubkey} size="xs" />
<UserLink pubkey={request.pubkey} />
{payment.amount && <Text>{readableAmountInSats(payment.amount)}</Text>}
{payment.amount && <Text>{readablizeSats(payment.amount / 1000)}</Text>}
{request.content && (
<Button variant="link" onClick={onToggle}>
Show message

View File

@ -17,5 +17,6 @@
"jsx": "react-jsx"
},
"include": ["src"],
"exclude": ["node_modules"],
"references": [{ "path": "./tsconfig.node.json" }]
}