feat: add reply form

This commit is contained in:
2024-01-17 12:24:04 +07:00
parent a20f5ca15d
commit 0b745cb40e
14 changed files with 512 additions and 95 deletions

View File

@@ -228,31 +228,25 @@ export class Ark {
return [...events];
}
public getCleanEventId(id: string) {
let eventId: string = id.replace("nostr:", "").split("'")[0].split(".")[0];
if (
eventId.startsWith("nevent1") ||
eventId.startsWith("note1") ||
eventId.startsWith("naddr1")
) {
const decode = nip19.decode(eventId);
if (decode.type === "nevent") eventId = decode.data.id;
if (decode.type === "note") eventId = decode.data;
}
return eventId;
}
public async getEventById(id: string) {
try {
let eventId: string = id
.replace("nostr:", "")
.split("'")[0]
.split(".")[0];
if (
eventId.startsWith("nevent1") ||
eventId.startsWith("note1") ||
eventId.startsWith("naddr1")
) {
const decode = nip19.decode(eventId);
if (decode.type === "nevent") eventId = decode.data.id;
if (decode.type === "note") eventId = decode.data;
if (decode.type === "naddr") {
return await this.ndk.fetchEvent({
kinds: [decode.data.kind],
"#d": [decode.data.identifier],
authors: [decode.data.pubkey],
});
}
}
const eventId = this.getCleanEventId(id);
return await this.ndk.fetchEvent(eventId);
} catch {
throw new Error("event not found");
@@ -304,19 +298,19 @@ export class Ark {
};
}
public async getThreads({ id }: { id: string }) {
public async getThreads(id: string) {
const eventId = this.getCleanEventId(id);
const fetcher = NostrFetcher.withCustomPool(ndkAdapter(this.ndk));
const relayUrls = [...this.ndk.pool.relays.values()].map(
(item) => item.url,
);
try {
const relayUrls = [...this.ndk.pool.relays.values()].map(
(item) => item.url,
);
const rawEvents = (await fetcher.fetchAllEvents(
relayUrls,
{
kinds: [NDKKind.Text],
"#e": [id],
"#e": [eventId],
},
{ since: 0 },
{ sort: true },

View File

@@ -48,12 +48,12 @@ export function ImagePreview({ url }: { url: string }) {
<button
type="button"
onClick={(e) => downloadImage(e)}
className="absolute z-10 items-center justify-center hidden w-10 h-10 bg-blue-500 rounded-lg right-2 top-2 group-hover:inline-flex hover:bg-blue-600"
className="absolute z-10 items-center justify-center hidden size-8 bg-white/10 text-white backdrop-blur-xl rounded-lg right-2 top-2 group-hover:inline-flex hover:bg-blue-500"
>
{downloaded ? (
<CheckCircleIcon className="w-5 h-5 text-white" />
<CheckCircleIcon className="size-4" />
) : (
<DownloadIcon className="w-5 h-5 text-white" />
<DownloadIcon className="size-4" />
)}
</button>
</div>

View File

@@ -6,15 +6,13 @@ export function ChildReply({
}: { event: NDKEvent; rootEventId?: string }) {
return (
<Note.Provider event={event}>
<Note.Root className="gap-2 pl-4 mb-5">
<div className="flex items-center justify-between px-3 h-14">
<Note.User className="flex-1 pr-1" />
<Note.Root className="pl-6">
<div className="flex items-center justify-between h-14">
<Note.User className="flex-1" />
<Note.Menu />
</div>
<Note.Content className="min-w-0" />
<div className="flex items-center gap-10 -ml-1">
<Note.Reply />
<Note.Reaction />
<Note.Content />
<div className="flex items-center justify-end gap-10 mt-4">
<Note.Repost />
<Note.Zap />
</div>

View File

@@ -1,8 +1,8 @@
import { NavArrowDownIcon } from "@lume/icons";
import { NDKEventWithReplies } from "@lume/types";
import { cn } from "@lume/utils";
import * as Collapsible from "@radix-ui/react-collapsible";
import { useState } from "react";
import { twMerge } from "tailwind-merge";
import { Note } from "..";
import { ChildReply } from "./childReply";
@@ -16,35 +16,33 @@ export function Reply({
return (
<Collapsible.Root open={open} onOpenChange={setOpen}>
<Note.Provider event={event}>
<Note.Root>
<div className="flex items-center justify-between px-3 h-14">
<Note.Root className="pt-2">
<div className="flex items-center justify-between h-14">
<Note.User className="flex-1 pr-1" />
<Note.Menu />
</div>
<Note.Content className="min-w-0 px-3" />
<div className="flex items-center justify-between px-3 -ml-1 h-14">
<Note.Content />
<div className="flex items-center justify-between h-14">
{event.replies?.length > 0 ? (
<Collapsible.Trigger asChild>
<div className="inline-flex items-center gap-1 ml-1 font-semibold text-blue-500 h-14">
<div className="inline-flex items-center gap-1 font-semibold text-sm text-neutral-600 dark:text-neutral-400 h-14">
<NavArrowDownIcon
className={twMerge(
"h-3 w-3",
open ? "rotate-180 transform" : "",
)}
className={cn("size-5", open ? "rotate-180 transform" : "")}
/>
{`${event.replies?.length} ${
event.replies?.length === 1 ? "reply" : "replies"
}`}
</div>
</Collapsible.Trigger>
) : null}
) : (
<div />
)}
<div className="inline-flex items-center gap-10">
<Note.Reply />
<Note.Repost />
<Note.Zap />
</div>
</div>
<div className={twMerge("px-3", open ? "pb-3" : "")}>
<div className={cn("", open ? "pb-3" : "")}>
{event.replies?.length > 0 ? (
<Collapsible.Content>
{event.replies?.map((childEvent) => (

View File

@@ -1,4 +1,5 @@
import { RepostIcon } from "@lume/icons";
import { cn } from "@lume/utils";
import { NDKEvent, NostrEvent } from "@nostr-dev-kit/ndk";
import { useQuery } from "@tanstack/react-query";
import { Note } from "..";
@@ -69,7 +70,12 @@ export function RepostNote({
}
return (
<Note.Root className={className}>
<Note.Root
className={cn(
"flex flex-col rounded-xl bg-neutral-50 dark:bg-neutral-950",
className,
)}
>
<User.Provider pubkey={event.pubkey}>
<User.Root className="flex gap-2 px-3 h-14">
<div className="inline-flex shrink-0 w-10 items-center justify-center">

View File

@@ -1,3 +1,4 @@
import { cn } from "@lume/utils";
import { NDKEvent } from "@nostr-dev-kit/ndk";
import { Note } from "..";
@@ -7,7 +8,12 @@ export function TextNote({
}: { event: NDKEvent; className?: string }) {
return (
<Note.Provider event={event}>
<Note.Root className={className}>
<Note.Root
className={cn(
"flex flex-col rounded-xl bg-neutral-50 dark:bg-neutral-950",
className,
)}
>
<div className="flex items-center justify-between px-3 h-14">
<Note.User className="flex-1 pr-1" />
<Note.Menu />

View File

@@ -11,7 +11,7 @@ export function ThreadNote({ eventId }: { eventId: string }) {
return (
<Note.Provider event={data}>
<Note.Root>
<Note.Root className="flex flex-col rounded-xl bg-neutral-50 dark:bg-neutral-950">
<div className="flex items-center justify-between px-3 h-16">
<User.Provider pubkey={data.pubkey}>
<User.Root className="flex h-16 items-center gap-3 flex-1">
@@ -29,11 +29,10 @@ export function ThreadNote({ eventId }: { eventId: string }) {
<Note.Menu />
</div>
<Note.Thread className="mb-2" />
<Note.Content className="min-w-0 px-3" isTranslatable />
<Note.Content className="min-w-0 px-3" />
<div className="flex items-center justify-between px-3 h-14">
<Note.Pin />
<div className="inline-flex items-center gap-10">
<Note.Reply />
<Note.Repost />
<Note.Zap />
</div>

View File

@@ -10,10 +10,7 @@ export function NoteRoot({
}) {
return (
<div
className={cn(
"flex h-min w-full flex-col overflow-hidden rounded-xl bg-neutral-50 dark:bg-neutral-950",
className,
)}
className={cn("h-min w-full overflow-hidden", className)}
contentEditable={false}
>
{children}