Frontend config and Loader component (#12)

* Add FrontendConfig and Loader

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* Merge more setup into the application loader

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* Rename config files

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* Remove blank line

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* Fix url

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* Make strings more specific

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* Restructure store use

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* split methods and actions

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* extract code

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* remove actions.ts

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* add reason and rename component

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* remove unused call

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* Use redux store in api

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* activate email in backend config

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* add new line

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* reduce code

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* Make error more specific

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* Use expectedResponseCode

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

* Update src/redux/backend-config/types.ts

Co-authored-by: Philip Molares <git@molar.es>

* Update src/components/application-loader/application-loader.tsx

Co-authored-by: Philip Molares <git@molar.es>

* Update src/components/application-loader/application-loader.tsx

Co-authored-by: Philip Molares <git@molar.es>

* Update src/components/application-loader/application-loader.tsx

Co-authored-by: Philip Molares <git@molar.es>

* Use fragment

Signed-off-by: Tilman Vatteroth <tilman.vatteroth@tu-dortmund.de>

Co-authored-by: Philip Molares <git@molar.es>
This commit is contained in:
mrdrogdrog 2020-05-15 23:10:12 +02:00 committed by GitHub
parent ef17c7acbb
commit a490e1240b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
36 changed files with 425 additions and 256 deletions

View file

@ -0,0 +1,80 @@
.loader {
.animation-pulse {
animation: pulse 2s ease-in-out infinite;
}
.animation-shake {
animation: shake 0.3s ease-in-out;
}
.icon {
width: 150px;
height: 150px;
}
height: 100vh;
width: 100vw;
&.middle, .middle {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}
.progress {
width: 50%;
}
@keyframes pulse {
0% {
transform: scale(1, 1);
filter: drop-shadow(0 0 0px black);
}
10% {
transform: scale(1.5, 1.5);
filter: drop-shadow(0 0 100px white);
}
100% {
transform: scale(1, 1);
filter: drop-shadow(0 0 0px black);
}
}
@keyframes shake {
0% {
transform: translate(1px, 1px) rotate(0deg);
}
10% {
transform: translate(-1px, -2px) rotate(-1deg);
}
20% {
transform: translate(-3px, 0px) rotate(1deg);
}
30% {
transform: translate(3px, 2px) rotate(0deg);
}
40% {
transform: translate(1px, -1px) rotate(1deg);
}
50% {
transform: translate(-1px, 2px) rotate(-1deg);
}
60% {
transform: translate(-3px, 1px) rotate(0deg);
}
70% {
transform: translate(3px, 1px) rotate(-1deg);
}
80% {
transform: translate(-1px, -1px) rotate(1deg);
}
90% {
transform: translate(1px, 2px) rotate(0deg);
}
100% {
transform: translate(1px, -2px) rotate(-1deg);
}
}
}

View file

@ -0,0 +1,41 @@
import React, {Fragment, useEffect, useState} from "react";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import "./application-loader.scss";
import {Alert} from "react-bootstrap";
interface ApplicationLoaderProps {
initTasks: Promise<any>[]
}
export const ApplicationLoader: React.FC<ApplicationLoaderProps> = ({children, initTasks}) => {
const [failed, setFailed] = useState<boolean>(false);
const [doneTasks, setDoneTasks] = useState<number>(0);
useEffect(() => {
setDoneTasks(0);
initTasks.map(task =>
task.then(() =>
setDoneTasks(prevDoneTasks => {
return prevDoneTasks + 1;
}))
.catch((reason) => {
setFailed(true);
console.error(reason);
})
)
}, [initTasks]);
return (<Fragment>{
doneTasks < initTasks.length || initTasks.length === 0 ? (
<div className="loader middle">
<div className="icon">
<FontAwesomeIcon icon="file-alt" size="6x"
className={failed ? "animation-shake" : "animation-pulse"}/>
</div>
{
failed ? <Alert variant={"danger"}>An error occured while loading the application!</Alert> : null
}
</div>
) : children
}</Fragment>);
}

View file

@ -1,44 +0,0 @@
import {useDispatch} from "react-redux";
import React from "react";
import {getConfig} from "../../api/config";
import {ApplicationConfigState} from "../../redux/application-config/types";
import {setApplicationConfig} from "../../redux/application-config/actions";
const InitializeConfigStateFromApi: React.FC = () => {
const dispatch = useDispatch();
getConfig()
.then((response) => {
if (response.status === 200) {
return (response.json() as Promise<ApplicationConfigState>);
}
})
.then(config => {
if (!config) {
return;
}
dispatch(setApplicationConfig({
allowAnonymous: config.allowAnonymous,
authProviders: {
facebook: config.authProviders.facebook,
github: config.authProviders.github,
twitter: config.authProviders.twitter,
gitlab: config.authProviders.gitlab,
dropbox: config.authProviders.dropbox,
ldap: config.authProviders.ldap,
google: config.authProviders.google,
saml: config.authProviders.saml,
oauth2: config.authProviders.oauth2,
email: config.authProviders.email
},
specialLinks: {
privacy: config.specialLinks.privacy,
termsOfUse: config.specialLinks.termsOfUse,
imprint: config.specialLinks.imprint,
}
}));
});
return null;
}
export { InitializeConfigStateFromApi }

View file

@ -1,35 +0,0 @@
import {useDispatch} from "react-redux";
import {getMe} from "../../api/user";
import {setUser} from "../../redux/user/actions";
import {LoginStatus, UserState} from "../../redux/user/types";
import React from "react";
import {Dispatch} from "redux";
export const getAndSetUser = (dispatch: Dispatch<any>) => {
getMe()
.then((me) => {
if (me.status === 200) {
return (me.json() as Promise<UserState>);
}
})
.then(user => {
if (!user) {
return;
}
dispatch(setUser({
status: LoginStatus.ok,
id: user.id,
name: user.name,
photo: user.photo,
}));
});
}
const InitializeUserStateFromApi: React.FC = () => {
const dispatch = useDispatch();
getAndSetUser(dispatch);
return null;
}
export { InitializeUserStateFromApi }

View file

@ -18,7 +18,7 @@ const PoweredByLinks: React.FC = () => {
}
]
const config = useSelector((state: ApplicationState) => state.applicationConfig);
const config = useSelector((state: ApplicationState) => state.backendConfig);
const specialLinks = Object.entries(config.specialLinks)
.filter(([_, value]) => value !== "")

View file

@ -9,20 +9,19 @@ import "./style/index.scss";
import {Login} from "../pages/login/login";
export const Landing: React.FC = () => {
return (
<Container>
<HeaderBar/>
<Switch>
<Route path="/history">
<History/>
</Route>
<Route path="/intro">
<Intro/>
</Route>
<Route path="/login">
<Login/>
</Route>
</Switch>
<Footer/>
</Container>);
return (<Container>
<HeaderBar/>
<Switch>
<Route path="/history">
<History/>
</Route>
<Route path="/intro">
<Intro/>
</Route>
<Route path="/login">
<Login/>
</Route>
</Switch>
<Footer/>
</Container>)
}

View file

@ -1,4 +1,4 @@
import React from 'react'
import React, {Fragment} from 'react'
import {Navbar} from 'react-bootstrap';
import {useSelector} from "react-redux";
import {ApplicationState} from "../../../../../redux";
@ -27,19 +27,19 @@ const HeaderBar: React.FC = () => {
</div>
<div className="d-inline-flex">
{user.status === LoginStatus.forbidden ?
<>
<Fragment>
<span className={"mr-1"}>
<NewGuestNoteButton/>
</span>
<SignInButton/>
</>
</Fragment>
:
<>
<Fragment>
<span className={"mr-1"}>
<NewUserNoteButton/>
</span>
<UserDropdown/>
</>
</Fragment>
}
</div>
</Navbar>

View file

@ -1,16 +1,15 @@
import {Dropdown} from "react-bootstrap";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import React from "react";
import {useDispatch, useSelector} from "react-redux";
import {useSelector} from "react-redux";
import {ApplicationState} from "../../../../../redux";
import {LinkContainer} from "react-router-bootstrap";
import {clearUser} from "../../../../../redux/user/actions";
import {clearUser} from "../../../../../redux/user/methods";
import "./user-dropdown.scss";
import {Trans} from "react-i18next";
export const UserDropdown: React.FC = () => {
const user = useSelector((state: ApplicationState) => state.user);
const dispatch = useDispatch()
return (
<Dropdown alignRight>
@ -43,7 +42,7 @@ export const UserDropdown: React.FC = () => {
</Dropdown.Item>
<Dropdown.Item
onClick={() => {
dispatch(clearUser());
clearUser();
}}>
<FontAwesomeIcon icon="sign-out-alt"/>&nbsp;
<Trans i18nKey="signOut"/>

View file

@ -1,13 +1,11 @@
import {Trans, useTranslation} from "react-i18next";
import {Button, Form, Alert} from "react-bootstrap";
import {Alert, Button, Form} from "react-bootstrap";
import React, {Fragment, useState} from "react";
import {postEmailLogin} from "../../../../../api/user";
import {useDispatch} from "react-redux";
import {getAndSetUser} from "../../../../initialize/initialize-user-state-from-api";
import {getAndSetUser} from "../../../../../utils/apiUtils";
const ViaEMail: React.FC = () => {
const {t} = useTranslation();
const dispatch = useDispatch();
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [error, setError] = useState(false);
@ -15,10 +13,11 @@ const ViaEMail: React.FC = () => {
postEmailLogin(email, password)
.then(loginJson => {
console.log(loginJson)
getAndSetUser(dispatch);
}).catch(_reason => {
getAndSetUser();
}).catch(_reason => {
setError(true);
})
}
)
event.preventDefault();
}
@ -55,8 +54,7 @@ const ViaEMail: React.FC = () => {
<Button
type="submit"
size="sm"
variant="primary"
>
variant="primary">
<Trans i18nKey="signIn"/>
</Button>
</Form>
@ -64,4 +62,4 @@ const ViaEMail: React.FC = () => {
);
}
export { ViaEMail }
export {ViaEMail}

View file

@ -9,7 +9,7 @@ import {ApplicationState} from "../../../../redux";
const Login: React.FC = () => {
useTranslation();
const authProviders = useSelector((state: ApplicationState) => state.applicationConfig.authProviders);
const authProviders = useSelector((state: ApplicationState) => state.backendConfig.authProviders);
const emailForm = authProviders.email ? <ViaEMail/> : null
const ldapForm = authProviders.ldap ? <ViaLdap/> : null