removed redundant code
Massimo Melina committed
May 22, 2026 at 18:11 UTC
7528c8ba6b2433277e296569c150f70674a9c8ee
12 files changed
+26
-28
admin/src/DateTimeField.ts
+3
-2
@@ -3,13 +3,14 @@ import dayjs from 'dayjs'
3
import { FieldProps } from '@hfs/mui-grid-form'
4
import { createElement as h } from 'react'
5
import { Box } from '@mui/material'
6
-import { isTimestampString, objSameKeys } from './misc'
6
+import { isTimestampString } from './misc'
7
+import _ from 'lodash'
8
import { mergeSx } from './mui'
9
10
export function DateTimeField({ onChange, error, helperText, ...rest }: FieldProps<Date>) {
11
return h(Box, {},
12
h(DateTimePicker, {
12
- ...objSameKeys(rest, x => isTimestampString(x) || x && x instanceof Date ? dayjs(x) : (x ?? null)), // null to not be considered uncontrolled
13
+ ..._.mapValues(rest, x => isTimestampString(x) || x && x instanceof Date ? dayjs(x) : (x ?? null)), // null to not be considered uncontrolled
14
sx: mergeSx({ width: '100%', color: 'error.main' }, rest.sx),
15
onChange(v: any) {
16
onChange(v && new Date(v), { was: rest.value, event: undefined })
admin/src/FileForm.ts
+2
-2
@@ -8,7 +8,7 @@ import {
8
} from '@hfs/mui-grid-form'
9
import { apiCall, UseApi, useApiEx } from './api'
10
import {
11
- basename, defaultPerms, formatBytes, formatTimestamp, isWhoObject, newDialog, objSameKeys, useRequestRender, try_,
11
+ basename, defaultPerms, formatBytes, formatTimestamp, isWhoObject, newDialog, useRequestRender, try_,
12
onlyTruthy, prefix, VfsPerms, wantArray, WhoVfs, WhoObject, matches, xlate, md, Callback, copyTextToClipboard,
13
normalizeHost, splitAt, IMAGE_FILEMASK, CFG, MASK_IN_TESTS, WHO_ANY_ACCOUNT, WHO_ADMIN, WHO_NO_ONE, WHO_ANYONE,
14
} from './misc'
@@ -45,7 +45,7 @@ export default function FileForm({ file, addToBar, statusApi, accountsApi, saved
45
const { parent, children, isRoot, byMasks, ...rest } = file
46
const [values, setValues] = useState(rest)
47
useEffect(() => {
48
- setValues(Object.assign(objSameKeys(defaultPerms, () => null), rest))
48
+ setValues(Object.assign(_.mapValues(defaultPerms, () => null), rest))
49
}, [file]) //eslint-disable-line
50
51
const inheritedDefault = useMemo(() => {
admin/src/HomePage.ts
+2
-2
@@ -4,7 +4,7 @@ import { createElement as h, ReactNode, useState } from 'react'
4
import { Box, Card, CardContent, Link } from '@mui/material'
5
import { apiCall, useApiEx, useApiList } from './api'
6
import {
7
- dontBotherWithKeys, objSameKeys, onlyTruthy, prefix, REPO_URL, md,
7
+ dontBotherWithKeys, onlyTruthy, prefix, REPO_URL, md,
8
replaceStringToReact, wait, with_, DAY, HOUR, PREVIOUS_TAG
9
} from './misc'
10
import { Btn, Flex, InLink, LinkBtn, wikiLink, } from './mui'
@@ -40,7 +40,7 @@ export default function HomePage() {
40
const goSecure = !http?.listening && https?.listening ? 's' : ''
41
const srv = goSecure ? https : (http?.listening && http)
42
const href = srv && `http${goSecure}://`+window.location.hostname + (srv.port === (goSecure ? 443 : 80) ? '' : ':'+srv.port)
43
- const serverErrors = objSameKeys({ http, https }, v =>
43
+ const serverErrors = _.mapValues({ http, https }, v =>
44
v.busy ? [`port ${v.configuredPort} already used by ${v.busy}${SOLUTION_SEP}choose a `, cfgLink('different port'), ` or stop ${v.busy}`]
45
: v.error )
46
const errors = serverErrors && onlyTruthy(Object.entries(serverErrors).map(([k,v]) =>
admin/src/api.ts
+2
-2
@@ -1,7 +1,7 @@
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, useCallback, useEffect, useMemo, useRef, useState } from 'react'
4
-import { Dict, err2msg, Falsy, LIST, useStateMounted, wantArray, xlate, objSameKeys,
4
+import { Dict, err2msg, Falsy, LIST, useStateMounted, wantArray, xlate,
5
HTTP_FORBIDDEN, HTTP_UNAUTHORIZED } from './misc'
6
import { IconBtn, spinner } from './mui'
7
import { Alert } from '@mui/material'
@@ -73,7 +73,7 @@ export function useApiList<T=any, S=T>(cmd:string|Falsy, params: Dict={}, { map,
73
setConnecting(true)
74
setInitializing(true)
75
setList([])
76
- const src = apiEvents(cmd, objSameKeys(params, x => x === false ? undefined : x), (type, data) => {
76
+ const src = apiEvents(cmd, _.mapValues(params, x => x === false ? undefined : x), (type, data) => {
77
switch (type) {
78
case 'connected':
79
setConnecting(false)
frontend/src/state.ts
+2
-2
@@ -3,7 +3,7 @@
3
import _ from 'lodash'
4
import { proxy, useSnapshot } from 'valtio'
5
import { subscribeKey } from 'valtio/utils'
6
-import { Dict, FRONTEND_OPTIONS, getHFS, hfsEvent, hIcon, objSameKeys, pathEncode, typedKeys } from './misc'
6
+import { FRONTEND_OPTIONS, getHFS, hfsEvent, hIcon, pathEncode, typedKeys } from './misc'
7
import { DirEntry as ServerDirEntry } from '../../src/api.get_file_list'
8
9
export const state = proxy<typeof FRONTEND_OPTIONS & {
@@ -45,7 +45,7 @@ export const state = proxy<typeof FRONTEND_OPTIONS & {
45
uri: '',
46
canChangePassword: false,
47
props: {},
48
- ...objSameKeys(FRONTEND_OPTIONS, (v,k) => getHFS()[k] ?? v),
48
+ ..._.mapValues(FRONTEND_OPTIONS, (v,k) => getHFS()[k] ?? v),
49
iconsReady: false,
50
username: '',
51
list: [],
shared/dialogs.ts
+3
-2
@@ -3,7 +3,8 @@
3
import { createElement as h, Fragment, FunctionComponent, isValidElement, ReactNode, useEffect, useRef,
4
HTMLAttributes, useState } from 'react'
5
import { proxy, ref, useSnapshot } from 'valtio'
6
-import { domOn, isPrimitive, objSameKeys, wait } from '.'
6
+import _ from 'lodash'
7
+import { domOn, isPrimitive, wait } from '.'
8
9
export interface DialogOptions {
10
Content: FunctionComponent<any>,
@@ -209,7 +210,7 @@ export function componentOrNode(x: ReactNode | FunctionComponent) {
210
export function newDialog(options: DialogOptions) {
211
const $id = Math.random()
212
const ts = performance.now()
212
- const d: Dialog = Object.assign(objSameKeys(options, x => isValidElement(x) ? ref(x) : x) as typeof options, { // encapsulate elements as React will try to write, but valtio makes them readonly
213
+ const d: Dialog = Object.assign(_.mapValues(options, x => isValidElement(x) ? ref(x) : x) as typeof options, { // encapsulate elements as React will try to write, but valtio makes them readonly
214
close, ts, $id, // object identity is not working on dialog object because it's proxied (valtio). This is a possible workaround
215
restoreFocus: options.restoreFocus ?? ref(document.activeElement || {}),
216
})
shared/index.ts
+2
-2
@@ -3,7 +3,7 @@
3
import _ from 'lodash'
4
import { apiCall } from './api'
5
import {
6
- DAY, Dict, formatBytes, HOUR, MINUTE, objFromKeys, objSameKeys, typedEntries, wantArray, stringBefore,
6
+ DAY, Dict, formatBytes, HOUR, MINUTE, objFromKeys, typedEntries, wantArray, stringBefore,
7
PLUGINS_PUB_URI
8
} from '../src/cross'
9
export * from './react'
@@ -202,7 +202,7 @@ type DurationUnit = 'day' | 'hour' | 'minute' | 'second'
202
export function createDurationFormatter({ locale=undefined, unitDisplay='narrow', largest='day', smallest='second', maxTokens, skipZeroes }:
203
{ skipZeroes?: boolean, largest?: DurationUnit, smallest?: DurationUnit, locale?: string, unitDisplay?: 'long' | 'short' | 'narrow', maxTokens?: 1 | 2 | 3 }={}) {
204
const multipliers: Record<DurationUnit, number> = { day: DAY, hour: HOUR, minute: MINUTE, second: 1000 }
205
- const fmt = objSameKeys(multipliers, (v,k) => Intl.NumberFormat(locale, { style: 'unit', unit: k, unitDisplay }).format)
205
+ const fmt = _.mapValues(multipliers, (v,k) => Intl.NumberFormat(locale, { style: 'unit', unit: k, unitDisplay }).format)
206
const fmtList = new Intl.ListFormat(locale, { style: 'narrow', type: 'unit' })
207
return (ms: number) => {
208
const a = []
src/cross.ts
-4
@@ -143,10 +143,6 @@ export function haveTimeout<T>(ms: number, job: Promise<T>, error?: any) {
143
])
144
}
145
146
-export function objSameKeys<S extends object,VR=any>(src: S, newValue:(value:Truthy<S[keyof S]>, key:keyof S)=>VR) {
147
- return Object.fromEntries(Object.entries(src).map(([k,v]) => [k, newValue(v,k as keyof S)])) as { [K in keyof S]:VR }
148
-}
149
-
146
export function objFromKeys<K extends string, VR=unknown>(src: K[], getValue: (value: K)=> VR) {
147
return Object.fromEntries(src.map(k => [k, getValue(k)]))
148
}
src/listen.ts
+2
-2
@@ -10,7 +10,7 @@ import { getConnections, newConnection } from './connections'
10
import { TLSSocket } from 'node:tls'
11
import open from 'open'
12
import {
13
- CFG, debounceAsync, ipForUrl, makeNetMatcher, MINUTE, objSameKeys, onlyTruthy, prefix, runAt, wait, xlate
13
+ CFG, debounceAsync, ipForUrl, makeNetMatcher, MINUTE, onlyTruthy, prefix, runAt, wait, xlate
14
} from './misc'
15
import { PORT_DISABLED, ADMIN_URI, IS_WINDOWS } from './const'
16
import findProcess from 'find-process'
@@ -105,7 +105,7 @@ export function getCertObject() {
105
if (!c) return
106
const all = new X509Certificate(c)
107
const some = _.pick(all, ['subject', 'issuer', 'validFrom', 'validTo'])
108
- const ret = objSameKeys(some, v => v?.includes('=') ? Object.fromEntries(v.split('\n').map(x => x.split('='))) : v)
108
+ const ret = _.mapValues(some, v => v?.includes('=') ? Object.fromEntries(v.split('\n').map(x => x.split('='))) : v)
109
return Object.assign(ret, { altNames: all.subjectAltName?.replace(/DNS:/g, '').split(/, */) })
110
}
111
src/perm.ts
+2
-2
@@ -1,7 +1,7 @@
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 _ from 'lodash'
4
-import { objRenameKey, objSameKeys, setHidden, typedEntries, wantArray } from './misc'
4
+import { objRenameKey, setHidden, typedEntries, wantArray } from './misc'
5
import { defineConfig, saveConfigAsap } from './config'
6
import { createVerifierAndSalt, SRPParameters, SRPRoutines } from 'tssrp6a'
7
import events from './events'
@@ -88,7 +88,7 @@ export async function updateAccount(account: Account, change: Partial<Account> |
88
const u = normalizeUsername(change.username || '')
89
if (u && u !== usernameWas && getAccount(u))
90
throw "username already exists"
91
- Object.assign(account, objSameKeys(change, x => x || undefined))
91
+ Object.assign(account, _.mapValues(change, x => x || undefined))
92
}
93
for (const [k,v] of typedEntries(account))
94
if (!v) delete account[k] // we consider all account fields, when falsy, as equivalent to be missing (so, default value applies)
src/plugins.ts
+3
-3
@@ -10,7 +10,7 @@ import {
10
import * as Const from './const'
11
import Koa from 'koa'
12
import {
13
- escapeGlobPath, callable, Callback, CFG, debounceAsync, Dict, objSameKeys, onlyTruthy, prefix,
13
+ escapeGlobPath, callable, Callback, CFG, debounceAsync, Dict, onlyTruthy, prefix,
14
PendingPromise, pendingPromise, Promisable, same, tryJson, wait, waitFor, wantArray, watchDir, objFromKeys, patchKey
15
} from './misc'
16
import * as misc from './misc'
@@ -129,7 +129,7 @@ async function initPlugin(pl: any, morePassedToInit?: { id: string } & Dict) {
129
const controlledEvents = Object.create(events, objFromKeys(['on', 'once', 'multi'], k => ({
130
value() {
131
if (k === 'multi')
132
- arguments[0] = objSameKeys(arguments[0], trap)
132
+ arguments[0] = _.mapValues(arguments[0], trap)
133
else
134
arguments[1] = trap(arguments[1])
135
const ret = (events[k] as any)(...arguments)
@@ -581,7 +581,7 @@ function watchPlugin(id: string, path: string) {
581
getConfig(cfgKey?: string) {
582
const cur = pluginsConfig.get()?.[id]
583
return cfgKey ? cur?.[cfgKey] ?? pluginData.config?.[cfgKey]?.defaultValue
584
- : _.defaults(cur, objSameKeys(pluginData.config, x => x.defaultValue))
584
+ : _.defaults(cur, _.mapValues(pluginData.config, x => x.defaultValue))
585
},
586
setConfig: (cfgKey: string, value: any) =>
587
setPluginConfig(id, { [cfgKey]: value }),
src/serveGuiFiles.ts
+3
-3
@@ -11,7 +11,7 @@ import { authApis } from './api.auth'
11
import { ApiError } from './apiMiddleware'
12
import { join, extname, sep } from 'path'
13
import {
14
- CFG, debounceAsync, formatBytes, FRONTEND_OPTIONS, isPrimitive, newObj, objSameKeys, onlyTruthy, parseFile,
14
+ CFG, debounceAsync, formatBytes, FRONTEND_OPTIONS, isPrimitive, newObj, onlyTruthy, parseFile,
15
enforceStarting, statWithTimeout, shortenAgent
16
} from './misc'
17
import { favicon, title } from './adminApis'
@@ -130,7 +130,7 @@ async function treatIndex(ctx: Koa.Context, filesUri: string, body: string) {
130
${getSection('htmlHead')}`}
131
`
132
function iconsToObj(icons: CustomizedIcons, pre='') {
133
- return icons && objSameKeys(icons, (v, k) => ctx.state.revProxyPath + ICONS_URI + pre + k)
133
+ return icons && _.mapValues(icons, (v, k) => ctx.state.revProxyPath + ICONS_URI + pre + k)
134
}
135
136
if (isBody && isOpen)
@@ -171,7 +171,7 @@ async function treatIndex(ctx: Koa.Context, filesUri: string, body: string) {
171
v = ctx.state.revProxyPath + v
172
}
173
else if (type === 'array' && Array.isArray(v))
174
- v = v.map(x => objSameKeys(x, (xv, xk) => adjustValueByConfig(xv, cfg.fields[xk])))
174
+ v = v.map(x => _.mapValues(x, (xv, xk) => adjustValueByConfig(xv, cfg.fields[xk])))
175
return v
176
}
177