use tarpc::context; use tarpc::tokio_serde::formats::Json; use tokio::sync::broadcast; use tokio::sync::broadcast::{Receiver, Sender}; use tracing::log::*; use realm_auth::types::RealmAuthClient; use realm_server::types::{RealmChatClient, Room, User}; use realm_shared::stoken; use realm_shared::types::ErrorCode::*; use realm_shared::types::ErrorCode; use crate::types::{CServer, CUser}; use crate::ui::gui; /// We derive Deserialize/Serialize so we can persist app state on shutdown. #[derive(serde::Deserialize, serde::Serialize)] #[serde(default)] // if we add new fields, give them default values when deserializing old state pub struct RealmApp { #[serde(skip)] pub selected_serverid: String, #[serde(skip)] pub selected_roomid: String, #[serde(skip)] pub current_user: Option, pub saved_username: Option, pub saved_token: Option, pub saved_auth_address: Option, #[serde(skip)] pub active_servers: Option>, #[serde(skip)] pub value: f32, #[serde(skip)] pub login_window_open: bool, #[serde(skip)] pub login_window_username: String, #[serde(skip)] pub login_window_code: String, #[serde(skip)] pub login_window_server_domain: String, #[serde(skip)] pub login_window_server_port: String, #[serde(skip)] pub login_window_email: String, #[serde(skip)] pub login_ready_for_code_input: bool, #[serde(skip)] pub signup_window_open: bool, #[serde(skip)] pub server_window_open: bool, #[serde(skip)] pub server_window_domain: String, #[serde(skip)] pub server_window_port: String, #[serde(skip)] pub room_window_open: bool, #[serde(skip)] pub room_window_name: String, #[serde(skip)] pub room_window_admin_only_send: bool, #[serde(skip)] pub room_window_admin_only_view: bool, #[serde(skip)] pub info_window_open: bool, #[serde(skip)] pub login_start_channel: (Sender>, Receiver>), #[serde(skip)] pub login_ending_channel: (Sender>, Receiver>), #[serde(skip)] pub fetching_user_data_channel: (Sender>, Receiver>), #[serde(skip)] pub add_server_channel: (Sender>, Receiver>), #[serde(skip)] pub remove_server_channel: (Sender>, Receiver>), #[serde(skip)] pub join_server_channel: (Sender>, Receiver>), #[serde(skip)] pub leave_server_channel: (Sender>, Receiver>), #[serde(skip)] pub fetching_servers_channel: (Sender>, Receiver>), #[serde(skip)] pub add_room_channel: (Sender>, Receiver>), #[serde(skip)] pub delete_room_channel: (Sender>, Receiver>), #[serde(skip)] pub room_changes_channel: (Sender), ErrorCode>>, Receiver), ErrorCode>>) } impl Default for RealmApp { fn default() -> Self { Self { selected_serverid: String::new(), selected_roomid: String::new(), current_user: None, saved_username: None, saved_token: None, saved_auth_address: None, active_servers: None, value: 2.7, login_window_open: false, login_window_username: String::new(), login_window_code: String::new(), login_window_server_domain: String::new(), login_window_server_port: "5052".to_string(), login_start_channel: broadcast::channel(256), login_ending_channel: broadcast::channel(256), login_ready_for_code_input: false, login_window_email: String::new(), signup_window_open: false, server_window_open: false, server_window_domain: String::new(), server_window_port: "5051".to_string(), room_window_open: false, room_window_name: String::new(), room_window_admin_only_send: false, room_window_admin_only_view: false, info_window_open: false, fetching_user_data_channel: broadcast::channel(256), add_server_channel: broadcast::channel(256), remove_server_channel: broadcast::channel(256), join_server_channel: broadcast::channel(256), leave_server_channel: broadcast::channel(256), fetching_servers_channel: broadcast::channel(256), add_room_channel: broadcast::channel(256), delete_room_channel: broadcast::channel(256), room_changes_channel: broadcast::channel(256), } } } impl RealmApp { /// Called once before the first frame. pub fn new(cc: &eframe::CreationContext<'_>) -> Self { // This is also where you can customize the look and feel of egui using // `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`. // Load previous app state (if any). // Note that you must enable the `persistence` feature for this to work. // if let Some(storage) = cc.storage { // return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default(); // } Default::default() } } pub fn fetch_user_data(send_channel: Sender>, server_address: String, username: String, token: String) { let _handle = tokio::spawn(async move { let mut transport = tarpc::serde_transport::tcp::connect(&server_address, Json::default); transport.config_mut().max_frame_length(usize::MAX); let result = transport.await; let auth_connection = match result { Ok(connection) => connection, Err(_) => { send_channel.send(Err(UnableToConnectToServer)).unwrap(); return; } }; let client = RealmAuthClient::new(tarpc::client::Config::default(), auth_connection).spawn(); let result = client.get_all_data(context::current(), username, token.clone()).await; match result { Ok(r) => { if let Err(code) = r { send_channel.send(Err(code)).unwrap(); } else { let auth_user = r.unwrap(); send_channel.send(Ok(CUser { id: auth_user.id, auth_address: server_address, username: auth_user.username, email: auth_user.email, //avatar: auth_user.avatar, server_addresses: auth_user.servers.split('|').map(|s| s.to_string()).collect(), token, })).unwrap(); } } Err(_) => { send_channel.send(Err(RPCError)).unwrap(); } }; }); } pub fn fetch_server_data(channel: Sender>, addresses: Vec, token: String, username: String){ for server_address in addresses { let send_channel = channel.clone(); let token = token.clone(); let userid = username.clone(); let _handle = tokio::spawn(async move { let mut transport = tarpc::serde_transport::tcp::connect(&server_address, Json::default); transport.config_mut().max_frame_length(usize::MAX); let result = transport.await; let connection = match result { Ok(connection) => connection, Err(_) => { send_channel.send(Err(UnableToConnectToServer)).unwrap(); return; } }; let client = RealmChatClient::new(tarpc::client::Config::default(), connection).spawn(); let info = client.get_info(context::current()).await.unwrap(); let domain = server_address.split(':').collect::>()[0].to_string(); let port = server_address.split(':').collect::>()[1].to_string().parse::().unwrap(); let stoken = stoken(&token, &info.server_id, &domain, port); let is_admin = client.is_user_admin(context::current(), userid.clone()).await.unwrap(); let is_owner = client.is_user_owner(context::current(), userid.clone()).await.unwrap(); let rooms = client.get_rooms(context::current(), stoken.clone(), userid.clone()).await.unwrap().unwrap(); send_channel.send(Ok(CServer { tarpc_conn: client, server_id: info.server_id, domain, port, is_admin, is_owner, rooms, })).unwrap(); }); } } pub fn fetch_rooms_data(send_channel: Sender), ErrorCode>>, server: CServer, token: String, userid: String) { let _handle = tokio::spawn(async move { let result = server.tarpc_conn.get_rooms( context::current(), stoken(&token, &server.server_id, &server.domain, server.port), userid ).await; match result { Ok(r) => { if let Ok(rooms) = r { send_channel.send(Ok((server, rooms))).unwrap(); } else { send_channel.send(Err(r.unwrap_err())).unwrap(); } } Err(_) => { send_channel.send(Err(RPCError)).unwrap(); } } }); } impl eframe::App for RealmApp { /// Called each time the UI needs repainting, which may be many times per second. fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { // Put your widgets into a `SidePanel`, `TopBottomPanel`, `CentralPanel`, `Window` or `Area`. // For inspiration and more examples, go to https://emilk.github.io/egui // Init at launch and refresh our user data from the auth server if self.current_user.is_none() && self.saved_token.is_some() && self.saved_auth_address.is_some() && self.saved_username.is_some() { let send_channel = self.fetching_user_data_channel.0.clone(); let server_address = self.saved_auth_address.clone().unwrap(); let username = self.saved_username.clone().unwrap(); let token = self.saved_token.clone().unwrap(); fetch_user_data(send_channel, server_address, username, token); } // Fetching servers if self.active_servers.is_none() && self.current_user.is_some() { self.active_servers = Some(Vec::new()); fetch_server_data( self.fetching_servers_channel.0.clone(), self.current_user.as_ref().unwrap().server_addresses.clone(), self.current_user.as_ref().unwrap().token.clone(), self.current_user.as_ref().unwrap().username.clone() ); } // Starting the login flow while let Ok(result) = self.login_start_channel.1.try_recv() { match result { Ok(_) => self.login_ready_for_code_input = true, Err(e) => tracing::error!("Error in login/account creation flow: {:?}", e), } } // End of the login flow while let Ok(result) = self.login_ending_channel.1.try_recv() { match result { Ok(token) => { info!("Login successful! Token: {token}"); self.login_ready_for_code_input = false; self.login_window_open = false; self.signup_window_open = false; self.login_window_code.clear(); info!("Fetching user data..."); let send_channel = self.fetching_user_data_channel.0.clone(); let server_address = format!("{}:{}", self.login_window_server_domain, self.login_window_server_port); let port = self.login_window_server_port.clone().parse::().unwrap(); let username = format!("@{}:{}", self.login_window_username, self.login_window_server_domain); self.saved_token = Some(token.clone()); self.saved_username = Some(username.clone()); self.saved_auth_address = Some(server_address.clone()); fetch_user_data(send_channel, server_address, username, token); } Err(e) => tracing::error!("Error in login flow: {:?}", e), } } // Fetching user data while let Ok(result) = self.fetching_user_data_channel.1.try_recv() { match result { Ok(client_user) => { info!("Got data! User: {:?}", client_user); self.current_user.replace(client_user); } Err(e) => error!("Error fetching data: {:?}", e), } } // Adding a server (auth) while let Ok(result) = self.add_server_channel.1.try_recv() { match result { Ok(address) => { info!("New server added at: {:?}", address); self.server_window_open = false; let send_channel = self.join_server_channel.0.clone(); let auth_address = self.saved_auth_address.clone().unwrap(); let username = self.saved_username.clone().unwrap(); let token = self.saved_token.clone().unwrap(); let thread_username = username.clone(); let thread_token = token.clone(); let _handle = tokio::spawn(async move { let mut transport = tarpc::serde_transport::tcp::connect(&address, Json::default); transport.config_mut().max_frame_length(usize::MAX); let result = transport.await; let connection = match result { Ok(connection) => connection, Err(_) => { send_channel.clone().send(Err(UnableToConnectToServer)).unwrap(); return; } }; let client = RealmChatClient::new(tarpc::client::Config::default(), connection).spawn(); let domain = address.split(':').collect::>()[0].to_string(); let port = address.split(':').collect::>()[1].to_string().parse::().unwrap(); let info = client.get_info(context::current()).await.unwrap(); let result = client.join_server(context::current(), stoken(&thread_token, &info.server_id, &domain, port), thread_username).await; match result { Ok(r) => { info!("Joined server!"); match r { Ok(_) => { send_channel.send(Ok(())).unwrap(); }, Err(e) => { send_channel.send(Err(e)).unwrap(); }, } }, Err(_) => { error!("Error joining server"); send_channel.send(Err(RPCError)).unwrap(); }, } }); fetch_user_data(self.fetching_user_data_channel.0.clone(), auth_address, username, token); } Err(e) => error!("Error in adding a server: {:?}", e), } } // Joining a server while let Ok(result) = self.join_server_channel.1.try_recv() { match result { Ok(_) => { info!("Successfully joined a server"); fetch_server_data( self.fetching_servers_channel.0.clone(), self.current_user.as_ref().unwrap().server_addresses.clone(), self.current_user.as_ref().unwrap().token.clone(), self.current_user.as_ref().unwrap().username.clone() ); }, Err(code) => { error!("Error joining server: {:?}", code); } } } // Leaving a server while let Ok(result) = self.leave_server_channel.1.try_recv() { match result { Ok((serverid, domain, port)) => { info!("Successfully left a server"); self.active_servers.as_mut().unwrap().retain(|s| !s.server_id.eq(&serverid)); self.selected_serverid.clear(); self.selected_roomid.clear(); let send_channel = self.remove_server_channel.0.clone(); let auth_address = self.current_user.clone().unwrap().auth_address; let username = self.current_user.clone().unwrap().username; let token = self.current_user.clone().unwrap().token; let _handle = tokio::spawn(async move { let mut transport = tarpc::serde_transport::tcp::connect(&auth_address, Json::default); transport.config_mut().max_frame_length(usize::MAX); let result = transport.await; let connection = match result { Ok(connection) => connection, Err(_) => { send_channel.clone().send(Err(UnableToConnectToServer)).unwrap(); return; } }; let client = RealmAuthClient::new(tarpc::client::Config::default(), connection).spawn(); let result = client.remove_server(context::current(), username, token, domain, port).await; match result { Ok(r) => { send_channel.send(r).unwrap(); }, Err(_) => { send_channel.send(Err(RPCError)).unwrap(); }, } }); }, Err(code) => { error!("Error leaving server: {:?}", code); } } } // Removing a server (auth) while let Ok(result) = self.remove_server_channel.1.try_recv() { match result { Ok(_) => { let send_channel = self.fetching_user_data_channel.0.clone(); let server_address = self.saved_auth_address.clone().unwrap(); let username = self.saved_username.clone().unwrap(); let token = self.saved_token.clone().unwrap(); fetch_user_data(send_channel, server_address, username, token); info!("Successfully removed a server"); } Err(code) => { error!("Error removing server: {:?}", code); } } } // Fetching servers while let Ok(result) = self.fetching_servers_channel.1.try_recv() { match result { Ok(server) => { info!("Got server data! Server: {:?}", server); if let Some(active_servers) = &mut self.active_servers { active_servers.push(server); } } Err(e) => error!("Error fetching server data: {:?}", e), } } // Added Room while let Ok(result) = self.add_room_channel.1.try_recv() { match result { Ok(server) => { info!("Got room add! Fetching them..."); fetch_rooms_data( self.room_changes_channel.0.clone(), server, self.current_user.as_ref().unwrap().token.clone(), self.current_user.as_ref().unwrap().username.clone() ); self.room_window_open = false; } Err(e) => error!("Error adding room: {:?}", e), } } // Deleting a room while let Ok(result) = self.delete_room_channel.1.try_recv() { match result { Ok(server) => { info!("Got room delete! Fetching them..."); self.selected_roomid.clear(); fetch_rooms_data( self.room_changes_channel.0.clone(), server, self.current_user.as_ref().unwrap().token.clone(), self.current_user.as_ref().unwrap().username.clone() ); } Err(e) => error!("Error deleting room: {:?}", e), } } // Fetching rooms while let Ok(result) = self.room_changes_channel.1.try_recv() { match result { Ok(tuple) => { info!("Got room data for a server: {:?}", tuple); if let Some(servers) = &mut self.active_servers { for server in servers { if server.server_id.eq(&tuple.0.server_id) { server.rooms = tuple.1.clone(); } } } } Err(e) => error!("Error fetching room data: {:?}", e), } } // File -> Quit gui::top_panel(self, ctx); gui::servers(self, ctx); gui::rooms(self, ctx); gui::messages(self, ctx); gui::modals(self, ctx) } /// Called by the frame work to save state before shutdown. fn save(&mut self, storage: &mut dyn eframe::Storage) { eframe::set_value(storage, eframe::APP_KEY, self); } }