Skip to content

Commit

Permalink
fear(NcSettingsSelectGroup): Reduce API calls for loading groups
Browse files Browse the repository at this point in the history
* Debounce search requests when typing
* Cache initial groups (empty search) for all components of current page

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
  • Loading branch information
susnux committed May 22, 2023
1 parent 48f5f45 commit 48ae6dd
Showing 1 changed file with 70 additions and 6 deletions.
76 changes: 70 additions & 6 deletions src/components/NcSettingsSelectGroup/NcSettingsSelectGroup.vue
Expand Up @@ -61,7 +61,7 @@ section * {
:close-on-select="false"
:disabled="disabled"
@input="update"
@search="findGroup" />
@search="onSearch" />
</div>
</template>

Expand All @@ -74,6 +74,7 @@ import GenRandomId from '../../utils/GenRandomId.js'
import axios from '@nextcloud/axios'
import { showError } from '@nextcloud/dialogs'
import { generateOcsUrl } from '@nextcloud/router'
import { debounce } from 'debounce'
export default {
name: 'NcSettingsSelectGroup',
Expand Down Expand Up @@ -136,11 +137,20 @@ export default {
}
},
computed: {
inputValue() {
return this.getValueObject
/**
* Validate input value and only return valid strings (group IDs)
*
* @return {string[]}
*/
filteredValue() {
return this.value.filter((group) => group !== '' && typeof group === 'string')
},
getValueObject() {
return this.value.filter((group) => group !== '' && typeof group !== 'undefined').map(
/**
* value property converted to an array of group objects used as input for the NcSelect
*/
inputValue() {
return this.filteredValue.map(
(id) => {
if (typeof this.groups[id] === 'undefined') {
return {
Expand All @@ -152,17 +162,64 @@ export default {
}
)
},
/**
* Convert groups object to array of groups required for NcSelect.options
*
* @return {object[]}
*/
groupsArray() {
return Object.values(this.groups)
},
},
watch: {
/**
* If the value is changed, check that all groups are loaded so we show the correct display name
*/
value: {
handler() {
const loadedGroupIds = Object.keys(this.groups)
const missing = this.filteredValue.filter(group => !loadedGroupIds.includes(group))
missing.forEach((groupId) => {
this.loadGroup(groupId)
})
},
// Run the watch handler also when the component is initially mounted
immediate: true,
},
},
/**
* Load groups matching the empty query to reduce API calls
*/
async mounted() {
// version scoped to prevent issues with different library versions
const savedGroups = window.sessionStorage.getItem(`${appName}:${appVersion}/initialGroups`)
if (savedGroups) {
JSON.parse(savedGroups).forEach((group) => { this.groups[group.id] = group.displayname })
} else {
await this.loadGroup('')
window.sessionStorage.setItem(`${appName}:${appVersion}/initialGroups`, JSON.stringify(this.groupsArray))
}
},
methods: {
/**
* Called when a new group is selected or previous group is deselected to emit the update event
*
* @param {object[]} updatedValue Array of selected groups
*/
update(updatedValue) {
const value = updatedValue.map((element) => element.id)
/** Emitted when the groups selection changes<br />**Payload:** `value` (`Array`) - *Ids of selected groups */
this.$emit('input', value)
},
async findGroup(query) {
/**
* Use provisioning API to search for given group and save it in the groups object
*
* @param {string} query The query like parts of the id oder display name
* @return {boolean}
*/
async loadGroup(query) {
try {
query = typeof query === 'string' ? encodeURI(query) : ''
const response = await axios.get(generateOcsUrl(`cloud/groups/details?search=${query}&limit=10`, 2))
Expand All @@ -182,6 +239,13 @@ export default {
}
return false
},
/**
* Debounce the group search (reduce API calls)
*/
onSearch: debounce(function(query) {
this.loadGroup(query)
}),
},
}
</script>

0 comments on commit 48ae6dd

Please sign in to comment.