Compare commits

...

10 Commits

Author SHA1 Message Date
Sven Czarnian
c500ddbe00 fix style issue 2022-11-05 02:29:26 +01:00
Sven Czarnian
8c6d6ae5e2 resolve the TODOs 2022-11-05 02:28:16 +01:00
Sven Czarnian
03919f7b97 check if the login was automatically triggered 2022-11-05 02:28:04 +01:00
Sven Czarnian
67f56c823e extend the session manager and rename the bearer functions 2022-11-05 02:19:37 +01:00
Sven Czarnian
041c4d0820 reorganize imports 2022-11-05 02:15:04 +01:00
Sven Czarnian
bf862a52a7 handle the active theme and use MenuItem for the tree 2022-11-05 02:14:49 +01:00
Sven Czarnian
425655ad3c export the theme provider 2022-11-05 02:14:05 +01:00
Sven Czarnian
e2a1dc6001 allow toggles and storage of active theme 2022-11-05 02:13:55 +01:00
Sven Czarnian
d2df32d392 remove unused import 2022-11-05 01:44:37 +01:00
Sven Czarnian
4824d831e3 remove duplicated information 2022-11-05 01:44:30 +01:00
11 changed files with 93 additions and 49 deletions

View File

@@ -1,10 +1,9 @@
import React from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { Auth, Login, Logout, NavBar, Overview } from './components';
import { AuthProvider, TimeProvider } from './contexts';
import './App.css';
import { AuthProvider, ThemeProvider, TimeProvider } from './contexts';
import { Airport } from './components/airport';
import { ThemeProvider } from './contexts/themecontext';
import './App.css';
const App: React.FC = () => (
<>

View File

@@ -0,0 +1,19 @@
import React, { useState } from 'react';
import { TabView, TabPanel } from 'primereact/tabview';
export const Airport: React.FC = () => {
const [activeIndex, setActiveIndex] = useState<number>(0);
return (
<>
<TabView activeIndex={activeIndex} onTabChange={(event) => setActiveIndex(event.index)} className='p-3'>
<TabPanel header='Create airport'>
CREATE AIRPORT
</TabPanel>
<TabPanel header='Manage airport'>
MANAGE AIRPORT
</TabPanel>
</TabView>
</>
);
}

View File

@@ -1,17 +1,29 @@
import React from 'react';
import { useSearchParams } from 'react-router-dom';
import React, { useContext } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { AuthContext } from '../contexts';
import { Session } from '../services';
export const Auth: React.FC = () => {
const { reloadAuth } = useContext(AuthContext);
const [searchParams] = useSearchParams();
const token = searchParams.get('token');
const navigate = useNavigate();
const baseUrl = `${window.location.protocol}//${window.location.host}`
if (token) {
Session.setBearerToken(token);
window.location.replace(`${baseUrl}/overview`);
const lastRoute = Session.lastShownComponent();
if (lastRoute !== null) {
reloadAuth();
Session.resetLastShownComponent();
navigate(lastRoute);
} else {
window.location.replace(`${baseUrl}/overview`);
}
} else {
Session.reset();
Session.resetLastShownComponent();
Session.resetBearerToken();
window.location.replace(`${baseUrl}/`);
}

View File

@@ -13,7 +13,8 @@ export const Login: React.FC = () => {
useEffect(() => {
Auth.tokenIsValid().then((status) => {
if (status !== BackendReturnStatus.Ok) {
Session.reset();
Session.resetLastShownComponent();
Session.resetBearerToken();
setLoading(false);
} else {
navigate('/overview');

View File

@@ -8,7 +8,8 @@ export const Logout: React.FC = () => {
const navigate = useNavigate();
useEffect(() => {
Session.reset();
Session.resetLastShownComponent();
Session.resetBearerToken();
context.resetAuth();
navigate('/');
// eslint-disable-next-line react-hooks/exhaustive-deps

View File

@@ -1,20 +1,21 @@
import { Menubar } from 'primereact/menubar';
import { MenuItem } from 'primereact/menuitem';
import React, { useContext, useEffect, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { AuthContext, TimeContext } from '../contexts';
import { AuthContext, ThemeContext } from '../contexts';
import { Configuration } from '../services';
import { Airport } from '../services';
import { AirportOverview, BackendReturnStatus, IAuthState } from '../types';
export const NavBar: React.FC = () => {
const [timestamp, setTimestamp] = useState<string>('');
const [fullName, setFullName] = useState<string>('');
const [menuTree, setMenuTree] = useState<any>(undefined);
const themeContext = useContext(ThemeContext);
const authContext = useContext(AuthContext);
const timeContext = useContext(TimeContext);
const currentAuth = useRef<IAuthState>();
const currentTheme = useRef<boolean>();
const navigate = useNavigate();
currentTheme.current = themeContext.darkMode;
currentAuth.current = authContext.auth;
const firBasedSubMenu = (airports: AirportOverview[], endpoint: string): any[] => {
@@ -66,10 +67,10 @@ export const NavBar: React.FC = () => {
Airport.all().then((response) => {
if (response.status !== BackendReturnStatus.Ok) return [];
const newMenuTree: { label: string; items?: any[]; command?: () => void }[] = [
const newMenuTree: MenuItem[] = [
{
label: 'Airports',
items: [] as any[],
items: [],
}
];
@@ -106,6 +107,14 @@ export const NavBar: React.FC = () => {
});
}
newMenuTree.push({
label: currentTheme.current ? 'Light mode' : 'Dark mode',
command: () => {
themeContext.setDarkMode(!currentTheme.current);
updateMenuItems();
},
});
newMenuTree.push({
label: 'Logout',
command: () => navigate('/logout'),
@@ -121,18 +130,7 @@ export const NavBar: React.FC = () => {
updateMenuItems();
}
const timeInterval = setInterval(() => {
const serverUtcTime = new Date(new Date().getTime() + timeContext.offset);
const hours = String(serverUtcTime.getUTCHours()).padStart(2, '0');
const minutes = String(serverUtcTime.getUTCMinutes()).padStart(2, '0');
const seconds = String(serverUtcTime.getUTCSeconds()).padStart(2, '0');
if (currentAuth.current?.valid) {
setTimestamp(`${hours}:${minutes}:${seconds}`);
}
}, 1000);
return () => {
clearInterval(timeInterval);
event.close();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -140,14 +138,8 @@ export const NavBar: React.FC = () => {
useEffect(() => {
if (currentAuth.current?.valid) {
if (currentAuth.current.user.fullName !== '') {
setFullName(currentAuth.current.user.fullName);
} else {
setFullName(currentAuth.current.user.vatsimId);
}
updateMenuItems();
} else {
setFullName('');
setMenuTree([]);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -155,9 +147,9 @@ export const NavBar: React.FC = () => {
if (menuTree === undefined || !currentAuth.current.valid) return <></>;
const rightSideInfo = (
<div>{fullName} | {timestamp}</div>
return (
menuTree.length !== 0 ?
<Menubar model={menuTree} />
: <></>
);
return (menuTree.length !== 0 ? <Menubar model={menuTree} end={rightSideInfo} /> : <></>);
};

View File

@@ -4,7 +4,7 @@ import { Card } from 'primereact/card';
import { DataTable } from 'primereact/datatable';
import { Password } from 'primereact/password';
import { AuthContext } from '../contexts';
import { Auth } from '../services';
import { Auth, Session } from '../services';
import { BackendReturnStatus } from '../types';
import { Column } from 'primereact/column';
@@ -18,9 +18,8 @@ export const Overview: React.FC = () => {
const regenerateKey = async () => {
Auth.refreshRadarScopeKey().then((status) => {
if (status === BackendReturnStatus.Unauthorized) {
Session.setLastShownComponent('/overview');
Auth.triggerLoginFlow();
// TODO reload user data in auth
// TODO store current URL and get back after login
} else if (status === BackendReturnStatus.Failure) {
setStatusMessage({
error: true,

View File

@@ -1,9 +1,12 @@
import AuthContext, { AuthProvider } from "./authcontext";
import TimeContext, { TimeProvider } from "./timecontext";
import AuthContext, { AuthProvider } from './authcontext';
import ThemeContext, { ThemeProvider } from './themecontext';
import TimeContext, { TimeProvider } from './timecontext';
export {
AuthContext,
AuthProvider,
ThemeContext,
ThemeProvider,
TimeContext,
TimeProvider
};

View File

@@ -17,10 +17,10 @@ export const ThemeProvider = ({ children }: { children: any }) => {
const refDarkMode = useRef<boolean>();
refDarkMode.current = darkMode;
//useEffect(() => {
// Session.setTheme(refDarkMode.current ? 'dark' : 'light');
useEffect(() => {
Session.setTheme(refDarkMode.current ? 'dark' : 'light');
// window.location.reload();
//}, [darkMode]);
}, [darkMode]);
//if (darkMode) {
// import('primereact/resources/themes/bootstrap4-dark-blue/theme.css');

View File

@@ -1,5 +1,6 @@
import React, { Dispatch, SetStateAction, useEffect, useState } from 'react';
import { Auth, System } from '../services';
import React, { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { Auth, Session, System } from '../services';
import { BackendReturnStatus } from '../types';
const TimeContext = React.createContext<{
@@ -9,14 +10,19 @@ const TimeContext = React.createContext<{
export const TimeProvider = ({ children }: { children: any }) => {
const [offset, setOffset] = useState<number>(0);
const location = useLocation();
const refLocation = useRef<string>();
refLocation.current = location.pathname;
useEffect(() => {
const estimateTimeOffset = async () => {
System.timestamp().then((response) => {
if (response.status === BackendReturnStatus.Unauthorized) {
if (refLocation.current) {
Session.setLastShownComponent(refLocation.current);
}
Auth.triggerLoginFlow();
// TODO reload user data in auth
// TODO store current URL and get back after login
} else if (response.status === BackendReturnStatus.Ok) {
// calculate the time offset (not accurate) between the server and the client to show "correct" times
const clientTimeUtc = new Date().getTime()

View File

@@ -1,5 +1,5 @@
export class Session {
static reset(): void {
static resetBearerToken(): void {
localStorage.removeItem('token');
}
@@ -18,4 +18,16 @@ export class Session {
static theme(): string | null {
return localStorage.getItem('theme');
}
static resetLastShownComponent(): void {
localStorage.removeItem('path');
}
static setLastShownComponent(path: string): void {
localStorage.setItem('path', path);
}
static lastShownComponent(): string | null {
return localStorage.getItem('path');
}
}