From 7fb62a6afa7fa61bd0049af537b8fb134aa580e5 Mon Sep 17 00:00:00 2001 From: Ren Amamiya <123083837+reyamir@users.noreply.github.com> Date: Mon, 26 Jun 2023 10:24:12 +0700 Subject: [PATCH] WIP --- src/app/chat/components/list.tsx | 2 +- src/app/chat/components/modal.tsx | 8 +- src/app/root.tsx | 4 +- src/app/space/components/add.tsx | 63 +--- src/app/space/components/addFeed.tsx | 352 +++++++++++------- src/app/space/components/addImage.tsx | 314 ++++++++-------- src/app/space/components/blocks/feed.tsx | 15 +- src/app/space/components/blocks/following.tsx | 4 +- src/app/space/components/blocks/image.tsx | 5 +- src/app/space/index.tsx | 4 +- src/libs/storage.tsx | 41 +- src/shared/navigation.tsx | 3 +- src/shared/notes/note.tsx | 4 +- src/shared/notes/parent.tsx | 2 +- src/shared/notes/preview/image.tsx | 2 +- src/shared/notes/preview/link.tsx | 2 +- src/shared/notes/preview/video.tsx | 11 +- src/shared/notes/replies/list.tsx | 6 +- src/shared/notes/repost.tsx | 5 +- src/shared/user.tsx | 4 +- src/stores/shortcuts.tsx | 2 + 21 files changed, 479 insertions(+), 374 deletions(-) diff --git a/src/app/chat/components/list.tsx b/src/app/chat/components/list.tsx index 8a4cf88e..d5759d82 100644 --- a/src/app/chat/components/list.tsx +++ b/src/app/chat/components/list.tsx @@ -27,6 +27,7 @@ export function ChatsList() { return (
+ {account ? ( ) : ( @@ -59,7 +60,6 @@ export function ChatsList() {
)} -
); } diff --git a/src/app/chat/components/modal.tsx b/src/app/chat/components/modal.tsx index 879fc774..a6965f2e 100644 --- a/src/app/chat/components/modal.tsx +++ b/src/app/chat/components/modal.tsx @@ -10,7 +10,7 @@ import { useNavigate } from "react-router-dom"; export function NewMessageModal() { const navigate = useNavigate(); - const { status, data, isFetching }: any = useQuery(["plebs"], async () => { + const { status, data }: any = useQuery(["plebs"], async () => { return await getPlebs(); }); @@ -96,7 +96,7 @@ export function NewMessageModal() {
- {status === "loading" || isFetching ? ( + {status === "loading" ? (

Loading...

) : ( data.map((pleb) => ( @@ -111,10 +111,10 @@ export function NewMessageModal() { className="w-9 h-9 shrink-0 object-cover rounded" />
-

+

{pleb.display_name || pleb.name}

- + {pleb.nip05 || pleb.npub.substring(0, 16).concat("...")} diff --git a/src/app/root.tsx b/src/app/root.tsx index ebc57200..d6f32434 100644 --- a/src/app/root.tsx +++ b/src/app/root.tsx @@ -144,8 +144,8 @@ export function Root() { const notes = await fetchNotes(); if (notes) { const chats = await fetchChats(); - const channels = await fetchChannelMessages(); - if (chats && channels) { + // const channels = await fetchChannelMessages(); + if (chats) { navigate("/app/space", { replace: true }); } } diff --git a/src/app/space/components/add.tsx b/src/app/space/components/add.tsx index 21e0e82c..dfce2e1a 100644 --- a/src/app/space/components/add.tsx +++ b/src/app/space/components/add.tsx @@ -1,66 +1,11 @@ import { AddFeedBlock } from "@app/space/components/addFeed"; import { AddImageBlock } from "@app/space/components/addImage"; -import { Menu, Transition } from "@headlessui/react"; -import { FeedIcon, ImageIcon, PlusIcon } from "@shared/icons"; -import { Fragment, useState } from "react"; export function AddBlock() { - const [imageModal, setImageModal] = useState(false); - const [feedModal, setFeedModal] = useState(false); - - const openAddImageModal = () => { - setImageModal(true); - }; - - const openAddFeedModal = () => { - setFeedModal(true); - }; - return ( - <> - - -
- -
-
- - -
- - - - - - -
-
-
-
- {imageModal && } - {feedModal && } - +
+ + +
); } diff --git a/src/app/space/components/addFeed.tsx b/src/app/space/components/addFeed.tsx index 7a819833..5eb78026 100644 --- a/src/app/space/components/addFeed.tsx +++ b/src/app/space/components/addFeed.tsx @@ -1,24 +1,37 @@ +import { User } from "@app/auth/components/user"; import { Dialog, Transition } from "@headlessui/react"; -import { createBlock } from "@libs/storage"; -import { CancelIcon } from "@shared/icons"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { Combobox } from "@headlessui/react"; +import { createBlock, getPlebs } from "@libs/storage"; +import { CancelIcon, CheckCircleIcon, CommandIcon } from "@shared/icons"; +import { DEFAULT_AVATAR } from "@stores/constants"; +import { ADD_FEEDBLOCK_SHORTCUT } from "@stores/shortcuts"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { useAccount } from "@utils/hooks/useAccount"; import { nip19 } from "nostr-tools"; -import { Fragment, useState } from "react"; +import { Fragment, useEffect, useState } from "react"; import { useForm } from "react-hook-form"; +import { useHotkeys } from "react-hotkeys-hook"; -export function AddFeedBlock({ parentState }: { parentState: any }) { +export function AddFeedBlock() { const queryClient = useQueryClient(); const [loading, setLoading] = useState(false); - const [isOpen, setIsOpen] = useState(true); + const [isOpen, setIsOpen] = useState(false); + const [selected, setSelected] = useState([]); + const [query, setQuery] = useState(""); + + const { status, account } = useAccount(); + + const openModal = () => { + setIsOpen(true); + }; const closeModal = () => { - // update local state setIsOpen(false); - // update parent state - parentState(false); }; + useHotkeys(ADD_FEEDBLOCK_SHORTCUT, () => openModal()); + const block = useMutation({ mutationFn: (data: any) => { return createBlock(data.kind, data.title, data.content); @@ -38,151 +51,224 @@ export function AddFeedBlock({ parentState }: { parentState: any }) { const onSubmit = (data: any) => { setLoading(true); - let pubkey = data.content; - - if (pubkey.substring(0, 4) === "npub") { - pubkey = nip19.decode(pubkey).data; - } + selected.forEach((item, index) => { + if (item.substring(0, 4) === "npub") { + selected[index] = nip19.decode(item).data; + } + }); // insert to database - block.mutate({ kind: 1, title: data.title, content: pubkey }); + block.mutate({ + kind: 1, + title: data.title, + content: JSON.stringify(selected), + }); - setTimeout(() => { - setLoading(false); - // reset form - reset(); - // close modal - closeModal(); - }, 1200); + setLoading(false); + // reset form + reset(); + // close modal + closeModal(); }; return ( - - - -
- -
+ <> + + + - -
-
-
- - Create image block - - +
+ +
+ + +
+
+
+ + Create feed block + + +
+ + Specific newsfeed space for people you want to keep up to + date +
- - Pin your favorite image to Space then you can view every - time that you use Lume, your image will be broadcast to - Nostr Relay as well -
-
-
-
-
- -
+
+ +
+
-
-
- -
- +
+ +
+
+ + setQuery(event.target.value)} + spellCheck={false} + autoFocus={false} + placeholder="Enter pubkey or npub..." + className="mb-2 relative h-10 w-full rounded-md px-3 py-2 !outline-none placeholder:text-zinc-500 bg-zinc-700 text-zinc-100" + /> + + {query.length > 0 && ( + + {({ selected }) => ( + <> +
+ {query} +
+ + {query} + +
+
+ {selected && ( + + )} + + )} +
+ )} + {status === "loading" ? ( +

Loading...

+ ) : ( + JSON.parse(account.follows).map((follow) => ( + + {({ selected }) => ( + <> + + {selected && ( + + )} + + )} + + )) + )} +
+
+
+
-
-
- -
- -
- - -
-
-
+
+ +
+ +
+ + +
+
+
+ ); } diff --git a/src/app/space/components/addImage.tsx b/src/app/space/components/addImage.tsx index 7468aedb..fcbc2df5 100644 --- a/src/app/space/components/addImage.tsx +++ b/src/app/space/components/addImage.tsx @@ -1,10 +1,11 @@ import { Dialog, Transition } from "@headlessui/react"; import { createBlock } from "@libs/storage"; import { NDKEvent, NDKPrivateKeySigner } from "@nostr-dev-kit/ndk"; -import { CancelIcon } from "@shared/icons"; +import { CancelIcon, CommandIcon } from "@shared/icons"; import { Image } from "@shared/image"; import { RelayContext } from "@shared/relayProvider"; import { DEFAULT_AVATAR } from "@stores/constants"; +import { ADD_IMAGEBLOCK_SHORTCUT } from "@stores/shortcuts"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { open } from "@tauri-apps/api/dialog"; import { Body, fetch } from "@tauri-apps/api/http"; @@ -13,26 +14,30 @@ import { dateToUnix } from "@utils/date"; import { useAccount } from "@utils/hooks/useAccount"; import { Fragment, useContext, useEffect, useRef, useState } from "react"; import { useForm } from "react-hook-form"; +import { useHotkeys } from "react-hotkeys-hook"; -export function AddImageBlock({ parentState }: { parentState: any }) { +export function AddImageBlock() { const ndk = useContext(RelayContext); const queryClient = useQueryClient(); const [loading, setLoading] = useState(false); - const [isOpen, setIsOpen] = useState(true); + const [isOpen, setIsOpen] = useState(false); const [image, setImage] = useState(""); const { account } = useAccount(); const tags = useRef(null); - const closeModal = () => { - // update local state - setIsOpen(false); - // update parent state - parentState(false); + const openModal = () => { + setIsOpen(true); }; + const closeModal = () => { + setIsOpen(false); + }; + + useHotkeys(ADD_IMAGEBLOCK_SHORTCUT, () => openModal()); + const { register, handleSubmit, @@ -118,13 +123,11 @@ export function AddImageBlock({ parentState }: { parentState: any }) { // mutate block.mutate({ kind: 0, title: data.title, content: data.content }); - setTimeout(() => { - setLoading(false); - // reset form - reset(); - // close modal - closeModal(); - }, 1200); + setLoading(false); + // reset form + reset(); + // close modal + closeModal(); }; useEffect(() => { @@ -132,145 +135,164 @@ export function AddImageBlock({ parentState }: { parentState: any }) { }, [setValue, image]); return ( - - - -
- -
+ <> + + + - -
-
-
- - Create image block - - -
- - Pin your favorite image to Space then you can view every - time that you use Lume, your image will be broadcast to - Nostr Relay as well - -
-
-
-
- -
- -
- +
+ +
+ + +
+
+
+ + Create image block + +
+ + Pin your favorite image to Space then you can view every + time that you use Lume, your image will be broadcast to + Nostr Relay as well +
-
- -
- content -
- +
+
+ + +
+ +
+
-
-
- -
- -
- - -
-
-
+
+ +
+ content +
+ +
+
+
+
+ +
+ +
+ + +
+
+
+ ); } diff --git a/src/app/space/components/blocks/feed.tsx b/src/app/space/components/blocks/feed.tsx index a766ccb1..17487e2d 100644 --- a/src/app/space/components/blocks/feed.tsx +++ b/src/app/space/components/blocks/feed.tsx @@ -1,4 +1,4 @@ -import { getNotesByAuthor, removeBlock } from "@libs/storage"; +import { getNotesByAuthors, removeBlock } from "@libs/storage"; import { Note } from "@shared/notes/note"; import { NoteSkeleton } from "@shared/notes/skeleton"; import { TitleBar } from "@shared/titleBar"; @@ -25,7 +25,7 @@ export function FeedBlock({ params }: { params: any }) { }: any = useInfiniteQuery({ queryKey: ["newsfeed", params.content], queryFn: async ({ pageParam = 0 }) => { - return await getNotesByAuthor( + return await getNotesByAuthors( params.content, TIME, ITEM_PER_PAGE, @@ -91,9 +91,9 @@ export function FeedBlock({ params }: { params: any }) { className="scrollbar-hide flex w-full h-full flex-col justify-between gap-1.5 pt-1.5 pb-20 overflow-y-auto" style={{ contain: "strict" }} > - {status === "loading" || isFetching ? ( + {status === "loading" ? (
-
+
@@ -119,6 +119,13 @@ export function FeedBlock({ params }: { params: any }) {
)} + {isFetching && !isFetchingNextPage && ( +
+
+ +
+
+ )}
); diff --git a/src/app/space/components/blocks/following.tsx b/src/app/space/components/blocks/following.tsx index 6ebb08c0..98305147 100644 --- a/src/app/space/components/blocks/following.tsx +++ b/src/app/space/components/blocks/following.tsx @@ -112,7 +112,7 @@ export function FollowingBlock({ block }: { block: number }) { > {status === "loading" ? (
-
+
@@ -140,7 +140,7 @@ export function FollowingBlock({ block }: { block: number }) { )} {isFetching && !isFetchingNextPage && (
-
+
diff --git a/src/app/space/components/blocks/image.tsx b/src/app/space/components/blocks/image.tsx index 907c3c2e..36d41216 100644 --- a/src/app/space/components/blocks/image.tsx +++ b/src/app/space/components/blocks/image.tsx @@ -21,7 +21,10 @@ export function ImageBlock({ params }: { params: any }) {
-
+
+

+ {params.title} +

)} -
+
-
+
); } diff --git a/src/libs/storage.tsx b/src/libs/storage.tsx index 2019283b..bc469dbc 100644 --- a/src/libs/storage.tsx +++ b/src/libs/storage.tsx @@ -135,12 +135,14 @@ export async function countTotalNotes() { const result = await db.select( 'SELECT COUNT(*) AS "total" FROM notes WHERE kind IN (1, 6);', ); - return result[0].total; + return parseInt(result[0].total); } // get all notes export async function getNotes(time: number, limit: number, offset: number) { const db = await connect(); + const totalNotes = await countTotalNotes(); + const nextCursor = offset + limit; const notes: any = { data: null, nextCursor: 0 }; const query: any = await db.select( @@ -148,19 +150,22 @@ export async function getNotes(time: number, limit: number, offset: number) { ); notes["data"] = query; - notes["nextCursor"] = offset + limit; + notes["nextCursor"] = + Math.round(totalNotes / nextCursor) > 1 ? nextCursor : undefined; return notes; } -// get all notes by authors -export async function getNotesByAuthor( +// get all notes by pubkey +export async function getNotesByPubkey( pubkey: string, time: number, limit: number, offset: number, ) { const db = await connect(); + const totalNotes = await countTotalNotes(); + const nextCursor = offset + limit; const notes: any = { data: null, nextCursor: 0 }; const query: any = await db.select( @@ -168,7 +173,33 @@ export async function getNotesByAuthor( ); notes["data"] = query; - notes["nextCursor"] = offset + limit; + notes["nextCursor"] = + Math.round(totalNotes / nextCursor) > 1 ? nextCursor : undefined; + + return notes; +} + +// get all notes by authors +export async function getNotesByAuthors( + authors: string, + time: number, + limit: number, + offset: number, +) { + const db = await connect(); + const totalNotes = await countTotalNotes(); + const nextCursor = offset + limit; + const array = JSON.parse(authors); + const finalArray = `'${array.join("','")}'`; + + const notes: any = { data: null, nextCursor: 0 }; + const query: any = await db.select( + `SELECT * FROM notes WHERE created_at <= "${time}" AND pubkey IN (${finalArray}) AND kind IN (1, 6, 1063) GROUP BY parent_id ORDER BY created_at DESC LIMIT "${limit}" OFFSET "${offset}";`, + ); + + notes["data"] = query; + notes["nextCursor"] = + Math.round(totalNotes / nextCursor) > 1 ? nextCursor : undefined; return notes; } diff --git a/src/shared/navigation.tsx b/src/shared/navigation.tsx index 1b5d7b9e..691e9eea 100644 --- a/src/shared/navigation.tsx +++ b/src/shared/navigation.tsx @@ -64,7 +64,7 @@ export function Navigation({ reverse = false }: { reverse?: boolean }) {
- {/* Channels */} + {/* Channels {({ open }) => (
@@ -90,6 +90,7 @@ export function Navigation({ reverse = false }: { reverse?: boolean }) {
)}
+ */} {/* Chats */} {({ open }) => ( diff --git a/src/shared/notes/note.tsx b/src/shared/notes/note.tsx index c656d857..bd61a154 100644 --- a/src/shared/notes/note.tsx +++ b/src/shared/notes/note.tsx @@ -53,7 +53,7 @@ export function Note({ event, block }: Note) { Lume isn't fully support this kind in newsfeed

-
+

{event.content}

@@ -63,7 +63,7 @@ export function Note({ event, block }: Note) { return (
-
+
{renderParent}
-
+

{data.content || data.toString()}

diff --git a/src/shared/notes/preview/image.tsx b/src/shared/notes/preview/image.tsx index 24752a55..e0984dcf 100644 --- a/src/shared/notes/preview/image.tsx +++ b/src/shared/notes/preview/image.tsx @@ -10,7 +10,7 @@ export function ImagePreview({ urls }: { urls: string[] }) { src={url} fallback="https://void.cat/d/XTmrMkpid8DGLjv1AzdvcW" alt="image" - className="h-auto w-full rounded-lg object-cover" + className="h-auto w-full border border-zinc-800/50 rounded-lg object-cover" />
))} diff --git a/src/shared/notes/preview/link.tsx b/src/shared/notes/preview/link.tsx index 7efaa38e..2cc04b07 100644 --- a/src/shared/notes/preview/link.tsx +++ b/src/shared/notes/preview/link.tsx @@ -20,7 +20,7 @@ export function LinkPreview({ urls }: { urls: string[] }) {
) : ( {urls.map((url) => ( -
- -
+ ))}
); diff --git a/src/shared/notes/replies/list.tsx b/src/shared/notes/replies/list.tsx index 807f18ef..ebbdd01e 100644 --- a/src/shared/notes/replies/list.tsx +++ b/src/shared/notes/replies/list.tsx @@ -29,8 +29,10 @@ export function RepliesList({ parent_id }: { parent_id: string }) {
- -

No replies

+

👋

+

+ Share your thought on it... +

diff --git a/src/shared/notes/repost.tsx b/src/shared/notes/repost.tsx index ce9bab90..7f3b0359 100644 --- a/src/shared/notes/repost.tsx +++ b/src/shared/notes/repost.tsx @@ -15,7 +15,8 @@ export function Repost({ const { status, data, isFetching } = useEvent(repostID); return ( -
+
+
{isFetching || status === "loading" ? ( ) : ( @@ -34,7 +35,7 @@ export function Repost({ Lume isn't fully support this kind in newsfeed

-
+

{data.content || data.toString()}

diff --git a/src/shared/user.tsx b/src/shared/user.tsx index a6259ef8..fb86a4d6 100644 --- a/src/shared/user.tsx +++ b/src/shared/user.tsx @@ -33,14 +33,14 @@ export function User({ }`} > {pubkey} diff --git a/src/stores/shortcuts.tsx b/src/stores/shortcuts.tsx index dde9fe2c..451b83de 100644 --- a/src/stores/shortcuts.tsx +++ b/src/stores/shortcuts.tsx @@ -1 +1,3 @@ export const COMPOSE_SHORTCUT = "meta+n"; +export const ADD_IMAGEBLOCK_SHORTCUT = "meta+i"; +export const ADD_FEEDBLOCK_SHORTCUT = "meta+f";