VersionTab.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. <!--
  2. - @copyright 2022 Carl Schwan <carl@carlschwan.eu>
  3. - @license AGPL-3.0-or-later
  4. -
  5. - This program is free software: you can redistribute it and/or modify
  6. - it under the terms of the GNU Affero General Public License as
  7. - published by the Free Software Foundation, either version 3 of the
  8. - License, or (at your option) any later version.
  9. -
  10. - This program is distributed in the hope that it will be useful,
  11. - but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. - GNU Affero General Public License for more details.
  14. -
  15. - You should have received a copy of the GNU Affero General Public License
  16. - along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. -->
  18. <template>
  19. <div>
  20. <ul>
  21. <NcListItem v-for="version in versions"
  22. :key="version.mtime"
  23. class="version"
  24. :title="version.title"
  25. :href="version.url">
  26. <template #icon>
  27. <img lazy="true"
  28. :src="version.preview"
  29. alt=""
  30. height="256"
  31. width="256"
  32. class="version__image">
  33. </template>
  34. <template #subtitle>
  35. <div class="version__info">
  36. <span>{{ version.mtime | humanDateFromNow }}</span>
  37. <!-- Separate dot to improve alignement -->
  38. <span class="version__info__size">•</span>
  39. <span class="version__info__size">{{ version.size | humanReadableSize }}</span>
  40. </div>
  41. </template>
  42. <template v-if="!version.isCurrent" #actions>
  43. <NcActionLink :href="version.url"
  44. :download="version.url">
  45. <template #icon>
  46. <Download :size="22" />
  47. </template>
  48. {{ t('files_versions', 'Download version') }}
  49. </NcActionLink>
  50. <NcActionButton @click="restoreVersion(version)">
  51. <template #icon>
  52. <BackupRestore :size="22" />
  53. </template>
  54. {{ t('files_versions', 'Restore version') }}
  55. </NcActionButton>
  56. </template>
  57. </NcListItem>
  58. <NcEmptyContent v-if="!loading && versions.length === 1"
  59. :title="t('files_version', 'No versions yet')">
  60. <!-- length === 1, since we don't want to show versions if there is only the current file -->
  61. <template #icon>
  62. <BackupRestore />
  63. </template>
  64. </NcEmptyContent>
  65. </ul>
  66. </div>
  67. </template>
  68. <script>
  69. import BackupRestore from 'vue-material-design-icons/BackupRestore.vue'
  70. import Download from 'vue-material-design-icons/Download.vue'
  71. import NcActionButton from '@nextcloud/vue/dist/Components/NcActionButton.js'
  72. import NcActionLink from '@nextcloud/vue/dist/Components/NcActionLink.js'
  73. import NcListItem from '@nextcloud/vue/dist/Components/NcListItem.js'
  74. import NcEmptyContent from '@nextcloud/vue/dist/Components/NcEmptyContent.js'
  75. import { showError, showSuccess } from '@nextcloud/dialogs'
  76. import { fetchVersions, restoreVersion } from '../utils/versions.js'
  77. import moment from '@nextcloud/moment'
  78. export default {
  79. name: 'VersionTab',
  80. components: {
  81. NcEmptyContent,
  82. NcActionLink,
  83. NcActionButton,
  84. NcListItem,
  85. BackupRestore,
  86. Download,
  87. },
  88. filters: {
  89. humanReadableSize(bytes) {
  90. return OC.Util.humanFileSize(bytes)
  91. },
  92. humanDateFromNow(timestamp) {
  93. return moment(timestamp * 1000).fromNow()
  94. },
  95. },
  96. data() {
  97. return {
  98. fileInfo: null,
  99. /** @type {import('../utils/versions.js').Version[]} */
  100. versions: [],
  101. loading: false,
  102. }
  103. },
  104. methods: {
  105. /**
  106. * Update current fileInfo and fetch new data
  107. *
  108. * @param {object} fileInfo the current file FileInfo
  109. */
  110. async update(fileInfo) {
  111. this.fileInfo = fileInfo
  112. this.resetState()
  113. this.fetchVersions()
  114. },
  115. /**
  116. * Get the existing versions infos
  117. */
  118. async fetchVersions() {
  119. try {
  120. this.loading = true
  121. this.versions = await fetchVersions(this.fileInfo)
  122. } finally {
  123. this.loading = false
  124. }
  125. },
  126. /**
  127. * Restore the given version
  128. *
  129. * @param version
  130. */
  131. async restoreVersion(version) {
  132. try {
  133. await restoreVersion(version, this.fileInfo)
  134. // File info is not updated so we manually update its size and mtime if the restoration went fine.
  135. this.fileInfo.size = version.size
  136. this.fileInfo.mtime = version.lastmod
  137. showSuccess(t('files_versions', 'Version restored'))
  138. await this.fetchVersions()
  139. } catch (exception) {
  140. showError(t('files_versions', 'Could not restore version'))
  141. }
  142. },
  143. /**
  144. * Reset the current view to its default state
  145. */
  146. resetState() {
  147. this.versions = []
  148. },
  149. },
  150. }
  151. </script>
  152. <style scopped lang="scss">
  153. .version {
  154. display: flex;
  155. flex-direction: row;
  156. &__info {
  157. display: flex;
  158. flex-direction: row;
  159. align-items: center;
  160. gap: 0.5rem;
  161. &__size {
  162. color: var(--color-text-lighter);
  163. }
  164. }
  165. &__image {
  166. width: 3rem;
  167. height: 3rem;
  168. border: 1px solid var(--color-border);
  169. margin-right: 1rem;
  170. border-radius: var(--border-radius-large);
  171. }
  172. }
  173. </style>