diff --git a/package.json b/package.json index 36e9725..4829784 100644 --- a/package.json +++ b/package.json @@ -12,12 +12,12 @@ "web-vitals": "^2.1.4" }, "devDependencies": { - "eslint": "^8.27.0", - "eslint-plugin-react": "^7.31.10", - "http-proxy-middleware": "^2.0.6", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", - "@testing-library/user-event": "^13.5.0" + "@testing-library/user-event": "^13.5.0", + "eslint": "^8.27.0", + "eslint-plugin-react": "^7.31.10", + "http-proxy-middleware": "^2.0.6" }, "scripts": { "start": "react-scripts start", diff --git a/src/App.js b/src/App.js index bf0a04e..9a94681 100644 --- a/src/App.js +++ b/src/App.js @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { BrowserRouter, Navigate, Route, Routes} from 'react-router-dom'; +import { Navigate, Route, Routes, useNavigate} from 'react-router-dom'; import './css/App.css'; import Home from './pages/Home'; @@ -8,7 +8,7 @@ import Sidebar, { SidebarMenu } from './components/Sidebar'; import UserControls from './components/UserControls'; import { useLoginContext } from './structures/UserContext'; import Login from './pages/Login'; -import { fetchUser } from './util/Util'; +import { get, setSession, setSettings } from './util/Util'; import PrivateRoute from './structures/PrivateRoute'; import { UnauthedRoute } from './structures/UnauthedRoute'; import Users from './pages/Users'; @@ -20,12 +20,21 @@ function App() { const [user, updateUser] = useLoginContext(); const [loading, setLoading] = useState(true); + const navigate = useNavigate(); useEffect(() => { (async () => { - await fetchUser(); - updateUser(); + + const settings = await get('/api/settings'); + setSettings(settings.data); + + const result = await get('/api/user'); + if (result.status === 200) { + setSession(result.data); + updateUser(); + } setLoading(false); + if (result.data?.twoFactor) return navigate('/login/verify'); })(); }, []); @@ -42,8 +51,6 @@ function App() {
- - {user ?
@@ -93,7 +100,6 @@ function App() {
- diff --git a/src/components/Sidebar.js b/src/components/Sidebar.js index a1c31dc..98d5e0f 100644 --- a/src/components/Sidebar.js +++ b/src/components/Sidebar.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {} from 'react'; import '../css/components/Sidebar.css'; import NavLink from '../structures/NavLink'; import logoImg from '../img/logo.png'; @@ -14,7 +14,7 @@ const Sidebar = ({children}) => { }; -const expandMenu = (event) => { +const toggleMenu = (event) => { event.preventDefault(); event.target.parentElement.classList.toggle("open"); }; @@ -54,9 +54,10 @@ const SidebarMenu = ({menuItems = [], children}) => { if(relative) to = `${menuItem.to}${to}`; return {label}; }); + elements.push(
- - {label}{subElements && } + + {label}{subElements && } {subElements &&
{subElements} diff --git a/src/css/components/Sidebar.css b/src/css/components/Sidebar.css index 27976be..d5c9f03 100644 --- a/src/css/components/Sidebar.css +++ b/src/css/components/Sidebar.css @@ -5,6 +5,7 @@ padding: 1rem 0; z-index: 1000; border-radius: 0; + line-height: 1.95; } .sidebar-menu { diff --git a/src/css/index.css b/src/css/index.css index 765e0c4..6643dcf 100644 --- a/src/css/index.css +++ b/src/css/index.css @@ -82,10 +82,6 @@ h1, h2, h3, h4, h5, h6 { margin: 0.35em 0 0.35em; } -body{ - line-height: 1.95; -} - .pageTitle{ margin: 4px; } diff --git a/src/index.js b/src/index.js index 1068bdb..a39e5ab 100644 --- a/src/index.js +++ b/src/index.js @@ -4,11 +4,14 @@ import './css/index.css'; import App from './App'; // import reportWebVitals from './reportWebVitals'; import { UserContext } from './structures/UserContext'; +import { BrowserRouter } from 'react-router-dom'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( - + + + ); // root.render( diff --git a/src/pages/Home.js b/src/pages/Home.js index c1376be..3bd67d9 100644 --- a/src/pages/Home.js +++ b/src/pages/Home.js @@ -2,12 +2,13 @@ import React, { useRef, useState } from "react"; import { Route, Routes } from "react-router"; import '../css/pages/Home.css'; import { useLoginContext } from "../structures/UserContext"; -import { get, post } from "../util/Util"; +import ErrorBoundary from "../util/ErrorBoundary"; +import { capitalise, get, post } from "../util/Util"; -const Profile = () => { - - const [user] = useLoginContext(); +const TwoFactorControls = ({ user }) => { + const [twoFactorError, set2FAError] = useState(null); + const [displayInput, setDisplayInput] = useState(false); const [qr, setQr] = useState(null); const displayQr = async () => { @@ -16,36 +17,89 @@ const Profile = () => { setQr(response.message); }; - const authCodeRef = useRef(); - const disable2FA = async () => { - const code = authCodeRef.current.value; - const response = await post('/api/user/2fa/disable', {code}); - if(response.status !== 200) return set2FAError(response.message); + const codeRef = useRef(); + const disable2FA = async (event) => { + event.preventDefault(); + const code = codeRef.current.value; + if (!code) return; + const response = await post('/api/user/2fa/disable', { code }); + if (response.status !== 200) return set2FAError(response.message); }; - const codeRef = useRef(); - const submitCode = async () => { - + const submitCode = async (event) => { + event.preventDefault(); const code = codeRef.current.value; if (!code) return; const response = await post('/api/user/2fa/verify', { code }); if (response.status !== 200) return set2FAError(response.message); - }; + let inner =
+ {qr ? +
+ +
+ + +
+
: + } +
; + + + if (user.twoFactor) inner =
+ {displayInput ? +
+ + +
+ : } +
; + + return
+ {twoFactorError &&

{twoFactorError}

} + {inner} +
; + +}; + +const ExternalProfile = ({ profile }) => { + return
+ {capitalise(profile.provider)} +

Username: {profile.username}

+

ID: {profile.id}

+
; +}; + +const ThirdPartyConnections = ({user}) => { + + const {externalProfiles} = user; + + return
+ {externalProfiles.map(profile => )} +
; + +}; + +const Profile = () => { + + const [user] = useLoginContext(); + return
-

Profile

Profile Picture

- +
+

3rd party connections

+ +
@@ -67,25 +121,9 @@ const Profile = () => { - -

Two Factor

- {twoFactorError &&

{twoFactorError}

} - {!user.twoFactor ? -
- {qr ? -
- -
- - -
-
: - } -
: -
- -
- } + +

Two Factor: {user.twoFactor ? 'enabled' : 'disabled'}

+
; @@ -100,9 +138,11 @@ const Main = () => { const Home = () => { - return - } /> - } /> - ; + return + + } /> + } /> + + ; }; export default Home; \ No newline at end of file diff --git a/src/pages/Login.js b/src/pages/Login.js index 695598f..d7abdd3 100644 --- a/src/pages/Login.js +++ b/src/pages/Login.js @@ -4,15 +4,7 @@ import '../css/pages/Login.css'; import logoImg from '../img/logo.png'; import { useLoginContext } from "../structures/UserContext"; import LoadingBar from 'react-top-loading-bar'; -import { fetchUser, post } from "../util/Util"; - -const loginMethods = [ -{ - name: 'Discord', - img: 'https://assets-global.website-files.com/6257adef93867e50d84d30e2/636e0a69f118df70ad7828d4_icon_clyde_blurple_RGB.svg' -} -]; - +import { fetchUser, getSettings, post } from "../util/Util"; const CredentialFields = () => { @@ -21,6 +13,8 @@ const CredentialFields = () => { const navigate = useNavigate(); const ref = useRef(null); + const loginMethods = getSettings()?.OAuthProviders || []; + const loginClick = async (event) => { ref.current.continuousStart(); event.preventDefault(); @@ -71,15 +65,18 @@ const CredentialFields = () => {
Alternate login methods
- {loginMethods.map(method =>
{ window.location.pathname = `/api/login/${method.name}`; }} - />)} + onClick={() => { window.location.pathname = `/api/login/${method.name}`; }}> + {method.name} +

{method.name}

+
)}
; @@ -92,7 +89,8 @@ const TwoFactor = () => { const navigate = useNavigate(); const ref = useRef(null); - const twoFactorClick = async () => { + const twoFactorClick = async (event) => { + event.preventDefault(); const code = document.getElementById('2faCode').value; if (!code) return; ref.current.continuousStart(); @@ -115,8 +113,10 @@ const TwoFactor = () => {

Verification Code

{fail ?

Invalid code

: null} - - +
+ + +
; }; diff --git a/src/util/Util.js b/src/util/Util.js index 4c2ddb9..5f86e16 100644 --- a/src/util/Util.js +++ b/src/util/Util.js @@ -4,10 +4,6 @@ export const getUser = () => { return null; }; -export const getToken = () => { - return getUser()?.token || null; -}; - export const clearSession = () => { sessionStorage.removeItem('user'); }; @@ -25,6 +21,16 @@ export const fetchUser = async (force = false) => { setSession(result.data); return result.data; } + return result; +}; + +export const setSettings = (settings) => { + if(settings) sessionStorage.setItem('settings', JSON.stringify(settings)); +}; + +export const getSettings = () => { + const data = sessionStorage.getItem('settings'); + if (data) return JSON.parse(data); return null; }; @@ -66,4 +72,9 @@ export const get = async (url, params) => { .join('&'); const response = await fetch(url); return parseResponse(response); +}; + +export const capitalise = (str) => { + const first = str[0].toUpperCase(); + return `${first}${str.substring(1)}`; }; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index a998e3b..50b01da 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7498,7 +7498,7 @@ react-scripts@5.0.1: react-top-loading-bar@^2.3.1: version "2.3.1" - resolved "https://registry.yarnpkg.com/react-top-loading-bar/-/react-top-loading-bar-2.3.1.tgz#d727eb6aaa412eae52a990e5de9f33e9136ac714" + resolved "https://registry.corgi.wtf/react-top-loading-bar/-/react-top-loading-bar-2.3.1.tgz#d727eb6aaa412eae52a990e5de9f33e9136ac714" integrity sha512-rQk2Nm+TOBrM1C4E3e6KwT65iXyRSgBHjCkr2FNja1S51WaPulRA5nKj/xazuQ3x89wDDdGsrqkqy0RBIfd0xg== react@^18.2.0: