update
This commit is contained in:
@@ -1,18 +1,25 @@
|
|||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use auto_update::AutoUpdater;
|
use auto_update::AutoUpdater;
|
||||||
use gpui::prelude::FluentBuilder;
|
use gpui::prelude::FluentBuilder;
|
||||||
|
use gpui::{
|
||||||
|
Anchor, App, Context, Div, InteractiveElement, ParentElement, SharedString, Stateful, Styled,
|
||||||
|
Window, div, px,
|
||||||
|
};
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use gpui::{AnyElement, Entity, IntoElement};
|
use gpui::{AnyElement, Entity, IntoElement};
|
||||||
use gpui::{
|
use gpui_kit::base::StyledExt as _;
|
||||||
App, Context, Div, InteractiveElement, ParentElement, Stateful, Styled, Window, div, px,
|
|
||||||
};
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
use gpui_kit::component::Disableable;
|
use gpui_kit::component::Disableable;
|
||||||
use gpui_kit::component::button::{Button, ButtonVariant, ButtonVariants};
|
use gpui_kit::component::button::{Button, ButtonVariant, ButtonVariants};
|
||||||
|
use gpui_kit::component::clipboard::Clipboard;
|
||||||
use gpui_kit::component::dialog::DialogButtonProps;
|
use gpui_kit::component::dialog::DialogButtonProps;
|
||||||
use gpui_kit::component::menu::{DropdownMenu, PopupMenuItem};
|
use gpui_kit::component::menu::{DropdownMenu, PopupMenuItem};
|
||||||
|
use gpui_kit::component::popover::Popover;
|
||||||
|
use gpui_kit::component::separator::Separator;
|
||||||
use gpui_kit::component::{ActiveTheme, Icon, Selectable, Sizable, WindowExt, h_flex, v_flex};
|
use gpui_kit::component::{ActiveTheme, Icon, Selectable, Sizable, WindowExt, h_flex, v_flex};
|
||||||
use nostr_sdk::prelude::*;
|
use nostr_sdk::prelude::*;
|
||||||
|
use person::shorten_pubkey;
|
||||||
|
use settings::AppSettings;
|
||||||
use state::{CLIENT_NAME, NostrRegistry};
|
use state::{CLIENT_NAME, NostrRegistry};
|
||||||
use ui::{Avatar, IconName};
|
use ui::{Avatar, IconName};
|
||||||
|
|
||||||
@@ -31,6 +38,10 @@ impl Sidebar {
|
|||||||
let updater = AutoUpdater::try_global(cx);
|
let updater = AutoUpdater::try_global(cx);
|
||||||
let profile = self.directory.read(cx).person(current_user, cx);
|
let profile = self.directory.read(cx).person(current_user, cx);
|
||||||
let avatar = profile.avatar();
|
let avatar = profile.avatar();
|
||||||
|
let encryption_key = profile
|
||||||
|
.announcement()
|
||||||
|
.map(|announcement| announcement.public_key());
|
||||||
|
let encryption_enabled = AppSettings::get_nip4e(cx);
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.id("sidebar-user")
|
.id("sidebar-user")
|
||||||
@@ -71,6 +82,11 @@ impl Sidebar {
|
|||||||
});
|
});
|
||||||
|
|
||||||
actions
|
actions
|
||||||
|
.when(encryption_enabled, |actions| {
|
||||||
|
actions.when_some(encryption_key, |actions, encryption_key| {
|
||||||
|
actions.child(self.render_encryption(encryption_key))
|
||||||
|
})
|
||||||
|
})
|
||||||
.child(
|
.child(
|
||||||
Button::new("sidebar-search")
|
Button::new("sidebar-search")
|
||||||
.icon(IconName::Search)
|
.icon(IconName::Search)
|
||||||
@@ -111,6 +127,89 @@ impl Sidebar {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The encryption key popover: public key with copy, backup and reset
|
||||||
|
fn render_encryption(&self, encryption_key: PublicKey) -> impl IntoElement {
|
||||||
|
Popover::new("sidebar-encryption")
|
||||||
|
.anchor(Anchor::TopRight)
|
||||||
|
.trigger(
|
||||||
|
Button::new("encryption-key")
|
||||||
|
.icon(IconName::Lock)
|
||||||
|
.tooltip("Encryption Key")
|
||||||
|
.ghost()
|
||||||
|
.small(),
|
||||||
|
)
|
||||||
|
.content(move |_, _, cx| {
|
||||||
|
let popover = cx.entity();
|
||||||
|
let Ok(npub) = encryption_key.to_bech32();
|
||||||
|
|
||||||
|
v_flex()
|
||||||
|
.w_64()
|
||||||
|
.gap_2()
|
||||||
|
.text_sm()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.text_xs()
|
||||||
|
.font_semibold()
|
||||||
|
.text_color(cx.theme().muted_foreground)
|
||||||
|
.child("Public key"),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.items_center()
|
||||||
|
.gap_1()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.flex_1()
|
||||||
|
.min_w_0()
|
||||||
|
.truncate()
|
||||||
|
.text_color(cx.theme().muted_foreground)
|
||||||
|
.child(SharedString::from(shorten_pubkey(encryption_key, 12))),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Clipboard::new("encryption-key-copy")
|
||||||
|
.tooltip("Copy public key")
|
||||||
|
.value(npub),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.child(Separator::horizontal())
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.justify_end()
|
||||||
|
.gap_2()
|
||||||
|
.child(
|
||||||
|
Button::new("encryption-backup")
|
||||||
|
.label("Backup")
|
||||||
|
.outline()
|
||||||
|
.small()
|
||||||
|
.on_click({
|
||||||
|
let popover = popover.clone();
|
||||||
|
move |_, window, cx| {
|
||||||
|
popover
|
||||||
|
.update(cx, |state, cx| state.dismiss(window, cx));
|
||||||
|
window.dispatch_action(
|
||||||
|
Box::new(Command::BackupEncryption),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
.child(
|
||||||
|
Button::new("encryption-reset")
|
||||||
|
.label("Reset")
|
||||||
|
.ghost()
|
||||||
|
.small()
|
||||||
|
.on_click(move |_, window, cx| {
|
||||||
|
popover.update(cx, |state, cx| state.dismiss(window, cx));
|
||||||
|
window.dispatch_action(
|
||||||
|
Box::new(Command::ResetEncryption),
|
||||||
|
cx,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn render_footer(&self, loading: bool, _cx: &mut Context<Self>) -> Div {
|
pub(super) fn render_footer(&self, loading: bool, _cx: &mut Context<Self>) -> Div {
|
||||||
v_flex()
|
v_flex()
|
||||||
.p_2()
|
.p_2()
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ use nostr_sdk::prelude::*;
|
|||||||
use person::{Directory, PersonRegistry};
|
use person::{Directory, PersonRegistry};
|
||||||
pub(crate) use rows::open_room;
|
pub(crate) use rows::open_room;
|
||||||
use search::SearchResult;
|
use search::SearchResult;
|
||||||
|
use settings::AppSettings;
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
use state::{FIND_DELAY, NostrRegistry, StateEvent};
|
use state::{FIND_DELAY, NostrRegistry, StateEvent};
|
||||||
use ui::title_bar_drag_handlers;
|
use ui::title_bar_drag_handlers;
|
||||||
@@ -104,6 +105,8 @@ impl Sidebar {
|
|||||||
let chat = ChatRegistry::global(cx);
|
let chat = ChatRegistry::global(cx);
|
||||||
let groups = GroupsRegistry::global(cx);
|
let groups = GroupsRegistry::global(cx);
|
||||||
let nostr = NostrRegistry::global(cx);
|
let nostr = NostrRegistry::global(cx);
|
||||||
|
let settings = AppSettings::global(cx);
|
||||||
|
let directory = Directory::new(cx);
|
||||||
|
|
||||||
let find_input = cx.new(|cx| {
|
let find_input = cx.new(|cx| {
|
||||||
InputState::new(window, cx)
|
InputState::new(window, cx)
|
||||||
@@ -206,6 +209,11 @@ impl Sidebar {
|
|||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// The user header reads settings and the tracked profiles on render, so
|
||||||
|
// the encryption button reacts to toggles and announcement arrivals
|
||||||
|
subscriptions.push(cx.observe(&settings, |_, _, cx| cx.notify()));
|
||||||
|
subscriptions.push(cx.observe(&directory, |_, _, cx| cx.notify()));
|
||||||
|
|
||||||
tasks.push(cx.spawn(async move |this, cx| {
|
tasks.push(cx.spawn(async move |this, cx| {
|
||||||
let timer = Duration::from_millis(PROGRESS_TICK_MS);
|
let timer = Duration::from_millis(PROGRESS_TICK_MS);
|
||||||
loop {
|
loop {
|
||||||
@@ -229,7 +237,7 @@ impl Sidebar {
|
|||||||
chats_open: None,
|
chats_open: None,
|
||||||
contacts_open: false,
|
contacts_open: false,
|
||||||
contacts: None,
|
contacts: None,
|
||||||
directory: Directory::new(cx),
|
directory,
|
||||||
selected_contacts: HashSet::new(),
|
selected_contacts: HashSet::new(),
|
||||||
requests_open: false,
|
requests_open: false,
|
||||||
new_requests: false,
|
new_requests: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user