Archived
2
0

added file uploads to frontend

This commit is contained in:
William Harrell 2022-11-13 22:06:24 -05:00
parent 5ba4019e36
commit 19af921913

View File

@ -15,8 +15,8 @@
</div> </div>
<div class="transfer-area"> <div class="transfer-area">
<div class="transfers" v-if="transfers.length > 0"> <div class="transfers" v-if="transfers.length > 0">
<p>Downloads</p> <p v-if="downloads.length > 0">Downloads</p>
<div v-for="download in downloads" :key="download.name" class="transfers-list-item"> <div v-for="download in downloads" :key="download.id" class="transfers-list-item">
<div class="transfer-info"> <div class="transfer-info">
<p>{{ download.name }}</p> <p>{{ download.name }}</p>
<p class="file-size">{{ Math.min(100, Math.round(download.progress / download.size * 100))}}%</p> <p class="file-size">{{ Math.min(100, Math.round(download.progress / download.size * 100))}}%</p>
@ -25,10 +25,22 @@
<progress class="transfer-progress" :aria-label="download.name + ' progress'" :value="download.progress" <progress class="transfer-progress" :aria-label="download.name + ' progress'" :value="download.progress"
:max="download.size"></progress> :max="download.size"></progress>
</div> </div>
<p v-if="uploads.length > 0">Uploads</p>
<div v-for="upload in uploads" :key="upload.id" class="transfers-list-item">
<div class="transfer-info">
<p>{{ upload.name }}</p>
<p class="file-size">{{ Math.min(100, Math.round(upload.progress / upload.size * 100))}}%</p>
<i class="fas fa-xmark remove-transfer" @click="() => removeTransfer(upload)"></i>
</div>
<progress class="transfer-progress" :aria-label="upload.name + ' progress'" :value="upload.progress"
:max="upload.size"></progress>
</div>
</div> </div>
<div class="upload-area" @dragover.prevent @drop.prevent="onFileDrop"> <div class="upload-area" :class="{ 'upload-area-drag': uploadAreaDrag }"
@dragover.prevent="() => uploadAreaDrag = true" @dragleave.prevent="() => uploadAreaDrag = false"
@drop.prevent="(e) => upload(e.dataTransfer)" @click="openFileBrowser">
<i class="fas fa-file-arrow-up" /> <i class="fas fa-file-arrow-up" />
<p>Drag files here to upload</p> <p>Click or drag files here to upload</p>
</div> </div>
</div> </div>
</div> </div>
@ -116,6 +128,9 @@
margin: 10px 10px 10px 10px; margin: 10px 10px 10px 10px;
background-color: rgba($color: #fff, $alpha: 0.05); background-color: rgba($color: #fff, $alpha: 0.05);
border-radius: 5px; border-radius: 5px;
max-height: 50vh;
overflow-y: scroll;
overflow-x: hidden;
} }
.transfers > p { .transfers > p {
@ -145,6 +160,14 @@
border-radius: 5px; border-radius: 5px;
} }
.upload-area:hover {
cursor: pointer;
}
.upload-area-drag, .upload-area:hover {
background-color: rgba($color: #fff, $alpha: 0.10);
}
.upload-area > i { .upload-area > i {
font-size: 4em; font-size: 4em;
margin: 10px 10px 10px 10px; margin: 10px 10px 10px 10px;
@ -163,7 +186,7 @@
import Markdown from './markdown' import Markdown from './markdown'
import Content from './context.vue' import Content from './context.vue'
import { FileTransfer } from '~/neko/types' import { FileTransfer } from '~/neko/types'
@Component({ @Component({
name: 'neko-files', name: 'neko-files',
@ -174,6 +197,8 @@ import { FileTransfer } from '~/neko/types'
}) })
export default class extends Vue { export default class extends Vue {
public uploadAreaDrag: boolean = false;
get cwd() { get cwd() {
return this.$accessor.files.cwd return this.$accessor.files.cwd
} }
@ -208,8 +233,8 @@ import { FileTransfer } from '~/neko/types'
id: Math.round(Math.random() * 10000), id: Math.round(Math.random() * 10000),
name: item.name, name: item.name,
direction: 'download', direction: 'download',
// this is just an estimation, but for large files the content length // this may be smaller than the actual transfer amount, but for large files the
// is not sent (chunked transfer) // content length is not sent (chunked transfer)
size: item.size, size: item.size,
progress: 0, progress: 0,
status: 'pending', status: 'pending',
@ -228,6 +253,8 @@ import { FileTransfer } from '~/neko/types'
} }
if (transfer.progress === transfer.size) { if (transfer.progress === transfer.size) {
transfer.status = 'completed' transfer.status = 'completed'
} else if (transfer.status !== 'inprogress') {
transfer.status = 'inprogress'
} }
} }
}).then((res) => { }).then((res) => {
@ -245,6 +272,64 @@ import { FileTransfer } from '~/neko/types'
this.$accessor.files.addTransfer(transfer) this.$accessor.files.addTransfer(transfer)
} }
upload(dt: DataTransfer) {
this.uploadAreaDrag = false
for (const file of dt.files) {
const formdata = new FormData()
formdata.append("files", file, file.name)
const url = `/file?pwd=${this.$accessor.password}`
let transfer: FileTransfer = {
id: Math.round(Math.random() * 10000),
name: file.name,
direction: 'upload',
size: file.size,
progress: 0,
status: 'pending',
axios: null,
abortController: null
}
transfer.abortController = new AbortController()
this.$http.post(url, formdata, {
onUploadProgress: (x: any) => {
transfer.progress = x.loaded
if (transfer.size !== x.total) {
transfer.size = x.total
}
if (transfer.progress === transfer.size) {
transfer.status = 'completed'
} else if (transfer.status !== 'inprogress') {
transfer.status = 'inprogress'
}
}
}).catch((err) => {
this.$log.error(err)
})
this.$accessor.files.addTransfer(transfer)
}
}
openFileBrowser() {
const input = document.createElement('input')
input.type = 'file'
input.setAttribute('multiple', 'true')
input.click()
input.onchange = (e) => {
if (e === null) {
return
}
const dt = new DataTransfer()
const target = e.target as any
for (const f of target.files) {
dt.items.add(f)
}
this.upload(dt)
}
}
removeTransfer(transfer: FileTransfer) { removeTransfer(transfer: FileTransfer) {
if (transfer.status !== 'completed') { if (transfer.status !== 'completed') {
transfer.abortController?.abort() transfer.abortController?.abort()
@ -296,10 +381,6 @@ import { FileTransfer } from '~/neko/types'
return `${(size / 1000 ** 4).toFixed(3)} tb` return `${(size / 1000 ** 4).toFixed(3)} tb`
} }
onFileDrop(e: any) {
console.log('file dropped', e)
console.log(e.dataTransfer.files)
}
} }
</script> </script>