USER CACHE

This commit is contained in:
2026-03-15 19:34:55 -04:00
parent 54eb7d4261
commit a32f6392b7
8 changed files with 84 additions and 51 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ pub struct Client {
} }
pub type Rsc = DefaultRsc<Client>; pub type Rsc = DefaultRsc<Client>;
pub type ClientSender = EventSender<Rsc>; pub type ClientSender = EventSender<Client>;
pub enum ClientEvent { pub enum ClientEvent {
CacheUpdate, CacheUpdate,
+27 -12
View File
@@ -1,5 +1,6 @@
use crate::ClientSender; use crate::{Client, ClientSender, Rsc};
use dashmap::DashMap; use dashmap::DashMap;
use iris::prelude::DefaultRsc;
use openworm::net::{ use openworm::net::{
ClientMsg, ClientMsgInst, RecvHandler, RequestId, RequestMsg, SERVER_NAME, ServerMsg, ClientMsg, ClientMsgInst, RecvHandler, RequestId, RequestMsg, SERVER_NAME, ServerMsg,
ServerMsgInst, SkipServerVerification, recv_uni, send_uni, ServerMsgInst, SkipServerVerification, recv_uni, send_uni,
@@ -30,16 +31,27 @@ pub struct NetHandle {
} }
type NetResult<T> = Result<T, String>; type NetResult<T> = Result<T, String>;
type SyncReqFn = Box<dyn FnOnce(ServerMsg, &mut Rsc) + Send + Sync>;
pub enum NetCtrlMsg { pub enum NetCtrlMsg {
Send(ClientMsg), Send(ClientMsg),
Request(ClientMsg, oneshot::Sender<ServerMsg>), Request(ClientMsg, oneshot::Sender<ServerMsg>),
RequestSync(ClientMsg, Box<dyn FnOnce(ServerMsg) + Send + Sync>), RequestSync(ClientMsg, SyncReqFn),
Exit, Exit,
} }
type Resp<R: RequestMsg> = Result<R::Result, ()>; type Resp<R: RequestMsg> = Result<R::Result, ()>;
// TODO: move into iris?
pub trait MainDataCallback<Data, State>:
FnOnce(Data, &mut DefaultRsc<State>) + Sync + Send + 'static
{
}
impl<F: FnOnce(Data, &mut DefaultRsc<State>) + Sync + Send + 'static, Data, State>
MainDataCallback<Data, State> for F
{
}
impl NetHandle { impl NetHandle {
fn send_(&self, msg: NetCtrlMsg) { fn send_(&self, msg: NetCtrlMsg) {
let _ = self.send.send(msg); let _ = self.send.send(msg);
@@ -60,21 +72,22 @@ impl NetHandle {
} }
} }
pub fn request_sync<R: RequestMsg>(&self, msg: R) -> SyncRecv<R> { pub fn request_sync<R: RequestMsg>(
let (send, recv) = oneshot::channel(); &self,
let sender = self.event_sender.clone(); msg: R,
callback: impl MainDataCallback<Result<R::Result, ()>, Client>,
) {
self.send_(NetCtrlMsg::RequestSync( self.send_(NetCtrlMsg::RequestSync(
msg.into(), msg.into(),
Box::new(move |msg| { Box::new(move |msg, rsc| {
let _ = send.send(if let Some(res) = R::result(msg) { let res = if let Some(res) = R::result(msg) {
Ok(res) Ok(res)
} else { } else {
Err(()) Err(())
}); };
sender.run(); callback(res, rsc);
}), }),
)); ));
SyncRecv::<R> { recv }
} }
pub fn exit(self) { pub fn exit(self) {
@@ -174,6 +187,7 @@ impl NetHandle {
msg, msg,
requests_sync: DashMap::default(), requests_sync: DashMap::default(),
requests: DashMap::default(), requests: DashMap::default(),
event_sender: event_sender.clone(),
}); });
tokio::spawn(recv_uni(conn_, recv.clone())); tokio::spawn(recv_uni(conn_, recv.clone()));
tokio::spawn(async move { tokio::spawn(async move {
@@ -239,7 +253,8 @@ where
struct ServerRecv<F: MsgHandler> { struct ServerRecv<F: MsgHandler> {
requests: DashMap<RequestId, oneshot::Sender<ServerMsg>>, requests: DashMap<RequestId, oneshot::Sender<ServerMsg>>,
requests_sync: DashMap<RequestId, Box<dyn FnOnce(ServerMsg) + Send + Sync>>, requests_sync: DashMap<RequestId, SyncReqFn>,
event_sender: ClientSender,
msg: F, msg: F,
} }
@@ -249,7 +264,7 @@ impl<F: MsgHandler> RecvHandler<ServerMsgInst> for ServerRecv<F> {
if let Some((_, send)) = self.requests.remove(&id) { if let Some((_, send)) = self.requests.remove(&id) {
let _ = send.send(resp.msg); let _ = send.send(resp.msg);
} else if let Some((_, f)) = self.requests_sync.remove(&id) { } else if let Some((_, f)) = self.requests_sync.remove(&id) {
f(resp.msg) self.event_sender.run(|rsc| f(resp.msg, rsc));
} }
} else { } else {
self.msg.run(resp.msg).await; self.msg.run(resp.msg).await;
+19 -3
View File
@@ -1,8 +1,16 @@
use std::sync::{Arc, Mutex, MutexGuard};
use openworm::net::UserId; use openworm::net::UserId;
use crate::{net::NetHandle, ui::UserCache}; use crate::{net::NetHandle, ui::UserCache};
pub struct Session { // TODO: this really should not be async...
// I mean it could be used async but all widgets
// are sync so I don't really think it makes sense to be...
#[derive(Clone)]
pub struct Session(Arc<Mutex<SessionInner>>);
pub struct SessionInner {
pub con: NetHandle, pub con: NetHandle,
pub user_id: UserId, pub user_id: UserId,
pub cache: UserCache, pub cache: UserCache,
@@ -10,10 +18,18 @@ pub struct Session {
impl Session { impl Session {
pub fn new(con: NetHandle, user_id: UserId) -> Self { pub fn new(con: NetHandle, user_id: UserId) -> Self {
Self { Self(Arc::new(Mutex::new(SessionInner {
cache: UserCache::new(con.clone()), cache: UserCache::new(con.clone()),
con, con,
user_id, user_id,
} })))
}
pub fn con(&self) -> NetHandle {
self.get().con.clone()
}
pub fn get(&self) -> MutexGuard<'_, SessionInner> {
self.0.try_lock().unwrap()
} }
} }
+2 -2
View File
@@ -91,8 +91,8 @@ pub fn start(rsc: &mut Rsc, data: &ClientData) -> WeakWidget {
return fail("invalid password"); return fail("invalid password");
} }
}; };
let session = Session::new(con, user_id);
ctx.update(move |ctx, rsc| { ctx.update(move |ctx, rsc| {
let session = Session::new(con, user_id);
main_view(rsc, session).set_ptr(ctx.main_ui, rsc); main_view(rsc, session).set_ptr(ctx.main_ui, rsc);
}); });
}); });
@@ -190,8 +190,8 @@ pub fn create_account(rsc: &mut Rsc) -> WeakWidget {
return fail("invalid account token"); return fail("invalid account token");
} }
}; };
let session = Session::new(con, user_id);
ctx.update(move |ctx, rsc| { ctx.update(move |ctx, rsc| {
let session = Session::new(con, user_id);
main_view(rsc, session).set_ptr(ctx.main_ui, rsc); main_view(rsc, session).set_ptr(ctx.main_ui, rsc);
ctx.data.create_account( ctx.data.create_account(
ServerInfo { cert_hex }, ServerInfo { cert_hex },
+9 -7
View File
@@ -15,8 +15,8 @@ pub fn view(rsc: &mut Rsc, session: &Session) -> StrongWidget {
fn add_friend_area(rsc: &mut Rsc, session: &Session) -> WeakWidget { fn add_friend_area(rsc: &mut Rsc, session: &Session) -> WeakWidget {
let username_field = field("", "username").add(rsc); let username_field = field("", "username").add(rsc);
let add = Button::submit("add friend", rsc); let add = Button::submit("add friend", rsc);
let con = session.con.clone(); let con = session.con();
rsc.events.register(add, Submit, move |ctx, rsc| { rsc.events.register(add, Submit, move |_, rsc| {
let con = con.clone(); let con = con.clone();
let username = username_field.edit(rsc).take(); let username = username_field.edit(rsc).take();
add.disable(rsc); add.disable(rsc);
@@ -54,7 +54,7 @@ fn add_friend_area(rsc: &mut Rsc, session: &Session) -> WeakWidget {
fn gen_token(rsc: &mut Rsc, session: &Session) -> WeakWidget { fn gen_token(rsc: &mut Rsc, session: &Session) -> WeakWidget {
let generate = Button::normal("generate token", rsc); let generate = Button::normal("generate token", rsc);
let con = session.con.clone(); let con = session.con();
let token = wtext("") let token = wtext("")
.size(30) .size(30)
.editable(EditMode::SingleLine) .editable(EditMode::SingleLine)
@@ -84,9 +84,11 @@ fn gen_token(rsc: &mut Rsc, session: &Session) -> WeakWidget {
fn friends_list(rsc: &mut Rsc, session: &Session) -> WeakWidget { fn friends_list(rsc: &mut Rsc, session: &Session) -> WeakWidget {
let ptr = loading_area("loading friends").add(rsc); let ptr = loading_area("loading friends").add(rsc);
let con = session.con.clone(); let con = session.con();
let session = session.clone();
rsc.events.register(ptr, Draw, move |_, rsc| { rsc.events.register(ptr, Draw, move |_, rsc| {
let con = con.clone(); let con = con.clone();
let session = session.clone();
// TODO: maybe have rsc.request method that takes in &con? // TODO: maybe have rsc.request method that takes in &con?
// need to also handle error tho // need to also handle error tho
rsc.spawn_task(async move |mut ctx| { rsc.spawn_task(async move |mut ctx| {
@@ -97,13 +99,13 @@ fn friends_list(rsc: &mut Rsc, session: &Session) -> WeakWidget {
let mut all = Span::empty(Dir::DOWN); let mut all = Span::empty(Dir::DOWN);
if !resp.incoming.is_empty() { if !resp.incoming.is_empty() {
all.push(section_label("incoming").add_strong(rsc)); all.push(section_label("incoming").add_strong(rsc));
all.push(user_list(&resp.incoming, rsc).add_strong(rsc)) all.push(user_list(&resp.incoming, &session, rsc).add_strong(rsc))
} }
all.push(section_label("friends").add_strong(rsc)); all.push(section_label("friends").add_strong(rsc));
all.push(user_list(&resp.current, rsc).add_strong(rsc)); all.push(user_list(&resp.current, &session, rsc).add_strong(rsc));
if !resp.outgoing.is_empty() { if !resp.outgoing.is_empty() {
all.push(section_label("outgoing").add_strong(rsc)); all.push(section_label("outgoing").add_strong(rsc));
all.push(user_list(&resp.outgoing, rsc).add_strong(rsc)) all.push(user_list(&resp.outgoing, &session, rsc).add_strong(rsc))
} }
all.set_ptr(ptr, rsc); all.set_ptr(ptr, rsc);
}); });
+6 -2
View File
@@ -152,10 +152,14 @@ pub fn section_label(text: impl Into<String>) -> TextBuilder<Rsc> {
wtext(text).size(30) wtext(text).size(30)
} }
pub fn user_list<'a>(ids: impl IntoIterator<Item = &'a UserId>, rsc: &mut Rsc) -> Span { pub fn user_list<'a>(
ids: impl IntoIterator<Item = &'a UserId>,
session: &Session,
rsc: &mut Rsc,
) -> Span {
let mut span = Span::empty(Dir::DOWN); let mut span = Span::empty(Dir::DOWN);
for id in ids { for id in ids {
let thing = (wtext(id.to_string()).size(20),) let thing = (session.username(*id, rsc),)
.span(Dir::RIGHT) .span(Dir::RIGHT)
.gap(30) .gap(30)
.pad(15); .pad(15);
+1 -1
View File
@@ -29,7 +29,7 @@ fn info(rsc: &mut Rsc) -> StrongWidget {
fn users(rsc: &mut Rsc, session: &Session) -> StrongWidget { fn users(rsc: &mut Rsc, session: &Session) -> StrongWidget {
let ptr = loading_area("loading users").add(rsc); let ptr = loading_area("loading users").add(rsc);
let con = session.con.clone(); let con = session.con();
rsc.events.register(ptr, Draw, move |_, rsc| { rsc.events.register(ptr, Draw, move |_, rsc| {
let con = con.clone(); let con = con.clone();
rsc.spawn_task(async move |mut ctx| { rsc.spawn_task(async move |mut ctx| {
+18 -22
View File
@@ -19,35 +19,31 @@ impl UserCache {
widgets: Default::default(), widgets: Default::default(),
} }
} }
}
pub fn username(&mut self, id: UserId, rsc: &mut Rsc) -> WeakWidget { impl Session {
let text = if let Some(user) = self.users.get(&id) { pub fn username(&self, id: UserId, rsc: &mut Rsc) -> WeakWidget {
let session = self.clone();
let s = &mut self.get().cache;
let text = if let Some(user) = s.users.get(&id) {
&user.username &user.username
} else { } else {
if !self.requests.contains_key(&id) { if !s.requests.contains_key(&id) {
let recv = self.con.request_sync(RequestUserInfo { id }); s.con
self.requests.insert(id, recv); .request_sync(RequestUserInfo { id }, move |resp, rsc| {
}
"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 { if let Ok(info) = resp {
for &widget in self.widgets.get(id).into_iter().flatten() { let s = &mut session.get().cache;
for &widget in s.widgets.get(&id).into_iter().flatten() {
*rsc[widget].content = info.username.clone(); *rsc[widget].content = info.username.clone();
} }
self.users.insert(*id, info); s.users.insert(id, info);
}
false
} else {
true
} }
}); });
} }
"loading..."
};
let wid = wtext(text).size(20).add(rsc);
s.widgets.entry(id).or_default().push(wid);
wid
}
} }