Skip to content

Commit 1557281

Browse files
committed
subscription page
1 parent 8cc5b52 commit 1557281

File tree

15 files changed

+815
-8
lines changed

15 files changed

+815
-8
lines changed
64.8 KB
Binary file not shown.

src/components/actionbar/ComfyActionbar.vue

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
)
1515
"
1616
/>
17-
<ComfyQueueButton />
17+
<SubscribeToRun v-if="!activeSubscription" />
18+
<ComfyQueueButton v-else />
1819
</div>
1920
</Panel>
2021
</template>
@@ -30,14 +31,43 @@ import {
3031
} from '@vueuse/core'
3132
import { clamp } from 'es-toolkit/compat'
3233
import Panel from 'primevue/panel'
33-
import type { Ref } from 'vue'
34-
import { computed, inject, nextTick, onMounted, ref, watch } from 'vue'
34+
import type { Component, Ref } from 'vue'
35+
import {
36+
computed,
37+
defineAsyncComponent,
38+
inject,
39+
nextTick,
40+
onMounted,
41+
ref,
42+
watch
43+
} from 'vue'
3544
45+
import { isCloud } from '@/platform/distribution/types'
3646
import { useSettingStore } from '@/platform/settings/settingStore'
3747
import { cn } from '@/utils/tailwindUtil'
3848
3949
import ComfyQueueButton from './ComfyQueueButton.vue'
4050
51+
const activeSubscription = ref(false)
52+
const SubscribeToRun: Component | null = isCloud
53+
? defineAsyncComponent(() => import('./SubscribeToRun.vue'))
54+
: null
55+
56+
if (isCloud) {
57+
void import('@/platform/cloud/subscription/useSubscription').then(
58+
({ useSubscription }) => {
59+
const { isActiveSubscription } = useSubscription()
60+
watch(
61+
isActiveSubscription,
62+
(value) => {
63+
activeSubscription.value = value
64+
},
65+
{ immediate: true }
66+
)
67+
}
68+
)
69+
}
70+
4171
const settingsStore = useSettingStore()
4272
4373
const position = computed(() => settingsStore.get('Comfy.UseNewMenu'))
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<template>
2+
<Button
3+
v-tooltip.bottom="{
4+
value: $t('subscription.subscribeToRun'),
5+
showDelay: 600
6+
}"
7+
class="subscribe-to-run-button"
8+
:label="$t('subscription.subscribeToRun')"
9+
icon="pi pi-lock"
10+
severity="primary"
11+
size="small"
12+
data-testid="subscribe-to-run-button"
13+
@click="showSubscriptionDialog"
14+
/>
15+
</template>
16+
17+
<script setup lang="ts">
18+
import Button from 'primevue/button'
19+
20+
import { useSubscription } from '@/platform/cloud/subscription/useSubscription'
21+
22+
const { showSubscriptionDialog } = useSubscription()
23+
</script>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<template>
2+
<div class="flex flex-col gap-3">
3+
<div class="flex items-center gap-2">
4+
<i class="pi pi-check text-sm" />
5+
<span class="text-sm">
6+
{{ $t('subscription.benefits.benefit1') }}
7+
</span>
8+
</div>
9+
10+
<div class="flex items-center gap-2">
11+
<i class="pi pi-check text-sm" />
12+
<span class="text-sm">
13+
{{ $t('subscription.benefits.benefit2') }}
14+
</span>
15+
</div>
16+
17+
<Button
18+
:label="$t('subscription.viewMoreDetails')"
19+
text
20+
icon="pi pi-external-link"
21+
icon-pos="left"
22+
size="small"
23+
class="self-start text-sm text-muted"
24+
@click="handleViewMoreDetails"
25+
/>
26+
</div>
27+
</template>
28+
29+
<script setup lang="ts">
30+
import Button from 'primevue/button'
31+
32+
const handleViewMoreDetails = () => {
33+
window.open('https://www.comfy.org/cloud', '_blank')
34+
}
35+
</script>
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<template>
2+
<div class="grid h-full grid-cols-5">
3+
<div
4+
class="relative col-span-2 flex items-center justify-center overflow-hidden"
5+
>
6+
<video
7+
autoplay
8+
loop
9+
muted
10+
playsinline
11+
class="h-full min-w-[125%] object-cover"
12+
style="margin-left: -20%"
13+
>
14+
<source
15+
src="/assets/images/cloud-subscription.webm"
16+
type="video/webm"
17+
/>
18+
</video>
19+
</div>
20+
21+
<div class="col-span-3 flex flex-col p-4">
22+
<div class="flex flex-col gap-4">
23+
<div class="inline-flex items-center gap-2">
24+
<div class="text-sm">
25+
{{ $t('subscription.required.title') }}
26+
</div>
27+
<TopbarBadges />
28+
</div>
29+
30+
<div class="mt-4 mb-10 flex items-baseline gap-2">
31+
<span class="text-3xl font-bold">{{ formattedMonthlyPrice }}</span>
32+
<span class="text-xl">{{ $t('subscription.perMonth') }}</span>
33+
</div>
34+
</div>
35+
36+
<SubscriptionBenefits class="text-muted" />
37+
38+
<div class="mt-10 mb-4 flex flex-col">
39+
<Button
40+
:label="$t('subscription.required.subscribe')"
41+
size="large"
42+
class="w-full"
43+
:loading="isLoading"
44+
:disabled="isPolling"
45+
@click="handleSubscribe"
46+
/>
47+
</div>
48+
</div>
49+
</div>
50+
</template>
51+
52+
<script setup lang="ts">
53+
import Button from 'primevue/button'
54+
import { onBeforeUnmount, ref } from 'vue'
55+
56+
import SubscriptionBenefits from '@/components/common/SubscriptionBenefits.vue'
57+
import TopbarBadges from '@/components/topbar/TopbarBadges.vue'
58+
import { useSubscription } from '@/platform/cloud/subscription/useSubscription'
59+
60+
const emit = defineEmits<{
61+
close: [subscribed: boolean]
62+
}>()
63+
64+
const { subscribe, isActiveSubscription, formattedMonthlyPrice } =
65+
useSubscription()
66+
67+
const isLoading = ref(false)
68+
const isPolling = ref(false)
69+
let pollInterval: number | null = null
70+
71+
const POLL_INTERVAL_MS = 3000 // Poll every 3 seconds
72+
const MAX_POLL_DURATION_MS = 5 * 60 * 1000 // Stop polling after 5 minutes
73+
74+
const startPollingSubscriptionStatus = () => {
75+
isPolling.value = true
76+
isLoading.value = true
77+
78+
const startTime = Date.now()
79+
80+
const poll = async () => {
81+
try {
82+
if (Date.now() - startTime > MAX_POLL_DURATION_MS) {
83+
stopPolling()
84+
return
85+
}
86+
87+
if (isActiveSubscription.value) {
88+
stopPolling()
89+
emit('close', true)
90+
}
91+
} catch (error) {
92+
console.error(
93+
'[SubscriptionRequiredDialog] Error polling subscription status:',
94+
error
95+
)
96+
}
97+
}
98+
99+
void poll()
100+
pollInterval = window.setInterval(poll, POLL_INTERVAL_MS)
101+
}
102+
103+
const stopPolling = () => {
104+
if (pollInterval) {
105+
clearInterval(pollInterval)
106+
pollInterval = null
107+
}
108+
isPolling.value = false
109+
isLoading.value = false
110+
}
111+
112+
const handleSubscribe = async () => {
113+
isLoading.value = true
114+
try {
115+
await subscribe()
116+
117+
startPollingSubscriptionStatus()
118+
} catch (error) {
119+
console.error(
120+
'[SubscriptionRequiredDialog] Error initiating subscription:',
121+
error
122+
)
123+
isLoading.value = false
124+
}
125+
}
126+
127+
onBeforeUnmount(() => {
128+
stopPolling()
129+
})
130+
</script>
131+
132+
<style scoped>
133+
:deep(.bg-comfy-menu-secondary) {
134+
background-color: transparent;
135+
}
136+
137+
:deep(.p-button) {
138+
color: white;
139+
}
140+
</style>

0 commit comments

Comments
 (0)