@oxyhq/services 5.3.11 → 5.4.1

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 (213) hide show
  1. package/README.md +21 -0
  2. package/lib/commonjs/assets/assets/icons/OxyServices.tsx +67 -0
  3. package/lib/commonjs/assets/assets/icons/logo_OxyServices.svg +1 -0
  4. package/lib/commonjs/assets/icons/OxyServices.js +53 -0
  5. package/lib/commonjs/assets/icons/OxyServices.js.map +1 -0
  6. package/lib/commonjs/assets/icons/logo_OxyServices.svg +1 -0
  7. package/lib/commonjs/core/index.js +119 -23
  8. package/lib/commonjs/core/index.js.map +1 -1
  9. package/lib/commonjs/index.js +2 -0
  10. package/lib/commonjs/index.js.map +1 -1
  11. package/lib/commonjs/lib/sonner.js +15 -11
  12. package/lib/commonjs/lib/sonner.js.map +1 -1
  13. package/lib/commonjs/node/index.js +2 -0
  14. package/lib/commonjs/node/index.js.map +1 -1
  15. package/lib/commonjs/ui/components/GroupedItem.js +109 -0
  16. package/lib/commonjs/ui/components/GroupedItem.js.map +1 -0
  17. package/lib/commonjs/ui/components/GroupedSection.js +33 -0
  18. package/lib/commonjs/ui/components/GroupedSection.js.map +1 -0
  19. package/lib/commonjs/ui/components/OxyProvider.js +95 -112
  20. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  21. package/lib/commonjs/ui/components/ProfileCard.js +124 -0
  22. package/lib/commonjs/ui/components/ProfileCard.js.map +1 -0
  23. package/lib/commonjs/ui/components/QuickActions.js +87 -0
  24. package/lib/commonjs/ui/components/QuickActions.js.map +1 -0
  25. package/lib/commonjs/ui/components/Section.js +36 -0
  26. package/lib/commonjs/ui/components/Section.js.map +1 -0
  27. package/lib/commonjs/ui/components/SectionTitle.js +35 -0
  28. package/lib/commonjs/ui/components/SectionTitle.js.map +1 -0
  29. package/lib/commonjs/ui/components/bottomSheet/index.js +6 -6
  30. package/lib/commonjs/ui/components/index.js +97 -0
  31. package/lib/commonjs/ui/components/index.js.map +1 -0
  32. package/lib/commonjs/ui/navigation/OxyRouter.js +20 -3
  33. package/lib/commonjs/ui/navigation/OxyRouter.js.map +1 -1
  34. package/lib/commonjs/ui/screens/AccountCenterScreen.js +190 -207
  35. package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
  36. package/lib/commonjs/ui/screens/AccountManagementDemo.js +299 -0
  37. package/lib/commonjs/ui/screens/AccountManagementDemo.js.map +1 -0
  38. package/lib/commonjs/ui/screens/AccountOverviewScreen.js +669 -401
  39. package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
  40. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +695 -498
  41. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  42. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +451 -488
  43. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
  44. package/lib/commonjs/ui/screens/AppInfoScreen.js +498 -185
  45. package/lib/commonjs/ui/screens/AppInfoScreen.js.map +1 -1
  46. package/lib/commonjs/ui/screens/BillingManagementScreen.js +636 -0
  47. package/lib/commonjs/ui/screens/BillingManagementScreen.js.map +1 -0
  48. package/lib/commonjs/ui/screens/FileManagementScreen.js +2497 -0
  49. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -0
  50. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +1620 -0
  51. package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -0
  52. package/lib/commonjs/ui/screens/ProfileScreen.js +117 -13
  53. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  54. package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
  55. package/lib/commonjs/ui/screens/SignInScreen.js +1 -1
  56. package/lib/commonjs/ui/screens/SignUpScreen.js +1 -1
  57. package/lib/commonjs/utils/polyfills.js +42 -0
  58. package/lib/commonjs/utils/polyfills.js.map +1 -0
  59. package/lib/module/assets/assets/icons/OxyServices.tsx +67 -0
  60. package/lib/module/assets/assets/icons/logo_OxyServices.svg +1 -0
  61. package/lib/module/assets/icons/OxyServices.js +46 -0
  62. package/lib/module/assets/icons/OxyServices.js.map +1 -0
  63. package/lib/module/assets/icons/logo_OxyServices.svg +1 -0
  64. package/lib/module/core/index.js +119 -23
  65. package/lib/module/core/index.js.map +1 -1
  66. package/lib/module/index.js +3 -0
  67. package/lib/module/index.js.map +1 -1
  68. package/lib/module/lib/sonner.js +13 -1
  69. package/lib/module/lib/sonner.js.map +1 -1
  70. package/lib/module/node/index.js +3 -0
  71. package/lib/module/node/index.js.map +1 -1
  72. package/lib/module/ui/components/GroupedItem.js +104 -0
  73. package/lib/module/ui/components/GroupedItem.js.map +1 -0
  74. package/lib/module/ui/components/GroupedSection.js +28 -0
  75. package/lib/module/ui/components/GroupedSection.js.map +1 -0
  76. package/lib/module/ui/components/OxyProvider.js +97 -114
  77. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  78. package/lib/module/ui/components/ProfileCard.js +119 -0
  79. package/lib/module/ui/components/ProfileCard.js.map +1 -0
  80. package/lib/module/ui/components/QuickActions.js +82 -0
  81. package/lib/module/ui/components/QuickActions.js.map +1 -0
  82. package/lib/module/ui/components/Section.js +31 -0
  83. package/lib/module/ui/components/Section.js.map +1 -0
  84. package/lib/module/ui/components/SectionTitle.js +30 -0
  85. package/lib/module/ui/components/SectionTitle.js.map +1 -0
  86. package/lib/module/ui/components/bottomSheet/index.js +2 -5
  87. package/lib/module/ui/components/bottomSheet/index.js.map +1 -1
  88. package/lib/module/ui/components/index.js +18 -0
  89. package/lib/module/ui/components/index.js.map +1 -0
  90. package/lib/module/ui/navigation/OxyRouter.js +20 -3
  91. package/lib/module/ui/navigation/OxyRouter.js.map +1 -1
  92. package/lib/module/ui/screens/AccountCenterScreen.js +191 -208
  93. package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
  94. package/lib/module/ui/screens/AccountManagementDemo.js +296 -0
  95. package/lib/module/ui/screens/AccountManagementDemo.js.map +1 -0
  96. package/lib/module/ui/screens/AccountOverviewScreen.js +671 -403
  97. package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
  98. package/lib/module/ui/screens/AccountSettingsScreen.js +698 -501
  99. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  100. package/lib/module/ui/screens/AccountSwitcherScreen.js +450 -488
  101. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
  102. package/lib/module/ui/screens/AppInfoScreen.js +498 -186
  103. package/lib/module/ui/screens/AppInfoScreen.js.map +1 -1
  104. package/lib/module/ui/screens/BillingManagementScreen.js +631 -0
  105. package/lib/module/ui/screens/BillingManagementScreen.js.map +1 -0
  106. package/lib/module/ui/screens/FileManagementScreen.js +2492 -0
  107. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -0
  108. package/lib/module/ui/screens/PremiumSubscriptionScreen.js +1615 -0
  109. package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -0
  110. package/lib/module/ui/screens/ProfileScreen.js +118 -14
  111. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  112. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  113. package/lib/module/ui/screens/SignInScreen.js +1 -1
  114. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  115. package/lib/module/ui/screens/SignUpScreen.js +1 -1
  116. package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
  117. package/lib/module/utils/polyfills.js +36 -0
  118. package/lib/module/utils/polyfills.js.map +1 -0
  119. package/lib/typescript/assets/icons/OxyServices.d.ts +29 -0
  120. package/lib/typescript/assets/icons/OxyServices.d.ts.map +1 -0
  121. package/lib/typescript/core/index.d.ts +26 -1
  122. package/lib/typescript/core/index.d.ts.map +1 -1
  123. package/lib/typescript/index.d.ts +1 -0
  124. package/lib/typescript/index.d.ts.map +1 -1
  125. package/lib/typescript/lib/sonner.d.ts +5 -1
  126. package/lib/typescript/lib/sonner.d.ts.map +1 -1
  127. package/lib/typescript/models/interfaces.d.ts +1 -2
  128. package/lib/typescript/models/interfaces.d.ts.map +1 -1
  129. package/lib/typescript/node/index.d.ts +1 -0
  130. package/lib/typescript/node/index.d.ts.map +1 -1
  131. package/lib/typescript/ui/components/GroupedItem.d.ts +17 -0
  132. package/lib/typescript/ui/components/GroupedItem.d.ts.map +1 -0
  133. package/lib/typescript/ui/components/GroupedSection.d.ts +19 -0
  134. package/lib/typescript/ui/components/GroupedSection.d.ts.map +1 -0
  135. package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
  136. package/lib/typescript/ui/components/ProfileCard.d.ts +20 -0
  137. package/lib/typescript/ui/components/ProfileCard.d.ts.map +1 -0
  138. package/lib/typescript/ui/components/QuickActions.d.ts +15 -0
  139. package/lib/typescript/ui/components/QuickActions.d.ts.map +1 -0
  140. package/lib/typescript/ui/components/Section.d.ts +11 -0
  141. package/lib/typescript/ui/components/Section.d.ts.map +1 -0
  142. package/lib/typescript/ui/components/SectionTitle.d.ts +9 -0
  143. package/lib/typescript/ui/components/SectionTitle.d.ts.map +1 -0
  144. package/lib/typescript/ui/components/bottomSheet/index.d.ts +3 -2
  145. package/lib/typescript/ui/components/bottomSheet/index.d.ts.map +1 -1
  146. package/lib/typescript/ui/components/index.d.ts +13 -0
  147. package/lib/typescript/ui/components/index.d.ts.map +1 -0
  148. package/lib/typescript/ui/navigation/OxyRouter.d.ts.map +1 -1
  149. package/lib/typescript/ui/navigation/types.d.ts +8 -0
  150. package/lib/typescript/ui/navigation/types.d.ts.map +1 -1
  151. package/lib/typescript/ui/screens/AccountCenterScreen.d.ts.map +1 -1
  152. package/lib/typescript/ui/screens/AccountManagementDemo.d.ts +8 -0
  153. package/lib/typescript/ui/screens/AccountManagementDemo.d.ts.map +1 -0
  154. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
  155. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts +1 -4
  156. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  157. package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
  158. package/lib/typescript/ui/screens/AppInfoScreen.d.ts.map +1 -1
  159. package/lib/typescript/ui/screens/BillingManagementScreen.d.ts +5 -0
  160. package/lib/typescript/ui/screens/BillingManagementScreen.d.ts.map +1 -0
  161. package/lib/typescript/ui/screens/FileManagementScreen.d.ts +8 -0
  162. package/lib/typescript/ui/screens/FileManagementScreen.d.ts.map +1 -0
  163. package/lib/typescript/ui/screens/PremiumSubscriptionScreen.d.ts +5 -0
  164. package/lib/typescript/ui/screens/PremiumSubscriptionScreen.d.ts.map +1 -0
  165. package/lib/typescript/ui/screens/ProfileScreen.d.ts.map +1 -1
  166. package/lib/typescript/ui/screens/SessionManagementScreen.d.ts.map +1 -1
  167. package/lib/typescript/utils/polyfills.d.ts +6 -0
  168. package/lib/typescript/utils/polyfills.d.ts.map +1 -0
  169. package/package.json +11 -3
  170. package/src/__tests__/polyfills.test.ts +30 -0
  171. package/src/__tests__/setup.ts +43 -0
  172. package/src/__tests__/ui/screens/AccountSettingsScreen.test.tsx +8 -8
  173. package/src/assets/icons/OxyServices.tsx +67 -0
  174. package/src/assets/icons/logo_OxyServices.svg +1 -0
  175. package/src/core/index.ts +127 -19
  176. package/src/index.ts +3 -0
  177. package/src/lib/sonner.ts +10 -1
  178. package/src/models/interfaces.ts +1 -2
  179. package/src/node/index.ts +3 -0
  180. package/src/ui/components/GroupedItem.tsx +118 -0
  181. package/src/ui/components/GroupedSection.tsx +45 -0
  182. package/src/ui/components/OxyProvider.tsx +95 -120
  183. package/src/ui/components/ProfileCard.tsx +129 -0
  184. package/src/ui/components/QuickActions.tsx +90 -0
  185. package/src/ui/components/Section.tsx +37 -0
  186. package/src/ui/components/SectionTitle.tsx +31 -0
  187. package/src/ui/components/bottomSheet/index.tsx +13 -11
  188. package/src/ui/components/index.ts +15 -0
  189. package/src/ui/navigation/OxyRouter.tsx +20 -3
  190. package/src/ui/navigation/types.ts +10 -1
  191. package/src/ui/screens/AccountCenterScreen.tsx +188 -159
  192. package/src/ui/screens/AccountManagementDemo.tsx +297 -0
  193. package/src/ui/screens/AccountOverviewScreen.tsx +474 -310
  194. package/src/ui/screens/AccountSettingsScreen.tsx +648 -463
  195. package/src/ui/screens/AccountSwitcherScreen.tsx +385 -449
  196. package/src/ui/screens/AppInfoScreen.tsx +571 -140
  197. package/src/ui/screens/BillingManagementScreen.tsx +589 -0
  198. package/src/ui/screens/FileManagementScreen.tsx +2513 -0
  199. package/src/ui/screens/PremiumSubscriptionScreen.tsx +1628 -0
  200. package/src/ui/screens/ProfileScreen.tsx +101 -7
  201. package/src/ui/screens/SessionManagementScreen.tsx +1 -0
  202. package/src/ui/screens/SignInScreen.tsx +1 -1
  203. package/src/ui/screens/SignUpScreen.tsx +1 -1
  204. package/src/utils/polyfills.ts +34 -0
  205. package/lib/commonjs/lib/sonner.web.js +0 -17
  206. package/lib/commonjs/lib/sonner.web.js.map +0 -1
  207. package/lib/module/lib/sonner.web.js +0 -4
  208. package/lib/module/lib/sonner.web.js.map +0 -1
  209. package/lib/typescript/__tests__/ui/screens/AccountSettingsScreen.test.d.ts +0 -2
  210. package/lib/typescript/__tests__/ui/screens/AccountSettingsScreen.test.d.ts.map +0 -1
  211. package/lib/typescript/lib/sonner.web.d.ts +0 -2
  212. package/lib/typescript/lib/sonner.web.d.ts.map +0 -1
  213. package/src/lib/sonner.web.ts +0 -1
@@ -1,185 +1,134 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useState, useEffect, useRef } from 'react';
2
2
  import {
3
3
  View,
4
4
  Text,
5
- TextInput,
6
5
  TouchableOpacity,
7
6
  StyleSheet,
8
7
  ActivityIndicator,
9
8
  ScrollView,
10
9
  Alert,
11
- Platform,
12
- Switch,
10
+ TextInput,
11
+ Animated,
13
12
  } from 'react-native';
14
13
  import { BaseScreenProps } from '../navigation/types';
15
14
  import { useOxy } from '../context/OxyContext';
16
15
  import Avatar from '../components/Avatar';
16
+ import OxyIcon from '../components/icon/OxyIcon';
17
+ import { Ionicons } from '@expo/vector-icons';
17
18
  import { toast } from '../../lib/sonner';
19
+ import { fontFamilies } from '../styles/fonts';
18
20
 
19
- interface AccountSettingsScreenProps extends BaseScreenProps {
20
- activeTab?: 'profile' | 'password' | 'notifications';
21
- }
22
-
23
- const AccountSettingsScreen: React.FC<AccountSettingsScreenProps> = ({
24
- goBack,
21
+ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
22
+ onClose,
25
23
  theme,
26
- activeTab = 'profile',
24
+ goBack,
25
+ navigate,
27
26
  }) => {
28
27
  const { user, oxyServices, isLoading: authLoading } = useOxy();
29
- const [currentTab, setCurrentTab] = useState<'profile' | 'password' | 'notifications'>(activeTab);
30
28
  const [isLoading, setIsLoading] = useState(false);
31
- const [errorMessage, setErrorMessage] = useState('');
32
- const [successMessage, setSuccessMessage] = useState('');
29
+ const [isSaving, setIsSaving] = useState(false);
30
+
31
+ // Animation refs
32
+ const saveButtonScale = useRef(new Animated.Value(1)).current;
33
33
 
34
- // Profile form state
34
+ // Form state
35
+ const [displayName, setDisplayName] = useState('');
35
36
  const [username, setUsername] = useState('');
36
37
  const [email, setEmail] = useState('');
37
38
  const [bio, setBio] = useState('');
39
+ const [location, setLocation] = useState('');
40
+ const [website, setWebsite] = useState('');
38
41
  const [avatarUrl, setAvatarUrl] = useState('');
39
42
 
40
- // Password form state
41
- const [currentPassword, setCurrentPassword] = useState('');
42
- const [newPassword, setNewPassword] = useState('');
43
- const [confirmPassword, setConfirmPassword] = useState('');
43
+ // Editing states
44
+ const [editingField, setEditingField] = useState<string | null>(null);
44
45
 
45
- // Notification preferences
46
- const [emailNotifications, setEmailNotifications] = useState(true);
47
- const [pushNotifications, setPushNotifications] = useState(true);
46
+ // Temporary input states for inline editing
47
+ const [tempDisplayName, setTempDisplayName] = useState('');
48
+ const [tempUsername, setTempUsername] = useState('');
49
+ const [tempEmail, setTempEmail] = useState('');
50
+ const [tempBio, setTempBio] = useState('');
51
+ const [tempLocation, setTempLocation] = useState('');
52
+ const [tempWebsite, setTempWebsite] = useState('');
48
53
 
49
- // Theme and styling
50
54
  const isDarkTheme = theme === 'dark';
51
- const textColor = isDarkTheme ? '#FFFFFF' : '#000000';
52
- const backgroundColor = isDarkTheme ? '#121212' : '#FFFFFF';
53
- const secondaryBackgroundColor = isDarkTheme ? '#222222' : '#F5F5F5';
54
- const inputBackgroundColor = isDarkTheme ? '#333333' : '#F5F5F5';
55
- const borderColor = isDarkTheme ? '#444444' : '#E0E0E0';
56
- const primaryColor = '#0066CC';
57
- const placeholderColor = isDarkTheme ? '#AAAAAA' : '#999999';
55
+ const backgroundColor = isDarkTheme ? '#121212' : '#f2f2f2';
56
+ const primaryColor = '#007AFF';
57
+
58
+ // Animation functions
59
+ const animateSaveButton = (toValue: number) => {
60
+ Animated.spring(saveButtonScale, {
61
+ toValue,
62
+ useNativeDriver: true,
63
+ tension: 150,
64
+ friction: 8,
65
+ }).start();
66
+ };
58
67
 
59
68
  // Load user data
60
69
  useEffect(() => {
61
70
  if (user) {
71
+ const userDisplayName = typeof user.name === 'string'
72
+ ? user.name
73
+ : user.name?.full || user.name?.first || '';
74
+
75
+ setDisplayName(userDisplayName);
62
76
  setUsername(user.username || '');
63
77
  setEmail(user.email || '');
64
78
  setBio(user.bio || '');
79
+ setLocation(user.location || '');
80
+ setWebsite(user.website || '');
65
81
  setAvatarUrl(user.avatar?.url || '');
66
82
  }
67
83
  }, [user]);
68
84
 
69
- const validateEmail = (email: string) => {
70
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
71
- return emailRegex.test(email);
72
- };
73
-
74
- const handleSaveProfile = async () => {
75
- // Validate inputs
76
- if (!username) {
77
- setErrorMessage('Username is required');
78
- return;
79
- }
80
-
81
- if (email && !validateEmail(email)) {
82
- setErrorMessage('Please enter a valid email address');
83
- return;
84
- }
85
+ const handleSave = async () => {
86
+ if (!user) return;
85
87
 
86
88
  try {
87
- setIsLoading(true);
88
- setErrorMessage('');
89
- setSuccessMessage('');
89
+ setIsSaving(true);
90
+ animateSaveButton(0.95); // Scale down slightly for animation
90
91
 
91
- // Prepare updates object
92
92
  const updates: Record<string, any> = {
93
93
  username,
94
+ email,
94
95
  bio,
96
+ location,
97
+ website,
95
98
  };
96
99
 
97
- if (email) {
98
- updates.email = email;
100
+ // Handle name field
101
+ if (displayName) {
102
+ updates.name = displayName;
99
103
  }
100
104
 
101
- // Only include avatar if it's been changed
102
- if (avatarUrl !== user?.avatar?.url) {
105
+ // Handle avatar
106
+ if (avatarUrl !== user.avatar?.url) {
103
107
  updates.avatar = { url: avatarUrl };
104
108
  }
105
109
 
106
- // Call API to update user
107
- await oxyServices.updateUser(user!.id, updates);
110
+ await oxyServices.updateProfile(updates);
108
111
  toast.success('Profile updated successfully');
112
+
113
+ animateSaveButton(1); // Scale back to normal
114
+
115
+ if (onClose) {
116
+ onClose();
117
+ } else if (goBack) {
118
+ goBack();
119
+ }
109
120
  } catch (error: any) {
110
121
  toast.error(error.message || 'Failed to update profile');
122
+ animateSaveButton(1); // Scale back to normal on error
111
123
  } finally {
112
- setIsLoading(false);
113
- }
114
- };
115
-
116
- const handleChangePassword = async () => {
117
- // Validate inputs
118
- if (!currentPassword || !newPassword || !confirmPassword) {
119
- toast.error('Please fill in all password fields');
120
- return;
121
- }
122
-
123
- if (newPassword !== confirmPassword) {
124
- toast.error('New passwords do not match');
125
- return;
126
- }
127
-
128
- if (newPassword.length < 8) {
129
- toast.error('Password must be at least 8 characters long');
130
- return;
131
- }
132
-
133
- try {
134
- setIsLoading(true);
135
- setErrorMessage('');
136
- setSuccessMessage('');
137
-
138
- // Call API to update password
139
- await oxyServices.updateUser(user!.id, {
140
- currentPassword,
141
- password: newPassword,
142
- });
143
-
144
- // Clear form fields after successful update
145
- setCurrentPassword('');
146
- setNewPassword('');
147
- setConfirmPassword('');
148
- toast.success('Password updated successfully');
149
- } catch (error: any) {
150
- toast.error(error.message || 'Failed to update password');
151
- } finally {
152
- setIsLoading(false);
153
- }
154
- };
155
-
156
- const handleSaveNotifications = async () => {
157
- try {
158
- setIsLoading(true);
159
- setErrorMessage('');
160
- setSuccessMessage('');
161
-
162
- // Call API to update notification preferences
163
- await oxyServices.updateUser(user!.id, {
164
- notificationPreferences: {
165
- email: emailNotifications,
166
- push: pushNotifications,
167
- },
168
- });
169
- toast.success('Notification preferences updated successfully');
170
- } catch (error: any) {
171
- toast.error(error.message || 'Failed to update notification preferences');
172
- } finally {
173
- setIsLoading(false);
124
+ setIsSaving(false);
174
125
  }
175
126
  };
176
127
 
177
128
  const handleAvatarUpdate = () => {
178
- // In a real app, this would open an image picker
179
- // For now, we'll use a mock URL to demonstrate the concept
180
129
  Alert.alert(
181
130
  'Update Avatar',
182
- 'This would open an image picker in a real app. For now, we\'ll use a mock URL.',
131
+ 'Choose how you want to update your profile picture',
183
132
  [
184
133
  {
185
134
  text: 'Cancel',
@@ -188,291 +137,439 @@ const AccountSettingsScreen: React.FC<AccountSettingsScreenProps> = ({
188
137
  {
189
138
  text: 'Use Mock URL',
190
139
  onPress: () => {
191
- const mockUrl = `https://ui-avatars.com/api/?name=${username}&background=random`;
140
+ const mockUrl = `https://ui-avatars.com/api/?name=${displayName || username}&background=random`;
192
141
  setAvatarUrl(mockUrl);
193
142
  },
194
143
  },
144
+ {
145
+ text: 'Remove Avatar',
146
+ onPress: () => setAvatarUrl(''),
147
+ style: 'destructive',
148
+ },
195
149
  ]
196
150
  );
197
151
  };
198
152
 
199
- if (authLoading || !user) {
200
- return (
201
- <View style={[styles.container, { backgroundColor, justifyContent: 'center' }]}>
202
- <ActivityIndicator size="large" color={primaryColor} />
203
- </View>
204
- );
205
- }
206
-
207
- const renderProfileTab = () => (
208
- <View style={styles.tabContent}>
209
- <View style={styles.avatarSection}>
210
- <Avatar
211
- uri={avatarUrl}
212
- name={username}
213
- size={100}
214
- theme={theme}
215
- />
216
- <TouchableOpacity
217
- style={[styles.changeAvatarButton, { backgroundColor: primaryColor }]}
218
- onPress={handleAvatarUpdate}
219
- >
220
- <Text style={styles.changeAvatarText}>Change Avatar</Text>
221
- </TouchableOpacity>
222
- </View>
153
+ const startEditing = (type: string, currentValue: string) => {
154
+ switch (type) {
155
+ case 'displayName':
156
+ setTempDisplayName(currentValue);
157
+ break;
158
+ case 'username':
159
+ setTempUsername(currentValue);
160
+ break;
161
+ case 'email':
162
+ setTempEmail(currentValue);
163
+ break;
164
+ case 'bio':
165
+ setTempBio(currentValue);
166
+ break;
167
+ case 'location':
168
+ setTempLocation(currentValue);
169
+ break;
170
+ case 'website':
171
+ setTempWebsite(currentValue);
172
+ break;
173
+ }
174
+ setEditingField(type);
175
+ };
223
176
 
224
- <View style={styles.inputContainer}>
225
- <Text style={[styles.label, { color: textColor }]}>Username</Text>
226
- <TextInput
227
- style={[
228
- styles.input,
229
- { backgroundColor: inputBackgroundColor, borderColor, color: textColor }
230
- ]}
231
- placeholder="Enter your username"
232
- placeholderTextColor={placeholderColor}
233
- value={username}
234
- onChangeText={setUsername}
235
- testID="username-input"
236
- />
237
- </View>
177
+ const saveField = (type: string) => {
178
+ animateSaveButton(0.95); // Scale down slightly for animation
179
+
180
+ switch (type) {
181
+ case 'displayName':
182
+ setDisplayName(tempDisplayName);
183
+ break;
184
+ case 'username':
185
+ setUsername(tempUsername);
186
+ break;
187
+ case 'email':
188
+ setEmail(tempEmail);
189
+ break;
190
+ case 'bio':
191
+ setBio(tempBio);
192
+ break;
193
+ case 'location':
194
+ setLocation(tempLocation);
195
+ break;
196
+ case 'website':
197
+ setWebsite(tempWebsite);
198
+ break;
199
+ }
200
+
201
+ // Brief delay for animation, then reset and close editing
202
+ setTimeout(() => {
203
+ animateSaveButton(1);
204
+ setEditingField(null);
205
+ }, 150);
206
+ };
238
207
 
239
- <View style={styles.inputContainer}>
240
- <Text style={[styles.label, { color: textColor }]}>Email</Text>
241
- <TextInput
242
- style={[
243
- styles.input,
244
- { backgroundColor: inputBackgroundColor, borderColor, color: textColor }
245
- ]}
246
- placeholder="Enter your email"
247
- placeholderTextColor={placeholderColor}
248
- value={email}
249
- onChangeText={setEmail}
250
- keyboardType="email-address"
251
- testID="email-input"
252
- />
253
- </View>
208
+ const cancelEditing = () => {
209
+ setEditingField(null);
210
+ };
254
211
 
255
- <View style={styles.inputContainer}>
256
- <Text style={[styles.label, { color: textColor }]}>Bio</Text>
257
- <TextInput
258
- style={[
259
- styles.textArea,
260
- { backgroundColor: inputBackgroundColor, borderColor, color: textColor }
261
- ]}
262
- placeholder="Tell us about yourself"
263
- placeholderTextColor={placeholderColor}
264
- value={bio}
265
- onChangeText={setBio}
266
- multiline
267
- numberOfLines={4}
268
- testID="bio-input"
269
- />
270
- </View>
212
+ const getFieldLabel = (type: string) => {
213
+ const labels = {
214
+ displayName: 'Display Name',
215
+ username: 'Username',
216
+ email: 'Email',
217
+ bio: 'Bio',
218
+ location: 'Location',
219
+ website: 'Website'
220
+ };
221
+ return labels[type as keyof typeof labels] || 'Field';
222
+ };
271
223
 
272
- <TouchableOpacity
273
- style={[styles.saveButton, { backgroundColor: primaryColor, opacity: isLoading ? 0.7 : 1 }]}
274
- onPress={handleSaveProfile}
275
- disabled={isLoading}
276
- testID="save-profile-button"
277
- >
278
- {isLoading ? (
279
- <ActivityIndicator color="#FFFFFF" size="small" />
280
- ) : (
281
- <Text style={styles.saveButtonText}>Save Profile</Text>
282
- )}
283
- </TouchableOpacity>
284
- </View>
285
- );
224
+ const getFieldIcon = (type: string) => {
225
+ const icons = {
226
+ displayName: { name: 'person', color: '#007AFF' },
227
+ username: { name: 'at', color: '#5856D6' },
228
+ email: { name: 'mail', color: '#FF9500' },
229
+ bio: { name: 'document-text', color: '#34C759' },
230
+ location: { name: 'location', color: '#FF3B30' },
231
+ website: { name: 'link', color: '#32D74B' }
232
+ };
233
+ return icons[type as keyof typeof icons] || { name: 'person', color: '#007AFF' };
234
+ };
286
235
 
287
- const renderPasswordTab = () => (
288
- <View style={styles.tabContent}>
289
- <View style={styles.inputContainer}>
290
- <Text style={[styles.label, { color: textColor }]}>Current Password</Text>
291
- <TextInput
292
- style={[
293
- styles.input,
294
- { backgroundColor: inputBackgroundColor, borderColor, color: textColor }
295
- ]}
296
- placeholder="Enter your current password"
297
- placeholderTextColor={placeholderColor}
298
- value={currentPassword}
299
- onChangeText={setCurrentPassword}
300
- secureTextEntry
301
- testID="current-password-input"
302
- />
303
- </View>
236
+ const renderEditingField = (type: string) => {
237
+ const fieldConfig = {
238
+ displayName: { label: 'Display Name', value: displayName, placeholder: 'Enter your display name', icon: 'person', color: '#007AFF', multiline: false, keyboardType: 'default' as const },
239
+ username: { label: 'Username', value: username, placeholder: 'Choose a username', icon: 'at', color: '#5856D6', multiline: false, keyboardType: 'default' as const },
240
+ email: { label: 'Email', value: email, placeholder: 'Enter your email address', icon: 'mail', color: '#FF9500', multiline: false, keyboardType: 'email-address' as const },
241
+ bio: { label: 'Bio', value: bio, placeholder: 'Tell people about yourself...', icon: 'document-text', color: '#34C759', multiline: true, keyboardType: 'default' as const },
242
+ location: { label: 'Location', value: location, placeholder: 'Enter your location', icon: 'location', color: '#FF3B30', multiline: false, keyboardType: 'default' as const },
243
+ website: { label: 'Website', value: website, placeholder: 'Enter your website URL', icon: 'link', color: '#32D74B', multiline: false, keyboardType: 'url' as const }
244
+ };
245
+
246
+ const config = fieldConfig[type as keyof typeof fieldConfig];
247
+ if (!config) return null;
248
+
249
+ const tempValue = (() => {
250
+ switch (type) {
251
+ case 'displayName': return tempDisplayName;
252
+ case 'username': return tempUsername;
253
+ case 'email': return tempEmail;
254
+ case 'bio': return tempBio;
255
+ case 'location': return tempLocation;
256
+ case 'website': return tempWebsite;
257
+ default: return '';
258
+ }
259
+ })();
260
+
261
+ const setTempValue = (text: string) => {
262
+ switch (type) {
263
+ case 'displayName': setTempDisplayName(text); break;
264
+ case 'username': setTempUsername(text); break;
265
+ case 'email': setTempEmail(text); break;
266
+ case 'bio': setTempBio(text); break;
267
+ case 'location': setTempLocation(text); break;
268
+ case 'website': setTempWebsite(text); break;
269
+ }
270
+ };
304
271
 
305
- <View style={styles.inputContainer}>
306
- <Text style={[styles.label, { color: textColor }]}>New Password</Text>
307
- <TextInput
308
- style={[
309
- styles.input,
310
- { backgroundColor: inputBackgroundColor, borderColor, color: textColor }
311
- ]}
312
- placeholder="Enter your new password"
313
- placeholderTextColor={placeholderColor}
314
- value={newPassword}
315
- onChangeText={setNewPassword}
316
- secureTextEntry
317
- testID="new-password-input"
318
- />
319
- <Text style={[styles.passwordHint, { color: isDarkTheme ? '#AAAAAA' : '#666666' }]}>
320
- Password must be at least 8 characters long
321
- </Text>
272
+ return (
273
+ <View style={styles.editingFieldContainer}>
274
+ <View style={styles.editingFieldContent}>
275
+ <View style={styles.newValueSection}>
276
+ <Text style={styles.editingFieldLabel}>
277
+ {`Enter ${config.label.toLowerCase()}:`}
278
+ </Text>
279
+ <TextInput
280
+ style={[
281
+ config.multiline ? styles.editingFieldTextArea : styles.editingFieldInput,
282
+ {
283
+ backgroundColor: isDarkTheme ? '#333' : '#fff',
284
+ color: isDarkTheme ? '#fff' : '#000',
285
+ borderColor: primaryColor
286
+ }
287
+ ]}
288
+ value={tempValue}
289
+ onChangeText={setTempValue}
290
+ placeholder={config.placeholder}
291
+ placeholderTextColor={isDarkTheme ? '#aaa' : '#999'}
292
+ multiline={config.multiline}
293
+ numberOfLines={config.multiline ? 6 : 1}
294
+ keyboardType={config.keyboardType}
295
+ autoFocus
296
+ selectionColor={primaryColor}
297
+ />
298
+ </View>
299
+ </View>
322
300
  </View>
301
+ );
302
+ };
323
303
 
324
- <View style={styles.inputContainer}>
325
- <Text style={[styles.label, { color: textColor }]}>Confirm New Password</Text>
326
- <TextInput
327
- style={[
328
- styles.input,
329
- { backgroundColor: inputBackgroundColor, borderColor, color: textColor }
330
- ]}
331
- placeholder="Confirm your new password"
332
- placeholderTextColor={placeholderColor}
333
- value={confirmPassword}
334
- onChangeText={setConfirmPassword}
335
- secureTextEntry
336
- testID="confirm-password-input"
337
- />
338
- </View>
304
+ const renderField = (
305
+ type: string,
306
+ label: string,
307
+ value: string,
308
+ placeholder: string,
309
+ icon: string,
310
+ iconColor: string,
311
+ multiline = false,
312
+ keyboardType: 'default' | 'email-address' | 'url' = 'default',
313
+ isFirst = false,
314
+ isLast = false
315
+ ) => {
316
+ const itemStyles = [
317
+ styles.settingItem,
318
+ isFirst && styles.firstSettingItem,
319
+ isLast && styles.lastSettingItem
320
+ ];
339
321
 
340
- <TouchableOpacity
341
- style={[styles.saveButton, { backgroundColor: primaryColor, opacity: isLoading ? 0.7 : 1 }]}
342
- onPress={handleChangePassword}
343
- disabled={isLoading}
344
- testID="change-password-button"
322
+ return (
323
+ <TouchableOpacity
324
+ style={itemStyles}
325
+ onPress={() => startEditing(type, value)}
345
326
  >
346
- {isLoading ? (
347
- <ActivityIndicator color="#FFFFFF" size="small" />
348
- ) : (
349
- <Text style={styles.saveButtonText}>Change Password</Text>
350
- )}
327
+ <View style={styles.settingInfo}>
328
+ <OxyIcon name={icon} size={20} color={iconColor} style={styles.settingIcon} />
329
+ <View>
330
+ <Text style={styles.settingLabel}>{label}</Text>
331
+ <Text style={styles.settingDescription}>
332
+ {value || placeholder}
333
+ </Text>
334
+ </View>
335
+ </View>
336
+ <OxyIcon name="chevron-forward" size={16} color="#ccc" />
351
337
  </TouchableOpacity>
352
- </View>
353
- );
354
-
355
- const renderNotificationsTab = () => (
356
- <View style={styles.tabContent}>
357
- <View style={styles.settingRow}>
358
- <Text style={[styles.settingLabel, { color: textColor }]}>Email Notifications</Text>
359
- <Switch
360
- value={emailNotifications}
361
- onValueChange={setEmailNotifications}
362
- trackColor={{ false: '#767577', true: primaryColor }}
363
- thumbColor="#f4f3f4"
364
- testID="email-notifications-switch"
365
- />
366
- </View>
338
+ );
339
+ };
367
340
 
368
- <View style={styles.settingRow}>
369
- <Text style={[styles.settingLabel, { color: textColor }]}>Push Notifications</Text>
370
- <Switch
371
- value={pushNotifications}
372
- onValueChange={setPushNotifications}
373
- trackColor={{ false: '#767577', true: primaryColor }}
374
- thumbColor="#f4f3f4"
375
- testID="push-notifications-switch"
376
- />
341
+ if (authLoading || !user) {
342
+ return (
343
+ <View style={[styles.container, { backgroundColor, justifyContent: 'center' }]}>
344
+ <ActivityIndicator size="large" color={primaryColor} />
377
345
  </View>
378
-
379
- <TouchableOpacity
380
- style={[styles.saveButton, { backgroundColor: primaryColor, opacity: isLoading ? 0.7 : 1 }]}
381
- onPress={handleSaveNotifications}
382
- disabled={isLoading}
383
- testID="save-notifications-button"
384
- >
385
- {isLoading ? (
386
- <ActivityIndicator color="#FFFFFF" size="small" />
387
- ) : (
388
- <Text style={styles.saveButtonText}>Save Preferences</Text>
389
- )}
390
- </TouchableOpacity>
391
- </View>
392
- );
346
+ );
347
+ }
393
348
 
394
349
  return (
395
350
  <View style={[styles.container, { backgroundColor }]}>
396
- <ScrollView style={styles.scrollView} contentContainerStyle={styles.scrollContainer}>
397
- <View style={styles.header}>
398
- <TouchableOpacity
399
- style={styles.backButton}
400
- onPress={goBack}
401
- >
402
- <Text style={[styles.backButtonText, { color: primaryColor }]}>Back</Text>
403
- </TouchableOpacity>
404
- <Text style={[styles.title, { color: textColor }]}>Account Settings</Text>
405
- <View style={styles.backButtonPlaceholder} />
406
- </View>
407
-
408
- <View style={[styles.tabsContainer, { borderColor }]}>
409
- <TouchableOpacity
410
- style={[
411
- styles.tabButton,
412
- currentTab === 'profile' && [styles.activeTabButton, { borderColor: primaryColor }]
413
- ]}
414
- onPress={() => setCurrentTab('profile')}
415
- >
416
- <Text
417
- style={[
418
- styles.tabButtonText,
419
- { color: currentTab === 'profile' ? primaryColor : textColor }
420
- ]}
421
- >
422
- Profile
423
- </Text>
424
- </TouchableOpacity>
425
-
426
- <TouchableOpacity
427
- style={[
428
- styles.tabButton,
429
- currentTab === 'password' && [styles.activeTabButton, { borderColor: primaryColor }]
430
- ]}
431
- onPress={() => setCurrentTab('password')}
432
- >
433
- <Text
434
- style={[
435
- styles.tabButtonText,
436
- { color: currentTab === 'password' ? primaryColor : textColor }
437
- ]}
438
- >
439
- Password
440
- </Text>
441
- </TouchableOpacity>
442
-
443
- <TouchableOpacity
444
- style={[
445
- styles.tabButton,
446
- currentTab === 'notifications' && [styles.activeTabButton, { borderColor: primaryColor }]
447
- ]}
448
- onPress={() => setCurrentTab('notifications')}
449
- >
450
- <Text
451
- style={[
452
- styles.tabButtonText,
453
- { color: currentTab === 'notifications' ? primaryColor : textColor }
454
- ]}
455
- >
456
- Notifications
457
- </Text>
458
- </TouchableOpacity>
459
- </View>
460
-
461
- {errorMessage ? (
462
- <View style={styles.errorContainer}>
463
- <Text style={styles.errorText}>{errorMessage}</Text>
351
+ {/* Header */}
352
+ <View style={styles.header}>
353
+ {editingField ? (
354
+ <View style={styles.editingHeader}>
355
+ <View style={styles.editingHeaderTop}>
356
+ <TouchableOpacity style={styles.cancelButton} onPress={cancelEditing}>
357
+ <Ionicons name="close" size={24} color="#666" />
358
+ </TouchableOpacity>
359
+ <Animated.View style={{ transform: [{ scale: saveButtonScale }] }}>
360
+ <TouchableOpacity
361
+ style={[
362
+ styles.saveHeaderButton,
363
+ {
364
+ opacity: isSaving ? 0.7 : 1,
365
+ backgroundColor: editingField ? getFieldIcon(editingField).color : '#007AFF'
366
+ }
367
+ ]}
368
+ onPress={() => saveField(editingField)}
369
+ disabled={isSaving}
370
+ >
371
+ {isSaving ? (
372
+ <ActivityIndicator size="small" color="#fff" />
373
+ ) : (
374
+ <Text style={styles.saveButtonText}>Save</Text>
375
+ )}
376
+ </TouchableOpacity>
377
+ </Animated.View>
378
+ </View>
379
+ <View style={styles.editingHeaderBottom}>
380
+ <View style={styles.headerTitleWithIcon}>
381
+ <OxyIcon
382
+ name={getFieldIcon(editingField).name}
383
+ size={50}
384
+ color={getFieldIcon(editingField).color}
385
+ style={styles.headerIcon}
386
+ />
387
+ <Text style={styles.headerTitleLarge}>{getFieldLabel(editingField)}</Text>
388
+ </View>
389
+ </View>
464
390
  </View>
465
- ) : null}
466
-
467
- {successMessage ? (
468
- <View style={styles.successContainer}>
469
- <Text style={styles.successText}>{successMessage}</Text>
391
+ ) : (
392
+ <View style={styles.normalHeader}>
393
+ <TouchableOpacity style={styles.cancelButton} onPress={onClose || goBack}>
394
+ <Ionicons name="close" size={24} color="#666" />
395
+ </TouchableOpacity>
396
+ <Text style={styles.headerTitle}>Account Settings</Text>
397
+ <Animated.View style={{ transform: [{ scale: saveButtonScale }] }}>
398
+ <TouchableOpacity
399
+ style={[styles.saveIconButton, { opacity: isSaving ? 0.7 : 1 }]}
400
+ onPress={handleSave}
401
+ disabled={isSaving}
402
+ >
403
+ {isSaving ? (
404
+ <ActivityIndicator size="small" color={primaryColor} />
405
+ ) : (
406
+ <Ionicons name="checkmark" size={24} color={primaryColor} />
407
+ )}
408
+ </TouchableOpacity>
409
+ </Animated.View>
470
410
  </View>
471
- ) : null}
411
+ )}
412
+ </View>
472
413
 
473
- {currentTab === 'profile' && renderProfileTab()}
474
- {currentTab === 'password' && renderPasswordTab()}
475
- {currentTab === 'notifications' && renderNotificationsTab()}
414
+ <ScrollView style={editingField ? styles.contentEditing : styles.content}>
415
+ {editingField ? (
416
+ // Show only the editing interface when editing
417
+ <View style={styles.editingOnlyContainer}>
418
+ {renderEditingField(editingField)}
419
+ </View>
420
+ ) : (
421
+ // Show all settings when not editing
422
+ <>
423
+ {/* Profile Picture Section */}
424
+ <View style={styles.section}>
425
+ <Text style={styles.sectionTitle}>Profile Picture</Text>
426
+
427
+ <TouchableOpacity
428
+ style={[styles.settingItem, styles.firstSettingItem, styles.lastSettingItem]}
429
+ onPress={handleAvatarUpdate}
430
+ >
431
+ <View style={styles.userIcon}>
432
+ <Avatar
433
+ uri={avatarUrl}
434
+ name={displayName || username}
435
+ size={50}
436
+ theme={theme}
437
+ />
438
+ </View>
439
+ <View style={styles.settingInfo}>
440
+ <View>
441
+ <Text style={styles.settingLabel}>Profile Photo</Text>
442
+ <Text style={styles.settingDescription}>
443
+ {avatarUrl ? 'Tap to change your profile picture' : 'Tap to add a profile picture'}
444
+ </Text>
445
+ </View>
446
+ </View>
447
+ <OxyIcon name="chevron-forward" size={16} color="#ccc" />
448
+ </TouchableOpacity>
449
+ </View>
450
+
451
+ {/* Basic Information */}
452
+ <View style={styles.section}>
453
+ <Text style={styles.sectionTitle}>Basic Information</Text>
454
+
455
+ {renderField(
456
+ 'displayName',
457
+ 'Display Name',
458
+ displayName,
459
+ 'Add your display name',
460
+ 'person',
461
+ '#007AFF',
462
+ false,
463
+ 'default',
464
+ true,
465
+ false
466
+ )}
467
+
468
+ {renderField(
469
+ 'username',
470
+ 'Username',
471
+ username,
472
+ 'Choose a username',
473
+ 'at',
474
+ '#5856D6',
475
+ false,
476
+ 'default',
477
+ false,
478
+ false
479
+ )}
480
+
481
+ {renderField(
482
+ 'email',
483
+ 'Email',
484
+ email,
485
+ 'Add your email address',
486
+ 'mail',
487
+ '#FF9500',
488
+ false,
489
+ 'email-address',
490
+ false,
491
+ true
492
+ )}
493
+ </View>
494
+
495
+ {/* About You */}
496
+ <View style={styles.section}>
497
+ <Text style={styles.sectionTitle}>About You</Text>
498
+
499
+ {renderField(
500
+ 'bio',
501
+ 'Bio',
502
+ bio,
503
+ 'Tell people about yourself',
504
+ 'document-text',
505
+ '#34C759',
506
+ true,
507
+ 'default',
508
+ true,
509
+ false
510
+ )}
511
+
512
+ {renderField(
513
+ 'location',
514
+ 'Location',
515
+ location,
516
+ 'Add your location',
517
+ 'location',
518
+ '#FF3B30',
519
+ false,
520
+ 'default',
521
+ false,
522
+ false
523
+ )}
524
+
525
+ {renderField(
526
+ 'website',
527
+ 'Website',
528
+ website,
529
+ 'Add your website',
530
+ 'link',
531
+ '#32D74B',
532
+ false,
533
+ 'url',
534
+ false,
535
+ true
536
+ )}
537
+ </View>
538
+
539
+ {/* Quick Actions */}
540
+ <View style={styles.section}>
541
+ <Text style={styles.sectionTitle}>Quick Actions</Text>
542
+
543
+ <TouchableOpacity
544
+ style={[styles.settingItem, styles.firstSettingItem]}
545
+ onPress={() => toast.info('Privacy settings coming soon!')}
546
+ >
547
+ <View style={styles.settingInfo}>
548
+ <OxyIcon name="shield-checkmark" size={20} color="#8E8E93" style={styles.settingIcon} />
549
+ <View>
550
+ <Text style={styles.settingLabel}>Privacy Settings</Text>
551
+ <Text style={styles.settingDescription}>Control who can see your profile</Text>
552
+ </View>
553
+ </View>
554
+ <OxyIcon name="chevron-forward" size={16} color="#ccc" />
555
+ </TouchableOpacity>
556
+
557
+ <TouchableOpacity
558
+ style={[styles.settingItem, styles.lastSettingItem]}
559
+ onPress={() => toast.info('Account verification coming soon!')}
560
+ >
561
+ <View style={styles.settingInfo}>
562
+ <OxyIcon name="checkmark-circle" size={20} color="#30D158" style={styles.settingIcon} />
563
+ <View>
564
+ <Text style={styles.settingLabel}>Verify Account</Text>
565
+ <Text style={styles.settingDescription}>Get a verified badge</Text>
566
+ </View>
567
+ </View>
568
+ <OxyIcon name="chevron-forward" size={16} color="#ccc" />
569
+ </TouchableOpacity>
570
+ </View>
571
+ </>
572
+ )}
476
573
  </ScrollView>
477
574
  </View>
478
575
  );
@@ -481,137 +578,225 @@ const AccountSettingsScreen: React.FC<AccountSettingsScreenProps> = ({
481
578
  const styles = StyleSheet.create({
482
579
  container: {
483
580
  flex: 1,
581
+ backgroundColor: '#f2f2f2',
484
582
  },
485
- scrollView: {
486
- flex: 1,
583
+ header: {
584
+ paddingHorizontal: 20,
585
+ paddingVertical: 10,
586
+ backgroundColor: '#fff',
587
+ borderBottomWidth: 1,
588
+ borderBottomColor: '#e0e0e0',
487
589
  },
488
- scrollContainer: {
489
- padding: 20,
590
+ normalHeader: {
591
+ flexDirection: 'row',
592
+ justifyContent: 'space-between',
593
+ alignItems: 'center',
490
594
  },
491
- header: {
595
+ editingHeader: {
596
+ flexDirection: 'column',
597
+ },
598
+ editingHeaderTop: {
492
599
  flexDirection: 'row',
493
600
  justifyContent: 'space-between',
494
601
  alignItems: 'center',
495
- marginBottom: 24,
602
+ marginBottom: 16,
496
603
  },
497
- title: {
604
+ editingHeaderBottom: {
605
+ flexDirection: 'row',
606
+ alignItems: 'center',
607
+ justifyContent: 'flex-start',
608
+ },
609
+ headerTitle: {
498
610
  fontSize: 24,
499
611
  fontWeight: 'bold',
612
+ color: '#000',
613
+ fontFamily: fontFamilies.phuduBold,
614
+ },
615
+ headerTitleWithIcon: {
616
+ flexDirection: 'column',
617
+ alignItems: 'flex-start',
618
+ flex: 1,
619
+ justifyContent: 'flex-start',
620
+ maxWidth: '90%',
621
+ },
622
+ headerTitleLarge: {
623
+ fontSize: 48,
624
+ fontWeight: '800',
625
+ color: '#000',
626
+ fontFamily: fontFamilies.phuduExtraBold,
627
+ textAlign: 'left',
628
+ },
629
+ headerIcon: {
630
+ marginBottom: 2,
500
631
  },
501
- backButton: {
502
- padding: 10,
632
+ cancelButton: {
633
+ padding: 5,
503
634
  },
504
- backButtonText: {
635
+ saveHeaderButton: {
636
+ paddingHorizontal: 16,
637
+ paddingVertical: 8,
638
+ borderRadius: 20,
639
+ minWidth: 60,
640
+ alignItems: 'center',
641
+ justifyContent: 'center',
642
+ },
643
+ saveIconButton: {
644
+ padding: 5,
645
+ },
646
+ saveButtonText: {
647
+ color: '#fff',
505
648
  fontSize: 16,
506
649
  fontWeight: '600',
650
+ fontFamily: fontFamilies.phuduSemiBold,
651
+ },
652
+ content: {
653
+ flex: 1,
654
+ padding: 16,
507
655
  },
508
- backButtonPlaceholder: {
509
- width: 40,
656
+ contentEditing: {
657
+ flex: 1,
658
+ padding: 0,
510
659
  },
511
- tabsContainer: {
512
- flexDirection: 'row',
660
+ section: {
513
661
  marginBottom: 24,
514
- borderBottomWidth: 1,
515
662
  },
516
- tabButton: {
517
- flex: 1,
663
+ sectionTitle: {
664
+ fontSize: 16,
665
+ fontWeight: '600',
666
+ color: '#333',
667
+ marginBottom: 12,
668
+ fontFamily: fontFamilies.phuduSemiBold,
669
+ },
670
+ settingItem: {
671
+ backgroundColor: '#fff',
672
+ padding: 16,
673
+ flexDirection: 'row',
674
+ alignItems: 'center',
675
+ justifyContent: 'space-between',
676
+ marginBottom: 2,
677
+ },
678
+ firstSettingItem: {
679
+ borderTopLeftRadius: 24,
680
+ borderTopRightRadius: 24,
681
+ },
682
+ lastSettingItem: {
683
+ borderBottomLeftRadius: 24,
684
+ borderBottomRightRadius: 24,
685
+ marginBottom: 8,
686
+ },
687
+ settingInfo: {
688
+ flexDirection: 'row',
518
689
  alignItems: 'center',
519
- paddingVertical: 12,
690
+ flex: 1,
520
691
  },
521
- activeTabButton: {
522
- borderBottomWidth: 2,
692
+ settingIcon: {
693
+ marginRight: 12,
523
694
  },
524
- tabButtonText: {
695
+ settingLabel: {
525
696
  fontSize: 16,
526
697
  fontWeight: '500',
698
+ color: '#333',
699
+ marginBottom: 2,
527
700
  },
528
- tabContent: {
529
- marginBottom: 24,
701
+ settingDescription: {
702
+ fontSize: 14,
703
+ color: '#666',
530
704
  },
531
- avatarSection: {
532
- alignItems: 'center',
533
- marginBottom: 24,
705
+ userIcon: {
706
+ marginRight: 12,
534
707
  },
535
- changeAvatarButton: {
536
- marginTop: 12,
537
- paddingVertical: 8,
538
- paddingHorizontal: 16,
539
- borderRadius: 20,
708
+ // Inline editing styles
709
+ editingContainer: {
710
+ flex: 1,
540
711
  },
541
- changeAvatarText: {
542
- color: '#FFFFFF',
543
- fontWeight: '600',
712
+ editingActions: {
713
+ flexDirection: 'row',
714
+ alignItems: 'center',
544
715
  },
545
- inputContainer: {
546
- marginBottom: 16,
716
+ editingButton: {
717
+ padding: 8,
547
718
  },
548
- label: {
719
+ editingButtonText: {
549
720
  fontSize: 16,
550
- marginBottom: 8,
721
+ fontWeight: '500',
551
722
  },
552
- input: {
553
- height: 50,
554
- borderRadius: 8,
723
+ inlineInput: {
724
+ backgroundColor: '#f8f8f8',
555
725
  borderWidth: 1,
556
- paddingHorizontal: 12,
726
+ borderColor: '#e0e0e0',
727
+ borderRadius: 8,
728
+ padding: 12,
557
729
  fontSize: 16,
730
+ minHeight: 44,
558
731
  },
559
- textArea: {
560
- minHeight: 100,
561
- borderRadius: 8,
732
+ inlineTextArea: {
733
+ backgroundColor: '#f8f8f8',
562
734
  borderWidth: 1,
563
- paddingHorizontal: 12,
564
- paddingTop: 12,
735
+ borderColor: '#e0e0e0',
736
+ borderRadius: 8,
737
+ padding: 12,
565
738
  fontSize: 16,
739
+ minHeight: 100,
566
740
  textAlignVertical: 'top',
567
741
  },
568
- passwordHint: {
569
- fontSize: 14,
570
- marginTop: 4,
742
+ // Editing-only mode styles
743
+ editingOnlyContainer: {
744
+ flex: 1,
571
745
  },
572
- saveButton: {
573
- height: 50,
574
- borderRadius: 25,
575
- alignItems: 'center',
576
- justifyContent: 'center',
577
- marginTop: 16,
746
+ editingFieldContainer: {
747
+ backgroundColor: '#fff',
748
+ padding: 16,
749
+ flex: 1,
578
750
  },
579
- saveButtonText: {
580
- color: '#FFFFFF',
581
- fontSize: 16,
582
- fontWeight: '600',
751
+ editingFieldHeader: {
752
+ marginBottom: 16,
583
753
  },
584
- settingRow: {
754
+ editingFieldTitleContainer: {
585
755
  flexDirection: 'row',
586
- justifyContent: 'space-between',
587
756
  alignItems: 'center',
588
- paddingVertical: 16,
589
- borderBottomWidth: 1,
590
- borderBottomColor: '#E0E0E0',
591
757
  },
592
- settingLabel: {
593
- fontSize: 16,
758
+ editingFieldIcon: {
759
+ marginRight: 12,
594
760
  },
595
- errorContainer: {
596
- backgroundColor: '#FFEBEE',
597
- padding: 16,
598
- borderRadius: 8,
599
- marginBottom: 16,
761
+ editingFieldTitle: {
762
+ fontSize: 20,
763
+ fontWeight: '600',
764
+ color: '#000',
600
765
  },
601
- errorText: {
602
- color: '#D32F2F',
603
- fontSize: 14,
766
+ editingFieldContent: {
767
+ flex: 1,
604
768
  },
605
- successContainer: {
606
- backgroundColor: '#E8F5E9',
769
+ newValueSection: {
770
+ flex: 1,
771
+ },
772
+ editingFieldLabel: {
773
+ fontSize: 16,
774
+ fontWeight: '600',
775
+ color: '#333',
776
+ marginBottom: 12,
777
+ fontFamily: fontFamilies.phuduSemiBold,
778
+ },
779
+ editingFieldInput: {
780
+ backgroundColor: '#fff',
781
+ borderWidth: 2,
782
+ borderColor: '#e0e0e0',
783
+ borderRadius: 12,
607
784
  padding: 16,
608
- borderRadius: 8,
609
- marginBottom: 16,
785
+ fontSize: 17,
786
+ minHeight: 52,
787
+ fontWeight: '400',
610
788
  },
611
- successText: {
612
- color: '#2E7D32',
613
- fontSize: 14,
789
+ editingFieldTextArea: {
790
+ backgroundColor: '#fff',
791
+ borderWidth: 2,
792
+ borderColor: '#e0e0e0',
793
+ borderRadius: 12,
794
+ padding: 16,
795
+ fontSize: 17,
796
+ minHeight: 120,
797
+ textAlignVertical: 'top',
798
+ fontWeight: '400',
614
799
  },
615
800
  });
616
801
 
617
- export default AccountSettingsScreen;
802
+ export default AccountSettingsScreen;