Compare commits
4 Commits
54eb7d4261
...
work
| Author | SHA1 | Date | |
|---|---|---|---|
| e7a50cbdbc | |||
| 24299bfa17 | |||
| 3e6df06411 | |||
| a32f6392b7 |
Generated
+655
-568
File diff suppressed because it is too large
Load Diff
+1
-1
Submodule iris updated: 1102dc7338...a648c62aa2
@@ -29,11 +29,10 @@ impl ClientData {
|
||||
self.data.load::<AccountList>().push(info);
|
||||
}
|
||||
|
||||
pub fn password(&self, info: &AccountInfo) -> String {
|
||||
pub fn password(&self, info: &AccountInfo) -> Result<String, String> {
|
||||
let user_path = info.path();
|
||||
let entry =
|
||||
keyring_core::Entry::new("openworm", &user_path).expect("failed to open keyring entry");
|
||||
entry.get_password().unwrap()
|
||||
let entry = keyring_core::Entry::new("openworm", &user_path).map_err(|e| e.to_string())?;
|
||||
entry.get_password().map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
pub fn accounts(&self) -> DataGuard<AccountList> {
|
||||
|
||||
@@ -29,7 +29,7 @@ pub struct Client {
|
||||
}
|
||||
|
||||
pub type Rsc = DefaultRsc<Client>;
|
||||
pub type ClientSender = EventSender<Rsc>;
|
||||
pub type ClientSender = EventSender<Client>;
|
||||
|
||||
pub enum ClientEvent {
|
||||
CacheUpdate,
|
||||
|
||||
+27
-12
@@ -1,5 +1,6 @@
|
||||
use crate::ClientSender;
|
||||
use crate::{Client, ClientSender, Rsc};
|
||||
use dashmap::DashMap;
|
||||
use iris::prelude::DefaultRsc;
|
||||
use openworm::net::{
|
||||
ClientMsg, ClientMsgInst, RecvHandler, RequestId, RequestMsg, SERVER_NAME, ServerMsg,
|
||||
ServerMsgInst, SkipServerVerification, recv_uni, send_uni,
|
||||
@@ -30,16 +31,27 @@ pub struct NetHandle {
|
||||
}
|
||||
|
||||
type NetResult<T> = Result<T, String>;
|
||||
type SyncReqFn = Box<dyn FnOnce(ServerMsg, &mut Rsc) + Send + Sync>;
|
||||
|
||||
pub enum NetCtrlMsg {
|
||||
Send(ClientMsg),
|
||||
Request(ClientMsg, oneshot::Sender<ServerMsg>),
|
||||
RequestSync(ClientMsg, Box<dyn FnOnce(ServerMsg) + Send + Sync>),
|
||||
RequestSync(ClientMsg, SyncReqFn),
|
||||
Exit,
|
||||
}
|
||||
|
||||
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 {
|
||||
fn send_(&self, msg: NetCtrlMsg) {
|
||||
let _ = self.send.send(msg);
|
||||
@@ -60,21 +72,22 @@ impl NetHandle {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request_sync<R: RequestMsg>(&self, msg: R) -> SyncRecv<R> {
|
||||
let (send, recv) = oneshot::channel();
|
||||
let sender = self.event_sender.clone();
|
||||
pub fn request_sync<R: RequestMsg>(
|
||||
&self,
|
||||
msg: R,
|
||||
callback: impl MainDataCallback<Result<R::Result, ()>, Client>,
|
||||
) {
|
||||
self.send_(NetCtrlMsg::RequestSync(
|
||||
msg.into(),
|
||||
Box::new(move |msg| {
|
||||
let _ = send.send(if let Some(res) = R::result(msg) {
|
||||
Box::new(move |msg, rsc| {
|
||||
let res = if let Some(res) = R::result(msg) {
|
||||
Ok(res)
|
||||
} else {
|
||||
Err(())
|
||||
});
|
||||
sender.run();
|
||||
};
|
||||
callback(res, rsc);
|
||||
}),
|
||||
));
|
||||
SyncRecv::<R> { recv }
|
||||
}
|
||||
|
||||
pub fn exit(self) {
|
||||
@@ -174,6 +187,7 @@ impl NetHandle {
|
||||
msg,
|
||||
requests_sync: DashMap::default(),
|
||||
requests: DashMap::default(),
|
||||
event_sender: event_sender.clone(),
|
||||
});
|
||||
tokio::spawn(recv_uni(conn_, recv.clone()));
|
||||
tokio::spawn(async move {
|
||||
@@ -239,7 +253,8 @@ where
|
||||
|
||||
struct ServerRecv<F: MsgHandler> {
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -249,7 +264,7 @@ impl<F: MsgHandler> RecvHandler<ServerMsgInst> for ServerRecv<F> {
|
||||
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)
|
||||
self.event_sender.run(|rsc| f(resp.msg, rsc));
|
||||
}
|
||||
} else {
|
||||
self.msg.run(resp.msg).await;
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
use std::sync::{Arc, Mutex, MutexGuard};
|
||||
|
||||
use iris::prelude::*;
|
||||
use openworm::net::UserId;
|
||||
|
||||
use crate::{net::NetHandle, ui::UserCache};
|
||||
use crate::{Rsc, 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 user_id: UserId,
|
||||
pub cache: UserCache,
|
||||
@@ -10,10 +19,23 @@ pub struct Session {
|
||||
|
||||
impl Session {
|
||||
pub fn new(con: NetHandle, user_id: UserId) -> Self {
|
||||
Self {
|
||||
Self(Arc::new(Mutex::new(SessionInner {
|
||||
cache: UserCache::new(con.clone()),
|
||||
con,
|
||||
user_id,
|
||||
}
|
||||
})))
|
||||
}
|
||||
|
||||
pub fn con(&self) -> NetHandle {
|
||||
self.get().con.clone()
|
||||
}
|
||||
|
||||
pub fn get(&self) -> MutexGuard<'_, SessionInner> {
|
||||
self.0.try_lock().unwrap()
|
||||
}
|
||||
|
||||
pub fn reactive_span(&self, rsc: &mut Rsc) -> WeakWidget {
|
||||
let span = Span::empty(Dir::DOWN).add(rsc);
|
||||
span
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,4 +2,5 @@ use super::*;
|
||||
|
||||
pub const MODAL_BG: UiColor = UiColor::BLACK.brighter(0.05);
|
||||
pub const GREEN: UiColor = UiColor::rgb(0, 150, 0);
|
||||
pub const RED: UiColor = UiColor::rgb(255, 100, 100);
|
||||
pub const DARK: UiColor = UiColor::BLACK.brighter(0.02);
|
||||
|
||||
@@ -31,8 +31,7 @@ pub fn start(rsc: &mut Rsc, data: &ClientData) -> WeakWidget {
|
||||
.add(rsc),
|
||||
color::DARK,
|
||||
rsc,
|
||||
)
|
||||
.add(rsc);
|
||||
);
|
||||
let account = account.clone();
|
||||
let cert_hex = data
|
||||
.data
|
||||
@@ -44,14 +43,22 @@ pub fn start(rsc: &mut Rsc, data: &ClientData) -> WeakWidget {
|
||||
let cert = decode_hex(&cert_hex).unwrap();
|
||||
keyring::use_native_store(true).unwrap();
|
||||
rsc.events.register(button, Submit, move |ctx, rsc| {
|
||||
let password = match ctx.state.data.password(&account) {
|
||||
Ok(v) => v,
|
||||
Err(err) => {
|
||||
ctx.state.error(&err, rsc);
|
||||
return;
|
||||
}
|
||||
};
|
||||
button.disable(rsc);
|
||||
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();
|
||||
ctx.update(move |ctx, rsc| {
|
||||
button.enable(rsc);
|
||||
ctx.error(&reason, rsc);
|
||||
})
|
||||
};
|
||||
@@ -91,8 +98,8 @@ pub fn start(rsc: &mut Rsc, data: &ClientData) -> WeakWidget {
|
||||
return fail("invalid password");
|
||||
}
|
||||
};
|
||||
let session = Session::new(con, user_id);
|
||||
ctx.update(move |ctx, rsc| {
|
||||
let session = Session::new(con, user_id);
|
||||
main_view(rsc, session).set_ptr(ctx.main_ui, rsc);
|
||||
});
|
||||
});
|
||||
@@ -190,8 +197,8 @@ pub fn create_account(rsc: &mut Rsc) -> WeakWidget {
|
||||
return fail("invalid account token");
|
||||
}
|
||||
};
|
||||
let session = Session::new(con, user_id);
|
||||
ctx.update(move |ctx, rsc| {
|
||||
let session = Session::new(con, user_id);
|
||||
main_view(rsc, session).set_ptr(ctx.main_ui, rsc);
|
||||
ctx.data.create_account(
|
||||
ServerInfo { cert_hex },
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
use openworm::net::{AddFriend, AddFriendResp, GenerateToken, RequestFriends, ServerPerms};
|
||||
use openworm::net::{
|
||||
AddFriend, AddFriendResp, AnswerFriendRequest, FriendRequestAction, GenerateToken,
|
||||
RequestFriends, ServerPerms, UserId,
|
||||
};
|
||||
|
||||
use crate::net::NetHandle;
|
||||
|
||||
use super::*;
|
||||
|
||||
@@ -15,8 +20,8 @@ pub fn view(rsc: &mut Rsc, session: &Session) -> StrongWidget {
|
||||
fn add_friend_area(rsc: &mut Rsc, session: &Session) -> WeakWidget {
|
||||
let username_field = field("", "username").add(rsc);
|
||||
let add = Button::submit("add friend", rsc);
|
||||
let con = session.con.clone();
|
||||
rsc.events.register(add, Submit, move |ctx, rsc| {
|
||||
let con = session.con();
|
||||
rsc.events.register(add, Submit, move |_, rsc| {
|
||||
let con = con.clone();
|
||||
let username = username_field.edit(rsc).take();
|
||||
add.disable(rsc);
|
||||
@@ -54,7 +59,7 @@ fn add_friend_area(rsc: &mut Rsc, session: &Session) -> WeakWidget {
|
||||
|
||||
fn gen_token(rsc: &mut Rsc, session: &Session) -> WeakWidget {
|
||||
let generate = Button::normal("generate token", rsc);
|
||||
let con = session.con.clone();
|
||||
let con = session.con();
|
||||
let token = wtext("")
|
||||
.size(30)
|
||||
.editable(EditMode::SingleLine)
|
||||
@@ -84,9 +89,11 @@ fn gen_token(rsc: &mut Rsc, session: &Session) -> WeakWidget {
|
||||
|
||||
fn friends_list(rsc: &mut Rsc, session: &Session) -> WeakWidget {
|
||||
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| {
|
||||
let con = con.clone();
|
||||
let session = session.clone();
|
||||
// TODO: maybe have rsc.request method that takes in &con?
|
||||
// need to also handle error tho
|
||||
rsc.spawn_task(async move |mut ctx| {
|
||||
@@ -97,13 +104,36 @@ fn friends_list(rsc: &mut Rsc, session: &Session) -> WeakWidget {
|
||||
let mut all = Span::empty(Dir::DOWN);
|
||||
if !resp.incoming.is_empty() {
|
||||
all.push(section_label("incoming").add_strong(rsc));
|
||||
all.push(user_list(&resp.incoming, rsc).add_strong(rsc))
|
||||
all.push(
|
||||
resp.incoming
|
||||
.rsc_map(|id, rsc| {
|
||||
(
|
||||
session.user_rect(id, rsc),
|
||||
accept_req(id, &con, rsc),
|
||||
deny_req(id, &con, rsc),
|
||||
)
|
||||
.span(Dir::RIGHT)
|
||||
.add(rsc)
|
||||
})
|
||||
.span(Dir::DOWN)
|
||||
.add_strong(rsc),
|
||||
)
|
||||
}
|
||||
all.push(section_label("friends").add_strong(rsc));
|
||||
all.push(user_list(&resp.current, rsc).add_strong(rsc));
|
||||
all.push(
|
||||
session
|
||||
.user_list(&resp.current)
|
||||
.span(Dir::DOWN)
|
||||
.add_strong(rsc),
|
||||
);
|
||||
if !resp.outgoing.is_empty() {
|
||||
all.push(section_label("outgoing").add_strong(rsc));
|
||||
all.push(user_list(&resp.outgoing, rsc).add_strong(rsc))
|
||||
all.push(
|
||||
session
|
||||
.user_list(&resp.outgoing)
|
||||
.span(Dir::DOWN)
|
||||
.add_strong(rsc),
|
||||
)
|
||||
}
|
||||
all.set_ptr(ptr, rsc);
|
||||
});
|
||||
@@ -111,3 +141,33 @@ fn friends_list(rsc: &mut Rsc, session: &Session) -> WeakWidget {
|
||||
});
|
||||
ptr
|
||||
}
|
||||
|
||||
pub fn accept_req(id: UserId, con: &NetHandle, rsc: &mut Rsc) -> WeakWidget {
|
||||
let button = Button::submit("", rsc);
|
||||
let con = con.clone();
|
||||
rsc.events.register(button, Submit, move |_, rsc| {
|
||||
let con = con.clone();
|
||||
rsc.tasks.spawn(async move |_| {
|
||||
con.send(AnswerFriendRequest {
|
||||
id,
|
||||
action: FriendRequestAction::Accept,
|
||||
});
|
||||
});
|
||||
});
|
||||
button.width(60).add(rsc)
|
||||
}
|
||||
|
||||
pub fn deny_req(id: UserId, con: &NetHandle, rsc: &mut Rsc) -> WeakWidget {
|
||||
let button = Button::new("X", color::RED, rsc);
|
||||
let con = con.clone();
|
||||
rsc.events.register(button, Submit, move |_, rsc| {
|
||||
let con = con.clone();
|
||||
rsc.tasks.spawn(async move |_| {
|
||||
con.send(AnswerFriendRequest {
|
||||
id,
|
||||
action: FriendRequestAction::Deny,
|
||||
});
|
||||
});
|
||||
});
|
||||
button.width(60).add(rsc)
|
||||
}
|
||||
|
||||
@@ -152,18 +152,6 @@ pub fn section_label(text: impl Into<String>) -> TextBuilder<Rsc> {
|
||||
wtext(text).size(30)
|
||||
}
|
||||
|
||||
pub fn user_list<'a>(ids: impl IntoIterator<Item = &'a UserId>, rsc: &mut Rsc) -> Span {
|
||||
let mut span = Span::empty(Dir::DOWN);
|
||||
for id in ids {
|
||||
let thing = (wtext(id.to_string()).size(20),)
|
||||
.span(Dir::RIGHT)
|
||||
.gap(30)
|
||||
.pad(15);
|
||||
span.push(thing.add_strong(rsc));
|
||||
}
|
||||
span
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn error(&self, text: &str, rsc: &mut Rsc) {
|
||||
rsc[self.notif].inner = Some(werror(text, rsc));
|
||||
|
||||
@@ -29,7 +29,7 @@ fn info(rsc: &mut Rsc) -> StrongWidget {
|
||||
|
||||
fn users(rsc: &mut Rsc, session: &Session) -> StrongWidget {
|
||||
let ptr = loading_area("loading users").add(rsc);
|
||||
let con = session.con.clone();
|
||||
let con = session.con();
|
||||
rsc.events.register(ptr, Draw, move |_, rsc| {
|
||||
let con = con.clone();
|
||||
rsc.spawn_task(async move |mut ctx| {
|
||||
|
||||
+32
-21
@@ -19,35 +19,46 @@ impl UserCache {
|
||||
widgets: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn username(&mut self, id: UserId, rsc: &mut Rsc) -> WeakWidget {
|
||||
let text = if let Some(user) = self.users.get(&id) {
|
||||
impl Session {
|
||||
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
|
||||
} else {
|
||||
if !self.requests.contains_key(&id) {
|
||||
let recv = self.con.request_sync(RequestUserInfo { id });
|
||||
self.requests.insert(id, recv);
|
||||
if !s.requests.contains_key(&id) {
|
||||
s.con
|
||||
.request_sync(RequestUserInfo { id }, move |resp, rsc| {
|
||||
if let Ok(info) = resp {
|
||||
let s = &mut session.get().cache;
|
||||
for &widget in s.widgets.get(&id).into_iter().flatten() {
|
||||
*rsc[widget].content = info.username.clone();
|
||||
}
|
||||
s.users.insert(id, info);
|
||||
}
|
||||
});
|
||||
}
|
||||
"loading..."
|
||||
};
|
||||
let wid = wtext(text).add(rsc);
|
||||
self.widgets.entry(id).or_default().push(wid);
|
||||
let wid = wtext(text).size(20).add(rsc);
|
||||
s.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
|
||||
}
|
||||
});
|
||||
pub fn user_list<'a>(
|
||||
&self,
|
||||
ids: impl IntoIterator<Item = &'a UserId>,
|
||||
) -> impl IntoIterator<Item = impl FnOnce(&mut Rsc) -> WeakWidget> {
|
||||
ids.rsc_map(|id, rsc: &mut Rsc| self.user_rect(*id, rsc))
|
||||
}
|
||||
|
||||
pub fn user_rect(&self, id: UserId, rsc: &mut Rsc) -> WeakWidget {
|
||||
(self.username(id, rsc),)
|
||||
.span(Dir::RIGHT)
|
||||
.gap(30)
|
||||
.pad(15)
|
||||
.add(rsc) as WeakWidget
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +164,10 @@ impl ClientHandler {
|
||||
if user.friends.outgoing.contains(&other_id) {
|
||||
reply!(AddFriendResp::AlreadySent);
|
||||
}
|
||||
// TODO: just accept in this case?
|
||||
if user.friends.incoming.contains(&other_id) {
|
||||
reply!(AddFriendResp::AlreadySent);
|
||||
}
|
||||
if other_id == user_id {
|
||||
reply!(AddFriendResp::CannotAddSelf);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user