Login Implementation
This commit is contained in:
2
AobaClient/Cargo.lock
generated
2
AobaClient/Cargo.lock
generated
@@ -37,12 +37,14 @@ name = "aoba-client"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dioxus",
|
||||
"js-sys",
|
||||
"prost",
|
||||
"serde",
|
||||
"serde_repr",
|
||||
"tonic",
|
||||
"tonic-build",
|
||||
"tonic-web-wasm-client",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -16,6 +16,8 @@ tonic = { version = "*", default-features = false, features = [
|
||||
] }
|
||||
prost = "0.13"
|
||||
tonic-web-wasm-client = "0.7"
|
||||
js-sys = { version = "0.3.77" }
|
||||
web-sys = { version = "0.3.77", features = ["Storage", "Window"] }
|
||||
|
||||
[build-dependencies]
|
||||
tonic-build = { version = "*", default-features = false, features = ["prost"] }
|
||||
|
||||
@@ -2,3 +2,8 @@
|
||||
display: grid;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#main {
|
||||
#main:has(#content) {
|
||||
display: grid;
|
||||
grid-template-columns: $navBarSize 1fr;
|
||||
grid-template-areas: "Nav Content";
|
||||
@@ -65,3 +65,40 @@ body {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#main:has(#centralModal) {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
height: 100dvh;
|
||||
width: 100dvw;
|
||||
}
|
||||
|
||||
#centralModal {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.notif {
|
||||
background-color: red;
|
||||
display: grid;
|
||||
grid-template-columns: 50px 1fr;
|
||||
height: 50px;
|
||||
border-radius: 20px;
|
||||
border-bottom-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
|
||||
.icon {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 10px;
|
||||
align-self: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ pub fn Input(props: InputProps) -> Element {
|
||||
input {
|
||||
type : props.r#type.unwrap_or("text".into()),
|
||||
value: props.value,
|
||||
oninput: move |e| if let Some(mut s) = props.value { s.set(e.value()); },
|
||||
name: props.name,
|
||||
placeholder:ph,
|
||||
required: props.required
|
||||
|
||||
52
AobaClient/src/components/icons.rs
Normal file
52
AobaClient/src/components/icons.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[component]
|
||||
pub fn Info() -> Element {
|
||||
rsx! {
|
||||
svg {
|
||||
class: "size-6",
|
||||
fill: "currentColor",
|
||||
view_box: "0 0 24 24",
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
path {
|
||||
clip_rule: "evenodd",
|
||||
d: "M2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm8.706-1.442c1.146-.573 2.437.463 2.126 1.706l-.709 2.836.042-.02a.75.75 0 0 1 .67 1.34l-.04.022c-1.147.573-2.438-.463-2.127-1.706l.71-2.836-.042.02a.75.75 0 1 1-.671-1.34l.041-.022ZM12 9a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z",
|
||||
fill_rule: "evenodd",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Warn() -> Element {
|
||||
rsx! {
|
||||
svg {
|
||||
class: "size-6",
|
||||
fill: "currentColor",
|
||||
view_box: "0 0 24 24",
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
path {
|
||||
clip_rule: "evenodd",
|
||||
d: "M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z",
|
||||
fill_rule: "evenodd",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Error() -> Element {
|
||||
rsx! {
|
||||
svg {
|
||||
class: "size-6",
|
||||
fill: "currentColor",
|
||||
view_box: "0 0 24 24",
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
path {
|
||||
clip_rule: "evenodd",
|
||||
d: "M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003ZM12 8.25a.75.75 0 0 1 .75.75v3.75a.75.75 0 0 1-1.5 0V9a.75.75 0 0 1 .75-.75Zm0 8.25a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Z",
|
||||
fill_rule: "evenodd",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,11 @@ pub mod basic;
|
||||
mod media_grid;
|
||||
mod media_item;
|
||||
mod navbar;
|
||||
mod notif;
|
||||
mod search;
|
||||
pub use media_grid::*;
|
||||
pub use media_item::*;
|
||||
pub use navbar::*;
|
||||
pub use notif::*;
|
||||
pub use search::*;
|
||||
mod icons;
|
||||
|
||||
44
AobaClient/src/components/notif.rs
Normal file
44
AobaClient/src/components/notif.rs
Normal file
@@ -0,0 +1,44 @@
|
||||
use dioxus::{html::u::icon, prelude::*};
|
||||
|
||||
use crate::components::icons;
|
||||
|
||||
#[derive(PartialEq, Clone, Props)]
|
||||
pub struct NotifProps {
|
||||
r#type: Option<NotifType>,
|
||||
message: String,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Clone)]
|
||||
pub enum NotifType {
|
||||
Notice,
|
||||
Error,
|
||||
Warning,
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Notif(props: NotifProps) -> Element {
|
||||
let t = props.r#type.unwrap_or(NotifType::Notice);
|
||||
let type_class = match t {
|
||||
NotifType::Notice => "notice",
|
||||
NotifType::Error => "error",
|
||||
NotifType::Warning => "warning",
|
||||
};
|
||||
let m = props.message;
|
||||
rsx! {
|
||||
div{
|
||||
class: "notif {type_class}",
|
||||
div{
|
||||
class: "icon",
|
||||
match t {
|
||||
NotifType::Notice => icons::Error(),
|
||||
NotifType::Error => icons::Error(),
|
||||
NotifType::Warning => icons::Warn(),
|
||||
}
|
||||
}
|
||||
div{
|
||||
class: "message",
|
||||
"{m}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,35 @@
|
||||
use dioxus::signals::Signal;
|
||||
use dioxus::signals::{Signal, Writable};
|
||||
use web_sys::window;
|
||||
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct AuthContext {
|
||||
pub jwt: Signal<Option<String>>,
|
||||
}
|
||||
|
||||
impl AuthContext {
|
||||
pub fn set_token(&mut self, token: String) {
|
||||
self.jwt.set(Some(token.clone()));
|
||||
let local_storage = window().unwrap().local_storage().unwrap().unwrap();
|
||||
_ = local_storage.set_item("token", token.as_str());
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
println!("new");
|
||||
let local_storage = window().unwrap().local_storage().unwrap().unwrap();
|
||||
match local_storage.get_item("token") {
|
||||
Ok(value) => {
|
||||
if let Some(jwt) = value {
|
||||
println!("jwt");
|
||||
return AuthContext {
|
||||
jwt: Signal::new(Some(jwt)),
|
||||
};
|
||||
}
|
||||
return AuthContext::default();
|
||||
}
|
||||
Err(_) => {
|
||||
println!("err");
|
||||
AuthContext::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::route::Route;
|
||||
|
||||
#[component]
|
||||
pub fn BasicLayout() -> Element {
|
||||
rsx! {
|
||||
Outlet::<Route> {}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
use dioxus::prelude::*;
|
||||
|
||||
use crate::{components::Navbar, contexts::AuthContext, layouts::BasicLayout, views::Login, Route};
|
||||
use crate::{components::Navbar, contexts::AuthContext, views::Login, Route};
|
||||
|
||||
#[component]
|
||||
pub fn MainLayout() -> Element {
|
||||
let auth_context = use_context::<AuthContext>();
|
||||
|
||||
// if auth_context.jwt.cloned().is_none() {
|
||||
// return rsx! { Login { } };
|
||||
// }
|
||||
if auth_context.jwt.cloned().is_none() {
|
||||
return rsx! { Login { } };
|
||||
}
|
||||
|
||||
return rsx! {
|
||||
Navbar {}
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
mod basic_layout;
|
||||
mod main_layout;
|
||||
pub use basic_layout::*;
|
||||
pub use main_layout::*;
|
||||
|
||||
@@ -24,7 +24,7 @@ fn main() {
|
||||
|
||||
#[component]
|
||||
fn App() -> Element {
|
||||
let _auth_state = use_context_provider(|| AuthContext::default());
|
||||
let _auth_state = use_context_provider(|| AuthContext::new());
|
||||
rsx! {
|
||||
document::Link { rel: "icon", href: FAVICON }
|
||||
document::Link { rel: "preconnect", href: "https://fonts.googleapis.com" }
|
||||
|
||||
@@ -2,7 +2,7 @@ use dioxus::prelude::*;
|
||||
use tonic::IntoRequest;
|
||||
|
||||
use crate::{
|
||||
components::basic::{Button, Input},
|
||||
components::{basic::Input, Notif, NotifType},
|
||||
contexts::AuthContext,
|
||||
rpc::{aoba::Credentials, get_auth_rpc_client},
|
||||
};
|
||||
@@ -11,9 +11,16 @@ use crate::{
|
||||
pub fn Login() -> Element {
|
||||
let username = use_signal(|| "".to_string());
|
||||
let password = use_signal(|| "".to_string());
|
||||
let mut error: Signal<Option<String>> = use_signal(|| None);
|
||||
let mut auth_context = use_context::<AuthContext>();
|
||||
|
||||
let onclick = move |_| {
|
||||
let login = move |e: Event<MouseData>| {
|
||||
e.prevent_default();
|
||||
if username.cloned().is_empty() || password.cloned().is_empty() {
|
||||
error.set(Some("Username and Password are required".into()));
|
||||
return;
|
||||
}
|
||||
|
||||
spawn(async move {
|
||||
let mut auth = get_auth_rpc_client();
|
||||
let result = auth
|
||||
@@ -31,8 +38,9 @@ pub fn Login() -> Element {
|
||||
crate::rpc::aoba::login_response::Result::Jwt(jwt) => {
|
||||
auth_context.jwt.set(Some(jwt.token));
|
||||
}
|
||||
crate::rpc::aoba::login_response::Result::Error(_login_error) => {
|
||||
crate::rpc::aoba::login_response::Result::Error(login_error) => {
|
||||
auth_context.jwt.set(None);
|
||||
error.set(Some(login_error.message));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -46,12 +54,15 @@ pub fn Login() -> Element {
|
||||
rsx! {
|
||||
div{
|
||||
id: "centralModal",
|
||||
if let Some(err) = error.cloned() {
|
||||
Notif{ type: NotifType::Error, message: err}
|
||||
}
|
||||
form{
|
||||
Input { type : "text", name: "username", label: "Username", value: username, required: true },
|
||||
Input { type : "password", name: "password", label: "Password", value: password, required: true },
|
||||
Button {
|
||||
text: "Login!",
|
||||
onclick: onclick
|
||||
button {
|
||||
onclick: login,
|
||||
"Login!",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user