@oxyhq/services 5.13.0 → 5.13.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.
- package/lib/commonjs/core/OxyServices.js +7 -7
- package/lib/commonjs/core/OxyServices.js.map +1 -1
- package/lib/commonjs/i18n/index.js +37 -1
- package/lib/commonjs/i18n/index.js.map +1 -1
- package/lib/commonjs/i18n/locales/ar-SA.json +128 -0
- package/lib/commonjs/i18n/locales/ca-ES.json +128 -0
- package/lib/commonjs/i18n/locales/de-DE.json +128 -0
- package/lib/commonjs/i18n/locales/en-US.json +85 -12
- package/lib/commonjs/i18n/locales/es-ES.json +58 -6
- package/lib/commonjs/i18n/locales/fr-FR.json +128 -0
- package/lib/commonjs/i18n/locales/it-IT.json +128 -0
- package/lib/commonjs/i18n/locales/ja-JP.json +127 -0
- package/lib/commonjs/i18n/locales/ko-KR.json +128 -0
- package/lib/commonjs/i18n/locales/pt-PT.json +128 -0
- package/lib/commonjs/i18n/locales/zh-CN.json +128 -0
- package/lib/commonjs/ui/components/FontLoader.js +22 -42
- package/lib/commonjs/ui/components/FontLoader.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +5 -8
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/StepBasedScreen.js +64 -44
- package/lib/commonjs/ui/components/StepBasedScreen.js.map +1 -1
- package/lib/commonjs/ui/components/internal/GroupedPillButtons.js +14 -35
- package/lib/commonjs/ui/components/internal/GroupedPillButtons.js.map +1 -1
- package/lib/commonjs/ui/components/internal/PinInput.js +2 -2
- package/lib/commonjs/ui/components/internal/PinInput.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +434 -321
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignInScreen.js +43 -39
- package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +139 -125
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js +2 -4
- package/lib/commonjs/ui/screens/internal/SignInUsernameStep.js.map +1 -1
- package/lib/commonjs/ui/screens/steps/RecoverRequestStep.js +45 -25
- package/lib/commonjs/ui/screens/steps/RecoverRequestStep.js.map +1 -1
- package/lib/commonjs/ui/screens/steps/RecoverResetPasswordStep.js +88 -53
- package/lib/commonjs/ui/screens/steps/RecoverResetPasswordStep.js.map +1 -1
- package/lib/commonjs/ui/screens/steps/RecoverSuccessStep.js +79 -58
- package/lib/commonjs/ui/screens/steps/RecoverSuccessStep.js.map +1 -1
- package/lib/commonjs/ui/screens/steps/RecoverVerifyStep.js +61 -52
- package/lib/commonjs/ui/screens/steps/RecoverVerifyStep.js.map +1 -1
- package/lib/commonjs/ui/screens/steps/SignInPasswordStep.js +220 -31
- package/lib/commonjs/ui/screens/steps/SignInPasswordStep.js.map +1 -1
- package/lib/commonjs/ui/screens/steps/SignInTotpStep.js +77 -50
- package/lib/commonjs/ui/screens/steps/SignInTotpStep.js.map +1 -1
- package/lib/commonjs/ui/screens/steps/SignInUsernameStep.js +527 -66
- package/lib/commonjs/ui/screens/steps/SignInUsernameStep.js.map +1 -1
- package/lib/commonjs/ui/screens/steps/SignUpIdentityStep.js +55 -30
- package/lib/commonjs/ui/screens/steps/SignUpIdentityStep.js.map +1 -1
- package/lib/commonjs/ui/screens/steps/SignUpSecurityStep.js +64 -46
- package/lib/commonjs/ui/screens/steps/SignUpSecurityStep.js.map +1 -1
- package/lib/commonjs/ui/screens/steps/SignUpSummaryStep.js +84 -146
- package/lib/commonjs/ui/screens/steps/SignUpSummaryStep.js.map +1 -1
- package/lib/commonjs/ui/screens/steps/SignUpWelcomeStep.js +113 -34
- package/lib/commonjs/ui/screens/steps/SignUpWelcomeStep.js.map +1 -1
- package/lib/commonjs/ui/stores/authStore.js +16 -20
- package/lib/commonjs/ui/stores/authStore.js.map +1 -1
- package/lib/commonjs/ui/styles/authStyles.js +2 -1
- package/lib/commonjs/ui/styles/authStyles.js.map +1 -1
- package/lib/commonjs/ui/styles/index.js +11 -0
- package/lib/commonjs/ui/styles/index.js.map +1 -1
- package/lib/commonjs/ui/styles/spacing.js +51 -0
- package/lib/commonjs/ui/styles/spacing.js.map +1 -0
- package/lib/commonjs/utils/validationUtils.js +1 -1
- package/lib/module/core/OxyServices.js +7 -7
- package/lib/module/core/OxyServices.js.map +1 -1
- package/lib/module/i18n/index.js +37 -1
- package/lib/module/i18n/index.js.map +1 -1
- package/lib/module/i18n/locales/ar-SA.json +128 -0
- package/lib/module/i18n/locales/ca-ES.json +128 -0
- package/lib/module/i18n/locales/de-DE.json +128 -0
- package/lib/module/i18n/locales/en-US.json +85 -12
- package/lib/module/i18n/locales/es-ES.json +58 -6
- package/lib/module/i18n/locales/fr-FR.json +128 -0
- package/lib/module/i18n/locales/it-IT.json +128 -0
- package/lib/module/i18n/locales/ja-JP.json +127 -0
- package/lib/module/i18n/locales/ko-KR.json +128 -0
- package/lib/module/i18n/locales/pt-PT.json +128 -0
- package/lib/module/i18n/locales/zh-CN.json +128 -0
- package/lib/module/ui/components/FontLoader.js +23 -43
- package/lib/module/ui/components/FontLoader.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +6 -8
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/StepBasedScreen.js +65 -45
- package/lib/module/ui/components/StepBasedScreen.js.map +1 -1
- package/lib/module/ui/components/internal/GroupedPillButtons.js +14 -35
- package/lib/module/ui/components/internal/GroupedPillButtons.js.map +1 -1
- package/lib/module/ui/components/internal/PinInput.js +2 -2
- package/lib/module/ui/components/internal/PinInput.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +434 -321
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/SignInScreen.js +44 -40
- package/lib/module/ui/screens/SignInScreen.js.map +1 -1
- package/lib/module/ui/screens/WelcomeNewUserScreen.js +138 -126
- package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/module/ui/screens/internal/SignInUsernameStep.js +2 -4
- package/lib/module/ui/screens/internal/SignInUsernameStep.js.map +1 -1
- package/lib/module/ui/screens/steps/RecoverRequestStep.js +45 -25
- package/lib/module/ui/screens/steps/RecoverRequestStep.js.map +1 -1
- package/lib/module/ui/screens/steps/RecoverResetPasswordStep.js +89 -54
- package/lib/module/ui/screens/steps/RecoverResetPasswordStep.js.map +1 -1
- package/lib/module/ui/screens/steps/RecoverSuccessStep.js +80 -59
- package/lib/module/ui/screens/steps/RecoverSuccessStep.js.map +1 -1
- package/lib/module/ui/screens/steps/RecoverVerifyStep.js +62 -53
- package/lib/module/ui/screens/steps/RecoverVerifyStep.js.map +1 -1
- package/lib/module/ui/screens/steps/SignInPasswordStep.js +221 -32
- package/lib/module/ui/screens/steps/SignInPasswordStep.js.map +1 -1
- package/lib/module/ui/screens/steps/SignInTotpStep.js +78 -51
- package/lib/module/ui/screens/steps/SignInTotpStep.js.map +1 -1
- package/lib/module/ui/screens/steps/SignInUsernameStep.js +530 -68
- package/lib/module/ui/screens/steps/SignInUsernameStep.js.map +1 -1
- package/lib/module/ui/screens/steps/SignUpIdentityStep.js +55 -30
- package/lib/module/ui/screens/steps/SignUpIdentityStep.js.map +1 -1
- package/lib/module/ui/screens/steps/SignUpSecurityStep.js +65 -47
- package/lib/module/ui/screens/steps/SignUpSecurityStep.js.map +1 -1
- package/lib/module/ui/screens/steps/SignUpSummaryStep.js +84 -146
- package/lib/module/ui/screens/steps/SignUpSummaryStep.js.map +1 -1
- package/lib/module/ui/screens/steps/SignUpWelcomeStep.js +114 -35
- package/lib/module/ui/screens/steps/SignUpWelcomeStep.js.map +1 -1
- package/lib/module/ui/stores/authStore.js +16 -20
- package/lib/module/ui/stores/authStore.js.map +1 -1
- package/lib/module/ui/styles/authStyles.js +2 -1
- package/lib/module/ui/styles/authStyles.js.map +1 -1
- package/lib/module/ui/styles/index.js +1 -0
- package/lib/module/ui/styles/index.js.map +1 -1
- package/lib/module/ui/styles/spacing.js +48 -0
- package/lib/module/ui/styles/spacing.js.map +1 -0
- package/lib/module/utils/validationUtils.js +1 -1
- package/lib/typescript/core/OxyServices.d.ts +4 -2
- package/lib/typescript/core/OxyServices.d.ts.map +1 -1
- package/lib/typescript/i18n/index.d.ts.map +1 -1
- package/lib/typescript/ui/components/FontLoader.d.ts +3 -3
- package/lib/typescript/ui/components/FontLoader.d.ts.map +1 -1
- package/lib/typescript/ui/components/OxyProvider.d.ts +2 -2
- package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/ui/components/StepBasedScreen.d.ts.map +1 -1
- package/lib/typescript/ui/components/internal/GroupedPillButtons.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts +1 -0
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/steps/RecoverRequestStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/steps/RecoverResetPasswordStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/steps/RecoverSuccessStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/steps/RecoverVerifyStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/steps/SignInPasswordStep.d.ts +2 -0
- package/lib/typescript/ui/screens/steps/SignInPasswordStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/steps/SignInTotpStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/steps/SignInUsernameStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/steps/SignUpIdentityStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/steps/SignUpSecurityStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/steps/SignUpSummaryStep.d.ts.map +1 -1
- package/lib/typescript/ui/screens/steps/SignUpWelcomeStep.d.ts.map +1 -1
- package/lib/typescript/ui/stores/authStore.d.ts +7 -3
- package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
- package/lib/typescript/ui/styles/authStyles.d.ts +1 -0
- package/lib/typescript/ui/styles/authStyles.d.ts.map +1 -1
- package/lib/typescript/ui/styles/index.d.ts +1 -0
- package/lib/typescript/ui/styles/index.d.ts.map +1 -1
- package/lib/typescript/ui/styles/spacing.d.ts +43 -0
- package/lib/typescript/ui/styles/spacing.d.ts.map +1 -0
- package/lib/typescript/utils/validationUtils.d.ts +1 -1
- package/package.json +1 -1
- package/src/core/OxyServices.ts +10 -8
- package/src/i18n/index.ts +36 -0
- package/src/i18n/locales/ar-SA.json +128 -0
- package/src/i18n/locales/ca-ES.json +128 -0
- package/src/i18n/locales/de-DE.json +128 -0
- package/src/i18n/locales/en-US.json +85 -12
- package/src/i18n/locales/es-ES.json +58 -6
- package/src/i18n/locales/fr-FR.json +128 -0
- package/src/i18n/locales/it-IT.json +128 -0
- package/src/i18n/locales/ja-JP.json +127 -0
- package/src/i18n/locales/ko-KR.json +128 -0
- package/src/i18n/locales/pt-PT.json +128 -0
- package/src/i18n/locales/zh-CN.json +128 -0
- package/src/ui/components/FontLoader.tsx +17 -37
- package/src/ui/components/OxyProvider.tsx +14 -13
- package/src/ui/components/StepBasedScreen.tsx +66 -43
- package/src/ui/components/internal/GroupedPillButtons.tsx +15 -31
- package/src/ui/components/internal/PinInput.tsx +2 -2
- package/src/ui/context/OxyContext.tsx +404 -285
- package/src/ui/screens/FileManagementScreen.tsx +15 -15
- package/src/ui/screens/SignInScreen.tsx +59 -36
- package/src/ui/screens/WelcomeNewUserScreen.tsx +102 -91
- package/src/ui/screens/internal/SignInUsernameStep.tsx +1 -1
- package/src/ui/screens/steps/RecoverRequestStep.tsx +34 -24
- package/src/ui/screens/steps/RecoverResetPasswordStep.tsx +65 -36
- package/src/ui/screens/steps/RecoverSuccessStep.tsx +71 -47
- package/src/ui/screens/steps/RecoverVerifyStep.tsx +60 -50
- package/src/ui/screens/steps/SignInPasswordStep.tsx +191 -29
- package/src/ui/screens/steps/SignInTotpStep.tsx +68 -34
- package/src/ui/screens/steps/SignInUsernameStep.tsx +586 -57
- package/src/ui/screens/steps/SignUpIdentityStep.tsx +49 -35
- package/src/ui/screens/steps/SignUpSecurityStep.tsx +56 -39
- package/src/ui/screens/steps/SignUpSummaryStep.tsx +99 -89
- package/src/ui/screens/steps/SignUpWelcomeStep.tsx +88 -20
- package/src/ui/stores/authStore.ts +15 -19
- package/src/ui/styles/authStyles.ts +2 -1
- package/src/ui/styles/index.ts +1 -0
- package/src/ui/styles/spacing.ts +46 -0
- package/src/utils/validationUtils.ts +1 -1
|
@@ -159,7 +159,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
159
159
|
return;
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
|
-
|
|
162
|
+
|
|
163
163
|
// Update file visibility if it differs from defaultVisibility
|
|
164
164
|
const fileVisibility = (file.metadata as any)?.visibility || 'private';
|
|
165
165
|
if (fileVisibility !== defaultVisibility) {
|
|
@@ -171,25 +171,25 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
171
171
|
// Continue anyway - selection shouldn't fail if visibility update fails
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
|
-
|
|
174
|
+
|
|
175
175
|
// Link file to entity if linkContext is provided
|
|
176
176
|
if (linkContext) {
|
|
177
177
|
try {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
178
|
+
await oxyServices.assetLink(
|
|
179
|
+
file.id,
|
|
180
|
+
linkContext.app,
|
|
181
|
+
linkContext.entityType,
|
|
182
|
+
linkContext.entityId,
|
|
183
|
+
defaultVisibility,
|
|
184
|
+
(linkContext as any).webhookUrl
|
|
185
|
+
);
|
|
186
186
|
console.log(`Linked file ${file.id} to ${linkContext.app}/${linkContext.entityType}/${linkContext.entityId}`);
|
|
187
187
|
} catch (error) {
|
|
188
188
|
console.error('Failed to link file:', error);
|
|
189
189
|
// Continue anyway - selection shouldn't fail if linking fails
|
|
190
190
|
}
|
|
191
191
|
}
|
|
192
|
-
|
|
192
|
+
|
|
193
193
|
if (!multiSelect) {
|
|
194
194
|
onSelect?.(file);
|
|
195
195
|
if (afterSelect === 'back') {
|
|
@@ -220,7 +220,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
220
220
|
const map: Record<string, FileMetadata> = {};
|
|
221
221
|
files.forEach(f => { map[f.id] = f; });
|
|
222
222
|
const chosen = Array.from(selectedIds).map(id => map[id]).filter(Boolean);
|
|
223
|
-
|
|
223
|
+
|
|
224
224
|
// Update visibility and link files if needed
|
|
225
225
|
const updatePromises = chosen.map(async (file) => {
|
|
226
226
|
// Update visibility if needed
|
|
@@ -233,7 +233,7 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
233
233
|
console.error(`Failed to update visibility for ${file.id}:`, error);
|
|
234
234
|
}
|
|
235
235
|
}
|
|
236
|
-
|
|
236
|
+
|
|
237
237
|
// Link file to entity if linkContext provided
|
|
238
238
|
if (linkContext) {
|
|
239
239
|
try {
|
|
@@ -251,10 +251,10 @@ const FileManagementScreen: React.FC<FileManagementScreenProps> = ({
|
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
253
|
});
|
|
254
|
-
|
|
254
|
+
|
|
255
255
|
// Wait for all updates (but don't block on failures)
|
|
256
256
|
await Promise.allSettled(updatePromises);
|
|
257
|
-
|
|
257
|
+
|
|
258
258
|
onConfirmSelection?.(chosen);
|
|
259
259
|
onClose?.();
|
|
260
260
|
}, [selectMode, multiSelect, selectedIds, files, onConfirmSelection, onClose, defaultVisibility, oxyServices, linkContext]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
|
-
import { useState,
|
|
2
|
+
import { useState, useMemo, useCallback } from 'react';
|
|
3
3
|
import type { BaseScreenProps } from '../navigation/types';
|
|
4
4
|
import { useOxy } from '../context/OxyContext';
|
|
5
5
|
import { useThemeColors } from '../styles';
|
|
@@ -29,11 +29,9 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
29
29
|
const [validationStatus, setValidationStatus] = useState<'idle' | 'validating' | 'valid' | 'invalid'>(
|
|
30
30
|
initialUserProfile ? 'valid' : 'idle'
|
|
31
31
|
);
|
|
32
|
+
const [existingSession, setExistingSession] = useState<any>(null);
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
const validationCache = useRef<Map<string, { profile: any }>>(new Map());
|
|
35
|
-
|
|
36
|
-
const { login, completeMfaLogin, isLoading, user, isAuthenticated, sessions, oxyServices } = useOxy();
|
|
34
|
+
const { login, completeMfaLogin, isLoading, user, isAuthenticated, sessions, oxyServices, switchSession } = useOxy();
|
|
37
35
|
|
|
38
36
|
// Only log props in development mode to reduce console noise
|
|
39
37
|
if (__DEV__) {
|
|
@@ -68,14 +66,13 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
68
66
|
return false;
|
|
69
67
|
}
|
|
70
68
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (
|
|
74
|
-
if (__DEV__) console.log('
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
return true;
|
|
69
|
+
const offlineDetected = typeof navigator !== 'undefined' && navigator.onLine === false;
|
|
70
|
+
|
|
71
|
+
if (offlineDetected) {
|
|
72
|
+
if (__DEV__) console.log('⚠️ Offline detected, skipping username validation');
|
|
73
|
+
setValidationStatus('invalid');
|
|
74
|
+
setErrorMessage('No connection. Check your internet connection and try again.');
|
|
75
|
+
return false;
|
|
79
76
|
}
|
|
80
77
|
|
|
81
78
|
if (__DEV__) console.log('🔄 Validating username with API...');
|
|
@@ -97,14 +94,24 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
97
94
|
|
|
98
95
|
if (__DEV__) console.log('✅ Username is valid:', profileData);
|
|
99
96
|
setUserProfile(profileData);
|
|
97
|
+
|
|
98
|
+
// Check if this account is already signed in
|
|
99
|
+
const profileUserId = profile.id?.toString();
|
|
100
|
+
const existing = sessions?.find(s => {
|
|
101
|
+
const sessionUserId = s.userId?.toString();
|
|
102
|
+
return sessionUserId === profileUserId;
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
if (existing) {
|
|
106
|
+
setExistingSession(existing);
|
|
107
|
+
if (__DEV__) console.log('✅ Account already signed in:', existing);
|
|
108
|
+
} else {
|
|
109
|
+
setExistingSession(null);
|
|
110
|
+
}
|
|
111
|
+
|
|
100
112
|
setValidationStatus('valid');
|
|
101
113
|
setErrorMessage('');
|
|
102
114
|
|
|
103
|
-
// Cache the result
|
|
104
|
-
validationCache.current.set(usernameToValidate, {
|
|
105
|
-
profile: profileData
|
|
106
|
-
});
|
|
107
|
-
|
|
108
115
|
return true;
|
|
109
116
|
} else {
|
|
110
117
|
if (__DEV__) console.log('❌ Username not found');
|
|
@@ -116,25 +123,29 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
116
123
|
if (__DEV__) console.log('🚨 Validation error:', error);
|
|
117
124
|
|
|
118
125
|
// If user not found (404), username doesn't exist
|
|
119
|
-
if (error
|
|
126
|
+
if (error?.status === 404 || error?.code === 'USER_NOT_FOUND') {
|
|
120
127
|
console.log('❌ Username not found (404)');
|
|
121
128
|
setValidationStatus('invalid');
|
|
122
129
|
setErrorMessage('Username not found.');
|
|
123
130
|
return false;
|
|
124
131
|
}
|
|
125
132
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
+
const isNetworkError =
|
|
134
|
+
error?.status === 0 ||
|
|
135
|
+
error?.code === 'ECONNABORTED' ||
|
|
136
|
+
error?.code === 'ERR_NETWORK' ||
|
|
137
|
+
error?.message?.toLowerCase?.().includes('network request failed') ||
|
|
138
|
+
error?.message?.toLowerCase?.().includes('network error') ||
|
|
139
|
+
error?.name === 'AbortError' ||
|
|
140
|
+
error?.type === 'network';
|
|
133
141
|
|
|
134
|
-
// For other errors, show generic message
|
|
135
142
|
console.error('Username validation error:', error);
|
|
136
143
|
setValidationStatus('invalid');
|
|
137
|
-
setErrorMessage(
|
|
144
|
+
setErrorMessage(
|
|
145
|
+
isNetworkError
|
|
146
|
+
? 'No connection. Check your internet connection and try again.'
|
|
147
|
+
: 'Unable to validate username. Please try again.'
|
|
148
|
+
);
|
|
138
149
|
return false;
|
|
139
150
|
} finally {
|
|
140
151
|
setIsValidating(false);
|
|
@@ -177,6 +188,22 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
177
188
|
|
|
178
189
|
const [mfaToken, setMfaToken] = useState<string | null>(null);
|
|
179
190
|
|
|
191
|
+
const handleContinueWithExistingAccount = useCallback(async () => {
|
|
192
|
+
if (!existingSession) return;
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
setErrorMessage('');
|
|
196
|
+
await switchSession(existingSession.sessionId);
|
|
197
|
+
// Get the user for the authenticated callback
|
|
198
|
+
const currentUser = await oxyServices.getUserBySession(existingSession.sessionId);
|
|
199
|
+
if (onAuthenticated) {
|
|
200
|
+
onAuthenticated(currentUser);
|
|
201
|
+
}
|
|
202
|
+
} catch (error: any) {
|
|
203
|
+
setErrorMessage(error.message || 'Failed to switch account');
|
|
204
|
+
}
|
|
205
|
+
}, [existingSession, switchSession, oxyServices, onAuthenticated]);
|
|
206
|
+
|
|
180
207
|
const handleSignIn = useCallback(async () => {
|
|
181
208
|
if (!password) {
|
|
182
209
|
setErrorMessage('Please enter your password.');
|
|
@@ -186,6 +213,7 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
186
213
|
setErrorMessage('Please enter a valid username first.');
|
|
187
214
|
return;
|
|
188
215
|
}
|
|
216
|
+
|
|
189
217
|
try {
|
|
190
218
|
setErrorMessage('');
|
|
191
219
|
const user = await login(username, password);
|
|
@@ -201,13 +229,6 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
201
229
|
}
|
|
202
230
|
}, [username, password, login, onAuthenticated, userProfile]);
|
|
203
231
|
|
|
204
|
-
// Simple cleanup on unmount - that's all we need for username validation
|
|
205
|
-
useEffect(() => {
|
|
206
|
-
return () => {
|
|
207
|
-
validationCache.current.clear();
|
|
208
|
-
};
|
|
209
|
-
}, []);
|
|
210
|
-
|
|
211
232
|
// Step configurations
|
|
212
233
|
const steps: StepConfig[] = useMemo(() => {
|
|
213
234
|
const base: StepConfig[] = [
|
|
@@ -259,6 +280,8 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
259
280
|
handleInputBlur,
|
|
260
281
|
handleSignIn, // Add sign-in function for password step
|
|
261
282
|
mfaToken,
|
|
283
|
+
existingSession,
|
|
284
|
+
handleContinueWithExistingAccount,
|
|
262
285
|
},
|
|
263
286
|
...(mfaToken ? [{
|
|
264
287
|
username,
|
|
@@ -272,7 +295,7 @@ const SignInScreen: React.FC<BaseScreenProps> = ({
|
|
|
272
295
|
username, password, errorMessage, validationStatus, userProfile, mfaToken,
|
|
273
296
|
isValidating, isInputFocused, isAddAccountMode, user, showPassword,
|
|
274
297
|
isLoading, handleUsernameChange, handlePasswordChange, handleInputFocus, handleInputBlur,
|
|
275
|
-
validateUsername, handleSignIn, completeMfaLogin
|
|
298
|
+
validateUsername, handleSignIn, completeMfaLogin, existingSession, handleContinueWithExistingAccount
|
|
276
299
|
]);
|
|
277
300
|
|
|
278
301
|
return (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import React, { useCallback, useMemo, useRef, useState } from 'react';
|
|
1
|
+
import React, { useCallback, useMemo, useRef, useState, useEffect } from 'react';
|
|
2
2
|
import { View, Text, TouchableOpacity, StyleSheet, Platform, Animated, ScrollView } from 'react-native';
|
|
3
|
+
import AnimatedReanimated, { useSharedValue, useAnimatedStyle, withTiming } from 'react-native-reanimated';
|
|
3
4
|
import type { BaseScreenProps } from '../navigation/types';
|
|
4
5
|
import { useOxy } from '../context/OxyContext';
|
|
5
6
|
import Avatar from '../components/Avatar';
|
|
@@ -10,6 +11,41 @@ import { useThemeColors } from '../styles';
|
|
|
10
11
|
import GroupedPillButtons from '../components/internal/GroupedPillButtons';
|
|
11
12
|
import { useI18n } from '../hooks/useI18n';
|
|
12
13
|
|
|
14
|
+
const GAP = 12;
|
|
15
|
+
const INNER_GAP = 8;
|
|
16
|
+
|
|
17
|
+
// Individual animated progress dot
|
|
18
|
+
const AnimatedProgressDot: React.FC<{
|
|
19
|
+
isActive: boolean;
|
|
20
|
+
colors: any;
|
|
21
|
+
styles: any;
|
|
22
|
+
}> = ({ isActive, colors, styles }) => {
|
|
23
|
+
const width = useSharedValue(isActive ? 12 : 6);
|
|
24
|
+
const backgroundColor = useSharedValue(isActive ? colors.primary : colors.border);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
width.value = withTiming(isActive ? 12 : 6, { duration: 300 });
|
|
28
|
+
backgroundColor.value = withTiming(
|
|
29
|
+
isActive ? colors.primary : colors.border,
|
|
30
|
+
{ duration: 300 }
|
|
31
|
+
);
|
|
32
|
+
}, [isActive, colors.primary, colors.border, width, backgroundColor]);
|
|
33
|
+
|
|
34
|
+
const animatedStyle = useAnimatedStyle(() => ({
|
|
35
|
+
width: width.value,
|
|
36
|
+
backgroundColor: backgroundColor.value,
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<AnimatedReanimated.View
|
|
41
|
+
style={[
|
|
42
|
+
styles.progressDot,
|
|
43
|
+
animatedStyle,
|
|
44
|
+
]}
|
|
45
|
+
/>
|
|
46
|
+
);
|
|
47
|
+
};
|
|
48
|
+
|
|
13
49
|
/**
|
|
14
50
|
* Post-signup welcome & onboarding screen.
|
|
15
51
|
* - Greets the newly registered user
|
|
@@ -138,33 +174,44 @@ const WelcomeNewUserScreen: React.FC<BaseScreenProps & { newUser?: any }> = ({
|
|
|
138
174
|
<View style={styles.container}>
|
|
139
175
|
<View style={styles.progressContainer}>
|
|
140
176
|
{steps.map((s, i) => (
|
|
141
|
-
<
|
|
177
|
+
<AnimatedProgressDot
|
|
178
|
+
key={s.key}
|
|
179
|
+
isActive={i === currentStep}
|
|
180
|
+
colors={colors}
|
|
181
|
+
styles={styles}
|
|
182
|
+
/>
|
|
142
183
|
))}
|
|
143
184
|
</View>
|
|
144
185
|
<Animated.View style={{ opacity: fadeAnim, transform: [{ translateX: slideAnim }] }}>
|
|
145
186
|
<ScrollView contentContainerStyle={styles.scrollInner} showsVerticalScrollIndicator={false}>
|
|
146
|
-
<
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
{step.bullets.map(b => (
|
|
151
|
-
<View key={b} style={styles.bulletRow}>
|
|
152
|
-
<Ionicons name="ellipse" size={8} color={colors.primary} style={{ marginTop: 6 }} />
|
|
153
|
-
<Text style={styles.bulletText}>{b}</Text>
|
|
154
|
-
</View>
|
|
155
|
-
))}
|
|
187
|
+
<View style={styles.contentContainer}>
|
|
188
|
+
<View style={[styles.header, styles.sectionSpacing]}>
|
|
189
|
+
<Text style={[styles.title, { color: colors.text }]}>{step.title}</Text>
|
|
190
|
+
{step.body && <Text style={[styles.body, { color: colors.secondaryText }]}>{step.body}</Text>}
|
|
156
191
|
</View>
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
192
|
+
{Array.isArray(step.bullets) && step.bullets.length > 0 && (
|
|
193
|
+
<View style={[styles.bulletContainer, styles.sectionSpacing]}>
|
|
194
|
+
{step.bullets.map(b => (
|
|
195
|
+
<View key={b} style={styles.bulletRow}>
|
|
196
|
+
<Ionicons name="ellipse" size={8} color={colors.primary} style={{ marginTop: 6 }} />
|
|
197
|
+
<Text style={[styles.bulletText, { color: colors.secondaryText }]}>{b}</Text>
|
|
198
|
+
</View>
|
|
199
|
+
))}
|
|
200
|
+
</View>
|
|
201
|
+
)}
|
|
202
|
+
{step.showAvatar && (
|
|
203
|
+
<View style={[styles.avatarSection, styles.sectionSpacing]}>
|
|
204
|
+
<Avatar size={120} name={currentUser?.username} uri={avatarUri} theme={theme} style={styles.avatar} />
|
|
205
|
+
<TouchableOpacity style={[styles.changeAvatarButton, { backgroundColor: colors.primary }]} onPress={openAvatarPicker}>
|
|
206
|
+
<Ionicons name="image-outline" size={18} color="#FFFFFF" />
|
|
207
|
+
<Text style={styles.changeAvatarText}>{avatarUri ? (t('welcomeNew.avatar.change') || 'Change Avatar') : (t('welcomeNew.avatar.add') || 'Add Avatar')}</Text>
|
|
208
|
+
</TouchableOpacity>
|
|
209
|
+
</View>
|
|
210
|
+
)}
|
|
211
|
+
<View style={styles.sectionSpacing}>
|
|
212
|
+
<GroupedPillButtons buttons={pillButtons} colors={colors} />
|
|
165
213
|
</View>
|
|
166
|
-
|
|
167
|
-
<GroupedPillButtons buttons={pillButtons} colors={colors} />
|
|
214
|
+
</View>
|
|
168
215
|
</ScrollView>
|
|
169
216
|
</Animated.View>
|
|
170
217
|
</View>
|
|
@@ -174,11 +221,7 @@ const WelcomeNewUserScreen: React.FC<BaseScreenProps & { newUser?: any }> = ({
|
|
|
174
221
|
|
|
175
222
|
const createStyles = (theme: string) => {
|
|
176
223
|
const isDark = theme === 'dark';
|
|
177
|
-
const textColor = isDark ? '#FFFFFF' : '#000000';
|
|
178
|
-
const secondary = isDark ? '#CCCCCC' : '#555555';
|
|
179
|
-
const cardBg = isDark ? '#1E1E1E' : '#FFFFFF';
|
|
180
224
|
const border = isDark ? '#333333' : '#E0E0E0';
|
|
181
|
-
const primary = '#007AFF';
|
|
182
225
|
return StyleSheet.create({
|
|
183
226
|
container: {
|
|
184
227
|
width: '100%',
|
|
@@ -186,25 +229,38 @@ const createStyles = (theme: string) => {
|
|
|
186
229
|
},
|
|
187
230
|
scrollInner: {
|
|
188
231
|
paddingBottom: 12,
|
|
232
|
+
paddingTop: 0,
|
|
233
|
+
},
|
|
234
|
+
contentContainer: {
|
|
235
|
+
width: '100%',
|
|
236
|
+
maxWidth: 420,
|
|
237
|
+
alignSelf: 'center',
|
|
238
|
+
},
|
|
239
|
+
sectionSpacing: {
|
|
240
|
+
marginBottom: GAP,
|
|
241
|
+
},
|
|
242
|
+
header: {
|
|
243
|
+
alignItems: 'flex-start',
|
|
244
|
+
width: '100%',
|
|
245
|
+
gap: INNER_GAP / 2,
|
|
189
246
|
},
|
|
190
247
|
title: {
|
|
191
248
|
fontSize: 42,
|
|
192
249
|
fontFamily: Platform.OS === 'web' ? 'Phudu' : 'Phudu-Bold',
|
|
193
250
|
fontWeight: Platform.OS === 'web' ? 'bold' : undefined,
|
|
194
251
|
letterSpacing: -1,
|
|
195
|
-
|
|
196
|
-
marginBottom: 12,
|
|
252
|
+
textAlign: 'left',
|
|
197
253
|
},
|
|
198
254
|
body: {
|
|
199
255
|
fontSize: 16,
|
|
200
256
|
lineHeight: 22,
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
257
|
+
textAlign: 'left',
|
|
258
|
+
maxWidth: 320,
|
|
259
|
+
alignSelf: 'flex-start',
|
|
204
260
|
},
|
|
205
261
|
bulletContainer: {
|
|
206
|
-
gap:
|
|
207
|
-
|
|
262
|
+
gap: INNER_GAP,
|
|
263
|
+
width: '100%',
|
|
208
264
|
},
|
|
209
265
|
bulletRow: {
|
|
210
266
|
flexDirection: 'row',
|
|
@@ -215,26 +271,26 @@ const createStyles = (theme: string) => {
|
|
|
215
271
|
flex: 1,
|
|
216
272
|
fontSize: 15,
|
|
217
273
|
lineHeight: 20,
|
|
218
|
-
color: secondary,
|
|
219
274
|
},
|
|
220
275
|
avatarSection: {
|
|
221
276
|
width: '100%',
|
|
222
277
|
alignItems: 'center',
|
|
223
|
-
marginBottom: 40,
|
|
224
278
|
},
|
|
225
279
|
avatar: {
|
|
226
|
-
marginBottom:
|
|
227
|
-
borderWidth: 4,
|
|
228
|
-
borderColor: primary + '40',
|
|
280
|
+
marginBottom: INNER_GAP,
|
|
229
281
|
},
|
|
230
282
|
changeAvatarButton: {
|
|
231
283
|
flexDirection: 'row',
|
|
232
284
|
alignItems: 'center',
|
|
233
|
-
backgroundColor: primary,
|
|
234
285
|
paddingHorizontal: 18,
|
|
235
286
|
paddingVertical: 10,
|
|
236
|
-
borderRadius:
|
|
287
|
+
borderRadius: 28,
|
|
237
288
|
gap: 8,
|
|
289
|
+
shadowOpacity: 0,
|
|
290
|
+
shadowRadius: 0,
|
|
291
|
+
shadowOffset: { width: 0, height: 0 },
|
|
292
|
+
elevation: 0,
|
|
293
|
+
...(Platform.OS === 'web' ? { boxShadow: 'none' } : null),
|
|
238
294
|
},
|
|
239
295
|
changeAvatarText: {
|
|
240
296
|
color: '#FFFFFF',
|
|
@@ -245,61 +301,16 @@ const createStyles = (theme: string) => {
|
|
|
245
301
|
flexDirection: 'row',
|
|
246
302
|
width: '100%',
|
|
247
303
|
justifyContent: 'center',
|
|
248
|
-
|
|
249
|
-
|
|
304
|
+
marginTop: 24, // Space for bottom sheet handle (~20px) + small buffer
|
|
305
|
+
marginBottom: 24, // Equal spacing below dots
|
|
250
306
|
},
|
|
251
307
|
progressDot: {
|
|
252
|
-
height:
|
|
253
|
-
width:
|
|
254
|
-
borderRadius:
|
|
255
|
-
marginHorizontal:
|
|
308
|
+
height: 6,
|
|
309
|
+
width: 6,
|
|
310
|
+
borderRadius: 3,
|
|
311
|
+
marginHorizontal: 3,
|
|
256
312
|
backgroundColor: border,
|
|
257
313
|
},
|
|
258
|
-
navBar: {
|
|
259
|
-
flexDirection: 'row',
|
|
260
|
-
alignItems: 'center',
|
|
261
|
-
width: '100%',
|
|
262
|
-
gap: 12,
|
|
263
|
-
marginTop: 8,
|
|
264
|
-
},
|
|
265
|
-
navButton: {
|
|
266
|
-
flexDirection: 'row',
|
|
267
|
-
alignItems: 'center',
|
|
268
|
-
gap: 6,
|
|
269
|
-
paddingHorizontal: 14,
|
|
270
|
-
paddingVertical: 10,
|
|
271
|
-
borderRadius: 12,
|
|
272
|
-
},
|
|
273
|
-
backButton: {
|
|
274
|
-
backgroundColor: cardBg,
|
|
275
|
-
borderWidth: 1,
|
|
276
|
-
borderColor: border,
|
|
277
|
-
},
|
|
278
|
-
skipButton: {
|
|
279
|
-
marginLeft: 'auto',
|
|
280
|
-
backgroundColor: 'transparent',
|
|
281
|
-
paddingHorizontal: 4,
|
|
282
|
-
},
|
|
283
|
-
navText: {
|
|
284
|
-
fontSize: 14,
|
|
285
|
-
fontWeight: '500',
|
|
286
|
-
},
|
|
287
|
-
primaryButton: {
|
|
288
|
-
flexDirection: 'row',
|
|
289
|
-
alignItems: 'center',
|
|
290
|
-
justifyContent: 'center',
|
|
291
|
-
gap: 8,
|
|
292
|
-
backgroundColor: primary,
|
|
293
|
-
paddingVertical: 18,
|
|
294
|
-
borderRadius: 16,
|
|
295
|
-
width: '100%',
|
|
296
|
-
},
|
|
297
|
-
primaryButtonText: {
|
|
298
|
-
color: '#FFFFFF',
|
|
299
|
-
fontSize: 16,
|
|
300
|
-
fontWeight: '600',
|
|
301
|
-
letterSpacing: 0.5,
|
|
302
|
-
},
|
|
303
314
|
});
|
|
304
315
|
};
|
|
305
316
|
|
|
@@ -95,7 +95,7 @@ const SignInUsernameStep: React.FC<SignInUsernameStepProps> = ({
|
|
|
95
95
|
<View style={[styles.modernInfoCard, { backgroundColor: colors.inputBackground }]}>
|
|
96
96
|
<Ionicons name="information-circle" size={20} color={colors.primary} />
|
|
97
97
|
<Text style={[styles.modernInfoText, { color: colors.text }]}>
|
|
98
|
-
{t('signin.currentlySignedInAs'
|
|
98
|
+
{t('signin.currentlySignedInAs') || 'Currently signed in as'} <Text style={{ fontWeight: 'bold' }}>{user?.name?.full || user?.username}</Text>
|
|
99
99
|
</Text>
|
|
100
100
|
</View>
|
|
101
101
|
)}
|
|
@@ -9,6 +9,7 @@ import GroupedPillButtons from '../../components/internal/GroupedPillButtons';
|
|
|
9
9
|
import { toast } from '../../../lib/sonner';
|
|
10
10
|
import type { OxyServices } from '../../../core';
|
|
11
11
|
import { useI18n } from '../../hooks/useI18n';
|
|
12
|
+
import { stepStyles } from '../../styles/spacing';
|
|
12
13
|
|
|
13
14
|
interface RecoverRequestStepProps {
|
|
14
15
|
// Common props from StepBasedScreen
|
|
@@ -51,6 +52,7 @@ const RecoverRequestStep: React.FC<RecoverRequestStepProps> = ({
|
|
|
51
52
|
}) => {
|
|
52
53
|
const inputRef = useRef<any>(null);
|
|
53
54
|
const { t } = useI18n();
|
|
55
|
+
const baseStyles = stepStyles;
|
|
54
56
|
|
|
55
57
|
const handleIdentifierChange = (text: string) => {
|
|
56
58
|
setIdentifier(text);
|
|
@@ -83,13 +85,15 @@ const RecoverRequestStep: React.FC<RecoverRequestStepProps> = ({
|
|
|
83
85
|
|
|
84
86
|
return (
|
|
85
87
|
<>
|
|
86
|
-
<
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
88
|
+
<View style={[baseStyles.container, baseStyles.sectionSpacing, { alignItems: 'flex-start' }]}>
|
|
89
|
+
<HighFive width={100} height={100} />
|
|
90
|
+
</View>
|
|
91
|
+
<View style={[baseStyles.container, baseStyles.sectionSpacing, baseStyles.header]}>
|
|
92
|
+
<Text style={[styles.modernTitle, baseStyles.title, { color: colors.text, marginBottom: 0, marginTop: 0 }]}>{t('recover.title')}</Text>
|
|
93
|
+
<Text style={[styles.modernSubtitle, baseStyles.subtitle, { color: colors.secondaryText, marginBottom: 0, marginTop: 0 }]}>{t('recover.noEmail')}</Text>
|
|
90
94
|
</View>
|
|
91
95
|
|
|
92
|
-
<View style={
|
|
96
|
+
<View style={[baseStyles.container, baseStyles.sectionSpacing]}>
|
|
93
97
|
<TextField
|
|
94
98
|
ref={inputRef}
|
|
95
99
|
label={t('recover.username.label')}
|
|
@@ -101,31 +105,37 @@ const RecoverRequestStep: React.FC<RecoverRequestStepProps> = ({
|
|
|
101
105
|
testID="recover-identifier-input"
|
|
102
106
|
variant="filled"
|
|
103
107
|
error={errorMessage || undefined}
|
|
108
|
+
helperText={t('recover.username.helper') || 'Enter your username or email'}
|
|
104
109
|
editable={!isLoading}
|
|
105
110
|
onSubmitEditing={handleRequestWithFocus}
|
|
106
111
|
autoFocus
|
|
112
|
+
accessibilityLabel={t('recover.username.label')}
|
|
113
|
+
accessibilityHint={t('recover.username.helper') || 'Enter your username or email to recover your account'}
|
|
114
|
+
style={{ marginBottom: 0 }}
|
|
107
115
|
/>
|
|
108
116
|
</View>
|
|
109
117
|
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
{
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
118
|
+
<View style={[baseStyles.container, baseStyles.sectionSpacing, baseStyles.buttonContainer]}>
|
|
119
|
+
<GroupedPillButtons
|
|
120
|
+
buttons={[
|
|
121
|
+
{
|
|
122
|
+
text: t('common.actions.back'),
|
|
123
|
+
onPress: () => navigate('SignIn'),
|
|
124
|
+
icon: 'arrow-back',
|
|
125
|
+
variant: 'transparent',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
text: t('common.actions.continue'),
|
|
129
|
+
onPress: handleRequest,
|
|
130
|
+
icon: 'information-circle-outline',
|
|
131
|
+
variant: 'primary',
|
|
132
|
+
loading: isLoading,
|
|
133
|
+
disabled: isLoading || !identifier || identifier.length < 3,
|
|
134
|
+
},
|
|
135
|
+
]}
|
|
136
|
+
colors={colors}
|
|
137
|
+
/>
|
|
138
|
+
</View>
|
|
129
139
|
</>
|
|
130
140
|
);
|
|
131
141
|
};
|