mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-06-07 16:24:18 +00:00
170 lines
5.1 KiB
TypeScript
170 lines
5.1 KiB
TypeScript
import { defineAsyncComponent } from 'vue'
|
|
import { useDialogService } from '@/services/dialogService'
|
|
import { useDialogStore } from '@/stores/dialogStore'
|
|
import { useFeatureFlags } from '@/composables/useFeatureFlags'
|
|
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
|
import { isCloud } from '@/platform/distribution/types'
|
|
import { useTeamWorkspaceStore } from '@/platform/workspace/stores/teamWorkspaceStore'
|
|
|
|
const DIALOG_KEY = 'subscription-required'
|
|
const FREE_TIER_DIALOG_KEY = 'free-tier-info'
|
|
const RESUME_PRICING_KEY = 'comfy:resume-team-pricing'
|
|
|
|
export type SubscriptionDialogReason =
|
|
| 'subscription_required'
|
|
| 'out_of_credits'
|
|
| 'top_up_blocked'
|
|
|
|
export const useSubscriptionDialog = () => {
|
|
const { flags } = useFeatureFlags()
|
|
const dialogService = useDialogService()
|
|
const dialogStore = useDialogStore()
|
|
const workspaceStore = useTeamWorkspaceStore()
|
|
const { isFreeTier } = useSubscription()
|
|
|
|
function hide() {
|
|
dialogStore.closeDialog({ key: DIALOG_KEY })
|
|
dialogStore.closeDialog({ key: FREE_TIER_DIALOG_KEY })
|
|
}
|
|
|
|
function showPricingTable(options?: { reason?: SubscriptionDialogReason }) {
|
|
if (!isCloud) return
|
|
|
|
// Shared dialog shell styling for both variants.
|
|
const dialogComponentProps = {
|
|
style: 'width: min(1328px, 95vw); max-height: 958px;',
|
|
pt: {
|
|
root: {
|
|
class: 'rounded-2xl bg-transparent h-full'
|
|
},
|
|
content: {
|
|
class:
|
|
'!p-0 rounded-2xl border border-border-default bg-base-background/60 backdrop-blur-md shadow-[0_25px_80px_rgba(5,6,12,0.45)] h-full'
|
|
}
|
|
}
|
|
}
|
|
|
|
// Jun-5 model: a single unified pricing table (personal/team plan toggle on
|
|
// one workspace) when team workspaces are enabled. Replaces the old
|
|
// personal-vs-team workspace fork. Flag-off keeps the legacy table.
|
|
if (flags.teamWorkspacesEnabled) {
|
|
dialogService.showLayoutDialog({
|
|
key: DIALOG_KEY,
|
|
component: defineAsyncComponent(
|
|
() =>
|
|
import('@/platform/workspace/components/SubscriptionRequiredDialogContentUnified.vue')
|
|
),
|
|
props: { onClose: hide, reason: options?.reason },
|
|
dialogComponentProps
|
|
})
|
|
return
|
|
}
|
|
|
|
dialogService.showLayoutDialog({
|
|
key: DIALOG_KEY,
|
|
component: defineAsyncComponent(
|
|
() =>
|
|
import('@/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vue')
|
|
),
|
|
props: {
|
|
onClose: hide,
|
|
reason: options?.reason,
|
|
onChooseTeam: () => startTeamWorkspaceUpgradeFlow()
|
|
},
|
|
dialogComponentProps
|
|
})
|
|
}
|
|
|
|
function show(options?: { reason?: SubscriptionDialogReason }) {
|
|
if (isFreeTier.value && workspaceStore.isInPersonalWorkspace) {
|
|
const component = defineAsyncComponent(
|
|
() =>
|
|
import('@/platform/cloud/subscription/components/FreeTierDialogContent.vue')
|
|
)
|
|
|
|
dialogService.showLayoutDialog({
|
|
key: FREE_TIER_DIALOG_KEY,
|
|
component,
|
|
props: {
|
|
reason: options?.reason,
|
|
onClose: hide,
|
|
onUpgrade: () => {
|
|
hide()
|
|
showPricingTable(options)
|
|
}
|
|
},
|
|
dialogComponentProps: {
|
|
style: 'width: min(640px, 95vw);',
|
|
pt: {
|
|
root: {
|
|
class: 'rounded-2xl bg-transparent'
|
|
},
|
|
content: {
|
|
class:
|
|
'!p-0 rounded-2xl border border-border-default bg-base-background/60 backdrop-blur-md shadow-[0_25px_80px_rgba(5,6,12,0.45)]'
|
|
}
|
|
}
|
|
}
|
|
})
|
|
return
|
|
}
|
|
|
|
showPricingTable(options)
|
|
}
|
|
|
|
/**
|
|
* Start the two-stage team workspace upgrade flow:
|
|
* 1. Close the current pricing dialog
|
|
* 2. Open the create workspace dialog
|
|
* 3. On successful creation, persist a resume intent so the team pricing
|
|
* dialog reopens automatically after the page reload
|
|
*
|
|
* Uses sessionStorage (not a store) because the intent must survive
|
|
* a full page reload triggered by workspace switching.
|
|
*/
|
|
function startTeamWorkspaceUpgradeFlow() {
|
|
hide()
|
|
dialogService
|
|
.showTeamWorkspacesDialog(() => {
|
|
try {
|
|
sessionStorage.setItem(RESUME_PRICING_KEY, '1')
|
|
} catch {
|
|
// sessionStorage may be unavailable
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
console.error(
|
|
'[useSubscriptionDialog] Failed to open team workspaces dialog:',
|
|
error
|
|
)
|
|
showPricingTable()
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Check for and consume a pending team pricing resume intent.
|
|
* Call once after workspace initialization on app boot.
|
|
*/
|
|
function resumePendingPricingFlow() {
|
|
try {
|
|
const pending = sessionStorage.getItem(RESUME_PRICING_KEY)
|
|
if (!pending) return
|
|
sessionStorage.removeItem(RESUME_PRICING_KEY)
|
|
|
|
if (!workspaceStore.isInPersonalWorkspace) {
|
|
showPricingTable()
|
|
}
|
|
} catch {
|
|
// sessionStorage may be unavailable
|
|
}
|
|
}
|
|
|
|
return {
|
|
show,
|
|
showPricingTable,
|
|
hide,
|
|
startTeamWorkspaceUpgradeFlow,
|
|
resumePendingPricingFlow
|
|
}
|
|
}
|