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" 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> </HoppSmartSelectWrapper>
<template #content="{ hide }"> <template #content="{ hide, state }">
<div <div
ref="accountActions" ref="accountActions"
class="flex flex-col focus:outline-none" class="flex flex-col focus:outline-none"
@ -211,7 +211,7 @@
@keyup.escape="hide()" @keyup.escape="hide()"
@click="hide()" @click="hide()"
> >
<WorkspaceSelector /> <WorkspaceSelector :state="state" />
</div> </div>
</template> </template>
</tippy> </tippy>

View file

@ -1,5 +1,5 @@
<template> <template>
<div ref="rootEl"> <div class="flex flex-col">
<div class="flex flex-col"> <div class="flex flex-col">
<div class="flex flex-col"> <div class="flex flex-col">
<HoppSmartItem <HoppSmartItem
@ -81,6 +81,7 @@
/> />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, watch } from "vue" import { computed, ref, watch } from "vue"
import { useReadonlyStream } from "~/composables/stream" import { useReadonlyStream } from "~/composables/stream"
@ -96,11 +97,16 @@ import { useLocalState } from "~/newstore/localstate"
import { defineActionHandler, invokeAction } from "~/helpers/actions" import { defineActionHandler, invokeAction } from "~/helpers/actions"
import { WorkspaceService } from "~/services/workspace.service" import { WorkspaceService } from "~/services/workspace.service"
import { useService } from "dioc/vue" 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 t = useI18n()
const colorMode = useColorMode() const colorMode = useColorMode()
const props = defineProps<{
state: TippyState | null
}>()
const showModalAdd = ref(false) const showModalAdd = ref(false)
const currentUser = useReadonlyStream( const currentUser = useReadonlyStream(
@ -116,27 +122,35 @@ const teamListAdapterError = useReadonlyStream(teamListadapter.error$, null)
const REMEMBERED_TEAM_ID = useLocalState("REMEMBERED_TEAM_ID") const REMEMBERED_TEAM_ID = useLocalState("REMEMBERED_TEAM_ID")
const teamListFetched = ref(false) const teamListFetched = ref(false)
const rootEl = ref<HTMLElement>() const {
const elVisible = useElementVisibility(rootEl) pause: pauseListPoll,
resume: resumeListPoll,
const { pause: pauseListPoll, resume: resumeListPoll } = useIntervalFn(() => { isActive: isListPolling,
if (teamListadapter.isInitialized) { } = useIntervalFn(
teamListadapter.fetchList()
}
}, 10000)
watch(
elVisible,
() => { () => {
if (elVisible.value) { if (teamListadapter.isInitialized) {
teamListadapter.fetchList() 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 { } else {
pauseListPoll() pauseListPoll()
} }
}, },
{ immediate: true } { debounce: 200 }
) )
watch(myTeams, (teams) => { watch(myTeams, (teams) => {

View file

@ -5,7 +5,7 @@ import {
getDefaultRESTRequest, getDefaultRESTRequest,
translateToNewRESTCollection, translateToNewRESTCollection,
HoppGQLRequest, HoppGQLRequest,
translateToNewGQLCollection translateToNewGQLCollection,
} from "@hoppscotch/data" } from "@hoppscotch/data"
import * as A from "fp-ts/Array" import * as A from "fp-ts/Array"
import * as O from "fp-ts/Option" 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/border.css"
import "tippy.js/dist/svg-arrow.css" import "tippy.js/dist/svg-arrow.css"
export type TippyState = {
isEnabled: boolean
isVisible: boolean
isDestroyed: boolean
isMounted: boolean
isShown: boolean
}
export default <HoppModule>{ export default <HoppModule>{
onVueAppInit(app) { onVueAppInit(app) {
app.use(VueTippy) app.use(VueTippy)

View file

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

View file

@ -1,11 +1,11 @@
import { Service } from "dioc" import { Service } from "dioc"
import { markRaw } from "vue" import { markRaw } from "vue"
import { import {
body, body,
relayRequestToNativeAdapter, relayRequestToNativeAdapter,
RelayRequest, RelayRequest,
RelayResponse, RelayResponse,
RelayCapabilities RelayCapabilities,
} from "@hoppscotch/kernel" } from "@hoppscotch/kernel"
import * as E from "fp-ts/Either" import * as E from "fp-ts/Either"
import { pipe } from "fp-ts/function" 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}`)) collections.forEach((coll) => this.entityIDs.add(`collection-${coll.id}`))
requests.forEach((req) => this.entityIDs.add(`request-${req.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] this.collections.value = [...tree]
} finally { } finally {
this.loadingCollections.value = this.loadingCollections.value.filter( this.loadingCollections.value = this.loadingCollections.value.filter(