From 54eb7d42618f771fa184531e78c219f2c3752ce1 Mon Sep 17 00:00:00 2001 From: shadow cat Date: Thu, 26 Feb 2026 19:18:33 -0500 Subject: [PATCH] work --- iris | 2 +- src/bin/client/main.rs | 9 +++-- src/bin/client/net.rs | 70 +++++++++++++++++++++++++++++++----- src/bin/client/session.rs | 13 ++++++- src/bin/client/ui/connect.rs | 8 +++-- src/bin/client/ui/mod.rs | 2 ++ src/bin/client/ui/user.rs | 53 +++++++++++++++++++++++++++ src/bin/server/handle.rs | 14 ++++++-- src/net/data.rs | 14 +++++++- src/net/msg.rs | 4 ++- src/net/request.rs | 2 +- 11 files changed, 169 insertions(+), 22 deletions(-) create mode 100644 src/bin/client/ui/user.rs diff --git a/iris b/iris index 1aadef0..1102dc7 160000 --- a/iris +++ b/iris @@ -1 +1 @@ -Subproject commit 1aadef0e7e755fd211eea30b611c8194a6e63442 +Subproject commit 1102dc7338f1b4ec5ad6362da33ab3c52fcdff98 diff --git a/src/bin/client/main.rs b/src/bin/client/main.rs index c403a88..d5af39f 100644 --- a/src/bin/client/main.rs +++ b/src/bin/client/main.rs @@ -29,8 +29,10 @@ pub struct Client { } pub type Rsc = DefaultRsc; +pub type ClientSender = EventSender; pub enum ClientEvent { + CacheUpdate, ServerMsg(ServerMsg), Err(String), } @@ -38,11 +40,7 @@ pub enum ClientEvent { impl DefaultAppState for Client { type Event = ClientEvent; - fn new( - mut ui_state: DefaultUiState, - rsc: &mut DefaultRsc, - _: Proxy, - ) -> Self { + fn new(mut ui_state: DefaultUiState, rsc: &mut DefaultRsc) -> Self { let notif = WidgetPtr::default().add(rsc); // let popup = WidgetPtr::default().add(rsc); let main_ui = WidgetPtr::default().add(rsc); @@ -82,6 +80,7 @@ impl DefaultAppState for Client { ClientEvent::Err(msg) => { rsc[self.notif].inner = Some(ui::werror(&msg, rsc)); } + ClientEvent::CacheUpdate => {} } } diff --git a/src/bin/client/net.rs b/src/bin/client/net.rs index cd77195..2790557 100644 --- a/src/bin/client/net.rs +++ b/src/bin/client/net.rs @@ -1,4 +1,4 @@ -use crate::ClientEvent; +use crate::ClientSender; use dashmap::DashMap; use openworm::net::{ ClientMsg, ClientMsgInst, RecvHandler, RequestId, RequestMsg, SERVER_NAME, ServerMsg, @@ -26,6 +26,7 @@ pub struct ConnectInfo { #[derive(Clone)] pub struct NetHandle { send: UnboundedSender, + event_sender: ClientSender, } type NetResult = Result; @@ -33,9 +34,12 @@ type NetResult = Result; pub enum NetCtrlMsg { Send(ClientMsg), Request(ClientMsg, oneshot::Sender), + RequestSync(ClientMsg, Box), Exit, } +type Resp = Result; + impl NetHandle { fn send_(&self, msg: NetCtrlMsg) { let _ = self.send.send(msg); @@ -45,7 +49,7 @@ impl NetHandle { self.send_(NetCtrlMsg::Send(msg.into())); } - pub async fn request(&self, msg: R) -> Result { + pub async fn request(&self, msg: R) -> Resp { let (send, recv) = oneshot::channel(); self.send_(NetCtrlMsg::Request(msg.into(), send)); let Ok(recv) = recv.await else { return Err(()) }; @@ -56,11 +60,42 @@ impl NetHandle { } } + pub fn request_sync(&self, msg: R) -> SyncRecv { + let (send, recv) = oneshot::channel(); + let sender = self.event_sender.clone(); + self.send_(NetCtrlMsg::RequestSync( + msg.into(), + Box::new(move |msg| { + let _ = send.send(if let Some(res) = R::result(msg) { + Ok(res) + } else { + Err(()) + }); + sender.run(); + }), + )); + SyncRecv:: { recv } + } + pub fn exit(self) { self.send_(NetCtrlMsg::Exit); } } +pub struct SyncRecv { + recv: oneshot::Receiver>, +} + +impl SyncRecv { + pub fn try_recv(&mut self) -> Option> { + match self.recv.try_recv() { + Ok(res) => Some(res), + Err(oneshot::error::TryRecvError::Empty) => None, + Err(oneshot::error::TryRecvError::Closed) => Some(Err(())), + } + } +} + async fn connection_cert( addr: SocketAddr, cert: CertificateDer<'_>, @@ -117,7 +152,11 @@ async fn connection_no_cert(addr: SocketAddr) -> NetResult<(Endpoint, Connection } impl NetHandle { - pub async fn connect(msg: impl MsgHandler, info: ConnectInfo) -> Result { + pub async fn connect( + msg: impl MsgHandler, + info: ConnectInfo, + event_sender: ClientSender, + ) -> Result { let (send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel::(); let cert = CertificateDer::from_slice(&info.cert); @@ -133,6 +172,7 @@ impl NetHandle { let mut req_id = RequestId::first(); let recv = Arc::new(ServerRecv { msg, + requests_sync: DashMap::default(), requests: DashMap::default(), }); tokio::spawn(recv_uni(conn_, recv.clone())); @@ -161,6 +201,17 @@ impl NetHandle { break; } } + NetCtrlMsg::RequestSync(msg, f) => { + let msg = ClientMsgInst { + id: request_id, + msg, + }; + recv.requests_sync.insert(request_id, f); + if send_uni(&conn, msg).await.is_err() { + println!("disconnected from server"); + break; + } + } NetCtrlMsg::Exit => { conn.close(0u32.into(), &[]); endpoint.wait_idle().await; @@ -170,7 +221,7 @@ impl NetHandle { } }); - Ok(NetHandle { send }) + Ok(NetHandle { send, event_sender }) } } @@ -188,15 +239,18 @@ where struct ServerRecv { requests: DashMap>, + requests_sync: DashMap>, msg: F, } impl RecvHandler for ServerRecv { async fn msg(&self, resp: ServerMsgInst) { - if let Some(id) = resp.id - && let Some((_, send)) = self.requests.remove(&id) - { - let _ = send.send(resp.msg); + if let Some(id) = resp.id { + if let Some((_, send)) = self.requests.remove(&id) { + let _ = send.send(resp.msg); + } else if let Some((_, f)) = self.requests_sync.remove(&id) { + f(resp.msg) + } } else { self.msg.run(resp.msg).await; } diff --git a/src/bin/client/session.rs b/src/bin/client/session.rs index 84512d3..902054c 100644 --- a/src/bin/client/session.rs +++ b/src/bin/client/session.rs @@ -1,8 +1,19 @@ use openworm::net::UserId; -use crate::net::NetHandle; +use crate::{net::NetHandle, ui::UserCache}; pub struct Session { pub con: NetHandle, pub user_id: UserId, + pub cache: UserCache, +} + +impl Session { + pub fn new(con: NetHandle, user_id: UserId) -> Self { + Self { + cache: UserCache::new(con.clone()), + con, + user_id, + } + } } diff --git a/src/bin/client/ui/connect.rs b/src/bin/client/ui/connect.rs index f6953fe..6ed679b 100644 --- a/src/bin/client/ui/connect.rs +++ b/src/bin/client/ui/connect.rs @@ -47,6 +47,7 @@ pub fn start(rsc: &mut Rsc, data: &ClientData) -> WeakWidget { let account = account.clone(); let cert = cert.clone(); let password = ctx.state.data.password(&account); + let event_sender = rsc.window_event.clone(); rsc.spawn_task(async move |mut ctx| { let mut fail = |reason: &str| { let reason = reason.to_string(); @@ -62,6 +63,7 @@ pub fn start(rsc: &mut Rsc, data: &ClientData) -> WeakWidget { url: account.url.clone(), cert, }, + event_sender, ) .await { @@ -89,7 +91,7 @@ pub fn start(rsc: &mut Rsc, data: &ClientData) -> WeakWidget { return fail("invalid password"); } }; - let session = Session { con, user_id }; + let session = Session::new(con, user_id); ctx.update(move |ctx, rsc| { main_view(rsc, session).set_ptr(ctx.main_ui, rsc); }); @@ -141,6 +143,7 @@ pub fn create_account(rsc: &mut Rsc) -> WeakWidget { let password = rsc[password].content(); create.disable(rsc); + let event_sender = rsc.window_event.clone(); rsc.spawn_task(async move |mut ctx| { let mut fail = |reason: &str| { let reason = reason.to_string(); @@ -158,6 +161,7 @@ pub fn create_account(rsc: &mut Rsc) -> WeakWidget { url: url.clone(), cert, }, + event_sender, ) .await { @@ -186,7 +190,7 @@ pub fn create_account(rsc: &mut Rsc) -> WeakWidget { return fail("invalid account token"); } }; - let session = Session { con, user_id }; + let session = Session::new(con, user_id); ctx.update(move |ctx, rsc| { main_view(rsc, session).set_ptr(ctx.main_ui, rsc); ctx.data.create_account( diff --git a/src/bin/client/ui/mod.rs b/src/bin/client/ui/mod.rs index e7fc639..ebbb22b 100644 --- a/src/bin/client/ui/mod.rs +++ b/src/bin/client/ui/mod.rs @@ -8,7 +8,9 @@ mod friends; mod main; mod misc; mod server; +mod user; pub use connect::*; pub use main::*; pub use misc::*; +pub use user::*; diff --git a/src/bin/client/ui/user.rs b/src/bin/client/ui/user.rs new file mode 100644 index 0000000..436ef36 --- /dev/null +++ b/src/bin/client/ui/user.rs @@ -0,0 +1,53 @@ +use super::*; +use crate::net::{NetHandle, SyncRecv}; +use iris::core::util::HashMap; +use openworm::net::{RequestUserInfo, UserId, UserInfo}; + +pub struct UserCache { + pub con: NetHandle, + pub requests: HashMap>, + pub users: HashMap, + pub widgets: HashMap>>, +} + +impl UserCache { + pub fn new(con: NetHandle) -> Self { + Self { + con, + requests: Default::default(), + users: Default::default(), + widgets: Default::default(), + } + } + + pub fn username(&mut self, id: UserId, rsc: &mut Rsc) -> WeakWidget { + let text = if let Some(user) = self.users.get(&id) { + &user.username + } else { + if !self.requests.contains_key(&id) { + let recv = self.con.request_sync(RequestUserInfo { id }); + self.requests.insert(id, recv); + } + "loading..." + }; + let wid = wtext(text).add(rsc); + self.widgets.entry(id).or_default().push(wid); + wid + } + + pub fn update(&mut self, rsc: &mut Rsc) { + self.requests.retain(|id, req| { + if let Some(resp) = req.try_recv() { + if let Ok(info) = resp { + for &widget in self.widgets.get(id).into_iter().flatten() { + *rsc[widget].content = info.username.clone(); + } + self.users.insert(*id, info); + } + false + } else { + true + } + }); + } +} diff --git a/src/bin/server/handle.rs b/src/bin/server/handle.rs index c441aa9..e85b987 100644 --- a/src/bin/server/handle.rs +++ b/src/bin/server/handle.rs @@ -49,7 +49,7 @@ impl ClientHandler { if let Some(user) = self.db.users.get(uid) { Some((*uid, user)) } else { - reply!(AccountDeleted); + reply!(InvalidUser); } } else { reply!(NotLoggedIn); @@ -156,7 +156,7 @@ impl ClientHandler { loop { let mut tx = db.write_tx(); let Some(mut user) = tx.get(&db.users, &user_id) else { - reply!(AccountDeleted); + reply!(InvalidUser); }; let Some(other_id) = tx.get(&db.usernames, &info.username) else { reply!(AddFriendResp::UnknownUser); @@ -242,6 +242,16 @@ impl ClientHandler { } None } + ClientMsg::RequestUserInfo(info) => { + check_user().await?; + let Some(other_user) = db.users.get(&info.id) else { + reply!(InvalidUser); + }; + // TODO: check perms... (privacy settings) + reply!(UserInfo { + username: other_user.username + }); + } } } } diff --git a/src/net/data.rs b/src/net/data.rs index 7200569..b601e88 100644 --- a/src/net/data.rs +++ b/src/net/data.rs @@ -1,5 +1,17 @@ use iris::core::util::HashSet; +#[derive(Debug, bitcode::Encode, bitcode::Decode)] +pub struct RequestUserInfo { + pub id: UserId, +} + +pub type RequestUserInfoResp = UserInfo; + +#[derive(Debug, bitcode::Encode, bitcode::Decode)] +pub struct UserInfo { + pub username: String, +} + pub type UserId = u64; pub type AccountToken = String; @@ -107,4 +119,4 @@ pub struct NotLoggedIn; #[derive(Debug, bitcode::Encode, bitcode::Decode)] pub struct NoPermission; #[derive(Debug, bitcode::Encode, bitcode::Decode)] -pub struct AccountDeleted; +pub struct InvalidUser; diff --git a/src/net/msg.rs b/src/net/msg.rs index 3e59f62..00102c9 100644 --- a/src/net/msg.rs +++ b/src/net/msg.rs @@ -9,17 +9,19 @@ msg_type!(ClientMsg: { 5: RemoveFriend, 6: AnswerFriendRequest, 7: GenerateToken => GenerateTokenResp, + 8: RequestUserInfo => RequestUserInfoResp, }); msg_type!(ServerMsg: { 0: NotLoggedIn, 1: NoPermission, - 2: AccountDeleted, + 2: InvalidUser, 3: CreateAccountResp, 4: LoginResp, 5: RequestUsersResp, 6: RequestFriendsResp, 7: AddFriendResp, 8: GenerateTokenResp, + 9: RequestUserInfoResp, }); macro_rules! msg_type { diff --git a/src/net/request.rs b/src/net/request.rs index de10846..ad4f742 100644 --- a/src/net/request.rs +++ b/src/net/request.rs @@ -31,6 +31,6 @@ pub struct ServerMsgInst { } pub trait RequestMsg: Into { - type Result; + type Result: Send + Sync + 'static; fn result(msg: ServerMsg) -> Option; }