You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

NavigationQuota.vue 4.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. <template>
  2. <NcAppNavigationItem v-if="storageStats"
  3. :aria-label="t('files', 'Storage informations')"
  4. :class="{ 'app-navigation-entry__settings-quota--not-unlimited': storageStats.quota >= 0}"
  5. :loading="loadingStorageStats"
  6. :name="storageStatsTitle"
  7. :title="storageStatsTooltip"
  8. class="app-navigation-entry__settings-quota"
  9. data-cy-files-navigation-settings-quota
  10. @click.stop.prevent="debounceUpdateStorageStats">
  11. <ChartPie slot="icon" :size="20" />
  12. <!-- Progress bar -->
  13. <NcProgressBar v-if="storageStats.quota >= 0"
  14. slot="extra"
  15. :error="storageStats.relative > 80"
  16. :value="Math.min(storageStats.relative, 100)" />
  17. </NcAppNavigationItem>
  18. </template>
  19. <script>
  20. import { formatFileSize } from '@nextcloud/files'
  21. import { generateUrl } from '@nextcloud/router'
  22. import { loadState } from '@nextcloud/initial-state'
  23. import { showError } from '@nextcloud/dialogs'
  24. import { debounce, throttle } from 'throttle-debounce'
  25. import { translate } from '@nextcloud/l10n'
  26. import axios from '@nextcloud/axios'
  27. import ChartPie from 'vue-material-design-icons/ChartPie.vue'
  28. import NcAppNavigationItem from '@nextcloud/vue/dist/Components/NcAppNavigationItem.js'
  29. import NcProgressBar from '@nextcloud/vue/dist/Components/NcProgressBar.js'
  30. import logger from '../logger.js'
  31. import { subscribe } from '@nextcloud/event-bus'
  32. export default {
  33. name: 'NavigationQuota',
  34. components: {
  35. ChartPie,
  36. NcAppNavigationItem,
  37. NcProgressBar,
  38. },
  39. data() {
  40. return {
  41. loadingStorageStats: false,
  42. storageStats: loadState('files', 'storageStats', null),
  43. }
  44. },
  45. computed: {
  46. storageStatsTitle() {
  47. const usedQuotaByte = formatFileSize(this.storageStats?.used)
  48. const quotaByte = formatFileSize(this.storageStats?.quota)
  49. // If no quota set
  50. if (this.storageStats?.quota < 0) {
  51. return this.t('files', '{usedQuotaByte} used', { usedQuotaByte })
  52. }
  53. return this.t('files', '{used} of {quota} used', {
  54. used: usedQuotaByte,
  55. quota: quotaByte,
  56. })
  57. },
  58. storageStatsTooltip() {
  59. if (!this.storageStats.relative) {
  60. return ''
  61. }
  62. return this.t('files', '{relative}% used', this.storageStats)
  63. },
  64. },
  65. beforeMount() {
  66. /**
  67. * Update storage stats every minute
  68. * TODO: remove when all views are migrated to Vue
  69. */
  70. setInterval(this.throttleUpdateStorageStats, 60 * 1000)
  71. subscribe('files:file:created', this.throttleUpdateStorageStats)
  72. subscribe('files:file:deleted', this.throttleUpdateStorageStats)
  73. subscribe('files:file:moved', this.throttleUpdateStorageStats)
  74. subscribe('files:file:updated', this.throttleUpdateStorageStats)
  75. subscribe('files:folder:created', this.throttleUpdateStorageStats)
  76. subscribe('files:folder:deleted', this.throttleUpdateStorageStats)
  77. subscribe('files:folder:moved', this.throttleUpdateStorageStats)
  78. subscribe('files:folder:updated', this.throttleUpdateStorageStats)
  79. },
  80. methods: {
  81. // From user input
  82. debounceUpdateStorageStats: debounce(200, function(event) {
  83. this.updateStorageStats(event)
  84. }),
  85. // From interval or event bus
  86. throttleUpdateStorageStats: throttle(1000, function(event) {
  87. this.updateStorageStats(event)
  88. }),
  89. /**
  90. * Update the storage stats
  91. * Throttled at max 1 refresh per minute
  92. *
  93. * @param {Event} [event = null] if user interaction
  94. */
  95. async updateStorageStats(event = null) {
  96. if (this.loadingStorageStats) {
  97. return
  98. }
  99. this.loadingStorageStats = true
  100. try {
  101. const response = await axios.get(generateUrl('/apps/files/api/v1/stats'))
  102. if (!response?.data?.data) {
  103. throw new Error('Invalid storage stats')
  104. }
  105. this.storageStats = response.data.data
  106. } catch (error) {
  107. logger.error('Could not refresh storage stats', { error })
  108. // Only show to the user if it was manually triggered
  109. if (event) {
  110. showError(t('files', 'Could not refresh storage stats'))
  111. }
  112. } finally {
  113. this.loadingStorageStats = false
  114. }
  115. },
  116. t: translate,
  117. },
  118. }
  119. </script>
  120. <style lang="scss" scoped>
  121. // User storage stats display
  122. .app-navigation-entry__settings-quota {
  123. // Align title with progress and icon
  124. &--not-unlimited::v-deep .app-navigation-entry__title {
  125. margin-top: -4px;
  126. }
  127. progress {
  128. position: absolute;
  129. bottom: 10px;
  130. margin-left: 44px;
  131. width: calc(100% - 44px - 22px);
  132. }
  133. }
  134. </style>