daydream

A small matrix web client written in rust
git clone git://archive.git.mtrnord.blog/daydream-mx/daydream.git
Log | Files | Refs | README | LICENSE

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 }