better error handling
Massimo Melina committed
Dec 24, 2021 at 17:16 UTC
ce1bfcd5321282d9fc8bb2f5ee6742df69a47b70
4 files changed
+19
-22
frontend/src/BrowseFiles.ts
+8
-12
@@ -13,32 +13,28 @@ export function usePath() {
13
14
interface DirEntry { n:string, s?:number, m?:string, c?:string }
15
export type DirList = DirEntry[]
16
-interface ListRes { list:DirList, unfinished?:boolean, err?:Error }
16
+interface ListRes { list:DirList, loading?:boolean, err?:Error }
17
18
-export const ListContext = createContext<ListRes>({ list:[], unfinished: false })
18
+export const ListContext = createContext<ListRes>({ list:[], loading: false })
19
20
export function BrowseFiles() {
21
- const { list, unfinished, error } = useFetchList()
22
- if (error)
23
- return hError(error)
24
- if (!list)
25
- return h(Spinner)
26
- return h(ListContext.Provider, { value:{ list, unfinished } },
21
+ const { list, loading, error } = useFetchList()
22
+ return h(ListContext.Provider, { value:{ list, loading } },
23
h(Head),
28
- h(FilesList))
24
+ hError(error && 'Failed to retrieve list') || h(list ? FilesList : Spinner))
25
}
26
27
function FilesList() {
32
- const { list, unfinished } = useContext(ListContext)
28
+ const { list, loading } = useContext(ListContext)
29
const snap = useSnapState()
30
if (!list) return null
31
const filter = snap.listFilter > '' && new RegExp(_.escapeRegExp(snap.listFilter),'i')
32
let n = 0 // if I try to use directly the state as counter I get a "too many re-renders" error
33
const ret = h('ul', { className: 'dir' },
38
- !list.length ? (unfinished || 'Nothing here')
34
+ !list.length ? (loading || 'Nothing here')
35
: list.map((entry: DirEntry) =>
36
h(File, { key: entry.n, hidden: filter && !filter.test(entry.n) || !++n, ...entry })),
41
- unfinished && h(Spinner))
37
+ loading && h(Spinner))
38
state.filteredEntries = filter ? n : -1
39
return ret
40
}
frontend/src/Head.ts
+2
-2
@@ -99,7 +99,7 @@ function LoginButton() {
99
}
100
101
function FolderStats() {
102
- const { list, unfinished } = useContext(ListContext)
102
+ const { list, loading } = useContext(ListContext)
103
const stats = useMemo(() =>{
104
let files = 0, folders = 0, size = 0
105
for (const x of list) {
@@ -113,7 +113,7 @@ function FolderStats() {
113
}, [list])
114
const { filteredEntries, stoppedSearch } = useSnapState()
115
return h('div', { id:'folder-stats' },
116
- stoppedSearch ? hIcon('interrupted') : unfinished && h(Spinner),
116
+ stoppedSearch ? hIcon('interrupted') : loading && h(Spinner),
117
[
118
prefix('', stats.files,' file(s)'),
119
prefix('', stats.folders, ' folder(s)'),
frontend/src/misc.ts
+2
-2
@@ -7,8 +7,8 @@ export function hIcon(name: string, props?:any) {
7
return h(Icon, { name, ...props })
8
}
9
10
-export function hError(err: Error) {
11
- return h('div', { className:'error-msg' }, err.message)
10
+export function hError(err?: Error | string) {
11
+ return err && h('div', { className:'error-msg' }, typeof err === 'string' ? err : err.message)
12
}
13
14
export function formatBytes(n: number, post: string = 'B') {
frontend/src/useFetchList.ts
+7
-6
@@ -8,7 +8,7 @@ export default function useFetchList() {
8
const desiredPath = usePath()
9
const search = snap.remoteSearch || undefined
10
const [list, setList] = useState<DirList>([])
11
- const [unfinished, setUnfinished] = useState(true)
11
+ const [loading, setLoading] = useState(false)
12
const [error, setError] = useState<Error>()
13
const lastPath = useRef('')
14
useEffect(()=>{
@@ -25,13 +25,13 @@ export default function useFetchList() {
25
state.stopSearch?.()
26
return
27
}
28
+ setLoading(true)
29
30
;(async ()=>{
31
const API = 'file_list'
32
const sse = search
33
const baseParams = { path:desiredPath, search, sse, omit:'c' }
34
let list: DirList = []
34
- setUnfinished(true)
35
setList(list)
36
37
if (sse) { // buffering entries is necessary against burst of events that will hang the browser
@@ -49,7 +49,7 @@ export default function useFetchList() {
49
case 'closed':
50
flush()
51
state.stopSearch?.()
52
- return setUnfinished(false)
52
+ return setLoading(false)
53
case 'msg':
54
if (src?.readyState === src?.CLOSED)
55
return state.stopSearch?.()
@@ -58,6 +58,7 @@ export default function useFetchList() {
58
})
59
state.stopSearch = ()=>{
60
buffer.length = 0
61
+ setLoading(false)
62
clearInterval(timer)
63
state.stopSearch = undefined
64
src.close()
@@ -68,7 +69,7 @@ export default function useFetchList() {
69
let offset = 0
70
while (1) {
71
const limit = list.length ? 1000 : 100
71
- const res = await apiCall(API, { ...baseParams, offset, limit })
72
+ const res = await apiCall(API, { ...baseParams, offset, limit }).catch(e => e)
73
|| Error()
74
if (res instanceof Error)
75
return setError(res)
@@ -78,9 +79,9 @@ export default function useFetchList() {
79
break
80
offset = list.length
81
}
81
- setUnfinished(false)
82
+ setLoading(false)
83
})()
84
}, [desiredPath, search])
84
- return { list, unfinished, error }
85
+ return { list, loading, error }
86
}
87