diff --git a/app/(app)/(profile)/[npub]/page.tsx b/app/(app)/(profile)/[npub]/page.tsx new file mode 100644 index 0000000..60aa59a --- /dev/null +++ b/app/(app)/(profile)/[npub]/page.tsx @@ -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 ( +
+
+
+
+
+
+ banner +
+
+
+
+
+ profile picture +
+ + +
+
+
+

+ Zach Meyer +

+ +
+
+

@zach

·
+

zach@ordstr.com

+
+
+

+ This is my bio. You should check it out now. +

+
+
+
+
+
+ {demo.map((e) => ( + + ))} +
+
+ setActiveTab(t.name)} + /> +
+
+
+ ); +} diff --git a/app/(app)/_layout/Header.tsx b/app/(app)/_layout/Header.tsx index 5eed387..510dbc6 100644 --- a/app/(app)/_layout/Header.tsx +++ b/app/(app)/_layout/Header.tsx @@ -2,6 +2,8 @@ import { UserMenu } from "./components/UserMenu"; import { Search } from "./components/Search"; import { Notifications } from "./components/Notifications"; import { MobileMenu } from "./components/MobileMenu"; +import { Relays } from "./components/Relays"; + import Logo from "@/assets/Logo"; export default function Header() { return ( @@ -9,7 +11,7 @@ export default function Header() {
- +
Flockstr
@@ -19,6 +21,7 @@ export default function Header() {
+
diff --git a/app/(app)/_layout/Keystone.tsx b/app/(app)/_layout/Keystone.tsx index f8f6a2a..a800ef3 100644 --- a/app/(app)/_layout/Keystone.tsx +++ b/app/(app)/_layout/Keystone.tsx @@ -5,7 +5,7 @@ export default function Keystone() {
diff --git a/app/(app)/_layout/components/Relays.tsx b/app/(app)/_layout/components/Relays.tsx new file mode 100644 index 0000000..300d2fd --- /dev/null +++ b/app/(app)/_layout/components/Relays.tsx @@ -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 ( + + + + + + {RELAYS.map((r) => ( + + {r} + + ))} + + + + Manage Relays + ⇧⌘M + + + + ); +} diff --git a/app/(app)/app/_sections/BecomeACreator.tsx b/app/(app)/app/_sections/BecomeACreator.tsx index 465f418..94716ef 100644 --- a/app/(app)/app/_sections/BecomeACreator.tsx +++ b/app/(app)/app/_sections/BecomeACreator.tsx @@ -3,7 +3,7 @@ import { Button } from "@/components/ui/button"; export default function BecomeACreator() { return ( -
+
creator icons -
+

Start earning on Nostr

diff --git a/app/(app)/app/_sections/FeaturedLists.tsx b/app/(app)/app/_sections/FeaturedLists.tsx index 35f3070..bc4162a 100644 --- a/app/(app)/app/_sections/FeaturedLists.tsx +++ b/app/(app)/app/_sections/FeaturedLists.tsx @@ -77,7 +77,7 @@ export default function FeaturedLists() { )} />
- +
+ + Long form content + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/app/(app)/app/page.tsx b/app/(app)/app/page.tsx index 3aa1ac6..22849ba 100644 --- a/app/(app)/app/page.tsx +++ b/app/(app)/app/page.tsx @@ -1,16 +1,10 @@ import { Button } from "@/components/ui/button"; import HorizontalCarousel from "./_sections/HorizontalCarousel"; import { RiArrowRightLine } from "react-icons/ri"; -import LongFormContentCard from "@/components/LongFormContentCard"; import BecomeACreator from "./_sections/BecomeACreator"; -import { - Section, - SectionHeader, - SectionTitle, - SectionContent, -} from "@/containers/PageSection"; import LiveStreamingSection from "./_sections/LiveStreaming"; import FeaturedListsSection from "./_sections/FeaturedLists"; +import LongFormContentSection from "./_sections/LongFormContent"; export default function Page() { return ( @@ -26,20 +20,7 @@ export default function Page() {
-
- - Long form content - - - - - - - - -
+ diff --git a/app/(app)/article/[eventId]/page.tsx b/app/(app)/article/[eventId]/page.tsx index 2d8b268..398f4e3 100644 --- a/app/(app)/article/[eventId]/page.tsx +++ b/app/(app)/article/[eventId]/page.tsx @@ -1,13 +1,5 @@ "use client"; -import { useMemo } from "react"; -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, -}); +import Article from "@/containers/Article"; export default function ArticlePage({ params: { eventId }, @@ -16,48 +8,5 @@ export default function ArticlePage({ eventId: string; }; }) { - const router = useRouter(); - const markdown = `This is a test -### test text - -- First -- Second -1 nest`; - - return ( -
-
-
- - - SC - - - Derek Seivers - -
- -
-
- -
- ); + return
; } diff --git a/assets/Logo/index.tsx b/assets/Logo/index.tsx index faca468..2a2b7ad 100644 --- a/assets/Logo/index.tsx +++ b/assets/Logo/index.tsx @@ -6,6 +6,7 @@ const Logo = (props: SVGProps) => ( width={188.303} height={289.598} viewBox="54.17 96.938 188.303 289.598" + fill="currentColor" {...props} > diff --git a/bun.lockb b/bun.lockb index 2ee3a5c..9717de9 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/components/KindCard/1.tsx b/components/KindCard/1.tsx new file mode 100644 index 0000000..ad8befd --- /dev/null +++ b/components/KindCard/1.tsx @@ -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 ( + + + The start of the Nostr revolution + + + 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. + + + ); +} diff --git a/components/KindCard/30023.tsx b/components/KindCard/30023.tsx new file mode 100644 index 0000000..993237d --- /dev/null +++ b/components/KindCard/30023.tsx @@ -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 ( + + + {title} + + + {summary ?? content} + + + ); +} diff --git a/components/KindCard/3745.tsx b/components/KindCard/3745.tsx new file mode 100644 index 0000000..9d9e839 --- /dev/null +++ b/components/KindCard/3745.tsx @@ -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 ( + + + The start of the Nostr revolution + + + 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. + + + ); +} diff --git a/components/KindCard/components/Actions.tsx b/components/KindCard/components/Actions.tsx new file mode 100644 index 0000000..9cd3f22 --- /dev/null +++ b/components/KindCard/components/Actions.tsx @@ -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 ( +
+
+
+ + +
+ +
+
+ ); +} diff --git a/components/KindCard/components/Container.tsx b/components/KindCard/components/Container.tsx new file mode 100644 index 0000000..1b91571 --- /dev/null +++ b/components/KindCard/components/Container.tsx @@ -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 ( + + + +
+ {formatDate(new Date("10-5-23"), "MMM Do")} + +
+
+ + {children} + {!!contentTags?.length && ( +
+ +
+ )} + +
+
+ ); +} diff --git a/components/KindCard/components/ProfileHeader.tsx b/components/KindCard/components/ProfileHeader.tsx new file mode 100644 index 0000000..db627fb --- /dev/null +++ b/components/KindCard/components/ProfileHeader.tsx @@ -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 ( +
+ + + SC + +
+ + Derek Seivers + + +
+
+ ); +} diff --git a/components/KindCard/components/Tags.tsx b/components/KindCard/components/Tags.tsx new file mode 100644 index 0000000..91e6b1f --- /dev/null +++ b/components/KindCard/components/Tags.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { Badge } from "@/components/ui/badge"; + +type TagsProps = { + tags: string[]; +}; +export default function Tags({ tags }: TagsProps) { + return ( +
+ {tags.map((t, idx) => ( + + {t} + + ))} +
+ ); +} diff --git a/components/KindCard/default.tsx b/components/KindCard/default.tsx new file mode 100644 index 0000000..c53af98 --- /dev/null +++ b/components/KindCard/default.tsx @@ -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 ( + + + The start of the Nostr revolution + + + 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. + + + ); +} diff --git a/components/KindCard/index.tsx b/components/KindCard/index.tsx new file mode 100644 index 0000000..e19a7b2 --- /dev/null +++ b/components/KindCard/index.tsx @@ -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> = { + 1: KindCard1, + 30023: KindCard30023, + 3745: KindCard3745, +}; + +type KindCardProps = Event; +export default function KindCard(props: KindCardProps) { + const { kind } = props; + const KindCard_ = componentMap[kind] ?? KindCardDefault; + return ; +} diff --git a/components/LongForm/Editor.tsx b/components/LongForm/Editor.tsx index 3ae37ba..2635c09 100644 --- a/components/LongForm/Editor.tsx +++ b/components/LongForm/Editor.tsx @@ -1,59 +1,27 @@ "use client"; -import { useEffect, useState } from "react"; +import { useState } from "react"; 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 Spinner from "../spinner"; import "@blocknote/core/style.css"; interface EditorProps { - onChange: (value: string) => void; - initialMarkdown?: string; editable?: boolean; } -const Editor = ({ onChange, initialMarkdown, editable }: EditorProps) => { +const Editor = ({ editable }: EditorProps) => { const { resolvedTheme } = useTheme(); - const [loading, setLoading] = useState(true); - const [initialContent, setInitialContent] = useState(); + const [content, setContent] = useState(""); const editor: BlockNoteEditor = useBlockNote({ editable, - initialContent: initialContent, 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 ( -
- -
- ); - } - return ( -
+
(); + 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 ( +
+ +
+ ); + } + return ( +
+ +
+ ); +} diff --git a/components/LongFormContentCard/index.tsx b/components/LongFormContentCard/index.tsx deleted file mode 100644 index 37baf35..0000000 --- a/components/LongFormContentCard/index.tsx +++ /dev/null @@ -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 ( - - -
- - - SC - - - Derek Seivers - -
-
- {formatDate(new Date("10-5-23"), "MMM Do")} - -
-
- - - The start of the Nostr revolution - - - 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. - -
-
-
- - -
- -
-
-
-
- ); -} diff --git a/components/SubscriptionCard/index.tsx b/components/SubscriptionCard/index.tsx new file mode 100644 index 0000000..5590c9a --- /dev/null +++ b/components/SubscriptionCard/index.tsx @@ -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 ( + +
+ {title} +
+
+ + {title} + + {description} + + + + + + +
+
+ ); +} diff --git a/components/Tabs/index.tsx b/components/Tabs/index.tsx new file mode 100644 index 0000000..d7f08d8 --- /dev/null +++ b/components/Tabs/index.tsx @@ -0,0 +1,45 @@ +import { cn } from "@/lib/utils"; + +type TabsProps = { + tabs: T[]; + activeTab: string; + setActiveTab: (tab: T) => void; +}; +export default function Tabs({ + tabs, + activeTab, + setActiveTab, +}: TabsProps) { + return ( +
+
+ {tabs.map((tab, idx) => ( + + ))} +
+
+ ); +} diff --git a/components/ui/tabs.tsx b/components/ui/tabs.tsx new file mode 100644 index 0000000..0f4caeb --- /dev/null +++ b/components/ui/tabs.tsx @@ -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, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsList.displayName = TabsPrimitive.List.displayName + +const TabsTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName + +const TabsContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsContent.displayName = TabsPrimitive.Content.displayName + +export { Tabs, TabsList, TabsTrigger, TabsContent } diff --git a/constants/dummy.ts b/constants/dummy.ts new file mode 100644 index 0000000..2a76b6f --- /dev/null +++ b/constants/dummy.ts @@ -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", +}; diff --git a/constants/index.ts b/constants/index.ts index 6d2e816..5f94006 100644 --- a/constants/index.ts +++ b/constants/index.ts @@ -1 +1,2 @@ export * from "./relays"; +export * from "./dummy"; diff --git a/containers/Article/Actions.tsx b/containers/Article/Actions.tsx new file mode 100644 index 0000000..db4a1bc --- /dev/null +++ b/containers/Article/Actions.tsx @@ -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 ( +
+
+ + + +
+
+ ); +} diff --git a/containers/Article/index.tsx b/containers/Article/index.tsx new file mode 100644 index 0000000..b894ac2 --- /dev/null +++ b/containers/Article/index.tsx @@ -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 ( +
+
+
+ + + SC + + + Derek Seivers + +
+ +
+
+
+
+
+
+ +
+ + {formatDate(new Date("10-2-22"), "MMMM Do, YYYY")} + + +
+
+

+ This is the large title for the article. It's time to take over. +

+
+ +
+
+

+ Here is a short summary for the article that you are about to + start reading. Get ready to really enojy your self. +

+
+
+ +
+
+
+ ); +} diff --git a/lib/nostr/dates.ts b/lib/nostr/dates.ts new file mode 100644 index 0000000..e7e1814 --- /dev/null +++ b/lib/nostr/dates.ts @@ -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); +} diff --git a/lib/nostr/utils.ts b/lib/nostr/utils.ts new file mode 100644 index 0000000..a654bc9 --- /dev/null +++ b/lib/nostr/utils.ts @@ -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) ?? []; +}; diff --git a/package.json b/package.json index 4b665fa..1b1b8eb 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tabs": "^1.0.4", "@tailwindcss/container-queries": "^0.1.1", "class-variance-authority": "^0.7.0", "clsx": "^2.0.0",