@onairos/react-native 3.0.39 → 3.0.42

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 (26) hide show
  1. package/lib/commonjs/components/TrainingModal.js +195 -19
  2. package/lib/commonjs/components/TrainingModal.js.map +1 -1
  3. package/lib/commonjs/components/UniversalOnboarding.js +21 -9
  4. package/lib/commonjs/components/UniversalOnboarding.js.map +1 -1
  5. package/lib/commonjs/components/onboarding/OAuthWebView.js +56 -5
  6. package/lib/commonjs/components/onboarding/OAuthWebView.js.map +1 -1
  7. package/lib/commonjs/services/platformAuthService.js +64 -1
  8. package/lib/commonjs/services/platformAuthService.js.map +1 -1
  9. package/lib/module/components/TrainingModal.js +195 -20
  10. package/lib/module/components/TrainingModal.js.map +1 -1
  11. package/lib/module/components/UniversalOnboarding.js +23 -11
  12. package/lib/module/components/UniversalOnboarding.js.map +1 -1
  13. package/lib/module/components/onboarding/OAuthWebView.js +56 -5
  14. package/lib/module/components/onboarding/OAuthWebView.js.map +1 -1
  15. package/lib/module/services/platformAuthService.js +62 -0
  16. package/lib/module/services/platformAuthService.js.map +1 -1
  17. package/lib/typescript/components/TrainingModal.d.ts.map +1 -1
  18. package/lib/typescript/components/UniversalOnboarding.d.ts.map +1 -1
  19. package/lib/typescript/components/onboarding/OAuthWebView.d.ts.map +1 -1
  20. package/lib/typescript/services/platformAuthService.d.ts +7 -0
  21. package/lib/typescript/services/platformAuthService.d.ts.map +1 -1
  22. package/package.json +4 -2
  23. package/src/components/TrainingModal.tsx +200 -14
  24. package/src/components/UniversalOnboarding.tsx +25 -10
  25. package/src/components/onboarding/OAuthWebView.tsx +51 -5
  26. package/src/services/platformAuthService.ts +64 -0
@@ -1,4 +1,4 @@
1
- import React from 'react';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
2
  import {
3
3
  View,
4
4
  Text,
@@ -10,8 +10,10 @@ import {
10
10
  Animated,
11
11
  TouchableWithoutFeedback,
12
12
  SafeAreaView,
13
+ Image,
13
14
  } from 'react-native';
14
15
  import Icon from 'react-native-vector-icons/MaterialIcons';
16
+ import { io, Socket } from 'socket.io-client';
15
17
  import { COLORS } from '../constants';
16
18
  import type { TrainingModalProps } from '../types';
17
19
 
@@ -26,7 +28,170 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
26
28
  modelKey,
27
29
  username,
28
30
  }) => {
29
- const progressPercentage = Math.round(progress * 100);
31
+ // Socket and training state
32
+ const socketRef = useRef<Socket | null>(null);
33
+ const [socketConnected, setSocketConnected] = useState(false);
34
+ const [trainingStatus, setTrainingStatus] = useState('Initializing...');
35
+ const [hasError, setHasError] = useState(false);
36
+ const [isTrainingComplete, setIsTrainingComplete] = useState(false);
37
+ const [userTraits, setUserTraits] = useState<any>(null);
38
+ const [inferenceResults, setInferenceResults] = useState<any>(null);
39
+ const [internalProgress, setInternalProgress] = useState(0);
40
+
41
+ // Use internal progress if available, otherwise fall back to prop progress
42
+ const currentProgress = internalProgress > 0 ? internalProgress / 100 : progress;
43
+ const progressPercentage = Math.round(currentProgress * 100);
44
+
45
+ // Mock user data for now - in real implementation this would come from props
46
+ const userToken = 'mock-token'; // TODO: Get from actual auth
47
+ const userInfo = {
48
+ username: username || 'mobile_user',
49
+ email: null,
50
+ id: null,
51
+ name: username || 'mobile_user'
52
+ };
53
+
54
+ // Start Enoch training via API
55
+ const startEnochTraining = async (socketId: string) => {
56
+ try {
57
+ setTrainingStatus('Starting training...');
58
+ setInternalProgress(10);
59
+
60
+ if (!userToken) {
61
+ throw new Error('No authentication token available');
62
+ }
63
+
64
+ console.log('🚀 Starting Enoch training with socketId:', socketId);
65
+
66
+ // Prepare user data for training
67
+ const trainingData = {
68
+ socketId,
69
+ username: userInfo?.username || userInfo?.name || username || 'mobile_user',
70
+ email: userInfo?.email || null,
71
+ modelKey: modelKey || null,
72
+ userId: userInfo?.id || null
73
+ };
74
+
75
+ console.log('📤 Sending training data:', trainingData);
76
+
77
+ // For now, simulate the API call since we don't have real tokens
78
+ // TODO: Uncomment when real authentication is available
79
+ /*
80
+ const response = await fetch('https://api2.onairos.uk/enoch/trainModel/mobile', {
81
+ method: 'POST',
82
+ headers: {
83
+ 'Content-Type': 'application/json',
84
+ 'Authorization': `Bearer ${userToken}`
85
+ },
86
+ body: JSON.stringify(trainingData)
87
+ });
88
+
89
+ const result = await response.json();
90
+
91
+ if (result.success) {
92
+ console.log('🚀 Training Started:', result.message);
93
+ setTrainingStatus('Training model...');
94
+ setInternalProgress(20);
95
+ } else {
96
+ console.error('Training start failed:', result.error);
97
+ setTrainingStatus(`Error: ${result.error}`);
98
+ setHasError(true);
99
+ }
100
+ */
101
+
102
+ // Simulate successful training start
103
+ console.log('🚀 Training Started (simulated)');
104
+ setTrainingStatus('Training model...');
105
+ setInternalProgress(20);
106
+
107
+ } catch (error) {
108
+ console.error('Training start error:', error);
109
+ setTrainingStatus(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`);
110
+ setHasError(true);
111
+ }
112
+ };
113
+
114
+ // Setup socket connection and training
115
+ useEffect(() => {
116
+ if (!visible || !userInfo) return;
117
+
118
+ console.log('Setting up socket connection for training...');
119
+ console.log('🧑‍💻 User info available:', userInfo);
120
+
121
+ // For now, simulate the socket connection since we don't have real auth
122
+ // TODO: Uncomment when real authentication is available
123
+ /*
124
+ // Initialize socket connection
125
+ socketRef.current = io('https://api2.onairos.uk', {
126
+ transports: ['websocket'],
127
+ autoConnect: false
128
+ });
129
+
130
+ // Socket event listeners
131
+ socketRef.current.on('connect', () => {
132
+ console.log('✅ Socket connected for training');
133
+ setSocketConnected(true);
134
+ const socketId = socketRef.current?.id;
135
+ if (socketId) {
136
+ startEnochTraining(socketId);
137
+ }
138
+ });
139
+
140
+ socketRef.current.on('disconnect', () => {
141
+ console.log('❌ Socket disconnected');
142
+ setSocketConnected(false);
143
+ });
144
+
145
+ socketRef.current.on('trainingCompleted', (data) => {
146
+ console.log('✅ Training Complete:', data);
147
+ setTrainingStatus('Running test inference...');
148
+ setInternalProgress(60);
149
+ });
150
+
151
+ socketRef.current.on('inferenceCompleted', (data) => {
152
+ console.log('🧠 Inference Complete:', data);
153
+ setTrainingStatus('Uploading to S3...');
154
+ setInternalProgress(80);
155
+ setUserTraits(data.traits);
156
+ setInferenceResults(data.inferenceResults);
157
+ });
158
+
159
+ socketRef.current.on('modelStandby', (data) => {
160
+ console.log('🎉 All Complete:', data);
161
+ setIsTrainingComplete(true);
162
+ setTrainingStatus('Complete!');
163
+ setInternalProgress(100);
164
+ });
165
+
166
+ socketRef.current.on('trainingUpdate', (data) => {
167
+ if (data.error) {
168
+ console.error('Training update error:', data.error);
169
+ setTrainingStatus(`Error: ${data.error}`);
170
+ setHasError(true);
171
+ } else if (data.progress) {
172
+ setInternalProgress(data.progress);
173
+ setTrainingStatus(data.status || 'Training in progress...');
174
+ }
175
+ });
176
+
177
+ // Connect to socket
178
+ socketRef.current.connect();
179
+ */
180
+
181
+ // Simulate socket connection and training for now
182
+ console.log('🔌 Simulating socket connection...');
183
+ setSocketConnected(true);
184
+ startEnochTraining('simulated-socket-id');
185
+
186
+ // Cleanup function
187
+ return () => {
188
+ if (socketRef.current) {
189
+ console.log('🔌 Disconnecting training socket...');
190
+ socketRef.current.disconnect();
191
+ socketRef.current = null;
192
+ }
193
+ };
194
+ }, [visible, userInfo]);
30
195
 
31
196
  return (
32
197
  <Modal
@@ -46,14 +211,20 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
46
211
 
47
212
  <SafeAreaView style={styles.container}>
48
213
  <View style={styles.content}>
49
- <View style={styles.onairosLogo}>
50
- <Text style={styles.onairosLogoText}>O</Text>
51
- </View>
214
+ <Image
215
+ source={require('../assets/images/onairos_logo.png')}
216
+ style={styles.onairosLogo}
217
+ resizeMode="contain"
218
+ />
52
219
 
53
220
  <Text style={styles.title}>Training Your AI</Text>
54
221
  <Text style={styles.subtitle}>
55
222
  Creating your personalized model
56
223
  </Text>
224
+
225
+ <Text style={styles.statusText}>
226
+ {trainingStatus}
227
+ </Text>
57
228
 
58
229
  <View style={styles.progressContainer}>
59
230
  <View style={styles.progressBar}>
@@ -71,8 +242,17 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
71
242
 
72
243
  <View style={styles.loadingContainer}>
73
244
  <ActivityIndicator size="small" color={COLORS.primary} />
74
- <Text style={styles.loadingText}>Processing your data...</Text>
245
+ <Text style={styles.loadingText}>
246
+ {socketConnected ? 'Processing your data...' : 'Connecting to training server...'}
247
+ </Text>
75
248
  </View>
249
+
250
+ {hasError && (
251
+ <View style={styles.errorContainer}>
252
+ <Icon name="error-outline" size={24} color="#FF6B6B" />
253
+ <Text style={styles.errorText}>Training encountered an error</Text>
254
+ </View>
255
+ )}
76
256
 
77
257
  <View style={styles.footer}>
78
258
  <TouchableOpacity
@@ -82,7 +262,7 @@ export const TrainingModal: React.FC<TrainingModalProps> = ({
82
262
  <Text style={styles.cancelButtonText}>Cancel</Text>
83
263
  </TouchableOpacity>
84
264
 
85
- {progress >= 1 && (
265
+ {(currentProgress >= 1 || isTrainingComplete) && (
86
266
  <TouchableOpacity
87
267
  style={styles.completeButton}
88
268
  onPress={onComplete}
@@ -153,6 +333,11 @@ const styles = StyleSheet.create({
153
333
  textAlign: 'center',
154
334
  marginBottom: 32,
155
335
  },
336
+ statusText: {
337
+ fontSize: 14,
338
+ color: COLORS.text.secondary,
339
+ marginBottom: 24,
340
+ },
156
341
  progressContainer: {
157
342
  width: '100%',
158
343
  marginBottom: 16,
@@ -224,14 +409,15 @@ const styles = StyleSheet.create({
224
409
  onairosLogo: {
225
410
  width: 48,
226
411
  height: 48,
227
- borderRadius: 24,
228
- backgroundColor: COLORS.primary,
229
- justifyContent: 'center',
412
+ },
413
+ errorContainer: {
414
+ flexDirection: 'row',
230
415
  alignItems: 'center',
416
+ marginBottom: 24,
231
417
  },
232
- onairosLogoText: {
233
- fontSize: 24,
234
- fontWeight: '600',
235
- color: '#fff',
418
+ errorText: {
419
+ fontSize: 14,
420
+ color: '#FF6B6B',
421
+ marginLeft: 8,
236
422
  },
237
423
  });
@@ -15,6 +15,7 @@ import {
15
15
  Image,
16
16
  Switch,
17
17
  Linking,
18
+ Alert,
18
19
  } from 'react-native';
19
20
  import Icon from 'react-native-vector-icons/MaterialIcons';
20
21
  import { PlatformList } from './PlatformList';
@@ -23,7 +24,7 @@ import { TrainingModal } from './TrainingModal';
23
24
  import { OAuthWebView } from './onboarding/OAuthWebView';
24
25
  import { useConnections } from '../hooks/useConnections';
25
26
  import { COLORS, DEEP_LINK_CONFIG } from '../constants';
26
- import { initiateOAuth, initiateNativeAuth, hasNativeSDK, isOAuthCallback } from '../services/platformAuthService';
27
+ import { initiateOAuth, initiateNativeAuth, hasNativeSDK, isOAuthCallback, testApiConnectivity } from '../services/platformAuthService';
27
28
  import type { UniversalOnboardingProps, ConnectionStatus } from '../types';
28
29
 
29
30
  const { height, width } = Dimensions.get('window');
@@ -161,15 +162,27 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
161
162
  // Attempt to connect platform
162
163
  try {
163
164
  setIsConnectingPlatform(true);
164
- console.log(`Initiating connection for ${platformId}`);
165
+ console.log(`🔌 Initiating connection for ${platformId}`);
166
+
167
+ // Test API connectivity first
168
+ console.log('🔍 Testing API connectivity...');
169
+ const connectivityTest = await testApiConnectivity();
170
+
171
+ if (!connectivityTest.success) {
172
+ console.error('❌ API connectivity test failed:', connectivityTest.error);
173
+ Alert.alert('Network Error', `${connectivityTest.error}\n\nPlease check your internet connection and try again.`);
174
+ return;
175
+ }
176
+
177
+ console.log('✅ API connectivity confirmed');
165
178
 
166
179
  // Check if platform has a native SDK
167
180
  if (hasNativeSDK(platformId)) {
168
- console.log(`Using native SDK for ${platformId}`);
181
+ console.log(`📱 Using native SDK for ${platformId}`);
169
182
  // Use native SDK for authentication
170
183
  const success = await initiateNativeAuth(platformId);
171
184
  if (success) {
172
- console.log(`Native authentication successful for ${platformId}`);
185
+ console.log(`✅ Native authentication successful for ${platformId}`);
173
186
  // Update platform toggle state
174
187
  setPlatformToggles(prev => ({
175
188
  ...prev,
@@ -183,26 +196,28 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
183
196
  }));
184
197
  }
185
198
  } else {
186
- console.log(`Initiating OAuth flow for ${platformId}`);
199
+ console.log(`🌐 Initiating OAuth flow for ${platformId}`);
187
200
  // Use OAuth flow through proxy server
188
201
  const oauthUrl = await initiateOAuth(platformId, username, AppName);
189
202
  if (oauthUrl) {
190
- console.log(`Received OAuth URL for ${platformId}: ${oauthUrl}`);
203
+ console.log(`✅ Received OAuth URL for ${platformId}:`, oauthUrl);
191
204
  setCurrentPlatform(platformId);
192
205
  setOauthUrl(oauthUrl);
193
206
  setStep('oauth');
194
207
  } else {
195
- console.error(`No OAuth URL returned for ${platformId}`);
208
+ console.error(`❌ No OAuth URL returned for ${platformId}`);
209
+ Alert.alert('Error', 'Failed to get authorization URL for this platform. Please try again.');
196
210
  }
197
211
  }
198
212
  } catch (error) {
199
- console.error(`Error connecting ${platformId}:`, error);
213
+ console.error(`❌ Error connecting ${platformId}:`, error);
214
+ Alert.alert('Connection Error', `Failed to connect to ${platformId}: ${error instanceof Error ? error.message : 'Unknown error'}`);
200
215
  } finally {
201
216
  setIsConnectingPlatform(false);
202
217
  }
203
218
  } else {
204
219
  // Disconnect platform
205
- console.log(`Disconnecting ${platformId}`);
220
+ console.log(`🔌 Disconnecting ${platformId}`);
206
221
  setPlatformToggles(prev => ({
207
222
  ...prev,
208
223
  [platformId]: false
@@ -215,7 +230,7 @@ export const UniversalOnboarding: React.FC<UniversalOnboardingProps> = ({
215
230
  return newConnections;
216
231
  });
217
232
  }
218
- }, [platformToggles, username]);
233
+ }, [platformToggles, username, AppName]);
219
234
 
220
235
  /**
221
236
  * Handles OAuth callback URLs
@@ -36,7 +36,13 @@ export const OAuthWebView: React.FC<OAuthWebViewProps> = ({
36
36
  const webViewRef = useRef<WebView>(null);
37
37
  const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
38
38
 
39
- console.log('Opening OAuth WebView with URL:', url);
39
+ console.log('🌐 Opening OAuth WebView with URL:', url);
40
+ console.log('🔗 Platform:', platform);
41
+ console.log('📱 URL Components:', {
42
+ protocol: url.split('://')[0],
43
+ domain: url.split('://')[1]?.split('/')[0],
44
+ path: url.split('://')[1]?.split('/').slice(1).join('/'),
45
+ });
40
46
 
41
47
  // Set up timeout for OAuth flow
42
48
  React.useEffect(() => {
@@ -157,10 +163,50 @@ export const OAuthWebView: React.FC<OAuthWebViewProps> = ({
157
163
 
158
164
  const handleError = (syntheticEvent: any) => {
159
165
  const { nativeEvent } = syntheticEvent;
160
- console.error('WebView error:', nativeEvent);
166
+ console.error('🚨 WebView error details:', {
167
+ description: nativeEvent.description,
168
+ code: nativeEvent.code,
169
+ domain: nativeEvent.domain,
170
+ url: nativeEvent.url,
171
+ canGoBack: nativeEvent.canGoBack,
172
+ canGoForward: nativeEvent.canGoForward,
173
+ loading: nativeEvent.loading,
174
+ target: nativeEvent.target,
175
+ title: nativeEvent.title,
176
+ });
177
+
178
+ // Check for specific NSURLErrorDomain codes
179
+ let errorMessage = 'Failed to load OAuth page.';
180
+
181
+ if (nativeEvent.domain === 'NSURLErrorDomain') {
182
+ switch (nativeEvent.code) {
183
+ case -1009: // NSURLErrorNotConnectedToInternet
184
+ errorMessage = 'No internet connection. Please check your network and try again.';
185
+ break;
186
+ case -1003: // NSURLErrorCannotFindHost
187
+ errorMessage = 'Cannot reach authentication server. Please check your internet connection.';
188
+ break;
189
+ case -1001: // NSURLErrorTimedOut
190
+ errorMessage = 'Connection timed out. Please try again.';
191
+ break;
192
+ case -1200: // NSURLErrorSecureConnectionFailed
193
+ errorMessage = 'Secure connection failed. Please try again.';
194
+ break;
195
+ case -1022: // NSURLErrorAppTransportSecurityRequiresSecureConnection
196
+ errorMessage = 'App Transport Security error. Connection must be secure.';
197
+ break;
198
+ case -1004: // NSURLErrorCannotConnectToHost
199
+ errorMessage = 'Cannot connect to authentication server. Please try again later.';
200
+ break;
201
+ default:
202
+ errorMessage = `Network error (${nativeEvent.code}): ${nativeEvent.description || 'Please check your connection and try again.'}`;
203
+ }
204
+ }
205
+
206
+ console.error('🔴 OAuth WebView Error:', errorMessage);
161
207
 
162
208
  if (retryCount < 2) {
163
- console.log(`Retrying OAuth load (attempt ${retryCount + 1})`);
209
+ console.log(`🔄 Retrying OAuth load (attempt ${retryCount + 1}/2)`);
164
210
  setRetryCount(prev => prev + 1);
165
211
  setIsLoading(true);
166
212
  setError(null);
@@ -170,9 +216,9 @@ export const OAuthWebView: React.FC<OAuthWebViewProps> = ({
170
216
  if (webViewRef.current) {
171
217
  webViewRef.current.reload();
172
218
  }
173
- }, 2000);
219
+ }, 3000);
174
220
  } else {
175
- setError('Failed to load OAuth page. Please try again or use the browser option.');
221
+ setError(errorMessage);
176
222
  setIsLoading(false);
177
223
  }
178
224
  };
@@ -72,17 +72,26 @@ export const getPlatformColor = (platform: string): string => {
72
72
  */
73
73
  export const initiateOAuth = async (platform: string, username: string, appName?: string): Promise<string | null> => {
74
74
  try {
75
+ console.log('🚀 Initiating OAuth for platform:', platform);
76
+ console.log('👤 Username:', username);
77
+ console.log('📱 App name:', appName);
78
+
75
79
  // Check if the platform is supported
76
80
  if (!PLATFORM_AUTH_CONFIG[platform]) {
81
+ console.error('❌ Unsupported platform:', platform);
77
82
  throw new Error(`Unsupported platform: ${platform}`);
78
83
  }
79
84
 
80
85
  // Check if platform has a native SDK
81
86
  if (PLATFORM_AUTH_CONFIG[platform].hasNativeSDK) {
87
+ console.log('📱 Platform uses native SDK, returning null');
82
88
  // Return null to indicate that we should use the native SDK
83
89
  return null;
84
90
  }
85
91
 
92
+ console.log('🌐 Platform uses OAuth WebView flow');
93
+ console.log('🔗 Auth endpoint:', PLATFORM_AUTH_CONFIG[platform].authEndpoint);
94
+
86
95
  // Handle Instagram with specific API format
87
96
  if (platform === 'instagram') {
88
97
  const state = 'djksbfds';
@@ -93,6 +102,8 @@ export const initiateOAuth = async (platform: string, username: string, appName?
93
102
  },
94
103
  };
95
104
 
105
+ console.log('📤 Sending Instagram OAuth request:', jsonData);
106
+
96
107
  const response = await fetch('https://api2.onairos.uk/instagram/authorize', {
97
108
  method: 'POST',
98
109
  headers: {
@@ -101,12 +112,24 @@ export const initiateOAuth = async (platform: string, username: string, appName?
101
112
  body: JSON.stringify(jsonData),
102
113
  });
103
114
 
115
+ console.log('📡 Instagram OAuth response status:', response.status);
116
+ console.log('📡 Instagram OAuth response headers:', response.headers);
117
+
118
+ if (!response.ok) {
119
+ const errorText = await response.text();
120
+ console.error('❌ Instagram OAuth API error:', errorText);
121
+ throw new Error(`Instagram OAuth API error: ${response.status} - ${errorText}`);
122
+ }
123
+
104
124
  const responseData = await response.json();
125
+ console.log('📥 Instagram OAuth response data:', responseData);
105
126
 
106
127
  if (responseData.instagramURL) {
128
+ console.log('✅ Instagram OAuth URL received:', responseData.instagramURL);
107
129
  return responseData.instagramURL;
108
130
  }
109
131
 
132
+ console.error('❌ No Instagram URL found in response');
110
133
  throw new Error('No Instagram URL found in response');
111
134
  }
112
135
 
@@ -119,6 +142,8 @@ export const initiateOAuth = async (platform: string, username: string, appName?
119
142
  },
120
143
  };
121
144
 
145
+ console.log(`📤 Sending ${platform} OAuth request:`, jsonData);
146
+
122
147
  // Make the request to get the OAuth URL
123
148
  const response = await fetch(PLATFORM_AUTH_CONFIG[platform].authEndpoint, {
124
149
  method: 'POST',
@@ -128,8 +153,18 @@ export const initiateOAuth = async (platform: string, username: string, appName?
128
153
  body: JSON.stringify(jsonData),
129
154
  });
130
155
 
156
+ console.log(`📡 ${platform} OAuth response status:`, response.status);
157
+ console.log(`📡 ${platform} OAuth response headers:`, response.headers);
158
+
159
+ if (!response.ok) {
160
+ const errorText = await response.text();
161
+ console.error(`❌ ${platform} OAuth API error:`, errorText);
162
+ throw new Error(`${platform} OAuth API error: ${response.status} - ${errorText}`);
163
+ }
164
+
131
165
  // Parse the response
132
166
  const data = await response.json();
167
+ console.log(`📥 ${platform} OAuth response data:`, data);
133
168
 
134
169
  // Check if the response contains the OAuth URL based on platform
135
170
  switch (platform) {
@@ -249,3 +284,32 @@ export const isOAuthCallback = (url: string): boolean => {
249
284
  // Check if the URL starts with our redirect URI
250
285
  return url.startsWith('onairosanime://auth/');
251
286
  };
287
+
288
+ /**
289
+ * Test connectivity to the Onairos API server
290
+ */
291
+ export const testApiConnectivity = async (): Promise<{ success: boolean; error?: string }> => {
292
+ try {
293
+ console.log('🔍 Testing connectivity to Onairos API...');
294
+
295
+ const response = await fetch('https://api2.onairos.uk/health', {
296
+ method: 'GET',
297
+ headers: {
298
+ 'Content-Type': 'application/json',
299
+ },
300
+ });
301
+
302
+ console.log('🌐 API connectivity test response:', response.status);
303
+
304
+ if (response.ok) {
305
+ console.log('✅ API server is reachable');
306
+ return { success: true };
307
+ } else {
308
+ console.log('⚠️ API server responded with error:', response.status);
309
+ return { success: false, error: `API server error: ${response.status}` };
310
+ }
311
+ } catch (error) {
312
+ console.error('❌ API connectivity test failed:', error);
313
+ return { success: false, error: error instanceof Error ? error.message : 'Unknown error' };
314
+ }
315
+ };