diff --git a/changelog/unreleased/bugfix-update-file-list-on-new-file b/changelog/unreleased/bugfix-update-file-list-on-new-file new file mode 100644 index 00000000000..ab531f9e9ff --- /dev/null +++ b/changelog/unreleased/bugfix-update-file-list-on-new-file @@ -0,0 +1,6 @@ +Bugfix: Update file list when creating new files + +We update the file list now when creating a file in an editor that openes in a new tab (like draw.io). + +https://github.com/owncloud/web/issues/5530 +https://github.com/owncloud/web/pull/6358 diff --git a/changelog/unreleased/enhancement-redesign-create-upload-buttons b/changelog/unreleased/enhancement-redesign-create-upload-buttons new file mode 100644 index 00000000000..581e50dd641 --- /dev/null +++ b/changelog/unreleased/enhancement-redesign-create-upload-buttons @@ -0,0 +1,7 @@ +Enhancement: Redesign create and upload buttons + +We have separated the "Create new file/folder" and "Upload" actions above the files list into two separate buttons, +also using the new resource type icons for more consistency. + +https://github.com/owncloud/web/issues/6279 +https://github.com/owncloud/web/pull/6358 diff --git a/package.json b/package.json index 30bc073b7e8..c3faf4eb2f2 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "vue": "^2.6.10" }, "devDependencies": { - "@babel/core": "^7.16.5", + "@babel/core": "^7.17.5", "@babel/eslint-parser": "^7.16.5", "@babel/polyfill": "^7.12.1", "@babel/preset-env": "^7.16.5", @@ -51,7 +51,7 @@ "@playwright/test": "^1.17.2", "@rollup/plugin-alias": "^3.1.9", "@rollup/plugin-commonjs": "^17.0.0", - "@rollup/plugin-html": "^0.2.0", + "@rollup/plugin-html": "^0.2.4", "@rollup/plugin-inject": "^4.0.4", "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-typescript": "^8.3.0", @@ -95,7 +95,7 @@ "jest-serializer-vue": "^2.0.2", "join-path": "^1.1.1", "lodash": "^4.17.21", - "node-fetch": "^2.6.1", + "node-fetch": "^2.6.7", "pino": "^7.6.3", "pino-pretty": "^7.3.0", "playwright": "^1.17.1", diff --git a/packages/web-app-draw-io/src/index.js b/packages/web-app-draw-io/src/index.js index 32f90039321..96e059c72db 100644 --- a/packages/web-app-draw-io/src/index.js +++ b/packages/web-app-draw-io/src/index.js @@ -24,7 +24,7 @@ const appInfo = { routeName: 'draw-io', newFileMenu: { menuTitle($gettext) { - return $gettext('New draw.io document…') + return $gettext('Draw.io document') } } }, diff --git a/packages/web-app-files/src/components/ActionMenuItem.vue b/packages/web-app-files/src/components/ActionMenuItem.vue index 10076744df0..aebd5d6a3bc 100644 --- a/packages/web-app-files/src/components/ActionMenuItem.vue +++ b/packages/web-app-files/src/components/ActionMenuItem.vue @@ -14,6 +14,13 @@ alt="" class="oc-icon oc-icon-m" /> + - + @@ -136,30 +56,28 @@ import { mapActions, mapGetters, mapState, mapMutations } from 'vuex' import pathUtil from 'path' import Mixins from '../../mixins' -import MixinFileActions, { EDITOR_MODE_CREATE } from '../../mixins/fileActions' +import MixinFileActions from '../../mixins/fileActions' import { buildResource, buildWebDavFilesPath, buildWebDavSpacesPath } from '../../helpers/resources' import { bus } from 'web-pkg/src/instance' -import { isLocationActive, isLocationPublicActive, isLocationSpacesActive } from '../../router' +import { DavProperties } from 'web-pkg/src/constants' +import { isLocationPublicActive, isLocationSpacesActive } from '../../router' import { useActiveLocation } from '../../composables' import BatchActions from './SelectedResources/BatchActions.vue' +import ContextActions from '../FilesList/ContextActions.vue' +import CreateAndUpload from './CreateAndUpload.vue' import FileDrop from './Upload/FileDrop.vue' -import FileUpload from './Upload/FileUpload.vue' -import FolderUpload from './Upload/FolderUpload.vue' import SizeInfo from './SelectedResources/SizeInfo.vue' import ViewOptions from './ViewOptions.vue' -import { DavProperties, DavProperty } from 'web-pkg/src/constants' -import ContextActions from '../FilesList/ContextActions.vue' export default { components: { BatchActions, + ContextActions, + CreateAndUpload, FileDrop, - FileUpload, - FolderUpload, SizeInfo, - ViewOptions, - ContextActions + ViewOptions }, mixins: [Mixins, MixinFileActions], setup() { @@ -187,31 +105,6 @@ export default { ...mapGetters('Files', ['files', 'currentFolder', 'selectedFiles', 'publicLinkPassword']), ...mapState('Files', ['areHiddenFilesShown']), - mimetypesAllowedForCreation() { - // we can't use `mapGetters` here because the External app doesn't exist in all deployments - const mimeTypes = this.$store.getters['External/mimeTypes'] - if (!mimeTypes) { - return [] - } - return mimeTypes.filter((mimetype) => mimetype.allow_creation) || [] - }, - newButtonTooltip() { - if (!this.canUpload) { - return this.$gettext('You have no permission to upload!') - } - if (!this.hasFreeSpace) { - return this.$gettext('You have not enough space left to upload!') - } - return null - }, - newButtonAriaLabel() { - const tooltip = this.newButtonTooltip - if (tooltip) { - return tooltip - } - return this.$gettext('Add files or folders') - }, - currentPath() { const path = this.$route.params.item || '' if (path.endsWith('/')) { @@ -332,10 +225,6 @@ export default { return this.selectedFiles.length < 1 }, - isNewBtnDisabled() { - return !this.canUpload || !this.hasFreeSpace - }, - selectedResourcesAnnouncement() { if (this.selectedFiles.length === 0) { return this.$gettext('No items selected.') @@ -346,12 +235,6 @@ export default { this.selectedFiles.length ) return this.$gettextInterpolate(translated, { amount: this.selectedFiles.length }) - }, - - newFileHandlersForRoute() { - return this.newFileHandlers.filter(({ routes = [] }) => - isLocationActive(this.$router, ...routes.map((name) => ({ name }))) - ) } }, @@ -367,318 +250,10 @@ export default { methods: { ...mapActions('Files', ['updateFileProgress', 'removeFilesFromTrashbin', 'loadIndicators']), - ...mapActions(['openFile', 'showMessage', 'createModal', 'setModalInputErrorMessage']), + ...mapActions(['openFile', 'showMessage']), ...mapMutations('Files', ['UPSERT_RESOURCE', 'SET_HIDDEN_FILES_VISIBILITY']), ...mapMutations(['SET_QUOTA']), - showCreateResourceModal( - isFolder = true, - ext = 'txt', - openAction = null, - addAppProviderFile = false - ) { - const defaultName = isFolder - ? this.$gettext('New folder') - : this.$gettext('New file') + '.' + ext - const checkInputValue = (value) => { - this.setModalInputErrorMessage( - isFolder ? this.checkNewFolderName(value) : this.checkNewFileName(value) - ) - } - - // Sets action to be executed after creation of the file - if (!isFolder) { - this.newFileAction = openAction - } - - const modal = { - variation: 'passive', - title: isFolder ? this.$gettext('Create a new folder') : this.$gettext('Create a new file'), - cancelText: this.$gettext('Cancel'), - confirmText: this.$gettext('Create'), - hasInput: true, - inputValue: defaultName, - inputLabel: isFolder ? this.$gettext('Folder name') : this.$gettext('File name'), - inputError: isFolder - ? this.checkNewFolderName(defaultName) - : this.checkNewFileName(defaultName), - onCancel: this.hideModal, - onConfirm: isFolder - ? this.addNewFolder - : addAppProviderFile - ? this.addAppProviderFile - : this.addNewFile, - onInput: checkInputValue - } - - this.createModal(modal) - }, - - async addNewFolder(folderName) { - if (folderName === '') { - return - } - - this.fileFolderCreationLoading = true - - try { - let path = pathUtil.join(this.currentPath, folderName) - let resource - - if (this.isPersonalLocation) { - path = buildWebDavFilesPath(this.user.id, path) - await this.$client.files.createFolder(path) - resource = await this.$client.files.fileInfo(path, DavProperties.Default) - } else if (this.isSpacesProjectLocation) { - path = buildWebDavSpacesPath(this.$route.params.spaceId, path) - await this.$client.files.createFolder(path) - resource = await this.$client.files.fileInfo(path, DavProperties.Default) - } else { - await this.$client.publicFiles.createFolder(path, null, this.publicLinkPassword) - resource = await this.$client.publicFiles.getFileInfo( - path, - this.publicLinkPassword, - DavProperties.PublicLink - ) - } - resource = buildResource(resource) - - this.UPSERT_RESOURCE(resource) - this.hideModal() - - if (this.isPersonalLocation) { - this.loadIndicators({ - client: this.$client, - currentFolder: this.currentFolder.path - }) - } - - this.showMessage({ - title: this.$gettextInterpolate( - this.$gettext('"%{folderName}" was created successfully'), - { - folderName - } - ) - }) - } catch (error) { - console.error(error) - this.showMessage({ - title: this.$gettext('Failed to create folder'), - status: 'danger' - }) - } - - this.fileFolderCreationLoading = false - }, - - checkNewFolderName(folderName) { - if (folderName === '') { - return this.$gettext('Folder name cannot be empty') - } - - if (/[/]/.test(folderName)) { - return this.$gettext('Folder name cannot contain "/"') - } - - if (folderName === '.') { - return this.$gettext('Folder name cannot be equal to "."') - } - - if (folderName === '..') { - return this.$gettext('Folder name cannot be equal to ".."') - } - - if (/\s+$/.test(folderName)) { - return this.$gettext('Folder name cannot end with whitespace') - } - - const exists = this.files.find((file) => file.name === folderName) - - if (exists) { - const translated = this.$gettext('%{name} already exists') - return this.$gettextInterpolate(translated, { name: folderName }, true) - } - - return null - }, - - async addNewFile(fileName) { - if (fileName === '') { - return - } - - this.fileFolderCreationLoading = true - - try { - let resource - let path = pathUtil.join(this.currentPath, fileName) - - if (this.isPersonalLocation) { - path = buildWebDavFilesPath(this.user.id, path) - await this.$client.files.putFileContents(path, '') - resource = await this.$client.files.fileInfo(path, DavProperties.Default) - } else if (this.isSpacesProjectLocation) { - path = buildWebDavSpacesPath(this.$route.params.spaceId, path) - await this.$client.files.putFileContents(path, '') - resource = await this.$client.files.fileInfo(path, DavProperties.Default) - } else { - await this.$client.publicFiles.putFileContents('', path, this.publicLinkPassword, '') - resource = await this.$client.publicFiles.getFileInfo( - path, - this.publicLinkPassword, - DavProperties.PublicLink - ) - } - - if (this.newFileAction) { - const fileId = resource.fileInfo[DavProperty.FileId] - - this.$_fileActions_openEditor(this.newFileAction, path, fileId, EDITOR_MODE_CREATE) - this.hideModal() - - return - } - - resource = buildResource(resource) - - this.UPSERT_RESOURCE(resource) - this.hideModal() - - if (this.isPersonalLocation) { - this.loadIndicators({ - client: this.$client, - currentFolder: this.currentFolder.path - }) - } - - this.showMessage({ - title: this.$gettextInterpolate(this.$gettext('"%{fileName}" was created successfully'), { - fileName - }) - }) - } catch (error) { - console.error(error) - this.showMessage({ - title: this.$gettext('Failed to create file'), - status: 'danger' - }) - } - - this.fileFolderCreationLoading = false - }, - async addAppProviderFile(fileName) { - // FIXME: this belongs in web-app-external, but the app provider handles file creation differently than other editor extensions. Needs more refactoring. - if (fileName === '') { - return - } - try { - const parent = this.currentFolder.fileId - const publicToken = (this.$router.currentRoute.params.item || '').split('/')[0] - - const configUrl = this.configuration.server - const appNewUrl = this.capabilities.files.app_providers[0].new_url.replace(/^\/+/, '') - const url = - configUrl + - appNewUrl + - `?parent_container_id=${parent}&filename=${encodeURIComponent(fileName)}` - - const headers = { - 'X-Requested-With': 'XMLHttpRequest', - ...(this.isPublicLocation && - publicToken && { - 'public-token': publicToken - }), - ...(this.isPublicLocation && - this.publicLinkPassword && { - Authorization: - 'Basic ' + - Buffer.from(['public', this.publicLinkPassword].join(':')).toString('base64') - }), - ...(this.getToken && { - Authorization: 'Bearer ' + this.getToken - }) - } - - const response = await fetch(url, { - method: 'POST', - headers - }) - - if (response.status !== 200) { - throw new Error(`An error has occurred: ${response.status}`) - } - - let resource - let path = pathUtil.join(this.currentPath, fileName) - - if (this.isPersonalLocation) { - path = buildWebDavFilesPath(this.user.id, path) - resource = await this.$client.files.fileInfo(path, DavProperties.Default) - } else if (this.isSpacesProjectLocation) { - path = buildWebDavSpacesPath(this.$route.params.spaceId, path) - resource = await this.$client.files.fileInfo(path, DavProperties.Default) - } else { - resource = await this.$client.publicFiles.getFileInfo( - path, - this.publicLinkPassword, - DavProperties.PublicLink - ) - } - resource = buildResource(resource) - this.$_fileActions_triggerDefaultAction(resource) - this.UPSERT_RESOURCE(resource) - this.hideModal() - - if (this.isPersonalLocation) { - this.loadIndicators({ - client: this.$client, - currentFolder: this.currentFolder.path - }) - } - this.showMessage({ - title: this.$gettextInterpolate(this.$gettext('"%{fileName}" was created successfully'), { - fileName - }) - }) - } catch (error) { - console.error(error) - this.showMessage({ - title: this.$gettext('Failed to create file'), - status: 'danger' - }) - } - }, - checkNewFileName(fileName) { - if (fileName === '') { - return this.$gettext('File name cannot be empty') - } - - if (/[/]/.test(fileName)) { - return this.$gettext('File name cannot contain "/"') - } - - if (fileName === '.') { - return this.$gettext('File name cannot be equal to "."') - } - - if (fileName === '..') { - return this.$gettext('File name cannot be equal to ".."') - } - - if (/\s+$/.test(fileName)) { - return this.$gettext('File name cannot end with whitespace') - } - - const exists = this.files.find((file) => file.name === fileName) - - if (exists) { - const translated = this.$gettext('%{name} already exists') - return this.$gettextInterpolate(translated, { name: fileName }, true) - } - - return null - }, async onFileSuccess(event, file) { try { if (file.name) { diff --git a/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue new file mode 100644 index 00000000000..bd8f1deb7de --- /dev/null +++ b/packages/web-app-files/src/components/AppBar/CreateAndUpload.vue @@ -0,0 +1,549 @@ + + + + diff --git a/packages/web-app-files/src/components/AppBar/Upload/FileUpload.vue b/packages/web-app-files/src/components/AppBar/Upload/FileUpload.vue index 271a9ad6fae..3303719fdf9 100644 --- a/packages/web-app-files/src/components/AppBar/Upload/FileUpload.vue +++ b/packages/web-app-files/src/components/AppBar/Upload/FileUpload.vue @@ -1,8 +1,8 @@