Files
ComfyUI_frontend/src/platform/onboarding/cloud/CloudVerifyEmailView.vue
Christian Byrne 0d4d68fec9 track cloud-specific onboarding events and add performance optimizations for hosted cloud app (#6158)
## Summary
- Complete telemetry implementation with circular dependency fix
- Add build performance optimizations from main branch

### Telemetry Features
-  Final telemetry events: signup opened, survey flow, email
verification
-  Onboarding mode to prevent circular dependencies during app
initialization
-  Lazy composable loading with dynamic imports for workflow tracking
-  Survey responses as both event properties and persistent user
properties
-  User identification method for onboarding flow
-  Deferred user property setting until user is authenticated

### Performance Optimizations  
-  Tree-shaking enabled to remove unused code
-  Manual chunk splitting for vendor libraries (primevue, vue, tiptap,
chart.js, etc.)
-  Enhanced esbuild minification with console removal in production
builds
-  GENERATE_SOURCEMAP environment variable control
-  Maintained ImportMap disabled for cloud performance

## Test plan
- [x] Telemetry events track correctly in Mixpanel
- [x] No circular dependency errors on app startup
- [x] Survey responses appear as both event properties and user
properties
- [x] Build optimizations reduce bundle size and improve loading
performance
- [x] All lint/format/typecheck passes

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-6158-track-cloud-specific-onboarding-events-and-add-performance-optimizations-for-hosted-cloud-2926d73d365081a7b533dde249d5f734)
by [Unito](https://www.unito.io)

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-19 23:16:56 -07:00

173 lines
4.3 KiB
Vue

<template>
<div class="mx-auto max-w-[640px] px-6 py-8">
<!-- Back button -->
<button
type="button"
class="text-foreground/80 flex size-10 items-center justify-center rounded-lg border border-white bg-transparent"
aria-label="{{ t('cloudVerifyEmail_back') }}"
@click="goBack"
>
<i class="pi pi-arrow-left" />
</button>
<!-- Title -->
<h1 class="mt-8 text-2xl font-semibold">
{{ t('cloudVerifyEmail_title') }}
</h1>
<!-- Body copy -->
<p class="text-foreground/80 mt-6 text-base">
{{ t('cloudVerifyEmail_sent') }}
</p>
<p class="mt-3 text-base font-medium">{{ authStore.userEmail }}</p>
<p class="text-foreground/80 mt-6 text-base">
{{ t('cloudVerifyEmail_clickToContinue') }}
</p>
<p class="text-foreground/80 mt-10 text-base">
{{ t('cloudVerifyEmail_didntReceive') }}
<span class="cursor-pointer text-blue-400 no-underline" @click="onSend">
{{ t('cloudVerifyEmail_resend') }}</span
>
</p>
</div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRoute, useRouter } from 'vue-router'
import { useFirebaseAuth } from 'vuefire'
import { isCloud } from '@/platform/distribution/types'
import { useTelemetry } from '@/platform/telemetry'
import { useToastStore } from '@/platform/updates/common/toastStore'
import { useFirebaseAuthStore } from '@/stores/firebaseAuthStore'
const authStore = useFirebaseAuthStore()
const auth = useFirebaseAuth()!
const router = useRouter()
const route = useRoute()
const { t } = useI18n()
let intervalId: number | null = null
let timeoutId: number | null = null
const redirectInProgress = ref(false)
function clearPolling(): void {
if (intervalId !== null) {
clearInterval(intervalId)
intervalId = null
}
if (timeoutId !== null) {
clearTimeout(timeoutId)
timeoutId = null
}
}
async function redirectToNextStep(): Promise<void> {
if (redirectInProgress.value) return
redirectInProgress.value = true
clearPolling()
const inviteCode = route.query.inviteCode as string | undefined
if (inviteCode) {
await router.push({
name: 'cloud-invite-check',
query: { inviteCode }
})
} else {
await router.push({ name: 'cloud-user-check' })
}
}
const goBack = async () => {
const inviteCode = route.query.inviteCode as string | undefined
const authStore = useFirebaseAuthStore()
// If the user is already verified (email link already clicked),
// continue to the next step automatically.
if (authStore.isEmailVerified) {
await router.push({
name: 'cloud-invite-check',
query: inviteCode ? { inviteCode } : {}
})
} else {
await router.push({
name: 'cloud-login',
query: {
inviteCode
}
})
}
}
async function onSend() {
try {
await authStore.verifyEmail()
// Track email verification requested
if (isCloud) {
useTelemetry()?.trackEmailVerification('requested')
}
useToastStore().add({
severity: 'success',
summary: t('cloudVerifyEmail_toast_success', {
email: authStore.userEmail
})
})
} catch (e) {
useToastStore().add({
severity: 'error',
summary: t('cloudVerifyEmail_toast_failed')
})
}
}
onMounted(async () => {
// Track email verification screen opened
if (isCloud) {
useTelemetry()?.trackEmailVerification('opened')
}
// If the user is already verified (email link already clicked),
// continue to the next step automatically.
if (authStore.isEmailVerified) {
return redirectToNextStep()
}
// Send initial verification email
await onSend()
// Start polling to check email verification status
intervalId = window.setInterval(async () => {
if (auth.currentUser && !redirectInProgress.value) {
await auth.currentUser.reload()
if (auth.currentUser?.emailVerified) {
// Track email verification completed
if (isCloud) {
useTelemetry()?.trackEmailVerification('completed')
}
void redirectToNextStep()
}
}
}, 5000) // Check every 5 seconds
// Stop polling after 5 minutes
timeoutId = window.setTimeout(
() => {
clearPolling()
},
5 * 60 * 1000
)
})
onUnmounted(() => {
clearPolling()
})
</script>