Fix direct uploads and downloads, transfer bar UI

This commit is contained in:
Leo Vasanko 2023-11-21 16:11:54 +00:00
parent 369dc3ecaf
commit a383358369
5 changed files with 81 additions and 62 deletions

View File

@ -10,6 +10,10 @@
<main> <main>
<RouterView :path="path.pathList" :query="path.query" /> <RouterView :path="path.pathList" :query="path.query" />
</main> </main>
<footer>
<TransferBar :status=store.uprogress @cancel=store.cancelUploads class=upload />
<TransferBar :status=store.dprogress @cancel=store.cancelDownloads class=download />
</footer>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">

View File

@ -1,6 +1,5 @@
<template> <template>
<SvgButton name="download" data-tooltip="Download" @click="download" /> <SvgButton name="download" data-tooltip="Download" @click="download" />
<TransferBar :status=progress @cancel=cancelDownloads />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -26,22 +25,22 @@ const status_init = {
filepos: 0, filepos: 0,
status: 'idle', status: 'idle',
} }
const progress = reactive({...status_init}) store.dprogress = {...status_init}
setInterval(() => { setInterval(() => {
if (Date.now() - progress.tlast > 3000) { if (Date.now() - store.dprogress.tlast > 3000) {
// Reset // Reset
progress.statbytes = 0 store.dprogress.statbytes = 0
progress.statdur = 1 store.dprogress.statdur = 1
} else { } else {
// Running average by decay // Running average by decay
progress.statbytes *= .9 store.dprogress.statbytes *= .9
progress.statdur *= .9 store.dprogress.statdur *= .9
} }
}, 100) }, 100)
const statReset = () => { const statReset = () => {
Object.assign(progress, status_init) Object.assign(store.dprogress, status_init)
progress.t0 = Date.now() store.dprogress.t0 = Date.now()
progress.tlast = progress.t0 + 1 store.dprogress.tlast = store.dprogress.t0 + 1
} }
const cancelDownloads = () => { const cancelDownloads = () => {
location.reload() // FIXME location.reload() // FIXME
@ -61,9 +60,9 @@ const filesystemdl = async (sel: SelectedItems, handle: FileSystemDirectoryHandl
console.log('Downloading to filesystem', sel.recursive) console.log('Downloading to filesystem', sel.recursive)
for (const [rel, full, doc] of sel.recursive) { for (const [rel, full, doc] of sel.recursive) {
if (doc.dir) continue if (doc.dir) continue
progress.files.push(rel) store.dprogress.files.push(rel)
++progress.filecount ++store.dprogress.filecount
progress.total += doc.size store.dprogress.total += doc.size
} }
for (const [rel, full, doc] of sel.recursive) { for (const [rel, full, doc] of sel.recursive) {
// Create any missing directories // Create any missing directories
@ -73,6 +72,7 @@ const filesystemdl = async (sel: SelectedItems, handle: FileSystemDirectoryHandl
} }
const r = rel.slice(hdir.length) const r = rel.slice(hdir.length)
for (const dir of r.split('/').slice(0, doc.dir ? undefined : -1)) { for (const dir of r.split('/').slice(0, doc.dir ? undefined : -1)) {
if (!dir) continue
hdir += `${dir}/` hdir += `${dir}/`
try { try {
h = await h.getDirectoryHandle(dir.normalize('NFC'), { create: true }) h = await h.getDirectoryHandle(dir.normalize('NFC'), { create: true })
@ -101,22 +101,22 @@ const filesystemdl = async (sel: SelectedItems, handle: FileSystemDirectoryHandl
throw new Error(`Failed to download ${url}: ${res.status} ${res.statusText}`) throw new Error(`Failed to download ${url}: ${res.status} ${res.statusText}`)
} }
if (res.body) { if (res.body) {
++progress.fileidx ++store.dprogress.fileidx
const reader = res.body.getReader() const reader = res.body.getReader()
await writable.truncate(0) await writable.truncate(0)
store.error = "Direct download." store.error = "Direct download."
progress.tlast = Date.now() store.dprogress.tlast = Date.now()
while (true) { while (true) {
const { value, done } = await reader.read() const { value, done } = await reader.read()
if (done) break if (done) break
await writable.write(value) await writable.write(value)
const now = Date.now() const now = Date.now()
const size = value.byteLength const size = value.byteLength
progress.xfer += size store.dprogress.xfer += size
progress.filepos += size store.dprogress.filepos += size
progress.statbytes += size store.dprogress.statbytes += size
progress.statdur += now - progress.tlast store.dprogress.statdur += now - store.dprogress.tlast
progress.tlast = now store.dprogress.tlast = now
} }
} }
await writable.close() await writable.close()

View File

@ -57,13 +57,12 @@ const speeddisp = computed(() => speed.value ? speed.value.toFixed(speed.value <
display: flex; display: flex;
flex-direction: column; flex-direction: column;
color: var(--primary-color); color: var(--primary-color);
position: fixed; width: 100%;
left: 0;
bottom: 0;
width: 100vw;
} }
.statustext { .statustext {
display: flex; display: flex;
align-items: center;
margin: 0 .5em;
padding: 0.5rem 0; padding: 0.5rem 0;
} }
span { span {
@ -84,4 +83,12 @@ span {
.position { min-width: 4em } .position { min-width: 4em }
.speed { min-width: 4em } .speed { min-width: 4em }
.upload .statustext::before {
font-size: 1.5em;
content: '🔺'
}
.download .statustext::before {
font-size: 1.5em;
content: '🔻'
}
</style> </style>

View File

@ -1,3 +1,12 @@
<template>
<template>
<input ref="fileInput" @change="uploadHandler" type="file" multiple>
<input ref="folderInput" @change="uploadHandler" type="file" webkitdirectory>
</template>
<SvgButton name="add-file" data-tooltip="Upload files" @click="fileInput.click()" />
<SvgButton name="add-folder" data-tooltip="Upload folder" @click="folderInput.click()" />
</template>
<script setup lang="ts"> <script setup lang="ts">
import { connect, uploadUrl } from '@/repositories/WS'; import { connect, uploadUrl } from '@/repositories/WS';
import { useMainStore } from '@/stores/main' import { useMainStore } from '@/stores/main'
@ -108,50 +117,50 @@ const uprogress_init = {
filepos: 0, filepos: 0,
status: 'idle', status: 'idle',
} }
const uprogress = reactive({...uprogress_init}) store.uprogress = {...uprogress_init}
setInterval(() => { setInterval(() => {
if (Date.now() - uprogress.tlast > 3000) { if (Date.now() - store.uprogress.tlast > 3000) {
// Reset // Reset
uprogress.statbytes = 0 store.uprogress.statbytes = 0
uprogress.statdur = 1 store.uprogress.statdur = 1
} else { } else {
// Running average by decay // Running average by decay
uprogress.statbytes *= .9 store.uprogress.statbytes *= .9
uprogress.statdur *= .9 store.uprogress.statdur *= .9
} }
}, 100) }, 100)
const statUpdate = ({name, size, start, end}: {name: string, size: number, start: number, end: number}) => { const statUpdate = ({name, size, start, end}: {name: string, size: number, start: number, end: number}) => {
if (name !== uprogress.filename) return // If stats have been reset if (name !== store.uprogress.filename) return // If stats have been reset
const now = Date.now() const now = Date.now()
uprogress.xfer = uprogress.filestart + end store.uprogress.xfer = store.uprogress.filestart + end
uprogress.filepos = end store.uprogress.filepos = end
uprogress.statbytes += end - start store.uprogress.statbytes += end - start
uprogress.statdur += now - uprogress.tlast store.uprogress.statdur += now - store.uprogress.tlast
uprogress.tlast = now store.uprogress.tlast = now
// File finished? // File finished?
if (end === size) { if (end === size) {
uprogress.filestart += size store.uprogress.filestart += size
statNextFile() statNextFile()
if (++uprogress.fileidx >= uprogress.filecount) statReset() if (++store.uprogress.fileidx >= store.uprogress.filecount) statReset()
} }
} }
const statNextFile = () => { const statNextFile = () => {
const f = uprogress.files.shift() const f = store.uprogress.files.shift()
if (!f) return statReset() if (!f) return statReset()
uprogress.filepos = 0 store.uprogress.filepos = 0
uprogress.filesize = f.file.size store.uprogress.filesize = f.file.size
uprogress.filename = f.cloudName store.uprogress.filename = f.cloudName
} }
const statReset = () => { const statReset = () => {
Object.assign(uprogress, uprogress_init) Object.assign(store.uprogress, uprogress_init)
uprogress.t0 = Date.now() store.uprogress.t0 = Date.now()
uprogress.tlast = uprogress.t0 + 1 store.uprogress.tlast = store.uprogress.t0 + 1
} }
const statsAdd = (f: CloudFile[]) => { const statsAdd = (f: CloudFile[]) => {
if (uprogress.files.length === 0) statReset() if (store.uprogress.files.length === 0) statReset()
uprogress.total += f.reduce((a, b) => a + b.file.size, 0) store.uprogress.total += f.reduce((a, b) => a + b.file.size, 0)
uprogress.filecount += f.length store.uprogress.filecount += f.length
uprogress.files = [...uprogress.files, ...f] store.uprogress.files = [...store.uprogress.files, ...f]
statNextFile() statNextFile()
} }
let upqueue = [] as CloudFile[] let upqueue = [] as CloudFile[]
@ -181,7 +190,7 @@ const WSCreate = async () => await new Promise<WebSocket>(resolve => {
// @ts-ignore // @ts-ignore
ws.sendData = async (data: any) => { ws.sendData = async (data: any) => {
// Wait until the WS is ready to send another message // Wait until the WS is ready to send another message
uprogress.status = "uploading" store.uprogress.status = "uploading"
await new Promise(resolve => { await new Promise(resolve => {
const t = setInterval(() => { const t = setInterval(() => {
if (ws.bufferedAmount > 1<<20) return if (ws.bufferedAmount > 1<<20) return
@ -189,7 +198,7 @@ const WSCreate = async () => await new Promise<WebSocket>(resolve => {
clearInterval(t) clearInterval(t)
}, 1) }, 1)
}) })
uprogress.status = "processing" store.uprogress.status = "processing"
ws.send(data) ws.send(data)
} }
}) })
@ -210,7 +219,7 @@ const worker = async () => {
if (f.cloudPos === f.file.size) upqueue.shift() if (f.cloudPos === f.file.size) upqueue.shift()
} }
if (upqueue.length) startWorker() if (upqueue.length) startWorker()
uprogress.status = "idle" store.uprogress.status = "idle"
workerRunning = false workerRunning = false
} }
let workerRunning: any = false let workerRunning: any = false
@ -233,12 +242,3 @@ onUnmounted(() => {
removeEventListener('drop', uploadHandler) removeEventListener('drop', uploadHandler)
}) })
</script> </script>
<template>
<template>
<input ref="fileInput" @change="uploadHandler" type="file" multiple>
<input ref="folderInput" @change="uploadHandler" type="file" webkitdirectory>
</template>
<SvgButton name="add-file" data-tooltip="Upload files" @click="fileInput.click()" />
<SvgButton name="add-folder" data-tooltip="Upload folder" @click="folderInput.click()" />
<TransferBar :status=uprogress @cancel=cancelUploads />
</template>

View File

@ -19,6 +19,8 @@ export const useMainStore = defineStore({
cursor: '' as string, cursor: '' as string,
server: {} as Record<string, any>, server: {} as Record<string, any>,
dialog: '' as '' | 'login' | 'settings', dialog: '' as '' | 'login' | 'settings',
uprogress: {} as any,
dprogress: {} as any,
prefs: { prefs: {
gallery: false, gallery: false,
sortListing: '' as SortOrder, sortListing: '' as SortOrder,
@ -89,7 +91,13 @@ export const useMainStore = defineStore({
}, },
focusBreadcrumb() { focusBreadcrumb() {
(document.querySelector('.breadcrumb') as HTMLAnchorElement).focus() (document.querySelector('.breadcrumb') as HTMLAnchorElement).focus()
} },
cancelDownloads() {
location.reload() // FIXME
},
cancelUploads() {
location.reload() // FIXME
},
}, },
getters: { getters: {
sortOrder(): SortOrder { return this.query ? this.prefs.sortFiltered : this.prefs.sortListing }, sortOrder(): SortOrder { return this.query ? this.prefs.sortFiltered : this.prefs.sortListing },