@samitouri / QOSami-HFS / commits / 85bfe191

fix: admin/home: link "get a free..." not working

Massimo Melina committed Jun 19, 2024 at 11:50 UTC 85bfe1919db2208953563eb41e01e055deed11d0
5 files changed +24 -9
admin/src/AccountsPage.ts
+1 -1
@@ -69,7 +69,7 @@ export default function AccountsPage() {
69 Content: () => sideContent,
70 onClose: selectNone,
71 })
72 - return close
72 + return () => void close()
73 }, [isSideBreakpoint, sel, selectedAccount])
74
75 return element || h(Grid, { container: true, maxWidth: '80em' },
admin/src/OptionsPage.ts
+1 -1
@@ -353,7 +353,7 @@ export async function suggestMakingCert() {
353 onClose: resolve,
354 Content: () => h(Box, { p: 1, lineHeight: 1.5, },
355 h(Box, {}, "HTTPS needs a certificate to work."),
356 - h(Box, {}, "We suggest you to ", h(InLink, { to: 'internet', onClick: close }, "get a free but proper certificate"), '.'),
356 + h(Box, {}, "We suggest you to ", h(InLink, { to: 'internet' }, "get a free but proper certificate"), '.'),
357 h(Box, {}, "If you don't have a domain ", h(LinkBtn, { onClick: makeCertAndSave }, "make a self-signed certificate"),
358 " but that ", wikiLink('HTTPS#certificate', " won't be perfect"), '.' ),
359 )
admin/src/VfsPage.ts
+1 -1
@@ -86,7 +86,7 @@ export default function VfsPage() {
86 },
87 })
88 closeDialogRef.current = close
89 - return close // auto-close dialog if we are switching to side-panel
89 + return () => void close() // auto-close dialog if we are switching to side-panel
90 }, [isSideBreakpoint, _.last(selectedFiles)?.id])
91
92 useEffect(() => {
admin/src/mui.ts
+13 -3
@@ -7,12 +7,12 @@ import { createElement as h, forwardRef, Fragment, ReactElement, ReactNode, useC
7 ForwardedRef, useState, useMemo } from 'react'
8 import { Box, BoxProps, Breakpoint, ButtonProps, CircularProgress, IconButton, IconButtonProps, Link, LinkProps,
9 Tooltip, TooltipProps, useMediaQuery } from '@mui/material'
10 -import { formatPerc, isIpLan, isIpLocalHost, prefix, WIKI_URL, with_ } from './misc'
10 +import { anyDialogOpen, closeDialog, formatPerc, isIpLan, isIpLocalHost, prefix, WIKI_URL, with_ } from './misc'
11 import { dontBotherWithKeys, restartAnimation, useBatch, useStateMounted } from '@hfs/shared'
12 import { Promisable, StringField } from '@hfs/mui-grid-form'
13 import { alertDialog, confirmDialog, toast } from './dialog'
14 import { LoadingButton } from '@mui/lab'
15 -import { Link as RouterLink } from 'react-router-dom'
15 +import { Link as RouterLink, LinkProps as RouterLinkProps, useNavigate } from 'react-router-dom'
16 import { SvgIconProps } from '@mui/material/SvgIcon/SvgIcon'
17 import _ from 'lodash'
18 import { ALL as COUNTRIES } from './countries'
@@ -197,13 +197,23 @@ export function iconTooltip(icon: SvgIconComponent, tooltip: ReactNode, sx?: SxP
197 return hTooltip(tooltip, undefined, h(icon, { sx, ...props }) )
198 }
199
200 -export function InLink(props:any) {
200 +// link for internal navigation
201 +export function InLink({ ...props }: LinkProps & RouterLinkProps) {
202 + // make links inside dialogs work correctly
203 + const nav = useNavigate()
204 + props.onClickCapture = async ev => {
205 + ev.preventDefault()
206 + while (anyDialogOpen())
207 + await closeDialog()?.closed
208 + nav(props.to)
209 + }
210 return h(Link, { component: RouterLink, ...props })
211 }
212
213 export const Center = forwardRef((props: BoxProps, ref) =>
214 h(Box, { ref, display:'flex', height:'100%', width:'100%', justifyContent:'center', alignItems:'center', flexDirection: 'column', ...props }))
215
216 +// looks like a link, but it's a button
217 export function LinkBtn({ ...rest }: LinkProps) {
218 return h(Link, {
219 ...rest,
shared/dialogs.ts
+8 -3
@@ -8,7 +8,8 @@ import { domOn, isPrimitive, objSameKeys, wait } from '.'
8 export interface DialogOptions {
9 Content: FunctionComponent<any>,
10 closable?: boolean,
11 - onClose?: (v?:any)=> any,
11 + onClose?: (v?: any) => any,
12 + closingValue?: any,
13 className?: string,
14 icon?: string | ReactNode | FunctionComponent,
15 closableProps?: any,
@@ -186,6 +187,7 @@ export function newDialog(options: DialogOptions) {
187 options = objSameKeys(options, x => isValidElement(x) ? ref(x) : x) as typeof options // encapsulate elements as react will try to write, but valtio makes them readonly
188 options.$opening = setTimeout(() => { // in case dialogs were just closed, account for window.history delay. This should be harmless as ux is unaffected, and programmatically you already didn't expect this to happen immediately but at state change
189 dialogs.push(options)
190 + options = dialogs[dialogs.length - 1] // replace with proxy object, to stay in sync with its changes
191 if (options.closable !== false)
192 history.pushState({ $dialog: $id, ts, idx: history.state.idx + 1 }, '')
193 }, 10) // 10 for firefox, chrome125 seems to be ok with 1
@@ -197,7 +199,8 @@ export function newDialog(options: DialogOptions) {
199 if (i < 0) return
200 if (history.state.$dialog === $id)
201 options.closed = back()
200 - return closeDialogAt(i, v)
202 + closeDialogAt(i, v)
203 + return options
204 }
205 }
206
@@ -220,7 +223,9 @@ export function closeDialog(v?:any, skipHistory=false) {
223 function closeDialogAt(i: number, value?: any) {
224 const [d] = dialogs.splice(i,1)
225 ;(focusBak.pop() as any)?.focus?.() // if element is not HTMLElement, it doesn't have focus method
223 - return d?.onClose?.(value)
226 + d.closingValue = value
227 + d?.onClose?.(value)
228 + return d
229 }
230
231 export function anyDialogOpen() {