customizable "unauthorized" page

Massimo Melina committed Nov 11, 2023 at 00:09 UTC b7cbb6f2152b3d49464408ca46ff5ce5e50ff71d
5 files changed +19 -14
frontend/src/BrowseFiles.ts
+10 -4
@@ -1,6 +1,6 @@
1 // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3 -import { Link } from 'react-router-dom'
3 +import { Link, useNavigate } from 'react-router-dom'
4 import { createElement as h, Fragment, memo, MouseEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'
5 import { useWindowSize } from 'usehooks-ts'
6 import { domOn, formatBytes, ErrorMsg, hIcon, onlyTruthy } from './misc'
@@ -9,7 +9,7 @@ import { Head } from './Head'
9 import { DirEntry, state, useSnapState } from './state'
10 import { alertDialog } from './dialog'
11 import useFetchList from './useFetchList'
12 -import { useAuthorized } from './login'
12 +import { loginDialog, useAuthorized } from './login'
13 import { acceptDropFiles, enqueue } from './upload'
14 import _ from 'lodash'
15 import { t, useI18N } from './i18n'
@@ -20,13 +20,19 @@ export const MISSING_PERM = "Missing permission"
20 export function BrowseFiles() {
21 useFetchList()
22 const { error } = useSnapState()
23 - return useAuthorized() && h(Fragment, {},
23 + const navigate = useNavigate()
24 + return useAuthorized() ? h(Fragment, {},
25 h(CustomCode, { name: 'beforeHeader' }),
26 h(Head),
27 h(CustomCode, { name: 'afterHeader' }),
28 error ? h(ErrorMsg, { err: error }) : h(FilesList),
29 h(CustomCode, { name: 'afterList' }),
29 - )
30 + ) : h(CustomCode, { name: 'unauthorized',
31 + ifEmpty: () => h('h1', {
32 + className: 'unauthorized',
33 + onClick: () => loginDialog(navigate)
34 + }, t`Unauthorized`)
35 + })
36 }
37
38 function FilesList() {
frontend/src/components.ts
+1 -1
@@ -82,7 +82,7 @@ export function CustomCode({ name, props, ifEmpty }: { name: string, props?: any
82 if (html?.trim?.())
83 ret.push(h(Html, { key: 'x', code: html }))
84 return ret
85 - }, props ? Object.values(props) : [])
85 + }, [name, ...props ? Object.values(props) : []])
86 return children.length || !ifEmpty ? h(Fragment, {}, children) : h(ifEmpty)
87 }
88
frontend/src/index.scss
+2
@@ -496,6 +496,8 @@ button label {
496 }
497 }
498
499 +.unauthorized { text-align: center }
500 +
501 #root>.tiles-mode {
502 max-width: none;
503 margin: 0 1em;
frontend/src/login.ts
+5 -8
@@ -109,7 +109,7 @@ export async function loginDialog(navigate: ReturnType<typeof useNavigate>) {
109 going = true
110 try {
111 const res = await login(usr, pwd)
112 - close()
112 + close(true)
113 if (res?.redirect)
114 navigate(getPrefixUrl() + res.redirect)
115 } catch (err: any) {
@@ -132,13 +132,10 @@ export function useAuthorized() {
132 state.loginRequired = true
133 const navigate = useNavigate()
134 useEffect(() => {
135 - (async () => {
136 - if (!loginRequired)
137 - return closeLoginDialog?.()
138 - if (closeLoginDialog) return
139 - while (state.loginRequired)
140 - await loginDialog(navigate)
141 - })()
135 + if (!loginRequired)
136 + return closeLoginDialog?.()
137 + if (!closeLoginDialog)
138 + loginDialog(navigate).then()
139 }, [loginRequired, navigate])
140 return loginRequired ? null : true
141 }
src/customHtml.ts
+1 -1
@@ -8,7 +8,7 @@ import { mapPlugins } from './plugins'
8 const FILE = 'custom.html'
9
10 export const customHtmlSections: ReadonlyArray<string> = ['beforeHeader', 'afterHeader', 'afterMenuBar', 'afterList',
11 - 'top', 'bottom', 'afterEntryName', 'beforeLogin']
11 + 'top', 'bottom', 'afterEntryName', 'beforeLogin', 'unauthorized']
12
13 export const customHtmlState = proxy({
14 sections: watchLoadCustomHtml().state