fix: folder-size didn't work on some entries
Massimo Melina committed
Jan 19, 2025 at 18:06 UTC
7341aead49ffbbaef5582c49b317409ea77bd568
3 files changed
+24
-23
frontend/src/fileMenu.ts
+13
-12
@@ -1,6 +1,6 @@
1
import {
2
dontBotherWithKeys, formatBytes, getHFS, hfsEvent, hIcon, newDialog, prefix, with_, working,
3
- pathEncode, closeDialog, anyDialogOpen, Falsy, operationSuccessful, randomId
3
+ pathEncode, closeDialog, anyDialogOpen, Falsy, operationSuccessful, randomId, err2msg
4
} from './misc'
5
import { createElement as h, Fragment, isValidElement, MouseEvent, ReactNode, useState } from 'react'
6
import { Btn, Bytes, Spinner } from './components'
@@ -86,7 +86,7 @@ export function openFileMenu(entry: DirEntry, ev: MouseEvent, addToMenu: (Falsy
86
onClick: () => closeDialog(null, true)
87
}, folder.replaceAll('/', ' / '))
88
},
89
- isFolder && { id: 'folderSize', label: t`Size`, value: h(FolderSize) },
89
+ isFolder && !entry.cantOpen && { id: 'folderSize', label: t`Size`, value: h(FolderSize) },
90
].filter(Boolean)
91
const res = hfsEvent('fileMenu', { entry, menu, props })
92
menu.push(...res.flat()) // flat because each plugin may return an array of entries
@@ -149,19 +149,20 @@ export function openFileMenu(entry: DirEntry, ev: MouseEvent, addToMenu: (Falsy
149
function FolderSize() {
150
const [go, setGo] = useState(false)
151
const [id] = useState(() => randomId())
152
- const { data } = useApi(go && 'get_folder_size', { uri: entry.uri, id }, { timeout: false })
153
- const partial = useApi(go && !data && 'get_folder_size_partial', { id })
152
+ const { data, error, loading } = useApi(go && 'get_folder_size', { uri: entry.uri, id }, { timeout: false })
153
+ const partial = useApi(loading && 'get_folder_size_partial', { id })
154
useInterval(partial.reload, 1000)
155
- return data ? showRes(data)
156
- : !go ? h(Btn, { asText: true, label: t`Calculate`, onClick() { setGo(true) } })
157
- : h('span', {},
158
- showRes(partial.data),
159
- ' ',
160
- h(Btn, { asText: true, label: t`Cancel`, icon: h(Spinner), onClick() { setGo(false) } })
161
- )
155
+ return showRes(data || error)
156
+ || (!loading ? h(Btn, { asText: true, label: t`Calculate`, onClick() { setGo(true) } })
157
+ : h('span', {}, showRes(partial.data), ' ',
158
+ h(Btn, { asText: true, label: t`Cancel`, icon: h(Spinner), onClick() { setGo(false) } }) )
159
+ )
160
161
function showRes(data: any) {
164
- return data && h('span', {}, h(Bytes, _.pick(data,'bytes')), ' / ', t('n_files', { n: data.files.toLocaleString() }, '{n,plural,one{# file} other{# files}}') )
162
+ return data && (
163
+ data.code ? err2msg(data)
164
+ : h('span', {}, h(Bytes, _.pick(data,'bytes')), ' / ', t('n_files', { n: data.files.toLocaleString() }, '{n,plural,one{# file} other{# files}}') )
165
+ )
166
}
167
}
168
}
shared/api.ts
+10
-10
@@ -84,8 +84,8 @@ export function useApi<T=any>(cmd: string | Falsy, params?: object, options: Api
84
const [data, setData] = useStateMounted<Awaited<ReturnType<typeof apiCall<T>>> | undefined>(undefined)
85
const [error, setError] = useStateMounted<Error | undefined>(undefined)
86
const [forcer, setForcer] = useStateMounted(0)
87
- const loadingRef = useRef<ReturnType<typeof apiCall>>()
88
- const reloadingRef = useRef<any>()
87
+ const [loading, setLoading, getLoading] = useStateMounted<undefined | ReturnType<typeof apiCall>>(undefined)
88
+ const reloadPromise = useRef<any>()
89
const dataRef = useRef<any>()
90
useEffect(() => {
91
setError(undefined)
@@ -98,25 +98,25 @@ export function useApi<T=any>(cmd: string | Falsy, params?: object, options: Api
98
setError(err)
99
setData(dataRef.current = undefined)
100
})
101
- .finally(() => loadingRef.current = reloadingRef.current = undefined)
102
- loadingRef.current = Object.assign(wholePromise, {
101
+ .finally(() => setLoading(reloadPromise.current = undefined))
102
+ if (cmd && !aborted) setLoading(Object.assign(wholePromise, {
103
abort() {
104
aborted = true
105
req?.abort()
106
}
107
- })
108
- reloadingRef.current?.resolve(wholePromise)
109
- return () => loadingRef.current?.abort()
107
+ }))
108
+ reloadPromise.current?.resolve(wholePromise)
109
+ return () => getLoading()?.abort()
110
}, [cmd, JSON.stringify(params), forcer]) //eslint-disable-line -- json-ize to detect deep changes
111
const reload = useCallback(() => {
112
- if (loadingRef.current) return
112
+ if (getLoading()) return
113
setForcer(v => v + 1)
114
- reloadingRef.current = pendingPromise()
114
+ reloadPromise.current = pendingPromise()
115
}, [setForcer])
116
const ee = useMemo(() => new BetterEventEmitter, [])
117
const sub = useCallback((cb: Callback) => ee.on('data', cb), [ee])
118
useEffect(() => { ee.emit('data') }, [data])
119
- return { data, setData, error, reload, sub, loading: loadingRef.current || reloadingRef.current, getData: () => dataRef.current }
119
+ return { data, setData, error, reload, sub, loading, getData: () => dataRef.current }
120
}
121
122
type EventHandler = (type:string, data?:any) => void
src/frontEndApis.ts
+1
-1
@@ -194,7 +194,7 @@ export const frontEndApis: ApiHandlers = {
194
files++
195
partialFolderSize[id] = { bytes, files }
196
}
197
- return popKey(partialFolderSize, id)
197
+ return popKey(partialFolderSize, id) || { bytes, files }
198
},
199
}
200