From ccb73186fb12b8abc81f8b92389552a11415b305 Mon Sep 17 00:00:00 2001 From: Simula_r <18093452+simula-r@users.noreply.github.com> Date: Fri, 19 Dec 2025 17:52:37 -0800 Subject: [PATCH] refactor: start on removing FF for subscription tiers (#7596) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Refactor: remove FF for subscription tier, remove legacy code for non subscription tier logic. ## Review Focus Preexisting cloud functionality impact. ## Screenshots (if applicable) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7596-refactor-start-on-removing-FF-for-subscription-tiers-2cc6d73d3650816bac3aef893e4f37cd) by [Unito](https://www.unito.io) --- src/components/common/UserCredit.vue | 10 +- .../content/TopUpCreditsDialogContent.vue | 70 +---------- .../credit/LegacyCreditTopUpOption.vue | 119 ------------------ .../topbar/CurrentUserPopover.test.ts | 9 -- src/components/topbar/CurrentUserPopover.vue | 3 - src/composables/node/usePriceBadge.ts | 57 +++------ src/composables/useFeatureFlags.ts | 11 -- .../components/SubscribeButton.vue | 11 +- .../composables/useSubscription.ts | 6 +- src/platform/remoteConfig/types.ts | 1 - .../settings/composables/useSettingUI.ts | 3 - .../vueNodes/components/NodeHeader.vue | 15 +-- .../composables/node/useCreditsBadge.test.ts | 19 +-- 13 files changed, 26 insertions(+), 308 deletions(-) delete mode 100644 src/components/dialog/content/credit/LegacyCreditTopUpOption.vue diff --git a/src/components/common/UserCredit.vue b/src/components/common/UserCredit.vue index 396df4819..ee2fb722d 100644 --- a/src/components/common/UserCredit.vue +++ b/src/components/common/UserCredit.vue @@ -14,13 +14,7 @@ class="p-1 text-amber-400" >
@@ -36,7 +30,6 @@ import { computed } from 'vue' import { useI18n } from 'vue-i18n' import { formatCreditsFromCents } from '@/base/credits/comfyCredits' -import { useFeatureFlags } from '@/composables/useFeatureFlags' import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore' const { textClass, showCreditsOnly } = defineProps<{ @@ -45,7 +38,6 @@ const { textClass, showCreditsOnly } = defineProps<{ }>() const authStore = useFirebaseAuthStore() -const { flags } = useFeatureFlags() const balanceLoading = computed(() => authStore.isFetchingBalance) const { t, locale } = useI18n() diff --git a/src/components/dialog/content/TopUpCreditsDialogContent.vue b/src/components/dialog/content/TopUpCreditsDialogContent.vue index abbd9c4de..4b79d6e7d 100644 --- a/src/components/dialog/content/TopUpCreditsDialogContent.vue +++ b/src/components/dialog/content/TopUpCreditsDialogContent.vue @@ -1,6 +1,5 @@ diff --git a/src/components/dialog/content/credit/LegacyCreditTopUpOption.vue b/src/components/dialog/content/credit/LegacyCreditTopUpOption.vue deleted file mode 100644 index f33c3845b..000000000 --- a/src/components/dialog/content/credit/LegacyCreditTopUpOption.vue +++ /dev/null @@ -1,119 +0,0 @@ - - - diff --git a/src/components/topbar/CurrentUserPopover.test.ts b/src/components/topbar/CurrentUserPopover.test.ts index cb2efd7b7..72ef7a1ee 100644 --- a/src/components/topbar/CurrentUserPopover.test.ts +++ b/src/components/topbar/CurrentUserPopover.test.ts @@ -138,15 +138,6 @@ vi.mock('@/composables/useExternalLink', () => ({ })) })) -// Mock useFeatureFlags -vi.mock('@/composables/useFeatureFlags', () => ({ - useFeatureFlags: vi.fn(() => ({ - flags: { - subscriptionTiersEnabled: true - } - })) -})) - // Mock useTelemetry vi.mock('@/platform/telemetry', () => ({ useTelemetry: vi.fn(() => ({ diff --git a/src/components/topbar/CurrentUserPopover.vue b/src/components/topbar/CurrentUserPopover.vue index f5a6f0610..5c500e570 100644 --- a/src/components/topbar/CurrentUserPopover.vue +++ b/src/components/topbar/CurrentUserPopover.vue @@ -42,7 +42,6 @@ formattedBalance }} @@ -147,7 +146,6 @@ import UserAvatar from '@/components/common/UserAvatar.vue' import { useCurrentUser } from '@/composables/auth/useCurrentUser' import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions' import { useExternalLink } from '@/composables/useExternalLink' -import { useFeatureFlags } from '@/composables/useFeatureFlags' import SubscribeButton from '@/platform/cloud/subscription/components/SubscribeButton.vue' import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription' import { useSubscriptionDialog } from '@/platform/cloud/subscription/composables/useSubscriptionDialog' @@ -174,7 +172,6 @@ const { fetchStatus } = useSubscription() const subscriptionDialog = useSubscriptionDialog() -const { flags } = useFeatureFlags() const { locale } = useI18n() const formattedBalance = computed(() => { diff --git a/src/composables/node/usePriceBadge.ts b/src/composables/node/usePriceBadge.ts index 26cecdfac..93f396a5a 100644 --- a/src/composables/node/usePriceBadge.ts +++ b/src/composables/node/usePriceBadge.ts @@ -1,7 +1,6 @@ import type { LGraph, LGraphNode } from '@/lib/litegraph/src/litegraph' import { LGraphBadge } from '@/lib/litegraph/src/litegraph' -import { useFeatureFlags } from '@/composables/useFeatureFlags' import { useColorPaletteStore } from '@/stores/workspace/colorPaletteStore' import { adjustColor } from '@/utils/colorUtil' @@ -10,7 +9,6 @@ componentIconSvg.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='none' stroke='oklch(83.01%25 0.163 83.16)' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M15.536 11.293a1 1 0 0 0 0 1.414l2.376 2.377a1 1 0 0 0 1.414 0l2.377-2.377a1 1 0 0 0 0-1.414l-2.377-2.377a1 1 0 0 0-1.414 0zm-13.239 0a1 1 0 0 0 0 1.414l2.377 2.377a1 1 0 0 0 1.414 0l2.377-2.377a1 1 0 0 0 0-1.414L6.088 8.916a1 1 0 0 0-1.414 0zm6.619 6.619a1 1 0 0 0 0 1.415l2.377 2.376a1 1 0 0 0 1.414 0l2.377-2.376a1 1 0 0 0 0-1.415l-2.377-2.376a1 1 0 0 0-1.414 0zm0-13.238a1 1 0 0 0 0 1.414l2.377 2.376a1 1 0 0 0 1.414 0l2.377-2.376a1 1 0 0 0 0-1.414l-2.377-2.377a1 1 0 0 0-1.414 0z'/%3E%3C/svg%3E" export const usePriceBadge = () => { - const { flags } = useFeatureFlags() function updateSubgraphCredits(node: LGraphNode) { if (!node.isSubgraphNode()) return node.badges = node.badges.filter((b) => !isCreditsBadge(b)) @@ -40,53 +38,26 @@ export const usePriceBadge = () => { function isCreditsBadge(badge: LGraphBadge | (() => LGraphBadge)): boolean { const badgeInstance = typeof badge === 'function' ? badge() : badge - if (flags.subscriptionTiersEnabled) { - return badgeInstance.icon?.image === componentIconSvg - } else { - return badgeInstance.icon?.unicode === '\ue96b' - } + return badgeInstance.icon?.image === componentIconSvg } const colorPaletteStore = useColorPaletteStore() function getCreditsBadge(price: string): LGraphBadge { const isLightTheme = colorPaletteStore.completedActivePalette.light_theme - if (flags.subscriptionTiersEnabled) { - return new LGraphBadge({ - text: price, - iconOptions: { - image: componentIconSvg, - size: 8 - }, - fgColor: - colorPaletteStore.completedActivePalette.colors.litegraph_base - .BADGE_FG_COLOR, - bgColor: isLightTheme - ? adjustColor('#8D6932', { lightness: 0.5 }) - : '#8D6932' - }) - } else { - return new LGraphBadge({ - text: price, - iconOptions: { - unicode: '\ue96b', - fontFamily: 'PrimeIcons', - color: isLightTheme - ? adjustColor('#FABC25', { lightness: 0.5 }) - : '#FABC25', - bgColor: isLightTheme - ? adjustColor('#654020', { lightness: 0.5 }) - : '#654020', - fontSize: 8 - }, - fgColor: - colorPaletteStore.completedActivePalette.colors.litegraph_base - .BADGE_FG_COLOR, - bgColor: isLightTheme - ? adjustColor('#8D6932', { lightness: 0.5 }) - : '#8D6932' - }) - } + return new LGraphBadge({ + text: price, + iconOptions: { + image: componentIconSvg, + size: 8 + }, + fgColor: + colorPaletteStore.completedActivePalette.colors.litegraph_base + .BADGE_FG_COLOR, + bgColor: isLightTheme + ? adjustColor('#8D6932', { lightness: 0.5 }) + : '#8D6932' + }) } return { getCreditsBadge, diff --git a/src/composables/useFeatureFlags.ts b/src/composables/useFeatureFlags.ts index 07b82ff40..4f2e65abd 100644 --- a/src/composables/useFeatureFlags.ts +++ b/src/composables/useFeatureFlags.ts @@ -13,7 +13,6 @@ export enum ServerFeatureFlag { MODEL_UPLOAD_BUTTON_ENABLED = 'model_upload_button_enabled', ASSET_UPDATE_OPTIONS_ENABLED = 'asset_update_options_enabled', PRIVATE_MODELS_ENABLED = 'private_models_enabled', - SUBSCRIPTION_TIERS_ENABLED = 'subscription_tiers_enabled', ONBOARDING_SURVEY_ENABLED = 'onboarding_survey_enabled' } @@ -58,16 +57,6 @@ export function useFeatureFlags() { api.getServerFeature(ServerFeatureFlag.PRIVATE_MODELS_ENABLED, false) ) }, - get subscriptionTiersEnabled() { - // Check remote config first (from /api/features), fall back to websocket feature flags - return ( - remoteConfig.value.subscription_tiers_enabled ?? - api.getServerFeature( - ServerFeatureFlag.SUBSCRIPTION_TIERS_ENABLED, - true // Default to true (new design) - ) - ) - }, get onboardingSurveyEnabled() { return ( remoteConfig.value.onboarding_survey_enabled ?? diff --git a/src/platform/cloud/subscription/components/SubscribeButton.vue b/src/platform/cloud/subscription/components/SubscribeButton.vue index 670fb48b0..de3572a00 100644 --- a/src/platform/cloud/subscription/components/SubscribeButton.vue +++ b/src/platform/cloud/subscription/components/SubscribeButton.vue @@ -26,7 +26,6 @@ import Button from 'primevue/button' import { computed, onBeforeUnmount, ref, watch } from 'vue' -import { useFeatureFlags } from '@/composables/useFeatureFlags' import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription' import { isCloud } from '@/platform/distribution/types' import { useTelemetry } from '@/platform/telemetry' @@ -54,10 +53,7 @@ const emit = defineEmits<{ const { subscribe, isActiveSubscription, fetchStatus, showSubscriptionDialog } = useSubscription() -const { flags } = useFeatureFlags() -const shouldUseStripePricing = computed( - () => isCloud && Boolean(flags.subscriptionTiersEnabled) -) + const telemetry = useTelemetry() const isLoading = ref(false) @@ -112,7 +108,7 @@ const stopPolling = () => { watch( [isAwaitingStripeSubscription, isActiveSubscription], ([awaiting, isActive]) => { - if (shouldUseStripePricing.value && awaiting && isActive) { + if (isCloud && awaiting && isActive) { emit('subscribed') isAwaitingStripeSubscription.value = false } @@ -122,9 +118,6 @@ watch( const handleSubscribe = async () => { if (isCloud) { useTelemetry()?.trackSubscription('subscribe_clicked') - } - - if (shouldUseStripePricing.value) { isAwaitingStripeSubscription.value = true showSubscriptionDialog() return diff --git a/src/platform/cloud/subscription/composables/useSubscription.ts b/src/platform/cloud/subscription/composables/useSubscription.ts index 2647d35ff..77e889f13 100644 --- a/src/platform/cloud/subscription/composables/useSubscription.ts +++ b/src/platform/cloud/subscription/composables/useSubscription.ts @@ -16,9 +16,9 @@ import { useDialogService } from '@/services/dialogService' import type { components, operations } from '@/types/comfyRegistryTypes' import { useSubscriptionCancellationWatcher } from './useSubscriptionCancellationWatcher' -type CloudSubscriptionCheckoutResponse = { - checkout_url: string -} +type CloudSubscriptionCheckoutResponse = NonNullable< + operations['createCloudSubscriptionCheckout']['responses']['201']['content']['application/json'] +> export type CloudSubscriptionStatusResponse = NonNullable< operations['GetCloudSubscriptionStatus']['responses']['200']['content']['application/json'] diff --git a/src/platform/remoteConfig/types.ts b/src/platform/remoteConfig/types.ts index e2660853b..1a4ef1261 100644 --- a/src/platform/remoteConfig/types.ts +++ b/src/platform/remoteConfig/types.ts @@ -37,6 +37,5 @@ export type RemoteConfig = { model_upload_button_enabled?: boolean asset_update_options_enabled?: boolean private_models_enabled?: boolean - subscription_tiers_enabled?: boolean onboarding_survey_enabled?: boolean } diff --git a/src/platform/settings/composables/useSettingUI.ts b/src/platform/settings/composables/useSettingUI.ts index 70ade08b2..79a54fba4 100644 --- a/src/platform/settings/composables/useSettingUI.ts +++ b/src/platform/settings/composables/useSettingUI.ts @@ -3,7 +3,6 @@ import type { Component } from 'vue' import { useI18n } from 'vue-i18n' import { useCurrentUser } from '@/composables/auth/useCurrentUser' -import { useFeatureFlags } from '@/composables/useFeatureFlags' import { isCloud } from '@/platform/distribution/types' import type { SettingTreeNode } from '@/platform/settings/settingStore' import { useSettingStore } from '@/platform/settings/settingStore' @@ -36,7 +35,6 @@ export function useSettingUI( const { shouldRenderVueNodes } = useVueFeatureFlags() const { isActiveSubscription } = useSubscription() - const { flags } = useFeatureFlags() const settingRoot = computed(() => { const root = buildTree( @@ -106,7 +104,6 @@ export function useSettingUI( const shouldShowPlanCreditsPanel = computed(() => { if (!subscriptionPanel) return false - if (!flags.subscriptionTiersEnabled) return true return isActiveSubscription.value }) diff --git a/src/renderer/extensions/vueNodes/components/NodeHeader.vue b/src/renderer/extensions/vueNodes/components/NodeHeader.vue index e767d2702..fe99c5d62 100644 --- a/src/renderer/extensions/vueNodes/components/NodeHeader.vue +++ b/src/renderer/extensions/vueNodes/components/NodeHeader.vue @@ -40,17 +40,7 @@
-
+
() -const { flags } = useFeatureFlags() - // Error boundary implementation const renderError = ref(null) const { toastErrorHandler } = useErrorHandling() diff --git a/tests-ui/tests/composables/node/useCreditsBadge.test.ts b/tests-ui/tests/composables/node/useCreditsBadge.test.ts index 1725eb043..eb894566a 100644 --- a/tests-ui/tests/composables/node/useCreditsBadge.test.ts +++ b/tests-ui/tests/composables/node/useCreditsBadge.test.ts @@ -1,8 +1,6 @@ import { describe, expect, vi } from 'vitest' import { LGraphNode } from '@/lib/litegraph/src/litegraph' -import type { LGraphBadge } from '@/lib/litegraph/src/LGraphBadge' -import type { LGraphIcon } from '@/lib/litegraph/src/LGraphIcon' import { subgraphTest } from '../../litegraph/subgraph/fixtures/subgraphFixtures' @@ -17,23 +15,10 @@ vi.mock('@/stores/workspace/colorPaletteStore', () => ({ }) })) -vi.mock('@/composables/useFeatureFlags', () => ({ - useFeatureFlags: () => ({ - flags: { - subscriptionTiersEnabled: false // Test legacy badge behavior - } - }) -})) - -const { updateSubgraphCredits } = usePriceBadge() +const { updateSubgraphCredits, getCreditsBadge } = usePriceBadge() const mockNode = new LGraphNode('mock node') -const mockIcon: Partial = { unicode: '\ue96b' } -const badge: Partial = { - icon: mockIcon as LGraphIcon, - text: '$0.05/Run' -} -mockNode.badges = [badge as LGraphBadge] +mockNode.badges = [getCreditsBadge('$0.05/Run')] function getBadgeText(node: LGraphNode): string { const badge = node.badges[0]