friending work
This commit is contained in:
2
iris
2
iris
Submodule iris updated: 426ff0adfc...1aadef0e7e
@@ -44,6 +44,7 @@ impl DefaultAppState for Client {
|
|||||||
_: Proxy<Self::Event>,
|
_: Proxy<Self::Event>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let notif = WidgetPtr::default().add(rsc);
|
let notif = WidgetPtr::default().add(rsc);
|
||||||
|
// let popup = WidgetPtr::default().add(rsc);
|
||||||
let main_ui = WidgetPtr::default().add(rsc);
|
let main_ui = WidgetPtr::default().add(rsc);
|
||||||
let bg = (
|
let bg = (
|
||||||
image(include_bytes!("./assets/fuit.jpg")),
|
image(include_bytes!("./assets/fuit.jpg")),
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use openworm::net::{CreateAccount, CreateAccountResp, Login, LoginResp};
|
use openworm::net::{CreateAccount, CreateAccountResp, Login, LoginResp};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
session::Session,
|
|
||||||
data::{AccountInfo, ClientData, ServerInfo, ServerList},
|
data::{AccountInfo, ClientData, ServerInfo, ServerList},
|
||||||
net::{ConnectInfo, NetHandle},
|
net::{ConnectInfo, NetHandle},
|
||||||
|
session::Session,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -51,7 +51,7 @@ pub fn start(rsc: &mut Rsc, data: &ClientData) -> WeakWidget {
|
|||||||
let mut fail = |reason: &str| {
|
let mut fail = |reason: &str| {
|
||||||
let reason = reason.to_string();
|
let reason = reason.to_string();
|
||||||
ctx.update(move |ctx, rsc| {
|
ctx.update(move |ctx, rsc| {
|
||||||
rsc[ctx.notif].inner = Some(werror(&reason, rsc));
|
ctx.error(&reason, rsc);
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
let con = match NetHandle::connect(
|
let con = match NetHandle::connect(
|
||||||
@@ -122,11 +122,11 @@ pub fn start(rsc: &mut Rsc, data: &ClientData) -> WeakWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_account(rsc: &mut Rsc) -> WeakWidget {
|
pub fn create_account(rsc: &mut Rsc) -> WeakWidget {
|
||||||
let url = field("", "server", rsc);
|
let url = field("", "server").add(rsc);
|
||||||
let token = field("", "account creation token", rsc);
|
let token = field("", "account creation token").add(rsc);
|
||||||
let cert = field("", "certificate hex", rsc);
|
let cert = field("", "certificate hex").add(rsc);
|
||||||
let username = field("", "username", rsc);
|
let username = field("", "username").add(rsc);
|
||||||
let password = field("", "password", rsc);
|
let password = field("", "password").add(rsc);
|
||||||
|
|
||||||
let create = Button::submit("create", rsc);
|
let create = Button::submit("create", rsc);
|
||||||
rsc.events.register(create, Submit, move |ctx, rsc| {
|
rsc.events.register(create, Submit, move |ctx, rsc| {
|
||||||
@@ -145,7 +145,7 @@ pub fn create_account(rsc: &mut Rsc) -> WeakWidget {
|
|||||||
let mut fail = |reason: &str| {
|
let mut fail = |reason: &str| {
|
||||||
let reason = reason.to_string();
|
let reason = reason.to_string();
|
||||||
ctx.update(move |ctx, rsc| {
|
ctx.update(move |ctx, rsc| {
|
||||||
rsc[ctx.notif].inner = Some(werror(&reason, rsc));
|
ctx.error(&reason, rsc);
|
||||||
create.enable(rsc);
|
create.enable(rsc);
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
@@ -200,11 +200,11 @@ pub fn create_account(rsc: &mut Rsc) -> WeakWidget {
|
|||||||
|
|
||||||
(
|
(
|
||||||
wtext("Create Account").text_align(Align::CENTER).size(30),
|
wtext("Create Account").text_align(Align::CENTER).size(30),
|
||||||
field_box(url, rsc),
|
field_box(url),
|
||||||
field_box(token, rsc),
|
field_box(token),
|
||||||
field_box(cert, rsc),
|
field_box(cert),
|
||||||
field_box(username, rsc),
|
field_box(username),
|
||||||
field_box(password, rsc),
|
field_box(password),
|
||||||
create,
|
create,
|
||||||
)
|
)
|
||||||
.span(Dir::DOWN)
|
.span(Dir::DOWN)
|
||||||
|
|||||||
@@ -1,12 +1,113 @@
|
|||||||
// pub fn view(rsc: &mut Rsc, session: &Session) -> StrongWidget {
|
use openworm::net::{AddFriend, AddFriendResp, GenerateToken, RequestFriends, ServerPerms};
|
||||||
// let mut view = WidgetSelector::new(View::Info, info(rsc));
|
|
||||||
// view.set(View::User, users(rsc, session));
|
use super::*;
|
||||||
// let view = view.add(rsc);
|
|
||||||
// let [info, server] = tabs(rsc, view, [("info", View::Info), ("users", View::User)]);
|
pub fn view(rsc: &mut Rsc, session: &Session) -> StrongWidget {
|
||||||
//
|
(
|
||||||
// let side_bar = rect(Color::BLACK.alpha(150))
|
gen_token(rsc, session),
|
||||||
// .foreground((info, server).span(Dir::DOWN))
|
add_friend_area(rsc, session),
|
||||||
// .width(260);
|
friends_list(rsc, session),
|
||||||
//
|
)
|
||||||
// (side_bar, view).span(Dir::RIGHT).add_strong(rsc)
|
.span(Dir::DOWN)
|
||||||
// }
|
.add_strong(rsc)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = con.clone();
|
||||||
|
let username = username_field.edit(rsc).take();
|
||||||
|
add.disable(rsc);
|
||||||
|
rsc.tasks.spawn(async move |mut ctx| {
|
||||||
|
let Ok(resp) = con.request(AddFriend { username }).await else {
|
||||||
|
ctx.update(move |ctx, rsc| {
|
||||||
|
ctx.error("failed to add user", rsc);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
ctx.update(move |ctx, rsc| {
|
||||||
|
add.enable(rsc);
|
||||||
|
match resp {
|
||||||
|
AddFriendResp::Ok => {}
|
||||||
|
AddFriendResp::UnknownUser => {
|
||||||
|
ctx.error("unknown user", rsc);
|
||||||
|
}
|
||||||
|
AddFriendResp::CannotAddSelf => {
|
||||||
|
ctx.error("cannot add self", rsc);
|
||||||
|
}
|
||||||
|
AddFriendResp::AlreadySent => {
|
||||||
|
ctx.error("already sent request to user", rsc);
|
||||||
|
}
|
||||||
|
AddFriendResp::AlreadyFriends => {
|
||||||
|
ctx.error("already friends with user", rsc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
(field_box(username_field).width(300), add.width(200))
|
||||||
|
.span(Dir::RIGHT)
|
||||||
|
.add(rsc)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gen_token(rsc: &mut Rsc, session: &Session) -> WeakWidget {
|
||||||
|
let generate = Button::normal("generate token", rsc);
|
||||||
|
let con = session.con.clone();
|
||||||
|
let token = wtext("")
|
||||||
|
.size(30)
|
||||||
|
.editable(EditMode::SingleLine)
|
||||||
|
.attr::<Selectable>(())
|
||||||
|
.add(rsc);
|
||||||
|
rsc.events.register(generate, Submit, move |_, rsc| {
|
||||||
|
let con = con.clone();
|
||||||
|
token.edit(rsc).set("loading...");
|
||||||
|
rsc.spawn_task(async move |mut ctx| {
|
||||||
|
let resp = con
|
||||||
|
.request(GenerateToken {
|
||||||
|
perms: ServerPerms::NONE,
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
ctx.update(move |_, rsc| match resp {
|
||||||
|
Ok(resp) => {
|
||||||
|
token.edit(rsc).set(&resp.token);
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
token.edit(rsc).set("failed to generate token");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
(generate.width(200), token).span(Dir::RIGHT).add(rsc)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn friends_list(rsc: &mut Rsc, session: &Session) -> WeakWidget {
|
||||||
|
let ptr = loading_area("loading friends").add(rsc);
|
||||||
|
let con = session.con.clone();
|
||||||
|
rsc.events.register(ptr, Draw, move |_, rsc| {
|
||||||
|
let con = con.clone();
|
||||||
|
// TODO: maybe have rsc.request method that takes in &con?
|
||||||
|
// need to also handle error tho
|
||||||
|
rsc.spawn_task(async move |mut ctx| {
|
||||||
|
let Ok(resp) = con.request(RequestFriends).await else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
ctx.update(move |_, rsc| {
|
||||||
|
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(section_label("friends").add_strong(rsc));
|
||||||
|
all.push(user_list(&resp.current, rsc).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.set_ptr(ptr, rsc);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ptr
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
use crate::{Rsc, session::Session};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub const SIZE: u32 = 20;
|
pub const SIZE: u32 = 20;
|
||||||
@@ -16,6 +14,7 @@ pub enum MainView {
|
|||||||
pub fn main_view(rsc: &mut Rsc, session: Session) -> WeakWidget {
|
pub fn main_view(rsc: &mut Rsc, session: Session) -> WeakWidget {
|
||||||
let mut view = WidgetSelector::new(MainView::Channel, channel::view(rsc));
|
let mut view = WidgetSelector::new(MainView::Channel, channel::view(rsc));
|
||||||
view.set(MainView::Server, server::view(rsc, &session));
|
view.set(MainView::Server, server::view(rsc, &session));
|
||||||
|
view.set(MainView::Friends, friends::view(rsc, &session));
|
||||||
let view = view.add(rsc);
|
let view = view.add(rsc);
|
||||||
let [channel, friends, server] = tabs(
|
let [channel, friends, server] = tabs(
|
||||||
rsc,
|
rsc,
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
use openworm::net::UserId;
|
||||||
|
|
||||||
|
use crate::Client;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub fn werror(msg: &str, rsc: &mut Rsc) -> StrongWidget {
|
pub fn werror(msg: &str, rsc: &mut Rsc) -> StrongWidget {
|
||||||
@@ -16,20 +20,18 @@ pub fn large_hint_text(msg: impl Into<String>) -> TextBuilder<Rsc> {
|
|||||||
wtext(msg).size(30).color(Color::GRAY)
|
wtext(msg).size(30).color(Color::GRAY)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn field(default: &str, hint: &str, rsc: &mut Rsc) -> WeakWidget<TextEdit> {
|
pub fn field(default: &str, hint: &str) -> impl FnOnce(&mut Rsc) -> TextEdit {
|
||||||
wtext(default)
|
wtext(default)
|
||||||
.editable(EditMode::SingleLine)
|
.editable(EditMode::SingleLine)
|
||||||
.size(20)
|
.size(20)
|
||||||
.hint(hint_text(hint))
|
.hint(hint_text(hint))
|
||||||
.add(rsc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn field_box(field: WeakWidget<TextEdit>, rsc: &mut Rsc) -> WeakWidget {
|
pub fn field_box(field: WeakWidget<TextEdit>) -> impl WidgetIdFn<Rsc, Stack> {
|
||||||
field
|
field
|
||||||
.pad(10)
|
.pad(10)
|
||||||
.background(rect(Color::BLACK.brighter(0.1)).radius(15))
|
.background(rect(Color::BLACK.brighter(0.1)).radius(15))
|
||||||
.attr::<Selector>(field)
|
.attr::<Selector>(field)
|
||||||
.add(rsc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, WidgetView)]
|
#[derive(Clone, Copy, WidgetView)]
|
||||||
@@ -92,6 +94,10 @@ impl Button {
|
|||||||
Self::new(text, color::GREEN, rsc)
|
Self::new(text, color::GREEN, rsc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn normal(text: &str, rsc: &mut Rsc) -> Self {
|
||||||
|
Self::new(text, color::DARK, rsc)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn disable(&self, rsc: &mut Rsc) {
|
pub fn disable(&self, rsc: &mut Rsc) {
|
||||||
rsc[self.enabled] = false;
|
rsc[self.enabled] = false;
|
||||||
rsc[self.rect].color = self.color.darker(0.8);
|
rsc[self.rect].color = self.color.darker(0.8);
|
||||||
@@ -123,10 +129,43 @@ pub fn tabs<T: Eq + std::hash::Hash + 'static + Copy, const N: usize>(
|
|||||||
list: [(&'static str, T); N],
|
list: [(&'static str, T); N],
|
||||||
) -> [WeakWidget; N] {
|
) -> [WeakWidget; N] {
|
||||||
list.map(|(name, sel)| {
|
list.map(|(name, sel)| {
|
||||||
Button::new(name, color::DARK, rsc)
|
Button::normal(name, rsc)
|
||||||
.on(Submit, move |_, rsc: &mut Rsc| {
|
.on(Submit, move |_, rsc: &mut Rsc| {
|
||||||
rsc[view].select(sel);
|
rsc[view].select(sel);
|
||||||
})
|
})
|
||||||
.add(rsc)
|
.add(rsc)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn loading_area(text: &str) -> impl FnOnce(&mut Rsc) -> WidgetPtr {
|
||||||
|
move |rsc| {
|
||||||
|
WidgetPtr::new(
|
||||||
|
large_hint_text(text)
|
||||||
|
.center_text()
|
||||||
|
.width(rest(1))
|
||||||
|
.add_strong(rsc),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::Rsc;
|
use crate::{Rsc, session::Session};
|
||||||
use iris::prelude::*;
|
use iris::prelude::*;
|
||||||
|
|
||||||
mod channel;
|
mod channel;
|
||||||
|
|||||||
@@ -28,13 +28,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 = WidgetPtr::new(
|
let ptr = loading_area("loading users").add(rsc);
|
||||||
large_hint_text("loading users...")
|
|
||||||
.center_text()
|
|
||||||
.width(rest(1))
|
|
||||||
.add_strong(rsc),
|
|
||||||
)
|
|
||||||
.add(rsc);
|
|
||||||
let con = session.con.clone();
|
let con = session.con.clone();
|
||||||
rsc.events.register(ptr, Draw, move |_, rsc| {
|
rsc.events.register(ptr, Draw, move |_, rsc| {
|
||||||
let con = con.clone();
|
let con = con.clone();
|
||||||
|
|||||||
@@ -158,26 +158,26 @@ impl ClientHandler {
|
|||||||
let Some(mut user) = tx.get(&db.users, &user_id) else {
|
let Some(mut user) = tx.get(&db.users, &user_id) else {
|
||||||
reply!(AccountDeleted);
|
reply!(AccountDeleted);
|
||||||
};
|
};
|
||||||
let Some(friend_id) = tx.get(&db.usernames, &info.username) else {
|
let Some(other_id) = tx.get(&db.usernames, &info.username) else {
|
||||||
reply!(AddFriendResp::UnknownUser);
|
reply!(AddFriendResp::UnknownUser);
|
||||||
};
|
};
|
||||||
if user.friends.outgoing.contains(&friend_id) {
|
if user.friends.outgoing.contains(&other_id) {
|
||||||
reply!(AddFriendResp::AlreadySent);
|
reply!(AddFriendResp::AlreadySent);
|
||||||
}
|
}
|
||||||
if friend_id == user_id {
|
if other_id == user_id {
|
||||||
reply!(AddFriendResp::CannotAddSelf);
|
reply!(AddFriendResp::CannotAddSelf);
|
||||||
}
|
}
|
||||||
let Some(mut other) = tx.get(&db.users, &user_id) else {
|
let Some(mut other) = tx.get(&db.users, &other_id) else {
|
||||||
println!("WARNING: username without valid user!");
|
println!("WARNING: username without valid user!");
|
||||||
reply!(AddFriendResp::UnknownUser);
|
reply!(AddFriendResp::UnknownUser);
|
||||||
};
|
};
|
||||||
if other.friends.current.contains(&user_id) {
|
if other.friends.current.contains(&user_id) {
|
||||||
reply!(AddFriendResp::AlreadyFriends);
|
reply!(AddFriendResp::AlreadyFriends);
|
||||||
}
|
}
|
||||||
user.friends.outgoing.insert(friend_id);
|
user.friends.outgoing.insert(other_id);
|
||||||
other.friends.incoming.insert(user_id);
|
other.friends.incoming.insert(user_id);
|
||||||
tx.insert(&db.users, &user_id, &user);
|
tx.insert(&db.users, &user_id, &user);
|
||||||
tx.insert(&db.users, &friend_id, &other);
|
tx.insert(&db.users, &other_id, &other);
|
||||||
if tx.commit() {
|
if tx.commit() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -189,15 +189,15 @@ impl ClientHandler {
|
|||||||
loop {
|
loop {
|
||||||
let mut tx = db.write_tx();
|
let mut tx = db.write_tx();
|
||||||
let mut user = tx.get(&db.users, &user_id)?;
|
let mut user = tx.get(&db.users, &user_id)?;
|
||||||
let friend_id = info.id;
|
let other_id = info.id;
|
||||||
let Some(mut other) = tx.get(&db.users, &user_id) else {
|
let Some(mut other) = tx.get(&db.users, &other_id) else {
|
||||||
println!("WARNING: username without valid user!");
|
println!("WARNING: username without valid user!");
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
user.friends.current.remove(&friend_id);
|
user.friends.current.remove(&other_id);
|
||||||
other.friends.current.remove(&user_id);
|
other.friends.current.remove(&user_id);
|
||||||
tx.insert(&db.users, &user_id, &user);
|
tx.insert(&db.users, &user_id, &user);
|
||||||
tx.insert(&db.users, &friend_id, &other);
|
tx.insert(&db.users, &other_id, &other);
|
||||||
if tx.commit() {
|
if tx.commit() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -218,7 +218,7 @@ impl ClientHandler {
|
|||||||
let mut tx = db.write_tx();
|
let mut tx = db.write_tx();
|
||||||
let mut user = tx.get(&db.users, &user_id)?;
|
let mut user = tx.get(&db.users, &user_id)?;
|
||||||
let other_id = answer.id;
|
let other_id = answer.id;
|
||||||
let Some(mut other) = tx.get(&db.users, &user_id) else {
|
let Some(mut other) = tx.get(&db.users, &other_id) else {
|
||||||
println!("WARNING: username without valid user!");
|
println!("WARNING: username without valid user!");
|
||||||
return None;
|
return None;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user