admin/plugins: keep sorted even if a plugin is added after the list is sent
Massimo Melina committed
Nov 24, 2024 at 18:17 UTC
9688a8f9d4248d38c655b201f3e74aebd50f68ac
2 files changed
+14
-25
admin/src/InstalledPlugins.ts
+5
-13
@@ -5,14 +5,7 @@ import { createElement as h, Fragment, useEffect } from 'react'
5
import { Box, Link } from '@mui/material'
6
import { DataTable, DataTableColumn } from './DataTable'
7
import {
8
- Clear,
9
- Delete,
10
- Error as ErrorIcon,
11
- FormatPaint as ThemeIcon,
12
- PlayCircle,
13
- Settings,
14
- StopCircle,
15
- Upgrade
8
+ Clear, Delete, Error as ErrorIcon, FormatPaint as ThemeIcon, PlayCircle, Settings, StopCircle, Upgrade
9
} from '@mui/icons-material'
10
import { HTTP_FAILED_DEPENDENCY, md, newObj, prefix, with_, xlate } from './misc'
11
import { alertDialog, confirmDialog, formDialog, toast } from './dialog'
@@ -26,12 +19,11 @@ import { Btn, hTooltip, IconBtn, iconTooltip } from './mui'
19
import VfsPathField from './VfsPathField'
20
21
export default function InstalledPlugins({ updates }: { updates?: true }) {
29
- const { list, error, updateList, initializing } = useApiList(updates ? 'get_plugin_updates' : 'get_plugins')
22
+ const { list, error, setList, initializing } = useApiList(updates ? 'get_plugin_updates' : 'get_plugins')
23
useEffect(() => {
31
- if (!initializing)
32
- updateList(list =>
33
- _.sortBy(list, x => (x.started || x.error ? '0' : '1') + treatPluginName(x.id)))
34
- }, [initializing]);
24
+ setList(list =>
25
+ _.sortBy(list, x => (x.started || x.error ? '0' : '1') + treatPluginName(x.id)))
26
+ }, [list.length]);
27
const size = 'small'
28
return h(DataTable, {
29
error: xlate(error, PLUGIN_ERRORS),
admin/src/api.ts
+9
-12
@@ -1,6 +1,6 @@
1
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
2
3
-import { createElement as h, useEffect, useMemo, useRef, useState } from 'react'
3
+import { createElement as h, useCallback, useEffect, useMemo, useRef, useState } from 'react'
4
import { Dict, err2msg, Falsy, LIST, useStateMounted, wantArray, xlate, objSameKeys,
5
HTTP_FORBIDDEN, HTTP_UNAUTHORIZED } from './misc'
6
import { IconBtn, spinner } from './mui'
@@ -161,21 +161,18 @@ export function useApiList<T=any, S=T>(cmd:string|Falsy, params: Dict={}, { map,
161
apply.flush()
162
}
163
}, [reloader, cmd, JSON.stringify(params)]) //eslint-disable-line
164
- return { list: pausedList ?? list, props, loading, error, initializing, connecting, setList, updateList, updateEntry, reload }
165
-
166
- function reload() {
167
- setReloader(x => x + 1)
168
- }
169
-
170
- function updateList(cb: (toModify: Draft<typeof list>) => void) {
171
- setList(produce(list, cb))
172
- }
173
-
174
- function updateEntry(search: T, change: T) {
164
+ const updateList = useCallback((cb: (toModify: Draft<typeof list>) => void) => setList(list => produce(list, cb)),
165
+ [setList])
166
+ const updateEntry = useCallback((search: T, change: T) => {
167
updateList(list => {
168
const res = _.find(list, search as any)
169
if (res)
170
Object.assign(res, change)
171
})
172
+ }, [updateList])
173
+ return { list: pausedList ?? list, props, loading, error, initializing, connecting, setList, updateList, updateEntry, reload }
174
+
175
+ function reload() {
176
+ setReloader(x => x + 1)
177
}
178
}