diff --git a/.eslintrc.json b/.eslintrc.json index bde2111..f4f0fe9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -7,7 +7,8 @@ "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended", - "plugin:react/recommended" + "plugin:react/recommended", + "plugin:react-hooks/recommended" ], "parserOptions": { "ecmaFeatures": { @@ -17,7 +18,8 @@ "sourceType": "module" }, "plugins": [ - "react" + "react", + "react-hooks" ], "rules": { "nonblock-statement-body-position": [ diff --git a/src/components/TitledPage.tsx b/src/components/TitledPage.tsx index a429d1a..cef1de4 100644 --- a/src/components/TitledPage.tsx +++ b/src/components/TitledPage.tsx @@ -4,14 +4,18 @@ import { Helmet } from 'react-helmet-async'; // import socket from '../util/Socket'; type SubtitleContext = { - subtitle: string | null, + breadcrumbs: string[], set: (name: string) => void, - clear: () => void + clear: () => void, + push: (crumb: string) => void, + pop: () => void, } const Context = React.createContext({ - subtitle: null, + breadcrumbs: [], set: () => null, - clear: () => null + clear: () => null, + push: () => null, + pop: () => null, }); let TO: NodeJS.Timeout | null = null; @@ -23,13 +27,14 @@ export const useSubtitle = () => return useContext(Context); }; -const Main = ({ title, subtitle, children }: {title: string, subtitle?: string | null, children: React.ReactNode}) => +const Main = ({ title, crumbs: crumbs, children }: {title: string, crumbs: string[], children: React.ReactNode}) => { + const trail = crumbs.length ? `/ ${crumbs.join(' / ')}` : ''; return
- {title} { subtitle ? `/ ${subtitle}` : ''} + {title} {trail} -

{title} { subtitle ? `/ ${subtitle}` : ''}

+

{title} {trail}

{children}
@@ -39,23 +44,42 @@ const Main = ({ title, subtitle, children }: {title: string, subtitle?: string | const TitledPage = ({ title, children }: {title: string, children: React.ReactNode}) => { // let subtitle: string | null = null; - const [ subtitle, setSubtitle ] = useState(null); + const [ trail, setTrail ] = useState([]); let page = `${title}`; - if (subtitle) - page += ` / ${subtitle}`; + if (trail.length) + page += ` / ${trail.join(' / ')}`; const onlineState = { page, url: window.location.href, state: 'Online' }; const set = (str: string) => { // subtitle = str; // console.log('set subtitle', str); - setSubtitle(() => str); + setTrail(() => [ str ]); }; const clear = () => { // subtitle = null; // console.log('clear'); - setSubtitle(null); + setTrail([]); + }; + + const push = (name: string) => + { + // console.log('push', name); + setTrail(trail => + { + return [ ...trail, name ]; + }); + }; + + const pop = () => + { + setTrail(trail => + { + trail.pop(); + // console.log('pop', name); + return [ ...trail ]; + }); }; const setAfk = () => @@ -115,10 +139,11 @@ const TitledPage = ({ title, children }: {title: string, children: React.ReactNo document.removeEventListener('keypress', activityListener); document.removeEventListener('visibilitychange', visibilityListener); }; - }, [ title, subtitle ]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ title, trail ]); - return -
+ return +
{children}
; diff --git a/src/pages/admin/Admin.tsx b/src/pages/admin/Admin.tsx index f54b504..f563df1 100644 --- a/src/pages/admin/Admin.tsx +++ b/src/pages/admin/Admin.tsx @@ -47,6 +47,7 @@ const Admin = () => { if(window.location.href.endsWith('/admin')) subtitle.clear(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [ window.location.href ]); return diff --git a/src/pages/admin/Roles.tsx b/src/pages/admin/Roles.tsx index ae3ff3a..d9962be 100644 --- a/src/pages/admin/Roles.tsx +++ b/src/pages/admin/Roles.tsx @@ -33,24 +33,23 @@ const Role = ({ refreshList }: RoleProps) => const [ role, setRole ] = useState(roleId ? roles.get(roleId) ?? null : null); const [ refresh, setRefresh ] = useState(false); - if (!roleId) - return
- -

Invalid userId

-
; - - useEffect(() => { if (!role) (async () => { - const response = await get(`/api/roles/${roleId}`); + const response = await get(`/api/roles/${roleId}`); if (!response.success || !response.data) return errorToast(response.message); - setRole(response.data as RoleStruct); + setRole(response.data); })(); - }, [ roleId ]); + }, [ role, roleId ]); + + if (!roleId) + return
+ +

Invalid userId

+
; if (!role) return
@@ -308,7 +307,7 @@ const Main = () => setRoles(new Map(response.data.roles.map((role) => [ role.id, role ]))); setLoadState(RefresherState.success); })(); - }, [ page, filters, refresh ]); + }, [ page, filters, refresh, setRoles ]); return setRefresh(val => !val) }} />} /> @@ -322,7 +321,8 @@ const Roles = () => useEffect(() => { subtitle.set('Roles'); - }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ ]); return
diff --git a/src/pages/admin/Users.tsx b/src/pages/admin/Users.tsx index b3f28c3..405b2fa 100644 --- a/src/pages/admin/Users.tsx +++ b/src/pages/admin/Users.tsx @@ -359,16 +359,11 @@ type UserProps = { } const User = ({ refreshList }: UserProps) => { + // const subtitle = useSubtitle(); const { users, refreshState } = useContext(Context); const { userId } = useParams(); - if (!userId) - return
- -

Invalid userId

-
; - - const [ user, setUser ] = useState(users.get(userId) ?? null); + const [ user, setUser ] = useState(users.get(userId ?? '') ?? null); const [ applications, setApplications ] = useState>(new Map()); useEffect(() => @@ -376,12 +371,18 @@ const User = ({ refreshList }: UserProps) => if (!user) (async () => { - const response = await get(`/api/users/${userId}`); + const response = await get(`/api/users/${userId}`); if (!response.success || !response.data) return errorToast(response.message); - setUser(response.data as UserStruct); + setUser(response.data); })(); - }, [ userId, refreshState ]); + }, [ userId, refreshState, user ]); + + if (!userId) + return
+ +

Invalid userId

+
; return @@ -543,7 +544,7 @@ const Main = () => setUsers(new Map(response.data.users.map((user) => [ user.id, user ]))); setLoadState(RefresherState.success); })(); - }, [ page, searchFilter, refresh ]); + }, [ page, searchFilter, refresh, setUsers ]); useEffect(() => { @@ -554,7 +555,7 @@ const Main = () => return errorToast(response.message); setRoles(new Map(response.data.roles.map((role) => [ role.id, role ]))); })(); - }, [ refresh ]); + }, [ refresh, setRoles ]); return setRefresh(val => !val) }} />} /> @@ -569,7 +570,8 @@ const Users = () => useEffect(() => { subtitle.set('Users'); - }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); return