@oxyhq/services 5.13.4 → 5.13.11
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.
- package/lib/commonjs/core/HttpClient.js +1 -1
- package/lib/commonjs/core/HttpClient.js.map +1 -1
- package/lib/commonjs/core/OxyServices.js +91 -30
- package/lib/commonjs/core/OxyServices.js.map +1 -1
- package/lib/commonjs/core/index.js +0 -7
- package/lib/commonjs/core/index.js.map +1 -1
- package/lib/commonjs/i18n/locales/en-US.json +222 -6
- package/lib/commonjs/index.js +0 -7
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/lib/sonner.js.map +1 -1
- package/lib/commonjs/ui/components/GroupedItem.js +24 -22
- package/lib/commonjs/ui/components/GroupedItem.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +35 -14
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/navigation/routes.js +36 -1
- package/lib/commonjs/ui/navigation/routes.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js +150 -5
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +475 -319
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountVerificationScreen.js +217 -0
- package/lib/commonjs/ui/screens/AccountVerificationScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/FileManagementScreen.js +911 -213
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/HelpSupportScreen.js +131 -0
- package/lib/commonjs/ui/screens/HelpSupportScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/HistoryViewScreen.js +258 -0
- package/lib/commonjs/ui/screens/HistoryViewScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/LegalDocumentsScreen.js +211 -0
- package/lib/commonjs/ui/screens/LegalDocumentsScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js +0 -1
- package/lib/commonjs/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +307 -0
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/ProfileScreen.js +1 -7
- package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SavesCollectionsScreen.js +205 -0
- package/lib/commonjs/ui/screens/SavesCollectionsScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/SearchSettingsScreen.js +239 -0
- package/lib/commonjs/ui/screens/SearchSettingsScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/SignInScreen.js +14 -29
- package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
- package/lib/commonjs/utils/asyncUtils.js +1 -0
- package/lib/commonjs/utils/asyncUtils.js.map +1 -1
- package/lib/commonjs/utils/cache.js +4 -4
- package/lib/commonjs/utils/cache.js.map +1 -1
- package/lib/commonjs/utils/index.js +0 -6
- package/lib/commonjs/utils/index.js.map +1 -1
- package/lib/module/core/HttpClient.js +1 -1
- package/lib/module/core/HttpClient.js.map +1 -1
- package/lib/module/core/OxyServices.js +90 -29
- package/lib/module/core/OxyServices.js.map +1 -1
- package/lib/module/core/index.js +1 -1
- package/lib/module/core/index.js.map +1 -1
- package/lib/module/i18n/locales/en-US.json +222 -6
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/lib/sonner.js.map +1 -1
- package/lib/module/ui/components/GroupedItem.js +24 -22
- package/lib/module/ui/components/GroupedItem.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +40 -17
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/navigation/routes.js +36 -1
- package/lib/module/ui/navigation/routes.js.map +1 -1
- package/lib/module/ui/screens/AccountOverviewScreen.js +151 -6
- package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +475 -319
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountVerificationScreen.js +212 -0
- package/lib/module/ui/screens/AccountVerificationScreen.js.map +1 -0
- package/lib/module/ui/screens/FileManagementScreen.js +913 -212
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/HelpSupportScreen.js +126 -0
- package/lib/module/ui/screens/HelpSupportScreen.js.map +1 -0
- package/lib/module/ui/screens/HistoryViewScreen.js +253 -0
- package/lib/module/ui/screens/HistoryViewScreen.js.map +1 -0
- package/lib/module/ui/screens/LegalDocumentsScreen.js +206 -0
- package/lib/module/ui/screens/LegalDocumentsScreen.js.map +1 -0
- package/lib/module/ui/screens/PremiumSubscriptionScreen.js +0 -1
- package/lib/module/ui/screens/PremiumSubscriptionScreen.js.map +1 -1
- package/lib/module/ui/screens/PrivacySettingsScreen.js +302 -0
- package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -0
- package/lib/module/ui/screens/ProfileScreen.js +1 -7
- package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/module/ui/screens/SavesCollectionsScreen.js +200 -0
- package/lib/module/ui/screens/SavesCollectionsScreen.js.map +1 -0
- package/lib/module/ui/screens/SearchSettingsScreen.js +234 -0
- package/lib/module/ui/screens/SearchSettingsScreen.js.map +1 -0
- package/lib/module/ui/screens/SignInScreen.js +14 -29
- package/lib/module/ui/screens/SignInScreen.js.map +1 -1
- package/lib/module/utils/asyncUtils.js +1 -0
- package/lib/module/utils/asyncUtils.js.map +1 -1
- package/lib/module/utils/cache.js +3 -3
- package/lib/module/utils/cache.js.map +1 -1
- package/lib/module/utils/index.js +1 -1
- package/lib/module/utils/index.js.map +1 -1
- package/lib/typescript/core/OxyServices.d.ts +35 -23
- package/lib/typescript/core/OxyServices.d.ts.map +1 -1
- package/lib/typescript/core/index.d.ts +1 -1
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +1 -1
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/lib/sonner.d.ts +1 -0
- package/lib/typescript/lib/sonner.d.ts.map +1 -1
- package/lib/typescript/types/expo-document-picker.d.ts +36 -0
- package/lib/typescript/ui/components/GroupedItem.d.ts.map +1 -1
- package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/ui/navigation/routes.d.ts +1 -1
- package/lib/typescript/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountVerificationScreen.d.ts +5 -0
- package/lib/typescript/ui/screens/AccountVerificationScreen.d.ts.map +1 -0
- package/lib/typescript/ui/screens/FileManagementScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/HelpSupportScreen.d.ts +5 -0
- package/lib/typescript/ui/screens/HelpSupportScreen.d.ts.map +1 -0
- package/lib/typescript/ui/screens/HistoryViewScreen.d.ts +5 -0
- package/lib/typescript/ui/screens/HistoryViewScreen.d.ts.map +1 -0
- package/lib/typescript/ui/screens/LegalDocumentsScreen.d.ts +5 -0
- package/lib/typescript/ui/screens/LegalDocumentsScreen.d.ts.map +1 -0
- package/lib/typescript/ui/screens/PremiumSubscriptionScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/PrivacySettingsScreen.d.ts +5 -0
- package/lib/typescript/ui/screens/PrivacySettingsScreen.d.ts.map +1 -0
- package/lib/typescript/ui/screens/ProfileScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SavesCollectionsScreen.d.ts +5 -0
- package/lib/typescript/ui/screens/SavesCollectionsScreen.d.ts.map +1 -0
- package/lib/typescript/ui/screens/SearchSettingsScreen.d.ts +5 -0
- package/lib/typescript/ui/screens/SearchSettingsScreen.d.ts.map +1 -0
- package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
- package/lib/typescript/utils/asyncUtils.d.ts.map +1 -1
- package/lib/typescript/utils/cache.d.ts +3 -3
- package/lib/typescript/utils/cache.d.ts.map +1 -1
- package/lib/typescript/utils/index.d.ts +1 -1
- package/lib/typescript/utils/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/core/HttpClient.ts +1 -1
- package/src/core/OxyServices.ts +88 -30
- package/src/core/index.ts +1 -1
- package/src/i18n/locales/en-US.json +222 -6
- package/src/index.ts +1 -1
- package/src/lib/sonner.ts +1 -0
- package/src/types/expo-document-picker.d.ts +36 -0
- package/src/ui/components/GroupedItem.tsx +23 -21
- package/src/ui/components/OxyProvider.tsx +33 -11
- package/src/ui/navigation/routes.ts +42 -0
- package/src/ui/screens/AccountOverviewScreen.tsx +175 -5
- package/src/ui/screens/AccountSettingsScreen.tsx +521 -360
- package/src/ui/screens/AccountVerificationScreen.tsx +235 -0
- package/src/ui/screens/FileManagementScreen.tsx +934 -208
- package/src/ui/screens/HelpSupportScreen.tsx +143 -0
- package/src/ui/screens/HistoryViewScreen.tsx +280 -0
- package/src/ui/screens/LegalDocumentsScreen.tsx +220 -0
- package/src/ui/screens/PremiumSubscriptionScreen.tsx +0 -1
- package/src/ui/screens/PrivacySettingsScreen.tsx +332 -0
- package/src/ui/screens/ProfileScreen.tsx +1 -8
- package/src/ui/screens/SavesCollectionsScreen.tsx +222 -0
- package/src/ui/screens/SearchSettingsScreen.tsx +219 -0
- package/src/ui/screens/SignInScreen.tsx +19 -35
- package/src/utils/asyncUtils.ts +1 -0
- package/src/utils/cache.ts +3 -3
- package/src/utils/index.ts +1 -1
- package/lib/commonjs/ui/components/StepBasedScreen.README.md +0 -337
- package/lib/commonjs/ui/components/internal/TextField.md +0 -436
- package/lib/commonjs/ui/styles/FONTS.md +0 -126
- package/lib/module/ui/components/StepBasedScreen.README.md +0 -337
- package/lib/module/ui/components/internal/TextField.md +0 -436
- package/lib/module/ui/styles/FONTS.md +0 -126
- package/src/ui/components/StepBasedScreen.README.md +0 -337
- package/src/ui/components/internal/TextField.md +0 -436
- package/src/ui/styles/FONTS.md +0 -126
|
@@ -39,11 +39,18 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
39
39
|
goBack,
|
|
40
40
|
navigate,
|
|
41
41
|
}) => {
|
|
42
|
-
const { user, oxyServices, isLoading: authLoading, isAuthenticated, showBottomSheet, activeSessionId } = useOxy();
|
|
42
|
+
const { user: userFromContext, oxyServices, isLoading: authLoading, isAuthenticated, showBottomSheet, activeSessionId } = useOxy();
|
|
43
43
|
const { t } = useI18n();
|
|
44
44
|
const updateUser = useAuthStore((state) => state.updateUser);
|
|
45
|
+
// Get user directly from store to ensure reactivity to avatar changes
|
|
46
|
+
const user = useAuthStore((state) => state.user) || userFromContext;
|
|
45
47
|
const [isLoading, setIsLoading] = useState(false);
|
|
46
48
|
const [isSaving, setIsSaving] = useState(false);
|
|
49
|
+
const [isUpdatingAvatar, setIsUpdatingAvatar] = useState(false);
|
|
50
|
+
const [optimisticAvatarId, setOptimisticAvatarId] = useState<string | null>(null);
|
|
51
|
+
const scrollViewRef = useRef<ScrollView>(null);
|
|
52
|
+
const avatarSectionRef = useRef<View>(null);
|
|
53
|
+
const [avatarSectionY, setAvatarSectionY] = useState<number | null>(null);
|
|
47
54
|
|
|
48
55
|
// Two-Factor (TOTP) state
|
|
49
56
|
const [totpSetupUrl, setTotpSetupUrl] = useState<string | null>(null);
|
|
@@ -69,7 +76,6 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
69
76
|
// Editing states
|
|
70
77
|
const [editingField, setEditingField] = useState<string | null>(null);
|
|
71
78
|
|
|
72
|
-
// Temporary input states for inline editing
|
|
73
79
|
const [tempDisplayName, setTempDisplayName] = useState('');
|
|
74
80
|
const [tempLastName, setTempLastName] = useState('');
|
|
75
81
|
const [tempUsername, setTempUsername] = useState('');
|
|
@@ -117,84 +123,123 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
117
123
|
}, [theme]);
|
|
118
124
|
|
|
119
125
|
// Memoize animation function to prevent recreation on every render
|
|
120
|
-
const animateSaveButton = useCallback((toValue: number) => {
|
|
126
|
+
const animateSaveButton = useCallback((toValue: number, onComplete?: () => void) => {
|
|
121
127
|
Animated.spring(saveButtonScale, {
|
|
122
128
|
toValue,
|
|
123
129
|
useNativeDriver: Platform.OS !== 'web',
|
|
124
130
|
tension: 150,
|
|
125
131
|
friction: 8,
|
|
126
|
-
}).start()
|
|
132
|
+
}).start(onComplete ? (finished) => {
|
|
133
|
+
if (finished) {
|
|
134
|
+
onComplete();
|
|
135
|
+
}
|
|
136
|
+
} : undefined);
|
|
127
137
|
}, [saveButtonScale]);
|
|
128
138
|
|
|
129
|
-
//
|
|
139
|
+
// Track initialization to prevent unnecessary resets
|
|
140
|
+
const isInitializedRef = useRef(false);
|
|
141
|
+
const previousUserIdRef = useRef<string | null>(null);
|
|
142
|
+
const previousAvatarRef = useRef<string | null>(null);
|
|
143
|
+
|
|
144
|
+
// Load user data - only reset fields when user actually changes (not just avatar)
|
|
130
145
|
useEffect(() => {
|
|
131
146
|
if (user) {
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
147
|
+
const currentUserId = user.id;
|
|
148
|
+
const currentAvatar = typeof user.avatar === 'string' ? user.avatar : '';
|
|
149
|
+
const isNewUser = previousUserIdRef.current !== currentUserId;
|
|
150
|
+
const isAvatarOnlyUpdate = !isNewUser && previousUserIdRef.current === currentUserId &&
|
|
151
|
+
previousAvatarRef.current !== currentAvatar &&
|
|
152
|
+
previousAvatarRef.current !== null;
|
|
153
|
+
const shouldInitialize = !isInitializedRef.current || isNewUser;
|
|
154
|
+
|
|
155
|
+
// Only reset all fields if it's a new user or first load
|
|
156
|
+
// Skip reset if it's just an avatar update
|
|
157
|
+
if (shouldInitialize && !isAvatarOnlyUpdate) {
|
|
158
|
+
const userDisplayName = typeof user.name === 'string'
|
|
159
|
+
? user.name
|
|
160
|
+
: user.name?.first || user.name?.full || '';
|
|
161
|
+
const userLastName = typeof user.name === 'object' ? user.name?.last || '' : '';
|
|
162
|
+
setDisplayName(userDisplayName);
|
|
163
|
+
setLastName(userLastName);
|
|
164
|
+
setUsername(user.username || '');
|
|
165
|
+
setEmail(user.email || '');
|
|
166
|
+
setBio(user.bio || '');
|
|
167
|
+
setLocation(user.location || '');
|
|
168
|
+
|
|
169
|
+
// Handle locations - convert single location to array format
|
|
170
|
+
if (user.locations && Array.isArray(user.locations)) {
|
|
171
|
+
setTempLocations(user.locations.map((loc, index) => ({
|
|
172
|
+
id: loc.id || `existing-${index}`,
|
|
173
|
+
name: loc.name,
|
|
174
|
+
label: loc.label,
|
|
175
|
+
coordinates: loc.coordinates
|
|
176
|
+
})));
|
|
177
|
+
} else if (user.location) {
|
|
178
|
+
// Convert single location string to array format
|
|
179
|
+
setTempLocations([{
|
|
180
|
+
id: 'existing-0',
|
|
181
|
+
name: user.location,
|
|
182
|
+
label: 'Location'
|
|
183
|
+
}]);
|
|
184
|
+
} else {
|
|
185
|
+
setTempLocations([]);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Handle links - simple and direct like other fields
|
|
189
|
+
if (user.linksMetadata && Array.isArray(user.linksMetadata)) {
|
|
190
|
+
const urls = user.linksMetadata.map(l => l.url);
|
|
191
|
+
setLinks(urls);
|
|
192
|
+
const metadataWithIds = user.linksMetadata.map((link, index) => ({
|
|
193
|
+
...link,
|
|
194
|
+
id: link.id || `existing-${index}`
|
|
195
|
+
}));
|
|
196
|
+
setTempLinksWithMetadata(metadataWithIds);
|
|
197
|
+
} else if (Array.isArray(user.links)) {
|
|
198
|
+
const simpleLinks = user.links.map(l => typeof l === 'string' ? l : l.link).filter(Boolean);
|
|
199
|
+
setLinks(simpleLinks);
|
|
200
|
+
const linksWithMetadata = simpleLinks.map((url, index) => ({
|
|
201
|
+
url,
|
|
202
|
+
title: url.replace(/^https?:\/\//, '').replace(/\/$/, ''),
|
|
203
|
+
description: `Link to ${url}`,
|
|
204
|
+
image: undefined,
|
|
205
|
+
id: `existing-${index}`
|
|
206
|
+
}));
|
|
207
|
+
setTempLinksWithMetadata(linksWithMetadata);
|
|
208
|
+
} else if (user.website) {
|
|
209
|
+
setLinks([user.website]);
|
|
210
|
+
setTempLinksWithMetadata([{
|
|
211
|
+
url: user.website,
|
|
212
|
+
title: user.website.replace(/^https?:\/\//, '').replace(/\/$/, ''),
|
|
213
|
+
description: `Link to ${user.website}`,
|
|
214
|
+
image: undefined,
|
|
215
|
+
id: 'existing-0'
|
|
216
|
+
}]);
|
|
217
|
+
} else {
|
|
218
|
+
setLinks([]);
|
|
219
|
+
setTempLinksWithMetadata([]);
|
|
220
|
+
}
|
|
221
|
+
isInitializedRef.current = true;
|
|
160
222
|
}
|
|
161
223
|
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
...link,
|
|
168
|
-
id: link.id || `existing-${index}`
|
|
169
|
-
}));
|
|
170
|
-
setTempLinksWithMetadata(metadataWithIds);
|
|
171
|
-
} else if (Array.isArray(user.links)) {
|
|
172
|
-
const simpleLinks = user.links.map(l => typeof l === 'string' ? l : l.link).filter(Boolean);
|
|
173
|
-
setLinks(simpleLinks);
|
|
174
|
-
const linksWithMetadata = simpleLinks.map((url, index) => ({
|
|
175
|
-
url,
|
|
176
|
-
title: url.replace(/^https?:\/\//, '').replace(/\/$/, ''),
|
|
177
|
-
description: `Link to ${url}`,
|
|
178
|
-
image: undefined,
|
|
179
|
-
id: `existing-${index}`
|
|
180
|
-
}));
|
|
181
|
-
setTempLinksWithMetadata(linksWithMetadata);
|
|
182
|
-
} else if (user.website) {
|
|
183
|
-
setLinks([user.website]);
|
|
184
|
-
setTempLinksWithMetadata([{
|
|
185
|
-
url: user.website,
|
|
186
|
-
title: user.website.replace(/^https?:\/\//, '').replace(/\/$/, ''),
|
|
187
|
-
description: `Link to ${user.website}`,
|
|
188
|
-
image: undefined,
|
|
189
|
-
id: 'existing-0'
|
|
190
|
-
}]);
|
|
191
|
-
} else {
|
|
192
|
-
setLinks([]);
|
|
193
|
-
setTempLinksWithMetadata([]);
|
|
224
|
+
// Update avatar only if it changed and we're not in optimistic/updating state
|
|
225
|
+
// This allows the server response to update the avatar without resetting other fields
|
|
226
|
+
// But don't override if we have a pending optimistic update
|
|
227
|
+
if (currentAvatar !== avatarFileId && !isUpdatingAvatar && !optimisticAvatarId) {
|
|
228
|
+
setAvatarFileId(currentAvatar);
|
|
194
229
|
}
|
|
195
|
-
|
|
230
|
+
|
|
231
|
+
// If we just finished updating and the server avatar matches our optimistic one, clear optimistic state
|
|
232
|
+
// Also clear if the server avatar matches our current avatarFileId (update completed)
|
|
233
|
+
if (isUpdatingAvatar === false && optimisticAvatarId) {
|
|
234
|
+
if (currentAvatar === optimisticAvatarId || currentAvatar === avatarFileId) {
|
|
235
|
+
setOptimisticAvatarId(null);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
previousUserIdRef.current = currentUserId;
|
|
240
|
+
previousAvatarRef.current = currentAvatar;
|
|
196
241
|
}
|
|
197
|
-
}, [user]);
|
|
242
|
+
}, [user, avatarFileId, isUpdatingAvatar, optimisticAvatarId]);
|
|
198
243
|
|
|
199
244
|
const handleSave = async () => {
|
|
200
245
|
if (!user) return;
|
|
@@ -213,9 +258,6 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
213
258
|
linksMetadata: tempLinksWithMetadata.length > 0 ? tempLinksWithMetadata : undefined,
|
|
214
259
|
};
|
|
215
260
|
|
|
216
|
-
console.log('Saving updates:', updates);
|
|
217
|
-
console.log('Links metadata being saved:', tempLinksWithMetadata);
|
|
218
|
-
|
|
219
261
|
// Handle name field
|
|
220
262
|
if (displayName || lastName) {
|
|
221
263
|
updates.name = { first: displayName, last: lastName };
|
|
@@ -268,32 +310,68 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
268
310
|
toast.info?.(t('editProfile.toasts.avatarUnchanged') || 'Avatar unchanged');
|
|
269
311
|
return;
|
|
270
312
|
}
|
|
313
|
+
|
|
314
|
+
// Optimistically update UI immediately
|
|
315
|
+
setOptimisticAvatarId(file.id);
|
|
271
316
|
setAvatarFileId(file.id);
|
|
272
|
-
|
|
317
|
+
|
|
273
318
|
// Auto-save avatar immediately (does not close edit profile screen)
|
|
274
319
|
(async () => {
|
|
275
320
|
try {
|
|
276
|
-
|
|
277
|
-
setIsSaving(true);
|
|
321
|
+
setIsUpdatingAvatar(true);
|
|
278
322
|
|
|
279
323
|
// Update file visibility to public for avatar
|
|
280
324
|
try {
|
|
281
325
|
await oxyServices.assetUpdateVisibility(file.id, 'public');
|
|
282
|
-
console.log('[AccountSettings] Avatar visibility updated to public');
|
|
283
326
|
} catch (visError) {
|
|
284
|
-
console.warn('[AccountSettings] Failed to update avatar visibility, continuing anyway:', visError);
|
|
285
327
|
// Continue with avatar update even if visibility update fails
|
|
286
328
|
}
|
|
287
329
|
|
|
288
|
-
|
|
289
|
-
//
|
|
290
|
-
|
|
330
|
+
// Update on server directly without using updateUser (which triggers fetchUser)
|
|
331
|
+
// This prevents the entire component from re-rendering
|
|
332
|
+
await oxyServices.updateProfile({ avatar: file.id });
|
|
333
|
+
|
|
334
|
+
// Update the user object in store directly without triggering fetchUser
|
|
335
|
+
// This prevents isLoading from being set to true, which would show loading screen
|
|
336
|
+
const currentUser = useAuthStore.getState().user;
|
|
337
|
+
if (currentUser) {
|
|
338
|
+
useAuthStore.setState({
|
|
339
|
+
user: { ...currentUser, avatar: file.id },
|
|
340
|
+
// Don't update lastUserFetch to avoid cache issues
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Update local state - keep avatarFileId set to the new value
|
|
345
|
+
// Don't clear optimisticAvatarId yet - let it persist until user object updates
|
|
346
|
+
// This ensures the avatar displays correctly
|
|
347
|
+
setAvatarFileId(file.id);
|
|
348
|
+
|
|
291
349
|
toast.success(t('editProfile.toasts.avatarUpdated') || 'Avatar updated');
|
|
350
|
+
|
|
351
|
+
// Scroll to avatar section after a brief delay to ensure UI is updated
|
|
352
|
+
requestAnimationFrame(() => {
|
|
353
|
+
requestAnimationFrame(() => {
|
|
354
|
+
if (avatarSectionY !== null) {
|
|
355
|
+
scrollViewRef.current?.scrollTo({
|
|
356
|
+
y: Math.max(0, avatarSectionY - 100), // Offset to show section near top
|
|
357
|
+
animated: true,
|
|
358
|
+
});
|
|
359
|
+
} else {
|
|
360
|
+
// Fallback: scroll to approximate position
|
|
361
|
+
scrollViewRef.current?.scrollTo({
|
|
362
|
+
y: 200, // Approximate position of avatar section
|
|
363
|
+
animated: true,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
});
|
|
292
368
|
} catch (e: any) {
|
|
293
|
-
|
|
369
|
+
// Revert optimistic update on error
|
|
370
|
+
setAvatarFileId(typeof user?.avatar === 'string' ? user.avatar : '');
|
|
371
|
+
setOptimisticAvatarId(null);
|
|
294
372
|
toast.error(e.message || t('editProfile.toasts.updateAvatarFailed') || 'Failed to update avatar');
|
|
295
373
|
} finally {
|
|
296
|
-
|
|
374
|
+
setIsUpdatingAvatar(false);
|
|
297
375
|
}
|
|
298
376
|
})();
|
|
299
377
|
},
|
|
@@ -301,7 +379,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
301
379
|
disabledMimeTypes: ['video/', 'audio/', 'application/pdf']
|
|
302
380
|
}
|
|
303
381
|
});
|
|
304
|
-
}, [showBottomSheet, oxyServices, avatarFileId, updateUser]);
|
|
382
|
+
}, [showBottomSheet, oxyServices, avatarFileId, updateUser, user]);
|
|
305
383
|
|
|
306
384
|
const startEditing = (type: string, currentValue: string) => {
|
|
307
385
|
switch (type) {
|
|
@@ -362,11 +440,10 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
362
440
|
break;
|
|
363
441
|
}
|
|
364
442
|
|
|
365
|
-
//
|
|
366
|
-
|
|
367
|
-
animateSaveButton(1);
|
|
443
|
+
// Complete animation, then reset and close editing
|
|
444
|
+
animateSaveButton(1, () => {
|
|
368
445
|
setEditingField(null);
|
|
369
|
-
}
|
|
446
|
+
});
|
|
370
447
|
};
|
|
371
448
|
|
|
372
449
|
const cancelEditing = () => {
|
|
@@ -385,11 +462,9 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
385
462
|
|
|
386
463
|
try {
|
|
387
464
|
setIsFetchingMetadata(true);
|
|
388
|
-
console.log('Fetching metadata for URL:', url);
|
|
389
465
|
|
|
390
466
|
// Use the backend API to fetch metadata
|
|
391
467
|
const metadata = await oxyServices.fetchLinkMetadata(url);
|
|
392
|
-
console.log('Received metadata:', metadata);
|
|
393
468
|
|
|
394
469
|
const result = {
|
|
395
470
|
...metadata,
|
|
@@ -400,7 +475,6 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
400
475
|
linkMetadataCache.set(cacheKey, result);
|
|
401
476
|
return result;
|
|
402
477
|
} catch (error) {
|
|
403
|
-
console.error('Error fetching metadata:', error);
|
|
404
478
|
// Fallback to basic metadata
|
|
405
479
|
const fallback = {
|
|
406
480
|
url: url.startsWith('http') ? url : 'https://' + url,
|
|
@@ -437,12 +511,11 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
437
511
|
`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(query)}&limit=5&addressdetails=1`
|
|
438
512
|
);
|
|
439
513
|
const data = await response.json();
|
|
440
|
-
|
|
514
|
+
|
|
441
515
|
// Cache the results
|
|
442
516
|
locationSearchCache.set(cacheKey, data);
|
|
443
517
|
setLocationSearchResults(data);
|
|
444
518
|
} catch (error) {
|
|
445
|
-
console.error('Error searching locations:', error);
|
|
446
519
|
setLocationSearchResults([]);
|
|
447
520
|
} finally {
|
|
448
521
|
setIsSearchingLocations(false);
|
|
@@ -491,10 +564,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
491
564
|
if (!newLinkUrl.trim()) return;
|
|
492
565
|
|
|
493
566
|
const url = newLinkUrl.trim();
|
|
494
|
-
console.log('Adding link:', url);
|
|
495
|
-
|
|
496
567
|
const metadata = await fetchLinkMetadata(url);
|
|
497
|
-
console.log('Final metadata for adding:', metadata);
|
|
498
568
|
|
|
499
569
|
setTempLinksWithMetadata(prev => [...prev, metadata]);
|
|
500
570
|
setNewLinkUrl('');
|
|
@@ -552,7 +622,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
552
622
|
</TouchableOpacity>
|
|
553
623
|
) : (
|
|
554
624
|
<View style={{ alignItems: 'center', gap: 16 }}>
|
|
555
|
-
<View style={{ padding: 16, backgroundColor: '#fff', borderRadius:
|
|
625
|
+
<View style={{ padding: 16, backgroundColor: '#fff', borderRadius: 16 }}>
|
|
556
626
|
<QRCode value={totpSetupUrl} size={180} />
|
|
557
627
|
</View>
|
|
558
628
|
<View>
|
|
@@ -646,7 +716,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
646
716
|
<View style={styles.editingFieldContent}>
|
|
647
717
|
<View style={styles.newValueSection}>
|
|
648
718
|
<View style={styles.editingFieldHeader}>
|
|
649
|
-
<Text style={[styles.editingFieldLabel, { color: themeStyles.isDarkTheme ? '#FFFFFF' : '#1A1A1A' }]}>Edit
|
|
719
|
+
<Text style={[styles.editingFieldLabel, { color: themeStyles.isDarkTheme ? '#FFFFFF' : '#1A1A1A' }]}>Edit Full Name</Text>
|
|
650
720
|
</View>
|
|
651
721
|
<View style={{ flexDirection: 'row', gap: 12 }}>
|
|
652
722
|
<View style={{ flex: 1 }}>
|
|
@@ -972,27 +1042,27 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
972
1042
|
};
|
|
973
1043
|
|
|
974
1044
|
return (
|
|
975
|
-
<View style={[styles.editingFieldContainer, { backgroundColor: themeStyles.
|
|
1045
|
+
<View style={[styles.editingFieldContainer, { backgroundColor: themeStyles.isDarkTheme ? '#000000' : '#FFFFFF' }]}>
|
|
976
1046
|
<View style={styles.editingFieldContent}>
|
|
977
1047
|
<View style={styles.newValueSection}>
|
|
978
1048
|
<View style={styles.editingFieldHeader}>
|
|
979
|
-
<Text style={[styles.editingFieldLabel, { color: themeStyles.isDarkTheme ? '#FFFFFF' : '#
|
|
980
|
-
{
|
|
1049
|
+
<Text style={[styles.editingFieldLabel, { color: themeStyles.isDarkTheme ? '#FFFFFF' : '#000000' }]}>
|
|
1050
|
+
{config.label}
|
|
981
1051
|
</Text>
|
|
982
1052
|
</View>
|
|
983
1053
|
<TextInput
|
|
984
1054
|
style={[
|
|
985
1055
|
config.multiline ? styles.editingFieldTextArea : styles.editingFieldInput,
|
|
986
1056
|
{
|
|
987
|
-
backgroundColor: themeStyles.isDarkTheme ? '#
|
|
988
|
-
color: themeStyles.isDarkTheme ? '#
|
|
989
|
-
borderColor: themeStyles.
|
|
1057
|
+
backgroundColor: themeStyles.isDarkTheme ? '#1C1C1E' : '#F2F2F7',
|
|
1058
|
+
color: themeStyles.isDarkTheme ? '#FFFFFF' : '#000000',
|
|
1059
|
+
borderColor: themeStyles.isDarkTheme ? '#38383A' : '#E5E5EA',
|
|
990
1060
|
}
|
|
991
1061
|
]}
|
|
992
1062
|
value={tempValue}
|
|
993
1063
|
onChangeText={setTempValue}
|
|
994
1064
|
placeholder={config.placeholder}
|
|
995
|
-
placeholderTextColor={themeStyles.isDarkTheme ? '#
|
|
1065
|
+
placeholderTextColor={themeStyles.isDarkTheme ? '#636366' : '#8E8E93'}
|
|
996
1066
|
multiline={config.multiline}
|
|
997
1067
|
numberOfLines={config.multiline ? 6 : 1}
|
|
998
1068
|
keyboardType={config.keyboardType}
|
|
@@ -1009,25 +1079,42 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
1009
1079
|
|
|
1010
1080
|
if (authLoading || !isAuthenticated) {
|
|
1011
1081
|
return (
|
|
1012
|
-
<View style={[styles.container, {
|
|
1082
|
+
<View style={[styles.container, {
|
|
1083
|
+
backgroundColor: themeStyles.isDarkTheme ? '#000000' : '#F5F5F7',
|
|
1084
|
+
justifyContent: 'center'
|
|
1085
|
+
}]}>
|
|
1013
1086
|
<ActivityIndicator size="large" color={themeStyles.primaryColor} />
|
|
1014
1087
|
</View>
|
|
1015
1088
|
);
|
|
1016
1089
|
}
|
|
1017
1090
|
|
|
1018
1091
|
return (
|
|
1019
|
-
<View style={[styles.container, { backgroundColor: themeStyles.
|
|
1092
|
+
<View style={[styles.container, { backgroundColor: themeStyles.isDarkTheme ? '#000000' : '#F5F5F7' }]}>
|
|
1020
1093
|
{/* Header */}
|
|
1021
1094
|
{editingField ? (
|
|
1022
|
-
<View style={[styles.editingHeader, {
|
|
1095
|
+
<View style={[styles.editingHeader, {
|
|
1096
|
+
backgroundColor: themeStyles.isDarkTheme ? '#000000' : '#FFFFFF',
|
|
1097
|
+
borderBottomColor: themeStyles.isDarkTheme ? '#38383A' : '#E5E5EA'
|
|
1098
|
+
}]}>
|
|
1023
1099
|
<View style={styles.editingHeaderContent}>
|
|
1024
|
-
<TouchableOpacity
|
|
1025
|
-
|
|
1100
|
+
<TouchableOpacity
|
|
1101
|
+
style={[styles.editingBackButton, {
|
|
1102
|
+
backgroundColor: themeStyles.isDarkTheme ? '#1C1C1E' : '#F2F2F7'
|
|
1103
|
+
}]}
|
|
1104
|
+
onPress={cancelEditing}
|
|
1105
|
+
>
|
|
1106
|
+
<Ionicons name="chevron-back" size={20} color={themeStyles.primaryColor} />
|
|
1026
1107
|
</TouchableOpacity>
|
|
1027
1108
|
<View style={styles.editingTitleContainer}>
|
|
1028
1109
|
</View>
|
|
1029
1110
|
<TouchableOpacity
|
|
1030
|
-
style={[
|
|
1111
|
+
style={[
|
|
1112
|
+
styles.editingSaveButton,
|
|
1113
|
+
{
|
|
1114
|
+
opacity: isSaving ? 0.5 : 1,
|
|
1115
|
+
backgroundColor: themeStyles.isDarkTheme ? '#1C1C1E' : '#F2F2F7'
|
|
1116
|
+
}
|
|
1117
|
+
]}
|
|
1031
1118
|
onPress={() => saveField(editingField)}
|
|
1032
1119
|
disabled={isSaving}
|
|
1033
1120
|
>
|
|
@@ -1039,27 +1126,35 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
1039
1126
|
</TouchableOpacity>
|
|
1040
1127
|
</View>
|
|
1041
1128
|
<View style={styles.editingHeaderBottom}>
|
|
1042
|
-
<
|
|
1043
|
-
|
|
1044
|
-
editingField === '
|
|
1045
|
-
editingField === '
|
|
1046
|
-
editingField === '
|
|
1047
|
-
editingField === '
|
|
1048
|
-
editingField === '
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1129
|
+
<View style={[styles.editingIconContainer, {
|
|
1130
|
+
backgroundColor: editingField === 'displayName' ? '#007AFF20' :
|
|
1131
|
+
editingField === 'username' ? '#5856D620' :
|
|
1132
|
+
editingField === 'email' ? '#FF950020' :
|
|
1133
|
+
editingField === 'bio' ? '#34C75920' :
|
|
1134
|
+
editingField === 'location' ? '#FF3B3020' :
|
|
1135
|
+
editingField === 'links' ? '#32D74B20' : '#007AFF20'
|
|
1136
|
+
}]}>
|
|
1137
|
+
<Ionicons
|
|
1138
|
+
name={
|
|
1139
|
+
editingField === 'displayName' ? 'person' as any :
|
|
1140
|
+
editingField === 'username' ? 'at' as any :
|
|
1141
|
+
editingField === 'email' ? 'mail' as any :
|
|
1142
|
+
editingField === 'bio' ? 'document-text' as any :
|
|
1143
|
+
editingField === 'location' ? 'location' as any :
|
|
1144
|
+
editingField === 'links' ? 'link' as any : 'person' as any
|
|
1145
|
+
}
|
|
1146
|
+
size={28}
|
|
1147
|
+
color={
|
|
1148
|
+
editingField === 'displayName' ? '#007AFF' :
|
|
1149
|
+
editingField === 'username' ? '#5856D6' :
|
|
1150
|
+
editingField === 'email' ? '#FF9500' :
|
|
1151
|
+
editingField === 'bio' ? '#34C759' :
|
|
1152
|
+
editingField === 'location' ? '#FF3B30' :
|
|
1153
|
+
editingField === 'links' ? '#32D74B' : '#007AFF'
|
|
1154
|
+
}
|
|
1155
|
+
/>
|
|
1156
|
+
</View>
|
|
1157
|
+
<Text style={[styles.editingBottomTitle, { color: themeStyles.isDarkTheme ? '#FFFFFF' : '#000000' }]}>
|
|
1063
1158
|
{editingField === 'displayName' ? (t('editProfile.items.displayName.title') || 'Display Name') :
|
|
1064
1159
|
editingField === 'username' ? (t('editProfile.items.username.title') || 'Username') :
|
|
1065
1160
|
editingField === 'email' ? (t('editProfile.items.email.title') || 'Email') :
|
|
@@ -1084,7 +1179,10 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
1084
1179
|
/>
|
|
1085
1180
|
)}
|
|
1086
1181
|
|
|
1087
|
-
<ScrollView
|
|
1182
|
+
<ScrollView
|
|
1183
|
+
ref={scrollViewRef}
|
|
1184
|
+
style={editingField ? styles.contentEditing : styles.content}
|
|
1185
|
+
>
|
|
1088
1186
|
{editingField ? (
|
|
1089
1187
|
// Show only the editing interface when editing
|
|
1090
1188
|
<View style={styles.editingOnlyContainer}>
|
|
@@ -1095,7 +1193,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
1095
1193
|
<>
|
|
1096
1194
|
{showRecoveryModal && (
|
|
1097
1195
|
<View style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.6)', zIndex: 50, padding: 16, justifyContent: 'center' }}>
|
|
1098
|
-
<View style={{ backgroundColor: '#fff', borderRadius:
|
|
1196
|
+
<View style={{ backgroundColor: '#fff', borderRadius: 20, padding: 20, maxHeight: '80%' }}>
|
|
1099
1197
|
<Text style={{ fontSize: 18, fontWeight: '700', marginBottom: 12 }}>Save These Codes Now</Text>
|
|
1100
1198
|
<Text style={{ fontSize: 14, color: '#444', marginBottom: 12 }}>
|
|
1101
1199
|
Backup codes and your Recovery Key are shown only once. Store them securely (paper or password manager).
|
|
@@ -1103,7 +1201,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
1103
1201
|
{generatedBackupCodes && generatedBackupCodes.length > 0 && (
|
|
1104
1202
|
<View style={{ marginBottom: 12 }}>
|
|
1105
1203
|
<Text style={{ fontSize: 16, fontWeight: '600', marginBottom: 8 }}>Backup Codes</Text>
|
|
1106
|
-
<View style={{ backgroundColor: '#F8F9FA', borderRadius:
|
|
1204
|
+
<View style={{ backgroundColor: '#F8F9FA', borderRadius: 12, padding: 12 }}>
|
|
1107
1205
|
{generatedBackupCodes.map((c, idx) => (
|
|
1108
1206
|
<Text key={idx} style={{ fontFamily: Platform.OS === 'web' ? 'monospace' as any : 'monospace', fontSize: 14, marginBottom: 4 }}>{c}</Text>
|
|
1109
1207
|
))}
|
|
@@ -1113,7 +1211,7 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
1113
1211
|
{generatedRecoveryKey && (
|
|
1114
1212
|
<View style={{ marginBottom: 12 }}>
|
|
1115
1213
|
<Text style={{ fontSize: 16, fontWeight: '600', marginBottom: 8 }}>Recovery Key</Text>
|
|
1116
|
-
<View style={{ backgroundColor: '#F8F9FA', borderRadius:
|
|
1214
|
+
<View style={{ backgroundColor: '#F8F9FA', borderRadius: 12, padding: 12 }}>
|
|
1117
1215
|
<Text style={{ fontFamily: Platform.OS === 'web' ? 'monospace' as any : 'monospace', fontSize: 14 }}>{generatedRecoveryKey}</Text>
|
|
1118
1216
|
</View>
|
|
1119
1217
|
</View>
|
|
@@ -1129,221 +1227,276 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
1129
1227
|
</View>
|
|
1130
1228
|
)}
|
|
1131
1229
|
{/* Profile Picture Section */}
|
|
1132
|
-
<View
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
},
|
|
1147
|
-
...(avatarFileId ? [
|
|
1230
|
+
<View
|
|
1231
|
+
ref={avatarSectionRef}
|
|
1232
|
+
style={styles.section}
|
|
1233
|
+
onLayout={(event) => {
|
|
1234
|
+
const { y } = event.nativeEvent.layout;
|
|
1235
|
+
setAvatarSectionY(y);
|
|
1236
|
+
}}
|
|
1237
|
+
>
|
|
1238
|
+
<Text style={[styles.sectionTitle, { color: themeStyles.isDarkTheme ? '#8E8E93' : '#8E8E93' }]}>
|
|
1239
|
+
{t('editProfile.sections.profilePicture') || 'PROFILE PICTURE'}
|
|
1240
|
+
</Text>
|
|
1241
|
+
<View style={styles.groupedSectionWrapper}>
|
|
1242
|
+
<GroupedSection
|
|
1243
|
+
items={[
|
|
1148
1244
|
{
|
|
1149
|
-
id: '
|
|
1150
|
-
icon: '
|
|
1151
|
-
iconColor: '#
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1245
|
+
id: 'profile-photo',
|
|
1246
|
+
icon: avatarFileId ? undefined : 'person',
|
|
1247
|
+
iconColor: '#007AFF',
|
|
1248
|
+
// Use optimistic avatar ID if available, otherwise use saved one
|
|
1249
|
+
image: (optimisticAvatarId || avatarFileId) ? oxyServices.getFileDownloadUrl(optimisticAvatarId || avatarFileId, 'thumb') : undefined,
|
|
1250
|
+
imageSize: 40,
|
|
1251
|
+
title: 'Profile Photo',
|
|
1252
|
+
subtitle: isUpdatingAvatar
|
|
1253
|
+
? 'Updating profile picture...'
|
|
1254
|
+
: (avatarFileId ? 'Tap to change your profile picture' : 'Tap to add a profile picture'),
|
|
1255
|
+
onPress: isUpdatingAvatar ? undefined : openAvatarPicker,
|
|
1256
|
+
disabled: isUpdatingAvatar,
|
|
1257
|
+
customIcon: isUpdatingAvatar ? (
|
|
1258
|
+
<Animated.View style={{ position: 'relative', width: 40, height: 40 }}>
|
|
1259
|
+
{(optimisticAvatarId || avatarFileId) && (
|
|
1260
|
+
<Animated.Image
|
|
1261
|
+
source={{ uri: oxyServices.getFileDownloadUrl(optimisticAvatarId || avatarFileId, 'thumb') }}
|
|
1262
|
+
style={{
|
|
1263
|
+
width: 40,
|
|
1264
|
+
height: 40,
|
|
1265
|
+
borderRadius: 22,
|
|
1266
|
+
opacity: 0.6
|
|
1267
|
+
}}
|
|
1268
|
+
/>
|
|
1269
|
+
)}
|
|
1270
|
+
<View style={{
|
|
1271
|
+
position: 'absolute',
|
|
1272
|
+
top: 0,
|
|
1273
|
+
left: 0,
|
|
1274
|
+
right: 0,
|
|
1275
|
+
bottom: 0,
|
|
1276
|
+
justifyContent: 'center',
|
|
1277
|
+
alignItems: 'center',
|
|
1278
|
+
backgroundColor: themeStyles.isDarkTheme ? 'rgba(0, 0, 0, 0.4)' : 'rgba(255, 255, 255, 0.7)',
|
|
1279
|
+
borderRadius: 22,
|
|
1280
|
+
}}>
|
|
1281
|
+
<ActivityIndicator size="small" color={themeStyles.primaryColor} />
|
|
1282
|
+
</View>
|
|
1283
|
+
</Animated.View>
|
|
1284
|
+
) : undefined,
|
|
1285
|
+
},
|
|
1286
|
+
...(avatarFileId && !isUpdatingAvatar ? [
|
|
1287
|
+
{
|
|
1288
|
+
id: 'remove-profile-photo',
|
|
1289
|
+
icon: 'trash',
|
|
1290
|
+
iconColor: '#FF3B30',
|
|
1291
|
+
title: 'Remove Photo',
|
|
1292
|
+
subtitle: 'Delete current profile picture',
|
|
1293
|
+
onPress: handleAvatarRemove,
|
|
1294
|
+
}
|
|
1295
|
+
] : []),
|
|
1296
|
+
]}
|
|
1297
|
+
theme={theme}
|
|
1298
|
+
/>
|
|
1299
|
+
</View>
|
|
1160
1300
|
</View>
|
|
1161
1301
|
|
|
1162
1302
|
{/* Basic Information */}
|
|
1163
1303
|
<View style={styles.section}>
|
|
1164
|
-
<Text style={styles.sectionTitle
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1304
|
+
<Text style={[styles.sectionTitle, { color: themeStyles.isDarkTheme ? '#8E8E93' : '#8E8E93' }]}>
|
|
1305
|
+
{t('editProfile.sections.basicInfo') || 'BASIC INFORMATION'}
|
|
1306
|
+
</Text>
|
|
1307
|
+
<View style={styles.groupedSectionWrapper}>
|
|
1308
|
+
<GroupedSection
|
|
1309
|
+
items={[
|
|
1310
|
+
{
|
|
1311
|
+
id: 'display-name',
|
|
1312
|
+
icon: 'person',
|
|
1313
|
+
iconColor: '#007AFF',
|
|
1314
|
+
title: t('editProfile.items.displayName.title') || 'Display Name',
|
|
1315
|
+
subtitle: [displayName, lastName].filter(Boolean).join(' ') || (t('editProfile.items.displayName.add') || 'Add your display name'),
|
|
1316
|
+
onPress: () => startEditing('displayName', ''),
|
|
1317
|
+
},
|
|
1318
|
+
{
|
|
1319
|
+
id: 'username',
|
|
1320
|
+
icon: 'at',
|
|
1321
|
+
iconColor: '#5856D6',
|
|
1322
|
+
title: t('editProfile.items.username.title') || 'Username',
|
|
1323
|
+
subtitle: username || (t('editProfile.items.username.choose') || 'Choose a username'),
|
|
1324
|
+
onPress: () => startEditing('username', username),
|
|
1325
|
+
},
|
|
1326
|
+
{
|
|
1327
|
+
id: 'email',
|
|
1328
|
+
icon: 'mail',
|
|
1329
|
+
iconColor: '#FF9500',
|
|
1330
|
+
title: t('editProfile.items.email.title') || 'Email',
|
|
1331
|
+
subtitle: email || (t('editProfile.items.email.add') || 'Add your email address'),
|
|
1332
|
+
onPress: () => startEditing('email', email),
|
|
1333
|
+
},
|
|
1334
|
+
]}
|
|
1335
|
+
theme={theme}
|
|
1336
|
+
/>
|
|
1337
|
+
</View>
|
|
1195
1338
|
</View>
|
|
1196
1339
|
|
|
1197
1340
|
{/* About You */}
|
|
1198
1341
|
<View style={styles.section}>
|
|
1199
|
-
<Text style={styles.sectionTitle
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
{location.name.charAt(0).toUpperCase()}
|
|
1229
|
-
</Text>
|
|
1230
|
-
</View>
|
|
1231
|
-
<View style={styles.linkPreviewContent}>
|
|
1232
|
-
<Text style={styles.linkPreviewTitle} numberOfLines={1}>
|
|
1233
|
-
{location.name}
|
|
1234
|
-
</Text>
|
|
1235
|
-
{location.label && (
|
|
1236
|
-
<Text style={styles.linkPreviewSubtitle}>
|
|
1237
|
-
{location.label}
|
|
1238
|
-
</Text>
|
|
1239
|
-
)}
|
|
1240
|
-
</View>
|
|
1241
|
-
</View>
|
|
1242
|
-
))}
|
|
1243
|
-
{tempLocations.length > 2 && (
|
|
1244
|
-
<Text style={styles.linkPreviewMore}>
|
|
1245
|
-
+{tempLocations.length - 2} more
|
|
1246
|
-
</Text>
|
|
1247
|
-
)}
|
|
1248
|
-
</View>
|
|
1249
|
-
),
|
|
1250
|
-
},
|
|
1251
|
-
{
|
|
1252
|
-
id: 'links',
|
|
1253
|
-
icon: 'link',
|
|
1254
|
-
iconColor: '#32D74B',
|
|
1255
|
-
title: t('editProfile.items.links.title') || 'Links',
|
|
1256
|
-
subtitle: tempLinksWithMetadata.length > 0
|
|
1257
|
-
? (tempLinksWithMetadata.length === 1
|
|
1258
|
-
? (t('editProfile.items.links.count', { count: tempLinksWithMetadata.length }) || `${tempLinksWithMetadata.length} link added`)
|
|
1259
|
-
: (t('editProfile.items.links.count_plural', { count: tempLinksWithMetadata.length }) || `${tempLinksWithMetadata.length} links added`))
|
|
1260
|
-
: (t('editProfile.items.links.add') || 'Add your links'),
|
|
1261
|
-
onPress: () => startEditing('links', ''),
|
|
1262
|
-
multiRow: true,
|
|
1263
|
-
customContentBelow: tempLinksWithMetadata.length > 0 && (
|
|
1264
|
-
<View style={styles.linksPreviewContainer}>
|
|
1265
|
-
{tempLinksWithMetadata.slice(0, 2).map((link, index) => (
|
|
1266
|
-
<View key={link.id || index} style={styles.linkPreviewItem}>
|
|
1267
|
-
{link.image ? (
|
|
1268
|
-
<Image source={{ uri: link.image }} style={styles.linkPreviewImage} />
|
|
1269
|
-
) : (
|
|
1342
|
+
<Text style={[styles.sectionTitle, { color: themeStyles.isDarkTheme ? '#8E8E93' : '#8E8E93' }]}>
|
|
1343
|
+
{t('editProfile.sections.about') || 'ABOUT YOU'}
|
|
1344
|
+
</Text>
|
|
1345
|
+
<View style={styles.groupedSectionWrapper}>
|
|
1346
|
+
<GroupedSection
|
|
1347
|
+
items={[
|
|
1348
|
+
{
|
|
1349
|
+
id: 'bio',
|
|
1350
|
+
icon: 'document-text',
|
|
1351
|
+
iconColor: '#34C759',
|
|
1352
|
+
title: t('editProfile.items.bio.title') || 'Bio',
|
|
1353
|
+
subtitle: bio || (t('editProfile.items.bio.placeholder') || 'Tell people about yourself'),
|
|
1354
|
+
onPress: () => startEditing('bio', bio),
|
|
1355
|
+
},
|
|
1356
|
+
{
|
|
1357
|
+
id: 'locations',
|
|
1358
|
+
icon: 'location',
|
|
1359
|
+
iconColor: '#FF3B30',
|
|
1360
|
+
title: t('editProfile.items.locations.title') || 'Locations',
|
|
1361
|
+
subtitle: tempLocations.length > 0
|
|
1362
|
+
? (tempLocations.length === 1
|
|
1363
|
+
? (t('editProfile.items.locations.count', { count: tempLocations.length }) || `${tempLocations.length} location added`)
|
|
1364
|
+
: (t('editProfile.items.locations.count_plural', { count: tempLocations.length }) || `${tempLocations.length} locations added`))
|
|
1365
|
+
: (t('editProfile.items.locations.add') || 'Add your locations'),
|
|
1366
|
+
onPress: () => startEditing('location', ''),
|
|
1367
|
+
customContentBelow: tempLocations.length > 0 && (
|
|
1368
|
+
<View style={styles.linksPreviewContainer}>
|
|
1369
|
+
{tempLocations.slice(0, 2).map((location, index) => (
|
|
1370
|
+
<View key={location.id || index} style={styles.linkPreviewItem}>
|
|
1270
1371
|
<View style={styles.linkPreviewImage}>
|
|
1271
1372
|
<Text style={styles.linkPreviewImageText}>
|
|
1272
|
-
{
|
|
1373
|
+
{location.name.charAt(0).toUpperCase()}
|
|
1273
1374
|
</Text>
|
|
1274
1375
|
</View>
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1376
|
+
<View style={styles.linkPreviewContent}>
|
|
1377
|
+
<Text style={styles.linkPreviewTitle} numberOfLines={1}>
|
|
1378
|
+
{location.name}
|
|
1379
|
+
</Text>
|
|
1380
|
+
{location.label && (
|
|
1381
|
+
<Text style={styles.linkPreviewSubtitle}>
|
|
1382
|
+
{location.label}
|
|
1383
|
+
</Text>
|
|
1384
|
+
)}
|
|
1385
|
+
</View>
|
|
1386
|
+
</View>
|
|
1387
|
+
))}
|
|
1388
|
+
{tempLocations.length > 2 && (
|
|
1389
|
+
<Text style={styles.linkPreviewMore}>
|
|
1390
|
+
+{tempLocations.length - 2} more
|
|
1278
1391
|
</Text>
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1392
|
+
)}
|
|
1393
|
+
</View>
|
|
1394
|
+
),
|
|
1395
|
+
},
|
|
1396
|
+
{
|
|
1397
|
+
id: 'links',
|
|
1398
|
+
icon: 'link',
|
|
1399
|
+
iconColor: '#32D74B',
|
|
1400
|
+
title: t('editProfile.items.links.title') || 'Links',
|
|
1401
|
+
subtitle: tempLinksWithMetadata.length > 0
|
|
1402
|
+
? (tempLinksWithMetadata.length === 1
|
|
1403
|
+
? (t('editProfile.items.links.count', { count: tempLinksWithMetadata.length }) || `${tempLinksWithMetadata.length} link added`)
|
|
1404
|
+
: (t('editProfile.items.links.count_plural', { count: tempLinksWithMetadata.length }) || `${tempLinksWithMetadata.length} links added`))
|
|
1405
|
+
: (t('editProfile.items.links.add') || 'Add your links'),
|
|
1406
|
+
onPress: () => startEditing('links', ''),
|
|
1407
|
+
multiRow: true,
|
|
1408
|
+
customContentBelow: tempLinksWithMetadata.length > 0 && (
|
|
1409
|
+
<View style={styles.linksPreviewContainer}>
|
|
1410
|
+
{tempLinksWithMetadata.slice(0, 2).map((link, index) => (
|
|
1411
|
+
<View key={link.id || index} style={styles.linkPreviewItem}>
|
|
1412
|
+
{link.image ? (
|
|
1413
|
+
<Image source={{ uri: link.image }} style={styles.linkPreviewImage} />
|
|
1414
|
+
) : (
|
|
1415
|
+
<View style={styles.linkPreviewImage}>
|
|
1416
|
+
<Text style={styles.linkPreviewImageText}>
|
|
1417
|
+
{link.title?.charAt(0).toUpperCase() || link.url.charAt(0).toUpperCase()}
|
|
1418
|
+
</Text>
|
|
1419
|
+
</View>
|
|
1420
|
+
)}
|
|
1421
|
+
<Text style={styles.linkPreviewTitle} numberOfLines={1}>
|
|
1422
|
+
{link.title || link.url}
|
|
1423
|
+
</Text>
|
|
1424
|
+
</View>
|
|
1425
|
+
))}
|
|
1426
|
+
{tempLinksWithMetadata.length > 2 && (
|
|
1427
|
+
<Text style={styles.linkPreviewMore}>
|
|
1428
|
+
+{tempLinksWithMetadata.length - 2} more
|
|
1429
|
+
</Text>
|
|
1430
|
+
)}
|
|
1431
|
+
</View>
|
|
1432
|
+
),
|
|
1433
|
+
},
|
|
1434
|
+
]}
|
|
1435
|
+
theme={theme}
|
|
1436
|
+
/>
|
|
1437
|
+
</View>
|
|
1292
1438
|
</View>
|
|
1293
1439
|
|
|
1294
1440
|
{/* Quick Actions */}
|
|
1295
1441
|
<View style={styles.section}>
|
|
1296
|
-
<Text style={styles.sectionTitle
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1442
|
+
<Text style={[styles.sectionTitle, { color: themeStyles.isDarkTheme ? '#8E8E93' : '#8E8E93' }]}>
|
|
1443
|
+
{t('editProfile.sections.quickActions') || 'QUICK ACTIONS'}
|
|
1444
|
+
</Text>
|
|
1445
|
+
<View style={styles.groupedSectionWrapper}>
|
|
1446
|
+
<GroupedSection
|
|
1447
|
+
items={[
|
|
1448
|
+
{
|
|
1449
|
+
id: 'preview-profile',
|
|
1450
|
+
icon: 'eye',
|
|
1451
|
+
iconColor: '#007AFF',
|
|
1452
|
+
title: t('editProfile.items.previewProfile.title') || 'Preview Profile',
|
|
1453
|
+
subtitle: t('editProfile.items.previewProfile.subtitle') || 'See how your profile looks to others',
|
|
1454
|
+
onPress: () => navigate?.('Profile', { userId: user?.id }),
|
|
1455
|
+
},
|
|
1456
|
+
{
|
|
1457
|
+
id: 'privacy-settings',
|
|
1458
|
+
icon: 'shield-checkmark',
|
|
1459
|
+
iconColor: '#8E8E93',
|
|
1460
|
+
title: t('editProfile.items.privacySettings.title') || 'Privacy Settings',
|
|
1461
|
+
subtitle: t('editProfile.items.privacySettings.subtitle') || 'Control who can see your profile',
|
|
1462
|
+
onPress: () => navigate?.('PrivacySettings'),
|
|
1463
|
+
},
|
|
1464
|
+
{
|
|
1465
|
+
id: 'verify-account',
|
|
1466
|
+
icon: 'checkmark-circle',
|
|
1467
|
+
iconColor: '#30D158',
|
|
1468
|
+
title: t('editProfile.items.verifyAccount.title') || 'Verify Account',
|
|
1469
|
+
subtitle: t('editProfile.items.verifyAccount.subtitle') || 'Get a verified badge',
|
|
1470
|
+
onPress: () => navigate?.('AccountVerification'),
|
|
1471
|
+
},
|
|
1472
|
+
]}
|
|
1473
|
+
theme={theme}
|
|
1474
|
+
/>
|
|
1475
|
+
</View>
|
|
1327
1476
|
</View>
|
|
1328
1477
|
|
|
1329
1478
|
{/* Security */}
|
|
1330
1479
|
<View style={styles.section}>
|
|
1331
|
-
<Text style={styles.sectionTitle
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
:
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1480
|
+
<Text style={[styles.sectionTitle, { color: themeStyles.isDarkTheme ? '#8E8E93' : '#8E8E93' }]}>
|
|
1481
|
+
{t('editProfile.sections.security') || 'SECURITY'}
|
|
1482
|
+
</Text>
|
|
1483
|
+
<View style={styles.groupedSectionWrapper}>
|
|
1484
|
+
<GroupedSection
|
|
1485
|
+
items={[
|
|
1486
|
+
{
|
|
1487
|
+
id: 'two-factor',
|
|
1488
|
+
icon: 'shield-checkmark',
|
|
1489
|
+
iconColor: '#007AFF',
|
|
1490
|
+
title: t('editProfile.items.twoFactor.title') || 'Two‑Factor Authentication',
|
|
1491
|
+
subtitle: user?.privacySettings?.twoFactorEnabled
|
|
1492
|
+
? (t('editProfile.items.twoFactor.enabled') || 'Enabled')
|
|
1493
|
+
: (t('editProfile.items.twoFactor.disabled') || 'Disabled (recommended)'),
|
|
1494
|
+
onPress: () => startEditing('twoFactor', ''),
|
|
1495
|
+
},
|
|
1496
|
+
]}
|
|
1497
|
+
theme={theme}
|
|
1498
|
+
/>
|
|
1499
|
+
</View>
|
|
1347
1500
|
</View>
|
|
1348
1501
|
</>
|
|
1349
1502
|
)}
|
|
@@ -1355,26 +1508,34 @@ const AccountSettingsScreen: React.FC<BaseScreenProps> = ({
|
|
|
1355
1508
|
const styles = StyleSheet.create({
|
|
1356
1509
|
container: {
|
|
1357
1510
|
flex: 1,
|
|
1358
|
-
backgroundColor: '#f2f2f2',
|
|
1359
1511
|
},
|
|
1360
1512
|
content: {
|
|
1361
1513
|
flex: 1,
|
|
1362
|
-
|
|
1514
|
+
paddingTop: 8,
|
|
1515
|
+
paddingBottom: 24,
|
|
1363
1516
|
},
|
|
1364
1517
|
contentEditing: {
|
|
1365
1518
|
flex: 1,
|
|
1366
1519
|
padding: 0,
|
|
1367
1520
|
},
|
|
1368
1521
|
section: {
|
|
1369
|
-
marginBottom:
|
|
1522
|
+
marginBottom: 32,
|
|
1370
1523
|
},
|
|
1371
1524
|
sectionTitle: {
|
|
1372
|
-
fontSize:
|
|
1525
|
+
fontSize: 13,
|
|
1373
1526
|
fontWeight: '600',
|
|
1374
|
-
color: '#
|
|
1375
|
-
marginBottom:
|
|
1527
|
+
color: '#8E8E93',
|
|
1528
|
+
marginBottom: 8,
|
|
1529
|
+
marginTop: 4,
|
|
1530
|
+
marginHorizontal: 16,
|
|
1531
|
+
textTransform: 'uppercase',
|
|
1532
|
+
letterSpacing: 0.5,
|
|
1376
1533
|
fontFamily: fontFamilies.phuduSemiBold,
|
|
1377
1534
|
},
|
|
1535
|
+
groupedSectionWrapper: {
|
|
1536
|
+
marginHorizontal: 16,
|
|
1537
|
+
backgroundColor: 'transparent',
|
|
1538
|
+
},
|
|
1378
1539
|
|
|
1379
1540
|
userIcon: {
|
|
1380
1541
|
marginRight: 12,
|
|
@@ -1413,21 +1574,21 @@ const styles = StyleSheet.create({
|
|
|
1413
1574
|
flex: 1,
|
|
1414
1575
|
},
|
|
1415
1576
|
editingFieldLabel: {
|
|
1416
|
-
fontSize:
|
|
1577
|
+
fontSize: 13,
|
|
1417
1578
|
fontWeight: '600',
|
|
1418
|
-
|
|
1419
|
-
marginBottom: 12,
|
|
1579
|
+
marginBottom: 8,
|
|
1420
1580
|
fontFamily: fontFamilies.phuduSemiBold,
|
|
1581
|
+
textTransform: 'uppercase',
|
|
1582
|
+
letterSpacing: 0.5,
|
|
1421
1583
|
},
|
|
1422
1584
|
editingFieldInput: {
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
borderColor: '#e0e0e0',
|
|
1426
|
-
borderRadius: 12,
|
|
1585
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1586
|
+
borderRadius: 14,
|
|
1427
1587
|
padding: 16,
|
|
1428
1588
|
fontSize: 17,
|
|
1429
1589
|
minHeight: 52,
|
|
1430
1590
|
fontWeight: '400',
|
|
1591
|
+
letterSpacing: -0.2,
|
|
1431
1592
|
},
|
|
1432
1593
|
editingFieldDescription: {
|
|
1433
1594
|
fontSize: 14,
|
|
@@ -1450,22 +1611,20 @@ const styles = StyleSheet.create({
|
|
|
1450
1611
|
fontWeight: '600',
|
|
1451
1612
|
},
|
|
1452
1613
|
editingFieldTextArea: {
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
borderColor: '#e0e0e0',
|
|
1456
|
-
borderRadius: 12,
|
|
1614
|
+
borderWidth: StyleSheet.hairlineWidth,
|
|
1615
|
+
borderRadius: 14,
|
|
1457
1616
|
padding: 16,
|
|
1458
1617
|
fontSize: 17,
|
|
1459
1618
|
minHeight: 120,
|
|
1460
1619
|
textAlignVertical: 'top',
|
|
1461
1620
|
fontWeight: '400',
|
|
1621
|
+
letterSpacing: -0.2,
|
|
1462
1622
|
},
|
|
1463
1623
|
// Custom editing header styles
|
|
1464
1624
|
editingHeader: {
|
|
1465
1625
|
paddingTop: Platform.OS === 'ios' ? 50 : 16,
|
|
1466
1626
|
paddingBottom: 0,
|
|
1467
|
-
borderBottomWidth:
|
|
1468
|
-
backgroundColor: '#fff',
|
|
1627
|
+
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
1469
1628
|
},
|
|
1470
1629
|
editingHeaderContent: {
|
|
1471
1630
|
flexDirection: 'row',
|
|
@@ -1474,10 +1633,9 @@ const styles = StyleSheet.create({
|
|
|
1474
1633
|
minHeight: 44,
|
|
1475
1634
|
},
|
|
1476
1635
|
editingBackButton: {
|
|
1477
|
-
width:
|
|
1478
|
-
height:
|
|
1479
|
-
borderRadius:
|
|
1480
|
-
backgroundColor: '#F8F9FA',
|
|
1636
|
+
width: 36,
|
|
1637
|
+
height: 36,
|
|
1638
|
+
borderRadius: 20,
|
|
1481
1639
|
alignItems: 'center',
|
|
1482
1640
|
justifyContent: 'center',
|
|
1483
1641
|
marginRight: 12,
|
|
@@ -1505,8 +1663,7 @@ const styles = StyleSheet.create({
|
|
|
1505
1663
|
editingSaveButton: {
|
|
1506
1664
|
paddingHorizontal: 16,
|
|
1507
1665
|
paddingVertical: 8,
|
|
1508
|
-
borderRadius:
|
|
1509
|
-
backgroundColor: '#F8F9FA',
|
|
1666
|
+
borderRadius: 20,
|
|
1510
1667
|
minWidth: 60,
|
|
1511
1668
|
alignItems: 'center',
|
|
1512
1669
|
justifyContent: 'center',
|
|
@@ -1519,20 +1676,24 @@ const styles = StyleSheet.create({
|
|
|
1519
1676
|
editingHeaderBottom: {
|
|
1520
1677
|
flexDirection: 'column',
|
|
1521
1678
|
alignItems: 'flex-start',
|
|
1522
|
-
paddingHorizontal:
|
|
1523
|
-
paddingBottom:
|
|
1524
|
-
paddingTop:
|
|
1525
|
-
},
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1679
|
+
paddingHorizontal: 20,
|
|
1680
|
+
paddingBottom: 20,
|
|
1681
|
+
paddingTop: 24,
|
|
1682
|
+
},
|
|
1683
|
+
editingIconContainer: {
|
|
1684
|
+
width: 64,
|
|
1685
|
+
height: 64,
|
|
1686
|
+
borderRadius: 20,
|
|
1687
|
+
alignItems: 'center',
|
|
1688
|
+
justifyContent: 'center',
|
|
1689
|
+
marginBottom: 16,
|
|
1529
1690
|
},
|
|
1530
1691
|
editingBottomTitle: {
|
|
1531
|
-
fontSize:
|
|
1692
|
+
fontSize: 28,
|
|
1532
1693
|
fontWeight: '700',
|
|
1533
1694
|
fontFamily: fontFamilies.phuduBold,
|
|
1534
1695
|
letterSpacing: -0.5,
|
|
1535
|
-
lineHeight:
|
|
1696
|
+
lineHeight: 34,
|
|
1536
1697
|
textAlign: 'left',
|
|
1537
1698
|
alignSelf: 'flex-start',
|
|
1538
1699
|
},
|