clean up
Some checks failed
Rust / build (ubuntu-latest, stable) (push) Failing after 1m47s
Rust / build (ubuntu-latest, stable) (pull_request) Failing after 1m40s
Some checks failed
Rust / build (ubuntu-latest, stable) (push) Failing after 1m47s
Rust / build (ubuntu-latest, stable) (pull_request) Failing after 1m40s
This commit is contained in:
58
Cargo.lock
generated
58
Cargo.lock
generated
@@ -1180,7 +1180,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collections"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"rustc-hash 2.1.1",
|
||||
@@ -1619,7 +1619,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "derive_refineable"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -2542,7 +2542,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui"
|
||||
version = "0.2.2"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"as-raw-xcb-connection",
|
||||
@@ -2644,7 +2644,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui_macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@@ -2655,7 +2655,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gpui_tokio"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"gpui",
|
||||
@@ -2877,7 +2877,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "http_client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-compression",
|
||||
@@ -2902,7 +2902,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "http_client_tls"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"rustls-platform-verifier",
|
||||
@@ -2997,9 +2997,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.64"
|
||||
version = "0.1.65"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb"
|
||||
checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"core-foundation-sys",
|
||||
@@ -3663,7 +3663,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "media"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bindgen",
|
||||
@@ -4264,9 +4264,9 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "oneshot"
|
||||
version = "0.1.12"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ce66197e99546da6c6d991285f605192e794ceae69686c17163844a7bf8fcc2"
|
||||
checksum = "269bca4c2591a28585d6bf10d9ed0332b7d76900a1b02bec41bdc3a2cdcda107"
|
||||
|
||||
[[package]]
|
||||
name = "oo7"
|
||||
@@ -4502,7 +4502,7 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
[[package]]
|
||||
name = "perf"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"collections",
|
||||
"serde",
|
||||
@@ -5137,7 +5137,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "refineable"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"derive_refineable",
|
||||
]
|
||||
@@ -5236,7 +5236,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "reqwest_client"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -5290,7 +5290,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "rope"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"log",
|
||||
@@ -5569,7 +5569,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "scheduler"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"async-task",
|
||||
"backtrace",
|
||||
@@ -5942,9 +5942,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "1.0.1"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||
checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e"
|
||||
|
||||
[[package]]
|
||||
name = "skrifa"
|
||||
@@ -6161,7 +6161,7 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
[[package]]
|
||||
name = "sum_tree"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"log",
|
||||
@@ -7102,7 +7102,7 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||
[[package]]
|
||||
name = "util"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-fs",
|
||||
@@ -7140,7 +7140,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "util_macros"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"perf",
|
||||
"quote",
|
||||
@@ -8521,18 +8521,18 @@ checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.33"
|
||||
version = "0.8.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd"
|
||||
checksum = "fdea86ddd5568519879b8187e1cf04e24fce28f7fe046ceecbce472ff19a2572"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.33"
|
||||
version = "0.8.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1"
|
||||
checksum = "0c15e1b46eff7c6c91195752e0eeed8ef040e391cdece7c25376957d5f15df22"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -8616,7 +8616,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "zlog"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@@ -8633,7 +8633,7 @@ checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439"
|
||||
[[package]]
|
||||
name = "ztracing"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
dependencies = [
|
||||
"tracing",
|
||||
"tracing-subscriber",
|
||||
@@ -8644,7 +8644,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "ztracing_macro"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/zed-industries/zed#aceff52f8a93a31009d6f5284253f0b334a043dc"
|
||||
source = "git+https://github.com/zed-industries/zed#1870425b2b262e3f28c90931782aba435afd5b99"
|
||||
|
||||
[[package]]
|
||||
name = "zune-core"
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod compose;
|
||||
pub mod profile;
|
||||
pub mod screening;
|
||||
|
||||
@@ -9,7 +9,7 @@ use gpui::{
|
||||
};
|
||||
use gpui_tokio::Tokio;
|
||||
use nostr_sdk::prelude::*;
|
||||
use person::{Person, PersonRegistry};
|
||||
use person::PersonRegistry;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use state::NostrRegistry;
|
||||
use theme::ActiveTheme;
|
||||
@@ -17,13 +17,13 @@ use ui::avatar::Avatar;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
use ui::{h_flex, v_flex, Icon, IconName, Sizable, StyledExt};
|
||||
|
||||
pub fn init(public_key: PublicKey, window: &mut Window, cx: &mut App) -> Entity<ProfileViewer> {
|
||||
cx.new(|cx| ProfileViewer::new(public_key, window, cx))
|
||||
pub fn init(public_key: PublicKey, window: &mut Window, cx: &mut App) -> Entity<ProfileDialog> {
|
||||
cx.new(|cx| ProfileDialog::new(public_key, window, cx))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ProfileViewer {
|
||||
profile: Person,
|
||||
pub struct ProfileDialog {
|
||||
public_key: PublicKey,
|
||||
|
||||
/// Follow status
|
||||
followed: bool,
|
||||
@@ -38,13 +38,13 @@ pub struct ProfileViewer {
|
||||
_tasks: SmallVec<[Task<()>; 1]>,
|
||||
}
|
||||
|
||||
impl ProfileViewer {
|
||||
pub fn new(target: PublicKey, window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
impl ProfileDialog {
|
||||
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(&target, cx);
|
||||
let profile = persons.read(cx).get(&public_key, cx);
|
||||
|
||||
let mut tasks = smallvec![];
|
||||
|
||||
@@ -53,12 +53,12 @@ impl ProfileViewer {
|
||||
let public_key = signer.get_public_key().await?;
|
||||
let contact_list = client.database().contacts_public_keys(public_key).await?;
|
||||
|
||||
Ok(contact_list.contains(&target))
|
||||
Ok(contact_list.contains(&public_key))
|
||||
});
|
||||
|
||||
let verify_nip05 = if let Some(address) = profile.metadata().nip05 {
|
||||
Some(Tokio::spawn(cx, async move {
|
||||
nip05_verify(target, &address).await.unwrap_or(false)
|
||||
nip05_verify(public_key, &address).await.unwrap_or(false)
|
||||
}))
|
||||
} else {
|
||||
None
|
||||
@@ -90,7 +90,7 @@ impl ProfileViewer {
|
||||
);
|
||||
|
||||
Self {
|
||||
profile,
|
||||
public_key,
|
||||
followed: false,
|
||||
verified: false,
|
||||
copied: false,
|
||||
@@ -98,12 +98,18 @@ impl ProfileViewer {
|
||||
}
|
||||
}
|
||||
|
||||
fn address(&self, _cx: &Context<Self>) -> Option<String> {
|
||||
self.profile.metadata().nip05
|
||||
fn address(&self, cx: &Context<Self>) -> Option<String> {
|
||||
let persons = PersonRegistry::global(cx);
|
||||
let profile = persons.read(cx).get(&self.public_key, cx);
|
||||
|
||||
profile.metadata().nip05
|
||||
}
|
||||
|
||||
fn copy_pubkey(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let Ok(bech32) = self.profile.public_key().to_bech32();
|
||||
let persons = PersonRegistry::global(cx);
|
||||
let profile = persons.read(cx).get(&self.public_key, cx);
|
||||
|
||||
let Ok(bech32) = profile.public_key().to_bech32();
|
||||
let item = ClipboardItem::new_string(bech32);
|
||||
cx.write_to_clipboard(item);
|
||||
|
||||
@@ -132,9 +138,11 @@ impl ProfileViewer {
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for ProfileViewer {
|
||||
impl Render for ProfileDialog {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let bech32 = shorten_pubkey(self.profile.public_key(), 16);
|
||||
let persons = PersonRegistry::global(cx);
|
||||
let profile = persons.read(cx).get(&self.public_key, cx);
|
||||
let bech32 = shorten_pubkey(profile.public_key(), 16);
|
||||
let shared_bech32 = SharedString::from(bech32);
|
||||
|
||||
v_flex()
|
||||
@@ -146,14 +154,14 @@ impl Render for ProfileViewer {
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.text_center()
|
||||
.child(Avatar::new(self.profile.avatar()).size(rems(4.)))
|
||||
.child(Avatar::new(profile.avatar()).size(rems(4.)))
|
||||
.child(
|
||||
v_flex()
|
||||
.child(
|
||||
div()
|
||||
.font_semibold()
|
||||
.line_height(relative(1.25))
|
||||
.child(self.profile.name()),
|
||||
.child(profile.name()),
|
||||
)
|
||||
.when_some(self.address(cx), |this, address| {
|
||||
this.child(
|
||||
@@ -208,7 +216,7 @@ impl Render for ProfileViewer {
|
||||
.rounded(cx.theme().radius)
|
||||
.bg(cx.theme().elevated_surface_background)
|
||||
.child(
|
||||
self.profile
|
||||
profile
|
||||
.metadata()
|
||||
.about
|
||||
.map(SharedString::from)
|
||||
@@ -15,7 +15,6 @@ mod actions;
|
||||
mod dialogs;
|
||||
mod panels;
|
||||
mod sidebar;
|
||||
mod user;
|
||||
mod workspace;
|
||||
|
||||
fn main() {
|
||||
|
||||
@@ -140,7 +140,7 @@ impl Render for GreeterPanel {
|
||||
.gap_2()
|
||||
.when(relay_list_state == RelayState::NotSet, |this| {
|
||||
this.child(
|
||||
Button::new("connect")
|
||||
Button::new("relaylist")
|
||||
.icon(Icon::new(IconName::Door))
|
||||
.label("Set up relay list")
|
||||
.ghost()
|
||||
|
||||
@@ -47,7 +47,7 @@ pub struct ImportPanel {
|
||||
|
||||
impl ImportPanel {
|
||||
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let key_input = cx.new(|cx| InputState::new(window, cx));
|
||||
let key_input = cx.new(|cx| InputState::new(window, cx).masked(true));
|
||||
let pass_input = cx.new(|cx| InputState::new(window, cx).masked(true));
|
||||
|
||||
let error = cx.new(|_| None);
|
||||
|
||||
@@ -1,388 +0,0 @@
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Error};
|
||||
use common::{nip96_upload, shorten_pubkey};
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
div, img, App, AppContext, ClipboardItem, Context, Entity, IntoElement, ParentElement,
|
||||
PathPromptOptions, Render, SharedString, Styled, Task, Window,
|
||||
};
|
||||
use gpui_tokio::Tokio;
|
||||
use nostr_sdk::prelude::*;
|
||||
use person::Person;
|
||||
use settings::AppSettings;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use smol::fs;
|
||||
use state::NostrRegistry;
|
||||
use theme::ActiveTheme;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
use ui::input::{InputState, TextInput};
|
||||
use ui::{h_flex, v_flex, Disableable, IconName, Sizable, StyledExt, WindowExtension};
|
||||
|
||||
pub mod viewer;
|
||||
|
||||
pub fn init(window: &mut Window, cx: &mut App) -> Entity<UserProfile> {
|
||||
cx.new(|cx| UserProfile::new(window, cx))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct UserProfile {
|
||||
/// User profile
|
||||
profile: Option<Profile>,
|
||||
|
||||
/// User's name text input
|
||||
name_input: Entity<InputState>,
|
||||
|
||||
/// User's avatar url text input
|
||||
avatar_input: Entity<InputState>,
|
||||
|
||||
/// User's bio multi line input
|
||||
bio_input: Entity<InputState>,
|
||||
|
||||
/// User's website url text input
|
||||
website_input: Entity<InputState>,
|
||||
|
||||
/// Uploading state
|
||||
uploading: bool,
|
||||
|
||||
/// Copied states
|
||||
copied: bool,
|
||||
|
||||
/// Async operations
|
||||
_tasks: SmallVec<[Task<()>; 1]>,
|
||||
}
|
||||
|
||||
impl UserProfile {
|
||||
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let name_input = cx.new(|cx| InputState::new(window, cx).placeholder("Alice"));
|
||||
let avatar_input = cx.new(|cx| InputState::new(window, cx).placeholder("alice.me/a.jpg"));
|
||||
let website_input = cx.new(|cx| InputState::new(window, cx).placeholder("alice.me"));
|
||||
|
||||
// Use multi-line input for bio
|
||||
let bio_input = cx.new(|cx| {
|
||||
InputState::new(window, cx)
|
||||
.multi_line()
|
||||
.auto_grow(3, 8)
|
||||
.placeholder("A short introduce about you.")
|
||||
});
|
||||
|
||||
let get_profile = Self::get_profile(cx);
|
||||
let mut tasks = smallvec![];
|
||||
|
||||
tasks.push(
|
||||
// Get metadata in the background
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
if let Ok(profile) = get_profile.await {
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
this.set_profile(profile, window, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
Self {
|
||||
profile: None,
|
||||
name_input,
|
||||
avatar_input,
|
||||
bio_input,
|
||||
website_input,
|
||||
uploading: false,
|
||||
copied: false,
|
||||
_tasks: tasks,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_profile(cx: &App) -> Task<Result<Profile, Error>> {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
|
||||
let metadata = client
|
||||
.database()
|
||||
.metadata(public_key)
|
||||
.await?
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(Profile::new(public_key, metadata))
|
||||
})
|
||||
}
|
||||
|
||||
fn set_profile(&mut self, profile: Profile, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let metadata = profile.metadata();
|
||||
|
||||
self.avatar_input.update(cx, |this, cx| {
|
||||
if let Some(avatar) = metadata.picture.as_ref() {
|
||||
this.set_value(avatar, window, cx);
|
||||
}
|
||||
});
|
||||
|
||||
self.bio_input.update(cx, |this, cx| {
|
||||
if let Some(bio) = metadata.about.as_ref() {
|
||||
this.set_value(bio, window, cx);
|
||||
}
|
||||
});
|
||||
|
||||
self.name_input.update(cx, |this, cx| {
|
||||
if let Some(display_name) = metadata.display_name.as_ref() {
|
||||
this.set_value(display_name, window, cx);
|
||||
}
|
||||
});
|
||||
|
||||
self.website_input.update(cx, |this, cx| {
|
||||
if let Some(website) = metadata.website.as_ref() {
|
||||
this.set_value(website, window, cx);
|
||||
}
|
||||
});
|
||||
|
||||
self.profile = Some(profile);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn copy(&mut self, value: String, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let item = ClipboardItem::new_string(value);
|
||||
cx.write_to_clipboard(item);
|
||||
|
||||
self.set_copied(true, window, cx);
|
||||
}
|
||||
|
||||
fn set_copied(&mut self, status: bool, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.copied = status;
|
||||
cx.notify();
|
||||
|
||||
if status {
|
||||
self._tasks.push(
|
||||
// Reset the copied state after a delay
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
cx.background_executor().timer(Duration::from_secs(2)).await;
|
||||
cx.update(|window, cx| {
|
||||
this.update(cx, |this, cx| {
|
||||
this.set_copied(false, window, cx);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.ok();
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn uploading(&mut self, status: bool, cx: &mut Context<Self>) {
|
||||
self.uploading = status;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
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_file_server(cx);
|
||||
|
||||
// Open native file dialog
|
||||
let paths = cx.prompt_for_paths(PathPromptOptions {
|
||||
files: true,
|
||||
directories: false,
|
||||
multiple: false,
|
||||
prompt: None,
|
||||
});
|
||||
|
||||
let task = Tokio::spawn(cx, async move {
|
||||
match paths.await {
|
||||
Ok(Ok(Some(mut paths))) => {
|
||||
if let Some(path) = paths.pop() {
|
||||
let file = fs::read(path).await?;
|
||||
let url = nip96_upload(&client, &nip96_server, file).await?;
|
||||
|
||||
Ok(url)
|
||||
} else {
|
||||
Err(anyhow!("Path not found"))
|
||||
}
|
||||
}
|
||||
_ => Err(anyhow!("Error")),
|
||||
}
|
||||
});
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let result = task.await;
|
||||
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
match result {
|
||||
Ok(Ok(url)) => {
|
||||
this.avatar_input.update(cx, |this, cx| {
|
||||
this.set_value(url.to_string(), window, cx);
|
||||
});
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
window.push_notification(e.to_string(), cx);
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Failed to upload avatar: {e}");
|
||||
}
|
||||
};
|
||||
this.uploading(false, cx);
|
||||
})
|
||||
.expect("Entity has been released");
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub fn set_metadata(&mut self, cx: &mut Context<Self>) -> Task<Result<Person, Error>> {
|
||||
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();
|
||||
let website = self.website_input.read(cx).value().to_string();
|
||||
|
||||
// Get the current profile metadata
|
||||
let old_metadata = self
|
||||
.profile
|
||||
.as_ref()
|
||||
.map(|profile| profile.metadata())
|
||||
.unwrap_or_default();
|
||||
|
||||
// Construct the new metadata
|
||||
let mut new_metadata = old_metadata.display_name(name).about(bio);
|
||||
|
||||
if let Ok(url) = Url::from_str(&avatar) {
|
||||
new_metadata = new_metadata.picture(url);
|
||||
};
|
||||
|
||||
if let Ok(url) = Url::from_str(&website) {
|
||||
new_metadata = new_metadata.website(url);
|
||||
}
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let public_key = nostr.read(cx).identity().read(cx).public_key();
|
||||
let write_relays = nostr.read(cx).write_relays(&public_key, cx);
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let urls = write_relays.await;
|
||||
let signer = client.signer().await?;
|
||||
|
||||
// Sign the new metadata event
|
||||
let event = EventBuilder::metadata(&new_metadata).sign(&signer).await?;
|
||||
|
||||
// Send event to user's write relayss
|
||||
client.send_event_to(urls, &event).await?;
|
||||
|
||||
// Return the updated profile
|
||||
let metadata = Metadata::from_json(&event.content).unwrap_or_default();
|
||||
let profile = Person::new(event.pubkey, metadata);
|
||||
|
||||
Ok(profile)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for UserProfile {
|
||||
fn render(&mut self, _window: &mut gpui::Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.gap_3()
|
||||
.child(
|
||||
v_flex()
|
||||
.relative()
|
||||
.w_full()
|
||||
.h_32()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.gap_2()
|
||||
.bg(cx.theme().surface_background)
|
||||
.rounded(cx.theme().radius)
|
||||
.map(|this| {
|
||||
let picture = self.avatar_input.read(cx).value();
|
||||
let source = if picture.is_empty() {
|
||||
"brand/avatar.png"
|
||||
} else {
|
||||
picture.as_str()
|
||||
};
|
||||
this.child(img(source).rounded_full().size_10().flex_shrink_0())
|
||||
})
|
||||
.child(
|
||||
Button::new("upload")
|
||||
.icon(IconName::Upload)
|
||||
.label("Change")
|
||||
.ghost()
|
||||
.small()
|
||||
.disabled(self.uploading)
|
||||
.on_click(cx.listener(move |this, _, window, cx| {
|
||||
this.upload(window, cx);
|
||||
})),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.text_sm()
|
||||
.child(SharedString::from("Name:"))
|
||||
.child(TextInput::new(&self.name_input).small()),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.text_sm()
|
||||
.child(SharedString::from("Bio:"))
|
||||
.child(TextInput::new(&self.bio_input).small()),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.text_sm()
|
||||
.child(SharedString::from("Website:"))
|
||||
.child(TextInput::new(&self.website_input).small()),
|
||||
)
|
||||
.when_some(self.profile.as_ref(), |this, profile| {
|
||||
let public_key = profile.public_key();
|
||||
let display = SharedString::from(shorten_pubkey(profile.public_key(), 8));
|
||||
|
||||
this.child(div().my_1().h_px().w_full().bg(cx.theme().border))
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().text_placeholder)
|
||||
.font_semibold()
|
||||
.child(SharedString::from("Public Key:")),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_2()
|
||||
.w_full()
|
||||
.h_12()
|
||||
.justify_center()
|
||||
.bg(cx.theme().surface_background)
|
||||
.rounded(cx.theme().radius)
|
||||
.text_sm()
|
||||
.child(display)
|
||||
.child(
|
||||
Button::new("copy")
|
||||
.icon({
|
||||
if self.copied {
|
||||
IconName::CheckCircle
|
||||
} else {
|
||||
IconName::Copy
|
||||
}
|
||||
})
|
||||
.xsmall()
|
||||
.ghost()
|
||||
.on_click(cx.listener(move |this, _e, window, cx| {
|
||||
this.copy(
|
||||
public_key.to_bech32().unwrap(),
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
})),
|
||||
),
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -11,17 +11,16 @@ use gpui::{
|
||||
IntoElement, ParentElement, Render, SharedString, Styled, Subscription, Window,
|
||||
};
|
||||
use nostr_connect::prelude::*;
|
||||
use person::PersonRegistry;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use theme::{ActiveTheme, Theme, ThemeRegistry};
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
use ui::modal::ModalButtonProps;
|
||||
use ui::{h_flex, v_flex, Root, Sizable, WindowExtension};
|
||||
|
||||
use crate::actions::{reset, KeyringPopup, Logout, Themes, ViewProfile};
|
||||
use crate::actions::{reset, KeyringPopup, Logout, Themes};
|
||||
use crate::dialogs::profile;
|
||||
use crate::panels::greeter;
|
||||
use crate::user::viewer;
|
||||
use crate::{sidebar, user};
|
||||
use crate::sidebar;
|
||||
|
||||
pub fn init(window: &mut Window, cx: &mut App) -> Entity<Workspace> {
|
||||
cx.new(|cx| Workspace::new(window, cx))
|
||||
@@ -150,53 +149,6 @@ impl Workspace {
|
||||
});
|
||||
}
|
||||
|
||||
fn on_profile(&mut self, _ev: &ViewProfile, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let view = user::init(window, cx);
|
||||
let entity = view.downgrade();
|
||||
|
||||
window.open_modal(cx, move |modal, _window, _cx| {
|
||||
let entity = entity.clone();
|
||||
|
||||
modal
|
||||
.title("Profile")
|
||||
.confirm()
|
||||
.child(view.clone())
|
||||
.button_props(ModalButtonProps::default().ok_text("Update"))
|
||||
.on_ok(move |_, window, cx| {
|
||||
entity
|
||||
.update(cx, |this, cx| {
|
||||
let persons = PersonRegistry::global(cx);
|
||||
let set_metadata = this.set_metadata(cx);
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let result = set_metadata.await;
|
||||
|
||||
this.update_in(cx, |_, window, cx| {
|
||||
match result {
|
||||
Ok(person) => {
|
||||
persons.update(cx, |this, cx| {
|
||||
this.insert(person, cx);
|
||||
// Close the edit profile modal
|
||||
window.close_all_modals(cx);
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
window.push_notification(e.to_string(), cx);
|
||||
}
|
||||
};
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
.detach();
|
||||
})
|
||||
.ok();
|
||||
|
||||
// false to keep the modal open
|
||||
false
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn on_themes(&mut self, _ev: &Themes, window: &mut Window, cx: &mut Context<Self>) {
|
||||
window.open_modal(cx, move |this, _window, cx| {
|
||||
let registry = ThemeRegistry::global(cx);
|
||||
@@ -255,7 +207,7 @@ impl Workspace {
|
||||
|
||||
fn on_open_pubkey(&mut self, ev: &OpenPublicKey, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let public_key = ev.0;
|
||||
let view = viewer::init(public_key, window, cx);
|
||||
let view = profile::init(public_key, window, cx);
|
||||
|
||||
window.open_modal(cx, move |this, _window, _cx| {
|
||||
this.alert()
|
||||
@@ -319,7 +271,6 @@ impl Render for Workspace {
|
||||
|
||||
div()
|
||||
.id(SharedString::from("workspace"))
|
||||
.on_action(cx.listener(Self::on_profile))
|
||||
.on_action(cx.listener(Self::on_themes))
|
||||
.on_action(cx.listener(Self::on_sign_out))
|
||||
.on_action(cx.listener(Self::on_open_pubkey))
|
||||
|
||||
Reference in New Issue
Block a user