Add auth global state and login button
All checks were successful
Build Crate / build (push) Successful in 1m42s
All checks were successful
Build Crate / build (push) Successful in 1m42s
This commit is contained in:
@@ -1,4 +1,7 @@
|
|||||||
|
use crate::templates::global_state::{AppStateRx, LoginState};
|
||||||
|
use perseus::prelude::*;
|
||||||
use sycamore::prelude::*;
|
use sycamore::prelude::*;
|
||||||
|
use web_sys::Event;
|
||||||
|
|
||||||
#[derive(Prop)]
|
#[derive(Prop)]
|
||||||
pub struct LayoutProps<'a, G: Html> {
|
pub struct LayoutProps<'a, G: Html> {
|
||||||
@@ -6,6 +9,8 @@ pub struct LayoutProps<'a, G: Html> {
|
|||||||
pub children: Children<'a, G>,
|
pub children: Children<'a, G>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Using elements from here: https://flowbite.com/docs/components/buttons/
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Layout<'a, G: Html>(
|
pub fn Layout<'a, G: Html>(
|
||||||
cx: Scope<'a>,
|
cx: Scope<'a>,
|
||||||
@@ -15,33 +20,84 @@ pub fn Layout<'a, G: Html>(
|
|||||||
}: LayoutProps<'a, G>,
|
}: LayoutProps<'a, G>,
|
||||||
) -> View<G> {
|
) -> View<G> {
|
||||||
let children = children.call(cx);
|
let children = children.call(cx);
|
||||||
|
// Get global state to get authentication info
|
||||||
|
let global_state = Reactor::<G>::from_cx(cx).get_global_state::<AppStateRx>(cx);
|
||||||
|
|
||||||
|
// Check if the client is authenticated or not
|
||||||
|
#[cfg(client)]
|
||||||
|
global_state.auth.detect_state();
|
||||||
|
|
||||||
|
// TODO -> move into function
|
||||||
|
let handle_log_in = move |_event: Event| {
|
||||||
|
#[cfg(client)]
|
||||||
|
{
|
||||||
|
spawn_local_scoped(cx, async move {
|
||||||
|
let global_state = Reactor::<G>::from_cx(cx).get_global_state::<AppStateRx>(cx);
|
||||||
|
global_state.auth.state.set(LoginState::Authenticated);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
view! { cx,
|
view! { cx,
|
||||||
|
// Main page header
|
||||||
header {
|
header {
|
||||||
div (class = "flex items-center justify-between") {
|
div (class = "flex items-center justify-between w-full md:text-center") {
|
||||||
div (class = "w-full text-gray-700 md:text-center text-2xl font-semibold") {
|
div(class = "flex-1") {}
|
||||||
|
div(class = "text-gray-700 text-2xl font-semibold py-2") {
|
||||||
"Pool Elo - Season 1"
|
"Pool Elo - Season 1"
|
||||||
}
|
}
|
||||||
}
|
div(class = "flex-1 py-2") {(
|
||||||
|
match *global_state.auth.state.get() {
|
||||||
div (class = "container mx-auto px-6 py-3") {
|
LoginState::NotAuthenticated => {
|
||||||
nav (class = "sm:flex sm:justify-center sm:items-center mt-4 hidden") {
|
view! { cx,
|
||||||
div (class = "flex flex-col sm:flex-row"){
|
button(class = "text-gray-900 bg-white border border-gray-300 focus:outline-none hover:bg-gray-100 focus:ring-4 focus:ring-gray-100 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700 dark:hover:border-gray-600 dark:focus:ring-gray-700") {
|
||||||
a(href = "add-game-form",
|
"Register"
|
||||||
class = "mt-3 text-gray-600 hover:underline sm:mx-3 sm:mt-0"
|
}
|
||||||
) { "Add game result" }
|
button(on:click = handle_log_in,class = "text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 me-2 mb-2 dark:bg-blue-600 dark:hover:bg-blue-700 focus:outline-none dark:focus:ring-blue-800") {
|
||||||
a(href = "one-v-one-board",
|
"Login"
|
||||||
class = "mt-3 text-gray-600 hover:underline sm:mx-3 sm:mt-0"
|
}
|
||||||
) { "1v1 Leaderboard" }
|
}
|
||||||
a(href = "overall-board",
|
}
|
||||||
class = "mt-3 text-gray-600 hover:underline sm:mx-3 sm:mt-0"
|
LoginState::Authenticated => {
|
||||||
) { "Overall Leaderboard" }
|
view! { cx,
|
||||||
}
|
div {
|
||||||
|
"Hello {username}!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Will only appear for a few seconds
|
||||||
|
LoginState::Unknown => {
|
||||||
|
view! { cx,
|
||||||
|
div (class = "px-5 py-2.5 me-2 mb-2"){
|
||||||
|
"Loading..."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main(style = "my-8") {
|
main(style = "my-8") {
|
||||||
|
// Body header
|
||||||
|
div {
|
||||||
|
div (class = "container mx-auto px-6 py-3") {
|
||||||
|
nav (class = "sm:flex sm:justify-center sm:items-center mt-4 hidden") {
|
||||||
|
div (class = "flex flex-col sm:flex-row"){
|
||||||
|
a(href = "add-game-form",
|
||||||
|
class = "mt-3 text-gray-600 hover:underline sm:mx-3 sm:mt-0"
|
||||||
|
) { "Add game result" }
|
||||||
|
a(href = "one-v-one-board",
|
||||||
|
class = "mt-3 text-gray-600 hover:underline sm:mx-3 sm:mt-0"
|
||||||
|
) { "1v1 Leaderboard" }
|
||||||
|
a(href = "overall-board",
|
||||||
|
class = "mt-3 text-gray-600 hover:underline sm:mx-3 sm:mt-0"
|
||||||
|
) { "Overall Leaderboard" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Actual body
|
||||||
div(class = "container mx-auto px-6") {
|
div(class = "container mx-auto px-6") {
|
||||||
div(class = "md:flex mt-8 md:-mx-4") {
|
div(class = "md:flex mt-8 md:-mx-4") {
|
||||||
div(class = "rounded-md overflow-hidden bg-cover bg-center") {
|
div(class = "rounded-md overflow-hidden bg-cover bg-center") {
|
||||||
|
|||||||
@@ -11,25 +11,54 @@ cfg_if::cfg_if! {
|
|||||||
|
|
||||||
#[derive(Serialize, Deserialize, ReactiveState, Clone)]
|
#[derive(Serialize, Deserialize, ReactiveState, Clone)]
|
||||||
#[rx(alias = "AppStateRx")]
|
#[rx(alias = "AppStateRx")]
|
||||||
pub struct AppState {}
|
pub struct AppState {
|
||||||
|
#[rx(nested)]
|
||||||
pub fn get_global_state_creator() -> GlobalStateCreator {
|
pub auth: AuthData,
|
||||||
GlobalStateCreator::new()
|
|
||||||
.build_state_fn(get_build_state)
|
|
||||||
.request_state_fn(get_request_state)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[engine_only_fn]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
fn get_state() -> AppState {
|
pub enum LoginState {
|
||||||
AppState {}
|
Authenticated,
|
||||||
|
NotAuthenticated,
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, ReactiveState, Clone)]
|
||||||
|
#[rx(alias = "AuthDataRx")]
|
||||||
|
pub struct AuthData {
|
||||||
|
pub state: LoginState,
|
||||||
|
pub username: Option<String>,
|
||||||
|
pub claims: Claims,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, ReactiveState, Clone)]
|
||||||
|
#[rx(alias = "ClaimsRx")]
|
||||||
|
pub struct Claims {}
|
||||||
|
|
||||||
|
pub fn get_global_state_creator() -> GlobalStateCreator {
|
||||||
|
GlobalStateCreator::new().build_state_fn(get_build_state)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[engine_only_fn]
|
#[engine_only_fn]
|
||||||
pub async fn get_build_state() -> AppState {
|
pub async fn get_build_state() -> AppState {
|
||||||
get_state()
|
AppState {
|
||||||
|
auth: AuthData {
|
||||||
|
state: LoginState::Unknown,
|
||||||
|
username: None,
|
||||||
|
claims: Claims {},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[engine_only_fn]
|
// Client only code to check if they're authenticated
|
||||||
pub async fn get_request_state(_req: Request) -> AppState {
|
#[cfg(client)]
|
||||||
get_state()
|
impl AuthDataRx {
|
||||||
|
pub fn detect_state(&self) {
|
||||||
|
// If the user is in a known state, return
|
||||||
|
if let LoginState::Authenticated | LoginState::NotAuthenticated = *self.state.get() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// TODO -> Get state from storage
|
||||||
|
self.state.set(LoginState::NotAuthenticated);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user