diff --git a/src/contexts/AuthContext.js b/src/contexts/AuthContext.js
index 01cd1e9..af41643 100644
--- a/src/contexts/AuthContext.js
+++ b/src/contexts/AuthContext.js
@@ -59,17 +59,58 @@ export const AuthProvider = ({ children }) => {
const userData = await AsyncStorage.getItem('user_data');
if (token && userData) {
+ const user = JSON.parse(userData);
+ console.log('🔄 Found cached auth data, loading user...');
+ console.log('🎫 Token:', `${token.slice(0, 20)}...`);
+ console.log('👤 Cached user:', user);
+
dispatch({
type: 'LOGIN_SUCCESS',
- payload: {
- token,
- user: JSON.parse(userData),
- },
+ payload: { token, user },
});
+
+ // Try to refresh profile data in background
+ console.log('🔄 Refreshing profile in background...');
+ try {
+ const freshProfileData = await authService.getProfile();
+ console.log('👤 Fresh profile data:', freshProfileData);
+
+ // Validate fresh data
+ if (freshProfileData.name && freshProfileData.phone) {
+ // Update cached data
+ await AsyncStorage.setItem('user_data', JSON.stringify(freshProfileData));
+
+ console.log('✅ Profile refreshed successfully on app startup');
+
+ // Update state with fresh data
+ dispatch({
+ type: 'LOGIN_SUCCESS',
+ payload: { token, user: freshProfileData },
+ });
+ } else {
+ console.warn('⚠️ Fresh profile data is invalid, keeping cached data');
+ }
+ } catch (profileError) {
+ console.warn('⚠️ Could not refresh profile on startup:', profileError.message);
+
+ // Check if it's a token expiration error
+ if (profileError.message.includes('Session expired')) {
+ console.log('🔓 Token expired, clearing auth data');
+ await AsyncStorage.removeItem('auth_token');
+ await AsyncStorage.removeItem('user_data');
+ dispatch({ type: 'LOGOUT' });
+ return;
+ }
+
+ // Otherwise, continue with cached data
+ console.log('📦 Using cached user data');
+ }
} else {
+ console.log('🔍 No cached auth data found');
dispatch({ type: 'LOADING', payload: false });
}
} catch (error) {
+ console.error('💥 Error checking auth status:', error.message);
dispatch({ type: 'LOADING', payload: false });
}
};
@@ -117,23 +158,47 @@ export const AuthProvider = ({ children }) => {
}
dispatch({ type: 'LOADING', payload: true });
- const response = await authService.verify(state.pendingVerification.phone, code);
- // Assuming the API returns a token after successful verification
- // You might need to adjust this based on your actual API response
- const token = response.token || 'dummy_token_for_demo';
- const user = response.user || { phone: state.pendingVerification.phone };
+ // Step 1: Verify the code and get the token
+ const verificationResponse = await authService.verify(state.pendingVerification.phone, code);
- // Store auth data
+ console.log('🔍 Verification Response:', verificationResponse);
+
+ // Extract data from your API response structure
+ if (!verificationResponse.success) {
+ dispatch({ type: 'LOADING', payload: false });
+ return { success: false, error: verificationResponse.message || 'Verification failed' };
+ }
+
+ const token = verificationResponse.token;
+ const basicUserData = verificationResponse.user;
+
+ if (!token) {
+ dispatch({ type: 'LOADING', payload: false });
+ return { success: false, error: 'No access token received from verification' };
+ }
+
+ console.log('✅ Token received:', `${token.slice(0, 20)}...`);
+ console.log('👤 Basic user data:', basicUserData);
+
+ // Step 2: Cache auth data (token and user data from verification)
await AsyncStorage.setItem('auth_token', token);
- await AsyncStorage.setItem('user_data', JSON.stringify(user));
+ await AsyncStorage.setItem('user_data', JSON.stringify(basicUserData));
+
+ console.log('✅ Auth data cached successfully');
+ console.log('🎫 Token stored:', `${token.slice(0, 20)}...`);
+ console.log('👤 User data cached:', basicUserData);
dispatch({
type: 'LOGIN_SUCCESS',
- payload: { token, user },
+ payload: { token, user: basicUserData },
});
- return { success: true, message: response.message };
+ return {
+ success: true,
+ message: verificationResponse.message,
+ user: basicUserData
+ };
} catch (error) {
dispatch({ type: 'LOADING', payload: false });
return { success: false, error: error.message };
@@ -154,6 +219,66 @@ export const AuthProvider = ({ children }) => {
dispatch({ type: 'CLEAR_PENDING_VERIFICATION' });
};
+ const updateUserProfile = async (profileData) => {
+ try {
+ const response = await authService.updateProfile(profileData);
+ const updatedUser = { ...state.user, ...profileData };
+
+ // Update AsyncStorage
+ await AsyncStorage.setItem('user_data', JSON.stringify(updatedUser));
+
+ // Update state
+ dispatch({
+ type: 'LOGIN_SUCCESS',
+ payload: {
+ token: state.token,
+ user: updatedUser,
+ },
+ });
+
+ return { success: true, message: response.message };
+ } catch (error) {
+ return { success: false, error: error.message };
+ }
+ };
+
+ const fetchUserProfile = async () => {
+ try {
+ console.log('🔄 Manually fetching user profile...');
+ const profileData = await authService.getProfile();
+ console.log('👤 Fetched profile data:', profileData);
+
+ // Validate profile data structure
+ if (!profileData.name || !profileData.phone) {
+ throw new Error('Invalid profile data structure received');
+ }
+
+ // Update AsyncStorage
+ await AsyncStorage.setItem('user_data', JSON.stringify(profileData));
+
+ // Update state
+ dispatch({
+ type: 'LOGIN_SUCCESS',
+ payload: {
+ token: state.token,
+ user: profileData,
+ },
+ });
+
+ console.log('✅ Profile updated successfully');
+ return { success: true, user: profileData };
+ } catch (error) {
+ console.error('💥 Failed to fetch profile:', error.message);
+
+ // If unauthorized, logout user
+ if (error.message.includes('Session expired')) {
+ console.log('🔓 Session expired during profile fetch, logging out');
+ await logout();
+ }
+ return { success: false, error: error.message };
+ }
+ };
+
const value = {
...state,
login,
@@ -161,6 +286,8 @@ export const AuthProvider = ({ children }) => {
verify,
logout,
clearPendingVerification,
+ updateUserProfile,
+ fetchUserProfile,
};
return {children};
diff --git a/src/screens/Main/ProfileScreen.js b/src/screens/Main/ProfileScreen.js
index df60d6d..b63c7e3 100644
--- a/src/screens/Main/ProfileScreen.js
+++ b/src/screens/Main/ProfileScreen.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { useState, useEffect } from 'react';
import {
View,
Text,
@@ -7,14 +7,47 @@ import {
ScrollView,
TouchableOpacity,
Alert,
+ ActivityIndicator,
+ RefreshControl,
} from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { Ionicons } from '@expo/vector-icons';
import { useAuth } from '../../contexts/AuthContext';
+import apiService from '../../services/apiService';
import { COLORS } from '../../constants/colors';
const ProfileScreen = () => {
- const { user, logout } = useAuth();
+ const { user, logout, fetchUserProfile } = useAuth();
+ const [isLoading, setIsLoading] = useState(false);
+ const [isRefreshing, setIsRefreshing] = useState(false);
+ const [profileData, setProfileData] = useState(null);
+
+ useEffect(() => {
+ loadProfileData();
+ }, []);
+
+ const loadProfileData = async () => {
+ if (isLoading) return;
+
+ setIsLoading(true);
+ try {
+ // Fetch fresh profile data from API
+ const result = await fetchUserProfile();
+ if (result.success) {
+ setProfileData(result.user);
+ }
+ } catch (error) {
+ console.error('Error loading profile:', error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const handleRefresh = async () => {
+ setIsRefreshing(true);
+ await loadProfileData();
+ setIsRefreshing(false);
+ };
const profileSections = [
{
@@ -53,8 +86,100 @@ const ProfileScreen = () => {
];
const handleProfileItemPress = (item) => {
- console.log('Profile item pressed:', item.title);
- // Handle navigation or action here
+ switch (item.id) {
+ case 1: // Personal Information
+ handleEditPersonalInfo();
+ break;
+ case 2: // Contact Information
+ handleEditContactInfo();
+ break;
+ case 3: // Change Password
+ handleChangePassword();
+ break;
+ case 4: // Notifications
+ handleNotificationSettings();
+ break;
+ case 5: // Security
+ handleSecuritySettings();
+ break;
+ default:
+ Alert.alert('Üns beriň', 'Bu funksiýa entek işlenok');
+ }
+ };
+
+ const handleEditPersonalInfo = () => {
+ // Navigate to edit personal info screen
+ Alert.alert('Şahsy maglumatlar', 'Şahsy maglumatlar sahypasy açylýar...');
+ };
+
+ const handleEditContactInfo = () => {
+ // Navigate to edit contact info screen
+ Alert.alert('Aragatnaşyk', 'Aragatnaşyk maglumatlar sahypasy açylýar...');
+ };
+
+ const handleChangePassword = () => {
+ Alert.prompt(
+ 'Paroly üýtget',
+ 'Häzirki parolyňyzy giriziň',
+ [
+ { text: 'Ýatyr', style: 'cancel' },
+ {
+ text: 'Dowam et',
+ onPress: (currentPassword) => {
+ if (currentPassword) {
+ promptForNewPassword(currentPassword);
+ }
+ }
+ }
+ ],
+ 'secure-text'
+ );
+ };
+
+ const promptForNewPassword = (currentPassword) => {
+ Alert.prompt(
+ 'Täze parol',
+ 'Täze parolyňyzy giriziň',
+ [
+ { text: 'Ýatyr', style: 'cancel' },
+ {
+ text: 'Üýtget',
+ onPress: (newPassword) => {
+ if (newPassword && newPassword.length >= 6) {
+ changePassword(currentPassword, newPassword);
+ } else {
+ Alert.alert('Ýalňyşlyk', 'Parol azyndan 6 harp bolmaly');
+ }
+ }
+ }
+ ],
+ 'secure-text'
+ );
+ };
+
+ const changePassword = async (currentPassword, newPassword) => {
+ try {
+ setIsLoading(true);
+ const result = await apiService.changePassword(currentPassword, newPassword);
+
+ if (result.success) {
+ Alert.alert('Üstünlik', 'Parol üstünlikli üýtgedildi');
+ } else {
+ Alert.alert('Ýalňyşlyk', result.error || 'Paroly üýtgetmek amala aşmady');
+ }
+ } catch (error) {
+ Alert.alert('Ýalňyşlyk', error.message || 'Paroly üýtgetmek amala aşmady');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const handleNotificationSettings = () => {
+ Alert.alert('Bildirişler', 'Bildiriş sazlamalary sahypasy açylýar...');
+ };
+
+ const handleSecuritySettings = () => {
+ Alert.alert('Howpsuzlyk', 'Howpsuzlyk sazlamalary sahypasy açylýar...');
};
const handleLogout = () => {
@@ -78,9 +203,12 @@ const ProfileScreen = () => {
const formatPhoneNumber = (phone) => {
if (!phone) return '';
const phoneStr = phone.toString();
- return `+993 ${phoneStr.slice(0, 2)} ${phoneStr.slice(2, 5)} ${phoneStr.slice(5)}`;
+ return `+993 ${phoneStr.slice(0, 2)} ${phoneStr.slice(2, 4)}-${phoneStr.slice(4, 6)}-${phoneStr.slice(6)}`;
};
+ // Use profile data from API or fallback to cached user data
+ const currentUser = profileData || user;
+
return (
@@ -89,19 +217,36 @@ const ProfileScreen = () => {
Profil
-
+
+ }
+ >
{/* User Info Card */}
- {user?.name ? user.name.split(' ').map(n => n[0]).join('').toUpperCase() : 'U'}
+ {currentUser?.name ? currentUser.name.split(' ').map(n => n[0]).join('').toUpperCase() : 'U'}
- {user?.name || 'Ulanyjy'}
- {formatPhoneNumber(user?.phone)}
+
+ {currentUser?.name || 'Ulanyjy'}
+ {isLoading && }
+
+ {formatPhoneNumber(currentUser?.phone)}
+ {currentUser?.email && (
+ {currentUser.email}
+ )}
-
+
@@ -219,6 +364,11 @@ const styles = StyleSheet.create({
fontSize: 16,
color: COLORS.textSecondary,
},
+ userEmail: {
+ fontSize: 14,
+ color: COLORS.textSecondary,
+ marginTop: 2,
+ },
editButton: {
padding: 8,
},
diff --git a/src/services/apiService.js b/src/services/apiService.js
new file mode 100644
index 0000000..b5d7ed3
--- /dev/null
+++ b/src/services/apiService.js
@@ -0,0 +1,82 @@
+import authService from './authService';
+
+class ApiService {
+ // Profile methods
+ async getProfile() {
+ return authService.getProfile();
+ }
+
+ async updateProfile(data) {
+ return authService.updateProfile(data);
+ }
+
+ async changePassword(currentPassword, newPassword) {
+ return authService.changePassword(currentPassword, newPassword);
+ }
+
+ async deleteAccount() {
+ return authService.deleteAccount();
+ }
+
+ // Balance and transactions
+ async getBalance() {
+ return authService.getBalance();
+ }
+
+ async getTransactions(page = 1, limit = 20) {
+ return authService.getTransactions(page, limit);
+ }
+
+ // Transfer and payments
+ async transferMoney(data) {
+ return authService.transferMoney(data);
+ }
+
+ async payBill(data) {
+ return authService.payBill(data);
+ }
+
+ // Cards management
+ async getCards() {
+ return authService.getCards();
+ }
+
+ async addCard(data) {
+ return authService.addCard(data);
+ }
+
+ async blockCard(cardId) {
+ return authService.blockCard(cardId);
+ }
+
+ async unblockCard(cardId) {
+ return authService.unblockCard(cardId);
+ }
+
+ // Utility methods for common operations
+ async getUserDashboardData() {
+ try {
+ const [balance, transactions, cards] = await Promise.all([
+ this.getBalance().catch(() => ({ balance: 0 })),
+ this.getTransactions(1, 5).catch(() => ({ transactions: [] })),
+ this.getCards().catch(() => ({ cards: [] }))
+ ]);
+
+ return {
+ success: true,
+ data: {
+ balance: balance.balance || 0,
+ recentTransactions: transactions.transactions || [],
+ cards: cards.cards || []
+ }
+ };
+ } catch (error) {
+ return {
+ success: false,
+ error: error.message
+ };
+ }
+ }
+}
+
+export default new ApiService();
\ No newline at end of file
diff --git a/src/services/authService.js b/src/services/authService.js
index d406f1c..8ff515b 100644
--- a/src/services/authService.js
+++ b/src/services/authService.js
@@ -1,30 +1,87 @@
+import AsyncStorage from '@react-native-async-storage/async-storage';
import { API_CONFIG } from '../constants/api';
class AuthService {
- async makeRequest(endpoint, data, token = null) {
+ async getStoredToken() {
+ try {
+ return await AsyncStorage.getItem('auth_token');
+ } catch (error) {
+ return null;
+ }
+ }
+
+ async makeRequest(endpoint, data, requiresAuth = false, method = 'POST') {
+ const fullUrl = `${API_CONFIG.BASE_URL}${endpoint}`;
+ const requestId = Math.random().toString(36).substr(2, 9);
+
try {
const headers = {
...API_CONFIG.HEADERS,
};
- if (token) {
- headers.Authorization = `Bearer ${token}`;
+ // Auto-include token for authenticated requests
+ if (requiresAuth) {
+ const token = await this.getStoredToken();
+ if (token) {
+ headers.Authorization = `Bearer ${token}`;
+ }
}
- const response = await fetch(`${API_CONFIG.BASE_URL}${endpoint}`, {
- method: 'POST',
- headers,
- body: JSON.stringify(data),
+ // Log request details
+ console.log(`\n🚀 [${requestId}] API REQUEST`);
+ console.log(`📍 URL: ${method} ${fullUrl}`);
+ console.log(`🔐 Auth Required: ${requiresAuth}`);
+ console.log(`📝 Headers:`, {
+ ...headers,
+ Authorization: headers.Authorization ? `Bearer ${headers.Authorization.slice(-10)}...` : 'None'
});
+ if (data) {
+ console.log(`📦 Body:`, data);
+ }
+ console.log(`⏰ Time: ${new Date().toISOString()}`);
+
+ const startTime = Date.now();
+
+ const response = await fetch(fullUrl, {
+ method,
+ headers,
+ body: data ? JSON.stringify(data) : undefined,
+ });
+
+ const endTime = Date.now();
+ const duration = endTime - startTime;
const result = await response.json();
+ // Log response details
+ console.log(`\n✅ [${requestId}] API RESPONSE`);
+ console.log(`📊 Status: ${response.status} ${response.statusText}`);
+ console.log(`⏱️ Duration: ${duration}ms`);
+ console.log(`📄 Response:`, result);
+
if (!response.ok) {
+ console.log(`❌ [${requestId}] API ERROR`);
+ console.log(`🔴 Status: ${response.status}`);
+ console.log(`💬 Error Message:`, result.message || 'Unknown error');
+
+ // Handle token expiration
+ if (response.status === 401 && requiresAuth) {
+ console.log(`🔓 [${requestId}] TOKEN EXPIRED - Clearing storage`);
+ await AsyncStorage.removeItem('auth_token');
+ await AsyncStorage.removeItem('user_data');
+ throw new Error('Session expired. Please login again.');
+ }
throw new Error(result.message || 'An error occurred');
}
+ console.log(`✨ [${requestId}] REQUEST COMPLETED SUCCESSFULLY\n`);
return result;
} catch (error) {
+ console.log(`\n💥 [${requestId}] API REQUEST FAILED`);
+ console.log(`📍 URL: ${method} ${fullUrl}`);
+ console.log(`🔴 Error:`, error.message);
+ console.log(`📅 Time: ${new Date().toISOString()}\n`);
+
throw new Error(error.message || 'Network error');
}
}
@@ -50,6 +107,58 @@ class AuthService {
code: parseInt(code),
});
}
+
+ // Authenticated API methods
+ async getProfile() {
+ return this.makeRequest('/profile', null, true, 'GET');
+ }
+
+ async updateProfile(data) {
+ return this.makeRequest('/profile', data, true, 'PUT');
+ }
+
+ async getTransactions(page = 1, limit = 20) {
+ return this.makeRequest(`/user/transactions?page=${page}&limit=${limit}`, null, true, 'GET');
+ }
+
+ async getBalance() {
+ return this.makeRequest('/user/balance', null, true, 'GET');
+ }
+
+ async transferMoney(data) {
+ return this.makeRequest('/user/transfer', data, true);
+ }
+
+ async payBill(data) {
+ return this.makeRequest('/user/pay-bill', data, true);
+ }
+
+ async getCards() {
+ return this.makeRequest('/user/cards', null, true, 'GET');
+ }
+
+ async addCard(data) {
+ return this.makeRequest('/user/cards', data, true);
+ }
+
+ async blockCard(cardId) {
+ return this.makeRequest(`/user/cards/${cardId}/block`, null, true);
+ }
+
+ async unblockCard(cardId) {
+ return this.makeRequest(`/user/cards/${cardId}/unblock`, null, true);
+ }
+
+ async changePassword(currentPassword, newPassword) {
+ return this.makeRequest('/user/change-password', {
+ current_password: currentPassword,
+ new_password: newPassword,
+ }, true);
+ }
+
+ async deleteAccount() {
+ return this.makeRequest('/user/delete-account', null, true, 'DELETE');
+ }
}
export default new AuthService();
\ No newline at end of file