feat: make global state simpler

This commit is contained in:
2025-02-10 20:05:03 +07:00
parent bfc9588738
commit 1bb9729e75
10 changed files with 211 additions and 147 deletions

View File

@@ -12,7 +12,6 @@ path = "src/main.rs"
ui = { path = "../ui" }
common = { path = "../common" }
state = { path = "../state" }
app_state = { path = "../app_state" }
chat_state = { path = "../chat_state" }
gpui.workspace = true
@@ -30,5 +29,19 @@ dirs.workspace = true
rust-embed.workspace = true
smol.workspace = true
cargo-packager-updater = "0.2.2"
tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
log = "0.4"
[package.metadata.packager]
before-packaging-command = "cargo build --release"
product-name = "Coop"
identifier = "su.reya.coop"
resources = ["src", "icons/*", "Cargo.toml", "../../README.md"]
icons = [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico",
]

View File

@@ -1,4 +1,3 @@
use app_state::registry::AppRegistry;
use asset::Assets;
use async_utility::task::spawn;
use chat_state::registry::ChatRegistry;
@@ -215,8 +214,6 @@ fn main() {
.with_assets(Assets)
.with_http_client(Arc::new(reqwest_client::ReqwestClient::new()))
.run(move |cx| {
// Initialize app global state
AppRegistry::set_global(cx);
// Initialize chat global state
ChatRegistry::set_global(cx);

View File

@@ -1,10 +1,8 @@
use app_state::registry::AppRegistry;
use chat_state::registry::ChatRegistry;
use common::profile::NostrProfile;
use gpui::{
actions, div, img, impl_internal_actions, px, App, AppContext, Axis, BorrowAppContext, Context,
Entity, InteractiveElement, IntoElement, ObjectFit, ParentElement, Render, Styled, StyledImage,
Window,
actions, div, img, impl_internal_actions, px, App, AppContext, Axis, Context, Entity,
InteractiveElement, IntoElement, ObjectFit, ParentElement, Render, Styled, StyledImage, Window,
};
use nostr_sdk::prelude::*;
use serde::Deserialize;
@@ -200,13 +198,11 @@ impl AppView {
}
}
PanelKind::Profile => {
if let Some(profile) = cx.global::<AppRegistry>().user() {
let panel = Arc::new(profile::init(profile, window, cx));
let panel = Arc::new(profile::init(self.account.clone(), window, cx));
self.dock.update(cx, |dock_area, cx| {
dock_area.add_panel(panel, action.position, window, cx);
});
}
self.dock.update(cx, |dock_area, cx| {
dock_area.add_panel(panel, action.position, window, cx);
});
}
PanelKind::Contacts => {
let panel = Arc::new(contacts::init(window, cx));
@@ -229,10 +225,6 @@ impl AppView {
cx.background_spawn(async move { get_client().reset().await })
.detach();
cx.update_global::<AppRegistry, _>(|this, _cx| {
this.set_user(None);
});
window.replace_root(cx, |window, cx| {
Root::new(onboarding::init(window, cx).into(), window, cx)
});

View File

@@ -1,8 +1,7 @@
use app_state::registry::AppRegistry;
use common::{profile::NostrProfile, qr::create_qr};
use common::{profile::NostrProfile, qr::create_qr, utils::preload};
use gpui::{
div, img, prelude::FluentBuilder, relative, svg, App, AppContext, BorrowAppContext,
ClipboardItem, Context, Div, Entity, IntoElement, ParentElement, Render, Styled, Window,
div, img, prelude::FluentBuilder, relative, svg, App, AppContext, ClipboardItem, Context, Div,
Entity, IntoElement, ParentElement, Render, Styled, Window,
};
use nostr_connect::prelude::*;
use state::get_client;
@@ -103,8 +102,10 @@ impl Onboarding {
.ok()
.unwrap_or_default();
_ = client.set_signer(signer).await;
_ = tx.send(NostrProfile::new(*public_key, metadata));
if tx.send(NostrProfile::new(*public_key, metadata)).is_ok() {
_ = client.set_signer(signer).await;
_ = preload(client, *public_key).await;
}
}
}
}
@@ -113,10 +114,6 @@ impl Onboarding {
if let Ok(profile) = rx.await {
_ = cx.update_window(window_handle, |_, window, cx| {
cx.update_global::<AppRegistry, _>(|this, _cx| {
this.set_user(Some(profile.clone()));
});
window.replace_root(cx, |window, cx| {
Root::new(app::init(profile, window, cx).into(), window, cx)
});
@@ -173,18 +170,16 @@ impl Onboarding {
.ok()
.unwrap_or_default();
_ = client.set_signer(keys).await;
_ = tx.send(NostrProfile::new(public_key, metadata));
if tx.send(NostrProfile::new(public_key, metadata)).is_ok() {
_ = client.set_signer(keys).await;
_ = preload(client, public_key).await;
}
}
})
.detach();
if let Ok(profile) = rx.await {
_ = cx.update_window(window_handle, |_, window, cx| {
cx.update_global::<AppRegistry, _>(|this, _cx| {
this.set_user(Some(profile.clone()));
});
window.replace_root(cx, |window, cx| {
Root::new(app::init(profile, window, cx).into(), window, cx)
});

View File

@@ -1,4 +1,3 @@
use app_state::registry::AppRegistry;
use chat_state::registry::ChatRegistry;
use common::{
constants::FAKE_SIG,
@@ -141,18 +140,9 @@ impl Compose {
return;
}
let current_user = if let Some(profile) = cx.global::<AppRegistry>().user() {
profile
} else {
return;
};
// Show loading spinner
self.set_submitting(true, cx);
// Get nostr client
let client = get_client();
// Get message from user's input
let content = message.to_string();
@@ -163,9 +153,7 @@ impl Compose {
);
// Get all pubkeys
let current_user = current_user.public_key();
let mut pubkeys: Vec<PublicKey> = selected.iter().copied().collect();
pubkeys.push(current_user);
// Convert selected pubkeys into Nostr tags
let mut tag_list: Vec<Tag> = selected.iter().map(|pk| Tag::public_key(*pk)).collect();
@@ -178,14 +166,18 @@ impl Compose {
let (tx, rx) = oneshot::channel::<Event>();
cx.background_spawn(async move {
let client = get_client();
let public_key = signer_public_key(client).await.unwrap();
let mut event: Option<Event> = None;
pubkeys.push(public_key);
for pubkey in pubkeys.iter() {
if let Ok(output) = client
.send_private_msg(*pubkey, &content, tags.clone())
.await
{
if pubkey == &current_user && event.is_none() {
if pubkey == &public_key && event.is_none() {
if let Ok(Some(ev)) = client.database().event_by_id(&output.val).await {
if let Ok(UnwrappedGift { mut rumor, .. }) =
client.unwrap_gift_wrap(&ev).await

View File

@@ -1,12 +0,0 @@
[package]
name = "app_state"
version = "0.1.0"
edition = "2021"
publish = false
[dependencies]
common = { path = "../common" }
state = { path = "../state" }
gpui.workspace = true
nostr-sdk.workspace = true

View File

@@ -1 +0,0 @@
pub mod registry;

View File

@@ -1,75 +0,0 @@
use common::{
constants::{ALL_MESSAGES_SUB_ID, NEW_MESSAGE_SUB_ID},
profile::NostrProfile,
};
use gpui::{App, AppContext, Global};
use nostr_sdk::prelude::*;
use state::get_client;
use std::time::Duration;
pub struct AppRegistry {
user: Option<NostrProfile>,
}
impl Global for AppRegistry {}
impl AppRegistry {
pub fn set_global(cx: &mut App) {
cx.observe_global::<Self>(|cx| {
if let Some(profile) = cx.global::<Self>().user() {
let client = get_client();
let public_key = profile.public_key();
cx.background_spawn(async move {
let subscription = Filter::new()
.kind(Kind::ContactList)
.author(public_key)
.limit(1);
// Get contact list
_ = client.sync(subscription, &SyncOptions::default()).await;
let all_messages_sub_id = SubscriptionId::new(ALL_MESSAGES_SUB_ID);
let new_message_sub_id = SubscriptionId::new(NEW_MESSAGE_SUB_ID);
// Create a filter for getting all gift wrapped events send to current user
let all_messages = Filter::new().kind(Kind::GiftWrap).pubkey(public_key);
// Create a filter for getting new message
let new_message = Filter::new()
.kind(Kind::GiftWrap)
.pubkey(public_key)
.limit(0);
// Subscribe for all messages
_ = client
.subscribe_with_id(
all_messages_sub_id,
all_messages,
Some(SubscribeAutoCloseOptions::default().exit_policy(
ReqExitPolicy::WaitDurationAfterEOSE(Duration::from_secs(3)),
)),
)
.await;
// Subscribe for new message
_ = client
.subscribe_with_id(new_message_sub_id, new_message, None)
.await
})
.detach();
}
})
.detach();
cx.set_global(Self { user: None });
}
pub fn set_user(&mut self, profile: Option<NostrProfile>) {
self.user = profile;
}
pub fn user(&self) -> Option<NostrProfile> {
self.user.clone()
}
}

View File

@@ -1,10 +1,11 @@
use crate::constants::NIP96_SERVER;
use crate::constants::{ALL_MESSAGES_SUB_ID, NEW_MESSAGE_SUB_ID, NIP96_SERVER};
use itertools::Itertools;
use nostr_sdk::prelude::*;
use rnglib::{Language, RNG};
use std::{
collections::HashSet,
hash::{DefaultHasher, Hash, Hasher},
time::Duration,
};
pub async fn signer_public_key(client: &Client) -> anyhow::Result<PublicKey, anyhow::Error> {
@@ -14,6 +15,47 @@ pub async fn signer_public_key(client: &Client) -> anyhow::Result<PublicKey, any
Ok(public_key)
}
pub async fn preload(client: &Client, public_key: PublicKey) -> anyhow::Result<(), anyhow::Error> {
let subscription = Filter::new()
.kind(Kind::ContactList)
.author(public_key)
.limit(1);
// Get contact list
_ = client.sync(subscription, &SyncOptions::default()).await;
let all_messages_sub_id = SubscriptionId::new(ALL_MESSAGES_SUB_ID);
let new_message_sub_id = SubscriptionId::new(NEW_MESSAGE_SUB_ID);
// Create a filter for getting all gift wrapped events send to current user
let all_messages = Filter::new().kind(Kind::GiftWrap).pubkey(public_key);
// Create a filter for getting new message
let new_message = Filter::new()
.kind(Kind::GiftWrap)
.pubkey(public_key)
.limit(0);
// Subscribe for all messages
_ = client
.subscribe_with_id(
all_messages_sub_id,
all_messages,
Some(
SubscribeAutoCloseOptions::default()
.exit_policy(ReqExitPolicy::WaitDurationAfterEOSE(Duration::from_secs(3))),
),
)
.await;
// Subscribe for new message
_ = client
.subscribe_with_id(new_message_sub_id, new_message, None)
.await;
Ok(())
}
pub async fn nip96_upload(client: &Client, file: Vec<u8>) -> anyhow::Result<Url, anyhow::Error> {
let signer = client.signer().await?;
let server_url = Url::parse(NIP96_SERVER)?;