Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions src/composables/auth/useCurrentUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,19 @@
return null
})

<<<<<<< HEAD

Check failure on line 37 in src/composables/auth/useCurrentUser.ts

View workflow job for this annotation

GitHub Actions / setup

Merge conflict marker encountered.

Check failure on line 37 in src/composables/auth/useCurrentUser.ts

View workflow job for this annotation

GitHub Actions / collect

Merge conflict marker encountered.
const onUserResolved = (callback: (user: AuthUserInfo) => void) =>
whenever(resolvedUserInfo, callback, { immediate: true })
=======

Check failure on line 40 in src/composables/auth/useCurrentUser.ts

View workflow job for this annotation

GitHub Actions / setup

Merge conflict marker encountered.

Check failure on line 40 in src/composables/auth/useCurrentUser.ts

View workflow job for this annotation

GitHub Actions / collect

Merge conflict marker encountered.
const onUserResolved = (callback: (user: AuthUserInfo) => void) => {
if (resolvedUserInfo.value) {
callback(resolvedUserInfo.value)
}

const stop = whenever(resolvedUserInfo, callback)
return () => stop()
}
>>>>>>> 775c856bf (port user ID expose hook from 6786d8e to cloud)

Check failure on line 49 in src/composables/auth/useCurrentUser.ts

View workflow job for this annotation

GitHub Actions / setup

Merge conflict marker encountered.

Check failure on line 49 in src/composables/auth/useCurrentUser.ts

View workflow job for this annotation

GitHub Actions / collect

Merge conflict marker encountered.
Comment on lines +40 to +49
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do note use this. take HEAD


const userDisplayName = computed(() => {
if (isApiKeyLogin.value) {
Expand Down Expand Up @@ -132,7 +143,10 @@
resolvedUserInfo,
handleSignOut,
handleSignIn,
<<<<<<< HEAD

Check failure on line 146 in src/composables/auth/useCurrentUser.ts

View workflow job for this annotation

GitHub Actions / setup

Merge conflict marker encountered.

Check failure on line 146 in src/composables/auth/useCurrentUser.ts

View workflow job for this annotation

GitHub Actions / collect

Merge conflict marker encountered.
handleDeleteAccount,
=======

Check failure on line 148 in src/composables/auth/useCurrentUser.ts

View workflow job for this annotation

GitHub Actions / setup

Merge conflict marker encountered.

Check failure on line 148 in src/composables/auth/useCurrentUser.ts

View workflow job for this annotation

GitHub Actions / collect

Merge conflict marker encountered.
>>>>>>> 775c856bf (port user ID expose hook from 6786d8e to cloud)

Check failure on line 149 in src/composables/auth/useCurrentUser.ts

View workflow job for this annotation

GitHub Actions / setup

Merge conflict marker encountered.

Check failure on line 149 in src/composables/auth/useCurrentUser.ts

View workflow job for this annotation

GitHub Actions / collect

Merge conflict marker encountered.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Take deletion

onUserResolved
}
}
8 changes: 8 additions & 0 deletions src/composables/auth/useFirebaseAuthActions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { FirebaseError } from 'firebase/app'
import { AuthErrorCodes } from 'firebase/auth'
import { ref } from 'vue'
import { useRouter } from 'vue-router'

import { useErrorHandling } from '@/composables/useErrorHandling'
import type { ErrorRecoveryStrategy } from '@/composables/useErrorHandling'
Expand All @@ -18,6 +19,7 @@ import { usdToMicros } from '@/utils/formatUtil'
export const useFirebaseAuthActions = () => {
const authStore = useFirebaseAuthStore()
const toastStore = useToastStore()
const router = useRouter()
const { wrapWithErrorHandlingAsync, toastErrorHandler } = useErrorHandling()

const accessError = ref(false)
Expand Down Expand Up @@ -54,6 +56,12 @@ export const useFirebaseAuthActions = () => {
detail: t('auth.signOut.successDetail'),
life: 5000
})

// Redirect to login page if we're on cloud domain
const hostname = window.location.hostname
if (hostname.includes('cloud.comfy.org')) {
Copy link
Contributor Author

@arjansingh arjansingh Oct 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

short term: if(isCloud)...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

longer term: /logout route that calls firebase and then redirects the users.

await router.push({ name: 'cloud-login' })
}
}, reportError)

const sendPasswordReset = wrapWithErrorHandlingAsync(
Expand Down
39 changes: 39 additions & 0 deletions src/locales/en/main.json
Original file line number Diff line number Diff line change
Expand Up @@ -2069,6 +2069,45 @@
"buttons": {
"Close": "Close"
}
<<<<<<< HEAD

Check failure on line 2072 in src/locales/en/main.json

View workflow job for this annotation

GitHub Actions / setup

Merge conflict marker encountered.

Check failure on line 2072 in src/locales/en/main.json

View workflow job for this annotation

GitHub Actions / collect

Merge conflict marker encountered.
=======

Check failure on line 2073 in src/locales/en/main.json

View workflow job for this annotation

GitHub Actions / setup

Merge conflict marker encountered.

Check failure on line 2073 in src/locales/en/main.json

View workflow job for this annotation

GitHub Actions / collect

Merge conflict marker encountered.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just resolve this to take the strings

},
"waitlist": {
"title": "Cloud Waitlist",
"message": "You have been added to the waitlist. We will notify you when access is available."
},
"forgotPassword": {
"title": "Forgot Password",
"instructions": "Enter your email address and we'll send you a link to reset your password.",
"emailLabel": "Email",
"emailPlaceholder": "Enter your email",
"sendResetLink": "Send reset link",
"backToLogin": "Back to login",
"didntReceiveEmail": "Didn't receive an email? Contact us at",
"passwordResetSent": "Password reset email sent",
"passwordResetError": "Failed to send password reset email. Please try again.",
"emailRequired": "Email is required"
},
"privateBeta": {
"title": "Cloud is currently in private beta",
"desc": "Sign in to join the waitlist. We’ll notify you when it’s your turn. Already been notified? Sign in start using Cloud."
},
"start": {
"title": "start creating in seconds",
"desc": "Zero setup required. Works on any device.",
"explain": "Generate multiple outputs at once. Share workflows with ease.",
"learnAboutButton": "Learn about Cloud",
"wantToRun": "Want to run comfyUI locally instead?",
"download": "Download ComfyUI"
},
"checkingStatus": "Checking your account status...",
"retrying": "Retrying...",
"retry": "Try Again",
"authTimeout": {
"title": "Connection Taking Too Long",
"message": "We're having trouble connecting to ComfyUI Cloud. This could be due to a slow connection or temporary service issue.",
"restart": "Sign Out & Try Again"
>>>>>>> 18b3b11b9 (feat: add error handling and timeout recovery to cloud onboarding (#5573))

Check failure on line 2110 in src/locales/en/main.json

View workflow job for this annotation

GitHub Actions / setup

Merge conflict marker encountered.

Check failure on line 2110 in src/locales/en/main.json

View workflow job for this annotation

GitHub Actions / collect

Merge conflict marker encountered.
}
}
}
82 changes: 82 additions & 0 deletions src/onboardingCloudRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type { RouteRecordRaw } from 'vue-router'

export const cloudOnboardingRoutes: RouteRecordRaw[] = [
{
path: '/cloud',
component: () =>
import('@/platform/onboarding/cloud/components/CloudLayoutView.vue'),
children: [
{
path: 'login',
name: 'cloud-login',
component: () =>
import('@/platform/onboarding/cloud/CloudLoginView.vue')
},
{
path: 'signup',
name: 'cloud-signup',
component: () =>
import('@/platform/onboarding/cloud/CloudSignupView.vue')
},
{
path: 'forgot-password',
name: 'cloud-forgot-password',
component: () =>
import('@/platform/onboarding/cloud/CloudForgotPasswordView.vue')
},
{
path: 'survey',
name: 'cloud-survey',
component: () =>
import('@/platform/onboarding/cloud/CloudSurveyView.vue'),
meta: { requiresAuth: true }
},
{
path: 'waitlist',
name: 'cloud-waitlist',
component: () =>
import('@/platform/onboarding/cloud/CloudWaitlistView.vue'),
meta: { requiresAuth: true }
},
{
path: 'user-check',
name: 'cloud-user-check',
component: () =>
import('@/platform/onboarding/cloud/UserCheckView.vue'),
meta: { requiresAuth: true }
},
{
path: 'invite-check',
name: 'cloud-invite-check',
component: () =>
import('@/platform/onboarding/cloud/InviteCheckView.vue'),
meta: { requiresAuth: true }
},
{
path: 'claim-invite',
name: 'cloud-claim-invite',
component: () =>
import('@/platform/onboarding/cloud/CloudClaimInviteView.vue'),
meta: { requiresAuth: true }
},
{
path: 'verify-email',
name: 'cloud-verify-email',
component: () =>
import('@/platform/onboarding/cloud/CloudVerifyEmailView.vue')
},
{
path: 'sorry-contact-support',
name: 'cloud-sorry-contact-support',
component: () =>
import('@/platform/onboarding/cloud/CloudSorryContactSupportView.vue')
},
{
path: 'auth-timeout',
name: 'cloud-auth-timeout',
component: () =>
import('@/platform/onboarding/cloud/CloudAuthTimeoutView.vue')
}
]
}
]
35 changes: 35 additions & 0 deletions src/platform/onboarding/cloud/CloudAuthTimeoutView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<template>
<div class="h-full flex items-center justify-center p-8">
<div class="w-96 text-center">
<h2 class="text-xl mb-4">
{{ $t('cloudOnboarding.authTimeout.title') }}
</h2>
<p class="mb-6 text-gray-600">
{{ $t('cloudOnboarding.authTimeout.message') }}
</p>

<div class="flex flex-col gap-3">
<Button
:label="$t('cloudOnboarding.authTimeout.restart')"
class="w-full"
@click="handleRestart"
/>
</div>
</div>
</div>
</template>

<script setup lang="ts">
import Button from 'primevue/button'
import { useRouter } from 'vue-router'

import { useFirebaseAuthActions } from '@/composables/auth/useFirebaseAuthActions'

const router = useRouter()
const { logout } = useFirebaseAuthActions()

const handleRestart = async () => {
await logout()
await router.replace({ name: 'cloud-login' })
}
</script>
104 changes: 104 additions & 0 deletions src/platform/onboarding/cloud/UserCheckView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<template>
<CloudLoginViewSkeleton v-if="skeletonType === 'login'" />
<CloudSurveyViewSkeleton v-else-if="skeletonType === 'survey'" />
<CloudWaitlistViewSkeleton v-else-if="skeletonType === 'waitlist'" />
<div v-else-if="error" class="h-full flex items-center justify-center p-8">
<div class="w-96 p-2 text-center">
<p class="text-red-500 mb-4">{{ errorMessage }}</p>
<Button
:label="
isRetrying
? $t('cloudOnboarding.retrying')
: $t('cloudOnboarding.retry')
"
:loading="isRetrying"
class="w-full"
@click="handleRetry"
/>
</div>
</div>
<div v-else class="flex items-center justify-center min-h-screen">
<div class="animate-pulse text-gray-500">{{ $t('g.loading') }}</div>
</div>
</template>

<script setup lang="ts">
import { useAsyncState } from '@vueuse/core'
import Button from 'primevue/button'
import { computed, nextTick, ref } from 'vue'
import { useRouter } from 'vue-router'

import { getSurveyCompletedStatus, getUserCloudStatus } from '@/api/auth'
import { useErrorHandling } from '@/composables/useErrorHandling'

import CloudLoginViewSkeleton from './skeletons/CloudLoginViewSkeleton.vue'
import CloudSurveyViewSkeleton from './skeletons/CloudSurveyViewSkeleton.vue'
import CloudWaitlistViewSkeleton from './skeletons/CloudWaitlistViewSkeleton.vue'

const router = useRouter()
const { wrapWithErrorHandlingAsync } = useErrorHandling()

const skeletonType = ref<'login' | 'survey' | 'waitlist' | 'loading'>('loading')

const {
isLoading,
error,
execute: checkUserStatus
} = useAsyncState(
wrapWithErrorHandlingAsync(async () => {
await nextTick()

const [cloudUserStats, surveyStatus] = await Promise.all([
getUserCloudStatus(),
getSurveyCompletedStatus()
])

// Navigate based on user status
if (!cloudUserStats) {
skeletonType.value = 'login'
await router.replace({ name: 'cloud-login' })
return
}

if (!surveyStatus) {
skeletonType.value = 'survey'
await router.replace({ name: 'cloud-survey' })
return
}

if (cloudUserStats.status !== 'active') {
skeletonType.value = 'waitlist'
await router.replace({ name: 'cloud-waitlist' })
return
}

// User is fully onboarded
window.location.href = '/'
}),
null,
{ resetOnExecute: false }
)

const errorMessage = computed(() => {
if (!error.value) return ''

// Provide user-friendly error messages
const errorStr = error.value.toString().toLowerCase()

if (errorStr.includes('network') || errorStr.includes('fetch')) {
return 'Connection problem. Please check your internet connection.'
}

if (errorStr.includes('timeout')) {
return 'Request timed out. Please try again.'
}

return 'Unable to check account status. Please try again.'
})

const isRetrying = computed(() => isLoading.value && !!error.value)

const handleRetry = async () => {
await checkUserStatus()
}
</script>
Loading
Loading