admin/logs: persist columns configuration #817

Massimo Melina committed Nov 24, 2024 at 15:28 UTC 15bf0b01d298a0dbb4aaf920e76735e9104cc9aa
4 files changed +23 -4
admin/src/DataTable.ts
+13 -3
@@ -6,6 +6,7 @@ import { callable, Callback, newDialog, onlyTruthy, useGetSize } from '@hfs/shar
6 import _ from 'lodash'
7 import { Center, Flex } from './mui'
8 import { SxProps } from '@mui/system'
9 +import { state, updateStateObject } from './state'
10
11 const ACTIONS = 'Actions'
12
@@ -27,8 +28,9 @@ export interface DataTableProps<R extends GridValidRowModel=any> extends Omit<Da
28 compact?: boolean
29 footerSide?: (width: number) => ReactNode
30 fillFlex?: boolean
31 + persist?: string
32 }
31 -export function DataTable({ columns, initialState={}, actions, actionsProps, initializing, noRows, error, compact, footerSide, fillFlex, ...rest }: DataTableProps) {
33 +export function DataTable({ columns, initialState={}, actions, actionsProps, initializing, noRows, error, compact, footerSide, fillFlex, persist, ...rest }: DataTableProps) {
34 const theme = useTheme()
35 const apiRef = useGridApiRef()
36 const [actionsLength, setActionsLength] = useState(0)
@@ -104,7 +106,7 @@ export function DataTable({ columns, initialState={}, actions, actionsProps, ini
106 setMerged(_.sumBy(fields, k => _.find(columns, col => !fields.includes(col.field) && col.mergeRender?.[k]) ? 1 : 0))
107 return fields
108 }, [manipulatedColumns, width])
107 - const [vis, setVis] = useState({})
109 + const [vis, setVis] = useState(persist && state.dataTablePersistence[persist]?.columnVisibility || {})
110
111 const displayingDetails = useRef<any>({})
112 useEffect(() => {
@@ -188,7 +190,15 @@ export function DataTable({ columns, initialState={}, actions, actionsProps, ini
190 }
191 })
192 },
191 - onColumnVisibilityModelChange: x => setVis(x),
193 + onColumnVisibilityModelChange: vis => {
194 + setVis(vis)
195 + if (!persist) return
196 + updateStateObject(state, 'dataTablePersistence', x => {
197 + x[persist] = {
198 + columnVisibility: _.omitBy(vis, (v, k) => hideCols.includes(k) === (v === false))
199 + }
200 + })
201 + },
202 columnVisibilityModel: {
203 ...Object.fromEntries(hideCols.map(x => [x, false])),
204 ...rest.columnVisibilityModel,
admin/src/LogsPage.ts
+1
@@ -157,6 +157,7 @@ export function LogFile({ file, footerSide, hidden, limit, filter, ...rest }: Lo
157 const isConsole = file === 'console'
158 const isIps = file === 'ips'
159 return hidden ? null : h(DataTable, {
160 + persist: 'log_' + file,
161 error,
162 loading: connecting,
163 rows,
admin/src/MonitorPage.ts
+1
@@ -144,6 +144,7 @@ function Connections() {
144 h(Grid, { container: true, flex: 1, columnSpacing: 1 },
145 h(Grid, { item: true, xs: 12 - logSize },
146 h(DataTable, {
147 + persist: 'connections',
148 error,
149 rows,
150 noRows: monitorOnlyFiles && "No downloads at the moment",
admin/src/state.ts
+8 -1
@@ -5,6 +5,7 @@ import { Dict } from './misc'
5 import { VfsNode } from './VfsPage'
6 import _ from 'lodash'
7 import { subscribeKey } from 'valtio/utils'
8 +import produce from 'immer'
9
10 const STORAGE_KEY = 'admin_state'
11 const INIT = {
@@ -19,6 +20,7 @@ const INIT = {
20 monitorWithLog: true,
21 customHtmlSection: '',
22 darkTheme: undefined as undefined | boolean,
23 + dataTablePersistence: {} as any,
24 onlinePluginsColumns: {
25 version: false,
26 pushed_at: false,
@@ -28,7 +30,7 @@ const INIT = {
30 Object.assign(INIT, JSON.parse(localStorage[STORAGE_KEY]||null))
31 export const state = proxy(INIT)
32
31 -const SETTINGS_TO_STORE: (keyof typeof state)[] = ['onlinePluginsColumns', 'monitorOnlyFiles', 'monitorWithLog', 'customHtmlSection', 'darkTheme']
33 +const SETTINGS_TO_STORE: (keyof typeof state)[] = ['onlinePluginsColumns', 'monitorOnlyFiles', 'monitorWithLog', 'customHtmlSection', 'darkTheme', 'dataTablePersistence']
34 const storeSettings = _.debounce(() =>
35 localStorage[STORAGE_KEY] = JSON.stringify(_.pick(state, SETTINGS_TO_STORE)), 500, { maxWait: 1000 })
36 for (const k of SETTINGS_TO_STORE)
@@ -37,3 +39,8 @@ for (const k of SETTINGS_TO_STORE)
39 export function useSnapState() {
40 return useSnapshot(state)
41 }
42 +
43 +// use this to reflect a deep change in an object to its root, so that valtio is triggered
44 +export function updateStateObject(obj: any, k: string, cb: (x: any) => void) {
45 + obj[k] = produce(obj[k], cb)
46 +}
\ No newline at end of file