235 lines
7.9 KiB
Rust
235 lines
7.9 KiB
Rust
use crate::input::{self, Input, Value};
|
|
use crate::widget::centerbox;
|
|
use service::authentication::{self, Email, Name, Password, RegisterData};
|
|
use service::{
|
|
Authenticated, AuthenticationContract,
|
|
authentication::{Error, LoginData, Result},
|
|
};
|
|
|
|
use iced::futures::lock::Mutex;
|
|
use iced::widget::{Space, button, checkbox, column, container, row, text};
|
|
use iced::{Length, Task, padding};
|
|
use std::sync::Arc;
|
|
|
|
pub struct Register<S> {
|
|
name: Input<Name>,
|
|
email: Input<Email>,
|
|
password: Input<Password>,
|
|
repeat: Input<String>,
|
|
show_password: bool,
|
|
|
|
state: State,
|
|
service: Arc<Mutex<S>>,
|
|
}
|
|
enum State {
|
|
None,
|
|
Success,
|
|
Requesting,
|
|
Error(String),
|
|
}
|
|
|
|
pub enum Event {
|
|
SwitchToLogin,
|
|
Task(Task<Message>),
|
|
Authenticated(Authenticated),
|
|
}
|
|
impl From<Task<Message>> for Event {
|
|
fn from(value: Task<Message>) -> Self {
|
|
Self::Task(value)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum Message {
|
|
NameChanged(String),
|
|
EmailChanged(String),
|
|
PasswordChanged(String),
|
|
RepeatChanged(String),
|
|
ShowPasswordToggled(bool),
|
|
|
|
EmailSubmitted,
|
|
NameSubmitted,
|
|
PasswordSubmitted,
|
|
RepeatSubmitted,
|
|
|
|
RegisterPressed,
|
|
LoginPressed,
|
|
|
|
RequestResult(Arc<Result<Authenticated>>),
|
|
}
|
|
|
|
impl<S: AuthenticationContract + 'static> Register<S> {
|
|
pub fn new(service: Arc<Mutex<S>>) -> Self {
|
|
Self {
|
|
name: Input::new("register_name"),
|
|
email: Input::new("register_email"),
|
|
password: Input::new("register_password"),
|
|
repeat: Input::new("register_repeat"),
|
|
show_password: false,
|
|
|
|
state: State::None,
|
|
service,
|
|
}
|
|
}
|
|
|
|
fn check_passwords(&mut self) {
|
|
if self.password.as_ref() == self.repeat.as_ref() {
|
|
self.repeat
|
|
.set_value(Value::Valid(self.repeat.as_ref().to_string()));
|
|
} else {
|
|
self.repeat.set_error(&"passwords are different");
|
|
}
|
|
}
|
|
|
|
pub fn update(&mut self, message: Message) -> Option<Event> {
|
|
match message {
|
|
Message::NameChanged(s) => self.name.update(s),
|
|
Message::EmailChanged(s) => self.email.update(s),
|
|
Message::PasswordChanged(s) => {
|
|
self.password.update(s);
|
|
self.check_passwords();
|
|
}
|
|
Message::RepeatChanged(s) => {
|
|
self.repeat.set_value(Value::Valid(s));
|
|
self.check_passwords();
|
|
}
|
|
Message::ShowPasswordToggled(b) => self.show_password = b,
|
|
|
|
Message::NameSubmitted if self.name.critical() => (),
|
|
Message::NameSubmitted => return Some(self.email.focus().into()),
|
|
Message::EmailSubmitted if self.email.critical() => (),
|
|
Message::EmailSubmitted => return Some(self.password.focus().into()),
|
|
Message::PasswordSubmitted if self.password.critical() => (),
|
|
Message::PasswordSubmitted => return Some(self.repeat.focus().into()),
|
|
Message::RegisterPressed | Message::RepeatSubmitted
|
|
if self.repeat.error().is_some() =>
|
|
{
|
|
return Some(self.repeat.focus().into());
|
|
}
|
|
Message::RegisterPressed | Message::RepeatSubmitted => {
|
|
let register_data = RegisterData {
|
|
name: match self.name.submit() {
|
|
Ok(x) => x,
|
|
Err(t) => return Some(t.into()),
|
|
},
|
|
email: match self.email.submit() {
|
|
Ok(x) => x,
|
|
Err(t) => return Some(t.into()),
|
|
},
|
|
password: match self.password.submit() {
|
|
Ok(x) => x,
|
|
Err(t) => return Some(t.into()),
|
|
},
|
|
};
|
|
|
|
self.state = State::Requesting;
|
|
let arc = self.service.clone();
|
|
|
|
return Some(
|
|
Task::perform(
|
|
async move {
|
|
let Some(mut service) = arc.try_lock() else {
|
|
return Err(Error::Other(
|
|
"other authentication request is being performed".into(),
|
|
));
|
|
};
|
|
service.register(register_data).await
|
|
},
|
|
|r| Message::RequestResult(Arc::new(r)),
|
|
)
|
|
.into(),
|
|
);
|
|
}
|
|
|
|
Message::LoginPressed => return Some(Event::SwitchToLogin),
|
|
Message::RequestResult(r) => match &*r {
|
|
Ok(a) => {
|
|
self.state = State::Success;
|
|
return Some(Event::Authenticated(a.clone()));
|
|
}
|
|
|
|
Err(e) => {
|
|
self.state = State::None;
|
|
match e {
|
|
Error::NameExists => self.name.set_warning(e),
|
|
Error::EmailExists => self.email.set_warning(e),
|
|
Error::IncorrectPassword => self.password.set_warning(e),
|
|
Error::InvalidPassword(_) => self.password.set_error(e),
|
|
|
|
_ => self.state = State::Error(e.to_string()),
|
|
}
|
|
}
|
|
},
|
|
}
|
|
|
|
None
|
|
}
|
|
|
|
pub fn view(&self) -> iced::Element<Message> {
|
|
centerbox(
|
|
column![
|
|
container(text(self.title()).size(20))
|
|
.center_x(Length::Fill)
|
|
.padding(padding::bottom(10)),
|
|
self.name
|
|
.view("Username")
|
|
.on_input(Message::NameChanged)
|
|
.on_submit(Message::NameSubmitted),
|
|
self.email
|
|
.view("Email")
|
|
.on_input(Message::EmailChanged)
|
|
.on_submit(Message::EmailSubmitted),
|
|
self.password
|
|
.view("Password")
|
|
.on_input(Message::PasswordChanged)
|
|
.on_submit(Message::PasswordSubmitted)
|
|
.secure(!self.show_password),
|
|
self.repeat
|
|
.view("Repeat Password")
|
|
.on_input(Message::RepeatChanged)
|
|
.on_submit(Message::RepeatSubmitted)
|
|
.secure(!self.show_password),
|
|
checkbox("Show password", self.show_password)
|
|
.on_toggle(Message::ShowPasswordToggled),
|
|
row![
|
|
button(text("Login").center().size(18))
|
|
.on_press(Message::LoginPressed)
|
|
.style(button::secondary)
|
|
.width(Length::FillPortion(3))
|
|
.padding(10),
|
|
Space::with_width(Length::FillPortion(2)),
|
|
button(text("Register").center().size(18))
|
|
.on_press(Message::RegisterPressed)
|
|
.style(button::primary)
|
|
.width(Length::FillPortion(3))
|
|
.padding(10),
|
|
]
|
|
.padding(padding::top(15)),
|
|
]
|
|
.width(Length::Fixed(250.))
|
|
.spacing(20),
|
|
)
|
|
}
|
|
|
|
pub fn title(&self) -> String {
|
|
let errors = [
|
|
self.name.error(),
|
|
self.email.error(),
|
|
self.password.error(),
|
|
self.repeat.error(),
|
|
self.name.warning(),
|
|
self.email.warning(),
|
|
self.password.warning(),
|
|
self.repeat.warning(),
|
|
];
|
|
let error = errors.into_iter().flatten().next();
|
|
|
|
match &self.state {
|
|
State::None => error.map_or_else(|| "Register".into(), Into::into),
|
|
State::Success => "Success".into(),
|
|
State::Requesting => "Requesting...".into(),
|
|
State::Error(e) => e.into(),
|
|
}
|
|
}
|
|
}
|