updating article page
This commit is contained in:
parent
6ca0233e31
commit
e210351578
113
app/(app)/(profile)/[npub]/page.tsx
Normal file
113
app/(app)/(profile)/[npub]/page.tsx
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
"use client";
|
||||||
|
import { useState } from "react";
|
||||||
|
import Image from "next/image";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import SubscriptionCard from "@/components/SubscriptionCard";
|
||||||
|
import { HiCheckBadge } from "react-icons/hi2";
|
||||||
|
import Tabs from "@/components/Tabs";
|
||||||
|
|
||||||
|
export default function ProfilePage({
|
||||||
|
params: { npub },
|
||||||
|
}: {
|
||||||
|
params: {
|
||||||
|
npub: string;
|
||||||
|
};
|
||||||
|
}) {
|
||||||
|
const [activeTab, setActiveTab] = useState("feed");
|
||||||
|
const demo = [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
title: "BTC Radio",
|
||||||
|
description:
|
||||||
|
"BTC Radio is the best fuking show ever. you should sub to it. now",
|
||||||
|
picture:
|
||||||
|
"https://assets.whop.com/cdn-cgi/image/width=1080/https://assets.whop.com/images/images/51602.original.png?1693358530",
|
||||||
|
tags: ["music", "crypto", "art"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<div className="relative mx-auto max-w-5xl space-y-6">
|
||||||
|
<div className="relative -mx-5 @container ">
|
||||||
|
<div className="absolute top-0 h-[8rem] w-full" />
|
||||||
|
<div className="mx-auto max-w-5xl p-0">
|
||||||
|
<div className="m-0 @4xl:pt-8 @5xl:px-5">
|
||||||
|
<div className="relative w-full overflow-hidden bg-gradient-to-b from-primary pb-[29%] @5xl:rounded-[20px]">
|
||||||
|
<Image
|
||||||
|
className="absolute inset-0 h-full w-full object-cover align-middle"
|
||||||
|
src={
|
||||||
|
"https://images.lumacdn.com/cdn-cgi/image/format=auto,fit=cover,dpr=2,quality=75,width=1250,height=357.14285714285717/calendar-cover-images/4m/c50dff9c-12e1-4b8a-ae95-68a36364b760"
|
||||||
|
}
|
||||||
|
width={400}
|
||||||
|
height={100}
|
||||||
|
alt="banner"
|
||||||
|
unoptimized
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="relative mx-auto mb-4 mt-[calc(-0.4375_*_4rem)] flex max-w-[800px] items-end justify-between gap-2 px-3 sm:mt-[calc(-0.4375_*_4.5rem)] sm:px-5 md:mt-[calc(-0.5625_*_5rem)] lg:mt-[calc(-0.5625_*_6rem)]">
|
||||||
|
<div className="z-1 ml-[calc(-1_*_3px)] overflow-hidden rounded-[0.5rem] bg-background p-[3px] sm:ml-[calc(-1_*_4px)] sm:p-[4px] lg:ml-[calc(-1_*_6px)] lg:rounded-[1rem] lg:p-[6px]">
|
||||||
|
<Image
|
||||||
|
src={
|
||||||
|
"https://images.lumacdn.com/cdn-cgi/image/format=auto,fit=cover,dpr=2,background=white,quality=75,width=96,height=96/calendars/hw/70772773-6d97-4fbb-a076-fc4dee603080"
|
||||||
|
}
|
||||||
|
className="aspect-square w-[4rem] overflow-hidden rounded-[calc(0.5rem_-_3px)] object-cover object-center sm:w-[4.5rem] sm:rounded-[calc(0.5rem_-_4px)] md:w-[5rem] lg:w-[6rem] lg:rounded-[calc(1rem_-_6px)]"
|
||||||
|
unoptimized
|
||||||
|
alt="profile picture"
|
||||||
|
width={16}
|
||||||
|
height={16}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Button size={"sm"} className="rounded-sm px-5 sm:hidden">
|
||||||
|
Follow
|
||||||
|
</Button>
|
||||||
|
<Button className="rounded-sm px-5 max-sm:hidden">Follow</Button>
|
||||||
|
</div>
|
||||||
|
<div className="mx-auto max-w-[800px] space-y-1 px-4">
|
||||||
|
<div className="flex items-center gap-x-1.5 lg:gap-x-2.5">
|
||||||
|
<h2 className="text-xl font-semibold sm:text-2xl lg:text-3xl">
|
||||||
|
Zach Meyer
|
||||||
|
</h2>
|
||||||
|
<HiCheckBadge className="h-5 w-5 text-primary lg:h-7 lg:w-7" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center text-xs text-muted-foreground/80 md:text-sm">
|
||||||
|
<p>@zach</p> <div className="inline-flex px-1">·</div>
|
||||||
|
<p>zach@ordstr.com</p>
|
||||||
|
</div>
|
||||||
|
<div className="pt-1 md:pt-2">
|
||||||
|
<p className="line-clamp-3 text-xs text-muted-foreground md:text-sm">
|
||||||
|
This is my bio. You should check it out now.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mx-auto max-w-[800px] space-y-6">
|
||||||
|
<div className="flex max-w-2xl flex-col gap-5">
|
||||||
|
{demo.map((e) => (
|
||||||
|
<SubscriptionCard {...e} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<Tabs
|
||||||
|
tabs={[
|
||||||
|
{
|
||||||
|
name: "feed",
|
||||||
|
label: "Feed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "media",
|
||||||
|
label: "Media",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "subscriptions",
|
||||||
|
label: "Subscriptions",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
activeTab={activeTab}
|
||||||
|
setActiveTab={(t) => setActiveTab(t.name)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -2,6 +2,8 @@ import { UserMenu } from "./components/UserMenu";
|
|||||||
import { Search } from "./components/Search";
|
import { Search } from "./components/Search";
|
||||||
import { Notifications } from "./components/Notifications";
|
import { Notifications } from "./components/Notifications";
|
||||||
import { MobileMenu } from "./components/MobileMenu";
|
import { MobileMenu } from "./components/MobileMenu";
|
||||||
|
import { Relays } from "./components/Relays";
|
||||||
|
|
||||||
import Logo from "@/assets/Logo";
|
import Logo from "@/assets/Logo";
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
return (
|
return (
|
||||||
@ -9,7 +11,7 @@ export default function Header() {
|
|||||||
<div className="fixed z-header flex h-[var(--header-height)] w-full grow border-b bg-background p-5 sm:w-[calc(100vw_-_var(--sidebar-closed-width))] sm:border-b-0 sm:py-0 xl:w-[calc(100vw_-_var(--sidebar-open-width))]">
|
<div className="fixed z-header flex h-[var(--header-height)] w-full grow border-b bg-background p-5 sm:w-[calc(100vw_-_var(--sidebar-closed-width))] sm:border-b-0 sm:py-0 xl:w-[calc(100vw_-_var(--sidebar-open-width))]">
|
||||||
<div className="flex flex-1 items-stretch justify-between gap-x-4 sm:border-b">
|
<div className="flex flex-1 items-stretch justify-between gap-x-4 sm:border-b">
|
||||||
<div className="center justify-between gap-x-3 text-foreground">
|
<div className="center justify-between gap-x-3 text-foreground">
|
||||||
<Logo className="h-[30px] w-[30px] sm:hidden" />
|
<Logo className="h-[30px] w-[30px] text-primary sm:hidden" />
|
||||||
<div className="text-xl font-semibold">Flockstr</div>
|
<div className="text-xl font-semibold">Flockstr</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex grow items-center justify-end">
|
<div className="flex grow items-center justify-end">
|
||||||
@ -19,6 +21,7 @@ export default function Header() {
|
|||||||
<div className="hidden items-center gap-x-4 sm:flex">
|
<div className="hidden items-center gap-x-4 sm:flex">
|
||||||
<Search />
|
<Search />
|
||||||
<Notifications />
|
<Notifications />
|
||||||
|
<Relays />
|
||||||
<UserMenu />
|
<UserMenu />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,7 +5,7 @@ export default function Keystone() {
|
|||||||
<div className="center hidden sm:flex">
|
<div className="center hidden sm:flex">
|
||||||
<Link
|
<Link
|
||||||
href="/"
|
href="/"
|
||||||
className="center fixed h-[var(--header-height)] w-[var(--sidebar-closed-width)] border-r hover:text-primary xl:w-[var(--sidebar-open-width)] xl:justify-start xl:pl-5"
|
className="center fixed h-[var(--header-height)] w-[var(--sidebar-closed-width)] border-r text-primary hover:text-primary/80 xl:w-[var(--sidebar-open-width)] xl:justify-start xl:pl-5"
|
||||||
>
|
>
|
||||||
<Logo className="h-[30px] w-[30px]" />
|
<Logo className="h-[30px] w-[30px]" />
|
||||||
</Link>
|
</Link>
|
||||||
|
41
app/(app)/_layout/components/Relays.tsx
Normal file
41
app/(app)/_layout/components/Relays.tsx
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuGroup,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuShortcut,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu";
|
||||||
|
import { SiRelay } from "react-icons/si";
|
||||||
|
import { RELAYS } from "@/constants";
|
||||||
|
export function Relays() {
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="center relative h-8 w-8 rounded-full bg-muted text-foreground"
|
||||||
|
>
|
||||||
|
<SiRelay className="h-[18px] w-[18px] text-foreground" />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="z-header+ w-56" align="end" forceMount>
|
||||||
|
{RELAYS.map((r) => (
|
||||||
|
<DropdownMenuGroup key={r}>
|
||||||
|
<DropdownMenuItem>{r}</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Manage Relays
|
||||||
|
<DropdownMenuShortcut>⇧⌘M</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
);
|
||||||
|
}
|
@ -3,7 +3,7 @@ import { Button } from "@/components/ui/button";
|
|||||||
|
|
||||||
export default function BecomeACreator() {
|
export default function BecomeACreator() {
|
||||||
return (
|
return (
|
||||||
<div className="padded-container overflow-x-clip pb-8 md:py-[120px]">
|
<div className="padded-container overflow-x-clip pb-8 max-sm:-mx-5 md:py-[120px]">
|
||||||
<div className="flex w-full flex-col items-center justify-between gap-8 lg:flex-row">
|
<div className="flex w-full flex-col items-center justify-between gap-8 lg:flex-row">
|
||||||
<Image
|
<Image
|
||||||
alt="creator icons"
|
alt="creator icons"
|
||||||
@ -14,7 +14,7 @@ export default function BecomeACreator() {
|
|||||||
}
|
}
|
||||||
className="h-44 object-cover object-bottom lg:h-auto"
|
className="h-44 object-cover object-bottom lg:h-auto"
|
||||||
/>
|
/>
|
||||||
<div className="md:self-start lg:max-w-lg lg:self-center">
|
<div className="max-sm:px-5 md:self-start lg:max-w-lg lg:self-center">
|
||||||
<h2 className="font-condensed text-2xl font-bold text-foreground sm:text-3xl">
|
<h2 className="font-condensed text-2xl font-bold text-foreground sm:text-3xl">
|
||||||
Start earning on Nostr
|
Start earning on Nostr
|
||||||
</h2>
|
</h2>
|
||||||
|
@ -77,7 +77,7 @@ export default function FeaturedLists() {
|
|||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<CardContent className="flex gap-x-3 p-0 sm:p-4">
|
<CardContent className="flex gap-x-3 p-0 sm:p-3">
|
||||||
<div className="shrink-0">
|
<div className="shrink-0">
|
||||||
<Avatar className="center h-[60px] w-[60px] overflow-hidden rounded-sm bg-muted sm:h-12 sm:w-12">
|
<Avatar className="center h-[60px] w-[60px] overflow-hidden rounded-sm bg-muted sm:h-12 sm:w-12">
|
||||||
<AvatarImage
|
<AvatarImage
|
||||||
|
46
app/(app)/app/_sections/LongFormContent.tsx
Normal file
46
app/(app)/app/_sections/LongFormContent.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import {
|
||||||
|
Section,
|
||||||
|
SectionHeader,
|
||||||
|
SectionTitle,
|
||||||
|
SectionContent,
|
||||||
|
} from "@/containers/PageSection";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { RiArrowRightLine } from "react-icons/ri";
|
||||||
|
import KindCard from "@/components/KindCard";
|
||||||
|
import { DUMMY_30023 } from "@/constants";
|
||||||
|
import Link from "next/link";
|
||||||
|
export default function LongFormContentSection() {
|
||||||
|
return (
|
||||||
|
<Section>
|
||||||
|
<SectionHeader>
|
||||||
|
<SectionTitle>Long form content</SectionTitle>
|
||||||
|
<Button variant={"ghost"}>
|
||||||
|
View all <RiArrowRightLine className="ml-1 h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</SectionHeader>
|
||||||
|
<SectionContent className="sm:lg-feed-cols relative mx-auto flex flex-col gap-4">
|
||||||
|
<Link href="/article/ere">
|
||||||
|
<KindCard {...DUMMY_30023} />
|
||||||
|
</Link>
|
||||||
|
<Link href="/article/ere">
|
||||||
|
<KindCard {...DUMMY_30023} />
|
||||||
|
</Link>
|
||||||
|
<Link href="/article/ere">
|
||||||
|
<KindCard {...DUMMY_30023} />
|
||||||
|
</Link>
|
||||||
|
<Link href="/article/ere">
|
||||||
|
<KindCard {...DUMMY_30023} />
|
||||||
|
</Link>
|
||||||
|
<Link href="/article/ere">
|
||||||
|
<KindCard {...DUMMY_30023} />
|
||||||
|
</Link>
|
||||||
|
<Link href="/article/ere">
|
||||||
|
<KindCard {...DUMMY_30023} />
|
||||||
|
</Link>
|
||||||
|
<Link href="/article/ere">
|
||||||
|
<KindCard {...DUMMY_30023} />
|
||||||
|
</Link>
|
||||||
|
</SectionContent>
|
||||||
|
</Section>
|
||||||
|
);
|
||||||
|
}
|
@ -1,16 +1,10 @@
|
|||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import HorizontalCarousel from "./_sections/HorizontalCarousel";
|
import HorizontalCarousel from "./_sections/HorizontalCarousel";
|
||||||
import { RiArrowRightLine } from "react-icons/ri";
|
import { RiArrowRightLine } from "react-icons/ri";
|
||||||
import LongFormContentCard from "@/components/LongFormContentCard";
|
|
||||||
import BecomeACreator from "./_sections/BecomeACreator";
|
import BecomeACreator from "./_sections/BecomeACreator";
|
||||||
import {
|
|
||||||
Section,
|
|
||||||
SectionHeader,
|
|
||||||
SectionTitle,
|
|
||||||
SectionContent,
|
|
||||||
} from "@/containers/PageSection";
|
|
||||||
import LiveStreamingSection from "./_sections/LiveStreaming";
|
import LiveStreamingSection from "./_sections/LiveStreaming";
|
||||||
import FeaturedListsSection from "./_sections/FeaturedLists";
|
import FeaturedListsSection from "./_sections/FeaturedLists";
|
||||||
|
import LongFormContentSection from "./_sections/LongFormContent";
|
||||||
|
|
||||||
export default function Page() {
|
export default function Page() {
|
||||||
return (
|
return (
|
||||||
@ -26,20 +20,7 @@ export default function Page() {
|
|||||||
</div>
|
</div>
|
||||||
<HorizontalCarousel />
|
<HorizontalCarousel />
|
||||||
</section>
|
</section>
|
||||||
<Section>
|
<LongFormContentSection />
|
||||||
<SectionHeader>
|
|
||||||
<SectionTitle>Long form content</SectionTitle>
|
|
||||||
<Button variant={"ghost"}>
|
|
||||||
View all <RiArrowRightLine className="ml-1 h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</SectionHeader>
|
|
||||||
<SectionContent className="sm:lg-feed-cols relative mx-auto flex flex-col gap-4">
|
|
||||||
<LongFormContentCard />
|
|
||||||
<LongFormContentCard />
|
|
||||||
<LongFormContentCard />
|
|
||||||
<LongFormContentCard />
|
|
||||||
</SectionContent>
|
|
||||||
</Section>
|
|
||||||
<BecomeACreator />
|
<BecomeACreator />
|
||||||
<LiveStreamingSection />
|
<LiveStreamingSection />
|
||||||
<FeaturedListsSection />
|
<FeaturedListsSection />
|
||||||
|
@ -1,13 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { useMemo } from "react";
|
import Article from "@/containers/Article";
|
||||||
import dynamic from "next/dynamic";
|
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { RiCloseFill } from "react-icons/ri";
|
|
||||||
import { Avatar, AvatarImage, AvatarFallback } from "@radix-ui/react-avatar";
|
|
||||||
import { useRouter } from "next/navigation";
|
|
||||||
const Viewer = dynamic(() => import("@/components/LongForm/Viewer"), {
|
|
||||||
ssr: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
export default function ArticlePage({
|
export default function ArticlePage({
|
||||||
params: { eventId },
|
params: { eventId },
|
||||||
@ -16,48 +8,5 @@ export default function ArticlePage({
|
|||||||
eventId: string;
|
eventId: string;
|
||||||
};
|
};
|
||||||
}) {
|
}) {
|
||||||
const router = useRouter();
|
return <Article />;
|
||||||
const markdown = `This is a test
|
|
||||||
### test text
|
|
||||||
|
|
||||||
- First
|
|
||||||
- Second
|
|
||||||
1 nest`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="relative">
|
|
||||||
<div className="absolute inset-x-0 top-0 flex items-center justify-between border-b pb-4 pt-4">
|
|
||||||
<div className="center gap-x-3">
|
|
||||||
<Avatar className="center h-8 w-8 overflow-hidden rounded-sm bg-muted">
|
|
||||||
<AvatarImage
|
|
||||||
src={
|
|
||||||
"https://images.unsplash.com/photo-1566492031773-4f4e44671857?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=4&w=256&h=256&q=60"
|
|
||||||
}
|
|
||||||
alt="user"
|
|
||||||
/>
|
|
||||||
<AvatarFallback className="text-xs">SC</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<span className="text-xs uppercase text-muted-foreground">
|
|
||||||
Derek Seivers
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
if (sessionStorage.getItem("RichHistory")) {
|
|
||||||
void router.back();
|
|
||||||
} else {
|
|
||||||
void router.push("/app");
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
size="icon"
|
|
||||||
variant={"outline"}
|
|
||||||
className=""
|
|
||||||
>
|
|
||||||
<RiCloseFill className="h-5 w-5" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="h-[70px] w-full"></div>
|
|
||||||
<Viewer initialMarkdown={markdown} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ const Logo = (props: SVGProps<SVGSVGElement>) => (
|
|||||||
width={188.303}
|
width={188.303}
|
||||||
height={289.598}
|
height={289.598}
|
||||||
viewBox="54.17 96.938 188.303 289.598"
|
viewBox="54.17 96.938 188.303 289.598"
|
||||||
|
fill="currentColor"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<path d="M230.869 98.463a118.54 118.54 0 0 0-2.062 3.007 341.651 341.651 0 0 0-3.038 4.613c-7.325 11.203-15.753 17.585-30.313 26.161-14.559 8.576-30.42 10.49-63.656 28.87-5.655 3.127-19.19 9.271-19.642 12.208-.45 2.937-.281 14.794-.281 14.794.116 2.402-.032 3.511-.128 5.997-.095 2.486-.139 6.348.186 14.256.525 13.241-2.282 9.535-4.203 3.548-1.922-5.987-4.622-15.41-5.397-26.819-.4-5.951-.525-8.938-1.587-8.747-1.063.19-3.063 3.557-7.213 10.312-6.975 11.306-11.4 22.838-13.244 34.376-1.843 11.537-1.106 23.08 2.244 34.406.25.824.375 1.236.231 1.236-.143 0-.556-.412-1.381-1.236-2.975-3.006-5.337-7.248-6.956-12.304-1.619-5.056-2.494-10.925-2.494-17.185 0-4.407-.091-6.577-.868-6.318-.777.259-2.24 2.946-4.982 8.253-6.45 12.48-10.045 22.685-11.204 32.338-1.158 9.654.12 18.757 3.421 29.035 3.012 9.377 6.837 16.353 10.18 21.205 3.34 4.853 6.198 7.581 7.274 8.464 5.532 4.538 6.337 6.521 4.078 5.959-2.258-.562-7.581-3.67-14.306-9.312-6.8-5.663-10.5-8.392-11.225-8.144-.725.247 1.525 3.47 6.625 9.71 7.4 9.06 15.129 17.366 29.704 25.226 14.576 7.86 15.039 6.407 26.214 11.596 10.175 4.737 16.35 7.702 20.994 10.24 4.644 2.538 7.756 4.65 11.806 7.676 2.35 1.771 5.588 4.12 8.913 6.467 3.325 2.347 6.737 4.695 9.437 6.466 8.85 5.87 4.065-4.607-3.96-11.69-17.175-15.158-21.043-16.706-24.778-27.568-3.735-10.861-5.266-15.307 5.1-33.732 5.401-9.6 14.637-17.98 23.146-26.608 8.51-8.628 20.374-18.548 38.424-34.54 10.517-9.317 16.709-16.023 17.834-19.015 1.125-2.991-2.817-2.27-12.567 3.27-2.475 1.442-6.875 3.759-11.962 6.333a699.312 699.312 0 0 1-16.088 7.877 1981.677 1981.677 0 0 0-15.475 7.409c-4.712 2.28-8.6 4.185-10.525 5.153-7.625 3.872-11.616 6.915-14.007 8.692-2.391 1.778-3.183 2.288-4.408 1.094-.825-.824-.607-6.257 1.44-13.542 2.048-7.284 5.926-16.419 12.423-24.646l4.314-4.652 5.608-3.487s36.235-21.656 43.472-27.222c7.236-5.567 13.818-9.906 20.852-17.141 5.8-5.966 9.862-12.71 13.2-23.294 3.337-10.584 3.255-22.41-.32-39.893-2-9.802-3.437-15.63-4.762-18.488-1.325-2.857-2.538-2.744-4.088-.664Zm-25.627 64.881c-1.863 2.475-4.967 5.83-9.228 9.458-4.261 3.627-12.102 9.792-17.795 13.846-14.714 10.478-21.444 15.399-33.585 27.488-12.142 12.09-19.646 22.125-27.67 37.529-5.379 10.328-7.509 21.846-8.7 30.74-1.192 8.895-.115 12.854 1.014 24.396 1.918 9.248 1.333 13.436-1.667 4.062-3-9.373-3.992-18.894-2.85-28.948 2.393-21.084 13.206-41.627 27.407-58.617 14.2-16.99 23.827-24.467 52.125-45.241 17.024-12.498 14.794-12.12 19.233-15.42 4.438-3.3 11.06-11.702 1.716.707Z" />
|
<path d="M230.869 98.463a118.54 118.54 0 0 0-2.062 3.007 341.651 341.651 0 0 0-3.038 4.613c-7.325 11.203-15.753 17.585-30.313 26.161-14.559 8.576-30.42 10.49-63.656 28.87-5.655 3.127-19.19 9.271-19.642 12.208-.45 2.937-.281 14.794-.281 14.794.116 2.402-.032 3.511-.128 5.997-.095 2.486-.139 6.348.186 14.256.525 13.241-2.282 9.535-4.203 3.548-1.922-5.987-4.622-15.41-5.397-26.819-.4-5.951-.525-8.938-1.587-8.747-1.063.19-3.063 3.557-7.213 10.312-6.975 11.306-11.4 22.838-13.244 34.376-1.843 11.537-1.106 23.08 2.244 34.406.25.824.375 1.236.231 1.236-.143 0-.556-.412-1.381-1.236-2.975-3.006-5.337-7.248-6.956-12.304-1.619-5.056-2.494-10.925-2.494-17.185 0-4.407-.091-6.577-.868-6.318-.777.259-2.24 2.946-4.982 8.253-6.45 12.48-10.045 22.685-11.204 32.338-1.158 9.654.12 18.757 3.421 29.035 3.012 9.377 6.837 16.353 10.18 21.205 3.34 4.853 6.198 7.581 7.274 8.464 5.532 4.538 6.337 6.521 4.078 5.959-2.258-.562-7.581-3.67-14.306-9.312-6.8-5.663-10.5-8.392-11.225-8.144-.725.247 1.525 3.47 6.625 9.71 7.4 9.06 15.129 17.366 29.704 25.226 14.576 7.86 15.039 6.407 26.214 11.596 10.175 4.737 16.35 7.702 20.994 10.24 4.644 2.538 7.756 4.65 11.806 7.676 2.35 1.771 5.588 4.12 8.913 6.467 3.325 2.347 6.737 4.695 9.437 6.466 8.85 5.87 4.065-4.607-3.96-11.69-17.175-15.158-21.043-16.706-24.778-27.568-3.735-10.861-5.266-15.307 5.1-33.732 5.401-9.6 14.637-17.98 23.146-26.608 8.51-8.628 20.374-18.548 38.424-34.54 10.517-9.317 16.709-16.023 17.834-19.015 1.125-2.991-2.817-2.27-12.567 3.27-2.475 1.442-6.875 3.759-11.962 6.333a699.312 699.312 0 0 1-16.088 7.877 1981.677 1981.677 0 0 0-15.475 7.409c-4.712 2.28-8.6 4.185-10.525 5.153-7.625 3.872-11.616 6.915-14.007 8.692-2.391 1.778-3.183 2.288-4.408 1.094-.825-.824-.607-6.257 1.44-13.542 2.048-7.284 5.926-16.419 12.423-24.646l4.314-4.652 5.608-3.487s36.235-21.656 43.472-27.222c7.236-5.567 13.818-9.906 20.852-17.141 5.8-5.966 9.862-12.71 13.2-23.294 3.337-10.584 3.255-22.41-.32-39.893-2-9.802-3.437-15.63-4.762-18.488-1.325-2.857-2.538-2.744-4.088-.664Zm-25.627 64.881c-1.863 2.475-4.967 5.83-9.228 9.458-4.261 3.627-12.102 9.792-17.795 13.846-14.714 10.478-21.444 15.399-33.585 27.488-12.142 12.09-19.646 22.125-27.67 37.529-5.379 10.328-7.509 21.846-8.7 30.74-1.192 8.895-.115 12.854 1.014 24.396 1.918 9.248 1.333 13.436-1.667 4.062-3-9.373-3.992-18.894-2.85-28.948 2.393-21.084 13.206-41.627 27.407-58.617 14.2-16.99 23.827-24.467 52.125-45.241 17.024-12.498 14.794-12.12 19.233-15.42 4.438-3.3 11.06-11.702 1.716.707Z" />
|
||||||
|
18
components/KindCard/1.tsx
Normal file
18
components/KindCard/1.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import Container from "./components/Container";
|
||||||
|
import { CardTitle, CardDescription } from "@/components/ui/card";
|
||||||
|
import { type Event } from "nostr-tools";
|
||||||
|
|
||||||
|
export default function Kind1({}: Event) {
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<CardTitle className="mb-1.5 line-clamp-2 text-lg font-semibold">
|
||||||
|
The start of the Nostr revolution
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription className="line-clamp-4 text-sm">
|
||||||
|
This is the summary of this artilce. Let's hope that it is a good
|
||||||
|
article and that it will end up being worth reading. I don't want to
|
||||||
|
waste my time on some random other stuff.
|
||||||
|
</CardDescription>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
23
components/KindCard/30023.tsx
Normal file
23
components/KindCard/30023.tsx
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
"use client";
|
||||||
|
import Container from "./components/Container";
|
||||||
|
import { CardTitle, CardDescription } from "@/components/ui/card";
|
||||||
|
import { getTagValues, getTagsValues } from "@/lib/nostr/utils";
|
||||||
|
import { type Event } from "nostr-tools";
|
||||||
|
import { removeDuplicates } from "@/lib/utils";
|
||||||
|
|
||||||
|
export default function Kind30023({ content, tags }: Event) {
|
||||||
|
const title = getTagValues("title", tags);
|
||||||
|
const summary = getTagValues("summary", tags);
|
||||||
|
const contentTags = removeDuplicates(getTagsValues("t", tags));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container contentTags={contentTags}>
|
||||||
|
<CardTitle className="mb-1.5 line-clamp-2 text-lg font-semibold">
|
||||||
|
{title}
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription className="line-clamp-4 text-sm">
|
||||||
|
{summary ?? content}
|
||||||
|
</CardDescription>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
18
components/KindCard/3745.tsx
Normal file
18
components/KindCard/3745.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import Container from "./components/Container";
|
||||||
|
import { CardTitle, CardDescription } from "@/components/ui/card";
|
||||||
|
import { type Event } from "nostr-tools";
|
||||||
|
|
||||||
|
export default function Kind3745({}: Event) {
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<CardTitle className="mb-1.5 line-clamp-2 text-lg font-semibold">
|
||||||
|
The start of the Nostr revolution
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription className="line-clamp-4 text-sm">
|
||||||
|
This is the summary of this artilce. Let's hope that it is a good
|
||||||
|
article and that it will end up being worth reading. I don't want to
|
||||||
|
waste my time on some random other stuff.
|
||||||
|
</CardDescription>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
27
components/KindCard/components/Actions.tsx
Normal file
27
components/KindCard/components/Actions.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
HiOutlineHandThumbUp,
|
||||||
|
HiOutlineChatBubbleLeftEllipsis,
|
||||||
|
} from "react-icons/hi2";
|
||||||
|
import { HiOutlineLightningBolt } from "react-icons/hi";
|
||||||
|
|
||||||
|
export default function Actions() {
|
||||||
|
return (
|
||||||
|
<div className="mt-3 flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Button size={"sm"} variant={"ghost"} className="gap-x-1.5 px-2">
|
||||||
|
<HiOutlineHandThumbUp className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<Button size={"sm"} variant={"ghost"} className="gap-x-1.5 px-2">
|
||||||
|
<HiOutlineChatBubbleLeftEllipsis className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Button size={"sm"} className="gap-x-1.5">
|
||||||
|
<HiOutlineLightningBolt className="h-4 w-4" />
|
||||||
|
<span>zap</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
42
components/KindCard/components/Container.tsx
Normal file
42
components/KindCard/components/Container.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
"use client";
|
||||||
|
import Image from "next/image";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { RiMoreFill } from "react-icons/ri";
|
||||||
|
import { Card, CardContent, CardHeader } from "@/components/ui/card";
|
||||||
|
import { formatDate } from "@/lib/utils/dates";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { ReactNode } from "react";
|
||||||
|
import ProfileHeader from "./ProfileHeader";
|
||||||
|
import Actions from "./Actions";
|
||||||
|
import Tags from "./Tags";
|
||||||
|
import { type Event } from "nostr-tools";
|
||||||
|
|
||||||
|
type CreatorCardProps = {
|
||||||
|
contentTags?: string[];
|
||||||
|
children: ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function Container({ children, contentTags }: CreatorCardProps) {
|
||||||
|
return (
|
||||||
|
<Card className="relative overflow-hidden">
|
||||||
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 p-4 pb-4">
|
||||||
|
<ProfileHeader />
|
||||||
|
<div className="-mr-1 flex items-center gap-x-1.5 text-xs text-muted-foreground">
|
||||||
|
{formatDate(new Date("10-5-23"), "MMM Do")}
|
||||||
|
<Button size={"sm"} variant={"ghost"} className="center h-6 w-6 p-0">
|
||||||
|
<RiMoreFill className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="px-4 pb-3">
|
||||||
|
{children}
|
||||||
|
{!!contentTags?.length && (
|
||||||
|
<div className="-mb-2 mt-1 max-h-[52px] overflow-hidden">
|
||||||
|
<Tags tags={contentTags} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<Actions />
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
25
components/KindCard/components/ProfileHeader.tsx
Normal file
25
components/KindCard/components/ProfileHeader.tsx
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { Avatar, AvatarImage, AvatarFallback } from "@radix-ui/react-avatar";
|
||||||
|
import { HiCheckBadge } from "react-icons/hi2";
|
||||||
|
|
||||||
|
type ProfileHeaderProps = {};
|
||||||
|
export default function ProfileHeader({}: ProfileHeaderProps) {
|
||||||
|
return (
|
||||||
|
<div className="center gap-x-3">
|
||||||
|
<Avatar className="center h-8 w-8 overflow-hidden rounded-sm bg-muted">
|
||||||
|
<AvatarImage
|
||||||
|
src={
|
||||||
|
"https://images.unsplash.com/photo-1566492031773-4f4e44671857?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=4&w=256&h=256&q=60"
|
||||||
|
}
|
||||||
|
alt="user"
|
||||||
|
/>
|
||||||
|
<AvatarFallback className="text-xs">SC</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<div className="center 5 gap-1">
|
||||||
|
<span className="text-xs uppercase text-muted-foreground">
|
||||||
|
Derek Seivers
|
||||||
|
</span>
|
||||||
|
<HiCheckBadge className="h-4 w-4 text-primary" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
18
components/KindCard/components/Tags.tsx
Normal file
18
components/KindCard/components/Tags.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
|
||||||
|
type TagsProps = {
|
||||||
|
tags: string[];
|
||||||
|
};
|
||||||
|
export default function Tags({ tags }: TagsProps) {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-row-reverse flex-wrap items-center gap-2">
|
||||||
|
{tags.map((t, idx) => (
|
||||||
|
<Badge key={idx} variant={"secondary"}>
|
||||||
|
{t}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
18
components/KindCard/default.tsx
Normal file
18
components/KindCard/default.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import Container from "./components/Container";
|
||||||
|
import { CardTitle, CardDescription } from "@/components/ui/card";
|
||||||
|
import { type Event } from "nostr-tools";
|
||||||
|
|
||||||
|
export default function KindDefault({}: Event) {
|
||||||
|
return (
|
||||||
|
<Container>
|
||||||
|
<CardTitle className="mb-1.5 line-clamp-2 text-lg font-semibold">
|
||||||
|
The start of the Nostr revolution
|
||||||
|
</CardTitle>
|
||||||
|
<CardDescription className="line-clamp-4 text-sm">
|
||||||
|
This is the summary of this artilce. Let's hope that it is a good
|
||||||
|
article and that it will end up being worth reading. I don't want to
|
||||||
|
waste my time on some random other stuff.
|
||||||
|
</CardDescription>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
|
}
|
31
components/KindCard/index.tsx
Normal file
31
components/KindCard/index.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { type ComponentType } from "react";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
import { type Event } from "nostr-tools";
|
||||||
|
|
||||||
|
const KindCard1 = dynamic(() => import("./1"), {
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
const KindCard3745 = dynamic(() => import("./3745"), {
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
const KindCard30023 = dynamic(() => import("./30023"), {
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
const KindCardDefault = dynamic(() => import("./default"), {
|
||||||
|
ssr: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
const componentMap: Record<number, ComponentType<KindCardProps>> = {
|
||||||
|
1: KindCard1,
|
||||||
|
30023: KindCard30023,
|
||||||
|
3745: KindCard3745,
|
||||||
|
};
|
||||||
|
|
||||||
|
type KindCardProps = Event<number>;
|
||||||
|
export default function KindCard(props: KindCardProps) {
|
||||||
|
const { kind } = props;
|
||||||
|
const KindCard_ = componentMap[kind] ?? KindCardDefault;
|
||||||
|
return <KindCard_ {...props} />;
|
||||||
|
}
|
@ -1,59 +1,27 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { useEffect, useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
import { BlockNoteEditor, PartialBlock, Block } from "@blocknote/core";
|
import { BlockNoteEditor, PartialBlock } from "@blocknote/core";
|
||||||
import { BlockNoteView, useBlockNote } from "@blocknote/react";
|
import { BlockNoteView, useBlockNote } from "@blocknote/react";
|
||||||
import Spinner from "../spinner";
|
|
||||||
import "@blocknote/core/style.css";
|
import "@blocknote/core/style.css";
|
||||||
|
|
||||||
interface EditorProps {
|
interface EditorProps {
|
||||||
onChange: (value: string) => void;
|
|
||||||
initialMarkdown?: string;
|
|
||||||
editable?: boolean;
|
editable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Editor = ({ onChange, initialMarkdown, editable }: EditorProps) => {
|
const Editor = ({ editable }: EditorProps) => {
|
||||||
const { resolvedTheme } = useTheme();
|
const { resolvedTheme } = useTheme();
|
||||||
const [loading, setLoading] = useState(true);
|
const [content, setContent] = useState("");
|
||||||
const [initialContent, setInitialContent] = useState<Block[]>();
|
|
||||||
|
|
||||||
const editor: BlockNoteEditor = useBlockNote({
|
const editor: BlockNoteEditor = useBlockNote({
|
||||||
editable,
|
editable,
|
||||||
initialContent: initialContent,
|
|
||||||
onEditorContentChange: (editor) => {
|
onEditorContentChange: (editor) => {
|
||||||
onChange(JSON.stringify(editor.topLevelBlocks, null, 2));
|
setContent(JSON.stringify(editor.topLevelBlocks, null, 2));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (editor) {
|
|
||||||
if (!initialContent && initialMarkdown) {
|
|
||||||
// Whenever the current Markdown content changes, converts it to an array
|
|
||||||
// of Block objects and replaces the editor's content with them.
|
|
||||||
const getBlocks = async () => {
|
|
||||||
const blocks: Block[] =
|
|
||||||
await editor.markdownToBlocks(initialMarkdown);
|
|
||||||
setInitialContent(blocks);
|
|
||||||
editor.replaceBlocks(editor.topLevelBlocks, blocks);
|
|
||||||
setLoading(false);
|
|
||||||
};
|
|
||||||
void getBlocks();
|
|
||||||
} else if (loading) {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [editor]);
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<div className="">
|
|
||||||
<Spinner />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="">
|
||||||
<BlockNoteView
|
<BlockNoteView
|
||||||
editor={editor}
|
editor={editor}
|
||||||
theme={resolvedTheme === "dark" ? "dark" : "light"}
|
theme={resolvedTheme === "dark" ? "dark" : "light"}
|
||||||
|
57
components/LongForm/index.tsx
Normal file
57
components/LongForm/index.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
"use client";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
|
import { BlockNoteEditor, Block } from "@blocknote/core";
|
||||||
|
import { BlockNoteView, useBlockNote } from "@blocknote/react";
|
||||||
|
import Spinner from "../spinner";
|
||||||
|
|
||||||
|
type MarkdoneProps = {
|
||||||
|
content?: string;
|
||||||
|
editable?: boolean;
|
||||||
|
};
|
||||||
|
export default function Markdown({ content }: MarkdoneProps) {
|
||||||
|
const { resolvedTheme } = useTheme();
|
||||||
|
// const [blocks, setBlocks] = useState<Block[]>();
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
const editor: BlockNoteEditor = useBlockNote({
|
||||||
|
editable: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (editor) {
|
||||||
|
if (content) {
|
||||||
|
console.log("initial md", content);
|
||||||
|
// Whenever the current Markdown content changes, converts it to an array
|
||||||
|
// of Block objects and replaces the editor's content with them.
|
||||||
|
const getBlocks = async () => {
|
||||||
|
const blocks: Block[] = await editor.markdownToBlocks(content);
|
||||||
|
console.log("Blocks", blocks);
|
||||||
|
editor.replaceBlocks(editor.topLevelBlocks, blocks);
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
void getBlocks();
|
||||||
|
} else if (loading) {
|
||||||
|
console.log("TURING LOADING OFF");
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [editor]);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<div className="">
|
||||||
|
<Spinner />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="pt-10">
|
||||||
|
<BlockNoteView
|
||||||
|
editor={editor}
|
||||||
|
theme={resolvedTheme === "dark" ? "dark" : "light"}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,83 +0,0 @@
|
|||||||
"use client";
|
|
||||||
import Image from "next/image";
|
|
||||||
import Link from "next/link";
|
|
||||||
import { RiMoreFill } from "react-icons/ri";
|
|
||||||
import { HiOutlineLightningBolt } from "react-icons/hi";
|
|
||||||
import {
|
|
||||||
HiOutlineHandThumbUp,
|
|
||||||
HiOutlineChatBubbleLeftEllipsis,
|
|
||||||
HiOutlineEllipsisHorizontal,
|
|
||||||
} from "react-icons/hi2";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Card,
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardHeader,
|
|
||||||
CardTitle,
|
|
||||||
} from "@/components/ui/card";
|
|
||||||
import { Avatar, AvatarImage, AvatarFallback } from "@radix-ui/react-avatar";
|
|
||||||
import { formatDate } from "@/lib/utils/dates";
|
|
||||||
import { Button } from "../ui/button";
|
|
||||||
|
|
||||||
type CreatorCardProps = {
|
|
||||||
displayName: string;
|
|
||||||
about: string;
|
|
||||||
picture: string;
|
|
||||||
banner: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function LongFormContentCard() {
|
|
||||||
return (
|
|
||||||
<Card className="relative overflow-hidden">
|
|
||||||
<CardHeader className="flex flex-row items-center justify-between space-y-0 p-4 pb-4">
|
|
||||||
<div className="center gap-x-3">
|
|
||||||
<Avatar className="center h-8 w-8 overflow-hidden rounded-sm bg-muted">
|
|
||||||
<AvatarImage
|
|
||||||
src={
|
|
||||||
"https://images.unsplash.com/photo-1566492031773-4f4e44671857?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=4&w=256&h=256&q=60"
|
|
||||||
}
|
|
||||||
alt="user"
|
|
||||||
/>
|
|
||||||
<AvatarFallback className="text-xs">SC</AvatarFallback>
|
|
||||||
</Avatar>
|
|
||||||
<span className="text-xs uppercase text-muted-foreground">
|
|
||||||
Derek Seivers
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div className="-mr-1 flex items-center gap-x-1.5 text-xs text-muted-foreground">
|
|
||||||
{formatDate(new Date("10-5-23"), "MMM Do")}
|
|
||||||
<Button size={"sm"} variant={"ghost"} className="center h-6 w-6 p-0">
|
|
||||||
<RiMoreFill className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="px-4 pb-3">
|
|
||||||
<CardTitle className="mb-1.5 line-clamp-2 text-lg font-semibold">
|
|
||||||
The start of the Nostr revolution
|
|
||||||
</CardTitle>
|
|
||||||
<CardDescription className="line-clamp-4 text-sm">
|
|
||||||
This is the summary of this artilce. Let's hope that it is a good
|
|
||||||
article and that it will end up being worth reading. I don't want to
|
|
||||||
waste my time on some random other stuff.
|
|
||||||
</CardDescription>
|
|
||||||
<div className="mt-3 flex items-center justify-between">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<Button size={"sm"} variant={"ghost"} className="gap-x-1.5 px-2">
|
|
||||||
<HiOutlineHandThumbUp className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
<Button size={"sm"} variant={"ghost"} className="gap-x-1.5 px-2">
|
|
||||||
<HiOutlineChatBubbleLeftEllipsis className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<Button size={"sm"} className="gap-x-1.5">
|
|
||||||
<HiOutlineLightningBolt className="h-4 w-4" />
|
|
||||||
<span>zap</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}
|
|
56
components/SubscriptionCard/index.tsx
Normal file
56
components/SubscriptionCard/index.tsx
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import Image from "next/image";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/components/ui/card";
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { HiOutlineCheckBadge } from "react-icons/hi2";
|
||||||
|
|
||||||
|
type SubscriptionCardProps = {
|
||||||
|
id: string;
|
||||||
|
title: string;
|
||||||
|
picture: string;
|
||||||
|
description: string;
|
||||||
|
tags: string[];
|
||||||
|
};
|
||||||
|
export default function SubscriptionCard({
|
||||||
|
picture,
|
||||||
|
title,
|
||||||
|
tags,
|
||||||
|
description,
|
||||||
|
}: SubscriptionCardProps) {
|
||||||
|
return (
|
||||||
|
<Card className="group sm:flex">
|
||||||
|
<div className="overflow-hidden max-sm:h-[100px] max-sm:rounded-t-md sm:w-[250px] sm:rounded-l-md">
|
||||||
|
<Image
|
||||||
|
width={250}
|
||||||
|
height={150}
|
||||||
|
src={picture}
|
||||||
|
alt={title}
|
||||||
|
unoptimized
|
||||||
|
className={cn(
|
||||||
|
"w-auto object-cover object-center transition-all group-hover:scale-105 sm:h-full",
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="">
|
||||||
|
<CardHeader className="">
|
||||||
|
<CardTitle className="line-clamp-2">{title}</CardTitle>
|
||||||
|
<CardDescription className="line-clamp-3">
|
||||||
|
{description}
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="items-strech flex w-full flex-col items-center gap-2 sm:max-w-md sm:flex-row sm:gap-4">
|
||||||
|
<Button className="w-full">Buy now</Button>
|
||||||
|
<Button variant={"secondary"} className="w-full">
|
||||||
|
Details
|
||||||
|
</Button>
|
||||||
|
</CardContent>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
45
components/Tabs/index.tsx
Normal file
45
components/Tabs/index.tsx
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
|
type TabsProps<T> = {
|
||||||
|
tabs: T[];
|
||||||
|
activeTab: string;
|
||||||
|
setActiveTab: (tab: T) => void;
|
||||||
|
};
|
||||||
|
export default function Tabs<T extends { name: string; label: string }>({
|
||||||
|
tabs,
|
||||||
|
activeTab,
|
||||||
|
setActiveTab,
|
||||||
|
}: TabsProps<T>) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between pb-3">
|
||||||
|
<div
|
||||||
|
role="tablist"
|
||||||
|
aria-orientation="horizontal"
|
||||||
|
className="inline-flex h-9 w-full items-center justify-start rounded-none border-b bg-transparent p-0 text-muted-foreground"
|
||||||
|
data-orientation="horizontal"
|
||||||
|
>
|
||||||
|
{tabs.map((tab, idx) => (
|
||||||
|
<button
|
||||||
|
onClick={() => setActiveTab(tab)}
|
||||||
|
type="button"
|
||||||
|
role="tab"
|
||||||
|
aria-selected="true"
|
||||||
|
aria-controls="content-preview"
|
||||||
|
data-state="active"
|
||||||
|
id="trigger-preview"
|
||||||
|
className={cn(
|
||||||
|
"relative inline-flex h-9 items-center justify-center whitespace-nowrap rounded-none border-b-2 border-b-transparent bg-transparent px-4 py-1 pb-3 pt-2 text-sm font-semibold text-muted-foreground shadow-none ring-offset-background transition-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||||
|
activeTab === tab.name &&
|
||||||
|
"data-[state=active]:border-b-primary data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-none",
|
||||||
|
)}
|
||||||
|
tabIndex={idx}
|
||||||
|
data-orientation="horizontal"
|
||||||
|
data-radix-collection-item=""
|
||||||
|
>
|
||||||
|
{tab.label}
|
||||||
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
55
components/ui/tabs.tsx
Normal file
55
components/ui/tabs.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const Tabs = TabsPrimitive.Root
|
||||||
|
|
||||||
|
const TabsList = React.forwardRef<
|
||||||
|
React.ElementRef<typeof TabsPrimitive.List>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<TabsPrimitive.List
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TabsList.displayName = TabsPrimitive.List.displayName
|
||||||
|
|
||||||
|
const TabsTrigger = React.forwardRef<
|
||||||
|
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<TabsPrimitive.Trigger
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
||||||
|
|
||||||
|
const TabsContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<TabsPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TabsContent.displayName = TabsPrimitive.Content.displayName
|
||||||
|
|
||||||
|
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
38
constants/dummy.ts
Normal file
38
constants/dummy.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { type Event } from "nostr-tools";
|
||||||
|
import { unixTimeNowInSeconds } from "@/lib/nostr/dates";
|
||||||
|
|
||||||
|
export const DUMMY_1: Event = {
|
||||||
|
id: "test",
|
||||||
|
content: "Time for nostr to take over twitter",
|
||||||
|
kind: 1,
|
||||||
|
pubkey: "235235",
|
||||||
|
sig: "wetwet",
|
||||||
|
tags: [["t", "nostr"]],
|
||||||
|
created_at: unixTimeNowInSeconds() - 3600,
|
||||||
|
};
|
||||||
|
export const DUMMY_30023: Event = {
|
||||||
|
kind: 30023,
|
||||||
|
created_at: 1675642635,
|
||||||
|
content:
|
||||||
|
"Lorem [ipsum][nostr:nevent1qqst8cujky046negxgwwm5ynqwn53t8aqjr6afd8g59nfqwxpdhylpcpzamhxue69uhhyetvv9ujuetcv9khqmr99e3k7mg8arnc9] dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n\nRead more at nostr:naddr1qqzkjurnw4ksz9thwden5te0wfjkccte9ehx7um5wghx7un8qgs2d90kkcq3nk2jry62dyf50k0h36rhpdtd594my40w9pkal876jxgrqsqqqa28pccpzu.",
|
||||||
|
tags: [
|
||||||
|
["d", "lorem-ipsum"],
|
||||||
|
["title", "Lorem Ipsum"],
|
||||||
|
["published_at", "1296962229"],
|
||||||
|
["t", "placeholder"],
|
||||||
|
["t", "nostr"],
|
||||||
|
[
|
||||||
|
"e",
|
||||||
|
"b3e392b11f5d4f28321cedd09303a748acfd0487aea5a7450b3481c60b6e4f87",
|
||||||
|
"wss://relay.example.com",
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"a",
|
||||||
|
"30023:a695f6b60119d9521934a691347d9f78e8770b56da16bb255ee286ddf9fda919:ipsum",
|
||||||
|
"wss://relay.nostr.org",
|
||||||
|
],
|
||||||
|
],
|
||||||
|
pubkey: "...",
|
||||||
|
id: "...",
|
||||||
|
sig: "wetwet",
|
||||||
|
};
|
@ -1 +1,2 @@
|
|||||||
export * from "./relays";
|
export * from "./relays";
|
||||||
|
export * from "./dummy";
|
||||||
|
26
containers/Article/Actions.tsx
Normal file
26
containers/Article/Actions.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
"use client";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import {
|
||||||
|
HiOutlineHandThumbUp,
|
||||||
|
HiOutlineChatBubbleLeftEllipsis,
|
||||||
|
} from "react-icons/hi2";
|
||||||
|
import { HiOutlineLightningBolt } from "react-icons/hi";
|
||||||
|
|
||||||
|
export default function Actions() {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<Button size={"sm"} variant={"outline"} className="gap-x-1.5">
|
||||||
|
<HiOutlineHandThumbUp className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<Button size={"sm"} variant={"outline"} className="gap-x-1.5">
|
||||||
|
<HiOutlineChatBubbleLeftEllipsis className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<Button size={"sm"} className="gap-x-1.5">
|
||||||
|
<HiOutlineLightningBolt className="h-4 w-4" />
|
||||||
|
<span>zap</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
109
containers/Article/index.tsx
Normal file
109
containers/Article/index.tsx
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
"use client";
|
||||||
|
import dynamic from "next/dynamic";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { RiCloseFill } from "react-icons/ri";
|
||||||
|
import { Avatar, AvatarImage, AvatarFallback } from "@radix-ui/react-avatar";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
import { formatDate } from "@/lib/utils/dates";
|
||||||
|
import Actions from "./Actions";
|
||||||
|
|
||||||
|
export default function ArticlePage() {
|
||||||
|
const Viewer = useMemo(
|
||||||
|
() => dynamic(() => import("@/components/LongForm"), { ssr: false }),
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const router = useRouter();
|
||||||
|
const markdown = `Do you have any thoughts of YakiHonne? Share it and earn SATs!
|
||||||
|
|
||||||
|
Comment2Earn | Earn SATs by sharing your comments on YakiHonne
|
||||||
|
|
||||||
|
Earn SATs by sharing your comments on YakiHonne.
|
||||||
|
|
||||||
|
⏰2nd - 15th Oct
|
||||||
|
|
||||||
|
### Follow Us
|
||||||
|
|
||||||
|
- Nostr: npub1yzvxlwp7wawed5vgefwfmugvumtp8c8t0etk3g8sky4n0ndvyxesnxrf8q
|
||||||
|
- Twitter: https://twitter.com/YakiHonne
|
||||||
|
- Facebook Profile: https://www.facebook.com/profi…1715056704
|
||||||
|
- Facebook Page: https://www.facebook.com/profi…2076811240
|
||||||
|
- Facebook Group: https://www.facebook.com/group…4539860115
|
||||||
|
- Youtube: https://www.youtube.com/channe…f4EyFJ7BlA
|
||||||
|
|
||||||
|
### How to Get SATs:
|
||||||
|
1. Post your thoughts about YakiHonne on at least one of the above social media, and be sure to @ YakiHonne.
|
||||||
|
2. Follow YakiHonne on at least one of the social media above.
|
||||||
|
3. Back to this article, leave your social account which followed YakiHonne in the Comments.
|
||||||
|
4. Be zapped with SATs.
|
||||||
|
|
||||||
|
### What You Will Get:
|
||||||
|
1. 500 SATs, if you finished all steps.
|
||||||
|
2. 1000 SATs, if you finished all steps and`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="relative @container">
|
||||||
|
<div className="absolute inset-x-0 top-0 flex items-center justify-between border-b pb-4 pt-4">
|
||||||
|
<div className="center gap-x-3">
|
||||||
|
<Avatar className="center h-8 w-8 overflow-hidden rounded-sm bg-muted">
|
||||||
|
<AvatarImage
|
||||||
|
src={
|
||||||
|
"https://images.unsplash.com/photo-1566492031773-4f4e44671857?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=facearea&facepad=4&w=256&h=256&q=60"
|
||||||
|
}
|
||||||
|
alt="user"
|
||||||
|
/>
|
||||||
|
<AvatarFallback className="text-xs">SC</AvatarFallback>
|
||||||
|
</Avatar>
|
||||||
|
<span className="text-xs uppercase text-muted-foreground">
|
||||||
|
Derek Seivers
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (sessionStorage.getItem("RichHistory")) {
|
||||||
|
void router.back();
|
||||||
|
} else {
|
||||||
|
void router.push("/app");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
size="icon"
|
||||||
|
variant={"outline"}
|
||||||
|
className=""
|
||||||
|
>
|
||||||
|
<RiCloseFill className="h-5 w-5" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className="h-[70px] w-full"></div>
|
||||||
|
<div className="max-h-[calc(100vh_-_100px)] overflow-y-auto">
|
||||||
|
<article className="prose dark:prose-invert prose-zinc relative mx-auto max-w-3xl pt-7">
|
||||||
|
<div className="">
|
||||||
|
<div className="flex items-center justify-between gap-1 lg:mb-2">
|
||||||
|
<Button variant={"link"} className="px-0">
|
||||||
|
Balaji's News Letter
|
||||||
|
</Button>
|
||||||
|
<div className="center text-xs text-muted-foreground/50">
|
||||||
|
<span className="mr-2.5">
|
||||||
|
{formatDate(new Date("10-2-22"), "MMMM Do, YYYY")}
|
||||||
|
</span>
|
||||||
|
<span className="h-3 w-[1px] rounded-full bg-muted-foreground/50"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h1 className="">
|
||||||
|
This is the large title for the article. It's time to take over.
|
||||||
|
</h1>
|
||||||
|
<div className="mb-3 flex items-center justify-end">
|
||||||
|
<Actions />
|
||||||
|
</div>
|
||||||
|
<div className="rounded-r-lg border-l-[4px] border-primary bg-muted p-4">
|
||||||
|
<p className="m-0">
|
||||||
|
Here is a short summary for the article that you are about to
|
||||||
|
start reading. Get ready to really enojy your self.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Viewer content={markdown} />
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
17
lib/nostr/dates.ts
Normal file
17
lib/nostr/dates.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
export function unixTimeNowInSeconds() {
|
||||||
|
return Math.floor(new Date().getTime() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function dateTomorrow() {
|
||||||
|
return new Date(Date.now() + 3600 * 1000 * 24);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formattedDate(unixTimestampInSeconds: number): string {
|
||||||
|
const options = {
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
} as const;
|
||||||
|
const date = new Date(unixTimestampInSeconds * 1000);
|
||||||
|
return date.toLocaleDateString("en-US", options);
|
||||||
|
}
|
64
lib/nostr/utils.ts
Normal file
64
lib/nostr/utils.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { nip19 } from "nostr-tools";
|
||||||
|
|
||||||
|
export const NOSTR_BECH32_REGEXP =
|
||||||
|
/^(npub|nprofile|note|nevent|naddr|nrelay)1[023456789acdefghjklmnpqrstuvwxyz]+/;
|
||||||
|
|
||||||
|
export function nip19ToTag(nip19Id: string): string[] | undefined {
|
||||||
|
const decoded = nip19.decode(nip19Id);
|
||||||
|
|
||||||
|
let tag: string[];
|
||||||
|
switch (decoded.type) {
|
||||||
|
case "npub":
|
||||||
|
case "note":
|
||||||
|
return ["p", decoded.data];
|
||||||
|
case "nprofile":
|
||||||
|
tag = ["e", decoded.data.pubkey];
|
||||||
|
if (decoded.data.relays && decoded.data.relays.length > 0)
|
||||||
|
tag.push(decoded.data.relays[0]);
|
||||||
|
return tag;
|
||||||
|
case "nevent":
|
||||||
|
tag = ["e", decoded.data.id];
|
||||||
|
if (decoded.data.relays && decoded.data.relays.length > 0)
|
||||||
|
tag.push(decoded.data.relays[0]);
|
||||||
|
return tag;
|
||||||
|
case "naddr":
|
||||||
|
tag = [
|
||||||
|
"a",
|
||||||
|
`${decoded.data.kind}:${decoded.data.pubkey}:${decoded.data.identifier}`,
|
||||||
|
];
|
||||||
|
if (decoded.data.relays && decoded.data.relays.length > 0) {
|
||||||
|
tag.push(decoded.data.relays[0]);
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function aTagToNip19(aTag: string[]): string {
|
||||||
|
if (aTag[0] !== "a") throw new Error("Not an a tag");
|
||||||
|
const tagIdSplit = aTag[1].split(":");
|
||||||
|
|
||||||
|
return nip19.naddrEncode({
|
||||||
|
kind: parseInt(tagIdSplit[0]),
|
||||||
|
pubkey: tagIdSplit[1],
|
||||||
|
identifier: tagIdSplit[2],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export const getTagValues = (name: string, tags: string[][]) => {
|
||||||
|
const [itemTag] = tags.filter((tag: string[]) => tag[0] === name);
|
||||||
|
const [, item] = itemTag || [, undefined];
|
||||||
|
return item;
|
||||||
|
};
|
||||||
|
export const getTagAllValues = (name: string, tags: string[][]) => {
|
||||||
|
const [itemTag] = tags.filter((tag: string[]) => tag[0] === name);
|
||||||
|
const itemValues = itemTag || [, undefined];
|
||||||
|
itemValues.shift();
|
||||||
|
return itemValues;
|
||||||
|
};
|
||||||
|
export const getTagsValues = (name: string, tags: string[][]) => {
|
||||||
|
const itemTags = tags.filter((tag: string[]) => tag[0] === name);
|
||||||
|
return itemTags.map(([key, val]) => val) ?? [];
|
||||||
|
};
|
||||||
|
export const getTagsAllValues = (name: string, tags: string[][]) => {
|
||||||
|
const itemTags = tags.filter((tag: string[]) => tag[0] === name);
|
||||||
|
return itemTags.map(([key, ...vals]) => vals) ?? [];
|
||||||
|
};
|
@ -24,6 +24,7 @@
|
|||||||
"@radix-ui/react-select": "^2.0.0",
|
"@radix-ui/react-select": "^2.0.0",
|
||||||
"@radix-ui/react-slot": "^1.0.2",
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
"@radix-ui/react-switch": "^1.0.3",
|
"@radix-ui/react-switch": "^1.0.3",
|
||||||
|
"@radix-ui/react-tabs": "^1.0.4",
|
||||||
"@tailwindcss/container-queries": "^0.1.1",
|
"@tailwindcss/container-queries": "^0.1.1",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.0.0",
|
"clsx": "^2.0.0",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user