From ed538c91c6da40dbbd8cee94dc9c39e32e583e5c Mon Sep 17 00:00:00 2001 From: reya Date: Wed, 27 Dec 2023 15:01:40 +0700 Subject: [PATCH] feat(columns): update timeline column --- packages/@columns/timeline/package.json | 2 + packages/@columns/timeline/src/home.tsx | 5 +- packages/@columns/timeline/src/user.tsx | 212 +++++++++++++++++- packages/ark/src/components/column/live.tsx | 2 +- .../src/components/note/builds/childReply.tsx | 4 +- .../ark/src/components/note/mentions/note.tsx | 34 ++- .../ark/src/components/note/mentions/user.tsx | 45 ++-- packages/ark/src/components/note/user.tsx | 18 +- packages/ark/src/hooks/useRichContent.tsx | 4 +- packages/ark/src/hooks/useSuggestion.ts | 78 ------- packages/ark/src/index.ts | 1 - packages/ui/src/index.ts | 1 + .../src/components => ui/src}/mentions.tsx | 0 pnpm-lock.yaml | 6 + 14 files changed, 282 insertions(+), 130 deletions(-) delete mode 100644 packages/ark/src/hooks/useSuggestion.ts rename packages/{ark/src/components => ui/src}/mentions.tsx (100%) diff --git a/packages/@columns/timeline/package.json b/packages/@columns/timeline/package.json index e6603ecb..47c3229e 100644 --- a/packages/@columns/timeline/package.json +++ b/packages/@columns/timeline/package.json @@ -6,11 +6,13 @@ "dependencies": { "@lume/ark": "workspace:^", "@lume/icons": "workspace:^", + "@lume/ui": "workspace:^", "@lume/utils": "workspace:^", "@nostr-dev-kit/ndk": "^2.3.1", "@tanstack/react-query": "^5.14.2", "react": "^18.2.0", "react-router-dom": "^6.21.0", + "sonner": "^1.2.4", "virtua": "^0.18.0" }, "devDependencies": { diff --git a/packages/@columns/timeline/src/home.tsx b/packages/@columns/timeline/src/home.tsx index 5e95274d..7c542baa 100644 --- a/packages/@columns/timeline/src/home.tsx +++ b/packages/@columns/timeline/src/home.tsx @@ -87,9 +87,8 @@ export function HomeRoute({ colKey }: { colKey: string }) {
{isLoading ? ( -
- - Loading +
+
) : ( allEvents.map((item) => renderItem(item)) diff --git a/packages/@columns/timeline/src/user.tsx b/packages/@columns/timeline/src/user.tsx index d6bbbb09..8d46f957 100644 --- a/packages/@columns/timeline/src/user.tsx +++ b/packages/@columns/timeline/src/user.tsx @@ -1,7 +1,213 @@ -import { useParams } from "react-router-dom"; +import { + RepostNote, + TextNote, + useArk, + useProfile, + useStorage, +} from "@lume/ark"; +import { ArrowLeftIcon, ArrowRightCircleIcon, LoaderIcon } from "@lume/icons"; +import { NIP05 } from "@lume/ui"; +import { FETCH_LIMIT, displayNpub } from "@lume/utils"; +import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; +import { useInfiniteQuery } from "@tanstack/react-query"; +import { useEffect, useMemo, useState } from "react"; +import { Link, useNavigate, useParams } from "react-router-dom"; +import { toast } from "sonner"; +import { WVList } from "virtua"; export function UserRoute() { - const { id } = useParams(); + const ark = useArk(); + const storage = useStorage(); + const navigate = useNavigate(); - return
{id}
; + const { id } = useParams(); + const { user } = useProfile(id); + const { data, hasNextPage, isLoading, isFetchingNextPage, fetchNextPage } = + useInfiniteQuery({ + queryKey: ["user-posts", id], + initialPageParam: 0, + queryFn: async ({ + signal, + pageParam, + }: { + signal: AbortSignal; + pageParam: number; + }) => { + const events = await ark.getInfiniteEvents({ + filter: { + kinds: [NDKKind.Text, NDKKind.Repost], + authors: [id], + }, + limit: FETCH_LIMIT, + pageParam, + signal, + }); + + return events; + }, + getNextPageParam: (lastPage) => { + const lastEvent = lastPage.at(-1); + if (!lastEvent) return; + return lastEvent.created_at - 1; + }, + refetchOnWindowFocus: false, + }); + + const [followed, setFollowed] = useState(false); + + const allEvents = useMemo( + () => (data ? data.pages.flatMap((page) => page) : []), + [data], + ); + + const follow = async (pubkey: string) => { + try { + const add = await ark.createContact({ pubkey }); + if (add) { + setFollowed(true); + } else { + toast.success("You already follow this user"); + } + } catch (error) { + console.log(error); + } + }; + + const unfollow = async (pubkey: string) => { + try { + const remove = await ark.deleteContact({ pubkey }); + if (remove) { + setFollowed(false); + } + } catch (error) { + console.log(error); + } + }; + + const renderItem = (event: NDKEvent) => { + switch (event.kind) { + case NDKKind.Text: + return ; + case NDKKind.Repost: + return ; + default: + return ; + } + }; + + useEffect(() => { + if (storage.account.contacts.includes(id)) { + setFollowed(true); + } + }, []); + + return ( + +
+ +
+
+
+
+ {id} +
+ {followed ? ( + + ) : ( + + )} + + Message + +
+
+
+
+
+ {user?.name || + user?.display_name || + user?.displayName || + "Anon"} +
+ {user?.nip05 ? ( + + ) : ( + + {displayNpub(id, 16)} + + )} +
+
+ {user?.about} +
+
+
+
+

+ Latest posts +

+
+ {isLoading ? ( +
+ +
+ ) : ( + allEvents.map((item) => renderItem(item)) + )} +
+ {hasNextPage ? ( + + ) : null} +
+
+
+
+
+ ); } diff --git a/packages/ark/src/components/column/live.tsx b/packages/ark/src/components/column/live.tsx index b15060a8..f163d624 100644 --- a/packages/ark/src/components/column/live.tsx +++ b/packages/ark/src/components/column/live.tsx @@ -41,7 +41,7 @@ export function ColumnLiveWidget({ className="inline-flex h-9 w-max items-center justify-center gap-1 rounded-full bg-blue-500 px-2.5 text-sm font-semibold text-white hover:bg-blue-600" > - {events.length} {events.length === 1 ? "event" : "events"} + {events.length} {events.length === 1 ? "new event" : "new events"}
); diff --git a/packages/ark/src/components/note/builds/childReply.tsx b/packages/ark/src/components/note/builds/childReply.tsx index 27464d59..b5d4488e 100644 --- a/packages/ark/src/components/note/builds/childReply.tsx +++ b/packages/ark/src/components/note/builds/childReply.tsx @@ -7,10 +7,10 @@ export function ChildReply({ }: { event: NDKEvent; rootEventId?: string }) { return ( - + -
+
diff --git a/packages/ark/src/components/note/mentions/note.tsx b/packages/ark/src/components/note/mentions/note.tsx index 0ebebe21..522f6a34 100644 --- a/packages/ark/src/components/note/mentions/note.tsx +++ b/packages/ark/src/components/note/mentions/note.tsx @@ -39,23 +39,21 @@ export const MentionNote = memo(function MentionNote({ } return ( - -
- -
-
- {renderKind(data)} - - Show more - -
-
+ + +
+ +
+
+ {renderKind(data)} + + Show more + +
+
+
); }); diff --git a/packages/ark/src/components/note/mentions/user.tsx b/packages/ark/src/components/note/mentions/user.tsx index be82534a..43018e5c 100644 --- a/packages/ark/src/components/note/mentions/user.tsx +++ b/packages/ark/src/components/note/mentions/user.tsx @@ -1,5 +1,7 @@ import { WIDGET_KIND } from "@lume/utils"; +import * as DropdownMenu from "@radix-ui/react-dropdown-menu"; import { memo } from "react"; +import { Link } from "react-router-dom"; import { useProfile } from "../../../hooks/useProfile"; import { useWidget } from "../../../hooks/useWidget"; @@ -10,18 +12,35 @@ export const MentionUser = memo(function MentionUser({ const { addWidget } = useWidget(); return ( - + + + {`@${user?.name || user?.displayName || user?.username || "anon"}`} + + + + + View profile + + + + + + + ); }); diff --git a/packages/ark/src/components/note/user.tsx b/packages/ark/src/components/note/user.tsx index 722a820b..f693b339 100644 --- a/packages/ark/src/components/note/user.tsx +++ b/packages/ark/src/components/note/user.tsx @@ -63,7 +63,7 @@ export function NoteUser({ @@ -94,12 +94,12 @@ export function NoteUser({ if (variant === "repost") { if (isLoading) { return ( -
-
+
+
-
+
@@ -107,8 +107,8 @@ export function NoteUser({ } return ( -
-
+
+
@@ -116,7 +116,7 @@ export function NoteUser({ @@ -161,7 +161,7 @@ export function NoteUser({ @@ -212,7 +212,7 @@ export function NoteUser({ diff --git a/packages/ark/src/hooks/useRichContent.tsx b/packages/ark/src/hooks/useRichContent.tsx index ba9bfb1e..058117d2 100644 --- a/packages/ark/src/hooks/useRichContent.tsx +++ b/packages/ark/src/hooks/useRichContent.tsx @@ -1,3 +1,4 @@ +import { nanoid } from "nanoid"; import { nip19 } from "nostr-tools"; import { ReactNode } from "react"; import { Link } from "react-router-dom"; @@ -172,7 +173,6 @@ export function useRichContent(content: string, textmode = false) { /(https?:\/\/\S+)/g, (match, i) => { const url = new URL(match); - url.search = ""; if (!linkPreview && !textmode) { linkPreview = match; @@ -194,7 +194,7 @@ export function useRichContent(content: string, textmode = false) { ); parsedContent = reactStringReplace(parsedContent, "\n", () => { - return null; + return
; }); if (typeof parsedContent[0] === "string") { diff --git a/packages/ark/src/hooks/useSuggestion.ts b/packages/ark/src/hooks/useSuggestion.ts deleted file mode 100644 index 34edc12f..00000000 --- a/packages/ark/src/hooks/useSuggestion.ts +++ /dev/null @@ -1,78 +0,0 @@ -import { MentionOptions } from "@tiptap/extension-mention"; -import { ReactRenderer } from "@tiptap/react"; -import tippy from "tippy.js"; -import { MentionList } from "../components/mentions"; -import { useStorage } from "../provider"; - -export function useSuggestion() { - const storage = useStorage(); - - const suggestion: MentionOptions["suggestion"] = { - items: async ({ query }) => { - const users = await storage.getAllCacheUsers(); - return users - .filter((item) => { - if (item.name) - return item.name.toLowerCase().startsWith(query.toLowerCase()); - return item.displayName.toLowerCase().startsWith(query.toLowerCase()); - }) - .slice(0, 5); - }, - render: () => { - let component; - let popup; - - return { - onStart: (props) => { - component = new ReactRenderer(MentionList, { - props, - editor: props.editor, - }); - - if (!props.clientRect) { - return; - } - - popup = tippy("body", { - getReferenceClientRect: props.clientRect, - appendTo: () => document.body, - content: component.element, - showOnCreate: true, - interactive: true, - trigger: "manual", - placement: "bottom-start", - }); - }, - - onUpdate(props) { - component.updateProps(props); - - if (!props.clientRect) { - return; - } - - popup[0].setProps({ - getReferenceClientRect: props.clientRect, - }); - }, - - onKeyDown(props) { - if (props.event.key === "Escape") { - popup[0].hide(); - - return true; - } - - return component.ref?.onKeyDown(props); - }, - - onExit() { - popup[0].destroy(); - component.destroy(); - }, - }; - }, - }; - - return { suggestion }; -} diff --git a/packages/ark/src/index.ts b/packages/ark/src/index.ts index a4d68e34..62d47af8 100644 --- a/packages/ark/src/index.ts +++ b/packages/ark/src/index.ts @@ -6,5 +6,4 @@ export * from "./hooks/useWidget"; export * from "./hooks/useRichContent"; export * from "./hooks/useEvent"; export * from "./hooks/useProfile"; -export * from "./hooks/useSuggestion"; export * from "./hooks/useRelay"; diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 6868f604..c6eab7fb 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -10,3 +10,4 @@ export * from "./layouts/auth"; export * from "./layouts/composer"; export * from "./layouts/home"; export * from "./layouts/settings"; +export * from "./mentions"; diff --git a/packages/ark/src/components/mentions.tsx b/packages/ui/src/mentions.tsx similarity index 100% rename from packages/ark/src/components/mentions.tsx rename to packages/ui/src/mentions.tsx diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 60baf1d4..2fe29e9b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -371,6 +371,9 @@ importers: '@lume/icons': specifier: workspace:^ version: link:../../icons + '@lume/ui': + specifier: workspace:^ + version: link:../../ui '@lume/utils': specifier: workspace:^ version: link:../../utils @@ -386,6 +389,9 @@ importers: react-router-dom: specifier: ^6.21.0 version: 6.21.0(react-dom@18.2.0)(react@18.2.0) + sonner: + specifier: ^1.2.4 + version: 1.2.4(react-dom@18.2.0)(react@18.2.0) virtua: specifier: ^0.18.0 version: 0.18.0(react-dom@18.2.0)(react@18.2.0)