11import React , { useCallback , useEffect , useLayoutEffect , useState } from 'react' ;
2- import { ActivityIndicator , Alert , Pressable , View } from 'react-native' ;
2+ import { ActivityIndicator , Alert , View } from 'react-native' ;
33import AsyncStorage from '@react-native-async-storage/async-storage' ;
44
55import { useNavigation } from 'expo-router' ;
66
7- import { fetchDailyFortune } from '../src/services/geminiService ' ;
8- import { FortuneData , UserSettings } from '@/types' ;
7+ import { useFortune } from '@/hooks/useFortune ' ;
8+ import { UserSettings } from '@/types' ;
99import Onboarding from '@/components/Onboarding' ;
1010import UserInfoForm from '@/components/UserInfoForm' ;
1111import CalendarPage from '@/components/CalendarPage' ;
1212import SettingsSheet from '@/components/SettingsSheet' ;
13- import { Feather } from '@expo/vector-icons' ;
13+ import LoginScreen from '@/components/LoginScreen' ;
14+ import { useAuth } from '@/providers/AuthProvider' ;
15+ import { ProfileApiError , updateUserProfile } from '@/services/userProfileService' ;
1416
1517const HAS_ONBOARDED_KEY = 'hasOnboarded' ;
1618const USER_SETTINGS_KEY = 'userSettings' ;
@@ -22,9 +24,16 @@ export default function Home() {
2224 const [ currentDate , setCurrentDate ] = useState ( new Date ( ) ) ;
2325 const [ hasOnboarded , setHasOnboarded ] = useState ( false ) ;
2426 const [ userSettings , setUserSettings ] = useState < UserSettings | null > ( null ) ;
25- const [ fortune , setFortune ] = useState < FortuneData | null > ( null ) ;
26- const [ loadingFortune , setLoadingFortune ] = useState ( false ) ;
2727 const [ isSettingsOpen , setIsSettingsOpen ] = useState ( false ) ;
28+ const [ needsProfileSetup , setNeedsProfileSetup ] = useState ( false ) ;
29+ const [ profileSaving , setProfileSaving ] = useState ( false ) ;
30+
31+ const { isBootstrapping : authBootstrapping , isSignedIn, signOut } = useAuth ( ) ;
32+
33+ const { fortune, loading : loadingFortune , error } = useFortune (
34+ currentDate ,
35+ hasOnboarded && ! ! userSettings && isSignedIn ,
36+ ) ;
2837
2938 // Load persisted state
3039 useEffect ( ( ) => {
@@ -62,33 +71,39 @@ export default function Home() {
6271 await AsyncStorage . setItem ( USER_SETTINGS_KEY , JSON . stringify ( settings ) ) ;
6372 } ;
6473
65- // Fortune fetch on date/settings change
66- useEffect ( ( ) => {
67- let isMounted = true ;
68-
69- const loadFortune = async ( ) => {
70- if ( ! hasOnboarded || ! userSettings ) return ;
71- setLoadingFortune ( true ) ;
72- setFortune ( null ) ;
73- try {
74- const data = await fetchDailyFortune ( currentDate , userSettings ) ;
75- if ( isMounted ) setFortune ( data ) ;
76- } catch ( error ) {
77- console . warn ( 'Failed to fetch fortune' , error ) ;
78- if ( isMounted ) {
79- Alert . alert ( '운세를 불러오지 못했어요' , '잠시 후 다시 시도해주세요.' ) ;
80- }
81- } finally {
82- if ( isMounted ) setLoadingFortune ( false ) ;
74+ const handleUserInfoSubmit = async ( settings : UserSettings ) => {
75+ if ( profileSaving ) return ;
76+ setProfileSaving ( true ) ;
77+ try {
78+ const updatedSettings = await updateUserProfile ( settings ) ;
79+ await persistUserSettings ( updatedSettings ) ;
80+ if ( ! hasOnboarded ) {
81+ await persistHasOnboarded ( ) ;
8382 }
84- } ;
85-
86- loadFortune ( ) ;
83+ setNeedsProfileSetup ( false ) ;
84+ } catch ( error ) {
85+ if ( error instanceof ProfileApiError && error . status === 401 ) {
86+ Alert . alert ( '로그인이 필요합니다' , '다시 로그인해주세요.' ) ;
87+ await signOut ( ) ;
88+ return ;
89+ }
90+ const message =
91+ error instanceof Error ? error . message : '프로필을 저장하지 못했어요.' ;
92+ Alert . alert ( '프로필 저장 실패' , message ) ;
93+ } finally {
94+ setProfileSaving ( false ) ;
95+ }
96+ } ;
8797
88- return ( ) => {
89- isMounted = false ;
90- } ;
91- } , [ currentDate , hasOnboarded , userSettings ] ) ;
98+ useEffect ( ( ) => {
99+ if ( ! error ) return ;
100+ if ( error . status === 401 ) {
101+ signOut ( ) ;
102+ Alert . alert ( '로그인이 필요합니다' , '다시 로그인해주세요.' ) ;
103+ return ;
104+ }
105+ Alert . alert ( '운세를 불러오지 못했어요' , error . message ) ;
106+ } , [ error , signOut ] ) ;
92107
93108 const handleNextDay = useCallback ( ( ) => {
94109 setCurrentDate ( ( prev ) => {
@@ -106,22 +121,30 @@ export default function Home() {
106121 } ) ;
107122 } , [ ] ) ;
108123
109- if ( bootLoading ) {
124+ if ( bootLoading || authBootstrapping ) {
110125 return (
111126 < View className = "flex-1 items-center justify-center bg-stone-200" >
112127 < ActivityIndicator size = "large" color = "#191F28" />
113128 </ View >
114129 ) ;
115130 }
116131
117- const showOnboarding = ! hasOnboarded ;
118- const showUserForm = hasOnboarded && ! userSettings ;
132+ if ( ! isSignedIn ) {
133+ return < LoginScreen onSignUpSuccess = { ( ) => setNeedsProfileSetup ( true ) } /> ;
134+ }
135+
136+ const showOnboarding = ! hasOnboarded && ! needsProfileSetup ;
137+ const showUserForm = needsProfileSetup || ( hasOnboarded && ! userSettings ) ;
119138
120139 return (
121140 < View className = "flex-1 bg-stone-200" >
122141 { showOnboarding && < Onboarding onComplete = { persistHasOnboarded } /> }
123142 { showUserForm && ! showOnboarding && (
124- < UserInfoForm initialValues = { userSettings || undefined } onSubmit = { persistUserSettings } />
143+ < UserInfoForm
144+ initialValues = { userSettings || undefined }
145+ onSubmit = { handleUserInfoSubmit }
146+ isSubmitting = { profileSaving }
147+ />
125148 ) }
126149
127150 { ! showOnboarding && ! showUserForm && (
0 commit comments