Refactoring Document storage #5
|
@ -79,28 +79,28 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, watchEffect, onMounted, onUnmounted } from 'vue'
|
import { ref, computed, watchEffect, shallowRef } from 'vue'
|
||||||
import { useDocumentStore } from '@/stores/documents'
|
import { useDocumentStore } from '@/stores/documents'
|
||||||
import type { Document } from '@/repositories/Document'
|
import { Doc } from '@/repositories/Document'
|
||||||
import FileRenameInput from './FileRenameInput.vue'
|
import FileRenameInput from './FileRenameInput.vue'
|
||||||
import { connect, controlUrl } from '@/repositories/WS'
|
import { connect, controlUrl } from '@/repositories/WS'
|
||||||
import { collator, formatSize, formatUnixDate } from '@/utils'
|
import { collator, formatSize } from '@/utils'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
path: Array<string>
|
path: Array<string>
|
||||||
documents: Document[]
|
documents: Doc[]
|
||||||
}>()
|
}>()
|
||||||
const documentStore = useDocumentStore()
|
const documentStore = useDocumentStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const url_for = (doc: Document) => {
|
const url_for = (doc: Doc) => {
|
||||||
const p = doc.loc ? `${doc.loc}/${doc.name}` : doc.name
|
const p = doc.loc ? `${doc.loc}/${doc.name}` : doc.name
|
||||||
return doc.dir ? `#/${p}/` : `/files/${p}`
|
return doc.dir ? `#/${p}/` : `/files/${p}`
|
||||||
}
|
}
|
||||||
const cursor = ref<Document | null>(null)
|
const cursor = shallowRef<Doc | null>(null)
|
||||||
// File rename
|
// File rename
|
||||||
const editing = ref<Document | null>(null)
|
const editing = shallowRef<Doc | null>(null)
|
||||||
const rename = (doc: Document, newName: string) => {
|
const rename = (doc: Doc, newName: string) => {
|
||||||
const oldName = doc.name
|
const oldName = doc.name
|
||||||
const control = connect(controlUrl, {
|
const control = connect(controlUrl, {
|
||||||
message(ev: MessageEvent) {
|
message(ev: MessageEvent) {
|
||||||
|
@ -124,7 +124,7 @@ const rename = (doc: Document, newName: string) => {
|
||||||
}
|
}
|
||||||
doc.name = newName // We should get an update from watch but this is quicker
|
doc.name = newName // We should get an update from watch but this is quicker
|
||||||
}
|
}
|
||||||
const sortedDocuments = computed(() => sorted(props.documents as Document[]))
|
const sortedDocuments = computed(() => sorted(props.documents))
|
||||||
const showFolderBreadcrumb = (i: number) => {
|
const showFolderBreadcrumb = (i: number) => {
|
||||||
const docs = sortedDocuments.value
|
const docs = sortedDocuments.value
|
||||||
const docloc = docs[i].loc
|
const docloc = docs[i].loc
|
||||||
|
@ -132,19 +132,15 @@ const showFolderBreadcrumb = (i: number) => {
|
||||||
}
|
}
|
||||||
defineExpose({
|
defineExpose({
|
||||||
newFolder() {
|
newFolder() {
|
||||||
const now = Date.now() / 1000
|
const now = Math.floor(Date.now() / 1000)
|
||||||
editing.value = {
|
editing.value = new Doc({
|
||||||
loc: loc.value,
|
loc: loc.value,
|
||||||
key: 'new',
|
key: 'new',
|
||||||
name: 'New Folder',
|
name: 'New Folder',
|
||||||
dir: true,
|
dir: true,
|
||||||
mtime: now,
|
mtime: now,
|
||||||
size: 0,
|
size: 0,
|
||||||
sizedisp: formatSize(0),
|
})
|
||||||
modified: formatUnixDate(now),
|
|
||||||
haystack: '',
|
|
||||||
}
|
|
||||||
console.log("New")
|
|
||||||
},
|
},
|
||||||
toggleSelectAll() {
|
toggleSelectAll() {
|
||||||
console.log('Select')
|
console.log('Select')
|
||||||
|
@ -229,14 +225,7 @@ watchEffect(() => {
|
||||||
focusBreadcrumb()
|
focusBreadcrumb()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// Update human-readable x seconds ago messages from mtimes
|
const mkdir = (doc: Doc, name: string) => {
|
||||||
let modifiedTimer: any = null
|
|
||||||
const updateModified = () => {
|
|
||||||
for (const doc of props.documents) doc.modified = formatUnixDate(doc.mtime)
|
|
||||||
}
|
|
||||||
onMounted(() => { updateModified(); modifiedTimer = setInterval(updateModified, 1000) })
|
|
||||||
onUnmounted(() => { clearInterval(modifiedTimer) })
|
|
||||||
const mkdir = (doc: Document, name: string) => {
|
|
||||||
const control = connect(controlUrl, {
|
const control = connect(controlUrl, {
|
||||||
open() {
|
open() {
|
||||||
control.send(
|
control.send(
|
||||||
|
@ -257,7 +246,9 @@ const mkdir = (doc: Document, name: string) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
doc.name = name // We should get an update from watch but this is quicker
|
// We should get an update from watch but this is quicker
|
||||||
|
doc.name = name
|
||||||
|
doc.key = crypto.randomUUID()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Column sort
|
// Column sort
|
||||||
|
@ -266,11 +257,11 @@ const toggleSort = (name: string) => {
|
||||||
}
|
}
|
||||||
const sort = ref<string>('')
|
const sort = ref<string>('')
|
||||||
const sortCompare = {
|
const sortCompare = {
|
||||||
name: (a: Document, b: Document) => collator.compare(a.name, b.name),
|
name: (a: Doc, b: Doc) => collator.compare(a.name, b.name),
|
||||||
modified: (a: Document, b: Document) => b.mtime - a.mtime,
|
modified: (a: Doc, b: Doc) => b.mtime - a.mtime,
|
||||||
size: (a: Document, b: Document) => b.size - a.size
|
size: (a: Doc, b: Doc) => b.size - a.size
|
||||||
}
|
}
|
||||||
const sorted = (documents: Document[]) => {
|
const sorted = (documents: Doc[]) => {
|
||||||
const cmp = sortCompare[sort.value as keyof typeof sortCompare]
|
const cmp = sortCompare[sort.value as keyof typeof sortCompare]
|
||||||
const sorted = [...documents]
|
const sorted = [...documents]
|
||||||
if (cmp) sorted.sort(cmp)
|
if (cmp) sorted.sort(cmp)
|
||||||
|
@ -280,7 +271,7 @@ const selectionIndeterminate = computed({
|
||||||
get: () => {
|
get: () => {
|
||||||
return (
|
return (
|
||||||
props.documents.length > 0 &&
|
props.documents.length > 0 &&
|
||||||
props.documents.some((doc: Document) => documentStore.selected.has(doc.key)) &&
|
props.documents.some((doc: Doc) => documentStore.selected.has(doc.key)) &&
|
||||||
!allSelected.value
|
!allSelected.value
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -291,7 +282,7 @@ const allSelected = computed({
|
||||||
get: () => {
|
get: () => {
|
||||||
return (
|
return (
|
||||||
props.documents.length > 0 &&
|
props.documents.length > 0 &&
|
||||||
props.documents.every((doc: Document) => documentStore.selected.has(doc.key))
|
props.documents.every((doc: Doc) => documentStore.selected.has(doc.key))
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
set: (value: boolean) => {
|
set: (value: boolean) => {
|
||||||
|
@ -308,7 +299,7 @@ const allSelected = computed({
|
||||||
|
|
||||||
const loc = computed(() => props.path.join('/'))
|
const loc = computed(() => props.path.join('/'))
|
||||||
|
|
||||||
const contextMenu = (ev: Event, doc: Document) => {
|
const contextMenu = (ev: Event, doc: Doc) => {
|
||||||
cursor.value = doc
|
cursor.value = doc
|
||||||
console.log('Context menu', ev, doc)
|
console.log('Context menu', ev, doc)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Document } from '@/repositories/Document'
|
import { Doc } from '@/repositories/Document'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
const datetime = computed(() =>
|
const datetime = computed(() =>
|
||||||
|
@ -17,6 +17,6 @@ const tooltip = computed(() =>
|
||||||
)
|
)
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
doc: Document
|
doc: Doc
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Document } from '@/repositories/Document'
|
import { Doc } from '@/repositories/Document'
|
||||||
import { ref, onMounted, nextTick } from 'vue'
|
import { ref, onMounted, nextTick } from 'vue'
|
||||||
|
|
||||||
const input = ref<HTMLInputElement | null>(null)
|
const input = ref<HTMLInputElement | null>(null)
|
||||||
|
@ -28,8 +28,8 @@ onMounted(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
doc: Document
|
doc: Doc
|
||||||
rename: (doc: Document, newName: string) => void
|
rename: (doc: Doc, newName: string) => void
|
||||||
exit: () => void
|
exit: () => void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Document } from '@/repositories/Document'
|
import { Doc } from '@/repositories/Document'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
const sizeClass = computed(() => {
|
const sizeClass = computed(() => {
|
||||||
|
@ -12,7 +12,7 @@ const sizeClass = computed(() => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
doc: Document
|
doc: Doc
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,34 @@
|
||||||
|
import { formatSize, formatUnixDate, haystackFormat } from "@/utils"
|
||||||
|
|
||||||
export type FUID = string
|
export type FUID = string
|
||||||
|
|
||||||
export type Document = {
|
export type DocProps = {
|
||||||
loc: string
|
loc: string
|
||||||
name: string
|
name: string
|
||||||
key: FUID
|
key: FUID
|
||||||
size: number
|
size: number
|
||||||
sizedisp: string
|
|
||||||
mtime: number
|
mtime: number
|
||||||
modified: string
|
|
||||||
haystack: string
|
|
||||||
dir: boolean
|
dir: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class Doc {
|
||||||
|
private _name: string = ""
|
||||||
|
public loc: string = ""
|
||||||
|
public key: FUID = ""
|
||||||
|
public size: number = 0
|
||||||
|
public mtime: number = 0
|
||||||
|
public haystack: string = ""
|
||||||
|
public dir: boolean = false
|
||||||
|
|
||||||
|
constructor(props: Partial<DocProps> = {}) { Object.assign(this, props) }
|
||||||
|
get name() { return this._name }
|
||||||
|
set name(name: string) {
|
||||||
|
this._name = name
|
||||||
|
this.haystack = haystackFormat(name)
|
||||||
|
}
|
||||||
|
get sizedisp(): string { return formatSize(this.size) }
|
||||||
|
get modified(): string { return formatUnixDate(this.mtime) }
|
||||||
|
}
|
||||||
export type errorEvent = {
|
export type errorEvent = {
|
||||||
error: {
|
error: {
|
||||||
code: number
|
code: number
|
||||||
|
@ -36,7 +53,7 @@ export type UpdateEntry = ['k', number] | ['d', number] | ['i', Array<FileEntry>
|
||||||
// Helper structure for selections
|
// Helper structure for selections
|
||||||
export interface SelectedItems {
|
export interface SelectedItems {
|
||||||
keys: FUID[]
|
keys: FUID[]
|
||||||
docs: Record<FUID, Document>
|
docs: Record<FUID, Doc>
|
||||||
recursive: Array<[string, string, Document]>
|
recursive: Array<[string, string, Doc]>
|
||||||
missing: Set<FUID>
|
missing: Set<FUID>
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
import type { Document, FileEntry, FUID, SelectedItems } from '@/repositories/Document'
|
import type { FileEntry, FUID, SelectedItems } from '@/repositories/Document'
|
||||||
import { formatSize, formatUnixDate, haystackFormat } from '@/utils'
|
import { Doc } from '@/repositories/Document'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { collator } from '@/utils'
|
import { collator } from '@/utils'
|
||||||
import { logoutUser } from '@/repositories/User'
|
import { logoutUser } from '@/repositories/User'
|
||||||
import { watchConnect } from '@/repositories/WS'
|
import { watchConnect } from '@/repositories/WS'
|
||||||
|
import { shallowRef } from 'vue'
|
||||||
|
|
||||||
type FileData = { id: string; mtime: number; size: number; dir: DirectoryData }
|
|
||||||
type DirectoryData = {
|
|
||||||
[filename: string]: FileData
|
|
||||||
}
|
|
||||||
type User = {
|
type User = {
|
||||||
username: string
|
username: string
|
||||||
privileged: boolean
|
privileged: boolean
|
||||||
|
@ -19,7 +16,7 @@ type User = {
|
||||||
export const useDocumentStore = defineStore({
|
export const useDocumentStore = defineStore({
|
||||||
id: 'documents',
|
id: 'documents',
|
||||||
state: () => ({
|
state: () => ({
|
||||||
document: [] as Document[],
|
document: shallowRef<Doc[]>([]),
|
||||||
selected: new Set<FUID>(),
|
selected: new Set<FUID>(),
|
||||||
fileExplorer: null as any,
|
fileExplorer: null as any,
|
||||||
error: '' as string,
|
error: '' as string,
|
||||||
|
@ -38,20 +35,17 @@ export const useDocumentStore = defineStore({
|
||||||
let loc = [] as string[]
|
let loc = [] as string[]
|
||||||
for (const [level, name, key, mtime, size, isfile] of root) {
|
for (const [level, name, key, mtime, size, isfile] of root) {
|
||||||
loc = loc.slice(0, level - 1)
|
loc = loc.slice(0, level - 1)
|
||||||
docs.push({
|
docs.push(new Doc({
|
||||||
name,
|
name,
|
||||||
loc: level ? loc.join('/') : '/',
|
loc: level ? loc.join('/') : '/',
|
||||||
key,
|
key,
|
||||||
size,
|
size,
|
||||||
sizedisp: formatSize(size),
|
|
||||||
mtime,
|
mtime,
|
||||||
modified: formatUnixDate(mtime),
|
|
||||||
haystack: haystackFormat(name),
|
|
||||||
dir: !isfile,
|
dir: !isfile,
|
||||||
})
|
}))
|
||||||
loc.push(name)
|
loc.push(name)
|
||||||
}
|
}
|
||||||
this.document = docs as Document[]
|
this.document = docs
|
||||||
},
|
},
|
||||||
login(username: string, privileged: boolean) {
|
login(username: string, privileged: boolean) {
|
||||||
this.user.username = username
|
this.user.username = username
|
||||||
|
@ -75,12 +69,12 @@ export const useDocumentStore = defineStore({
|
||||||
isUserLogged(): boolean {
|
isUserLogged(): boolean {
|
||||||
return this.user.isLoggedIn
|
return this.user.isLoggedIn
|
||||||
},
|
},
|
||||||
recentDocuments(): Document[] {
|
recentDocuments(): Doc[] {
|
||||||
const ret = [...this.document]
|
const ret = [...this.document]
|
||||||
ret.sort((a, b) => b.mtime - a.mtime)
|
ret.sort((a, b) => b.mtime - a.mtime)
|
||||||
return ret
|
return ret
|
||||||
},
|
},
|
||||||
largeDocuments(): Document[] {
|
largeDocuments(): Doc[] {
|
||||||
const ret = [...this.document]
|
const ret = [...this.document]
|
||||||
ret.sort((a, b) => b.size - a.size)
|
ret.sort((a, b) => b.size - a.size)
|
||||||
return ret
|
return ret
|
||||||
|
@ -105,7 +99,7 @@ export const useDocumentStore = defineStore({
|
||||||
for (const key of selected) if (!found.has(key)) ret.missing.add(key)
|
for (const key of selected) if (!found.has(key)) ret.missing.add(key)
|
||||||
// Build a flat list including contents recursively
|
// Build a flat list including contents recursively
|
||||||
const relnames = new Set<string>()
|
const relnames = new Set<string>()
|
||||||
function add(rel: string, full: string, doc: Document) {
|
function add(rel: string, full: string, doc: Doc) {
|
||||||
if (!doc.dir && relnames.has(rel)) throw Error(`Multiple selections conflict for: ${rel}`)
|
if (!doc.dir && relnames.has(rel)) throw Error(`Multiple selections conflict for: ${rel}`)
|
||||||
relnames.add(rel)
|
relnames.add(rel)
|
||||||
ret.recursive.push([rel, full, doc])
|
ret.recursive.push([rel, full, doc])
|
||||||
|
|
Loading…
Reference in New Issue
Block a user