diff --git a/src/components/actionbar/ComfyRunButton/CloudRunButtonWrapper.vue b/src/components/actionbar/ComfyRunButton/CloudRunButtonWrapper.vue index 7cca31fc5..788c6a3ab 100644 --- a/src/components/actionbar/ComfyRunButton/CloudRunButtonWrapper.vue +++ b/src/components/actionbar/ComfyRunButton/CloudRunButtonWrapper.vue @@ -1,7 +1,7 @@ diff --git a/src/components/dialog/content/setting/CreditsPanel.vue b/src/components/dialog/content/setting/CreditsPanel.vue index 4412568b1..4ac16a297 100644 --- a/src/components/dialog/content/setting/CreditsPanel.vue +++ b/src/components/dialog/content/setting/CreditsPanel.vue @@ -15,7 +15,7 @@ { +const isSubscriptionRequirementMet = computed(() => { if (!isCloud) return true - return subscription?.isSubscribedOrIsNotCloud.value ?? false + return subscription?.isSubscriptionRequirementMet.value ?? false }) const loading = computed(() => authStore.loading) const balanceLoading = computed(() => authStore.isFetchingBalance) diff --git a/src/components/topbar/CurrentUserPopover.test.ts b/src/components/topbar/CurrentUserPopover.test.ts index 10a03de01..0a590a0d6 100644 --- a/src/components/topbar/CurrentUserPopover.test.ts +++ b/src/components/topbar/CurrentUserPopover.test.ts @@ -82,7 +82,7 @@ vi.mock('@/stores/firebaseAuthStore', () => ({ const mockFetchStatus = vi.fn().mockResolvedValue(undefined) vi.mock('@/platform/cloud/subscription/composables/useSubscription', () => ({ useSubscription: vi.fn(() => ({ - isSubscribedOrIsNotCloud: { value: true }, + isSubscriptionRequirementMet: { value: true }, fetchStatus: mockFetchStatus })) })) diff --git a/src/components/topbar/CurrentUserPopover.vue b/src/components/topbar/CurrentUserPopover.vue index 9d3f6e779..548439a42 100644 --- a/src/components/topbar/CurrentUserPopover.vue +++ b/src/components/topbar/CurrentUserPopover.vue @@ -24,7 +24,7 @@ @@ -71,7 +71,7 @@ /> import Button from 'primevue/button' import Divider from 'primevue/divider' -import { onMounted } from 'vue' +import { computed, onMounted } from 'vue' import UserAvatar from '@/components/common/UserAvatar.vue' import UserCredit from '@/components/common/UserCredit.vue' @@ -125,7 +125,10 @@ const { userDisplayName, userEmail, userPhotoUrl, handleSignOut } = useCurrentUser() const authActions = useFirebaseAuthActions() const dialogService = useDialogService() -const { isSubscribedOrIsNotCloud, fetchStatus } = useSubscription() +const subscription = isCloud ? useSubscription() : null +const isSubscriptionRequirementMet = + subscription?.isSubscriptionRequirementMet ?? computed(() => true) +const fetchStatus = subscription?.fetchStatus ?? (async () => {}) const handleOpenUserSettings = () => { dialogService.showSettingsDialog('user') diff --git a/src/composables/auth/useFirebaseAuthActions.ts b/src/composables/auth/useFirebaseAuthActions.ts index 8b20558b1..390231247 100644 --- a/src/composables/auth/useFirebaseAuthActions.ts +++ b/src/composables/auth/useFirebaseAuthActions.ts @@ -6,7 +6,6 @@ import { useErrorHandling } from '@/composables/useErrorHandling' import type { ErrorRecoveryStrategy } from '@/composables/useErrorHandling' import { t } from '@/i18n' import { isCloud } from '@/platform/distribution/types' -import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription' import { useTelemetry } from '@/platform/telemetry' import { useToastStore } from '@/platform/updates/common/toastStore' import { useDialogService } from '@/services/dialogService' @@ -83,8 +82,11 @@ export const useFirebaseAuthActions = () => { const purchaseCredits = wrapWithErrorHandlingAsync(async (amount: number) => { if (isCloud) { - const { isSubscribedOrIsNotCloud } = useSubscription() - if (!isSubscribedOrIsNotCloud.value) return + const { useSubscription } = await import( + '@/platform/cloud/subscription/composables/useSubscription' + ) + const { isSubscriptionRequirementMet } = useSubscription() + if (!isSubscriptionRequirementMet.value) return } const response = await authStore.initiateCreditPurchase({ diff --git a/src/composables/useCoreCommands.ts b/src/composables/useCoreCommands.ts index dce23b465..34d6f2ecc 100644 --- a/src/composables/useCoreCommands.ts +++ b/src/composables/useCoreCommands.ts @@ -91,7 +91,7 @@ export function useCoreCommands(): ComfyCommand[] { const subscription = isCloud ? useSubscription() : null const subscriptionState = - subscription?.isSubscribedOrIsNotCloud ?? defaultSubscriptionState + subscription?.isSubscriptionRequirementMet ?? defaultSubscriptionState const subscriptionDialog = subscription?.showSubscriptionDialog ?? noop const moveSelectedNodes = ( diff --git a/src/platform/cloud/subscription/components/SubscribeButton.vue b/src/platform/cloud/subscription/components/SubscribeButton.vue index c08df2ba2..26f74cfb9 100644 --- a/src/platform/cloud/subscription/components/SubscribeButton.vue +++ b/src/platform/cloud/subscription/components/SubscribeButton.vue @@ -51,7 +51,8 @@ const emit = defineEmits<{ subscribed: [] }>() -const { subscribe, isSubscribedOrIsNotCloud, fetchStatus } = useSubscription() +const { subscribe, isSubscriptionRequirementMet, fetchStatus } = + useSubscription() const telemetry = useTelemetry() const isLoading = ref(false) @@ -76,7 +77,7 @@ const startPollingSubscriptionStatus = () => { await fetchStatus() - if (isSubscribedOrIsNotCloud.value) { + if (isSubscriptionRequirementMet.value) { stopPolling() telemetry?.trackMonthlySubscriptionSucceeded() emit('subscribed') diff --git a/src/platform/cloud/subscription/components/SubscriptionPanel.vue b/src/platform/cloud/subscription/components/SubscriptionPanel.vue index 7b78cf7d2..d84fda706 100644 --- a/src/platform/cloud/subscription/components/SubscriptionPanel.vue +++ b/src/platform/cloud/subscription/components/SubscriptionPanel.vue @@ -4,7 +4,7 @@ {{ - isSubscribedOrIsNotCloud + isSubscriptionRequirementMet ? $t('subscription.title') : $t('subscription.titleUnsubscribed') }} @@ -27,7 +27,7 @@ }} @@ -47,7 +47,7 @@ (null) const telemetry = useTelemetry() - const isSubscribedOrIsNotCloud = computed(() => { + const isSubscriptionRequirementMet = computed(() => { if (!isCloud || !window.__CONFIG__?.subscription_required) return true return subscriptionStatus.value?.is_active ?? false @@ -111,7 +111,7 @@ function useSubscriptionInternal() { const { startCancellationWatcher, stopCancellationWatcher } = useSubscriptionCancellationWatcher({ fetchStatus, - isSubscribedOrIsNotCloud, + isSubscriptionRequirementMet, subscriptionStatus, telemetry, shouldWatchCancellation @@ -125,7 +125,7 @@ function useSubscriptionInternal() { const requireActiveSubscription = async (): Promise => { await fetchSubscriptionStatus() - if (!isSubscribedOrIsNotCloud.value) { + if (!isSubscriptionRequirementMet.value) { showSubscriptionDialog() } } @@ -223,7 +223,7 @@ function useSubscriptionInternal() { return { // State - isSubscribedOrIsNotCloud, + isSubscriptionRequirementMet, isCancelled, formattedRenewalDate, formattedEndDate, diff --git a/src/platform/cloud/subscription/composables/useSubscriptionActions.ts b/src/platform/cloud/subscription/composables/useSubscriptionActions.ts index 594981ec6..95baf2059 100644 --- a/src/platform/cloud/subscription/composables/useSubscriptionActions.ts +++ b/src/platform/cloud/subscription/composables/useSubscriptionActions.ts @@ -36,8 +36,8 @@ export function useSubscriptionActions() { void handleRefresh() }) - const handleAddApiCredits = () => { - dialogService.showTopUpCreditsDialog() + const handleAddApiCredits = async () => { + await dialogService.showTopUpCreditsDialog() } const handleMessageSupport = async () => { diff --git a/src/platform/cloud/subscription/composables/useSubscriptionCancellationWatcher.ts b/src/platform/cloud/subscription/composables/useSubscriptionCancellationWatcher.ts index 5831f6c05..83b886d26 100644 --- a/src/platform/cloud/subscription/composables/useSubscriptionCancellationWatcher.ts +++ b/src/platform/cloud/subscription/composables/useSubscriptionCancellationWatcher.ts @@ -12,7 +12,7 @@ const CANCELLATION_BACKOFF_MULTIPLIER = 3 // 5s, 15s, 45s, 135s intervals type CancellationWatcherOptions = { fetchStatus: () => Promise - isSubscribedOrIsNotCloud: ComputedRef + isSubscriptionRequirementMet: ComputedRef subscriptionStatus: Ref telemetry: Pick | null shouldWatchCancellation: () => boolean @@ -20,7 +20,7 @@ type CancellationWatcherOptions = { export function useSubscriptionCancellationWatcher({ fetchStatus, - isSubscribedOrIsNotCloud, + isSubscriptionRequirementMet, subscriptionStatus, telemetry, shouldWatchCancellation @@ -73,7 +73,7 @@ export function useSubscriptionCancellationWatcher({ try { await fetchStatus() - if (!isSubscribedOrIsNotCloud.value) { + if (!isSubscriptionRequirementMet.value) { if (!cancellationTracked.value) { cancellationTracked.value = true try { diff --git a/src/scripts/app.ts b/src/scripts/app.ts index 9eea41a67..032ccf7c6 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -681,8 +681,8 @@ export class ComfyApp { isInsufficientCredits: true }) } else { - const { isSubscribedOrIsNotCloud } = useSubscription() - if (isSubscribedOrIsNotCloud.value) { + const { isSubscriptionRequirementMet } = useSubscription() + if (isSubscriptionRequirementMet.value) { useDialogService().showTopUpCreditsDialog({ isInsufficientCredits: true }) diff --git a/src/services/dialogService.ts b/src/services/dialogService.ts index eb60c4cee..80d0361bb 100644 --- a/src/services/dialogService.ts +++ b/src/services/dialogService.ts @@ -17,7 +17,6 @@ import SettingDialogHeader from '@/components/dialog/header/SettingDialogHeader. import { t } from '@/i18n' import { useTelemetry } from '@/platform/telemetry' import { isCloud } from '@/platform/distribution/types' -import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription' import SettingDialogContent from '@/platform/settings/components/SettingDialogContent.vue' import type { ExecutionErrorWsMessage } from '@/schemas/apiSchema' import { useDialogStore } from '@/stores/dialogStore' @@ -377,12 +376,15 @@ export const useDialogService = () => { }) } - function showTopUpCreditsDialog(options?: { + async function showTopUpCreditsDialog(options?: { isInsufficientCredits?: boolean }) { if (isCloud) { - const { isSubscribedOrIsNotCloud } = useSubscription() - if (!isSubscribedOrIsNotCloud.value) return + const { useSubscription } = await import( + '@/platform/cloud/subscription/composables/useSubscription' + ) + const { isSubscriptionRequirementMet } = useSubscription() + if (!isSubscriptionRequirementMet.value) return } return dialogStore.showDialog({ diff --git a/tests-ui/tests/composables/useCoreCommands.test.ts b/tests-ui/tests/composables/useCoreCommands.test.ts index 100f0ddea..e40280883 100644 --- a/tests-ui/tests/composables/useCoreCommands.test.ts +++ b/tests-ui/tests/composables/useCoreCommands.test.ts @@ -100,7 +100,7 @@ vi.mock('@/composables/auth/useFirebaseAuthActions', () => ({ vi.mock('@/platform/cloud/subscription/composables/useSubscription', () => ({ useSubscription: vi.fn(() => ({ - isSubscribedOrIsNotCloud: { value: true }, + isSubscriptionRequirementMet: { value: true }, showSubscriptionDialog: vi.fn() })) })) diff --git a/tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts b/tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts index d5d4e9571..0bb05a44d 100644 --- a/tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts +++ b/tests-ui/tests/platform/cloud/subscription/components/SubscriptionPanel.test.ts @@ -7,7 +7,7 @@ import SubscriptionPanel from '@/platform/cloud/subscription/components/Subscrip // Mock composables const mockSubscriptionData = { - isSubscribedOrIsNotCloud: false, + isSubscriptionRequirementMet: false, isCancelled: false, formattedRenewalDate: '2024-12-31', formattedEndDate: '2024-12-31', @@ -120,14 +120,14 @@ describe('SubscriptionPanel', () => { describe('subscription state functionality', () => { it('shows correct UI for active subscription', () => { - mockSubscriptionData.isSubscribedOrIsNotCloud = true + mockSubscriptionData.isSubscriptionRequirementMet = true const wrapper = createWrapper() expect(wrapper.text()).toContain('Manage Subscription') expect(wrapper.text()).toContain('Add Credits') }) it('shows correct UI for inactive subscription', () => { - mockSubscriptionData.isSubscribedOrIsNotCloud = false + mockSubscriptionData.isSubscriptionRequirementMet = false const wrapper = createWrapper() expect(wrapper.findComponent({ name: 'SubscribeButton' }).exists()).toBe( true @@ -137,14 +137,14 @@ describe('SubscriptionPanel', () => { }) it('shows renewal date for active non-cancelled subscription', () => { - mockSubscriptionData.isSubscribedOrIsNotCloud = true + mockSubscriptionData.isSubscriptionRequirementMet = true mockSubscriptionData.isCancelled = false const wrapper = createWrapper() expect(wrapper.text()).toContain('Renews 2024-12-31') }) it('shows expiry date for cancelled subscription', () => { - mockSubscriptionData.isSubscribedOrIsNotCloud = true + mockSubscriptionData.isSubscriptionRequirementMet = true mockSubscriptionData.isCancelled = true const wrapper = createWrapper() expect(wrapper.text()).toContain('Expires 2024-12-31') diff --git a/tests-ui/tests/platform/cloud/subscription/useSubscription.test.ts b/tests-ui/tests/platform/cloud/subscription/useSubscription.test.ts index 5986b2546..64348f4dc 100644 --- a/tests-ui/tests/platform/cloud/subscription/useSubscription.test.ts +++ b/tests-ui/tests/platform/cloud/subscription/useSubscription.test.ts @@ -92,7 +92,7 @@ describe('useSubscription', () => { }) describe('computed properties', () => { - it('should compute isSubscribedOrIsNotCloud correctly when subscription is active', async () => { + it('should compute isSubscriptionRequirementMet correctly when subscription is active', async () => { vi.mocked(global.fetch).mockResolvedValue({ ok: true, json: async () => ({ @@ -103,13 +103,13 @@ describe('useSubscription', () => { } as Response) mockIsLoggedIn.value = true - const { isSubscribedOrIsNotCloud, fetchStatus } = useSubscription() + const { isSubscriptionRequirementMet, fetchStatus } = useSubscription() await fetchStatus() - expect(isSubscribedOrIsNotCloud.value).toBe(true) + expect(isSubscriptionRequirementMet.value).toBe(true) }) - it('should compute isSubscribedOrIsNotCloud as false when subscription is inactive', async () => { + it('should compute isSubscriptionRequirementMet as false when subscription is inactive', async () => { vi.mocked(global.fetch).mockResolvedValue({ ok: true, json: async () => ({ @@ -120,10 +120,10 @@ describe('useSubscription', () => { } as Response) mockIsLoggedIn.value = true - const { isSubscribedOrIsNotCloud, fetchStatus } = useSubscription() + const { isSubscriptionRequirementMet, fetchStatus } = useSubscription() await fetchStatus() - expect(isSubscribedOrIsNotCloud.value).toBe(false) + expect(isSubscriptionRequirementMet.value).toBe(false) }) it('should format renewal date correctly', async () => { diff --git a/tests-ui/tests/platform/cloud/subscription/useSubscriptionCancellationWatcher.test.ts b/tests-ui/tests/platform/cloud/subscription/useSubscriptionCancellationWatcher.test.ts index 82b130fd7..028a66be1 100644 --- a/tests-ui/tests/platform/cloud/subscription/useSubscriptionCancellationWatcher.test.ts +++ b/tests-ui/tests/platform/cloud/subscription/useSubscriptionCancellationWatcher.test.ts @@ -24,7 +24,7 @@ describe('useSubscriptionCancellationWatcher', () => { baseStatus ) const isActive = ref(true) - const isSubscribedOrIsNotCloud = computed(() => isActive.value) + const isSubscriptionRequirementMet = computed(() => isActive.value) let shouldWatch = true const shouldWatchCancellation = () => shouldWatch @@ -76,7 +76,7 @@ describe('useSubscriptionCancellationWatcher', () => { const { startCancellationWatcher } = initWatcher({ fetchStatus, - isSubscribedOrIsNotCloud, + isSubscriptionRequirementMet, subscriptionStatus, telemetry: telemetryMock, shouldWatchCancellation @@ -106,7 +106,7 @@ describe('useSubscriptionCancellationWatcher', () => { const { startCancellationWatcher } = initWatcher({ fetchStatus, - isSubscribedOrIsNotCloud, + isSubscriptionRequirementMet, subscriptionStatus, telemetry: telemetryMock, shouldWatchCancellation @@ -128,7 +128,7 @@ describe('useSubscriptionCancellationWatcher', () => { const { startCancellationWatcher } = initWatcher({ fetchStatus, - isSubscribedOrIsNotCloud, + isSubscriptionRequirementMet, subscriptionStatus, telemetry: telemetryMock, shouldWatchCancellation @@ -153,7 +153,7 @@ describe('useSubscriptionCancellationWatcher', () => { const { startCancellationWatcher } = initWatcher({ fetchStatus, - isSubscribedOrIsNotCloud, + isSubscriptionRequirementMet, subscriptionStatus, telemetry: telemetryMock, shouldWatchCancellation