Skip to content

Commit

Permalink
fixup! feat(files): Quota in navigation
Browse files Browse the repository at this point in the history
Signed-off-by: John Molakvoæ <skjnldsv@protonmail.com>
  • Loading branch information
skjnldsv committed Jan 11, 2023
1 parent e086296 commit e55eb37
Show file tree
Hide file tree
Showing 14 changed files with 1,869 additions and 748 deletions.
9 changes: 8 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@ module.exports = {
oc_userconfig: true,
dayNames: true,
firstDay: true,
'cypress/globals': true,
},
extends: ['@nextcloud'],
plugins: [
'cypress',
],
extends: [
'@nextcloud',
'plugin:cypress/recommended',
],
rules: {
'no-tabs': 'warn',
// TODO: make sure we fix this as this is bad vue coding style.
Expand Down
134 changes: 134 additions & 0 deletions apps/files/src/components/NavigationQuota.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
<template>
<NcAppNavigationItem v-if="storageStats"
:aria-label="t('files', 'Storage informations')"
:class="{ 'app-navigation-entry__settings-quota--not-unlimited': storageStats.quota >= 0}"
:loading="loadingStorageStats"
:name="storageStatsTitle"
:title="storageStatsTooltip"
class="app-navigation-entry__settings-quota"
data-cy-files-navigation-settings-quota
@click="updateStorageStats">
<ChartPie slot="icon" :size="20" />

<!-- Progress bar -->
<NcProgressBar v-if="storageStats.quota >= 0"
slot="extra"
:error="storageStats.relative > 80"
:value="Math.min(storageStats.relative, 100)" />
</NcAppNavigationItem>
</template>

<script>
import { formatFileSize } from '@nextcloud/files'
import { generateUrl } from '@nextcloud/router'
import { loadState } from '@nextcloud/initial-state'
import { showError } from '@nextcloud/dialogs'
import { translate } from '@nextcloud/l10n'
import axios from '@nextcloud/axios'
import ChartPie from 'vue-material-design-icons/ChartPie.vue'
import NcAppNavigationItem from '@nextcloud/vue/dist/Components/NcAppNavigationItem.js'
import NcProgressBar from '@nextcloud/vue/dist/Components/NcProgressBar.js'
import logger from '../logger.js'
export default {
name: 'NavigationQuota',
components: {
ChartPie,
NcAppNavigationItem,
NcProgressBar,
},
data() {
return {
loadingStorageStats: false,
storageStats: loadState('files', 'storageStats', null),
}
},
computed: {
storageStatsTitle() {
const usedQuotaByte = formatFileSize(this.storageStats?.used)
const quotaByte = formatFileSize(this.storageStats?.quota)
// If no quota set
if (this.storageStats?.quota < 0) {
return this.t('files', '{usedQuotaByte} used', { usedQuotaByte })
}
return this.t('files', '{used} of {quota} used', {
used: usedQuotaByte,
quota: quotaByte,
})
},
storageStatsTooltip() {
if (!this.storageStats.relative) {
return ''
}
return this.t('files', '{relative}% used', this.storageStats)
},
},
beforeMount() {
/**
* Update storage stats every minute
* TODO: remove when all views are migrated to Vue
*/
setTimeout(this.updateStorageStats, 60 * 1000)
},
methods: {
/**
* Update the storage stats
*
* @param {Event} [event = null] if user interaction
*/
async updateStorageStats(event = null) {
event?.preventDefault()
event?.stopPropagation()
if (this.loadingStorageStats) {
return
}
this.loadingStorageStats = true
try {
const response = await axios.get(generateUrl('/apps/files/api/v1/stats'))
if (!response?.data?.data) {
throw new Error('Invalid storage stats')
}
this.storageStats = response.data.data
this.$el.querySelector('.app-navigation-entry__title').title = this.storageStatsTooltip
} catch (error) {
logger.error('Could not refresh storage stats', error)
if (event) {
showError(t('files', 'Could not refresh storage stats'))
}
} finally {
this.loadingStorageStats = false
}
},
t: translate,
},
}
</script>
<style lang="scss" scoped>
// User storage stats display
.app-navigation-entry__settings-quota {
// Align title with progress and icon
&--not-unlimited::v-deep .app-navigation-entry__title {
margin-top: -4px;
}
progress {
position: absolute;
bottom: 10px;
margin-left: 44px;
width: calc(100% - 44px - 22px);
}
}
</style>
109 changes: 106 additions & 3 deletions apps/files/src/views/Navigation.cy.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
/* eslint-disable import/first */
import * as InitialState from '@nextcloud/initial-state'
import * as L10n from '@nextcloud/l10n'
import FolderSvg from '@mdi/svg/svg/folder.svg'
import ShareSvg from '@mdi/svg/svg/share-variant.svg'

import NavigationService from '../services/Navigation'
import NavigationView from './Navigation.vue'
import router from '../router/router.js'

const Navigation = new NavigationService()

describe('Navigation renders', () => {
const Navigation = new NavigationService()

before(() => {
cy.stub(InitialState, 'loadState')
.returns({
used: 1024 * 1024 * 1024,
quota: -1,
})

})

it('renders', () => {
cy.mount(NavigationView, {
propsData: {
Expand All @@ -17,11 +27,14 @@ describe('Navigation renders', () => {
})

cy.get('[data-cy-files-navigation]').should('be.visible')
cy.get('[data-cy-files-navigation-settings-quota]').should('be.visible')
cy.get('[data-cy-files-navigation-settings-button]').should('be.visible')
})
})

describe('Navigation API', () => {
const Navigation = new NavigationService()

it('Check API entries rendering', () => {
Navigation.register({
id: 'files',
Expand Down Expand Up @@ -114,3 +127,93 @@ describe('Navigation API', () => {
}).to.throw('Navigation id files is already registered')
})
})

describe('Quota rendering', () => {
const Navigation = new NavigationService()

beforeEach(() => {
// TODO: remove when @nextcloud/l10n 2.0 is released
// https://github.com/nextcloud/nextcloud-l10n/pull/542
cy.stub(L10n, 'translate', (app, text, vars = {}, number) => {
cy.log({app, text, vars, number})
return text.replace(/%n/g, '' + number).replace(/{([^{}]*)}/g, (match, key) => {
return vars[key]
})
})
})

it('Unknown quota', () => {
cy.stub(InitialState, 'loadState')
.as('loadStateStats')
.returns(undefined)

cy.mount(NavigationView, {
propsData: {
Navigation,
},
})

cy.get('[data-cy-files-navigation-settings-quota]').should('not.exist')
})

it('Unlimited quota', () => {
cy.stub(InitialState, 'loadState')
.as('loadStateStats')
.returns({
used: 1024 * 1024 * 1024,
quota: -1,
})

cy.mount(NavigationView, {
propsData: {
Navigation,
},
})

cy.get('[data-cy-files-navigation-settings-quota]').should('be.visible')
cy.get('[data-cy-files-navigation-settings-quota]').should('contain.text', '1 GB used')
cy.get('[data-cy-files-navigation-settings-quota] progress').should('not.exist')
})

it('Non-reached quota', () => {
cy.stub(InitialState, 'loadState')
.as('loadStateStats')
.returns({
used: 1024 * 1024 * 1024,
quota: 5 * 1024 * 1024 * 1024,
relative: 20, // percent
})

cy.mount(NavigationView, {
propsData: {
Navigation,
},
})

cy.get('[data-cy-files-navigation-settings-quota]').should('be.visible')
cy.get('[data-cy-files-navigation-settings-quota]').should('contain.text', '1 GB of 5 GB used')
cy.get('[data-cy-files-navigation-settings-quota] progress').should('be.visible')
cy.get('[data-cy-files-navigation-settings-quota] progress').should('have.attr', 'value', '20')
})

it('Reached quota', () => {
cy.stub(InitialState, 'loadState')
.as('loadStateStats')
.returns({
used: 5 * 1024 * 1024 * 1024,
quota: 1024 * 1024 * 1024,
relative: 500, // percent
})

cy.mount(NavigationView, {
propsData: {
Navigation,
},
})

cy.get('[data-cy-files-navigation-settings-quota]').should('be.visible')
cy.get('[data-cy-files-navigation-settings-quota]').should('contain.text', '5 GB of 1 GB used')
cy.get('[data-cy-files-navigation-settings-quota] progress').should('be.visible')
cy.get('[data-cy-files-navigation-settings-quota] progress').should('have.attr', 'value', '100') // progress max is 100
})
})
Loading

0 comments on commit e55eb37

Please sign in to comment.