wip: handle events
This commit is contained in:
@@ -4,7 +4,7 @@ use anyhow::Error;
|
||||
use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task};
|
||||
use nostr_sdk::prelude::*;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use state::NostrRegistry;
|
||||
use state::client;
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
Account::set_global(cx.new(Account::new), cx);
|
||||
@@ -48,9 +48,6 @@ impl Account {
|
||||
|
||||
/// Create a new account instance
|
||||
fn new(cx: &mut Context<Self>) -> Self {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
// Collect command line arguments
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let account = args.get(1).and_then(|s| Keys::parse(s).ok());
|
||||
@@ -67,7 +64,9 @@ impl Account {
|
||||
// Set the signer
|
||||
cx.background_executor()
|
||||
.await_on_background(async move {
|
||||
let client = client();
|
||||
client.set_signer(keys).await;
|
||||
|
||||
log::info!("Signer is set");
|
||||
})
|
||||
.await;
|
||||
@@ -109,10 +108,8 @@ impl Account {
|
||||
}
|
||||
|
||||
fn init(&mut self, public_key: PublicKey, cx: &mut Context<Self>) {
|
||||
let nostr = NostrRegistry::global(cx);
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
|
||||
let client = client();
|
||||
let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE);
|
||||
|
||||
// Construct a filter to get the user's metadata
|
||||
|
||||
@@ -34,3 +34,4 @@ futures.workspace = true
|
||||
oneshot.workspace = true
|
||||
|
||||
tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
|
||||
flume = { version = "0.12", default-features = false, features = ["async", "select"] }
|
||||
|
||||
@@ -27,8 +27,3 @@ pub fn load_embedded_fonts(cx: &App) {
|
||||
.add_fonts(embedded_fonts.into_inner().unwrap())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn quit(_: &Quit, cx: &mut App) {
|
||||
log::info!("Gracefully quitting the application . . .");
|
||||
cx.quit();
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use assets::Assets;
|
||||
use common::{APP_ID, CLIENT_NAME};
|
||||
use common::{APP_ID, BOOTSTRAP_RELAYS, CLIENT_NAME, SEARCH_RELAYS};
|
||||
use gpui::{
|
||||
point, px, size, AppContext, Application, Bounds, KeyBinding, Menu, MenuItem, SharedString,
|
||||
TitlebarOptions, WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind,
|
||||
WindowOptions,
|
||||
point, px, size, AppContext, Application, Bounds, SharedString, TitlebarOptions,
|
||||
WindowBackgroundAppearance, WindowBounds, WindowDecorations, WindowKind, WindowOptions,
|
||||
};
|
||||
use gpui_component::Root;
|
||||
use state::client;
|
||||
|
||||
use crate::actions::{load_embedded_fonts, quit, Quit};
|
||||
use crate::actions::load_embedded_fonts;
|
||||
use crate::workspace::Workspace;
|
||||
|
||||
mod actions;
|
||||
@@ -29,28 +29,32 @@ fn main() {
|
||||
.with_assets(Assets)
|
||||
.with_http_client(Arc::new(reqwest_client::ReqwestClient::new()));
|
||||
|
||||
// Initialize the nostr client
|
||||
let client = client();
|
||||
|
||||
// Establish connection to all bootstrap relays
|
||||
app.background_executor()
|
||||
.spawn_with_priority(gpui::Priority::High, async move {
|
||||
// Add bootstrap relays to the relay pool
|
||||
for url in BOOTSTRAP_RELAYS {
|
||||
client.add_relay(url).await.ok();
|
||||
}
|
||||
|
||||
// Add search relays to the relay pool
|
||||
for url in SEARCH_RELAYS {
|
||||
client.add_relay(url).await.ok();
|
||||
}
|
||||
|
||||
// Connect to all added relays
|
||||
client.connect().await;
|
||||
})
|
||||
.detach();
|
||||
|
||||
// Run application
|
||||
app.run(move |cx| {
|
||||
// Load embedded fonts in assets/fonts
|
||||
load_embedded_fonts(cx);
|
||||
|
||||
// Register the `quit` function
|
||||
cx.on_action(quit);
|
||||
|
||||
// Register the `quit` function with CMD+Q (macOS)
|
||||
#[cfg(target_os = "macos")]
|
||||
cx.bind_keys([KeyBinding::new("cmd-q", Quit, None)]);
|
||||
|
||||
// Register the `quit` function with Super+Q (others)
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
cx.bind_keys([KeyBinding::new("super-q", Quit, None)]);
|
||||
|
||||
// Set menu items
|
||||
cx.set_menus(vec![Menu {
|
||||
name: "Lume".into(),
|
||||
items: vec![MenuItem::action("Quit", Quit)],
|
||||
}]);
|
||||
|
||||
// Set up the window bounds
|
||||
let bounds = Bounds::centered(None, size(px(920.0), px(700.0)), cx);
|
||||
|
||||
@@ -83,9 +87,6 @@ fn main() {
|
||||
// Initialize themes
|
||||
themes::init(cx);
|
||||
|
||||
// Initialize app state
|
||||
state::init(cx);
|
||||
|
||||
// Initialize account
|
||||
account::init(cx);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use gpui::{
|
||||
div, App, AppContext, Context, Entity, EventEmitter, FocusHandle, Focusable, IntoElement,
|
||||
ParentElement, Render, Window,
|
||||
ParentElement, Render, SharedString, Window,
|
||||
};
|
||||
use gpui_component::dock::{Panel, PanelEvent};
|
||||
|
||||
@@ -24,6 +24,10 @@ impl Panel for Startup {
|
||||
fn panel_name(&self) -> &'static str {
|
||||
"Startup"
|
||||
}
|
||||
|
||||
fn title(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
SharedString::from("Welcome")
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<PanelEvent> for Startup {}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use account::Account;
|
||||
use anyhow::Error;
|
||||
use common::{CLIENT_NAME, DEFAULT_SIDEBAR_WIDTH};
|
||||
use gpui::{
|
||||
div, px, AppContext, Axis, Context, Entity, InteractiveElement, IntoElement, ParentElement,
|
||||
Render, Styled, Subscription, Window,
|
||||
Render, Styled, Subscription, Task, Window,
|
||||
};
|
||||
use gpui_component::dock::{DockArea, DockItem};
|
||||
use gpui_component::{v_flex, Root, Theme};
|
||||
use nostr_sdk::prelude::*;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use state::{client, StateEvent};
|
||||
|
||||
use crate::panels::startup;
|
||||
use crate::sidebar;
|
||||
@@ -24,6 +27,9 @@ pub struct Workspace {
|
||||
|
||||
/// Event subscriptions
|
||||
_subscriptions: SmallVec<[Subscription; 1]>,
|
||||
|
||||
/// Background tasks
|
||||
_tasks: SmallVec<[Task<Result<(), Error>>; 2]>,
|
||||
}
|
||||
|
||||
impl Workspace {
|
||||
@@ -34,15 +40,13 @@ impl Workspace {
|
||||
|
||||
let mut subscriptions = smallvec![];
|
||||
|
||||
subscriptions.push(
|
||||
// Automatically sync theme with system appearance
|
||||
window.observe_window_appearance(|window, cx| {
|
||||
Theme::sync_system_appearance(Some(window), cx);
|
||||
}),
|
||||
);
|
||||
// Automatically sync theme with system appearance
|
||||
subscriptions.push(window.observe_window_appearance(|window, cx| {
|
||||
Theme::sync_system_appearance(Some(window), cx);
|
||||
}));
|
||||
|
||||
// Observe account entity changes
|
||||
subscriptions.push(
|
||||
// Observe account entity changes
|
||||
cx.observe_in(&account, window, move |this, state, window, cx| {
|
||||
if state.read(cx).has_account() {
|
||||
this.init_app_layout(window, cx);
|
||||
@@ -50,10 +54,51 @@ impl Workspace {
|
||||
}),
|
||||
);
|
||||
|
||||
let mut tasks = smallvec![];
|
||||
let (tx, rx) = flume::bounded::<StateEvent>(2048);
|
||||
|
||||
// Handle nostr notifications
|
||||
tasks.push(cx.background_spawn(async move {
|
||||
let client = client();
|
||||
let mut notifications = client.notifications();
|
||||
|
||||
while let Ok(notification) = notifications.recv().await {
|
||||
let RelayPoolNotification::Message { message, relay_url } = notification else {
|
||||
continue;
|
||||
};
|
||||
|
||||
match message {
|
||||
RelayMessage::Event { event, .. } => {
|
||||
// TODO
|
||||
}
|
||||
RelayMessage::EndOfStoredEvents(subscription_id) => {
|
||||
// TODO
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
// Handle state events
|
||||
tasks.push(cx.spawn_in(window, async move |this, cx| {
|
||||
while let Ok(event) = rx.recv_async().await {
|
||||
cx.update(|window, cx| {
|
||||
// TODO
|
||||
})
|
||||
// Entity has been released, ignore any errors
|
||||
.ok();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}));
|
||||
|
||||
Self {
|
||||
dock,
|
||||
title_bar,
|
||||
_subscriptions: subscriptions,
|
||||
_tasks: tasks,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
4
crates/state/src/event.rs
Normal file
4
crates/state/src/event.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum StateEvent {
|
||||
//
|
||||
}
|
||||
@@ -1,43 +1,18 @@
|
||||
use std::sync::OnceLock;
|
||||
use std::time::Duration;
|
||||
|
||||
use common::{config_dir, BOOTSTRAP_RELAYS, SEARCH_RELAYS};
|
||||
use gpui::{App, AppContext, Context, Entity, Global, Task};
|
||||
use common::config_dir;
|
||||
pub use event::*;
|
||||
use nostr_gossip_memory::prelude::*;
|
||||
use nostr_lmdb::NostrLmdb;
|
||||
use nostr_sdk::prelude::*;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
NostrRegistry::set_global(cx.new(NostrRegistry::new), cx);
|
||||
}
|
||||
mod event;
|
||||
|
||||
struct GlobalNostrRegistry(Entity<NostrRegistry>);
|
||||
static NOSTR_CLIENT: OnceLock<Client> = OnceLock::new();
|
||||
|
||||
impl Global for GlobalNostrRegistry {}
|
||||
|
||||
/// Nostr Registry
|
||||
#[derive(Debug)]
|
||||
pub struct NostrRegistry {
|
||||
/// Nostr Client
|
||||
client: Client,
|
||||
|
||||
/// Tasks for asynchronous operations
|
||||
_tasks: SmallVec<[Task<()>; 1]>,
|
||||
}
|
||||
|
||||
impl NostrRegistry {
|
||||
/// Retrieve the global nostr state
|
||||
pub fn global(cx: &App) -> Entity<Self> {
|
||||
cx.global::<GlobalNostrRegistry>().0.clone()
|
||||
}
|
||||
|
||||
/// Set the global nostr instance
|
||||
fn set_global(state: Entity<Self>, cx: &mut App) {
|
||||
cx.set_global(GlobalNostrRegistry(state));
|
||||
}
|
||||
|
||||
/// Create a new nostr instance
|
||||
fn new(cx: &mut Context<Self>) -> Self {
|
||||
pub fn client() -> &'static Client {
|
||||
NOSTR_CLIENT.get_or_init(|| {
|
||||
// rustls uses the `aws_lc_rs` provider by default
|
||||
// This only errors if the default provider has already
|
||||
// been installed. We can ignore this `Result`.
|
||||
@@ -54,7 +29,7 @@ impl NostrRegistry {
|
||||
});
|
||||
|
||||
// Construct the lmdb
|
||||
let lmdb = cx.background_executor().block(async move {
|
||||
let lmdb = smol::block_on(async move {
|
||||
let path = config_dir().join("nostr");
|
||||
NostrLmdb::open(path)
|
||||
.await
|
||||
@@ -62,55 +37,10 @@ impl NostrRegistry {
|
||||
});
|
||||
|
||||
// Construct the nostr client
|
||||
let client = ClientBuilder::default()
|
||||
ClientBuilder::default()
|
||||
.database(lmdb)
|
||||
.gossip(NostrGossipMemory::unbounded())
|
||||
.opts(opts)
|
||||
.build();
|
||||
|
||||
let mut tasks = smallvec![];
|
||||
|
||||
tasks.push(
|
||||
// Establish connection to the bootstrap relays
|
||||
//
|
||||
// And handle notifications from the nostr relay pool channel
|
||||
cx.background_spawn({
|
||||
let client = client.clone();
|
||||
|
||||
async move {
|
||||
// Connect to the bootstrap relays
|
||||
Self::connect(&client).await;
|
||||
|
||||
// Handle notifications from the relay pool
|
||||
// Self::handle_notifications(&client, &gossip, &tracker).await;
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
Self {
|
||||
client,
|
||||
_tasks: tasks,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the nostr client instance
|
||||
pub fn client(&self) -> Client {
|
||||
self.client.clone()
|
||||
}
|
||||
|
||||
/// Establish connection to the bootstrap relays
|
||||
async fn connect(client: &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();
|
||||
}
|
||||
|
||||
// Connect to all added relays
|
||||
client.connect().await;
|
||||
}
|
||||
.build()
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user