Archived
2
0
This repository has been archived on 2025-04-21. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
repo/src/authentication/register.rs
T
2025-02-12 14:33:42 +02:00

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(),
}
}
}