Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
pascalwengerter committed Mar 30, 2022
1 parent 5ae9e3f commit c8e71ce
Show file tree
Hide file tree
Showing 18 changed files with 443 additions and 778 deletions.
9 changes: 9 additions & 0 deletions changelog/unreleased/enhancement-resumeable-uploads
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Enhancement: Resumeable uploads

Draft:
- Introduced resumeable (depending on backend capabilities)
- Improved rendering of uploadProgress-visualization
- Removed `vue2-dropzone` and `vue-drag-drop` libraries.

https://github.com/owncloud/web/pull/6202
https://github.com/owncloud/web/issues/6268
8 changes: 6 additions & 2 deletions packages/web-app-files/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@
"description": "ownCloud web files",
"license": "AGPL-3.0",
"dependencies": {
"copy-to-clipboard": "^3.3.1",
"vue2-dropzone": "^3.6.0"
"@uppy/core": "^2.1.7",
"@uppy/drop-target": "^1.1.1",
"@uppy/status-bar": "^2.1.3",
"@uppy/tus": "^2.2.2",
"@uppy/xhr-upload": "^2.0.7",
"copy-to-clipboard": "^3.3.1"
}
}
195 changes: 145 additions & 50 deletions packages/web-app-files/src/components/AppBar/CreateAndUpload.vue
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,11 @@
<translate>New folder</translate>
</oc-button>
</template>
<file-drop
v-if="!uploadOrFileCreationBlocked"
:root-path="currentPath"
:path="currentPath"
:headers="headers"
@success="onFileSuccess"
@error="onFileError"
@progress="onFileProgress"
/>
<oc-button
<!-- PoC since the components don't get recognized on
initial page rendering as (hidden) OcDrop elements -->
<folder-upload />
<file-upload />
<!-- <oc-button
id="upload-menu-btn"
key="upload-menu-btn-enabled"
v-oc-tooltip="uploadButtonTooltip"
Expand All @@ -99,52 +94,42 @@
>
<oc-list id="upload-list">
<li>
<folder-upload
:root-path="currentPath"
:path="currentPath"
:headers="headers"
@success="onFileSuccess"
@error="onFileError"
@progress="onFileProgress"
/>
<folder-upload />
</li>
<li>
<file-upload
:path="currentPath"
:headers="headers"
@success="onFileSuccess"
@error="onFileError"
@progress="onFileProgress"
/>
<file-upload />
</li>
</oc-list>
</oc-drop>
</oc-drop> -->
</div>
</template>

<script>
import { mapActions, mapGetters, mapState, mapMutations } from 'vuex'
import pathUtil from 'path'
import Uppy from '@uppy/core'
import Tus from '@uppy/tus'
import XHRUpload from '@uppy/xhr-upload'
import StatusBar from '@uppy/status-bar'
import DropTarget from '@uppy/drop-target'
import Mixins from '../../mixins'
import MixinFileActions, { EDITOR_MODE_CREATE } from '../../mixins/fileActions'
import { buildResource, buildWebDavFilesPath, buildWebDavSpacesPath } from '../../helpers/resources'
import { isLocationPublicActive, isLocationSpacesActive } from '../../router'
import { useActiveLocation } from '../../composables'
import { DavProperties, DavProperty } from 'web-pkg/src/constants'
import FileDrop from './Upload/FileDrop.vue'
// TODO: Simplify to one UploadButton component and fill from here
import FileUpload from './Upload/FileUpload.vue'
import FolderUpload from './Upload/FolderUpload.vue'
export default {
components: {
FileDrop,
FileUpload,
FolderUpload
},
mixins: [Mixins, MixinFileActions],
mixins: [MixinFileActions],
setup() {
return {
isPersonalLocation: useActiveLocation(isLocationSpacesActive, 'files-spaces-personal-home'),
Expand All @@ -161,7 +146,6 @@ export default {
computed: {
...mapGetters(['getToken', 'capabilities', 'configuration', 'newFileHandlers', 'user']),
...mapGetters('Files', ['files', 'currentFolder', 'publicLinkPassword']),
...mapState('Files', ['areHiddenFilesShown']),
mimetypesAllowedForCreation() {
// we can't use `mapGetters` here because the External app doesn't exist in all deployments
Expand Down Expand Up @@ -256,25 +240,140 @@ export default {
)
}
},
mounted() {
// TODO: Clarify why not to use this.headers
const client = this.$client
const uppyHeaders = client.helpers.buildHeaders()
const uploadPath = this.$client.files.getFileUrl(
buildWebDavFilesPath(this.user.id, this.currentPath)
)
// might make sense to initialize an Uppy instance in the runtime?
// TODO: set debug to false
const uppy = new Uppy({ debug: true, autoProceed: true })
// TODO: Build headers differently in public context?
// if (this.publicPage()) { ... maybe use this.headers to obtain token ... }
// TODO: What about flaky capability loading and its implications?
if (this.capabilities.files.tus_support?.max_chunk_size > 0) {
const chunkSize =
this.capabilities.files.tus_support.max_chunk_size > 0 &&
this.configuration.uploadChunkSize !== Infinity
? Math.max(
this.capabilities.files.tus_support.max_chunk_size,
this.configuration.uploadChunkSize
)
: this.configuration.uploadChunkSize
delete uppyHeaders['OCS-APIREQUEST']
uppy.use(Tus, {
endpoint: uploadPath,
headers: uppyHeaders,
chunkSize: chunkSize,
removeFingerprintOnSuccess: true,
overridePatchMethod: !!this.capabilities.files.tus_support.http_method_override,
retryDelays: [0, 3000, 5000, 10000, 20000]
})
} else {
console.log('uppy on oc10')
uppy.use(XHRUpload, {
endpoint: uploadPath,
method: 'put',
headers: uppyHeaders
})
}
// upload button handling (files & folders separately)
// doesn't recognize elements yet since they're tippy children, maybe use $refs?
const uploadInputTarget = document.querySelectorAll('.upload-input-target')
uploadInputTarget.forEach((item) => {
item.addEventListener('change', (event) => {
const files = Array.from(event.target.files)
files.forEach((file) => {
try {
console.log('beginning upload for file:', file)
uppy.addFile({
source: 'file input',
name: file.name,
type: file.type,
data: file
})
} catch (err) {
console.error('error upload file:', file)
if (err.isRestriction) {
// handle restrictions
console.log('Restriction error:', err)
} else {
// handle other errors
console.error(err)
}
}
})
})
})
// // upload via drag&drop handling
uppy.use(DropTarget, {
target: '#files-view'
})
uppy.use(StatusBar, {
id: 'StatusBar',
target: '#files-app-bar',
hideAfterFinish: true,
showProgressDetails: true,
hideUploadButton: false,
hideRetryButton: false,
hidePauseResumeButton: false,
hideCancelButton: false,
doneButtonHandler: null,
locale: {
// TODO: Uppy l10n research
}
})
uppy.on('upload-error', (file, error, response) => {
console.log('error with file:', file.id)
console.log('error message:', error)
this.onFileError(error.toString())
})
uppy.on('file-removed', () => {
uploadInputTarget.forEach((item) => {
item.value = null
})
})
uppy.on('complete', (result) => {
result.successful.forEach((file) => {
this.onFileSuccess(file)
})
uploadInputTarget.forEach((item) => {
item.value = null
})
console.log('successful files:', result.successful)
console.log('failed files:', result.failed)
})
},
beforeDestroy() {
// maybe bad that we close uppy this early, eventually on rerender even?
// currently very brittle, selecting a resource in the table
// results in a rerendering that logs an error since uppy is gone before it can close
// this.uppy.close()
},
methods: {
...mapActions('Files', [
'loadPreview',
'updateFileProgress',
'removeFilesFromTrashbin',
'loadIndicators'
]),
...mapActions('Files', ['loadPreview', 'loadIndicators']),
...mapActions(['openFile', 'showMessage', 'createModal', 'setModalInputErrorMessage']),
...mapMutations('Files', [
'UPSERT_RESOURCE',
'SET_HIDDEN_FILES_VISIBILITY',
'REMOVE_FILE',
'REMOVE_FILE_FROM_SEARCHED',
'SET_FILE_SELECTION',
'REMOVE_FILE_SELECTION'
]),
...mapMutations('Files', ['UPSERT_RESOURCE']),
...mapMutations(['SET_QUOTA']),
async onFileSuccess(event, file) {
async onFileSuccess(file) {
try {
if (file.name) {
file = file.name
Expand Down Expand Up @@ -327,10 +426,6 @@ export default {
})
},
onFileProgress(progress) {
this.updateFileProgress(progress)
},
showCreateResourceModal(
isFolder = true,
ext = 'txt',
Expand Down
Loading

0 comments on commit c8e71ce

Please sign in to comment.