diff options
author | John Molakvoæ <skjnldsv@protonmail.com> | 2023-10-12 09:44:53 +0200 |
---|---|---|
committer | John Molakvoæ <skjnldsv@protonmail.com> | 2023-10-17 11:19:01 +0200 |
commit | 60260bb58e22a9309c126d54441e0ac28d2b140f (patch) | |
tree | c141af73a287b2e341e85e4c05084151149bb467 /apps/files | |
parent | 2887e6419d967217e09936bed702a6f87d49ab23 (diff) | |
download | nextcloud-server-60260bb58e22a9309c126d54441e0ac28d2b140f.tar.gz nextcloud-server-60260bb58e22a9309c126d54441e0ac28d2b140f.zip |
chore(files): split FileEntry Preview
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
Diffstat (limited to 'apps/files')
-rw-r--r-- | apps/files/src/components/FileEntry.vue | 164 | ||||
-rw-r--r-- | apps/files/src/components/FileEntry/FavoriteIcon.vue (renamed from apps/files/src/components/FavoriteIcon.vue) | 0 | ||||
-rw-r--r-- | apps/files/src/components/FileEntry/FileEntryPreview.vue | 211 | ||||
-rw-r--r-- | apps/files/src/components/FilesListVirtual.vue | 21 |
4 files changed, 238 insertions, 158 deletions
diff --git a/apps/files/src/components/FileEntry.vue b/apps/files/src/components/FileEntry.vue index f1606d218c2..1a1bf6d9baf 100644 --- a/apps/files/src/components/FileEntry.vue +++ b/apps/files/src/components/FileEntry.vue @@ -48,36 +48,10 @@ <!-- Link to file --> <td class="files-list__row-name" data-cy-files-list-row-name> <!-- Icon or preview --> - <span class="files-list__row-icon" @click="execDefaultAction"> - <template v-if="source.type === 'folder'"> - <FolderOpenIcon v-if="dragover" /> - <template v-else> - <FolderIcon /> - <OverlayIcon :is="folderOverlay" - v-if="folderOverlay" - class="files-list__row-icon-overlay" /> - </template> - </template> - - <!-- Decorative image, should not be aria documented --> - <img v-else-if="previewUrl && backgroundFailed !== true" - ref="previewImg" - alt="" - class="files-list__row-icon-preview" - :class="{'files-list__row-icon-preview--loaded': backgroundFailed === false}" - :src="previewUrl" - @error="backgroundFailed = true" - @load="backgroundFailed = false"> - - <FileIcon v-else /> - - <!-- Favorite icon --> - <span v-if="isFavorite" - class="files-list__row-icon-favorite" - :aria-label="t('files', 'Favorite')"> - <FavoriteIcon /> - </span> - </span> + <FileEntryPreview ref="preview" + :source="source" + :dragover="dragover" + @click.native="execDefaultAction" /> <!-- Rename input --> <form v-if="isRenaming" @@ -189,28 +163,18 @@ <script lang="ts"> import type { PropType } from 'vue' -import { emit, subscribe } from '@nextcloud/event-bus' +import { emit } from '@nextcloud/event-bus' import { extname, join } from 'path' -import { generateUrl } from '@nextcloud/router' import { getFileActions, DefaultType, FileType, formatFileSize, Permission, Folder, File as NcFile, FileAction, NodeStatus, Node } from '@nextcloud/files' import { getUploader } from '@nextcloud/upload' +import { loadState } from '@nextcloud/initial-state' import { showError, showSuccess } from '@nextcloud/dialogs' import { translate as t } from '@nextcloud/l10n' -import { Type as ShareType } from '@nextcloud/sharing' import { vOnClickOutside } from '@vueuse/components' import axios from '@nextcloud/axios' import moment from '@nextcloud/moment' import Vue from 'vue' -import AccountGroupIcon from 'vue-material-design-icons/AccountGroup.vue' -import FileIcon from 'vue-material-design-icons/File.vue' -import FolderIcon from 'vue-material-design-icons/Folder.vue' -import FolderOpenIcon from 'vue-material-design-icons/FolderOpen.vue' -import KeyIcon from 'vue-material-design-icons/Key.vue' -import TagIcon from 'vue-material-design-icons/Tag.vue' -import LinkIcon from 'vue-material-design-icons/Link.vue' -import NetworkIcon from 'vue-material-design-icons/Network.vue' -import AccountPlusIcon from 'vue-material-design-icons/AccountPlus.vue' import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js' import NcActions from '@nextcloud/vue/dist/Components/NcActions.js' import NcCheckboxRadioSwitch from '@nextcloud/vue/dist/Components/NcCheckboxRadioSwitch.js' @@ -229,11 +193,9 @@ import { useFilesStore } from '../store/files.ts' import { useKeyboardStore } from '../store/keyboard.ts' import { useRenamingStore } from '../store/renaming.ts' import { useSelectionStore } from '../store/selection.ts' -import { useUserConfigStore } from '../store/userconfig.ts' import CustomElementRender from './CustomElementRender.vue' -import FavoriteIcon from './FavoriteIcon.vue' +import FileEntryPreview from './FileEntry/FileEntryPreview.vue' import logger from '../logger.js' -import { loadState } from '@nextcloud/initial-state' // The registered actions list const actions = getFileActions() @@ -246,23 +208,14 @@ export default Vue.extend({ name: 'FileEntry', components: { - AccountGroupIcon, - AccountPlusIcon, CustomElementRender, - FavoriteIcon, - FileIcon, - FolderIcon, - FolderOpenIcon, - KeyIcon, - LinkIcon, + FileEntryPreview, NcActionButton, NcActions, NcCheckboxRadioSwitch, NcIconSvgWrapper, NcLoadingIcon, NcTextField, - NetworkIcon, - TagIcon, }, props: { @@ -303,7 +256,6 @@ export default Vue.extend({ const keyboardStore = useKeyboardStore() const renamingStore = useRenamingStore() const selectionStore = useSelectionStore() - const userConfigStore = useUserConfigStore() return { actionsMenuStore, draggingStore, @@ -311,13 +263,11 @@ export default Vue.extend({ keyboardStore, renamingStore, selectionStore, - userConfigStore, } }, data() { return { - backgroundFailed: undefined, loading: '', dragover: false, @@ -326,9 +276,6 @@ export default Vue.extend({ }, computed: { - userConfig() { - return this.userConfigStore.userConfig - }, currentView() { return this.$navigation.active @@ -418,43 +365,6 @@ export default Vue.extend({ return '' }, - folderOverlay() { - if (this.source.type !== FileType.Folder) { - return null - } - - // Encrypted folders - if (this.source?.attributes?.['is-encrypted'] === 1) { - return KeyIcon - } - - // System tags - if (this.source?.attributes?.['is-tag']) { - return TagIcon - } - - // Link and mail shared folders - const shareTypes = Object.values(this.source?.attributes?.['share-types'] || {}).flat() as number[] - if (shareTypes.some(type => type === ShareType.SHARE_TYPE_LINK || type === ShareType.SHARE_TYPE_EMAIL)) { - return LinkIcon - } - - // Shared folders - if (shareTypes.length > 0) { - return AccountPlusIcon - } - - switch (this.source?.attributes?.['mount-type']) { - case 'external': - case 'external-session': - return NetworkIcon - case 'group': - return AccountGroupIcon - } - - return null - }, - linkTo() { if (this.source.attributes.failed) { return { @@ -495,38 +405,6 @@ export default Vue.extend({ return this.selectedFiles.includes(this.fileid) }, - cropPreviews() { - return this.userConfig.crop_image_previews - }, - previewUrl() { - if (this.source.type === FileType.Folder) { - return null - } - - if (this.backgroundFailed === true) { - return null - } - - try { - const previewUrl = this.source.attributes.previewUrl - || generateUrl('/core/preview?fileId={fileid}', { - fileid: this.fileid, - }) - const url = new URL(window.location.origin + previewUrl) - - // Request tiny previews - url.searchParams.set('x', '32') - url.searchParams.set('y', '32') - url.searchParams.set('mimeFallback', 'true') - - // Handle cropping - url.searchParams.set('a', this.cropPreviews === true ? '0' : '1') - return url.href - } catch (e) { - return null - } - }, - // Sorted actions that are enabled for this node enabledActions() { if (this.source.attributes.failed) { @@ -583,10 +461,6 @@ export default Vue.extend({ uniqueId() { return hashCode(this.source.source) }, - - isFavorite() { - return this.source.attributes.favorite === 1 - }, isLoading() { return this.source.status === NodeStatus.LOADING }, @@ -675,11 +549,7 @@ export default Vue.extend({ // Reset loading state this.loading = '' - // Reset background state - this.backgroundFailed = undefined - if (this.$refs.previewImg) { - this.$refs.previewImg.src = '' - } + this.$refs.preview.reset() // Close menu this.openedMenu = false @@ -1066,22 +936,6 @@ tr { background-color: var(--color-background-dark); } } - -// Folder overlay -.files-list__row-icon-overlay { - position: absolute; - max-height: 18px; - max-width: 18px; - color: var(--color-main-background); - // better alignment with the folder icon - margin-top: 2px; -} - -/* Preview not loaded animation effect */ -.files-list__row-icon-preview:not(.files-list__row-icon-preview--loaded) { - background: var(--color-loading-dark); - // animation: preview-gradient-fade 1.2s ease-in-out infinite; -} </style> <style> diff --git a/apps/files/src/components/FavoriteIcon.vue b/apps/files/src/components/FileEntry/FavoriteIcon.vue index 6eb1fbd8edd..6eb1fbd8edd 100644 --- a/apps/files/src/components/FavoriteIcon.vue +++ b/apps/files/src/components/FileEntry/FavoriteIcon.vue diff --git a/apps/files/src/components/FileEntry/FileEntryPreview.vue b/apps/files/src/components/FileEntry/FileEntryPreview.vue new file mode 100644 index 00000000000..7766980b144 --- /dev/null +++ b/apps/files/src/components/FileEntry/FileEntryPreview.vue @@ -0,0 +1,211 @@ +<!-- + - @copyright Copyright (c) 2023 John Molakvoæ <skjnldsv@protonmail.com> + - + - @author John Molakvoæ <skjnldsv@protonmail.com> + - + - @license GNU AGPL version 3 or any later version + - + - This program is free software: you can redistribute it and/or modify + - it under the terms of the GNU Affero General Public License as + - published by the Free Software Foundation, either version 3 of the + - License, or (at your option) any later version. + - + - This program is distributed in the hope that it will be useful, + - but WITHOUT ANY WARRANTY; without even the implied warranty of + - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + - GNU Affero General Public License for more details. + - + - You should have received a copy of the GNU Affero General Public License + - along with this program. If not, see <http://www.gnu.org/licenses/>. + - + --> +<template> + <span class="files-list__row-icon"> + <template v-if="source.type === 'folder'"> + <FolderOpenIcon v-if="dragover" /> + <template v-else> + <FolderIcon /> + <OverlayIcon :is="folderOverlay" + v-if="folderOverlay" + class="files-list__row-icon-overlay" /> + </template> + </template> + + <!-- Decorative image, should not be aria documented --> + <img v-else-if="previewUrl && backgroundFailed !== true" + ref="previewImg" + alt="" + class="files-list__row-icon-preview" + :class="{'files-list__row-icon-preview--loaded': backgroundFailed === false}" + :src="previewUrl" + @error="backgroundFailed = true" + @load="backgroundFailed = false"> + + <FileIcon v-else /> + + <!-- Favorite icon --> + <span v-if="isFavorite" + class="files-list__row-icon-favorite" + :aria-label="t('files', 'Favorite')"> + <FavoriteIcon /> + </span> + </span> +</template> + +<script lang="ts"> +import type { UserConfig } from '../../types.ts' + +import { File, Folder, Node, FileType } from '@nextcloud/files' +import { generateUrl } from '@nextcloud/router' +import { translate as t } from '@nextcloud/l10n' +import { Type as ShareType } from '@nextcloud/sharing' +import Vue, { PropType } from 'vue' + +import AccountGroupIcon from 'vue-material-design-icons/AccountGroup.vue' +import AccountPlusIcon from 'vue-material-design-icons/AccountPlus.vue' +import FileIcon from 'vue-material-design-icons/File.vue' +import FolderIcon from 'vue-material-design-icons/Folder.vue' +import FolderOpenIcon from 'vue-material-design-icons/FolderOpen.vue' +import KeyIcon from 'vue-material-design-icons/Key.vue' +import LinkIcon from 'vue-material-design-icons/Link.vue' +import NetworkIcon from 'vue-material-design-icons/Network.vue' +import TagIcon from 'vue-material-design-icons/Tag.vue' + +import { useUserConfigStore } from '../../store/userconfig.ts' +import FavoriteIcon from './FavoriteIcon.vue' + +export default Vue.extend({ + name: 'FileEntryPreview', + + components: { + AccountGroupIcon, + AccountPlusIcon, + FavoriteIcon, + FileIcon, + FolderIcon, + FolderOpenIcon, + KeyIcon, + LinkIcon, + NetworkIcon, + TagIcon, + }, + + props: { + source: { + type: Object as PropType<Node>, + required: true, + }, + dragover: { + type: Boolean, + default: false, + }, + }, + + setup() { + const userConfigStore = useUserConfigStore() + return { + userConfigStore, + } + }, + + data() { + return { + backgroundFailed: undefined as boolean | undefined, + } + }, + + computed: { + fileid() { + return this.source?.fileid?.toString?.() + }, + isFavorite(): boolean { + return this.source.attributes.favorite === 1 + }, + + userConfig(): UserConfig { + return this.userConfigStore.userConfig + }, + cropPreviews(): boolean { + return this.userConfig.crop_image_previews === true + }, + + previewUrl() { + if (this.source.type === FileType.Folder) { + return null + } + + if (this.backgroundFailed === true) { + return null + } + + try { + const previewUrl = this.source.attributes.previewUrl + || generateUrl('/core/preview?fileId={fileid}', { + fileid: this.fileid, + }) + const url = new URL(window.location.origin + previewUrl) + + // Request tiny previews + url.searchParams.set('x', '32') + url.searchParams.set('y', '32') + url.searchParams.set('mimeFallback', 'true') + + // Handle cropping + url.searchParams.set('a', this.cropPreviews === true ? '0' : '1') + return url.href + } catch (e) { + return null + } + }, + + folderOverlay() { + if (this.source.type !== FileType.Folder) { + return null + } + + // Encrypted folders + if (this.source?.attributes?.['is-encrypted'] === 1) { + return KeyIcon + } + + // System tags + if (this.source?.attributes?.['is-tag']) { + return TagIcon + } + + // Link and mail shared folders + const shareTypes = Object.values(this.source?.attributes?.['share-types'] || {}).flat() as number[] + if (shareTypes.some(type => type === ShareType.SHARE_TYPE_LINK || type === ShareType.SHARE_TYPE_EMAIL)) { + return LinkIcon + } + + // Shared folders + if (shareTypes.length > 0) { + return AccountPlusIcon + } + + switch (this.source?.attributes?.['mount-type']) { + case 'external': + case 'external-session': + return NetworkIcon + case 'group': + return AccountGroupIcon + } + + return null + }, + }, + + methods: { + reset() { + // Reset background state + this.backgroundFailed = undefined + if (this.$refs.previewImg) { + this.$refs.previewImg.src = '' + } + }, + + t, + }, +}) +</script> diff --git a/apps/files/src/components/FilesListVirtual.vue b/apps/files/src/components/FilesListVirtual.vue index c5ff9e663a3..914cfa7ec4d 100644 --- a/apps/files/src/components/FilesListVirtual.vue +++ b/apps/files/src/components/FilesListVirtual.vue @@ -465,10 +465,15 @@ export default Vue.extend({ width: var(--icon-preview-size); height: var(--icon-preview-size); border-radius: var(--border-radius); - background-repeat: no-repeat; // Center and contain the preview - background-position: center; - background-size: contain; + object-fit: contain; + object-position: center; + + /* Preview not loaded animation effect */ + &:not(.files-list__row-icon-preview--loaded) { + background: var(--color-loading-dark); + // animation: preview-gradient-fade 1.2s ease-in-out infinite; + } } &-favorite { @@ -476,6 +481,16 @@ export default Vue.extend({ top: 0px; right: -10px; } + + // Folder overlay + &-overlay { + position: absolute; + max-height: 18px; + max-width: 18px; + color: var(--color-main-background); + // better alignment with the folder icon + margin-top: 2px; + } } // Entry link |