login.rs (11216B)
1 use std::include_str; 2 use std::thread::sleep; 3 use std::time::Duration; 4 5 use log::*; 6 use serde::{Deserialize, Serialize}; 7 use yew::prelude::*; 8 9 use tr::tr; 10 11 use crate::app::components::raw_html::RawHTML; 12 use crate::app::matrix::{MatrixAgent, Request, Response}; 13 use crate::errors::{Field, MatrixError}; 14 15 pub struct Login { 16 link: ComponentLink<Self>, 17 state: State, 18 matrix_agent: Box<dyn Bridge<MatrixAgent>>, 19 } 20 21 pub enum Msg { 22 NewMessage(Response), 23 SetHomeserver(String), 24 SetUsername(String), 25 SetPassword(String), 26 Login, 27 } 28 29 #[derive(Serialize, Deserialize, Default)] 30 pub struct State { 31 loading: bool, 32 homeserver: String, 33 username: String, 34 password: String, 35 error: Option<String>, 36 error_field: Option<Field>, 37 retries: u64, 38 } 39 40 impl Component for Login { 41 type Message = Msg; 42 type Properties = (); 43 44 fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self { 45 let matrix_callback = link.callback(Msg::NewMessage); 46 let matrix_agent = MatrixAgent::bridge(matrix_callback); 47 let state = State { 48 loading: false, 49 homeserver: "".to_string(), 50 username: "".to_string(), 51 password: "".to_string(), 52 error: None, 53 error_field: None, 54 retries: 0, 55 }; 56 Login { 57 link, 58 state, 59 matrix_agent, 60 } 61 } 62 63 fn update(&mut self, msg: Self::Message) -> ShouldRender { 64 match msg { 65 Msg::SetHomeserver(homeserver) => { 66 self.state.homeserver = homeserver.clone(); 67 self.matrix_agent.send(Request::SetHomeserver(homeserver)); 68 true 69 } 70 Msg::SetUsername(username) => { 71 self.state.username = username.clone(); 72 self.matrix_agent.send(Request::SetUsername(username)); 73 true 74 } 75 Msg::SetPassword(password) => { 76 self.state.password = password.clone(); 77 self.matrix_agent.send(Request::SetPassword(password)); 78 true 79 } 80 Msg::Login => { 81 // Reset Errors 82 self.state.error = None; 83 self.state.error_field = None; 84 85 // Start loading 86 self.matrix_agent.send(Request::Login); 87 self.state.loading = true; 88 true 89 } 90 Msg::NewMessage(Response::Error(error)) => { 91 match &error { 92 MatrixError::MissingFields(field) => { 93 self.state.loading = false; 94 self.state.error = Some(error.to_string()); 95 self.state.error_field = Some(field.clone()); 96 true 97 } 98 MatrixError::LoginTimeout => { 99 // If we had less than 10 tries try again 100 if self.state.retries < 10 { 101 self.state.retries += 1; 102 info!("Trying login again in 5 seconds"); 103 sleep(Duration::from_secs(5)); 104 self.link.send_message(Msg::Login); 105 false 106 } else { 107 self.state.loading = false; 108 self.state.error = Some("Login failed after 10 tries.".to_string()); 109 true 110 } 111 } 112 MatrixError::SDKError(e) => { 113 // TODO handle login error != timeout better 114 error!("SDK Error: {}", e); 115 false 116 } 117 _ => false, 118 } 119 } 120 Msg::NewMessage(_) => false, 121 } 122 } 123 124 fn change(&mut self, _props: Self::Properties) -> ShouldRender { 125 false 126 } 127 128 //noinspection RsTypeCheck 129 fn view(&self) -> Html { 130 let mut homeserver_classes = "login-input"; 131 let mut mxid_classes = "login-input"; 132 let mut password_classes = "login-input"; 133 if let Some(v) = self.state.error_field.as_ref() { 134 match v { 135 Field::Homeserver => homeserver_classes = "login-input uk-form-danger", 136 Field::MXID => mxid_classes = "login-input uk-form-danger", 137 Field::Password => password_classes = "login-input uk-form-danger", 138 } 139 } 140 141 if self.state.loading { 142 html! { 143 <div class="container"> 144 <div class="uk-position-center uk-padding sun-animation"> 145 <RawHTML inner_html=include_str!("../svgs/loading_animation.svg")/> 146 </div> 147 </div> 148 } 149 } else { 150 let error = match &self.state.error { 151 Some(v) => html! { 152 <h4 class="error"> 153 { 154 tr!( 155 // {0} is the Error that happened on login 156 // The error message of the Login page 157 "Error: {0}", 158 v 159 ) 160 } 161 </h4> 162 }, 163 None => html! {}, 164 }; 165 166 html! { 167 <> 168 <div class="login-page-bg"></div> 169 <div class="scrollable" style="width:100vw; padding:2px;"> 170 <div> 171 <div class="login-bg"> 172 <div class="daydream-title"><RawHTML inner_html=include_str!("../svgs/DaydreamLogo_v0_light.svg")/></div> 173 <div class="login-content"> 174 <h1 class="login-title"> 175 { 176 tr!( 177 // The Login Button of the Login page 178 "Login" 179 ) 180 } 181 </h1> 182 { error } 183 184 <form id="login_form" onsubmit=self.link.callback(|e: FocusEvent| { e.prevent_default(); Msg::Login })> 185 <div class="login-inline login-input-first"> 186 <span class="material-icons login-icons" id="ma-icon" style="font-size: 28px !important;">{"http"}</span> 187 <input 188 pattern="^https:\\/\\/.*$" 189 required=true 190 class=homeserver_classes 191 type="url" 192 id="homeserver" 193 name="homeserver" 194 placeholder= 195 { 196 tr!( 197 // The URL Field of the Login page 198 "Homeserver URL" 199 ) 200 } 201 value=&self.state.homeserver 202 oninput=self.link.callback(|e: InputData| Msg::SetHomeserver(e.value)) 203 /> 204 </div> 205 <div class="login-inline"> 206 <span class="material-icons login-icons" id="ma-icon">{"person"}</span> 207 <input 208 required=true 209 pattern="^(@[\x21-\x39\x3B-\x7E]+:.*|[\x21-\x39\x3B-\x7E]+)$" 210 class=mxid_classes 211 id="username" 212 name="username" 213 placeholder= 214 { 215 tr!( 216 // The Matrix ID Field of the Login page 217 "MXID" 218 ) 219 } 220 value=&self.state.username 221 oninput=self.link.callback(|e: InputData| Msg::SetUsername(e.value)) 222 /> 223 </div> 224 <div class="login-inline"> 225 <span class="material-icons login-icons" id="ma-icon">{"vpn_key"}</span> 226 <input 227 required=true 228 class=password_classes 229 type="password" 230 id="password" 231 name="password" 232 placeholder= 233 { 234 tr!( 235 // The Password Field of the Login page 236 "Password" 237 ) 238 } 239 value=&self.state.password 240 oninput=self.link.callback(|e: InputData| Msg::SetPassword(e.value)) 241 /> 242 </div> 243 244 <button class="login-button"> 245 { 246 tr!( 247 // The Login Button of the Login page 248 "Login" 249 ) 250 } 251 </button> 252 </form> 253 </div> 254 </div> 255 </div> 256 </div> 257 </> 258 } 259 } 260 } 261 }