Version 3 starter

This commit is contained in:
Jamie Curnow
2021-06-14 19:29:35 +10:00
parent 60fc57431a
commit 6205434140
642 changed files with 25817 additions and 32319 deletions

View File

@@ -0,0 +1,76 @@
import React, { ReactNode, useState } from "react";
import { getToken, refreshToken, TokenResponse } from "api/npm";
import AuthStore from "modules/AuthStore";
import { useInterval } from "rooks";
// Context
export interface AuthContextType {
authenticated: boolean;
login: (username: string, password: string) => Promise<void>;
logout: () => void;
token?: string;
}
const initalValue = null;
const AuthContext = React.createContext<AuthContextType | null>(initalValue);
// Provider
interface Props {
children?: ReactNode;
tokenRefreshInterval?: number;
}
function AuthProvider({
children,
tokenRefreshInterval = 5 * 60 * 1000,
}: Props) {
const [authenticated, setAuthenticated] = useState(
AuthStore.hasActiveToken(),
);
const handleTokenUpdate = (response: TokenResponse) => {
AuthStore.set(response);
setAuthenticated(true);
};
const login = async (identity: string, secret: string) => {
const type = "password";
const response = await getToken({ payload: { type, identity, secret } });
handleTokenUpdate(response);
};
const logout = () => {
AuthStore.clear();
setAuthenticated(false);
};
const refresh = async () => {
const response = await refreshToken();
handleTokenUpdate(response);
};
useInterval(
() => {
if (authenticated) {
refresh();
}
},
tokenRefreshInterval,
true,
);
const value = { authenticated, login, logout };
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
function useAuthState() {
const context = React.useContext(AuthContext);
if (!context) {
throw new Error(`useAuthState must be used within a AuthProvider`);
}
return context;
}
export { AuthProvider, useAuthState };
export default AuthContext;

View File

@@ -0,0 +1,82 @@
import React, { ReactNode, useState, useEffect, useCallback } from "react";
import { HealthResponse, requestHealth } from "api/npm";
import { Unhealthy } from "components";
import { useInterval } from "rooks";
interface HealthResponseLoaded extends HealthResponse {
loading: boolean;
}
export interface HealthContextType {
health: HealthResponseLoaded;
refreshHealth: () => void;
}
const initalValue = null;
const HealthContext =
React.createContext<HealthContextType | null>(initalValue);
interface Props {
children: ReactNode;
}
function HealthProvider({ children }: Props) {
const [health, setHealth] = useState({
loading: true,
commit: "",
healthy: false,
setup: false,
errorReporting: true,
version: "",
});
const handleHealthUpdate = (response: HealthResponse) => {
setHealth({ ...response, loading: false });
};
const refreshHealth = async () => {
const response = await requestHealth();
handleHealthUpdate(response);
};
const asyncFetch = useCallback(async () => {
try {
const response = await requestHealth();
handleHealthUpdate(response);
if (response.healthy) {
if (!health.loading && health.commit !== response.commit) {
// New backend version detected, let's reload the entire window
window.location.assign(`?hash=${response.commit}`);
}
}
} catch ({ message }) {
console.error("Health failed:", message);
}
}, [health.commit, health.loading]);
useEffect(() => {
asyncFetch();
}, [asyncFetch]);
useInterval(asyncFetch, 30 * 1000, true);
if (!health.loading && !health.healthy) {
return <Unhealthy />;
}
const value = { health, refreshHealth };
return (
<HealthContext.Provider value={value}>{children}</HealthContext.Provider>
);
}
function useHealthState() {
const context = React.useContext(HealthContext);
if (!context) {
throw new Error(`useHealthState must be used within a HealthProvider`);
}
return context;
}
export { HealthProvider, useHealthState };
export default HealthContext;

View File

@@ -0,0 +1,54 @@
import React, { useState, useEffect } from "react";
import { getUser, UserResponse } from "api/npm";
import { useAuthState } from "context";
// Context
const initalValue = null;
const UserContext = React.createContext<UserResponse | null>(initalValue);
// Provider
interface Props {
children?: JSX.Element;
}
function UserProvider({ children }: Props) {
const [userData, setUserData] = useState<UserResponse>({
id: 0,
name: "",
nickname: "",
email: "",
createdOn: 0,
updatedOn: 0,
roles: [],
gravatarUrl: "",
isDisabled: false,
});
const { authenticated } = useAuthState();
const fetchUserData = async () => {
const response = await getUser();
setUserData({ ...userData, ...response });
};
useEffect(() => {
if (authenticated) {
fetchUserData();
}
/* eslint-disable-next-line */
}, [authenticated]);
return (
<UserContext.Provider value={userData}>{children}</UserContext.Provider>
);
}
function useUserState() {
const context = React.useContext(UserContext);
if (!context) {
throw new Error(`useUserState must be used within a UserProvider`);
}
return context;
}
export { UserProvider, useUserState };
export default UserContext;

View File

@@ -0,0 +1,3 @@
export * from "./AuthContext";
export * from "./HealthContext";
export * from "./UserContext";