//! The login prompt use iced::{ widget::{button::State as ButtonState, text_input::State as InputState}, Button, Column, Command, Container, Element, Length, Space, Text, TextInput, Toggler, }; use self::Message::*; use crate::{matrix, style}; /// Data for the login prompt #[derive(Debug, Default)] pub struct Login { /// The username user: String, /// The password password: String, /// Whether the password should be visible. show_password: bool, /// The homeserver URL. homeserver: String, /// Whether we're waiting for a response to a login attempt waiting: bool, /// The text of an error message error: Option, /// Widget state state: State, } /// Login prompt widget state #[derive(Debug, Default)] pub struct State { /// State for the login input user: InputState, /// State from the password input password: InputState, /// State for the homeserver input homeserver: InputState, /// State for the login button login: ButtonState, } /// Notification to change the state for the login view. #[derive(Debug, Clone)] pub enum Message { /// The login input changed. InputUser(String), /// The password input changed. InputPassword(String), /// The homerserver input changed. InputHomeserver(String), /// The "show password" toggle has been switched. TogglePassword(bool), /// Triggers login. Login, /// A login attempt failed. LoginFailed(String), /// Login completed. LoggedIn(matrix_sdk::Client), /// Hide the error message. ResetError, } impl Login { /// Update state pub fn update(&mut self, message: Message) -> Command { match message { InputUser(input) => self.user = input, InputPassword(input) => self.password = input, InputHomeserver(input) => self.homeserver = input, TogglePassword(toggle) => self.show_password = toggle, Login => { self.waiting = true; let homeserver = self.homeserver.clone(); let user = self.user.clone(); let password = self.password.clone(); let command = async move { let client = match matrix::login(&homeserver, &user, &password).await { Ok(client) => client, Err(e) => return LoginFailed(e.to_string()), }; LoggedIn(client) }; return Command::perform(command, super::Message::from); } LoginFailed(error) => { self.waiting = false; self.error = Some(error); } LoggedIn(_) => (), ResetError => self.error = None, }; Command::none() } /// Generate widgets for this view pub fn view(&mut self) -> Element { let error_message: iced::Element<_> = match self.error { Some(ref error) => Container::new(Text::new(error.clone())) .center_x() .width(Length::Fill) .padding(5) .style(style::container::Error) .into(), None => Space::new(0.into(), 0.into()).into(), }; let user_input = TextInput::new(&mut self.state.user, "alice", &self.user, |i| InputUser(i).into()) .padding(5); let mut password_input = TextInput::new(&mut self.state.password, "verysecret", &self.password, |i| { InputPassword(i).into() }) .padding(5); if !self.show_password { password_input = password_input.password(); } let password_toggle = Toggler::new(self.show_password, String::from("show password"), |b| { TogglePassword(b).into() }) .text_size(15); let homeserver_input = TextInput::new( &mut self.state.homeserver, "https://matrix.org", &self.homeserver, |i| InputHomeserver(i).into(), ) .padding(5); let login_button = Button::new( &mut self.state.login, Text::new("Log in") .horizontal_alignment(iced::alignment::Horizontal::Center) .width(Length::Fill), ) .on_press(Message::Login.into()) .width(Length::Fill); let mut column = Column::new() .width(500.into()) .push(error_message) .push(Text::new("User name")) .push(user_input) .push(Space::with_height(10.into())) .push(Text::new("Password")) .push(password_input) .push(password_toggle) .push(Space::with_height(5.into())) .push(Text::new("Homeserver address")) .push(homeserver_input) .push(login_button); if self.waiting { column = column.push(Text::new("Logging in")); } Container::new(column).center_x().center_y().width(Length::Fill).height(Length::Fill).into() } } impl From for super::Message { fn from(message: Message) -> Self { super::Message::Login(message) } }