mirror of
https://github.com/luminous-devs/lume.git
synced 2024-09-19 11:43:30 +00:00
clean up & save onboarding process as state
This commit is contained in:
parent
d63690e9d0
commit
e6d8f084ae
@ -59,13 +59,11 @@
|
|||||||
"cheerio": "1.0.0-rc.12",
|
"cheerio": "1.0.0-rc.12",
|
||||||
"dayjs": "^1.11.9",
|
"dayjs": "^1.11.9",
|
||||||
"destr": "^1.2.2",
|
"destr": "^1.2.2",
|
||||||
"framer-motion": "^10.15.1",
|
|
||||||
"get-urls": "^11.0.0",
|
"get-urls": "^11.0.0",
|
||||||
"html-to-text": "^9.0.5",
|
"html-to-text": "^9.0.5",
|
||||||
"immer": "^10.0.2",
|
"immer": "^10.0.2",
|
||||||
"light-bolt11-decoder": "^3.0.0",
|
"light-bolt11-decoder": "^3.0.0",
|
||||||
"lru-cache": "^10.0.0",
|
"lru-cache": "^10.0.0",
|
||||||
"million": "2.5.4-beta.2",
|
|
||||||
"nostr-fetch": "^0.12.2",
|
"nostr-fetch": "^0.12.2",
|
||||||
"nostr-tools": "^1.14.0",
|
"nostr-tools": "^1.14.0",
|
||||||
"qrcode.react": "^3.1.0",
|
"qrcode.react": "^3.1.0",
|
||||||
@ -77,9 +75,8 @@
|
|||||||
"react-player": "^2.12.0",
|
"react-player": "^2.12.0",
|
||||||
"react-router-dom": "^6.14.2",
|
"react-router-dom": "^6.14.2",
|
||||||
"react-string-replace": "^1.1.1",
|
"react-string-replace": "^1.1.1",
|
||||||
"react-virtuoso": "^4.4.2",
|
"react-virtuoso": "^4.5.0",
|
||||||
"remark-gfm": "^3.0.1",
|
"remark-gfm": "^3.0.1",
|
||||||
"tauri-controls": "^0.0.5",
|
|
||||||
"tippy.js": "^6.3.7",
|
"tippy.js": "^6.3.7",
|
||||||
"zustand": "^4.4.1"
|
"zustand": "^4.4.1"
|
||||||
},
|
},
|
||||||
@ -89,7 +86,7 @@
|
|||||||
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
|
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
|
||||||
"@types/html-to-text": "^9.0.1",
|
"@types/html-to-text": "^9.0.1",
|
||||||
"@types/node": "^18.17.4",
|
"@types/node": "^18.17.4",
|
||||||
"@types/react": "^18.2.19",
|
"@types/react": "^18.2.20",
|
||||||
"@types/react-dom": "^18.2.7",
|
"@types/react-dom": "^18.2.7",
|
||||||
"@types/youtube-player": "^5.5.7",
|
"@types/youtube-player": "^5.5.7",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||||
|
644
pnpm-lock.yaml
644
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
51
src-tauri/Cargo.lock
generated
51
src-tauri/Cargo.lock
generated
@ -172,9 +172,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anstyle-wincon"
|
name = "anstyle-wincon"
|
||||||
version = "1.0.1"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188"
|
checksum = "c677ab05e09154296dd37acecd46420c17b9713e8366facafa8fc0885167cf4c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstyle",
|
"anstyle",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
@ -281,7 +281,7 @@ dependencies = [
|
|||||||
"polling",
|
"polling",
|
||||||
"rustix 0.37.23",
|
"rustix 0.37.23",
|
||||||
"slab",
|
"slab",
|
||||||
"socket2",
|
"socket2 0.4.9",
|
||||||
"waker-fn",
|
"waker-fn",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2211,7 +2211,7 @@ dependencies = [
|
|||||||
"httpdate",
|
"httpdate",
|
||||||
"itoa 1.0.9",
|
"itoa 1.0.9",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2 0.4.9",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
@ -2269,9 +2269,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "image"
|
name = "image"
|
||||||
version = "0.24.6"
|
version = "0.24.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "527909aa81e20ac3a44803521443a765550f09b5130c2c2fa1ea59c2f8f50a3a"
|
checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
@ -2677,8 +2677,6 @@ dependencies = [
|
|||||||
name = "lume"
|
name = "lume"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cocoa 0.24.1",
|
|
||||||
"objc",
|
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rust-argon2",
|
"rust-argon2",
|
||||||
"serde",
|
"serde",
|
||||||
@ -2703,7 +2701,6 @@ dependencies = [
|
|||||||
"tauri-plugin-updater",
|
"tauri-plugin-updater",
|
||||||
"tauri-plugin-upload",
|
"tauri-plugin-upload",
|
||||||
"tauri-plugin-window",
|
"tauri-plugin-window",
|
||||||
"window-shadows",
|
|
||||||
"window-vibrancy",
|
"window-vibrancy",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3388,9 +3385,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.11"
|
version = "0.2.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2c516611246607d0c04186886dbb3a754368ef82c79e9827a802c6d836dd111c"
|
checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-utils"
|
name = "pin-utils"
|
||||||
@ -4332,6 +4329,16 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2538b18701741680e0322a2302176d3253a35388e2e62f172f64f4f16605f877"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "soup3"
|
name = "soup3"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@ -5401,9 +5408,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tiff"
|
name = "tiff"
|
||||||
version = "0.8.1"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7449334f9ff2baf290d55d73983a7d6fa15e01198faef72af07e2a8db851e471"
|
checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"flate2",
|
"flate2",
|
||||||
"jpeg-decoder",
|
"jpeg-decoder",
|
||||||
@ -5455,18 +5462,17 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.29.1"
|
version = "1.30.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da"
|
checksum = "2d3ce25f50619af8b0aec2eb23deebe84249e19e2ddd393a6e16e3300a6dadfd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
"libc",
|
"libc",
|
||||||
"mio",
|
"mio",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2 0.5.3",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys 0.48.0",
|
"windows-sys 0.48.0",
|
||||||
]
|
]
|
||||||
@ -6042,17 +6048,6 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "window-shadows"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "git+https://github.com/tauri-apps/window-shadows?branch=dev#dd8ca283fa50b1484043e686fa0b1e45ba5a969d"
|
|
||||||
dependencies = [
|
|
||||||
"cocoa 0.25.0",
|
|
||||||
"objc",
|
|
||||||
"raw-window-handle",
|
|
||||||
"windows-sys 0.48.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "window-vibrancy"
|
name = "window-vibrancy"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
@ -36,7 +36,6 @@ tauri-plugin-os = { git = "https://github.com/tauri-apps/plugins-workspace", bra
|
|||||||
tauri-plugin-window = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
|
tauri-plugin-window = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
|
||||||
tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
|
tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
|
||||||
tauri-plugin-shell = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
|
tauri-plugin-shell = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v2" }
|
||||||
window-shadows = { git = "https://github.com/tauri-apps/window-shadows", branch = "dev" }
|
|
||||||
window-vibrancy = { git = "https://github.com/tauri-apps/window-vibrancy", branch = "dev" }
|
window-vibrancy = { git = "https://github.com/tauri-apps/window-vibrancy", branch = "dev" }
|
||||||
sqlx-cli = { version = "0.7.0", default-features = false, features = [
|
sqlx-cli = { version = "0.7.0", default-features = false, features = [
|
||||||
"sqlite",
|
"sqlite",
|
||||||
@ -49,10 +48,6 @@ git = "https://github.com/tauri-apps/plugins-workspace"
|
|||||||
branch = "v2"
|
branch = "v2"
|
||||||
features = ["sqlite"]
|
features = ["sqlite"]
|
||||||
|
|
||||||
[target.'cfg(target_os = "macos")'.dependencies]
|
|
||||||
objc = "0.2.7"
|
|
||||||
cocoa = "0.24.1"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
# by default Tauri runs in production mode
|
# by default Tauri runs in production mode
|
||||||
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
|
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
use tauri::Manager;
|
use tauri::Manager;
|
||||||
use tauri_plugin_autostart::MacosLauncher;
|
use tauri_plugin_autostart::MacosLauncher;
|
||||||
use tauri_plugin_sql::{Migration, MigrationKind};
|
use tauri_plugin_sql::{Migration, MigrationKind};
|
||||||
use window_shadows::set_shadow;
|
|
||||||
use window_vibrancy::{apply_mica, apply_vibrancy, NSVisualEffectMaterial};
|
use window_vibrancy::{apply_mica, apply_vibrancy, NSVisualEffectMaterial};
|
||||||
|
|
||||||
#[derive(Clone, serde::Serialize)]
|
#[derive(Clone, serde::Serialize)]
|
||||||
@ -164,9 +163,6 @@ fn main() {
|
|||||||
.setup(|app| {
|
.setup(|app| {
|
||||||
let window = app.get_window("main").unwrap();
|
let window = app.get_window("main").unwrap();
|
||||||
|
|
||||||
// native shadow
|
|
||||||
set_shadow(&window, true).expect("Unsupported platform!");
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, None)
|
apply_vibrancy(&window, NSVisualEffectMaterial::HudWindow, None, None)
|
||||||
.expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS");
|
.expect("Unsupported platform! 'apply_vibrancy' is only supported on macOS");
|
||||||
|
@ -111,7 +111,7 @@
|
|||||||
{
|
{
|
||||||
"width": 400,
|
"width": 400,
|
||||||
"height": 500,
|
"height": 500,
|
||||||
"decorations": true,
|
"decorations": false,
|
||||||
"hiddenTitle": true,
|
"hiddenTitle": true,
|
||||||
"center": true,
|
"center": true,
|
||||||
"resizable": false,
|
"resizable": false,
|
||||||
|
@ -40,6 +40,12 @@ const appLoader = async () => {
|
|||||||
const account = await getActiveAccount();
|
const account = await getActiveAccount();
|
||||||
const stronghold = sessionStorage.getItem('stronghold');
|
const stronghold = sessionStorage.getItem('stronghold');
|
||||||
const privkey = JSON.parse(stronghold).state.privkey || null;
|
const privkey = JSON.parse(stronghold).state.privkey || null;
|
||||||
|
const onboarding = localStorage.getItem('onboarding');
|
||||||
|
const step = JSON.parse(onboarding).state.step || null;
|
||||||
|
|
||||||
|
if (step) {
|
||||||
|
return redirect(step);
|
||||||
|
}
|
||||||
|
|
||||||
if (!account) {
|
if (!account) {
|
||||||
return redirect('/auth/welcome');
|
return redirect('/auth/welcome');
|
||||||
|
@ -1,6 +1,19 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { useOnboarding } from '@stores/onboarding';
|
||||||
|
import { useStronghold } from '@stores/stronghold';
|
||||||
|
|
||||||
export function AuthCreateScreen() {
|
export function AuthCreateScreen() {
|
||||||
|
const [step, tmpPrivkey] = useOnboarding((state) => [state.step, state.tempPrivkey]);
|
||||||
|
const setPrivkey = useStronghold((state) => state.setPrivkey);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (step) {
|
||||||
|
setPrivkey(tmpPrivkey);
|
||||||
|
}
|
||||||
|
}, [tmpPrivkey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { BaseDirectory, writeTextFile } from '@tauri-apps/plugin-fs';
|
import { BaseDirectory, writeTextFile } from '@tauri-apps/plugin-fs';
|
||||||
import { generatePrivateKey, getPublicKey, nip19 } from 'nostr-tools';
|
import { generatePrivateKey, getPublicKey, nip19 } from 'nostr-tools';
|
||||||
import { useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { createAccount } from '@libs/storage';
|
import { createAccount } from '@libs/storage';
|
||||||
@ -17,7 +17,9 @@ export function CreateStep1Screen() {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const setPrivkey = useStronghold((state) => state.setPrivkey);
|
const setPrivkey = useStronghold((state) => state.setPrivkey);
|
||||||
|
const setTempPrivkey = useOnboarding((state) => state.setTempPrivkey);
|
||||||
const setPubkey = useOnboarding((state) => state.setPubkey);
|
const setPubkey = useOnboarding((state) => state.setPubkey);
|
||||||
|
const setStep = useOnboarding((state) => state.setStep);
|
||||||
|
|
||||||
const [privkeyInput, setPrivkeyInput] = useState('password');
|
const [privkeyInput, setPrivkeyInput] = useState('password');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@ -61,8 +63,9 @@ export function CreateStep1Screen() {
|
|||||||
const submit = () => {
|
const submit = () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
setPubkey(pubkey);
|
|
||||||
setPrivkey(privkey);
|
setPrivkey(privkey);
|
||||||
|
setTempPrivkey(privkey); // only use if user close app and reopen it
|
||||||
|
setPubkey(pubkey);
|
||||||
|
|
||||||
account.mutate({
|
account.mutate({
|
||||||
npub,
|
npub,
|
||||||
@ -75,6 +78,11 @@ export function CreateStep1Screen() {
|
|||||||
setTimeout(() => navigate('/auth/create/step-2', { replace: true }), 1200);
|
setTimeout(() => navigate('/auth/create/step-2', { replace: true }), 1200);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// save current step, if user close app and reopen it
|
||||||
|
setStep('/auth/create/step-1');
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-md">
|
<div className="mx-auto w-full max-w-md">
|
||||||
<div className="mb-8 text-center">
|
<div className="mb-8 text-center">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Resolver, useForm } from 'react-hook-form';
|
import { Resolver, useForm } from 'react-hook-form';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
@ -30,13 +30,13 @@ const resolver: Resolver<FormValues> = async (values) => {
|
|||||||
|
|
||||||
export function CreateStep2Screen() {
|
export function CreateStep2Screen() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const setStep = useOnboarding((state) => state.setStep);
|
||||||
|
const pubkey = useOnboarding((state) => state.pubkey);
|
||||||
|
const privkey = useStronghold((state) => state.privkey);
|
||||||
|
|
||||||
const [passwordInput, setPasswordInput] = useState('password');
|
const [passwordInput, setPasswordInput] = useState('password');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const privkey = useStronghold((state) => state.privkey);
|
|
||||||
const pubkey = useOnboarding((state) => state.pubkey);
|
|
||||||
|
|
||||||
const { save } = useSecureStorage();
|
const { save } = useSecureStorage();
|
||||||
|
|
||||||
// toggle private key
|
// toggle private key
|
||||||
@ -72,6 +72,11 @@ export function CreateStep2Screen() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// save current step, if user close app and reopen it
|
||||||
|
setStep('/auth/create/step-2');
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-md">
|
<div className="mx-auto w-full max-w-md">
|
||||||
<div className="mb-8 text-center">
|
<div className="mb-8 text-center">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
@ -10,19 +10,20 @@ import { ArrowRightCircleIcon } from '@shared/icons/arrowRightCircle';
|
|||||||
import { Image } from '@shared/image';
|
import { Image } from '@shared/image';
|
||||||
|
|
||||||
import { DEFAULT_AVATAR } from '@stores/constants';
|
import { DEFAULT_AVATAR } from '@stores/constants';
|
||||||
|
import { useOnboarding } from '@stores/onboarding';
|
||||||
|
|
||||||
import { usePublish } from '@utils/hooks/usePublish';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
|
|
||||||
export function CreateStep3Screen() {
|
export function CreateStep3Screen() {
|
||||||
const { publish } = usePublish();
|
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const setStep = useOnboarding((state) => state.setStep);
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [picture, setPicture] = useState(DEFAULT_AVATAR);
|
const [picture, setPicture] = useState(DEFAULT_AVATAR);
|
||||||
const [banner, setBanner] = useState('');
|
const [banner, setBanner] = useState('');
|
||||||
|
|
||||||
|
const { publish } = useNostr();
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@ -57,6 +58,11 @@ export function CreateStep3Screen() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// save current step, if user close app and reopen it
|
||||||
|
setStep('/auth/create/step-3');
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-md">
|
<div className="mx-auto w-full max-w-md">
|
||||||
<div className="mb-8 text-center">
|
<div className="mb-8 text-center">
|
||||||
|
@ -1,6 +1,19 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { useOnboarding } from '@stores/onboarding';
|
||||||
|
import { useStronghold } from '@stores/stronghold';
|
||||||
|
|
||||||
export function AuthImportScreen() {
|
export function AuthImportScreen() {
|
||||||
|
const [step, tmpPrivkey] = useOnboarding((state) => [state.step, state.tempPrivkey]);
|
||||||
|
const setPrivkey = useStronghold((state) => state.setPrivkey);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (step) {
|
||||||
|
setPrivkey(tmpPrivkey);
|
||||||
|
}
|
||||||
|
}, [tmpPrivkey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { getPublicKey, nip19 } from 'nostr-tools';
|
import { getPublicKey, nip19 } from 'nostr-tools';
|
||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Resolver, useForm } from 'react-hook-form';
|
import { Resolver, useForm } from 'react-hook-form';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
@ -34,7 +34,9 @@ export function ImportStep1Screen() {
|
|||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const setPrivkey = useStronghold((state) => state.setPrivkey);
|
const setPrivkey = useStronghold((state) => state.setPrivkey);
|
||||||
|
const setTempPubkey = useOnboarding((state) => state.setTempPrivkey);
|
||||||
const setPubkey = useOnboarding((state) => state.setPubkey);
|
const setPubkey = useOnboarding((state) => state.setPubkey);
|
||||||
|
const setStep = useOnboarding((state) => state.setStep);
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
@ -72,10 +74,9 @@ export function ImportStep1Screen() {
|
|||||||
const pubkey = getPublicKey(privkey);
|
const pubkey = getPublicKey(privkey);
|
||||||
const npub = nip19.npubEncode(pubkey);
|
const npub = nip19.npubEncode(pubkey);
|
||||||
|
|
||||||
// use for onboarding process only
|
|
||||||
setPubkey(pubkey);
|
|
||||||
// add stronghold state
|
|
||||||
setPrivkey(privkey);
|
setPrivkey(privkey);
|
||||||
|
setTempPubkey(privkey); // only use if user close app and reopen it
|
||||||
|
setPubkey(pubkey);
|
||||||
|
|
||||||
// add account to local database
|
// add account to local database
|
||||||
account.mutate({
|
account.mutate({
|
||||||
@ -91,11 +92,16 @@ export function ImportStep1Screen() {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
setError('privkey', {
|
setError('privkey', {
|
||||||
type: 'custom',
|
type: 'custom',
|
||||||
message: 'Private Key is invalid, please check again',
|
message: 'Private key is invalid, please check again',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// save current step, if user close app and reopen it
|
||||||
|
setStep('/auth/import/step-1');
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-md">
|
<div className="mx-auto w-full max-w-md">
|
||||||
<div className="mb-8 text-center">
|
<div className="mb-8 text-center">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Resolver, useForm } from 'react-hook-form';
|
import { Resolver, useForm } from 'react-hook-form';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
@ -30,13 +30,13 @@ const resolver: Resolver<FormValues> = async (values) => {
|
|||||||
|
|
||||||
export function ImportStep2Screen() {
|
export function ImportStep2Screen() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const setStep = useOnboarding((state) => state.setStep);
|
||||||
|
const pubkey = useOnboarding((state) => state.pubkey);
|
||||||
|
const privkey = useStronghold((state) => state.privkey);
|
||||||
|
|
||||||
const [passwordInput, setPasswordInput] = useState('password');
|
const [passwordInput, setPasswordInput] = useState('password');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
const privkey = useStronghold((state) => state.privkey);
|
|
||||||
const pubkey = useOnboarding((state) => state.pubkey);
|
|
||||||
|
|
||||||
const { save } = useSecureStorage();
|
const { save } = useSecureStorage();
|
||||||
|
|
||||||
// toggle private key
|
// toggle private key
|
||||||
@ -72,6 +72,11 @@ export function ImportStep2Screen() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// save current step, if user close app and reopen it
|
||||||
|
setStep('/auth/import/step-2');
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-md">
|
<div className="mx-auto w-full max-w-md">
|
||||||
<div className="mb-8 text-center">
|
<div className="mb-8 text-center">
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useQueryClient } from '@tanstack/react-query';
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { User } from '@app/auth/components/user';
|
import { User } from '@app/auth/components/user';
|
||||||
@ -8,12 +8,15 @@ import { updateLastLogin } from '@libs/storage';
|
|||||||
|
|
||||||
import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons';
|
import { ArrowRightCircleIcon, LoaderIcon } from '@shared/icons';
|
||||||
|
|
||||||
|
import { useOnboarding } from '@stores/onboarding';
|
||||||
|
|
||||||
import { useAccount } from '@utils/hooks/useAccount';
|
import { useAccount } from '@utils/hooks/useAccount';
|
||||||
import { useNostr } from '@utils/hooks/useNostr';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
|
|
||||||
export function ImportStep3Screen() {
|
export function ImportStep3Screen() {
|
||||||
const navigate = useNavigate();
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const setStep = useOnboarding((state) => state.setStep);
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
@ -39,6 +42,11 @@ export function ImportStep3Screen() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// save current step, if user close app and reopen it
|
||||||
|
setStep('/auth/import/step-3');
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-md">
|
<div className="mx-auto w-full max-w-md">
|
||||||
<div className="mb-8 text-center">
|
<div className="mb-8 text-center">
|
||||||
|
@ -1,6 +1,19 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { useOnboarding } from '@stores/onboarding';
|
||||||
|
import { useStronghold } from '@stores/stronghold';
|
||||||
|
|
||||||
export function OnboardingScreen() {
|
export function OnboardingScreen() {
|
||||||
|
const [step, tmpPrivkey] = useOnboarding((state) => [state.step, state.tempPrivkey]);
|
||||||
|
const setPrivkey = useStronghold((state) => state.setPrivkey);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (step) {
|
||||||
|
setPrivkey(tmpPrivkey);
|
||||||
|
}
|
||||||
|
}, [tmpPrivkey]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full w-full items-center justify-center">
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Link, useNavigate } from 'react-router-dom';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { User } from '@app/auth/components/user';
|
import { User } from '@app/auth/components/user';
|
||||||
@ -8,17 +8,18 @@ import { updateAccount } from '@libs/storage';
|
|||||||
|
|
||||||
import { ArrowRightCircleIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons';
|
import { ArrowRightCircleIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons';
|
||||||
|
|
||||||
|
import { useOnboarding } from '@stores/onboarding';
|
||||||
|
|
||||||
import { useAccount } from '@utils/hooks/useAccount';
|
import { useAccount } from '@utils/hooks/useAccount';
|
||||||
import { useNostr } from '@utils/hooks/useNostr';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
import { usePublish } from '@utils/hooks/usePublish';
|
|
||||||
import { arrayToNIP02 } from '@utils/transform';
|
import { arrayToNIP02 } from '@utils/transform';
|
||||||
|
|
||||||
export function OnboardStep1Screen() {
|
export function OnboardStep1Screen() {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const setStep = useOnboarding((state) => state.setStep);
|
||||||
|
|
||||||
const { publish } = usePublish();
|
const { publish, fetchNotes } = useNostr();
|
||||||
const { fetchNotes } = useNostr();
|
|
||||||
const { account } = useAccount();
|
const { account } = useAccount();
|
||||||
const { status, data } = useQuery(['trending-profiles'], async () => {
|
const { status, data } = useQuery(['trending-profiles'], async () => {
|
||||||
const res = await fetch('https://api.nostr.band/v0/trending/profiles');
|
const res = await fetch('https://api.nostr.band/v0/trending/profiles');
|
||||||
@ -62,6 +63,11 @@ export function OnboardStep1Screen() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// save current step, if user close app and reopen it
|
||||||
|
setStep('/auth/onboarding');
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-md">
|
<div className="mx-auto w-full max-w-md">
|
||||||
<div className="mb-8 text-center">
|
<div className="mb-8 text-center">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { Link, useNavigate } from 'react-router-dom';
|
import { Link, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
import { createBlock } from '@libs/storage';
|
import { createBlock } from '@libs/storage';
|
||||||
@ -6,6 +6,7 @@ import { createBlock } from '@libs/storage';
|
|||||||
import { ArrowRightCircleIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons';
|
import { ArrowRightCircleIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons';
|
||||||
|
|
||||||
import { BLOCK_KINDS } from '@stores/constants';
|
import { BLOCK_KINDS } from '@stores/constants';
|
||||||
|
import { useOnboarding } from '@stores/onboarding';
|
||||||
|
|
||||||
const data = [
|
const data = [
|
||||||
{ hashtag: '#bitcoin' },
|
{ hashtag: '#bitcoin' },
|
||||||
@ -27,6 +28,7 @@ const data = [
|
|||||||
|
|
||||||
export function OnboardStep2Screen() {
|
export function OnboardStep2Screen() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const setStep = useOnboarding((state) => state.setStep);
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [tags, setTags] = useState(new Set<string>());
|
const [tags, setTags] = useState(new Set<string>());
|
||||||
@ -57,6 +59,11 @@ export function OnboardStep2Screen() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// save current step, if user close app and reopen it
|
||||||
|
setStep('/auth/onboarding/step-2');
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-md">
|
<div className="mx-auto w-full max-w-md">
|
||||||
<div className="mb-8 text-center">
|
<div className="mb-8 text-center">
|
||||||
|
@ -10,17 +10,19 @@ import { createRelay } from '@libs/storage';
|
|||||||
import { ArrowRightCircleIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons';
|
import { ArrowRightCircleIcon, CheckCircleIcon, LoaderIcon } from '@shared/icons';
|
||||||
|
|
||||||
import { FULL_RELAYS } from '@stores/constants';
|
import { FULL_RELAYS } from '@stores/constants';
|
||||||
|
import { useOnboarding } from '@stores/onboarding';
|
||||||
|
|
||||||
import { useAccount } from '@utils/hooks/useAccount';
|
import { useAccount } from '@utils/hooks/useAccount';
|
||||||
import { usePublish } from '@utils/hooks/usePublish';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
|
|
||||||
export function OnboardStep3Screen() {
|
export function OnboardStep3Screen() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const [setStep, clearStep] = useOnboarding((state) => [state.setStep, state.clearStep]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const [relays, setRelays] = useState(new Set<string>());
|
const [relays, setRelays] = useState(new Set<string>());
|
||||||
|
|
||||||
const { publish } = usePublish();
|
const { publish } = useNostr();
|
||||||
const { account } = useAccount();
|
const { account } = useAccount();
|
||||||
const { fetcher, relayUrls } = useNDK();
|
const { fetcher, relayUrls } = useNDK();
|
||||||
const { status, data } = useQuery(
|
const { status, data } = useQuery(
|
||||||
@ -48,6 +50,9 @@ export function OnboardStep3Screen() {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// save current step, if user close app and reopen it
|
||||||
|
setStep('/auth/onboarding/step-3');
|
||||||
|
|
||||||
const toggleRelay = (relay: string) => {
|
const toggleRelay = (relay: string) => {
|
||||||
if (relays.has(relay)) {
|
if (relays.has(relay)) {
|
||||||
setRelays((prev) => {
|
setRelays((prev) => {
|
||||||
@ -76,6 +81,7 @@ export function OnboardStep3Screen() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
clearStep();
|
||||||
navigate('/', { replace: true });
|
navigate('/', { replace: true });
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -9,7 +9,9 @@ export function WelcomeScreen() {
|
|||||||
async function setWindow() {
|
async function setWindow() {
|
||||||
await appWindow.setSize(new LogicalSize(400, 500));
|
await appWindow.setSize(new LogicalSize(400, 500));
|
||||||
await appWindow.setResizable(false);
|
await appWindow.setResizable(false);
|
||||||
|
await appWindow.center();
|
||||||
}
|
}
|
||||||
|
|
||||||
setWindow();
|
setWindow();
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
|
@ -4,7 +4,7 @@ import { useCallback, useState } from 'react';
|
|||||||
import { EnterIcon } from '@shared/icons';
|
import { EnterIcon } from '@shared/icons';
|
||||||
import { MediaUploader } from '@shared/mediaUploader';
|
import { MediaUploader } from '@shared/mediaUploader';
|
||||||
|
|
||||||
import { usePublish } from '@utils/hooks/usePublish';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
|
|
||||||
export function ChatMessageForm({
|
export function ChatMessageForm({
|
||||||
receiverPubkey,
|
receiverPubkey,
|
||||||
@ -14,7 +14,7 @@ export function ChatMessageForm({
|
|||||||
userPubkey: string;
|
userPubkey: string;
|
||||||
userPrivkey: string;
|
userPrivkey: string;
|
||||||
}) {
|
}) {
|
||||||
const { publish } = usePublish();
|
const { publish } = useNostr();
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
|
|
||||||
const encryptMessage = useCallback(async () => {
|
const encryptMessage = useCallback(async () => {
|
||||||
|
@ -12,14 +12,14 @@ import { Image } from '@shared/image';
|
|||||||
import { BLOCK_KINDS, DEFAULT_AVATAR } from '@stores/constants';
|
import { BLOCK_KINDS, DEFAULT_AVATAR } from '@stores/constants';
|
||||||
import { ADD_IMAGEBLOCK_SHORTCUT } from '@stores/shortcuts';
|
import { ADD_IMAGEBLOCK_SHORTCUT } from '@stores/shortcuts';
|
||||||
|
|
||||||
import { usePublish } from '@utils/hooks/usePublish';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
import { useImageUploader } from '@utils/hooks/useUploader';
|
import { useImageUploader } from '@utils/hooks/useUploader';
|
||||||
|
|
||||||
export function ImageModal() {
|
export function ImageModal() {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const upload = useImageUploader();
|
const upload = useImageUploader();
|
||||||
|
|
||||||
const { publish } = usePublish();
|
const { publish } = useNostr();
|
||||||
|
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
@ -4,8 +4,6 @@ import { useEffect, useRef } from 'react';
|
|||||||
import { useNDK } from '@libs/ndk/provider';
|
import { useNDK } from '@libs/ndk/provider';
|
||||||
import { createNote } from '@libs/storage';
|
import { createNote } from '@libs/storage';
|
||||||
|
|
||||||
import { useNote } from '@stores/note';
|
|
||||||
|
|
||||||
import { useAccount } from '@utils/hooks/useAccount';
|
import { useAccount } from '@utils/hooks/useAccount';
|
||||||
|
|
||||||
export function useNewsfeed() {
|
export function useNewsfeed() {
|
||||||
@ -15,8 +13,6 @@ export function useNewsfeed() {
|
|||||||
const { ndk } = useNDK();
|
const { ndk } = useNDK();
|
||||||
const { status, account } = useAccount();
|
const { status, account } = useAccount();
|
||||||
|
|
||||||
const toggleHasNewNote = useNote((state) => state.toggleHasNewNote);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (status === 'success' && account) {
|
if (status === 'success' && account) {
|
||||||
const filter: NDKFilter = {
|
const filter: NDKFilter = {
|
||||||
@ -37,8 +33,6 @@ export function useNewsfeed() {
|
|||||||
event.content,
|
event.content,
|
||||||
event.created_at
|
event.created_at
|
||||||
);
|
);
|
||||||
// notify user about created note
|
|
||||||
toggleHasNewNote(true);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { invoke } from '@tauri-apps/api/tauri';
|
import { invoke } from '@tauri-apps/api/tauri';
|
||||||
import { platform } from '@tauri-apps/plugin-os';
|
import { useEffect, useState } from 'react';
|
||||||
import { appWindow } from '@tauri-apps/plugin-window';
|
|
||||||
import { useEffect } from 'react';
|
|
||||||
|
|
||||||
import { getActiveAccount, updateLastLogin } from '@libs/storage';
|
import { getActiveAccount, updateLastLogin } from '@libs/storage';
|
||||||
|
|
||||||
@ -10,47 +8,76 @@ import { LoaderIcon } from '@shared/icons';
|
|||||||
import { useNostr } from '@utils/hooks/useNostr';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
|
|
||||||
const account = await getActiveAccount();
|
const account = await getActiveAccount();
|
||||||
const osPlatform = await platform();
|
|
||||||
|
|
||||||
if (osPlatform !== 'macos') {
|
|
||||||
appWindow.setDecorations(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SplashScreen() {
|
export function SplashScreen() {
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
const { fetchChats, fetchNotes } = useNostr();
|
const { fetchChats, fetchNotes } = useNostr();
|
||||||
|
|
||||||
|
if (!account) {
|
||||||
|
setTimeout(async () => await invoke('close_splashscreen'), 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
const skip = async () => {
|
||||||
|
await invoke('close_splashscreen');
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function prefetch() {
|
async function prefetch() {
|
||||||
|
const onboarding = localStorage.getItem('onboarding');
|
||||||
|
const step = JSON.parse(onboarding).state.step || null;
|
||||||
|
if (step) await invoke('close_splashscreen');
|
||||||
|
|
||||||
const notes = await fetchNotes();
|
const notes = await fetchNotes();
|
||||||
const chats = await fetchChats();
|
const chats = await fetchChats();
|
||||||
if (notes && chats) {
|
|
||||||
|
if (notes.status === 'ok' && chats.status === 'ok') {
|
||||||
const now = Math.floor(Date.now() / 1000);
|
const now = Math.floor(Date.now() / 1000);
|
||||||
await updateLastLogin(now);
|
await updateLastLogin(now);
|
||||||
invoke('close_splashscreen');
|
invoke('close_splashscreen');
|
||||||
|
} else {
|
||||||
|
setLoading(false);
|
||||||
|
console.log('fetch notes failed, error: ', notes.message);
|
||||||
|
console.log('fetch chats failed, error: ', chats.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (account) {
|
if (account && loading) {
|
||||||
prefetch();
|
prefetch();
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!account) {
|
|
||||||
setTimeout(() => invoke('close_splashscreen'), 1000);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative flex h-screen w-screen items-center justify-center bg-black">
|
<div className="relative flex h-screen w-screen items-center justify-center bg-black">
|
||||||
<div data-tauri-drag-region className="absolute left-0 top-0 z-10 h-11 w-full" />
|
<div data-tauri-drag-region className="absolute left-0 top-0 z-10 h-11 w-full" />
|
||||||
<div className="flex min-h-0 w-full flex-1 items-center justify-center">
|
<div className="flex min-h-0 w-full flex-1 items-center justify-center">
|
||||||
<div className="flex flex-col items-center justify-center gap-4">
|
<div className="flex flex-col items-center justify-center gap-4">
|
||||||
<LoaderIcon className="h-6 w-6 animate-spin text-white" />
|
<LoaderIcon className="h-6 w-6 animate-spin text-white" />
|
||||||
<div className="flex flex-col gap-1 text-center">
|
{loading ? (
|
||||||
<h3 className="font-semibold leading-none text-white">Prefetching data</h3>
|
<div className="mt-2 flex flex-col gap-1 text-center">
|
||||||
<p className="text-sm leading-none text-white/50">
|
<h3 className="text-lg font-semibold leading-none text-white">
|
||||||
This may take a few seconds, please don't close app.
|
Prefetching data
|
||||||
</p>
|
</h3>
|
||||||
</div>
|
<p className="text-white/50">
|
||||||
|
This may take a few seconds, please don't close app.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="mt-2 flex flex-col gap-1 text-center">
|
||||||
|
<h3 className="text-lg font-semibold leading-none text-white">
|
||||||
|
Something wrong!
|
||||||
|
</h3>
|
||||||
|
<p className="text-white/50">
|
||||||
|
Prefetching process failed, click skip to continue.
|
||||||
|
</p>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={skip}
|
||||||
|
className="mx-auto mt-4 inline-flex h-10 w-max items-center justify-center rounded-md bg-white/10 px-8 text-sm font-medium leading-none text-white backdrop-blur-xl hover:bg-white/20"
|
||||||
|
>
|
||||||
|
Skip
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// source: https://github.com/nostr-dev-kit/ndk-react/
|
// inspire by: https://github.com/nostr-dev-kit/ndk-react/
|
||||||
import NDK from '@nostr-dev-kit/ndk';
|
import NDK from '@nostr-dev-kit/ndk';
|
||||||
import { ndkAdapter } from '@nostr-fetch/adapter-ndk';
|
import { ndkAdapter } from '@nostr-fetch/adapter-ndk';
|
||||||
import { NostrFetcher } from 'nostr-fetch';
|
import { NostrFetcher } from 'nostr-fetch';
|
||||||
@ -10,21 +10,16 @@ import { getExplicitRelayUrls } from '@libs/storage';
|
|||||||
import { FULL_RELAYS } from '@stores/constants';
|
import { FULL_RELAYS } from '@stores/constants';
|
||||||
|
|
||||||
export const NDKInstance = () => {
|
export const NDKInstance = () => {
|
||||||
const cacheAdapter = useMemo(() => new TauriAdapter(), []);
|
|
||||||
|
|
||||||
const [ndk, setNDK] = useState<NDK | undefined>(undefined);
|
const [ndk, setNDK] = useState<NDK | undefined>(undefined);
|
||||||
const [relayUrls, setRelayUrls] = useState<string[]>([]);
|
const [relayUrls, setRelayUrls] = useState<string[]>([]);
|
||||||
const [fetcher, setFetcher] = useState<NostrFetcher>(undefined);
|
|
||||||
|
|
||||||
useEffect(() => {
|
const cacheAdapter = useMemo(() => new TauriAdapter(), []);
|
||||||
if (!ndk) loadNdk();
|
const fetcher = useMemo<NostrFetcher>(
|
||||||
|
() => NostrFetcher.withCustomPool(ndkAdapter(ndk)),
|
||||||
|
[ndk]
|
||||||
|
);
|
||||||
|
|
||||||
return () => {
|
async function initNDK() {
|
||||||
cacheAdapter.save();
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
async function loadNdk() {
|
|
||||||
let explicitRelayUrls: string[];
|
let explicitRelayUrls: string[];
|
||||||
const explicitRelayUrlsFromDB = await getExplicitRelayUrls();
|
const explicitRelayUrlsFromDB = await getExplicitRelayUrls();
|
||||||
|
|
||||||
@ -34,23 +29,29 @@ export const NDKInstance = () => {
|
|||||||
explicitRelayUrls = FULL_RELAYS;
|
explicitRelayUrls = FULL_RELAYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ndkInstance = new NDK({ explicitRelayUrls, cacheAdapter });
|
const instance = new NDK({ explicitRelayUrls, cacheAdapter });
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await ndkInstance.connect();
|
await instance.connect();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('ERROR loading NDK NDKInstance', error);
|
console.error('NDK instance init failed: ', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
setNDK(ndkInstance);
|
setNDK(instance);
|
||||||
setRelayUrls(explicitRelayUrls);
|
setRelayUrls(explicitRelayUrls);
|
||||||
setFetcher(NostrFetcher.withCustomPool(ndkAdapter(ndkInstance)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ndk) initNDK();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cacheAdapter.save();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ndk,
|
ndk,
|
||||||
relayUrls,
|
relayUrls,
|
||||||
fetcher,
|
fetcher,
|
||||||
loadNdk,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -9,18 +9,16 @@ interface NDKContext {
|
|||||||
ndk: NDK;
|
ndk: NDK;
|
||||||
relayUrls: string[];
|
relayUrls: string[];
|
||||||
fetcher: NostrFetcher;
|
fetcher: NostrFetcher;
|
||||||
loadNdk: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const NDKContext = createContext<NDKContext>({
|
const NDKContext = createContext<NDKContext>({
|
||||||
ndk: new NDK({}),
|
ndk: new NDK({}),
|
||||||
relayUrls: [],
|
relayUrls: [],
|
||||||
fetcher: undefined,
|
fetcher: undefined,
|
||||||
loadNdk: undefined,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const NDKProvider = ({ children }: PropsWithChildren<object>) => {
|
const NDKProvider = ({ children }: PropsWithChildren<object>) => {
|
||||||
const { ndk, relayUrls, fetcher, loadNdk } = NDKInstance();
|
const { ndk, relayUrls, fetcher } = NDKInstance();
|
||||||
|
|
||||||
if (ndk)
|
if (ndk)
|
||||||
return (
|
return (
|
||||||
@ -29,7 +27,6 @@ const NDKProvider = ({ children }: PropsWithChildren<object>) => {
|
|||||||
ndk,
|
ndk,
|
||||||
relayUrls,
|
relayUrls,
|
||||||
fetcher,
|
fetcher,
|
||||||
loadNdk,
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
@ -15,12 +15,12 @@ import { MentionNote } from '@shared/notes';
|
|||||||
|
|
||||||
import { useComposer } from '@stores/composer';
|
import { useComposer } from '@stores/composer';
|
||||||
|
|
||||||
import { usePublish } from '@utils/hooks/usePublish';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
import { useImageUploader } from '@utils/hooks/useUploader';
|
import { useImageUploader } from '@utils/hooks/useUploader';
|
||||||
import { sendNativeNotification } from '@utils/notification';
|
import { sendNativeNotification } from '@utils/notification';
|
||||||
|
|
||||||
export function Composer() {
|
export function Composer() {
|
||||||
const { publish } = usePublish();
|
const { publish } = useNostr();
|
||||||
|
|
||||||
const [status, setStatus] = useState<null | 'loading' | 'done'>(null);
|
const [status, setStatus] = useState<null | 'loading' | 'done'>(null);
|
||||||
const [reply, clearReply] = useComposer((state) => [state.reply, state.clearReply]);
|
const [reply, clearReply] = useComposer((state) => [state.reply, state.clearReply]);
|
||||||
|
@ -13,7 +13,7 @@ import { Image } from '@shared/image';
|
|||||||
import { DEFAULT_AVATAR } from '@stores/constants';
|
import { DEFAULT_AVATAR } from '@stores/constants';
|
||||||
|
|
||||||
import { useAccount } from '@utils/hooks/useAccount';
|
import { useAccount } from '@utils/hooks/useAccount';
|
||||||
import { usePublish } from '@utils/hooks/usePublish';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
|
|
||||||
export function EditProfileModal() {
|
export function EditProfileModal() {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
@ -24,7 +24,7 @@ export function EditProfileModal() {
|
|||||||
const [banner, setBanner] = useState('');
|
const [banner, setBanner] = useState('');
|
||||||
const [nip05, setNIP05] = useState({ verified: false, text: '' });
|
const [nip05, setNIP05] = useState({ verified: false, text: '' });
|
||||||
|
|
||||||
const { publish } = usePublish();
|
const { publish } = useNostr();
|
||||||
const { account } = useAccount();
|
const { account } = useAccount();
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@ -65,7 +65,6 @@ export function EditProfileModal() {
|
|||||||
|
|
||||||
const res: any = await fetch(verifyURL, {
|
const res: any = await fetch(verifyURL, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
timeout: 30,
|
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json; charset=utf-8',
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
},
|
},
|
||||||
|
@ -3,7 +3,7 @@ import { useState } from 'react';
|
|||||||
|
|
||||||
import { ReactionIcon } from '@shared/icons';
|
import { ReactionIcon } from '@shared/icons';
|
||||||
|
|
||||||
import { usePublish } from '@utils/hooks/usePublish';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
|
|
||||||
const REACTIONS = [
|
const REACTIONS = [
|
||||||
{
|
{
|
||||||
@ -32,7 +32,7 @@ export function NoteReaction({ id, pubkey }: { id: string; pubkey: string }) {
|
|||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [reaction, setReaction] = useState<string | null>(null);
|
const [reaction, setReaction] = useState<string | null>(null);
|
||||||
|
|
||||||
const { publish } = usePublish();
|
const { publish } = useNostr();
|
||||||
|
|
||||||
const getReactionImage = (content: string) => {
|
const getReactionImage = (content: string) => {
|
||||||
const reaction: { img: string } = REACTIONS.find((el) => el.content === content);
|
const reaction: { img: string } = REACTIONS.find((el) => el.content === content);
|
||||||
|
@ -2,16 +2,14 @@ import * as Tooltip from '@radix-ui/react-tooltip';
|
|||||||
|
|
||||||
import { RepostIcon } from '@shared/icons';
|
import { RepostIcon } from '@shared/icons';
|
||||||
|
|
||||||
import { FULL_RELAYS } from '@stores/constants';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
|
|
||||||
import { usePublish } from '@utils/hooks/usePublish';
|
|
||||||
|
|
||||||
export function NoteRepost({ id, pubkey }: { id: string; pubkey: string }) {
|
export function NoteRepost({ id, pubkey }: { id: string; pubkey: string }) {
|
||||||
const { publish } = usePublish();
|
const { publish } = useNostr();
|
||||||
|
|
||||||
const submit = async () => {
|
const submit = async () => {
|
||||||
const tags = [
|
const tags = [
|
||||||
['e', id, FULL_RELAYS[0], 'root'],
|
['e', id, 'wss://relayable.org', 'root'],
|
||||||
['p', pubkey],
|
['p', pubkey],
|
||||||
];
|
];
|
||||||
await publish({ content: '', kind: 6, tags: tags });
|
await publish({ content: '', kind: 6, tags: tags });
|
||||||
|
@ -8,10 +8,10 @@ import { Button } from '@shared/button';
|
|||||||
import { CancelIcon, ZapIcon } from '@shared/icons';
|
import { CancelIcon, ZapIcon } from '@shared/icons';
|
||||||
|
|
||||||
import { useEvent } from '@utils/hooks/useEvent';
|
import { useEvent } from '@utils/hooks/useEvent';
|
||||||
import { usePublish } from '@utils/hooks/usePublish';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
|
|
||||||
export function NoteZap({ id }: { id: string }) {
|
export function NoteZap({ id }: { id: string }) {
|
||||||
const { createZap } = usePublish();
|
const { createZap } = useNostr();
|
||||||
const { data: event } = useEvent(id);
|
const { data: event } = useEvent(id);
|
||||||
|
|
||||||
const [amount, setAmount] = useState<null | number>(null);
|
const [amount, setAmount] = useState<null | number>(null);
|
||||||
@ -23,6 +23,7 @@ export function NoteZap({ id }: { id: string }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const createZapRequest = async () => {
|
const createZapRequest = async () => {
|
||||||
|
// @ts-expect-error, todo: fix this
|
||||||
const res = await createZap(event as unknown as NostrEvent, amount);
|
const res = await createZap(event as unknown as NostrEvent, amount);
|
||||||
if (res) setInvoice(res);
|
if (res) setInvoice(res);
|
||||||
};
|
};
|
||||||
|
@ -5,12 +5,12 @@ import { Image } from '@shared/image';
|
|||||||
|
|
||||||
import { DEFAULT_AVATAR, FULL_RELAYS } from '@stores/constants';
|
import { DEFAULT_AVATAR, FULL_RELAYS } from '@stores/constants';
|
||||||
|
|
||||||
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
import { useProfile } from '@utils/hooks/useProfile';
|
import { useProfile } from '@utils/hooks/useProfile';
|
||||||
import { usePublish } from '@utils/hooks/usePublish';
|
|
||||||
import { displayNpub } from '@utils/shortenKey';
|
import { displayNpub } from '@utils/shortenKey';
|
||||||
|
|
||||||
export function NoteReplyForm({ id, pubkey }: { id: string; pubkey: string }) {
|
export function NoteReplyForm({ id, pubkey }: { id: string; pubkey: string }) {
|
||||||
const { publish } = usePublish();
|
const { publish } = useNostr();
|
||||||
const { status, user } = useProfile(pubkey);
|
const { status, user } = useProfile(pubkey);
|
||||||
|
|
||||||
const [value, setValue] = useState('');
|
const [value, setValue] = useState('');
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
import { create } from 'zustand';
|
|
||||||
|
|
||||||
interface NoteState {
|
|
||||||
hasNewNote: boolean;
|
|
||||||
toggleHasNewNote: (status: boolean) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useNote = create<NoteState>((set) => ({
|
|
||||||
hasNewNote: false,
|
|
||||||
toggleHasNewNote: (status: boolean) => {
|
|
||||||
set({ hasNewNote: status });
|
|
||||||
},
|
|
||||||
}));
|
|
@ -1,20 +1,38 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
|
import { createJSONStorage, persist } from 'zustand/middleware';
|
||||||
|
|
||||||
interface OnboardingState {
|
interface OnboardingState {
|
||||||
profile: { [x: string]: string };
|
step: null | string;
|
||||||
pubkey: string;
|
pubkey: null | string;
|
||||||
createProfile: (data: { [x: string]: string }) => void;
|
tempPrivkey: null | string;
|
||||||
setPubkey: (pubkey: string) => void;
|
setPubkey: (pubkey: string) => void;
|
||||||
|
setTempPrivkey: (privkey: string) => void;
|
||||||
|
setStep: (url: string) => void;
|
||||||
|
clearStep: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useOnboarding = create<OnboardingState>((set) => ({
|
export const useOnboarding = create<OnboardingState>()(
|
||||||
profile: {},
|
persist(
|
||||||
pubkey: '',
|
(set) => ({
|
||||||
privkey: '',
|
step: null,
|
||||||
createProfile: (data: { [x: string]: string }) => {
|
pubkey: null,
|
||||||
set({ profile: data });
|
tempPrivkey: null,
|
||||||
},
|
setPubkey: (pubkey: string) => {
|
||||||
setPubkey: (pubkey: string) => {
|
set({ pubkey });
|
||||||
set({ pubkey: pubkey });
|
},
|
||||||
},
|
setTempPrivkey: (privkey: string) => {
|
||||||
}));
|
set({ tempPrivkey: privkey });
|
||||||
|
},
|
||||||
|
setStep: (url: string) => {
|
||||||
|
set({ step: url });
|
||||||
|
},
|
||||||
|
clearStep: () => {
|
||||||
|
set({ step: null, pubkey: null, tempPrivkey: null });
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: 'onboarding',
|
||||||
|
storage: createJSONStorage(() => localStorage),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { NDKUser } from '@nostr-dev-kit/ndk';
|
import { NDKEvent, NDKKind, NDKPrivateKeySigner, NDKUser } from '@nostr-dev-kit/ndk';
|
||||||
|
import destr from 'destr';
|
||||||
import { LRUCache } from 'lru-cache';
|
import { LRUCache } from 'lru-cache';
|
||||||
import { NostrEvent } from 'nostr-fetch';
|
import { NostrEvent } from 'nostr-fetch';
|
||||||
import { nip19 } from 'nostr-tools';
|
import { nip19 } from 'nostr-tools';
|
||||||
@ -8,18 +9,22 @@ import {
|
|||||||
countTotalNotes,
|
countTotalNotes,
|
||||||
createChat,
|
createChat,
|
||||||
createNote,
|
createNote,
|
||||||
getActiveAccount,
|
|
||||||
getLastLogin,
|
getLastLogin,
|
||||||
updateAccount,
|
updateAccount,
|
||||||
} from '@libs/storage';
|
} from '@libs/storage';
|
||||||
|
|
||||||
|
import { useStronghold } from '@stores/stronghold';
|
||||||
|
|
||||||
import { nHoursAgo } from '@utils/date';
|
import { nHoursAgo } from '@utils/date';
|
||||||
|
import { useAccount } from '@utils/hooks/useAccount';
|
||||||
|
|
||||||
export function useNostr() {
|
export function useNostr() {
|
||||||
|
const privkey = useStronghold((state) => state.privkey);
|
||||||
|
|
||||||
const { ndk, relayUrls, fetcher } = useNDK();
|
const { ndk, relayUrls, fetcher } = useNDK();
|
||||||
|
const { account } = useAccount();
|
||||||
|
|
||||||
async function fetchNetwork(prevFollow?: string[]) {
|
async function fetchNetwork(prevFollow?: string[]) {
|
||||||
const account = await getActiveAccount();
|
|
||||||
const follows = new Set<string>(prevFollow || []);
|
const follows = new Set<string>(prevFollow || []);
|
||||||
const lruNetwork = new LRUCache<string, string, void>({ max: 300 });
|
const lruNetwork = new LRUCache<string, string, void>({ max: 300 });
|
||||||
|
|
||||||
@ -27,6 +32,7 @@ export function useNostr() {
|
|||||||
|
|
||||||
// fetch user's follows
|
// fetch user's follows
|
||||||
if (!prevFollow) {
|
if (!prevFollow) {
|
||||||
|
console.log("fetching user's follow...");
|
||||||
const user = ndk.getUser({ hexpubkey: account.pubkey });
|
const user = ndk.getUser({ hexpubkey: account.pubkey });
|
||||||
const list = await user.follows();
|
const list = await user.follows();
|
||||||
list.forEach((item: NDKUser) => {
|
list.forEach((item: NDKUser) => {
|
||||||
@ -36,7 +42,7 @@ export function useNostr() {
|
|||||||
|
|
||||||
// fetch network
|
// fetch network
|
||||||
if (!account.network) {
|
if (!account.network) {
|
||||||
console.log('fetching network...', follows.size);
|
console.log("fetching user's network...");
|
||||||
const events = await fetcher.fetchAllEvents(
|
const events = await fetcher.fetchAllEvents(
|
||||||
relayUrls,
|
relayUrls,
|
||||||
{ kinds: [3], authors: [...follows] },
|
{ kinds: [3], authors: [...follows] },
|
||||||
@ -62,14 +68,15 @@ export function useNostr() {
|
|||||||
return [...new Set([...follows, ...network])];
|
return [...new Set([...follows, ...network])];
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchNotes = async (prevFollow?: string[]) => {
|
async function fetchNotes(prevFollow?: string[]) {
|
||||||
try {
|
try {
|
||||||
const network = (await fetchNetwork(prevFollow)) as string[];
|
const network = await fetchNetwork(prevFollow);
|
||||||
|
|
||||||
const totalNotes = await countTotalNotes();
|
const totalNotes = await countTotalNotes();
|
||||||
const lastLogin = await getLastLogin();
|
const lastLogin = await getLastLogin();
|
||||||
|
|
||||||
if (network.length > 0) {
|
if (network.length > 0) {
|
||||||
|
console.log('fetching notes...');
|
||||||
|
|
||||||
let since: number;
|
let since: number;
|
||||||
if (totalNotes === 0 || lastLogin === 0) {
|
if (totalNotes === 0 || lastLogin === 0) {
|
||||||
since = nHoursAgo(24);
|
since = nHoursAgo(24);
|
||||||
@ -96,15 +103,15 @@ export function useNostr() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return { status: 'ok' };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('error: ', e);
|
console.error('failed fetch notes, error: ', e);
|
||||||
|
return { status: 'failed', message: e };
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const fetchChats = async () => {
|
async function fetchChats() {
|
||||||
try {
|
try {
|
||||||
const account = await getActiveAccount();
|
|
||||||
const lastLogin = await getLastLogin();
|
const lastLogin = await getLastLogin();
|
||||||
const incomingMessages = await fetcher.fetchAllEvents(
|
const incomingMessages = await fetcher.fetchAllEvents(
|
||||||
relayUrls,
|
relayUrls,
|
||||||
@ -128,11 +135,60 @@ export function useNostr() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return { status: 'ok' };
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('error: ', e);
|
console.error('failed fetch incoming messages, error: ', e);
|
||||||
|
return { status: 'failed', message: e };
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const publish = async ({
|
||||||
|
content,
|
||||||
|
kind,
|
||||||
|
tags,
|
||||||
|
}: {
|
||||||
|
content: string;
|
||||||
|
kind: NDKKind | number;
|
||||||
|
tags: string[][];
|
||||||
|
}): Promise<NDKEvent> => {
|
||||||
|
if (!privkey) throw new Error('Private key not found');
|
||||||
|
|
||||||
|
const event = new NDKEvent(ndk);
|
||||||
|
const signer = new NDKPrivateKeySigner(privkey);
|
||||||
|
|
||||||
|
event.content = content;
|
||||||
|
event.kind = kind;
|
||||||
|
event.created_at = Math.floor(Date.now() / 1000);
|
||||||
|
event.pubkey = account.pubkey;
|
||||||
|
event.tags = tags;
|
||||||
|
|
||||||
|
await event.sign(signer);
|
||||||
|
await event.publish();
|
||||||
|
|
||||||
|
return event;
|
||||||
};
|
};
|
||||||
|
|
||||||
return { fetchNotes, fetchChats };
|
const createZap = async (event: NostrEvent, amount: number, message?: string) => {
|
||||||
|
// @ts-expect-error, LumeEvent to NostrEvent
|
||||||
|
event.id = event.event_id;
|
||||||
|
|
||||||
|
// @ts-expect-error, LumeEvent to NostrEvent
|
||||||
|
if (typeof event.content !== 'string') event.content = event.content.original;
|
||||||
|
|
||||||
|
if (typeof event.tags === 'string') event.tags = destr(event.tags);
|
||||||
|
|
||||||
|
if (!privkey) throw new Error('Private key not found');
|
||||||
|
|
||||||
|
if (!ndk.signer) {
|
||||||
|
const signer = new NDKPrivateKeySigner(privkey);
|
||||||
|
ndk.signer = signer;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ndkEvent = new NDKEvent(ndk, event);
|
||||||
|
const res = await ndkEvent.zap(amount, message ?? 'zap from lume');
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
return { fetchNotes, fetchChats, publish, createZap };
|
||||||
}
|
}
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
import { NDKEvent, NDKKind, NDKPrivateKeySigner, NostrEvent } from '@nostr-dev-kit/ndk';
|
|
||||||
import destr from 'destr';
|
|
||||||
|
|
||||||
import { useNDK } from '@libs/ndk/provider';
|
|
||||||
|
|
||||||
import { useStronghold } from '@stores/stronghold';
|
|
||||||
|
|
||||||
import { useAccount } from '@utils/hooks/useAccount';
|
|
||||||
|
|
||||||
export function usePublish() {
|
|
||||||
const { ndk } = useNDK();
|
|
||||||
const { account } = useAccount();
|
|
||||||
|
|
||||||
const privkey = useStronghold((state) => state.privkey);
|
|
||||||
|
|
||||||
const publish = async ({
|
|
||||||
content,
|
|
||||||
kind,
|
|
||||||
tags,
|
|
||||||
}: {
|
|
||||||
content: string;
|
|
||||||
kind: NDKKind | number;
|
|
||||||
tags: string[][];
|
|
||||||
}): Promise<NDKEvent> => {
|
|
||||||
if (!privkey) throw new Error('Private key not found');
|
|
||||||
|
|
||||||
const event = new NDKEvent(ndk);
|
|
||||||
const signer = new NDKPrivateKeySigner(privkey);
|
|
||||||
|
|
||||||
event.content = content;
|
|
||||||
event.kind = kind;
|
|
||||||
event.created_at = Math.floor(Date.now() / 1000);
|
|
||||||
event.pubkey = account.pubkey;
|
|
||||||
event.tags = tags;
|
|
||||||
|
|
||||||
await event.sign(signer);
|
|
||||||
await event.publish();
|
|
||||||
|
|
||||||
return event;
|
|
||||||
};
|
|
||||||
|
|
||||||
const createZap = async (event: NostrEvent, amount: number, message?: string) => {
|
|
||||||
// @ts-expect-error, lumeevent to nostrevent
|
|
||||||
event.id = event.event_id;
|
|
||||||
// @ts-expect-error, lumeevent to nostrevent
|
|
||||||
if (typeof event.content !== 'string') event.content = event.content.original;
|
|
||||||
if (typeof event.tags === 'string') event.tags = destr(event.tags);
|
|
||||||
if (!privkey) throw new Error('Private key not found');
|
|
||||||
if (!ndk.signer) {
|
|
||||||
const signer = new NDKPrivateKeySigner(privkey);
|
|
||||||
ndk.signer = signer;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ndkEvent = new NDKEvent(ndk, event);
|
|
||||||
const res = await ndkEvent.zap(amount, message ?? 'test zap from lume');
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
return { publish, createZap };
|
|
||||||
}
|
|
@ -5,13 +5,14 @@ import { createNote } from '@libs/storage';
|
|||||||
|
|
||||||
import { nHoursAgo } from '@utils/date';
|
import { nHoursAgo } from '@utils/date';
|
||||||
import { useAccount } from '@utils/hooks/useAccount';
|
import { useAccount } from '@utils/hooks/useAccount';
|
||||||
import { usePublish } from '@utils/hooks/usePublish';
|
|
||||||
import { nip02ToArray } from '@utils/transform';
|
import { nip02ToArray } from '@utils/transform';
|
||||||
|
|
||||||
|
import { useNostr } from './useNostr';
|
||||||
|
|
||||||
export function useSocial() {
|
export function useSocial() {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
const { publish } = usePublish();
|
const { publish } = useNostr();
|
||||||
const { fetcher, relayUrls } = useNDK();
|
const { fetcher, relayUrls } = useNDK();
|
||||||
const { account } = useAccount();
|
const { account } = useAccount();
|
||||||
const { status, data: userFollows } = useQuery(
|
const { status, data: userFollows } = useQuery(
|
||||||
|
@ -3,10 +3,10 @@ import { open } from '@tauri-apps/plugin-dialog';
|
|||||||
import { VoidApi } from '@void-cat/api';
|
import { VoidApi } from '@void-cat/api';
|
||||||
|
|
||||||
import { createBlobFromFile } from '@utils/createBlobFromFile';
|
import { createBlobFromFile } from '@utils/createBlobFromFile';
|
||||||
import { usePublish } from '@utils/hooks/usePublish';
|
import { useNostr } from '@utils/hooks/useNostr';
|
||||||
|
|
||||||
export function useImageUploader() {
|
export function useImageUploader() {
|
||||||
const { publish } = usePublish();
|
const { publish } = useNostr();
|
||||||
|
|
||||||
const upload = async (file: null | string, nip94?: boolean) => {
|
const upload = async (file: null | string, nip94?: boolean) => {
|
||||||
const voidcat = new VoidApi('https://void.cat');
|
const voidcat = new VoidApi('https://void.cat');
|
||||||
|
Loading…
Reference in New Issue
Block a user