wip: refactor
This commit is contained in:
@@ -10,6 +10,7 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
ui = { path = "../ui" }
|
||||
common = { path = "../common" }
|
||||
|
||||
gpui.workspace = true
|
||||
reqwest_client.workspace = true
|
||||
@@ -26,4 +27,3 @@ rust-embed.workspace = true
|
||||
smol.workspace = true
|
||||
|
||||
tracing-subscriber = { version = "0.3.18", features = ["fmt"] }
|
||||
random_name_generator = "0.3.6"
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
pub const KEYRING_SERVICE: &str = "Coop Safe Storage";
|
||||
pub const APP_NAME: &str = "Coop";
|
||||
pub const FAKE_SIG: &str = "f9e79d141c004977192d05a86f81ec7c585179c371f7350a5412d33575a2a356433f58e405c2296ed273e2fe0aafa25b641e39cc4e1f3f261ebf55bce0cbac83";
|
||||
pub const NEW_MESSAGE_SUB_ID: &str = "listen_new_giftwrap";
|
||||
pub const ALL_MESSAGES_SUB_ID: &str = "listen_all_giftwraps";
|
||||
pub const METADATA_DELAY: u64 = 200;
|
||||
pub const IMAGE_SERVICE: &str = "https://wsrv.nl";
|
||||
pub const NIP96_SERVER: &str = "https://nostrcheck.me";
|
||||
@@ -1,12 +1,15 @@
|
||||
use asset::Assets;
|
||||
use constants::{
|
||||
ALL_MESSAGES_SUB_ID, APP_NAME, FAKE_SIG, KEYRING_SERVICE, METADATA_DELAY, NEW_MESSAGE_SUB_ID,
|
||||
use common::constants::{
|
||||
ALL_MESSAGES_SUB_ID, APP_ID, APP_NAME, FAKE_SIG, KEYRING_SERVICE, METADATA_DELAY,
|
||||
NEW_MESSAGE_SUB_ID,
|
||||
};
|
||||
use dirs::config_dir;
|
||||
use gpui::{
|
||||
actions, point, px, size, App, AppContext, Bounds, SharedString, TitlebarOptions,
|
||||
VisualContext, WindowBounds, WindowDecorations, WindowKind, WindowOptions,
|
||||
VisualContext, WindowBounds, WindowKind, WindowOptions,
|
||||
};
|
||||
#[cfg(target_os = "linux")]
|
||||
use gpui::{WindowBackgroundAppearance, WindowDecorations};
|
||||
use nostr_sdk::prelude::*;
|
||||
use states::{app::AppRegistry, chat::ChatRegistry};
|
||||
use std::{
|
||||
@@ -24,9 +27,7 @@ use ui::Root;
|
||||
use views::app::AppView;
|
||||
|
||||
mod asset;
|
||||
mod constants;
|
||||
mod states;
|
||||
mod utils;
|
||||
mod views;
|
||||
|
||||
actions!(main_menu, [Quit]);
|
||||
@@ -38,8 +39,6 @@ static CLIENT: OnceLock<Client> = OnceLock::new();
|
||||
pub enum Signal {
|
||||
/// Receive event
|
||||
Event(Event),
|
||||
/// Receive metadata
|
||||
Metadata(PublicKey),
|
||||
/// Receive EOSE
|
||||
Eose,
|
||||
}
|
||||
@@ -157,11 +156,6 @@ async fn main() {
|
||||
};
|
||||
}
|
||||
}
|
||||
Kind::Metadata => {
|
||||
if let Err(e) = signal_tx.send(Signal::Metadata(event.pubkey)).await {
|
||||
println!("Send error: {}", e)
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else if let RelayMessage::EndOfStoredEvents(subscription_id) = message {
|
||||
@@ -226,40 +220,6 @@ async fn main() {
|
||||
// Set quit action
|
||||
cx.on_action(quit);
|
||||
|
||||
cx.spawn(|async_cx| {
|
||||
let task = cx.read_credentials(KEYRING_SERVICE);
|
||||
|
||||
async move {
|
||||
if let Ok(res) = task.await {
|
||||
if let Some((npub, secret)) = res {
|
||||
let public_key = PublicKey::from_bech32(&npub).unwrap();
|
||||
let hex = String::from_utf8(secret).unwrap();
|
||||
let keys = Keys::parse(&hex).unwrap();
|
||||
|
||||
// Update signer
|
||||
async_cx
|
||||
.background_executor()
|
||||
.spawn(async move { client.set_signer(keys).await })
|
||||
.detach();
|
||||
|
||||
// Update global state
|
||||
_ = async_cx.update_global::<AppRegistry, _>(|state, cx| {
|
||||
state.set_user(public_key, cx);
|
||||
});
|
||||
} else {
|
||||
_ = async_cx.update_global::<AppRegistry, _>(|state, _| {
|
||||
state.set_loading();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
_ = async_cx.update_global::<AppRegistry, _>(|state, _| {
|
||||
state.set_loading();
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
cx.spawn(|async_cx| async move {
|
||||
let (tx, rx) = smol::channel::unbounded::<Signal>();
|
||||
|
||||
@@ -281,11 +241,6 @@ async fn main() {
|
||||
chat.init(cx);
|
||||
});
|
||||
}
|
||||
Signal::Metadata(public_key) => {
|
||||
_ = async_cx.update_global::<AppRegistry, _>(|state, cx| {
|
||||
state.set_refresh(public_key, cx);
|
||||
});
|
||||
}
|
||||
Signal::Event(event) => {
|
||||
_ = async_cx.update_global::<ChatRegistry, _>(|state, cx| {
|
||||
state.receive(event, cx)
|
||||
@@ -296,8 +251,33 @@ async fn main() {
|
||||
})
|
||||
.detach();
|
||||
|
||||
// Set window size
|
||||
let bounds = Bounds::centered(None, size(px(900.0), px(680.0)), cx);
|
||||
cx.spawn(|async_cx| {
|
||||
let task = cx.read_credentials(KEYRING_SERVICE);
|
||||
|
||||
async move {
|
||||
if let Ok(Some((npub, secret))) = task.await {
|
||||
let public_key = PublicKey::from_bech32(&npub).expect("Something wrong.");
|
||||
let hex = String::from_utf8(secret).expect("Something wrong.");
|
||||
let keys = Keys::parse(&hex).expect("Something wrong.");
|
||||
|
||||
// Update signer
|
||||
async_cx
|
||||
.background_executor()
|
||||
.spawn(async move { client.set_signer(keys).await })
|
||||
.detach();
|
||||
|
||||
// Update global state
|
||||
_ = async_cx.update_global::<AppRegistry, _>(|state, cx| {
|
||||
state.set_user(public_key, cx);
|
||||
});
|
||||
} else {
|
||||
_ = async_cx.update_global::<AppRegistry, _>(|state, _| {
|
||||
state.is_loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
|
||||
let opts = WindowOptions {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
@@ -306,8 +286,11 @@ async fn main() {
|
||||
traffic_light_position: Some(point(px(9.0), px(9.0))),
|
||||
appears_transparent: true,
|
||||
}),
|
||||
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||
window_decorations: Some(WindowDecorations::Client),
|
||||
window_bounds: Some(WindowBounds::Windowed(Bounds::centered(
|
||||
None,
|
||||
size(px(900.0), px(680.0)),
|
||||
cx,
|
||||
))),
|
||||
#[cfg(target_os = "linux")]
|
||||
window_background: WindowBackgroundAppearance::Transparent,
|
||||
#[cfg(target_os = "linux")]
|
||||
@@ -317,11 +300,11 @@ async fn main() {
|
||||
};
|
||||
|
||||
cx.open_window(opts, |cx| {
|
||||
let app_view = cx.new_view(AppView::new);
|
||||
|
||||
cx.set_window_title("Coop");
|
||||
cx.set_window_title(APP_NAME);
|
||||
cx.set_app_id(APP_ID);
|
||||
cx.activate(true);
|
||||
cx.new_view(|cx| Root::new(app_view.into(), cx))
|
||||
|
||||
cx.new_view(|cx| Root::new(cx.new_view(AppView::new).into(), cx))
|
||||
})
|
||||
.expect("System error");
|
||||
});
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use crate::{
|
||||
constants::{ALL_MESSAGES_SUB_ID, NEW_MESSAGE_SUB_ID},
|
||||
get_client,
|
||||
};
|
||||
use crate::get_client;
|
||||
use common::constants::{ALL_MESSAGES_SUB_ID, NEW_MESSAGE_SUB_ID};
|
||||
use gpui::*;
|
||||
use nostr_sdk::prelude::*;
|
||||
use std::time::Duration;
|
||||
@@ -81,10 +79,6 @@ impl AppRegistry {
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub fn set_loading(&mut self) {
|
||||
self.is_loading = false
|
||||
}
|
||||
|
||||
pub fn set_user(&mut self, public_key: PublicKey, cx: &mut AppContext) {
|
||||
self.user.update(cx, |model, cx| {
|
||||
*model = Some(public_key);
|
||||
@@ -94,13 +88,6 @@ impl AppRegistry {
|
||||
self.is_loading = false;
|
||||
}
|
||||
|
||||
pub fn set_refresh(&mut self, public_key: PublicKey, cx: &mut AppContext) {
|
||||
self.refreshs.update(cx, |this, cx| {
|
||||
this.push(public_key);
|
||||
cx.notify();
|
||||
})
|
||||
}
|
||||
|
||||
pub fn current_user(&self) -> WeakModel<Option<PublicKey>> {
|
||||
self.user.downgrade()
|
||||
}
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use crate::{
|
||||
get_client,
|
||||
utils::{compare, room_hash},
|
||||
};
|
||||
use common::utils::{compare, room_hash};
|
||||
use gpui::{AppContext, Context, Global, Model, WeakModel};
|
||||
use itertools::Itertools;
|
||||
use nostr_sdk::prelude::*;
|
||||
use room::Room;
|
||||
use std::cmp::Reverse;
|
||||
|
||||
use crate::get_client;
|
||||
|
||||
pub mod room;
|
||||
|
||||
pub struct Inbox {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{
|
||||
use common::{
|
||||
constants::IMAGE_SERVICE,
|
||||
utils::{compare, random_name, room_hash, shorted_public_key},
|
||||
};
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
use crate::{constants::NIP96_SERVER, get_client};
|
||||
use chrono::{Datelike, Local, TimeZone};
|
||||
use nostr_sdk::prelude::*;
|
||||
use rnglib::{Language, RNG};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
hash::{DefaultHasher, Hash, Hasher},
|
||||
};
|
||||
|
||||
pub async fn nip96_upload(file: Vec<u8>) -> anyhow::Result<Url, anyhow::Error> {
|
||||
let client = get_client();
|
||||
let signer = client.signer().await?;
|
||||
let server_url = Url::parse(NIP96_SERVER)?;
|
||||
|
||||
let config: ServerConfig = nip96::get_server_config(server_url, None).await?;
|
||||
let url = nip96::upload_data(&signer, &config, file, None, None).await?;
|
||||
|
||||
Ok(url)
|
||||
}
|
||||
|
||||
pub fn room_hash(tags: &Tags) -> u64 {
|
||||
let pubkeys: Vec<PublicKey> = tags.public_keys().copied().collect();
|
||||
let mut hasher = DefaultHasher::new();
|
||||
// Generate unique hash
|
||||
pubkeys.hash(&mut hasher);
|
||||
|
||||
hasher.finish()
|
||||
}
|
||||
|
||||
pub fn random_name(length: usize) -> String {
|
||||
let rng = RNG::from(&Language::Roman);
|
||||
rng.generate_names(length, true).join("-").to_lowercase()
|
||||
}
|
||||
|
||||
pub fn compare<T>(a: &[T], b: &[T]) -> bool
|
||||
where
|
||||
T: Eq + Hash,
|
||||
{
|
||||
let a: HashSet<_> = a.iter().collect();
|
||||
let b: HashSet<_> = b.iter().collect();
|
||||
|
||||
a == b
|
||||
}
|
||||
|
||||
pub fn shorted_public_key(public_key: PublicKey) -> String {
|
||||
let pk = public_key.to_string();
|
||||
format!("{}:{}", &pk[0..4], &pk[pk.len() - 4..])
|
||||
}
|
||||
|
||||
pub fn message_ago(time: Timestamp) -> String {
|
||||
let now = Local::now();
|
||||
let input_time = Local.timestamp_opt(time.as_u64() as i64, 0).unwrap();
|
||||
let diff = (now - input_time).num_hours();
|
||||
|
||||
if diff < 24 {
|
||||
let duration = now.signed_duration_since(input_time);
|
||||
|
||||
if duration.num_seconds() < 60 {
|
||||
"now".to_string()
|
||||
} else if duration.num_minutes() == 1 {
|
||||
"1m".to_string()
|
||||
} else if duration.num_minutes() < 60 {
|
||||
format!("{}m", duration.num_minutes())
|
||||
} else if duration.num_hours() == 1 {
|
||||
"1h".to_string()
|
||||
} else if duration.num_hours() < 24 {
|
||||
format!("{}h", duration.num_hours())
|
||||
} else if duration.num_days() == 1 {
|
||||
"1d".to_string()
|
||||
} else {
|
||||
format!("{}d", duration.num_days())
|
||||
}
|
||||
} else {
|
||||
input_time.format("%b %d").to_string()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn message_time(time: Timestamp) -> String {
|
||||
let now = Local::now();
|
||||
let input_time = Local.timestamp_opt(time.as_u64() as i64, 0).unwrap();
|
||||
|
||||
if input_time.day() == now.day() {
|
||||
format!("Today at {}", input_time.format("%H:%M %p"))
|
||||
} else if input_time.day() == now.day() - 1 {
|
||||
format!("Yesterday at {}", input_time.format("%H:%M %p"))
|
||||
} else {
|
||||
format!(
|
||||
"{}, {}",
|
||||
input_time.format("%d/%m/%y"),
|
||||
input_time.format("%H:%M %p")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::{constants::IMAGE_SERVICE, get_client, states::app::AppRegistry};
|
||||
use crate::{get_client, states::app::AppRegistry};
|
||||
use common::constants::IMAGE_SERVICE;
|
||||
use gpui::prelude::FluentBuilder;
|
||||
use gpui::{
|
||||
actions, img, Context, IntoElement, Model, ObjectFit, ParentElement, Render, Styled,
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
use crate::{
|
||||
use async_utility::task::spawn;
|
||||
use common::{
|
||||
constants::IMAGE_SERVICE,
|
||||
get_client,
|
||||
states::chat::room::Room,
|
||||
utils::{compare, message_time, nip96_upload},
|
||||
};
|
||||
use async_utility::task::spawn;
|
||||
use gpui::{
|
||||
div, img, list, px, white, AnyElement, AppContext, Context, EventEmitter, Flatten, FocusHandle,
|
||||
FocusableView, InteractiveElement, IntoElement, ListAlignment, ListState, Model, ObjectFit,
|
||||
@@ -29,6 +27,8 @@ use ui::{
|
||||
v_flex, ContextModal, Icon, IconName, Sizable,
|
||||
};
|
||||
|
||||
use crate::{get_client, states::chat::room::Room};
|
||||
|
||||
mod message;
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -363,7 +363,9 @@ impl ChatPanel {
|
||||
let (tx, rx) = oneshot::channel::<Url>();
|
||||
|
||||
spawn(async move {
|
||||
if let Ok(url) = nip96_upload(file_data).await {
|
||||
let client = get_client();
|
||||
|
||||
if let Ok(url) = nip96_upload(client, file_data).await {
|
||||
_ = tx.send(url);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
use crate::{constants::KEYRING_SERVICE, get_client, states::app::AppRegistry};
|
||||
use common::constants::KEYRING_SERVICE;
|
||||
use gpui::{div, IntoElement, ParentElement, Render, Styled, View, ViewContext, VisualContext};
|
||||
use nostr_sdk::prelude::*;
|
||||
use ui::input::{InputEvent, TextInput};
|
||||
|
||||
use crate::{get_client, states::app::AppRegistry};
|
||||
|
||||
pub struct Onboarding {
|
||||
input: View<TextInput>,
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ use crate::{
|
||||
app::AppRegistry,
|
||||
chat::room::{Member, Room},
|
||||
},
|
||||
utils::{random_name, room_hash},
|
||||
};
|
||||
use common::utils::{random_name, room_hash};
|
||||
use gpui::{
|
||||
div, img, impl_internal_actions, px, uniform_list, Context, FocusHandle, InteractiveElement,
|
||||
IntoElement, Model, ParentElement, Render, SharedString, StatefulInteractiveElement, Styled,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
states::chat::ChatRegistry,
|
||||
utils::message_ago,
|
||||
views::app::{AddPanel, PanelKind},
|
||||
};
|
||||
use common::utils::message_ago;
|
||||
use gpui::{
|
||||
div, img, percentage, prelude::FluentBuilder, px, InteractiveElement, IntoElement,
|
||||
ParentElement, Render, SharedString, StatefulInteractiveElement, Styled, ViewContext,
|
||||
|
||||
Reference in New Issue
Block a user