update encryption panel
Some checks failed
Rust / build (ubuntu-latest, stable) (push) Failing after 1m58s
Rust / build (ubuntu-latest, stable) (pull_request) Failing after 1m39s
Rust / build (macos-latest, stable) (push) Has been cancelled
Rust / build (windows-latest, stable) (push) Has been cancelled
Rust / build (macos-latest, stable) (pull_request) Has been cancelled
Rust / build (windows-latest, stable) (pull_request) Has been cancelled
Some checks failed
Rust / build (ubuntu-latest, stable) (push) Failing after 1m58s
Rust / build (ubuntu-latest, stable) (pull_request) Failing after 1m39s
Rust / build (macos-latest, stable) (push) Has been cancelled
Rust / build (windows-latest, stable) (push) Has been cancelled
Rust / build (macos-latest, stable) (pull_request) Has been cancelled
Rust / build (windows-latest, stable) (pull_request) Has been cancelled
This commit is contained in:
3
assets/icons/reset.svg
Normal file
3
assets/icons/reset.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
|
||||
<path d="M13 17.25C16.4518 17.25 19.25 14.4518 19.25 11V10.5C19.25 6.77208 16.2279 3.75 12.5 3.75C8.77208 3.75 5.75 6.77208 5.75 10.5V20" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="M2 16.5L5.75 20.25L9.5 16.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 435 B |
@@ -13,38 +13,6 @@ const MINUTES_IN_HOUR: i64 = 60;
|
||||
const HOURS_IN_DAY: i64 = 24;
|
||||
const DAYS_IN_MONTH: i64 = 30;
|
||||
|
||||
pub trait RenderedProfile {
|
||||
fn avatar(&self) -> SharedString;
|
||||
fn display_name(&self) -> SharedString;
|
||||
}
|
||||
|
||||
impl RenderedProfile for Profile {
|
||||
fn avatar(&self) -> SharedString {
|
||||
self.metadata()
|
||||
.picture
|
||||
.as_ref()
|
||||
.filter(|picture| !picture.is_empty())
|
||||
.map(|picture| picture.into())
|
||||
.unwrap_or_else(|| "brand/avatar.png".into())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> SharedString {
|
||||
if let Some(display_name) = self.metadata().display_name.as_ref() {
|
||||
if !display_name.is_empty() {
|
||||
return SharedString::from(display_name);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(name) = self.metadata().name.as_ref() {
|
||||
if !name.is_empty() {
|
||||
return SharedString::from(name);
|
||||
}
|
||||
}
|
||||
|
||||
SharedString::from(shorten_pubkey(self.public_key(), 4))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait RenderedTimestamp {
|
||||
fn to_human_time(&self) -> SharedString;
|
||||
fn to_ago(&self) -> SharedString;
|
||||
@@ -126,13 +94,3 @@ impl<T: AsRef<str>> TextUtils for T {
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shorten_pubkey(public_key: PublicKey, len: usize) -> String {
|
||||
let Ok(pubkey) = public_key.to_bech32();
|
||||
|
||||
format!(
|
||||
"{}:{}",
|
||||
&pubkey[0..(len + 1)],
|
||||
&pubkey[pubkey.len() - len..]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2,14 +2,14 @@ use std::collections::HashMap;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{Context as AnyhowContext, Error};
|
||||
use common::{shorten_pubkey, RenderedTimestamp};
|
||||
use common::RenderedTimestamp;
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
div, px, relative, rems, uniform_list, App, AppContext, Context, Div, Entity,
|
||||
InteractiveElement, IntoElement, ParentElement, Render, SharedString, Styled, Task, Window,
|
||||
};
|
||||
use nostr_sdk::prelude::*;
|
||||
use person::{Person, PersonRegistry};
|
||||
use person::{shorten_pubkey, Person, PersonRegistry};
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use state::{NostrAddress, NostrRegistry, BOOTSTRAP_RELAYS, TIMEOUT};
|
||||
use theme::ActiveTheme;
|
||||
|
||||
@@ -1,27 +1,151 @@
|
||||
use anyhow::Error;
|
||||
use device::DeviceRegistry;
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable,
|
||||
IntoElement, Render, SharedString, Styled, Window,
|
||||
div, px, AnyElement, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable,
|
||||
IntoElement, ParentElement, Render, SharedString, Styled, Task, Window,
|
||||
};
|
||||
use nostr_sdk::prelude::*;
|
||||
use person::{shorten_pubkey, PersonRegistry};
|
||||
use state::Announcement;
|
||||
use theme::ActiveTheme;
|
||||
use ui::button::{Button, ButtonVariants};
|
||||
use ui::dock_area::panel::{Panel, PanelEvent};
|
||||
use ui::v_flex;
|
||||
use ui::notification::Notification;
|
||||
use ui::{divider, h_flex, v_flex, Disableable, IconName, Sizable, StyledExt, WindowExtension};
|
||||
|
||||
pub fn init(window: &mut Window, cx: &mut App) -> Entity<EncryptionPanel> {
|
||||
cx.new(|cx| EncryptionPanel::new(window, cx))
|
||||
const MSG: &str =
|
||||
"Encryption Key is a special key that used to encrypt and decrypt your messages. \
|
||||
Your identity is completely decoupled from all encryption processes to protect your privacy.";
|
||||
|
||||
const NOTICE: &str = "By resetting your encryption key, you will lose access to \
|
||||
all your encrypted messages before. This action cannot be undone.";
|
||||
|
||||
pub fn init(public_key: PublicKey, window: &mut Window, cx: &mut App) -> Entity<EncryptionPanel> {
|
||||
cx.new(|cx| EncryptionPanel::new(public_key, window, cx))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EncryptionPanel {
|
||||
name: SharedString,
|
||||
focus_handle: FocusHandle,
|
||||
|
||||
/// User's public key
|
||||
public_key: PublicKey,
|
||||
|
||||
/// Whether the panel is loading
|
||||
loading: bool,
|
||||
|
||||
/// Tasks
|
||||
tasks: Vec<Task<Result<(), Error>>>,
|
||||
}
|
||||
|
||||
impl EncryptionPanel {
|
||||
fn new(_window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
fn new(public_key: PublicKey, _window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
Self {
|
||||
name: "Encryption".into(),
|
||||
focus_handle: cx.focus_handle(),
|
||||
public_key,
|
||||
loading: false,
|
||||
tasks: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn set_loading(&mut self, status: bool, cx: &mut Context<Self>) {
|
||||
self.loading = status;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn approve(&mut self, event: &Event, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let device = DeviceRegistry::global(cx);
|
||||
let task = device.read(cx).approve(event, cx);
|
||||
let id = event.id;
|
||||
|
||||
// Update loading status
|
||||
self.set_loading(true, cx);
|
||||
|
||||
self.tasks.push(cx.spawn_in(window, async move |this, cx| {
|
||||
match task.await {
|
||||
Ok(_) => {
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
// Reset loading status
|
||||
this.set_loading(false, cx);
|
||||
|
||||
// Remove request
|
||||
device.update(cx, |this, cx| {
|
||||
this.remove_request(&id, cx);
|
||||
});
|
||||
|
||||
window.push_notification("Approved", cx);
|
||||
})?;
|
||||
}
|
||||
Err(e) => {
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
this.set_loading(false, cx);
|
||||
window.push_notification(Notification::error(e.to_string()), cx);
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
}
|
||||
|
||||
fn render_requests(&mut self, cx: &mut Context<Self>) -> Vec<impl IntoElement> {
|
||||
const TITLE: &str = "You've requested for the Encryption Key from:";
|
||||
|
||||
let device = DeviceRegistry::global(cx);
|
||||
let requests = device.read(cx).requests.clone();
|
||||
let mut items = Vec::new();
|
||||
|
||||
for event in requests.into_iter() {
|
||||
let request = Announcement::from(&event);
|
||||
let client_name = request.client_name();
|
||||
let target = request.public_key();
|
||||
|
||||
items.push(
|
||||
v_flex()
|
||||
.gap_2()
|
||||
.text_sm()
|
||||
.child(SharedString::from(TITLE))
|
||||
.child(
|
||||
v_flex()
|
||||
.h_12()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.px_2()
|
||||
.rounded(cx.theme().radius)
|
||||
.bg(cx.theme().warning_background)
|
||||
.text_color(cx.theme().warning_foreground)
|
||||
.child(client_name.clone()),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.h_7()
|
||||
.w_full()
|
||||
.px_2()
|
||||
.rounded(cx.theme().radius)
|
||||
.bg(cx.theme().elevated_surface_background)
|
||||
.child(SharedString::from(target.to_hex())),
|
||||
)
|
||||
.child(
|
||||
h_flex().justify_end().gap_2().child(
|
||||
Button::new("approve")
|
||||
.label("Approve")
|
||||
.ghost()
|
||||
.small()
|
||||
.disabled(self.loading)
|
||||
.loading(self.loading)
|
||||
.on_click(cx.listener(move |this, _ev, window, cx| {
|
||||
this.approve(&event, window, cx);
|
||||
})),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
items
|
||||
}
|
||||
}
|
||||
|
||||
impl Panel for EncryptionPanel {
|
||||
@@ -43,12 +167,128 @@ impl Focusable for EncryptionPanel {
|
||||
}
|
||||
|
||||
impl Render for EncryptionPanel {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let device = DeviceRegistry::global(cx);
|
||||
let state = device.read(cx).state();
|
||||
let has_requests = device.read(cx).has_requests();
|
||||
|
||||
let persons = PersonRegistry::global(cx);
|
||||
let profile = persons.read(cx).get(&self.public_key, cx);
|
||||
|
||||
let Some(announcement) = profile.announcement() else {
|
||||
return div();
|
||||
};
|
||||
|
||||
let pubkey = SharedString::from(shorten_pubkey(announcement.public_key(), 16));
|
||||
let client_name = announcement.client_name();
|
||||
|
||||
v_flex()
|
||||
.size_full()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.p_2()
|
||||
.gap_10()
|
||||
.p_3()
|
||||
.gap_3()
|
||||
.w_full()
|
||||
.child(
|
||||
div()
|
||||
.text_xs()
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(SharedString::from(MSG)),
|
||||
)
|
||||
.child(divider(cx))
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_3()
|
||||
.text_sm()
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1p5()
|
||||
.child(
|
||||
div()
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(SharedString::from("Device Name:")),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.h_12()
|
||||
.items_center()
|
||||
.justify_center()
|
||||
.rounded(cx.theme().radius)
|
||||
.bg(cx.theme().elevated_surface_background)
|
||||
.child(client_name.clone()),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_1p5()
|
||||
.child(
|
||||
div()
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(SharedString::from("Encryption Public Key:")),
|
||||
)
|
||||
.child(
|
||||
h_flex()
|
||||
.h_7()
|
||||
.w_full()
|
||||
.px_2()
|
||||
.rounded(cx.theme().radius)
|
||||
.bg(cx.theme().elevated_surface_background)
|
||||
.child(pubkey),
|
||||
),
|
||||
),
|
||||
)
|
||||
.when(has_requests, |this| {
|
||||
this.child(divider(cx)).child(
|
||||
v_flex()
|
||||
.gap_1p5()
|
||||
.w_full()
|
||||
.child(
|
||||
div()
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(SharedString::from("Requests:")),
|
||||
)
|
||||
.child(
|
||||
v_flex()
|
||||
.gap_2()
|
||||
.flex_1()
|
||||
.w_full()
|
||||
.children(self.render_requests(cx)),
|
||||
),
|
||||
)
|
||||
})
|
||||
.child(divider(cx))
|
||||
.when(state.requesting(), |this| {
|
||||
this.child(
|
||||
h_flex()
|
||||
.h_8()
|
||||
.justify_center()
|
||||
.text_xs()
|
||||
.text_center()
|
||||
.text_color(cx.theme().text_accent)
|
||||
.bg(cx.theme().elevated_surface_background)
|
||||
.rounded(cx.theme().radius)
|
||||
.child(SharedString::from(
|
||||
"Please open other device and approve the request",
|
||||
)),
|
||||
)
|
||||
})
|
||||
.when(state.set(), |this| {
|
||||
this.child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
Button::new("reset")
|
||||
.icon(IconName::Reset)
|
||||
.label("Reset")
|
||||
.warning()
|
||||
.small()
|
||||
.font_semibold(),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.italic()
|
||||
.text_size(px(10.))
|
||||
.text_color(cx.theme().text_muted)
|
||||
.child(SharedString::from(NOTICE)),
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,6 +214,7 @@ impl MessagingRelayPanel {
|
||||
}
|
||||
Err(e) => {
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
this.set_updating(false, cx);
|
||||
this.set_error(e.to_string(), window, cx);
|
||||
})?;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::{anyhow, Error};
|
||||
use common::shorten_pubkey;
|
||||
use gpui::{
|
||||
div, rems, AnyElement, App, AppContext, ClipboardItem, Context, Entity, EventEmitter,
|
||||
FocusHandle, Focusable, IntoElement, ParentElement, PathPromptOptions, Render, SharedString,
|
||||
@@ -10,7 +9,7 @@ use gpui::{
|
||||
};
|
||||
use gpui_tokio::Tokio;
|
||||
use nostr_sdk::prelude::*;
|
||||
use person::{Person, PersonRegistry};
|
||||
use person::{shorten_pubkey, Person, PersonRegistry};
|
||||
use settings::AppSettings;
|
||||
use smol::fs;
|
||||
use state::{nostr_upload, NostrRegistry};
|
||||
|
||||
@@ -234,6 +234,7 @@ impl RelayListPanel {
|
||||
}
|
||||
Err(e) => {
|
||||
this.update_in(cx, |this, window, cx| {
|
||||
this.set_updating(false, cx);
|
||||
this.set_error(e.to_string(), window, cx);
|
||||
})?;
|
||||
}
|
||||
|
||||
@@ -182,14 +182,19 @@ impl Workspace {
|
||||
fn on_command(&mut self, command: &Command, window: &mut Window, cx: &mut Context<Self>) {
|
||||
match command {
|
||||
Command::OpenEncryptionPanel => {
|
||||
self.dock.update(cx, |this, cx| {
|
||||
this.add_panel(
|
||||
Arc::new(encryption_key::init(window, cx)),
|
||||
DockPlacement::Right,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let signer = nostr.read(cx).signer();
|
||||
|
||||
if let Some(public_key) = signer.public_key() {
|
||||
self.dock.update(cx, |this, cx| {
|
||||
this.add_panel(
|
||||
Arc::new(encryption_key::init(public_key, window, cx)),
|
||||
DockPlacement::Right,
|
||||
window,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
Command::OpenInboxPanel => {
|
||||
self.dock.update(cx, |this, cx| {
|
||||
|
||||
@@ -25,12 +25,12 @@ impl Global for GlobalDeviceRegistry {}
|
||||
/// NIP-4e: https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md
|
||||
#[derive(Debug)]
|
||||
pub struct DeviceRegistry {
|
||||
/// Request for encryption key from other devices
|
||||
pub requests: Vec<Event>,
|
||||
|
||||
/// Device state
|
||||
state: DeviceState,
|
||||
|
||||
/// Device requests
|
||||
requests: Entity<HashSet<Event>>,
|
||||
|
||||
/// Async tasks
|
||||
tasks: Vec<Task<Result<(), Error>>>,
|
||||
|
||||
@@ -52,8 +52,6 @@ impl DeviceRegistry {
|
||||
/// Create a new device registry instance
|
||||
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let requests = cx.new(|_| HashSet::default());
|
||||
|
||||
let mut subscriptions = smallvec![];
|
||||
|
||||
subscriptions.push(
|
||||
@@ -77,7 +75,7 @@ impl DeviceRegistry {
|
||||
});
|
||||
|
||||
Self {
|
||||
requests,
|
||||
requests: vec![],
|
||||
state: DeviceState::default(),
|
||||
tasks: vec![],
|
||||
_subscriptions: subscriptions,
|
||||
@@ -89,7 +87,7 @@ impl DeviceRegistry {
|
||||
let client = nostr.read(cx).client();
|
||||
let (tx, rx) = flume::bounded::<Event>(100);
|
||||
|
||||
cx.background_spawn(async move {
|
||||
self.tasks.push(cx.background_spawn(async move {
|
||||
let mut notifications = client.notifications();
|
||||
let mut processed_events = HashSet::new();
|
||||
|
||||
@@ -107,20 +105,21 @@ impl DeviceRegistry {
|
||||
match event.kind {
|
||||
Kind::Custom(4454) => {
|
||||
if verify_author(&client, event.as_ref()).await {
|
||||
tx.send_async(event.into_owned()).await.ok();
|
||||
tx.send_async(event.into_owned()).await?;
|
||||
}
|
||||
}
|
||||
Kind::Custom(4455) => {
|
||||
if verify_author(&client, event.as_ref()).await {
|
||||
tx.send_async(event.into_owned()).await.ok();
|
||||
tx.send_async(event.into_owned()).await?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
self.tasks.push(
|
||||
// Update GPUI states
|
||||
@@ -147,8 +146,8 @@ impl DeviceRegistry {
|
||||
}
|
||||
|
||||
/// Get the device state
|
||||
pub fn state(&self) -> &DeviceState {
|
||||
&self.state
|
||||
pub fn state(&self) -> DeviceState {
|
||||
self.state.clone()
|
||||
}
|
||||
|
||||
/// Set the device state
|
||||
@@ -181,19 +180,25 @@ impl DeviceRegistry {
|
||||
/// Reset the device state
|
||||
fn reset(&mut self, cx: &mut Context<Self>) {
|
||||
self.state = DeviceState::Idle;
|
||||
self.requests.update(cx, |this, cx| {
|
||||
this.clear();
|
||||
cx.notify();
|
||||
});
|
||||
self.requests.clear();
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// Add a request for device keys
|
||||
fn add_request(&mut self, request: Event, cx: &mut Context<Self>) {
|
||||
self.requests.update(cx, |this, cx| {
|
||||
this.insert(request);
|
||||
cx.notify();
|
||||
});
|
||||
self.requests.push(request);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// Remove a request for device keys
|
||||
pub fn remove_request(&mut self, id: &EventId, cx: &mut Context<Self>) {
|
||||
self.requests.retain(|r| r.id != *id);
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// Check if there are any pending requests
|
||||
pub fn has_requests(&self) -> bool {
|
||||
!self.requests.is_empty()
|
||||
}
|
||||
|
||||
/// Get all messages for encryption keys
|
||||
@@ -290,12 +295,12 @@ impl DeviceRegistry {
|
||||
match task.await {
|
||||
Ok(event) => {
|
||||
this.update(cx, |this, cx| {
|
||||
this.init_device_signer(&event, cx);
|
||||
this.new_signer(&event, cx);
|
||||
})?;
|
||||
}
|
||||
Err(_) => {
|
||||
this.update(cx, |this, cx| {
|
||||
this.announce_device(cx);
|
||||
this.announce(cx);
|
||||
})?;
|
||||
}
|
||||
}
|
||||
@@ -305,13 +310,15 @@ impl DeviceRegistry {
|
||||
}
|
||||
|
||||
/// Create a new device signer and announce it
|
||||
fn announce_device(&mut self, cx: &mut Context<Self>) {
|
||||
fn announce(&mut self, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
// Get current user
|
||||
let signer = nostr.read(cx).signer();
|
||||
let public_key = signer.public_key().unwrap();
|
||||
|
||||
// Get user's write relays
|
||||
let write_relays = nostr.read(cx).write_relays(&public_key, cx);
|
||||
|
||||
let keys = Keys::generate();
|
||||
@@ -338,20 +345,20 @@ impl DeviceRegistry {
|
||||
Ok(())
|
||||
});
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
self.tasks.push(cx.spawn(async move |this, cx| {
|
||||
if task.await.is_ok() {
|
||||
this.update(cx, |this, cx| {
|
||||
this.set_signer(keys, cx);
|
||||
this.listen_device_request(cx);
|
||||
})
|
||||
.ok();
|
||||
this.listen_request(cx);
|
||||
})?;
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
}
|
||||
|
||||
/// Initialize device signer (decoupled encryption key) for the current user
|
||||
fn init_device_signer(&mut self, event: &Event, cx: &mut Context<Self>) {
|
||||
fn new_signer(&mut self, event: &Event, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
@@ -375,14 +382,14 @@ impl DeviceRegistry {
|
||||
Ok(keys) => {
|
||||
this.update(cx, |this, cx| {
|
||||
this.set_signer(keys, cx);
|
||||
this.listen_device_request(cx);
|
||||
this.listen_request(cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
Err(e) => {
|
||||
this.update(cx, |this, cx| {
|
||||
this.request_device_keys(cx);
|
||||
this.listen_device_approval(cx);
|
||||
this.request(cx);
|
||||
this.listen_approval(cx);
|
||||
})
|
||||
.ok();
|
||||
|
||||
@@ -394,7 +401,7 @@ impl DeviceRegistry {
|
||||
}
|
||||
|
||||
/// Listen for device key requests on user's write relays
|
||||
fn listen_device_request(&mut self, cx: &mut Context<Self>) {
|
||||
fn listen_request(&mut self, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
@@ -426,7 +433,7 @@ impl DeviceRegistry {
|
||||
}
|
||||
|
||||
/// Listen for device key approvals on user's write relays
|
||||
fn listen_device_approval(&mut self, cx: &mut Context<Self>) {
|
||||
fn listen_approval(&mut self, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
@@ -435,7 +442,7 @@ impl DeviceRegistry {
|
||||
|
||||
let write_relays = nostr.read(cx).write_relays(&public_key, cx);
|
||||
|
||||
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||
self.tasks.push(cx.background_spawn(async move {
|
||||
let urls = write_relays.await;
|
||||
|
||||
// Construct a filter for device key requests
|
||||
@@ -452,13 +459,11 @@ impl DeviceRegistry {
|
||||
client.subscribe(target).await?;
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
task.detach();
|
||||
}));
|
||||
}
|
||||
|
||||
/// Request encryption keys from other device
|
||||
fn request_device_keys(&mut self, cx: &mut Context<Self>) {
|
||||
fn request(&mut self, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
@@ -559,34 +564,32 @@ impl DeviceRegistry {
|
||||
Ok(keys)
|
||||
});
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
match task.await {
|
||||
Ok(keys) => {
|
||||
this.update(cx, |this, cx| {
|
||||
this.set_signer(keys, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Error: {e}")
|
||||
}
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
self.tasks.push(cx.spawn(async move |this, cx| {
|
||||
let keys = task.await?;
|
||||
|
||||
// Update signer
|
||||
this.update(cx, |this, cx| {
|
||||
this.set_signer(keys, cx);
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
}
|
||||
|
||||
/// Approve requests for device keys from other devices
|
||||
#[allow(dead_code)]
|
||||
fn approve(&mut self, event: Event, cx: &mut Context<Self>) {
|
||||
pub fn approve(&self, event: &Event, cx: &App) -> Task<Result<(), Error>> {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
// Get current user
|
||||
let signer = nostr.read(cx).signer();
|
||||
let public_key = signer.public_key().unwrap();
|
||||
|
||||
// Get user's write relays
|
||||
let write_relays = nostr.read(cx).write_relays(&public_key, cx);
|
||||
let event = event.clone();
|
||||
|
||||
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||
cx.background_spawn(async move {
|
||||
let urls = write_relays.await;
|
||||
|
||||
// Get device keys
|
||||
@@ -619,9 +622,7 @@ impl DeviceRegistry {
|
||||
client.send_event(&event).to(urls).await?;
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
task.detach();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use std::sync::OnceLock;
|
||||
pub const CLIENT_NAME: &str = "Coop";
|
||||
|
||||
/// COOP's public key
|
||||
pub const COOP_PUBKEY: &str = "npub126kl5fruqan90py77gf6pvfvygefl2mu2ukew6xdx5pc5uqscwgsnkgarv";
|
||||
pub const COOP_PUBKEY: &str = "npub1j3rz3ndl902lya6ywxvy5c983lxs8mpukqnx4pa4lt5wrykwl5ys7wpw3x";
|
||||
|
||||
/// App ID
|
||||
pub const APP_ID: &str = "su.reya.coop";
|
||||
|
||||
@@ -9,6 +9,20 @@ pub enum DeviceState {
|
||||
Set,
|
||||
}
|
||||
|
||||
impl DeviceState {
|
||||
pub fn idle(&self) -> bool {
|
||||
matches!(self, DeviceState::Idle)
|
||||
}
|
||||
|
||||
pub fn requesting(&self) -> bool {
|
||||
matches!(self, DeviceState::Requesting)
|
||||
}
|
||||
|
||||
pub fn set(&self) -> bool {
|
||||
matches!(self, DeviceState::Set)
|
||||
}
|
||||
}
|
||||
|
||||
/// Announcement
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Announcement {
|
||||
|
||||
@@ -47,6 +47,7 @@ pub enum IconName {
|
||||
Plus,
|
||||
PlusCircle,
|
||||
Profile,
|
||||
Reset,
|
||||
Relay,
|
||||
Reply,
|
||||
Refresh,
|
||||
@@ -112,6 +113,7 @@ impl IconNamed for IconName {
|
||||
Self::Plus => "icons/plus.svg",
|
||||
Self::PlusCircle => "icons/plus-circle.svg",
|
||||
Self::Profile => "icons/profile.svg",
|
||||
Self::Reset => "icons/reset.svg",
|
||||
Self::Relay => "icons/relay.svg",
|
||||
Self::Reply => "icons/reply.svg",
|
||||
Self::Refresh => "icons/refresh.svg",
|
||||
|
||||
Reference in New Issue
Block a user