Skip to content

Commit e9f1fc3

Browse files
feat: add upgrade modal for model upload when private models disabled
Add separate upgrade modal that displays when users without private models try to upload models, prompting them to upgrade their subscription. - Add upgrade modal with body, header, and footer components - Conditionally show upgrade or upload modal based on privateModelsEnabled flag - Integrate with subscription system via showSubscriptionDialog() - Add localization keys for upgrade messaging 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 2c437ac commit e9f1fc3

File tree

7 files changed

+141
-15
lines changed

7 files changed

+141
-15
lines changed

src/composables/useFeatureFlags.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ export enum ServerFeatureFlag {
99
SUPPORTS_PREVIEW_METADATA = 'supports_preview_metadata',
1010
MAX_UPLOAD_SIZE = 'max_upload_size',
1111
MANAGER_SUPPORTS_V4 = 'extension.manager.supports_v4',
12-
MODEL_UPLOAD_BUTTON_ENABLED = 'model_upload_button_enabled'
12+
MODEL_UPLOAD_BUTTON_ENABLED = 'model_upload_button_enabled',
13+
PRIVATE_MODELS_ENABLED = 'private_models_enabled'
1314
}
1415

1516
/**
@@ -31,6 +32,12 @@ export function useFeatureFlags() {
3132
ServerFeatureFlag.MODEL_UPLOAD_BUTTON_ENABLED,
3233
false
3334
)
35+
},
36+
get privateModelsEnabled() {
37+
return api.getServerFeature(
38+
ServerFeatureFlag.PRIVATE_MODELS_ENABLED,
39+
false
40+
)
3441
}
3542
})
3643

src/locales/en/main.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,6 +2124,8 @@
21242124
"modelUploaded": "Model imported! 🎉",
21252125
"findInLibrary": "Find it in the {type} section of the models library.",
21262126
"finish": "Finish",
2127+
"upgradeToUnlockFeature": "Upgrade to unlock this feature",
2128+
"upgradeFeatureDescription": "This feature is only available with Creator or Pro plans.",
21272129
"allModels": "All Models",
21282130
"allCategory": "All {category}",
21292131
"unknown": "Unknown",

src/platform/assets/components/AssetBrowserModal.vue

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ import AssetFilterBar from '@/platform/assets/components/AssetFilterBar.vue'
7575
import AssetGrid from '@/platform/assets/components/AssetGrid.vue'
7676
import UploadModelDialog from '@/platform/assets/components/UploadModelDialog.vue'
7777
import UploadModelDialogHeader from '@/platform/assets/components/UploadModelDialogHeader.vue'
78+
import UploadModelUpgradeModal from '@/platform/assets/components/UploadModelUpgradeModal.vue'
79+
import UploadModelUpgradeModalHeader from '@/platform/assets/components/UploadModelUpgradeModalHeader.vue'
7880
import type { AssetDisplayItem } from '@/platform/assets/composables/useAssetBrowser'
7981
import { useAssetBrowser } from '@/platform/assets/composables/useAssetBrowser'
8082
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
@@ -193,21 +195,37 @@ const { flags } = useFeatureFlags()
193195
const isUploadButtonEnabled = computed(() => flags.modelUploadButtonEnabled)
194196
195197
function handleUploadClick() {
196-
dialogStore.showDialog({
197-
key: 'upload-model',
198-
headerComponent: UploadModelDialogHeader,
199-
component: UploadModelDialog,
200-
props: {
201-
onUploadSuccess: async () => {
202-
await execute()
198+
if (!flags.privateModelsEnabled) {
199+
// Show upgrade modal if private models are disabled
200+
dialogStore.showDialog({
201+
key: 'upload-model-upgrade',
202+
headerComponent: UploadModelUpgradeModalHeader,
203+
component: UploadModelUpgradeModal,
204+
dialogComponentProps: {
205+
pt: {
206+
header: 'py-0! pl-0!',
207+
content: 'p-0!'
208+
}
203209
}
204-
},
205-
dialogComponentProps: {
206-
pt: {
207-
header: 'py-0! pl-0!',
208-
content: 'p-0!'
210+
})
211+
} else {
212+
// Show regular upload modal
213+
dialogStore.showDialog({
214+
key: 'upload-model',
215+
headerComponent: UploadModelDialogHeader,
216+
component: UploadModelDialog,
217+
props: {
218+
onUploadSuccess: async () => {
219+
await execute()
220+
}
221+
},
222+
dialogComponentProps: {
223+
pt: {
224+
header: 'py-0! pl-0!',
225+
content: 'p-0!'
226+
}
209227
}
210-
}
211-
})
228+
})
229+
}
212230
}
213231
</script>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<template>
2+
<div
3+
class="upload-model-upgrade-modal flex flex-col justify-between gap-6 p-4 pt-6 border-t-[1px] border-border-default"
4+
>
5+
<!-- Upgrade Content -->
6+
<UploadModelUpgradeModalBody />
7+
8+
<!-- Footer -->
9+
<UploadModelUpgradeModalFooter
10+
@close="handleClose"
11+
@subscribe="handleSubscribe"
12+
/>
13+
</div>
14+
</template>
15+
16+
<script setup lang="ts">
17+
import UploadModelUpgradeModalBody from '@/platform/assets/components/UploadModelUpgradeModalBody.vue'
18+
import UploadModelUpgradeModalFooter from '@/platform/assets/components/UploadModelUpgradeModalFooter.vue'
19+
import { useSubscription } from '@/platform/cloud/subscription/composables/useSubscription'
20+
import { useDialogStore } from '@/stores/dialogStore'
21+
22+
const dialogStore = useDialogStore()
23+
const { showSubscriptionDialog } = useSubscription()
24+
25+
function handleClose() {
26+
dialogStore.closeDialog({ key: 'upload-model-upgrade' })
27+
}
28+
29+
function handleSubscribe() {
30+
showSubscriptionDialog()
31+
}
32+
</script>
33+
34+
<style scoped>
35+
.upload-model-upgrade-modal {
36+
width: 90vw;
37+
max-width: 500px;
38+
min-height: 200px;
39+
}
40+
41+
@media (min-width: 640px) {
42+
.upload-model-upgrade-modal {
43+
width: auto;
44+
min-width: 450px;
45+
}
46+
}
47+
</style>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<template>
2+
<div
3+
class="flex flex-1 flex-col items-center justify-center gap-6 text-base text-muted-foreground"
4+
>
5+
<p class="m-0 max-w-md">
6+
{{ $t('assetBrowser.upgradeFeatureDescription') }}
7+
</p>
8+
</div>
9+
</template>
10+
11+
<script setup lang="ts"></script>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<template>
2+
<div class="flex justify-end gap-2 w-full">
3+
<span
4+
class="text-muted-foreground mr-auto underline flex items-center gap-2"
5+
>
6+
<i class="icon-[lucide--circle-question-mark]" />
7+
<a
8+
href="https://blog.comfy.org/p/comfy-cloud-new-features-and-pricing"
9+
target="_blank"
10+
class="text-muted-foreground"
11+
>{{ $t('Learn more') }}</a
12+
>
13+
</span>
14+
<TextButton
15+
:label="$t('g.close')"
16+
type="transparent"
17+
size="md"
18+
@click="emit('close')"
19+
/>
20+
<TextButton
21+
:label="$t('Subscribe')"
22+
type="secondary"
23+
size="md"
24+
@click="emit('subscribe')"
25+
/>
26+
</div>
27+
</template>
28+
29+
<script setup lang="ts">
30+
import TextButton from '@/components/button/TextButton.vue'
31+
32+
const emit = defineEmits<{
33+
(e: 'close'): void
34+
(e: 'subscribe'): void
35+
}>()
36+
</script>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<template>
2+
<div class="flex items-center gap-2 p-4 font-bold">
3+
<span>{{ $t('assetBrowser.upgradeToUnlockFeature') }}</span>
4+
</div>
5+
</template>

0 commit comments

Comments
 (0)