diff --git a/src/components/NcSettingsSelectGroup/NcSettingsSelectGroup.vue b/src/components/NcSettingsSelectGroup/NcSettingsSelectGroup.vue index 4591c6da93..4433786bff 100644 --- a/src/components/NcSettingsSelectGroup/NcSettingsSelectGroup.vue +++ b/src/components/NcSettingsSelectGroup/NcSettingsSelectGroup.vue @@ -61,7 +61,7 @@ section * { :close-on-select="false" :disabled="disabled" @input="update" - @search="findGroup" /> + @search="onSearch" /> @@ -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', @@ -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 { @@ -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
**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)) @@ -182,6 +239,13 @@ export default { } return false }, + + /** + * Debounce the group search (reduce API calls) + */ + onSearch: debounce(function(query) { + this.loadGroup(query) + }, 200), }, }