use crate::{
api::Response,
route::RouterTarget,
service::{
cookie::CookieService,
uikit::{NotificationStatus, UIkitService},
},
string::{
AUTHENTICATION_ERROR, INPUT_PASSWORD, INPUT_USERNAME, REQUEST_ERROR, RESPONSE_ERROR,
TEXT_LOGIN,
},
SESSION_COOKIE,
};
use log::{error, info, warn};
use webapp::{
protocol::{model::Session, request::LoginCredentials, response::Login},
API_URL_LOGIN_CREDENTIALS,
};
use yew::{format::Cbor, html, prelude::*, services::fetch::FetchTask};
use yew_router::{self, RouterAgent};
pub struct LoginComponent {
component_link: ComponentLink<LoginComponent>,
cookie_service: CookieService,
fetch_task: Option<FetchTask>,
inputs_disabled: bool,
login_button_disabled: bool,
password: String,
router_agent: Box<Bridge<RouterAgent<()>>>,
uikit_service: UIkitService,
username: String,
}
pub enum Message {
Fetch(Response<Login>),
Ignore,
LoginRequest,
UpdatePassword(String),
UpdateUsername(String),
}
impl Component for LoginComponent {
type Message = Message;
type Properties = ();
fn create(_: Self::Properties, mut link: ComponentLink<Self>) -> Self {
Self {
cookie_service: CookieService::new(),
fetch_task: None,
inputs_disabled: false,
login_button_disabled: true,
password: String::new(),
router_agent: RouterAgent::bridge(link.send_back(|_| Message::Ignore)),
component_link: link,
uikit_service: UIkitService::new(),
username: String::new(),
}
}
fn change(&mut self, _: Self::Properties) -> ShouldRender {
true
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Message::LoginRequest => {
self.fetch_task = fetch! {
LoginCredentials {
username: self.username.to_owned(),
password: self.password.to_owned(),
} => API_URL_LOGIN_CREDENTIALS,
self.component_link, Message::Fetch,
|| {
self.login_button_disabled = true;
self.inputs_disabled = true;
},
|| {
error!("Unable to create credentials login request");
self.uikit_service
.notify(REQUEST_ERROR, &NotificationStatus::Danger);
}
};
}
Message::UpdateUsername(new_username) => {
self.username = new_username;
self.update_button_state();
}
Message::UpdatePassword(new_password) => {
self.password = new_password;
self.update_button_state();
}
Message::Fetch(response) => {
let (meta, Cbor(body)) = response.into_parts();
if meta.status.is_success() {
match body {
Ok(Login(Session { token })) => {
info!("Credential based login succeed");
self.cookie_service.set(SESSION_COOKIE, &token);
self.router_agent.send(yew_router::Request::ChangeRoute(
RouterTarget::Content.into(),
));
}
_ => {
warn!("Got wrong credentials login response");
self.uikit_service
.notify(RESPONSE_ERROR, &NotificationStatus::Danger);
}
}
} else {
warn!("Credentials login failed with status: {}", meta.status);
self.uikit_service
.notify(AUTHENTICATION_ERROR, &NotificationStatus::Warning);
self.login_button_disabled = false;
self.inputs_disabled = false;
}
self.fetch_task = None;
}
Message::Ignore => {}
}
true
}
}
impl LoginComponent {
fn update_button_state(&mut self) {
self.login_button_disabled = self.username.is_empty() || self.password.is_empty();
}
}
impl Renderable<LoginComponent> for LoginComponent {
fn view(&self) -> Html<Self> {
html! {
<div class="uk-card uk-card-default uk-card-body uk-width-1-3@s uk-position-center",>
<h1 class="uk-card-title",>{TEXT_LOGIN}</h1>
<form>
<fieldset class="uk-fieldset",>
<input class="uk-input uk-margin",
placeholder=INPUT_USERNAME,
disabled=self.inputs_disabled,
value=&self.username,
oninput=|e| Message::UpdateUsername(e.value), />
<input class="uk-input uk-margin-bottom",
type="password",
placeholder=INPUT_PASSWORD,
disabled=self.inputs_disabled,
value=&self.password,
oninput=|e| Message::UpdatePassword(e.value), />
<button class="uk-button uk-button-primary",
type="submit",
disabled=self.login_button_disabled,
onclick=|_| Message::LoginRequest,>{TEXT_LOGIN}</button>
</fieldset>
</form>
</div>
}
}
}