wip
This commit is contained in:
@@ -253,6 +253,11 @@ impl ChatRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the loading status of the chat registry
|
||||
pub fn loading(&self) -> bool {
|
||||
self.loading
|
||||
}
|
||||
|
||||
/// Set the loading status of the chat registry
|
||||
pub fn set_loading(&mut self, loading: bool, cx: &mut Context<Self>) {
|
||||
self.loading = loading;
|
||||
@@ -511,7 +516,7 @@ impl ChatRegistry {
|
||||
}
|
||||
|
||||
// Set this room is ongoing if the new message is from current user
|
||||
if author == nostr.read(cx).identity(cx).public_key() {
|
||||
if author == nostr.read(cx).identity().read(cx).public_key() {
|
||||
this.set_ongoing(cx);
|
||||
}
|
||||
|
||||
|
||||
@@ -263,7 +263,7 @@ impl Room {
|
||||
pub fn display_member(&self, cx: &App) -> Profile {
|
||||
let persons = PersonRegistry::global(cx);
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let public_key = nostr.read(cx).identity(cx).public_key();
|
||||
let public_key = nostr.read(cx).identity().read(cx).public_key();
|
||||
|
||||
let target_member = self
|
||||
.members
|
||||
@@ -369,7 +369,7 @@ impl Room {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
|
||||
// Get current user
|
||||
let public_key = nostr.read(cx).identity(cx).public_key();
|
||||
let public_key = nostr.read(cx).identity().read(cx).public_key();
|
||||
|
||||
// Get room's subject
|
||||
let subject = self.subject.clone();
|
||||
@@ -438,7 +438,7 @@ impl Room {
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
// Get current user's public key and relays
|
||||
let current_user = nostr.read(cx).identity(cx).public_key();
|
||||
let current_user = nostr.read(cx).identity().read(cx).public_key();
|
||||
let current_user_relays = nostr.read(cx).messaging_relays(¤t_user, cx);
|
||||
|
||||
let rumor = rumor.to_owned();
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::collections::HashSet;
|
||||
use std::time::Duration;
|
||||
|
||||
pub use actions::*;
|
||||
use chat::{Message, RenderedMessage, Room, RoomKind, RoomSignal, SendOptions, SendReport};
|
||||
use chat::{Message, RenderedMessage, Room, RoomKind, RoomSignal, SendReport};
|
||||
use common::{nip96_upload, RenderedProfile, RenderedTimestamp};
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
@@ -59,7 +59,6 @@ pub struct ChatPanel {
|
||||
|
||||
// New Message
|
||||
input: Entity<InputState>,
|
||||
options: Entity<SendOptions>,
|
||||
replies_to: Entity<HashSet<EventId>>,
|
||||
|
||||
// Media Attachment
|
||||
@@ -87,7 +86,6 @@ impl ChatPanel {
|
||||
|
||||
let attachments = cx.new(|_| vec![]);
|
||||
let replies_to = cx.new(|_| HashSet::new());
|
||||
let options = cx.new(|_| SendOptions::default());
|
||||
|
||||
let id = room.read(cx).id.to_string().into();
|
||||
let messages = BTreeSet::from([Message::system()]);
|
||||
@@ -145,18 +143,11 @@ impl ChatPanel {
|
||||
cx.subscribe_in(&room, window, move |this, _, signal, window, cx| {
|
||||
match signal {
|
||||
RoomSignal::NewMessage((gift_wrap_id, event)) => {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let tracker = nostr.read(cx).tracker();
|
||||
let gift_wrap_id = gift_wrap_id.to_owned();
|
||||
let message = Message::user(event.clone());
|
||||
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
let tracker = tracker.read().await;
|
||||
|
||||
this.update_in(cx, |this, _window, cx| {
|
||||
if !tracker.sent_ids().contains(&gift_wrap_id) {
|
||||
this.insert_message(message, false, cx);
|
||||
}
|
||||
this.insert_message(message, false, cx);
|
||||
})
|
||||
.ok();
|
||||
})
|
||||
@@ -189,7 +180,6 @@ impl ChatPanel {
|
||||
input,
|
||||
replies_to,
|
||||
attachments,
|
||||
options,
|
||||
rendered_texts_by_id: BTreeMap::new(),
|
||||
reports_by_id: BTreeMap::new(),
|
||||
uploading: false,
|
||||
@@ -271,14 +261,13 @@ impl ChatPanel {
|
||||
|
||||
// Get the current room entity
|
||||
let room = self.room.read(cx);
|
||||
let opts = self.options.read(cx);
|
||||
|
||||
// Create a temporary message for optimistic update
|
||||
let rumor = room.create_message(&content, replies.as_ref(), cx);
|
||||
let rumor_id = rumor.id.unwrap();
|
||||
|
||||
// Create a task for sending the message in the background
|
||||
let send_message = room.send_message(&rumor, opts, cx);
|
||||
let send_message = room.send_message(&rumor, cx);
|
||||
|
||||
// Optimistically update message list
|
||||
cx.spawn_in(window, async move |this, cx| {
|
||||
@@ -440,10 +429,6 @@ impl ChatPanel {
|
||||
persons.read(cx).get_person(public_key, cx)
|
||||
}
|
||||
|
||||
fn signer_kind(&self, cx: &App) -> SignerKind {
|
||||
self.options.read(cx).signer_kind
|
||||
}
|
||||
|
||||
fn scroll_to(&self, id: EventId) {
|
||||
if let Some(ix) = self.messages.iter().position(|m| {
|
||||
if let Message::User(msg) = m {
|
||||
@@ -1235,71 +1220,6 @@ impl ChatPanel {
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
fn on_open_seen_on(&mut self, ev: &SeenOn, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let id = ev.0;
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let tracker = nostr.read(cx).tracker();
|
||||
|
||||
let task: Task<Result<Vec<RelayUrl>, Error>> = cx.background_spawn(async move {
|
||||
let tracker = tracker.read().await;
|
||||
let mut relays: Vec<RelayUrl> = vec![];
|
||||
|
||||
let filter = Filter::new()
|
||||
.kind(Kind::ApplicationSpecificData)
|
||||
.event(id)
|
||||
.limit(1);
|
||||
|
||||
if let Some(event) = client.database().query(filter).await?.first_owned() {
|
||||
if let Some(Ok(id)) = event.tags.identifier().map(EventId::parse) {
|
||||
if let Some(urls) = tracker.seen_on_relays.get(&id).cloned() {
|
||||
relays.extend(urls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(relays)
|
||||
});
|
||||
|
||||
cx.spawn_in(window, async move |_, cx| {
|
||||
if let Ok(urls) = task.await {
|
||||
cx.update(|window, cx| {
|
||||
window.open_modal(cx, move |this, _window, cx| {
|
||||
this.show_close(true)
|
||||
.title(SharedString::from("Seen on"))
|
||||
.child(v_flex().pb_4().gap_2().children({
|
||||
let mut items = Vec::with_capacity(urls.len());
|
||||
|
||||
for url in urls.clone().into_iter() {
|
||||
items.push(
|
||||
h_flex()
|
||||
.h_8()
|
||||
.px_2()
|
||||
.bg(cx.theme().elevated_surface_background)
|
||||
.rounded(cx.theme().radius)
|
||||
.font_semibold()
|
||||
.text_xs()
|
||||
.child(SharedString::from(url.to_string())),
|
||||
)
|
||||
}
|
||||
|
||||
items
|
||||
}))
|
||||
});
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn on_set_encryption(&mut self, ev: &SetSigner, _: &mut Window, cx: &mut Context<Self>) {
|
||||
self.options.update(cx, move |this, cx| {
|
||||
this.signer_kind = ev.0;
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Panel for ChatPanel {
|
||||
@@ -1339,11 +1259,7 @@ impl Focusable for ChatPanel {
|
||||
|
||||
impl Render for ChatPanel {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let kind = self.signer_kind(cx);
|
||||
|
||||
v_flex()
|
||||
.on_action(cx.listener(Self::on_open_seen_on))
|
||||
.on_action(cx.listener(Self::on_set_encryption))
|
||||
.image_cache(self.image_cache.clone())
|
||||
.size_full()
|
||||
.child(
|
||||
@@ -1398,31 +1314,7 @@ impl Render for ChatPanel {
|
||||
.large(),
|
||||
),
|
||||
)
|
||||
.child(TextInput::new(&self.input))
|
||||
.child(
|
||||
Button::new("encryptions")
|
||||
.icon(IconName::Encryption)
|
||||
.ghost()
|
||||
.large()
|
||||
.popup_menu(move |this, _window, _cx| {
|
||||
this.label("Encrypt by:")
|
||||
.menu_with_check(
|
||||
"Encryption Key",
|
||||
matches!(kind, SignerKind::Encryption),
|
||||
Box::new(SetSigner(SignerKind::Encryption)),
|
||||
)
|
||||
.menu_with_check(
|
||||
"User's Identity",
|
||||
matches!(kind, SignerKind::User),
|
||||
Box::new(SetSigner(SignerKind::User)),
|
||||
)
|
||||
.menu_with_check(
|
||||
"Auto",
|
||||
matches!(kind, SignerKind::Auto),
|
||||
Box::new(SetSigner(SignerKind::Auto)),
|
||||
)
|
||||
}),
|
||||
),
|
||||
.child(TextInput::new(&self.input)),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use account::Account;
|
||||
use auto_update::{AutoUpdateStatus, AutoUpdater};
|
||||
use chat::{ChatEvent, ChatRegistry};
|
||||
use chat_ui::{CopyPublicKey, OpenPublicKey};
|
||||
use common::{RenderedProfile, DEFAULT_SIDEBAR_WIDTH};
|
||||
use encryption::Encryption;
|
||||
use encryption_ui::EncryptionPanel;
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
deferred, div, px, relative, rems, App, AppContext, Axis, ClipboardItem, Context, Entity,
|
||||
@@ -19,6 +16,7 @@ use person::PersonRegistry;
|
||||
use relay_auth::RelayAuth;
|
||||
use settings::AppSettings;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use state::NostrRegistry;
|
||||
use theme::{ActiveTheme, Theme, ThemeMode, ThemeRegistry};
|
||||
use title_bar::TitleBar;
|
||||
use ui::avatar::Avatar;
|
||||
@@ -27,7 +25,6 @@ use ui::dock_area::dock::DockPlacement;
|
||||
use ui::dock_area::panel::PanelView;
|
||||
use ui::dock_area::{ClosePanel, DockArea, DockItem};
|
||||
use ui::modal::ModalButtonProps;
|
||||
use ui::popover::{Popover, PopoverContent};
|
||||
use ui::popup_menu::PopupMenuExt;
|
||||
use ui::{h_flex, v_flex, ContextModal, IconName, Root, Sizable, StyledExt};
|
||||
|
||||
@@ -61,9 +58,6 @@ pub struct ChatSpace {
|
||||
/// App's Dock Area
|
||||
dock: Entity<DockArea>,
|
||||
|
||||
/// App's Encryption Panel
|
||||
encryption_panel: Entity<EncryptionPanel>,
|
||||
|
||||
/// Determines if the chat space is ready to use
|
||||
ready: bool,
|
||||
|
||||
@@ -73,13 +67,14 @@ pub struct ChatSpace {
|
||||
|
||||
impl ChatSpace {
|
||||
pub fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let chat = ChatRegistry::global(cx);
|
||||
let keystore = KeyStore::global(cx);
|
||||
let account = Account::global(cx);
|
||||
|
||||
let title_bar = cx.new(|_| TitleBar::new());
|
||||
let dock = cx.new(|cx| DockArea::new(window, cx));
|
||||
let encryption_panel = encryption_ui::init(window, cx);
|
||||
|
||||
let identity = nostr.read(cx).identity();
|
||||
|
||||
let mut subscriptions = smallvec![];
|
||||
|
||||
@@ -92,8 +87,8 @@ impl ChatSpace {
|
||||
|
||||
subscriptions.push(
|
||||
// Observe account entity changes
|
||||
cx.observe_in(&account, window, move |this, state, window, cx| {
|
||||
if !this.ready && state.read(cx).has_account() {
|
||||
cx.observe_in(&identity, window, move |this, state, window, cx| {
|
||||
if !this.ready && state.read(cx).has_public_key() {
|
||||
this.set_default_layout(window, cx);
|
||||
|
||||
// Load all chat room in the database if available
|
||||
@@ -175,7 +170,6 @@ impl ChatSpace {
|
||||
Self {
|
||||
dock,
|
||||
title_bar,
|
||||
encryption_panel,
|
||||
ready: false,
|
||||
_subscriptions: subscriptions,
|
||||
}
|
||||
@@ -447,11 +441,11 @@ impl ChatSpace {
|
||||
}
|
||||
|
||||
fn titlebar_left(&mut self, _window: &mut Window, cx: &Context<Self>) -> impl IntoElement {
|
||||
let account = Account::global(cx);
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let chat = ChatRegistry::global(cx);
|
||||
let status = chat.read(cx).loading;
|
||||
let status = chat.read(cx).loading();
|
||||
|
||||
if !account.read(cx).has_account() {
|
||||
if !nostr.read(cx).identity().read(cx).has_public_key() {
|
||||
return div();
|
||||
}
|
||||
|
||||
@@ -479,10 +473,12 @@ impl ChatSpace {
|
||||
fn titlebar_right(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let proxy = AppSettings::get_proxy_user_avatars(cx);
|
||||
let auto_update = AutoUpdater::global(cx);
|
||||
let account = Account::global(cx);
|
||||
|
||||
let relay_auth = RelayAuth::global(cx);
|
||||
let pending_requests = relay_auth.read(cx).pending_requests(cx);
|
||||
let encryption_panel = self.encryption_panel.downgrade();
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let identity = nostr.read(cx).identity();
|
||||
|
||||
h_flex()
|
||||
.gap_2()
|
||||
@@ -542,16 +538,10 @@ impl ChatSpace {
|
||||
}),
|
||||
)
|
||||
})
|
||||
.when(account.read(cx).has_account(), |this| {
|
||||
let account = Account::global(cx);
|
||||
let public_key = account.read(cx).public_key();
|
||||
|
||||
.when_some(identity.read(cx).option_public_key(), |this, public_key| {
|
||||
let persons = PersonRegistry::global(cx);
|
||||
let profile = persons.read(cx).get_person(&public_key, cx);
|
||||
|
||||
let encryption = Encryption::global(cx);
|
||||
let has_encryption = encryption.read(cx).has_encryption(cx);
|
||||
|
||||
let keystore = KeyStore::global(cx);
|
||||
let is_using_file_keystore = keystore.read(cx).is_using_file_keystore();
|
||||
|
||||
@@ -562,82 +552,38 @@ impl ChatSpace {
|
||||
};
|
||||
|
||||
this.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.child(
|
||||
Popover::new("encryption")
|
||||
.trigger(
|
||||
Button::new("encryption-trigger")
|
||||
.tooltip("Manage Encryption Key")
|
||||
.icon(IconName::Encryption)
|
||||
.rounded()
|
||||
.small()
|
||||
.cta()
|
||||
.map(|this| match has_encryption {
|
||||
true => this.ghost_alt(),
|
||||
false => this.warning(),
|
||||
}),
|
||||
Button::new("user")
|
||||
.small()
|
||||
.reverse()
|
||||
.transparent()
|
||||
.icon(IconName::CaretDown)
|
||||
.child(Avatar::new(profile.avatar(proxy)).size(rems(1.45)))
|
||||
.popup_menu(move |this, _window, _cx| {
|
||||
this.label(profile.display_name())
|
||||
.menu_with_icon(
|
||||
"Profile",
|
||||
IconName::EmojiFill,
|
||||
Box::new(ViewProfile),
|
||||
)
|
||||
.content(move |window, cx| {
|
||||
let encryption_panel = encryption_panel.clone();
|
||||
|
||||
cx.new(|cx| {
|
||||
PopoverContent::new(window, cx, move |_window, _cx| {
|
||||
if let Some(view) = encryption_panel.upgrade() {
|
||||
view.clone().into_any_element()
|
||||
} else {
|
||||
div().into_any_element()
|
||||
}
|
||||
})
|
||||
})
|
||||
}),
|
||||
)
|
||||
.child(
|
||||
Button::new("user")
|
||||
.small()
|
||||
.reverse()
|
||||
.transparent()
|
||||
.icon(IconName::CaretDown)
|
||||
.child(Avatar::new(profile.avatar(proxy)).size(rems(1.45)))
|
||||
.popup_menu(move |this, _window, _cx| {
|
||||
this.label(profile.display_name())
|
||||
.menu_with_icon(
|
||||
"Profile",
|
||||
IconName::EmojiFill,
|
||||
Box::new(ViewProfile),
|
||||
)
|
||||
.menu_with_icon(
|
||||
"Messaging Relays",
|
||||
IconName::Server,
|
||||
Box::new(ViewRelays),
|
||||
)
|
||||
.separator()
|
||||
.label(SharedString::from("Keyring Service"))
|
||||
.menu_with_icon_and_disabled(
|
||||
keyring_label.clone(),
|
||||
IconName::Encryption,
|
||||
Box::new(KeyringPopup),
|
||||
!is_using_file_keystore,
|
||||
)
|
||||
.separator()
|
||||
.menu_with_icon(
|
||||
"Dark Mode",
|
||||
IconName::Sun,
|
||||
Box::new(DarkMode),
|
||||
)
|
||||
.menu_with_icon("Themes", IconName::Moon, Box::new(Themes))
|
||||
.menu_with_icon(
|
||||
"Settings",
|
||||
IconName::Settings,
|
||||
Box::new(Settings),
|
||||
)
|
||||
.menu_with_icon(
|
||||
"Sign Out",
|
||||
IconName::Logout,
|
||||
Box::new(Logout),
|
||||
)
|
||||
}),
|
||||
),
|
||||
.menu_with_icon(
|
||||
"Messaging Relays",
|
||||
IconName::Server,
|
||||
Box::new(ViewRelays),
|
||||
)
|
||||
.separator()
|
||||
.label(SharedString::from("Keyring Service"))
|
||||
.menu_with_icon_and_disabled(
|
||||
keyring_label.clone(),
|
||||
IconName::Encryption,
|
||||
Box::new(KeyringPopup),
|
||||
!is_using_file_keystore,
|
||||
)
|
||||
.separator()
|
||||
.menu_with_icon("Dark Mode", IconName::Sun, Box::new(DarkMode))
|
||||
.menu_with_icon("Themes", IconName::Moon, Box::new(Themes))
|
||||
.menu_with_icon("Settings", IconName::Settings, Box::new(Settings))
|
||||
.menu_with_icon("Sign Out", IconName::Logout, Box::new(Logout))
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -96,12 +96,6 @@ fn main() {
|
||||
// Initialize settings
|
||||
settings::init(cx);
|
||||
|
||||
// Initialize account state
|
||||
account::init(cx);
|
||||
|
||||
// Initialize encryption state
|
||||
encryption::init(cx);
|
||||
|
||||
// Initialize app registry
|
||||
chat::init(cx);
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::ops::Range;
|
||||
use std::time::Duration;
|
||||
|
||||
use account::Account;
|
||||
use anyhow::{anyhow, Error};
|
||||
use chat::{ChatEvent, ChatRegistry, Room, RoomKind};
|
||||
use common::{DebouncedDelay, RenderedTimestamp, TextUtils, BOOTSTRAP_RELAYS, SEARCH_RELAYS};
|
||||
@@ -199,11 +198,9 @@ impl Sidebar {
|
||||
}
|
||||
|
||||
fn search_by_nip50(&mut self, query: &str, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let account = Account::global(cx);
|
||||
let public_key = account.read(cx).public_key();
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let public_key = nostr.read(cx).identity().read(cx).public_key();
|
||||
|
||||
let query = query.to_owned();
|
||||
|
||||
@@ -597,7 +594,7 @@ impl Focusable for Sidebar {
|
||||
impl Render for Sidebar {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let chat = ChatRegistry::global(cx);
|
||||
let loading = chat.read(cx).loading;
|
||||
let loading = chat.read(cx).loading();
|
||||
|
||||
// Get rooms from either search results or the chat registry
|
||||
let rooms = if let Some(results) = self.search_results.read(cx).as_ref() {
|
||||
|
||||
@@ -259,17 +259,11 @@ impl UserProfile {
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let gossip = nostr.read(cx).gossip();
|
||||
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 signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
|
||||
let gossip = gossip.read().await;
|
||||
let write_relays = gossip.inbox_relays(&public_key);
|
||||
|
||||
// Ensure connections to the write relays
|
||||
gossip.ensure_connections(&client, &write_relays).await;
|
||||
|
||||
// Sign the new metadata event
|
||||
let event = EventBuilder::metadata(&new_metadata).sign(&signer).await?;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use std::ops::Range;
|
||||
use std::time::Duration;
|
||||
|
||||
use account::Account;
|
||||
use anyhow::{anyhow, Error};
|
||||
use chat::{ChatRegistry, Room};
|
||||
use common::{nip05_profile, RenderedProfile, TextUtils, BOOTSTRAP_RELAYS};
|
||||
@@ -312,9 +311,8 @@ 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 nostr = NostrRegistry::global(cx);
|
||||
let public_key = nostr.read(cx).identity().read(cx).public_key();
|
||||
|
||||
let receivers: Vec<PublicKey> = self.selected(cx);
|
||||
let subject_input = self.title_input.read(cx).value();
|
||||
|
||||
@@ -158,17 +158,13 @@ impl SetupRelay {
|
||||
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
let gossip = nostr.read(cx).gossip();
|
||||
let public_key = nostr.read(cx).identity().read(cx).public_key();
|
||||
let write_relays = nostr.read(cx).write_relays(&public_key, cx);
|
||||
|
||||
let relays = self.relays.clone();
|
||||
|
||||
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||
let signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
let gossip = gossip.read().await;
|
||||
let write_relays = gossip.inbox_relays(&public_key);
|
||||
|
||||
// Ensure connections to the write relays
|
||||
gossip.ensure_connections(&client, &write_relays).await;
|
||||
|
||||
let tags: Vec<Tag> = relays
|
||||
.iter()
|
||||
|
||||
@@ -29,11 +29,16 @@ pub fn init(cre: Credential, window: &mut Window, cx: &mut App) -> Entity<Startu
|
||||
/// Startup
|
||||
#[derive(Debug)]
|
||||
pub struct Startup {
|
||||
credential: Credential,
|
||||
loading: bool,
|
||||
|
||||
name: SharedString,
|
||||
focus_handle: FocusHandle,
|
||||
|
||||
/// Local user credentials
|
||||
credential: Credential,
|
||||
|
||||
/// Whether the loadng is in progress
|
||||
loading: bool,
|
||||
|
||||
/// Image cache
|
||||
image_cache: Entity<RetainAllImageCache>,
|
||||
|
||||
/// Event subscriptions
|
||||
@@ -164,15 +169,12 @@ impl Startup {
|
||||
}
|
||||
|
||||
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);
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
|
||||
// Update the signer
|
||||
cx.background_spawn(async move {
|
||||
client.set_signer(keys).await;
|
||||
nostr.update(cx, |this, cx| {
|
||||
this.set_signer(keys, cx);
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn set_loading(&mut self, status: bool, cx: &mut Context<Self>) {
|
||||
|
||||
@@ -75,6 +75,11 @@ impl Identity {
|
||||
self.public_key = None;
|
||||
}
|
||||
|
||||
/// Returns the public key of the identity.
|
||||
pub fn option_public_key(&self) -> Option<PublicKey> {
|
||||
self.public_key
|
||||
}
|
||||
|
||||
/// Returns the public key of the identity.
|
||||
pub fn public_key(&self) -> PublicKey {
|
||||
// This method is safe to unwrap because the public key is always called when the identity is created.
|
||||
|
||||
@@ -286,8 +286,8 @@ impl NostrRegistry {
|
||||
}
|
||||
|
||||
/// Get current identity
|
||||
pub fn identity(&self, cx: &App) -> Identity {
|
||||
self.identity.read(cx).clone()
|
||||
pub fn identity(&self) -> Entity<Identity> {
|
||||
self.identity.clone()
|
||||
}
|
||||
|
||||
/// Get a relay hint (messaging relay) for a given public key
|
||||
@@ -299,9 +299,58 @@ impl NostrRegistry {
|
||||
.cloned()
|
||||
}
|
||||
|
||||
/// Get a list of write relays for a given public key
|
||||
pub fn write_relays(&self, public_key: &PublicKey, cx: &App) -> Vec<RelayUrl> {
|
||||
let client = self.client();
|
||||
let relays = self.gossip.read(cx).write_relays(public_key);
|
||||
let async_relays = relays.clone();
|
||||
|
||||
// Ensure relay connections
|
||||
cx.background_spawn(async move {
|
||||
for url in async_relays.iter() {
|
||||
client.add_relay(url).await.ok();
|
||||
client.connect_relay(url).await.ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
relays
|
||||
}
|
||||
|
||||
/// Get a list of read relays for a given public key
|
||||
pub fn read_relays(&self, public_key: &PublicKey, cx: &App) -> Vec<RelayUrl> {
|
||||
let client = self.client();
|
||||
let relays = self.gossip.read(cx).read_relays(public_key);
|
||||
let async_relays = relays.clone();
|
||||
|
||||
// Ensure relay connections
|
||||
cx.background_spawn(async move {
|
||||
for url in async_relays.iter() {
|
||||
client.add_relay(url).await.ok();
|
||||
client.connect_relay(url).await.ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
relays
|
||||
}
|
||||
|
||||
/// Get a list of messaging relays for a given public key
|
||||
pub fn messaging_relays(&self, public_key: &PublicKey, cx: &App) -> Vec<RelayUrl> {
|
||||
self.gossip.read(cx).messaging_relays(public_key)
|
||||
let client = self.client();
|
||||
let relays = self.gossip.read(cx).messaging_relays(public_key);
|
||||
let async_relays = relays.clone();
|
||||
|
||||
// Ensure relay connections
|
||||
cx.background_spawn(async move {
|
||||
for url in async_relays.iter() {
|
||||
client.add_relay(url).await.ok();
|
||||
client.connect_relay(url).await.ok();
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
relays
|
||||
}
|
||||
|
||||
/// Set the signer for the nostr client and verify the public key
|
||||
@@ -370,7 +419,7 @@ impl NostrRegistry {
|
||||
fn get_relay_list(&mut self, cx: &mut Context<Self>) {
|
||||
let client = self.client();
|
||||
let async_identity = self.identity.downgrade();
|
||||
let public_key = self.identity(cx).public_key();
|
||||
let public_key = self.identity().read(cx).public_key();
|
||||
|
||||
let task: Task<Result<RelayState, Error>> = cx.background_spawn(async move {
|
||||
let filter = Filter::new()
|
||||
@@ -415,8 +464,8 @@ impl NostrRegistry {
|
||||
fn get_messaging_relays(&mut self, cx: &mut Context<Self>) {
|
||||
let client = self.client();
|
||||
let async_identity = self.identity.downgrade();
|
||||
let public_key = self.identity(cx).public_key();
|
||||
let write_relays = self.gossip.read(cx).write_relays(&public_key);
|
||||
let public_key = self.identity().read(cx).public_key();
|
||||
let write_relays = self.write_relays(&public_key, cx);
|
||||
|
||||
let task: Task<Result<RelayState, Error>> = cx.background_spawn(async move {
|
||||
let filter = Filter::new()
|
||||
@@ -460,8 +509,8 @@ impl NostrRegistry {
|
||||
/// Continuously get gift wrap events for the current user in their messaging relays
|
||||
fn get_messages(&mut self, cx: &mut Context<Self>) {
|
||||
let client = self.client();
|
||||
let public_key = self.identity(cx).public_key();
|
||||
let messaging_relays = self.gossip.read(cx).messaging_relays(&public_key);
|
||||
let public_key = self.identity().read(cx).public_key();
|
||||
let messaging_relays = self.messaging_relays(&public_key, cx);
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let id = SubscriptionId::new(GIFTWRAP_SUBSCRIPTION);
|
||||
@@ -480,7 +529,7 @@ impl NostrRegistry {
|
||||
/// Publish an event to author's write relays
|
||||
pub fn publish(&self, event: Event, cx: &App) -> Task<Result<Output<EventId>, Error>> {
|
||||
let client = self.client();
|
||||
let write_relays = self.gossip.read(cx).write_relays(&event.pubkey);
|
||||
let write_relays = self.write_relays(&event.pubkey, cx);
|
||||
|
||||
cx.background_spawn(async move { Ok(client.send_event_to(&write_relays, &event).await?) })
|
||||
}
|
||||
@@ -491,7 +540,7 @@ impl NostrRegistry {
|
||||
I: Into<Vec<Kind>>,
|
||||
{
|
||||
let client = self.client();
|
||||
let write_relays = self.gossip.read(cx).write_relays(&author);
|
||||
let write_relays = self.write_relays(&author, cx);
|
||||
|
||||
// Construct filters based on event kinds
|
||||
let filters: Vec<Filter> = kinds
|
||||
|
||||
Reference in New Issue
Block a user