summaryrefslogtreecommitdiffstats
path: root/apps/files_versions/src/components/Version.vue
diff options
context:
space:
mode:
Diffstat (limited to 'apps/files_versions/src/components/Version.vue')
-rw-r--r--apps/files_versions/src/components/Version.vue302
1 files changed, 302 insertions, 0 deletions
diff --git a/apps/files_versions/src/components/Version.vue b/apps/files_versions/src/components/Version.vue
new file mode 100644
index 00000000000..a06d6a0ba8e
--- /dev/null
+++ b/apps/files_versions/src/components/Version.vue
@@ -0,0 +1,302 @@
+<!--
+ - @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/>.
+ -->
+<template>
+ <div>
+ <NcListItem class="version"
+ :title="versionLabel"
+ :href="downloadURL"
+ :force-display-actions="true">
+ <template #icon>
+ <img lazy="true"
+ :src="previewURL"
+ alt=""
+ height="256"
+ width="256"
+ class="version__image">
+ </template>
+ <template #subtitle>
+ <div class="version__info">
+ <span v-tooltip="formattedDate">{{ version.mtime | humanDateFromNow }}</span>
+ <!-- Separate dot to improve alignement -->
+ <span class="version__info__size">•</span>
+ <span class="version__info__size">{{ version.size | humanReadableSize }}</span>
+ </div>
+ </template>
+ <template #actions>
+ <NcActionButton v-if="capabilities.files.version_labeling === true"
+ :close-after-click="true"
+ @click="openVersionLabelModal">
+ <template #icon>
+ <Pencil :size="22" />
+ </template>
+ {{ version.label === '' ? t('files_versions', 'Name this version') : t('files_versions', 'Edit version name') }}
+ </NcActionButton>
+ <NcActionButton v-if="!isCurrent"
+ :close-after-click="true"
+ @click="restoreVersion">
+ <template #icon>
+ <BackupRestore :size="22" />
+ </template>
+ {{ t('files_versions', 'Restore version') }}
+ </NcActionButton>
+ <NcActionLink :href="downloadURL"
+ :close-after-click="true"
+ :download="downloadURL">
+ <template #icon>
+ <Download :size="22" />
+ </template>
+ {{ t('files_versions', 'Download version') }}
+ </NcActionLink>
+ <NcActionButton v-if="!isCurrent"
+ :close-after-click="true"
+ @click="deleteVersion">
+ <template #icon>
+ <Delete :size="22" />
+ </template>
+ {{ t('files_versions', 'Delete version') }}
+ </NcActionButton>
+ </template>
+ </NcListItem>
+ <NcModal v-if="showVersionLabelForm"
+ :title="t('files_versions', 'Name this version')"
+ @close="showVersionLabelForm = false">
+ <form class="version-label-modal"
+ @submit.prevent="setVersionLabel(formVersionLabelValue)">
+ <label>
+ <div class="version-label-modal__title">{{ t('photos', 'Version name') }}</div>
+ <NcTextField ref="labelInput"
+ :value.sync="formVersionLabelValue"
+ :placeholder="t('photos', 'Version name')"
+ :label-outside="true" />
+ </label>
+
+ <div class="version-label-modal__info">
+ {{ t('photos', 'Named versions are persisted, and excluded from automatic cleanups when your storage quota is full.') }}
+ </div>
+
+ <div class="version-label-modal__actions">
+ <NcButton :disabled="formVersionLabelValue.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>
+ </NcModal>
+ </div>
+</template>
+
+<script>
+import BackupRestore from 'vue-material-design-icons/BackupRestore.vue'
+import Download from 'vue-material-design-icons/Download.vue'
+import Pencil from 'vue-material-design-icons/Pencil.vue'
+import Check from 'vue-material-design-icons/Check.vue'
+import Delete from 'vue-material-design-icons/Delete'
+import { NcActionButton, NcActionLink, NcListItem, NcModal, NcButton, NcTextField, Tooltip } from '@nextcloud/vue'
+import moment from '@nextcloud/moment'
+import { translate } from '@nextcloud/l10n'
+import { joinPaths } from '@nextcloud/paths'
+import { generateUrl } from '@nextcloud/router'
+import { loadState } from '@nextcloud/initial-state'
+
+export default {
+ name: 'Version',
+ components: {
+ NcActionLink,
+ NcActionButton,
+ NcListItem,
+ NcModal,
+ NcButton,
+ NcTextField,
+ BackupRestore,
+ Download,
+ Pencil,
+ Check,
+ Delete,
+ },
+ directives: {
+ tooltip: Tooltip,
+ },
+ filters: {
+ /**
+ * @param {number} bytes
+ * @return {string}
+ */
+ humanReadableSize(bytes) {
+ return OC.Util.humanFileSize(bytes)
+ },
+ /**
+ * @param {number} timestamp
+ * @return {string}
+ */
+ humanDateFromNow(timestamp) {
+ return moment(timestamp).fromNow()
+ },
+ },
+ props: {
+ /** @type {Vue.PropOptions<import('../utils/versions.js').Version>} */
+ version: {
+ type: Object,
+ required: true,
+ },
+ fileInfo: {
+ type: Object,
+ required: true,
+ },
+ isCurrent: {
+ type: Boolean,
+ default: false,
+ },
+ isFirstVersion: {
+ type: Boolean,
+ default: false,
+ },
+ },
+ data() {
+ return {
+ showVersionLabelForm: false,
+ formVersionLabelValue: this.version.label,
+ capabilities: loadState('core', 'capabilities', { files: { version_labeling: false } }),
+ }
+ },
+ computed: {
+ /**
+ * @return {string}
+ */
+ versionLabel() {
+ if (this.isCurrent) {
+ if (this.version.label === '') {
+ return translate('files_versions', 'Current version')
+ } else {
+ return `${this.version.label} (${translate('files_versions', 'Current version')})`
+ }
+ }
+
+ if (this.isFirstVersion && this.version.label === '') {
+ return translate('files_versions', 'Initial version')
+ }
+
+ return this.version.label
+ },
+
+ /**
+ * @return {string}
+ */
+ downloadURL() {
+ if (this.isCurrent) {
+ return joinPaths('/remote.php/webdav', this.fileInfo.path, this.fileInfo.name)
+ } else {
+ return this.version.url
+ }
+ },
+
+ /**
+ * @return {string}
+ */
+ previewURL() {
+ if (this.isCurrent) {
+ return generateUrl('/core/preview?fileId={fileId}&c={fileEtag}&x=250&y=250&forceIcon=0&a=0', {
+ fileId: this.fileInfo.id,
+ fileEtag: this.fileInfo.etag,
+ })
+ } else {
+ return this.version.preview
+ }
+ },
+ },
+ methods: {
+ openVersionLabelModal() {
+ this.showVersionLabelForm = true
+ this.$nextTick(() => {
+ this.$refs.labelInput.$el.getElementsByTagName('input')[0].focus()
+ })
+ },
+
+ restoreVersion() {
+ this.$emit('restore', this.version)
+ },
+
+ setVersionLabel(label) {
+ this.formVersionLabelValue = label
+ this.showVersionLabelForm = false
+ this.$emit('label-update', this.version, label)
+ },
+
+ deleteVersion() {
+ this.$emit('delete', this.version)
+ },
+
+ formattedDate() {
+ return moment(this.version.mtime)
+ },
+ },
+}
+</script>
+
+<style scoped lang="scss">
+.version {
+ display: flex;
+ flex-direction: row;
+
+ &__info {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 0.5rem;
+
+ &__size {
+ color: var(--color-text-lighter);
+ }
+ }
+
+ &__image {
+ width: 3rem;
+ height: 3rem;
+ border: 1px solid var(--color-border);
+ border-radius: var(--border-radius-large);
+ }
+}
+
+.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>