better lists
This commit is contained in:
parent
4edede47e6
commit
de586251a7
@ -1,4 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
import Link from "next/link";
|
||||||
import {
|
import {
|
||||||
Section,
|
Section,
|
||||||
SectionHeader,
|
SectionHeader,
|
||||||
@ -15,108 +16,65 @@ import {
|
|||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card";
|
} from "@/components/ui/card";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn, formatNumber, getTwoLetters } from "@/lib/utils";
|
||||||
import { Avatar, AvatarImage, AvatarFallback } from "@radix-ui/react-avatar";
|
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { AspectRatio } from "@/components/ui/aspect-ratio";
|
import { AspectRatio } from "@/components/ui/aspect-ratio";
|
||||||
|
import useEvents from "@/lib/hooks/useEvents";
|
||||||
|
import { Event } from "nostr-tools";
|
||||||
|
import KindLoading from "@/components/KindCard/loading";
|
||||||
|
import { nip19 } from "nostr-tools";
|
||||||
|
import { getTagValues, getTagsValues } from "@/lib/nostr/utils";
|
||||||
|
import { NOTABLE_ACCOUNTS } from "@/constants";
|
||||||
|
import { type NDKKind } from "@nostr-dev-kit/ndk";
|
||||||
|
import { uniqBy } from "ramda";
|
||||||
|
import useProfile from "@/lib/hooks/useProfile";
|
||||||
|
import ListCard from "@/components/ListCard";
|
||||||
|
|
||||||
export default function FeaturedLists() {
|
export default function FeaturedLists() {
|
||||||
const demo = [
|
const { events } = useEvents({
|
||||||
{
|
filter: {
|
||||||
id: 1,
|
kinds: [30001 as NDKKind],
|
||||||
title: "BTC Radio",
|
authors: NOTABLE_ACCOUNTS.map((a) => nip19.decode(a).data.toString()),
|
||||||
picture:
|
limit: 50,
|
||||||
"https://assets.whop.com/cdn-cgi/image/width=1080/https://assets.whop.com/images/images/51602.original.png?1693358530",
|
|
||||||
tags: ["music", "crypto", "art"],
|
|
||||||
},
|
},
|
||||||
{
|
});
|
||||||
id: 2,
|
|
||||||
title: "The Book of Alpha: NFTs and crypto taking over. Market Talk",
|
const processedEvents = events
|
||||||
picture:
|
.sort((a, b) => {
|
||||||
"https://assets.whop.com/cdn-cgi/image/width=1080/https://assets.whop.com/images/images/31095.thumbnail.png?1692203850",
|
const aTitle =
|
||||||
tags: ["NFTs", "crypto", "art", "trading"],
|
getTagValues("title", a.tags) ??
|
||||||
},
|
getTagValues("description", a.tags) ??
|
||||||
{
|
a.content;
|
||||||
id: 3,
|
const bTitle =
|
||||||
title: "Space Talk: What's Elon up to?",
|
getTagValues("title", b.tags) ??
|
||||||
picture:
|
getTagValues("description", b.tags) ??
|
||||||
"https://assets.whop.com/cdn-cgi/image/width=1080/https://assets.whop.com/images/images/40088.original.png?1692206315",
|
b.content;
|
||||||
tags: ["Space"],
|
const aTitleLength = aTitle?.split(" ").length ?? 0;
|
||||||
},
|
const bTitleLength = bTitle?.split(" ").length ?? 0;
|
||||||
{
|
if (aTitleLength && bTitleLength) {
|
||||||
id: 4,
|
if (aTitleLength < bTitleLength) {
|
||||||
title: "The Book of Alpha: NFTs and crypto taking over. Market Talk",
|
return 1;
|
||||||
picture:
|
} else return -1;
|
||||||
"https://assets.whop.com/cdn-cgi/image/width=1080/https://assets.whop.com/images/images/40680.original.png?1692206434",
|
}
|
||||||
tags: ["Market"],
|
if (bTitleLength) return 1;
|
||||||
},
|
return -1;
|
||||||
];
|
})
|
||||||
|
.slice(0, 6);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Section>
|
<Section>
|
||||||
<SectionHeader>
|
<SectionHeader>
|
||||||
<div className="center gap-x-2">
|
<div className="center gap-x-2">
|
||||||
<SectionTitle>Featured Lists</SectionTitle>
|
<SectionTitle>Featured Lists</SectionTitle>
|
||||||
|
{processedEvents.length}
|
||||||
</div>
|
</div>
|
||||||
<Button variant={"ghost"}>
|
<Button variant={"ghost"}>
|
||||||
View all <RiArrowRightLine className="ml-1 h-4 w-4" />
|
View all <RiArrowRightLine className="ml-1 h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</SectionHeader>
|
</SectionHeader>
|
||||||
<SectionContent className="sm:md-feed-cols relative flex flex-col gap-3">
|
<SectionContent className="sm:md-feed-cols relative flex flex-col gap-3">
|
||||||
{demo.map((e) => (
|
{processedEvents.map((e) => (
|
||||||
<Card key={e.id} className="max-sm:border-0 max-sm:shadow-none">
|
<ListCard key={e.id} event={e} />
|
||||||
<div className="hidden overflow-hidden rounded-t-md sm:flex">
|
|
||||||
<AspectRatio ratio={16 / 9} className="bg-muted">
|
|
||||||
<Image
|
|
||||||
width={250}
|
|
||||||
height={150}
|
|
||||||
src={e.picture}
|
|
||||||
alt={e.title}
|
|
||||||
unoptimized
|
|
||||||
className={cn(
|
|
||||||
"h-auto w-auto object-cover transition-all group-hover:scale-105",
|
|
||||||
"aspect-video",
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</AspectRatio>
|
|
||||||
</div>
|
|
||||||
<CardContent className="flex gap-x-3 p-0 sm:p-3">
|
|
||||||
<div className="shrink-0">
|
|
||||||
<Avatar className="center h-[60px] w-[60px] overflow-hidden rounded-sm bg-muted sm:h-12 sm:w-12">
|
|
||||||
<AvatarImage
|
|
||||||
src={e.picture}
|
|
||||||
alt="user"
|
|
||||||
className="h-full w-auto max-w-none object-cover"
|
|
||||||
/>
|
|
||||||
<AvatarFallback className="text-sm">SC</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-1 justify-between gap-3 max-sm:items-center">
|
|
||||||
<div className="flex flex-1 flex-col justify-between">
|
|
||||||
<div className="">
|
|
||||||
<CardTitle className="line-clamp-1 max-sm:text-sm">
|
|
||||||
{e.title}
|
|
||||||
</CardTitle>
|
|
||||||
<CardDescription className="line-clamp-2 text-xs">
|
|
||||||
Here is my description of this list that I am offering
|
|
||||||
</CardDescription>
|
|
||||||
</div>
|
|
||||||
<div className="max-sm:hidden">
|
|
||||||
<Badge variant={"outline"}>100 subs</Badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="gap-y-1.5 text-right">
|
|
||||||
<div className="max-sm:hidden">
|
|
||||||
<div className="text-sm font-medium">2k sats</div>
|
|
||||||
<div className="text-[10px] text-muted-foreground">
|
|
||||||
/month
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<Badge variant={"green"} className="">
|
|
||||||
Free
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
))}
|
))}
|
||||||
</SectionContent>
|
</SectionContent>
|
||||||
</Section>
|
</Section>
|
||||||
|
@ -17,7 +17,7 @@ import KindLoading from "@/components/KindCard/loading";
|
|||||||
import { nip19 } from "nostr-tools";
|
import { nip19 } from "nostr-tools";
|
||||||
import { getTagValues, getTagsValues } from "@/lib/nostr/utils";
|
import { getTagValues, getTagsValues } from "@/lib/nostr/utils";
|
||||||
import { NOTABLE_ACCOUNTS } from "@/constants";
|
import { NOTABLE_ACCOUNTS } from "@/constants";
|
||||||
import { NDKKind } from "@nostr-dev-kit/ndk";
|
import { type NDKKind } from "@nostr-dev-kit/ndk";
|
||||||
import { uniqBy } from "ramda";
|
import { uniqBy } from "ramda";
|
||||||
|
|
||||||
export default function LiveStreamingSection() {
|
export default function LiveStreamingSection() {
|
||||||
@ -67,7 +67,6 @@ export default function LiveStreamingSection() {
|
|||||||
.filter((e) => !!getTagValues("summary", e.tags))
|
.filter((e) => !!getTagValues("summary", e.tags))
|
||||||
.slice(0, 6)
|
.slice(0, 6)
|
||||||
.map((e, idx) => {
|
.map((e, idx) => {
|
||||||
if (idx > 6) return null;
|
|
||||||
const event = e.rawEvent() as Event;
|
const event = e.rawEvent() as Event;
|
||||||
const image = getTagValues("image", event.tags) as string;
|
const image = getTagValues("image", event.tags) as string;
|
||||||
const title = getTagValues("title", event.tags) as string;
|
const title = getTagValues("title", event.tags) as string;
|
||||||
|
@ -15,7 +15,7 @@ export default function Layout(props: {
|
|||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { data, type } = nip19.decode(props.params.key);
|
const { data, type } = nip19.decode(props.params.key);
|
||||||
const pubkey = type === "nevent" ? data.author ?? "" : "";
|
const pubkey = type === "naddr" ? data.pubkey ?? "" : "";
|
||||||
const { profile } = useProfile(pubkey);
|
const { profile } = useProfile(pubkey);
|
||||||
const npub = nip19.npubEncode(pubkey);
|
const npub = nip19.npubEncode(pubkey);
|
||||||
return (
|
return (
|
||||||
|
@ -56,7 +56,7 @@ export default function CreatorCard({
|
|||||||
profile?.picture ??
|
profile?.picture ??
|
||||||
`https://bitcoinfaces.xyz/api/get-image?name=${npub}&onchain=false`
|
`https://bitcoinfaces.xyz/api/get-image?name=${npub}&onchain=false`
|
||||||
}
|
}
|
||||||
className="absolute left-1/2 top-1/2 aspect-square -translate-x-1/2 -translate-y-[70%] transform overflow-hidden rounded-lg object-cover transition-all duration-300 group-hover:left-[50px] group-hover:top-[65px] group-hover:w-[70px]"
|
className="absolute left-1/2 top-1/2 aspect-square -translate-x-1/2 -translate-y-[70%] transform overflow-hidden rounded-lg bg-muted object-cover transition-all duration-300 group-hover:left-[50px] group-hover:top-[65px] group-hover:w-[70px]"
|
||||||
height={100}
|
height={100}
|
||||||
width={100}
|
width={100}
|
||||||
unoptimized
|
unoptimized
|
||||||
|
@ -7,7 +7,6 @@ import { toast } from "sonner";
|
|||||||
import { copyText } from "@/lib/utils";
|
import { copyText } from "@/lib/utils";
|
||||||
import { RenderText } from "../TextRendering";
|
import { RenderText } from "../TextRendering";
|
||||||
import { getTagValues, getTagsValues } from "@/lib/nostr/utils";
|
import { getTagValues, getTagsValues } from "@/lib/nostr/utils";
|
||||||
import LinkCard from "@/components/LinkCard";
|
|
||||||
import ReactPlayer from "react-player";
|
import ReactPlayer from "react-player";
|
||||||
|
|
||||||
export default function Kind30311(props: Event) {
|
export default function Kind30311(props: Event) {
|
||||||
@ -40,7 +39,12 @@ export default function Kind30311(props: Event) {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<ReactPlayer url={streamingUrl} muted={false} controls={true} />
|
<ReactPlayer
|
||||||
|
url={streamingUrl}
|
||||||
|
playing={true}
|
||||||
|
muted={false}
|
||||||
|
controls={true}
|
||||||
|
/>
|
||||||
<div className="border-t pt-4">
|
<div className="border-t pt-4">
|
||||||
{!!title && <CardTitle className="text-base">{title}</CardTitle>}
|
{!!title && <CardTitle className="text-base">{title}</CardTitle>}
|
||||||
{!!summary && <CardDescription>{summary}</CardDescription>}
|
{!!summary && <CardDescription>{summary}</CardDescription>}
|
||||||
|
@ -45,7 +45,7 @@ export default function Container({
|
|||||||
|
|
||||||
<div className="-mr-1 flex items-center gap-x-1.5 text-xs text-muted-foreground">
|
<div className="-mr-1 flex items-center gap-x-1.5 text-xs text-muted-foreground">
|
||||||
{!!createdAt &&
|
{!!createdAt &&
|
||||||
formatDate(new Date(createdAt * 1000), "MMM Do, h:m a")}
|
formatDate(new Date(createdAt * 1000), "MMM Do, h:mm a")}
|
||||||
<DropDownMenu options={actionOptions}>
|
<DropDownMenu options={actionOptions}>
|
||||||
<Button
|
<Button
|
||||||
size={"sm"}
|
size={"sm"}
|
||||||
|
154
components/ListCard/index.tsx
Normal file
154
components/ListCard/index.tsx
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import Image from "next/image";
|
||||||
|
import { cn, formatNumber, getTwoLetters, formatCount } from "@/lib/utils";
|
||||||
|
import { Badge } from "../ui/badge";
|
||||||
|
import { RxClock } from "react-icons/rx";
|
||||||
|
import { HiOutlineUsers } from "react-icons/hi";
|
||||||
|
import { AspectRatio } from "@/components/ui/aspect-ratio";
|
||||||
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
|
import { type Event } from "nostr-tools";
|
||||||
|
import { formatDate } from "@/lib/utils/dates";
|
||||||
|
import { getTagValues, getTagsValues, getPrice } from "@/lib/nostr/utils";
|
||||||
|
import { nip19 } from "nostr-tools";
|
||||||
|
import useProfile from "@/lib/hooks/useProfile";
|
||||||
|
import { type NDKEvent } from "@nostr-dev-kit/ndk";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/components/ui/card";
|
||||||
|
import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar";
|
||||||
|
|
||||||
|
type ListCardProps = {
|
||||||
|
event: NDKEvent;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ListCard({ className, event: e }: ListCardProps) {
|
||||||
|
const event = e.rawEvent() as Event;
|
||||||
|
const image = getTagValues("image", event.tags) as string;
|
||||||
|
const title =
|
||||||
|
getTagValues("title", event.tags) ?? getTagValues("name", event.tags);
|
||||||
|
const description = getTagValues("description", event.tags);
|
||||||
|
const userCount = getTagsValues("p", event.tags).length;
|
||||||
|
const price = getPrice(event.tags);
|
||||||
|
const tags = getTagsValues("t", event.tags) as string[];
|
||||||
|
const { profile } = useProfile(e.pubkey);
|
||||||
|
const npub = nip19.npubEncode(e.pubkey);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card
|
||||||
|
onClick={() => console.log(event.tags)}
|
||||||
|
className="max-sm:border-0 max-sm:shadow-none"
|
||||||
|
>
|
||||||
|
<div className="hidden overflow-hidden rounded-t-md sm:flex">
|
||||||
|
<AspectRatio ratio={16 / 9} className="bg-muted">
|
||||||
|
<Image
|
||||||
|
width={250}
|
||||||
|
height={150}
|
||||||
|
src={image ?? profile?.banner}
|
||||||
|
alt={title ?? ""}
|
||||||
|
unoptimized
|
||||||
|
className={cn(
|
||||||
|
"h-auto w-auto object-cover transition-all group-hover:scale-105",
|
||||||
|
"aspect-video",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</AspectRatio>
|
||||||
|
</div>
|
||||||
|
<CardContent className="flex gap-x-3 p-0 sm:p-3">
|
||||||
|
<div className="shrink-0">
|
||||||
|
<Avatar className="center h-[60px] w-[60px] overflow-hidden rounded-sm bg-muted sm:h-12 sm:w-12">
|
||||||
|
<AvatarImage
|
||||||
|
src={profile?.image}
|
||||||
|
alt="user"
|
||||||
|
className="h-full w-auto max-w-none object-cover"
|
||||||
|
/>
|
||||||
|
<AvatarFallback className="text-sm">
|
||||||
|
{getTwoLetters({ profile, npub })}
|
||||||
|
</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-1 justify-between gap-3 max-sm:items-center">
|
||||||
|
<div className="flex flex-1 flex-col justify-between">
|
||||||
|
<div className="">
|
||||||
|
<CardTitle className="line-clamp-1 max-sm:text-sm">
|
||||||
|
{title}
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription className="line-clamp-2 text-xs">
|
||||||
|
{description}
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
<div className="max-sm:hidden">
|
||||||
|
<Badge variant={"outline"}>{`${userCount} subs`}</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="gap-y-1.5 text-right">
|
||||||
|
{/* <div className="max-sm:hidden">
|
||||||
|
<div className="text-sm font-medium">2k sats</div>
|
||||||
|
<div className="text-[10px] text-muted-foreground">
|
||||||
|
/month
|
||||||
|
</div>
|
||||||
|
</div> */}
|
||||||
|
{price ? (
|
||||||
|
<>
|
||||||
|
<Badge variant={"green"} className="">
|
||||||
|
{`${formatCount(price.asSats)} sats`}
|
||||||
|
</Badge>
|
||||||
|
{!!price.frequency && (
|
||||||
|
<div className="text-[10px] text-muted-foreground">
|
||||||
|
{`per ${price.frequency}`}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<Badge variant={"green"} className="">
|
||||||
|
Free
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export function ListCardLoading({ className }: { className?: string }) {
|
||||||
|
return (
|
||||||
|
<Card className="max-sm:border-0 max-sm:shadow-none">
|
||||||
|
<div className="hidden overflow-hidden rounded-t-md sm:flex">
|
||||||
|
<AspectRatio ratio={16 / 9} className="bg-muted"></AspectRatio>
|
||||||
|
</div>
|
||||||
|
<CardContent className="flex gap-x-3 p-0 sm:p-3">
|
||||||
|
<div className="shrink-0">
|
||||||
|
<Avatar className="center h-[60px] w-[60px] overflow-hidden rounded-sm bg-muted sm:h-12 sm:w-12"></Avatar>
|
||||||
|
</div>
|
||||||
|
<div className="flex flex-1 justify-between gap-3 max-sm:items-center">
|
||||||
|
<div className="flex flex-1 flex-col justify-between">
|
||||||
|
<div className="">
|
||||||
|
<CardTitle className="line-clamp-1 max-sm:text-sm">
|
||||||
|
<Skeleton className="h-4 w-2/3 bg-muted" />
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription className="line-clamp-2 text-xs">
|
||||||
|
<Skeleton className="h-2 w-1/2 bg-muted" />
|
||||||
|
<Skeleton className="h-2 w-1/3 bg-muted" />
|
||||||
|
<Skeleton className="h-2 w-2/5 bg-muted" />
|
||||||
|
</CardDescription>
|
||||||
|
</div>
|
||||||
|
<div className="max-sm:hidden">
|
||||||
|
<Skeleton className="h-2 w-[30px] bg-muted" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="gap-y-1.5 text-right">
|
||||||
|
{/* <div className="max-sm:hidden">
|
||||||
|
<div className="text-sm font-medium">2k sats</div>
|
||||||
|
<div className="text-[10px] text-muted-foreground">
|
||||||
|
/month
|
||||||
|
</div>
|
||||||
|
</div> */}
|
||||||
|
<Skeleton className="h-3 w-[30px] bg-muted" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
@ -57,12 +57,12 @@ export default function VideoCard({ className, card }: VideoCardProps) {
|
|||||||
{!!startTime && (
|
{!!startTime && (
|
||||||
<div className="center gap-x-1 text-xs text-muted-foreground">
|
<div className="center gap-x-1 text-xs text-muted-foreground">
|
||||||
<RxClock className="h-4 w-4 text-primary" />
|
<RxClock className="h-4 w-4 text-primary" />
|
||||||
<span>{formatDate(new Date(startTime), "h:m a")}</span>
|
<span>{formatDate(new Date(startTime), "h:mm a")}</span>
|
||||||
{!!endTime && (
|
{!!endTime && (
|
||||||
<>
|
<>
|
||||||
{" "}
|
{" "}
|
||||||
<span>-</span>{" "}
|
<span>-</span>{" "}
|
||||||
<span>{formatDate(new Date(endTime), "h:m a")}</span>
|
<span>{formatDate(new Date(endTime), "h:mm a")}</span>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -33,6 +33,7 @@ export const NOTABLE_ACCOUNTS = [
|
|||||||
"npub1csamkk8zu67zl9z4wkp90a462v53q775aqn5q6xzjdkxnkvcpd7srtz4x9",
|
"npub1csamkk8zu67zl9z4wkp90a462v53q775aqn5q6xzjdkxnkvcpd7srtz4x9",
|
||||||
"npub1ejxswthae3nkljavznmv66p9ahp4wmj4adux525htmsrff4qym9sz2t3tv",
|
"npub1ejxswthae3nkljavznmv66p9ahp4wmj4adux525htmsrff4qym9sz2t3tv",
|
||||||
"npub107jk7htfv243u0x5ynn43scq9wrxtaasmrwwa8lfu2ydwag6cx2quqncxg",
|
"npub107jk7htfv243u0x5ynn43scq9wrxtaasmrwwa8lfu2ydwag6cx2quqncxg",
|
||||||
|
"npub1zach44xjpc4yyhx6pgse2cj2pf98838kja03dv2e8ly8lfr094vqvm5dy5",
|
||||||
];
|
];
|
||||||
|
|
||||||
export const BANNER =
|
export const BANNER =
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { nip19 } from "nostr-tools";
|
import { nip19 } from "nostr-tools";
|
||||||
|
import { btcToSats } from "../utils";
|
||||||
export const NOSTR_BECH32_REGEXP =
|
export const NOSTR_BECH32_REGEXP =
|
||||||
/^(npub|nprofile|note|nevent|naddr|nrelay)1[023456789acdefghjklmnpqrstuvwxyz]+/;
|
/^(npub|nprofile|note|nevent|naddr|nrelay)1[023456789acdefghjklmnpqrstuvwxyz]+/;
|
||||||
|
|
||||||
@ -62,3 +62,24 @@ export const getTagsAllValues = (name: string, tags: string[][]) => {
|
|||||||
const itemTags = tags.filter((tag: string[]) => tag[0] === name);
|
const itemTags = tags.filter((tag: string[]) => tag[0] === name);
|
||||||
return itemTags.map(([key, ...vals]) => vals) ?? [];
|
return itemTags.map(([key, ...vals]) => vals) ?? [];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getPrice = (tags: string[][]) => {
|
||||||
|
const price = tags.find(([i]) => i === "price");
|
||||||
|
if (!price) return;
|
||||||
|
const [_, amount, currency = "BTC", frequency] = price as [
|
||||||
|
string,
|
||||||
|
string,
|
||||||
|
string | undefined,
|
||||||
|
string | undefined,
|
||||||
|
];
|
||||||
|
|
||||||
|
return {
|
||||||
|
amount,
|
||||||
|
currency,
|
||||||
|
frequency,
|
||||||
|
asSats:
|
||||||
|
currency?.toLowerCase() === "btc"
|
||||||
|
? btcToSats(parseFloat(amount))
|
||||||
|
: parseInt(amount),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user