diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 15c033b6..acf0fa48 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -8,6 +8,7 @@ "build": "vite build" }, "dependencies": { + "@columns/antenas": "workspace:^", "@columns/group": "workspace:^", "@columns/hashtag": "workspace:^", "@columns/notification": "workspace:^", @@ -64,7 +65,7 @@ "html-to-text": "^9.0.5", "light-bolt11-decoder": "^3.0.0", "lru-cache": "^10.1.0", - "markdown-to-jsx": "^7.3.2", + "markdown-to-jsx": "^7.4.0", "minidenticons": "^4.2.0", "nanoid": "^5.0.4", "nostr-fetch": "^0.14.1", @@ -81,7 +82,7 @@ "sonner": "^1.3.1", "tippy.js": "^6.3.7", "tiptap-markdown": "^0.8.8", - "virtua": "^0.18.0" + "virtua": "^0.18.1" }, "devDependencies": { "@lume/tailwindcss": "workspace:^", diff --git a/apps/desktop/src/routes/home/index.tsx b/apps/desktop/src/routes/home/index.tsx index 8fe9e08c..b655fae1 100644 --- a/apps/desktop/src/routes/home/index.tsx +++ b/apps/desktop/src/routes/home/index.tsx @@ -1,3 +1,4 @@ +import { Antenas } from "@columns/antenas"; import { Group } from "@columns/group"; import { Hashtag } from "@columns/hashtag"; import { Thread } from "@columns/thread"; @@ -26,6 +27,8 @@ export function HomeScreen() { return ; case COL_TYPES.group: return ; + case COL_TYPES.antenas: + return ; default: return ; } diff --git a/packages/@columns/antenas/package.json b/packages/@columns/antenas/package.json new file mode 100644 index 00000000..f05252eb --- /dev/null +++ b/packages/@columns/antenas/package.json @@ -0,0 +1,26 @@ +{ + "name": "@columns/antenas", + "version": "0.0.0", + "private": true, + "main": "./src/index.tsx", + "dependencies": { + "@lume/ark": "workspace:^", + "@lume/icons": "workspace:^", + "@lume/ui": "workspace:^", + "@lume/utils": "workspace:^", + "@nostr-dev-kit/ndk": "^2.3.2", + "@tanstack/react-query": "^5.17.0", + "react": "^18.2.0", + "react-router-dom": "^6.21.1", + "sonner": "^1.3.1", + "virtua": "^0.18.1" + }, + "devDependencies": { + "@lume/tailwindcss": "workspace:^", + "@lume/tsconfig": "workspace:^", + "@lume/types": "workspace:^", + "@types/react": "^18.2.46", + "tailwind": "^4.0.0", + "typescript": "^5.3.3" + } +} diff --git a/packages/@columns/antenas/src/components/form.tsx b/packages/@columns/antenas/src/components/form.tsx new file mode 100644 index 00000000..0e90d09c --- /dev/null +++ b/packages/@columns/antenas/src/components/form.tsx @@ -0,0 +1,139 @@ +import { useColumnContext } from "@lume/ark"; +import { CancelIcon, PlusIcon } from "@lume/icons"; +import { useState } from "react"; +import { toast } from "sonner"; + +export function AntenasForm({ id }: { id: number }) { + const { updateColumn, removeColumn } = useColumnContext(); + + const [title, setTitle] = useState(`Antena-${id}`); + const [source, setSource] = useState("contacts"); + const [hashtag, setHashtag] = useState(""); + const [hashtags, setHashtags] = useState([]); + + const addHashtag = () => { + if (!hashtag.startsWith("#")) + return toast.error("Hashtag need to start with #"); + if (hashtag.length > 64) return toast.error("Hashtag too long"); + setHashtags((prev) => [...prev, hashtag]); + setHashtag(""); + }; + + const removeHashtag = (item: string) => { + setHashtags((prev) => prev.filter((tag) => tag !== item)); + }; + + const submit = async () => { + const content = { + hashtags, + source, + }; + await updateColumn(id, title, JSON.stringify(content)); + }; + + return ( +
+
+
+

Create a new Antena

+ +
+
+
+ + + setTitle(e.target.value)} + placeholder="Nostrichs..." + className="relative block w-full appearance-none rounded-lg px-[calc(theme(spacing[3.5])-1px)] py-[calc(theme(spacing[2.5])-1px)] sm:px-[calc(theme(spacing[3])-1px)] sm:py-[calc(theme(spacing[1.5])-1px)] focus:ring-0 text-base/6 text-neutral-950 placeholder:text-neutral-500 sm:text-sm/6 dark:text-white border border-neutral-950/10 data-[hover]:border-neutral-950/20 dark:border-white/10 dark:data-[hover]:border-white/20 bg-transparent dark:bg-white/5 focus:outline-none data-[invalid]:border-red-500 data-[invalid]:data-[hover]:border-red-500 data-[invalid]:dark:border-red-500 data-[invalid]:data-[hover]:dark:border-red-500 data-[disabled]:border-neutral-950/20 dark:data-[hover]:data-[disabled]:border-white/15 data-[disabled]:dark:border-white/15 data-[disabled]:dark:bg-white/[2.5%]" + /> + +
+
+ + + + +
+
+ +
+ + setHashtag(e.target.value)} + onKeyPress={(event) => { + if (event.key === "Enter") addHashtag(); + }} + placeholder="#nostr..." + className="relative block w-full appearance-none rounded-lg px-[calc(theme(spacing[3.5])-1px)] py-[calc(theme(spacing[2.5])-1px)] sm:px-[calc(theme(spacing[3])-1px)] sm:py-[calc(theme(spacing[1.5])-1px)] focus:ring-0 text-base/6 text-neutral-950 placeholder:text-neutral-500 sm:text-sm/6 dark:text-white border border-neutral-950/10 data-[hover]:border-neutral-950/20 dark:border-white/10 dark:data-[hover]:border-white/20 bg-transparent dark:bg-white/5 focus:outline-none data-[invalid]:border-red-500 data-[invalid]:data-[hover]:border-red-500 data-[invalid]:dark:border-red-500 data-[invalid]:data-[hover]:dark:border-red-500 data-[disabled]:border-neutral-950/20 dark:data-[hover]:data-[disabled]:border-white/15 data-[disabled]:dark:border-white/15 data-[disabled]:dark:bg-white/[2.5%]" + /> + + +
+
+ {hashtags.map((item) => ( + + ))} +
+
+
+ +
+
+
+
+ ); +} diff --git a/packages/@columns/antenas/src/event.tsx b/packages/@columns/antenas/src/event.tsx new file mode 100644 index 00000000..28ebae62 --- /dev/null +++ b/packages/@columns/antenas/src/event.tsx @@ -0,0 +1,29 @@ +import { ThreadNote } from "@lume/ark"; +import { ArrowLeftIcon } from "@lume/icons"; +import { ReplyList } from "@lume/ui"; +import { useNavigate, useParams } from "react-router-dom"; +import { WVList } from "virtua"; + +export function EventRoute() { + const { id } = useParams(); + const navigate = useNavigate(); + + return ( + +
+ +
+
+ + +
+
+ ); +} diff --git a/packages/@columns/antenas/src/home.tsx b/packages/@columns/antenas/src/home.tsx new file mode 100644 index 00000000..0220d664 --- /dev/null +++ b/packages/@columns/antenas/src/home.tsx @@ -0,0 +1,133 @@ +import { RepostNote, TextNote, useArk, useStorage } from "@lume/ark"; +import { ArrowRightCircleIcon, LoaderIcon } from "@lume/icons"; +import { FETCH_LIMIT } from "@lume/utils"; +import { NDKEvent, NDKFilter, NDKKind } from "@nostr-dev-kit/ndk"; +import { useInfiniteQuery } from "@tanstack/react-query"; +import { useEffect, useMemo, useRef } from "react"; +import { CacheSnapshot, VList, VListHandle } from "virtua"; + +export function HomeRoute({ + colKey, + content, +}: { colKey: string; content: string }) { + const ark = useArk(); + const storage = useStorage(); + const ref = useRef(); + const cacheKey = `${colKey}-vlist`; + + const [offset, cache] = useMemo(() => { + const serialized = sessionStorage.getItem(cacheKey); + if (!serialized) return []; + return JSON.parse(serialized) as [number, CacheSnapshot]; + }, []); + + const { data, hasNextPage, isLoading, isFetchingNextPage, fetchNextPage } = + useInfiniteQuery({ + queryKey: [colKey], + initialPageParam: 0, + queryFn: async ({ + signal, + pageParam, + }: { + signal: AbortSignal; + pageParam: number; + }) => { + let filter: NDKFilter; + const parsed: { hashtags: string[]; source: string } = + JSON.parse(content); + + if (parsed.source === "contacts") { + filter = { + kinds: [NDKKind.Text, NDKKind.Repost], + "#t": parsed.hashtags.map((item) => item.replace("#", "")), + authors: storage.account.contacts, + }; + } else { + filter = { + kinds: [NDKKind.Text, NDKKind.Repost], + "#t": parsed.hashtags.map((item) => item.replace("#", "")), + }; + } + + const events = await ark.getInfiniteEvents({ + filter, + 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 allEvents = useMemo( + () => (data ? data.pages.flatMap((page) => page) : []), + [data], + ); + + const renderItem = (event: NDKEvent) => { + switch (event.kind) { + case NDKKind.Text: + return ; + case NDKKind.Repost: + return ; + default: + return ; + } + }; + + useEffect(() => { + if (!ref.current) return; + const handle = ref.current; + + if (offset) { + handle.scrollTo(offset); + } + + return () => { + sessionStorage.setItem( + cacheKey, + JSON.stringify([handle.scrollOffset, handle.cache]), + ); + }; + }, []); + + return ( +
+ + {isLoading ? ( +
+ +
+ ) : ( + allEvents.map((item) => renderItem(item)) + )} +
+ {hasNextPage ? ( + + ) : null} +
+
+
+ ); +} diff --git a/packages/@columns/antenas/src/index.tsx b/packages/@columns/antenas/src/index.tsx new file mode 100644 index 00000000..3bd36305 --- /dev/null +++ b/packages/@columns/antenas/src/index.tsx @@ -0,0 +1,36 @@ +import { Column } from "@lume/ark"; +import { GroupFeedsIcon } from "@lume/icons"; +import { IColumn } from "@lume/types"; +import { AntenasForm } from "./components/form"; +import { EventRoute } from "./event"; +import { HomeRoute } from "./home"; +import { UserRoute } from "./user"; + +export function Antenas({ column }: { column: IColumn }) { + const colKey = `antenas-${column.id}`; + const created = !!column.content?.length; + + return ( + + {created ? ( + <> + } + /> + + } + /> + } /> + } /> + + + ) : ( + + )} + + ); +} diff --git a/packages/@columns/antenas/src/user.tsx b/packages/@columns/antenas/src/user.tsx new file mode 100644 index 00000000..8d46f957 --- /dev/null +++ b/packages/@columns/antenas/src/user.tsx @@ -0,0 +1,213 @@ +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 ark = useArk(); + const storage = useStorage(); + const navigate = useNavigate(); + + 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/@columns/antenas/tailwind.config.js b/packages/@columns/antenas/tailwind.config.js new file mode 100644 index 00000000..49c48c7a --- /dev/null +++ b/packages/@columns/antenas/tailwind.config.js @@ -0,0 +1,8 @@ +import sharedConfig from "@lume/tailwindcss"; + +const config = { + content: ["./src/**/*.{js,ts,jsx,tsx}"], + presets: [sharedConfig], +}; + +export default config; diff --git a/packages/@columns/antenas/tsconfig.json b/packages/@columns/antenas/tsconfig.json new file mode 100644 index 00000000..34a32891 --- /dev/null +++ b/packages/@columns/antenas/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@lume/tsconfig/base.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/@columns/group/package.json b/packages/@columns/group/package.json index 54fe5f88..d0f192de 100644 --- a/packages/@columns/group/package.json +++ b/packages/@columns/group/package.json @@ -13,7 +13,7 @@ "react": "^18.2.0", "react-router-dom": "^6.21.1", "sonner": "^1.3.1", - "virtua": "^0.18.0" + "virtua": "^0.18.1" }, "devDependencies": { "@lume/tailwindcss": "workspace:^", diff --git a/packages/@columns/group/src/components/form.tsx b/packages/@columns/group/src/components/form.tsx index 8eaa2940..e3efe6f3 100644 --- a/packages/@columns/group/src/components/form.tsx +++ b/packages/@columns/group/src/components/form.tsx @@ -48,7 +48,7 @@ export function GroupForm({ id }: { id: number }) { value={title} onChange={(e) => setTitle(e.target.value)} placeholder="Nostrichs..." - className="px-3 rounded-xl border-neutral-200 dark:border-neutral-900 h-11 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800" + className="px-3 rounded-lg border-neutral-200 dark:border-neutral-900 h-11 placeholder:text-neutral-500 focus:border-blue-500 focus:ring focus:ring-blue-200 dark:placeholder:text-neutral-400 dark:focus:ring-blue-800" />
@@ -64,7 +64,7 @@ export function GroupForm({ id }: { id: number }) { key={item} type="button" onClick={() => toggleUser(item)} - className="inline-flex items-center justify-between px-3 py-2 rounded-xl bg-neutral-50 dark:bg-neutral-950 hover:bg-neutral-100 dark:hover:bg-neutral-900" + className="inline-flex items-center justify-between px-3 py-2 rounded-lg bg-neutral-50 dark:bg-neutral-950 hover:bg-neutral-100 dark:hover:bg-neutral-900" > {users.includes(item) ? ( diff --git a/packages/@columns/group/src/event.tsx b/packages/@columns/group/src/event.tsx new file mode 100644 index 00000000..1174c079 --- /dev/null +++ b/packages/@columns/group/src/event.tsx @@ -0,0 +1,29 @@ +import { ThreadNote } from "@lume/ark"; +import { ArrowLeftIcon } from "@lume/icons"; +import { ReplyList } from "@lume/ui"; +import { useNavigate, useParams } from "react-router-dom"; +import { WVList } from "virtua"; + +export function EventRoute() { + const { id } = useParams(); + const navigate = useNavigate(); + + return ( + +
+ +
+
+ + +
+
+ ); +} diff --git a/packages/@columns/group/src/home.tsx b/packages/@columns/group/src/home.tsx index ada62adb..f92e2686 100644 --- a/packages/@columns/group/src/home.tsx +++ b/packages/@columns/group/src/home.tsx @@ -31,7 +31,7 @@ export function HomeRoute({ signal: AbortSignal; pageParam: number; }) => { - const authors = JSON.parse(content); + const authors: string[] = JSON.parse(content); const events = await ark.getInfiniteEvents({ filter: { kinds: [NDKKind.Text, NDKKind.Repost], diff --git a/packages/@columns/group/src/index.tsx b/packages/@columns/group/src/index.tsx index bbbafd16..5ca24d6b 100644 --- a/packages/@columns/group/src/index.tsx +++ b/packages/@columns/group/src/index.tsx @@ -2,7 +2,9 @@ import { Column } from "@lume/ark"; import { GroupFeedsIcon } from "@lume/icons"; import { IColumn } from "@lume/types"; import { GroupForm } from "./components/form"; +import { EventRoute } from "./event"; import { HomeRoute } from "./home"; +import { UserRoute } from "./user"; export function Group({ column }: { column: IColumn }) { const colKey = `group-${column.id}`; @@ -22,6 +24,8 @@ export function Group({ column }: { column: IColumn }) { path="/" element={} /> + } /> + } /> ) : ( diff --git a/packages/@columns/group/src/user.tsx b/packages/@columns/group/src/user.tsx new file mode 100644 index 00000000..8d46f957 --- /dev/null +++ b/packages/@columns/group/src/user.tsx @@ -0,0 +1,213 @@ +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 ark = useArk(); + const storage = useStorage(); + const navigate = useNavigate(); + + 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/@columns/hashtag/package.json b/packages/@columns/hashtag/package.json index a1ef1154..43f8d126 100644 --- a/packages/@columns/hashtag/package.json +++ b/packages/@columns/hashtag/package.json @@ -13,7 +13,7 @@ "react": "^18.2.0", "react-router-dom": "^6.21.1", "sonner": "^1.3.1", - "virtua": "^0.18.0" + "virtua": "^0.18.1" }, "devDependencies": { "@lume/tailwindcss": "workspace:^", diff --git a/packages/@columns/notification/package.json b/packages/@columns/notification/package.json index 220889d3..a2372483 100644 --- a/packages/@columns/notification/package.json +++ b/packages/@columns/notification/package.json @@ -10,7 +10,7 @@ "@nostr-dev-kit/ndk": "^2.3.2", "@tanstack/react-query": "^5.17.0", "react": "^18.2.0", - "virtua": "^0.18.0" + "virtua": "^0.18.1" }, "devDependencies": { "@lume/tailwindcss": "workspace:^", diff --git a/packages/@columns/thread/package.json b/packages/@columns/thread/package.json index 39fd0a1e..0eceea8e 100644 --- a/packages/@columns/thread/package.json +++ b/packages/@columns/thread/package.json @@ -13,7 +13,7 @@ "react": "^18.2.0", "react-router-dom": "^6.21.1", "sonner": "^1.3.1", - "virtua": "^0.18.0" + "virtua": "^0.18.1" }, "devDependencies": { "@lume/tailwindcss": "workspace:^", diff --git a/packages/@columns/timeline/package.json b/packages/@columns/timeline/package.json index b7597be8..e3e2b59e 100644 --- a/packages/@columns/timeline/package.json +++ b/packages/@columns/timeline/package.json @@ -13,7 +13,7 @@ "react": "^18.2.0", "react-router-dom": "^6.21.1", "sonner": "^1.3.1", - "virtua": "^0.18.0" + "virtua": "^0.18.1" }, "devDependencies": { "@lume/tailwindcss": "workspace:^", diff --git a/packages/@columns/user/package.json b/packages/@columns/user/package.json index 741f7d40..673a6566 100644 --- a/packages/@columns/user/package.json +++ b/packages/@columns/user/package.json @@ -13,7 +13,7 @@ "react": "^18.2.0", "react-router-dom": "^6.21.1", "sonner": "^1.3.1", - "virtua": "^0.18.0" + "virtua": "^0.18.1" }, "devDependencies": { "@lume/tailwindcss": "workspace:^", diff --git a/packages/ark/package.json b/packages/ark/package.json index 09ca25be..58e87b92 100644 --- a/packages/ark/package.json +++ b/packages/ark/package.json @@ -33,7 +33,7 @@ "@tiptap/react": "^2.1.13", "@vidstack/react": "^1.9.8", "get-urls": "^12.1.0", - "markdown-to-jsx": "^7.3.2", + "markdown-to-jsx": "^7.4.0", "minidenticons": "^4.2.0", "nanoid": "^5.0.4", "nostr-fetch": "^0.14.1", diff --git a/packages/icons/src/antenas.tsx b/packages/icons/src/antenas.tsx new file mode 100644 index 00000000..cf9efb8c --- /dev/null +++ b/packages/icons/src/antenas.tsx @@ -0,0 +1,19 @@ +export function AntenasIcon(props: JSX.IntrinsicElements["svg"]) { + return ( + + + + ); +} diff --git a/packages/utils/src/constants.ts b/packages/utils/src/constants.ts index b1f2a2fc..9d65e920 100644 --- a/packages/utils/src/constants.ts +++ b/packages/utils/src/constants.ts @@ -34,16 +34,13 @@ export const HASHTAGS = [ export const COL_TYPES = { user: 1, thread: 2, - group: 3, - article: 4, - file: 5, - trendingNotes: 6, - trendingAccounts: 7, - topic: 8, - hashtag: 9, - notification: 9998, + hashtag: 3, + group: 4, + antenas: 5, + topic: 6, + trendingNotes: 9000, + trendingAccounts: 9001, newsfeed: 9999, - list: 10000, }; export const TOPICS = [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a5a8b096..2c1a4b36 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,6 +20,9 @@ importers: apps/desktop: dependencies: + '@columns/antenas': + specifier: workspace:^ + version: link:../../packages/@columns/antenas '@columns/group': specifier: workspace:^ version: link:../../packages/@columns/group @@ -189,8 +192,8 @@ importers: specifier: ^10.1.0 version: 10.1.0 markdown-to-jsx: - specifier: ^7.3.2 - version: 7.3.2(react@18.2.0) + specifier: ^7.4.0 + version: 7.4.0(react@18.2.0) minidenticons: specifier: ^4.2.0 version: 4.2.0 @@ -240,8 +243,8 @@ importers: specifier: ^0.8.8 version: 0.8.8(@tiptap/core@2.1.13) virtua: - specifier: ^0.18.0 - version: 0.18.0(react-dom@18.2.0)(react@18.2.0) + specifier: ^0.18.1 + version: 0.18.1(react-dom@18.2.0)(react@18.2.0) devDependencies: '@lume/tailwindcss': specifier: workspace:^ @@ -292,6 +295,58 @@ importers: specifier: ^4.2.3 version: 4.2.3(typescript@5.3.3)(vite@4.5.1) + packages/@columns/antenas: + dependencies: + '@lume/ark': + specifier: workspace:^ + version: link:../../ark + '@lume/icons': + specifier: workspace:^ + version: link:../../icons + '@lume/ui': + specifier: workspace:^ + version: link:../../ui + '@lume/utils': + specifier: workspace:^ + version: link:../../utils + '@nostr-dev-kit/ndk': + specifier: ^2.3.2 + version: 2.3.2(typescript@5.3.3) + '@tanstack/react-query': + specifier: ^5.17.0 + version: 5.17.0(react@18.2.0) + react: + specifier: ^18.2.0 + version: 18.2.0 + react-router-dom: + specifier: ^6.21.1 + version: 6.21.1(react-dom@18.2.0)(react@18.2.0) + sonner: + specifier: ^1.3.1 + version: 1.3.1(react-dom@18.2.0)(react@18.2.0) + virtua: + specifier: ^0.18.1 + version: 0.18.1(react-dom@18.2.0)(react@18.2.0) + devDependencies: + '@lume/tailwindcss': + specifier: workspace:^ + version: link:../../tailwindcss + '@lume/tsconfig': + specifier: workspace:^ + version: link:../../tsconfig + '@lume/types': + specifier: workspace:^ + version: link:../../types + '@types/react': + specifier: ^18.2.46 + version: 18.2.46 + tailwind: + specifier: ^4.0.0 + version: 4.0.0 + typescript: + specifier: ^5.3.3 + version: 5.3.3 + packages/@columns/group: dependencies: '@lume/ark': @@ -322,8 +377,8 @@ importers: specifier: ^1.3.1 version: 1.3.1(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) + specifier: ^0.18.1 + version: 0.18.1(react-dom@18.2.0)(react@18.2.0) devDependencies: '@lume/tailwindcss': specifier: workspace:^ @@ -374,8 +429,8 @@ importers: specifier: ^1.3.1 version: 1.3.1(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) + specifier: ^0.18.1 + version: 0.18.1(react-dom@18.2.0)(react@18.2.0) devDependencies: '@lume/tailwindcss': specifier: workspace:^ @@ -417,8 +472,8 @@ importers: specifier: ^18.2.0 version: 18.2.0 virtua: - specifier: ^0.18.0 - version: 0.18.0(react-dom@18.2.0)(react@18.2.0) + specifier: ^0.18.1 + version: 0.18.1(react-dom@18.2.0)(react@18.2.0) devDependencies: '@lume/tailwindcss': specifier: workspace:^ @@ -466,8 +521,8 @@ importers: specifier: ^1.3.1 version: 1.3.1(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) + specifier: ^0.18.1 + version: 0.18.1(react-dom@18.2.0)(react@18.2.0) devDependencies: '@lume/tailwindcss': specifier: workspace:^ @@ -518,8 +573,8 @@ importers: specifier: ^1.3.1 version: 1.3.1(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) + specifier: ^0.18.1 + version: 0.18.1(react-dom@18.2.0)(react@18.2.0) devDependencies: '@lume/tailwindcss': specifier: workspace:^ @@ -570,8 +625,8 @@ importers: specifier: ^1.3.1 version: 1.3.1(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) + specifier: ^0.18.1 + version: 0.18.1(react-dom@18.2.0)(react@18.2.0) devDependencies: '@lume/tailwindcss': specifier: workspace:^ @@ -682,8 +737,8 @@ importers: specifier: ^12.1.0 version: 12.1.0 markdown-to-jsx: - specifier: ^7.3.2 - version: 7.3.2(react@18.2.0) + specifier: ^7.4.0 + version: 7.4.0(react@18.2.0) minidenticons: specifier: ^4.2.0 version: 4.2.0 @@ -2369,8 +2424,8 @@ packages: resolution: {integrity: sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==} dependencies: '@noble/curves': 1.1.0 - '@noble/hashes': 1.3.1 - '@scure/base': 1.1.1 + '@noble/hashes': 1.3.3 + '@scure/base': 1.1.5 dev: false /@scure/bip39@1.2.1: @@ -4672,8 +4727,8 @@ packages: uc.micro: 2.0.0 dev: false - /markdown-to-jsx@7.3.2(react@18.2.0): - resolution: {integrity: sha512-B+28F5ucp83aQm+OxNrPkS8z0tMKaeHiy0lHJs3LqCyDQFtWuenaIrkaVTgAm1pf1AU85LXltva86hlaT17i8Q==} + /markdown-to-jsx@7.4.0(react@18.2.0): + resolution: {integrity: sha512-zilc+MIkVVXPyTb4iIUTIz9yyqfcWjszGXnwF9K/aiBWcHXFcmdEMTkG01/oQhwSCH7SY1BnG6+ev5BzWmbPrg==} engines: {node: '>= 10'} peerDependencies: react: '>= 0.14.0' @@ -6285,8 +6340,8 @@ packages: engines: {node: '>= 0.8'} dev: true - /virtua@0.18.0(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-ztDBzTbzSluK//xvcXhCfjDbwkuBFSPD43ZO2SR5tNI46/BEb1oNBI/lu6CAE8MmlXr10ZdaFOdbm6LtC0hvlQ==} + /virtua@0.18.1(react-dom@18.2.0)(react@18.2.0): + resolution: {integrity: sha512-Viyg7r6zeHJO/upSgTXmykRAWHKOhnEjso9eUPVfLc1BEf4SWdGeq2dhD9CdsNpSXwkhQ7GyNepVu6L8DG7wow==} peerDependencies: react: '>=16.14.0' react-dom: '>=16.14.0'