better api: return object as it's clearer
Massimo Melina committed
Sep 12, 2023 at 14:45 UTC
eff151f2c898cca1b529de6910806cb45ae051b1
6 files changed
+23
-21
admin/src/HomePage.ts
+2
-2
@@ -2,7 +2,7 @@
2
3
import { createElement as h, ReactNode, useState } from 'react'
4
import { Box, Button, Card, CardContent, LinearProgress, Link } from '@mui/material'
5
-import { apiCall, useApi, useApiEx, useApiList } from './api'
5
+import { apiCall, useApiEx, useApiList } from './api'
6
import {
7
Btn,
8
dontBotherWithKeys,
@@ -43,7 +43,7 @@ export default function HomePage() {
43
const { username } = useSnapState()
44
const { data: status, reload: reloadStatus, element: statusEl } = useApiEx<Status>('get_status')
45
const { data: vfs } = useApiEx<{ root?: VfsNode }>('get_vfs')
46
- const [account] = useApi<Account>(username && 'get_account')
46
+ const { data: account } = useApiEx<Account>(username && 'get_account')
47
const { data: cfg, reload: reloadCfg } = useApiEx('get_config', { only: ['https_port', 'cert', 'private_key', 'proxies', 'update_to_beta'] })
48
const { list: plugins } = useApiList('get_plugins')
49
const [updates, setUpdates] = useState<undefined | any[]>()
admin/src/OptionsPage.ts
+2
-2
@@ -2,7 +2,7 @@
2
3
import { Box, Button, FormHelperText, Link } from '@mui/material';
4
import { createElement as h, Fragment, useEffect, useRef } from 'react';
5
-import { apiCall, useApi, useApiEx } from './api'
5
+import { apiCall, useApiEx } from './api'
6
import { state, useSnapState } from './state'
7
import { Info, Refresh, Warning } from '@mui/icons-material'
8
import { Dict, Flex, iconTooltip, LinkBtn, modifiedSx, REPO_URL, wikiLink, with_ } from './misc'
@@ -46,7 +46,7 @@ export default function OptionsPage() {
46
useEffect(() => void(reloadStatus()), [data]) //eslint-disable-line
47
useEffect(() => () => exposedReloadStatus = undefined, []) // clear on unmount
48
49
- const admins = useApi('get_admins')[0]?.list
49
+ const admins = useApiEx('get_admins').data?.list
50
51
if (element)
52
return element
admin/src/VfsTree.ts
+1
-1
@@ -9,7 +9,7 @@ import {
9
} from '@mui/icons-material'
10
import { Box } from '@mui/material'
11
import { reloadVfs, VfsNode, Who } from './VfsPage'
12
-import { iconTooltip, isWindowsDrive, onlyTruthy } from './misc'
12
+import { iconTooltip, onlyTruthy } from './misc'
13
import { apiCall } from './api'
14
import { alertDialog, confirmDialog } from './dialog'
15
admin/src/api.ts
+10
-8
@@ -19,16 +19,18 @@ setDefaultApiCallOptions({
19
}
20
})
21
22
+// expand useApi with things that cannot be shared with Frontend
23
export function useApiEx<T=any>(...args: Parameters<typeof useApi>) {
23
- const [data, error, reload, loading] = useApi<T>(...args)
24
- const cmd = args[0]
25
- const element = useMemo(() =>
26
- !cmd ? null
27
- : error ? h(Alert, { severity: 'error' }, String(error), h(IconBtn, { icon: Refresh, onClick: reload, sx: { m:'-8px 0 -8px 16px' } }))
28
- : loading ? spinner()
24
+ const res = useApi<T>(...args)
25
+ return {
26
+ ...res,
27
+ element: useMemo(() =>
28
+ !args[0] ? null
29
+ : res.error ? h(Alert, { severity: 'error' }, String(res.error), h(IconBtn, { icon: Refresh, onClick: res.reload, sx: { m: '-8px 0 -8px 16px' } }))
30
+ : res.loading ? spinner()
31
: null,
30
- [error, cmd, loading, reload])
31
- return { data, error, reload, loading, element }
32
+ Object.values(res))
33
+ }
34
}
35
36
export function useApiList<T=any, S=T>(cmd:string|Falsy, params: Dict={}, { map }: { map?: (rec: S) => T }={}) {
frontend/src/fileMenu.ts
+1
-1
@@ -68,7 +68,7 @@ export function openFileMenu(entry: DirEntry, ev: MouseEvent, addToMenu: (FileMe
68
: [ev.pageX, ev.pageY - scrollY] as [number, number],
69
Content() {
70
const {t} = useI18N()
71
- const [details] = useApi('get_file_details', { uris: [entry.uri] });
71
+ const details = useApi('get_file_details', { uris: [entry.uri] }).data
72
const showProps = [ ...props,
73
with_(details?.[0]?.upload, x => x && [ t`Uploader`, x.ip + prefix(' (', x.username, ')') ])
74
]
shared/api.ts
+7
-7
@@ -68,20 +68,20 @@ export class ApiError extends Error {
68
}
69
}
70
71
-export function useApi<T=any>(cmd: string | Falsy, params?: object) : [T | undefined, undefined | Error, ()=>void, boolean] {
72
- const [ret, setRet] = useStateMounted<T | undefined>(undefined)
73
- const [err, setErr] = useStateMounted<Error | undefined>(undefined)
71
+export function useApi<T=any>(cmd: string | Falsy, params?: object) {
72
+ const [data, setData] = useStateMounted<T | undefined>(undefined)
73
+ const [error, setError] = useStateMounted<Error | undefined>(undefined)
74
const [forcer, setForcer] = useStateMounted(0)
75
const loadingRef = useRef<ReturnType<typeof apiCall>>()
76
const reloadingRef = useRef<any>()
77
useEffect(()=>{
78
loadingRef.current?.abort()
79
- setRet(undefined)
80
- setErr(undefined)
79
+ setData(undefined)
80
+ setError(undefined)
81
if (!cmd) return
82
let aborted = false
83
const req = apiCall<T>(cmd, params)
84
- const wholePromise = req.then(x => aborted || setRet(x), x => aborted || setErr(x))
84
+ const wholePromise = req.then(x => aborted || setData(x), x => aborted || setError(x))
85
.finally(() => loadingRef.current = reloadingRef.current = undefined)
86
loadingRef.current = Object.assign(wholePromise, {
87
abort() {
@@ -94,7 +94,7 @@ export function useApi<T=any>(cmd: string | Falsy, params?: object) : [T | undef
94
const reload = useCallback(() => loadingRef.current
95
|| setForcer(v => v+1) || (reloadingRef.current = pendingPromise()),
96
[setForcer])
97
- return [ret, err, reload, ret === undefined || Boolean(loadingRef.current || reloadingRef.current)]
97
+ return { data, error, reload, loading: data === undefined || Boolean(loadingRef.current || reloadingRef.current) }
98
}
99
100
type EventHandler = (type:string, data?:any) => void