/* * This file is part of laurelin/client * Copyright (C) 2023 Jonni Liljamo * * 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); fn start_register_call( mut commands: Commands, cfg_dev: Res, inputs: Res, ) { 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, mut cfg_user: ResMut, mut save_event_writer: EventWriter, mut console: EventWriter, ) { 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::(); commands.entity(entity).despawn_recursive(); } }