diff --git a/src/client/mod.rs b/src/client/mod.rs index 207e5f3..48ccc87 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -21,6 +21,7 @@ pub use app::AppHandle; pub enum ClientEvent { Connect { send: NetSender, username: String }, ServerMsg(ServerMsg), + Err(String), } pub struct Client { @@ -34,6 +35,7 @@ pub struct Client { dir: DataDir, data: ClientData, handle: AppHandle, + error: Option>, } impl Client { @@ -52,6 +54,7 @@ impl Client { focus: None, username: "".to_string(), clipboard: Clipboard::new().unwrap(), + error: None, }; ui::init(&mut s); s @@ -80,6 +83,11 @@ impl Client { } } }, + ClientEvent::Err(msg) => { + if let Some(err) = &self.error { + self.ui[err].inner = Some(ui::error(&mut self.ui, &msg)); + } + } } } diff --git a/src/client/ui.rs b/src/client/ui.rs index 266e552..c514d94 100644 --- a/src/client/ui.rs +++ b/src/client/ui.rs @@ -27,7 +27,7 @@ pub fn init(client: &mut Client) { login_screen(client).set_root(&mut client.ui); } -pub fn main_view(client: &mut Client, network: NetSender) -> WidgetId { +pub fn main_view(client: &mut Client, network: NetSender) -> WidgetId { let msg_panel = msg_panel(client, network); let side_bar = rect(Color::BLACK.brighter(0.05)).width(80); @@ -37,9 +37,22 @@ pub fn main_view(client: &mut Client, network: NetSender) -> WidgetId .any() } -fn login_screen(client: &mut Client) -> WidgetId { +pub fn error(ui: &mut Ui, msg: &str) -> WidgetId { + text(msg) + .size(20) + .color(Color::RED.brighter(0.3)) + .pad(10) + .add(ui) + .any() +} + +fn login_screen(client: &mut Client) -> WidgetId { let Client { - ui, handle, data, .. + ui, + handle, + data, + error, + .. } = client; let mut field = |name| text(name).editable().size(20).add(ui); @@ -69,7 +82,7 @@ fn login_screen(client: &mut Client) -> WidgetId { connect(handle.clone(), ConnectInfo { ip, username }); }) .height(40); - ( + let modal = ( text("login").size(30), fbx(ip .id_on(Edited, |id, client: &mut Client, _| { @@ -89,9 +102,12 @@ fn login_screen(client: &mut Client) -> WidgetId { .pad(15) .background(rect(Color::BLACK.brighter(0.2)).radius(15)) .width(400) - .align(Align::Center) - .add(ui) - .any() + .align(Align::Center); + + let err = WidgetPtr::default().add(ui); + client.error = Some(err.clone()); + + (modal, err.align(Align::Top)).stack().add(ui).any() } pub fn msg_widget(msg: Msg) -> impl WidgetLike { diff --git a/src/net/client.rs b/src/net/client.rs index b0b0013..473c107 100644 --- a/src/net/client.rs +++ b/src/net/client.rs @@ -6,10 +6,14 @@ use crate::{ transfer::{RecvHandler, recv_uni, send_uni}, }, }; -use quinn::{ClientConfig, Connection, Endpoint, crypto::rustls::QuicClientConfig}; +use quinn::{ + ClientConfig, Connection, Endpoint, IdleTimeout, TransportConfig, + crypto::rustls::QuicClientConfig, +}; use std::{ net::{Ipv6Addr, SocketAddr, SocketAddrV6, ToSocketAddrs}, sync::Arc, + time::Duration, }; use tokio::sync::mpsc::UnboundedSender; @@ -22,11 +26,15 @@ pub struct ConnectInfo { } pub fn connect(handle: AppHandle, info: ConnectInfo) { - std::thread::spawn(|| { - connect_the(handle, info).unwrap(); + std::thread::spawn(move || { + if let Err(msg) = connect_the(handle.clone(), info) { + handle.send(ClientEvent::Err(msg)); + } }); } +type NetResult = Result; + type MsgPayload = ClientMsg; pub struct NetSender { send: UnboundedSender, @@ -34,7 +42,7 @@ pub struct NetSender { impl NetSender { pub fn send(&self, msg: ClientMsg) { - self.send.send(msg).unwrap(); + let _ = self.send.send(msg); } } @@ -64,35 +72,50 @@ impl NetSender { // .map_err(|e| anyhow::anyhow!("failed to connect: {}", e)) // } -async fn connection_no_cert(addr: SocketAddr) -> anyhow::Result { - let mut endpoint = Endpoint::client(CLIENT_SOCKET)?; +async fn connection_no_cert(addr: SocketAddr) -> NetResult { + let mut endpoint = Endpoint::client(CLIENT_SOCKET).map_err(|e| e.to_string())?; - endpoint.set_default_client_config(ClientConfig::new(Arc::new(QuicClientConfig::try_from( + let quic = QuicClientConfig::try_from( quinn::rustls::ClientConfig::builder() .dangerous() .with_custom_certificate_verifier(SkipServerVerification::new()) .with_no_client_auth(), - )?))); + ) + .map_err(|e| e.to_string())?; + + let mut transport = TransportConfig::default(); + transport.max_idle_timeout(Some(IdleTimeout::try_from(Duration::from_secs(5)).unwrap())); + + let mut config = ClientConfig::new(Arc::new(quic)); + config.transport_config(transport.into()); + + endpoint.set_default_client_config(config); // connect to server endpoint .connect(addr, SERVER_NAME) - .unwrap() + .map_err(|e| e.to_string())? .await - .map_err(|e| anyhow::anyhow!("failed to connect: {e}")) + .map_err(|e| e.to_string()) } #[tokio::main] -async fn connect_the(handle: AppHandle, info: ConnectInfo) -> anyhow::Result<()> { +async fn connect_the(handle: AppHandle, info: ConnectInfo) -> NetResult<()> { let (send, mut ui_recv) = tokio::sync::mpsc::unbounded_channel::(); + let addr = info + .ip + .to_socket_addrs() + .map_err(|e| e.to_string())? + .next() + .ok_or("no addresses found".to_string())?; + let conn = connection_no_cert(addr).await?; + let conn_ = conn.clone(); + handle.send(ClientEvent::Connect { username: info.username, send: NetSender { send }, }); - let addr = info.ip.to_socket_addrs().unwrap().next().unwrap(); - let conn = connection_no_cert(addr).await?; - let conn_ = conn.clone(); let recv = ServerRecv { handle }; tokio::spawn(recv_uni(conn_, recv));