chore: refactor account and fixes

This commit is contained in:
2025-04-21 15:18:02 +07:00
parent a30f2dcc8a
commit 87f038248c
10 changed files with 270 additions and 184 deletions

93
Cargo.lock generated
View File

@@ -219,7 +219,7 @@ dependencies = [
"enumflags2", "enumflags2",
"futures-channel", "futures-channel",
"futures-util", "futures-util",
"rand 0.9.0", "rand 0.9.1",
"serde", "serde",
"serde_repr", "serde_repr",
"url", "url",
@@ -485,9 +485,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-lc-sys" name = "aws-lc-sys"
version = "0.28.0" version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9f7720b74ed28ca77f90769a71fd8c637a0137f6fae4ae947e1050229cff57f" checksum = "0ddeb19ee86cb16ecfc871e5b0660aff6285760957aaedda6284cf0e790d3769"
dependencies = [ dependencies = [
"bindgen 0.69.5", "bindgen 0.69.5",
"cc", "cc",
@@ -1015,9 +1015,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.36" version = "4.5.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04" checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@@ -1025,9 +1025,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.36" version = "4.5.37"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5" checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@@ -1135,7 +1135,7 @@ dependencies = [
[[package]] [[package]]
name = "collections" name = "collections"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" source = "git+https://github.com/zed-industries/zed#107d8ca483276263362c23482612d1cadea81c71"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"rustc-hash 2.1.1", "rustc-hash 2.1.1",
@@ -1524,7 +1524,7 @@ dependencies = [
[[package]] [[package]]
name = "derive_refineable" name = "derive_refineable"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" source = "git+https://github.com/zed-industries/zed#107d8ca483276263362c23482612d1cadea81c71"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -2306,7 +2306,7 @@ dependencies = [
[[package]] [[package]]
name = "gpui" name = "gpui"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" source = "git+https://github.com/zed-industries/zed#107d8ca483276263362c23482612d1cadea81c71"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"as-raw-xcb-connection", "as-raw-xcb-connection",
@@ -2387,6 +2387,7 @@ dependencies = [
"windows 0.61.1", "windows 0.61.1",
"windows-core 0.61.0", "windows-core 0.61.0",
"windows-numerics", "windows-numerics",
"windows-registry 0.5.1",
"workspace-hack", "workspace-hack",
"x11-clipboard", "x11-clipboard",
"x11rb", "x11rb",
@@ -2397,7 +2398,7 @@ dependencies = [
[[package]] [[package]]
name = "gpui_macros" name = "gpui_macros"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" source = "git+https://github.com/zed-industries/zed#107d8ca483276263362c23482612d1cadea81c71"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -2621,7 +2622,7 @@ dependencies = [
[[package]] [[package]]
name = "http_client" name = "http_client"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" source = "git+https://github.com/zed-industries/zed#107d8ca483276263362c23482612d1cadea81c71"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@@ -2638,7 +2639,7 @@ dependencies = [
[[package]] [[package]]
name = "http_client_tls" name = "http_client_tls"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" source = "git+https://github.com/zed-industries/zed#107d8ca483276263362c23482612d1cadea81c71"
dependencies = [ dependencies = [
"rustls", "rustls",
"rustls-platform-verifier", "rustls-platform-verifier",
@@ -3197,7 +3198,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-targets 0.52.6", "windows-targets 0.48.5",
] ]
[[package]] [[package]]
@@ -3379,7 +3380,7 @@ dependencies = [
[[package]] [[package]]
name = "media" name = "media"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" source = "git+https://github.com/zed-industries/zed#107d8ca483276263362c23482612d1cadea81c71"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bindgen 0.71.1", "bindgen 0.71.1",
@@ -3577,7 +3578,7 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]] [[package]]
name = "nostr" name = "nostr"
version = "0.41.0" version = "0.41.0"
source = "git+https://github.com/rust-nostr/nostr#48de47bc7d6283e37e2e980f13608c3ed63286d9" source = "git+https://github.com/rust-nostr/nostr#54bdb993633146ca4bcfbcedd15735fff7dcbada"
dependencies = [ dependencies = [
"aes", "aes",
"base64", "base64",
@@ -3602,7 +3603,7 @@ dependencies = [
[[package]] [[package]]
name = "nostr-connect" name = "nostr-connect"
version = "0.41.0" version = "0.41.0"
source = "git+https://github.com/rust-nostr/nostr#48de47bc7d6283e37e2e980f13608c3ed63286d9" source = "git+https://github.com/rust-nostr/nostr#54bdb993633146ca4bcfbcedd15735fff7dcbada"
dependencies = [ dependencies = [
"async-utility", "async-utility",
"nostr", "nostr",
@@ -3614,7 +3615,7 @@ dependencies = [
[[package]] [[package]]
name = "nostr-database" name = "nostr-database"
version = "0.41.0" version = "0.41.0"
source = "git+https://github.com/rust-nostr/nostr#48de47bc7d6283e37e2e980f13608c3ed63286d9" source = "git+https://github.com/rust-nostr/nostr#54bdb993633146ca4bcfbcedd15735fff7dcbada"
dependencies = [ dependencies = [
"flatbuffers", "flatbuffers",
"lru", "lru",
@@ -3625,7 +3626,7 @@ dependencies = [
[[package]] [[package]]
name = "nostr-lmdb" name = "nostr-lmdb"
version = "0.41.0" version = "0.41.0"
source = "git+https://github.com/rust-nostr/nostr#48de47bc7d6283e37e2e980f13608c3ed63286d9" source = "git+https://github.com/rust-nostr/nostr#54bdb993633146ca4bcfbcedd15735fff7dcbada"
dependencies = [ dependencies = [
"async-utility", "async-utility",
"heed", "heed",
@@ -3638,7 +3639,7 @@ dependencies = [
[[package]] [[package]]
name = "nostr-relay-pool" name = "nostr-relay-pool"
version = "0.41.0" version = "0.41.0"
source = "git+https://github.com/rust-nostr/nostr#48de47bc7d6283e37e2e980f13608c3ed63286d9" source = "git+https://github.com/rust-nostr/nostr#54bdb993633146ca4bcfbcedd15735fff7dcbada"
dependencies = [ dependencies = [
"async-utility", "async-utility",
"async-wsocket", "async-wsocket",
@@ -3655,7 +3656,7 @@ dependencies = [
[[package]] [[package]]
name = "nostr-sdk" name = "nostr-sdk"
version = "0.41.0" version = "0.41.0"
source = "git+https://github.com/rust-nostr/nostr#48de47bc7d6283e37e2e980f13608c3ed63286d9" source = "git+https://github.com/rust-nostr/nostr#54bdb993633146ca4bcfbcedd15735fff7dcbada"
dependencies = [ dependencies = [
"async-utility", "async-utility",
"nostr", "nostr",
@@ -4085,7 +4086,7 @@ dependencies = [
"num", "num",
"num-bigint-dig", "num-bigint-dig",
"pbkdf2", "pbkdf2",
"rand 0.9.0", "rand 0.9.1",
"serde", "serde",
"sha2", "sha2",
"subtle", "subtle",
@@ -4568,7 +4569,7 @@ checksum = "b820744eb4dc9b57a3398183639c511b5a26d2ed702cedd3febaa1393caa22cc"
dependencies = [ dependencies = [
"bytes", "bytes",
"getrandom 0.3.2", "getrandom 0.3.2",
"rand 0.9.0", "rand 0.9.1",
"ring", "ring",
"rustc-hash 2.1.1", "rustc-hash 2.1.1",
"rustls", "rustls",
@@ -4622,13 +4623,12 @@ dependencies = [
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.9.0" version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
dependencies = [ dependencies = [
"rand_chacha 0.9.0", "rand_chacha 0.9.0",
"rand_core 0.9.3", "rand_core 0.9.3",
"zerocopy 0.8.24",
] ]
[[package]] [[package]]
@@ -4813,7 +4813,7 @@ dependencies = [
[[package]] [[package]]
name = "refineable" name = "refineable"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" source = "git+https://github.com/zed-industries/zed#107d8ca483276263362c23482612d1cadea81c71"
dependencies = [ dependencies = [
"derive_refineable", "derive_refineable",
"workspace-hack", "workspace-hack",
@@ -4951,7 +4951,7 @@ dependencies = [
[[package]] [[package]]
name = "reqwest_client" name = "reqwest_client"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" source = "git+https://github.com/zed-industries/zed#107d8ca483276263362c23482612d1cadea81c71"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@@ -4969,9 +4969,9 @@ dependencies = [
[[package]] [[package]]
name = "resvg" name = "resvg"
version = "0.45.0" version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd43d1c474e9dadf09a8fdf22d713ba668b499b5117b9b9079500224e26b5b29" checksum = "a8928798c0a55e03c9ca6c4c6846f76377427d2c1e1f7e6de3c06ae57942df43"
dependencies = [ dependencies = [
"log", "log",
"pico-args", "pico-args",
@@ -5421,7 +5421,7 @@ checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749"
[[package]] [[package]]
name = "semantic_version" name = "semantic_version"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" source = "git+https://github.com/zed-industries/zed#107d8ca483276263362c23482612d1cadea81c71"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"serde", "serde",
@@ -5564,9 +5564,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.2" version = "1.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@@ -5744,7 +5744,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "sum_tree" name = "sum_tree"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" source = "git+https://github.com/zed-industries/zed#107d8ca483276263362c23482612d1cadea81c71"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"log", "log",
@@ -5832,9 +5832,9 @@ dependencies = [
[[package]] [[package]]
name = "svg_fmt" name = "svg_fmt"
version = "0.4.4" version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce5d813d71d82c4cbc1742135004e4a79fd870214c155443451c139c9470a0aa" checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb"
[[package]] [[package]]
name = "svgtypes" name = "svgtypes"
@@ -6411,7 +6411,7 @@ dependencies = [
"http", "http",
"httparse", "httparse",
"log", "log",
"rand 0.9.0", "rand 0.9.1",
"rustls", "rustls",
"rustls-pki-types", "rustls-pki-types",
"sha1", "sha1",
@@ -6588,9 +6588,9 @@ dependencies = [
[[package]] [[package]]
name = "usvg" name = "usvg"
version = "0.45.0" version = "0.45.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ac8e0e3e4696253dc06167990b3fe9a2668ab66270adf949a464db4088cb354" checksum = "80be9b06fbae3b8b303400ab20778c80bbaf338f563afe567cf3c9eea17b47ef"
dependencies = [ dependencies = [
"base64", "base64",
"data-url", "data-url",
@@ -6646,7 +6646,7 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "util" name = "util"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#a7a7335da4ebeb6ebd5c644f108edf2441f7d77b" source = "git+https://github.com/zed-industries/zed#107d8ca483276263362c23482612d1cadea81c71"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-fs", "async-fs",
@@ -7076,7 +7076,7 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
dependencies = [ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.48.0",
] ]
[[package]] [[package]]
@@ -7249,6 +7249,17 @@ dependencies = [
"windows-targets 0.53.0", "windows-targets 0.53.0",
] ]
[[package]]
name = "windows-registry"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad1da3e436dc7653dfdf3da67332e22bff09bb0e28b0239e1624499c7830842e"
dependencies = [
"windows-link",
"windows-result 0.3.2",
"windows-strings 0.4.0",
]
[[package]] [[package]]
name = "windows-result" name = "windows-result"
version = "0.1.2" version = "0.1.2"

View File

@@ -31,6 +31,7 @@ impl Account {
cx.set_global(GlobalAccount(account)); cx.set_global(GlobalAccount(account));
} }
/// Login to the account using the given signer.
pub fn login<S>(&mut self, signer: S, window: &mut Window, cx: &mut Context<Self>) pub fn login<S>(&mut self, signer: S, window: &mut Window, cx: &mut Context<Self>)
where where
S: NostrSigner + 'static, S: NostrSigner + 'static,
@@ -56,12 +57,15 @@ impl Account {
cx.spawn_in(window, async move |this, cx| match task.await { cx.spawn_in(window, async move |this, cx| match task.await {
Ok(profile) => { Ok(profile) => {
cx.update(|_, cx| { cx.update(|window, cx| {
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
this.profile = Some(profile); this.profile(profile, cx);
cx.defer_in(window, |this, _, cx| {
this.subscribe(cx); this.subscribe(cx);
cx.notify(); });
}) })
.ok();
}) })
.ok(); .ok();
} }
@@ -75,28 +79,80 @@ impl Account {
.detach(); .detach();
} }
/// Create a new account with the given metadata.
pub fn new_account(&mut self, metadata: Metadata, window: &mut Window, cx: &mut Context<Self>) { pub fn new_account(&mut self, metadata: Metadata, window: &mut Window, cx: &mut Context<Self>) {
let client = get_client(); const DEFAULT_NIP_65_RELAYS: [&str; 4] = [
"wss://relay.damus.io",
"wss://relay.primal.net",
"wss://relay.nostr.net",
"wss://nos.lol",
];
const DEFAULT_MESSAGING_RELAYS: [&str; 2] =
["wss://auth.nostr1.com", "wss://relay.0xchat.com"];
let keys = Keys::generate(); let keys = Keys::generate();
let public_key = keys.public_key();
let task: Task<Result<Profile, Error>> = cx.background_spawn(async move { let task: Task<Result<Profile, Error>> = cx.background_spawn(async move {
let public_key = keys.public_key(); let client = get_client();
// Update signer // Update signer
client.set_signer(keys).await; client.set_signer(keys).await;
// Set metadata // Set metadata
client.set_metadata(&metadata).await?; client.set_metadata(&metadata).await?;
// Create relay list
let tags: Vec<Tag> = DEFAULT_NIP_65_RELAYS
.into_iter()
.filter_map(|url| {
if let Ok(url) = RelayUrl::parse(url) {
Some(Tag::relay_metadata(url, None))
} else {
None
}
})
.collect();
let builder = EventBuilder::new(Kind::RelayList, "").tags(tags);
if let Err(e) = client.send_event_builder(builder).await {
log::error!("Failed to send relay list event: {}", e);
};
// Create messaging relay list
let tags: Vec<Tag> = DEFAULT_MESSAGING_RELAYS
.into_iter()
.filter_map(|url| {
if let Ok(url) = RelayUrl::parse(url) {
Some(Tag::relay(url))
} else {
None
}
})
.collect();
let builder = EventBuilder::new(Kind::InboxRelays, "").tags(tags);
if let Err(e) = client.send_event_builder(builder).await {
log::error!("Failed to send messaging relay list event: {}", e);
};
Ok(Profile::new(public_key, metadata)) Ok(Profile::new(public_key, metadata))
}); });
cx.spawn_in(window, async move |this, cx| { cx.spawn_in(window, async move |this, cx| {
if let Ok(profile) = task.await { if let Ok(profile) = task.await {
cx.update(|_, cx| { cx.update(|window, cx| {
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
this.profile = Some(profile); this.profile(profile, cx);
cx.defer_in(window, |this, _, cx| {
this.subscribe(cx); this.subscribe(cx);
cx.notify(); });
}) })
.ok();
}) })
.ok(); .ok();
} else { } else {
@@ -109,12 +165,18 @@ impl Account {
.detach(); .detach();
} }
pub fn subscribe(&self, cx: &Context<Self>) { /// Sets the profile for the account.
pub fn profile(&mut self, profile: Profile, cx: &mut Context<Self>) {
self.profile = Some(profile);
cx.notify();
}
/// Subscribes to the current account's metadata.
pub fn subscribe(&self, cx: &mut Context<Self>) {
let Some(profile) = self.profile.as_ref() else { let Some(profile) = self.profile.as_ref() else {
return; return;
}; };
let client = get_client();
let user = profile.public_key(); let user = profile.public_key();
let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE); let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE);
@@ -145,6 +207,7 @@ impl Account {
let new_msg = Filter::new().kind(Kind::GiftWrap).pubkey(user).limit(0); let new_msg = Filter::new().kind(Kind::GiftWrap).pubkey(user).limit(0);
let task: Task<Result<(), Error>> = cx.background_spawn(async move { let task: Task<Result<(), Error>> = cx.background_spawn(async move {
let client = get_client();
client.subscribe(metadata, Some(opts)).await?; client.subscribe(metadata, Some(opts)).await?;
client.subscribe(data, None).await?; client.subscribe(data, None).await?;

View File

@@ -337,13 +337,7 @@ impl Room {
.author(*pubkey) .author(*pubkey)
.limit(1); .limit(1);
let is_ready = client let is_ready = client.database().query(filter).await?.first().is_some();
.database()
.query(filter)
.await
.ok()
.and_then(|events| events.first_owned())
.is_some();
result.push((*pubkey, is_ready)); result.push((*pubkey, is_ready));
} }

View File

@@ -1,8 +1,11 @@
use account::Account; use account::Account;
use anyhow::Error;
use global::get_client;
use gpui::{ use gpui::{
div, impl_internal_actions, prelude::FluentBuilder, px, App, AppContext, Axis, Context, Entity, div, impl_internal_actions, prelude::FluentBuilder, px, App, AppContext, Axis, Context, Entity,
InteractiveElement, IntoElement, ParentElement, Render, Styled, Subscription, Window, InteractiveElement, IntoElement, ParentElement, Render, Styled, Subscription, Task, Window,
}; };
use nostr_sdk::prelude::*;
use serde::Deserialize; use serde::Deserialize;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use std::sync::Arc; use std::sync::Arc;
@@ -13,7 +16,7 @@ use ui::{
ContextModal, IconName, Root, Sizable, TitleBar, ContextModal, IconName, Root, Sizable, TitleBar,
}; };
use crate::views::{chat, compose, contacts, profile, relays, welcome}; use crate::views::{chat, compose, contacts, login, new_account, profile, relays, welcome};
use crate::views::{onboarding, sidebar}; use crate::views::{onboarding, sidebar};
const MODAL_WIDTH: f32 = 420.; const MODAL_WIDTH: f32 = 420.;
@@ -25,6 +28,16 @@ pub fn init(window: &mut Window, cx: &mut App) -> Entity<ChatSpace> {
ChatSpace::new(window, cx) ChatSpace::new(window, cx)
} }
pub fn login(window: &mut Window, cx: &mut App) {
let panel = login::init(window, cx);
ChatSpace::set_center_panel(panel, window, cx);
}
pub fn new_account(window: &mut Window, cx: &mut App) {
let panel = new_account::init(window, cx);
ChatSpace::set_center_panel(panel, window, cx);
}
#[derive(Clone, PartialEq, Eq, Deserialize)] #[derive(Clone, PartialEq, Eq, Deserialize)]
pub enum PanelKind { pub enum PanelKind {
Room(u64), Room(u64),
@@ -37,6 +50,7 @@ pub enum ModalKind {
Compose, Compose,
Contact, Contact,
Relay, Relay,
SetupRelay,
} }
#[derive(Clone, PartialEq, Eq, Deserialize)] #[derive(Clone, PartialEq, Eq, Deserialize)]
@@ -65,10 +79,17 @@ pub struct ChatSpace {
impl ChatSpace { impl ChatSpace {
pub fn new(window: &mut Window, cx: &mut App) -> Entity<Self> { pub fn new(window: &mut Window, cx: &mut App) -> Entity<Self> {
let account = Account::global(cx); let dock = cx.new(|cx| {
let dock = cx.new(|cx| DockArea::new(window, cx)); let panel = Arc::new(onboarding::init(window, cx));
let center = DockItem::panel(panel);
let mut dock = DockArea::new(window, cx);
// Initialize the dock area with the center panel
dock.set_center(center, window, cx);
dock
});
cx.new(|cx| { cx.new(|cx| {
let account = Account::global(cx);
let mut subscriptions = smallvec![]; let mut subscriptions = smallvec![];
subscriptions.push(cx.observe_in( subscriptions.push(cx.observe_in(
@@ -83,35 +104,17 @@ impl ChatSpace {
}, },
)); ));
let mut this = Self { Self {
dock, dock,
subscriptions, subscriptions,
titlebar: false, titlebar: false,
};
if Account::global(cx).read(cx).profile.is_some() {
this.open_chats(window, cx);
} else {
this.open_onboarding(window, cx);
} }
this
}) })
} }
pub fn set_center_panel<P: PanelView>(panel: P, window: &mut Window, cx: &mut App) { fn show_titlebar(&mut self, cx: &mut Context<Self>) {
if let Some(Some(root)) = window.root::<Root>() { self.titlebar = true;
if let Ok(chatspace) = root.read(cx).view().clone().downcast::<ChatSpace>() { cx.notify();
let panel = Arc::new(panel);
let center = DockItem::panel(panel);
chatspace.update(cx, |this, cx| {
this.dock.update(cx, |this, cx| {
this.set_center(center, window, cx);
});
});
}
}
} }
fn open_onboarding(&mut self, window: &mut Window, cx: &mut Context<Self>) { fn open_onboarding(&mut self, window: &mut Window, cx: &mut Context<Self>) {
@@ -147,31 +150,44 @@ impl ChatSpace {
this.set_left_dock(left, Some(px(SIDEBAR_WIDTH)), true, window, cx); this.set_left_dock(left, Some(px(SIDEBAR_WIDTH)), true, window, cx);
this.set_center(center, window, cx); this.set_center(center, window, cx);
}); });
}
fn show_titlebar(&mut self, cx: &mut Context<Self>) { cx.defer_in(window, |this, window, cx| {
self.titlebar = true; let verify_messaging_relays = this.verify_messaging_relays(cx);
cx.notify();
}
fn render_appearance_btn(&self, cx: &mut Context<Self>) -> impl IntoElement { cx.spawn_in(window, async move |_, cx| {
Button::new("appearance") if let Ok(status) = verify_messaging_relays.await {
.xsmall() if !status {
.ghost() cx.update(|window, cx| {
.map(|this| { window.dispatch_action(
if cx.theme().appearance.is_dark() { Box::new(ToggleModal {
this.icon(IconName::Sun) modal: ModalKind::SetupRelay,
} else { }),
this.icon(IconName::Moon) cx,
);
})
.ok();
}
} }
}) })
.on_click(cx.listener(|_, _, window, cx| { .detach();
if cx.theme().appearance.is_dark() { });
Theme::change(Appearance::Light, Some(window), cx);
} else {
Theme::change(Appearance::Dark, Some(window), cx);
} }
}))
fn verify_messaging_relays(&self, cx: &App) -> Task<Result<bool, Error>> {
cx.background_spawn(async move {
let client = get_client();
let signer = client.signer().await?;
let public_key = signer.get_public_key().await?;
let filter = Filter::new()
.kind(Kind::InboxRelays)
.author(public_key)
.limit(1);
let exist = client.database().query(filter).await?.first().is_some();
Ok(exist)
})
} }
fn on_panel_action(&mut self, action: &AddPanel, window: &mut Window, cx: &mut Context<Self>) { fn on_panel_action(&mut self, action: &AddPanel, window: &mut Window, cx: &mut Context<Self>) {
@@ -235,8 +251,32 @@ impl ChatSpace {
.child(relays.clone()) .child(relays.clone())
}); });
} }
ModalKind::SetupRelay => {
let relays = relays::init(window, cx);
window.open_modal(cx, move |this, _, _| {
this.width(px(MODAL_WIDTH))
.title("Your Messaging Relays are not configured")
.child(relays.clone())
});
}
}; };
} }
fn set_center_panel<P: PanelView>(panel: P, window: &mut Window, cx: &mut App) {
if let Some(Some(root)) = window.root::<Root>() {
if let Ok(chatspace) = root.read(cx).view().clone().downcast::<ChatSpace>() {
let panel = Arc::new(panel);
let center = DockItem::panel(panel);
chatspace.update(cx, |this, cx| {
this.dock.update(cx, |this, cx| {
this.set_center(center, window, cx);
});
});
}
}
}
} }
impl Render for ChatSpace { impl Render for ChatSpace {
@@ -266,7 +306,33 @@ impl Render for ChatSpace {
.justify_end() .justify_end()
.gap_2() .gap_2()
.px_2() .px_2()
.child(self.render_appearance_btn(cx)), .child(
Button::new("appearance")
.xsmall()
.ghost()
.map(|this| {
if cx.theme().appearance.is_dark() {
this.icon(IconName::Sun)
} else {
this.icon(IconName::Moon)
}
})
.on_click(cx.listener(|_, _, window, cx| {
if cx.theme().appearance.is_dark() {
Theme::change(
Appearance::Light,
Some(window),
cx,
);
} else {
Theme::change(
Appearance::Dark,
Some(window),
cx,
);
}
})),
),
), ),
) )
}) })

View File

@@ -27,7 +27,7 @@ use std::{collections::HashSet, mem, sync::Arc, time::Duration};
use ui::{theme::Theme, Root}; use ui::{theme::Theme, Root};
pub(crate) mod asset; pub(crate) mod asset;
pub(crate) mod chat_space; pub(crate) mod chatspace;
pub(crate) mod views; pub(crate) mod views;
actions!(coop, [Quit]); actions!(coop, [Quit]);
@@ -84,7 +84,7 @@ fn main() {
}; };
let filter = Filter::new() let filter = Filter::new()
.kind(Kind::ArticlesCurationSet) .kind(Kind::ReleaseArtifactSet)
.coordinate(&coordinate) .coordinate(&coordinate)
.limit(1); .limit(1);
@@ -334,7 +334,7 @@ fn main() {
}) })
.detach(); .detach();
Root::new(chat_space::init(window, cx).into(), window, cx) Root::new(chatspace::init(window, cx).into(), window, cx)
}) })
}) })
.expect("Failed to open window. Please restart the application."); .expect("Failed to open window. Please restart the application.");

View File

@@ -178,6 +178,7 @@ impl Login {
} }
} else if content.starts_with("bunker://") { } else if content.starts_with("bunker://") {
let keys = get_client_keys().to_owned(); let keys = get_client_keys().to_owned();
let Ok(uri) = NostrConnectURI::parse(content.as_ref()) else { let Ok(uri) = NostrConnectURI::parse(content.as_ref()) else {
self.set_error_message("Bunker URL is not valid".to_owned(), cx); self.set_error_message("Bunker URL is not valid".to_owned(), cx);
self.set_logging_in(false, cx); self.set_logging_in(false, cx);
@@ -196,8 +197,8 @@ impl Login {
} }
} }
} else { } else {
self.set_logging_in(false, cx);
window.push_notification(Notification::error(INPUT_INVALID), cx); window.push_notification(Notification::error(INPUT_INVALID), cx);
self.set_logging_in(false, cx);
}; };
} }

View File

@@ -3,7 +3,7 @@ use async_utility::task::spawn;
use common::nip96_upload; use common::nip96_upload;
use global::{constants::IMAGE_SERVICE, get_client}; use global::{constants::IMAGE_SERVICE, get_client};
use gpui::{ use gpui::{
div, img, prelude::FluentBuilder, px, relative, AnyElement, App, AppContext, Context, Entity, div, img, prelude::FluentBuilder, relative, AnyElement, App, AppContext, Context, Entity,
EventEmitter, Flatten, FocusHandle, Focusable, IntoElement, ParentElement, PathPromptOptions, EventEmitter, Flatten, FocusHandle, Focusable, IntoElement, ParentElement, PathPromptOptions,
Render, SharedString, Styled, Window, Render, SharedString, Styled, Window,
}; };
@@ -19,9 +19,6 @@ use ui::{
Disableable, Icon, IconName, Sizable, Size, StyledExt, Disableable, Icon, IconName, Sizable, Size, StyledExt,
}; };
const STEAM_ID_DESCRIPTION: &str =
"Steam ID is used to get your currently playing game and update your status.";
pub fn init(window: &mut Window, cx: &mut App) -> Entity<NewAccount> { pub fn init(window: &mut Window, cx: &mut App) -> Entity<NewAccount> {
NewAccount::new(window, cx) NewAccount::new(window, cx)
} }
@@ -30,7 +27,6 @@ pub struct NewAccount {
name_input: Entity<TextInput>, name_input: Entity<TextInput>,
avatar_input: Entity<TextInput>, avatar_input: Entity<TextInput>,
bio_input: Entity<TextInput>, bio_input: Entity<TextInput>,
steam_input: Entity<TextInput>,
is_uploading: bool, is_uploading: bool,
is_submitting: bool, is_submitting: bool,
// Panel // Panel
@@ -59,12 +55,6 @@ impl NewAccount {
.placeholder("https://example.com/avatar.jpg") .placeholder("https://example.com/avatar.jpg")
}); });
let steam_input = cx.new(|cx| {
TextInput::new(window, cx)
.text_size(Size::Small)
.placeholder("76561199810385277")
});
let bio_input = cx.new(|cx| { let bio_input = cx.new(|cx| {
TextInput::new(window, cx) TextInput::new(window, cx)
.text_size(Size::Small) .text_size(Size::Small)
@@ -75,7 +65,6 @@ impl NewAccount {
Self { Self {
name_input, name_input,
avatar_input, avatar_input,
steam_input,
bio_input, bio_input,
is_uploading: false, is_uploading: false,
is_submitting: false, is_submitting: false,
@@ -92,12 +81,8 @@ impl NewAccount {
let avatar = self.avatar_input.read(cx).text().to_string(); let avatar = self.avatar_input.read(cx).text().to_string();
let name = self.name_input.read(cx).text().to_string(); let name = self.name_input.read(cx).text().to_string();
let bio = self.bio_input.read(cx).text().to_string(); let bio = self.bio_input.read(cx).text().to_string();
let steam = self.steam_input.read(cx).text().to_string();
let mut metadata = Metadata::new() let mut metadata = Metadata::new().display_name(name).about(bio);
.display_name(name)
.about(bio)
.custom_field("steam", steam);
if let Ok(url) = Url::from_str(&avatar) { if let Ok(url) = Url::from_str(&avatar) {
metadata = metadata.picture(url); metadata = metadata.picture(url);
@@ -126,6 +111,7 @@ impl NewAccount {
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
this.set_uploading(false, cx); this.set_uploading(false, cx);
}) })
.ok();
}) })
.ok(); .ok();
@@ -299,21 +285,6 @@ impl Render for NewAccount {
.child("Bio:") .child("Bio:")
.child(self.bio_input.clone()), .child(self.bio_input.clone()),
) )
.child(
div()
.flex()
.flex_col()
.gap_1()
.text_sm()
.child("Steam ID:")
.child(self.steam_input.clone())
.child(
div()
.text_size(px(10.))
.text_color(cx.theme().base.step(cx, ColorScaleStep::ELEVEN))
.child(STEAM_ID_DESCRIPTION),
),
)
.child( .child(
div() div()
.my_2() .my_2()

View File

@@ -10,13 +10,7 @@ use ui::{
Icon, IconName, StyledExt, Icon, IconName, StyledExt,
}; };
use crate::chat_space::ChatSpace; use crate::chatspace;
use super::{login, new_account};
const LOGO_URL: &str = "brand/coop.svg";
const TITLE: &str = "Welcome to Coop!";
const SUBTITLE: &str = "Secure Communication on Nostr.";
pub fn init(window: &mut Window, cx: &mut App) -> Entity<Onboarding> { pub fn init(window: &mut Window, cx: &mut App) -> Entity<Onboarding> {
Onboarding::new(window, cx) Onboarding::new(window, cx)
@@ -42,16 +36,6 @@ impl Onboarding {
focus_handle: cx.focus_handle(), focus_handle: cx.focus_handle(),
} }
} }
fn open_new_account(&self, window: &mut Window, cx: &mut Context<Self>) {
let new_account = new_account::init(window, cx);
ChatSpace::set_center_panel(new_account, window, cx);
}
fn open_login(&self, window: &mut Window, cx: &mut Context<Self>) {
let login = login::init(window, cx);
ChatSpace::set_center_panel(login, window, cx);
}
} }
impl Panel for Onboarding { impl Panel for Onboarding {
@@ -74,10 +58,6 @@ impl Panel for Onboarding {
fn popup_menu(&self, menu: PopupMenu, _cx: &App) -> PopupMenu { fn popup_menu(&self, menu: PopupMenu, _cx: &App) -> PopupMenu {
menu.track_focus(&self.focus_handle) menu.track_focus(&self.focus_handle)
} }
fn toolbar_buttons(&self, _window: &Window, _cx: &App) -> Vec<Button> {
vec![]
}
} }
impl EventEmitter<PanelEvent> for Onboarding {} impl EventEmitter<PanelEvent> for Onboarding {}
@@ -90,6 +70,9 @@ impl Focusable for Onboarding {
impl Render for Onboarding { impl Render for Onboarding {
fn render(&mut self, _window: &mut gpui::Window, cx: &mut Context<Self>) -> impl IntoElement { fn render(&mut self, _window: &mut gpui::Window, cx: &mut Context<Self>) -> impl IntoElement {
const TITLE: &str = "Welcome to Coop!";
const SUBTITLE: &str = "Secure Communication on Nostr.";
div() div()
.py_4() .py_4()
.size_full() .size_full()
@@ -106,7 +89,7 @@ impl Render for Onboarding {
.gap_4() .gap_4()
.child( .child(
svg() svg()
.path(LOGO_URL) .path("brand/coop.svg")
.size_16() .size_16()
.text_color(cx.theme().base.step(cx, ColorScaleStep::THREE)), .text_color(cx.theme().base.step(cx, ColorScaleStep::THREE)),
) )
@@ -139,8 +122,8 @@ impl Render for Onboarding {
.label("Start Messaging") .label("Start Messaging")
.primary() .primary()
.reverse() .reverse()
.on_click(cx.listener(move |this, _, window, cx| { .on_click(cx.listener(move |_, _, window, cx| {
this.open_new_account(window, cx); chatspace::new_account(window, cx);
})), })),
) )
.child( .child(
@@ -148,8 +131,8 @@ impl Render for Onboarding {
.label("Already have an account? Log in.") .label("Already have an account? Log in.")
.ghost() .ghost()
.underline() .underline()
.on_click(cx.listener(move |this, _, window, cx| { .on_click(cx.listener(move |_, _, window, cx| {
this.open_login(window, cx); chatspace::login(window, cx);
})), })),
), ),
) )

View File

@@ -1,4 +1,4 @@
use anyhow::{anyhow, Error}; use anyhow::Error;
use global::{constants::NEW_MESSAGE_SUB_ID, get_client}; use global::{constants::NEW_MESSAGE_SUB_ID, get_client};
use gpui::{ use gpui::{
div, prelude::FluentBuilder, px, uniform_list, App, AppContext, Context, Entity, FocusHandle, div, prelude::FluentBuilder, px, uniform_list, App, AppContext, Context, Entity, FocusHandle,
@@ -41,11 +41,6 @@ impl Relays {
}); });
let relays = cx.new(|cx| { let relays = cx.new(|cx| {
let relays = vec![
RelayUrl::parse("wss://auth.nostr1.com").unwrap(),
RelayUrl::parse("wss://relay.0xchat.com").unwrap(),
];
let task: Task<Result<Vec<RelayUrl>, Error>> = cx.background_spawn(async move { let task: Task<Result<Vec<RelayUrl>, Error>> = cx.background_spawn(async move {
let client = get_client(); let client = get_client();
let signer = client.signer().await?; let signer = client.signer().await?;
@@ -59,16 +54,18 @@ impl Relays {
if let Some(event) = client.database().query(filter).await?.first_owned() { if let Some(event) = client.database().query(filter).await?.first_owned() {
let relays = event let relays = event
.tags .tags
.filter_standardized(TagKind::Relay) .filter(TagKind::Relay)
.filter_map(|t| match t { .filter_map(|tag| RelayUrl::parse(tag.content()?).ok())
TagStandard::Relay(url) => Some(url.to_owned()),
_ => None,
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
Ok(relays) Ok(relays)
} else { } else {
Err(anyhow!("Messaging Relays not found.")) let relays = vec![
RelayUrl::parse("wss://auth.nostr1.com")?,
RelayUrl::parse("wss://relay.0xchat.com")?,
];
Ok(relays)
} }
}); });
@@ -86,7 +83,7 @@ impl Relays {
}) })
.detach(); .detach();
relays vec![]
}); });
cx.new(|cx| { cx.new(|cx| {

View File

@@ -25,7 +25,7 @@ use ui::{
IconName, Sizable, StyledExt, IconName, Sizable, StyledExt,
}; };
use crate::chat_space::{AddPanel, ModalKind, PanelKind, ToggleModal}; use crate::chatspace::{AddPanel, ModalKind, PanelKind, ToggleModal};
mod button; mod button;
mod folder; mod folder;