From 63db8b1423aed0eab31fa9b078b14e5e8859b4de Mon Sep 17 00:00:00 2001 From: reya Date: Sun, 25 Feb 2024 15:52:47 +0700 Subject: [PATCH] chore: clean up --- .../src/routes/$account/home/local.lazy.tsx | 13 +- apps/desktop2/src/routes/index.tsx | 5 +- packages/ui/src/avatarUploadButton.tsx | 46 -- packages/ui/src/editor/addMedia.tsx | 45 -- packages/ui/src/editor/column.tsx | 33 -- packages/ui/src/editor/form.tsx | 377 ---------------- packages/ui/src/editor/replyForm.tsx | 403 ------------------ packages/ui/src/editor/utils.ts | 89 ---- packages/ui/src/index.ts | 8 - packages/ui/src/interestModal.tsx | 157 ------- packages/ui/src/mentions.tsx | 120 ------ packages/ui/src/note/child.tsx | 12 +- packages/ui/src/note/mentions/note.tsx | 6 +- packages/ui/src/note/preview/image.tsx | 100 ++--- packages/ui/src/note/preview/link.tsx | 6 +- packages/ui/src/note/preview/video.tsx | 4 +- packages/ui/src/note/thread.tsx | 2 +- packages/ui/src/onboarding/finish.tsx | 66 --- packages/ui/src/onboarding/home.tsx | 49 --- packages/ui/src/onboarding/interest.tsx | 127 ------ packages/ui/src/onboarding/modal.tsx | 25 -- packages/ui/src/onboarding/profile.tsx | 171 -------- packages/ui/src/onboarding/router.tsx | 28 -- packages/ui/src/routes/event.tsx | 36 -- packages/ui/src/routes/suggest.tsx | 127 ------ packages/ui/src/routes/user.tsx | 148 ------- packages/ui/src/translateRegisterModal.tsx | 145 ------- packages/ui/src/tutorial/finish.tsx | 35 -- packages/ui/src/tutorial/manageColumn.tsx | 27 -- packages/ui/src/tutorial/modal.tsx | 38 -- packages/ui/src/tutorial/newColumn.tsx | 29 -- packages/ui/src/tutorial/router.tsx | 31 -- packages/ui/src/tutorial/welcome.tsx | 30 -- packages/ui/src/unread.tsx | 14 - src-tauri/capabilities/main.json | 1 + src-tauri/gen/schemas/capabilities.json | 2 +- src-tauri/src/main.rs | 3 - src-tauri/src/nostr/event.rs | 12 +- src-tauri/src/nostr/keys.rs | 19 - 39 files changed, 88 insertions(+), 2501 deletions(-) delete mode 100644 packages/ui/src/avatarUploadButton.tsx delete mode 100644 packages/ui/src/editor/addMedia.tsx delete mode 100644 packages/ui/src/editor/column.tsx delete mode 100644 packages/ui/src/editor/form.tsx delete mode 100644 packages/ui/src/editor/replyForm.tsx delete mode 100644 packages/ui/src/editor/utils.ts delete mode 100644 packages/ui/src/interestModal.tsx delete mode 100644 packages/ui/src/mentions.tsx delete mode 100644 packages/ui/src/onboarding/finish.tsx delete mode 100644 packages/ui/src/onboarding/home.tsx delete mode 100644 packages/ui/src/onboarding/interest.tsx delete mode 100644 packages/ui/src/onboarding/modal.tsx delete mode 100644 packages/ui/src/onboarding/profile.tsx delete mode 100644 packages/ui/src/onboarding/router.tsx delete mode 100644 packages/ui/src/routes/event.tsx delete mode 100644 packages/ui/src/routes/suggest.tsx delete mode 100644 packages/ui/src/routes/user.tsx delete mode 100644 packages/ui/src/translateRegisterModal.tsx delete mode 100644 packages/ui/src/tutorial/finish.tsx delete mode 100644 packages/ui/src/tutorial/manageColumn.tsx delete mode 100644 packages/ui/src/tutorial/modal.tsx delete mode 100644 packages/ui/src/tutorial/newColumn.tsx delete mode 100644 packages/ui/src/tutorial/router.tsx delete mode 100644 packages/ui/src/tutorial/welcome.tsx delete mode 100644 packages/ui/src/unread.tsx diff --git a/apps/desktop2/src/routes/$account/home/local.lazy.tsx b/apps/desktop2/src/routes/$account/home/local.lazy.tsx index 4f2843d0..7cfc47d1 100644 --- a/apps/desktop2/src/routes/$account/home/local.lazy.tsx +++ b/apps/desktop2/src/routes/$account/home/local.lazy.tsx @@ -1,5 +1,10 @@ import { useArk } from "@lume/ark"; -import { ArrowRightCircleIcon, LoaderIcon, SearchIcon } from "@lume/icons"; +import { + ArrowRightCircleIcon, + ArrowRightIcon, + LoaderIcon, + SearchIcon, +} from "@lume/icons"; import { Event, Kind } from "@lume/types"; import { EmptyFeed } from "@lume/ui"; import { FETCH_LIMIT } from "@lume/utils"; @@ -19,7 +24,7 @@ function LocalTimeline() { const { account } = Route.useParams(); const { data, hasNextPage, isLoading, isFetchingNextPage, fetchNextPage } = useInfiniteQuery({ - queryKey: ["newsfeed", account], + queryKey: ["local_newsfeed", account], initialPageParam: 0, queryFn: async ({ pageParam }: { pageParam: number }) => { const events = await ark.get_events( @@ -61,10 +66,10 @@ function LocalTimeline() { - Find accounts to follow + ) : ( diff --git a/apps/desktop2/src/routes/index.tsx b/apps/desktop2/src/routes/index.tsx index 64be4989..fb2d0163 100644 --- a/apps/desktop2/src/routes/index.tsx +++ b/apps/desktop2/src/routes/index.tsx @@ -1,6 +1,7 @@ import { useArk } from "@lume/ark"; import { LoaderIcon, PlusIcon } from "@lume/icons"; import { User } from "@lume/ui"; +import { Link } from "@tanstack/react-router"; import { createFileRoute, redirect, useNavigate } from "@tanstack/react-router"; import { useState } from "react"; @@ -89,14 +90,14 @@ function Screen() { ))} - + )} diff --git a/packages/ui/src/avatarUploadButton.tsx b/packages/ui/src/avatarUploadButton.tsx deleted file mode 100644 index 8abd1d57..00000000 --- a/packages/ui/src/avatarUploadButton.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { useArk } from "@lume/ark"; -import { LoaderIcon } from "@lume/icons"; -import { Dispatch, SetStateAction, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { toast } from "sonner"; - -export function AvatarUploadButton({ - setPicture, -}: { - setPicture: Dispatch>; -}) { - const ark = useArk(); - - const [t] = useTranslation(); - const [loading, setLoading] = useState(false); - - const uploadAvatar = async () => { - try { - // start loading - setLoading(true); - - const image = await ark.upload({ fileExts: [] }); - if (image) { - setPicture(image); - setLoading(false); - } - } catch (e) { - setLoading(false); - toast.error(String(e)); - } - }; - - return ( - - ); -} diff --git a/packages/ui/src/editor/addMedia.tsx b/packages/ui/src/editor/addMedia.tsx deleted file mode 100644 index 443e696c..00000000 --- a/packages/ui/src/editor/addMedia.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { useArk } from "@lume/ark"; -import { AddMediaIcon, LoaderIcon } from "@lume/icons"; -import { useState } from "react"; -import { useSlateStatic } from "slate-react"; -import { toast } from "sonner"; -import { insertImage } from "./utils"; - -export function EditorAddMedia() { - const ark = useArk(); - const editor = useSlateStatic(); - - const [loading, setLoading] = useState(false); - - const uploadToNostrBuild = async () => { - try { - setLoading(true); - - const image = await ark.upload({ - fileExts: ["mp4", "mp3", "webm", "mkv", "avi", "mov"], - }); - - if (image) { - insertImage(editor, image); - setLoading(false); - } - } catch (e) { - setLoading(false); - toast.error(`Upload failed, error: ${e}`); - } - }; - - return ( - - ); -} diff --git a/packages/ui/src/editor/column.tsx b/packages/ui/src/editor/column.tsx deleted file mode 100644 index 56a27a57..00000000 --- a/packages/ui/src/editor/column.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { editorAtom } from "@lume/utils"; -import { AnimatePresence, motion } from "framer-motion"; -import { useAtomValue } from "jotai"; -import { EditorForm } from "./form"; - -export function Editor() { - const isEditorOpen = useAtomValue(editorAtom); - - return ( - - {isEditorOpen ? ( - - - - ) : null} - - ); -} diff --git a/packages/ui/src/editor/form.tsx b/packages/ui/src/editor/form.tsx deleted file mode 100644 index 5c767deb..00000000 --- a/packages/ui/src/editor/form.tsx +++ /dev/null @@ -1,377 +0,0 @@ -import { LoaderIcon, TrashIcon } from "@lume/icons"; -import { cn, editorValueAtom } from "@lume/utils"; -import { invoke } from "@tauri-apps/api/core"; -import { useAtom } from "jotai"; -import { useEffect, useRef, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { - Descendant, - Editor, - Node, - Range, - Transforms, - createEditor, -} from "slate"; -import { - Editable, - ReactEditor, - Slate, - useFocused, - useSelected, - useSlateStatic, - withReact, -} from "slate-react"; -import { toast } from "sonner"; -import { EditorAddMedia } from "./addMedia"; -import { - Portal, - insertImage, - insertMention, - insertNostrEvent, - isImageUrl, -} from "./utils"; -import { MentionNote } from "../note/mentions/note"; - -const withNostrEvent = (editor: ReactEditor) => { - const { insertData, isVoid } = editor; - - editor.isVoid = (element) => { - // @ts-expect-error, wtf - return element.type === "event" ? true : isVoid(element); - }; - - editor.insertData = (data) => { - const text = data.getData("text/plain"); - - if (text.startsWith("nevent1") || text.startsWith("note1")) { - insertNostrEvent(editor, text); - } else { - insertData(data); - } - }; - - return editor; -}; - -const withMentions = (editor: ReactEditor) => { - const { isInline, isVoid, markableVoid } = editor; - - editor.isInline = (element) => { - // @ts-expect-error, wtf - return element.type === "mention" ? true : isInline(element); - }; - - editor.isVoid = (element) => { - // @ts-expect-error, wtf - return element.type === "mention" ? true : isVoid(element); - }; - - editor.markableVoid = (element) => { - // @ts-expect-error, wtf - return element.type === "mention" || markableVoid(element); - }; - - return editor; -}; - -const withImages = (editor: ReactEditor) => { - const { insertData, isVoid } = editor; - - editor.isVoid = (element) => { - // @ts-expect-error, wtf - return element.type === "image" ? true : isVoid(element); - }; - - editor.insertData = (data) => { - const text = data.getData("text/plain"); - - if (isImageUrl(text)) { - insertImage(editor, text); - } else { - insertData(data); - } - }; - - return editor; -}; - -const Image = ({ attributes, children, element }) => { - const editor = useSlateStatic(); - const path = ReactEditor.findPath(editor as ReactEditor, element); - - const selected = useSelected(); - const focused = useFocused(); - - return ( -
- {children} -
- {element.url} - -
-
- ); -}; - -const Mention = ({ attributes, element }) => { - const editor = useSlateStatic(); - const path = ReactEditor.findPath(editor as ReactEditor, element); - - return ( - Transforms.removeNodes(editor, { at: path })} - className="inline-block align-baseline text-blue-500 hover:text-blue-600" - >{`@${element.name}`} - ); -}; - -const Event = ({ attributes, element, children }) => { - const editor = useSlateStatic(); - const path = ReactEditor.findPath(editor as ReactEditor, element); - - return ( -
- {children} - {/* biome-ignore lint/a11y/useKeyWithClickEvents: */} -
Transforms.removeNodes(editor, { at: path })} - className="user-select-none relative my-2" - > - -
-
- ); -}; - -const Element = (props) => { - const { attributes, children, element } = props; - - switch (element.type) { - case "image": - return ; - case "mention": - return ; - case "event": - return ; - default: - return ( -

- {children} -

- ); - } -}; - -export function EditorForm() { - const ref = useRef(); - - const [editorValue, setEditorValue] = useAtom(editorValueAtom); - const [contacts, setContacts] = useState([]); - const [target, setTarget] = useState(); - const [index, setIndex] = useState(0); - const [search, setSearch] = useState(""); - const [loading, setLoading] = useState(false); - const [editor] = useState(() => - withMentions(withNostrEvent(withImages(withReact(createEditor())))), - ); - - const { t } = useTranslation(); - - const filters = contacts - ?.filter((c) => c?.name?.toLowerCase().startsWith(search.toLowerCase())) - ?.slice(0, 10); - - const reset = () => { - // @ts-expect-error, backlog - editor.children = [{ type: "paragraph", children: [{ text: "" }] }]; - setEditorValue([{ type: "paragraph", children: [{ text: "" }] }]); - }; - - const serialize = (nodes: Descendant[]) => { - return nodes - .map((n) => { - // @ts-expect-error, backlog - if (n.type === "image") return n.url; - // @ts-expect-error, backlog - if (n.type === "event") return n.eventId; - - // @ts-expect-error, backlog - if (n.children.length) { - // @ts-expect-error, backlog - return n.children - .map((n) => { - if (n.type === "mention") return n.npub; - return Node.string(n).trim(); - }) - .join(" "); - } - - return Node.string(n); - }) - .join("\n"); - }; - - const submit = async () => { - try { - setLoading(true); - - const content = serialize(editor.children); - const publish = await invoke("publish", { content }); - - if (publish) { - console.log(publish); - toast.success(t("editor.successMessage")); - - return reset(); - } - - setLoading(false); - } catch (e) { - setLoading(false); - toast.error(String(e)); - } - }; - - /* - useEffect(() => { - async function loadContacts() { - const res = await storage.getAllCacheUsers(); - if (res) setContacts(res); - } - - loadContacts(); - }, []); - */ - - useEffect(() => { - if (target && filters.length > 0) { - const el = ref.current; - const domRange = ReactEditor.toDOMRange(editor, target); - const rect = domRange.getBoundingClientRect(); - el.style.top = `${rect.top + window.pageYOffset + 24}px`; - el.style.left = `${rect.left + window.pageXOffset}px`; - } - }, [filters.length, editor, index, search, target]); - - return ( -
- { - const { selection } = editor; - - if (selection && Range.isCollapsed(selection)) { - const [start] = Range.edges(selection); - const wordBefore = Editor.before(editor, start, { unit: "word" }); - const before = wordBefore && Editor.before(editor, wordBefore); - const beforeRange = before && Editor.range(editor, before, start); - const beforeText = - beforeRange && Editor.string(editor, beforeRange); - const beforeMatch = beforeText?.match(/^@(\w+)$/); - const after = Editor.after(editor, start); - const afterRange = Editor.range(editor, start, after); - const afterText = Editor.string(editor, afterRange); - const afterMatch = afterText.match(/^(\s|$)/); - - if (beforeMatch && afterMatch) { - setTarget(beforeRange); - setSearch(beforeMatch[1]); - setIndex(0); - return; - } - } - - setTarget(null); - }} - > -
-
-

{t("editor.title")}

-
-
-
- -
-
- -
-
-
- } - placeholder={t("editor.placeholder")} - className="focus:outline-none" - /> - {target && filters.length > 0 && ( - -
- {filters.map((contact, i) => ( - - ))} -
-
- )} -
- -
- ); -} diff --git a/packages/ui/src/editor/replyForm.tsx b/packages/ui/src/editor/replyForm.tsx deleted file mode 100644 index 30e6c529..00000000 --- a/packages/ui/src/editor/replyForm.tsx +++ /dev/null @@ -1,403 +0,0 @@ -import { LoaderIcon, TrashIcon } from "@lume/icons"; -import { useStorage } from "@lume/storage"; -import { NDKCacheUserProfile } from "@lume/types"; -import { cn } from "@lume/utils"; -import { NDKEvent, NDKKind } from "@nostr-dev-kit/ndk"; -import { Portal } from "@radix-ui/react-dropdown-menu"; -import { useEffect, useRef, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { - Descendant, - Editor, - Node, - Range, - Transforms, - createEditor, -} from "slate"; -import { - Editable, - ReactEditor, - Slate, - useFocused, - useSelected, - useSlateStatic, - withReact, -} from "slate-react"; -import { toast } from "sonner"; -import { EditorAddMedia } from "./addMedia"; -import { - insertImage, - insertMention, - insertNostrEvent, - isImageUrl, -} from "./utils"; -import { MentionNote } from "../note/mentions/note"; -import { useArk } from "@lume/ark"; -import { User } from "../user"; - -const withNostrEvent = (editor: ReactEditor) => { - const { insertData, isVoid } = editor; - - editor.isVoid = (element) => { - // @ts-expect-error, wtf - return element.type === "event" ? true : isVoid(element); - }; - - editor.insertData = (data) => { - const text = data.getData("text/plain"); - - if (text.startsWith("nevent1") || text.startsWith("note1")) { - insertNostrEvent(editor, text); - } else { - insertData(data); - } - }; - - return editor; -}; - -const withMentions = (editor: ReactEditor) => { - const { isInline, isVoid, markableVoid } = editor; - - editor.isInline = (element) => { - // @ts-expect-error, wtf - return element.type === "mention" ? true : isInline(element); - }; - - editor.isVoid = (element) => { - // @ts-expect-error, wtf - return element.type === "mention" ? true : isVoid(element); - }; - - editor.markableVoid = (element) => { - // @ts-expect-error, wtf - return element.type === "mention" || markableVoid(element); - }; - - return editor; -}; - -const withImages = (editor: ReactEditor) => { - const { insertData, isVoid } = editor; - - editor.isVoid = (element) => { - // @ts-expect-error, wtf - return element.type === "image" ? true : isVoid(element); - }; - - editor.insertData = (data) => { - const text = data.getData("text/plain"); - - if (isImageUrl(text)) { - insertImage(editor, text); - } else { - insertData(data); - } - }; - - return editor; -}; - -const Image = ({ attributes, children, element }) => { - const editor = useSlateStatic(); - const path = ReactEditor.findPath(editor as ReactEditor, element); - - const selected = useSelected(); - const focused = useFocused(); - - return ( -
- {children} -
- {element.url} - -
-
- ); -}; - -const Mention = ({ attributes, element }) => { - const editor = useSlateStatic(); - const path = ReactEditor.findPath(editor as ReactEditor, element); - - return ( - Transforms.removeNodes(editor, { at: path })} - className="inline-block align-baseline text-blue-500 hover:text-blue-600" - >{`@${element.name}`} - ); -}; - -const Event = ({ attributes, element, children }) => { - const editor = useSlateStatic(); - const path = ReactEditor.findPath(editor as ReactEditor, element); - - return ( -
- {children} - {/* biome-ignore lint/a11y/useKeyWithClickEvents: */} -
Transforms.removeNodes(editor, { at: path })} - className="user-select-none relative" - > - -
-
- ); -}; - -const Element = (props) => { - const { attributes, children, element } = props; - - switch (element.type) { - case "image": - return ; - case "mention": - return ; - case "event": - return ; - default: - return ( -

- {children} -

- ); - } -}; - -export function ReplyForm({ - eventId, - className, -}: { - eventId: string; - className?: string; -}) { - const ark = useArk(); - const storage = useStorage(); - const ref = useRef(); - - const [editorValue, setEditorValue] = useState([ - { - type: "paragraph", - children: [{ text: "" }], - }, - ]); - const [contacts, setContacts] = useState([]); - const [target, setTarget] = useState(); - const [index, setIndex] = useState(0); - const [search, setSearch] = useState(""); - const [loading, setLoading] = useState(false); - const [editor] = useState(() => - withMentions(withNostrEvent(withImages(withReact(createEditor())))), - ); - - const { t } = useTranslation(); - - const filters = contacts - ?.filter((c) => c?.name?.toLowerCase().startsWith(search.toLowerCase())) - ?.slice(0, 10); - - const reset = () => { - // @ts-expect-error, backlog - editor.children = [{ type: "paragraph", children: [{ text: "" }] }]; - setEditorValue([{ type: "paragraph", children: [{ text: "" }] }]); - }; - - const serialize = (nodes: Descendant[]) => { - return nodes - .map((n) => { - // @ts-expect-error, backlog - if (n.type === "image") return n.url; - // @ts-expect-error, backlog - if (n.type === "event") return n.eventId; - - // @ts-expect-error, backlog - if (n.children.length) { - // @ts-expect-error, backlog - return n.children - .map((n) => { - if (n.type === "mention") return n.npub; - return Node.string(n).trim(); - }) - .join(" "); - } - - return Node.string(n); - }) - .join("\n"); - }; - - const submit = async () => { - try { - setLoading(true); - - const event = new NDKEvent(ark.ndk); - event.kind = NDKKind.Text; - event.content = serialize(editor.children); - - const rootEvent = await ark.getEventById(eventId); - event.tag(rootEvent, "root"); - - const publish = await event.publish(); - - if (publish) { - toast.success( - `Event has been published successfully to ${publish.size} relays.`, - ); - - setLoading(false); - - return reset(); - } - } catch (e) { - setLoading(false); - toast.error(String(e)); - } - }; - - useEffect(() => { - async function loadContacts() { - const res = await storage.getAllCacheUsers(); - if (res) setContacts(res); - } - - loadContacts(); - }, []); - - useEffect(() => { - if (target && filters.length > 0) { - const el = ref.current; - const domRange = ReactEditor.toDOMRange(editor, target); - const rect = domRange.getBoundingClientRect(); - el.style.top = `${rect.top + window.pageYOffset + 24}px`; - el.style.left = `${rect.left + window.pageXOffset}px`; - } - }, [filters.length, editor, index, search, target]); - - return ( -
- - - - - -
- { - const { selection } = editor; - - if (selection && Range.isCollapsed(selection)) { - const [start] = Range.edges(selection); - const wordBefore = Editor.before(editor, start, { unit: "word" }); - const before = wordBefore && Editor.before(editor, wordBefore); - const beforeRange = before && Editor.range(editor, before, start); - const beforeText = - beforeRange && Editor.string(editor, beforeRange); - const beforeMatch = beforeText?.match(/^@(\w+)$/); - const after = Editor.after(editor, start); - const afterRange = Editor.range(editor, start, after); - const afterText = Editor.string(editor, afterRange); - const afterMatch = afterText.match(/^(\s|$)/); - - if (beforeMatch && afterMatch) { - setTarget(beforeRange); - setSearch(beforeMatch[1]); - setIndex(0); - return; - } - } - - setTarget(null); - }} - > -
- } - placeholder={t("editor.replyPlaceholder")} - className="h-28 focus:outline-none" - /> - {target && filters.length > 0 && ( - -
- {filters.map((contact, i) => ( - // biome-ignore lint/a11y/useKeyWithClickEvents: -
{ - Transforms.select(editor, target); - insertMention(editor, contact); - setTarget(null); - }} - className="rounded-md px-2 py-2 hover:bg-neutral-100 dark:hover:bg-neutral-900" - > - - - -
- -
-
-
-
- ))} -
-
- )} -
-
-
-
-
- -
-
- -
-
- -
-
- ); -} diff --git a/packages/ui/src/editor/utils.ts b/packages/ui/src/editor/utils.ts deleted file mode 100644 index 5bf9e946..00000000 --- a/packages/ui/src/editor/utils.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { ReactNode } from "react"; -import ReactDOM from "react-dom"; -import { BaseEditor, Transforms } from "slate"; -import { ReactEditor } from "slate-react"; - -export const Portal = ({ children }: { children?: ReactNode }) => { - return typeof document === "object" - ? ReactDOM.createPortal(children, document.body) - : null; -}; - -export const isImageUrl = (url: string) => { - try { - if (!url) return false; - const ext = new URL(url).pathname.split(".").pop(); - return ["jpg", "jpeg", "gif", "png", "webp", "avif", "tiff"].includes(ext); - } catch { - return false; - } -}; - -export const insertImage = (editor: ReactEditor | BaseEditor, url: string) => { - const text = { text: "" }; - const image = [ - { - type: "image", - url, - children: [text], - }, - ]; - const extraText = [ - { - type: "paragraph", - children: [text], - }, - ]; - - // @ts-ignore, idk - ReactEditor.focus(editor); - Transforms.insertNodes(editor, image); - Transforms.insertNodes(editor, extraText); -}; - -export const insertMention = ( - editor: ReactEditor | BaseEditor, - contact: NDKCacheUserProfile, -) => { - const text = { text: "" }; - const mention = { - type: "mention", - npub: `nostr:${contact.npub}`, - name: contact.name || contact.displayName || "anon", - children: [text], - }; - const extraText = [ - { - type: "paragraph", - children: [text], - }, - ]; - - // @ts-ignore, idk - ReactEditor.focus(editor); - Transforms.insertNodes(editor, mention); - Transforms.insertNodes(editor, extraText); -}; - -export const insertNostrEvent = ( - editor: ReactEditor | BaseEditor, - eventId: string, -) => { - const text = { text: "" }; - const event = [ - { - type: "event", - eventId: `nostr:${eventId}`, - children: [text], - }, - ]; - const extraText = [ - { - type: "paragraph", - children: [text], - }, - ]; - - Transforms.insertNodes(editor, event); - Transforms.insertNodes(editor, extraText); -}; diff --git a/packages/ui/src/index.ts b/packages/ui/src/index.ts index 3203cdce..f9213137 100644 --- a/packages/ui/src/index.ts +++ b/packages/ui/src/index.ts @@ -2,12 +2,4 @@ export * from "./user"; export * from "./note"; export * from "./column"; - -// Deprecated -export * from "./routes/event"; -export * from "./routes/user"; -export * from "./routes/suggest"; -export * from "./mentions"; export * from "./emptyFeed"; -export * from "./translateRegisterModal"; -export * from "./account/active"; diff --git a/packages/ui/src/interestModal.tsx b/packages/ui/src/interestModal.tsx deleted file mode 100644 index b52647a5..00000000 --- a/packages/ui/src/interestModal.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import { ArrowLeftIcon, EditInterestIcon, LoaderIcon } from "@lume/icons"; -import { useStorage } from "@lume/storage"; -import { TOPICS, cn } from "@lume/utils"; -import * as Dialog from "@radix-ui/react-dialog"; -import { useQueryClient } from "@tanstack/react-query"; -import { ReactNode, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { toast } from "sonner"; - -export function InterestModal({ - queryKey, - className, - children, -}: { queryKey: string[]; className?: string; children?: ReactNode }) { - const storage = useStorage(); - const queryClient = useQueryClient(); - - const [t] = useTranslation(); - const [open, setOpen] = useState(false); - const [loading, setLoading] = useState(false); - const [hashtags, setHashtags] = useState(storage.interests?.hashtags || []); - - const toggleHashtag = (item: string) => { - const arr = hashtags.includes(item) - ? hashtags.filter((i) => i !== item) - : [...hashtags, item]; - setHashtags(arr); - }; - - const toggleAll = (item: string[]) => { - const sets = new Set([...hashtags, ...item]); - setHashtags([...sets]); - }; - - const submit = async () => { - try { - setLoading(true); - - const save = await storage.createSetting( - "interests", - JSON.stringify({ hashtags }), - ); - - if (save) { - storage.interests = { hashtags, users: [], words: [] }; - await queryClient.refetchQueries({ queryKey }); - } - - setLoading(false); - setOpen(false); - } catch (e) { - setLoading(false); - toast.error(String(e)); - } - }; - - return ( - - - {children ? ( - children - ) : ( - <> - - {t("interests.edit")} - - )} - - - - -
-
-
-
-
-

{t("interests.edit")}

-
-
-
-
-
- {TOPICS.map((topic) => ( -
-
-
- {topic.title} -

- {topic.title} -

-
- -
-
- {topic.content.map((hashtag) => ( - - ))} -
-
- ))} -
-
-
- - - {t("global.cancel")} - - -
-
-
-
- - - - ); -} diff --git a/packages/ui/src/mentions.tsx b/packages/ui/src/mentions.tsx deleted file mode 100644 index b217c3f9..00000000 --- a/packages/ui/src/mentions.tsx +++ /dev/null @@ -1,120 +0,0 @@ -import * as Avatar from "@radix-ui/react-avatar"; -import { minidenticon } from "minidenticons"; -import { - Ref, - forwardRef, - useEffect, - useImperativeHandle, - useState, -} from "react"; - -import { NDKCacheUserProfile } from "@lume/types"; -import { cn } from "@lume/utils"; -import { useTranslation } from "react-i18next"; - -type MentionListRef = { - onKeyDown: (props: { event: Event }) => boolean; -}; - -const List = ( - props: { - items: NDKCacheUserProfile[]; - command: (arg0: { id: string }) => void; - }, - ref: Ref, -) => { - const [t] = useTranslation(); - const [selectedIndex, setSelectedIndex] = useState(0); - - const selectItem = (index) => { - const item = props.items[index]; - if (item) { - props.command({ id: item.pubkey }); - } - }; - - const upHandler = () => { - setSelectedIndex( - (selectedIndex + props.items.length - 1) % props.items.length, - ); - }; - - const downHandler = () => { - setSelectedIndex((selectedIndex + 1) % props.items.length); - }; - - const enterHandler = () => { - selectItem(selectedIndex); - }; - - useEffect(() => setSelectedIndex(0), [props.items]); - - useImperativeHandle(ref, () => ({ - onKeyDown: ({ event }) => { - if (event.key === "ArrowUp") { - upHandler(); - return true; - } - - if (event.key === "ArrowDown") { - downHandler(); - return true; - } - - if (event.key === "Enter") { - enterHandler(); - return true; - } - - return false; - }, - })); - - return ( -
- {props.items.length ? ( - props.items.map((item, index) => ( - - )) - ) : ( -
- {t("global.noResult")} -
- )} -
- ); -}; - -export const MentionList = forwardRef(List); diff --git a/packages/ui/src/note/child.tsx b/packages/ui/src/note/child.tsx index 446509d2..0103f387 100644 --- a/packages/ui/src/note/child.tsx +++ b/packages/ui/src/note/child.tsx @@ -81,8 +81,8 @@ export function NoteChild({ if (isLoading) { return (
-
-
+
+ Loading...
); @@ -91,7 +91,7 @@ export function NoteChild({ if (isError || !data) { return (
-
+
{t("note.error")}
@@ -100,8 +100,8 @@ export function NoteChild({ return (
-
-
+
+
{richContent}
@@ -109,7 +109,7 @@ export function NoteChild({ -
+
{isRoot ? t("note.posted") : t("note.replied")}: diff --git a/packages/ui/src/note/mentions/note.tsx b/packages/ui/src/note/mentions/note.tsx index 19335184..f1eca4f5 100644 --- a/packages/ui/src/note/mentions/note.tsx +++ b/packages/ui/src/note/mentions/note.tsx @@ -84,7 +84,7 @@ export function MentionNote({ return (

Loading...

@@ -95,7 +95,7 @@ export function MentionNote({ return (
{t("note.error")}
@@ -103,7 +103,7 @@ export function MentionNote({ } return ( -
+
diff --git a/packages/ui/src/note/preview/image.tsx b/packages/ui/src/note/preview/image.tsx index 0a40c7cf..ab21d5c5 100644 --- a/packages/ui/src/note/preview/image.tsx +++ b/packages/ui/src/note/preview/image.tsx @@ -5,60 +5,60 @@ import { download } from "@tauri-apps/plugin-upload"; import { SyntheticEvent, useState } from "react"; export function ImagePreview({ url }: { url: string }) { - const [downloaded, setDownloaded] = useState(false); + const [downloaded, setDownloaded] = useState(false); - const downloadImage = async (e: { stopPropagation: () => void }) => { - try { - e.stopPropagation(); + const downloadImage = async (e: { stopPropagation: () => void }) => { + try { + e.stopPropagation(); - const downloadDirPath = await downloadDir(); - const filename = url.substring(url.lastIndexOf("/") + 1); - await download(url, `${downloadDirPath}/${filename}`); + const downloadDirPath = await downloadDir(); + const filename = url.substring(url.lastIndexOf("/") + 1); + await download(url, `${downloadDirPath}/${filename}`); - setDownloaded(true); - } catch (e) { - console.error(e); - } - }; + setDownloaded(true); + } catch (e) { + console.error(e); + } + }; - const open = async () => { - const name = new URL(url).pathname.split("/").pop(); - return new WebviewWindow("image-viewer", { - url, - title: name, - }); - }; + const open = async () => { + const name = new URL(url).pathname.split("/").pop(); + return new WebviewWindow("image-viewer", { + url, + title: name, + }); + }; - const fallback = (event: SyntheticEvent) => { - event.currentTarget.src = "/fallback-image.jpg"; - }; + const fallback = (event: SyntheticEvent) => { + event.currentTarget.src = "/fallback-image.jpg"; + }; - return ( - // biome-ignore lint/a11y/useKeyWithClickEvents: -
- {url} - -
- ); + return ( + // biome-ignore lint/a11y/useKeyWithClickEvents: +
+ {url} + +
+ ); } diff --git a/packages/ui/src/note/preview/link.tsx b/packages/ui/src/note/preview/link.tsx index 751a0a4a..c9319a10 100644 --- a/packages/ui/src/note/preview/link.tsx +++ b/packages/ui/src/note/preview/link.tsx @@ -10,7 +10,7 @@ export function LinkPreview({ url }: { url: string }) { if (isLoading) { return ( -
+
@@ -54,7 +54,7 @@ export function LinkPreview({ url }: { url: string }) { href={url} target="_blank" rel="noreferrer" - className="my-1 flex w-full flex-col overflow-hidden rounded-xl bg-neutral-100 ring-1 ring-black/5 dark:bg-neutral-900 dark:ring-white/5" + className="my-1 flex w-full flex-col overflow-hidden rounded-2xl border border-black/10 dark:border-white/10" > {isImage(data.image) ? (
{data.title ? ( -
+
{data.title}
) : null} diff --git a/packages/ui/src/note/preview/video.tsx b/packages/ui/src/note/preview/video.tsx index 6fbe229a..ae6334cb 100644 --- a/packages/ui/src/note/preview/video.tsx +++ b/packages/ui/src/note/preview/video.tsx @@ -9,14 +9,14 @@ import { export function VideoPreview({ url }: { url: string }) { return ( -
+