diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index b768bfcc..a6543f97 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -2,4 +2,5 @@ pub mod account; pub mod event; pub mod metadata; pub mod relay; +pub mod sync; pub mod window; diff --git a/src-tauri/src/commands/sync.rs b/src-tauri/src/commands/sync.rs new file mode 100644 index 00000000..b8c4cd72 --- /dev/null +++ b/src-tauri/src/commands/sync.rs @@ -0,0 +1,71 @@ +use nostr_sdk::prelude::*; +use std::collections::HashSet; +use tauri::State; + +use crate::Nostr; + +#[tauri::command] +#[specta::specta] +pub async fn sync_all( + state: State<'_, Nostr>, + reader: tauri::ipc::Channel, +) -> Result<(), String> { + let client = &state.client; + + // Create a filter for get all public keys + let filter = Filter::new().kinds(vec![ + Kind::TextNote, + Kind::Repost, + Kind::FollowSet, + Kind::ContactList, + Kind::MuteList, + ]); + + let events = client + .database() + .query(vec![filter]) + .await + .map_err(|err| err.to_string())?; + + let public_keys: Vec = events + .iter() + .flat_map(|ev| ev.tags.public_keys().copied()) + .collect::>() + .into_iter() + .collect(); + + let (tx, mut rx) = SyncProgress::channel(); + let opts = SyncOptions::default().progress(tx); + + tauri::async_runtime::spawn(async move { + while rx.changed().await.is_ok() { + let progress = *rx.borrow_and_update(); + + if progress.total > 0 { + reader.send(progress.percentage() * 100.0).unwrap(); + } + } + }); + + for chunk in public_keys.chunks(200) { + let authors = chunk.to_owned(); + let filter = Filter::new().authors(authors).kinds(vec![ + Kind::Metadata, + Kind::ContactList, + Kind::FollowSet, + Kind::Interests, + Kind::InterestSet, + Kind::EventDeletion, + Kind::TextNote, + Kind::Repost, + Kind::Comment, + ]); + + let _ = client + .sync(filter, &opts) + .await + .map_err(|err| err.to_string())?; + } + + Ok(()) +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index ca7fb178..8bbe3f6e 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -5,7 +5,7 @@ #[cfg(target_os = "macos")] use border::WebviewWindowExt as BorderWebviewWindowExt; -use commands::{account::*, event::*, metadata::*, relay::*, window::*}; +use commands::{account::*, event::*, metadata::*, relay::*, sync::*, window::*}; use common::{get_all_accounts, parse_event}; use nostr_sdk::prelude::{Profile as DatabaseProfile, *}; use serde::{Deserialize, Serialize}; @@ -76,6 +76,7 @@ fn main() { tracing_subscriber::fmt::init(); let builder = Builder::::new().commands(collect_commands![ + sync_all, get_all_relays, get_all_relay_lists, is_relay_connected, @@ -365,6 +366,8 @@ fn main() { // Set interval let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(600)); + // Skip the first tick + interval.tick().await; loop { interval.tick().await; diff --git a/src/commands.gen.ts b/src/commands.gen.ts index c836c74d..32eb4e32 100644 --- a/src/commands.gen.ts +++ b/src/commands.gen.ts @@ -5,6 +5,14 @@ export const commands = { +async syncAll(reader: TAURI_CHANNEL) : Promise> { + try { + return { status: "ok", data: await TAURI_INVOKE("sync_all", { reader }) }; +} catch (e) { + if(e instanceof Error) throw e; + else return { status: "error", error: e as any }; +} +}, async getAllRelays() : Promise> { try { return { status: "ok", data: await TAURI_INVOKE("get_all_relays") }; @@ -554,6 +562,7 @@ export type Meta = { content: string; images: string[]; events: string[]; mentio export type NewWindow = { label: string; title: string; url: string; width: number; height: number; maximizable: boolean; minimizable: boolean; hidden_title: boolean; closable: boolean } export type RichEvent = { raw: string; parsed: Meta | null } export type Settings = { resize_service: boolean; content_warning: boolean; display_avatar: boolean; display_zap_button: boolean; display_repost_button: boolean; display_media: boolean } +export type TAURI_CHANNEL = null /** tauri-specta globals **/ diff --git a/src/routes.gen.ts b/src/routes.gen.ts index b514943d..e8801a80 100644 --- a/src/routes.gen.ts +++ b/src/routes.gen.ts @@ -19,7 +19,6 @@ import { Route as AppIndexImport } from './routes/_app/index' import { Route as ZapIdImport } from './routes/zap.$id' import { Route as SettingsWalletImport } from './routes/settings/wallet' import { Route as SettingsRelaysImport } from './routes/settings/relays' -import { Route as SettingsGeneralImport } from './routes/settings/general' import { Route as ColumnsLayoutImport } from './routes/columns/_layout' import { Route as IdSetProfileImport } from './routes/$id.set-profile' import { Route as IdSetInterestImport } from './routes/$id.set-interest' @@ -39,6 +38,8 @@ import { Route as ColumnsLayoutCreateNewsfeedF2fImport } from './routes/columns/ const ColumnsImport = createFileRoute('/columns')() const SettingsLazyImport = createFileRoute('/settings')() const NewLazyImport = createFileRoute('/new')() +const SettingsSyncLazyImport = createFileRoute('/settings/sync')() +const SettingsGeneralLazyImport = createFileRoute('/settings/general')() const NewAccountWatchLazyImport = createFileRoute('/new-account/watch')() const NewAccountImportLazyImport = createFileRoute('/new-account/import')() const NewAccountConnectLazyImport = createFileRoute('/new-account/connect')() @@ -118,6 +119,20 @@ const AppIndexRoute = AppIndexImport.update({ getParentRoute: () => AppRoute, } as any).lazy(() => import('./routes/_app/index.lazy').then((d) => d.Route)) +const SettingsSyncLazyRoute = SettingsSyncLazyImport.update({ + id: '/sync', + path: '/sync', + getParentRoute: () => SettingsLazyRoute, +} as any).lazy(() => import('./routes/settings/sync.lazy').then((d) => d.Route)) + +const SettingsGeneralLazyRoute = SettingsGeneralLazyImport.update({ + id: '/general', + path: '/general', + getParentRoute: () => SettingsLazyRoute, +} as any).lazy(() => + import('./routes/settings/general.lazy').then((d) => d.Route), +) + const NewAccountWatchLazyRoute = NewAccountWatchLazyImport.update({ id: '/new-account/watch', path: '/new-account/watch', @@ -164,14 +179,6 @@ const SettingsRelaysRoute = SettingsRelaysImport.update({ import('./routes/settings/relays.lazy').then((d) => d.Route), ) -const SettingsGeneralRoute = SettingsGeneralImport.update({ - id: '/general', - path: '/general', - getParentRoute: () => SettingsLazyRoute, -} as any).lazy(() => - import('./routes/settings/general.lazy').then((d) => d.Route), -) - const ColumnsLayoutRoute = ColumnsLayoutImport.update({ id: '/_layout', getParentRoute: () => ColumnsRoute, @@ -440,13 +447,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ColumnsLayoutImport parentRoute: typeof ColumnsRoute } - '/settings/general': { - id: '/settings/general' - path: '/general' - fullPath: '/settings/general' - preLoaderRoute: typeof SettingsGeneralImport - parentRoute: typeof SettingsLazyImport - } '/settings/relays': { id: '/settings/relays' path: '/relays' @@ -489,6 +489,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof NewAccountWatchLazyImport parentRoute: typeof rootRoute } + '/settings/general': { + id: '/settings/general' + path: '/general' + fullPath: '/settings/general' + preLoaderRoute: typeof SettingsGeneralLazyImport + parentRoute: typeof SettingsLazyImport + } + '/settings/sync': { + id: '/settings/sync' + path: '/sync' + fullPath: '/settings/sync' + preLoaderRoute: typeof SettingsSyncLazyImport + parentRoute: typeof SettingsLazyImport + } '/_app/': { id: '/_app/' path: '/' @@ -666,15 +680,17 @@ const AppRouteChildren: AppRouteChildren = { const AppRouteWithChildren = AppRoute._addFileChildren(AppRouteChildren) interface SettingsLazyRouteChildren { - SettingsGeneralRoute: typeof SettingsGeneralRoute SettingsRelaysRoute: typeof SettingsRelaysRoute SettingsWalletRoute: typeof SettingsWalletRoute + SettingsGeneralLazyRoute: typeof SettingsGeneralLazyRoute + SettingsSyncLazyRoute: typeof SettingsSyncLazyRoute } const SettingsLazyRouteChildren: SettingsLazyRouteChildren = { - SettingsGeneralRoute: SettingsGeneralRoute, SettingsRelaysRoute: SettingsRelaysRoute, SettingsWalletRoute: SettingsWalletRoute, + SettingsGeneralLazyRoute: SettingsGeneralLazyRoute, + SettingsSyncLazyRoute: SettingsSyncLazyRoute, } const SettingsLazyRouteWithChildren = SettingsLazyRoute._addFileChildren( @@ -768,13 +784,14 @@ export interface FileRoutesByFullPath { '/$id/set-interest': typeof IdSetInterestRoute '/$id/set-profile': typeof IdSetProfileRoute '/columns': typeof ColumnsLayoutRouteWithChildren - '/settings/general': typeof SettingsGeneralRoute '/settings/relays': typeof SettingsRelaysRoute '/settings/wallet': typeof SettingsWalletRoute '/zap/$id': typeof ZapIdRoute '/new-account/connect': typeof NewAccountConnectLazyRoute '/new-account/import': typeof NewAccountImportLazyRoute '/new-account/watch': typeof NewAccountWatchLazyRoute + '/settings/general': typeof SettingsGeneralLazyRoute + '/settings/sync': typeof SettingsSyncLazyRoute '/': typeof AppIndexRoute '/new-post': typeof NewPostIndexRoute '/columns/create-newsfeed': typeof ColumnsLayoutCreateNewsfeedRouteWithChildren @@ -807,13 +824,14 @@ export interface FileRoutesByTo { '/$id/set-interest': typeof IdSetInterestRoute '/$id/set-profile': typeof IdSetProfileRoute '/columns': typeof ColumnsLayoutRouteWithChildren - '/settings/general': typeof SettingsGeneralRoute '/settings/relays': typeof SettingsRelaysRoute '/settings/wallet': typeof SettingsWalletRoute '/zap/$id': typeof ZapIdRoute '/new-account/connect': typeof NewAccountConnectLazyRoute '/new-account/import': typeof NewAccountImportLazyRoute '/new-account/watch': typeof NewAccountWatchLazyRoute + '/settings/general': typeof SettingsGeneralLazyRoute + '/settings/sync': typeof SettingsSyncLazyRoute '/': typeof AppIndexRoute '/new-post': typeof NewPostIndexRoute '/columns/create-newsfeed': typeof ColumnsLayoutCreateNewsfeedRouteWithChildren @@ -849,13 +867,14 @@ export interface FileRoutesById { '/$id/set-profile': typeof IdSetProfileRoute '/columns': typeof ColumnsRouteWithChildren '/columns/_layout': typeof ColumnsLayoutRouteWithChildren - '/settings/general': typeof SettingsGeneralRoute '/settings/relays': typeof SettingsRelaysRoute '/settings/wallet': typeof SettingsWalletRoute '/zap/$id': typeof ZapIdRoute '/new-account/connect': typeof NewAccountConnectLazyRoute '/new-account/import': typeof NewAccountImportLazyRoute '/new-account/watch': typeof NewAccountWatchLazyRoute + '/settings/general': typeof SettingsGeneralLazyRoute + '/settings/sync': typeof SettingsSyncLazyRoute '/_app/': typeof AppIndexRoute '/new-post/': typeof NewPostIndexRoute '/columns/_layout/create-newsfeed': typeof ColumnsLayoutCreateNewsfeedRouteWithChildren @@ -891,13 +910,14 @@ export interface FileRouteTypes { | '/$id/set-interest' | '/$id/set-profile' | '/columns' - | '/settings/general' | '/settings/relays' | '/settings/wallet' | '/zap/$id' | '/new-account/connect' | '/new-account/import' | '/new-account/watch' + | '/settings/general' + | '/settings/sync' | '/' | '/new-post' | '/columns/create-newsfeed' @@ -929,13 +949,14 @@ export interface FileRouteTypes { | '/$id/set-interest' | '/$id/set-profile' | '/columns' - | '/settings/general' | '/settings/relays' | '/settings/wallet' | '/zap/$id' | '/new-account/connect' | '/new-account/import' | '/new-account/watch' + | '/settings/general' + | '/settings/sync' | '/' | '/new-post' | '/columns/create-newsfeed' @@ -969,13 +990,14 @@ export interface FileRouteTypes { | '/$id/set-profile' | '/columns' | '/columns/_layout' - | '/settings/general' | '/settings/relays' | '/settings/wallet' | '/zap/$id' | '/new-account/connect' | '/new-account/import' | '/new-account/watch' + | '/settings/general' + | '/settings/sync' | '/_app/' | '/new-post/' | '/columns/_layout/create-newsfeed' @@ -1068,9 +1090,10 @@ export const routeTree = rootRoute "/settings": { "filePath": "settings.lazy.tsx", "children": [ - "/settings/general", "/settings/relays", - "/settings/wallet" + "/settings/wallet", + "/settings/general", + "/settings/sync" ] }, "/$id/set-group": { @@ -1113,10 +1136,6 @@ export const routeTree = rootRoute "/columns/_layout/users/$id" ] }, - "/settings/general": { - "filePath": "settings/general.tsx", - "parent": "/settings" - }, "/settings/relays": { "filePath": "settings/relays.tsx", "parent": "/settings" @@ -1137,6 +1156,14 @@ export const routeTree = rootRoute "/new-account/watch": { "filePath": "new-account/watch.lazy.tsx" }, + "/settings/general": { + "filePath": "settings/general.lazy.tsx", + "parent": "/settings" + }, + "/settings/sync": { + "filePath": "settings/sync.lazy.tsx", + "parent": "/settings" + }, "/_app/": { "filePath": "_app/index.tsx", "parent": "/_app" diff --git a/src/routes/_app.lazy.tsx b/src/routes/_app.lazy.tsx index 05e0a2a9..38dc43d3 100644 --- a/src/routes/_app.lazy.tsx +++ b/src/routes/_app.lazy.tsx @@ -118,7 +118,7 @@ function Account({ pubkey }: { pubkey: string }) { const items = await Promise.all([ MenuItem.new({ text: "Unlock", - enabled: !isActive || true, + enabled: !isActive, action: async () => await commands.setSigner(pubkey), }), PredefinedMenuItem.new({ item: "Separator" }), @@ -183,7 +183,7 @@ function Account({ pubkey }: { pubkey: string }) { await menu.popup().catch((e) => console.error(e)); }, - [pubkey], + [isActive, pubkey], ); useEffect(() => { diff --git a/src/routes/settings.lazy.tsx b/src/routes/settings.lazy.tsx index 78599e86..c4015b2b 100644 --- a/src/routes/settings.lazy.tsx +++ b/src/routes/settings.lazy.tsx @@ -1,96 +1,118 @@ -import { cn } from '@/commons' -import { CurrencyBtc, GearSix, HardDrives } from '@phosphor-icons/react' -import * as ScrollArea from '@radix-ui/react-scroll-area' -import { Link } from '@tanstack/react-router' -import { Outlet, createLazyFileRoute } from '@tanstack/react-router' +import { cn } from "@/commons"; +import { + CloudArrowDown, + CurrencyBtc, + GearSix, + HardDrives, +} from "@phosphor-icons/react"; +import * as ScrollArea from "@radix-ui/react-scroll-area"; +import { Link } from "@tanstack/react-router"; +import { Outlet, createLazyFileRoute } from "@tanstack/react-router"; -export const Route = createLazyFileRoute('/settings')({ - component: Screen, -}) +export const Route = createLazyFileRoute("/settings")({ + component: Screen, +}); function Screen() { - const { platform } = Route.useRouteContext() + const { platform } = Route.useRouteContext(); - return ( -
-
-
-

Settings

-
- - {({ isActive }) => { - return ( -
- -

General

-
- ) - }} - - - {({ isActive }) => { - return ( -
- -

Relays

-
- ) - }} - - - {({ isActive }) => { - return ( -
- -

Wallet

-
- ) - }} - -
- - - - - - - - - -
- ) + return ( +
+
+
+

Settings

+
+ + {({ isActive }) => { + return ( +
+ +

General

+
+ ); + }} + + + {({ isActive }) => { + return ( +
+ +

Sync

+
+ ); + }} + + + {({ isActive }) => { + return ( +
+ +

Relays

+
+ ); + }} + + + {({ isActive }) => { + return ( +
+ +

Wallet

+
+ ); + }} + +
+ + + + + + + + + +
+ ); } diff --git a/src/routes/settings/general.lazy.tsx b/src/routes/settings/general.lazy.tsx index bf73ff1e..ffba139f 100644 --- a/src/routes/settings/general.lazy.tsx +++ b/src/routes/settings/general.lazy.tsx @@ -69,9 +69,7 @@ function Screen() {
-

- General -

+

General

-

- Appearance -

+

Appearance

@@ -140,9 +136,7 @@ function Screen() {
-

- Privacy & Performance -

+

Privacy & Performance

-

- Connected Relays -

+
+

Connected Relays

+

+ Learn more about Relays{" "} + + here + +

+
diff --git a/src/routes/settings/sync.lazy.tsx b/src/routes/settings/sync.lazy.tsx new file mode 100644 index 00000000..88dac659 --- /dev/null +++ b/src/routes/settings/sync.lazy.tsx @@ -0,0 +1,100 @@ +import { createLazyFileRoute } from "@tanstack/react-router"; +import { useEffect, useState, useTransition } from "react"; +import * as Progress from "@radix-ui/react-progress"; +import { Channel } from "@tauri-apps/api/core"; +import { commands } from "@/commands.gen"; +import { message } from "@tauri-apps/plugin-dialog"; +import { Spinner } from "@/components"; + +export const Route = createLazyFileRoute("/settings/sync")({ + component: Screen, +}); + +function Screen() { + const [channel, _setChannel] = useState>( + () => new Channel(), + ); + const [progress, setProgress] = useState(0); + const [isPending, startTransition] = useTransition(); + + const runSync = () => { + startTransition(async () => { + const res = await commands.syncAll(channel); + + if (res.status === "error") { + await message(res.error, { kind: "error" }); + } + + return; + }); + }; + + useEffect(() => { + channel.onmessage = (message) => { + setProgress(message); + }; + }, [channel]); + + return ( +
+
+
+

Sync events with Negentropy

+

+ Learn more about negentropy{" "} + + here + +

+
+
+
Data will be sync:
+
+ Metadata of all public keys that found in database. +
+
+ Contact list of all public keys that found in database. +
+
+ Follow and interest sets of all public keys that found in database. +
+
+ All notes and reposts of all public keys that found in database. +
+
+ All comments all public keys that found in database. +
+
+
+
+ + + +
+ +
+
+
+ ); +} diff --git a/src/routes/settings/wallet.lazy.tsx b/src/routes/settings/wallet.lazy.tsx index c17bff76..dd1c8642 100644 --- a/src/routes/settings/wallet.lazy.tsx +++ b/src/routes/settings/wallet.lazy.tsx @@ -33,9 +33,20 @@ function Screen() { return (
-

- Wallet -

+
+

Bitcoin Wallet

+

+ Learn more about Zap{" "} + + here + +

+