?onlyFiles and ?onlyFolders #686
Massimo Melina committed
Nov 30, 2024 at 00:09 UTC
41ff4f22731c8acadd78035171f29b4ad4a7caa0
5 files changed
+24
-11
README.md
+2
-1
@@ -126,7 +126,8 @@ If your language is missing, please consider [translating yourself](https://gith
126
- Right-click on toggle-all checkbox will invert each checkbox state
127
- Appending `?login=USER:PASSWORD` will automatically log in the browser
128
- Appending `?overwrite` on uploads, will override the dont_overwrite_uploading configuration, provided you also have delete permission
129
-- Appending `?search=PATTERN` will trigger search at start
129
+- Appending `?search=PATTERN` will trigger search at start
130
+- Appending `?onlyFiles` or `?onlyFolders` will limit type of results
131
- Appending `?autoplay=shuffle` will trigger show & play; `?autoplay` will not shuffle, but also will not start until the list is complete
132
- Right-click on "check for updates" will let you input a URL of a version to install
133
- Shift+click on a file will show & play
frontend/src/useFetchList.ts
+11
-4
@@ -7,7 +7,10 @@ import _ from 'lodash'
7
import { subscribeKey } from 'valtio/utils'
8
import { useIsMounted } from 'usehooks-ts'
9
import { alertDialog } from './dialog'
10
-import { hfsEvent, HTTP_MESSAGES, HTTP_METHOD_NOT_ALLOWED, HTTP_UNAUTHORIZED, LIST, urlParams, xlate } from './misc'
10
+import {
11
+ hfsEvent, LIST, urlParams, xlate, objFromKeys,
12
+ HTTP_MESSAGES, HTTP_METHOD_NOT_ALLOWED, HTTP_UNAUTHORIZED,
13
+} from './misc'
14
import { t } from './i18n'
15
import { useLocation, useNavigate } from 'react-router-dom'
16
import { closeLoginDialog } from './login'
@@ -18,8 +21,11 @@ export function usePath() {
21
}
22
23
// allow links with ?search
21
-setTimeout(() => // wait, urlParams is defined at top level
22
- state.remoteSearch = urlParams.search || '')
24
+let firstListRequest: any
25
+setTimeout(() => {// wait, urlParams is defined at top level
26
+ state.remoteSearch = urlParams.search || ''
27
+ firstListRequest = objFromKeys(['onlyFiles', 'onlyFolders'], x => x in urlParams || undefined)
28
+})
29
30
let autoPlayOnce: string | undefined = urlParams.autoplay // this will be consumed, so to only act once
31
@@ -47,7 +53,7 @@ export default function useFetchList() {
53
return
54
}
55
50
- const params = { uri, search, ...snap.searchOptions }
56
+ const params = { uri, search, ...firstListRequest, ...snap.searchOptions }
57
params.wild = params.wild ? undefined : 'no'
58
if (snap.listReloader === lastReloader.current && _.isEqual(params, lastParams.current)) return
59
lastParams.current = params
@@ -83,6 +89,7 @@ export default function useFetchList() {
89
if (autoPlayOnce === '') play = true
90
if (autoPlayOnce === 'shuffle') playShuffle = true
91
autoPlayOnce = undefined
92
+ firstListRequest = undefined
93
return
94
case 'error':
95
state.stopSearch?.()
src/api.get_file_list.ts
+2
-2
@@ -19,7 +19,7 @@ import { SendListReadable } from './SendList'
19
20
export interface DirEntry { n:string, s?:number, m?:Date, c?:Date, p?: string, comment?: string, web?: boolean, url?: string, target?: string, icon?: string | true }
21
22
-export const get_file_list: ApiHandler = async ({ uri='/', offset, limit, search, wild, c, onlyFolders, admin }, ctx) => {
22
+export const get_file_list: ApiHandler = async ({ uri='/', offset, limit, search, wild, c, onlyFolders, onlyFiles, admin }, ctx) => {
23
const node = await urlToNode(uri, ctx)
24
const list = ctx.get('accept') === 'text/event-stream' ? new SendListReadable() : undefined
25
if (!node)
@@ -37,7 +37,7 @@ export const get_file_list: ApiHandler = async ({ uri='/', offset, limit, search
37
search = String(search || '').toLocaleLowerCase()
38
const filter = wild === 'no' ? (s: string) => s.includes(search)
39
: pattern2filter(search)
40
- const walker = walkNode(node, { ctx: admin ? undefined : ctx, onlyFolders, depth: search ? Infinity : 0 })
40
+ const walker = walkNode(node, { ctx: admin ? undefined : ctx, onlyFolders, onlyFiles, depth: search ? Infinity : 0 })
41
const onDirEntryHandlers = mapPlugins(plug => plug.onDirEntry)
42
const can_upload = admin || hasPermission(node, 'can_upload', ctx)
43
const fakeChild = await applyParentToChild({ source: 'dummy-file' }, node) // used to check permission; simple but but can produce false results
src/cross.ts
+4
@@ -128,6 +128,10 @@ export function objSameKeys<S extends object,VR=any>(src: S, newValue:(value:Tru
128
return Object.fromEntries(Object.entries(src).map(([k,v]) => [k, newValue(v,k as keyof S)])) as { [K in keyof S]:VR }
129
}
130
131
+export function objFromKeys<K extends string, VR=unknown>(src: K[], getValue: (value: K)=> VR) {
132
+ return Object.fromEntries(src.map(k => [k, getValue(k)]))
133
+}
134
+
135
export function enforceFinal(sub:string, s:string, evenEmpty=false) {
136
return (s ? !s.endsWith(sub) : evenEmpty) ? s + sub : s
137
}
src/vfs.ts
+5
-4
@@ -268,15 +268,16 @@ export async function* walkNode(parent: VfsNode, {
268
depth = Infinity,
269
prefixPath = '',
270
requiredPerm,
271
- onlyFolders = false
272
-}: { ctx?: Koa.Context,depth?: number, prefixPath?: string, requiredPerm?: undefined | keyof VfsPerms, onlyFolders?: boolean } = {}): AsyncIterableIterator<VfsNode> {
271
+ onlyFolders = false,
272
+ onlyFiles = false,
273
+}: { ctx?: Koa.Context,depth?: number, prefixPath?: string, requiredPerm?: undefined | keyof VfsPerms, onlyFolders?: boolean, onlyFiles?: boolean } = {}): AsyncIterableIterator<VfsNode> {
274
const { children, source } = parent
275
const took = prefixPath ? undefined : new Set()
276
const maskApplier = parentMaskApplier(parent)
277
const parentsCache = new Map() // we use this only if depth > 0
278
if (children)
279
for (const child of children) {
279
- if (onlyFolders && !await nodeIsDirectory(child)) continue
280
+ if (await nodeIsDirectory(child) ? onlyFiles : onlyFolders) continue
281
const nodeName = getNodeName(child)
282
const name = prefixPath + nodeName
283
took?.add(normalizeFilename(name))
@@ -302,7 +303,7 @@ export async function* walkNode(parent: VfsNode, {
303
try {
304
let lastDir = prefixPath.slice(0, -1) || '.'
305
parentsCache.set(lastDir, parent)
305
- for await (const entry of dirStream(source, { depth, onlyFolders, hidden: showHiddenFiles.get() })) {
306
+ for await (const entry of dirStream(source, { depth, onlyFolders, onlyFiles, hidden: showHiddenFiles.get() })) {
307
if (ctx?.req.aborted)
308
return
309
const {path} = entry