@samitouri / QOSami-HFS / commits / 7239345f

better code: future-proof api for newDialog

Massimo Melina committed Jul 13, 2023 at 23:39 UTC 7239345fd56a08f8a9abcffe71f13de78f7d502a
15 files changed +33 -27
admin/src/AccountsPage.ts
+5 -4
@@ -56,7 +56,7 @@ export default function AccountsPage() {
56 addToBar: isSideBreakpoint && h(IconBtn, { // not really useful, but users misled in thinking it's a dialog will find satisfaction in dismissing the form
57 icon: Close,
58 title: "Close",
59 - onClick: close
59 + onClick: selectNone
60 }),
61 reload,
62 done(username) {
@@ -66,14 +66,15 @@ export default function AccountsPage() {
66 })
67 useEffect(() => {
68 if (isSideBreakpoint || !sideContent || !sel.length) return
69 - return newDialog({
69 + const { close } = newDialog({
70 title: _.isString(sel) ? _.startCase(sel)
71 : sel.length > 1 ? "Multiple selection"
72 : selectedAccount ? (selectedAccount.hasPassword ? "User: " : "Group: ") + selectedAccount.username
73 : '?', // never
74 Content: () => sideContent,
75 - onClose: close,
75 + onClose: selectNone,
76 })
77 + return close
78 }, [isSideBreakpoint, sel, selectedAccount])
79
80 return element || h(Grid, { container: true, maxWidth: '80em' },
@@ -140,7 +141,7 @@ export default function AccountsPage() {
141 h(Card, {}, h(CardContent, {}, sideContent) )),
142 )
143
143 - function close() {
144 + function selectNone() {
145 setSel([])
146 }
147
admin/src/FileField.ts
+1 -1
@@ -22,7 +22,7 @@ export default function FileField({ value, onChange, files=true, folders=false,
22 title: "Browse files...",
23 edge: 'end',
24 onClick() {
25 - const close = newDialog({
25 + const { close } = newDialog({
26 title: title ?? (files ? "Pick a file" : "Pick a folder"),
27 dialogProps: {
28 fullScreen: !large,
admin/src/FileForm.ts
+1 -1
@@ -245,7 +245,7 @@ function LinkField({ value, statusApi }: LinkFieldProps) {
245 )
246
247 function edit() {
248 - const close = newDialog({
248 + const { close } = newDialog({
249 title: "Change link",
250 Content() {
251 const [v, setV] = useState(base || '')
admin/src/OptionsPage.ts
+1 -1
@@ -312,7 +312,7 @@ function suggestMakingCert() {
312 export async function makeCertAndSave() {
313 if (!window.crypto.subtle)
314 return alertDialog("Retry this procedure on localhost", 'warning')
315 - const close = newDialog({
315 + const { close } = newDialog({
316 title: "Get a certificate",
317 Content: () => h(Flex, { flexDirection: 'column' },
318 h('p', {}, "HTTPS needs a certificate to work."),
admin/src/VfsPage.ts
+5 -4
@@ -47,7 +47,7 @@ export default function VfsPage() {
47 return b && !ret.includes(b) ? [b, ...ret] : ret
48 }, [status])
49
50 - function close() {
50 + function selectNone() {
51 state.selectedFiles = []
52 }
53
@@ -56,7 +56,7 @@ export default function VfsPage() {
56 addToBar: isSideBreakpoint && h(IconBtn, { // not really useful, but users misled in thinking it's a dialog will find satisfaction in dismissing the form
57 icon: Close,
58 title: "Close",
59 - onClick: close
59 + onClick: selectNone
60 }),
61 defaultPerms: data?.defaultPerms as VfsPerms,
62 anyMask,
@@ -76,11 +76,12 @@ export default function VfsPage() {
76
77 useEffect(() => {
78 if (isSideBreakpoint || !sideContent) return
79 - return newDialog({
79 + const { close } = newDialog({
80 title: selectedFiles.length > 1 ? "Multiple selection" : selectedFiles[0].name,
81 Content: () => sideContent,
82 - onClose: close,
82 + onClose: selectNone,
83 })
84 + return close
85 },[isSideBreakpoint, selectedFiles])
86
87 useEffect(() => {
admin/src/addFiles.ts
+1 -1
@@ -10,7 +10,7 @@ import FilePicker from './FilePicker'
10 import { onlyTruthy } from './misc'
11
12 export default function addFiles() {
13 - const close = newDialog({
13 + const { close } = newDialog({
14 title: "Add files or folders",
15 dialogProps: { sx:{ minWidth: 'min(90vw, 40em)', minHeight: 'calc(100vh - 9em)' } },
16 Content() {
admin/src/dialog.ts
+3 -3
@@ -94,7 +94,7 @@ export async function alertDialog(msg: ReactElement | string | Error, options?:
94 msg = msg.message || String(msg)
95 type = 'error'
96 }
97 - const close = newDialog({
97 + const { close } = newDialog({
98 className: 'dialog-alert-' + type,
99 icon: '!',
100 onClose: resolve,
@@ -208,12 +208,12 @@ export async function promptDialog(msg: string, props:any={}) : Promise<string |
208 }
209
210 export function waitDialog() {
211 - return newDialog({ Content: CircularProgress, closable: false })
211 + return newDialog({ Content: CircularProgress, closable: false }).close
212 }
213
214 export function toast(msg: string | ReactElement, type: AlertType | ReactElement='info') {
215 const ms = 3000
216 - const close = newDialog({
216 + const { close } = newDialog({
217 Content,
218 dialogProps: {
219 PaperProps: {
admin/src/importAccountsCsv.ts
+1 -1
@@ -57,7 +57,7 @@ export async function importAccountsCsv(cb?: () => void) {
57 },
58 })
59 if (!cfg) return
60 - const close = newDialog({
60 + const { close } = newDialog({
61 title: "Importing...",
62 Content() {
63 const [progress, setProgress] = useState(0)
frontend/src/dialog.ts
+2 -2
@@ -72,7 +72,7 @@ export async function alertDialog(msg: ReactElement | string | Error, type:Alert
72 icon: '!',
73 onClose: resolve,
74 Content
75 - })))
75 + }).close))
76
77 function Content(){
78 if (msg instanceof Error)
@@ -102,7 +102,7 @@ export async function confirmDialog(msg: ReactElement | string, options: Confirm
102 onClose: resolve,
103 ...rest,
104 Content
105 - })) )
105 + }).close) )
106
107 function Content() {
108 const [sec,setSec] = useState(Math.ceil(timeout||0))
frontend/src/fileMenu.ts
+1 -1
@@ -56,7 +56,7 @@ export function openFileMenu(entry: DirEntry, ev: MouseEvent, addToMenu: (FileMe
56 if (res)
57 menu.push(...res.flat())
58 const ico = getEntryIcon(entry)
59 - const close = newDialog({
59 + const { close } = newDialog({
60 title: isFolder ? t`Folder menu` : t`File menu`,
61 className: 'file-dialog',
62 icon: () => ico,
frontend/src/login.ts
+4 -3
@@ -45,7 +45,7 @@ export async function loginDialog(navigate: ReturnType<typeof useNavigate>) {
45 return lastPromise = new Promise(resolve => {
46 if (closeLoginDialog)
47 return lastPromise
48 - const closeDialog = closeLoginDialog = newDialog({
48 + const { close } = newDialog({
49 className: 'login-dialog dialog-login',
50 icon: () => hIcon('login'),
51 onClose(v) {
@@ -95,7 +95,7 @@ export async function loginDialog(navigate: ReturnType<typeof useNavigate>) {
95 function onKeyDown(ev: KeyboardEvent) {
96 const { key } = ev
97 if (key === 'Escape')
98 - return closeDialog(null)
98 + return close(null)
99 if (key === 'Enter')
100 return go()
101 }
@@ -107,7 +107,7 @@ export async function loginDialog(navigate: ReturnType<typeof useNavigate>) {
107 if (!usr || !pwd) return
108 try {
109 const res = await login(usr, pwd)
110 - closeDialog()
110 + close()
111 if (res?.redirect)
112 navigate(getPrefixUrl() + res.redirect)
113 } catch (err: any) {
@@ -118,6 +118,7 @@ export async function loginDialog(navigate: ReturnType<typeof useNavigate>) {
118
119 }
120 })
121 + closeLoginDialog = close
122 })
123 }
124
frontend/src/misc.ts
+2 -1
@@ -42,7 +42,7 @@ export function working() {
42 if (isWorking)
43 return ()=>{} // noop
44 isWorking = true
45 - return newDialog({
45 + const { close } = newDialog({
46 closable: false,
47 noFrame: true,
48 Content: Spinner,
@@ -52,6 +52,7 @@ export function working() {
52 isWorking = false
53 }
54 })
55 + return close
56 }
57
58 export function hfsEvent(name: string, params?:Dict) {
frontend/src/show.ts
+1 -1
@@ -8,7 +8,7 @@ import { openFileMenu } from './fileMenu'
8 import { useI18N } from './i18n'
9
10 export function fileShow(entry: DirEntry) {
11 - const close = newDialog({
11 + const { close } = newDialog({
12 noFrame: true,
13 className: 'file-show',
14 Content() {
frontend/src/upload.ts
+1 -1
@@ -82,7 +82,7 @@ export function showUpload() {
82 if (!uploadState.qs.length)
83 resetCounters()
84 uploadDialogIsOpen = true
85 - const close = newDialog({
85 + const { close } = newDialog({
86 dialogProps: { id: 'upload-dialog', style: { minHeight: '6em', minWidth: 'min(20em, 100vw - 1em)' } },
87 title: t`Upload`,
88 icon: () => hIcon('upload'),
shared/dialogs.ts
+4 -2
@@ -22,7 +22,7 @@ export interface DialogOptions {
22 }
23
24 const dialogs = proxy<DialogOptions[]>([])
25 -export type DialogCloser = ReturnType<typeof newDialog>
25 +export type DialogCloser = ReturnType<typeof newDialog>['close']
26
27 export const dialogsDefaults: Partial<DialogOptions> = {
28 closableContent: 'x',
@@ -103,7 +103,9 @@ export function newDialog(options: DialogOptions) {
103 const $id = Math.random()
104 options.$id = $id // object identity is not working because of the proxy. This is a possible workaround
105 dialogs.push(options)
106 - return (v?:any) => {
106 + return { close }
107 +
108 + function close(v?:any) {
109 const i = dialogs.findIndex(x => (x as any).$id === $id)
110 if (i < 0) return
111 dialogs.splice(i,1)