Rewrite document store to keep all docs: filter and path selection without recreation. Much faster sorting and filtering.
This commit is contained in:
@@ -6,10 +6,9 @@ import type {
|
||||
DirList,
|
||||
SelectedItems
|
||||
} from '@/repositories/Document'
|
||||
import { formatSize, formatUnixDate } from '@/utils'
|
||||
import { needleFormat, formatSize, formatUnixDate, haystackFormat, localeIncludes } from '@/utils'
|
||||
import { defineStore } from 'pinia'
|
||||
// @ts-ignore
|
||||
import { localeIncludes } from 'locale-includes'
|
||||
import { collator } from '@/utils'
|
||||
|
||||
type FileData = { id: string; mtime: number; size: number; dir: DirectoryData }
|
||||
type DirectoryData = {
|
||||
@@ -25,6 +24,7 @@ type User = {
|
||||
export type DocumentStore = {
|
||||
root: DirEntry
|
||||
document: Document[]
|
||||
search: string
|
||||
selected: Set<FUID>
|
||||
uploadingDocuments: Array<{ key: number; name: string; progress: number }>
|
||||
uploadCount: number
|
||||
@@ -40,6 +40,7 @@ export const useDocumentStore = defineStore({
|
||||
state: (): DocumentStore => ({
|
||||
root: {} as DirEntry,
|
||||
document: [] as Document[],
|
||||
search: "" as string,
|
||||
selected: new Set<FUID>(),
|
||||
uploadingDocuments: [],
|
||||
uploadCount: 0 as number,
|
||||
@@ -56,89 +57,46 @@ export const useDocumentStore = defineStore({
|
||||
}),
|
||||
|
||||
actions: {
|
||||
updateTable(matched: DirList) {
|
||||
// Transform data
|
||||
const dataMapped = []
|
||||
for (const [name, attr] of Object.entries(matched)) {
|
||||
const { key, size, mtime } = attr
|
||||
const element: Document = {
|
||||
name,
|
||||
key,
|
||||
size,
|
||||
sizedisp: formatSize(size),
|
||||
mtime,
|
||||
modified: formatUnixDate(mtime),
|
||||
type: 'dir' in attr ? 'folder' : 'file'
|
||||
updateRoot(root: DirEntry | null = null) {
|
||||
root ??= this.root
|
||||
// Transform tree data to flat documents array
|
||||
let loc = [] as Array<string>
|
||||
const mapper = ([name, attr]: [string, FileEntry | DirEntry]) => ({
|
||||
loc,
|
||||
name,
|
||||
type: 'dir' in attr ? 'folder' : 'file' as 'folder' | 'file',
|
||||
...attr,
|
||||
sizedisp: formatSize(attr.size),
|
||||
modified: formatUnixDate(attr.mtime),
|
||||
haystack: haystackFormat(name),
|
||||
})
|
||||
const queue = [...Object.entries(root.dir).map(mapper)]
|
||||
const docs = []
|
||||
for (let doc; (doc = queue.shift()) !== undefined;) {
|
||||
docs.push(doc)
|
||||
if ("dir" in doc) {
|
||||
loc = [...doc.loc, doc.name]
|
||||
queue.push(...Object.entries(doc.dir).map(mapper))
|
||||
}
|
||||
dataMapped.push(element)
|
||||
}
|
||||
// Pre sort directory entries folders first then files, names in natural ordering
|
||||
dataMapped.sort((a, b) =>
|
||||
a.type === b.type
|
||||
? a.name.localeCompare(b.name, undefined, {
|
||||
numeric: true,
|
||||
sensitivity: 'base'
|
||||
})
|
||||
: a.type === 'folder'
|
||||
? -1
|
||||
: 1
|
||||
docs.sort((a, b) =>
|
||||
// @ts-ignore
|
||||
(a.type === "file") - (b.type === "file") ||
|
||||
collator.compare(a.name, b.name)
|
||||
)
|
||||
this.document = dataMapped
|
||||
this.root = root
|
||||
this.document = docs
|
||||
},
|
||||
setFilter(filter: string) {
|
||||
if (filter === '') return this.updateTable({})
|
||||
function traverseDir(data: DirEntry | FileEntry, path: string) {
|
||||
if (!('dir' in data)) return
|
||||
for (const [name, attr] of Object.entries(data.dir)) {
|
||||
const fullname = `${path}/${name}`
|
||||
if (
|
||||
localeIncludes(name, filter, {
|
||||
usage: 'search',
|
||||
numeric: true,
|
||||
sensitivity: 'base'
|
||||
})
|
||||
) {
|
||||
matched[fullname.slice(1)] = attr // No initial slash on name
|
||||
if (!--count) throw Error('Too many matches')
|
||||
}
|
||||
traverseDir(attr, fullname)
|
||||
}
|
||||
}
|
||||
let count = 100
|
||||
const matched: any = {}
|
||||
try {
|
||||
traverseDir(this.root, '')
|
||||
} catch (error: any) {
|
||||
if (error.message !== 'Too many matches') throw error
|
||||
}
|
||||
this.updateTable(matched)
|
||||
find(query: string): Document[] {
|
||||
const needle = needleFormat(query)
|
||||
return this.document.filter(doc => localeIncludes(doc.haystack, needle))
|
||||
},
|
||||
setActualDocument(location: string) {
|
||||
location = decodeURIComponent(location)
|
||||
let data: FileEntry | DirEntry = this.root
|
||||
const actualDirArr = []
|
||||
try {
|
||||
// Navigate to target folder
|
||||
for (const dirname of location.split('/').slice(1)) {
|
||||
if (!dirname) continue
|
||||
if (!('dir' in data)) throw Error('Target folder not available')
|
||||
actualDirArr.push(dirname)
|
||||
data = data.dir[dirname]
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Cannot show requested folder',
|
||||
location,
|
||||
actualDirArr.join('/'),
|
||||
error
|
||||
)
|
||||
}
|
||||
if (!('dir' in data)) {
|
||||
// Target folder not available
|
||||
this.document = []
|
||||
return
|
||||
}
|
||||
this.updateTable(data.dir)
|
||||
directory(loc: string[]) {
|
||||
const ret = this.document.filter(
|
||||
doc => doc.loc.length === loc.length && doc.loc.every((e, i) => e === loc[i])
|
||||
)
|
||||
return ret
|
||||
},
|
||||
updateUploadingDocuments(key: number, progress: number) {
|
||||
for (const d of this.uploadingDocuments) {
|
||||
|
||||
Reference in New Issue
Block a user