Continue redesign for the v1 stable release #5

Merged
reya merged 11 commits from redesign-2 into master 2026-02-12 08:32:17 +00:00
10 changed files with 226 additions and 190 deletions
Showing only changes of commit 58571a806e - Show all commits

78
Cargo.lock generated
View File

@@ -601,9 +601,9 @@ dependencies = [
[[package]] [[package]]
name = "avif-serialize" name = "avif-serialize"
version = "0.8.6" version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f" checksum = "375082f007bd67184fb9c0374614b29f9aaa604ec301635f72338bb65386a53d"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
] ]
@@ -620,9 +620,9 @@ dependencies = [
[[package]] [[package]]
name = "aws-lc-sys" name = "aws-lc-sys"
version = "0.37.0" version = "0.37.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c34dda4df7017c8db52132f0f8a2e0f8161649d15723ed63fc00c82d0f2081a" checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549"
dependencies = [ dependencies = [
"cc", "cc",
"cmake", "cmake",
@@ -1142,9 +1142,9 @@ dependencies = [
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.57" version = "4.5.58"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6899ea499e3fb9305a65d5ebf6e3d2248c5fab291f300ad0a704fbe142eae31a" checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@@ -1152,9 +1152,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.57" version = "4.5.58"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b12c8b680195a62a8364d16b8447b01b6c2c8f9aaf68bee653be34d4245e238" checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
@@ -1176,9 +1176,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.7.7" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
[[package]] [[package]]
name = "cmake" name = "cmake"
@@ -1263,7 +1263,7 @@ dependencies = [
[[package]] [[package]]
name = "collections" name = "collections"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"rustc-hash 2.1.1", "rustc-hash 2.1.1",
@@ -1708,7 +1708,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#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -2639,7 +2639,7 @@ dependencies = [
[[package]] [[package]]
name = "gpui" name = "gpui"
version = "0.2.2" version = "0.2.2"
source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"as-raw-xcb-connection", "as-raw-xcb-connection",
@@ -2741,7 +2741,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#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
@@ -2752,7 +2752,7 @@ dependencies = [
[[package]] [[package]]
name = "gpui_tokio" name = "gpui_tokio"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"gpui", "gpui",
@@ -2987,7 +2987,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#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-compression", "async-compression",
@@ -3012,7 +3012,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#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"rustls", "rustls",
"rustls-platform-verifier", "rustls-platform-verifier",
@@ -3567,7 +3567,7 @@ checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
dependencies = [ dependencies = [
"bitflags 2.10.0", "bitflags 2.10.0",
"libc", "libc",
"redox_syscall 0.7.0", "redox_syscall 0.7.1",
] ]
[[package]] [[package]]
@@ -3780,7 +3780,7 @@ dependencies = [
[[package]] [[package]]
name = "media" name = "media"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bindgen", "bindgen",
@@ -4020,7 +4020,7 @@ checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
[[package]] [[package]]
name = "nostr" name = "nostr"
version = "0.44.1" version = "0.44.1"
source = "git+https://github.com/rust-nostr/nostr#aeee536bdec863afffffe4819150230e20b8ad6b" source = "git+https://github.com/rust-nostr/nostr#b968620ae4b8c31ee1cbcdb8ef8ec9ccd87e1fc6"
dependencies = [ dependencies = [
"aes", "aes",
"base64", "base64",
@@ -4045,7 +4045,7 @@ dependencies = [
[[package]] [[package]]
name = "nostr-connect" name = "nostr-connect"
version = "0.44.0" version = "0.44.0"
source = "git+https://github.com/rust-nostr/nostr#aeee536bdec863afffffe4819150230e20b8ad6b" source = "git+https://github.com/rust-nostr/nostr#b968620ae4b8c31ee1cbcdb8ef8ec9ccd87e1fc6"
dependencies = [ dependencies = [
"async-utility", "async-utility",
"futures-core", "futures-core",
@@ -4058,7 +4058,7 @@ dependencies = [
[[package]] [[package]]
name = "nostr-database" name = "nostr-database"
version = "0.44.0" version = "0.44.0"
source = "git+https://github.com/rust-nostr/nostr#aeee536bdec863afffffe4819150230e20b8ad6b" source = "git+https://github.com/rust-nostr/nostr#b968620ae4b8c31ee1cbcdb8ef8ec9ccd87e1fc6"
dependencies = [ dependencies = [
"btreecap", "btreecap",
"flatbuffers", "flatbuffers",
@@ -4070,7 +4070,7 @@ dependencies = [
[[package]] [[package]]
name = "nostr-gossip" name = "nostr-gossip"
version = "0.44.0" version = "0.44.0"
source = "git+https://github.com/rust-nostr/nostr#aeee536bdec863afffffe4819150230e20b8ad6b" source = "git+https://github.com/rust-nostr/nostr#b968620ae4b8c31ee1cbcdb8ef8ec9ccd87e1fc6"
dependencies = [ dependencies = [
"nostr", "nostr",
] ]
@@ -4078,7 +4078,7 @@ dependencies = [
[[package]] [[package]]
name = "nostr-gossip-memory" name = "nostr-gossip-memory"
version = "0.44.0" version = "0.44.0"
source = "git+https://github.com/rust-nostr/nostr#aeee536bdec863afffffe4819150230e20b8ad6b" source = "git+https://github.com/rust-nostr/nostr#b968620ae4b8c31ee1cbcdb8ef8ec9ccd87e1fc6"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"lru", "lru",
@@ -4090,7 +4090,7 @@ dependencies = [
[[package]] [[package]]
name = "nostr-lmdb" name = "nostr-lmdb"
version = "0.44.0" version = "0.44.0"
source = "git+https://github.com/rust-nostr/nostr#aeee536bdec863afffffe4819150230e20b8ad6b" source = "git+https://github.com/rust-nostr/nostr#b968620ae4b8c31ee1cbcdb8ef8ec9ccd87e1fc6"
dependencies = [ dependencies = [
"async-utility", "async-utility",
"flume", "flume",
@@ -4104,7 +4104,7 @@ dependencies = [
[[package]] [[package]]
name = "nostr-sdk" name = "nostr-sdk"
version = "0.44.1" version = "0.44.1"
source = "git+https://github.com/rust-nostr/nostr#aeee536bdec863afffffe4819150230e20b8ad6b" source = "git+https://github.com/rust-nostr/nostr#b968620ae4b8c31ee1cbcdb8ef8ec9ccd87e1fc6"
dependencies = [ dependencies = [
"async-utility", "async-utility",
"async-wsocket", "async-wsocket",
@@ -4638,7 +4638,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
[[package]] [[package]]
name = "perf" name = "perf"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"collections", "collections",
"serde", "serde",
@@ -5258,9 +5258,9 @@ dependencies = [
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.7.0" version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b"
dependencies = [ dependencies = [
"bitflags 2.10.0", "bitflags 2.10.0",
] ]
@@ -5299,7 +5299,7 @@ dependencies = [
[[package]] [[package]]
name = "refineable" name = "refineable"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"derive_refineable", "derive_refineable",
] ]
@@ -5398,7 +5398,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#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@@ -5453,7 +5453,7 @@ dependencies = [
[[package]] [[package]]
name = "rope" name = "rope"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"log", "log",
@@ -5715,7 +5715,7 @@ dependencies = [
[[package]] [[package]]
name = "scheduler" name = "scheduler"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"async-task", "async-task",
"backtrace", "backtrace",
@@ -6330,7 +6330,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#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"arrayvec", "arrayvec",
"log", "log",
@@ -7297,7 +7297,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#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-fs", "async-fs",
@@ -7335,7 +7335,7 @@ dependencies = [
[[package]] [[package]]
name = "util_macros" name = "util_macros"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"perf", "perf",
"quote", "quote",
@@ -8936,7 +8936,7 @@ dependencies = [
[[package]] [[package]]
name = "zlog" name = "zlog"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",
@@ -8953,7 +8953,7 @@ checksum = "4de98dfa5d5b7fef4ee834d0073d560c9ca7b6c46a71d058c48db7960f8cfaf7"
[[package]] [[package]]
name = "ztracing" name = "ztracing"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
dependencies = [ dependencies = [
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
@@ -8964,7 +8964,7 @@ dependencies = [
[[package]] [[package]]
name = "ztracing_macro" name = "ztracing_macro"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/zed-industries/zed#6cd7586c161b579ba7c1ecad7dea1b95ca3dd239" source = "git+https://github.com/zed-industries/zed#ee3f40fe25d206ca363b753e5b86e09ac6181eca"
[[package]] [[package]]
name = "zune-core" name = "zune-core"

View File

@@ -16,9 +16,10 @@ gpui_tokio = { git = "https://github.com/zed-industries/zed" }
reqwest_client = { git = "https://github.com/zed-industries/zed" } reqwest_client = { git = "https://github.com/zed-industries/zed" }
# Nostr # Nostr
nostr = { git = "https://github.com/rust-nostr/nostr", features = [ "nip96", "nip59", "nip49", "nip44" ] }
nostr-sdk = { git = "https://github.com/rust-nostr/nostr" }
nostr-lmdb = { git = "https://github.com/rust-nostr/nostr" } nostr-lmdb = { git = "https://github.com/rust-nostr/nostr" }
nostr-connect = { git = "https://github.com/rust-nostr/nostr" } nostr-connect = { git = "https://github.com/rust-nostr/nostr" }
nostr-sdk = { git = "https://github.com/rust-nostr/nostr", features = [ "nip96", "nip59", "nip49", "nip44" ] }
nostr-gossip-memory = { git = "https://github.com/rust-nostr/nostr" } nostr-gossip-memory = { git = "https://github.com/rust-nostr/nostr" }
# Others # Others

View File

@@ -6,6 +6,7 @@ publish.workspace = true
[dependencies] [dependencies]
gpui.workspace = true gpui.workspace = true
nostr.workspace = true
nostr-sdk.workspace = true nostr-sdk.workspace = true
anyhow.workspace = true anyhow.workspace = true
@@ -19,4 +20,3 @@ log.workspace = true
dirs = "5.0" dirs = "5.0"
qrcode = "0.14.1" qrcode = "0.14.1"
nostr = { git = "https://github.com/rust-nostr/nostr" }

View File

@@ -32,10 +32,10 @@ impl GreeterPanel {
fn add_profile_panel(&mut self, window: &mut Window, cx: &mut Context<Self>) { fn add_profile_panel(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
let signer_pkey = nostr.read(cx).signer_pkey(cx); let signer = nostr.read(cx).signer();
if let Some(public_key) = signer.public_key() {
cx.spawn_in(window, async move |_this, cx| { cx.spawn_in(window, async move |_this, cx| {
if let Ok(public_key) = signer_pkey.await {
cx.update(|window, cx| { cx.update(|window, cx| {
Workspace::add_panel( Workspace::add_panel(
profile::init(public_key, window, cx), profile::init(public_key, window, cx),
@@ -45,11 +45,11 @@ impl GreeterPanel {
); );
}) })
.ok(); .ok();
}
}) })
.detach(); .detach();
} }
} }
}
impl Panel for GreeterPanel { impl Panel for GreeterPanel {
fn panel_id(&self) -> SharedString { fn panel_id(&self) -> SharedString {
@@ -84,6 +84,8 @@ impl Render for GreeterPanel {
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
let nip65_state = nostr.read(cx).nip65_state(); let nip65_state = nostr.read(cx).nip65_state();
let nip17_state = nostr.read(cx).nip17_state(); let nip17_state = nostr.read(cx).nip17_state();
let signer = nostr.read(cx).signer();
let owned = signer.owned();
let required_actions = nip65_state.read(cx) == &RelayState::NotConfigured let required_actions = nip65_state.read(cx) == &RelayState::NotConfigured
|| nip17_state.read(cx) == &RelayState::NotConfigured; || nip17_state.read(cx) == &RelayState::NotConfigured;
@@ -186,7 +188,7 @@ impl Render for GreeterPanel {
), ),
) )
}) })
.when(!nostr.read(cx).owned_signer(), |this| { .when(!owned, |this| {
this.child( this.child(
v_flex() v_flex()
.gap_2() .gap_2()

View File

@@ -321,13 +321,13 @@ impl Sidebar {
let async_chat = chat.downgrade(); let async_chat = chat.downgrade();
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
let signer_pkey = nostr.read(cx).signer_pkey(cx); let signer = nostr.read(cx).signer();
// Get all selected public keys // Get all selected public keys
let receivers = self.get_selected(cx); let receivers = self.get_selected(cx);
self.tasks.push(cx.spawn_in(window, async move |this, cx| { self.tasks.push(cx.spawn_in(window, async move |this, cx| {
let public_key = signer_pkey.await?; let public_key = signer.get_public_key().await?;
// Create a new room and emit it // Create a new room and emit it
async_chat.update_in(cx, |this, _window, cx| { async_chat.update_in(cx, |this, _window, cx| {

View File

@@ -2,14 +2,13 @@ use std::sync::Arc;
use chat::{ChatEvent, ChatRegistry}; use chat::{ChatEvent, ChatRegistry};
use dock::dock::DockPlacement; use dock::dock::DockPlacement;
use dock::panel::PanelView; use dock::panel::{PanelStyle, PanelView};
use dock::{ClosePanel, DockArea, DockItem}; use dock::{ClosePanel, DockArea, DockItem};
use gpui::prelude::FluentBuilder; use gpui::prelude::FluentBuilder;
use gpui::{ use gpui::{
div, rems, App, AppContext, Axis, Context, Entity, InteractiveElement, IntoElement, div, rems, App, AppContext, Axis, Context, Entity, InteractiveElement, IntoElement,
ParentElement, Render, SharedString, Styled, Subscription, Window, ParentElement, Render, SharedString, Styled, Subscription, Window,
}; };
use nostr_sdk::prelude::*;
use person::PersonRegistry; use person::PersonRegistry;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use state::{NostrRegistry, RelayState}; use state::{NostrRegistry, RelayState};
@@ -34,24 +33,15 @@ pub struct Workspace {
/// App's Dock Area /// App's Dock Area
dock: Entity<DockArea>, dock: Entity<DockArea>,
/// Current User
current_user: Entity<Option<PublicKey>>,
/// Event subscriptions /// Event subscriptions
_subscriptions: SmallVec<[Subscription; 3]>, _subscriptions: SmallVec<[Subscription; 3]>,
} }
impl Workspace { impl Workspace {
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self { fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
let titlebar = cx.new(|_| TitleBar::new());
let dock =
cx.new(|cx| DockArea::new(window, cx).panel_style(dock::panel::PanelStyle::TabBar));
let chat = ChatRegistry::global(cx); let chat = ChatRegistry::global(cx);
let current_user = cx.new(|_| None); let titlebar = cx.new(|_| TitleBar::new());
let dock = cx.new(|cx| DockArea::new(window, cx).style(PanelStyle::TabBar));
let nostr = NostrRegistry::global(cx);
let nip65_state = nostr.read(cx).nip65_state();
let mut subscriptions = smallvec![]; let mut subscriptions = smallvec![];
@@ -99,15 +89,6 @@ impl Workspace {
}), }),
); );
subscriptions.push(
// Observe the NIP-65 state
cx.observe(&nip65_state, move |this, state, cx| {
if state.read(cx).idle() || state.read(cx).checking() {
this.get_current_user(cx);
}
}),
);
// Set the default layout for app's dock // Set the default layout for app's dock
cx.defer_in(window, |this, window, cx| { cx.defer_in(window, |this, window, cx| {
this.set_layout(window, cx); this.set_layout(window, cx);
@@ -116,7 +97,6 @@ impl Workspace {
Self { Self {
titlebar, titlebar,
dock, dock,
current_user,
_subscriptions: subscriptions, _subscriptions: subscriptions,
} }
} }
@@ -181,37 +161,19 @@ impl Workspace {
}); });
} }
fn get_current_user(&self, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let current_user = self.current_user.downgrade();
cx.spawn(async move |_this, cx| {
if let Some(signer) = client.signer() {
if let Ok(public_key) = signer.get_public_key().await {
current_user
.update(cx, |this, cx| {
*this = Some(public_key);
cx.notify();
})
.ok();
}
}
})
.detach();
}
fn titlebar_left(&mut self, _window: &mut Window, cx: &Context<Self>) -> impl IntoElement { fn titlebar_left(&mut self, _window: &mut Window, cx: &Context<Self>) -> impl IntoElement {
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
let nip65 = nostr.read(cx).nip65_state(); let nip65 = nostr.read(cx).nip65_state();
let nip17 = nostr.read(cx).nip17_state(); let nip17 = nostr.read(cx).nip17_state();
let signer = nostr.read(cx).signer();
let current_user = signer.public_key();
h_flex() h_flex()
.h(TITLEBAR_HEIGHT) .h(TITLEBAR_HEIGHT)
.flex_shrink_0() .flex_shrink_0()
.justify_between() .justify_between()
.gap_2() .gap_2()
.when_some(self.current_user.read(cx).as_ref(), |this, public_key| { .when_some(current_user.as_ref(), |this, public_key| {
let persons = PersonRegistry::global(cx); let persons = PersonRegistry::global(cx);
let profile = persons.read(cx).get(public_key, cx); let profile = persons.read(cx).get(public_key, cx);
@@ -232,6 +194,19 @@ impl Workspace {
}), }),
) )
}) })
.when(nostr.read(cx).creating_signer(), |this| {
this.child(div().text_xs().text_color(cx.theme().text_muted).child(
SharedString::from("Coop is creating a new identity for you..."),
))
})
.when(!nostr.read(cx).connected(), |this| {
this.child(
div()
.text_xs()
.text_color(cx.theme().text_muted)
.child(SharedString::from("Connecting...")),
)
})
.map(|this| match nip65.read(cx) { .map(|this| match nip65.read(cx) {
RelayState::Checking => this.child( RelayState::Checking => this.child(
div() div()
@@ -247,7 +222,7 @@ impl Workspace {
.text_xs() .text_xs()
.text_color(cx.theme().warning_foreground) .text_color(cx.theme().warning_foreground)
.bg(cx.theme().warning_background) .bg(cx.theme().warning_background)
.rounded_xs() .rounded_sm()
.child(SharedString::from("User hasn't configured a relay list")), .child(SharedString::from("User hasn't configured a relay list")),
), ),
_ => this, _ => this,
@@ -266,7 +241,7 @@ impl Workspace {
.text_xs() .text_xs()
.text_color(cx.theme().warning_foreground) .text_color(cx.theme().warning_foreground)
.bg(cx.theme().warning_background) .bg(cx.theme().warning_background)
.rounded_xs() .rounded_sm()
.child(SharedString::from( .child(SharedString::from(
"User hasn't configured a messaging relay list", "User hasn't configured a messaging relay list",
)), )),

View File

@@ -351,7 +351,7 @@ impl DockArea {
} }
/// Set the panel style of the dock area. /// Set the panel style of the dock area.
pub fn panel_style(mut self, style: PanelStyle) -> Self { pub fn style(mut self, style: PanelStyle) -> Self {
self.panel_style = style; self.panel_style = style;
self self
} }

View File

@@ -37,13 +37,12 @@ pub const USER_GIFTWRAP: &str = "user-gift-wraps";
pub const WOT_RELAYS: [&str; 1] = ["wss://relay.vertexlab.io"]; pub const WOT_RELAYS: [&str; 1] = ["wss://relay.vertexlab.io"];
/// Default search relays /// Default search relays
pub const SEARCH_RELAYS: [&str; 2] = ["wss://antiprimal.net", "wss://relay.noswhere.com"]; pub const SEARCH_RELAYS: [&str; 1] = ["wss://antiprimal.net"];
/// Default bootstrap relays /// Default bootstrap relays
pub const BOOTSTRAP_RELAYS: [&str; 4] = [ pub const BOOTSTRAP_RELAYS: [&str; 3] = [
"wss://relay.damus.io", "wss://relay.damus.io",
"wss://relay.primal.net", "wss://relay.primal.net",
"wss://relay.nos.social",
"wss://user.kindpag.es", "wss://user.kindpag.es",
]; ];

View File

@@ -48,14 +48,15 @@ pub struct NostrRegistry {
/// Nostr client /// Nostr client
client: Client, client: Client,
/// Whether the bootstrapping relays is connected
connected: bool,
/// Whether coop is creating a default signer
creating_signer: bool,
/// Nostr signer /// Nostr signer
signer: Arc<CoopSigner>, signer: Arc<CoopSigner>,
/// By default, Coop generates a new signer for new users.
///
/// This flag indicates whether the signer is user-owned or Coop-generated.
owned_signer: bool,
/// NIP-65 relay state /// NIP-65 relay state
nip65: Entity<RelayState>, nip65: Entity<RelayState>,
@@ -129,16 +130,17 @@ impl NostrRegistry {
cx.defer(|cx| { cx.defer(|cx| {
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
// Connect to the bootstrapping relays
nostr.update(cx, |this, cx| { nostr.update(cx, |this, cx| {
this.connect(cx); this.connect(cx);
this.get_identity(cx);
}); });
}); });
Self { Self {
client, client,
connected: false,
creating_signer: false,
signer, signer,
owned_signer: false,
nip65, nip65,
nip17, nip17,
app_keys, app_keys,
@@ -151,19 +153,29 @@ impl NostrRegistry {
fn connect(&mut self, cx: &mut Context<Self>) { fn connect(&mut self, cx: &mut Context<Self>) {
let client = self.client(); let client = self.client();
self.tasks.push(cx.background_spawn(async move { let task: Task<Result<(), Error>> = cx.background_spawn(async move {
// Add search relay to the relay pool // Add search relay to the relay pool
for url in SEARCH_RELAYS.into_iter() { for url in SEARCH_RELAYS.into_iter() {
client.add_relay(url).await?; client.add_relay(url).and_connect().await?;
} }
// Add bootstrap relay to the relay pool // Add bootstrap relay to the relay pool
for url in BOOTSTRAP_RELAYS.into_iter() { for url in BOOTSTRAP_RELAYS.into_iter() {
client.add_relay(url).await?; client.add_relay(url).and_connect().await?;
} }
// Connect to all added relays Ok(())
client.connect().await; });
self.tasks.push(cx.spawn(async move |this, cx| {
// Wait for the task to complete
task.await?;
// Update the state
this.update(cx, |this, cx| {
this.set_connected(cx);
this.get_signer(cx);
})?;
Ok(()) Ok(())
})); }));
@@ -174,22 +186,16 @@ impl NostrRegistry {
self.client.clone() self.client.clone()
} }
/// Get the nostr signer
pub fn signer(&self) -> Arc<CoopSigner> {
self.signer.clone()
}
/// Get the app keys /// Get the app keys
pub fn app_keys(&self) -> &Keys { pub fn app_keys(&self) -> &Keys {
&self.app_keys &self.app_keys
} }
/// Returns whether the current signer is owned by user
pub fn owned_signer(&self) -> bool {
self.owned_signer
}
/// Set whether the current signer is owned by user
pub fn set_owned_signer(&mut self, owned: bool, cx: &mut Context<Self>) {
self.owned_signer = owned;
cx.notify();
}
/// Get the NIP-65 state /// Get the NIP-65 state
pub fn nip65_state(&self) -> Entity<RelayState> { pub fn nip65_state(&self) -> Entity<RelayState> {
self.nip65.clone() self.nip65.clone()
@@ -200,16 +206,26 @@ impl NostrRegistry {
self.nip17.clone() self.nip17.clone()
} }
/// Get current signer's public key /// Get the connected state
pub fn signer_pkey(&self, cx: &App) -> Task<Result<PublicKey, Error>> { pub fn connected(&self) -> bool {
let client = self.client(); self.connected
}
cx.background_spawn(async move { /// Set the connected state
let signer = client.signer().context("Signer not found")?; fn set_connected(&mut self, cx: &mut Context<Self>) {
let public_key = signer.get_public_key().await?; self.connected = true;
cx.notify();
}
Ok(public_key) /// Get the creating signer status
}) pub fn creating_signer(&self) -> bool {
self.creating_signer
}
/// Set the creating signer status
fn set_creating_signer(&mut self, status: bool, cx: &mut Context<Self>) {
self.creating_signer = status;
cx.notify();
} }
/// Get a relay hint (messaging relay) for a given public key /// Get a relay hint (messaging relay) for a given public key
@@ -290,18 +306,17 @@ impl NostrRegistry {
T: NostrSigner + 'static, T: NostrSigner + 'static,
{ {
let client = self.client(); let client = self.client();
let signer = self.signer.clone(); let signer = self.signer();
// Create a task to update the signer and verify the public key // Create a task to update the signer and verify the public key
let task: Task<Result<(), Error>> = cx.background_spawn(async move { let task: Task<Result<(), Error>> = cx.background_spawn(async move {
// Update signer // Update signer
signer.switch(new).await; signer.switch(new, owned).await;
// Verify signer // Verify signer
let signer = client.signer().context("Signer not found")?; let signer = client.signer().context("Signer not found")?;
let public_key = signer.get_public_key().await?; let public_key = signer.get_public_key().await?;
log::info!("Signer's public key: {}", public_key);
log::info!("Signer's public key: {public_key}");
Ok(()) Ok(())
}); });
@@ -313,8 +328,6 @@ impl NostrRegistry {
// Update states // Update states
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
this.reset_relay_states(cx); this.reset_relay_states(cx);
this.get_relay_list(cx);
this.set_owned_signer(owned, cx);
})?; })?;
Ok(()) Ok(())
@@ -615,83 +628,61 @@ impl NostrRegistry {
}) })
} }
/// Get local stored identity
fn get_identity(&mut self, cx: &mut Context<Self>) {
let read_credential = cx.read_credentials(KEYRING);
self.tasks.push(cx.spawn(async move |this, cx| {
match read_credential.await {
Ok(Some((_, secret))) => {
let secret = SecretKey::from_slice(&secret)?;
let keys = Keys::new(secret);
this.update(cx, |this, cx| {
this.set_signer(keys, false, cx);
})
.ok();
}
_ => {
this.update(cx, |this, cx| {
this.get_bunker(cx);
})
.ok();
}
}
Ok(())
}));
}
/// Create a new identity /// Create a new identity
fn create_identity(&mut self, cx: &mut Context<Self>) { fn set_default_signer(&mut self, cx: &mut Context<Self>) {
let client = self.client(); let client = self.client();
let keys = Keys::generate(); let keys = Keys::generate();
let async_keys = keys.clone();
// Get write credential task // Create a write credential task
let write_credential = cx.write_credentials( let write_credential = cx.write_credentials(
KEYRING, KEYRING,
&keys.public_key().to_hex(), &keys.public_key().to_hex(),
&keys.secret_key().to_secret_bytes(), &keys.secret_key().to_secret_bytes(),
); );
// Update the signer
self.set_signer(keys, false, cx);
// Set the creating signer status
self.set_creating_signer(true, cx);
// Run async tasks in background // Run async tasks in background
let task: Task<Result<(), Error>> = cx.background_spawn(async move { let task: Task<Result<(), Error>> = cx.background_spawn(async move {
// Build and sign the relay list event let signer = client.signer().context("Signer not found")?;
// Get default relay list
let relay_list = default_relay_list(); let relay_list = default_relay_list();
let event = EventBuilder::relay_list(relay_list)
.sign(&async_keys)
.await?;
// Publish relay list event // Publish relay list event
client.send_event(&event).await?; let event = EventBuilder::relay_list(relay_list).sign(signer).await?;
let output = client.send_event(&event).broadcast().await?;
log::info!("Published relay list event: {:?}", output.id());
// Build and sign the metadata event // Construct the default metadata
let name = petname::petname(2, "-").unwrap_or("Cooper".to_string()); let name = petname::petname(2, "-").unwrap_or("Cooper".to_string());
let avatar = Url::parse(&format!("https://avatar.vercel.sh/{name}")).unwrap(); let avatar = Url::parse(&format!("https://avatar.vercel.sh/{name}")).unwrap();
let metadata = Metadata::new().display_name(&name).picture(avatar); let metadata = Metadata::new().display_name(&name).picture(avatar);
let event = EventBuilder::metadata(&metadata).sign(&async_keys).await?;
// Publish metadata event // Publish metadata event
client.send_event(&event).await?; let event = EventBuilder::metadata(&metadata).sign(signer).await?;
let output = client.send_event(&event).broadcast().await?;
log::info!("Published metadata event: {:?}", output.id());
// Build and sign the contact list event // Construct the default contact list
let contacts = vec![Contact::new(PublicKey::parse(COOP_PUBKEY).unwrap())]; let contacts = vec![Contact::new(PublicKey::parse(COOP_PUBKEY).unwrap())];
let event = EventBuilder::contact_list(contacts)
.sign(&async_keys)
.await?;
// Publish contact list event // Publish contact list event
client.send_event(&event).await?; let event = EventBuilder::contact_list(contacts).sign(signer).await?;
let output = client.send_event(&event).broadcast().await?;
log::info!("Published contact list event: {:?}", output.id());
// Build and sign the messaging relay list event // Construct the default messaging relay list
let relays = default_messaging_relays(); let relays = default_messaging_relays();
let event = EventBuilder::nip17_relay_list(relays)
.sign(&async_keys)
.await?;
// Publish messaging relay list event // Publish messaging relay list event
client.send_event(&event).await?; let event = EventBuilder::nip17_relay_list(relays).sign(signer).await?;
let output = client.send_event(&event).to_nip65().await?;
log::info!("Published messaging relay list event: {:?}", output.id());
// Write user's credentials to the system keyring // Write user's credentials to the system keyring
write_credential.await?; write_credential.await?;
@@ -703,10 +694,36 @@ impl NostrRegistry {
// Wait for the task to complete // Wait for the task to complete
task.await?; task.await?;
// Update the signer this.update(cx, |this, cx| {
this.set_creating_signer(false, cx);
this.get_relay_list(cx);
})?;
Ok(())
}));
}
/// Get local stored signer
fn get_signer(&mut self, cx: &mut Context<Self>) {
let read_credential = cx.read_credentials(KEYRING);
self.tasks.push(cx.spawn(async move |this, cx| {
match read_credential.await {
Ok(Some((_user, secret))) => {
let secret = SecretKey::from_slice(&secret)?;
let keys = Keys::new(secret);
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
this.set_signer(keys, false, cx); this.set_signer(keys, false, cx);
this.get_relay_list(cx);
})?; })?;
}
_ => {
this.update(cx, |this, cx| {
this.get_bunker(cx);
})?;
}
}
Ok(()) Ok(())
})); }));
@@ -741,6 +758,7 @@ impl NostrRegistry {
Ok(signer) => { Ok(signer) => {
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
this.set_signer(signer, true, cx); this.set_signer(signer, true, cx);
this.get_relay_list(cx);
}) })
.ok(); .ok();
} }
@@ -748,7 +766,7 @@ impl NostrRegistry {
log::warn!("Failed to get bunker: {e}"); log::warn!("Failed to get bunker: {e}");
// Create a new identity if no stored bunker exists // Create a new identity if no stored bunker exists
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
this.create_identity(cx); this.set_default_signer(cx);
}) })
.ok(); .ok();
} }

View File

@@ -1,5 +1,6 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::result::Result; use std::result::Result;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc; use std::sync::Arc;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
@@ -8,6 +9,17 @@ use smol::lock::RwLock;
#[derive(Debug)] #[derive(Debug)]
pub struct CoopSigner { pub struct CoopSigner {
signer: RwLock<Arc<dyn NostrSigner>>, signer: RwLock<Arc<dyn NostrSigner>>,
/// Signer's public key
signer_pkey: RwLock<Option<PublicKey>>,
/// Whether coop is creating a new identity
creating: AtomicBool,
/// By default, Coop generates a new signer for new users.
///
/// This flag indicates whether the signer is user-owned or Coop-generated.
owned: AtomicBool,
} }
impl CoopSigner { impl CoopSigner {
@@ -17,6 +29,9 @@ impl CoopSigner {
{ {
Self { Self {
signer: RwLock::new(signer.into_nostr_signer()), signer: RwLock::new(signer.into_nostr_signer()),
signer_pkey: RwLock::new(None),
creating: AtomicBool::new(false),
owned: AtomicBool::new(false),
} }
} }
@@ -25,13 +40,39 @@ impl CoopSigner {
self.signer.read().await.clone() self.signer.read().await.clone()
} }
/// Get public key
pub fn public_key(&self) -> Option<PublicKey> {
self.signer_pkey.read_blocking().to_owned()
}
/// Get the flag indicating whether the signer is creating a new identity.
pub fn creating(&self) -> bool {
self.creating.load(Ordering::SeqCst)
}
/// Get the flag indicating whether the signer is user-owned.
pub fn owned(&self) -> bool {
self.owned.load(Ordering::SeqCst)
}
/// Switch the current signer to a new signer. /// Switch the current signer to a new signer.
pub async fn switch<T>(&self, new: T) pub async fn switch<T>(&self, new: T, owned: bool)
where where
T: IntoNostrSigner, T: IntoNostrSigner,
{ {
let new_signer = new.into_nostr_signer();
let public_key = new_signer.get_public_key().await.ok();
let mut signer = self.signer.write().await; let mut signer = self.signer.write().await;
*signer = new.into_nostr_signer(); let mut signer_pkey = self.signer_pkey.write().await;
// Switch to the new signer
*signer = new_signer;
// Update the public key
*signer_pkey = public_key;
// Update the owned flag
self.owned.store(owned, Ordering::SeqCst);
} }
} }