From c24544a1b70f2b6ec135e7d4ecf9deeeea2454f6 Mon Sep 17 00:00:00 2001 From: Timo Knuth Date: Mon, 6 Jul 2026 13:50:22 +0200 Subject: [PATCH] feat(app): buffer onboarding answers before auth, flush to profile after sign-up/login Co-Authored-By: Claude Fable 5 --- app/auth/login.tsx | 7 ++++++ app/auth/signup.tsx | 7 ++++++ services/preAuthOnboardingService.ts | 36 ++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100644 services/preAuthOnboardingService.ts diff --git a/app/auth/login.tsx b/app/auth/login.tsx index 22e2fbe..9d47ffa 100644 --- a/app/auth/login.tsx +++ b/app/auth/login.tsx @@ -20,6 +20,7 @@ import { AuthService } from '../../services/authService'; import * as AppleAuthentication from 'expo-apple-authentication'; import Constants from 'expo-constants'; import { useSafeAnalytics } from '../../services/analytics'; +import { PreAuthOnboardingService } from '../../services/preAuthOnboardingService'; const ONBOARDING_AUTH_BACKGROUND = { light: '#fbfaf3', @@ -70,6 +71,9 @@ export default function LoginScreen() { try { const session = await AuthService.login(email, password); const billing = await hydrateSession(session); + if (session?.userId) { + await PreAuthOnboardingService.flushToProfile(session.userId).catch(() => {}); + } const isPro = billing?.entitlement?.plan === 'pro' && billing?.entitlement?.status === 'active'; // Non-pro accounts land on the paywall with context instead of being // bounced through the root redirect (which looks like an app restart). @@ -118,6 +122,9 @@ export default function LoginScreen() { name: fullName || undefined, }); const billing = await hydrateSession(session); + if (session?.userId) { + await PreAuthOnboardingService.flushToProfile(session.userId).catch(() => {}); + } if (session.isNewUser) { await AsyncStorage.setItem('greenlens_show_tour', 'true'); } diff --git a/app/auth/signup.tsx b/app/auth/signup.tsx index 1c5d225..4ff41fb 100644 --- a/app/auth/signup.tsx +++ b/app/auth/signup.tsx @@ -20,6 +20,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage'; import * as AppleAuthentication from 'expo-apple-authentication'; import Constants from 'expo-constants'; import { useSafeAnalytics } from '../../services/analytics'; +import { PreAuthOnboardingService } from '../../services/preAuthOnboardingService'; const ONBOARDING_AUTH_BACKGROUND = { light: '#fbfaf3', @@ -83,6 +84,9 @@ export default function SignupScreen() { try { const session = await AuthService.signUp(email, name, password); await hydrateSession(session); + if (session?.userId) { + await PreAuthOnboardingService.flushToProfile(session.userId).catch(() => {}); + } // Flag setzen: Tour beim nächsten App-Öffnen anzeigen await AsyncStorage.setItem('greenlens_show_tour', 'true'); router.replace('/onboarding/source'); @@ -132,6 +136,9 @@ export default function SignupScreen() { name: fullName || undefined, }); const billing = await hydrateSession(session); + if (session?.userId) { + await PreAuthOnboardingService.flushToProfile(session.userId).catch(() => {}); + } await AsyncStorage.setItem('greenlens_show_tour', 'true'); posthog.capture('apple_login_succeeded', { surface: 'signup' }); const isPro = billing?.entitlement?.plan === 'pro' && billing?.entitlement?.status === 'active'; diff --git a/services/preAuthOnboardingService.ts b/services/preAuthOnboardingService.ts new file mode 100644 index 0000000..c1b4f5d --- /dev/null +++ b/services/preAuthOnboardingService.ts @@ -0,0 +1,36 @@ +import AsyncStorage from '@react-native-async-storage/async-storage'; +import { OnboardingProgressService } from './onboardingProgressService'; + +const STORAGE_KEY = 'greenlens_preauth_onboarding_v1'; + +export type PreAuthAnswers = { + acquisitionSource?: string; + primaryGoal?: string; + experienceLevel?: string; +}; + +export const PreAuthOnboardingService = { + async setAnswer(key: K, value: PreAuthAnswers[K]): Promise { + const answers = await this.getAnswers(); + answers[key] = value; + await AsyncStorage.setItem(STORAGE_KEY, JSON.stringify(answers)); + }, + + async getAnswers(): Promise { + try { + const raw = await AsyncStorage.getItem(STORAGE_KEY); + return raw ? (JSON.parse(raw) as PreAuthAnswers) : {}; + } catch { + return {}; + } + }, + + // Persist buffered answers into the per-user profile after sign-up/login. + async flushToProfile(userId: number): Promise { + const answers = await this.getAnswers(); + if (answers.acquisitionSource) OnboardingProgressService.setAcquisitionSource(userId, answers.acquisitionSource); + if (answers.primaryGoal) OnboardingProgressService.setPrimaryGoal(userId, answers.primaryGoal); + if (answers.experienceLevel) OnboardingProgressService.setExperienceLevel(userId, answers.experienceLevel); + await AsyncStorage.removeItem(STORAGE_KEY); + }, +};