/** * Subscription Store * Global state management for user subscriptions with caching and deduplication */ import { defineStore } from 'pinia' import { ref, computed } from 'vue' import subscriptionsAPI from '@/api/subscriptions' import type { UserSubscription } from '@/types' // Cache TTL: 60 seconds const CACHE_TTL_MS = 60_000 // Request generation counter to invalidate stale in-flight responses let requestGeneration = 0 export const useSubscriptionStore = defineStore('subscriptions', () => { // State const activeSubscriptions = ref([]) const loading = ref(false) const loaded = ref(false) const lastFetchedAt = ref(null) // In-flight request deduplication let activePromise: Promise | null = null // Auto-refresh interval let pollerInterval: ReturnType | null = null // Computed const hasActiveSubscriptions = computed(() => activeSubscriptions.value.length > 0) /** * Fetch active subscriptions with caching and deduplication * @param force - Force refresh even if cache is valid */ async function fetchActiveSubscriptions(force = false): Promise { const now = Date.now() // Return cached data if valid if ( !force && loaded.value && lastFetchedAt.value && now - lastFetchedAt.value < CACHE_TTL_MS ) { return activeSubscriptions.value } // Return in-flight request if exists (deduplication) if (activePromise && !force) { return activePromise } const currentGeneration = ++requestGeneration // Start new request loading.value = true const requestPromise = subscriptionsAPI .getActiveSubscriptions() .then((data) => { if (currentGeneration === requestGeneration) { activeSubscriptions.value = data loaded.value = true lastFetchedAt.value = Date.now() } return data }) .catch((error) => { console.error('Failed to fetch active subscriptions:', error) throw error }) .finally(() => { if (activePromise === requestPromise) { loading.value = false activePromise = null } }) activePromise = requestPromise return activePromise } /** * Start auto-refresh polling */ function startPolling() { if (pollerInterval) return pollerInterval = setInterval(() => { fetchActiveSubscriptions(true).catch((error) => { console.error('Subscription polling failed:', error) }) }, 5 * 60 * 1000) } /** * Stop auto-refresh polling */ function stopPolling() { if (pollerInterval) { clearInterval(pollerInterval) pollerInterval = null } } /** * Clear all subscription data and stop polling */ function clear() { requestGeneration++ activePromise = null activeSubscriptions.value = [] loaded.value = false lastFetchedAt.value = null stopPolling() } /** * Invalidate cache (force next fetch to reload) */ function invalidateCache() { lastFetchedAt.value = null } return { // State activeSubscriptions, loading, hasActiveSubscriptions, // Actions fetchActiveSubscriptions, startPolling, stopPolling, clear, invalidateCache } })