feat: allow global tracking outside of components
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<template>
|
||||
<GlobalLoading />
|
||||
<div class="flex h-screen items-stretch bg-gray-100">
|
||||
<ChannelList class="w-1/4 border-r border-gray-200" />
|
||||
<router-view class="flex-1 overflow-auto" />
|
||||
@@ -8,12 +9,14 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import ChannelList from './ChannelList.vue'
|
||||
import GlobalLoading from './GlobalLoading.vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
|
||||
components: {
|
||||
ChannelList,
|
||||
GlobalLoading,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<script lang="ts" setup>
|
||||
import { useGlobalQueryLoading } from '@vue/apollo-composable'
|
||||
|
||||
const loading = useGlobalQueryLoading()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="fixed bg-white p-4 rounded-br top-0 right-0"
|
||||
data-test-id="global-loading"
|
||||
>
|
||||
<div v-if="loading">
|
||||
Global loading...
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -128,4 +128,21 @@ describe('Vue 3 + Apollo Composable', () => {
|
||||
cy.get('input[type="checkbox"]').click()
|
||||
cy.get('[data-test-id="data"]').should('contain', 'Loaded channel: General')
|
||||
})
|
||||
|
||||
it('global loading', () => {
|
||||
cy.get('[data-test-id="global-loading"]').should('contain', 'Global loading...')
|
||||
cy.get('.channel-link').should('have.lengthOf', 2)
|
||||
cy.get('[data-test-id="global-loading"]').should('not.contain', 'Global loading...')
|
||||
cy.get('.channel-link').eq(0).click()
|
||||
cy.get('[data-test-id="global-loading"]').should('contain', 'Global loading...')
|
||||
cy.contains('#app', 'Currently viewing # General')
|
||||
cy.get('[data-test-id="global-loading"]').should('not.contain', 'Global loading...')
|
||||
cy.get('.channel-link').eq(1).click()
|
||||
cy.get('[data-test-id="global-loading"]').should('contain', 'Global loading...')
|
||||
cy.contains('#app', 'Currently viewing # Random')
|
||||
cy.get('[data-test-id="global-loading"]').should('not.contain', 'Global loading...')
|
||||
cy.get('.channel-link').eq(0).click()
|
||||
cy.get('[data-test-id="global-loading"]').should('not.contain', 'Global loading...')
|
||||
cy.contains('#app', 'Currently viewing # General')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
import { getCurrentTracking, getAppTracking } from './util/loadingTracking'
|
||||
import { getCurrentTracking, globalTracking } from './util/loadingTracking'
|
||||
import { computed } from 'vue-demi'
|
||||
|
||||
export function useQueryLoading () {
|
||||
const { tracking } = getCurrentTracking()
|
||||
if (!tracking) throw new Error('useQueryLoading must be called inside a setup function.')
|
||||
return computed(() => tracking.queries.value > 0)
|
||||
}
|
||||
|
||||
export function useMutationLoading () {
|
||||
const { tracking } = getCurrentTracking()
|
||||
if (!tracking) throw new Error('useMutationLoading must be called inside a setup function.')
|
||||
return computed(() => tracking.mutations.value > 0)
|
||||
}
|
||||
|
||||
export function useSubscriptionLoading () {
|
||||
const { tracking } = getCurrentTracking()
|
||||
if (!tracking) throw new Error('useSubscriptionLoading must be called inside a setup function.')
|
||||
return computed(() => tracking.subscriptions.value > 0)
|
||||
}
|
||||
|
||||
export function useGlobalQueryLoading () {
|
||||
const { appTracking } = getAppTracking()
|
||||
return computed(() => appTracking.queries.value > 0)
|
||||
return computed(() => globalTracking.queries.value > 0)
|
||||
}
|
||||
|
||||
export function useGlobalMutationLoading () {
|
||||
const { appTracking } = getAppTracking()
|
||||
return computed(() => appTracking.mutations.value > 0)
|
||||
return computed(() => globalTracking.mutations.value > 0)
|
||||
}
|
||||
|
||||
export function useGlobalSubscriptionLoading () {
|
||||
const { appTracking } = getAppTracking()
|
||||
return computed(() => appTracking.subscriptions.value > 0)
|
||||
return computed(() => globalTracking.subscriptions.value > 0)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Ref, watch, onUnmounted, ref, getCurrentInstance, onBeforeUnmount } from 'vue-demi'
|
||||
import type { CurrentInstance } from './types'
|
||||
import { isServer } from './env.js'
|
||||
|
||||
export interface LoadingTracking {
|
||||
queries: Ref<number>
|
||||
@@ -11,71 +11,51 @@ export interface AppLoadingTracking extends LoadingTracking {
|
||||
components: Map<any, LoadingTracking>
|
||||
}
|
||||
|
||||
export function getAppTracking () {
|
||||
const vm = getCurrentInstance() as CurrentInstance | null
|
||||
const root = vm?.$root ?? vm?.root ?? vm?.proxy?.$root as CurrentInstance | null | undefined
|
||||
if (!root) {
|
||||
throw new Error('Instance $root not found')
|
||||
}
|
||||
|
||||
let appTracking: AppLoadingTracking
|
||||
|
||||
if (!root._apolloAppTracking) {
|
||||
// Add per Vue tracking
|
||||
appTracking = root._apolloAppTracking = {
|
||||
queries: ref(0),
|
||||
mutations: ref(0),
|
||||
subscriptions: ref(0),
|
||||
components: new Map(),
|
||||
}
|
||||
} else {
|
||||
appTracking = root._apolloAppTracking
|
||||
}
|
||||
|
||||
return {
|
||||
appTracking,
|
||||
}
|
||||
export const globalTracking: AppLoadingTracking = {
|
||||
queries: ref(0),
|
||||
mutations: ref(0),
|
||||
subscriptions: ref(0),
|
||||
components: new Map(),
|
||||
}
|
||||
|
||||
export function getCurrentTracking () {
|
||||
const vm = getCurrentInstance()
|
||||
if (!vm) {
|
||||
throw new Error('getCurrentTracking must be used during a component setup')
|
||||
return {}
|
||||
}
|
||||
|
||||
const { appTracking } = getAppTracking()
|
||||
|
||||
let tracking: LoadingTracking
|
||||
|
||||
if (!appTracking.components.has(vm)) {
|
||||
if (!globalTracking.components.has(vm)) {
|
||||
// Add per-component tracking
|
||||
appTracking.components.set(vm, tracking = {
|
||||
globalTracking.components.set(vm, tracking = {
|
||||
queries: ref(0),
|
||||
mutations: ref(0),
|
||||
subscriptions: ref(0),
|
||||
})
|
||||
// Cleanup
|
||||
onUnmounted(() => {
|
||||
appTracking.components.delete(vm)
|
||||
globalTracking.components.delete(vm)
|
||||
})
|
||||
} else {
|
||||
tracking = appTracking.components.get(vm) as LoadingTracking
|
||||
tracking = globalTracking.components.get(vm) as LoadingTracking
|
||||
}
|
||||
|
||||
return {
|
||||
appTracking,
|
||||
tracking,
|
||||
}
|
||||
}
|
||||
|
||||
function track (loading: Ref<boolean>, type: keyof LoadingTracking) {
|
||||
const { appTracking, tracking } = getCurrentTracking()
|
||||
if (isServer) return
|
||||
|
||||
const { tracking } = getCurrentTracking()
|
||||
|
||||
watch(loading, (value, oldValue) => {
|
||||
if (oldValue != null && value !== oldValue) {
|
||||
const mod = value ? 1 : -1
|
||||
tracking[type].value += mod
|
||||
appTracking[type].value += mod
|
||||
if (tracking) tracking[type].value += mod
|
||||
globalTracking[type].value += mod
|
||||
}
|
||||
}, {
|
||||
immediate: true,
|
||||
@@ -83,8 +63,8 @@ function track (loading: Ref<boolean>, type: keyof LoadingTracking) {
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
if (loading.value) {
|
||||
tracking[type].value--
|
||||
appTracking[type].value--
|
||||
if (tracking) tracking[type].value--
|
||||
globalTracking[type].value--
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user