From 39e7c9bf34c05fe04e22b3cc63ee345e93e98051 Mon Sep 17 00:00:00 2001 From: Ren Amamiya <123083837+reyamir@users.noreply.github.com> Date: Sun, 2 Apr 2023 16:22:50 +0700 Subject: [PATCH] wip: convert tauri sql to prisma --- src-tauri/.gitignore | 3 +- src-tauri/Cargo.lock | 1 + src-tauri/Cargo.toml | 1 + .../20230226004139_create_tables.sql | 104 ------------------ src-tauri/prisma/schema.prisma | 72 +++++++++++- src-tauri/src/main.rs | 84 +++++++++++++- src/pages/index.tsx | 17 +-- src/pages/onboarding/create/index.tsx | 38 +++---- src/pages/onboarding/create/step-2.tsx | 35 ++---- src/utils/bindings.ts | 26 +++++ src/utils/transform.tsx | 9 ++ 11 files changed, 228 insertions(+), 162 deletions(-) delete mode 100644 src-tauri/migrations/20230226004139_create_tables.sql create mode 100644 src/utils/bindings.ts diff --git a/src-tauri/.gitignore b/src-tauri/.gitignore index d35e841c..9f4929f3 100644 --- a/src-tauri/.gitignore +++ b/src-tauri/.gitignore @@ -3,4 +3,5 @@ /target/ # prisma -src/prisma.rs +src/db.rs +lume.db diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 7640a8e0..7129ab10 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2193,6 +2193,7 @@ dependencies = [ "tauri", "tauri-build", "tauri-specta", + "tokio", ] [[package]] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 6cd5dc26..a01d8208 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -21,6 +21,7 @@ prisma-client-rust = { git = "https://github.com/Brendonovich/prisma-client-rust prisma-client-rust-cli = { git = "https://github.com/Brendonovich/prisma-client-rust", tag = "0.6.7", default-features = false, features = ["sqlite", "migrations", "mocking", "specta"] } specta = "1.0.0" tauri-specta = { version = "1.0.0", features = ["typescript"] } +tokio = { version = "1.26.0", features = ["macros"] } [target.'cfg(target_os = "macos")'.dependencies] objc = "0.2.7" diff --git a/src-tauri/migrations/20230226004139_create_tables.sql b/src-tauri/migrations/20230226004139_create_tables.sql deleted file mode 100644 index ee4a05b7..00000000 --- a/src-tauri/migrations/20230226004139_create_tables.sql +++ /dev/null @@ -1,104 +0,0 @@ --- Add migration script here --- create relays -CREATE TABLE - relays ( - id INTEGER PRIMARY KEY, - relay_url TEXT NOT NULL, - relay_status INTEGER NOT NULL DEFAULT 1, - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - --- add default relays --- relay status: --- 0: off --- 1: on -INSERT INTO - relays (relay_url, relay_status) -VALUES - ("wss://relay.damus.io", "1"), - ("wss://eden.nostr.land", "0"), - ("wss://nostr-pub.wellorder.net", "1"), - ("wss://nostr.bongbong.com", "1"), - ("wss://nostr.zebedee.cloud", "1"), - ("wss://nostr.fmt.wiz.biz", "1"), - ("wss://nostr.walletofsatoshi.com", "0"), - ("wss://relay.snort.social", "1"), - ("wss://offchain.pub", "1"), - ("wss://brb.io", "0"), - ("wss://relay.current.fyi", "1"), - ("wss://nostr.relayer.se", "0"), - ("wss://nostr.bitcoiner.social", "1"), - ("wss://relay.nostr.info", "1"), - ("wss://relay.zeh.app", "0"), - ("wss://nostr-01.dorafactory.org", "1"), - ("wss://nostr.zhongwen.world", "1"), - ("wss://nostro.cc", "1"), - ("wss://relay.nostr.net.in", "1"), - ("wss://nos.lol", "1"); - --- create accounts --- is_active (part of multi-account feature): --- 0: false --- 1: true -CREATE TABLE - accounts ( - id TEXT PRIMARY KEY, - privkey TEXT NOT NULL, - npub TEXT NOT NULL, - nsec TEXT NOT NULL, - is_active INTEGER NOT NULL DEFAULT 0, - metadata TEXT - ); - --- create follows --- kind (part of multi-newsfeed feature): --- 0: direct --- 1: follow of follow -CREATE TABLE - follows ( - id INTEGER PRIMARY KEY, - pubkey TEXT NOT NULL, - account TEXT NOT NULL, - kind INTEGER NOT NULL DEFAULT 0, - metadata TEXT - ); - --- create index for pubkey in follows -CREATE UNIQUE INDEX index_pubkey_on_follows ON follows (pubkey); - --- create cache profiles -CREATE TABLE - cache_profiles ( - id TEXT PRIMARY KEY, - metadata TEXT, - created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP - ); - --- create cache notes -CREATE TABLE - cache_notes ( - id TEXT PRIMARY KEY, - pubkey TEXT NOT NULL, - created_at TEXT, - kind INTEGER NOT NULL DEFAULT 1, - tags TEXT NOT NULL, - content TEXT NOT NULL, - parent_id TEXT, - parent_comment_id TEXT - ); - --- create settings -CREATE TABLE - settings ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - setting_key TEXT NOT NULL, - setting_value TEXT NOT NULL - ); - --- add default setting -INSERT INTO - settings (setting_key, setting_value) -VALUES - ("last_login", "0"); \ No newline at end of file diff --git a/src-tauri/prisma/schema.prisma b/src-tauri/prisma/schema.prisma index 801e4503..3d8f7c78 100644 --- a/src-tauri/prisma/schema.prisma +++ b/src-tauri/prisma/schema.prisma @@ -1,11 +1,75 @@ datasource db { provider = "sqlite" - url = "file:lume.db" + url = "file:../lume.db" } generator client { - // Corresponds to the cargo alias created earlier - provider = "cargo prisma" + provider = "cargo prisma" // The location to generate the client. Is relative to the position of the schema - output = "../src/prisma.rs" + output = "../src/db.rs" + module_path = "db" +} + +model Account { + id Int @id @default(autoincrement()) + pubkey String + privkey String @unique + active Boolean @default(false) + metadata String + + // related + follows Follow[] + messages Message[] + notes Note[] + + @@index([pubkey]) +} + +model Follow { + id Int @id @default(autoincrement()) + pubkey String + kind Int + metadata String + + Account Account @relation(fields: [accountId], references: [id]) + accountId Int +} + +model Note { + id Int @id @default(autoincrement()) + pubkey String + kind Int + tags String + content String + parent_id String + parent_comment_id String + createdAt DateTime @default(now()) + + Account Account @relation(fields: [accountId], references: [id]) + accountId Int +} + +model Message { + id Int @id @default(autoincrement()) + pubkey String + content String + tags String + createdAt DateTime @default(now()) + + Account Account @relation(fields: [accountId], references: [id]) + accountId Int + + @@index([pubkey]) +} + +model Relay { + id Int @id @default(autoincrement()) + url String + active Boolean @default(true) +} + +model Setting { + id Int @id @default(autoincrement()) + key String + value String } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 67fe56dc..fafb703b 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -8,13 +8,87 @@ extern crate objc; use tauri::{Manager, WindowEvent}; - #[cfg(target_os = "macos")] use window_ext::WindowExt; #[cfg(target_os = "macos")] mod window_ext; -fn main() { +mod db; + +use db::*; +use serde::Deserialize; +use specta::{collect_types, Type}; +use std::sync::Arc; +use tauri::State; +use tauri_specta::ts; + +type DbState<'a> = State<'a, Arc>; + +#[derive(Deserialize, Type)] +struct CreateAccountData { + pubkey: String, + privkey: String, + metadata: String, +} + +#[derive(Deserialize, Type)] +struct CreateFollowData { + pubkey: String, + kind: i32, + metadata: String, + account_id: i32, +} + +#[tauri::command] +#[specta::specta] +async fn get_account(db: DbState<'_>) -> Result, ()> { + db.account() + .find_many(vec![account::active::equals(true)]) + .exec() + .await + .map_err(|_| ()) +} + +#[tauri::command] +#[specta::specta] +async fn create_account(db: DbState<'_>, data: CreateAccountData) -> Result { + db.account() + .create(data.pubkey, data.privkey, data.metadata, vec![]) + .exec() + .await + .map_err(|_| ()) +} + +#[tauri::command] +#[specta::specta] +async fn create_follow(db: DbState<'_>, data: CreateFollowData) -> Result { + db.follow() + .create( + data.pubkey, + data.kind, + data.metadata, + account::id::equals(data.account_id), + vec![], + ) + .exec() + .await + .map_err(|_| ()) +} + +#[tokio::main] +async fn main() { + let db = PrismaClient::_builder().build().await.unwrap(); + + #[cfg(debug_assertions)] + ts::export( + collect_types![get_account, create_account, create_follow], + "../src/utils/bindings.ts", + ) + .unwrap(); + + #[cfg(debug_assertions)] + db._db_push().await.unwrap(); + tauri::Builder::default() .setup(|app| { let main_window = app.get_window("main").unwrap(); @@ -38,6 +112,12 @@ fn main() { _ => {} } }) + .invoke_handler(tauri::generate_handler![ + get_account, + create_account, + create_follow + ]) + .manage(Arc::new(db)) .run(tauri::generate_context!()) .expect("error while running tauri application"); } diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 7b816aa6..c9581d5a 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -2,24 +2,27 @@ import BaseLayout from '@layouts/base'; import { activeAccountAtom } from '@stores/account'; -import { getActiveAccount } from '@utils/storage'; - import LumeSymbol from '@assets/icons/Lume'; import { useSetAtom } from 'jotai'; import { useRouter } from 'next/router'; -import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useEffect } from 'react'; +import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useCallback, useEffect } from 'react'; export default function Page() { const router = useRouter(); const setActiveAccount = useSetAtom(activeAccountAtom); + const fetchActiveAccount = useCallback(async () => { + const { getAccount } = await import('@utils/bindings'); + return await getAccount(); + }, []); + useEffect(() => { - getActiveAccount() + fetchActiveAccount() .then((res: any) => { - if (res) { + if (res.length > 0) { // update local storage - setActiveAccount(res); + setActiveAccount(res[0]); // redirect router.replace('/init'); } else { @@ -27,7 +30,7 @@ export default function Page() { } }) .catch(console.error); - }, [router, setActiveAccount]); + }, [fetchActiveAccount, setActiveAccount, router]); return (
diff --git a/src/pages/onboarding/create/index.tsx b/src/pages/onboarding/create/index.tsx index 70e3c265..55f86ea4 100644 --- a/src/pages/onboarding/create/index.tsx +++ b/src/pages/onboarding/create/index.tsx @@ -2,13 +2,20 @@ import BaseLayout from '@layouts/base'; import { RelayContext } from '@components/relaysProvider'; -import { createAccount } from '@utils/storage'; - import { ArrowLeftIcon, EyeClosedIcon, EyeOpenIcon } from '@radix-ui/react-icons'; import Image from 'next/image'; import { useRouter } from 'next/router'; import { generatePrivateKey, getEventHash, getPublicKey, nip19, signEvent } from 'nostr-tools'; -import { JSXElementConstructor, ReactElement, ReactFragment, ReactPortal, useContext, useMemo, useState } from 'react'; +import { + JSXElementConstructor, + ReactElement, + ReactFragment, + ReactPortal, + useCallback, + useContext, + useMemo, + useState, +} from 'react'; import { Config, names, uniqueNamesGenerator } from 'unique-names-generator'; const config: Config = { @@ -39,23 +46,11 @@ export default function Page() { display_name: name, name: name, username: name.toLowerCase(), - picture: 'https://void.cat/d/KmypFh2fBdYCEvyJrPiN89', + picture: 'https://void.cat/d/KmypFh2fBdYCEvyJrPiN89.webp', }), [name] ); - // build profile - const data = useMemo( - () => ({ - pubkey: pubKey, - privkey: privKey, - npub: npub, - nsec: nsec, - metadata: metadata, - }), - [metadata, npub, nsec, privKey, pubKey] - ); - // toggle privatek key const showPrivateKey = () => { if (type === 'password') { @@ -66,7 +61,8 @@ export default function Page() { }; // create account and broadcast to all relays - const submit = () => { + const submit = useCallback(async () => { + const { createAccount } = await import('@utils/bindings'); setLoading(true); // build event @@ -81,16 +77,16 @@ export default function Page() { event.sig = signEvent(event, privKey); // insert to database then broadcast - createAccount(data) - .then(() => { + createAccount({ pubkey: pubKey, privkey: privKey, metadata: JSON.stringify(metadata) }) + .then((res) => { pool.publish(event, relays); router.push({ pathname: '/onboarding/create/step-2', - query: { id: pubKey, privkey: privKey }, + query: { id: res.id, privkey: res.privkey }, }); }) .catch(console.error); - }; + }, [pool, pubKey, privKey, metadata, relays, router]); return (
diff --git a/src/pages/onboarding/create/step-2.tsx b/src/pages/onboarding/create/step-2.tsx index 9c8e6bd5..b31d4f78 100644 --- a/src/pages/onboarding/create/step-2.tsx +++ b/src/pages/onboarding/create/step-2.tsx @@ -3,7 +3,7 @@ import BaseLayout from '@layouts/base'; import { RelayContext } from '@components/relaysProvider'; import { UserBase } from '@components/user/base'; -import { createFollows } from '@utils/storage'; +import { followsTag } from '@utils/transform'; import { CheckCircledIcon } from '@radix-ui/react-icons'; import { createClient } from '@supabase/supabase-js'; @@ -15,6 +15,7 @@ import { ReactElement, ReactFragment, ReactPortal, + useCallback, useContext, useEffect, useState, @@ -76,18 +77,9 @@ export default function Page() { setFollows(arr); }; - // build event tags - const tags = () => { - const arr = []; - // push item to tags - follows.forEach((item) => { - arr.push(['p', item]); - }); - return arr; - }; - // save follows to database then broadcast - const submit = () => { + const submit = useCallback(async () => { + const { createFollow } = await import('@utils/bindings'); setLoading(true); // build event @@ -96,21 +88,18 @@ export default function Page() { created_at: Math.floor(Date.now() / 1000), kind: 3, pubkey: id, - tags: tags(), + tags: followsTag(follows), }; event.id = getEventHash(event); event.sig = signEvent(event, privkey); - createFollows(follows, id, 0) - .then((res) => { - if (res === 'ok') { - // publish to relays - pool.publish(event, relays); - router.replace('/'); - } - }) - .catch(console.error); - }; + follows.forEach((item) => { + createFollow({ pubkey: item, kind: 0, metadata: JSON.stringify({}), account_id: id }); + }); + + pool.publish(event, relays); + router.replace('/'); + }, [follows, id, pool, privkey, relays, router]); useEffect(() => { const fetchData = async () => { diff --git a/src/utils/bindings.ts b/src/utils/bindings.ts new file mode 100644 index 00000000..28b56dab --- /dev/null +++ b/src/utils/bindings.ts @@ -0,0 +1,26 @@ +// This file was generated by [tauri-specta](https://github.com/oscartbeaumont/tauri-specta). Do not edit this file manually. + +declare global { + interface Window { + __TAURI_INVOKE__(cmd: string, args?: Record): Promise; + } +} + +const invoke = window.__TAURI_INVOKE__; + +export function getAccount() { + return invoke('get_account'); +} + +export function createAccount(data: CreateAccountData) { + return invoke('create_account', { data }); +} + +export function createFollow(data: CreateFollowData) { + return invoke('create_follow', { data }); +} + +export type Account = { id: number; pubkey: string; privkey: string; active: boolean; metadata: string }; +export type Follow = { id: number; pubkey: string; kind: number; metadata: string; accountId: number }; +export type CreateFollowData = { pubkey: string; kind: number; metadata: string; account_id: number }; +export type CreateAccountData = { pubkey: string; privkey: string; metadata: string }; diff --git a/src/utils/transform.tsx b/src/utils/transform.tsx index 31dda620..ebc01fb9 100644 --- a/src/utils/transform.tsx +++ b/src/utils/transform.tsx @@ -9,6 +9,15 @@ export const tagsToArray = (arr) => { return newarr; }; +export const followsTag = (arr) => { + const newarr = []; + // push item to tags + arr.forEach((item) => { + arr.push(['p', item]); + }); + return newarr; +}; + export const pubkeyArray = (arr) => { const newarr = []; // push item to newarr