Fix direct uploads and downloads, transfer bar UI
This commit is contained in:
parent
369dc3ecaf
commit
a383358369
|
@ -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">
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
|
||||||
|
|
|
@ -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 },
|
||||||
|
|
Loading…
Reference in New Issue
Block a user