perf(settings): Cancel request on new search

Signed-off-by: Christopher Ng <chrng8@gmail.com>
pull/51336/head
Christopher Ng 2025-03-25 14:31:44 +07:00
parent a1a4988c27
commit 8a3a38815f
4 changed files with 70 additions and 35 deletions

@ -42,7 +42,7 @@
<NcAppNavigationList class="account-management__group-list"
aria-describedby="group-list-desc"
data-cy-users-settings-navigation-groups="custom">
<GroupListItem v-for="group in filteredGroups"
<GroupListItem v-for="group in userGroups"
:id="group.id"
ref="groupListItems"
:key="group.id"
@ -57,6 +57,8 @@
</template>
<script setup lang="ts">
import type { Group } from '../utils/groups.ts'
import { computed, ref, watch, onBeforeMount } from 'vue'
import { Fragment } from 'vue-frag'
import { useRoute, useRouter } from 'vue-router/composables'
@ -76,17 +78,9 @@ import GroupListItem from './GroupListItem.vue'
import { useFormatGroups } from '../composables/useGroupsNavigation.ts'
import { useStore } from '../store'
import { searchGroups } from '../service/groups.ts'
import logger from '../logger.ts'
interface Group {
id: string
displayname: string
usercount: number
disabled: number
canAdd: boolean
canRemove: boolean
}
const store = useStore()
const route = useRoute()
const router = useRouter()
@ -123,10 +117,6 @@ const loadingGroups = ref(false)
const offset = ref(0)
/** Search query for groups */
const groupsSearchQuery = ref('')
/** Filtered groups */
const filteredGroups = computed(() => userGroups.value.filter((group) => {
return group.title.toLocaleLowerCase().includes(groupsSearchQuery.value.toLocaleLowerCase())
}))
const groupListItems = ref([])
const lastGroupListItem = computed(() => {
@ -148,18 +138,24 @@ watch(groupsSearchQuery, async () => {
await loadGroups()
})
/** Cancelable promise for search groups request */
const promise = ref(null)
/**
* Load groups
*/
async function loadGroups() {
if (promise.value) {
promise.value.cancel()
}
loadingGroups.value = true
try {
const { data } = await store.dispatch('searchGroups', {
promise.value = searchGroups({
search: groupsSearchQuery.value,
offset: offset.value,
limit: 25,
})
const groups: Group[] = data.ocs?.data?.groups ?? []
const groups: Group[] = (await promise.value).data.ocs?.data?.groups ?? []
if (groups.length > 0) {
offset.value += 25
}
@ -176,6 +172,7 @@ async function loadGroups() {
} catch (error) {
logger.error(t('settings', 'Failed to load groups'), { error })
}
promise.value = null
loadingGroups.value = false
}

@ -0,0 +1,35 @@
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import type { AxiosResponse } from '@nextcloud/axios'
import axios from '@nextcloud/axios'
import { generateOcsUrl } from '@nextcloud/router'
import { CancelablePromise } from 'cancelable-promise'
/**
* Search groups
*
* @param {object} options Options
* @param {string} options.search Search query
* @param {number} options.offset Offset
* @param {number} options.limit Limit
*/
export const searchGroups = ({ search, offset, limit }): CancelablePromise<AxiosResponse> => {
const controller = new AbortController()
return new CancelablePromise(async (resolve, reject, onCancel) => {
onCancel(() => controller.abort())
try {
const response = await axios.get(
generateOcsUrl('/cloud/groups/details?search={search}&offset={offset}&limit={limit}', { search, offset, limit }), {
signal: controller.signal,
},
)
resolve(response)
} catch (error) {
reject(error)
}
})
}

@ -323,25 +323,6 @@ const actions = {
})
},
/**
* search groups
*
* @param {object} context Store context
* @param {object} options Options
* @param {string} options.search Search query
* @param {number} options.offset List offset
* @param {number} options.limit List limit
* @return {Promise}
*/
searchGroups(context, { search, offset, limit }) {
return api.get(generateOcsUrl('cloud/groups/details?search={search}&offset={offset}&limit={limit}', { search, offset, limit }))
.catch((error) => {
if (!axios.isCancel(error)) {
context.commit('API_FAILURE', error)
}
})
},
/**
* Get user details
*

@ -0,0 +1,22 @@
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
export interface Group {
id: string
displayname: string
usercount: number
disabled: number
canAdd: boolean
canRemove: boolean
}
export const formatGroup = (group: Group) => ({
id: group.id,
name: group.displayname,
usercount: group.usercount,
disabled: group.disabled,
canAdd: group.canAdd,
canRemove: group.canRemove,
})