@onairos/react-native 3.1.16 → 3.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/README.md +404 -0
  2. package/lib/commonjs/assets/images/Checkbox.svg +3 -3
  3. package/lib/commonjs/assets/images/EnochE.svg +19 -19
  4. package/lib/commonjs/assets/images/Personalityprofile.svg +3 -3
  5. package/lib/commonjs/assets/images/Personalitytraits.svg +3 -3
  6. package/lib/commonjs/assets/images/Userpreferences.svg +3 -3
  7. package/lib/commonjs/assets/images/arrow.svg +20 -20
  8. package/lib/commonjs/assets/images/basicproficon.svg +43 -43
  9. package/lib/commonjs/assets/images/basicprofile.svg +3 -3
  10. package/lib/commonjs/assets/images/checkmark.svg +4 -4
  11. package/lib/commonjs/assets/images/contentanalysis.svg +3 -3
  12. package/lib/commonjs/assets/images/contenticon.svg +23 -23
  13. package/lib/commonjs/assets/images/personalityicon.svg +18 -18
  14. package/lib/commonjs/assets/images/x-close.svg +3 -3
  15. package/lib/commonjs/components/ModalSheet.js +8 -2
  16. package/lib/commonjs/components/ModalSheet.js.map +1 -1
  17. package/lib/commonjs/components/OnairosButton.js +290 -0
  18. package/lib/commonjs/components/OnairosButton.js.map +1 -0
  19. package/lib/commonjs/components/OnairosSignInButton.js +32 -8
  20. package/lib/commonjs/components/OnairosSignInButton.js.map +1 -1
  21. package/lib/commonjs/components/UniversalOnboarding.js +4 -4
  22. package/lib/commonjs/components/WelcomeScreen.js +29 -13
  23. package/lib/commonjs/components/WelcomeScreen.js.map +1 -1
  24. package/lib/commonjs/config/api.js +2 -2
  25. package/lib/commonjs/hooks/useConnections.js +6 -6
  26. package/lib/commonjs/hooks/useUserConnections.js +10 -10
  27. package/lib/commonjs/index.js +13 -6
  28. package/lib/commonjs/index.js.map +1 -1
  29. package/lib/commonjs/services/apiClient.js +35 -35
  30. package/lib/commonjs/services/apiKeyService.js +99 -99
  31. package/lib/commonjs/services/authService.js +82 -82
  32. package/lib/commonjs/services/biometricPinService.js +10 -10
  33. package/lib/commonjs/services/connectedAccountsService.js +32 -32
  34. package/lib/commonjs/services/googleAuthService.js +15 -15
  35. package/lib/commonjs/services/imageCompressionService.js +15 -15
  36. package/lib/commonjs/services/jwtStorageService.js +59 -59
  37. package/lib/commonjs/services/mobileTrainingService.js +14 -14
  38. package/lib/commonjs/services/pinEncryptionService.js +10 -10
  39. package/lib/commonjs/services/pinStorageUtils.js +15 -15
  40. package/lib/commonjs/services/platformAuthService.js +47 -47
  41. package/lib/commonjs/services/storageService.js +31 -31
  42. package/lib/commonjs/services/trainingApiHelpers.js +33 -33
  43. package/lib/commonjs/services/userConnectionsService.js +24 -24
  44. package/lib/commonjs/utils/Portal.js +4 -4
  45. package/lib/commonjs/utils/api.js +24 -24
  46. package/lib/commonjs/utils/auth.js +18 -18
  47. package/lib/commonjs/utils/crypto.js +13 -13
  48. package/lib/commonjs/utils/encryption.js +12 -12
  49. package/lib/commonjs/utils/eventUtils.js +52 -52
  50. package/lib/commonjs/utils/programmaticFlow.js +16 -16
  51. package/lib/commonjs/utils/retryHelper.js +27 -27
  52. package/lib/module/assets/images/Checkbox.svg +3 -3
  53. package/lib/module/assets/images/EnochE.svg +19 -19
  54. package/lib/module/assets/images/Personalityprofile.svg +3 -3
  55. package/lib/module/assets/images/Personalitytraits.svg +3 -3
  56. package/lib/module/assets/images/Userpreferences.svg +3 -3
  57. package/lib/module/assets/images/arrow.svg +20 -20
  58. package/lib/module/assets/images/basicproficon.svg +43 -43
  59. package/lib/module/assets/images/basicprofile.svg +3 -3
  60. package/lib/module/assets/images/checkmark.svg +4 -4
  61. package/lib/module/assets/images/contentanalysis.svg +3 -3
  62. package/lib/module/assets/images/contenticon.svg +23 -23
  63. package/lib/module/assets/images/personalityicon.svg +18 -18
  64. package/lib/module/assets/images/x-close.svg +3 -3
  65. package/lib/module/components/ModalSheet.js +7 -2
  66. package/lib/module/components/ModalSheet.js.map +1 -1
  67. package/lib/module/components/OnairosButton.js +282 -0
  68. package/lib/module/components/OnairosButton.js.map +1 -0
  69. package/lib/module/components/OnairosSignInButton.js +32 -8
  70. package/lib/module/components/OnairosSignInButton.js.map +1 -1
  71. package/lib/module/components/UniversalOnboarding.js +4 -4
  72. package/lib/module/components/WelcomeScreen.js +25 -10
  73. package/lib/module/components/WelcomeScreen.js.map +1 -1
  74. package/lib/module/config/api.js +2 -2
  75. package/lib/module/hooks/useConnections.js +6 -6
  76. package/lib/module/hooks/useUserConnections.js +10 -10
  77. package/lib/module/index.js +11 -11
  78. package/lib/module/index.js.map +1 -1
  79. package/lib/module/services/apiClient.js +35 -35
  80. package/lib/module/services/apiKeyService.js +99 -99
  81. package/lib/module/services/authService.js +82 -82
  82. package/lib/module/services/biometricPinService.js +10 -10
  83. package/lib/module/services/connectedAccountsService.js +32 -32
  84. package/lib/module/services/googleAuthService.js +15 -15
  85. package/lib/module/services/imageCompressionService.js +15 -15
  86. package/lib/module/services/jwtStorageService.js +59 -59
  87. package/lib/module/services/mobileTrainingService.js +14 -14
  88. package/lib/module/services/pinEncryptionService.js +10 -10
  89. package/lib/module/services/pinStorageUtils.js +15 -15
  90. package/lib/module/services/platformAuthService.js +47 -47
  91. package/lib/module/services/storageService.js +31 -31
  92. package/lib/module/services/trainingApiHelpers.js +33 -33
  93. package/lib/module/services/userConnectionsService.js +24 -24
  94. package/lib/module/utils/Portal.js +4 -4
  95. package/lib/module/utils/api.js +24 -24
  96. package/lib/module/utils/auth.js +18 -18
  97. package/lib/module/utils/crypto.js +13 -13
  98. package/lib/module/utils/encryption.js +12 -12
  99. package/lib/module/utils/eventUtils.js +52 -52
  100. package/lib/module/utils/programmaticFlow.js +16 -16
  101. package/lib/module/utils/retryHelper.js +27 -27
  102. package/lib/typescript/components/ModalSheet.d.ts.map +1 -1
  103. package/lib/typescript/components/OnairosButton.d.ts +37 -0
  104. package/lib/typescript/components/OnairosButton.d.ts.map +1 -0
  105. package/lib/typescript/components/OnairosSignInButton.d.ts.map +1 -1
  106. package/lib/typescript/components/WelcomeScreen.d.ts.map +1 -1
  107. package/lib/typescript/index.d.ts +3 -1
  108. package/lib/typescript/index.d.ts.map +1 -1
  109. package/package.json +145 -163
  110. package/src/api/index.ts +151 -151
  111. package/src/assets/images/Checkbox.svg +3 -3
  112. package/src/assets/images/EnochE.svg +19 -19
  113. package/src/assets/images/Personalityprofile.svg +3 -3
  114. package/src/assets/images/Personalitytraits.svg +3 -3
  115. package/src/assets/images/Userpreferences.svg +3 -3
  116. package/src/assets/images/arrow.svg +20 -20
  117. package/src/assets/images/basicproficon.svg +43 -43
  118. package/src/assets/images/basicprofile.svg +3 -3
  119. package/src/assets/images/checkmark.svg +4 -4
  120. package/src/assets/images/contentanalysis.svg +3 -3
  121. package/src/assets/images/contenticon.svg +23 -23
  122. package/src/assets/images/personalityicon.svg +18 -18
  123. package/src/assets/images/x-close.svg +3 -3
  124. package/src/components/BodyText.tsx +33 -33
  125. package/src/components/BrandMark.tsx +62 -62
  126. package/src/components/CodeInput.tsx +32 -32
  127. package/src/components/DataRequestScreen.tsx +355 -355
  128. package/src/components/EmailInput.tsx +31 -31
  129. package/src/components/EmailVerificationModal.tsx +363 -363
  130. package/src/components/ExistingUserDataConfirmation.tsx +506 -506
  131. package/src/components/GoogleButton.tsx +55 -55
  132. package/src/components/HeadingGroup.tsx +49 -49
  133. package/src/components/ModalHeader.tsx +125 -125
  134. package/src/components/ModalSheet.tsx +59 -57
  135. package/src/components/Onairos.tsx +422 -422
  136. package/src/components/OnairosButton.tsx +339 -0
  137. package/src/components/OnairosSignInButton.tsx +31 -9
  138. package/src/components/Overlay.tsx +506 -506
  139. package/src/components/PersonaImage.tsx +79 -79
  140. package/src/components/PersonaLoadingScreen.tsx +201 -201
  141. package/src/components/PersonalizationConsentScreen.tsx +410 -410
  142. package/src/components/PinCreationScreen.tsx +492 -492
  143. package/src/components/PinInput.tsx +555 -555
  144. package/src/components/PlatformConnectorsStep.tsx +891 -891
  145. package/src/components/PlatformList.tsx +144 -144
  146. package/src/components/PlatformToggle.tsx +226 -226
  147. package/src/components/PrimaryButton.tsx +213 -213
  148. package/src/components/SignInMatchAnimation.tsx +225 -225
  149. package/src/components/SignInStep.tsx +217 -217
  150. package/src/components/TrainingModal.tsx +1047 -1047
  151. package/src/components/UniversalOnboarding.tsx +2887 -2887
  152. package/src/components/VerificationStep.tsx +198 -198
  153. package/src/components/WelcomeScreen.tsx +490 -473
  154. package/src/components/icons/Basicproficon.tsx +30 -30
  155. package/src/components/icons/Basicprofile.tsx +17 -17
  156. package/src/components/icons/Checkbox.tsx +17 -17
  157. package/src/components/icons/Checkmark.tsx +24 -24
  158. package/src/components/icons/Contentanalysis.tsx +17 -17
  159. package/src/components/icons/Contenticon.tsx +30 -30
  160. package/src/components/icons/EnochE.tsx +39 -39
  161. package/src/components/icons/Personalityicon.tsx +22 -22
  162. package/src/components/icons/Personalityprofile.tsx +17 -17
  163. package/src/components/icons/Personalitytraits.tsx +17 -17
  164. package/src/components/icons/Userpreferences.tsx +17 -17
  165. package/src/components/icons/index.ts +12 -12
  166. package/src/components/onboarding/OAuthWebView.tsx +232 -232
  167. package/src/config/api.ts +25 -25
  168. package/src/context/AuthContext.tsx +393 -393
  169. package/src/hooks/useConnectedAccounts.ts +138 -138
  170. package/src/hooks/useConnections.ts +161 -161
  171. package/src/hooks/useCredentials.ts +174 -174
  172. package/src/hooks/useUserConnections.ts +165 -165
  173. package/src/index.js +14 -0
  174. package/src/index.ts +99 -96
  175. package/src/services/apiClient.ts +336 -336
  176. package/src/services/apiKeyService.ts +919 -919
  177. package/src/services/authService.ts +1008 -1008
  178. package/src/services/biometricPinService.ts +192 -192
  179. package/src/services/connectedAccountsService.ts +289 -289
  180. package/src/services/googleAuthService.ts +279 -279
  181. package/src/services/imageCompressionService.ts +302 -302
  182. package/src/services/jwtStorageService.ts +256 -256
  183. package/src/services/mobileTrainingService.ts +203 -203
  184. package/src/services/pinEncryptionService.ts +75 -75
  185. package/src/services/pinStorageUtils.ts +96 -96
  186. package/src/services/platformAuthService.ts +1346 -1346
  187. package/src/services/storageService.ts +451 -451
  188. package/src/services/trainingApiHelpers.ts +66 -66
  189. package/src/services/userConnectionsService.ts +556 -556
  190. package/src/services/youtubeMigrationService.ts +453 -453
  191. package/src/theme/index.ts +239 -239
  192. package/src/types/ambient.d.ts +28 -28
  193. package/src/types/index.ts +265 -265
  194. package/src/types/node-fix.d.ts +18 -18
  195. package/src/types/node-override.d.ts +23 -23
  196. package/src/types/opacity.d.ts +15 -15
  197. package/src/types/types.d.ts +17 -17
  198. package/src/utils/Portal.tsx +82 -82
  199. package/src/utils/api.js +111 -111
  200. package/src/utils/auth.js +103 -103
  201. package/src/utils/crypto.js +59 -59
  202. package/src/utils/encryption.ts +68 -68
  203. package/src/utils/eventUtils.ts +302 -302
  204. package/src/utils/haptics.ts +58 -58
  205. package/src/utils/imagePreloader.ts +2 -2
  206. package/src/utils/programmaticFlow.ts +112 -112
  207. package/src/utils/retryHelper.ts +274 -274
@@ -1,393 +1,393 @@
1
- import React, { createContext, useState, useEffect, useContext } from 'react';
2
- import AsyncStorage from '@react-native-async-storage/async-storage';
3
- import { User } from '../types/index';
4
- import {
5
- saveAuthToken,
6
- getAuthToken,
7
- removeAuthToken,
8
- verifyToken,
9
- getUserProfile,
10
- authenticateWithApple,
11
- authenticateWithOnairos,
12
- authenticateWithOnairosSignIn,
13
- updateUserOnboardedStatus
14
- } from '../services/authService';
15
- import {
16
- saveUserProgress,
17
- saveAuthState,
18
- getAuthState,
19
- clearUserData,
20
- getResumeTarget,
21
- markStepCompleted,
22
- updateLastScreen,
23
- isReturningUser,
24
- markEventPageReached,
25
- hasEventAccessToken,
26
- STORAGE_KEYS
27
- } from '../services/storageService';
28
-
29
- interface AuthContextType {
30
- user: User | null;
31
- isLoading: boolean;
32
- hasCompletedOnboarding: boolean;
33
- login: (userData: User) => void;
34
- logout: () => void;
35
- deleteAccount: () => Promise<void>;
36
- completeOnboarding: (userData: Partial<User>) => void;
37
- updateUser: (userData: Partial<User>) => void;
38
- appleSignIn: (appleAuthData: any) => Promise<User>;
39
- onairosSignIn: (onairosAuthData: { token: string; email: string }) => Promise<User>;
40
- isAuthenticated: boolean;
41
- updateOnboardedStatus: () => Promise<void>;
42
- // New methods for progress tracking
43
- getResumeTarget: () => Promise<{ screen: string; params?: any }>;
44
- markStepCompleted: (step: string) => Promise<void>;
45
- updateLastScreen: (screen: string, params?: any) => Promise<void>;
46
- isReturningUser: () => Promise<boolean>;
47
- // New methods for event access token
48
- markEventPageReached: (eventData?: any) => Promise<void>;
49
- hasEventAccessToken: () => Promise<boolean>;
50
- }
51
-
52
- const AuthContext = createContext<AuthContextType | undefined>(undefined);
53
-
54
- export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
55
- const [user, setUser] = useState<User | null>(null);
56
- const [isLoading, setIsLoading] = useState(true);
57
- const [hasCompletedOnboarding, setHasCompletedOnboarding] = useState(false);
58
- const [isAuthenticated, setIsAuthenticated] = useState(false);
59
-
60
- useEffect(() => {
61
- // Check if user is already logged in
62
- const loadStoredUser = async () => {
63
- try {
64
- // PRIORITY 1: Check if user has completed onboarding (for release mode reliability)
65
- const hasEventToken = await hasEventAccessToken();
66
- const storedUser = await AsyncStorage.getItem('user');
67
-
68
- if (hasEventToken && storedUser) {
69
- console.log('🎯 Found event access token - user has completed onboarding, authentication confirmed');
70
- setUser(JSON.parse(storedUser));
71
- setIsAuthenticated(true);
72
- setHasCompletedOnboarding(true);
73
- setIsLoading(false);
74
- return; // Skip network verification for completed users
75
- }
76
-
77
- // PRIORITY 2: Check if we have a JWT token for users still in onboarding
78
- const token = await getAuthToken();
79
-
80
- if (token) {
81
- // For users with tokens but no completion token, try network verification
82
- // but don't fail if network is unreachable (important for release mode)
83
- let isValid = false;
84
- try {
85
- isValid = await verifyToken();
86
- } catch (networkError) {
87
- console.warn('🌐 Network verification failed, using local token validation:', networkError);
88
- // In release mode, network might be slow/unreliable
89
- // If we have a token and stored user, trust the local state
90
- if (storedUser) {
91
- console.log('💾 Network unreachable, trusting local authentication state');
92
- isValid = true; // Trust local state when network fails
93
- }
94
- }
95
-
96
- if (isValid) {
97
- // Token is valid (or we're trusting local state), get user data
98
- if (storedUser) {
99
- setUser(JSON.parse(storedUser));
100
- setIsAuthenticated(true);
101
- } else {
102
- // If user data is not in AsyncStorage, try to fetch it
103
- try {
104
- const userData = await getUserProfile();
105
- await AsyncStorage.setItem('user', JSON.stringify(userData));
106
- setUser(userData);
107
- setIsAuthenticated(true);
108
- } catch (profileError) {
109
- console.error('Failed to fetch user profile:', profileError);
110
- // Only remove token if we're sure it's invalid (not just network issues)
111
- const errorMessage = profileError instanceof Error ? profileError.message : String(profileError);
112
- if (errorMessage.includes('401') || errorMessage.includes('403')) {
113
- await removeAuthToken();
114
- } else {
115
- console.log('🌐 Profile fetch failed (likely network), keeping authentication state');
116
- // Create fallback user for offline scenarios
117
- if (token) {
118
- const fallbackUser = {
119
- id: 'offline_user',
120
- name: 'Onairos User',
121
- email: 'user@onairos.com',
122
- onboarded: false
123
- };
124
- await AsyncStorage.setItem('user', JSON.stringify(fallbackUser));
125
- setUser(fallbackUser);
126
- setIsAuthenticated(true);
127
- }
128
- }
129
- }
130
- }
131
- } else {
132
- // Token verification definitely failed - remove it
133
- await removeAuthToken();
134
- }
135
- }
136
-
137
- const onboardingCompleted = await AsyncStorage.getItem('onboardingCompleted');
138
- if (onboardingCompleted === 'true') {
139
- setHasCompletedOnboarding(true);
140
- }
141
- } catch (error) {
142
- console.error('Failed to load authentication state:', error);
143
- } finally {
144
- setIsLoading(false);
145
- }
146
- };
147
-
148
- loadStoredUser();
149
- }, []);
150
-
151
- const login = async (userData: User) => {
152
- try {
153
- // Ensure onboarded is set to false for new users
154
- const userWithOnboarded = {
155
- ...userData,
156
- onboarded: false // Always force camera flow for new logins
157
- };
158
-
159
- await AsyncStorage.setItem('user', JSON.stringify(userWithOnboarded));
160
- setUser(userWithOnboarded);
161
- setIsAuthenticated(true);
162
-
163
- // Save auth state and mark authentication step as completed
164
- await saveAuthState({
165
- isAuthenticated: true,
166
- authMethod: userData.email?.includes('apple') ? 'apple' : 'onairos',
167
- hasValidToken: true,
168
- userEmail: userData.email,
169
- userName: userData.name
170
- });
171
-
172
- await markStepCompleted('authentication');
173
-
174
- console.log('User logged in with onboarded=false:', userWithOnboarded);
175
- } catch (error) {
176
- console.error('Failed to save user data:', error);
177
- }
178
- };
179
-
180
- // Handle Apple Sign-In
181
- const appleSignIn = async (appleAuthData: any): Promise<User> => {
182
- try {
183
- setIsLoading(true);
184
-
185
- // Call the API to authenticate with Apple
186
- const authResponse = await authenticateWithApple(appleAuthData);
187
-
188
- // Save the JWT token
189
- await saveAuthToken(authResponse.token);
190
-
191
- // CRITICAL: Save auth method as 'apple' for proper button logic
192
- await AsyncStorage.setItem('auth_method', 'apple');
193
-
194
- // Save Onairos connection status from backend response
195
- if (authResponse.onairos) {
196
- await AsyncStorage.setItem('hasConnectedOnairos', authResponse.onairos.hasConnectedOnairos.toString());
197
- if (authResponse.onairos.onairosUserId) {
198
- await AsyncStorage.setItem('onairosUserId', authResponse.onairos.onairosUserId);
199
- }
200
- if (authResponse.onairos.lastConnectedAt) {
201
- await AsyncStorage.setItem('lastConnectedAt', authResponse.onairos.lastConnectedAt);
202
- }
203
- console.log('🔗 Stored Onairos connection status:', authResponse.onairos.hasConnectedOnairos);
204
- } else {
205
- // Default to false if no Onairos info provided
206
- await AsyncStorage.setItem('hasConnectedOnairos', 'false');
207
- console.log('🔗 No Onairos connection info from backend, defaulting to false');
208
- }
209
-
210
- // Create user object from response
211
- const userData: User = {
212
- id: authResponse.user.id,
213
- name: authResponse.user.name || 'Onairos User',
214
- email: authResponse.user.email,
215
- profilePicture: authResponse.user.profilePicture,
216
- onboarded: false // Default to false for new Apple sign-ins
217
- };
218
-
219
- // Save user data to AsyncStorage and update state
220
- await AsyncStorage.setItem('user', JSON.stringify(userData));
221
- setUser(userData);
222
- setIsAuthenticated(true);
223
-
224
- console.log('🍎 Apple sign-in completed - auth_method set to "apple"');
225
-
226
- return userData;
227
- } catch (error) {
228
- console.error('Apple sign-in failed:', error);
229
- throw error;
230
- } finally {
231
- setIsLoading(false);
232
- }
233
- };
234
-
235
- // Handle Onairos Sign-In (similar to Apple)
236
- const onairosSignIn = async (onairosAuthData: { token: string; email: string }): Promise<User> => {
237
- try {
238
- setIsLoading(true);
239
-
240
- // Call the API to authenticate with Onairos
241
- const authResponse = await authenticateWithOnairos(onairosAuthData);
242
-
243
- // Save the JWT token
244
- await saveAuthToken(authResponse.token);
245
-
246
- // CRITICAL: Save auth method as 'onairos' for proper button logic
247
- await AsyncStorage.setItem('auth_method', 'onairos');
248
-
249
- // Create user object from response
250
- const userData: User = {
251
- id: authResponse.user.id,
252
- name: authResponse.user.name || 'Onairos User',
253
- email: authResponse.user.email,
254
- profilePicture: authResponse.user.profilePicture,
255
- onboarded: false // Default to false for new Onairos sign-ins
256
- };
257
-
258
- // Save user data to AsyncStorage and update state
259
- await AsyncStorage.setItem('user', JSON.stringify(userData));
260
- setUser(userData);
261
- setIsAuthenticated(true);
262
-
263
- console.log('🔑 Onairos sign-in completed - auth_method set to "onairos"');
264
-
265
- return userData;
266
- } catch (error) {
267
- console.error('Onairos sign-in failed:', error);
268
- throw error;
269
- } finally {
270
- setIsLoading(false);
271
- }
272
- };
273
-
274
- const logout = async () => {
275
- try {
276
- // Clear all user data using the new storage service
277
- await clearUserData();
278
- setUser(null);
279
- setIsAuthenticated(false);
280
- } catch (error) {
281
- console.error('Failed to remove user data:', error);
282
- }
283
- };
284
-
285
- const completeOnboarding = async (userData: Partial<User>) => {
286
- try {
287
- // Update user data
288
- const updatedUser = { ...user, ...userData };
289
- await AsyncStorage.setItem('user', JSON.stringify(updatedUser));
290
- setUser(updatedUser as User);
291
-
292
- // Update onboarded status on backend
293
- await updateOnboardedStatus();
294
- } catch (error) {
295
- console.error('Failed to complete onboarding:', error);
296
- throw error;
297
- }
298
- };
299
-
300
- const updateUser = async (userData: Partial<User>) => {
301
- try {
302
- if (!user) return;
303
- const updatedUser = { ...user, ...userData };
304
- await AsyncStorage.setItem('user', JSON.stringify(updatedUser));
305
- setUser(updatedUser);
306
- } catch (error) {
307
- console.error('Failed to update user data:', error);
308
- }
309
- };
310
-
311
- // Update user onboarded status
312
- const updateOnboardedStatus = async (): Promise<void> => {
313
- try {
314
- setIsLoading(true);
315
-
316
- // Call the API to update onboarded status
317
- await updateUserOnboardedStatus();
318
-
319
- // Update local user data
320
- if (user) {
321
- const updatedUser = { ...user, onboarded: true };
322
- await AsyncStorage.setItem('user', JSON.stringify(updatedUser));
323
- setUser(updatedUser);
324
- }
325
- } catch (error) {
326
- console.error('Failed to update onboarded status:', error);
327
- throw error;
328
- } finally {
329
- setIsLoading(false);
330
- }
331
- };
332
-
333
- const deleteAccount = async () => {
334
- try {
335
- // In a real app, you would make an API call to delete the user account on the server
336
- // For example: await api.deleteUser(user.id);
337
-
338
- // Remove user data from AsyncStorage
339
- await AsyncStorage.removeItem('user');
340
-
341
- // Remove JWT token
342
- await removeAuthToken();
343
-
344
- // Clear any other user-related data from AsyncStorage
345
- await AsyncStorage.removeItem('onboardingCompleted');
346
-
347
- // Update state
348
- setUser(null);
349
- setIsAuthenticated(false);
350
- setHasCompletedOnboarding(false);
351
-
352
- console.log('Account deleted successfully');
353
- } catch (error) {
354
- console.error('Failed to delete account:', error);
355
- throw error;
356
- }
357
- };
358
-
359
- return (
360
- <AuthContext.Provider value={{
361
- user,
362
- isLoading,
363
- hasCompletedOnboarding,
364
- login,
365
- logout,
366
- deleteAccount,
367
- completeOnboarding,
368
- updateUser,
369
- appleSignIn,
370
- onairosSignIn,
371
- isAuthenticated,
372
- updateOnboardedStatus,
373
- // Progress tracking methods
374
- getResumeTarget,
375
- markStepCompleted,
376
- updateLastScreen,
377
- isReturningUser,
378
- // Event access token methods
379
- markEventPageReached,
380
- hasEventAccessToken
381
- }}>
382
- {children}
383
- </AuthContext.Provider>
384
- );
385
- };
386
-
387
- export const useAuth = (): AuthContextType => {
388
- const context = useContext(AuthContext);
389
- if (context === undefined) {
390
- throw new Error('useAuth must be used within an AuthProvider');
391
- }
392
- return context;
393
- };
1
+ import React, { createContext, useState, useEffect, useContext } from 'react';
2
+ import AsyncStorage from '@react-native-async-storage/async-storage';
3
+ import { User } from '../types/index';
4
+ import {
5
+ saveAuthToken,
6
+ getAuthToken,
7
+ removeAuthToken,
8
+ verifyToken,
9
+ getUserProfile,
10
+ authenticateWithApple,
11
+ authenticateWithOnairos,
12
+ authenticateWithOnairosSignIn,
13
+ updateUserOnboardedStatus
14
+ } from '../services/authService';
15
+ import {
16
+ saveUserProgress,
17
+ saveAuthState,
18
+ getAuthState,
19
+ clearUserData,
20
+ getResumeTarget,
21
+ markStepCompleted,
22
+ updateLastScreen,
23
+ isReturningUser,
24
+ markEventPageReached,
25
+ hasEventAccessToken,
26
+ STORAGE_KEYS
27
+ } from '../services/storageService';
28
+
29
+ interface AuthContextType {
30
+ user: User | null;
31
+ isLoading: boolean;
32
+ hasCompletedOnboarding: boolean;
33
+ login: (userData: User) => void;
34
+ logout: () => void;
35
+ deleteAccount: () => Promise<void>;
36
+ completeOnboarding: (userData: Partial<User>) => void;
37
+ updateUser: (userData: Partial<User>) => void;
38
+ appleSignIn: (appleAuthData: any) => Promise<User>;
39
+ onairosSignIn: (onairosAuthData: { token: string; email: string }) => Promise<User>;
40
+ isAuthenticated: boolean;
41
+ updateOnboardedStatus: () => Promise<void>;
42
+ // New methods for progress tracking
43
+ getResumeTarget: () => Promise<{ screen: string; params?: any }>;
44
+ markStepCompleted: (step: string) => Promise<void>;
45
+ updateLastScreen: (screen: string, params?: any) => Promise<void>;
46
+ isReturningUser: () => Promise<boolean>;
47
+ // New methods for event access token
48
+ markEventPageReached: (eventData?: any) => Promise<void>;
49
+ hasEventAccessToken: () => Promise<boolean>;
50
+ }
51
+
52
+ const AuthContext = createContext<AuthContextType | undefined>(undefined);
53
+
54
+ export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
55
+ const [user, setUser] = useState<User | null>(null);
56
+ const [isLoading, setIsLoading] = useState(true);
57
+ const [hasCompletedOnboarding, setHasCompletedOnboarding] = useState(false);
58
+ const [isAuthenticated, setIsAuthenticated] = useState(false);
59
+
60
+ useEffect(() => {
61
+ // Check if user is already logged in
62
+ const loadStoredUser = async () => {
63
+ try {
64
+ // PRIORITY 1: Check if user has completed onboarding (for release mode reliability)
65
+ const hasEventToken = await hasEventAccessToken();
66
+ const storedUser = await AsyncStorage.getItem('user');
67
+
68
+ if (hasEventToken && storedUser) {
69
+ console.log('🎯 Found event access token - user has completed onboarding, authentication confirmed');
70
+ setUser(JSON.parse(storedUser));
71
+ setIsAuthenticated(true);
72
+ setHasCompletedOnboarding(true);
73
+ setIsLoading(false);
74
+ return; // Skip network verification for completed users
75
+ }
76
+
77
+ // PRIORITY 2: Check if we have a JWT token for users still in onboarding
78
+ const token = await getAuthToken();
79
+
80
+ if (token) {
81
+ // For users with tokens but no completion token, try network verification
82
+ // but don't fail if network is unreachable (important for release mode)
83
+ let isValid = false;
84
+ try {
85
+ isValid = await verifyToken();
86
+ } catch (networkError) {
87
+ console.warn('🌐 Network verification failed, using local token validation:', networkError);
88
+ // In release mode, network might be slow/unreliable
89
+ // If we have a token and stored user, trust the local state
90
+ if (storedUser) {
91
+ console.log('💾 Network unreachable, trusting local authentication state');
92
+ isValid = true; // Trust local state when network fails
93
+ }
94
+ }
95
+
96
+ if (isValid) {
97
+ // Token is valid (or we're trusting local state), get user data
98
+ if (storedUser) {
99
+ setUser(JSON.parse(storedUser));
100
+ setIsAuthenticated(true);
101
+ } else {
102
+ // If user data is not in AsyncStorage, try to fetch it
103
+ try {
104
+ const userData = await getUserProfile();
105
+ await AsyncStorage.setItem('user', JSON.stringify(userData));
106
+ setUser(userData);
107
+ setIsAuthenticated(true);
108
+ } catch (profileError) {
109
+ console.error('Failed to fetch user profile:', profileError);
110
+ // Only remove token if we're sure it's invalid (not just network issues)
111
+ const errorMessage = profileError instanceof Error ? profileError.message : String(profileError);
112
+ if (errorMessage.includes('401') || errorMessage.includes('403')) {
113
+ await removeAuthToken();
114
+ } else {
115
+ console.log('🌐 Profile fetch failed (likely network), keeping authentication state');
116
+ // Create fallback user for offline scenarios
117
+ if (token) {
118
+ const fallbackUser = {
119
+ id: 'offline_user',
120
+ name: 'Onairos User',
121
+ email: 'user@onairos.com',
122
+ onboarded: false
123
+ };
124
+ await AsyncStorage.setItem('user', JSON.stringify(fallbackUser));
125
+ setUser(fallbackUser);
126
+ setIsAuthenticated(true);
127
+ }
128
+ }
129
+ }
130
+ }
131
+ } else {
132
+ // Token verification definitely failed - remove it
133
+ await removeAuthToken();
134
+ }
135
+ }
136
+
137
+ const onboardingCompleted = await AsyncStorage.getItem('onboardingCompleted');
138
+ if (onboardingCompleted === 'true') {
139
+ setHasCompletedOnboarding(true);
140
+ }
141
+ } catch (error) {
142
+ console.error('Failed to load authentication state:', error);
143
+ } finally {
144
+ setIsLoading(false);
145
+ }
146
+ };
147
+
148
+ loadStoredUser();
149
+ }, []);
150
+
151
+ const login = async (userData: User) => {
152
+ try {
153
+ // Ensure onboarded is set to false for new users
154
+ const userWithOnboarded = {
155
+ ...userData,
156
+ onboarded: false // Always force camera flow for new logins
157
+ };
158
+
159
+ await AsyncStorage.setItem('user', JSON.stringify(userWithOnboarded));
160
+ setUser(userWithOnboarded);
161
+ setIsAuthenticated(true);
162
+
163
+ // Save auth state and mark authentication step as completed
164
+ await saveAuthState({
165
+ isAuthenticated: true,
166
+ authMethod: userData.email?.includes('apple') ? 'apple' : 'onairos',
167
+ hasValidToken: true,
168
+ userEmail: userData.email,
169
+ userName: userData.name
170
+ });
171
+
172
+ await markStepCompleted('authentication');
173
+
174
+ console.log('User logged in with onboarded=false:', userWithOnboarded);
175
+ } catch (error) {
176
+ console.error('Failed to save user data:', error);
177
+ }
178
+ };
179
+
180
+ // Handle Apple Sign-In
181
+ const appleSignIn = async (appleAuthData: any): Promise<User> => {
182
+ try {
183
+ setIsLoading(true);
184
+
185
+ // Call the API to authenticate with Apple
186
+ const authResponse = await authenticateWithApple(appleAuthData);
187
+
188
+ // Save the JWT token
189
+ await saveAuthToken(authResponse.token);
190
+
191
+ // CRITICAL: Save auth method as 'apple' for proper button logic
192
+ await AsyncStorage.setItem('auth_method', 'apple');
193
+
194
+ // Save Onairos connection status from backend response
195
+ if (authResponse.onairos) {
196
+ await AsyncStorage.setItem('hasConnectedOnairos', authResponse.onairos.hasConnectedOnairos.toString());
197
+ if (authResponse.onairos.onairosUserId) {
198
+ await AsyncStorage.setItem('onairosUserId', authResponse.onairos.onairosUserId);
199
+ }
200
+ if (authResponse.onairos.lastConnectedAt) {
201
+ await AsyncStorage.setItem('lastConnectedAt', authResponse.onairos.lastConnectedAt);
202
+ }
203
+ console.log('🔗 Stored Onairos connection status:', authResponse.onairos.hasConnectedOnairos);
204
+ } else {
205
+ // Default to false if no Onairos info provided
206
+ await AsyncStorage.setItem('hasConnectedOnairos', 'false');
207
+ console.log('🔗 No Onairos connection info from backend, defaulting to false');
208
+ }
209
+
210
+ // Create user object from response
211
+ const userData: User = {
212
+ id: authResponse.user.id,
213
+ name: authResponse.user.name || 'Onairos User',
214
+ email: authResponse.user.email,
215
+ profilePicture: authResponse.user.profilePicture,
216
+ onboarded: false // Default to false for new Apple sign-ins
217
+ };
218
+
219
+ // Save user data to AsyncStorage and update state
220
+ await AsyncStorage.setItem('user', JSON.stringify(userData));
221
+ setUser(userData);
222
+ setIsAuthenticated(true);
223
+
224
+ console.log('🍎 Apple sign-in completed - auth_method set to "apple"');
225
+
226
+ return userData;
227
+ } catch (error) {
228
+ console.error('Apple sign-in failed:', error);
229
+ throw error;
230
+ } finally {
231
+ setIsLoading(false);
232
+ }
233
+ };
234
+
235
+ // Handle Onairos Sign-In (similar to Apple)
236
+ const onairosSignIn = async (onairosAuthData: { token: string; email: string }): Promise<User> => {
237
+ try {
238
+ setIsLoading(true);
239
+
240
+ // Call the API to authenticate with Onairos
241
+ const authResponse = await authenticateWithOnairos(onairosAuthData);
242
+
243
+ // Save the JWT token
244
+ await saveAuthToken(authResponse.token);
245
+
246
+ // CRITICAL: Save auth method as 'onairos' for proper button logic
247
+ await AsyncStorage.setItem('auth_method', 'onairos');
248
+
249
+ // Create user object from response
250
+ const userData: User = {
251
+ id: authResponse.user.id,
252
+ name: authResponse.user.name || 'Onairos User',
253
+ email: authResponse.user.email,
254
+ profilePicture: authResponse.user.profilePicture,
255
+ onboarded: false // Default to false for new Onairos sign-ins
256
+ };
257
+
258
+ // Save user data to AsyncStorage and update state
259
+ await AsyncStorage.setItem('user', JSON.stringify(userData));
260
+ setUser(userData);
261
+ setIsAuthenticated(true);
262
+
263
+ console.log('🔑 Onairos sign-in completed - auth_method set to "onairos"');
264
+
265
+ return userData;
266
+ } catch (error) {
267
+ console.error('Onairos sign-in failed:', error);
268
+ throw error;
269
+ } finally {
270
+ setIsLoading(false);
271
+ }
272
+ };
273
+
274
+ const logout = async () => {
275
+ try {
276
+ // Clear all user data using the new storage service
277
+ await clearUserData();
278
+ setUser(null);
279
+ setIsAuthenticated(false);
280
+ } catch (error) {
281
+ console.error('Failed to remove user data:', error);
282
+ }
283
+ };
284
+
285
+ const completeOnboarding = async (userData: Partial<User>) => {
286
+ try {
287
+ // Update user data
288
+ const updatedUser = { ...user, ...userData };
289
+ await AsyncStorage.setItem('user', JSON.stringify(updatedUser));
290
+ setUser(updatedUser as User);
291
+
292
+ // Update onboarded status on backend
293
+ await updateOnboardedStatus();
294
+ } catch (error) {
295
+ console.error('Failed to complete onboarding:', error);
296
+ throw error;
297
+ }
298
+ };
299
+
300
+ const updateUser = async (userData: Partial<User>) => {
301
+ try {
302
+ if (!user) return;
303
+ const updatedUser = { ...user, ...userData };
304
+ await AsyncStorage.setItem('user', JSON.stringify(updatedUser));
305
+ setUser(updatedUser);
306
+ } catch (error) {
307
+ console.error('Failed to update user data:', error);
308
+ }
309
+ };
310
+
311
+ // Update user onboarded status
312
+ const updateOnboardedStatus = async (): Promise<void> => {
313
+ try {
314
+ setIsLoading(true);
315
+
316
+ // Call the API to update onboarded status
317
+ await updateUserOnboardedStatus();
318
+
319
+ // Update local user data
320
+ if (user) {
321
+ const updatedUser = { ...user, onboarded: true };
322
+ await AsyncStorage.setItem('user', JSON.stringify(updatedUser));
323
+ setUser(updatedUser);
324
+ }
325
+ } catch (error) {
326
+ console.error('Failed to update onboarded status:', error);
327
+ throw error;
328
+ } finally {
329
+ setIsLoading(false);
330
+ }
331
+ };
332
+
333
+ const deleteAccount = async () => {
334
+ try {
335
+ // In a real app, you would make an API call to delete the user account on the server
336
+ // For example: await api.deleteUser(user.id);
337
+
338
+ // Remove user data from AsyncStorage
339
+ await AsyncStorage.removeItem('user');
340
+
341
+ // Remove JWT token
342
+ await removeAuthToken();
343
+
344
+ // Clear any other user-related data from AsyncStorage
345
+ await AsyncStorage.removeItem('onboardingCompleted');
346
+
347
+ // Update state
348
+ setUser(null);
349
+ setIsAuthenticated(false);
350
+ setHasCompletedOnboarding(false);
351
+
352
+ console.log('Account deleted successfully');
353
+ } catch (error) {
354
+ console.error('Failed to delete account:', error);
355
+ throw error;
356
+ }
357
+ };
358
+
359
+ return (
360
+ <AuthContext.Provider value={{
361
+ user,
362
+ isLoading,
363
+ hasCompletedOnboarding,
364
+ login,
365
+ logout,
366
+ deleteAccount,
367
+ completeOnboarding,
368
+ updateUser,
369
+ appleSignIn,
370
+ onairosSignIn,
371
+ isAuthenticated,
372
+ updateOnboardedStatus,
373
+ // Progress tracking methods
374
+ getResumeTarget,
375
+ markStepCompleted,
376
+ updateLastScreen,
377
+ isReturningUser,
378
+ // Event access token methods
379
+ markEventPageReached,
380
+ hasEventAccessToken
381
+ }}>
382
+ {children}
383
+ </AuthContext.Provider>
384
+ );
385
+ };
386
+
387
+ export const useAuth = (): AuthContextType => {
388
+ const context = useContext(AuthContext);
389
+ if (context === undefined) {
390
+ throw new Error('useAuth must be used within an AuthProvider');
391
+ }
392
+ return context;
393
+ };