mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-08 00:50:05 +00:00
feat: add upgrade modal for model upload when private models disabled (#7124)
## Summary Adds a dedicated upgrade modal that appears when users without private models access try to upload models, providing a clear path to upgrade their subscription. ## Changes - **New upgrade modal**: Created `UploadModelUpgradeModal` with dedicated body, header, and footer components - **Conditional rendering**: Modified `AssetBrowserModal` to show upgrade modal when `privateModelsEnabled` flag is false - **Subscription integration**: Connected upgrade flow to existing subscription system via `showSubscriptionDialog()` - **Localization**: Added localization keys for upgrade messaging ## Review Focus - Conditional logic in `AssetBrowserModal.handleUploadClick()` based on feature flags - Component naming consistency (all upgrade-related components prefixed with `UploadModelUpgrade`) - Footer component refactoring maintains existing upload wizard behavior 🤖 Generated with [Claude Code](https://claude.com/claude-code) ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-7124-feat-add-upgrade-modal-for-model-upload-when-private-models-disabled-2be6d73d36508147b72eea8a1d6ab772) by [Unito](https://www.unito.io) --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Alexander Brown <drjkl@comfy.org>
This commit is contained in:
committed by
GitHub
parent
52e915baf0
commit
8d4a6df7f8
@@ -11,7 +11,8 @@ export enum ServerFeatureFlag {
|
||||
MAX_UPLOAD_SIZE = 'max_upload_size',
|
||||
MANAGER_SUPPORTS_V4 = 'extension.manager.supports_v4',
|
||||
MODEL_UPLOAD_BUTTON_ENABLED = 'model_upload_button_enabled',
|
||||
ASSET_UPDATE_OPTIONS_ENABLED = 'asset_update_options_enabled'
|
||||
ASSET_UPDATE_OPTIONS_ENABLED = 'asset_update_options_enabled',
|
||||
PRIVATE_MODELS_ENABLED = 'private_models_enabled'
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -47,6 +48,13 @@ export function useFeatureFlags() {
|
||||
false
|
||||
)
|
||||
)
|
||||
},
|
||||
get privateModelsEnabled() {
|
||||
// Check remote config first (from /api/features), fall back to websocket feature flags
|
||||
return (
|
||||
remoteConfig.value.private_models_enabled ??
|
||||
api.getServerFeature(ServerFeatureFlag.PRIVATE_MODELS_ENABLED, false)
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -2121,6 +2121,8 @@
|
||||
"modelUploaded": "Model imported! 🎉",
|
||||
"findInLibrary": "Find it in the {type} section of the models library.",
|
||||
"finish": "Finish",
|
||||
"upgradeToUnlockFeature": "Upgrade to unlock this feature",
|
||||
"upgradeFeatureDescription": "This feature is only available with Creator or Pro plans.",
|
||||
"allModels": "All Models",
|
||||
"allCategory": "All {category}",
|
||||
"unknown": "Unknown",
|
||||
|
||||
30
src/platform/assets/components/UploadModelUpgradeModal.vue
Normal file
30
src/platform/assets/components/UploadModelUpgradeModal.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-col justify-between gap-10 p-4 border-t border-border-default w-auto max-w-[min(500px,90vw)]"
|
||||
>
|
||||
<UploadModelUpgradeModalBody />
|
||||
|
||||
<UploadModelUpgradeModalFooter
|
||||
@close="handleClose"
|
||||
@subscribe="handleSubscribe"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import UploadModelUpgradeModalBody from '@/platform/assets/components/UploadModelUpgradeModalBody.vue'
|
||||
import UploadModelUpgradeModalFooter from '@/platform/assets/components/UploadModelUpgradeModalFooter.vue'
|
||||
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
|
||||
const dialogStore = useDialogStore()
|
||||
const { showSubscriptionDialog } = useSubscription()
|
||||
|
||||
function handleClose() {
|
||||
dialogStore.closeDialog({ key: 'upload-model-upgrade' })
|
||||
}
|
||||
|
||||
function handleSubscribe() {
|
||||
showSubscriptionDialog()
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div
|
||||
class="flex flex-1 flex-col items-center justify-center text-base text-muted-foreground"
|
||||
>
|
||||
<p class="m-0 max-w-md">
|
||||
{{ $t('assetBrowser.upgradeFeatureDescription') }}
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,34 @@
|
||||
<template>
|
||||
<div class="flex flex-wrap justify-end gap-2 w-full">
|
||||
<a
|
||||
href="https://blog.comfy.org/p/comfy-cloud-new-features-and-pricing"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="text-muted-foreground mr-auto underline flex items-center gap-2"
|
||||
>
|
||||
<i class="icon-[lucide--external-link]" />
|
||||
<span>{{ $t('g.learnMore') }}</span>
|
||||
</a>
|
||||
<TextButton
|
||||
:label="$t('g.close')"
|
||||
type="transparent"
|
||||
size="md"
|
||||
@click="emit('close')"
|
||||
/>
|
||||
<TextButton
|
||||
:label="$t('subscription.required.subscribe')"
|
||||
type="secondary"
|
||||
size="md"
|
||||
@click="emit('subscribe')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import TextButton from '@/components/button/TextButton.vue'
|
||||
|
||||
const emit = defineEmits<{
|
||||
close: []
|
||||
subscribe: []
|
||||
}>()
|
||||
</script>
|
||||
@@ -0,0 +1,5 @@
|
||||
<template>
|
||||
<div class="flex items-center gap-2 p-4 font-bold">
|
||||
<span>{{ $t('assetBrowser.upgradeToUnlockFeature') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,6 +1,8 @@
|
||||
import { useFeatureFlags } from '@/composables/useFeatureFlags'
|
||||
import UploadModelDialog from '@/platform/assets/components/UploadModelDialog.vue'
|
||||
import UploadModelDialogHeader from '@/platform/assets/components/UploadModelDialogHeader.vue'
|
||||
import UploadModelUpgradeModal from '@/platform/assets/components/UploadModelUpgradeModal.vue'
|
||||
import UploadModelUpgradeModalHeader from '@/platform/assets/components/UploadModelUpgradeModalHeader.vue'
|
||||
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
|
||||
import { useDialogStore } from '@/stores/dialogStore'
|
||||
import type { UseAsyncStateReturn } from '@vueuse/core'
|
||||
@@ -14,22 +16,38 @@ export function useModelUpload(
|
||||
const isUploadButtonEnabled = computed(() => flags.modelUploadButtonEnabled)
|
||||
|
||||
function showUploadDialog() {
|
||||
dialogStore.showDialog({
|
||||
key: 'upload-model',
|
||||
headerComponent: UploadModelDialogHeader,
|
||||
component: UploadModelDialog,
|
||||
props: {
|
||||
onUploadSuccess: async () => {
|
||||
await execute?.()
|
||||
if (!flags.privateModelsEnabled) {
|
||||
// Show upgrade modal if private models are disabled
|
||||
dialogStore.showDialog({
|
||||
key: 'upload-model-upgrade',
|
||||
headerComponent: UploadModelUpgradeModalHeader,
|
||||
component: UploadModelUpgradeModal,
|
||||
dialogComponentProps: {
|
||||
pt: {
|
||||
header: 'py-0! pl-0!',
|
||||
content: 'p-0!'
|
||||
}
|
||||
}
|
||||
},
|
||||
dialogComponentProps: {
|
||||
pt: {
|
||||
header: 'py-0! pl-0!',
|
||||
content: 'p-0!'
|
||||
})
|
||||
} else {
|
||||
// Show regular upload modal
|
||||
dialogStore.showDialog({
|
||||
key: 'upload-model',
|
||||
headerComponent: UploadModelDialogHeader,
|
||||
component: UploadModelDialog,
|
||||
props: {
|
||||
onUploadSuccess: async () => {
|
||||
await execute?.()
|
||||
}
|
||||
},
|
||||
dialogComponentProps: {
|
||||
pt: {
|
||||
header: 'py-0! pl-0!',
|
||||
content: 'p-0!'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
return { isUploadButtonEnabled, showUploadDialog }
|
||||
}
|
||||
|
||||
@@ -36,4 +36,5 @@ export type RemoteConfig = {
|
||||
telemetry_disabled_events?: TelemetryEventName[]
|
||||
model_upload_button_enabled?: boolean
|
||||
asset_update_options_enabled?: boolean
|
||||
private_models_enabled?: boolean
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user