diff options
Diffstat (limited to 'apps/files_versions/src/components')
-rw-r--r-- | apps/files_versions/src/components/Version.vue | 166 | ||||
-rw-r--r-- | apps/files_versions/src/components/VersionLabelDialog.vue | 123 | ||||
-rw-r--r-- | apps/files_versions/src/components/VersionLabelForm.vue | 115 | ||||
-rw-r--r-- | apps/files_versions/src/components/VirtualScrolling.vue | 23 |
4 files changed, 219 insertions, 208 deletions
diff --git a/apps/files_versions/src/components/Version.vue b/apps/files_versions/src/components/Version.vue index 1afe28250c6..dc36e4134f9 100644 --- a/apps/files_versions/src/components/Version.vue +++ b/apps/files_versions/src/components/Version.vue @@ -1,29 +1,17 @@ <!-- - - @copyright 2022 Carl Schwan <carl@carlschwan.eu> - - @license AGPL-3.0-or-later - - - - 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/>. - --> + - SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> <template> <NcListItem class="version" :force-display-actions="true" + :actions-aria-label="t('files_versions', 'Actions for version from {versionHumanExplicitDate}', { versionHumanExplicitDate })" :data-files-versions-version="version.fileVersion" @click="click"> <!-- Icon --> <template #icon> <div v-if="!(loadPreview || previewLoaded)" class="version__image" /> - <img v-else-if="(isCurrent || version.hasPreview) && !previewErrored" + <img v-else-if="version.previewUrl && !previewErrored" :src="version.previewUrl" alt="" decoding="async" @@ -41,16 +29,26 @@ <!-- author --> <template #name> <div class="version__info"> - <div v-if="versionLabel" class="version__info__label">{{ versionLabel }}</div> - <div v-if="versionAuthor" class="version__info"> - <div v-if="versionLabel">•</div> + <div v-if="versionLabel" + class="version__info__label" + data-cy-files-version-label + :title="versionLabel"> + {{ versionLabel }} + </div> + <div v-if="versionAuthor" + class="version__info" + data-cy-files-version-author-name> + <span v-if="versionLabel">•</span> <NcAvatar class="avatar" :user="version.author" - :size="16" - :disable-menu="true" - :disable-tooltip="true" + :size="20" + disable-menu + disable-tooltip :show-user-status="false" /> - <div>{{ versionAuthor }}</div> + <div class="version__info__author_name" + :title="versionAuthor"> + {{ versionAuthor }} + </div> </div> </div> </template> @@ -58,10 +56,12 @@ <!-- Version file size as subline --> <template #subname> <div class="version__info version__info__subline"> - <span :title="formattedDate">{{ version.mtime | humanDateFromNow }}</span> - <!-- Separate dot to improve alignement --> + <NcDateTime class="version__info__date" + relative-time="short" + :timestamp="version.mtime" /> + <!-- Separate dot to improve alignment --> <span>•</span> - <span>{{ version.size | humanReadableSize }}</span> + <span>{{ humanReadableSize }}</span> </div> </template> @@ -116,31 +116,35 @@ </template> </NcListItem> </template> - <script lang="ts"> +import type { PropType } from 'vue' import type { Version } from '../utils/versions' +import { getCurrentUser } from '@nextcloud/auth' +import { Permission, formatFileSize } from '@nextcloud/files' +import { loadState } from '@nextcloud/initial-state' +import { t } from '@nextcloud/l10n' +import { joinPaths } from '@nextcloud/paths' +import { getRootUrl, generateUrl } from '@nextcloud/router' +import { defineComponent } from 'vue' + +import axios from '@nextcloud/axios' +import moment from '@nextcloud/moment' +import logger from '../utils/logger' + import BackupRestore from 'vue-material-design-icons/BackupRestore.vue' import Delete from 'vue-material-design-icons/Delete.vue' import Download from 'vue-material-design-icons/Download.vue' import FileCompare from 'vue-material-design-icons/FileCompare.vue' import ImageOffOutline from 'vue-material-design-icons/ImageOffOutline.vue' -import Pencil from 'vue-material-design-icons/Pencil.vue' +import Pencil from 'vue-material-design-icons/PencilOutline.vue' -import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js' -import NcActionLink from '@nextcloud/vue/dist/Components/NcActionLink.js' -import NcAvatar from '@nextcloud/vue/dist/Components/NcAvatar.js' -import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js' -import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip.js' - -import { defineComponent, type PropType } from 'vue' -import axios from '@nextcloud/axios' -import { getRootUrl, generateOcsUrl } from '@nextcloud/router' -import { joinPaths } from '@nextcloud/paths' -import { loadState } from '@nextcloud/initial-state' -import { Permission, formatFileSize } from '@nextcloud/files' -import { translate as t } from '@nextcloud/l10n' -import moment from '@nextcloud/moment' +import NcActionButton from '@nextcloud/vue/components/NcActionButton' +import NcActionLink from '@nextcloud/vue/components/NcActionLink' +import NcAvatar from '@nextcloud/vue/components/NcAvatar' +import NcDateTime from '@nextcloud/vue/components/NcDateTime' +import NcListItem from '@nextcloud/vue/components/NcListItem' +import Tooltip from '@nextcloud/vue/directives/Tooltip' const hasPermission = (permissions: number, permission: number): boolean => (permissions & permission) !== 0 @@ -151,6 +155,7 @@ export default defineComponent({ NcActionLink, NcActionButton, NcAvatar, + NcDateTime, NcListItem, BackupRestore, Download, @@ -164,20 +169,6 @@ export default defineComponent({ tooltip: Tooltip, }, - created() { - this.fetchDisplayName() - }, - - filters: { - humanReadableSize(bytes: number): string { - return formatFileSize(bytes) - }, - - humanDateFromNow(timestamp: number): string { - return moment(timestamp).fromNow() - }, - }, - props: { version: { type: Object as PropType<Version>, @@ -216,11 +207,15 @@ export default defineComponent({ previewLoaded: false, previewErrored: false, capabilities: loadState('core', 'capabilities', { files: { version_labeling: false, version_deletion: false } }), - versionAuthor: '', + versionAuthor: '' as string | null, } }, computed: { + humanReadableSize() { + return formatFileSize(this.version.size) + }, + versionLabel(): string { const label = this.version.label ?? '' @@ -239,6 +234,10 @@ export default defineComponent({ return label }, + versionHumanExplicitDate(): string { + return moment(this.version.mtime).format('LLLL') + }, + downloadURL(): string { if (this.isCurrent) { return getRootUrl() + joinPaths('/remote.php/webdav', this.fileInfo.path, this.fileInfo.name) @@ -247,10 +246,6 @@ export default defineComponent({ } }, - formattedDate(): string { - return moment(this.version.mtime).format('LLL') - }, - enableLabeling(): boolean { return this.capabilities.files.version_labeling === true }, @@ -277,7 +272,7 @@ export default defineComponent({ const downloadAttribute = this.fileInfo.shareAttributes .find((attribute) => attribute.scope === 'permissions' && attribute.key === 'download') || {} // If the download attribute is set to false, the file is not downloadable - if (downloadAttribute?.enabled === false) { + if (downloadAttribute?.value === false) { return false } } @@ -286,6 +281,10 @@ export default defineComponent({ }, }, + created() { + this.fetchDisplayName() + }, + methods: { labelUpdate() { this.$emit('label-update-request') @@ -304,21 +303,26 @@ export default defineComponent({ }, async fetchDisplayName() { - // check to make sure that we have a valid author - in case database did not migrate, null author, etc. - if (this.version.author) { + this.versionAuthor = null + if (!this.version.author) { + return + } + + if (this.version.author === getCurrentUser()?.uid) { + this.versionAuthor = t('files_versions', 'You') + } else { try { - const { data } = await axios.get(generateOcsUrl(`/cloud/users/${this.version.author}`)) - this.versionAuthor = data.ocs.data.displayname - } catch (e) { - // Promise got rejected - default to null author to not try to load author profile - this.versionAuthor = null + const { data } = await axios.post(generateUrl('/displaynames'), { users: [this.version.author] }) + this.versionAuthor = data.users[this.version.author] + } catch (error) { + logger.warn('Could not load user display name', { error }) } } }, click() { if (!this.canView) { - window.location = this.downloadURL + window.location.href = this.downloadURL return } this.$emit('click', { version: this.version }) @@ -348,14 +352,30 @@ export default defineComponent({ gap: 0.5rem; color: var(--color-main-text); font-weight: 500; + overflow: hidden; &__label { font-weight: 700; + // Fix overflow on narrow screens + overflow: hidden; + text-overflow: ellipsis; + min-width: 110px; + } + + &__author_name { + overflow: hidden; + text-overflow: ellipsis; + } + + &__date { + // Fix overflow on narrow screens + overflow: hidden; + text-overflow: ellipsis; } &__subline { - color: var(--color-text-maxcontrast) - } + color: var(--color-text-maxcontrast) + } } &__image { diff --git a/apps/files_versions/src/components/VersionLabelDialog.vue b/apps/files_versions/src/components/VersionLabelDialog.vue new file mode 100644 index 00000000000..760780cae61 --- /dev/null +++ b/apps/files_versions/src/components/VersionLabelDialog.vue @@ -0,0 +1,123 @@ +<!-- + - SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> +<template> + <NcDialog :buttons="dialogButtons" + content-classes="version-label-modal" + is-form + :open="open" + size="normal" + :name="t('files_versions', 'Name this version')" + @update:open="$emit('update:open', $event)" + @submit="setVersionLabel(editedVersionLabel)"> + <NcTextField ref="labelInput" + class="version-label-modal__input" + :label="t('files_versions', 'Version name')" + :placeholder="t('files_versions', 'Version name')" + :value.sync="editedVersionLabel" /> + + <p class="version-label-modal__info"> + {{ t('files_versions', 'Named versions are persisted, and excluded from automatic cleanups when your storage quota is full.') }} + </p> + </NcDialog> +</template> + +<script lang="ts"> +import { t } from '@nextcloud/l10n' +import { defineComponent } from 'vue' +import svgCheck from '@mdi/svg/svg/check.svg?raw' + +import NcDialog from '@nextcloud/vue/components/NcDialog' +import NcTextField from '@nextcloud/vue/components/NcTextField' + +type Focusable = Vue & { focus: () => void } + +export default defineComponent({ + name: 'VersionLabelDialog', + components: { + NcDialog, + NcTextField, + }, + props: { + open: { + type: Boolean, + default: false, + }, + versionLabel: { + type: String, + default: '', + }, + }, + data() { + return { + editedVersionLabel: '', + } + }, + computed: { + dialogButtons() { + const buttons: unknown[] = [] + if (this.versionLabel.trim() === '') { + // If there is no label just offer a cancel action that just closes the dialog + buttons.push({ + label: t('files_versions', 'Cancel'), + }) + } else { + // If there is already a label set, offer to remove the version label + buttons.push({ + label: t('files_versions', 'Remove version name'), + type: 'error', + nativeType: 'reset', + callback: () => { this.setVersionLabel('') }, + }) + } + return [ + ...buttons, + { + label: t('files_versions', 'Save version name'), + type: 'primary', + nativeType: 'submit', + icon: svgCheck, + }, + ] + }, + }, + watch: { + versionLabel: { + immediate: true, + handler(label) { + this.editedVersionLabel = label ?? '' + }, + }, + open: { + immediate: true, + handler(open) { + if (open) { + this.$nextTick(() => (this.$refs.labelInput as Focusable).focus()) + } + this.editedVersionLabel = this.versionLabel + }, + }, + }, + methods: { + setVersionLabel(label: string) { + this.$emit('label-update', label) + }, + + t, + }, +}) +</script> + +<style scoped lang="scss"> +.version-label-modal { + &__info { + color: var(--color-text-maxcontrast); + margin-block: calc(3 * var(--default-grid-baseline)); + } + + &__input { + margin-block-start: calc(2 * var(--default-grid-baseline)); + } +} +</style> diff --git a/apps/files_versions/src/components/VersionLabelForm.vue b/apps/files_versions/src/components/VersionLabelForm.vue deleted file mode 100644 index 5b8251b36c0..00000000000 --- a/apps/files_versions/src/components/VersionLabelForm.vue +++ /dev/null @@ -1,115 +0,0 @@ -<!-- - - @copyright Copyright (c) 2024 Louis Chemineau <louis@chmn.me> - - - - @author Louis Chemineau <louis@chmn.me> - - - - @license AGPL-3.0-or-later - - - - 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> - <form class="version-label-modal" - @submit.prevent="setVersionLabel(innerVersionLabel)"> - <label> - <div class="version-label-modal__title">{{ t('files_versions', 'Version name') }}</div> - <NcTextField ref="labelInput" - :value.sync="innerVersionLabel" - :placeholder="t('files_versions', 'Version name')" - :label-outside="true" /> - </label> - - <div class="version-label-modal__info"> - {{ t('files_versions', 'Named versions are persisted, and excluded from automatic cleanups when your storage quota is full.') }} - </div> - - <div class="version-label-modal__actions"> - <NcButton :disabled="innerVersionLabel.trim().length === 0" @click="setVersionLabel('')"> - {{ t('files_versions', 'Remove version name') }} - </NcButton> - <NcButton type="primary" native-type="submit"> - <template #icon> - <Check /> - </template> - {{ t('files_versions', 'Save version name') }} - </NcButton> - </div> - </form> -</template> - -<script lang="ts"> -import Check from 'vue-material-design-icons/Check.vue' -import NcButton from '@nextcloud/vue/dist/Components/NcButton.js' -import NcTextField from '@nextcloud/vue/dist/Components/NcTextField.js' -import { translate } from '@nextcloud/l10n' - -import { defineComponent } from 'vue' - -export default defineComponent({ - name: 'VersionLabelForm', - components: { - NcButton, - NcTextField, - Check, - }, - props: { - versionLabel: { - type: String, - default: '', - }, - }, - data() { - return { - innerVersionLabel: this.versionLabel, - } - }, - mounted() { - this.$nextTick(() => { - (this.$refs.labelInput as Vue).$el.getElementsByTagName('input')[0].focus() - }) - }, - methods: { - setVersionLabel(label: string) { - this.$emit('label-update', label) - }, - - t: translate, - }, -}) -</script> - -<style scoped lang="scss"> -.version-label-modal { - display: flex; - justify-content: space-between; - flex-direction: column; - height: 250px; - padding: 16px; - - &__title { - margin-bottom: 12px; - font-weight: 600; - } - - &__info { - margin-top: 12px; - color: var(--color-text-maxcontrast); - } - - &__actions { - display: flex; - justify-content: space-between; - margin-top: 64px; - } -} -</style> diff --git a/apps/files_versions/src/components/VirtualScrolling.vue b/apps/files_versions/src/components/VirtualScrolling.vue index fc199edd2ef..5a502036839 100644 --- a/apps/files_versions/src/components/VirtualScrolling.vue +++ b/apps/files_versions/src/components/VirtualScrolling.vue @@ -1,24 +1,7 @@ <!-- - - @copyright Copyright (c) 2022 Louis Chemineau <louis@chmn.me> - - - - @author Louis Chemineau <louis@chmn.me> - - - - @license AGPL-3.0-or-later - - - - 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/>. - - - --> + - SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + - SPDX-License-Identifier: AGPL-3.0-or-later +--> <template> <div v-if="!useWindow && containerElement === null" ref="container" class="vs-container"> <div ref="rowsContainer" |