chore: rewrite the backend (not tested) (#203)
* wip: refactor * refactor * clean up * . * rename * add relay auth * . * . * optimize * . * clean up * add encryption crate * . * . * . * . * . * add encryption crate * . * refactor nip4e * . * fix endless loop * fix metadata fetching
This commit is contained in:
@@ -32,14 +32,17 @@ ui = { path = "../ui" }
|
||||
title_bar = { path = "../title_bar" }
|
||||
theme = { path = "../theme" }
|
||||
common = { path = "../common" }
|
||||
states = { path = "../states" }
|
||||
state = { path = "../state" }
|
||||
key_store = { path = "../key_store" }
|
||||
chat = { path = "../chat" }
|
||||
chat_ui = { path = "../chat_ui" }
|
||||
settings = { path = "../settings" }
|
||||
auto_update = { path = "../auto_update" }
|
||||
account = { path = "../account" }
|
||||
encryption = { path = "../encryption" }
|
||||
encryption_ui = { path = "../encryption_ui" }
|
||||
person = { path = "../person" }
|
||||
relay_auth = { path = "../relay_auth" }
|
||||
|
||||
rust-i18n.workspace = true
|
||||
i18n.workspace = true
|
||||
@@ -49,19 +52,16 @@ reqwest_client.workspace = true
|
||||
|
||||
nostr-connect.workspace = true
|
||||
nostr-sdk.workspace = true
|
||||
nostr.workspace = true
|
||||
|
||||
anyhow.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
itertools.workspace = true
|
||||
dirs.workspace = true
|
||||
log.workspace = true
|
||||
smallvec.workspace = true
|
||||
smol.workspace = true
|
||||
futures.workspace = true
|
||||
oneshot.workspace = true
|
||||
flume.workspace = true
|
||||
webbrowser.workspace = true
|
||||
|
||||
indexset = "0.12.3"
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use std::sync::Mutex;
|
||||
|
||||
use gpui::{actions, App};
|
||||
use key_store::backend::KeyItem;
|
||||
use key_store::KeyStore;
|
||||
use key_store::{KeyItem, KeyStore};
|
||||
use nostr_connect::prelude::*;
|
||||
use states::app_state;
|
||||
use state::NostrRegistry;
|
||||
|
||||
actions!(coop, [ReloadMetadata, DarkMode, Settings, Logout, Quit]);
|
||||
actions!(coop, [KeyringPopup, DarkMode, Settings, Logout, Quit]);
|
||||
actions!(sidebar, [Reload, RelayStatus]);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -49,10 +48,9 @@ pub fn load_embedded_fonts(cx: &App) {
|
||||
|
||||
pub fn reset(cx: &mut App) {
|
||||
let backend = KeyStore::global(cx).read(cx).backend();
|
||||
let client = NostrRegistry::global(cx).read(cx).client();
|
||||
|
||||
cx.spawn(async move |cx| {
|
||||
let client = app_state().client();
|
||||
|
||||
// Remove the signer
|
||||
client.unset_signer().await;
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,12 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use assets::Assets;
|
||||
use common::{APP_ID, CLIENT_NAME};
|
||||
use gpui::{
|
||||
point, px, size, AppContext, Application, Bounds, KeyBinding, Menu, MenuItem, SharedString,
|
||||
TitlebarOptions, WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind,
|
||||
WindowOptions,
|
||||
};
|
||||
use states::{app_state, APP_ID, BOOTSTRAP_RELAYS, CLIENT_NAME, SEARCH_RELAYS};
|
||||
use ui::Root;
|
||||
|
||||
use crate::actions::{load_embedded_fonts, quit, Quit};
|
||||
@@ -26,29 +26,6 @@ fn main() {
|
||||
.with_assets(Assets)
|
||||
.with_http_client(Arc::new(reqwest_client::ReqwestClient::new()));
|
||||
|
||||
// Initialize app state
|
||||
let app_state = app_state();
|
||||
|
||||
// Connect to relays
|
||||
app.background_executor()
|
||||
.spawn(async move {
|
||||
let client = app_state.client();
|
||||
|
||||
// Get all bootstrapping relays
|
||||
let mut urls = vec![];
|
||||
urls.extend(BOOTSTRAP_RELAYS);
|
||||
urls.extend(SEARCH_RELAYS);
|
||||
|
||||
// Add relay to the relay pool
|
||||
for url in urls.into_iter() {
|
||||
client.add_relay(url).await.ok();
|
||||
}
|
||||
|
||||
// Establish connection to relays
|
||||
client.connect().await;
|
||||
})
|
||||
.detach();
|
||||
|
||||
// Run application
|
||||
app.run(move |cx| {
|
||||
// Load embedded fonts in assets/fonts
|
||||
@@ -102,18 +79,30 @@ fn main() {
|
||||
// Initialize components
|
||||
ui::init(cx);
|
||||
|
||||
// Initialize app registry
|
||||
chat::init(cx);
|
||||
// Initialize backend for keys storage
|
||||
key_store::init(cx);
|
||||
|
||||
// Initialize the nostr client
|
||||
state::init(cx);
|
||||
|
||||
// Initialize person registry
|
||||
person::init(cx);
|
||||
|
||||
// Initialize backend for keys storage
|
||||
key_store::init(cx);
|
||||
|
||||
// Initialize settings
|
||||
settings::init(cx);
|
||||
|
||||
// Initialize account state
|
||||
account::init(cx);
|
||||
|
||||
// Initialize encryption state
|
||||
encryption::init(cx);
|
||||
|
||||
// Initialize app registry
|
||||
chat::init(cx);
|
||||
|
||||
// Initialize relay auth registry
|
||||
relay_auth::init(window, cx);
|
||||
|
||||
// Initialize auto update
|
||||
auto_update::init(cx);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::fs;
|
||||
use std::time::Duration;
|
||||
|
||||
use dirs::document_dir;
|
||||
use common::home_dir;
|
||||
use gpui::{
|
||||
div, AppContext, ClipboardItem, Context, Entity, Flatten, IntoElement, ParentElement, Render,
|
||||
SharedString, Styled, Task, Window,
|
||||
@@ -46,9 +46,8 @@ impl BackupKeys {
|
||||
}
|
||||
|
||||
pub fn backup(&mut self, window: &mut Window, cx: &mut Context<Self>) -> Option<Task<()>> {
|
||||
let document_dir = document_dir().expect("Failed to get document directory");
|
||||
|
||||
let path = cx.prompt_for_new_path(&document_dir, Some("My Nostr Account"));
|
||||
let dir = home_dir();
|
||||
let path = cx.prompt_for_new_path(dir, Some("My Nostr Account"));
|
||||
let nsec = self.secret_input.read(cx).value().to_string();
|
||||
|
||||
Some(cx.spawn_in(window, async move |this, cx| {
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use std::ops::Range;
|
||||
use std::time::Duration;
|
||||
|
||||
use account::Account;
|
||||
use anyhow::{anyhow, Error};
|
||||
use chat::room::Room;
|
||||
use chat::ChatRegistry;
|
||||
use common::display::{RenderedProfile, TextUtils};
|
||||
use common::nip05::nip05_profile;
|
||||
use chat::{ChatRegistry, Room};
|
||||
use common::{nip05_profile, RenderedProfile, TextUtils, BOOTSTRAP_RELAYS};
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
div, px, relative, rems, uniform_list, App, AppContext, Context, Entity, InteractiveElement,
|
||||
@@ -18,7 +17,7 @@ use nostr_sdk::prelude::*;
|
||||
use person::PersonRegistry;
|
||||
use settings::AppSettings;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::{app_state, BOOTSTRAP_RELAYS};
|
||||
use state::NostrRegistry;
|
||||
use theme::ActiveTheme;
|
||||
use ui::avatar::Avatar;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
@@ -115,7 +114,10 @@ pub struct Compose {
|
||||
}
|
||||
|
||||
impl Compose {
|
||||
pub fn new(window: &mut Window, cx: &mut Context<'_, Self>) -> Self {
|
||||
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let contacts = cx.new(|_| vec![]);
|
||||
let error_message = cx.new(|_| None);
|
||||
|
||||
@@ -129,7 +131,6 @@ impl Compose {
|
||||
let mut tasks = smallvec![];
|
||||
|
||||
let get_contacts: Task<Result<Vec<Contact>, Error>> = cx.background_spawn(async move {
|
||||
let client = app_state().client();
|
||||
let signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
let profiles = client.database().contacts(public_key).await?;
|
||||
@@ -194,10 +195,7 @@ impl Compose {
|
||||
}
|
||||
}
|
||||
|
||||
async fn request_metadata(public_key: PublicKey) -> Result<(), Error> {
|
||||
let states = app_state();
|
||||
let client = states.client();
|
||||
|
||||
async fn request_metadata(client: &Client, public_key: PublicKey) -> Result<(), Error> {
|
||||
let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE);
|
||||
let kinds = vec![Kind::Metadata, Kind::ContactList, Kind::RelayList];
|
||||
let filter = Filter::new().author(public_key).kinds(kinds).limit(10);
|
||||
@@ -220,11 +218,13 @@ impl Compose {
|
||||
}
|
||||
|
||||
fn push_contact(&mut self, contact: Contact, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let pk = contact.public_key;
|
||||
|
||||
if !self.contacts.read(cx).iter().any(|c| c.public_key == pk) {
|
||||
self._tasks.push(cx.background_spawn(async move {
|
||||
Self::request_metadata(pk).await.ok();
|
||||
Self::request_metadata(&client, pk).await.ok();
|
||||
}));
|
||||
|
||||
cx.defer_in(window, |this, window, cx| {
|
||||
@@ -313,6 +313,10 @@ impl Compose {
|
||||
|
||||
fn submit(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let chat = ChatRegistry::global(cx);
|
||||
|
||||
let account = Account::global(cx);
|
||||
let public_key = account.read(cx).public_key();
|
||||
|
||||
let receivers: Vec<PublicKey> = self.selected(cx);
|
||||
let subject_input = self.title_input.read(cx).value();
|
||||
let subject = (!subject_input.is_empty()).then(|| subject_input.to_string());
|
||||
@@ -322,25 +326,11 @@ impl Compose {
|
||||
return;
|
||||
};
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let result = Room::new(subject, receivers).await;
|
||||
chat.update(cx, |this, cx| {
|
||||
this.push_room(cx.new(|_| Room::new(subject, public_key, receivers)), cx);
|
||||
});
|
||||
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
match result {
|
||||
Ok(room) => {
|
||||
chat.update(cx, |this, cx| {
|
||||
this.push_room(cx.new(|_| room), cx);
|
||||
});
|
||||
window.close_modal(cx);
|
||||
}
|
||||
Err(e) => {
|
||||
this.set_error(e.to_string(), cx);
|
||||
}
|
||||
};
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
window.close_modal(cx);
|
||||
}
|
||||
|
||||
fn set_error(&mut self, error: impl Into<SharedString>, cx: &mut Context<Self>) {
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Error;
|
||||
use common::nip96::nip96_upload;
|
||||
use common::nip96_upload;
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
div, img, App, AppContext, Context, Entity, Flatten, IntoElement, ParentElement,
|
||||
@@ -12,7 +12,7 @@ use i18n::{shared_t, t};
|
||||
use nostr_sdk::prelude::*;
|
||||
use settings::AppSettings;
|
||||
use smol::fs;
|
||||
use states::app_state;
|
||||
use state::NostrRegistry;
|
||||
use theme::ActiveTheme;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
use ui::input::{InputState, TextInput};
|
||||
@@ -34,12 +34,18 @@ pub struct EditProfile {
|
||||
|
||||
impl EditProfile {
|
||||
pub fn new(window: &mut Window, cx: &mut App) -> Entity<Self> {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let name_input =
|
||||
cx.new(|cx| InputState::new(window, cx).placeholder(t!("profile.placeholder_name")));
|
||||
|
||||
let avatar_input =
|
||||
cx.new(|cx| InputState::new(window, cx).placeholder("https://example.com/avatar.jpg"));
|
||||
|
||||
let website_input =
|
||||
cx.new(|cx| InputState::new(window, cx).placeholder("https://your-website.com"));
|
||||
|
||||
let bio_input = cx.new(|cx| {
|
||||
InputState::new(window, cx)
|
||||
.multi_line()
|
||||
@@ -58,7 +64,6 @@ impl EditProfile {
|
||||
};
|
||||
|
||||
let task: Task<Result<Option<Metadata>, Error>> = cx.background_spawn(async move {
|
||||
let client = app_state().client();
|
||||
let signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
let metadata = client
|
||||
@@ -104,8 +109,12 @@ impl EditProfile {
|
||||
}
|
||||
|
||||
fn upload(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let nip96 = AppSettings::get_media_server(cx);
|
||||
let avatar_input = self.avatar_input.downgrade();
|
||||
|
||||
let paths = cx.prompt_for_paths(PathPromptOptions {
|
||||
files: true,
|
||||
directories: false,
|
||||
@@ -125,9 +134,7 @@ impl EditProfile {
|
||||
let (tx, rx) = oneshot::channel::<Url>();
|
||||
|
||||
nostr_sdk::async_utility::task::spawn(async move {
|
||||
if let Ok(url) =
|
||||
nip96_upload(app_state().client(), &nip96, file_data).await
|
||||
{
|
||||
if let Ok(url) = nip96_upload(&client, &nip96, file_data).await {
|
||||
_ = tx.send(url);
|
||||
}
|
||||
});
|
||||
@@ -168,6 +175,9 @@ impl EditProfile {
|
||||
}
|
||||
|
||||
pub fn set_metadata(&mut self, cx: &mut Context<Self>) -> Task<Result<Profile, Error>> {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let avatar = self.avatar_input.read(cx).value().to_string();
|
||||
let name = self.name_input.read(cx).value().to_string();
|
||||
let bio = self.bio_input.read(cx).value().to_string();
|
||||
@@ -190,7 +200,6 @@ impl EditProfile {
|
||||
}
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let client = app_state().client();
|
||||
let signer = client.signer().await?;
|
||||
|
||||
// Sign the new metadata event
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::anyhow;
|
||||
use common::BUNKER_TIMEOUT;
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
div, relative, AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle,
|
||||
Focusable, IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Window,
|
||||
};
|
||||
use i18n::{shared_t, t};
|
||||
use key_store::backend::KeyItem;
|
||||
use key_store::KeyStore;
|
||||
use key_store::{KeyItem, KeyStore};
|
||||
use nostr_connect::prelude::*;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::{app_state, BUNKER_TIMEOUT};
|
||||
use state::NostrRegistry;
|
||||
use theme::ActiveTheme;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
use ui::dock_area::panel::{Panel, PanelEvent};
|
||||
@@ -213,8 +213,10 @@ impl Login {
|
||||
}
|
||||
|
||||
fn connect(&mut self, signer: NostrConnect, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let client = app_state().client();
|
||||
client.set_signer(signer).await;
|
||||
})
|
||||
.detach();
|
||||
@@ -262,6 +264,10 @@ impl Login {
|
||||
|
||||
pub fn login_with_keys(&mut self, keys: Keys, cx: &mut Context<Self>) {
|
||||
let keystore = KeyStore::global(cx).read(cx).backend();
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let username = keys.public_key().to_hex();
|
||||
let secret = keys.secret_key().to_secret_hex().into_bytes();
|
||||
|
||||
@@ -281,7 +287,6 @@ impl Login {
|
||||
|
||||
// Update the signer
|
||||
cx.background_spawn(async move {
|
||||
let client = app_state().client();
|
||||
client.set_signer(keys).await;
|
||||
})
|
||||
.detach();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
pub mod account;
|
||||
pub mod backup_keys;
|
||||
pub mod compose;
|
||||
pub mod edit_profile;
|
||||
@@ -9,5 +8,6 @@ pub mod preferences;
|
||||
pub mod screening;
|
||||
pub mod setup_relay;
|
||||
pub mod sidebar;
|
||||
pub mod startup;
|
||||
pub mod user_profile;
|
||||
pub mod welcome;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use anyhow::{anyhow, Error};
|
||||
use common::nip96::nip96_upload;
|
||||
use common::{default_nip17_relays, default_nip65_relays, nip96_upload, BOOTSTRAP_RELAYS};
|
||||
use gpui::{
|
||||
div, relative, rems, AnyElement, App, AppContext, AsyncWindowContext, Context, Entity,
|
||||
EventEmitter, Flatten, FocusHandle, Focusable, IntoElement, ParentElement, PathPromptOptions,
|
||||
@@ -7,12 +7,11 @@ use gpui::{
|
||||
};
|
||||
use gpui_tokio::Tokio;
|
||||
use i18n::{shared_t, t};
|
||||
use key_store::backend::KeyItem;
|
||||
use key_store::KeyStore;
|
||||
use key_store::{KeyItem, KeyStore};
|
||||
use nostr_sdk::prelude::*;
|
||||
use settings::AppSettings;
|
||||
use smol::fs;
|
||||
use states::{app_state, default_nip17_relays, default_nip65_relays, BOOTSTRAP_RELAYS};
|
||||
use state::NostrRegistry;
|
||||
use theme::ActiveTheme;
|
||||
use ui::avatar::Avatar;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
@@ -106,6 +105,9 @@ impl NewAccount {
|
||||
pub fn set_signer(&mut self, cx: &mut Context<Self>) {
|
||||
let keystore = KeyStore::global(cx).read(cx).backend();
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let keys = self.temp_keys.read(cx).clone();
|
||||
let username = keys.public_key().to_hex();
|
||||
let secret = keys.secret_key().to_secret_hex().into_bytes();
|
||||
@@ -130,8 +132,6 @@ impl NewAccount {
|
||||
// Update the signer
|
||||
// Set the client's signer with the current keys
|
||||
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||
let client = app_state().client();
|
||||
|
||||
// Set the client's signer with the current keys
|
||||
client.set_signer(keys).await;
|
||||
|
||||
@@ -178,6 +178,9 @@ impl NewAccount {
|
||||
fn upload(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.uploading(true, cx);
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
// Get the user's configured NIP96 server
|
||||
let nip96_server = AppSettings::get_media_server(cx);
|
||||
|
||||
@@ -194,7 +197,7 @@ impl NewAccount {
|
||||
Ok(Some(mut paths)) => {
|
||||
if let Some(path) = paths.pop() {
|
||||
let file = fs::read(path).await?;
|
||||
let url = nip96_upload(app_state().client(), &nip96_server, file).await?;
|
||||
let url = nip96_upload(&client, &nip96_server, file).await?;
|
||||
|
||||
Ok(url)
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use common::display::TextUtils;
|
||||
use common::{TextUtils, CLIENT_NAME, NOSTR_CONNECT_RELAY, NOSTR_CONNECT_TIMEOUT};
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
div, img, px, relative, svg, AnyElement, App, AppContext, Context, Entity, EventEmitter,
|
||||
@@ -9,11 +9,10 @@ use gpui::{
|
||||
SharedString, StatefulInteractiveElement, Styled, Task, Window,
|
||||
};
|
||||
use i18n::{shared_t, t};
|
||||
use key_store::backend::KeyItem;
|
||||
use key_store::KeyStore;
|
||||
use key_store::{KeyItem, KeyStore};
|
||||
use nostr_connect::prelude::*;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::{app_state, CLIENT_NAME, NOSTR_CONNECT_RELAY, NOSTR_CONNECT_TIMEOUT};
|
||||
use state::NostrRegistry;
|
||||
use theme::ActiveTheme;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
use ui::dock_area::panel::{Panel, PanelEvent};
|
||||
@@ -165,8 +164,10 @@ impl Onboarding {
|
||||
}
|
||||
|
||||
fn connect(&mut self, signer: NostrConnect, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let client = app_state().client();
|
||||
client.set_signer(signer).await;
|
||||
})
|
||||
.detach();
|
||||
@@ -223,7 +224,7 @@ impl Focusable 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 Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
h_flex()
|
||||
.size_full()
|
||||
.child(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use account::Account;
|
||||
use common::display::RenderedProfile;
|
||||
use common::RenderedProfile;
|
||||
use gpui::http_client::Url;
|
||||
use gpui::{
|
||||
div, px, relative, rems, App, AppContext, Context, Entity, InteractiveElement, IntoElement,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use common::display::{shorten_pubkey, RenderedProfile, RenderedTimestamp};
|
||||
use common::nip05::nip05_verify;
|
||||
use common::{nip05_verify, shorten_pubkey, RenderedProfile, RenderedTimestamp, BOOTSTRAP_RELAYS};
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
div, px, relative, rems, uniform_list, App, AppContext, Context, Div, Entity,
|
||||
@@ -13,7 +13,7 @@ use nostr_sdk::prelude::*;
|
||||
use person::PersonRegistry;
|
||||
use settings::AppSettings;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::{app_state, BOOTSTRAP_RELAYS};
|
||||
use state::NostrRegistry;
|
||||
use theme::ActiveTheme;
|
||||
use ui::avatar::Avatar;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
@@ -35,14 +35,17 @@ pub struct Screening {
|
||||
|
||||
impl Screening {
|
||||
pub fn new(public_key: PublicKey, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let persons = PersonRegistry::global(cx);
|
||||
let profile = persons.read(cx).get_person(&public_key, cx);
|
||||
|
||||
let mut tasks = smallvec![];
|
||||
|
||||
let contact_check: Task<Result<(bool, Vec<Profile>), Error>> =
|
||||
cx.background_spawn(async move {
|
||||
let client = app_state().client();
|
||||
let contact_check: Task<Result<(bool, Vec<Profile>), Error>> = cx.background_spawn({
|
||||
let client = Arc::clone(&client);
|
||||
async move {
|
||||
let signer = client.signer().await?;
|
||||
let signer_pubkey = signer.get_public_key().await?;
|
||||
|
||||
@@ -64,10 +67,10 @@ impl Screening {
|
||||
}
|
||||
|
||||
Ok((followed, mutual_contacts))
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let activity_check = cx.background_spawn(async move {
|
||||
let client = app_state().client();
|
||||
let filter = Filter::new().author(public_key).limit(1);
|
||||
let mut activity: Option<Timestamp> = None;
|
||||
|
||||
@@ -153,12 +156,12 @@ impl Screening {
|
||||
}
|
||||
|
||||
fn report(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let public_key = self.profile.public_key();
|
||||
|
||||
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||
let client = app_state().client();
|
||||
let signer = client.signer().await?;
|
||||
|
||||
let tag = Tag::public_key_report(public_key, Report::Impersonation);
|
||||
let event = EventBuilder::report(vec![tag], "").sign(&signer).await?;
|
||||
|
||||
|
||||
@@ -4,15 +4,14 @@ use std::time::Duration;
|
||||
use anyhow::{anyhow, Error};
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
div, px, uniform_list, App, AppContext, AsyncWindowContext, Context, Entity,
|
||||
InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Subscription,
|
||||
Task, TextAlign, UniformList, Window,
|
||||
div, px, uniform_list, App, AppContext, Context, Entity, InteractiveElement, IntoElement,
|
||||
ParentElement, Render, SharedString, Styled, Subscription, Task, TextAlign, UniformList,
|
||||
Window,
|
||||
};
|
||||
use i18n::{shared_t, t};
|
||||
use itertools::Itertools;
|
||||
use nostr_sdk::prelude::*;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::app_state;
|
||||
use state::NostrRegistry;
|
||||
use theme::ActiveTheme;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
use ui::input::{InputEvent, InputState, TextInput};
|
||||
@@ -39,6 +38,9 @@ pub struct SetupRelay {
|
||||
|
||||
impl SetupRelay {
|
||||
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let input = cx.new(|cx| InputState::new(window, cx).placeholder("wss://example.com"));
|
||||
|
||||
let mut subscriptions = smallvec![];
|
||||
@@ -47,7 +49,11 @@ impl SetupRelay {
|
||||
tasks.push(
|
||||
// Load user's relays in the local database
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
if let Ok(relays) = Self::load(cx).await {
|
||||
let result = cx
|
||||
.background_spawn(async move { Self::load(&client).await })
|
||||
.await;
|
||||
|
||||
if let Ok(relays) = result {
|
||||
this.update(cx, |this, cx| {
|
||||
this.relays.extend(relays);
|
||||
cx.notify();
|
||||
@@ -79,24 +85,21 @@ impl SetupRelay {
|
||||
}
|
||||
}
|
||||
|
||||
fn load(cx: &AsyncWindowContext) -> Task<Result<Vec<RelayUrl>, Error>> {
|
||||
cx.background_spawn(async move {
|
||||
let client = app_state().client();
|
||||
let signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
async fn load(client: &Client) -> Result<Vec<RelayUrl>, Error> {
|
||||
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 filter = Filter::new()
|
||||
.kind(Kind::InboxRelays)
|
||||
.author(public_key)
|
||||
.limit(1);
|
||||
|
||||
if let Some(event) = client.database().query(filter).await?.first_owned() {
|
||||
let urls = nip17::extract_owned_relay_list(event).collect();
|
||||
Ok(urls)
|
||||
} else {
|
||||
Err(anyhow!("Not found."))
|
||||
}
|
||||
})
|
||||
if let Some(event) = client.database().query(filter).await?.first_owned() {
|
||||
let urls = nip17::extract_owned_relay_list(event).collect();
|
||||
Ok(urls)
|
||||
} else {
|
||||
Err(anyhow!("Not found."))
|
||||
}
|
||||
}
|
||||
|
||||
fn add(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
@@ -150,13 +153,12 @@ impl SetupRelay {
|
||||
return;
|
||||
};
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let relays = self.relays.clone();
|
||||
|
||||
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||
let states = app_state();
|
||||
let client = states.client();
|
||||
let signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
|
||||
let tags: Vec<Tag> = relays
|
||||
.iter()
|
||||
@@ -177,12 +179,6 @@ impl SetupRelay {
|
||||
client.connect_relay(relay).await.ok();
|
||||
}
|
||||
|
||||
// Fetch gift wrap events
|
||||
states
|
||||
.get_messages(public_key, &relays.into_iter().collect_vec())
|
||||
.await
|
||||
.ok();
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use chat::room::RoomKind;
|
||||
use chat::ChatRegistry;
|
||||
use chat::{ChatRegistry, RoomKind};
|
||||
use chat_ui::{CopyPublicKey, OpenPublicKey};
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
div, rems, App, ClickEvent, InteractiveElement, IntoElement, ParentElement as _, RenderOnce,
|
||||
SharedString, SharedUri, StatefulInteractiveElement, Styled, Window,
|
||||
SharedString, StatefulInteractiveElement, Styled, Window,
|
||||
};
|
||||
use i18n::t;
|
||||
use nostr_sdk::prelude::*;
|
||||
@@ -26,7 +25,7 @@ pub struct RoomListItem {
|
||||
room_id: Option<u64>,
|
||||
public_key: Option<PublicKey>,
|
||||
name: Option<SharedString>,
|
||||
avatar: Option<SharedUri>,
|
||||
avatar: Option<SharedString>,
|
||||
created_at: Option<SharedString>,
|
||||
kind: Option<RoomKind>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
@@ -62,7 +61,7 @@ impl RoomListItem {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn avatar(mut self, avatar: impl Into<SharedUri>) -> Self {
|
||||
pub fn avatar(mut self, avatar: impl Into<SharedString>) -> Self {
|
||||
self.avatar = Some(avatar.into());
|
||||
self
|
||||
}
|
||||
|
||||
@@ -3,10 +3,8 @@ use std::ops::Range;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Error};
|
||||
use chat::room::{Room, RoomKind};
|
||||
use chat::{ChatEvent, ChatRegistry};
|
||||
use common::debounced_delay::DebouncedDelay;
|
||||
use common::display::{RenderedTimestamp, TextUtils};
|
||||
use chat::{ChatEvent, ChatRegistry, Room, RoomKind};
|
||||
use common::{DebouncedDelay, RenderedTimestamp, TextUtils, BOOTSTRAP_RELAYS, SEARCH_RELAYS};
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
deferred, div, relative, uniform_list, AnyElement, App, AppContext, Context, Entity,
|
||||
@@ -20,7 +18,7 @@ use list_item::RoomListItem;
|
||||
use nostr_sdk::prelude::*;
|
||||
use settings::AppSettings;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::{app_state, BOOTSTRAP_RELAYS, SEARCH_RELAYS};
|
||||
use state::NostrRegistry;
|
||||
use theme::ActiveTheme;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
use ui::dock_area::panel::{Panel, PanelEvent};
|
||||
@@ -137,8 +135,7 @@ impl Sidebar {
|
||||
}
|
||||
}
|
||||
|
||||
async fn request_metadata(public_key: PublicKey) -> Result<(), Error> {
|
||||
let client = app_state().client();
|
||||
async fn request_metadata(client: &Client, public_key: PublicKey) -> Result<(), Error> {
|
||||
let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE);
|
||||
let kinds = vec![Kind::Metadata, Kind::ContactList, Kind::RelayList];
|
||||
let filter = Filter::new().author(public_key).kinds(kinds).limit(10);
|
||||
@@ -152,18 +149,7 @@ impl Sidebar {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn create_temp_room(receiver: PublicKey) -> Result<Room, Error> {
|
||||
// Request to get user's metadata
|
||||
Self::request_metadata(receiver).await?;
|
||||
|
||||
// Create a temporary room
|
||||
let room = Room::new(None, vec![receiver]).await?;
|
||||
|
||||
Ok(room)
|
||||
}
|
||||
|
||||
async fn nip50(query: &str) -> Result<BTreeSet<Room>, Error> {
|
||||
let client = app_state().client();
|
||||
async fn nip50(client: &Client, query: &str) -> Result<BTreeSet<Room>, Error> {
|
||||
let signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
|
||||
@@ -186,10 +172,13 @@ impl Sidebar {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Return a temporary room
|
||||
if let Ok(room) = Self::create_temp_room(event.pubkey).await {
|
||||
rooms.insert(room);
|
||||
}
|
||||
// Request metadata event's author
|
||||
Self::request_metadata(client, event.pubkey).await?;
|
||||
|
||||
// Construct room
|
||||
let room = Room::new(None, public_key, vec![event.pubkey]);
|
||||
|
||||
rooms.insert(room);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,11 +201,13 @@ impl Sidebar {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let query = query.to_owned();
|
||||
let query_cloned = query.clone();
|
||||
|
||||
let task = smol::future::or(
|
||||
Tokio::spawn(cx, async move { Self::nip50(&query).await.ok() }),
|
||||
Tokio::spawn(cx, async move { Self::nip50(&client, &query).await.ok() }),
|
||||
Tokio::spawn(cx, async move {
|
||||
let _ = rx.recv().await.is_ok();
|
||||
None
|
||||
@@ -263,13 +254,20 @@ impl Sidebar {
|
||||
}
|
||||
|
||||
fn search_by_nip05(&mut self, query: &str, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let address = query.to_owned();
|
||||
|
||||
let task = Tokio::spawn(cx, async move {
|
||||
if let Ok(profile) = common::nip05::nip05_profile(&address).await {
|
||||
Self::create_temp_room(profile.public_key).await
|
||||
} else {
|
||||
Err(anyhow!(t!("sidebar.addr_error")))
|
||||
match common::nip05_profile(&address).await {
|
||||
Ok(profile) => {
|
||||
let signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
let room = Room::new(None, public_key, vec![profile.public_key]);
|
||||
|
||||
Ok(room)
|
||||
}
|
||||
Err(e) => Err(anyhow!(e)),
|
||||
}
|
||||
});
|
||||
|
||||
@@ -310,6 +308,9 @@ impl Sidebar {
|
||||
}
|
||||
|
||||
fn search_by_pubkey(&mut self, query: &str, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let Ok(public_key) = query.to_public_key() else {
|
||||
window.push_notification(t!("common.pubkey_invalid"), cx);
|
||||
self.set_finding(false, window, cx);
|
||||
@@ -317,8 +318,13 @@ impl Sidebar {
|
||||
};
|
||||
|
||||
let task: Task<Result<Room, Error>> = cx.background_spawn(async move {
|
||||
// Create a gift wrap event to represent as room
|
||||
Self::create_temp_room(public_key).await
|
||||
let signer = client.signer().await?;
|
||||
let author = signer.get_public_key().await?;
|
||||
let room = Room::new(None, author, vec![public_key]);
|
||||
|
||||
Self::request_metadata(&client, public_key).await?;
|
||||
|
||||
Ok(room)
|
||||
});
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
@@ -521,14 +527,16 @@ impl Sidebar {
|
||||
|
||||
fn on_reload(&mut self, _ev: &Reload, window: &mut Window, cx: &mut Context<Self>) {
|
||||
ChatRegistry::global(cx).update(cx, |this, cx| {
|
||||
this.load_rooms(window, cx);
|
||||
this.get_rooms(cx);
|
||||
});
|
||||
window.push_notification(t!("common.refreshed"), cx);
|
||||
}
|
||||
|
||||
fn on_manage(&mut self, _ev: &RelayStatus, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let task: Task<Result<Vec<Relay>, Error>> = cx.background_spawn(async move {
|
||||
let client = app_state().client();
|
||||
let subscription = client.subscription(&SubscriptionId::new("inbox")).await;
|
||||
let mut relays: Vec<Relay> = vec![];
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use common::display::RenderedProfile;
|
||||
use common::{RenderedProfile, BUNKER_TIMEOUT};
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
div, relative, rems, svg, AnyElement, App, AppContext, Context, Entity, EventEmitter,
|
||||
@@ -9,12 +9,11 @@ use gpui::{
|
||||
Window,
|
||||
};
|
||||
use i18n::{shared_t, t};
|
||||
use key_store::backend::KeyItem;
|
||||
use key_store::KeyStore;
|
||||
use key_store::{Credential, KeyItem, KeyStore};
|
||||
use nostr_connect::prelude::*;
|
||||
use person::PersonRegistry;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::{app_state, BUNKER_TIMEOUT};
|
||||
use state::NostrRegistry;
|
||||
use theme::ActiveTheme;
|
||||
use ui::avatar::Avatar;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
@@ -24,18 +23,14 @@ use ui::{h_flex, v_flex, ContextModal, Sizable, StyledExt};
|
||||
|
||||
use crate::actions::{reset, CoopAuthUrlHandler};
|
||||
|
||||
pub fn init(
|
||||
public_key: PublicKey,
|
||||
secret: String,
|
||||
window: &mut Window,
|
||||
cx: &mut App,
|
||||
) -> Entity<Account> {
|
||||
cx.new(|cx| Account::new(public_key, secret, window, cx))
|
||||
pub fn init(cre: Credential, window: &mut Window, cx: &mut App) -> Entity<Startup> {
|
||||
cx.new(|cx| Startup::new(cre, window, cx))
|
||||
}
|
||||
|
||||
pub struct Account {
|
||||
public_key: PublicKey,
|
||||
secret: String,
|
||||
/// Startup
|
||||
#[derive(Debug)]
|
||||
pub struct Startup {
|
||||
credential: Credential,
|
||||
loading: bool,
|
||||
|
||||
name: SharedString,
|
||||
@@ -49,13 +44,8 @@ pub struct Account {
|
||||
_tasks: SmallVec<[Task<()>; 1]>,
|
||||
}
|
||||
|
||||
impl Account {
|
||||
fn new(
|
||||
public_key: PublicKey,
|
||||
secret: String,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
impl Startup {
|
||||
fn new(credential: Credential, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let tasks = smallvec![];
|
||||
let mut subscriptions = smallvec![];
|
||||
|
||||
@@ -69,8 +59,7 @@ impl Account {
|
||||
);
|
||||
|
||||
Self {
|
||||
public_key,
|
||||
secret,
|
||||
credential,
|
||||
loading: false,
|
||||
name: "Account".into(),
|
||||
focus_handle: cx.focus_handle(),
|
||||
@@ -83,9 +72,11 @@ impl Account {
|
||||
fn login(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.set_loading(true, cx);
|
||||
|
||||
let secret = self.credential.secret();
|
||||
|
||||
// Try to login with bunker
|
||||
if self.secret.starts_with("bunker://") {
|
||||
match NostrConnectURI::parse(&self.secret) {
|
||||
if secret.starts_with("bunker://") {
|
||||
match NostrConnectURI::parse(secret) {
|
||||
Ok(uri) => {
|
||||
self.login_with_bunker(uri, window, cx);
|
||||
}
|
||||
@@ -98,7 +89,7 @@ impl Account {
|
||||
};
|
||||
|
||||
// Fall back to login with keys
|
||||
match SecretKey::parse(&self.secret) {
|
||||
match SecretKey::parse(secret) {
|
||||
Ok(secret) => {
|
||||
self.login_with_keys(secret, cx);
|
||||
}
|
||||
@@ -115,6 +106,8 @@ impl Account {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let keystore = KeyStore::global(cx).read(cx).backend();
|
||||
|
||||
// Handle connection in the background
|
||||
@@ -138,8 +131,6 @@ impl Account {
|
||||
this._tasks.push(
|
||||
// Handle connection in the background
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let client = app_state().client();
|
||||
|
||||
match signer.bunker_uri().await {
|
||||
Ok(_) => {
|
||||
client.set_signer(signer).await;
|
||||
@@ -171,11 +162,12 @@ impl Account {
|
||||
}
|
||||
|
||||
fn login_with_keys(&mut self, secret: SecretKey, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let keys = Keys::new(secret);
|
||||
|
||||
// Update the signer
|
||||
cx.background_spawn(async move {
|
||||
let client = app_state().client();
|
||||
client.set_signer(keys).await;
|
||||
})
|
||||
.detach();
|
||||
@@ -187,7 +179,7 @@ impl Account {
|
||||
}
|
||||
}
|
||||
|
||||
impl Panel for Account {
|
||||
impl Panel for Startup {
|
||||
fn panel_id(&self) -> SharedString {
|
||||
self.name.clone()
|
||||
}
|
||||
@@ -197,19 +189,21 @@ impl Panel for Account {
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<PanelEvent> for Account {}
|
||||
impl EventEmitter<PanelEvent> for Startup {}
|
||||
|
||||
impl Focusable for Account {
|
||||
impl Focusable for Startup {
|
||||
fn focus_handle(&self, _: &App) -> gpui::FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Account {
|
||||
impl Render for Startup {
|
||||
fn render(&mut self, _window: &mut gpui::Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let persons = PersonRegistry::global(cx);
|
||||
let profile = persons.read(cx).get_person(&self.public_key, cx);
|
||||
let bunker = self.secret.starts_with("bunker://");
|
||||
let bunker = self.credential.secret().starts_with("bunker://");
|
||||
let profile = persons
|
||||
.read(cx)
|
||||
.get_person(&self.credential.public_key(), cx);
|
||||
|
||||
v_flex()
|
||||
.image_cache(self.image_cache.clone())
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use common::display::RenderedProfile;
|
||||
use common::nip05::nip05_verify;
|
||||
use common::{nip05_verify, RenderedProfile};
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
div, relative, rems, App, AppContext, ClipboardItem, Context, Entity, IntoElement,
|
||||
@@ -13,7 +12,7 @@ use nostr_sdk::prelude::*;
|
||||
use person::PersonRegistry;
|
||||
use settings::AppSettings;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use states::app_state;
|
||||
use state::NostrRegistry;
|
||||
use theme::ActiveTheme;
|
||||
use ui::avatar::Avatar;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
@@ -33,13 +32,15 @@ pub struct UserProfile {
|
||||
|
||||
impl UserProfile {
|
||||
pub fn new(target: PublicKey, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let persons = PersonRegistry::global(cx);
|
||||
let profile = persons.read(cx).get_person(&target, cx);
|
||||
|
||||
let mut tasks = smallvec![];
|
||||
|
||||
let check_follow: Task<Result<bool, Error>> = cx.background_spawn(async move {
|
||||
let client = app_state().client();
|
||||
let signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
let contact_list = client.database().contacts_public_keys(public_key).await?;
|
||||
|
||||
Reference in New Issue
Block a user