refactor app state

This commit is contained in:
2024-11-24 15:17:35 +07:00
parent 17e7766401
commit db7e28a78a
12 changed files with 49 additions and 65 deletions

View File

@@ -12,8 +12,8 @@ components = { package = "ui", git = "https://github.com/longbridgeapp/gpui-comp
reqwest_client = { git = "https://github.com/huacnlee/zed.git", branch = "export-platform-window" } reqwest_client = { git = "https://github.com/huacnlee/zed.git", branch = "export-platform-window" }
# Nostr # Nostr
nostr-relay-builder = { git = "https://github.com/rust-nostr/nostr" } nostr-relay-builder = { git = "https://github.com/rust-nostr/nostr", branch = "nip17" }
nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = [ nostr-sdk = { git = "https://github.com/rust-nostr/nostr", branch = "nip17", features = [
"lmdb", "lmdb",
"all-nips", "all-nips",
] } ] }

View File

@@ -8,5 +8,3 @@ gpui.workspace = true
nostr-sdk.workspace = true nostr-sdk.workspace = true
dirs.workspace = true dirs.workspace = true
tokio.workspace = true tokio.workspace = true
keyring-search.workspace = true
keyring.workspace = true

View File

@@ -1,5 +1,4 @@
use gpui::Global; use gpui::Global;
use keyring::Entry;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use state::get_client; use state::get_client;
@@ -18,15 +17,4 @@ impl NostrClient {
Self { client } Self { client }
} }
pub fn add_account(&self, keys: Keys) -> Result<()> {
let public_key = keys.public_key().to_bech32()?;
let secret = keys.secret_key().to_secret_hex();
let entry = Entry::new("Coop Safe Storage", &public_key)?;
// Add secret to keyring
entry.set_password(&secret)?;
Ok(())
}
} }

View File

@@ -1,6 +1,6 @@
use dirs::config_dir; use dirs::config_dir;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use std::fs; use std::{fs, time::Duration};
use tokio::sync::OnceCell; use tokio::sync::OnceCell;
pub static CLIENT: OnceCell<Client> = OnceCell::const_new(); pub static CLIENT: OnceCell<Client> = OnceCell::const_new();
@@ -17,15 +17,17 @@ pub async fn get_client() -> &'static Client {
.expect("Database is NOT initialized"); .expect("Database is NOT initialized");
// Setup Nostr Client // Setup Nostr Client
let client = ClientBuilder::default().database(lmdb).build(); let opts = Options::new().gossip(true).timeout(Duration::from_secs(5));
let client = ClientBuilder::default().database(lmdb).opts(opts).build();
// Add some bootstrap relays // Add some bootstrap relays
let _ = client.add_relay("wss://relay.damus.io").await; let _ = client.add_relay("wss://relay.damus.io").await;
let _ = client.add_relay("wss://relay.primal.net").await; let _ = client.add_relay("wss://relay.primal.net").await;
let _ = client.add_relay("wss://nostr.fmt.wiz.biz").await; let _ = client.add_relay("wss://nostr.fmt.wiz.biz").await;
let _ = client.add_relay("wss://directory.yabu.me").await; let _ = client.add_relay("wss://directory.yabu.me").await;
let _ = client.add_relay("wss://purplepag.es").await;
let _ = client.add_relay("wss://user.kindpag.es/").await; let _ = client.add_discovery_relay("wss://user.kindpag.es/").await;
let _ = client.add_discovery_relay("wss://purplepag.es").await;
// Connect to all relays // Connect to all relays
client.connect().await; client.connect().await;

View File

@@ -22,4 +22,4 @@ reqwest_client.workspace = true
client = { version = "0.1.0", path = "../client" } client = { version = "0.1.0", path = "../client" }
rust-embed = "8.5.0" rust-embed = "8.5.0"
env_logger = "0.11.5" tracing-subscriber = { version = "0.3.18", features = ["fmt"] }

View File

@@ -15,11 +15,11 @@ actions!(main_menu, [Quit]);
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
env_logger::init(); tracing_subscriber::fmt::init();
// Initialize nostr client // Initialize nostr client
let nostr = NostrClient::init().await; let nostr = NostrClient::init().await;
// Initializ app state // Initialize app state
let app_state = AppState::new(); let app_state = AppState::new();
App::new() App::new()
@@ -31,9 +31,8 @@ async fn main() {
// Set custom theme // Set custom theme
let mut theme = Theme::from(ThemeColor::dark()); let mut theme = Theme::from(ThemeColor::dark());
// TODO: support light mode // Set dark mode by default
theme.mode = ThemeMode::Dark; theme.mode = ThemeMode::Dark;
// TODO: adjust color set
// Set app state // Set app state
cx.set_global(theme); cx.set_global(theme);
@@ -43,7 +42,7 @@ async fn main() {
// Set quit action // Set quit action
cx.on_action(quit); cx.on_action(quit);
// Rerender // Refresh
cx.refresh(); cx.refresh();
// Set window size // Set window size

View File

@@ -1,19 +1,15 @@
use gpui::Global; use gpui::Global;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use std::collections::HashSet;
use crate::utils::get_all_accounts_from_keyring;
pub struct AppState { pub struct AppState {
pub accounts: HashSet<PublicKey>, pub signer: Option<PublicKey>,
} }
impl Global for AppState {} impl Global for AppState {}
impl AppState { impl AppState {
pub fn new() -> Self { pub fn new() -> Self {
let accounts = get_all_accounts_from_keyring(); Self { signer: None }
Self { accounts }
} }
} }

View File

@@ -1,12 +1,11 @@
use keyring_search::{Limit, List, Search}; use keyring_search::{Limit, List, Search};
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use std::collections::HashSet;
pub fn get_all_accounts_from_keyring() -> HashSet<PublicKey> { pub fn get_all_accounts_from_keyring() -> Vec<PublicKey> {
let search = Search::new().expect("Keyring not working."); let search = Search::new().expect("Keyring not working.");
let results = search.by_service("Coop Safe Storage"); let results = search.by_service("Coop Safe Storage");
let list = List::list_credentials(&results, Limit::All); let list = List::list_credentials(&results, Limit::All);
let accounts: HashSet<PublicKey> = list let accounts: Vec<PublicKey> = list
.split_whitespace() .split_whitespace()
.filter(|v| v.starts_with("npub1") && !v.ends_with("coop")) .filter(|v| v.starts_with("npub1") && !v.ends_with("coop"))
.filter_map(|i| PublicKey::from_bech32(i).ok()) .filter_map(|i| PublicKey::from_bech32(i).ok())

View File

@@ -11,9 +11,9 @@ pub struct AppView {
impl AppView { impl AppView {
pub fn new(cx: &mut ViewContext<'_, Self>) -> AppView { pub fn new(cx: &mut ViewContext<'_, Self>) -> AppView {
// Onboarding model // Onboarding
let onboarding = cx.new_view(Onboarding::new); let onboarding = cx.new_view(Onboarding::new);
// Chat Space view // Chat Space
let chat_space = cx.new_view(ChatSpace::new); let chat_space = cx.new_view(ChatSpace::new);
AppView { AppView {
@@ -27,7 +27,7 @@ impl Render for AppView {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let mut content = div().size_full().flex().items_center().justify_center(); let mut content = div().size_full().flex().items_center().justify_center();
if cx.global::<AppState>().accounts.is_empty() { if cx.global::<AppState>().signer.is_none() {
content = content.child(self.onboarding.clone()) content = content.child(self.onboarding.clone())
} else { } else {
content = content.child(self.chat_space.clone()) content = content.child(self.chat_space.clone())

View File

@@ -74,27 +74,11 @@ impl RenderOnce for Account {
} }
} }
pub struct BottomBar { pub struct BottomBar {}
accounts: Vec<Account>,
}
impl BottomBar { impl BottomBar {
pub fn new(cx: &mut ViewContext<'_, Self>) -> BottomBar { pub fn new(cx: &mut ViewContext<'_, Self>) -> BottomBar {
let state: Vec<PublicKey> = cx BottomBar {}
.global::<AppState>()
.accounts
.clone()
.into_iter()
.collect();
let win_cx = cx.window_context();
let accounts = state
.into_iter()
.map(|pk| Account::new(pk, win_cx))
.collect::<Vec<_>>();
BottomBar { accounts }
} }
} }
@@ -108,6 +92,5 @@ impl Render for BottomBar {
.items_center() .items_center()
.justify_center() .justify_center()
.gap_1() .gap_1()
.children(self.accounts.clone())
} }
} }

View File

@@ -16,7 +16,6 @@ pub struct ChatSpace {
impl ChatSpace { impl ChatSpace {
pub fn new(cx: &mut ViewContext<'_, Self>) -> Self { pub fn new(cx: &mut ViewContext<'_, Self>) -> Self {
let navigation = cx.new_view(Navigation::new); let navigation = cx.new_view(Navigation::new);
let bottom_bar = cx.new_view(BottomBar::new);
let layout = cx.new_view(|cx| { let layout = cx.new_view(|cx| {
h_resizable(cx) h_resizable(cx)
@@ -28,7 +27,6 @@ impl ChatSpace {
.flex() .flex()
.flex_col() .flex_col()
.child(navigation.clone()) .child(navigation.clone())
.child(bottom_bar.clone())
.into_any_element() .into_any_element()
}), }),
cx, cx,

View File

@@ -4,6 +4,7 @@ use components::{
label::Label, label::Label,
}; };
use gpui::*; use gpui::*;
use keyring::Entry;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use crate::state::AppState; use crate::state::AppState;
@@ -21,17 +22,37 @@ impl Onboarding {
}); });
cx.subscribe(&input, move |_, text_input, input_event, cx| { cx.subscribe(&input, move |_, text_input, input_event, cx| {
let mut async_cx = cx.to_async();
let client = cx.global::<NostrClient>().client;
let view_id = cx.parent_view_id();
if let InputEvent::PressEnter = input_event { if let InputEvent::PressEnter = input_event {
let content = text_input.read(cx).text().to_string(); let content = text_input.read(cx).text().to_string();
if let Ok(keys) = Keys::parse(content) { if let Ok(keys) = Keys::parse(content) {
let public_key = keys.public_key(); cx.foreground_executor()
.spawn(async move {
let public_key = keys.public_key();
let secret = keys.secret_key().to_secret_hex();
if cx.global::<NostrClient>().add_account(keys).is_ok() { let entry =
cx.global_mut::<AppState>().accounts.insert(public_key); Entry::new("Coop Safe Storage", &public_key.to_bech32().unwrap())
cx.notify(); .unwrap();
}
}; // Store private key to OS Keyring
let _ = entry.set_password(&secret);
// Update signer
client.set_signer(keys).await;
// Update view
async_cx.update_global(|app_state: &mut AppState, cx| {
app_state.signer = Some(public_key);
cx.notify(view_id);
})
})
.detach();
}
} }
}) })
.detach(); .detach();