11import React , { useCallback , useEffect , useLayoutEffect , useState } from 'react' ;
2- import { ActivityIndicator , Alert , View } from 'react-native' ;
2+ import { ActivityIndicator , Alert , Platform , View } from 'react-native' ;
33import AsyncStorage from '@react-native-async-storage/async-storage' ;
44
55import { useNavigation } from 'expo-router' ;
@@ -13,27 +13,44 @@ import SettingsSheet from '@/components/SettingsSheet';
1313import LoginScreen from '@/components/LoginScreen' ;
1414import { useAuth } from '@/providers/AuthProvider' ;
1515import { registerForPushNotificationsAsync } from '@/services/pushNotifications' ;
16- import { ProfileApiError , updateUserProfile } from '@/services/userProfileService' ;
16+ import { updatePushToken } from '@/services/pushTokenService' ;
17+ import {
18+ fetchUserProfile ,
19+ ProfileApiError ,
20+ updateUserProfile ,
21+ } from '@/services/userProfileService' ;
1722
1823const HAS_ONBOARDED_KEY = 'hasOnboarded' ;
1924const USER_SETTINGS_KEY = 'userSettings' ;
25+ const HAS_LOGGED_IN_KEY = 'hasLoggedIn' ;
2026
2127export default function Home ( ) {
2228 const navigation = useNavigation ( ) ;
2329
2430 const [ bootLoading , setBootLoading ] = useState ( true ) ;
2531 const [ currentDate , setCurrentDate ] = useState ( new Date ( ) ) ;
2632 const [ hasOnboarded , setHasOnboarded ] = useState ( false ) ;
33+ const [ hasLoggedIn , setHasLoggedIn ] = useState ( false ) ;
2734 const [ userSettings , setUserSettings ] = useState < UserSettings | null > ( null ) ;
2835 const [ isSettingsOpen , setIsSettingsOpen ] = useState ( false ) ;
2936 const [ needsProfileSetup , setNeedsProfileSetup ] = useState ( false ) ;
3037 const [ needsOnboarding , setNeedsOnboarding ] = useState ( false ) ;
3138 const [ profileSaving , setProfileSaving ] = useState ( false ) ;
39+ const [ profileLoading , setProfileLoading ] = useState ( false ) ;
40+ const [ forceLogin , setForceLogin ] = useState ( false ) ;
41+ const [ pushToken , setPushToken ] = useState < string | null > ( null ) ;
3242
3343 const { isBootstrapping : authBootstrapping , isSignedIn, signOut } = useAuth ( ) ;
3444
45+ const shouldShowLogin = forceLogin || ( ! isSignedIn && ! hasLoggedIn ) ;
46+
3547 const canLoadFortune =
36- isSignedIn && ! ! userSettings && ! needsProfileSetup && ( ! needsOnboarding || hasOnboarded ) ;
48+ ! shouldShowLogin &&
49+ ( isSignedIn || hasLoggedIn ) &&
50+ ! ! userSettings &&
51+ ! needsProfileSetup &&
52+ ! profileLoading &&
53+ ( ! needsOnboarding || hasOnboarded ) ;
3754
3855 const {
3956 fortune,
@@ -45,13 +62,15 @@ export default function Home() {
4562 useEffect ( ( ) => {
4663 const load = async ( ) => {
4764 try {
48- const [ onboardedRaw , settingsRaw ] = await Promise . all ( [
65+ const [ onboardedRaw , settingsRaw , loggedInRaw ] = await Promise . all ( [
4966 AsyncStorage . getItem ( HAS_ONBOARDED_KEY ) ,
5067 AsyncStorage . getItem ( USER_SETTINGS_KEY ) ,
68+ AsyncStorage . getItem ( HAS_LOGGED_IN_KEY ) ,
5169 ] ) ;
5270
5371 if ( onboardedRaw === 'true' ) setHasOnboarded ( true ) ;
5472 if ( settingsRaw ) setUserSettings ( JSON . parse ( settingsRaw ) ) ;
73+ if ( loggedInRaw === 'true' ) setHasLoggedIn ( true ) ;
5574 } catch ( error ) {
5675 console . warn ( 'Failed to load saved state' , error ) ;
5776 } finally {
@@ -63,27 +82,101 @@ export default function Home() {
6382 } , [ ] ) ;
6483
6584 useEffect ( ( ) => {
85+ let active = true ;
86+
6687 const registerPushToken = async ( ) => {
6788 try {
6889 const token = await registerForPushNotificationsAsync ( ) ;
6990 if ( token ) {
7091 console . log ( 'Expo push token:' , token ) ;
92+ if ( active ) {
93+ setPushToken ( token ) ;
94+ }
7195 }
7296 } catch ( error ) {
7397 console . warn ( 'Failed to register for push notifications' , error ) ;
7498 }
7599 } ;
76100
77101 registerPushToken ( ) ;
102+
103+ return ( ) => {
104+ active = false ;
105+ } ;
78106 } , [ ] ) ;
79107
108+ useEffect ( ( ) => {
109+ if ( ! isSignedIn || ! pushToken ) return ;
110+
111+ const platform = Platform . OS === 'ios' || Platform . OS === 'android' ? Platform . OS : null ;
112+ if ( ! platform ) return ;
113+
114+ const syncPushToken = async ( ) => {
115+ try {
116+ const result = await updatePushToken ( { pushToken, platform } ) ;
117+ console . log ( 'Push token synced:' , result ) ;
118+ } catch ( error ) {
119+ console . warn ( 'Failed to sync push token' , error ) ;
120+ }
121+ } ;
122+
123+ syncPushToken ( ) ;
124+ } , [ isSignedIn , pushToken ] ) ;
125+
80126 useEffect ( ( ) => {
81127 if ( isSignedIn ) return ;
82128 setIsSettingsOpen ( false ) ;
83129 setNeedsProfileSetup ( false ) ;
84130 setNeedsOnboarding ( false ) ;
131+ setProfileLoading ( false ) ;
85132 } , [ isSignedIn ] ) ;
86133
134+ const requireLogin = useCallback ( ( ) => {
135+ setForceLogin ( true ) ;
136+ setIsSettingsOpen ( false ) ;
137+ void signOut ( ) ;
138+ } , [ signOut ] ) ;
139+
140+ useEffect ( ( ) => {
141+ if ( ! isSignedIn ) return ;
142+
143+ let active = true ;
144+ setProfileLoading ( true ) ;
145+
146+ const loadProfile = async ( ) => {
147+ try {
148+ const profile = await fetchUserProfile ( ) ;
149+ if ( ! active ) return ;
150+ if ( profile ) {
151+ setNeedsProfileSetup ( false ) ;
152+ setUserSettings ( profile ) ;
153+ await AsyncStorage . setItem ( USER_SETTINGS_KEY , JSON . stringify ( profile ) ) ;
154+ } else {
155+ setNeedsProfileSetup ( true ) ;
156+ setUserSettings ( null ) ;
157+ await AsyncStorage . removeItem ( USER_SETTINGS_KEY ) ;
158+ }
159+ } catch ( error ) {
160+ if ( ! active ) return ;
161+ if ( error instanceof ProfileApiError && error . status === 401 ) {
162+ Alert . alert ( '로그인이 필요합니다' , '다시 로그인해주세요.' ) ;
163+ requireLogin ( ) ;
164+ return ;
165+ }
166+ const message = error instanceof Error ? error . message : '프로필을 불러오지 못했어요.' ;
167+ Alert . alert ( '프로필 조회 실패' , message ) ;
168+ } finally {
169+ if ( active ) setProfileLoading ( false ) ;
170+ }
171+ } ;
172+
173+ loadProfile ( ) ;
174+
175+ return ( ) => {
176+ active = false ;
177+ } ;
178+ } , [ isSignedIn , requireLogin ] ) ;
179+
87180 // Header: show only when main screen is active
88181 useLayoutEffect ( ( ) => {
89182 navigation . setOptions ( { headerShown : false } ) ;
@@ -94,19 +187,29 @@ export default function Home() {
94187 await AsyncStorage . setItem ( HAS_ONBOARDED_KEY , 'true' ) ;
95188 } ;
96189
190+ const persistHasLoggedIn = useCallback ( async ( ) => {
191+ setHasLoggedIn ( true ) ;
192+ await AsyncStorage . setItem ( HAS_LOGGED_IN_KEY , 'true' ) ;
193+ } , [ ] ) ;
194+
97195 const persistUserSettings = async ( settings : UserSettings ) => {
98196 setUserSettings ( settings ) ;
99197 await AsyncStorage . setItem ( USER_SETTINGS_KEY , JSON . stringify ( settings ) ) ;
100198 } ;
101199
102200 const handleSignUpSuccess = useCallback ( ( ) => {
201+ void persistHasLoggedIn ( ) ;
202+ setForceLogin ( false ) ;
103203 setNeedsProfileSetup ( true ) ;
104204 setNeedsOnboarding ( true ) ;
105- } , [ ] ) ;
205+ } , [ persistHasLoggedIn ] ) ;
106206
107207 const handleSignInSuccess = useCallback ( ( ) => {
108- setNeedsProfileSetup ( true ) ;
109- } , [ ] ) ;
208+ void persistHasLoggedIn ( ) ;
209+ setForceLogin ( false ) ;
210+ setNeedsProfileSetup ( false ) ;
211+ setProfileLoading ( true ) ;
212+ } , [ persistHasLoggedIn ] ) ;
110213
111214 const handleOnboardingComplete = ( ) => {
112215 setNeedsOnboarding ( false ) ;
@@ -123,7 +226,7 @@ export default function Home() {
123226 } catch ( error ) {
124227 if ( error instanceof ProfileApiError && error . status === 401 ) {
125228 Alert . alert ( '로그인이 필요합니다' , '다시 로그인해주세요.' ) ;
126- await signOut ( ) ;
229+ requireLogin ( ) ;
127230 return ;
128231 }
129232 const message = error instanceof Error ? error . message : '프로필을 저장하지 못했어요.' ;
@@ -136,12 +239,12 @@ export default function Home() {
136239 useEffect ( ( ) => {
137240 if ( ! error ) return ;
138241 if ( error . status === 401 ) {
139- signOut ( ) ;
242+ requireLogin ( ) ;
140243 Alert . alert ( '로그인이 필요합니다' , '다시 로그인해주세요.' ) ;
141244 return ;
142245 }
143246 Alert . alert ( '운세를 불러오지 못했어요' , error . message ) ;
144- } , [ error , signOut ] ) ;
247+ } , [ error , requireLogin ] ) ;
145248
146249 const handleNextDay = useCallback ( ( ) => {
147250 setCurrentDate ( ( prev ) => {
@@ -159,15 +262,15 @@ export default function Home() {
159262 } ) ;
160263 } , [ ] ) ;
161264
162- if ( bootLoading || authBootstrapping ) {
265+ if ( bootLoading || authBootstrapping || ( profileLoading && ! forceLogin ) ) {
163266 return (
164267 < View className = "flex-1 items-center justify-center bg-stone-200" >
165268 < ActivityIndicator size = "large" color = "#191F28" />
166269 </ View >
167270 ) ;
168271 }
169272
170- if ( ! isSignedIn ) {
273+ if ( shouldShowLogin ) {
171274 return (
172275 < LoginScreen
173276 onSignUpSuccess = { handleSignUpSuccess }
@@ -216,6 +319,8 @@ export default function Home() {
216319 await persistUserSettings ( nextSettings ) ;
217320 setIsSettingsOpen ( false ) ;
218321 } }
322+ onLogout = { requireLogin }
323+ onUnauthorized = { requireLogin }
219324 />
220325 ) }
221326 </ View >
0 commit comments