From 18900f14106f4c2ae5d335458f2d71e0c13d21d2 Mon Sep 17 00:00:00 2001 From: Ren Amamiya <123083837+reyamir@users.noreply.github.com> Date: Sun, 26 Feb 2023 21:21:35 +0700 Subject: [PATCH] added database provider & refactor sql execute --- src/components/contexts/database.tsx | 13 ++++ .../contexts/relay.tsx} | 0 src/pages/_app.tsx | 10 ++- src/pages/feed/following.tsx | 46 ++++++++----- src/pages/index.tsx | 31 +++++---- src/pages/onboarding/create.tsx | 67 ++++++++----------- src/pages/onboarding/fetch-follows.tsx | 50 ++++++++------ src/pages/onboarding/fetch-profile.tsx | 45 ++++++++----- src/pages/onboarding/following.tsx | 12 ++-- 9 files changed, 161 insertions(+), 113 deletions(-) create mode 100644 src/components/contexts/database.tsx rename src/{stores/context.tsx => components/contexts/relay.tsx} (100%) diff --git a/src/components/contexts/database.tsx b/src/components/contexts/database.tsx new file mode 100644 index 00000000..c4059a99 --- /dev/null +++ b/src/components/contexts/database.tsx @@ -0,0 +1,13 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { createContext } 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 value = db; + + return {children}; +} diff --git a/src/stores/context.tsx b/src/components/contexts/relay.tsx similarity index 100% rename from src/stores/context.tsx rename to src/components/contexts/relay.tsx diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 97c17e48..ff4c5514 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -1,5 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import RelayProvider from '@stores/context'; +import DatabaseProvider from '@components/contexts/database'; +import RelayProvider from '@components/contexts/relay'; + import { relays } from '@stores/relays'; import { useStore } from '@nanostores/react'; @@ -24,5 +26,9 @@ export default function MyApp({ Component, pageProps }: AppPropsWithLayout) { // Get all relays const $relays = useStore(relays); - return {getLayout()}; + return ( + + {getLayout()} + + ); } diff --git a/src/pages/feed/following.tsx b/src/pages/feed/following.tsx index 1ad966fc..a90ad029 100644 --- a/src/pages/feed/following.tsx +++ b/src/pages/feed/following.tsx @@ -2,37 +2,59 @@ import BaseLayout from '@layouts/baseLayout'; import NewsFeedLayout from '@layouts/newsfeedLayout'; +import { RelayContext } from '@components/contexts/relay'; + import { hoursAgo } from '@utils/getDate'; -import { RelayContext } from '@stores/context'; +import { currentUser } from '@stores/currentUser'; import { follows } from '@stores/follows'; import { relays } from '@stores/relays'; import { useStore } from '@nanostores/react'; import { dateToUnix } from 'nostr-react'; -import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useEffect, useRef, useState } from 'react'; +import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useEffect, useRef } from 'react'; export default function Page() { const relayPool: any = useContext(RelayContext); - - const [data, setData] = useState([]); const now = useRef(new Date()); const $follows = useStore(follows); const $relays = useStore(relays); + const $currentUser = useStore(currentUser); useEffect(() => { const unsub = relayPool.subscribe( [ { - kinds: [1], + kinds: [0, 1, 3, 5, 7], authors: $follows, since: dateToUnix(hoursAgo(12, now.current)), }, ], $relays, - (event: any) => { - setData((data) => [event, ...data]); + async (event: any) => { + switch (event.kind) { + case 0: + //await db.execute(`INSERT OR IGNORE INTO cache_profiles (id, metadata) VALUES ("${event.pubkey}", '${JSON.stringify(event.content)}')`); + break; + case 3: + //await db.execute(`INSERT OR IGNORE INTO follows (pubkey, account, kind) VALUES ("${event.pubkey}", "${$currentUser.pubkey}", "1")`); + break; + case 1: + case 5: + case 7: + /* + const isMulti = event.tags.length > 0; + await db.execute( + `INSERT OR IGNORE INTO cache_notes (id, note, kind, is_multi) VALUES ("${event.pubkey}", '${JSON.stringify(event)}', "${ + event.kind + }", "${isMulti}")` + ); + */ + break; + default: + break; + } }, undefined, (events: any, relayURL: any) => { @@ -41,15 +63,9 @@ export default function Page() { ); return () => unsub(); - }, [$follows, $relays, relayPool]); + }, [$currentUser.pubkey, $follows, $relays, relayPool]); - return ( -
- {data.map((item, index) => ( -

{item.id}

- ))} -
- ); + return
; } Page.getLayout = function getLayout( diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 14a98545..688b4954 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -2,6 +2,8 @@ import BaseLayout from '@layouts/baseLayout'; import FullLayout from '@layouts/fullLayout'; +import { DatabaseContext } from '@components/contexts/database'; + import { currentUser } from '@stores/currentUser'; import { follows } from '@stores/follows'; @@ -10,12 +12,11 @@ import LumeSymbol from '@assets/icons/Lume'; import { isPermissionGranted, requestPermission, sendNotification } from '@tauri-apps/api/notification'; import { motion } from 'framer-motion'; import { useRouter } from 'next/router'; -import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useCallback, useEffect, useState } from 'react'; -import Database from 'tauri-plugin-sql-api'; - -const db = typeof window !== 'undefined' ? await Database.load('sqlite:lume.db') : null; +import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useCallback, useContext, useEffect, useState } from 'react'; export default function Page() { + const db: any = useContext(DatabaseContext); + const router = useRouter(); const [loading, setLoading] = useState(true); @@ -36,20 +37,22 @@ export default function Page() { const getAccount = useCallback(async () => { const result = await db.select(`SELECT * FROM accounts ASC LIMIT 1`); - return result; - }, []); + }, [db]); - const getFollows = useCallback(async (account: { id: string }) => { - const arr = []; - const result: any = await db.select(`SELECT pubkey FROM follows WHERE account = "${account.id}"`); + const getFollows = useCallback( + async (account: { id: string }) => { + const arr = []; + const result: any = await db.select(`SELECT pubkey FROM follows WHERE account = "${account.id}"`); - result.forEach((item: { pubkey: string }) => { - arr.push(item.pubkey); - }); + result.forEach((item: { pubkey: string }) => { + arr.push(item.pubkey); + }); - return arr; - }, []); + return arr; + }, + [db] + ); // Explain: // Step 1: request allow notification from system diff --git a/src/pages/onboarding/create.tsx b/src/pages/onboarding/create.tsx index 01043ce4..c836db7f 100644 --- a/src/pages/onboarding/create.tsx +++ b/src/pages/onboarding/create.tsx @@ -2,84 +2,79 @@ import BaseLayout from '@layouts/baseLayout'; import OnboardingLayout from '@layouts/onboardingLayout'; -import { currentUser } from '@stores/currentUser'; +import { DatabaseContext } from '@components/contexts/database'; +import { RelayContext } from '@components/contexts/relay'; +import { currentUser } from '@stores/currentUser'; +import { relays } from '@stores/relays'; + +import { useStore } from '@nanostores/react'; import { EyeClosedIcon, EyeOpenIcon } from '@radix-ui/react-icons'; import { motion } from 'framer-motion'; import Image from 'next/image'; import { useRouter } from 'next/router'; -import { dateToUnix, useNostr } from 'nostr-react'; import { generatePrivateKey, getEventHash, getPublicKey, nip19, signEvent } from 'nostr-tools'; -import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useState } from 'react'; -import Database from 'tauri-plugin-sql-api'; +import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useState } from 'react'; import { Config, names, uniqueNamesGenerator } from 'unique-names-generator'; const config: Config = { dictionaries: [names], }; -const defaultAvatars = [ - 'https://bafybeidfsbrzqbvontmucteomoz2rkrxugu462l5hyhh6uioslkfzzs4oq.ipfs.w3s.link/avatar-11.png', - 'https://bafybeid7mrvznbnd6r2ju2iu7lsxkcikufys6z6ssy5ldxrxq5qh3yqf4u.ipfs.w3s.link/avatar-12.png', - 'https://bafybeih5gpwu53ohui6p7scekjpxjk2d4lusq2jqohqhjsvhfkeu56ea4e.ipfs.w3s.link/avatar-13.png', - 'https://bafybeibpbvrpuphkerjygdbnh26av5brqggzunbbbmfl3ozlvcn2mj6zxa.ipfs.w3s.link/avatar-14.png', - 'https://bafybeia4ue4loinuflu7y5q3xu6hcvt653mzw5yorw25oarf2wqksig4ma.ipfs.w3s.link/avatar-15.png', - 'https://bafybeib3gzl6n2bebiru2cpkdljmlzbtqfsl6xcnqtabxt6jrpj7l7ltm4.ipfs.w3s.link/avatar-16.png', -]; - -const defaultBanners = [ - 'https://bafybeiacwit7hjmdefqggxqtgh6ht5dhth7ndptwn2msl5kpkodudsr7py.ipfs.w3s.link/banner-1.jpg', - 'https://bafybeiderllqadxsikh3envikobmyka3uwgojriwh6epctqartq2loswyi.ipfs.w3s.link/banner-2.jpg', - 'https://bafybeiba4tifde2kczvd26vxhbb5jpqi3wmgvccpkcrle4hse2cqrwlwiy.ipfs.w3s.link/banner-3.jpg', - 'https://bafybeifqpny2eom7ccvmaguxxxfajutmn5h3fotaasga7gce2xfx37p6oy.ipfs.w3s.link/banner-4.jpg', -]; - export default function Page() { + const db: any = useContext(DatabaseContext); const router = useRouter(); - const { publish } = useNostr(); + + const relayPool: any = useContext(RelayContext); + const $relays = useStore(relays); const [type, setType] = useState('password'); const [loading, setLoading] = useState(false); const [privKey] = useState(() => generatePrivateKey()); const [name] = useState(() => uniqueNamesGenerator(config).toString()); - const [avatar] = useState(() => defaultAvatars[Math.floor(Math.random() * defaultAvatars.length)]); - const [banner] = useState(() => defaultBanners[Math.floor(Math.random() * defaultBanners.length)]); const pubKey = getPublicKey(privKey); const npub = nip19.npubEncode(pubKey); const nsec = nip19.nsecEncode(privKey); + const showPrivateKey = () => { + if (type === 'password') { + setType('text'); + } else { + setType('password'); + } + }; + // auto-generated profile const data = { display_name: name, name: name, username: name.toLowerCase(), - picture: avatar, - banner: banner, + picture: 'https://bafybeidfsbrzqbvontmucteomoz2rkrxugu462l5hyhh6uioslkfzzs4oq.ipfs.w3s.link/avatar-11.png', + banner: 'https://bafybeiacwit7hjmdefqggxqtgh6ht5dhth7ndptwn2msl5kpkodudsr7py.ipfs.w3s.link/banner-1.jpg', }; const createAccount = async () => { setLoading(true); - // publish account to relays + // build event const event: any = { content: JSON.stringify(data), - created_at: dateToUnix(), + created_at: Math.floor(Date.now() / 1000), kind: 0, pubkey: pubKey, tags: [], }; event.id = getEventHash(event); event.sig = signEvent(event, privKey); - publish(event); + // publish to relays + relayPool.publish(event, $relays); // save account to database - const db = await Database.load('sqlite:lume.db'); await db.execute( `INSERT INTO accounts (id, privkey, npub, nsec, metadata) VALUES ("${pubKey}", "${privKey}", "${npub}", "${nsec}", '${JSON.stringify(data)}')` ); - await db.close(); // set currentUser in global state currentUser.set({ @@ -96,14 +91,6 @@ export default function Page() { }, 1500); }; - const showNsec = () => { - if (type === 'password') { - setType('text'); - } else { - setType('password'); - } - }; - return (
{/* spacer */}
@@ -137,7 +124,9 @@ export default function Page() { value={nsec} className="relative w-full rounded-lg border border-black/5 px-3.5 py-2 shadow-input shadow-black/5 !outline-none placeholder:text-zinc-400 dark:bg-zinc-800 dark:text-zinc-200 dark:shadow-black/10 dark:placeholder:text-zinc-600" /> -