@oxyhq/services 5.13.0 → 5.13.2
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/README.md +71 -0
- package/lib/commonjs/core/HttpClient.js +238 -0
- package/lib/commonjs/core/HttpClient.js.map +1 -0
- package/lib/commonjs/core/OxyServices.js +530 -332
- package/lib/commonjs/core/OxyServices.js.map +1 -1
- package/lib/commonjs/core/RequestManager.js +199 -0
- package/lib/commonjs/core/RequestManager.js.map +1 -0
- package/lib/commonjs/core/index.js +38 -1
- package/lib/commonjs/core/index.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/index.js +36 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/components/Avatar.js +94 -27
- package/lib/commonjs/ui/components/Avatar.js.map +1 -1
- package/lib/commonjs/ui/components/FollowButton.js +1 -0
- package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
- 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/components/internal/TextField.js +13 -8
- package/lib/commonjs/ui/components/internal/TextField.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +443 -371
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionSocket.js +80 -22
- package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/commonjs/ui/index.js +4 -1
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +32 -2
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +101 -59
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/FileManagementScreen.js +3 -2
- package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/LanguageSelectorScreen.js +75 -117
- package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignInScreen.js +43 -50
- package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/SignUpScreen.js +14 -16
- package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +188 -142
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js +10 -10
- package/lib/commonjs/ui/screens/internal/SignInPasswordStep.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 +218 -39
- 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 +424 -71
- 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/accountStore.js +237 -0
- package/lib/commonjs/ui/stores/accountStore.js.map +1 -0
- package/lib/commonjs/ui/stores/authStore.js +17 -20
- package/lib/commonjs/ui/stores/authStore.js.map +1 -1
- package/lib/commonjs/ui/styles/authStyles.js +16 -8
- 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/asyncUtils.js +9 -22
- package/lib/commonjs/utils/asyncUtils.js.map +1 -1
- package/lib/commonjs/utils/cache.js +259 -0
- package/lib/commonjs/utils/cache.js.map +1 -0
- package/lib/commonjs/utils/index.js +99 -0
- package/lib/commonjs/utils/index.js.map +1 -1
- package/lib/commonjs/utils/languageUtils.js +159 -0
- package/lib/commonjs/utils/languageUtils.js.map +1 -0
- package/lib/commonjs/utils/requestUtils.js +217 -0
- package/lib/commonjs/utils/requestUtils.js.map +1 -0
- package/lib/commonjs/utils/sessionUtils.js +191 -0
- package/lib/commonjs/utils/sessionUtils.js.map +1 -0
- package/lib/commonjs/utils/validationUtils.js +1 -1
- package/lib/module/core/HttpClient.js +232 -0
- package/lib/module/core/HttpClient.js.map +1 -0
- package/lib/module/core/OxyServices.js +528 -326
- package/lib/module/core/OxyServices.js.map +1 -1
- package/lib/module/core/RequestManager.js +194 -0
- package/lib/module/core/RequestManager.js.map +1 -0
- package/lib/module/core/index.js +2 -0
- package/lib/module/core/index.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/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/components/Avatar.js +94 -27
- package/lib/module/ui/components/Avatar.js.map +1 -1
- package/lib/module/ui/components/FollowButton.js +1 -0
- package/lib/module/ui/components/FollowButton.js.map +1 -1
- 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/components/internal/TextField.js +13 -8
- package/lib/module/ui/components/internal/TextField.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +442 -370
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/useSessionSocket.js +80 -22
- package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/module/ui/index.js +4 -2
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +33 -2
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSwitcherScreen.js +102 -60
- package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/module/ui/screens/FileManagementScreen.js +3 -2
- package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
- package/lib/module/ui/screens/LanguageSelectorScreen.js +73 -117
- package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
- package/lib/module/ui/screens/SignInScreen.js +44 -51
- package/lib/module/ui/screens/SignInScreen.js.map +1 -1
- package/lib/module/ui/screens/SignUpScreen.js +14 -16
- package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
- package/lib/module/ui/screens/WelcomeNewUserScreen.js +187 -143
- package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/module/ui/screens/internal/SignInPasswordStep.js +10 -10
- package/lib/module/ui/screens/internal/SignInPasswordStep.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 +219 -40
- 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 +426 -73
- 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/accountStore.js +229 -0
- package/lib/module/ui/stores/accountStore.js.map +1 -0
- package/lib/module/ui/stores/authStore.js +17 -20
- package/lib/module/ui/stores/authStore.js.map +1 -1
- package/lib/module/ui/styles/authStyles.js +16 -8
- 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/asyncUtils.js +10 -22
- package/lib/module/utils/asyncUtils.js.map +1 -1
- package/lib/module/utils/cache.js +250 -0
- package/lib/module/utils/cache.js.map +1 -0
- package/lib/module/utils/index.js +7 -0
- package/lib/module/utils/index.js.map +1 -1
- package/lib/module/utils/languageUtils.js +151 -0
- package/lib/module/utils/languageUtils.js.map +1 -0
- package/lib/module/utils/requestUtils.js +210 -0
- package/lib/module/utils/requestUtils.js.map +1 -0
- package/lib/module/utils/sessionUtils.js +180 -0
- package/lib/module/utils/sessionUtils.js.map +1 -0
- package/lib/module/utils/validationUtils.js +1 -1
- package/lib/typescript/core/HttpClient.d.ts +64 -0
- package/lib/typescript/core/HttpClient.d.ts.map +1 -0
- package/lib/typescript/core/OxyServices.d.ts +86 -73
- package/lib/typescript/core/OxyServices.d.ts.map +1 -1
- package/lib/typescript/core/RequestManager.d.ts +67 -0
- package/lib/typescript/core/RequestManager.d.ts.map +1 -0
- package/lib/typescript/core/index.d.ts +2 -0
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/i18n/index.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +2 -0
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/models/interfaces.d.ts +15 -0
- package/lib/typescript/models/interfaces.d.ts.map +1 -1
- package/lib/typescript/models/session.d.ts +1 -0
- package/lib/typescript/models/session.d.ts.map +1 -1
- package/lib/typescript/ui/components/Avatar.d.ts +6 -7
- package/lib/typescript/ui/components/Avatar.d.ts.map +1 -1
- package/lib/typescript/ui/components/FollowButton.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/components/internal/TextField.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts +5 -0
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
- package/lib/typescript/ui/index.d.ts +2 -2
- package/lib/typescript/ui/index.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/LanguageSelectorScreen.d.ts +3 -3
- package/lib/typescript/ui/screens/LanguageSelectorScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/SignUpScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/internal/SignInPasswordStep.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/accountStore.d.ts +34 -0
- package/lib/typescript/ui/stores/accountStore.d.ts.map +1 -0
- 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 +19 -2
- 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/asyncUtils.d.ts +2 -0
- package/lib/typescript/utils/asyncUtils.d.ts.map +1 -1
- package/lib/typescript/utils/cache.d.ts +128 -0
- package/lib/typescript/utils/cache.d.ts.map +1 -0
- package/lib/typescript/utils/index.d.ts +4 -0
- package/lib/typescript/utils/index.d.ts.map +1 -1
- package/lib/typescript/utils/languageUtils.d.ts +38 -0
- package/lib/typescript/utils/languageUtils.d.ts.map +1 -0
- package/lib/typescript/utils/requestUtils.d.ts +122 -0
- package/lib/typescript/utils/requestUtils.d.ts.map +1 -0
- package/lib/typescript/utils/sessionUtils.d.ts +55 -0
- package/lib/typescript/utils/sessionUtils.d.ts.map +1 -0
- package/lib/typescript/utils/validationUtils.d.ts +1 -1
- package/package.json +1 -1
- package/src/core/HttpClient.ts +277 -0
- package/src/core/OxyServices.ts +461 -352
- package/src/core/RequestManager.ts +240 -0
- package/src/core/index.ts +10 -0
- 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/index.ts +10 -0
- package/src/models/interfaces.ts +19 -0
- package/src/models/session.ts +1 -1
- package/src/ui/components/Avatar.tsx +151 -35
- package/src/ui/components/FollowButton.tsx +1 -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/components/internal/TextField.tsx +7 -6
- package/src/ui/context/OxyContext.tsx +441 -326
- package/src/ui/hooks/useSessionSocket.ts +72 -18
- package/src/ui/index.ts +4 -1
- package/src/ui/screens/AccountSettingsScreen.tsx +34 -2
- package/src/ui/screens/AccountSwitcherScreen.tsx +102 -68
- package/src/ui/screens/FileManagementScreen.tsx +16 -16
- package/src/ui/screens/LanguageSelectorScreen.tsx +86 -143
- package/src/ui/screens/SignInScreen.tsx +59 -43
- package/src/ui/screens/SignUpScreen.tsx +14 -15
- package/src/ui/screens/WelcomeNewUserScreen.tsx +153 -105
- package/src/ui/screens/internal/SignInPasswordStep.tsx +4 -6
- 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 +190 -32
- package/src/ui/screens/steps/SignInTotpStep.tsx +68 -34
- package/src/ui/screens/steps/SignInUsernameStep.tsx +446 -63
- 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/accountStore.ts +285 -0
- package/src/ui/stores/authStore.ts +16 -19
- package/src/ui/styles/authStyles.ts +16 -8
- package/src/ui/styles/index.ts +1 -0
- package/src/ui/styles/spacing.ts +46 -0
- package/src/utils/asyncUtils.ts +10 -24
- package/src/utils/cache.ts +264 -0
- package/src/utils/index.ts +19 -0
- package/src/utils/languageUtils.ts +174 -0
- package/src/utils/requestUtils.ts +234 -0
- package/src/utils/sessionUtils.ts +206 -0
- package/src/utils/validationUtils.ts +1 -1
|
@@ -1,17 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { createContext, useContext, useEffect, useCallback, useMemo, useRef, useState } from 'react';
|
|
4
|
-
import { View, Text } from 'react-native';
|
|
5
4
|
import { OxyServices } from '../../core';
|
|
5
|
+
import { normalizeAndSortSessions, mergeSessions, sessionsArraysEqual } from '../../utils/sessionUtils';
|
|
6
6
|
import { DeviceManager } from '../../utils/deviceManager';
|
|
7
7
|
import { useSessionSocket } from '../hooks/useSessionSocket';
|
|
8
8
|
import { toast } from '../../lib/sonner';
|
|
9
9
|
import { useAuthStore } from '../stores/authStore';
|
|
10
|
+
import { getLanguageMetadata, getLanguageName, getNativeLanguageName, normalizeLanguageCode } from '../../utils/languageUtils';
|
|
10
11
|
|
|
11
12
|
// Define the context shape
|
|
12
13
|
// NOTE: We intentionally avoid importing useFollow here to prevent a require cycle.
|
|
13
14
|
// If consumers relied on `const { useFollow } = useOxy()`, we provide a lazy proxy below.
|
|
14
15
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
16
|
+
// Empty follow hook fallback
|
|
17
|
+
const createEmptyFollowHook = () => {
|
|
18
|
+
const emptyResult = {
|
|
19
|
+
isFollowing: false,
|
|
20
|
+
isLoading: false,
|
|
21
|
+
error: null,
|
|
22
|
+
toggleFollow: async () => {},
|
|
23
|
+
setFollowStatus: () => {},
|
|
24
|
+
fetchStatus: async () => {},
|
|
25
|
+
clearError: () => {},
|
|
26
|
+
followerCount: null,
|
|
27
|
+
followingCount: null,
|
|
28
|
+
isLoadingCounts: false,
|
|
29
|
+
fetchUserCounts: async () => {},
|
|
30
|
+
setFollowerCount: () => {},
|
|
31
|
+
setFollowingCount: () => {}
|
|
32
|
+
};
|
|
33
|
+
return () => emptyResult;
|
|
34
|
+
};
|
|
35
|
+
|
|
15
36
|
// Create the context with default values
|
|
16
37
|
const OxyContext = /*#__PURE__*/createContext(null);
|
|
17
38
|
|
|
@@ -64,6 +85,8 @@ const getStorage = async () => {
|
|
|
64
85
|
const getStorageKeys = (prefix = 'oxy_session') => ({
|
|
65
86
|
activeSessionId: `${prefix}_active_session_id`,
|
|
66
87
|
// Only store the active session ID
|
|
88
|
+
sessionIds: `${prefix}_session_ids`,
|
|
89
|
+
// Store all session IDs for quick account loading
|
|
67
90
|
language: `${prefix}_language` // Store the selected language
|
|
68
91
|
});
|
|
69
92
|
export const OxyProvider = ({
|
|
@@ -103,43 +126,87 @@ export const OxyProvider = ({
|
|
|
103
126
|
const [minimalUser, setMinimalUser] = useState(null);
|
|
104
127
|
const [sessions, setSessions] = useState([]);
|
|
105
128
|
const [activeSessionId, setActiveSessionId] = useState(null);
|
|
129
|
+
|
|
130
|
+
// Track in-flight refresh to prevent duplicate calls
|
|
131
|
+
const refreshInFlightRef = useRef(null);
|
|
106
132
|
const [storage, setStorage] = useState(null);
|
|
107
133
|
const [currentLanguage, setCurrentLanguage] = useState('en-US');
|
|
108
134
|
|
|
109
|
-
//
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
135
|
+
// Storage keys (memoized to prevent infinite loops) - declared early for use in helpers
|
|
136
|
+
const keys = useMemo(() => getStorageKeys(storageKeyPrefix), [storageKeyPrefix]);
|
|
137
|
+
|
|
138
|
+
// Helper to apply language preference from user/server
|
|
139
|
+
const applyLanguagePreference = useCallback(async user => {
|
|
140
|
+
const userLanguage = user?.language;
|
|
141
|
+
if (!userLanguage || !storage) return;
|
|
142
|
+
try {
|
|
143
|
+
const serverLang = normalizeLanguageCode(userLanguage);
|
|
144
|
+
await storage.setItem(keys.language, serverLang);
|
|
145
|
+
setCurrentLanguage(serverLang);
|
|
146
|
+
} catch (e) {
|
|
147
|
+
if (__DEV__) {
|
|
148
|
+
console.warn('Failed to apply server language preference', e);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}, [storage, keys.language]);
|
|
152
|
+
const mapSessionsToClient = useCallback((sessions, fallbackDeviceId, fallbackUserId) => {
|
|
153
|
+
return sessions.map(s => ({
|
|
154
|
+
sessionId: s.sessionId,
|
|
155
|
+
deviceId: s.deviceId || fallbackDeviceId || '',
|
|
156
|
+
expiresAt: s.expiresAt || new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
157
|
+
lastActive: s.lastActive || new Date().toISOString(),
|
|
158
|
+
userId: s.user?.id || s.userId || s.user?._id?.toString() || fallbackUserId || '',
|
|
159
|
+
isCurrent: Boolean(s.isCurrent)
|
|
160
|
+
}));
|
|
127
161
|
}, []);
|
|
128
|
-
// Add a new state to track token restoration
|
|
129
|
-
const [tokenReady, setTokenReady] = useState(false);
|
|
130
162
|
|
|
131
|
-
//
|
|
132
|
-
const
|
|
163
|
+
// Save all session IDs to storage for quick loading on initialization
|
|
164
|
+
const saveSessionIds = useCallback(async sessionIds => {
|
|
165
|
+
if (!storage) return;
|
|
166
|
+
try {
|
|
167
|
+
const uniqueIds = Array.from(new Set(sessionIds));
|
|
168
|
+
await storage.setItem(keys.sessionIds, JSON.stringify(uniqueIds));
|
|
169
|
+
} catch (err) {
|
|
170
|
+
if (__DEV__) {
|
|
171
|
+
console.warn('Failed to save session IDs:', err);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}, [storage, keys.sessionIds]);
|
|
175
|
+
const updateSessions = useCallback((newSessions, mergeWithExisting = false) => {
|
|
176
|
+
setSessions(prevSessions => {
|
|
177
|
+
const sessionsToProcess = mergeWithExisting ? mergeSessions(prevSessions, newSessions, activeSessionId, false) : normalizeAndSortSessions(newSessions, activeSessionId, false);
|
|
178
|
+
|
|
179
|
+
// Save all session IDs to storage
|
|
180
|
+
if (storage) {
|
|
181
|
+
const allSessionIds = sessionsToProcess.map(s => s.sessionId);
|
|
182
|
+
saveSessionIds(allSessionIds).catch(() => {
|
|
183
|
+
// Ignore errors - non-critical
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
return sessionsArraysEqual(prevSessions, sessionsToProcess) ? prevSessions : sessionsToProcess;
|
|
187
|
+
});
|
|
188
|
+
}, [activeSessionId, storage, saveSessionIds]);
|
|
133
189
|
|
|
134
|
-
//
|
|
190
|
+
// Token ready state - start optimistically so children render immediately
|
|
191
|
+
const [tokenReady, setTokenReady] = useState(true);
|
|
192
|
+
|
|
193
|
+
// Clear all storage
|
|
135
194
|
const clearAllStorage = useCallback(async () => {
|
|
136
195
|
if (!storage) return;
|
|
137
196
|
try {
|
|
138
197
|
await storage.removeItem(keys.activeSessionId);
|
|
198
|
+
await storage.removeItem(keys.sessionIds);
|
|
139
199
|
} catch (err) {
|
|
140
|
-
|
|
200
|
+
if (__DEV__) {
|
|
201
|
+
console.error('Clear storage error:', err);
|
|
202
|
+
}
|
|
203
|
+
onError?.({
|
|
204
|
+
message: 'Failed to clear storage',
|
|
205
|
+
code: 'STORAGE_ERROR',
|
|
206
|
+
status: 500
|
|
207
|
+
});
|
|
141
208
|
}
|
|
142
|
-
}, [storage, keys]);
|
|
209
|
+
}, [storage, keys, onError]);
|
|
143
210
|
|
|
144
211
|
// Initialize storage
|
|
145
212
|
useEffect(() => {
|
|
@@ -148,25 +215,28 @@ export const OxyProvider = ({
|
|
|
148
215
|
const platformStorage = await getStorage();
|
|
149
216
|
setStorage(platformStorage);
|
|
150
217
|
} catch (error) {
|
|
151
|
-
|
|
218
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to initialize storage';
|
|
152
219
|
useAuthStore.setState({
|
|
153
|
-
error:
|
|
220
|
+
error: errorMessage
|
|
221
|
+
});
|
|
222
|
+
onError?.({
|
|
223
|
+
message: errorMessage,
|
|
224
|
+
code: 'STORAGE_INIT_ERROR',
|
|
225
|
+
status: 500
|
|
154
226
|
});
|
|
155
227
|
}
|
|
156
228
|
};
|
|
157
229
|
initStorage();
|
|
158
|
-
}, []);
|
|
230
|
+
}, [onError]);
|
|
159
231
|
|
|
160
232
|
// Initialize authentication state
|
|
233
|
+
// Note: We don't set isLoading during initialization to avoid showing spinners
|
|
234
|
+
// Children render immediately and can check isTokenReady/isAuthenticated themselves
|
|
161
235
|
useEffect(() => {
|
|
162
236
|
const initAuth = async () => {
|
|
163
237
|
if (!storage) return;
|
|
164
|
-
|
|
165
|
-
isLoading: true
|
|
166
|
-
});
|
|
238
|
+
// Don't set isLoading during initialization - let it happen in background
|
|
167
239
|
try {
|
|
168
|
-
setTokenReady(false);
|
|
169
|
-
|
|
170
240
|
// Load saved language preference
|
|
171
241
|
const savedLanguageRaw = await storage.getItem(keys.language);
|
|
172
242
|
const savedLanguage = normalizeLanguageCode(savedLanguageRaw) || savedLanguageRaw;
|
|
@@ -174,8 +244,51 @@ export const OxyProvider = ({
|
|
|
174
244
|
setCurrentLanguage(savedLanguage);
|
|
175
245
|
}
|
|
176
246
|
|
|
247
|
+
// Load all stored session IDs and validate them
|
|
248
|
+
const storedSessionIdsJson = await storage.getItem(keys.sessionIds);
|
|
249
|
+
const storedSessionIds = storedSessionIdsJson ? JSON.parse(storedSessionIdsJson) : [];
|
|
250
|
+
|
|
177
251
|
// Try to restore active session from storage
|
|
178
252
|
const storedActiveSessionId = await storage.getItem(keys.activeSessionId);
|
|
253
|
+
const validSessions = [];
|
|
254
|
+
|
|
255
|
+
// If we have stored session IDs, validate them (even without active session)
|
|
256
|
+
if (storedSessionIds.length > 0) {
|
|
257
|
+
if (__DEV__) {
|
|
258
|
+
console.log('Loading stored sessions on init:', storedSessionIds.length);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Validate each stored session ID and build session list
|
|
262
|
+
for (const sessionId of storedSessionIds) {
|
|
263
|
+
try {
|
|
264
|
+
const validation = await oxyServices.validateSession(sessionId, {
|
|
265
|
+
useHeaderValidation: true
|
|
266
|
+
});
|
|
267
|
+
if (validation.valid && validation.user) {
|
|
268
|
+
validSessions.push({
|
|
269
|
+
sessionId,
|
|
270
|
+
userId: validation.user.id?.toString() || '',
|
|
271
|
+
deviceId: '',
|
|
272
|
+
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
273
|
+
lastActive: new Date().toISOString(),
|
|
274
|
+
isCurrent: sessionId === storedActiveSessionId
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
} catch (e) {
|
|
278
|
+
// Session invalid, skip it
|
|
279
|
+
if (__DEV__) {
|
|
280
|
+
console.warn('Session validation failed for:', sessionId, e);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Update sessions list with validated sessions (even if no active session)
|
|
286
|
+
if (validSessions.length > 0) {
|
|
287
|
+
updateSessions(validSessions, false);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// If we have an active session, authenticate with it
|
|
179
292
|
if (storedActiveSessionId) {
|
|
180
293
|
try {
|
|
181
294
|
const validation = await oxyServices.validateSession(storedActiveSessionId, {
|
|
@@ -191,86 +304,66 @@ export const OxyProvider = ({
|
|
|
191
304
|
username: fullUser.username,
|
|
192
305
|
avatar: fullUser.avatar
|
|
193
306
|
});
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
console.warn('Failed to
|
|
307
|
+
await applyLanguagePreference(fullUser);
|
|
308
|
+
try {
|
|
309
|
+
const deviceSessions = await oxyServices.getDeviceSessions(storedActiveSessionId);
|
|
310
|
+
const allDeviceSessions = mapSessionsToClient(deviceSessions, undefined, fullUser.id);
|
|
311
|
+
updateSessions(allDeviceSessions, true);
|
|
312
|
+
} catch (e) {
|
|
313
|
+
if (__DEV__) {
|
|
314
|
+
console.warn('Failed to get device sessions on init, falling back to user sessions:', e);
|
|
202
315
|
}
|
|
316
|
+
const serverSessions = await oxyServices.getSessionsBySessionId(storedActiveSessionId);
|
|
317
|
+
updateSessions(mapSessionsToClient(serverSessions, undefined, fullUser.id), false);
|
|
203
318
|
}
|
|
204
|
-
const serverSessions = await oxyServices.getSessionsBySessionId(storedActiveSessionId);
|
|
205
|
-
const clientSessions = serverSessions.map(s => ({
|
|
206
|
-
sessionId: s.sessionId,
|
|
207
|
-
deviceId: s.deviceId,
|
|
208
|
-
expiresAt: s.expiresAt || new Date().toISOString(),
|
|
209
|
-
lastActive: s.lastActive || new Date().toISOString(),
|
|
210
|
-
userId: s.userId || fullUser.id
|
|
211
|
-
}));
|
|
212
|
-
setSessions(clientSessions);
|
|
213
319
|
onAuthStateChange?.(fullUser);
|
|
214
320
|
} else {
|
|
215
|
-
|
|
321
|
+
// Active session invalid, remove it but keep other sessions
|
|
322
|
+
await storage.removeItem(keys.activeSessionId);
|
|
323
|
+
// Update session list to remove invalid active session
|
|
324
|
+
updateSessions(validSessions.filter(s => s.sessionId !== storedActiveSessionId), false);
|
|
216
325
|
}
|
|
217
326
|
} catch (e) {
|
|
218
|
-
|
|
219
|
-
|
|
327
|
+
if (__DEV__) {
|
|
328
|
+
console.error('Active session validation error', e);
|
|
329
|
+
}
|
|
330
|
+
// Remove invalid active session but keep other sessions
|
|
331
|
+
await storage.removeItem(keys.activeSessionId);
|
|
332
|
+
updateSessions(validSessions.filter(s => s.sessionId !== storedActiveSessionId), false);
|
|
220
333
|
}
|
|
221
334
|
}
|
|
222
335
|
setTokenReady(true);
|
|
223
336
|
} catch (e) {
|
|
224
|
-
|
|
337
|
+
if (__DEV__) {
|
|
338
|
+
console.error('Auth init error', e);
|
|
339
|
+
}
|
|
225
340
|
await clearAllStorage();
|
|
226
|
-
|
|
227
|
-
useAuthStore.setState({
|
|
228
|
-
isLoading: false
|
|
229
|
-
});
|
|
341
|
+
setTokenReady(true);
|
|
230
342
|
}
|
|
231
343
|
};
|
|
232
344
|
initAuth();
|
|
233
|
-
}, [storage, oxyServices, keys, onAuthStateChange, loginSuccess, clearAllStorage]);
|
|
234
|
-
|
|
235
|
-
// Remove invalid session - refresh sessions from backend
|
|
236
|
-
const removeInvalidSession = useCallback(async sessionId => {
|
|
237
|
-
// Remove from local state
|
|
238
|
-
const filteredSessions = sessions.filter(s => s.sessionId !== sessionId);
|
|
239
|
-
setSessions(filteredSessions);
|
|
240
|
-
|
|
241
|
-
// If there are other sessions, switch to the first one
|
|
242
|
-
if (filteredSessions.length > 0) {
|
|
243
|
-
await switchToSession(filteredSessions[0].sessionId);
|
|
244
|
-
} else {
|
|
245
|
-
// No valid sessions left
|
|
246
|
-
setActiveSessionId(null);
|
|
247
|
-
logoutStore();
|
|
248
|
-
setMinimalUser(null);
|
|
249
|
-
await storage?.removeItem(keys.activeSessionId);
|
|
250
|
-
if (onAuthStateChange) {
|
|
251
|
-
onAuthStateChange(null);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}, [sessions, storage, keys, onAuthStateChange, logoutStore]);
|
|
345
|
+
}, [storage, oxyServices, keys, onAuthStateChange, loginSuccess, clearAllStorage, applyLanguagePreference, mapSessionsToClient, updateSessions]);
|
|
255
346
|
|
|
256
347
|
// Save active session ID to storage (only session ID, no user data)
|
|
257
348
|
const saveActiveSessionId = useCallback(async sessionId => {
|
|
258
349
|
if (!storage) return;
|
|
259
350
|
await storage.setItem(keys.activeSessionId, sessionId);
|
|
260
351
|
}, [storage, keys.activeSessionId]);
|
|
261
|
-
|
|
262
|
-
// Switch to a different session
|
|
263
352
|
const switchToSession = useCallback(async sessionId => {
|
|
264
353
|
try {
|
|
265
|
-
|
|
266
|
-
|
|
354
|
+
const validation = await oxyServices.validateSession(sessionId, {
|
|
355
|
+
useHeaderValidation: true
|
|
267
356
|
});
|
|
268
|
-
|
|
269
|
-
|
|
357
|
+
if (!validation.valid) {
|
|
358
|
+
updateSessions(sessions.filter(s => s.sessionId !== sessionId), false);
|
|
359
|
+
throw new Error('Session is invalid or expired');
|
|
360
|
+
}
|
|
361
|
+
if (!validation.user) {
|
|
362
|
+
throw new Error('User data not available from session validation');
|
|
363
|
+
}
|
|
364
|
+
const fullUser = validation.user;
|
|
270
365
|
await oxyServices.getTokenBySession(sessionId);
|
|
271
|
-
|
|
272
|
-
// Load full user data
|
|
273
|
-
const fullUser = await oxyServices.getUserBySession(sessionId);
|
|
366
|
+
setTokenReady(true);
|
|
274
367
|
setActiveSessionId(sessionId);
|
|
275
368
|
loginSuccess(fullUser);
|
|
276
369
|
setMinimalUser({
|
|
@@ -279,32 +372,52 @@ export const OxyProvider = ({
|
|
|
279
372
|
avatar: fullUser.avatar
|
|
280
373
|
});
|
|
281
374
|
await saveActiveSessionId(sessionId);
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
375
|
+
await applyLanguagePreference(fullUser);
|
|
376
|
+
oxyServices.getDeviceSessions(sessionId).then(deviceSessions => {
|
|
377
|
+
const allDeviceSessions = mapSessionsToClient(deviceSessions, undefined, fullUser.id);
|
|
378
|
+
updateSessions(allDeviceSessions, true);
|
|
379
|
+
}).catch(error => {
|
|
380
|
+
if (__DEV__) console.warn('Failed to get device sessions after switch:', error);
|
|
381
|
+
});
|
|
382
|
+
onAuthStateChange?.(fullUser);
|
|
383
|
+
} catch (error) {
|
|
384
|
+
const isInvalidSession = error?.response?.status === 401 || error?.message?.includes('Invalid or expired session') || error?.message?.includes('Session is invalid');
|
|
385
|
+
if (isInvalidSession) {
|
|
386
|
+
updateSessions(sessions.filter(s => s.sessionId !== sessionId), false);
|
|
387
|
+
if (sessionId === activeSessionId && sessions.length > 1) {
|
|
388
|
+
const otherSessions = sessions.filter(s => s.sessionId !== sessionId);
|
|
389
|
+
for (const otherSession of otherSessions) {
|
|
390
|
+
try {
|
|
391
|
+
const otherValidation = await oxyServices.validateSession(otherSession.sessionId, {
|
|
392
|
+
useHeaderValidation: true
|
|
393
|
+
});
|
|
394
|
+
if (otherValidation.valid) {
|
|
395
|
+
await switchToSession(otherSession.sessionId);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
} catch {
|
|
399
|
+
// Continue to next session
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
290
403
|
}
|
|
291
404
|
}
|
|
292
|
-
|
|
293
|
-
|
|
405
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to switch session';
|
|
406
|
+
if (__DEV__) {
|
|
407
|
+
console.error('Switch session error:', error);
|
|
294
408
|
}
|
|
295
|
-
} catch (error) {
|
|
296
|
-
console.error('Switch session error:', error);
|
|
297
409
|
useAuthStore.setState({
|
|
298
|
-
error:
|
|
410
|
+
error: errorMessage
|
|
299
411
|
});
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
412
|
+
onError?.({
|
|
413
|
+
message: errorMessage,
|
|
414
|
+
code: isInvalidSession ? 'INVALID_SESSION' : 'SESSION_SWITCH_ERROR',
|
|
415
|
+
status: isInvalidSession ? 401 : 500
|
|
303
416
|
});
|
|
417
|
+
setTokenReady(false);
|
|
418
|
+
throw error; // Re-throw so calling code can handle it
|
|
304
419
|
}
|
|
305
|
-
}, [oxyServices, onAuthStateChange, loginSuccess, saveActiveSessionId]);
|
|
306
|
-
|
|
307
|
-
// Login method - only store session ID, retrieve data from backend
|
|
420
|
+
}, [oxyServices, onAuthStateChange, loginSuccess, saveActiveSessionId, applyLanguagePreference, mapSessionsToClient, onError, activeSessionId, sessions]);
|
|
308
421
|
const login = useCallback(async (username, password, deviceName) => {
|
|
309
422
|
if (!storage) throw new Error('Storage not initialized');
|
|
310
423
|
useAuthStore.setState({
|
|
@@ -312,59 +425,71 @@ export const OxyProvider = ({
|
|
|
312
425
|
error: null
|
|
313
426
|
});
|
|
314
427
|
try {
|
|
315
|
-
// Get device fingerprint for enhanced device identification
|
|
316
428
|
const deviceFingerprint = DeviceManager.getDeviceFingerprint();
|
|
317
|
-
|
|
318
|
-
// Get or generate persistent device info
|
|
319
429
|
const deviceInfo = await DeviceManager.getDeviceInfo();
|
|
320
|
-
console.log('Auth - Using device fingerprint:', deviceFingerprint);
|
|
321
|
-
console.log('Auth - Using device ID:', deviceInfo.deviceId);
|
|
322
430
|
const response = await oxyServices.signIn(username, password, deviceName || deviceInfo.deviceName || DeviceManager.getDefaultDeviceName(), deviceFingerprint);
|
|
323
431
|
|
|
324
432
|
// Handle MFA requirement
|
|
325
|
-
if (response && response.mfaRequired) {
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
throw
|
|
433
|
+
if (response && 'mfaRequired' in response && response.mfaRequired) {
|
|
434
|
+
const mfaError = new Error('Multi-factor authentication required');
|
|
435
|
+
mfaError.code = 'MFA_REQUIRED';
|
|
436
|
+
mfaError.mfaToken = response.mfaToken;
|
|
437
|
+
mfaError.expiresAt = response.expiresAt;
|
|
438
|
+
throw mfaError;
|
|
331
439
|
}
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
// Load sessions from backend
|
|
346
|
-
const serverSessions = await oxyServices.getSessionsBySessionId(response.sessionId);
|
|
347
|
-
const clientSessions = serverSessions.map(serverSession => ({
|
|
348
|
-
sessionId: serverSession.sessionId,
|
|
349
|
-
deviceId: serverSession.deviceId,
|
|
350
|
-
expiresAt: serverSession.expiresAt || new Date().toISOString(),
|
|
351
|
-
lastActive: serverSession.lastActive || new Date().toISOString(),
|
|
352
|
-
userId: serverSession.userId || fullUser.id
|
|
353
|
-
}));
|
|
354
|
-
setSessions(clientSessions);
|
|
355
|
-
if (onAuthStateChange) {
|
|
356
|
-
onAuthStateChange(fullUser);
|
|
440
|
+
const sessionResponse = response;
|
|
441
|
+
await oxyServices.getTokenBySession(sessionResponse.sessionId);
|
|
442
|
+
const fullUser = await oxyServices.getUserBySession(sessionResponse.sessionId);
|
|
443
|
+
let allDeviceSessions = [];
|
|
444
|
+
try {
|
|
445
|
+
const deviceSessions = await oxyServices.getDeviceSessions(sessionResponse.sessionId);
|
|
446
|
+
allDeviceSessions = mapSessionsToClient(deviceSessions, sessionResponse.deviceId, fullUser.id);
|
|
447
|
+
} catch (error) {
|
|
448
|
+
if (__DEV__) {
|
|
449
|
+
console.warn('Failed to get device sessions, falling back to user sessions:', error);
|
|
450
|
+
}
|
|
451
|
+
const serverSessions = await oxyServices.getSessionsBySessionId(sessionResponse.sessionId);
|
|
452
|
+
allDeviceSessions = mapSessionsToClient(serverSessions, undefined, fullUser.id);
|
|
357
453
|
}
|
|
454
|
+
const userUserId = fullUser.id?.toString();
|
|
455
|
+
const existingSession = allDeviceSessions.find(s => s.userId?.toString() === userUserId && s.sessionId !== sessionResponse.sessionId);
|
|
456
|
+
if (existingSession) {
|
|
457
|
+
try {
|
|
458
|
+
await oxyServices.logoutSession(sessionResponse.sessionId, sessionResponse.sessionId);
|
|
459
|
+
} catch (logoutError) {
|
|
460
|
+
if (__DEV__) {
|
|
461
|
+
console.warn('Failed to logout duplicate session:', logoutError);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
await switchToSession(existingSession.sessionId);
|
|
465
|
+
loginSuccess(fullUser);
|
|
466
|
+
setMinimalUser(sessionResponse.user);
|
|
467
|
+
updateSessions(allDeviceSessions.filter(s => s.sessionId !== sessionResponse.sessionId), false);
|
|
468
|
+
onAuthStateChange?.(fullUser);
|
|
469
|
+
return fullUser;
|
|
470
|
+
}
|
|
471
|
+
setActiveSessionId(sessionResponse.sessionId);
|
|
472
|
+
await saveActiveSessionId(sessionResponse.sessionId);
|
|
473
|
+
loginSuccess(fullUser);
|
|
474
|
+
setMinimalUser(sessionResponse.user);
|
|
475
|
+
updateSessions(allDeviceSessions, true);
|
|
476
|
+
onAuthStateChange?.(fullUser);
|
|
358
477
|
return fullUser;
|
|
359
478
|
} catch (error) {
|
|
360
|
-
|
|
479
|
+
const errorMessage = error instanceof Error ? error.message : 'Login failed';
|
|
480
|
+
loginFailure(errorMessage);
|
|
481
|
+
onError?.({
|
|
482
|
+
message: errorMessage,
|
|
483
|
+
code: 'LOGIN_ERROR',
|
|
484
|
+
status: 401
|
|
485
|
+
});
|
|
361
486
|
throw error;
|
|
362
487
|
} finally {
|
|
363
488
|
useAuthStore.setState({
|
|
364
489
|
isLoading: false
|
|
365
490
|
});
|
|
366
491
|
}
|
|
367
|
-
}, [storage, oxyServices, saveActiveSessionId, loginSuccess,
|
|
492
|
+
}, [storage, oxyServices, saveActiveSessionId, loginSuccess, onAuthStateChange, loginFailure, mapSessionsToClient, onError, sessions, switchToSession]);
|
|
368
493
|
|
|
369
494
|
// Logout method
|
|
370
495
|
const logout = useCallback(async targetSessionId => {
|
|
@@ -372,18 +497,12 @@ export const OxyProvider = ({
|
|
|
372
497
|
try {
|
|
373
498
|
const sessionToLogout = targetSessionId || activeSessionId;
|
|
374
499
|
await oxyServices.logoutSession(activeSessionId, sessionToLogout);
|
|
375
|
-
|
|
376
|
-
// Remove session from local state
|
|
377
500
|
const filteredSessions = sessions.filter(s => s.sessionId !== sessionToLogout);
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
// If logging out active session
|
|
501
|
+
updateSessions(filteredSessions, false);
|
|
381
502
|
if (sessionToLogout === activeSessionId) {
|
|
382
503
|
if (filteredSessions.length > 0) {
|
|
383
|
-
// Switch to another session
|
|
384
504
|
await switchToSession(filteredSessions[0].sessionId);
|
|
385
505
|
} else {
|
|
386
|
-
// No sessions left
|
|
387
506
|
setActiveSessionId(null);
|
|
388
507
|
logoutStore();
|
|
389
508
|
setMinimalUser(null);
|
|
@@ -394,74 +513,57 @@ export const OxyProvider = ({
|
|
|
394
513
|
}
|
|
395
514
|
}
|
|
396
515
|
} catch (error) {
|
|
397
|
-
|
|
516
|
+
const errorMessage = error instanceof Error ? error.message : 'Logout failed';
|
|
517
|
+
if (__DEV__) {
|
|
518
|
+
console.error('Logout error:', error);
|
|
519
|
+
}
|
|
398
520
|
useAuthStore.setState({
|
|
399
|
-
error:
|
|
521
|
+
error: errorMessage
|
|
522
|
+
});
|
|
523
|
+
onError?.({
|
|
524
|
+
message: errorMessage,
|
|
525
|
+
code: 'LOGOUT_ERROR',
|
|
526
|
+
status: 500
|
|
400
527
|
});
|
|
401
528
|
}
|
|
402
|
-
}, [activeSessionId, oxyServices, sessions, switchToSession, logoutStore,
|
|
403
|
-
|
|
404
|
-
// Logout all sessions
|
|
529
|
+
}, [activeSessionId, oxyServices, sessions, switchToSession, logoutStore, storage, keys.activeSessionId, onAuthStateChange, onError]);
|
|
405
530
|
const logoutAll = useCallback(async () => {
|
|
406
|
-
console.log('logoutAll called with activeSessionId:', activeSessionId);
|
|
407
531
|
if (!activeSessionId) {
|
|
408
|
-
|
|
532
|
+
const error = new Error('No active session found');
|
|
409
533
|
useAuthStore.setState({
|
|
410
|
-
error:
|
|
534
|
+
error: error.message
|
|
411
535
|
});
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
useAuthStore.setState({
|
|
417
|
-
error: 'Service not available'
|
|
536
|
+
onError?.({
|
|
537
|
+
message: error.message,
|
|
538
|
+
code: 'NO_SESSION_ERROR',
|
|
539
|
+
status: 404
|
|
418
540
|
});
|
|
419
|
-
throw
|
|
541
|
+
throw error;
|
|
420
542
|
}
|
|
421
543
|
try {
|
|
422
|
-
console.log('Calling oxyServices.logoutAllSessions with sessionId:', activeSessionId);
|
|
423
544
|
await oxyServices.logoutAllSessions(activeSessionId);
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
// Clear all local data
|
|
427
|
-
setSessions([]);
|
|
545
|
+
updateSessions([], false);
|
|
428
546
|
setActiveSessionId(null);
|
|
429
547
|
logoutStore();
|
|
430
548
|
setMinimalUser(null);
|
|
431
549
|
await clearAllStorage();
|
|
432
|
-
|
|
433
|
-
if (onAuthStateChange) {
|
|
434
|
-
onAuthStateChange(null);
|
|
435
|
-
console.log('Auth state change callback called');
|
|
436
|
-
}
|
|
550
|
+
onAuthStateChange?.(null);
|
|
437
551
|
} catch (error) {
|
|
438
|
-
|
|
552
|
+
const errorMessage = error instanceof Error ? error.message : 'Logout all failed';
|
|
439
553
|
useAuthStore.setState({
|
|
440
|
-
error:
|
|
554
|
+
error: errorMessage
|
|
555
|
+
});
|
|
556
|
+
onError?.({
|
|
557
|
+
message: errorMessage,
|
|
558
|
+
code: 'LOGOUT_ALL_ERROR',
|
|
559
|
+
status: 500
|
|
441
560
|
});
|
|
442
561
|
throw error;
|
|
443
562
|
}
|
|
444
|
-
}, [activeSessionId, oxyServices, logoutStore,
|
|
563
|
+
}, [activeSessionId, oxyServices, logoutStore, clearAllStorage, onAuthStateChange, onError]);
|
|
445
564
|
|
|
446
|
-
//
|
|
447
|
-
|
|
448
|
-
const restoreToken = async () => {
|
|
449
|
-
if (activeSessionId && oxyServices) {
|
|
450
|
-
try {
|
|
451
|
-
await oxyServices.getTokenBySession(activeSessionId);
|
|
452
|
-
setTokenReady(true);
|
|
453
|
-
} catch (err) {
|
|
454
|
-
// If token restoration fails, force logout
|
|
455
|
-
await logout();
|
|
456
|
-
setTokenReady(false);
|
|
457
|
-
}
|
|
458
|
-
} else {
|
|
459
|
-
setTokenReady(true); // No session, so token is not needed
|
|
460
|
-
}
|
|
461
|
-
};
|
|
462
|
-
restoreToken();
|
|
463
|
-
// Only run when activeSessionId or oxyServices changes
|
|
464
|
-
}, [activeSessionId, oxyServices, logout]);
|
|
565
|
+
// Token restoration is handled in initAuth and switchToSession
|
|
566
|
+
// No separate effect needed - children render immediately with isTokenReady available
|
|
465
567
|
|
|
466
568
|
// Sign up method
|
|
467
569
|
const signUp = useCallback(async (username, email, password) => {
|
|
@@ -471,23 +573,24 @@ export const OxyProvider = ({
|
|
|
471
573
|
error: null
|
|
472
574
|
});
|
|
473
575
|
try {
|
|
474
|
-
|
|
475
|
-
const response = await oxyServices.signUp(username, email, password);
|
|
476
|
-
console.log('SignUp successful:', response);
|
|
477
|
-
|
|
478
|
-
// Now log the user in to create a session
|
|
479
|
-
// This will handle the session creation and device registration
|
|
576
|
+
await oxyServices.signUp(username, email, password);
|
|
480
577
|
const user = await login(username, password);
|
|
481
578
|
return user;
|
|
482
579
|
} catch (error) {
|
|
483
|
-
|
|
580
|
+
const errorMessage = error instanceof Error ? error.message : 'Sign up failed';
|
|
581
|
+
loginFailure(errorMessage);
|
|
582
|
+
onError?.({
|
|
583
|
+
message: errorMessage,
|
|
584
|
+
code: 'SIGNUP_ERROR',
|
|
585
|
+
status: 400
|
|
586
|
+
});
|
|
484
587
|
throw error;
|
|
485
588
|
} finally {
|
|
486
589
|
useAuthStore.setState({
|
|
487
590
|
isLoading: false
|
|
488
591
|
});
|
|
489
592
|
}
|
|
490
|
-
}, [storage, oxyServices, login, loginFailure]);
|
|
593
|
+
}, [storage, oxyServices, login, loginFailure, onError]);
|
|
491
594
|
|
|
492
595
|
// Complete MFA login by verifying TOTP
|
|
493
596
|
const completeMfaLogin = useCallback(async (mfaToken, code) => {
|
|
@@ -512,116 +615,106 @@ export const OxyProvider = ({
|
|
|
512
615
|
username: fullUser.username,
|
|
513
616
|
avatar: fullUser.avatar
|
|
514
617
|
});
|
|
515
|
-
|
|
516
|
-
if (fullUser?.language) {
|
|
517
|
-
try {
|
|
518
|
-
const serverLang = normalizeLanguageCode(fullUser.language) || fullUser.language;
|
|
519
|
-
await storage.setItem(keys.language, serverLang);
|
|
520
|
-
setCurrentLanguage(serverLang);
|
|
521
|
-
} catch (e) {
|
|
522
|
-
console.warn('Failed to apply server language on MFA login', e);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
618
|
+
await applyLanguagePreference(fullUser);
|
|
525
619
|
|
|
526
|
-
//
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
setSessions(clientSessions);
|
|
536
|
-
// Apply server language if present
|
|
537
|
-
if (fullUser?.language) {
|
|
538
|
-
try {
|
|
539
|
-
await storage.setItem(keys.language, fullUser.language);
|
|
540
|
-
setCurrentLanguage(fullUser.language);
|
|
541
|
-
} catch (e) {
|
|
542
|
-
console.warn('Failed to apply server language on MFA login', e);
|
|
620
|
+
// Get all device sessions to support multiple accounts
|
|
621
|
+
try {
|
|
622
|
+
const deviceSessions = await oxyServices.getDeviceSessions(response.sessionId);
|
|
623
|
+
const allDeviceSessions = mapSessionsToClient(deviceSessions, undefined, fullUser.id);
|
|
624
|
+
updateSessions(allDeviceSessions, true);
|
|
625
|
+
} catch (error) {
|
|
626
|
+
// Fallback to user sessions if device sessions fail
|
|
627
|
+
if (__DEV__) {
|
|
628
|
+
console.warn('Failed to get device sessions for MFA, falling back to user sessions:', error);
|
|
543
629
|
}
|
|
630
|
+
const serverSessions = await oxyServices.getSessionsBySessionId(response.sessionId);
|
|
631
|
+
const userSessions = mapSessionsToClient(serverSessions, undefined, fullUser.id);
|
|
632
|
+
updateSessions(userSessions, true);
|
|
544
633
|
}
|
|
545
|
-
|
|
634
|
+
onAuthStateChange?.(fullUser);
|
|
546
635
|
return fullUser;
|
|
547
636
|
} catch (error) {
|
|
548
|
-
|
|
637
|
+
const errorMessage = error instanceof Error ? error.message : 'MFA verification failed';
|
|
638
|
+
loginFailure(errorMessage);
|
|
639
|
+
onError?.({
|
|
640
|
+
message: errorMessage,
|
|
641
|
+
code: 'MFA_ERROR',
|
|
642
|
+
status: 401
|
|
643
|
+
});
|
|
549
644
|
throw error;
|
|
550
645
|
} finally {
|
|
551
646
|
useAuthStore.setState({
|
|
552
647
|
isLoading: false
|
|
553
648
|
});
|
|
554
649
|
}
|
|
555
|
-
}, [storage, oxyServices, loginSuccess, loginFailure, saveActiveSessionId, onAuthStateChange]);
|
|
556
|
-
|
|
557
|
-
// Switch session method
|
|
650
|
+
}, [storage, oxyServices, loginSuccess, loginFailure, saveActiveSessionId, onAuthStateChange, applyLanguagePreference, onError]);
|
|
558
651
|
const switchSession = useCallback(async sessionId => {
|
|
559
652
|
await switchToSession(sessionId);
|
|
560
653
|
}, [switchToSession]);
|
|
561
|
-
|
|
562
|
-
// Remove session method
|
|
563
654
|
const removeSession = useCallback(async sessionId => {
|
|
564
655
|
await logout(sessionId);
|
|
565
656
|
}, [logout]);
|
|
566
|
-
|
|
567
|
-
// Refresh sessions method
|
|
568
657
|
const refreshSessions = useCallback(async () => {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
658
|
+
if (!activeSessionId) return;
|
|
659
|
+
|
|
660
|
+
// If a refresh is already in progress, return the existing promise
|
|
661
|
+
if (refreshInFlightRef.current) {
|
|
662
|
+
return refreshInFlightRef.current;
|
|
573
663
|
}
|
|
574
|
-
try {
|
|
575
|
-
console.log('refreshSessions: Calling getSessionsBySessionId...');
|
|
576
|
-
const serverSessions = await oxyServices.getSessionsBySessionId(activeSessionId);
|
|
577
|
-
console.log('refreshSessions: Server sessions received:', serverSessions);
|
|
578
|
-
|
|
579
|
-
// Update local sessions with server data
|
|
580
|
-
const updatedSessions = serverSessions.map(serverSession => ({
|
|
581
|
-
sessionId: serverSession.sessionId,
|
|
582
|
-
deviceId: serverSession.deviceId,
|
|
583
|
-
expiresAt: serverSession.expiresAt || new Date().toISOString(),
|
|
584
|
-
lastActive: serverSession.lastActive || new Date().toISOString(),
|
|
585
|
-
userId: serverSession.userId || user?.id
|
|
586
|
-
}));
|
|
587
|
-
console.log('refreshSessions: Updated sessions:', updatedSessions);
|
|
588
|
-
setSessions(updatedSessions);
|
|
589
|
-
console.log('refreshSessions: Sessions updated in state');
|
|
590
|
-
} catch (error) {
|
|
591
|
-
console.error('Refresh sessions error:', error);
|
|
592
664
|
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
});
|
|
603
|
-
console.log('Found valid session, switching to:', session.sessionId);
|
|
604
|
-
await switchToSession(session.sessionId);
|
|
605
|
-
return; // Successfully switched to another session
|
|
606
|
-
} catch (sessionError) {
|
|
607
|
-
console.log('Session validation failed for:', session.sessionId, sessionError);
|
|
608
|
-
continue; // Try next session
|
|
609
|
-
}
|
|
665
|
+
// Create the refresh promise
|
|
666
|
+
const refreshPromise = (async () => {
|
|
667
|
+
try {
|
|
668
|
+
const deviceSessions = await oxyServices.getDeviceSessions(activeSessionId);
|
|
669
|
+
const allDeviceSessions = mapSessionsToClient(deviceSessions, undefined, user?.id);
|
|
670
|
+
updateSessions(allDeviceSessions, true);
|
|
671
|
+
} catch (error) {
|
|
672
|
+
if (__DEV__) {
|
|
673
|
+
console.warn('Failed to refresh device sessions, falling back to user sessions:', error);
|
|
610
674
|
}
|
|
611
|
-
|
|
675
|
+
try {
|
|
676
|
+
const serverSessions = await oxyServices.getSessionsBySessionId(activeSessionId);
|
|
677
|
+
const userSessions = mapSessionsToClient(serverSessions, undefined, user?.id);
|
|
678
|
+
updateSessions(userSessions, true);
|
|
679
|
+
} catch (fallbackError) {
|
|
680
|
+
if (__DEV__) {
|
|
681
|
+
console.error('Refresh sessions error:', fallbackError);
|
|
682
|
+
}
|
|
612
683
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
684
|
+
// If the current session is invalid, try to find another valid session
|
|
685
|
+
if (sessions.length > 1) {
|
|
686
|
+
const otherSessions = sessions.filter(s => s.sessionId !== activeSessionId);
|
|
687
|
+
for (const session of otherSessions) {
|
|
688
|
+
try {
|
|
689
|
+
const validation = await oxyServices.validateSession(session.sessionId, {
|
|
690
|
+
useHeaderValidation: true
|
|
691
|
+
});
|
|
692
|
+
if (validation.valid) {
|
|
693
|
+
await switchToSession(session.sessionId);
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
} catch {
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// No valid sessions found, clear all
|
|
703
|
+
updateSessions([], false);
|
|
704
|
+
setActiveSessionId(null);
|
|
705
|
+
logoutStore();
|
|
706
|
+
setMinimalUser(null);
|
|
707
|
+
await clearAllStorage();
|
|
708
|
+
onAuthStateChange?.(null);
|
|
709
|
+
}
|
|
710
|
+
} finally {
|
|
711
|
+
// Clear the in-flight ref when done
|
|
712
|
+
refreshInFlightRef.current = null;
|
|
622
713
|
}
|
|
623
|
-
}
|
|
624
|
-
|
|
714
|
+
})();
|
|
715
|
+
refreshInFlightRef.current = refreshPromise;
|
|
716
|
+
return refreshPromise;
|
|
717
|
+
}, [activeSessionId, oxyServices, user?.id, updateSessions, sessions, switchToSession, logoutStore, clearAllStorage, onAuthStateChange, mapSessionsToClient]);
|
|
625
718
|
|
|
626
719
|
// Device management methods
|
|
627
720
|
const getDeviceSessions = useCallback(async () => {
|
|
@@ -629,58 +722,67 @@ export const OxyProvider = ({
|
|
|
629
722
|
try {
|
|
630
723
|
return await oxyServices.getDeviceSessions(activeSessionId);
|
|
631
724
|
} catch (error) {
|
|
632
|
-
|
|
725
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to get device sessions';
|
|
726
|
+
onError?.({
|
|
727
|
+
message: errorMessage,
|
|
728
|
+
code: 'GET_DEVICE_SESSIONS_ERROR',
|
|
729
|
+
status: 500
|
|
730
|
+
});
|
|
633
731
|
throw error;
|
|
634
732
|
}
|
|
635
|
-
}, [activeSessionId, oxyServices]);
|
|
733
|
+
}, [activeSessionId, oxyServices, onError]);
|
|
636
734
|
const logoutAllDeviceSessions = useCallback(async () => {
|
|
637
735
|
if (!activeSessionId) throw new Error('No active session');
|
|
638
736
|
try {
|
|
639
737
|
await oxyServices.logoutAllDeviceSessions(activeSessionId);
|
|
640
|
-
|
|
641
|
-
// Clear all local sessions since we logged out from all devices
|
|
642
|
-
setSessions([]);
|
|
738
|
+
updateSessions([], false);
|
|
643
739
|
setActiveSessionId(null);
|
|
644
740
|
logoutStore();
|
|
645
741
|
setMinimalUser(null);
|
|
646
742
|
await clearAllStorage();
|
|
647
|
-
|
|
648
|
-
onAuthStateChange(null);
|
|
649
|
-
}
|
|
743
|
+
onAuthStateChange?.(null);
|
|
650
744
|
} catch (error) {
|
|
651
|
-
|
|
745
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to logout all device sessions';
|
|
746
|
+
onError?.({
|
|
747
|
+
message: errorMessage,
|
|
748
|
+
code: 'LOGOUT_ALL_DEVICES_ERROR',
|
|
749
|
+
status: 500
|
|
750
|
+
});
|
|
652
751
|
throw error;
|
|
653
752
|
}
|
|
654
|
-
}, [activeSessionId, oxyServices, logoutStore,
|
|
753
|
+
}, [activeSessionId, oxyServices, logoutStore, clearAllStorage, onAuthStateChange, onError]);
|
|
655
754
|
const updateDeviceName = useCallback(async deviceName => {
|
|
656
755
|
if (!activeSessionId) throw new Error('No active session');
|
|
657
756
|
try {
|
|
658
757
|
await oxyServices.updateDeviceName(activeSessionId, deviceName);
|
|
659
|
-
|
|
660
|
-
// Update local device info
|
|
661
758
|
await DeviceManager.updateDeviceName(deviceName);
|
|
662
759
|
} catch (error) {
|
|
663
|
-
|
|
760
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to update device name';
|
|
761
|
+
onError?.({
|
|
762
|
+
message: errorMessage,
|
|
763
|
+
code: 'UPDATE_DEVICE_NAME_ERROR',
|
|
764
|
+
status: 500
|
|
765
|
+
});
|
|
664
766
|
throw error;
|
|
665
767
|
}
|
|
666
|
-
}, [activeSessionId, oxyServices]);
|
|
768
|
+
}, [activeSessionId, oxyServices, onError]);
|
|
667
769
|
|
|
668
770
|
// Language management method
|
|
669
771
|
const setLanguage = useCallback(async languageId => {
|
|
670
772
|
if (!storage) throw new Error('Storage not initialized');
|
|
671
773
|
try {
|
|
672
|
-
// Save language preference
|
|
673
774
|
await storage.setItem(keys.language, languageId);
|
|
674
775
|
setCurrentLanguage(languageId);
|
|
675
|
-
console.log(`Language changed to ${languageId}`);
|
|
676
|
-
|
|
677
|
-
// TODO: Here you can add any additional logic needed for app-wide language updates
|
|
678
|
-
// such as updating i18n configuration, refreshing translations, etc.
|
|
679
776
|
} catch (error) {
|
|
680
|
-
|
|
777
|
+
const errorMessage = error instanceof Error ? error.message : 'Failed to save language preference';
|
|
778
|
+
onError?.({
|
|
779
|
+
message: errorMessage,
|
|
780
|
+
code: 'LANGUAGE_SAVE_ERROR',
|
|
781
|
+
status: 500
|
|
782
|
+
});
|
|
681
783
|
throw error;
|
|
682
784
|
}
|
|
683
|
-
}, [storage, keys.language]);
|
|
785
|
+
}, [storage, keys.language, onError]);
|
|
684
786
|
|
|
685
787
|
// Bottom sheet control methods
|
|
686
788
|
const showBottomSheet = useCallback(screenOrConfig => {
|
|
@@ -695,9 +797,8 @@ export const OxyProvider = ({
|
|
|
695
797
|
} else if (bottomSheetRef.current.present) {
|
|
696
798
|
if (__DEV__) console.log('Presenting bottom sheet');
|
|
697
799
|
bottomSheetRef.current.present();
|
|
698
|
-
} else {
|
|
800
|
+
} else if (__DEV__) {
|
|
699
801
|
console.warn('No expand or present method available on bottomSheetRef');
|
|
700
|
-
if (__DEV__) console.log('Available methods on bottomSheetRef.current:', Object.keys(bottomSheetRef.current));
|
|
701
802
|
}
|
|
702
803
|
|
|
703
804
|
// Then navigate to the specified screen if provided
|
|
@@ -715,10 +816,8 @@ export const OxyProvider = ({
|
|
|
715
816
|
}
|
|
716
817
|
}, 100);
|
|
717
818
|
}
|
|
718
|
-
} else {
|
|
719
|
-
console.warn('bottomSheetRef is not available');
|
|
720
|
-
console.warn('To fix this, ensure you pass a bottomSheetRef to OxyProvider:');
|
|
721
|
-
console.warn('<OxyProvider baseURL="..." bottomSheetRef={yourBottomSheetRef}>');
|
|
819
|
+
} else if (__DEV__) {
|
|
820
|
+
console.warn('bottomSheetRef is not available. Pass a bottomSheetRef to OxyProvider.');
|
|
722
821
|
}
|
|
723
822
|
}, [bottomSheetRef]);
|
|
724
823
|
const hideBottomSheet = useCallback(() => {
|
|
@@ -751,41 +850,22 @@ export const OxyProvider = ({
|
|
|
751
850
|
if (mod && typeof mod.useFollow === 'function') {
|
|
752
851
|
return mod.useFollow(userId);
|
|
753
852
|
}
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
error: null,
|
|
759
|
-
toggleFollow: async () => {},
|
|
760
|
-
setFollowStatus: () => {},
|
|
761
|
-
fetchStatus: async () => {},
|
|
762
|
-
clearError: () => {},
|
|
763
|
-
followerCount: null,
|
|
764
|
-
followingCount: null,
|
|
765
|
-
isLoadingCounts: false,
|
|
766
|
-
fetchUserCounts: async () => {},
|
|
767
|
-
setFollowerCount: () => {},
|
|
768
|
-
setFollowingCount: () => {}
|
|
769
|
-
};
|
|
853
|
+
if (__DEV__) {
|
|
854
|
+
console.warn('useFollow module did not export a function as expected');
|
|
855
|
+
}
|
|
856
|
+
return createEmptyFollowHook()(userId);
|
|
770
857
|
} catch (e) {
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
error: null,
|
|
776
|
-
toggleFollow: async () => {},
|
|
777
|
-
setFollowStatus: () => {},
|
|
778
|
-
fetchStatus: async () => {},
|
|
779
|
-
clearError: () => {},
|
|
780
|
-
followerCount: null,
|
|
781
|
-
followingCount: null,
|
|
782
|
-
isLoadingCounts: false,
|
|
783
|
-
fetchUserCounts: async () => {},
|
|
784
|
-
setFollowerCount: () => {},
|
|
785
|
-
setFollowingCount: () => {}
|
|
786
|
-
};
|
|
858
|
+
if (__DEV__) {
|
|
859
|
+
console.warn('Failed to dynamically load useFollow hook:', e);
|
|
860
|
+
}
|
|
861
|
+
return createEmptyFollowHook()(userId);
|
|
787
862
|
}
|
|
788
863
|
};
|
|
864
|
+
|
|
865
|
+
// Compute language metadata from currentLanguage
|
|
866
|
+
const languageMetadata = useMemo(() => getLanguageMetadata(currentLanguage), [currentLanguage]);
|
|
867
|
+
const languageName = useMemo(() => getLanguageName(currentLanguage), [currentLanguage]);
|
|
868
|
+
const nativeLanguageName = useMemo(() => getNativeLanguageName(currentLanguage), [currentLanguage]);
|
|
789
869
|
const contextValue = useMemo(() => ({
|
|
790
870
|
user,
|
|
791
871
|
minimalUser,
|
|
@@ -793,8 +873,12 @@ export const OxyProvider = ({
|
|
|
793
873
|
activeSessionId,
|
|
794
874
|
isAuthenticated,
|
|
795
875
|
isLoading,
|
|
876
|
+
isTokenReady: tokenReady,
|
|
796
877
|
error,
|
|
797
878
|
currentLanguage,
|
|
879
|
+
currentLanguageMetadata: languageMetadata,
|
|
880
|
+
currentLanguageName: languageName,
|
|
881
|
+
currentNativeLanguageName: nativeLanguageName,
|
|
798
882
|
login,
|
|
799
883
|
logout,
|
|
800
884
|
logoutAll,
|
|
@@ -816,21 +900,9 @@ export const OxyProvider = ({
|
|
|
816
900
|
// Only depend on user ID, not the entire user object
|
|
817
901
|
minimalUser?.id, sessions.length,
|
|
818
902
|
// Only depend on sessions count, not the entire array
|
|
819
|
-
activeSessionId, isAuthenticated, isLoading, error, currentLanguage, login, logout, logoutAll, signUp, completeMfaLogin, switchSession, removeSession, refreshSessions, setLanguage, getDeviceSessions, logoutAllDeviceSessions, updateDeviceName, oxyServices, bottomSheetRef, showBottomSheet, hideBottomSheet]);
|
|
820
|
-
|
|
821
|
-
//
|
|
822
|
-
if (!tokenReady) {
|
|
823
|
-
return /*#__PURE__*/_jsx(View, {
|
|
824
|
-
style: {
|
|
825
|
-
flex: 1,
|
|
826
|
-
justifyContent: 'center',
|
|
827
|
-
alignItems: 'center'
|
|
828
|
-
},
|
|
829
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
830
|
-
children: "Loading authentication..."
|
|
831
|
-
})
|
|
832
|
-
});
|
|
833
|
-
}
|
|
903
|
+
activeSessionId, isAuthenticated, isLoading, tokenReady, error, currentLanguage, languageMetadata, languageName, nativeLanguageName, login, logout, logoutAll, signUp, completeMfaLogin, switchSession, removeSession, refreshSessions, setLanguage, getDeviceSessions, logoutAllDeviceSessions, updateDeviceName, oxyServices, bottomSheetRef, showBottomSheet, hideBottomSheet]);
|
|
904
|
+
|
|
905
|
+
// Always render children - let the consuming app decide how to handle token loading state
|
|
834
906
|
return /*#__PURE__*/_jsx(OxyContext.Provider, {
|
|
835
907
|
value: contextValue,
|
|
836
908
|
children: children
|