feat: add support for launch arguments (#40)
Reviewed-on: #40
This commit was merged in pull request #40.
This commit is contained in:
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -1369,6 +1369,7 @@ dependencies = [
|
|||||||
"gpui_platform",
|
"gpui_platform",
|
||||||
"gpui_windows",
|
"gpui_windows",
|
||||||
"log",
|
"log",
|
||||||
|
"nostr-sdk",
|
||||||
"person",
|
"person",
|
||||||
"reqwest_client",
|
"reqwest_client",
|
||||||
"settings",
|
"settings",
|
||||||
@@ -3988,15 +3989,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "matchers"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
|
|
||||||
dependencies = [
|
|
||||||
"regex-automata",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "maybe-rayon"
|
name = "maybe-rayon"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
@@ -7477,14 +7469,10 @@ version = "0.3.23"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
|
checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"matchers",
|
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"once_cell",
|
|
||||||
"regex-automata",
|
|
||||||
"sharded-slab",
|
"sharded-slab",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thread_local",
|
"thread_local",
|
||||||
"tracing",
|
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-log",
|
"tracing-log",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ schemars = "1"
|
|||||||
smallvec = "1.14.0"
|
smallvec = "1.14.0"
|
||||||
smol = "2"
|
smol = "2"
|
||||||
webbrowser = "1.0.4"
|
webbrowser = "1.0.4"
|
||||||
tracing-subscriber = { version = "0.3.18", features = ["fmt", "env-filter"] }
|
tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
|
||||||
errno = { version = "0.3.14", default-features = false }
|
errno = { version = "0.3.14", default-features = false }
|
||||||
instant = "0.1"
|
instant = "0.1"
|
||||||
|
|
||||||
|
|||||||
@@ -627,13 +627,7 @@ impl ChatRegistry {
|
|||||||
|
|
||||||
/// Load all rooms from the database.
|
/// Load all rooms from the database.
|
||||||
pub fn get_rooms(&mut self, cx: &mut Context<Self>) {
|
pub fn get_rooms(&mut self, cx: &mut Context<Self>) {
|
||||||
let nostr = NostrRegistry::global(cx);
|
let task = self.get_rooms_task(cx);
|
||||||
|
|
||||||
let Some(public_key) = nostr.read(cx).current_user() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
let task = self.get_rooms_from_database(public_key, cx);
|
|
||||||
|
|
||||||
self.tasks.push(cx.spawn(async move |this, cx| {
|
self.tasks.push(cx.spawn(async move |this, cx| {
|
||||||
match task.await {
|
match task.await {
|
||||||
@@ -655,61 +649,53 @@ impl ChatRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a task to load rooms from the database
|
/// Create a task to load rooms from the database
|
||||||
fn get_rooms_from_database(
|
fn get_rooms_task(&self, cx: &App) -> Task<Result<HashSet<Room>, Error>> {
|
||||||
&self,
|
|
||||||
public_key: PublicKey,
|
|
||||||
cx: &App,
|
|
||||||
) -> Task<Result<HashSet<Room>, Error>> {
|
|
||||||
let nostr = NostrRegistry::global(cx);
|
let nostr = NostrRegistry::global(cx);
|
||||||
let client = nostr.read(cx).client();
|
let client = nostr.read(cx).client();
|
||||||
|
let signer = nostr.read(cx).signer();
|
||||||
|
|
||||||
cx.background_spawn(async move {
|
cx.background_spawn(async move {
|
||||||
|
let public_key = signer.get_public_key_async().await?;
|
||||||
let contacts = client
|
let contacts = client
|
||||||
.database()
|
.database()
|
||||||
.contacts_public_keys(public_key)
|
.contacts_public_keys(public_key)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
// Query all cached rumor events (works with both old and new cache formats)
|
|
||||||
let filter = Filter::new()
|
let filter = Filter::new()
|
||||||
.kind(Kind::ApplicationSpecificData)
|
.kind(Kind::ApplicationSpecificData)
|
||||||
.custom_tag(SingleLetterTag::lowercase(Alphabet::K), "14");
|
.custom_tag(SingleLetterTag::lowercase(Alphabet::K), "14");
|
||||||
let events = client.database().query(filter).await?;
|
|
||||||
|
|
||||||
let mut rooms: HashSet<Room> = HashSet::new();
|
let events = client.database().query(filter).await?;
|
||||||
let mut grouped: HashMap<u64, Vec<UnsignedEvent>> = HashMap::new();
|
let mut grouped: HashMap<u64, Vec<UnsignedEvent>> = HashMap::new();
|
||||||
|
|
||||||
for raw in events.into_iter() {
|
for raw in events.into_iter() {
|
||||||
if let Ok(rumor) = UnsignedEvent::from_json(&raw.content)
|
if let Ok(rumor) = UnsignedEvent::from_json(&raw.content)
|
||||||
&& rumor.tags.public_keys().peekable().peek().is_some()
|
&& rumor.tags.public_keys().next().is_some()
|
||||||
{
|
{
|
||||||
|
if rumor.pubkey != public_key
|
||||||
|
&& !rumor.tags.public_keys().any(|k| k == public_key)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
grouped.entry(rumor.uniq_id()).or_default().push(rumor);
|
grouped.entry(rumor.uniq_id()).or_default().push(rumor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (_id, mut messages) in grouped.into_iter() {
|
let mut rooms = HashSet::with_capacity(grouped.len());
|
||||||
messages.sort_by_key(|m| Reverse(m.created_at));
|
|
||||||
|
|
||||||
// Always use the latest message
|
for (_id, messages) in grouped.into_iter() {
|
||||||
let Some(latest) = messages.first() else {
|
let latest = messages.iter().max_by_key(|m| m.created_at).unwrap();
|
||||||
continue;
|
let room = Room::from(latest).organize(&public_key);
|
||||||
};
|
|
||||||
|
|
||||||
// Construct the room from the latest message.
|
|
||||||
//
|
|
||||||
// Call `.organize` to ensure the current user is at the end of the list.
|
|
||||||
let mut room = Room::from(latest).organize(&public_key);
|
|
||||||
|
|
||||||
// Check if the user has responded to the room
|
|
||||||
let user_sent = messages.iter().any(|m| m.pubkey == public_key);
|
let user_sent = messages.iter().any(|m| m.pubkey == public_key);
|
||||||
|
|
||||||
// Check if public keys are from the user's contacts
|
|
||||||
let is_contact = room.members.iter().any(|k| contacts.contains(k));
|
let is_contact = room.members.iter().any(|k| contacts.contains(k));
|
||||||
|
|
||||||
// Set the room's kind based on status
|
let room = if user_sent || is_contact {
|
||||||
if user_sent || is_contact {
|
room.kind(RoomKind::Ongoing)
|
||||||
room = room.kind(RoomKind::Ongoing);
|
} else {
|
||||||
}
|
room
|
||||||
|
};
|
||||||
|
|
||||||
rooms.insert(room);
|
rooms.insert(room);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ pub use nip4e::*;
|
|||||||
pub use nip05::*;
|
pub use nip05::*;
|
||||||
pub use signer::{CoopAuthUrlHandler, UniversalSigner};
|
pub use signer::{CoopAuthUrlHandler, UniversalSigner};
|
||||||
|
|
||||||
pub fn init(window: &mut Window, cx: &mut App) {
|
pub fn init(window: &mut Window, cx: &mut App, cli_key: Option<SecretKey>) {
|
||||||
// rustls uses the `aws_lc_rs` provider by default
|
// rustls uses the `aws_lc_rs` provider by default
|
||||||
// This only errors if the default provider has already
|
// This only errors if the default provider has already
|
||||||
// been installed. We can ignore this `Result`.
|
// been installed. We can ignore this `Result`.
|
||||||
@@ -37,7 +37,7 @@ pub fn init(window: &mut Window, cx: &mut App) {
|
|||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
gpui_tokio::init(cx);
|
gpui_tokio::init(cx);
|
||||||
|
|
||||||
NostrRegistry::set_global(cx.new(|cx| NostrRegistry::new(window, cx)), cx);
|
NostrRegistry::set_global(cx.new(|cx| NostrRegistry::new(window, cx, cli_key)), cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GlobalNostrRegistry(Entity<NostrRegistry>);
|
struct GlobalNostrRegistry(Entity<NostrRegistry>);
|
||||||
@@ -58,16 +58,16 @@ pub enum StateEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl StateEvent {
|
impl StateEvent {
|
||||||
|
pub fn signer_changed(&self) -> bool {
|
||||||
|
matches!(self, StateEvent::SignerChanged)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn error<T>(error: T) -> Self
|
pub fn error<T>(error: T) -> Self
|
||||||
where
|
where
|
||||||
T: Into<String>,
|
T: Into<String>,
|
||||||
{
|
{
|
||||||
Self::Error(error.into())
|
Self::Error(error.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn signer_changed(&self) -> bool {
|
|
||||||
matches!(self, StateEvent::SignerChanged)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Nostr Registry
|
/// Nostr Registry
|
||||||
@@ -100,7 +100,7 @@ impl NostrRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new nostr instance
|
/// Create a new nostr instance
|
||||||
fn new(window: &mut Window, cx: &mut Context<Self>) -> Self {
|
fn new(window: &mut Window, cx: &mut Context<Self>, cli_key: Option<SecretKey>) -> Self {
|
||||||
let signer = UniversalSigner::new(Keys::generate());
|
let signer = UniversalSigner::new(Keys::generate());
|
||||||
let authenticator = SignerAuthenticator::new(signer.clone());
|
let authenticator = SignerAuthenticator::new(signer.clone());
|
||||||
|
|
||||||
@@ -133,6 +133,10 @@ impl NostrRegistry {
|
|||||||
|
|
||||||
if cfg!(target_arch = "wasm32") {
|
if cfg!(target_arch = "wasm32") {
|
||||||
cx.emit(StateEvent::NoSigner);
|
cx.emit(StateEvent::NoSigner);
|
||||||
|
} else if let Some(secret) = cli_key {
|
||||||
|
// Use CLI-provided key -- same path as get_user_credential
|
||||||
|
let keys = Keys::new(secret);
|
||||||
|
this.set_signer(keys, cx);
|
||||||
} else {
|
} else {
|
||||||
this.get_user_credential(cx);
|
this.get_user_credential(cx);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,3 +48,4 @@ reqwest_client.workspace = true
|
|||||||
|
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
tracing-subscriber.workspace = true
|
tracing-subscriber.workspace = true
|
||||||
|
nostr-sdk.workspace = true
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ use gpui::{
|
|||||||
actions, point, px, size,
|
actions, point, px, size,
|
||||||
};
|
};
|
||||||
use gpui_platform::application;
|
use gpui_platform::application;
|
||||||
|
use nostr_sdk::prelude::SecretKey;
|
||||||
use state::{APP_ID, CLIENT_NAME};
|
use state::{APP_ID, CLIENT_NAME};
|
||||||
use ui::Root;
|
use ui::Root;
|
||||||
|
|
||||||
@@ -16,6 +17,14 @@ fn main() {
|
|||||||
// Initialize logging
|
// Initialize logging
|
||||||
tracing_subscriber::fmt::init();
|
tracing_subscriber::fmt::init();
|
||||||
|
|
||||||
|
// Parse CLI arguments for --sec <nsec1>
|
||||||
|
let cli_key = parse_cli_key();
|
||||||
|
if let Err(ref e) = cli_key {
|
||||||
|
eprintln!("Failed to parse --sec argument: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
let cli_key = cli_key.unwrap();
|
||||||
|
|
||||||
// Run application
|
// Run application
|
||||||
application()
|
application()
|
||||||
.with_assets(Assets)
|
.with_assets(Assets)
|
||||||
@@ -75,7 +84,7 @@ fn main() {
|
|||||||
settings::init(window, cx);
|
settings::init(window, cx);
|
||||||
|
|
||||||
// Initialize the nostr client
|
// Initialize the nostr client
|
||||||
state::init(window, cx);
|
state::init(window, cx, cli_key);
|
||||||
|
|
||||||
// Initialize person registry
|
// Initialize person registry
|
||||||
person::init(window, cx);
|
person::init(window, cx);
|
||||||
@@ -125,6 +134,25 @@ fn load_embedded_fonts(cx: &App) {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_cli_key() -> Result<Option<SecretKey>, String> {
|
||||||
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
let mut i = 0;
|
||||||
|
while i < args.len() {
|
||||||
|
if args[i] == "--sec" {
|
||||||
|
if i + 1 < args.len() {
|
||||||
|
let nsec = &args[i + 1];
|
||||||
|
return SecretKey::parse(nsec)
|
||||||
|
.map(Some)
|
||||||
|
.map_err(|e| format!("Invalid nsec key '{nsec}': {e}"));
|
||||||
|
} else {
|
||||||
|
return Err("--sec requires a value (nsec1...)".to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
fn quit(_ev: &Quit, cx: &mut App) {
|
fn quit(_ev: &Quit, cx: &mut App) {
|
||||||
log::info!("Gracefully quitting the application . . .");
|
log::info!("Gracefully quitting the application . . .");
|
||||||
cx.quit();
|
cx.quit();
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ pub fn run() -> Result<(), JsValue> {
|
|||||||
settings::init(window, cx);
|
settings::init(window, cx);
|
||||||
|
|
||||||
// Initialize the nostr client
|
// Initialize the nostr client
|
||||||
state::init(window, cx);
|
state::init(window, cx, None);
|
||||||
|
|
||||||
// Initialize person registry
|
// Initialize person registry
|
||||||
person::init(window, cx);
|
person::init(window, cx);
|
||||||
|
|||||||
Reference in New Issue
Block a user