diff --git a/src-tauri/migrations/20230521092300_add_block_model.sql b/src-tauri/migrations/20230521092300_add_block_model.sql index 5cbaa704..eb4616d6 100644 --- a/src-tauri/migrations/20230521092300_add_block_model.sql +++ b/src-tauri/migrations/20230521092300_add_block_model.sql @@ -6,5 +6,6 @@ CREATE TABLE kind INTEGER NOT NULL, title TEXT NOT NULL, content TEXT NOT NULL, + created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (account_id) REFERENCES accounts (id) ); \ No newline at end of file diff --git a/src/app.tsx b/src/app.tsx index a0c89a66..48865958 100644 --- a/src/app.tsx +++ b/src/app.tsx @@ -18,8 +18,6 @@ import { TrendingScreen } from "@app/trending"; import { AppLayout } from "@shared/appLayout"; import { AuthLayout } from "@shared/authLayout"; import { Protected } from "@shared/protected"; -import { RelayProvider } from "@shared/relayProvider"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { RouterProvider, createBrowserRouter } from "react-router-dom"; const router = createBrowserRouter([ @@ -74,18 +72,12 @@ const router = createBrowserRouter([ }, ]); -const queryClient = new QueryClient(); - export default function App() { return ( - - - Loading..

} - future={{ v7_startTransition: true }} - /> -
-
+ Loading..

} + future={{ v7_startTransition: true }} + /> ); } diff --git a/src/app/auth/create/step-1.tsx b/src/app/auth/create/step-1.tsx index cecbab7f..a8732ac7 100644 --- a/src/app/auth/create/step-1.tsx +++ b/src/app/auth/create/step-1.tsx @@ -1,6 +1,6 @@ import { createAccount, createBlock } from "@libs/storage"; import { Button } from "@shared/button"; -import { EyeOffIcon, EyeOnIcon } from "@shared/icons"; +import { EyeOffIcon, EyeOnIcon, LoaderIcon } from "@shared/icons"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { generatePrivateKey, getPublicKey, nip19 } from "nostr-tools"; import { useMemo, useState } from "react"; @@ -11,6 +11,7 @@ export function CreateStep1Screen() { const queryClient = useQueryClient(); const [type, setType] = useState("password"); + const [loading, setLoading] = useState(false); const privkey = useMemo(() => generatePrivateKey(), []); const pubkey = getPublicKey(privkey); @@ -27,21 +28,17 @@ export function CreateStep1Screen() { }; const account = useMutation({ - mutationFn: (data: any) => - createAccount(data.npub, data.pubkey, data.privkey, null, 1), - onSuccess: () => { - createBlock( - 0, - "Preserve your freedom", - "https://void.cat/d/949GNg7ZjSLHm2eTR3jZqv", - ); - queryClient.invalidateQueries({ queryKey: ["currentAccount"] }); - // redirect to next step - navigate("/auth/create/step-2", { replace: true }); + mutationFn: (data: any) => { + return createAccount(data.npub, data.pubkey, data.privkey, null, 1); + }, + onSuccess: (data: any) => { + queryClient.setQueryData(["currentAccount"], data); }, }); - const submit = async () => { + const submit = () => { + setLoading(true); + account.mutate({ npub, pubkey, @@ -49,6 +46,9 @@ export function CreateStep1Screen() { follows: null, is_active: 1, }); + + // redirect to next step + setTimeout(() => navigate("/auth/create/step-2", { replace: true }), 1200); }; return ( @@ -102,7 +102,11 @@ export function CreateStep1Screen() { diff --git a/src/app/auth/create/step-4.tsx b/src/app/auth/create/step-4.tsx index fde3aa3e..0a53c3b6 100644 --- a/src/app/auth/create/step-4.tsx +++ b/src/app/auth/create/step-4.tsx @@ -127,8 +127,9 @@ export function CreateStep4Screen() { }; const update = useMutation({ - mutationFn: (follows: any) => - updateAccount("follows", follows, account.pubkey), + mutationFn: (follows: any) => { + return updateAccount("follows", follows, account.pubkey); + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["currentAccount"] }); }, diff --git a/src/app/auth/import/step-1.tsx b/src/app/auth/import/step-1.tsx index b3c5a955..a104ee9e 100644 --- a/src/app/auth/import/step-1.tsx +++ b/src/app/auth/import/step-1.tsx @@ -2,6 +2,7 @@ import { createAccount, createBlock } from "@libs/storage"; import { LoaderIcon } from "@shared/icons"; import { useMutation, useQueryClient } from "@tanstack/react-query"; import { getPublicKey, nip19 } from "nostr-tools"; +import { useState } from "react"; import { Resolver, useForm } from "react-hook-form"; import { useNavigate } from "react-router-dom"; @@ -27,18 +28,14 @@ export function ImportStep1Screen() { const navigate = useNavigate(); const queryClient = useQueryClient(); + const [loading, setLoading] = useState(false); + const account = useMutation({ - mutationFn: (data: any) => - createAccount(data.npub, data.pubkey, data.privkey, null, 1), - onSuccess: () => { - createBlock( - 0, - "Preserve your freedom", - "https://void.cat/d/949GNg7ZjSLHm2eTR3jZqv", - ); - queryClient.invalidateQueries({ queryKey: ["currentAccount"] }); - // redirect to next step - navigate("/auth/import/step-2", { replace: true }); + mutationFn: (data: any) => { + return createAccount(data.npub, data.pubkey, data.privkey, null, 1); + }, + onSuccess: (data: any) => { + queryClient.setQueryData(["currentAccount"], data); }, }); @@ -46,13 +43,14 @@ export function ImportStep1Screen() { register, setError, handleSubmit, - formState: { errors, isDirty, isValid, isSubmitting }, + formState: { errors, isDirty, isValid }, } = useForm({ resolver }); const onSubmit = async (data: any) => { try { - let privkey = data["key"]; + setLoading(true); + let privkey = data["key"]; if (privkey.substring(0, 4) === "nsec") { privkey = nip19.decode(privkey).data; } @@ -69,6 +67,12 @@ export function ImportStep1Screen() { follows: null, is_active: 1, }); + + // redirect to step 2 + setTimeout( + () => navigate("/auth/import/step-2", { replace: true }), + 1200, + ); } } catch (error) { setError("key", { @@ -102,7 +106,7 @@ export function ImportStep1Screen() { disabled={!isDirty || !isValid} className="inline-flex items-center justify-center h-11 w-full bg-fuchsia-500 rounded-md font-medium text-zinc-100 hover:bg-fuchsia-600" > - {isSubmitting ? ( + {loading ? ( ) : ( "Continue →" diff --git a/src/app/auth/import/step-2.tsx b/src/app/auth/import/step-2.tsx index 02b23664..93222016 100644 --- a/src/app/auth/import/step-2.tsx +++ b/src/app/auth/import/step-2.tsx @@ -18,12 +18,11 @@ export function ImportStep2Screen() { const { status, account } = useAccount(); const update = useMutation({ - mutationFn: (follows: any) => - updateAccount("follows", follows, account.pubkey), + mutationFn: (follows: any) => { + return updateAccount("follows", follows, account.pubkey); + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["currentAccount"] }); - // redirect to next step - navigate("/auth/onboarding", { replace: true }); }, }); @@ -40,11 +39,16 @@ export function ImportStep2Screen() { // update update.mutate(followsList); + + // redirect to next step + setTimeout(() => navigate("/auth/onboarding", { replace: true }), 1200); } catch { console.log("error"); } }; + console.log(account); + return (
diff --git a/src/app/channel/components/createModal.tsx b/src/app/channel/components/createModal.tsx index 0924eca0..65b7be92 100644 --- a/src/app/channel/components/createModal.tsx +++ b/src/app/channel/components/createModal.tsx @@ -41,15 +41,16 @@ export function ChannelCreateModal() { } = useForm(); const addChannel = useMutation({ - mutationFn: (event: any) => - createChannel( + mutationFn: (event: any) => { + return createChannel( event.id, event.pubkey, event.name, event.picture, event.about, event.created_at, - ), + ); + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["channels"] }); }, diff --git a/src/app/chat/components/modal.tsx b/src/app/chat/components/modal.tsx index ee4fcfa8..879fc774 100644 --- a/src/app/chat/components/modal.tsx +++ b/src/app/chat/components/modal.tsx @@ -26,6 +26,7 @@ export function NewMessageModal() { const openChat = (npub: string) => { const pubkey = nip19.decode(npub).data; + closeModal(); navigate(`/app/chat/${pubkey}`); }; @@ -110,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/space/components/addFeed.tsx b/src/app/space/components/addFeed.tsx index 6ed79538..7a819833 100644 --- a/src/app/space/components/addFeed.tsx +++ b/src/app/space/components/addFeed.tsx @@ -2,14 +2,12 @@ import { Dialog, Transition } from "@headlessui/react"; import { createBlock } from "@libs/storage"; import { CancelIcon } from "@shared/icons"; import { useMutation, useQueryClient } from "@tanstack/react-query"; -import { useAccount } from "@utils/hooks/useAccount"; import { nip19 } from "nostr-tools"; import { Fragment, useState } from "react"; import { useForm } from "react-hook-form"; export function AddFeedBlock({ parentState }: { parentState: any }) { const queryClient = useQueryClient(); - const { account } = useAccount(); const [loading, setLoading] = useState(false); const [isOpen, setIsOpen] = useState(true); @@ -22,7 +20,9 @@ export function AddFeedBlock({ parentState }: { parentState: any }) { }; const block = useMutation({ - mutationFn: (data: any) => createBlock(data.kind, data.title, data.content), + mutationFn: (data: any) => { + return createBlock(data.kind, data.title, data.content); + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["blocks"] }); }, diff --git a/src/app/space/components/addImage.tsx b/src/app/space/components/addImage.tsx index fb82eec7..7468aedb 100644 --- a/src/app/space/components/addImage.tsx +++ b/src/app/space/components/addImage.tsx @@ -90,7 +90,9 @@ export function AddImageBlock({ parentState }: { parentState: any }) { }; const block = useMutation({ - mutationFn: (data: any) => createBlock(data.kind, data.title, data.content), + mutationFn: (data: any) => { + return createBlock(data.kind, data.title, data.content); + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["blocks"] }); }, diff --git a/src/app/space/components/blocks/feed.tsx b/src/app/space/components/blocks/feed.tsx index 99c2e9c9..a766ccb1 100644 --- a/src/app/space/components/blocks/feed.tsx +++ b/src/app/space/components/blocks/feed.tsx @@ -64,7 +64,9 @@ export function FeedBlock({ params }: { params: any }) { }, [notes.length, fetchNextPage, rowVirtualizer.getVirtualItems()]); const block = useMutation({ - mutationFn: (id: string) => removeBlock(id), + mutationFn: (id: string) => { + return removeBlock(id); + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["blocks"] }); }, diff --git a/src/app/space/components/blocks/image.tsx b/src/app/space/components/blocks/image.tsx index 1597ff0f..907c3c2e 100644 --- a/src/app/space/components/blocks/image.tsx +++ b/src/app/space/components/blocks/image.tsx @@ -1,4 +1,5 @@ import { removeBlock } from "@libs/storage"; +import { CancelIcon } from "@shared/icons"; import { Image } from "@shared/image"; import { TitleBar } from "@shared/titleBar"; import { DEFAULT_AVATAR } from "@stores/constants"; @@ -8,7 +9,9 @@ export function ImageBlock({ params }: { params: any }) { const queryClient = useQueryClient(); const block = useMutation({ - mutationFn: (id: string) => removeBlock(id), + mutationFn: (id: string) => { + return removeBlock(id); + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["blocks"] }); }, @@ -16,15 +19,24 @@ export function ImageBlock({ params }: { params: any }) { return (
-
-
- {params.title} +
+
+
+ +
+ {params.title}
); diff --git a/src/app/space/components/blocks/thread.tsx b/src/app/space/components/blocks/thread.tsx index 56bfeec6..e0420425 100644 --- a/src/app/space/components/blocks/thread.tsx +++ b/src/app/space/components/blocks/thread.tsx @@ -21,7 +21,9 @@ export function ThreadBlock({ params }: { params: any }) { ); const block = useMutation({ - mutationFn: (id: string) => removeBlock(id), + mutationFn: (id: string) => { + return removeBlock(id); + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["blocks"] }); }, diff --git a/src/libs/openGraph.tsx b/src/libs/openGraph.tsx index 15174b58..83c9b3f4 100644 --- a/src/libs/openGraph.tsx +++ b/src/libs/openGraph.tsx @@ -345,7 +345,7 @@ export async function getLinkPreview(text: string) { const fetchUrl = text; const options: FetchOptions = { method: "GET", - timeout: 30, + timeout: 5, responseType: ResponseType.Text, }; diff --git a/src/libs/storage.tsx b/src/libs/storage.tsx index 90563eb7..2019283b 100644 --- a/src/libs/storage.tsx +++ b/src/libs/storage.tsx @@ -45,10 +45,19 @@ export async function createAccount( is_active?: number, ) { const db = await connect(); - return await db.execute( + const res = await db.execute( "INSERT OR IGNORE INTO accounts (npub, pubkey, privkey, follows, is_active) VALUES (?, ?, ?, ?, ?);", [npub, pubkey, privkey, follows || "", is_active || 0], ); + if (res) { + await createBlock( + 0, + "Preserve your freedom", + "https://void.cat/d/949GNg7ZjSLHm2eTR3jZqv", + ); + } + const getAccount = await getActiveAccount(); + return getAccount; } // update account @@ -408,7 +417,7 @@ export async function getBlocks() { const db = await connect(); const activeAccount = await getActiveAccount(); const result: any = await db.select( - `SELECT * FROM blocks WHERE account_id <= "${activeAccount.id}";`, + `SELECT * FROM blocks WHERE account_id = "${activeAccount.id}" ORDER BY created_at DESC;`, ); return result; } diff --git a/src/main.tsx b/src/main.tsx index 30478b93..1ec94619 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,6 +1,23 @@ import App from "./app"; +import { RelayProvider } from "@shared/relayProvider"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { createRoot } from "react-dom/client"; +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + cacheTime: 1000 * 60 * 60 * 24, + }, + }, +}); + const container = document.getElementById("root"); const root = createRoot(container); -root.render(); + +root.render( + + + + + , +); diff --git a/src/shared/notes/mentions/note.tsx b/src/shared/notes/mentions/note.tsx index c6cdb8d6..2b12ad61 100644 --- a/src/shared/notes/mentions/note.tsx +++ b/src/shared/notes/mentions/note.tsx @@ -16,7 +16,9 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) { const queryClient = useQueryClient(); const block = useMutation({ - mutationFn: (data: any) => createBlock(data.kind, data.title, data.content), + mutationFn: (data: any) => { + return createBlock(data.kind, data.title, data.content); + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["blocks"] }); }, diff --git a/src/shared/notes/metadata/reply.tsx b/src/shared/notes/metadata/reply.tsx index 2587ff61..3df2437d 100644 --- a/src/shared/notes/metadata/reply.tsx +++ b/src/shared/notes/metadata/reply.tsx @@ -10,7 +10,9 @@ export function NoteReply({ const queryClient = useQueryClient(); const block = useMutation({ - mutationFn: (data: any) => createBlock(data.kind, data.title, data.content), + mutationFn: (data: any) => { + return createBlock(data.kind, data.title, data.content); + }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["blocks"] }); }, diff --git a/src/shared/notes/note.tsx b/src/shared/notes/note.tsx index 50fc244b..c656d857 100644 --- a/src/shared/notes/note.tsx +++ b/src/shared/notes/note.tsx @@ -71,7 +71,7 @@ export function Note({ event, block }: Note) { time={event.created_at} repost={isRepost} /> -
+
{renderContent} {!isRepost && (
@@ -22,10 +19,10 @@ export function NoteParent({ ) : ( <> -
- {kind1 && } - {kind1063 && } - {!kind1 && !kind1063 && ( +
+ {data.kind === 1 && } + {data.kind === 1063 && } + {data.kind !== 1 && data.kind !== 1063 && (
diff --git a/src/shared/notes/preview/link.tsx b/src/shared/notes/preview/link.tsx index 3c4658a3..7efaa38e 100644 --- a/src/shared/notes/preview/link.tsx +++ b/src/shared/notes/preview/link.tsx @@ -3,11 +3,11 @@ import { useOpenGraph } from "@utils/hooks/useOpenGraph"; export function LinkPreview({ urls }: { urls: string[] }) { const domain = new URL(urls[0]); - const { status, data, isFetching } = useOpenGraph(urls[0]); + const { status, data, error } = useOpenGraph(urls[0]); return (
- {isFetching || status === "loading" ? ( + {status === "loading" ? (
@@ -25,25 +25,35 @@ export function LinkPreview({ urls }: { urls: string[] }) { target="_blank" rel="noreferrer" > - {urls[0]} -
-
- {data.title} -
- {data.description && ( + {error ? ( +

- {data.description} + Can't fetch open graph, click to open webpage

- )} - - {domain.hostname} - -
+
+ ) : ( + <> + {urls[0]} +
+
+ {data.title} +
+ {data.description && ( +

+ {data.description} +

+ )} + + {domain.hostname} + +
+ + )} )}
diff --git a/src/shared/notes/repost.tsx b/src/shared/notes/repost.tsx index e60b7981..ce9bab90 100644 --- a/src/shared/notes/repost.tsx +++ b/src/shared/notes/repost.tsx @@ -14,9 +14,6 @@ export function Repost({ const repostID = getRepostID(event.tags); const { status, data, isFetching } = useEvent(repostID); - const kind1 = data?.kind === 1 ? data.content : null; - const kind1063 = data?.kind === 1063 ? data.tags : null; - return (
{isFetching || status === "loading" ? ( @@ -24,10 +21,10 @@ export function Repost({ ) : ( <> -
- {kind1 && } - {kind1063 && } - {!kind1 && !kind1063 && ( +
+ {data.kind === 1 && } + {data.kind === 1063 && } + {data.kind !== 1 && data.kind !== 1063 && (
diff --git a/src/shared/relayProvider.tsx b/src/shared/relayProvider.tsx index 7b635eb1..b137fa64 100644 --- a/src/shared/relayProvider.tsx +++ b/src/shared/relayProvider.tsx @@ -5,8 +5,7 @@ import { createContext } from "react"; export const RelayContext = createContext(null); -const ndk = new NDK({ explicitRelayUrls: FULL_RELAYS }); -await ndk.connect(); +const ndk = await initNDK(FULL_RELAYS); export function RelayProvider({ children }: { children: React.ReactNode }) { return {children}; diff --git a/src/utils/hooks/useAccount.tsx b/src/utils/hooks/useAccount.tsx index 00dda840..06027f6a 100644 --- a/src/utils/hooks/useAccount.tsx +++ b/src/utils/hooks/useAccount.tsx @@ -2,10 +2,16 @@ import { getActiveAccount } from "@libs/storage"; import { useQuery } from "@tanstack/react-query"; export function useAccount() { - const { status, data: account } = useQuery(["currentAccount"], async () => { - const res = await getActiveAccount(); - return res; - }); + const { status, data: account } = useQuery( + ["currentAccount"], + async () => await getActiveAccount(), + { + staleTime: Infinity, + refetchOnMount: true, + refetchOnWindowFocus: false, + refetchOnReconnect: true, + }, + ); return { status, account }; } diff --git a/src/utils/hooks/useOpenGraph.tsx b/src/utils/hooks/useOpenGraph.tsx index 0663e718..aa493397 100644 --- a/src/utils/hooks/useOpenGraph.tsx +++ b/src/utils/hooks/useOpenGraph.tsx @@ -5,7 +5,11 @@ export function useOpenGraph(url: string) { const { status, data, error, isFetching } = useQuery( ["preview", url], async () => { - return await getLinkPreview(url); + const res = await getLinkPreview(url); + if (!res) { + throw new Error("Can' fetch"); + } + return res; }, { refetchOnWindowFocus: false,