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