mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-02-03 22:59:14 +00:00
## 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>
173 lines
4.3 KiB
Vue
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>
|