mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-01-26 19:09:52 +00:00
Integrates Stripe's pricing table web component into the subscription dialog when the subscription_tiers_enabled feature flag is active. The implementation includes a new StripePricingTable component that loads Stripe's pricing table script and renders the table with proper error handling and loading states. The subscription dialog now displays the Stripe pricing table with contact us and enterprise links, using a 1100px width that balances multi-column layout with visual design. Configuration supports environment variables, remote config, and window config for the Stripe publishable key and pricing table ID. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7288-feat-add-Stripe-pricing-table-integration-for-subscription-dialog-conditional-on-featur-2c46d73d365081fa9d93c213df118996) by [Unito](https://www.unito.io)
243 lines
6.6 KiB
TypeScript
243 lines
6.6 KiB
TypeScript
import { computed, defineAsyncComponent, onMounted, ref } from 'vue'
|
|
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'
|
|
import type { SettingParams } from '@/platform/settings/types'
|
|
import { isElectron } from '@/utils/envUtil'
|
|
import { normalizeI18nKey } from '@/utils/formatUtil'
|
|
import { buildTree } from '@/utils/treeUtil'
|
|
import { useVueFeatureFlags } from '@/composables/useVueFeatureFlags'
|
|
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
|
|
|
interface SettingPanelItem {
|
|
node: SettingTreeNode
|
|
component: Component
|
|
}
|
|
|
|
export function useSettingUI(
|
|
defaultPanel?:
|
|
| 'about'
|
|
| 'keybinding'
|
|
| 'extension'
|
|
| 'server-config'
|
|
| 'user'
|
|
| 'credits'
|
|
| 'subscription'
|
|
) {
|
|
const { t } = useI18n()
|
|
const { isLoggedIn } = useCurrentUser()
|
|
const settingStore = useSettingStore()
|
|
const activeCategory = ref<SettingTreeNode | null>(null)
|
|
|
|
const { shouldRenderVueNodes } = useVueFeatureFlags()
|
|
const { isActiveSubscription } = useSubscription()
|
|
const { flags } = useFeatureFlags()
|
|
|
|
const settingRoot = computed<SettingTreeNode>(() => {
|
|
const root = buildTree(
|
|
Object.values(settingStore.settingsById).filter(
|
|
(setting: SettingParams) =>
|
|
setting.type !== 'hidden' &&
|
|
!(shouldRenderVueNodes.value && setting.hideInVueNodes)
|
|
),
|
|
(setting: SettingParams) => setting.category || setting.id.split('.')
|
|
)
|
|
|
|
const floatingSettings = (root.children ?? []).filter((node) => node.leaf)
|
|
if (floatingSettings.length) {
|
|
root.children = (root.children ?? []).filter((node) => !node.leaf)
|
|
root.children.push({
|
|
key: 'Other',
|
|
label: 'Other',
|
|
leaf: false,
|
|
children: floatingSettings
|
|
})
|
|
}
|
|
|
|
return root
|
|
})
|
|
|
|
const settingCategories = computed<SettingTreeNode[]>(
|
|
() => settingRoot.value.children ?? []
|
|
)
|
|
|
|
// Define panel items
|
|
const aboutPanel: SettingPanelItem = {
|
|
node: {
|
|
key: 'about',
|
|
label: 'About',
|
|
children: []
|
|
},
|
|
component: defineAsyncComponent(
|
|
() => import('@/components/dialog/content/setting/AboutPanel.vue')
|
|
)
|
|
}
|
|
|
|
const creditsPanel: SettingPanelItem = {
|
|
node: {
|
|
key: 'credits',
|
|
label: 'Credits',
|
|
children: []
|
|
},
|
|
component: defineAsyncComponent(
|
|
() => import('@/components/dialog/content/setting/CreditsPanel.vue')
|
|
)
|
|
}
|
|
|
|
const subscriptionPanel: SettingPanelItem | null =
|
|
!isCloud || !window.__CONFIG__?.subscription_required
|
|
? null
|
|
: {
|
|
node: {
|
|
key: 'subscription',
|
|
label: 'PlanCredits',
|
|
children: []
|
|
},
|
|
component: defineAsyncComponent(
|
|
() =>
|
|
import(
|
|
'@/platform/cloud/subscription/components/SubscriptionPanel.vue'
|
|
)
|
|
)
|
|
}
|
|
|
|
const shouldShowPlanCreditsPanel = computed(() => {
|
|
if (!subscriptionPanel) return false
|
|
if (!flags.subscriptionTiersEnabled) return true
|
|
return isActiveSubscription.value
|
|
})
|
|
|
|
const userPanel: SettingPanelItem = {
|
|
node: {
|
|
key: 'user',
|
|
label: 'User',
|
|
children: []
|
|
},
|
|
component: defineAsyncComponent(
|
|
() => import('@/components/dialog/content/setting/UserPanel.vue')
|
|
)
|
|
}
|
|
|
|
const keybindingPanel: SettingPanelItem = {
|
|
node: {
|
|
key: 'keybinding',
|
|
label: 'Keybinding',
|
|
children: []
|
|
},
|
|
component: defineAsyncComponent(
|
|
() => import('@/components/dialog/content/setting/KeybindingPanel.vue')
|
|
)
|
|
}
|
|
|
|
const extensionPanel: SettingPanelItem = {
|
|
node: {
|
|
key: 'extension',
|
|
label: 'Extension',
|
|
children: []
|
|
},
|
|
component: defineAsyncComponent(
|
|
() => import('@/platform/settings/components/ExtensionPanel.vue')
|
|
)
|
|
}
|
|
|
|
const serverConfigPanel: SettingPanelItem = {
|
|
node: {
|
|
key: 'server-config',
|
|
label: 'Server-Config',
|
|
children: []
|
|
},
|
|
component: defineAsyncComponent(
|
|
() => import('@/platform/settings/components/ServerConfigPanel.vue')
|
|
)
|
|
}
|
|
|
|
const panels = computed<SettingPanelItem[]>(() =>
|
|
[
|
|
aboutPanel,
|
|
creditsPanel,
|
|
userPanel,
|
|
keybindingPanel,
|
|
extensionPanel,
|
|
...(isElectron() ? [serverConfigPanel] : []),
|
|
...(shouldShowPlanCreditsPanel.value && subscriptionPanel
|
|
? [subscriptionPanel]
|
|
: [])
|
|
].filter((panel) => panel.component)
|
|
)
|
|
|
|
/**
|
|
* The default category to show when the dialog is opened.
|
|
*/
|
|
const defaultCategory = computed<SettingTreeNode>(() => {
|
|
if (!defaultPanel) return settingCategories.value[0]
|
|
// Search through all groups in groupedMenuTreeNodes
|
|
for (const group of groupedMenuTreeNodes.value) {
|
|
const found = group.children?.find((node) => node.key === defaultPanel)
|
|
if (found) return found
|
|
}
|
|
return settingCategories.value[0]
|
|
})
|
|
|
|
const translateCategory = (node: SettingTreeNode) => ({
|
|
...node,
|
|
translatedLabel: t(
|
|
`settingsCategories.${normalizeI18nKey(node.label)}`,
|
|
node.label
|
|
)
|
|
})
|
|
|
|
const groupedMenuTreeNodes = computed<SettingTreeNode[]>(() => [
|
|
// Account settings - show different panels based on distribution and auth state
|
|
{
|
|
key: 'account',
|
|
label: 'Account',
|
|
children: [
|
|
userPanel.node,
|
|
...(isLoggedIn.value &&
|
|
shouldShowPlanCreditsPanel.value &&
|
|
subscriptionPanel
|
|
? [subscriptionPanel.node]
|
|
: []),
|
|
...(isLoggedIn.value &&
|
|
!(isCloud && window.__CONFIG__?.subscription_required)
|
|
? [creditsPanel.node]
|
|
: [])
|
|
].map(translateCategory)
|
|
},
|
|
// Normal settings stored in the settingStore
|
|
{
|
|
key: 'settings',
|
|
label: 'Application Settings',
|
|
children: settingCategories.value.map(translateCategory)
|
|
},
|
|
// Special settings such as about, keybinding, extension, server-config
|
|
{
|
|
key: 'specialSettings',
|
|
label: 'Special Settings',
|
|
children: [
|
|
keybindingPanel.node,
|
|
extensionPanel.node,
|
|
aboutPanel.node,
|
|
...(isElectron() ? [serverConfigPanel.node] : [])
|
|
].map(translateCategory)
|
|
}
|
|
])
|
|
|
|
onMounted(() => {
|
|
activeCategory.value = defaultCategory.value
|
|
})
|
|
|
|
return {
|
|
panels,
|
|
activeCategory,
|
|
defaultCategory,
|
|
groupedMenuTreeNodes,
|
|
settingCategories
|
|
}
|
|
}
|