Skip to content

Commit 5e3a559

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 2bf45f2 commit 5e3a559

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
@@ -10,7 +10,8 @@ export enum ServerFeatureFlag {
1010
MAX_UPLOAD_SIZE = 'max_upload_size',
1111
MANAGER_SUPPORTS_V4 = 'extension.manager.supports_v4',
1212
MODEL_UPLOAD_BUTTON_ENABLED = 'model_upload_button_enabled',
13-
ASSET_UPDATE_OPTIONS_ENABLED = 'asset_update_options_enabled'
13+
ASSET_UPDATE_OPTIONS_ENABLED = 'asset_update_options_enabled',
14+
PRIVATE_MODELS_ENABLED = 'private_models_enabled'
1415
}
1516

1617
/**
@@ -38,6 +39,12 @@ export function useFeatureFlags() {
3839
ServerFeatureFlag.ASSET_UPDATE_OPTIONS_ENABLED,
3940
false
4041
)
42+
},
43+
get privateModelsEnabled() {
44+
return api.getServerFeature(
45+
ServerFeatureFlag.PRIVATE_MODELS_ENABLED,
46+
false
47+
)
4148
}
4249
})
4350

src/locales/en/main.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2121,6 +2121,8 @@
21212121
"modelUploaded": "Model imported! 🎉",
21222122
"findInLibrary": "Find it in the {type} section of the models library.",
21232123
"finish": "Finish",
2124+
"upgradeToUnlockFeature": "Upgrade to unlock this feature",
2125+
"upgradeFeatureDescription": "This feature is only available with Creator or Pro plans.",
21242126
"allModels": "All Models",
21252127
"allCategory": "All {category}",
21262128
"unknown": "Unknown",
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>
Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { useFeatureFlags } from '@/composables/useFeatureFlags'
22
import UploadModelDialog from '@/platform/assets/components/UploadModelDialog.vue'
33
import UploadModelDialogHeader from '@/platform/assets/components/UploadModelDialogHeader.vue'
4+
import UploadModelUpgradeModal from '@/platform/assets/components/UploadModelUpgradeModal.vue'
5+
import UploadModelUpgradeModalHeader from '@/platform/assets/components/UploadModelUpgradeModalHeader.vue'
46
import type { AssetItem } from '@/platform/assets/schemas/assetSchema'
57
import { useDialogStore } from '@/stores/dialogStore'
68
import type { UseAsyncStateReturn } from '@vueuse/core'
@@ -14,22 +16,38 @@ export function useModelUpload(
1416
const isUploadButtonEnabled = computed(() => flags.modelUploadButtonEnabled)
1517

1618
function showUploadDialog() {
17-
dialogStore.showDialog({
18-
key: 'upload-model',
19-
headerComponent: UploadModelDialogHeader,
20-
component: UploadModelDialog,
21-
props: {
22-
onUploadSuccess: async () => {
23-
await execute?.()
19+
if (!flags.privateModelsEnabled) {
20+
// Show upgrade modal if private models are disabled
21+
dialogStore.showDialog({
22+
key: 'upload-model-upgrade',
23+
headerComponent: UploadModelUpgradeModalHeader,
24+
component: UploadModelUpgradeModal,
25+
dialogComponentProps: {
26+
pt: {
27+
header: 'py-0! pl-0!',
28+
content: 'p-0!'
29+
}
2430
}
25-
},
26-
dialogComponentProps: {
27-
pt: {
28-
header: 'py-0! pl-0!',
29-
content: 'p-0!'
31+
})
32+
} else {
33+
// Show regular upload modal
34+
dialogStore.showDialog({
35+
key: 'upload-model',
36+
headerComponent: UploadModelDialogHeader,
37+
component: UploadModelDialog,
38+
props: {
39+
onUploadSuccess: async () => {
40+
await execute?.()
41+
}
42+
},
43+
dialogComponentProps: {
44+
pt: {
45+
header: 'py-0! pl-0!',
46+
content: 'p-0!'
47+
}
3048
}
31-
}
32-
})
49+
})
50+
}
3351
}
3452
return { isUploadButtonEnabled, showUploadDialog }
3553
}

0 commit comments

Comments
 (0)