fix: admin/fs: save button was not closing the dialog on mobile

Massimo Melina committed Apr 14, 2024 at 16:01 UTC c65918db824f290275ce915ff216a55e6066eef2
2 files changed +23 -18
admin/src/FileForm.ts
+9 -6
@@ -6,8 +6,10 @@ import { Alert, Box, Collapse, FormHelperText, Link, MenuItem, MenuList, useThem
6 import { BoolField, DisplayField, Field, FieldProps, Form, MultiSelectField, SelectField, StringField
7 } from '@hfs/mui-grid-form'
8 import { apiCall, UseApi } from './api'
9 -import { basename, defaultPerms, formatBytes, formatTimestamp, isEqualLax, isWhoObject, newDialog, objSameKeys,
10 - onlyTruthy, prefix, VfsPerms, wantArray, Who, WhoObject, matches, HTTP_MESSAGES, xlate, md } from './misc'
9 +import {
10 + basename, defaultPerms, formatBytes, formatTimestamp, isEqualLax, isWhoObject, newDialog, objSameKeys,
11 + onlyTruthy, prefix, VfsPerms, wantArray, Who, WhoObject, matches, HTTP_MESSAGES, xlate, md, Callback
12 +} from './misc'
13 import { Btn, IconBtn, LinkBtn, modifiedProps, useBreakpoint, wikiLink } from './mui'
14 import { reloadVfs, VfsNode } from './VfsPage'
15 import _ from 'lodash'
@@ -20,6 +22,8 @@ import QrCreator from 'qr-creator';
22 import MenuButton from './MenuButton'
23 import addFiles, { addLink, addVirtual } from './addFiles'
24
25 +const ACCEPT_LINK = "https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept"
26 +
27 export interface Account { username: string }
28
29 interface FileFormProps {
@@ -27,11 +31,9 @@ interface FileFormProps {
31 addToBar?: ReactNode
32 statusApi: UseApi
33 accounts: Account[]
34 + saved: Callback
35 }
31 -
32 -const ACCEPT_LINK = "https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept"
33 -
34 -export default function FileForm({ file, addToBar, statusApi, accounts }: FileFormProps) {
36 +export default function FileForm({ file, addToBar, statusApi, accounts, saved }: FileFormProps) {
37 const { parent, children, isRoot, byMasks, ...rest } = file
38 const [values, setValues] = useState(rest)
39 useEffect(() => {
@@ -135,6 +137,7 @@ export default function FileForm({ file, addToBar, statusApi, accounts }: FileFo
137 if (props.name !== file.name) // when the name changes, the id of the selected file is changing too, and we have to update it in the state if we want it to be correctly re-selected after reload
138 state.selectedFiles[0].id = file.parent!.id + props.name + (isDir ? '/' : '')
139 reloadVfs()
140 + saved()
141 }
142 },
143 fields: [
admin/src/VfsPage.ts
+14 -12
@@ -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 { createElement as h, Fragment, useEffect, useMemo, useState } from 'react'
3 +import { createElement as h, Fragment, useEffect, useMemo, useRef, useState } from 'react'
4 import { apiCall, useApiEx } from './api'
5 import { Alert, Box, Button, Card, CardContent, Grid, Link, List, ListItem, ListItemText, Typography } from '@mui/material'
6 import { state, useSnapState } from './state'
@@ -34,9 +34,18 @@ export default function VfsPage() {
34 const single = selectedFiles.length < 2 && (selectedFiles[0] as VfsNode || vfs)
35 const accountsApi = useApiEx<{ list: Account[] }>('get_accounts') // load accounts once and for all, or !isSideBreakpoint will cause a call for each selection
36
37 - const sideContent = accountsApi.element || !vfs ? null : single ? h(FileForm, {
37 + // this will take care of closing the dialog, for user's convenience, after "cut" button is pressed
38 + const closeDialogRef = useRef(_.noop)
39 + useEffect(() => {
40 + if (movingFile === selectedFiles[0]?.id)
41 + closeDialogRef.current()
42 + }, [movingFile])
43 +
44 + const sideContent = accountsApi.element || !vfs ? null
45 + : single ? h(FileForm, {
46 addToBar: isSideBreakpoint && h(Box, { flex: 1, textAlign: 'right', mr: 1, color: '#8883' }, vfsNodeIcon(single)),
47 statusApi,
48 + saved: () => closeDialogRef.current(),
49 accounts: accountsApi?.data?.list ?? [],
50 file: single // it's actually Snapshot<VfsNode> but it's easier this way
51 })
@@ -51,13 +60,6 @@ export default function VfsPage() {
60 )
61 )
62
54 - // this will take care of closing the dialog, for user's convenience, after "cut" button is pressed
55 - const [closeDialog, setCloseDialog] = useState(() => _.noop)
56 - useEffect(() => {
57 - if (movingFile === selectedFiles[0]?.id)
58 - closeDialog()
59 - }, [movingFile, closeDialog])
60 -
63 useEffect(() => {
64 if (isSideBreakpoint || !sideContent || !selectedFiles.length) return
65 const ancestors = ['']
@@ -80,9 +82,9 @@ export default function VfsPage() {
82 state.selectedFiles = []
83 },
84 })
83 - setCloseDialog(() => close)
85 + closeDialogRef.current = close
86 return close // auto-close dialog if we are switching to side-panel
85 - }, [isSideBreakpoint, selectedFiles])
87 + }, [isSideBreakpoint, _.last(selectedFiles)?.id])
88
89 useEffect(() => {
90 state.vfs = undefined
@@ -100,7 +102,7 @@ export default function VfsPage() {
102
103 function consumeSelectOnReload() {
104 if (selectOnReload)
103 - closeDialog() // noop when side-paneling
105 + closeDialogRef.current() // noop when side-paneling
106 const ret = selectOnReload && onlyTruthy(selectOnReload.map(id => id2node.get(id)))
107 selectOnReload = undefined
108 return ret