Agent oriented logged in state
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
860ae7ed5e
commit
ff2b884d42
|
@ -26,7 +26,7 @@ module Backend
|
||||||
class Mutation < GraphQL::BaseMutation
|
class Mutation < GraphQL::BaseMutation
|
||||||
@[GraphQL::Field]
|
@[GraphQL::Field]
|
||||||
# Logs in as *username* with credential *password*
|
# Logs in as *username* with credential *password*
|
||||||
def login(username : String, password : String) : LoginPayload
|
def login(username : String, password : String) : LoginPayload?
|
||||||
raise Errors::Authentication.new if username.empty? || password.empty?
|
raise Errors::Authentication.new if username.empty? || password.empty?
|
||||||
|
|
||||||
user = Db::User.query.find { var(:username) == username }
|
user = Db::User.query.find { var(:username) == username }
|
||||||
|
|
|
@ -9,11 +9,11 @@ wasm-logger = "0.2.0"
|
||||||
log = "0.4.6"
|
log = "0.4.6"
|
||||||
yew-router = "0.16.0"
|
yew-router = "0.16.0"
|
||||||
wee_alloc = "0.4.5"
|
wee_alloc = "0.4.5"
|
||||||
graphql_client = { version = "0.11.0", features = ["reqwest"] }
|
graphql_client = { git = "https://github.com/graphql-rust/graphql-client.git", branch = "main", features = ["reqwest"] }
|
||||||
reqwest = "0.11.12"
|
reqwest = "0.11.12"
|
||||||
wasm-bindgen-futures = "0.4.33"
|
|
||||||
serde = "1.0.147"
|
serde = "1.0.147"
|
||||||
web-sys = { version = "0.3.60", features = ["Window", "Location"] }
|
web-sys = { version = "0.3.60", features = ["Window", "Location"] }
|
||||||
wasm-cookies = "0.1.0"
|
wasm-cookies = "0.1.0"
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
const_format = "0.2.30"
|
const_format = "0.2.30"
|
||||||
|
yew-agent = "0.1.0"
|
||||||
|
|
|
@ -21,6 +21,11 @@ COPY ./graphql ./graphql
|
||||||
COPY ./src ./src
|
COPY ./src ./src
|
||||||
RUN trunk build --release
|
RUN trunk build --release
|
||||||
|
|
||||||
|
FROM tdewolff/minify as public
|
||||||
|
WORKDIR /usr/src/public
|
||||||
|
COPY --from=builder /usr/src/frontend/dist .
|
||||||
|
RUN minify . -r -o .
|
||||||
|
|
||||||
FROM nginx:alpine as runner
|
FROM nginx:alpine as runner
|
||||||
COPY ./nginx.conf /etc/nginx/nginx.conf
|
COPY ./nginx.conf /etc/nginx/nginx.conf
|
||||||
COPY --from=builder /usr/src/frontend/dist /var/www/html
|
COPY --from=public /usr/src/public /var/www/html
|
||||||
|
|
5
frontend/graphql/mutations/login.graphql
Normal file
5
frontend/graphql/mutations/login.graphql
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
mutation Login($username: String!, $password: String!) {
|
||||||
|
login(username: $username, password: $password) {
|
||||||
|
token
|
||||||
|
}
|
||||||
|
}
|
10
frontend/graphql/queries/me.graphql
Normal file
10
frontend/graphql/queries/me.graphql
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
query Me {
|
||||||
|
me {
|
||||||
|
firstName
|
||||||
|
role
|
||||||
|
student {
|
||||||
|
vote
|
||||||
|
}
|
||||||
|
teacher
|
||||||
|
}
|
||||||
|
}
|
|
@ -78,7 +78,7 @@ type Mutation {
|
||||||
createUser(checkLdap: Boolean! = true, input: UserCreateInput!): User!
|
createUser(checkLdap: Boolean! = true, input: UserCreateInput!): User!
|
||||||
createVote(input: VoteCreateInput!): Vote!
|
createVote(input: VoteCreateInput!): Vote!
|
||||||
deleteUser(id: Int!): Int!
|
deleteUser(id: Int!): Int!
|
||||||
login(password: String!, username: String!): LoginPayload!
|
login(password: String!, username: String!): LoginPayload
|
||||||
}
|
}
|
||||||
|
|
||||||
input UserCreateInput {
|
input UserCreateInput {
|
||||||
|
|
47
frontend/src/agents/logged_in.rs
Normal file
47
frontend/src/agents/logged_in.rs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::collections::HashSet;
|
||||||
|
use yew_agent::{Agent, AgentLink, Context, HandlerId};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub enum Request {
|
||||||
|
LoggedIn(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EventBus {
|
||||||
|
link: AgentLink<Self>,
|
||||||
|
subscribers: HashSet<HandlerId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Agent for EventBus {
|
||||||
|
type Reach = Context<Self>;
|
||||||
|
type Message = ();
|
||||||
|
type Input = Request;
|
||||||
|
type Output = bool;
|
||||||
|
|
||||||
|
fn create(link: AgentLink<Self>) -> Self {
|
||||||
|
Self {
|
||||||
|
link,
|
||||||
|
subscribers: HashSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _msg: Self::Message) {}
|
||||||
|
|
||||||
|
fn handle_input(&mut self, msg: Self::Input, _id: HandlerId) {
|
||||||
|
match msg {
|
||||||
|
Request::LoggedIn(x) => {
|
||||||
|
for sub in self.subscribers.iter() {
|
||||||
|
self.link.respond(*sub, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn connected(&mut self, id: HandlerId) {
|
||||||
|
self.subscribers.insert(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn disconnected(&mut self, id: HandlerId) {
|
||||||
|
self.subscribers.remove(&id);
|
||||||
|
}
|
||||||
|
}
|
1
frontend/src/agents/mod.rs
Normal file
1
frontend/src/agents/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod logged_in;
|
54
frontend/src/components/logged_in_handler.rs
Normal file
54
frontend/src/components/logged_in_handler.rs
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
use yew::prelude::*;
|
||||||
|
use yew_agent::{Bridge, Bridged};
|
||||||
|
use yew_router::prelude::*;
|
||||||
|
|
||||||
|
use crate::agents;
|
||||||
|
use crate::cookie_names;
|
||||||
|
use crate::routes;
|
||||||
|
|
||||||
|
pub enum Msg {
|
||||||
|
LoggedIn(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, PartialEq)]
|
||||||
|
pub struct LoggedInHandlerProps {
|
||||||
|
pub logged_in: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LoggedInHandler {
|
||||||
|
logged_in: bool,
|
||||||
|
_logged_in_producer: Box<dyn Bridge<agents::logged_in::EventBus>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for LoggedInHandler {
|
||||||
|
type Message = Msg;
|
||||||
|
type Properties = LoggedInHandlerProps;
|
||||||
|
|
||||||
|
fn create(ctx: &Context<Self>) -> Self {
|
||||||
|
Self {
|
||||||
|
logged_in: ctx.props().logged_in,
|
||||||
|
_logged_in_producer: agents::logged_in::EventBus::bridge(
|
||||||
|
ctx.link().callback(Msg::LoggedIn),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||||
|
match msg {
|
||||||
|
Msg::LoggedIn(x) => {
|
||||||
|
if self.logged_in && !x {
|
||||||
|
log::info!("Global logout!");
|
||||||
|
wasm_cookies::delete(cookie_names::TOKEN);
|
||||||
|
ctx.link().history().unwrap().push(routes::Route::Login);
|
||||||
|
}
|
||||||
|
self.logged_in = x;
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||||
|
html! {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
pub mod logged_in_handler;
|
|
@ -1,6 +1,7 @@
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub mod mutations;
|
||||||
pub mod queries;
|
pub mod queries;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
|
9
frontend/src/graphql/mutations/login.rs
Normal file
9
frontend/src/graphql/mutations/login.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use graphql_client::GraphQLQuery;
|
||||||
|
|
||||||
|
#[derive(GraphQLQuery)]
|
||||||
|
#[graphql(
|
||||||
|
schema_path = "graphql/schema.graphql",
|
||||||
|
query_path = "graphql/mutations/login.graphql",
|
||||||
|
response_derives = "Debug,Serialize,Deserialize"
|
||||||
|
)]
|
||||||
|
pub struct Login;
|
1
frontend/src/graphql/mutations/mod.rs
Normal file
1
frontend/src/graphql/mutations/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod login;
|
9
frontend/src/graphql/queries/me.rs
Normal file
9
frontend/src/graphql/queries/me.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
use graphql_client::GraphQLQuery;
|
||||||
|
|
||||||
|
#[derive(GraphQLQuery)]
|
||||||
|
#[graphql(
|
||||||
|
schema_path = "graphql/schema.graphql",
|
||||||
|
query_path = "graphql/queries/me.graphql",
|
||||||
|
response_derives = "Debug"
|
||||||
|
)]
|
||||||
|
pub struct Me;
|
2
frontend/src/graphql/queries/mod.rs
Normal file
2
frontend/src/graphql/queries/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pub mod me;
|
||||||
|
pub mod ok;
|
|
@ -6,4 +6,4 @@ use graphql_client::GraphQLQuery;
|
||||||
query_path = "graphql/queries/ok.graphql",
|
query_path = "graphql/queries/ok.graphql",
|
||||||
response_derives = "Debug"
|
response_derives = "Debug"
|
||||||
)]
|
)]
|
||||||
pub struct Ok;
|
pub struct Ok;
|
58
frontend/src/layouts/logged_in.rs
Normal file
58
frontend/src/layouts/logged_in.rs
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
use yew::prelude::*;
|
||||||
|
use yew_agent::{Bridge, Bridged};
|
||||||
|
use yew_router::prelude::*;
|
||||||
|
use yew_router::scope_ext::RouterScopeExt;
|
||||||
|
|
||||||
|
use crate::agents;
|
||||||
|
use crate::routes;
|
||||||
|
|
||||||
|
pub enum Msg {
|
||||||
|
LoggedIn(bool),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, PartialEq)]
|
||||||
|
pub struct LoggedInProps {
|
||||||
|
#[prop_or_default]
|
||||||
|
pub children: Children,
|
||||||
|
pub logged_in: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LoggedIn {
|
||||||
|
logged_in: bool,
|
||||||
|
_logged_in_producer: Box<dyn Bridge<agents::logged_in::EventBus>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for LoggedIn {
|
||||||
|
type Message = Msg;
|
||||||
|
type Properties = LoggedInProps;
|
||||||
|
|
||||||
|
fn create(ctx: &Context<Self>) -> Self {
|
||||||
|
Self {
|
||||||
|
logged_in: ctx.props().logged_in,
|
||||||
|
_logged_in_producer: agents::logged_in::EventBus::bridge(
|
||||||
|
ctx.link().callback(Msg::LoggedIn),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||||
|
match msg {
|
||||||
|
Msg::LoggedIn(x) => {
|
||||||
|
let prev = self.logged_in;
|
||||||
|
self.logged_in = x;
|
||||||
|
prev != self.logged_in
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||||
|
if !self.logged_in {
|
||||||
|
log::info!("Viewing logged in required site while not logged in!");
|
||||||
|
ctx.link().history().unwrap().push(routes::Route::Login);
|
||||||
|
}
|
||||||
|
|
||||||
|
html! {
|
||||||
|
{ for ctx.props().children.iter() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,56 +1,79 @@
|
||||||
use graphql_client::reqwest::post_graphql;
|
|
||||||
use wasm_bindgen_futures;
|
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
use yew_agent::{Dispatched, Dispatcher};
|
||||||
use yew_router::prelude::*;
|
use yew_router::prelude::*;
|
||||||
|
|
||||||
use crate::cookie_names;
|
use crate::agents;
|
||||||
use crate::graphql;
|
use crate::components;
|
||||||
use crate::routes;
|
use crate::routes;
|
||||||
|
|
||||||
|
pub enum Msg {
|
||||||
|
LogOut,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
#[derive(Properties, PartialEq)]
|
||||||
pub struct MainProps {
|
pub struct MainProps {
|
||||||
|
pub logged_in: bool,
|
||||||
#[prop_or_default]
|
#[prop_or_default]
|
||||||
pub children: Children,
|
pub children: Children,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[function_component(Main)]
|
pub struct Main {
|
||||||
pub fn main(props: &MainProps) -> Html {
|
logged_in: bool,
|
||||||
let client = reqwest::Client::new();
|
logged_in_event_bus: Dispatcher<agents::logged_in::EventBus>,
|
||||||
wasm_bindgen_futures::spawn_local(async move {
|
}
|
||||||
let response = post_graphql::<graphql::queries::Ok, _>(
|
|
||||||
&client,
|
|
||||||
graphql::URL.as_str(),
|
|
||||||
graphql::queries::ok::Variables {},
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
log::debug!("{:?}", response);
|
|
||||||
log::debug!("{:?}", wasm_cookies::get(cookie_names::TOKEN));
|
|
||||||
});
|
|
||||||
|
|
||||||
let history = use_history().unwrap();
|
impl Component for Main {
|
||||||
let loginout_onclick = Callback::once(move |_| history.push(routes::Route::Login));
|
type Message = Msg;
|
||||||
|
type Properties = MainProps;
|
||||||
|
|
||||||
html! {
|
fn create(ctx: &Context<Self>) -> Self {
|
||||||
<>
|
Self {
|
||||||
<nav>
|
logged_in: ctx.props().logged_in,
|
||||||
<ul>
|
logged_in_event_bus: agents::logged_in::EventBus::dispatcher(),
|
||||||
<li>
|
}
|
||||||
<Link<routes::Route> to={routes::Route::Home}>
|
}
|
||||||
<button>{ "Home" }</button>
|
|
||||||
</Link<routes::Route>>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<button onclick={loginout_onclick}>{ "Login/Logout" }</button>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<hr />
|
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||||
|
match msg {
|
||||||
|
Msg::LogOut => {
|
||||||
|
self.logged_in_event_bus
|
||||||
|
.send(agents::logged_in::Request::LoggedIn(false));
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
<main>
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||||
{ for props.children.iter() }
|
let logout_onclick = ctx.link().callback(move |_| Msg::LogOut);
|
||||||
</main>
|
|
||||||
</>
|
html! {
|
||||||
|
<>
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<Link<routes::Route> to={routes::Route::Home}>
|
||||||
|
<button>{ "Home" }</button>
|
||||||
|
</Link<routes::Route>>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
if self.logged_in {
|
||||||
|
<button onclick={logout_onclick}>{ "Logout" }</button>
|
||||||
|
} else {
|
||||||
|
<Link<routes::Route> to={routes::Route::Login}>
|
||||||
|
<button>{ "Login" }</button>
|
||||||
|
</Link<routes::Route>>
|
||||||
|
}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<components::logged_in_handler::LoggedInHandler logged_in={self.logged_in} />
|
||||||
|
<main>
|
||||||
|
{ for ctx.props().children.iter() }
|
||||||
|
</main>
|
||||||
|
</>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
pub mod logged_in;
|
||||||
pub mod main;
|
pub mod main;
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub mod agents;
|
||||||
pub mod components;
|
pub mod components;
|
||||||
pub mod cookie_names;
|
pub mod cookie_names;
|
||||||
pub mod graphql;
|
pub mod graphql;
|
||||||
|
|
|
@ -1,17 +1,27 @@
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yew_router::prelude::*;
|
use yew_router::prelude::*;
|
||||||
|
|
||||||
|
use frontend::routes;
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||||
|
|
||||||
use frontend::routes;
|
pub struct App;
|
||||||
|
|
||||||
#[function_component(App)]
|
impl Component for App {
|
||||||
fn app() -> Html {
|
type Message = ();
|
||||||
html! {
|
type Properties = ();
|
||||||
<BrowserRouter>
|
|
||||||
<Switch<routes::Route> render={Switch::render(routes::switch)} />
|
fn create(_ctx: &Context<Self>) -> Self {
|
||||||
</BrowserRouter>
|
Self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||||
|
html! {
|
||||||
|
<BrowserRouter>
|
||||||
|
<Switch<routes::Route> render={Switch::render(routes::switch)} />
|
||||||
|
</BrowserRouter>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,100 @@
|
||||||
|
use graphql_client::reqwest::post_graphql;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
|
||||||
#[function_component(Home)]
|
use crate::graphql;
|
||||||
pub fn home() -> Html {
|
|
||||||
html! {
|
pub enum Msg {
|
||||||
<h1>{ "HOME!" }</h1>
|
DoneFetching {
|
||||||
|
errors: Option<Vec<String>>,
|
||||||
|
data: graphql::queries::me::me::ResponseData,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Properties, PartialEq)]
|
||||||
|
pub struct HomeProps {
|
||||||
|
pub token: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
Fetching,
|
||||||
|
Done,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Home {
|
||||||
|
state: State,
|
||||||
|
errors: Option<Vec<String>>,
|
||||||
|
data: Option<graphql::queries::me::me::ResponseData>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Home {
|
||||||
|
type Message = Msg;
|
||||||
|
type Properties = HomeProps;
|
||||||
|
|
||||||
|
fn create(_ctx: &Context<Self>) -> Self {
|
||||||
|
Self {
|
||||||
|
state: State::Fetching,
|
||||||
|
errors: None,
|
||||||
|
data: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, _ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||||
|
match msg {
|
||||||
|
Msg::DoneFetching { errors, data } => {
|
||||||
|
self.state = State::Done;
|
||||||
|
self.errors = errors;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||||
|
match self.state {
|
||||||
|
State::Fetching => {
|
||||||
|
let mut headers = reqwest::header::HeaderMap::new();
|
||||||
|
headers.insert(
|
||||||
|
"Authorization",
|
||||||
|
reqwest::header::HeaderValue::from_str(&format!(
|
||||||
|
"Bearer {}",
|
||||||
|
ctx.props().token
|
||||||
|
))
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
ctx.link().send_future(async move {
|
||||||
|
let response = post_graphql::<graphql::queries::me::Me, _>(
|
||||||
|
&reqwest::Client::builder()
|
||||||
|
.default_headers(headers)
|
||||||
|
.build()
|
||||||
|
.unwrap(),
|
||||||
|
graphql::URL.as_str(),
|
||||||
|
graphql::queries::me::me::Variables,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
log::debug!("{:?}", response.data);
|
||||||
|
|
||||||
|
Msg::DoneFetching {
|
||||||
|
errors: response
|
||||||
|
.errors
|
||||||
|
.map(|x| x.iter().map(|e| e.message.to_owned()).collect()),
|
||||||
|
data: response.data.unwrap(),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
html! {
|
||||||
|
<p>{ "Fetching..." }</p>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
State::Done => html! {
|
||||||
|
if let Some(errors) = &self.errors {
|
||||||
|
<p>{ "Errors:" }</p>
|
||||||
|
<div>
|
||||||
|
{ for errors.iter().map(|e| html! { <p style="color: red;">{ e }</p> }) }
|
||||||
|
</div>
|
||||||
|
} else {
|
||||||
|
<h1>{ "Hey, !" }</h1>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,123 @@
|
||||||
|
use graphql_client::reqwest::post_graphql;
|
||||||
|
use web_sys::HtmlInputElement;
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
|
use yew_router::prelude::*;
|
||||||
|
|
||||||
#[function_component(Login)]
|
use crate::cookie_names;
|
||||||
pub fn login() -> Html {
|
use crate::graphql;
|
||||||
html! {
|
use crate::routes;
|
||||||
<h1>{ "LOGIN!" }</h1>
|
|
||||||
|
pub enum Msg {
|
||||||
|
Submit,
|
||||||
|
Login(Option<Vec<String>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Login {
|
||||||
|
username: NodeRef,
|
||||||
|
password: NodeRef,
|
||||||
|
errors: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Component for Login {
|
||||||
|
type Message = Msg;
|
||||||
|
type Properties = ();
|
||||||
|
|
||||||
|
fn create(_ctx: &Context<Self>) -> Self {
|
||||||
|
Self {
|
||||||
|
username: NodeRef::default(),
|
||||||
|
password: NodeRef::default(),
|
||||||
|
errors: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> bool {
|
||||||
|
match msg {
|
||||||
|
Msg::Submit => {
|
||||||
|
if let (Some(username), Some(password)) = (
|
||||||
|
self.username.cast::<HtmlInputElement>().map(|x| x.value()),
|
||||||
|
self.password.cast::<HtmlInputElement>().map(|x| x.value()),
|
||||||
|
) {
|
||||||
|
if !username.is_empty() && !password.is_empty() {
|
||||||
|
ctx.link().send_future(async {
|
||||||
|
let response = post_graphql::<graphql::mutations::login::Login, _>(
|
||||||
|
&reqwest::Client::new(),
|
||||||
|
graphql::URL.as_str(),
|
||||||
|
graphql::mutations::login::login::Variables { username, password },
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if response.errors.is_some() {
|
||||||
|
wasm_cookies::delete(cookie_names::TOKEN);
|
||||||
|
} else {
|
||||||
|
wasm_cookies::set(
|
||||||
|
cookie_names::TOKEN,
|
||||||
|
&response.data.unwrap().login.unwrap().token,
|
||||||
|
&wasm_cookies::CookieOptions::default(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Msg::Login(
|
||||||
|
response
|
||||||
|
.errors
|
||||||
|
.map(|x| x.iter().map(|e| e.message.to_owned()).collect()),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Msg::Login(errors) => {
|
||||||
|
self.errors = errors;
|
||||||
|
if self.errors.is_none() {
|
||||||
|
ctx.link().history().unwrap().push(routes::Route::Home);
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn view(&self, ctx: &Context<Self>) -> Html {
|
||||||
|
let link = ctx.link();
|
||||||
|
|
||||||
|
let onsubmit = link.callback(|e: FocusEvent| {
|
||||||
|
e.prevent_default();
|
||||||
|
|
||||||
|
Msg::Submit
|
||||||
|
});
|
||||||
|
|
||||||
|
html! {
|
||||||
|
<>
|
||||||
|
<form {onsubmit}>
|
||||||
|
<label for="username">
|
||||||
|
{ "Benutzername:" }
|
||||||
|
<br />
|
||||||
|
<input ref={self.username.clone()} type="text" id="username" />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<label for="password">
|
||||||
|
{ "Kennwort:" }
|
||||||
|
<br />
|
||||||
|
<input ref={self.password.clone()} type="password" id="password" />
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<br /><br />
|
||||||
|
|
||||||
|
<button type="submit">{ "Login" }</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
if let Some(errors) = &self.errors {
|
||||||
|
<div>
|
||||||
|
{ for errors.iter().map(|e| html! { <p style="color: red;">{ e }</p> }) }
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</>
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
use yew::prelude::*;
|
use yew::prelude::*;
|
||||||
use yew_router::prelude::*;
|
use yew_router::prelude::*;
|
||||||
|
|
||||||
|
use crate::cookie_names;
|
||||||
use crate::layouts;
|
use crate::layouts;
|
||||||
|
|
||||||
pub mod home;
|
pub mod home;
|
||||||
pub mod login;
|
pub mod login;
|
||||||
pub mod not_found;
|
pub mod not_found;
|
||||||
|
|
||||||
#[derive(Clone, Routable, PartialEq)]
|
#[derive(Clone, Routable, PartialEq, Eq)]
|
||||||
pub enum Route {
|
pub enum Route {
|
||||||
#[at("/")]
|
#[at("/")]
|
||||||
Home,
|
Home,
|
||||||
|
@ -19,11 +20,40 @@ pub enum Route {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn switch(routes: &Route) -> Html {
|
pub fn switch(routes: &Route) -> Html {
|
||||||
match routes {
|
let token = {
|
||||||
Route::Home => html! { <layouts::main::Main><home::Home /></layouts::main::Main> },
|
let tmp = wasm_cookies::get(cookie_names::TOKEN);
|
||||||
Route::Login => html! { <layouts::main::Main><login::Login /></layouts::main::Main> },
|
if let Some(x) = tmp {
|
||||||
Route::NotFound => {
|
if let Ok(y) = x {
|
||||||
html! { <layouts::main::Main><not_found::NotFound /></layouts::main::Main> }
|
Some(y)
|
||||||
|
} else {
|
||||||
|
wasm_cookies::delete(cookie_names::TOKEN);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
let logged_in = token.is_some();
|
||||||
|
|
||||||
|
match routes {
|
||||||
|
Route::Home => {
|
||||||
|
html! {
|
||||||
|
<layouts::logged_in::LoggedIn {logged_in}>
|
||||||
|
<layouts::main::Main {logged_in}>
|
||||||
|
<home::Home token={token.unwrap()} />
|
||||||
|
</layouts::main::Main>
|
||||||
|
</layouts::logged_in::LoggedIn>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Route::Login => html! {
|
||||||
|
<layouts::main::Main {logged_in}>
|
||||||
|
<login::Login />
|
||||||
|
</layouts::main::Main>
|
||||||
|
},
|
||||||
|
Route::NotFound => html! {
|
||||||
|
<layouts::main::Main {logged_in}>
|
||||||
|
<not_found::NotFound />
|
||||||
|
</layouts::main::Main>
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue