mirror of
https://github.com/lumehq/lume.git
synced 2025-03-17 13:22:05 +01:00
feat: add negentropy sync to settings
This commit is contained in:
parent
c93edde7d2
commit
c5d06a2492
@ -2,4 +2,5 @@ pub mod account;
|
|||||||
pub mod event;
|
pub mod event;
|
||||||
pub mod metadata;
|
pub mod metadata;
|
||||||
pub mod relay;
|
pub mod relay;
|
||||||
|
pub mod sync;
|
||||||
pub mod window;
|
pub mod window;
|
||||||
|
71
src-tauri/src/commands/sync.rs
Normal file
71
src-tauri/src/commands/sync.rs
Normal file
@ -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<f64>,
|
||||||
|
) -> 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<PublicKey> = events
|
||||||
|
.iter()
|
||||||
|
.flat_map(|ev| ev.tags.public_keys().copied())
|
||||||
|
.collect::<HashSet<_>>()
|
||||||
|
.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(())
|
||||||
|
}
|
@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use border::WebviewWindowExt as BorderWebviewWindowExt;
|
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 common::{get_all_accounts, parse_event};
|
||||||
use nostr_sdk::prelude::{Profile as DatabaseProfile, *};
|
use nostr_sdk::prelude::{Profile as DatabaseProfile, *};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -76,6 +76,7 @@ fn main() {
|
|||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
let builder = Builder::<tauri::Wry>::new().commands(collect_commands![
|
let builder = Builder::<tauri::Wry>::new().commands(collect_commands![
|
||||||
|
sync_all,
|
||||||
get_all_relays,
|
get_all_relays,
|
||||||
get_all_relay_lists,
|
get_all_relay_lists,
|
||||||
is_relay_connected,
|
is_relay_connected,
|
||||||
@ -365,6 +366,8 @@ fn main() {
|
|||||||
|
|
||||||
// Set interval
|
// Set interval
|
||||||
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(600));
|
let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(600));
|
||||||
|
// Skip the first tick
|
||||||
|
interval.tick().await;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
interval.tick().await;
|
interval.tick().await;
|
||||||
|
@ -5,6 +5,14 @@
|
|||||||
|
|
||||||
|
|
||||||
export const commands = {
|
export const commands = {
|
||||||
|
async syncAll(reader: TAURI_CHANNEL<number>) : Promise<Result<null, string>> {
|
||||||
|
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<Result<string[], string>> {
|
async getAllRelays() : Promise<Result<string[], string>> {
|
||||||
try {
|
try {
|
||||||
return { status: "ok", data: await TAURI_INVOKE("get_all_relays") };
|
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 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 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 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<TSend> = null
|
||||||
|
|
||||||
/** tauri-specta globals **/
|
/** tauri-specta globals **/
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ import { Route as AppIndexImport } from './routes/_app/index'
|
|||||||
import { Route as ZapIdImport } from './routes/zap.$id'
|
import { Route as ZapIdImport } from './routes/zap.$id'
|
||||||
import { Route as SettingsWalletImport } from './routes/settings/wallet'
|
import { Route as SettingsWalletImport } from './routes/settings/wallet'
|
||||||
import { Route as SettingsRelaysImport } from './routes/settings/relays'
|
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 ColumnsLayoutImport } from './routes/columns/_layout'
|
||||||
import { Route as IdSetProfileImport } from './routes/$id.set-profile'
|
import { Route as IdSetProfileImport } from './routes/$id.set-profile'
|
||||||
import { Route as IdSetInterestImport } from './routes/$id.set-interest'
|
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 ColumnsImport = createFileRoute('/columns')()
|
||||||
const SettingsLazyImport = createFileRoute('/settings')()
|
const SettingsLazyImport = createFileRoute('/settings')()
|
||||||
const NewLazyImport = createFileRoute('/new')()
|
const NewLazyImport = createFileRoute('/new')()
|
||||||
|
const SettingsSyncLazyImport = createFileRoute('/settings/sync')()
|
||||||
|
const SettingsGeneralLazyImport = createFileRoute('/settings/general')()
|
||||||
const NewAccountWatchLazyImport = createFileRoute('/new-account/watch')()
|
const NewAccountWatchLazyImport = createFileRoute('/new-account/watch')()
|
||||||
const NewAccountImportLazyImport = createFileRoute('/new-account/import')()
|
const NewAccountImportLazyImport = createFileRoute('/new-account/import')()
|
||||||
const NewAccountConnectLazyImport = createFileRoute('/new-account/connect')()
|
const NewAccountConnectLazyImport = createFileRoute('/new-account/connect')()
|
||||||
@ -118,6 +119,20 @@ const AppIndexRoute = AppIndexImport.update({
|
|||||||
getParentRoute: () => AppRoute,
|
getParentRoute: () => AppRoute,
|
||||||
} as any).lazy(() => import('./routes/_app/index.lazy').then((d) => d.Route))
|
} 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({
|
const NewAccountWatchLazyRoute = NewAccountWatchLazyImport.update({
|
||||||
id: '/new-account/watch',
|
id: '/new-account/watch',
|
||||||
path: '/new-account/watch',
|
path: '/new-account/watch',
|
||||||
@ -164,14 +179,6 @@ const SettingsRelaysRoute = SettingsRelaysImport.update({
|
|||||||
import('./routes/settings/relays.lazy').then((d) => d.Route),
|
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({
|
const ColumnsLayoutRoute = ColumnsLayoutImport.update({
|
||||||
id: '/_layout',
|
id: '/_layout',
|
||||||
getParentRoute: () => ColumnsRoute,
|
getParentRoute: () => ColumnsRoute,
|
||||||
@ -440,13 +447,6 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof ColumnsLayoutImport
|
preLoaderRoute: typeof ColumnsLayoutImport
|
||||||
parentRoute: typeof ColumnsRoute
|
parentRoute: typeof ColumnsRoute
|
||||||
}
|
}
|
||||||
'/settings/general': {
|
|
||||||
id: '/settings/general'
|
|
||||||
path: '/general'
|
|
||||||
fullPath: '/settings/general'
|
|
||||||
preLoaderRoute: typeof SettingsGeneralImport
|
|
||||||
parentRoute: typeof SettingsLazyImport
|
|
||||||
}
|
|
||||||
'/settings/relays': {
|
'/settings/relays': {
|
||||||
id: '/settings/relays'
|
id: '/settings/relays'
|
||||||
path: '/relays'
|
path: '/relays'
|
||||||
@ -489,6 +489,20 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof NewAccountWatchLazyImport
|
preLoaderRoute: typeof NewAccountWatchLazyImport
|
||||||
parentRoute: typeof rootRoute
|
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/': {
|
'/_app/': {
|
||||||
id: '/_app/'
|
id: '/_app/'
|
||||||
path: '/'
|
path: '/'
|
||||||
@ -666,15 +680,17 @@ const AppRouteChildren: AppRouteChildren = {
|
|||||||
const AppRouteWithChildren = AppRoute._addFileChildren(AppRouteChildren)
|
const AppRouteWithChildren = AppRoute._addFileChildren(AppRouteChildren)
|
||||||
|
|
||||||
interface SettingsLazyRouteChildren {
|
interface SettingsLazyRouteChildren {
|
||||||
SettingsGeneralRoute: typeof SettingsGeneralRoute
|
|
||||||
SettingsRelaysRoute: typeof SettingsRelaysRoute
|
SettingsRelaysRoute: typeof SettingsRelaysRoute
|
||||||
SettingsWalletRoute: typeof SettingsWalletRoute
|
SettingsWalletRoute: typeof SettingsWalletRoute
|
||||||
|
SettingsGeneralLazyRoute: typeof SettingsGeneralLazyRoute
|
||||||
|
SettingsSyncLazyRoute: typeof SettingsSyncLazyRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
const SettingsLazyRouteChildren: SettingsLazyRouteChildren = {
|
const SettingsLazyRouteChildren: SettingsLazyRouteChildren = {
|
||||||
SettingsGeneralRoute: SettingsGeneralRoute,
|
|
||||||
SettingsRelaysRoute: SettingsRelaysRoute,
|
SettingsRelaysRoute: SettingsRelaysRoute,
|
||||||
SettingsWalletRoute: SettingsWalletRoute,
|
SettingsWalletRoute: SettingsWalletRoute,
|
||||||
|
SettingsGeneralLazyRoute: SettingsGeneralLazyRoute,
|
||||||
|
SettingsSyncLazyRoute: SettingsSyncLazyRoute,
|
||||||
}
|
}
|
||||||
|
|
||||||
const SettingsLazyRouteWithChildren = SettingsLazyRoute._addFileChildren(
|
const SettingsLazyRouteWithChildren = SettingsLazyRoute._addFileChildren(
|
||||||
@ -768,13 +784,14 @@ export interface FileRoutesByFullPath {
|
|||||||
'/$id/set-interest': typeof IdSetInterestRoute
|
'/$id/set-interest': typeof IdSetInterestRoute
|
||||||
'/$id/set-profile': typeof IdSetProfileRoute
|
'/$id/set-profile': typeof IdSetProfileRoute
|
||||||
'/columns': typeof ColumnsLayoutRouteWithChildren
|
'/columns': typeof ColumnsLayoutRouteWithChildren
|
||||||
'/settings/general': typeof SettingsGeneralRoute
|
|
||||||
'/settings/relays': typeof SettingsRelaysRoute
|
'/settings/relays': typeof SettingsRelaysRoute
|
||||||
'/settings/wallet': typeof SettingsWalletRoute
|
'/settings/wallet': typeof SettingsWalletRoute
|
||||||
'/zap/$id': typeof ZapIdRoute
|
'/zap/$id': typeof ZapIdRoute
|
||||||
'/new-account/connect': typeof NewAccountConnectLazyRoute
|
'/new-account/connect': typeof NewAccountConnectLazyRoute
|
||||||
'/new-account/import': typeof NewAccountImportLazyRoute
|
'/new-account/import': typeof NewAccountImportLazyRoute
|
||||||
'/new-account/watch': typeof NewAccountWatchLazyRoute
|
'/new-account/watch': typeof NewAccountWatchLazyRoute
|
||||||
|
'/settings/general': typeof SettingsGeneralLazyRoute
|
||||||
|
'/settings/sync': typeof SettingsSyncLazyRoute
|
||||||
'/': typeof AppIndexRoute
|
'/': typeof AppIndexRoute
|
||||||
'/new-post': typeof NewPostIndexRoute
|
'/new-post': typeof NewPostIndexRoute
|
||||||
'/columns/create-newsfeed': typeof ColumnsLayoutCreateNewsfeedRouteWithChildren
|
'/columns/create-newsfeed': typeof ColumnsLayoutCreateNewsfeedRouteWithChildren
|
||||||
@ -807,13 +824,14 @@ export interface FileRoutesByTo {
|
|||||||
'/$id/set-interest': typeof IdSetInterestRoute
|
'/$id/set-interest': typeof IdSetInterestRoute
|
||||||
'/$id/set-profile': typeof IdSetProfileRoute
|
'/$id/set-profile': typeof IdSetProfileRoute
|
||||||
'/columns': typeof ColumnsLayoutRouteWithChildren
|
'/columns': typeof ColumnsLayoutRouteWithChildren
|
||||||
'/settings/general': typeof SettingsGeneralRoute
|
|
||||||
'/settings/relays': typeof SettingsRelaysRoute
|
'/settings/relays': typeof SettingsRelaysRoute
|
||||||
'/settings/wallet': typeof SettingsWalletRoute
|
'/settings/wallet': typeof SettingsWalletRoute
|
||||||
'/zap/$id': typeof ZapIdRoute
|
'/zap/$id': typeof ZapIdRoute
|
||||||
'/new-account/connect': typeof NewAccountConnectLazyRoute
|
'/new-account/connect': typeof NewAccountConnectLazyRoute
|
||||||
'/new-account/import': typeof NewAccountImportLazyRoute
|
'/new-account/import': typeof NewAccountImportLazyRoute
|
||||||
'/new-account/watch': typeof NewAccountWatchLazyRoute
|
'/new-account/watch': typeof NewAccountWatchLazyRoute
|
||||||
|
'/settings/general': typeof SettingsGeneralLazyRoute
|
||||||
|
'/settings/sync': typeof SettingsSyncLazyRoute
|
||||||
'/': typeof AppIndexRoute
|
'/': typeof AppIndexRoute
|
||||||
'/new-post': typeof NewPostIndexRoute
|
'/new-post': typeof NewPostIndexRoute
|
||||||
'/columns/create-newsfeed': typeof ColumnsLayoutCreateNewsfeedRouteWithChildren
|
'/columns/create-newsfeed': typeof ColumnsLayoutCreateNewsfeedRouteWithChildren
|
||||||
@ -849,13 +867,14 @@ export interface FileRoutesById {
|
|||||||
'/$id/set-profile': typeof IdSetProfileRoute
|
'/$id/set-profile': typeof IdSetProfileRoute
|
||||||
'/columns': typeof ColumnsRouteWithChildren
|
'/columns': typeof ColumnsRouteWithChildren
|
||||||
'/columns/_layout': typeof ColumnsLayoutRouteWithChildren
|
'/columns/_layout': typeof ColumnsLayoutRouteWithChildren
|
||||||
'/settings/general': typeof SettingsGeneralRoute
|
|
||||||
'/settings/relays': typeof SettingsRelaysRoute
|
'/settings/relays': typeof SettingsRelaysRoute
|
||||||
'/settings/wallet': typeof SettingsWalletRoute
|
'/settings/wallet': typeof SettingsWalletRoute
|
||||||
'/zap/$id': typeof ZapIdRoute
|
'/zap/$id': typeof ZapIdRoute
|
||||||
'/new-account/connect': typeof NewAccountConnectLazyRoute
|
'/new-account/connect': typeof NewAccountConnectLazyRoute
|
||||||
'/new-account/import': typeof NewAccountImportLazyRoute
|
'/new-account/import': typeof NewAccountImportLazyRoute
|
||||||
'/new-account/watch': typeof NewAccountWatchLazyRoute
|
'/new-account/watch': typeof NewAccountWatchLazyRoute
|
||||||
|
'/settings/general': typeof SettingsGeneralLazyRoute
|
||||||
|
'/settings/sync': typeof SettingsSyncLazyRoute
|
||||||
'/_app/': typeof AppIndexRoute
|
'/_app/': typeof AppIndexRoute
|
||||||
'/new-post/': typeof NewPostIndexRoute
|
'/new-post/': typeof NewPostIndexRoute
|
||||||
'/columns/_layout/create-newsfeed': typeof ColumnsLayoutCreateNewsfeedRouteWithChildren
|
'/columns/_layout/create-newsfeed': typeof ColumnsLayoutCreateNewsfeedRouteWithChildren
|
||||||
@ -891,13 +910,14 @@ export interface FileRouteTypes {
|
|||||||
| '/$id/set-interest'
|
| '/$id/set-interest'
|
||||||
| '/$id/set-profile'
|
| '/$id/set-profile'
|
||||||
| '/columns'
|
| '/columns'
|
||||||
| '/settings/general'
|
|
||||||
| '/settings/relays'
|
| '/settings/relays'
|
||||||
| '/settings/wallet'
|
| '/settings/wallet'
|
||||||
| '/zap/$id'
|
| '/zap/$id'
|
||||||
| '/new-account/connect'
|
| '/new-account/connect'
|
||||||
| '/new-account/import'
|
| '/new-account/import'
|
||||||
| '/new-account/watch'
|
| '/new-account/watch'
|
||||||
|
| '/settings/general'
|
||||||
|
| '/settings/sync'
|
||||||
| '/'
|
| '/'
|
||||||
| '/new-post'
|
| '/new-post'
|
||||||
| '/columns/create-newsfeed'
|
| '/columns/create-newsfeed'
|
||||||
@ -929,13 +949,14 @@ export interface FileRouteTypes {
|
|||||||
| '/$id/set-interest'
|
| '/$id/set-interest'
|
||||||
| '/$id/set-profile'
|
| '/$id/set-profile'
|
||||||
| '/columns'
|
| '/columns'
|
||||||
| '/settings/general'
|
|
||||||
| '/settings/relays'
|
| '/settings/relays'
|
||||||
| '/settings/wallet'
|
| '/settings/wallet'
|
||||||
| '/zap/$id'
|
| '/zap/$id'
|
||||||
| '/new-account/connect'
|
| '/new-account/connect'
|
||||||
| '/new-account/import'
|
| '/new-account/import'
|
||||||
| '/new-account/watch'
|
| '/new-account/watch'
|
||||||
|
| '/settings/general'
|
||||||
|
| '/settings/sync'
|
||||||
| '/'
|
| '/'
|
||||||
| '/new-post'
|
| '/new-post'
|
||||||
| '/columns/create-newsfeed'
|
| '/columns/create-newsfeed'
|
||||||
@ -969,13 +990,14 @@ export interface FileRouteTypes {
|
|||||||
| '/$id/set-profile'
|
| '/$id/set-profile'
|
||||||
| '/columns'
|
| '/columns'
|
||||||
| '/columns/_layout'
|
| '/columns/_layout'
|
||||||
| '/settings/general'
|
|
||||||
| '/settings/relays'
|
| '/settings/relays'
|
||||||
| '/settings/wallet'
|
| '/settings/wallet'
|
||||||
| '/zap/$id'
|
| '/zap/$id'
|
||||||
| '/new-account/connect'
|
| '/new-account/connect'
|
||||||
| '/new-account/import'
|
| '/new-account/import'
|
||||||
| '/new-account/watch'
|
| '/new-account/watch'
|
||||||
|
| '/settings/general'
|
||||||
|
| '/settings/sync'
|
||||||
| '/_app/'
|
| '/_app/'
|
||||||
| '/new-post/'
|
| '/new-post/'
|
||||||
| '/columns/_layout/create-newsfeed'
|
| '/columns/_layout/create-newsfeed'
|
||||||
@ -1068,9 +1090,10 @@ export const routeTree = rootRoute
|
|||||||
"/settings": {
|
"/settings": {
|
||||||
"filePath": "settings.lazy.tsx",
|
"filePath": "settings.lazy.tsx",
|
||||||
"children": [
|
"children": [
|
||||||
"/settings/general",
|
|
||||||
"/settings/relays",
|
"/settings/relays",
|
||||||
"/settings/wallet"
|
"/settings/wallet",
|
||||||
|
"/settings/general",
|
||||||
|
"/settings/sync"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"/$id/set-group": {
|
"/$id/set-group": {
|
||||||
@ -1113,10 +1136,6 @@ export const routeTree = rootRoute
|
|||||||
"/columns/_layout/users/$id"
|
"/columns/_layout/users/$id"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"/settings/general": {
|
|
||||||
"filePath": "settings/general.tsx",
|
|
||||||
"parent": "/settings"
|
|
||||||
},
|
|
||||||
"/settings/relays": {
|
"/settings/relays": {
|
||||||
"filePath": "settings/relays.tsx",
|
"filePath": "settings/relays.tsx",
|
||||||
"parent": "/settings"
|
"parent": "/settings"
|
||||||
@ -1137,6 +1156,14 @@ export const routeTree = rootRoute
|
|||||||
"/new-account/watch": {
|
"/new-account/watch": {
|
||||||
"filePath": "new-account/watch.lazy.tsx"
|
"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/": {
|
"/_app/": {
|
||||||
"filePath": "_app/index.tsx",
|
"filePath": "_app/index.tsx",
|
||||||
"parent": "/_app"
|
"parent": "/_app"
|
||||||
|
@ -118,7 +118,7 @@ function Account({ pubkey }: { pubkey: string }) {
|
|||||||
const items = await Promise.all([
|
const items = await Promise.all([
|
||||||
MenuItem.new({
|
MenuItem.new({
|
||||||
text: "Unlock",
|
text: "Unlock",
|
||||||
enabled: !isActive || true,
|
enabled: !isActive,
|
||||||
action: async () => await commands.setSigner(pubkey),
|
action: async () => await commands.setSigner(pubkey),
|
||||||
}),
|
}),
|
||||||
PredefinedMenuItem.new({ item: "Separator" }),
|
PredefinedMenuItem.new({ item: "Separator" }),
|
||||||
@ -183,7 +183,7 @@ function Account({ pubkey }: { pubkey: string }) {
|
|||||||
|
|
||||||
await menu.popup().catch((e) => console.error(e));
|
await menu.popup().catch((e) => console.error(e));
|
||||||
},
|
},
|
||||||
[pubkey],
|
[isActive, pubkey],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -1,96 +1,118 @@
|
|||||||
import { cn } from '@/commons'
|
import { cn } from "@/commons";
|
||||||
import { CurrencyBtc, GearSix, HardDrives } from '@phosphor-icons/react'
|
import {
|
||||||
import * as ScrollArea from '@radix-ui/react-scroll-area'
|
CloudArrowDown,
|
||||||
import { Link } from '@tanstack/react-router'
|
CurrencyBtc,
|
||||||
import { Outlet, createLazyFileRoute } from '@tanstack/react-router'
|
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')({
|
export const Route = createLazyFileRoute("/settings")({
|
||||||
component: Screen,
|
component: Screen,
|
||||||
})
|
});
|
||||||
|
|
||||||
function Screen() {
|
function Screen() {
|
||||||
const { platform } = Route.useRouteContext()
|
const { platform } = Route.useRouteContext();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex size-full">
|
<div className="flex size-full">
|
||||||
<div
|
<div
|
||||||
data-tauri-drag-region
|
data-tauri-drag-region
|
||||||
className={cn(
|
className={cn(
|
||||||
'w-[200px] shrink-0 flex flex-col gap-1 border-r border-black/10 dark:border-white/10 p-2',
|
"w-[200px] shrink-0 flex flex-col gap-1 border-r border-black/10 dark:border-white/10 p-2",
|
||||||
platform === 'macos' ? 'pt-11' : '',
|
platform === "macos" ? "pt-11" : "",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="h-8 px-1.5">
|
<div className="h-8 px-1.5">
|
||||||
<h1 className="text-lg font-semibold">Settings</h1>
|
<h1 className="text-lg font-semibold">Settings</h1>
|
||||||
</div>
|
</div>
|
||||||
<Link to="/settings/general">
|
<Link to="/settings/general">
|
||||||
{({ isActive }) => {
|
{({ isActive }) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-9 w-full inline-flex items-center gap-1.5 rounded-lg p-2',
|
"h-9 w-full inline-flex items-center gap-1.5 rounded-lg p-2",
|
||||||
isActive
|
isActive
|
||||||
? 'bg-black/10 hover:bg-black/20 dark:bg-white/10 text-neutral-900 dark:text-neutral-100 dark:hover:bg-bg-white/20'
|
? "bg-black/10 hover:bg-black/20 dark:bg-white/10 text-neutral-900 dark:text-neutral-100 dark:hover:bg-bg-white/20"
|
||||||
: 'text-neutral-700 hover:bg-black/10 dark:text-neutral-300 dark:hover:bg-white/10',
|
: "text-neutral-700 hover:bg-black/10 dark:text-neutral-300 dark:hover:bg-white/10",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<GearSix className="size-5 shrink-0" />
|
<GearSix className="size-5 shrink-0" />
|
||||||
<p className="text-sm font-medium">General</p>
|
<p className="text-sm font-medium">General</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}}
|
}}
|
||||||
</Link>
|
</Link>
|
||||||
<Link to="/settings/relays">
|
<Link to="/settings/sync">
|
||||||
{({ isActive }) => {
|
{({ isActive }) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-9 w-full inline-flex items-center gap-1.5 rounded-lg p-2',
|
"h-9 w-full inline-flex items-center gap-1.5 rounded-lg p-2",
|
||||||
isActive
|
isActive
|
||||||
? 'bg-black/10 hover:bg-black/20 dark:bg-white/10 text-neutral-900 dark:text-neutral-100 dark:hover:bg-bg-white/20'
|
? "bg-black/10 hover:bg-black/20 dark:bg-white/10 text-neutral-900 dark:text-neutral-100 dark:hover:bg-bg-white/20"
|
||||||
: 'text-neutral-700 hover:bg-black/10 dark:text-neutral-300 dark:hover:bg-white/10',
|
: "text-neutral-700 hover:bg-black/10 dark:text-neutral-300 dark:hover:bg-white/10",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<HardDrives className="size-5 shrink-0" />
|
<CloudArrowDown className="size-5 shrink-0" />
|
||||||
<p className="text-sm font-medium">Relays</p>
|
<p className="text-sm font-medium">Sync</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}}
|
}}
|
||||||
</Link>
|
</Link>
|
||||||
<Link to="/settings/wallet">
|
<Link to="/settings/relays">
|
||||||
{({ isActive }) => {
|
{({ isActive }) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'h-9 w-full inline-flex items-center gap-1.5 rounded-lg p-2',
|
"h-9 w-full inline-flex items-center gap-1.5 rounded-lg p-2",
|
||||||
isActive
|
isActive
|
||||||
? 'bg-black/10 hover:bg-black/20 dark:bg-white/10 text-neutral-900 dark:text-neutral-100 dark:hover:bg-bg-white/20'
|
? "bg-black/10 hover:bg-black/20 dark:bg-white/10 text-neutral-900 dark:text-neutral-100 dark:hover:bg-bg-white/20"
|
||||||
: 'text-neutral-700 hover:bg-black/10 dark:text-neutral-300 dark:hover:bg-white/10',
|
: "text-neutral-700 hover:bg-black/10 dark:text-neutral-300 dark:hover:bg-white/10",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<CurrencyBtc className="size-5 shrink-0" />
|
<HardDrives className="size-5 shrink-0" />
|
||||||
<p className="text-sm font-medium">Wallet</p>
|
<p className="text-sm font-medium">Relays</p>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}}
|
}}
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
<Link to="/settings/wallet">
|
||||||
<ScrollArea.Root
|
{({ isActive }) => {
|
||||||
type={'scroll'}
|
return (
|
||||||
scrollHideDelay={300}
|
<div
|
||||||
className="flex-1 overflow-hidden size-full"
|
className={cn(
|
||||||
>
|
"h-9 w-full inline-flex items-center gap-1.5 rounded-lg p-2",
|
||||||
<ScrollArea.Viewport className="relative h-full pt-12">
|
isActive
|
||||||
<Outlet />
|
? "bg-black/10 hover:bg-black/20 dark:bg-white/10 text-neutral-900 dark:text-neutral-100 dark:hover:bg-bg-white/20"
|
||||||
</ScrollArea.Viewport>
|
: "text-neutral-700 hover:bg-black/10 dark:text-neutral-300 dark:hover:bg-white/10",
|
||||||
<ScrollArea.Scrollbar
|
)}
|
||||||
className="flex select-none touch-none p-0.5 duration-[160ms] ease-out data-[orientation=vertical]:w-2"
|
>
|
||||||
orientation="vertical"
|
<CurrencyBtc className="size-5 shrink-0" />
|
||||||
>
|
<p className="text-sm font-medium">Wallet</p>
|
||||||
<ScrollArea.Thumb className="flex-1 bg-black/10 dark:bg-white/10 rounded-full relative before:content-[''] before:absolute before:top-1/2 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:w-full before:h-full before:min-w-[44px] before:min-h-[44px]" />
|
</div>
|
||||||
</ScrollArea.Scrollbar>
|
);
|
||||||
<ScrollArea.Corner className="bg-transparent" />
|
}}
|
||||||
</ScrollArea.Root>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
)
|
<ScrollArea.Root
|
||||||
|
type={"scroll"}
|
||||||
|
scrollHideDelay={300}
|
||||||
|
className="flex-1 overflow-hidden size-full"
|
||||||
|
>
|
||||||
|
<ScrollArea.Viewport className="relative h-full pt-12">
|
||||||
|
<Outlet />
|
||||||
|
</ScrollArea.Viewport>
|
||||||
|
<ScrollArea.Scrollbar
|
||||||
|
className="flex select-none touch-none p-0.5 duration-[160ms] ease-out data-[orientation=vertical]:w-2"
|
||||||
|
orientation="vertical"
|
||||||
|
>
|
||||||
|
<ScrollArea.Thumb className="flex-1 bg-black/10 dark:bg-white/10 rounded-full relative before:content-[''] before:absolute before:top-1/2 before:left-1/2 before:-translate-x-1/2 before:-translate-y-1/2 before:w-full before:h-full before:min-w-[44px] before:min-h-[44px]" />
|
||||||
|
</ScrollArea.Scrollbar>
|
||||||
|
<ScrollArea.Corner className="bg-transparent" />
|
||||||
|
</ScrollArea.Root>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -69,9 +69,7 @@ function Screen() {
|
|||||||
<div className="relative w-full">
|
<div className="relative w-full">
|
||||||
<div className="flex flex-col gap-6 px-3 pb-3">
|
<div className="flex flex-col gap-6 px-3 pb-3">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h2 className="text-sm font-semibold text-neutral-700 dark:text-neutral-300">
|
<h2 className="text-sm font-semibold">General</h2>
|
||||||
General
|
|
||||||
</h2>
|
|
||||||
<div className="flex flex-col px-3 divide-y divide-black/10 dark:divide-white/10 bg-black/5 dark:bg-white/5 rounded-xl">
|
<div className="flex flex-col px-3 divide-y divide-black/10 dark:divide-white/10 bg-black/5 dark:bg-white/5 rounded-xl">
|
||||||
<Setting
|
<Setting
|
||||||
name="Content Warning"
|
name="Content Warning"
|
||||||
@ -92,9 +90,7 @@ function Screen() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h2 className="text-sm font-semibold text-neutral-700 dark:text-neutral-300">
|
<h2 className="text-sm font-semibold">Appearance</h2>
|
||||||
Appearance
|
|
||||||
</h2>
|
|
||||||
<div className="flex flex-col px-3 divide-y divide-black/10 dark:divide-white/10 bg-black/5 dark:bg-white/5 rounded-xl">
|
<div className="flex flex-col px-3 divide-y divide-black/10 dark:divide-white/10 bg-black/5 dark:bg-white/5 rounded-xl">
|
||||||
<div className="flex items-start justify-between w-full gap-4 py-3">
|
<div className="flex items-start justify-between w-full gap-4 py-3">
|
||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
@ -140,9 +136,7 @@ function Screen() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h2 className="text-sm font-semibold text-neutral-700 dark:text-neutral-300">
|
<h2 className="text-sm font-semibold">Privacy & Performance</h2>
|
||||||
Privacy & Performance
|
|
||||||
</h2>
|
|
||||||
<div className="flex flex-col px-3 divide-y divide-black/10 dark:divide-white/10 bg-black/5 dark:bg-white/5 rounded-xl">
|
<div className="flex flex-col px-3 divide-y divide-black/10 dark:divide-white/10 bg-black/5 dark:bg-white/5 rounded-xl">
|
||||||
<Setting
|
<Setting
|
||||||
name="Resize Service"
|
name="Resize Service"
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router";
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/settings/general")();
|
|
@ -53,9 +53,20 @@ function Screen() {
|
|||||||
<div className="w-full px-3 pb-3">
|
<div className="w-full px-3 pb-3">
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-6">
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<h2 className="text-sm font-semibold text-neutral-700 dark:text-neutral-300">
|
<div>
|
||||||
Connected Relays
|
<h2 className="text-sm font-semibold">Connected Relays</h2>
|
||||||
</h2>
|
<p className="text-sm text-neutral-500">
|
||||||
|
Learn more about Relays{" "}
|
||||||
|
<a
|
||||||
|
href="https://nostr.how/en/relays"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="text-blue-500 !underline"
|
||||||
|
>
|
||||||
|
here
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div className="flex flex-col px-3 divide-y divide-black/10 dark:divide-white/10 bg-black/5 dark:bg-white/5 rounded-xl">
|
<div className="flex flex-col px-3 divide-y divide-black/10 dark:divide-white/10 bg-black/5 dark:bg-white/5 rounded-xl">
|
||||||
<div className="flex items-center h-14">
|
<div className="flex items-center h-14">
|
||||||
<div className="flex items-center w-full gap-2 mb-0">
|
<div className="flex items-center w-full gap-2 mb-0">
|
||||||
|
100
src/routes/settings/sync.lazy.tsx
Normal file
100
src/routes/settings/sync.lazy.tsx
Normal file
@ -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<Channel<number>>(
|
||||||
|
() => new Channel<number>(),
|
||||||
|
);
|
||||||
|
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 (
|
||||||
|
<div className="w-full h-full px-3 pb-3">
|
||||||
|
<div className="h-full flex flex-col w-full gap-2">
|
||||||
|
<div>
|
||||||
|
<h2 className="text-sm font-semibold">Sync events with Negentropy</h2>
|
||||||
|
<p className="text-sm text-neutral-500">
|
||||||
|
Learn more about negentropy{" "}
|
||||||
|
<a
|
||||||
|
href="https://github.com/hoytech/strfry/blob/nextneg/docs/negentropy.md"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="text-blue-500 !underline"
|
||||||
|
>
|
||||||
|
here
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-sm flex flex-col gap-2">
|
||||||
|
<h5 className="font-semibold">Data will be sync:</h5>
|
||||||
|
<div className="w-full h-9 inline-flex items-center px-2 bg-black/5 dark:bg-white/5 rounded-lg text-neutral-700 dark:text-neutral-300">
|
||||||
|
Metadata of all public keys that found in database.
|
||||||
|
</div>
|
||||||
|
<div className="w-full h-9 inline-flex items-center px-2 bg-black/5 dark:bg-white/5 rounded-lg text-neutral-700 dark:text-neutral-300">
|
||||||
|
Contact list of all public keys that found in database.
|
||||||
|
</div>
|
||||||
|
<div className="w-full h-9 inline-flex items-center px-2 bg-black/5 dark:bg-white/5 rounded-lg text-neutral-700 dark:text-neutral-300">
|
||||||
|
Follow and interest sets of all public keys that found in database.
|
||||||
|
</div>
|
||||||
|
<div className="w-full h-9 inline-flex items-center px-2 bg-black/5 dark:bg-white/5 rounded-lg text-neutral-700 dark:text-neutral-300">
|
||||||
|
All notes and reposts of all public keys that found in database.
|
||||||
|
</div>
|
||||||
|
<div className="w-full h-9 inline-flex items-center px-2 bg-black/5 dark:bg-white/5 rounded-lg text-neutral-700 dark:text-neutral-300">
|
||||||
|
All comments all public keys that found in database.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="relative mt-auto flex items-center gap-4 justify-between">
|
||||||
|
<div className="flex-1">
|
||||||
|
<Progress.Root
|
||||||
|
className="relative overflow-hidden bg-black/20 dark:bg-white/20 rounded-full w-full h-1"
|
||||||
|
style={{
|
||||||
|
transform: "translateZ(0)",
|
||||||
|
}}
|
||||||
|
value={progress}
|
||||||
|
>
|
||||||
|
<Progress.Indicator
|
||||||
|
className="bg-blue-500 size-full rounded-full transition-transform duration-[660ms] ease-[cubic-bezier(0.65, 0, 0.35, 1)]"
|
||||||
|
style={{ transform: `translateX(-${100 - progress}%)` }}
|
||||||
|
/>
|
||||||
|
</Progress.Root>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
disabled={isPending}
|
||||||
|
onClick={() => runSync()}
|
||||||
|
className="shrink-0 w-20 h-8 rounded-lg inline-flex items-center justify-center bg-blue-500 hover:bg-blue-600 text-white text-sm font-semibold"
|
||||||
|
>
|
||||||
|
{isPending ? <Spinner className="size-4" /> : "Sync"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -33,9 +33,20 @@ function Screen() {
|
|||||||
return (
|
return (
|
||||||
<div className="w-full px-3 pb-3">
|
<div className="w-full px-3 pb-3">
|
||||||
<div className="flex flex-col w-full gap-2">
|
<div className="flex flex-col w-full gap-2">
|
||||||
<h2 className="text-sm font-semibold text-neutral-700 dark:text-neutral-300">
|
<div>
|
||||||
Wallet
|
<h2 className="text-sm font-semibold">Bitcoin Wallet</h2>
|
||||||
</h2>
|
<p className="text-sm text-neutral-500">
|
||||||
|
Learn more about Zap{" "}
|
||||||
|
<a
|
||||||
|
href="https://nostr.how/en/zaps"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="text-blue-500 !underline"
|
||||||
|
>
|
||||||
|
here
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
<div className="w-full h-44 flex items-center justify-center bg-black/5 dark:bg-white/5 rounded-xl">
|
<div className="w-full h-44 flex items-center justify-center bg-black/5 dark:bg-white/5 rounded-xl">
|
||||||
<Button
|
<Button
|
||||||
onConnected={(provider) =>
|
onConnected={(provider) =>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user