/*
* This file is part of sdbclient
* Copyright (C) 2022 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
*/
use bevy::{
prelude::*,
tasks::{AsyncComputeTaskPool, Task},
};
use bevy_console::PrintConsoleLine;
use iyes_loopless::prelude::*;
use futures_lite::future;
use crate::{
api::{
self,
user::{ResponseRegister, ResponseToken},
},
cfg::{CfgDev, CfgUser},
plugins::config::{SaveEvent, SaveEventValue},
};
use super::MenuState;
pub mod ui;
pub struct AccountRegisterPlugin;
impl Plugin for AccountRegisterPlugin {
fn build(&self, app: &mut App) {
app.add_loopless_state(RegisterState::None)
// UI system
.insert_resource(ui::InputsUserRegister::new())
.add_system_set(
ConditionSet::new()
.run_in_state(RegisterState::Input)
.with_system(ui::account_register_ui)
.into(),
)
// Register system, as in calling the API
.add_enter_system(RegisterState::Registering, start_register_call)
.add_system_set(
ConditionSet::new()
.run_in_state(RegisterState::Registering)
.with_system(handle_register_call)
.into(),
);
}
}
/// Register State
#[derive(Clone, Eq, PartialEq, Debug, Hash)]
pub enum RegisterState {
None,
Input,
Registering,
}
struct RegisterCallResponse {
register: ResponseRegister,
token: ResponseToken,
}
#[derive(Component)]
struct RegisterCall(Task<RegisterCallResponse>);
fn start_register_call(
mut commands: Commands,
cfg_dev: Res<CfgDev>,
inputs: Res<ui::InputsUserRegister>,
) {
let api_address = cfg_dev.api_server.clone();
let i = inputs.clone();
let thread_pool = AsyncComputeTaskPool::get();
let task = thread_pool.spawn(async move {
let register_response = api::user::register(
api_address.clone(),
i.username.clone(),
i.email.clone(),
i.password.clone(),
);
// TODO: This is bad, if the above fails, this will too. Or maybe that doesn't matter?
let token_response =
api::user::token(api_address.clone(), i.email.clone(), i.password.clone());
RegisterCallResponse {
register: register_response,
token: token_response,
}
});
commands.spawn(RegisterCall(task));
}
fn handle_register_call(
mut commands: Commands,
mut register_call_tasks: Query<(Entity, &mut RegisterCall)>,
mut inputs: ResMut<ui::InputsUserRegister>,
mut cfg_user: ResMut<CfgUser>,
mut save_event_writer: EventWriter<SaveEvent>,
mut console: EventWriter<PrintConsoleLine>,
) {
let (entity, mut task) = register_call_tasks.single_mut();
if let Some(register_call_response) = future::block_on(future::poll_once(&mut task.0)) {
match register_call_response.register {
ResponseRegister::Valid(register_res) => {
console.send(PrintConsoleLine::new(
format!("Registered user: {}", register_res.username,).into(),
));
match register_call_response.token {
ResponseToken::Valid(token_res) => {
*cfg_user = CfgUser {
logged_in: true,
user_token: token_res.token,
id: register_res.id,
username: register_res.username,
email: inputs.email.clone(),
};
save_event_writer.send(SaveEvent {
value: SaveEventValue::User(cfg_user.into_inner().clone()),
});
commands.insert_resource(NextState(RegisterState::None));
commands.insert_resource(NextState(MenuState::AccountLoggedIn));
}
ResponseToken::Error(error) => {
// TODO: Handle? Is it possible to even get here without the server shitting itself between the register and token calls?
// And if the server does indeed shit itself between those calls, the user can just login normally, so 🤷♀️
console.send(PrintConsoleLine::new(format!(
"Something went wrong with getting the user token after registering, got error: '{}'", error
).into()));
commands.insert_resource(NextState(RegisterState::None));
commands.insert_resource(NextState(MenuState::AccountLoggedOut));
}
}
}
ResponseRegister::Error(error) => {
inputs.error = error.error.description;
commands.insert_resource(NextState(RegisterState::Input));
}
}
// Remove the task, since it's done now
commands.entity(entity).remove::<RegisterCall>();
commands.entity(entity).despawn_recursive();
}
}