diff --git a/package.json b/package.json index 439d3d9d..f8e607ea 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,6 @@ "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-popover": "^1.0.5", "@radix-ui/react-tabs": "^1.0.3", - "@rehooks/local-storage": "^2.4.4", "@supabase/supabase-js": "^2.12.1", "@tanstack/react-virtual": "3.0.0-beta.54", "@tauri-apps/api": "^1.2.0", @@ -29,6 +28,7 @@ "destr": "^1.2.2", "framer-motion": "^9.1.7", "jotai": "^2.0.3", + "jotai-cache": "^0.3.0", "next": "^13.2.4", "next-remove-imports": "^1.0.10", "nostr-relaypool": "^0.5.18", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1290aa7a..f8fa932c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,7 +7,6 @@ specifiers: '@radix-ui/react-icons': ^1.3.0 '@radix-ui/react-popover': ^1.0.5 '@radix-ui/react-tabs': ^1.0.3 - '@rehooks/local-storage': ^2.4.4 '@supabase/supabase-js': ^2.12.1 '@tailwindcss/typography': ^0.5.9 '@tanstack/react-virtual': 3.0.0-beta.54 @@ -34,6 +33,7 @@ specifiers: framer-motion: ^9.1.7 husky: ^8.0.3 jotai: ^2.0.3 + jotai-cache: ^0.3.0 lint-staged: ^13.2.0 next: ^13.2.4 next-remove-imports: ^1.0.10 @@ -62,7 +62,6 @@ dependencies: '@radix-ui/react-icons': 1.3.0_react@18.2.0 '@radix-ui/react-popover': 1.0.5_zula6vjvt3wdocc4mwcxqa6nzi '@radix-ui/react-tabs': 1.0.3_biqbaboplfbrettd7655fr4n2y - '@rehooks/local-storage': 2.4.4_react@18.2.0 '@supabase/supabase-js': 2.12.1 '@tanstack/react-virtual': 3.0.0-beta.54_react@18.2.0 '@tauri-apps/api': 1.2.0 @@ -73,6 +72,7 @@ dependencies: destr: 1.2.2 framer-motion: 9.1.7_biqbaboplfbrettd7655fr4n2y jotai: 2.0.3_react@18.2.0 + jotai-cache: 0.3.0_jotai@2.0.3 next: 13.2.4_biqbaboplfbrettd7655fr4n2y next-remove-imports: 1.0.10 nostr-relaypool: 0.5.18_ws@8.13.0 @@ -1177,15 +1177,6 @@ packages: '@babel/runtime': 7.21.0 dev: false - /@rehooks/local-storage/2.4.4_react@18.2.0: - resolution: - { integrity: sha512-zE+kfOkG59n/1UTxdmbwktIosclr67Nlbf2MzUJ9mNtCSypVscNHeD1qT6JCSo5Pjj8DO893IKWNLJqKKzDL/Q== } - peerDependencies: - react: '>=16.8.0' - dependencies: - react: 18.2.0 - dev: false - /@rushstack/eslint-patch/1.2.0: resolution: { integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg== } @@ -3829,6 +3820,15 @@ packages: { integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw== } dev: true + /jotai-cache/0.3.0_jotai@2.0.3: + resolution: + { integrity: sha512-hV6DUD1frRpW0EN8Ss7n4KNaMZRBokQw6KPT3seA4P35QRXctMNUn4pHRlg3hzO+KBPpEZw+fyKlWAqagmuIwQ== } + peerDependencies: + jotai: '>=1.11.0' + dependencies: + jotai: 2.0.3_react@18.2.0 + dev: false + /jotai/2.0.3_react@18.2.0: resolution: { integrity: sha512-MMjhSPAL3RoeZD9WbObufRT2quThEAEknHHridf2ma8Ml7ZVQmUiHk0ssdbR3F0h3kcwhYqSGJ59OjhPge7RRg== } diff --git a/src/components/columns/account/active.tsx b/src/components/columns/account/active.tsx index 2c3e744a..7daa181b 100644 --- a/src/components/columns/account/active.tsx +++ b/src/components/columns/account/active.tsx @@ -1,53 +1,41 @@ -import { DatabaseContext } from '@components/contexts/database'; -import { RelayContext } from '@components/contexts/relay'; +import { RelayContext } from '@components/relaysProvider'; + +import { relaysAtom } from '@stores/relays'; + +import { createFollows } from '@utils/storage'; +import { tagsToArray } from '@utils/tags'; import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; import { AvatarIcon, ExitIcon, GearIcon } from '@radix-ui/react-icons'; -import useLocalStorage from '@rehooks/local-storage'; +import { useAtom } from 'jotai'; import Image from 'next/image'; import { useRouter } from 'next/router'; -import { memo, useCallback, useContext, useMemo } from 'react'; +import { memo, useContext, useEffect } from 'react'; export const ActiveAccount = memo(function ActiveAccount({ user }: { user: any }) { + const pool: any = useContext(RelayContext); + const [relays] = useAtom(relaysAtom); + const router = useRouter(); const userData = JSON.parse(user.metadata); - const { db }: any = useContext(DatabaseContext); - const relayPool: any = useContext(RelayContext); - - const [relays]: any = useLocalStorage('relays'); - const [currentUser]: any = useLocalStorage('current-user'); - const openProfile = () => { - router.push(`/users/${currentUser.id}`); + router.push(`/users/${user.pubkey}`); }; - // save follows to database - const insertFollows = useCallback( - async (follows) => { - follows.forEach(async (item) => { - if (item) { - // insert to database - await db.execute( - `INSERT OR IGNORE INTO follows (pubkey, account, kind) VALUES ("${item[1]}", "${currentUser.id}", "0")` - ); - } - }); - }, - [db, currentUser.id] - ); - - useMemo(() => { - relayPool.subscribe( + useEffect(() => { + pool.subscribe( [ { kinds: [3], - authors: [currentUser.id], + authors: [user.pubkey], }, ], relays, (event: any) => { - insertFollows(event.tags); + if (event.tags.length > 0) { + createFollows(tagsToArray(event.tags), user.pubkey, 0); + } }, undefined, undefined, @@ -55,7 +43,7 @@ export const ActiveAccount = memo(function ActiveAccount({ user }: { user: any } unsubscribeOnEose: true, } ); - }, [currentUser.id, insertFollows, relayPool, relays]); + }, [pool, relays, user.pubkey]); return ( diff --git a/src/components/columns/account/list.tsx b/src/components/columns/account/list.tsx index 131edf0c..a5a8457d 100644 --- a/src/components/columns/account/list.tsx +++ b/src/components/columns/account/list.tsx @@ -1,35 +1,36 @@ import { ActiveAccount } from '@components/columns/account/active'; import { InactiveAccount } from '@components/columns/account/inactive'; -import useLocalStorage from '@rehooks/local-storage'; +import { activeAccountAtom } from '@stores/account'; + +import { getAccounts } from '@utils/storage'; + +import { useAtom } from 'jotai'; import { useCallback, useEffect, useState } from 'react'; -import Database from 'tauri-plugin-sql-api'; export default function AccountList() { - const [currentUser]: any = useLocalStorage('current-user'); + const [activeAccount] = useAtom(activeAccountAtom); const [users, setUsers] = useState([]); const renderAccount = useCallback( (user: { id: string }) => { - if (user.id === currentUser.id) { + if (user.id === activeAccount.id) { return ; } else { return ; } }, - [currentUser.id] + [activeAccount.id] ); - const getAccounts = useCallback(async () => { - const db = await Database.load('sqlite:lume.db'); - const result: any = await db.select('SELECT * FROM accounts'); - - setUsers(result); - }, []); - useEffect(() => { - getAccounts().catch(console.error); - }, [getAccounts]); + const fetchAccount = async () => { + const result: any = await getAccounts(); + setUsers(result); + }; + + fetchAccount().catch(console.error); + }, []); return <>{users.map((user) => renderAccount(user))}; } diff --git a/src/components/columns/navigator/messages/index.tsx b/src/components/columns/navigator/messages/index.tsx index 5d192060..101d6d6c 100644 --- a/src/components/columns/navigator/messages/index.tsx +++ b/src/components/columns/navigator/messages/index.tsx @@ -1,13 +1,24 @@ import { MessageList } from '@components/columns/navigator/messages/list'; +import { activeAccountAtom } from '@stores/account'; + +import { getAllFollowsByID } from '@utils/storage'; + import * as Collapsible from '@radix-ui/react-collapsible'; import { TriangleUpIcon } from '@radix-ui/react-icons'; -import useLocalStorage from '@rehooks/local-storage'; -import { useState } from 'react'; +import { useAtom } from 'jotai'; +import { useEffect, useState } from 'react'; export default function Messages() { const [open, setOpen] = useState(true); - const [follows] = useLocalStorage('follows'); + const [follows, setFollows] = useState([]); + const [activeAccount] = useAtom(activeAccountAtom); + + useEffect(() => { + getAllFollowsByID(activeAccount.id) + .then((res: any) => setFollows(res)) + .catch(console.error); + }, [activeAccount.id]); return ( diff --git a/src/components/contexts/database.tsx b/src/components/contexts/database.tsx deleted file mode 100644 index 83e55d87..00000000 --- a/src/components/contexts/database.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { deleteFromStorage, writeStorage } from '@rehooks/local-storage'; -import { createContext, useCallback, useEffect, useState } from 'react'; -import Database from 'tauri-plugin-sql-api'; - -export const DatabaseContext = createContext({}); - -const db = typeof window !== 'undefined' ? await Database.load('sqlite:lume.db') : null; - -export default function DatabaseProvider({ children }: { children: React.ReactNode }) { - const [done, setDone] = useState(false); - - const getRelays = useCallback(async () => { - const result: any[] = await db.select('SELECT relay_url FROM relays WHERE relay_status = "1"'); - const arr = []; - result.forEach((item: { relay_url: string }) => { - arr.push(item.relay_url); - }); - // delete old item then save new item to local storage - deleteFromStorage('relays'); - writeStorage('relays', arr); - // return - return; - }, []); - - const getAccount = useCallback(async () => { - const result = await db.select(`SELECT * FROM accounts LIMIT 1`); - // delete old item then save new item to local storage - deleteFromStorage('current-user'); - if (result[0]) { - writeStorage('current-user', result[0]); - } else { - writeStorage('current-user', null); - } - // return first record - return result[0]; - }, []); - - const getFollows = useCallback(async (id: string) => { - const result: any[] = await db.select(`SELECT pubkey FROM follows WHERE account = "${id}"`); - const arr = []; - result.forEach((item: { pubkey: string }) => { - arr.push(item.pubkey); - }); - // delete old item then save new item to local storage - deleteFromStorage('follows'); - writeStorage('follows', arr); - // return - return; - }, []); - - const clearCacheNote = useCallback(async () => { - const result: any = await db.select('SELECT COUNT(*) AS "total" FROM cache_notes'); - if (result[0].total >= 1000) { - await db.execute('DELETE FROM cache_notes'); - } - }, []); - - useEffect(() => { - getRelays().catch(console.error); - getAccount() - .then((res) => { - if (res) { - getFollows(res.id).catch(console.error); - clearCacheNote().catch(console.error); - } - setDone(true); - }) - .catch(console.error); - }, [getAccount, getFollows, clearCacheNote, getRelays]); - - if (!done) { - return <>; - } - - return {children}; -} diff --git a/src/components/note/connector.tsx b/src/components/note/connector.tsx index 5f2ff1cb..d9167d60 100644 --- a/src/components/note/connector.tsx +++ b/src/components/note/connector.tsx @@ -1,70 +1,53 @@ -import { DatabaseContext } from '@components/contexts/database'; -import { RelayContext } from '@components/contexts/relay'; +import { RelayContext } from '@components/relaysProvider'; +import { activeAccountAtom } from '@stores/account'; import { hasNewerNoteAtom } from '@stores/note'; +import { relaysAtom } from '@stores/relays'; import { dateToUnix, hoursAgo } from '@utils/getDate'; +import { createCacheNote, getAllFollowsByID } from '@utils/storage'; -import { useLocalStorage } from '@rehooks/local-storage'; -import { useSetAtom } from 'jotai'; -import { memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; +import { useAtom, useSetAtom } from 'jotai'; +import { memo, useContext, useEffect, useRef, useState } from 'react'; export const NoteConnector = memo(function NoteConnector() { - const { db }: any = useContext(DatabaseContext); - const relayPool: any = useContext(RelayContext); - - const [follows]: any = useLocalStorage('follows'); - const [relays]: any = useLocalStorage('relays'); + const pool: any = useContext(RelayContext); const setHasNewerNote = useSetAtom(hasNewerNoteAtom); - const [isOnline, setIsOnline] = useState(navigator.onLine); + const [relays] = useAtom(relaysAtom); + const [activeAccount] = useAtom(activeAccountAtom); + + const [isOnline] = useState(navigator.onLine); const now = useRef(new Date()); - const insertDB = useCallback( - async (event: any) => { - // insert to local database - await db.execute( - 'INSERT OR IGNORE INTO cache_notes (id, pubkey, created_at, kind, content, tags) VALUES (?, ?, ?, ?, ?, ?);', - [event.id, event.pubkey, event.created_at, event.kind, event.content, JSON.stringify(event.tags)] - ); - }, - [db] - ); - - useMemo(() => { - relayPool.subscribe( - [ - { - kinds: [1], - authors: follows, - since: dateToUnix(hoursAgo(12, now.current)), - }, - ], - relays, - (event: any) => { - // insert event to local database - insertDB(event).catch(console.error); - // ask user load newer note - if (event.created_at > dateToUnix(now.current)) { - setHasNewerNote(true); - } - } - ); - }, [relayPool, follows, relays, insertDB, setHasNewerNote]); - useEffect(() => { - const handleStatusChange = () => { - setIsOnline(navigator.onLine); - }; + let unsubscribe; - window.addEventListener('online', handleStatusChange); - window.addEventListener('offline', handleStatusChange); + getAllFollowsByID(activeAccount.id).then((follows) => { + unsubscribe = pool.subscribe( + [ + { + kinds: [1], + authors: follows, + since: dateToUnix(hoursAgo(12, now.current)), + }, + ], + relays, + (event: any) => { + // insert event to local database + createCacheNote(event); + // ask user load newer note + if (event.created_at > dateToUnix(now.current)) { + setHasNewerNote(true); + } + } + ); + }); return () => { - window.removeEventListener('online', handleStatusChange); - window.removeEventListener('offline', handleStatusChange); + unsubscribe(); }; - }, [isOnline]); + }, [activeAccount.id, pool, relays, setHasNewerNote]); return ( <> diff --git a/src/components/note/content/metadata.tsx b/src/components/note/content/metadata.tsx index 3fbac9ba..5a110239 100644 --- a/src/components/note/content/metadata.tsx +++ b/src/components/note/content/metadata.tsx @@ -1,8 +1,10 @@ -import { RelayContext } from '@components/contexts/relay'; import { CommentsCounter } from '@components/note/counter/comments'; import { LikesCounter } from '@components/note/counter/likes'; +import { RelayContext } from '@components/relaysProvider'; -import { useLocalStorage } from '@rehooks/local-storage'; +import { relaysAtom } from '@stores/relays'; + +import { useAtom } from 'jotai'; import { useContext, useEffect, useState } from 'react'; export default function NoteMetadata({ @@ -16,14 +18,14 @@ export default function NoteMetadata({ eventTime: string; eventContent: any; }) { - const relayPool: any = useContext(RelayContext); - const [relays]: any = useLocalStorage('relays'); + const pool: any = useContext(RelayContext); + const [relays] = useAtom(relaysAtom); const [likes, setLikes] = useState(0); const [comments, setComments] = useState(0); useEffect(() => { - const unsubscribe = relayPool.subscribe( + const unsubscribe = pool.subscribe( [ { '#e': [eventID], @@ -56,7 +58,7 @@ export default function NoteMetadata({ return () => { unsubscribe(); }; - }, [eventID, relayPool, relays]); + }, [eventID, pool, relays]); return (
diff --git a/src/components/note/counter/comments.tsx b/src/components/note/counter/comments.tsx index cc648194..1deeb615 100644 --- a/src/components/note/counter/comments.tsx +++ b/src/components/note/counter/comments.tsx @@ -1,14 +1,17 @@ -import { RelayContext } from '@components/contexts/relay'; import { ImageWithFallback } from '@components/imageWithFallback'; +import { RelayContext } from '@components/relaysProvider'; import { UserExtend } from '@components/user/extend'; +import { activeAccountAtom } from '@stores/account'; +import { relaysAtom } from '@stores/relays'; + import { dateToUnix } from '@utils/getDate'; import CommentIcon from '@assets/icons/comment'; import * as Dialog from '@radix-ui/react-dialog'; import { SizeIcon } from '@radix-ui/react-icons'; -import useLocalStorage from '@rehooks/local-storage'; +import { useAtom } from 'jotai'; import { useRouter } from 'next/router'; import { getEventHash, signEvent } from 'nostr-tools'; import { memo, useContext, useState } from 'react'; @@ -27,15 +30,15 @@ export const CommentsCounter = memo(function CommentsCounter({ eventContent: any; }) { const router = useRouter(); - const relayPool: any = useContext(RelayContext); + const pool: any = useContext(RelayContext); - const [relays]: any = useLocalStorage('relays'); - const [currentUser]: any = useLocalStorage('current-user'); + const [relays] = useAtom(relaysAtom); + const [activeAccount] = useAtom(activeAccountAtom); const [open, setOpen] = useState(false); const [value, setValue] = useState(''); - const profile = JSON.parse(currentUser.metadata); + const profile = JSON.parse(activeAccount.metadata); const openThread = () => { router.push(`/newsfeed/${eventID}`); @@ -46,13 +49,13 @@ export const CommentsCounter = memo(function CommentsCounter({ content: value, created_at: dateToUnix(), kind: 1, - pubkey: currentUser.id, + pubkey: activeAccount.id, tags: [['e', eventID]], }; event.id = getEventHash(event); - event.sig = signEvent(event, currentUser.privkey); + event.sig = signEvent(event, activeAccount.privkey); - relayPool.publish(event, relays); + pool.publish(event, relays); setOpen(false); }; diff --git a/src/components/note/counter/likes.tsx b/src/components/note/counter/likes.tsx index 07e35470..37ee6dc1 100644 --- a/src/components/note/counter/likes.tsx +++ b/src/components/note/counter/likes.tsx @@ -1,11 +1,14 @@ -import { RelayContext } from '@components/contexts/relay'; +import { RelayContext } from '@components/relaysProvider'; + +import { activeAccountAtom } from '@stores/account'; +import { relaysAtom } from '@stores/relays'; import { dateToUnix } from '@utils/getDate'; import LikeIcon from '@assets/icons/like'; import LikedIcon from '@assets/icons/liked'; -import { useLocalStorage } from '@rehooks/local-storage'; +import { useAtom } from 'jotai'; import { getEventHash, signEvent } from 'nostr-tools'; import { memo, useContext, useEffect, useState } from 'react'; @@ -18,10 +21,10 @@ export const LikesCounter = memo(function LikesCounter({ eventID: string; eventPubkey: string; }) { - const relayPool: any = useContext(RelayContext); + const pool: any = useContext(RelayContext); - const [relays]: any = useLocalStorage('relays'); - const [currentUser]: any = useLocalStorage('current-user'); + const [relays] = useAtom(relaysAtom); + const [activeAccount] = useAtom(activeAccountAtom); const [isReact, setIsReact] = useState(false); const [like, setLike] = useState(0); @@ -37,12 +40,12 @@ export const LikesCounter = memo(function LikesCounter({ ['p', eventPubkey], ], created_at: dateToUnix(), - pubkey: currentUser.id, + pubkey: activeAccount.id, }; event.id = getEventHash(event); - event.sig = signEvent(event, currentUser.privkey); + event.sig = signEvent(event, activeAccount.privkey); // publish event to all relays - relayPool.publish(event, relays); + pool.publish(event, relays); // update state to change icon to filled heart setIsReact(true); // update counter diff --git a/src/components/note/form/basic.tsx b/src/components/note/form/basic.tsx index cfec50c0..d10d47aa 100644 --- a/src/components/note/form/basic.tsx +++ b/src/components/note/form/basic.tsx @@ -1,11 +1,14 @@ -import { RelayContext } from '@components/contexts/relay'; +import { RelayContext } from '@components/relaysProvider'; + +import { activeAccountAtom } from '@stores/account'; +import { relaysAtom } from '@stores/relays'; import { dateToUnix } from '@utils/getDate'; import * as Dialog from '@radix-ui/react-dialog'; import { SizeIcon } from '@radix-ui/react-icons'; -import { useLocalStorage } from '@rehooks/local-storage'; import * as commands from '@uiw/react-md-editor/lib/commands'; +import { useAtom } from 'jotai'; import dynamic from 'next/dynamic'; import { getEventHash, signEvent } from 'nostr-tools'; import { useContext, useState } from 'react'; @@ -15,16 +18,16 @@ const MDEditor = dynamic(() => import('@uiw/react-md-editor').then((mod) => mod. }); export default function FormBasic() { - const relayPool: any = useContext(RelayContext); + const pool: any = useContext(RelayContext); - const [relays]: any = useLocalStorage('relays'); - const [currentUser]: any = useLocalStorage('current-user'); + const [relays] = useAtom(relaysAtom); + const [activeAccount] = useAtom(activeAccountAtom); const [open, setOpen] = useState(false); const [value, setValue] = useState(''); - const pubkey = currentUser.id; - const privkey = currentUser.privkey; + const pubkey = activeAccount.id; + const privkey = activeAccount.privkey; const submitEvent = () => { const event: any = { @@ -37,7 +40,7 @@ export default function FormBasic() { event.id = getEventHash(event); event.sig = signEvent(event, privkey); - relayPool.publish(event, relays); + pool.publish(event, relays); setValue(''); }; diff --git a/src/components/note/form/comment.tsx b/src/components/note/form/comment.tsx index e22a5650..91a60c2b 100644 --- a/src/components/note/form/comment.tsx +++ b/src/components/note/form/comment.tsx @@ -1,33 +1,36 @@ -import { RelayContext } from '@components/contexts/relay'; import { ImageWithFallback } from '@components/imageWithFallback'; +import { RelayContext } from '@components/relaysProvider'; + +import { activeAccountAtom } from '@stores/account'; +import { relaysAtom } from '@stores/relays'; import { dateToUnix } from '@utils/getDate'; -import { useLocalStorage } from '@rehooks/local-storage'; +import { useAtom } from 'jotai'; import { getEventHash, signEvent } from 'nostr-tools'; import { useContext, useState } from 'react'; export default function FormComment({ eventID }: { eventID: any }) { - const relayPool: any = useContext(RelayContext); + const pool: any = useContext(RelayContext); - const [relays]: any = useLocalStorage('relays'); - const [currentUser]: any = useLocalStorage('current-user'); + const [relays] = useAtom(relaysAtom); + const [activeAccount] = useAtom(activeAccountAtom); const [value, setValue] = useState(''); - const profile = JSON.parse(currentUser.metadata); + const profile = JSON.parse(activeAccount.metadata); const submitEvent = () => { const event: any = { content: value, created_at: dateToUnix(), kind: 1, - pubkey: currentUser.id, + pubkey: activeAccount.id, tags: [['e', eventID]], }; event.id = getEventHash(event); - event.sig = signEvent(event, currentUser.privkey); + event.sig = signEvent(event, activeAccount.privkey); - relayPool.publish(event, relays); + pool.publish(event, relays); }; return ( @@ -37,7 +40,7 @@ export default function FormComment({ eventID }: { eventID: any }) {
diff --git a/src/components/note/mention.tsx b/src/components/note/mention.tsx index 42ab3f17..29df6b14 100644 --- a/src/components/note/mention.tsx +++ b/src/components/note/mention.tsx @@ -1,35 +1,21 @@ -import { DatabaseContext } from '@components/contexts/database'; -import { RelayContext } from '@components/contexts/relay'; import { Content } from '@components/note/content'; +import { RelayContext } from '@components/relaysProvider'; -import useLocalStorage from '@rehooks/local-storage'; +import { relaysAtom } from '@stores/relays'; + +import { createCacheNote, getNoteByID } from '@utils/storage'; + +import { useAtom } from 'jotai'; import { memo, useCallback, useContext, useEffect, useState } from 'react'; export const MentionNote = memo(function MentionNote({ id }: { id: string }) { - const { db }: any = useContext(DatabaseContext); - const relayPool: any = useContext(RelayContext); + const pool: any = useContext(RelayContext); - const [relays]: any = useLocalStorage('relays'); + const [relays] = useAtom(relaysAtom); const [event, setEvent] = useState(null); - const insertDB = useCallback( - async (event: any) => { - // insert to local database - await db.execute( - 'INSERT OR IGNORE INTO cache_notes (id, pubkey, created_at, kind, content, tags, is_root) VALUES (?, ?, ?, ?, ?, ?, ?);', - [event.id, event.pubkey, event.created_at, event.kind, event.content, JSON.stringify(event.tags), 0] - ); - }, - [db] - ); - - const getData = useCallback(async () => { - const result = await db.select(`SELECT * FROM cache_notes WHERE id = "${id}"`); - return result[0]; - }, [db, id]); - const fetchEvent = useCallback(() => { - relayPool.subscribe( + pool.subscribe( [ { ids: [id], @@ -41,7 +27,7 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) { // update state setEvent(event); // insert to database - insertDB(event); + createCacheNote(event); }, undefined, undefined, @@ -49,19 +35,17 @@ export const MentionNote = memo(function MentionNote({ id }: { id: string }) { unsubscribeOnEose: true, } ); - }, [id, insertDB, relayPool, relays]); + }, [id, pool, relays]); useEffect(() => { - getData() - .then((res) => { - if (res) { - setEvent(res); - } else { - fetchEvent(); - } - }) - .catch(console.error); - }, [fetchEvent, getData]); + getNoteByID(id).then((res) => { + if (res) { + setEvent(res); + } else { + fetchEvent(); + } + }); + }, [fetchEvent, id]); if (event) { return ( diff --git a/src/components/note/root.tsx b/src/components/note/root.tsx index 1e942f07..ec4750f4 100644 --- a/src/components/note/root.tsx +++ b/src/components/note/root.tsx @@ -1,35 +1,21 @@ -import { DatabaseContext } from '@components/contexts/database'; -import { RelayContext } from '@components/contexts/relay'; import { Content } from '@components/note/content'; +import { RelayContext } from '@components/relaysProvider'; -import useLocalStorage from '@rehooks/local-storage'; +import { relaysAtom } from '@stores/relays'; + +import { createCacheNote, getNoteByID } from '@utils/storage'; + +import { useAtom } from 'jotai'; import { memo, useCallback, useContext, useEffect, useState } from 'react'; export const RootNote = memo(function RootNote({ id }: { id: string }) { - const { db }: any = useContext(DatabaseContext); - const relayPool: any = useContext(RelayContext); + const pool: any = useContext(RelayContext); - const [relays]: any = useLocalStorage('relays'); + const [relays] = useAtom(relaysAtom); const [event, setEvent] = useState(null); - const insertDB = useCallback( - async (event: any) => { - // insert to local database - await db.execute( - 'INSERT OR IGNORE INTO cache_notes (id, pubkey, created_at, kind, content, tags, is_root) VALUES (?, ?, ?, ?, ?, ?, ?);', - [event.id, event.pubkey, event.created_at, event.kind, event.content, JSON.stringify(event.tags), 1] - ); - }, - [db] - ); - - const getData = useCallback(async () => { - const result = await db.select(`SELECT * FROM cache_notes WHERE id = "${id}"`); - return result[0]; - }, [db, id]); - const fetchEvent = useCallback(() => { - relayPool.subscribe( + pool.subscribe( [ { ids: [id], @@ -41,7 +27,7 @@ export const RootNote = memo(function RootNote({ id }: { id: string }) { // update state setEvent(event); // insert to database - insertDB(event); + createCacheNote(event); }, undefined, undefined, @@ -49,19 +35,17 @@ export const RootNote = memo(function RootNote({ id }: { id: string }) { unsubscribeOnEose: true, } ); - }, [id, insertDB, relayPool, relays]); + }, [id, pool, relays]); useEffect(() => { - getData() - .then((res) => { - if (res) { - setEvent(res); - } else { - fetchEvent(); - } - }) - .catch(console.error); - }, [fetchEvent, getData]); + getNoteByID(id).then((res) => { + if (res) { + setEvent(res); + } else { + fetchEvent(); + } + }); + }, [fetchEvent, id]); if (event) { return ( diff --git a/src/components/contexts/relay.tsx b/src/components/relaysProvider.tsx similarity index 88% rename from src/components/contexts/relay.tsx rename to src/components/relaysProvider.tsx index b9bdab71..592a035c 100644 --- a/src/components/contexts/relay.tsx +++ b/src/components/relaysProvider.tsx @@ -3,7 +3,7 @@ import { createContext, useMemo } from 'react'; export const RelayContext = createContext({}); -export default function RelayProvider({ relays, children }: { relays: any; children: React.ReactNode }) { +export default function RelayProvider({ relays, children }: { relays: Array; children: React.ReactNode }) { const value = useMemo(() => new RelayPool(relays, { useEventCache: false, logSubscriptions: false }), [relays]); return {children}; } diff --git a/src/components/user/extend.tsx b/src/components/user/extend.tsx index 0fefe540..e6642dcb 100644 --- a/src/components/user/extend.tsx +++ b/src/components/user/extend.tsx @@ -1,6 +1,6 @@ -import { DatabaseContext } from '@components/contexts/database'; import { ImageWithFallback } from '@components/imageWithFallback'; +import { createCacheProfile, getCacheProfile } from '@utils/storage'; import { truncate } from '@utils/truncate'; import { DotsHorizontalIcon } from '@radix-ui/react-icons'; @@ -9,13 +9,12 @@ import Avatar from 'boring-avatars'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; import { useRouter } from 'next/router'; -import { memo, useCallback, useContext, useEffect, useState } from 'react'; +import { memo, useCallback, useEffect, useState } from 'react'; dayjs.extend(relativeTime); export const UserExtend = memo(function UserExtend({ pubkey, time }: { pubkey: string; time: any }) { const router = useRouter(); - const { db }: any = useContext(DatabaseContext); const [profile, setProfile] = useState(null); const openUserPage = (e) => { @@ -24,7 +23,6 @@ export const UserExtend = memo(function UserExtend({ pubkey, time }: { pubkey: s }; const fetchProfile = useCallback(async (id: string) => { - console.log('fetch'); const res = await fetch(`https://rbr.bio/${id}/metadata.json`, { method: 'GET', timeout: 30, @@ -32,34 +30,20 @@ export const UserExtend = memo(function UserExtend({ pubkey, time }: { pubkey: s return res.data; }, []); - const getCacheProfile = useCallback(async () => { - const result: any = await db.select(`SELECT metadata FROM cache_profiles WHERE id = "${pubkey}"`); - return result[0]; - }, [db, pubkey]); - - const insertCacheProfile = useCallback( - async (event) => { - // update state - setProfile(JSON.parse(event.content)); - // insert to database - await db.execute('INSERT OR IGNORE INTO cache_profiles (id, metadata) VALUES (?, ?);', [pubkey, event.content]); - }, - [db, pubkey] - ); - useEffect(() => { - getCacheProfile() - .then((res) => { - if (res !== undefined) { - setProfile(JSON.parse(res.metadata)); - } else { - fetchProfile(pubkey) - .then((res) => insertCacheProfile(res)) - .catch(console.error); - } - }) - .catch(console.error); - }, [fetchProfile, getCacheProfile, insertCacheProfile, pubkey]); + getCacheProfile(pubkey).then((res) => { + if (res) { + setProfile(JSON.parse(res.metadata)); + } else { + fetchProfile(pubkey) + .then((res: any) => { + setProfile(JSON.parse(res.content)); + createCacheProfile(pubkey, res.content); + }) + .catch(console.error); + } + }); + }, [fetchProfile, pubkey]); return (
openUserPage(e)} className="group flex items-start gap-2"> diff --git a/src/components/user/large.tsx b/src/components/user/large.tsx index c5264f97..d4180246 100644 --- a/src/components/user/large.tsx +++ b/src/components/user/large.tsx @@ -1,6 +1,6 @@ -import { DatabaseContext } from '@components/contexts/database'; import { ImageWithFallback } from '@components/imageWithFallback'; +import { createCacheProfile, getCacheProfile } from '@utils/storage'; import { truncate } from '@utils/truncate'; import { DotsHorizontalIcon } from '@radix-ui/react-icons'; @@ -8,14 +8,11 @@ import { fetch } from '@tauri-apps/api/http'; import Avatar from 'boring-avatars'; import dayjs from 'dayjs'; import relativeTime from 'dayjs/plugin/relativeTime'; -import { memo, useCallback, useContext, useEffect, useState } from 'react'; - -truncate; +import { memo, useCallback, useEffect, useState } from 'react'; dayjs.extend(relativeTime); export const UserLarge = memo(function UserLarge({ pubkey, time }: { pubkey: string; time: any }) { - const { db }: any = useContext(DatabaseContext); const [profile, setProfile] = useState(null); const fetchProfile = useCallback(async (id: string) => { @@ -26,34 +23,20 @@ export const UserLarge = memo(function UserLarge({ pubkey, time }: { pubkey: str return res.data; }, []); - const getCacheProfile = useCallback(async () => { - const result: any = await db.select(`SELECT metadata FROM cache_profiles WHERE id = "${pubkey}"`); - return result[0]; - }, [db, pubkey]); - - const insertCacheProfile = useCallback( - async (event) => { - // update state - setProfile(JSON.parse(event.content)); - // insert to database - await db.execute('INSERT OR IGNORE INTO cache_profiles (id, metadata) VALUES (?, ?);', [pubkey, event.content]); - }, - [db, pubkey] - ); - useEffect(() => { - getCacheProfile() - .then((res) => { - if (res !== undefined) { - setProfile(JSON.parse(res.metadata)); - } else { - fetchProfile(pubkey) - .then((res) => insertCacheProfile(res)) - .catch(console.error); - } - }) - .catch(console.error); - }, [fetchProfile, getCacheProfile, insertCacheProfile, pubkey]); + getCacheProfile(pubkey).then((res) => { + if (res) { + setProfile(JSON.parse(res.metadata)); + } else { + fetchProfile(pubkey) + .then((res: any) => { + setProfile(JSON.parse(res.content)); + createCacheProfile(pubkey, res.content); + }) + .catch(console.error); + } + }); + }, [fetchProfile, pubkey]); return (
diff --git a/src/components/user/mention.tsx b/src/components/user/mention.tsx index 3cfe132b..2a57cd3c 100644 --- a/src/components/user/mention.tsx +++ b/src/components/user/mention.tsx @@ -1,12 +1,10 @@ -import { DatabaseContext } from '@components/contexts/database'; - +import { createCacheProfile, getCacheProfile } from '@utils/storage'; import { truncate } from '@utils/truncate'; import { fetch } from '@tauri-apps/api/http'; -import { memo, useCallback, useContext, useEffect, useState } from 'react'; +import { memo, useCallback, useEffect, useState } from 'react'; export const UserMention = memo(function UserMention({ pubkey }: { pubkey: string }) { - const { db }: any = useContext(DatabaseContext); const [profile, setProfile] = useState(null); const fetchProfile = useCallback(async (id: string) => { @@ -17,34 +15,20 @@ export const UserMention = memo(function UserMention({ pubkey }: { pubkey: strin return res.data; }, []); - const getCacheProfile = useCallback(async () => { - const result: any = await db.select(`SELECT metadata FROM cache_profiles WHERE id = "${pubkey}"`); - return result[0]; - }, [db, pubkey]); - - const insertCacheProfile = useCallback( - async (event) => { - // insert to database - await db.execute('INSERT OR IGNORE INTO cache_profiles (id, metadata) VALUES (?, ?);', [pubkey, event.content]); - // update state - setProfile(JSON.parse(event.content)); - }, - [db, pubkey] - ); - useEffect(() => { - getCacheProfile() - .then((res) => { - if (res !== undefined) { - setProfile(JSON.parse(res.metadata)); - } else { - fetchProfile(pubkey) - .then((res) => insertCacheProfile(res)) - .catch(console.error); - } - }) - .catch(console.error); - }, [fetchProfile, getCacheProfile, insertCacheProfile, pubkey]); + getCacheProfile(pubkey).then((res) => { + if (res) { + setProfile(JSON.parse(res.metadata)); + } else { + fetchProfile(pubkey) + .then((res: any) => { + setProfile(JSON.parse(res.content)); + createCacheProfile(pubkey, res.content); + }) + .catch(console.error); + } + }); + }, [fetchProfile, pubkey]); return @{profile?.name || truncate(pubkey, 16, ' .... ')}; }); diff --git a/src/components/user/mini.tsx b/src/components/user/mini.tsx index 63d4776d..07a4e508 100644 --- a/src/components/user/mini.tsx +++ b/src/components/user/mini.tsx @@ -1,14 +1,13 @@ -import { DatabaseContext } from '@components/contexts/database'; import { ImageWithFallback } from '@components/imageWithFallback'; +import { createCacheProfile, getCacheProfile } from '@utils/storage'; import { truncate } from '@utils/truncate'; import { fetch } from '@tauri-apps/api/http'; import Avatar from 'boring-avatars'; -import { memo, useCallback, useContext, useEffect, useState } from 'react'; +import { memo, useCallback, useEffect, useState } from 'react'; export const UserMini = memo(function UserMini({ pubkey }: { pubkey: string }) { - const { db }: any = useContext(DatabaseContext); const [profile, setProfile] = useState(null); const fetchProfile = useCallback(async (id: string) => { @@ -19,34 +18,20 @@ export const UserMini = memo(function UserMini({ pubkey }: { pubkey: string }) { return res.data; }, []); - const getCacheProfile = useCallback(async () => { - const result: any = await db.select(`SELECT metadata FROM cache_profiles WHERE id = "${pubkey}"`); - return result[0]; - }, [db, pubkey]); - - const insertCacheProfile = useCallback( - async (event) => { - // update state - setProfile(JSON.parse(event.content)); - // insert to database - await db.execute('INSERT OR IGNORE INTO cache_profiles (id, metadata) VALUES (?, ?);', [pubkey, event.content]); - }, - [db, pubkey] - ); - useEffect(() => { - getCacheProfile() - .then((res) => { - if (res !== undefined) { - setProfile(JSON.parse(res.metadata)); - } else { - fetchProfile(pubkey) - .then((res) => insertCacheProfile(res)) - .catch(console.error); - } - }) - .catch(console.error); - }, [fetchProfile, getCacheProfile, insertCacheProfile, pubkey]); + getCacheProfile(pubkey).then((res) => { + if (res) { + setProfile(JSON.parse(res.metadata)); + } else { + fetchProfile(pubkey) + .then((res: any) => { + setProfile(JSON.parse(res.content)); + createCacheProfile(pubkey, res.content); + }) + .catch(console.error); + } + }); + }, [fetchProfile, pubkey]); return (
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index e3308cab..cac8697f 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,4 +1,8 @@ -import { Provider } from 'jotai'; +import RelayProvider from '@components/relaysProvider'; + +import { relaysAtom } from '@stores/relays'; + +import { Provider, useAtom } from 'jotai'; import type { NextPage } from 'next'; import type { AppProps } from 'next/app'; import { ReactElement, ReactNode } from 'react'; @@ -17,5 +21,11 @@ type AppPropsWithLayout = AppProps & { export default function MyApp({ Component, pageProps }: AppPropsWithLayout) { // Use the layout defined at the page level, if available const getLayout = Component.getLayout ?? ((page) => page); - return {getLayout()}; + const [relays] = useAtom(relaysAtom); + + return ( + + {getLayout()} + + ); } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index ede83f0b..a88903d7 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -1,6 +1,6 @@ import BaseLayout from '@layouts/base'; -import { getActiveAccount } from '@utils/storage'; +import { getAccounts } from '@utils/storage'; import LumeSymbol from '@assets/icons/Lume'; @@ -11,10 +11,10 @@ export default function Page() { const router = useRouter(); useEffect(() => { - getActiveAccount() - .then((res: any[]) => { + getAccounts() + .then((res: any) => { if (res.length > 0) { - router.push('/newsfeed/following'); + router.push('/newsfeed/circle'); } else { router.push('/onboarding'); } diff --git a/src/pages/newsfeed/[id].tsx b/src/pages/newsfeed/[id].tsx index 469a1018..b6d7ce27 100644 --- a/src/pages/newsfeed/[id].tsx +++ b/src/pages/newsfeed/[id].tsx @@ -1,11 +1,12 @@ import BaseLayout from '@layouts/base'; import WithSidebarLayout from '@layouts/withSidebar'; -import { DatabaseContext } from '@components/contexts/database'; -import { RelayContext } from '@components/contexts/relay'; import { Content } from '@components/note/content'; import { ContentExtend } from '@components/note/content/extend'; import FormComment from '@components/note/form/comment'; +import { RelayContext } from '@components/relaysProvider'; + +import { getNoteByID } from '@utils/storage'; import useLocalStorage from '@rehooks/local-storage'; import { useRouter } from 'next/router'; @@ -14,15 +15,13 @@ import { ReactElement, ReactFragment, ReactPortal, - useCallback, useContext, useEffect, useState, } from 'react'; export default function Page() { - const { db }: any = useContext(DatabaseContext); - const relayPool: any = useContext(RelayContext); + const pool: any = useContext(RelayContext); const router = useRouter(); const id = router.query.id; @@ -32,17 +31,15 @@ export default function Page() { const [rootEvent, setRootEvent] = useState(null); const [comments, setComments] = useState([]); - const fetchRoot = useCallback(async () => { - const result = await db.select(`SELECT * FROM cache_notes WHERE id = "${id}"`); - setRootEvent(result[0]); - }, [db, id]); - useEffect(() => { let unsubscribe: () => void; - fetchRoot() - .then(() => { - unsubscribe = relayPool.subscribe( + getNoteByID(id) + .then((res) => { + // update state + setRootEvent(res); + // get all comments + unsubscribe = pool.subscribe( [ { '#e': [id], @@ -60,7 +57,7 @@ export default function Page() { return () => { unsubscribe(); }; - }, [fetchRoot, id, relayPool, relays]); + }, [id, pool, relays]); return (
diff --git a/src/pages/onboarding/create/index.tsx b/src/pages/onboarding/create/index.tsx index e31f934b..3dfbf87e 100644 --- a/src/pages/onboarding/create/index.tsx +++ b/src/pages/onboarding/create/index.tsx @@ -1,13 +1,17 @@ import BaseLayout from '@layouts/base'; -import { pool } from '@utils/pool'; -import { createAccount, getAllRelays } from '@utils/storage'; +import { RelayContext } from '@components/relaysProvider'; + +import { relaysAtom } from '@stores/relays'; + +import { createAccount } from '@utils/storage'; import { EyeClosedIcon, EyeOpenIcon } from '@radix-ui/react-icons'; +import { useAtom } from 'jotai'; import Image from 'next/image'; import { useRouter } from 'next/router'; import { generatePrivateKey, getEventHash, getPublicKey, nip19, signEvent } from 'nostr-tools'; -import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useMemo, useState } from 'react'; +import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useMemo, useState } from 'react'; import { Config, names, uniqueNamesGenerator } from 'unique-names-generator'; const config: Config = { @@ -16,7 +20,9 @@ const config: Config = { export default function Page() { const router = useRouter(); + const pool: any = useContext(RelayContext); + const [relays] = useAtom(relaysAtom); const [type, setType] = useState('password'); const [loading, setLoading] = useState(false); @@ -78,16 +84,11 @@ export default function Page() { // insert to database then broadcast createAccount(data) .then(() => { - getAllRelays() - .then((res) => { - // publish to relays - pool(res).publish(event, res); - router.push({ - pathname: '/onboarding/create/step-2', - query: { id: pubKey, privkey: privKey }, - }); - }) - .catch(console.error); + pool.publish(event, relays); + router.push({ + pathname: '/onboarding/create/step-2', + query: { id: pubKey, privkey: privKey }, + }); }) .catch(console.error); }; diff --git a/src/pages/onboarding/create/step-2.tsx b/src/pages/onboarding/create/step-2.tsx index 5c902de5..c2db11e1 100644 --- a/src/pages/onboarding/create/step-2.tsx +++ b/src/pages/onboarding/create/step-2.tsx @@ -1,15 +1,27 @@ import BaseLayout from '@layouts/base'; +import { RelayContext } from '@components/relaysProvider'; import { UserBase } from '@components/user/base'; -import { pool } from '@utils/pool'; -import { createFollows, getAllRelays } from '@utils/storage'; +import { relaysAtom } from '@stores/relays'; + +import { createFollows } from '@utils/storage'; import { CheckCircledIcon } from '@radix-ui/react-icons'; import { createClient } from '@supabase/supabase-js'; +import { useAtom } from 'jotai'; import { useRouter } from 'next/router'; import { getEventHash, signEvent } from 'nostr-tools'; -import { JSXElementConstructor, Key, ReactElement, ReactFragment, ReactPortal, useEffect, useState } from 'react'; +import { + JSXElementConstructor, + Key, + ReactElement, + ReactFragment, + ReactPortal, + useContext, + useEffect, + useState, +} from 'react'; const supabase = createClient( 'https://niwaazauwnrwiwmnocnn.supabase.co', @@ -52,9 +64,12 @@ const initialList = [ ]; export default function Page() { + const pool: any = useContext(RelayContext); + const router = useRouter(); const { id, privkey }: any = router.query; + const [relays] = useAtom(relaysAtom); const [loading, setLoading] = useState(false); const [list, setList]: any = useState(initialList); const [follows, setFollows] = useState([]); @@ -93,13 +108,9 @@ export default function Page() { createFollows(follows, id, 0) .then((res) => { if (res === 'ok') { - getAllRelays() - .then((res) => { - // publish to relays - pool(res).publish(event, res); - router.push('/'); - }) - .catch(console.error); + // publish to relays + pool.publish(event, relays); + router.push('/'); } }) .catch(console.error); diff --git a/src/pages/onboarding/login/step-2.tsx b/src/pages/onboarding/login/step-2.tsx index def0e08c..9cf8c857 100644 --- a/src/pages/onboarding/login/step-2.tsx +++ b/src/pages/onboarding/login/step-2.tsx @@ -1,69 +1,72 @@ import BaseLayout from '@layouts/base'; -import { pool } from '@utils/pool'; -import { createAccount, createFollows, getAllRelays } from '@utils/storage'; +import { RelayContext } from '@components/relaysProvider'; + +import { relaysAtom } from '@stores/relays'; + +import { createAccount, createFollows } from '@utils/storage'; +import { tagsToArray } from '@utils/tags'; import { truncate } from '@utils/truncate'; import destr from 'destr'; +import { useAtom } from 'jotai'; import Image from 'next/image'; import { useRouter } from 'next/router'; import { getPublicKey, nip19 } from 'nostr-tools'; -import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useEffect, useState } from 'react'; - -const tags = (arr) => { - const newarr = []; - // push item to newarr - arr.forEach((item) => { - newarr.push(['p', item]); - }); - return newarr; -}; +import { + JSXElementConstructor, + ReactElement, + ReactFragment, + ReactPortal, + useContext, + useEffect, + useState, +} from 'react'; export default function Page() { + const pool: any = useContext(RelayContext); + const router = useRouter(); const privkey: any = router.query.privkey; const pubkey = getPublicKey(privkey); + const [relays] = useAtom(relaysAtom); const [profile, setProfile] = useState(null); useEffect(() => { - getAllRelays() - .then((res) => { - pool(res).subscribe( - [ - { - authors: [pubkey], - kinds: [0, 3], - since: 0, - }, - ], - res, - (event: any) => { - if (event.kind === 0) { - const data = { - pubkey: pubkey, - privkey: privkey, - npub: nip19.npubEncode(pubkey), - nsec: nip19.nsecEncode(privkey), - metadata: event.content, - }; - setProfile(destr(event.content)); - createAccount(data); - } else { - if (event.tags.length > 0) { - createFollows(tags(event.tags), pubkey, 0); - } - } - }, - undefined, - undefined, - { - unsubscribeOnEose: true, + pool.subscribe( + [ + { + authors: [pubkey], + kinds: [0, 3], + since: 0, + }, + ], + relays, + (event: any) => { + if (event.kind === 0) { + const data = { + pubkey: pubkey, + privkey: privkey, + npub: nip19.npubEncode(pubkey), + nsec: nip19.nsecEncode(privkey), + metadata: event.content, + }; + setProfile(destr(event.content)); + createAccount(data); + } else { + if (event.tags.length > 0) { + createFollows(tagsToArray(event.tags), pubkey, 0); } - ); - }) - .catch(console.error); - }, [privkey, pubkey]); + } + }, + undefined, + undefined, + { + unsubscribeOnEose: true, + } + ); + }, [pool, privkey, pubkey, relays]); // submit then redirect to home const submit = () => { diff --git a/src/stores/account.tsx b/src/stores/account.tsx new file mode 100644 index 00000000..5b4054fd --- /dev/null +++ b/src/stores/account.tsx @@ -0,0 +1,9 @@ +import { isSSR } from '@utils/ssr'; +import { getActiveAccount } from '@utils/storage'; + +import { atomWithCache } from 'jotai-cache'; + +export const activeAccountAtom = atomWithCache(async () => { + const response = isSSR ? null : await getActiveAccount(); + return response; +}); diff --git a/src/stores/relays.tsx b/src/stores/relays.tsx new file mode 100644 index 00000000..67d65fbe --- /dev/null +++ b/src/stores/relays.tsx @@ -0,0 +1,9 @@ +import { isSSR } from '@utils/ssr'; +import { getAllRelays } from '@utils/storage'; + +import { atomWithCache } from 'jotai-cache'; + +export const relaysAtom = atomWithCache(async () => { + const response = isSSR ? null : await getAllRelays(); + return response; +}); diff --git a/src/stores/user.tsx b/src/stores/user.tsx deleted file mode 100644 index b9cad4fb..00000000 --- a/src/stores/user.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import { atom } from 'jotai'; - -export const currentUser = atom({}); diff --git a/src/utils/pool.tsx b/src/utils/pool.tsx deleted file mode 100644 index 6f167652..00000000 --- a/src/utils/pool.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { RelayPool } from 'nostr-relaypool'; - -export function pool({ relays }: { relays: any }) { - const createPool = new RelayPool(relays, { useEventCache: false, logSubscriptions: false }); - return createPool; -} diff --git a/src/utils/ssr.tsx b/src/utils/ssr.tsx new file mode 100644 index 00000000..3f489e4f --- /dev/null +++ b/src/utils/ssr.tsx @@ -0,0 +1 @@ +export const isSSR = typeof window === 'undefined'; diff --git a/src/utils/storage.tsx b/src/utils/storage.tsx index 3afb9f1e..b99e24f3 100644 --- a/src/utils/storage.tsx +++ b/src/utils/storage.tsx @@ -25,7 +25,14 @@ export async function getAllRelays() { // get active account export async function getActiveAccount() { const db = await connect(); - return await db.select(`SELECT * FROM accounts LIMIT 1;`); + const result = await db.select(`SELECT * FROM accounts LIMIT 1;`); + return result[0]; +} + +// get all accounts +export async function getAccounts() { + const db = await connect(); + return await db.select(`SELECT * FROM accounts`); } // get all follows by account id @@ -71,3 +78,26 @@ export async function createCacheProfile(id, metadata) { const db = await connect(); return await db.execute('INSERT OR IGNORE INTO cache_profiles (id, metadata) VALUES (?, ?);', [id, metadata]); } + +// get cache profile +export async function getCacheProfile(id) { + const db = await connect(); + const result = await db.select(`SELECT metadata FROM cache_profiles WHERE id = "${id}"`); + return result[0]; +} + +// get note by id +export async function getNoteByID(id) { + const db = await connect(); + const result = await db.select(`SELECT * FROM cache_notes WHERE id = "${id}"`); + return result[0]; +} + +// create cache note +export async function createCacheNote(data) { + const db = await connect(); + return await db.execute( + 'INSERT OR IGNORE INTO cache_notes (id, pubkey, created_at, kind, content, tags, is_root) VALUES (?, ?, ?, ?, ?, ?, ?);', + [data.id, data.pubkey, data.created_at, data.kind, data.content, JSON.stringify(data.tags), 0] + ); +} diff --git a/src/utils/tags.tsx b/src/utils/tags.tsx new file mode 100644 index 00000000..a4d7b49e --- /dev/null +++ b/src/utils/tags.tsx @@ -0,0 +1,8 @@ +export const tagsToArray = (arr) => { + const newarr = []; + // push item to newarr + arr.forEach((item) => { + newarr.push(item[1]); + }); + return newarr; +};