mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-29 18:52:19 +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',
|
MAX_UPLOAD_SIZE = 'max_upload_size',
|
||||||
MANAGER_SUPPORTS_V4 = 'extension.manager.supports_v4',
|
MANAGER_SUPPORTS_V4 = 'extension.manager.supports_v4',
|
||||||
MODEL_UPLOAD_BUTTON_ENABLED = 'model_upload_button_enabled',
|
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
|
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! 🎉",
|
"modelUploaded": "Model imported! 🎉",
|
||||||
"findInLibrary": "Find it in the {type} section of the models library.",
|
"findInLibrary": "Find it in the {type} section of the models library.",
|
||||||
"finish": "Finish",
|
"finish": "Finish",
|
||||||
|
"upgradeToUnlockFeature": "Upgrade to unlock this feature",
|
||||||
|
"upgradeFeatureDescription": "This feature is only available with Creator or Pro plans.",
|
||||||
"allModels": "All Models",
|
"allModels": "All Models",
|
||||||
"allCategory": "All {category}",
|
"allCategory": "All {category}",
|
||||||
"unknown": "Unknown",
|
"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 { useFeatureFlags } from '@/composables/useFeatureFlags'
|
||||||
import UploadModelDialog from '@/platform/assets/components/UploadModelDialog.vue'
|
import UploadModelDialog from '@/platform/assets/components/UploadModelDialog.vue'
|
||||||
import UploadModelDialogHeader from '@/platform/assets/components/UploadModelDialogHeader.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 type { AssetItem } from '@/platform/assets/schemas/assetSchema'
|
||||||
import { useDialogStore } from '@/stores/dialogStore'
|
import { useDialogStore } from '@/stores/dialogStore'
|
||||||
import type { UseAsyncStateReturn } from '@vueuse/core'
|
import type { UseAsyncStateReturn } from '@vueuse/core'
|
||||||
@@ -14,22 +16,38 @@ export function useModelUpload(
|
|||||||
const isUploadButtonEnabled = computed(() => flags.modelUploadButtonEnabled)
|
const isUploadButtonEnabled = computed(() => flags.modelUploadButtonEnabled)
|
||||||
|
|
||||||
function showUploadDialog() {
|
function showUploadDialog() {
|
||||||
dialogStore.showDialog({
|
if (!flags.privateModelsEnabled) {
|
||||||
key: 'upload-model',
|
// Show upgrade modal if private models are disabled
|
||||||
headerComponent: UploadModelDialogHeader,
|
dialogStore.showDialog({
|
||||||
component: UploadModelDialog,
|
key: 'upload-model-upgrade',
|
||||||
props: {
|
headerComponent: UploadModelUpgradeModalHeader,
|
||||||
onUploadSuccess: async () => {
|
component: UploadModelUpgradeModal,
|
||||||
await execute?.()
|
dialogComponentProps: {
|
||||||
|
pt: {
|
||||||
|
header: 'py-0! pl-0!',
|
||||||
|
content: 'p-0!'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
})
|
||||||
dialogComponentProps: {
|
} else {
|
||||||
pt: {
|
// Show regular upload modal
|
||||||
header: 'py-0! pl-0!',
|
dialogStore.showDialog({
|
||||||
content: 'p-0!'
|
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 }
|
return { isUploadButtonEnabled, showUploadDialog }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,4 +36,5 @@ export type RemoteConfig = {
|
|||||||
telemetry_disabled_events?: TelemetryEventName[]
|
telemetry_disabled_events?: TelemetryEventName[]
|
||||||
model_upload_button_enabled?: boolean
|
model_upload_button_enabled?: boolean
|
||||||
asset_update_options_enabled?: boolean
|
asset_update_options_enabled?: boolean
|
||||||
|
private_models_enabled?: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user