fix: avoid rapid polling while fetching teams in selector (#5485)

This commit is contained in:
Nivedin 2025-10-22 19:06:29 +05:30 committed by GitHub
parent 53e8b28459
commit aa1583763a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 55 additions and 24 deletions

View file

@ -203,7 +203,7 @@
class="!focus-visible:text-blue-600 !hover:text-blue-600 h-8 rounded border border-blue-600/25 bg-blue-500/10 pr-8 !text-blue-500 hover:border-blue-600/20 hover:bg-blue-600/20 focus-visible:border-blue-600/20 focus-visible:bg-blue-600/20"
/>
</HoppSmartSelectWrapper>
<template #content="{ hide }">
<template #content="{ hide, state }">
<div
ref="accountActions"
class="flex flex-col focus:outline-none"
@ -211,7 +211,7 @@
@keyup.escape="hide()"
@click="hide()"
>
<WorkspaceSelector />
<WorkspaceSelector :state="state" />
</div>
</template>
</tippy>

View file

@ -1,5 +1,5 @@
<template>
<div ref="rootEl">
<div class="flex flex-col">
<div class="flex flex-col">
<div class="flex flex-col">
<HoppSmartItem
@ -81,6 +81,7 @@
/>
</div>
</template>
<script setup lang="ts">
import { computed, ref, watch } from "vue"
import { useReadonlyStream } from "~/composables/stream"
@ -96,11 +97,16 @@ import { useLocalState } from "~/newstore/localstate"
import { defineActionHandler, invokeAction } from "~/helpers/actions"
import { WorkspaceService } from "~/services/workspace.service"
import { useService } from "dioc/vue"
import { useElementVisibility, useIntervalFn } from "@vueuse/core"
import { useIntervalFn, watchDebounced } from "@vueuse/core"
import { TippyState } from "~/modules/tippy"
const t = useI18n()
const colorMode = useColorMode()
const props = defineProps<{
state: TippyState | null
}>()
const showModalAdd = ref(false)
const currentUser = useReadonlyStream(
@ -116,27 +122,35 @@ const teamListAdapterError = useReadonlyStream(teamListadapter.error$, null)
const REMEMBERED_TEAM_ID = useLocalState("REMEMBERED_TEAM_ID")
const teamListFetched = ref(false)
const rootEl = ref<HTMLElement>()
const elVisible = useElementVisibility(rootEl)
const { pause: pauseListPoll, resume: resumeListPoll } = useIntervalFn(() => {
if (teamListadapter.isInitialized) {
teamListadapter.fetchList()
}
}, 10000)
watch(
elVisible,
const {
pause: pauseListPoll,
resume: resumeListPoll,
isActive: isListPolling,
} = useIntervalFn(
() => {
if (elVisible.value) {
if (teamListadapter.isInitialized) {
teamListadapter.fetchList()
}
},
10000,
{ immediate: false }
)
resumeListPoll()
// A debounced watcher to avoid rapid polling when component is mounted.
// only poll when the component is visible and pause when not visible.
watchDebounced(
() => props.state?.isVisible,
(isVisible) => {
if (isVisible) {
if (!isListPolling.value) {
teamListadapter.fetchList()
resumeListPoll()
}
} else {
pauseListPoll()
}
},
{ immediate: true }
{ debounce: 200 }
)
watch(myTeams, (teams) => {

View file

@ -5,7 +5,7 @@ import {
getDefaultRESTRequest,
translateToNewRESTCollection,
HoppGQLRequest,
translateToNewGQLCollection
translateToNewGQLCollection,
} from "@hoppscotch/data"
import * as A from "fp-ts/Array"
import * as O from "fp-ts/Option"

View file

@ -6,6 +6,14 @@ import "tippy.js/animations/scale-subtle.css"
import "tippy.js/dist/border.css"
import "tippy.js/dist/svg-arrow.css"
export type TippyState = {
isEnabled: boolean
isVisible: boolean
isDestroyed: boolean
isMounted: boolean
isShown: boolean
}
export default <HoppModule>{
onVueAppInit(app) {
app.use(VueTippy)

View file

@ -8,7 +8,7 @@ import {
HoppRESTHeaders,
HoppRESTRequest,
makeCollection,
GQLHeader
GQLHeader,
} from "@hoppscotch/data"
import { cloneDeep } from "lodash-es"
import { pluck } from "rxjs/operators"

View file

@ -5,7 +5,7 @@ import {
relayRequestToNativeAdapter,
RelayRequest,
RelayResponse,
RelayCapabilities
RelayCapabilities,
} from "@hoppscotch/kernel"
import * as E from "fp-ts/Either"
import { pipe } from "fp-ts/function"

View file

@ -1059,6 +1059,15 @@ export class TeamCollectionsService extends Service<void> {
collections.forEach((coll) => this.entityIDs.add(`collection-${coll.id}`))
requests.forEach((req) => this.entityIDs.add(`request-${req.id}`))
this.collections.value = [...tree]
} catch (error) {
console.error(`Error expanding collection ${collectionID}:`, error)
// Set empty arrays instead of leaving as null to prevent future expansion attempts
// This prevents the infinite loop by ensuring the collection is marked as expanded
collection.children = []
collection.requests = []
this.collections.value = [...tree]
} finally {
this.loadingCollections.value = this.loadingCollections.value.filter(