@oxyhq/services 5.16.44 → 5.17.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/README.md +29 -10
- package/lib/commonjs/core/OxyServices.js +1 -1
- package/lib/commonjs/core/index.js +20 -15
- package/lib/commonjs/core/index.js.map +1 -1
- package/lib/commonjs/core/mixins/OxyServices.auth.js +36 -53
- package/lib/commonjs/core/mixins/OxyServices.auth.js.map +1 -1
- package/lib/commonjs/core/mixins/OxyServices.user.js +10 -17
- package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/commonjs/core/services/TokenService.js +27 -13
- package/lib/commonjs/core/services/TokenService.js.map +1 -1
- package/lib/commonjs/crypto/index.js +0 -16
- package/lib/commonjs/crypto/index.js.map +1 -1
- package/lib/commonjs/crypto/keyManager.js +21 -22
- package/lib/commonjs/crypto/keyManager.js.map +1 -1
- package/lib/commonjs/crypto/polyfill.js +1 -10
- package/lib/commonjs/crypto/polyfill.js.map +1 -1
- package/lib/commonjs/crypto/signatureService.js +18 -32
- package/lib/commonjs/crypto/signatureService.js.map +1 -1
- package/lib/commonjs/index.js +13 -134
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/models/interfaces.js +0 -7
- package/lib/commonjs/models/interfaces.js.map +1 -1
- package/lib/commonjs/node/index.js +1 -10
- package/lib/commonjs/node/index.js.map +1 -1
- package/lib/commonjs/ui/components/BottomSheetRouter.js +1 -9
- package/lib/commonjs/ui/components/BottomSheetRouter.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +9 -20
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +779 -450
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +551 -0
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -0
- package/lib/commonjs/ui/context/hooks/useDeviceManagement.js +73 -0
- package/lib/commonjs/ui/context/hooks/useDeviceManagement.js.map +1 -0
- package/lib/commonjs/ui/context/hooks/useStorage.js +79 -0
- package/lib/commonjs/ui/context/hooks/useStorage.js.map +1 -0
- package/lib/commonjs/ui/hooks/index.js +0 -20
- package/lib/commonjs/ui/hooks/index.js.map +1 -1
- package/lib/commonjs/ui/hooks/mutations/index.js +0 -12
- package/lib/commonjs/ui/hooks/mutations/index.js.map +1 -1
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +23 -74
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/index.js +0 -12
- package/lib/commonjs/ui/hooks/queries/index.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/queryKeys.js +1 -3
- package/lib/commonjs/ui/hooks/queries/queryKeys.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +28 -64
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useServicesQueries.js +6 -4
- package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -1
- package/lib/commonjs/ui/hooks/useDeviceManagement.js +73 -0
- package/lib/commonjs/ui/hooks/useDeviceManagement.js.map +1 -0
- package/lib/commonjs/ui/hooks/useProfileEditing.js +5 -3
- package/lib/commonjs/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +284 -0
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -0
- package/lib/commonjs/ui/index.js +2 -10
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/navigation/routes.js +1 -5
- package/lib/commonjs/ui/navigation/routes.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountCenterScreen.js +4 -9
- package/lib/commonjs/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +19 -37
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +5 -5
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/OxyAuthScreen.js +15 -2
- package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js +97 -76
- package/lib/commonjs/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ProfileScreen.js +6 -6
- package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/commonjs/ui/stores/authStore.js +6 -54
- package/lib/commonjs/ui/stores/authStore.js.map +1 -1
- package/lib/commonjs/ui/styles/spacing.js +2 -54
- package/lib/commonjs/ui/styles/spacing.js.map +1 -1
- package/lib/commonjs/ui/utils/avatarUtils.js +12 -9
- package/lib/commonjs/ui/utils/avatarUtils.js.map +1 -1
- package/lib/commonjs/ui/utils/sessionHelpers.js +1 -7
- package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/commonjs/utils/deviceManager.js +177 -0
- package/lib/commonjs/utils/deviceManager.js.map +1 -0
- package/lib/commonjs/utils/errorUtils.js +0 -13
- package/lib/commonjs/utils/errorUtils.js.map +1 -1
- package/lib/commonjs/utils/index.js +7 -0
- package/lib/commonjs/utils/index.js.map +1 -1
- package/lib/commonjs/utils/sessionUtils.js +1 -8
- package/lib/commonjs/utils/sessionUtils.js.map +1 -1
- package/lib/commonjs/utils/validationUtils.js +1 -15
- package/lib/commonjs/utils/validationUtils.js.map +1 -1
- package/lib/module/core/OxyServices.js +1 -1
- package/lib/module/core/index.js +4 -6
- package/lib/module/core/index.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.auth.js +36 -53
- package/lib/module/core/mixins/OxyServices.auth.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.user.js +10 -17
- package/lib/module/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/module/core/services/TokenService.js +27 -13
- package/lib/module/core/services/TokenService.js.map +1 -1
- package/lib/module/crypto/index.js +0 -3
- package/lib/module/crypto/index.js.map +1 -1
- package/lib/module/crypto/keyManager.js +21 -22
- package/lib/module/crypto/keyManager.js.map +1 -1
- package/lib/module/crypto/polyfill.js +1 -2
- package/lib/module/crypto/polyfill.js.map +1 -1
- package/lib/module/crypto/signatureService.js +18 -32
- package/lib/module/crypto/signatureService.js.map +1 -1
- package/lib/module/index.js +7 -19
- package/lib/module/index.js.map +1 -1
- package/lib/module/models/interfaces.js +0 -7
- package/lib/module/models/interfaces.js.map +1 -1
- package/lib/module/node/index.js +0 -3
- package/lib/module/node/index.js.map +1 -1
- package/lib/module/ui/components/BottomSheetRouter.js +2 -6
- package/lib/module/ui/components/BottomSheetRouter.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +9 -20
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +779 -450
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +545 -0
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -0
- package/lib/module/ui/context/hooks/useDeviceManagement.js +68 -0
- package/lib/module/ui/context/hooks/useDeviceManagement.js.map +1 -0
- package/lib/module/ui/context/hooks/useStorage.js +74 -0
- package/lib/module/ui/context/hooks/useStorage.js.map +1 -0
- package/lib/module/ui/hooks/index.js +0 -1
- package/lib/module/ui/hooks/index.js.map +1 -1
- package/lib/module/ui/hooks/mutations/index.js +1 -1
- package/lib/module/ui/hooks/mutations/index.js.map +1 -1
- package/lib/module/ui/hooks/mutations/useAccountMutations.js +21 -71
- package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
- package/lib/module/ui/hooks/queries/index.js +1 -1
- package/lib/module/ui/hooks/queries/index.js.map +1 -1
- package/lib/module/ui/hooks/queries/queryKeys.js +1 -3
- package/lib/module/ui/hooks/queries/queryKeys.js.map +1 -1
- package/lib/module/ui/hooks/queries/useAccountQueries.js +27 -61
- package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
- package/lib/module/ui/hooks/queries/useServicesQueries.js +6 -4
- package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -1
- package/lib/module/ui/hooks/useDeviceManagement.js +68 -0
- package/lib/module/ui/hooks/useDeviceManagement.js.map +1 -0
- package/lib/module/ui/hooks/useProfileEditing.js +5 -3
- package/lib/module/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js +279 -0
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -0
- package/lib/module/ui/index.js +1 -2
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/navigation/routes.js +1 -5
- package/lib/module/ui/navigation/routes.js.map +1 -1
- package/lib/module/ui/screens/AccountCenterScreen.js +4 -9
- package/lib/module/ui/screens/AccountCenterScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +19 -37
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSwitcherScreen.js +5 -5
- package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
- package/lib/module/ui/screens/OxyAuthScreen.js +15 -2
- package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/module/ui/screens/PrivacySettingsScreen.js +98 -77
- package/lib/module/ui/screens/PrivacySettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/ProfileScreen.js +6 -6
- package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/module/ui/stores/authStore.js +6 -54
- package/lib/module/ui/stores/authStore.js.map +1 -1
- package/lib/module/ui/styles/spacing.js +2 -6
- package/lib/module/ui/styles/spacing.js.map +1 -1
- package/lib/module/ui/utils/avatarUtils.js +12 -9
- package/lib/module/ui/utils/avatarUtils.js.map +1 -1
- package/lib/module/ui/utils/sessionHelpers.js +1 -7
- package/lib/module/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/module/utils/deviceManager.js +171 -0
- package/lib/module/utils/deviceManager.js.map +1 -0
- package/lib/module/utils/errorUtils.js +0 -7
- package/lib/module/utils/errorUtils.js.map +1 -1
- package/lib/module/utils/index.js +1 -2
- package/lib/module/utils/index.js.map +1 -1
- package/lib/module/utils/sessionUtils.js +1 -8
- package/lib/module/utils/sessionUtils.js.map +1 -1
- package/lib/module/utils/validationUtils.js +0 -13
- package/lib/module/utils/validationUtils.js.map +1 -1
- package/lib/typescript/core/OxyServices.d.ts +1 -1
- package/lib/typescript/core/index.d.ts +3 -3
- package/lib/typescript/core/index.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.auth.d.ts +21 -44
- package/lib/typescript/core/mixins/OxyServices.auth.d.ts.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.user.d.ts +1 -0
- package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -1
- package/lib/typescript/core/mixins/index.d.ts +8 -15
- package/lib/typescript/core/mixins/index.d.ts.map +1 -1
- package/lib/typescript/core/services/TokenService.d.ts.map +1 -1
- package/lib/typescript/crypto/index.d.ts +0 -1
- package/lib/typescript/crypto/index.d.ts.map +1 -1
- package/lib/typescript/crypto/keyManager.d.ts +2 -15
- package/lib/typescript/crypto/keyManager.d.ts.map +1 -1
- package/lib/typescript/crypto/polyfill.d.ts +1 -2
- package/lib/typescript/crypto/polyfill.d.ts.map +1 -1
- package/lib/typescript/crypto/signatureService.d.ts +0 -13
- package/lib/typescript/crypto/signatureService.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +7 -12
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/models/interfaces.d.ts +36 -5
- package/lib/typescript/models/interfaces.d.ts.map +1 -1
- package/lib/typescript/models/session.d.ts +18 -3
- package/lib/typescript/models/session.d.ts.map +1 -1
- package/lib/typescript/node/index.d.ts +0 -1
- package/lib/typescript/node/index.d.ts.map +1 -1
- package/lib/typescript/ui/components/BottomSheetRouter.d.ts +0 -5
- package/lib/typescript/ui/components/BottomSheetRouter.d.ts.map +1 -1
- package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/ui/components/TextField/Addons/Outline.d.ts +2 -2
- package/lib/typescript/ui/components/TextField/helpers.d.ts +2 -2
- package/lib/typescript/ui/components/TextField/types.d.ts +0 -1
- package/lib/typescript/ui/components/TextField/types.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts +28 -5
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts +59 -0
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -0
- package/lib/typescript/ui/context/hooks/useDeviceManagement.d.ts +27 -0
- package/lib/typescript/ui/context/hooks/useDeviceManagement.d.ts.map +1 -0
- package/lib/typescript/ui/context/hooks/useStorage.d.ts +22 -0
- package/lib/typescript/ui/context/hooks/useStorage.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/index.d.ts +0 -1
- package/lib/typescript/ui/hooks/index.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/mutations/index.d.ts +1 -1
- package/lib/typescript/ui/hooks/mutations/index.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts +8 -19
- package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/mutations/useServicesMutations.d.ts +1 -1
- package/lib/typescript/ui/hooks/mutations/useServicesMutations.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/queries/index.d.ts +1 -1
- package/lib/typescript/ui/hooks/queries/index.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/queries/queryKeys.d.ts +0 -2
- package/lib/typescript/ui/hooks/queries/queryKeys.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts +5 -17
- package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useDeviceManagement.d.ts +27 -0
- package/lib/typescript/ui/hooks/useDeviceManagement.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/useProfileEditing.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useSessionManagement.d.ts +41 -0
- package/lib/typescript/ui/hooks/useSessionManagement.d.ts.map +1 -0
- package/lib/typescript/ui/index.d.ts +0 -1
- package/lib/typescript/ui/index.d.ts.map +1 -1
- package/lib/typescript/ui/navigation/routes.d.ts +1 -1
- package/lib/typescript/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountCenterScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/PrivacySettingsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/stores/authStore.d.ts +1 -8
- package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
- package/lib/typescript/ui/styles/spacing.d.ts +0 -5
- package/lib/typescript/ui/styles/spacing.d.ts.map +1 -1
- package/lib/typescript/ui/utils/avatarUtils.d.ts +4 -1
- package/lib/typescript/ui/utils/avatarUtils.d.ts.map +1 -1
- package/lib/typescript/ui/utils/sessionHelpers.d.ts +0 -1
- package/lib/typescript/ui/utils/sessionHelpers.d.ts.map +1 -1
- package/lib/typescript/utils/deviceManager.d.ts +66 -0
- package/lib/typescript/utils/deviceManager.d.ts.map +1 -0
- package/lib/typescript/utils/errorUtils.d.ts +0 -6
- package/lib/typescript/utils/errorUtils.d.ts.map +1 -1
- package/lib/typescript/utils/index.d.ts +2 -0
- package/lib/typescript/utils/index.d.ts.map +1 -1
- package/lib/typescript/utils/sessionUtils.d.ts.map +1 -1
- package/lib/typescript/utils/validationUtils.d.ts +0 -8
- package/lib/typescript/utils/validationUtils.d.ts.map +1 -1
- package/package.json +1 -6
- package/src/core/OxyServices.ts +1 -1
- package/src/core/index.ts +5 -8
- package/src/core/mixins/OxyServices.auth.ts +44 -87
- package/src/core/mixins/OxyServices.user.ts +10 -18
- package/src/core/services/TokenService.ts +27 -16
- package/src/crypto/index.ts +1 -3
- package/src/crypto/keyManager.ts +21 -30
- package/src/crypto/polyfill.ts +1 -2
- package/src/crypto/signatureService.ts +19 -43
- package/src/index.ts +6 -41
- package/src/models/interfaces.ts +39 -12
- package/src/models/session.ts +19 -7
- package/src/node/index.ts +0 -3
- package/src/ui/components/BottomSheetRouter.tsx +1 -6
- package/src/ui/components/OxyProvider.tsx +10 -17
- package/src/ui/components/TextField/Addons/Outline.tsx +2 -2
- package/src/ui/components/TextField/helpers.tsx +2 -2
- package/src/ui/components/TextField/types.tsx +1 -1
- package/src/ui/context/OxyContext.tsx +831 -463
- package/src/ui/context/hooks/useAuthOperations.ts +620 -0
- package/src/ui/context/hooks/useDeviceManagement.ts +108 -0
- package/src/ui/context/hooks/useStorage.ts +104 -0
- package/src/ui/hooks/index.ts +1 -2
- package/src/ui/hooks/mutations/index.ts +0 -2
- package/src/ui/hooks/mutations/useAccountMutations.ts +20 -66
- package/src/ui/hooks/queries/index.ts +0 -2
- package/src/ui/hooks/queries/queryKeys.ts +0 -2
- package/src/ui/hooks/queries/useAccountQueries.ts +19 -53
- package/src/ui/hooks/queries/useServicesQueries.ts +5 -5
- package/src/ui/hooks/useDeviceManagement.ts +108 -0
- package/src/ui/hooks/useProfileEditing.ts +3 -3
- package/src/ui/hooks/useSessionManagement.ts +405 -0
- package/src/ui/index.ts +1 -2
- package/src/ui/navigation/routes.ts +2 -6
- package/src/ui/screens/AccountCenterScreen.tsx +4 -9
- package/src/ui/screens/AccountSettingsScreen.tsx +31 -49
- package/src/ui/screens/AccountSwitcherScreen.tsx +5 -5
- package/src/ui/screens/OxyAuthScreen.tsx +19 -4
- package/src/ui/screens/PrivacySettingsScreen.tsx +101 -67
- package/src/ui/screens/ProfileScreen.tsx +10 -10
- package/src/ui/stores/authStore.ts +8 -48
- package/src/ui/styles/spacing.ts +2 -15
- package/src/ui/utils/avatarUtils.ts +21 -19
- package/src/ui/utils/sessionHelpers.ts +0 -7
- package/src/utils/__tests__/validationUtils.test.ts +1 -16
- package/src/utils/deviceManager.ts +198 -0
- package/src/utils/errorUtils.ts +1 -8
- package/src/utils/index.ts +2 -1
- package/src/utils/sessionUtils.ts +0 -8
- package/src/utils/validationUtils.ts +0 -12
- package/lib/commonjs/adapters/expo/crypto.js +0 -56
- package/lib/commonjs/adapters/expo/crypto.js.map +0 -1
- package/lib/commonjs/adapters/expo/fetch.js +0 -30
- package/lib/commonjs/adapters/expo/fetch.js.map +0 -1
- package/lib/commonjs/adapters/expo/index.js +0 -48
- package/lib/commonjs/adapters/expo/index.js.map +0 -1
- package/lib/commonjs/adapters/expo/storage.js +0 -201
- package/lib/commonjs/adapters/expo/storage.js.map +0 -1
- package/lib/commonjs/adapters/index.js +0 -48
- package/lib/commonjs/adapters/index.js.map +0 -1
- package/lib/commonjs/adapters/node/crypto.js +0 -40
- package/lib/commonjs/adapters/node/crypto.js.map +0 -1
- package/lib/commonjs/adapters/node/fetch.js +0 -62
- package/lib/commonjs/adapters/node/fetch.js.map +0 -1
- package/lib/commonjs/adapters/node/index.js +0 -34
- package/lib/commonjs/adapters/node/index.js.map +0 -1
- package/lib/commonjs/adapters/node/storage.js +0 -163
- package/lib/commonjs/adapters/node/storage.js.map +0 -1
- package/lib/commonjs/core/identity-session/DeviceManager.js +0 -237
- package/lib/commonjs/core/identity-session/DeviceManager.js.map +0 -1
- package/lib/commonjs/core/identity-session/INTEGRATION_GUIDE.md +0 -287
- package/lib/commonjs/core/identity-session/IdentityManager.js +0 -400
- package/lib/commonjs/core/identity-session/IdentityManager.js.map +0 -1
- package/lib/commonjs/core/identity-session/IdentitySessionCore.js +0 -394
- package/lib/commonjs/core/identity-session/IdentitySessionCore.js.map +0 -1
- package/lib/commonjs/core/identity-session/RefreshManager.js +0 -137
- package/lib/commonjs/core/identity-session/RefreshManager.js.map +0 -1
- package/lib/commonjs/core/identity-session/SessionManager.js +0 -427
- package/lib/commonjs/core/identity-session/SessionManager.js.map +0 -1
- package/lib/commonjs/core/identity-session/createIdentitySessionCore.js +0 -24
- package/lib/commonjs/core/identity-session/createIdentitySessionCore.js.map +0 -1
- package/lib/commonjs/core/identity-session/errors.js +0 -176
- package/lib/commonjs/core/identity-session/errors.js.map +0 -1
- package/lib/commonjs/core/identity-session/index.js +0 -80
- package/lib/commonjs/core/identity-session/index.js.map +0 -1
- package/lib/commonjs/core/identity-session/types.js +0 -2
- package/lib/commonjs/core/identity-session/types.js.map +0 -1
- package/lib/commonjs/crypto/README.md +0 -142
- package/lib/commonjs/crypto/core.js +0 -147
- package/lib/commonjs/crypto/core.js.map +0 -1
- package/lib/commonjs/node/signatureService.js +0 -107
- package/lib/commonjs/node/signatureService.js.map +0 -1
- package/lib/commonjs/ui/hooks/auth/index.js +0 -37
- package/lib/commonjs/ui/hooks/auth/index.js.map +0 -1
- package/lib/commonjs/ui/hooks/auth/useUsernameValidation.js +0 -171
- package/lib/commonjs/ui/hooks/auth/useUsernameValidation.js.map +0 -1
- package/lib/commonjs/ui/hooks/useAvatarPicker.js +0 -52
- package/lib/commonjs/ui/hooks/useAvatarPicker.js.map +0 -1
- package/lib/commonjs/ui/hooks/useIdentityTransfer.js +0 -125
- package/lib/commonjs/ui/hooks/useIdentityTransfer.js.map +0 -1
- package/lib/commonjs/ui/hooks/useTransferCodesPersistence.js +0 -81
- package/lib/commonjs/ui/hooks/useTransferCodesPersistence.js.map +0 -1
- package/lib/commonjs/ui/hooks/useTransferQueries.js +0 -85
- package/lib/commonjs/ui/hooks/useTransferQueries.js.map +0 -1
- package/lib/commonjs/ui/stores/transferStore.js +0 -157
- package/lib/commonjs/ui/stores/transferStore.js.map +0 -1
- package/lib/module/adapters/expo/crypto.js +0 -51
- package/lib/module/adapters/expo/crypto.js.map +0 -1
- package/lib/module/adapters/expo/fetch.js +0 -26
- package/lib/module/adapters/expo/fetch.js.map +0 -1
- package/lib/module/adapters/expo/index.js +0 -45
- package/lib/module/adapters/expo/index.js.map +0 -1
- package/lib/module/adapters/expo/storage.js +0 -198
- package/lib/module/adapters/expo/storage.js.map +0 -1
- package/lib/module/adapters/index.js +0 -45
- package/lib/module/adapters/index.js.map +0 -1
- package/lib/module/adapters/node/crypto.js +0 -36
- package/lib/module/adapters/node/crypto.js.map +0 -1
- package/lib/module/adapters/node/fetch.js +0 -57
- package/lib/module/adapters/node/fetch.js.map +0 -1
- package/lib/module/adapters/node/index.js +0 -31
- package/lib/module/adapters/node/index.js.map +0 -1
- package/lib/module/adapters/node/storage.js +0 -159
- package/lib/module/adapters/node/storage.js.map +0 -1
- package/lib/module/core/identity-session/DeviceManager.js +0 -232
- package/lib/module/core/identity-session/DeviceManager.js.map +0 -1
- package/lib/module/core/identity-session/INTEGRATION_GUIDE.md +0 -287
- package/lib/module/core/identity-session/IdentityManager.js +0 -395
- package/lib/module/core/identity-session/IdentityManager.js.map +0 -1
- package/lib/module/core/identity-session/IdentitySessionCore.js +0 -390
- package/lib/module/core/identity-session/IdentitySessionCore.js.map +0 -1
- package/lib/module/core/identity-session/RefreshManager.js +0 -132
- package/lib/module/core/identity-session/RefreshManager.js.map +0 -1
- package/lib/module/core/identity-session/SessionManager.js +0 -422
- package/lib/module/core/identity-session/SessionManager.js.map +0 -1
- package/lib/module/core/identity-session/createIdentitySessionCore.js +0 -21
- package/lib/module/core/identity-session/createIdentitySessionCore.js.map +0 -1
- package/lib/module/core/identity-session/errors.js +0 -170
- package/lib/module/core/identity-session/errors.js.map +0 -1
- package/lib/module/core/identity-session/index.js +0 -17
- package/lib/module/core/identity-session/index.js.map +0 -1
- package/lib/module/core/identity-session/types.js +0 -2
- package/lib/module/core/identity-session/types.js.map +0 -1
- package/lib/module/crypto/README.md +0 -142
- package/lib/module/crypto/core.js +0 -133
- package/lib/module/crypto/core.js.map +0 -1
- package/lib/module/node/signatureService.js +0 -101
- package/lib/module/node/signatureService.js.map +0 -1
- package/lib/module/ui/hooks/auth/index.js +0 -7
- package/lib/module/ui/hooks/auth/index.js.map +0 -1
- package/lib/module/ui/hooks/auth/useUsernameValidation.js +0 -167
- package/lib/module/ui/hooks/auth/useUsernameValidation.js.map +0 -1
- package/lib/module/ui/hooks/useAvatarPicker.js +0 -48
- package/lib/module/ui/hooks/useAvatarPicker.js.map +0 -1
- package/lib/module/ui/hooks/useIdentityTransfer.js +0 -121
- package/lib/module/ui/hooks/useIdentityTransfer.js.map +0 -1
- package/lib/module/ui/hooks/useTransferCodesPersistence.js +0 -77
- package/lib/module/ui/hooks/useTransferCodesPersistence.js.map +0 -1
- package/lib/module/ui/hooks/useTransferQueries.js +0 -80
- package/lib/module/ui/hooks/useTransferQueries.js.map +0 -1
- package/lib/module/ui/stores/transferStore.js +0 -152
- package/lib/module/ui/stores/transferStore.js.map +0 -1
- package/lib/typescript/adapters/expo/crypto.d.ts +0 -17
- package/lib/typescript/adapters/expo/crypto.d.ts.map +0 -1
- package/lib/typescript/adapters/expo/fetch.d.ts +0 -16
- package/lib/typescript/adapters/expo/fetch.d.ts.map +0 -1
- package/lib/typescript/adapters/expo/index.d.ts +0 -23
- package/lib/typescript/adapters/expo/index.d.ts.map +0 -1
- package/lib/typescript/adapters/expo/storage.d.ts +0 -23
- package/lib/typescript/adapters/expo/storage.d.ts.map +0 -1
- package/lib/typescript/adapters/index.d.ts +0 -17
- package/lib/typescript/adapters/index.d.ts.map +0 -1
- package/lib/typescript/adapters/node/crypto.d.ts +0 -17
- package/lib/typescript/adapters/node/crypto.d.ts.map +0 -1
- package/lib/typescript/adapters/node/fetch.d.ts +0 -16
- package/lib/typescript/adapters/node/fetch.d.ts.map +0 -1
- package/lib/typescript/adapters/node/index.d.ts +0 -23
- package/lib/typescript/adapters/node/index.d.ts.map +0 -1
- package/lib/typescript/adapters/node/storage.d.ts +0 -23
- package/lib/typescript/adapters/node/storage.d.ts.map +0 -1
- package/lib/typescript/core/identity-session/DeviceManager.d.ts +0 -64
- package/lib/typescript/core/identity-session/DeviceManager.d.ts.map +0 -1
- package/lib/typescript/core/identity-session/IdentityManager.d.ts +0 -88
- package/lib/typescript/core/identity-session/IdentityManager.d.ts.map +0 -1
- package/lib/typescript/core/identity-session/IdentitySessionCore.d.ts +0 -141
- package/lib/typescript/core/identity-session/IdentitySessionCore.d.ts.map +0 -1
- package/lib/typescript/core/identity-session/RefreshManager.d.ts +0 -36
- package/lib/typescript/core/identity-session/RefreshManager.d.ts.map +0 -1
- package/lib/typescript/core/identity-session/SessionManager.d.ts +0 -104
- package/lib/typescript/core/identity-session/SessionManager.d.ts.map +0 -1
- package/lib/typescript/core/identity-session/createIdentitySessionCore.d.ts +0 -11
- package/lib/typescript/core/identity-session/createIdentitySessionCore.d.ts.map +0 -1
- package/lib/typescript/core/identity-session/errors.d.ts +0 -63
- package/lib/typescript/core/identity-session/errors.d.ts.map +0 -1
- package/lib/typescript/core/identity-session/index.d.ts +0 -14
- package/lib/typescript/core/identity-session/index.d.ts.map +0 -1
- package/lib/typescript/core/identity-session/types.d.ts +0 -196
- package/lib/typescript/core/identity-session/types.d.ts.map +0 -1
- package/lib/typescript/crypto/core.d.ts +0 -56
- package/lib/typescript/crypto/core.d.ts.map +0 -1
- package/lib/typescript/node/signatureService.d.ts +0 -55
- package/lib/typescript/node/signatureService.d.ts.map +0 -1
- package/lib/typescript/ui/hooks/auth/index.d.ts +0 -6
- package/lib/typescript/ui/hooks/auth/index.d.ts.map +0 -1
- package/lib/typescript/ui/hooks/auth/useUsernameValidation.d.ts +0 -32
- package/lib/typescript/ui/hooks/auth/useUsernameValidation.d.ts.map +0 -1
- package/lib/typescript/ui/hooks/useAvatarPicker.d.ts +0 -18
- package/lib/typescript/ui/hooks/useAvatarPicker.d.ts.map +0 -1
- package/lib/typescript/ui/hooks/useIdentityTransfer.d.ts +0 -24
- package/lib/typescript/ui/hooks/useIdentityTransfer.d.ts.map +0 -1
- package/lib/typescript/ui/hooks/useTransferCodesPersistence.d.ts +0 -6
- package/lib/typescript/ui/hooks/useTransferCodesPersistence.d.ts.map +0 -1
- package/lib/typescript/ui/hooks/useTransferQueries.d.ts +0 -26
- package/lib/typescript/ui/hooks/useTransferQueries.d.ts.map +0 -1
- package/lib/typescript/ui/stores/transferStore.d.ts +0 -36
- package/lib/typescript/ui/stores/transferStore.d.ts.map +0 -1
- package/src/adapters/expo/crypto.ts +0 -55
- package/src/adapters/expo/fetch.ts +0 -28
- package/src/adapters/expo/index.ts +0 -51
- package/src/adapters/expo/storage.ts +0 -228
- package/src/adapters/index.ts +0 -48
- package/src/adapters/node/crypto.ts +0 -39
- package/src/adapters/node/fetch.ts +0 -59
- package/src/adapters/node/index.ts +0 -37
- package/src/adapters/node/storage.ts +0 -170
- package/src/core/identity-session/DeviceManager.ts +0 -273
- package/src/core/identity-session/INTEGRATION_GUIDE.md +0 -287
- package/src/core/identity-session/IdentityManager.ts +0 -474
- package/src/core/identity-session/IdentitySessionCore.ts +0 -464
- package/src/core/identity-session/RefreshManager.ts +0 -189
- package/src/core/identity-session/SessionManager.ts +0 -500
- package/src/core/identity-session/createIdentitySessionCore.ts +0 -19
- package/src/core/identity-session/errors.ts +0 -197
- package/src/core/identity-session/index.ts +0 -15
- package/src/core/identity-session/types.ts +0 -188
- package/src/crypto/README.md +0 -142
- package/src/crypto/__tests__/core.test.ts +0 -203
- package/src/crypto/core.ts +0 -142
- package/src/node/signatureService.ts +0 -126
- package/src/ui/hooks/auth/index.ts +0 -9
- package/src/ui/hooks/auth/useUsernameValidation.ts +0 -177
- package/src/ui/hooks/useAvatarPicker.ts +0 -62
- package/src/ui/hooks/useIdentityTransfer.ts +0 -135
- package/src/ui/hooks/useTransferCodesPersistence.ts +0 -80
- package/src/ui/hooks/useTransferQueries.ts +0 -102
- package/src/ui/stores/transferStore.ts +0 -201
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import { Platform } from 'react-native';
|
|
4
5
|
import { OxyServices } from '../../core';
|
|
5
6
|
import { toast } from '../../lib/sonner';
|
|
6
7
|
import { useAuthStore } from '../stores/authStore';
|
|
@@ -8,20 +9,48 @@ import { useShallow } from 'zustand/react/shallow';
|
|
|
8
9
|
import { useSessionSocket } from '../hooks/useSessionSocket';
|
|
9
10
|
import { useStorage } from '../hooks/useStorage';
|
|
10
11
|
import { useLanguageManagement } from '../hooks/useLanguageManagement';
|
|
12
|
+
import { useSessionManagement } from '../hooks/useSessionManagement';
|
|
13
|
+
import { useAuthOperations } from './hooks/useAuthOperations';
|
|
14
|
+
import { useDeviceManagement } from '../hooks/useDeviceManagement';
|
|
11
15
|
import { getStorageKeys } from '../utils/storageHelpers';
|
|
12
16
|
import { isInvalidSessionError, isTimeoutOrNetworkError } from '../utils/errorHandlers';
|
|
13
17
|
import { showBottomSheet as globalShowBottomSheet } from '../navigation/bottomSheetManager';
|
|
14
18
|
import { useQueryClient } from '@tanstack/react-query';
|
|
19
|
+
import { useCurrentUser } from '../hooks/queries';
|
|
15
20
|
import { clearQueryCache } from '../hooks/queryClient';
|
|
16
|
-
import {
|
|
21
|
+
import { KeyManager } from '../../crypto';
|
|
22
|
+
import { translate } from '../../i18n';
|
|
23
|
+
import { updateAvatarVisibility, updateProfileWithAvatar } from '../utils/avatarUtils';
|
|
17
24
|
import { useAccountStore } from '../stores/accountStore';
|
|
18
|
-
import {
|
|
19
|
-
import { useCheckPendingTransfers } from '../hooks/useTransferQueries';
|
|
20
|
-
import { useTransferCodesPersistence } from '../hooks/useTransferCodesPersistence';
|
|
21
|
-
import { useIdentityTransfer } from '../hooks/useIdentityTransfer';
|
|
22
|
-
import { useAvatarPicker } from '../hooks/useAvatarPicker';
|
|
25
|
+
import { logger as loggerUtil } from '../../utils/loggerUtils';
|
|
23
26
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
24
27
|
const OxyContext = /*#__PURE__*/createContext(null);
|
|
28
|
+
let cachedUseFollowHook = null;
|
|
29
|
+
const loadUseFollowHook = () => {
|
|
30
|
+
if (cachedUseFollowHook) {
|
|
31
|
+
return cachedUseFollowHook;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
35
|
+
const {
|
|
36
|
+
useFollow
|
|
37
|
+
} = require('../hooks/useFollow');
|
|
38
|
+
cachedUseFollowHook = useFollow;
|
|
39
|
+
return cachedUseFollowHook;
|
|
40
|
+
} catch (error) {
|
|
41
|
+
if (__DEV__) {
|
|
42
|
+
loggerUtil.warn('useFollow hook is not available. Please import useFollow from @oxyhq/services directly.', {
|
|
43
|
+
component: 'OxyContext',
|
|
44
|
+
method: 'loadUseFollowHook'
|
|
45
|
+
}, error);
|
|
46
|
+
}
|
|
47
|
+
const fallback = () => {
|
|
48
|
+
throw new Error('useFollow hook is only available in the UI bundle. Import it from @oxyhq/services.');
|
|
49
|
+
};
|
|
50
|
+
cachedUseFollowHook = fallback;
|
|
51
|
+
return cachedUseFollowHook;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
25
54
|
export const OxyProvider = ({
|
|
26
55
|
children,
|
|
27
56
|
oxyServices: providedOxyServices,
|
|
@@ -44,8 +73,7 @@ export const OxyProvider = ({
|
|
|
44
73
|
}
|
|
45
74
|
const oxyServices = oxyServicesRef.current;
|
|
46
75
|
const {
|
|
47
|
-
|
|
48
|
-
isAuthenticated: isAuthenticatedFromStore,
|
|
76
|
+
isAuthenticated,
|
|
49
77
|
isLoading,
|
|
50
78
|
error,
|
|
51
79
|
loginSuccess,
|
|
@@ -57,7 +85,6 @@ export const OxyProvider = ({
|
|
|
57
85
|
setIdentitySynced,
|
|
58
86
|
setSyncing
|
|
59
87
|
} = useAuthStore(useShallow(state => ({
|
|
60
|
-
user: state.user,
|
|
61
88
|
isAuthenticated: state.isAuthenticated,
|
|
62
89
|
isLoading: state.isLoading,
|
|
63
90
|
error: state.error,
|
|
@@ -71,6 +98,7 @@ export const OxyProvider = ({
|
|
|
71
98
|
setSyncing: state.setSyncing
|
|
72
99
|
})));
|
|
73
100
|
const [tokenReady, setTokenReady] = useState(true);
|
|
101
|
+
const initializedRef = useRef(false);
|
|
74
102
|
const setAuthState = useAuthStore.setState;
|
|
75
103
|
const logger = useCallback((message, err) => {
|
|
76
104
|
if (__DEV__) {
|
|
@@ -90,29 +118,48 @@ export const OxyProvider = ({
|
|
|
90
118
|
logger
|
|
91
119
|
});
|
|
92
120
|
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
const [isCoreInitialized, setIsCoreInitialized] = useState(false);
|
|
121
|
+
// Identity integrity check and auto-restore on startup
|
|
122
|
+
// Skip on web platform - identity storage is only available on native platforms
|
|
96
123
|
useEffect(() => {
|
|
97
|
-
|
|
98
|
-
|
|
124
|
+
if (!storage || !isStorageReady) return;
|
|
125
|
+
if (Platform.OS === 'web') return; // Identity operations are native-only
|
|
126
|
+
|
|
127
|
+
const checkAndRestoreIdentity = async () => {
|
|
99
128
|
try {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
129
|
+
// Check if identity exists and verify integrity
|
|
130
|
+
const hasIdentity = await KeyManager.hasIdentity();
|
|
131
|
+
if (hasIdentity) {
|
|
132
|
+
const isValid = await KeyManager.verifyIdentityIntegrity();
|
|
133
|
+
if (!isValid) {
|
|
134
|
+
// Try to restore from backup
|
|
135
|
+
const restored = await KeyManager.restoreIdentityFromBackup();
|
|
136
|
+
if (__DEV__) {
|
|
137
|
+
logger(restored ? 'Identity restored from backup successfully' : 'Identity integrity check failed - user may need to restore from backup file');
|
|
138
|
+
}
|
|
139
|
+
} else {
|
|
140
|
+
// Identity is valid - ensure backup is up to date
|
|
141
|
+
await KeyManager.backupIdentity();
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
// No identity - try to restore from backup
|
|
145
|
+
const restored = await KeyManager.restoreIdentityFromBackup();
|
|
146
|
+
if (restored && __DEV__) {
|
|
147
|
+
logger('Identity restored from backup on startup');
|
|
148
|
+
}
|
|
104
149
|
}
|
|
105
150
|
} catch (error) {
|
|
106
151
|
if (__DEV__) {
|
|
107
|
-
logger('
|
|
152
|
+
logger('Error during identity integrity check', error);
|
|
108
153
|
}
|
|
154
|
+
// Don't block app startup - user can recover with backup file
|
|
109
155
|
}
|
|
110
156
|
};
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
157
|
+
checkAndRestoreIdentity();
|
|
158
|
+
}, [storage, isStorageReady, logger]);
|
|
159
|
+
|
|
160
|
+
// Offline queuing is now handled by TanStack Query mutations
|
|
161
|
+
// No need for custom offline queue
|
|
162
|
+
|
|
116
163
|
const {
|
|
117
164
|
currentLanguage,
|
|
118
165
|
metadata: currentLanguageMetadata,
|
|
@@ -127,112 +174,79 @@ export const OxyProvider = ({
|
|
|
127
174
|
logger
|
|
128
175
|
});
|
|
129
176
|
const queryClient = useQueryClient();
|
|
177
|
+
const {
|
|
178
|
+
sessions,
|
|
179
|
+
activeSessionId,
|
|
180
|
+
setActiveSessionId,
|
|
181
|
+
updateSessions,
|
|
182
|
+
switchSession,
|
|
183
|
+
refreshSessions,
|
|
184
|
+
clearSessionState,
|
|
185
|
+
saveActiveSessionId,
|
|
186
|
+
trackRemovedSession
|
|
187
|
+
} = useSessionManagement({
|
|
188
|
+
oxyServices,
|
|
189
|
+
storage,
|
|
190
|
+
storageKeyPrefix,
|
|
191
|
+
loginSuccess,
|
|
192
|
+
logoutStore,
|
|
193
|
+
applyLanguagePreference,
|
|
194
|
+
onAuthStateChange,
|
|
195
|
+
onError,
|
|
196
|
+
setAuthError: message => setAuthState({
|
|
197
|
+
error: message
|
|
198
|
+
}),
|
|
199
|
+
logger,
|
|
200
|
+
setTokenReady,
|
|
201
|
+
queryClient
|
|
202
|
+
});
|
|
130
203
|
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
204
|
+
// Get current user from query (no persistent cache - always fetch fresh)
|
|
205
|
+
// Services never caches profile - always fetch from backend
|
|
206
|
+
const {
|
|
207
|
+
data: userData
|
|
208
|
+
} = useCurrentUser({
|
|
209
|
+
enabled: isAuthenticated && !!activeSessionId
|
|
210
|
+
});
|
|
211
|
+
const user = userData ?? null;
|
|
212
|
+
const {
|
|
213
|
+
createIdentity,
|
|
214
|
+
importIdentity: importIdentityBase,
|
|
215
|
+
signIn,
|
|
216
|
+
logout,
|
|
217
|
+
logoutAll,
|
|
218
|
+
hasIdentity,
|
|
219
|
+
getPublicKey,
|
|
220
|
+
isIdentitySynced,
|
|
221
|
+
syncIdentity: syncIdentityBase
|
|
222
|
+
} = useAuthOperations({
|
|
223
|
+
oxyServices,
|
|
224
|
+
storage,
|
|
225
|
+
sessions,
|
|
226
|
+
activeSessionId,
|
|
227
|
+
setActiveSessionId,
|
|
228
|
+
updateSessions,
|
|
229
|
+
saveActiveSessionId,
|
|
230
|
+
clearSessionState,
|
|
231
|
+
switchSession,
|
|
232
|
+
applyLanguagePreference,
|
|
233
|
+
onAuthStateChange,
|
|
234
|
+
onError,
|
|
235
|
+
loginSuccess,
|
|
236
|
+
loginFailure,
|
|
237
|
+
logoutStore,
|
|
238
|
+
setAuthState,
|
|
239
|
+
setIdentitySynced,
|
|
240
|
+
setSyncing,
|
|
241
|
+
logger
|
|
242
|
+
});
|
|
167
243
|
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
if (!identityCore || !isCoreInitialized) return;
|
|
171
|
-
const unsubscribe = identityCore.subscribe(event => {
|
|
172
|
-
if (event.type === 'session:created' || event.type === 'session:refreshed' || event.type === 'session:invalidated' || event.type === 'session:all-invalidated') {
|
|
173
|
-
loadSessionsFromCore();
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
return unsubscribe;
|
|
177
|
-
}, [identityCore, isCoreInitialized, loadSessionsFromCore]);
|
|
178
|
-
|
|
179
|
-
// Identity operations using core
|
|
180
|
-
const hasIdentity = useCallback(async () => {
|
|
181
|
-
if (!identityCore || !isCoreInitialized) return false;
|
|
182
|
-
return await identityCore.hasIdentity();
|
|
183
|
-
}, [identityCore, isCoreInitialized]);
|
|
184
|
-
const getPublicKey = useCallback(async () => {
|
|
185
|
-
if (!identityCore || !isCoreInitialized) return null;
|
|
186
|
-
return await identityCore.getPublicKey();
|
|
187
|
-
}, [identityCore, isCoreInitialized]);
|
|
188
|
-
const isIdentitySynced = useCallback(async () => {
|
|
189
|
-
if (!storage) return false;
|
|
190
|
-
const synced = await storage.getItem('oxy_identity_synced');
|
|
191
|
-
return synced === 'true';
|
|
192
|
-
}, [storage]);
|
|
193
|
-
const createIdentity = useCallback(async username => {
|
|
194
|
-
if (!identityCore || !isCoreInitialized) {
|
|
195
|
-
throw new Error('Identity core not initialized');
|
|
196
|
-
}
|
|
197
|
-
setSyncing(true);
|
|
198
|
-
try {
|
|
199
|
-
// Create identity using core
|
|
200
|
-
const identity = await identityCore.createIdentity(username);
|
|
244
|
+
// syncIdentity - TanStack Query handles offline mutations automatically
|
|
245
|
+
const syncIdentity = useCallback(() => syncIdentityBase(), [syncIdentityBase]);
|
|
201
246
|
|
|
202
|
-
|
|
203
|
-
let synced = false;
|
|
204
|
-
try {
|
|
205
|
-
const {
|
|
206
|
-
signature,
|
|
207
|
-
publicKey,
|
|
208
|
-
timestamp
|
|
209
|
-
} = await identityCore.createRegistrationSignature();
|
|
210
|
-
const result = await oxyServices.register(publicKey, signature, timestamp, username);
|
|
211
|
-
if (result?.user) {
|
|
212
|
-
loginSuccess(result.user);
|
|
213
|
-
synced = true;
|
|
214
|
-
await storage?.setItem('oxy_identity_synced', 'true');
|
|
215
|
-
setIdentitySynced(true);
|
|
216
|
-
}
|
|
217
|
-
} catch (error) {
|
|
218
|
-
// Registration failed (likely offline) - identity still created locally
|
|
219
|
-
if (__DEV__) {
|
|
220
|
-
logger('Failed to register identity with backend (offline mode)', error);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
return {
|
|
224
|
-
synced
|
|
225
|
-
};
|
|
226
|
-
} finally {
|
|
227
|
-
setSyncing(false);
|
|
228
|
-
}
|
|
229
|
-
}, [identityCore, isCoreInitialized, oxyServices, storage, loginSuccess, setIdentitySynced, setSyncing, logger]);
|
|
247
|
+
// Wrapper for importIdentity to handle legacy calls gracefully
|
|
230
248
|
const importIdentity = useCallback(async (backupData, password) => {
|
|
231
|
-
|
|
232
|
-
throw new Error('Identity core not initialized');
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Handle legacy calls with single string argument
|
|
249
|
+
// Handle legacy calls with single string argument (old recovery phrase signature)
|
|
236
250
|
if (typeof backupData === 'string') {
|
|
237
251
|
throw new Error('Recovery phrase import is no longer supported. Please use backup file import or QR code transfer instead.');
|
|
238
252
|
}
|
|
@@ -241,279 +255,63 @@ export const OxyProvider = ({
|
|
|
241
255
|
if (!password || typeof password !== 'string') {
|
|
242
256
|
throw new Error('Password is required for backup file import.');
|
|
243
257
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
// Import identity using core
|
|
247
|
-
const identity = await identityCore.importIdentity(backupData, password);
|
|
248
|
-
|
|
249
|
-
// Try to register with backend if not already registered
|
|
250
|
-
let synced = false;
|
|
251
|
-
try {
|
|
252
|
-
const {
|
|
253
|
-
signature,
|
|
254
|
-
publicKey,
|
|
255
|
-
timestamp
|
|
256
|
-
} = await identityCore.createRegistrationSignature();
|
|
257
|
-
const result = await oxyServices.register(publicKey, signature, timestamp);
|
|
258
|
-
if (result?.user) {
|
|
259
|
-
loginSuccess(result.user);
|
|
260
|
-
synced = true;
|
|
261
|
-
await storage?.setItem('oxy_identity_synced', 'true');
|
|
262
|
-
setIdentitySynced(true);
|
|
263
|
-
}
|
|
264
|
-
} catch (error) {
|
|
265
|
-
// Check if user already exists (409 conflict)
|
|
266
|
-
if (error?.status === 409) {
|
|
267
|
-
// User already registered - try to sign in instead
|
|
268
|
-
try {
|
|
269
|
-
await signIn();
|
|
270
|
-
synced = true;
|
|
271
|
-
await storage?.setItem('oxy_identity_synced', 'true');
|
|
272
|
-
setIdentitySynced(true);
|
|
273
|
-
} catch (signInError) {
|
|
274
|
-
if (__DEV__) {
|
|
275
|
-
logger('Failed to sign in after import', signInError);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
} else if (__DEV__) {
|
|
279
|
-
logger('Failed to register identity with backend (offline mode)', error);
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return {
|
|
283
|
-
synced
|
|
284
|
-
};
|
|
285
|
-
} finally {
|
|
286
|
-
setSyncing(false);
|
|
287
|
-
}
|
|
288
|
-
}, [identityCore, isCoreInitialized, oxyServices, storage, loginSuccess, setIdentitySynced, setSyncing, logger]);
|
|
289
|
-
const syncIdentity = useCallback(async username => {
|
|
290
|
-
if (!identityCore || !isCoreInitialized) {
|
|
291
|
-
throw new Error('Identity core not initialized');
|
|
292
|
-
}
|
|
293
|
-
const publicKey = await identityCore.getPublicKey();
|
|
294
|
-
if (!publicKey) {
|
|
295
|
-
throw new Error('No identity found');
|
|
296
|
-
}
|
|
297
|
-
setSyncing(true);
|
|
298
|
-
try {
|
|
299
|
-
const {
|
|
300
|
-
signature,
|
|
301
|
-
publicKey: pk,
|
|
302
|
-
timestamp
|
|
303
|
-
} = await identityCore.createRegistrationSignature();
|
|
304
|
-
const result = await oxyServices.register(pk, signature, timestamp, username);
|
|
305
|
-
if (result?.user) {
|
|
306
|
-
loginSuccess(result.user);
|
|
307
|
-
await storage?.setItem('oxy_identity_synced', 'true');
|
|
308
|
-
setIdentitySynced(true);
|
|
309
|
-
return result.user;
|
|
310
|
-
}
|
|
311
|
-
throw new Error('Registration failed');
|
|
312
|
-
} catch (error) {
|
|
313
|
-
// Check if user already exists (409 conflict) - try to sign in
|
|
314
|
-
if (error?.status === 409) {
|
|
315
|
-
const user = await signIn();
|
|
316
|
-
await storage?.setItem('oxy_identity_synced', 'true');
|
|
317
|
-
setIdentitySynced(true);
|
|
318
|
-
return user;
|
|
319
|
-
}
|
|
320
|
-
throw error;
|
|
321
|
-
} finally {
|
|
322
|
-
setSyncing(false);
|
|
323
|
-
}
|
|
324
|
-
}, [identityCore, isCoreInitialized, oxyServices, storage, loginSuccess, setIdentitySynced, setSyncing]);
|
|
325
|
-
const signIn = useCallback(async deviceName => {
|
|
326
|
-
if (!identityCore || !isCoreInitialized) {
|
|
327
|
-
throw new Error('Identity core not initialized');
|
|
328
|
-
}
|
|
329
|
-
setAuthState({
|
|
330
|
-
isLoading: true
|
|
331
|
-
});
|
|
332
|
-
try {
|
|
333
|
-
// Create session using core
|
|
334
|
-
const session = await identityCore.createSession(deviceName);
|
|
335
|
-
|
|
336
|
-
// Get user from OxyServices
|
|
337
|
-
const user = await oxyServices.getUserBySession(session.sessionId);
|
|
338
|
-
if (!user) {
|
|
339
|
-
throw new Error('Failed to get user data');
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// Set token in OxyServices
|
|
343
|
-
if (session.accessToken) {
|
|
344
|
-
oxyServices.setTokens(session.accessToken, session.refreshToken || undefined);
|
|
345
|
-
}
|
|
258
|
+
return importIdentityBase(backupData, password);
|
|
259
|
+
}, [importIdentityBase]);
|
|
346
260
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
await applyLanguagePreference(user);
|
|
352
|
-
setTokenReady(true);
|
|
353
|
-
return user;
|
|
354
|
-
} catch (error) {
|
|
355
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
356
|
-
loginFailure(errorMessage);
|
|
357
|
-
throw error;
|
|
358
|
-
} finally {
|
|
359
|
-
setAuthState({
|
|
360
|
-
isLoading: false
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
}, [identityCore, isCoreInitialized, oxyServices, loginSuccess, loginFailure, setAuthState, loadSessionsFromCore, onAuthStateChange, applyLanguagePreference, setTokenReady]);
|
|
364
|
-
|
|
365
|
-
// Session management functions
|
|
366
|
-
const clearSessionState = useCallback(async () => {
|
|
367
|
-
setSessions([]);
|
|
368
|
-
setActiveSessionId(null);
|
|
369
|
-
logoutStore();
|
|
370
|
-
onAuthStateChange?.(null);
|
|
371
|
-
|
|
372
|
-
// Clear TanStack Query cache
|
|
261
|
+
// Clear all account data when identity is lost (for accounts app)
|
|
262
|
+
// In accounts app, identity = account, so losing identity means losing everything
|
|
263
|
+
const clearAllAccountData = useCallback(async () => {
|
|
264
|
+
// Clear TanStack Query cache (in-memory)
|
|
373
265
|
queryClient.clear();
|
|
374
266
|
|
|
375
|
-
// Clear persisted query cache
|
|
267
|
+
// Clear persisted query cache
|
|
376
268
|
if (storage) {
|
|
377
269
|
try {
|
|
378
270
|
await clearQueryCache(storage);
|
|
379
|
-
await storage.removeItem(storageKeys.activeSessionId);
|
|
380
|
-
await storage.removeItem(storageKeys.sessionIds);
|
|
381
|
-
await storage.removeItem('oxy_identity_synced').catch(() => {});
|
|
382
271
|
} catch (error) {
|
|
383
|
-
|
|
384
|
-
logger('Failed to clear session storage', error);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
}, [logoutStore, onAuthStateChange, queryClient, storage, storageKeys, logger]);
|
|
389
|
-
const logout = useCallback(async targetSessionId => {
|
|
390
|
-
if (!identityCore || !isCoreInitialized) return;
|
|
391
|
-
try {
|
|
392
|
-
await identityCore.invalidateSession(targetSessionId);
|
|
393
|
-
await loadSessionsFromCore();
|
|
394
|
-
await clearSessionState();
|
|
395
|
-
setTokenReady(false);
|
|
396
|
-
} catch (error) {
|
|
397
|
-
if (__DEV__) {
|
|
398
|
-
logger('Failed to logout', error);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}, [identityCore, isCoreInitialized, loadSessionsFromCore, clearSessionState, setTokenReady, logger]);
|
|
402
|
-
const logoutAll = useCallback(async () => {
|
|
403
|
-
if (!identityCore || !isCoreInitialized) return;
|
|
404
|
-
try {
|
|
405
|
-
await identityCore.invalidateAllSessions();
|
|
406
|
-
await loadSessionsFromCore();
|
|
407
|
-
await clearSessionState();
|
|
408
|
-
setTokenReady(false);
|
|
409
|
-
} catch (error) {
|
|
410
|
-
if (__DEV__) {
|
|
411
|
-
logger('Failed to logout all', error);
|
|
272
|
+
logger('Failed to clear persisted query cache', error);
|
|
412
273
|
}
|
|
413
274
|
}
|
|
414
|
-
}, [identityCore, isCoreInitialized, loadSessionsFromCore, clearSessionState, setTokenReady, logger]);
|
|
415
|
-
const switchSession = useCallback(async sessionId => {
|
|
416
|
-
if (!identityCore || !isCoreInitialized) {
|
|
417
|
-
throw new Error('Identity core not initialized');
|
|
418
|
-
}
|
|
419
|
-
try {
|
|
420
|
-
// Validate session
|
|
421
|
-
const validation = await oxyServices.validateSession(sessionId, {
|
|
422
|
-
useHeaderValidation: true
|
|
423
|
-
});
|
|
424
|
-
if (!validation?.valid || !validation.user) {
|
|
425
|
-
throw new Error('Session is invalid or expired');
|
|
426
|
-
}
|
|
427
|
-
const user = validation.user;
|
|
428
275
|
|
|
429
|
-
|
|
430
|
-
await oxyServices.getTokenBySession(sessionId);
|
|
431
|
-
|
|
432
|
-
// Update active session in core
|
|
433
|
-
const sessionManager = identityCore.getSessionManager();
|
|
434
|
-
await sessionManager.setActiveSessionId(sessionId);
|
|
435
|
-
|
|
436
|
-
// Update local state
|
|
437
|
-
setActiveSessionId(sessionId);
|
|
438
|
-
loginSuccess(user);
|
|
439
|
-
await applyLanguagePreference(user);
|
|
440
|
-
onAuthStateChange?.(user);
|
|
441
|
-
setTokenReady(true);
|
|
442
|
-
await loadSessionsFromCore();
|
|
443
|
-
return user;
|
|
444
|
-
} catch (error) {
|
|
445
|
-
if (isInvalidSessionError(error)) {
|
|
446
|
-
// Remove invalid session from list
|
|
447
|
-
setSessions(prev => prev.filter(s => s.sessionId !== sessionId));
|
|
448
|
-
if (sessionId === activeSessionId) {
|
|
449
|
-
await clearSessionState();
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
throw error;
|
|
453
|
-
}
|
|
454
|
-
}, [identityCore, isCoreInitialized, oxyServices, loginSuccess, applyLanguagePreference, onAuthStateChange, setTokenReady, loadSessionsFromCore, activeSessionId, clearSessionState]);
|
|
455
|
-
const refreshSessions = useCallback(async () => {
|
|
456
|
-
if (!identityCore || !isCoreInitialized || !activeSessionId) return;
|
|
457
|
-
try {
|
|
458
|
-
// Refresh current session
|
|
459
|
-
await identityCore.refreshSession();
|
|
460
|
-
|
|
461
|
-
// Reload sessions from core
|
|
462
|
-
await loadSessionsFromCore();
|
|
463
|
-
} catch (error) {
|
|
464
|
-
if (isInvalidSessionError(error)) {
|
|
465
|
-
await clearSessionState();
|
|
466
|
-
} else if (__DEV__) {
|
|
467
|
-
logger('Failed to refresh sessions', error);
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
}, [identityCore, isCoreInitialized, activeSessionId, loadSessionsFromCore, clearSessionState, logger]);
|
|
471
|
-
|
|
472
|
-
// Device management functions
|
|
473
|
-
const getDeviceSessions = useCallback(async () => {
|
|
474
|
-
if (!activeSessionId) throw new Error('No active session');
|
|
475
|
-
return await oxyServices.getDeviceSessions(activeSessionId);
|
|
476
|
-
}, [activeSessionId, oxyServices]);
|
|
477
|
-
const logoutAllDeviceSessions = useCallback(async () => {
|
|
478
|
-
if (!activeSessionId) throw new Error('No active session');
|
|
479
|
-
await oxyServices.logoutAllDeviceSessions(activeSessionId);
|
|
276
|
+
// Clear session state (sessions, activeSessionId, storage)
|
|
480
277
|
await clearSessionState();
|
|
481
|
-
}, [activeSessionId, oxyServices, clearSessionState]);
|
|
482
|
-
const updateDeviceName = useCallback(async deviceName => {
|
|
483
|
-
if (!identityCore || !isCoreInitialized || !activeSessionId) {
|
|
484
|
-
throw new Error('Identity core not initialized or no active session');
|
|
485
|
-
}
|
|
486
|
-
await oxyServices.updateDeviceName(activeSessionId, deviceName);
|
|
487
278
|
|
|
488
|
-
//
|
|
489
|
-
const deviceManager = identityCore.getDeviceManager();
|
|
490
|
-
await deviceManager.updateDeviceName(deviceName);
|
|
491
|
-
}, [identityCore, isCoreInitialized, activeSessionId, oxyServices]);
|
|
492
|
-
useTransferCodesPersistence(storageKeyPrefix);
|
|
493
|
-
const clearAllAccountData = useCallback(async () => {
|
|
494
|
-
if (__DEV__) logger('Clearing all account data - identity changed or lost');
|
|
495
|
-
queryClient.clear();
|
|
496
|
-
await clearSessionState();
|
|
279
|
+
// Clear identity sync state from storage
|
|
497
280
|
if (storage) {
|
|
498
281
|
try {
|
|
499
|
-
await clearQueryCache(storage);
|
|
500
282
|
await storage.removeItem('oxy_identity_synced');
|
|
501
283
|
} catch (error) {
|
|
502
|
-
logger('Failed to clear
|
|
284
|
+
logger('Failed to clear identity sync state', error);
|
|
503
285
|
}
|
|
504
286
|
}
|
|
287
|
+
|
|
288
|
+
// Reset auth store identity sync state
|
|
505
289
|
useAuthStore.getState().setIdentitySynced(false);
|
|
506
290
|
useAuthStore.getState().setSyncing(false);
|
|
291
|
+
|
|
292
|
+
// Reset account store
|
|
507
293
|
useAccountStore.getState().reset();
|
|
508
|
-
|
|
294
|
+
|
|
295
|
+
// Clear HTTP service cache
|
|
509
296
|
oxyServices.clearCache();
|
|
510
|
-
|
|
511
|
-
}, [queryClient, storage, clearSessionState, logger, oxyServices, identityCore]);
|
|
297
|
+
}, [queryClient, storage, clearSessionState, logger, oxyServices]);
|
|
512
298
|
|
|
513
|
-
//
|
|
514
|
-
const getAllPendingTransfers =
|
|
515
|
-
|
|
516
|
-
|
|
299
|
+
// Transfer code management functions (must be defined before deleteIdentityAndClearAccount)
|
|
300
|
+
const getAllPendingTransfers = useCallback(() => {
|
|
301
|
+
const pending = [];
|
|
302
|
+
transferCodesRef.current.forEach((data, transferId) => {
|
|
303
|
+
if (data.state === 'pending') {
|
|
304
|
+
pending.push({
|
|
305
|
+
transferId,
|
|
306
|
+
data
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
return pending;
|
|
311
|
+
}, []);
|
|
312
|
+
const getActiveTransferId = useCallback(() => {
|
|
313
|
+
return activeTransferIdRef.current;
|
|
314
|
+
}, []);
|
|
517
315
|
|
|
518
316
|
// Delete identity and clear all account data
|
|
519
317
|
// In accounts app, deleting identity means losing the account completely
|
|
@@ -535,112 +333,636 @@ export const OxyProvider = ({
|
|
|
535
333
|
await clearAllAccountData();
|
|
536
334
|
|
|
537
335
|
// Then delete the identity keys
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
336
|
+
await KeyManager.deleteIdentity(skipBackup, force, userConfirmed);
|
|
337
|
+
}, [clearAllAccountData, getAllPendingTransfers, getActiveTransferId]);
|
|
338
|
+
|
|
339
|
+
// Network reconnect sync - TanStack Query automatically retries mutations on reconnect
|
|
340
|
+
// We only need to sync identity if it's not synced
|
|
341
|
+
useEffect(() => {
|
|
342
|
+
if (!storage) return;
|
|
343
|
+
let wasOffline = false;
|
|
344
|
+
let checkTimeout = null;
|
|
345
|
+
let lastReconnectionLog = 0;
|
|
346
|
+
const RECONNECTION_LOG_DEBOUNCE_MS = 5000; // 5 seconds
|
|
347
|
+
|
|
348
|
+
// Circuit breaker and exponential backoff state
|
|
349
|
+
const stateRef = {
|
|
350
|
+
consecutiveFailures: 0,
|
|
351
|
+
currentInterval: 10000,
|
|
352
|
+
// Start with 10 seconds
|
|
353
|
+
baseInterval: 10000,
|
|
354
|
+
// Base interval in milliseconds
|
|
355
|
+
maxInterval: 60000,
|
|
356
|
+
// Maximum interval (60 seconds)
|
|
357
|
+
maxFailures: 5 // Circuit breaker threshold
|
|
358
|
+
};
|
|
359
|
+
const scheduleNextCheck = () => {
|
|
360
|
+
if (checkTimeout) {
|
|
361
|
+
clearTimeout(checkTimeout);
|
|
362
|
+
}
|
|
363
|
+
checkTimeout = setTimeout(() => {
|
|
364
|
+
checkNetworkAndSync();
|
|
365
|
+
}, stateRef.currentInterval);
|
|
366
|
+
};
|
|
367
|
+
const checkNetworkAndSync = async () => {
|
|
368
|
+
try {
|
|
369
|
+
// Try a lightweight health check to see if we're online
|
|
370
|
+
await oxyServices.healthCheck().catch(() => {
|
|
371
|
+
wasOffline = true;
|
|
372
|
+
throw new Error('Health check failed');
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// Health check succeeded - reset circuit breaker and backoff
|
|
376
|
+
if (stateRef.consecutiveFailures > 0) {
|
|
377
|
+
stateRef.consecutiveFailures = 0;
|
|
378
|
+
stateRef.currentInterval = stateRef.baseInterval;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// If we were offline and now we're online, sync identity if needed
|
|
382
|
+
if (wasOffline) {
|
|
383
|
+
const now = Date.now();
|
|
384
|
+
const timeSinceLastLog = now - lastReconnectionLog;
|
|
385
|
+
if (timeSinceLastLog >= RECONNECTION_LOG_DEBOUNCE_MS) {
|
|
386
|
+
logger('Network reconnected, checking identity sync...');
|
|
387
|
+
lastReconnectionLog = now;
|
|
388
|
+
|
|
389
|
+
// Sync identity first (if not synced)
|
|
390
|
+
try {
|
|
391
|
+
const hasIdentityValue = await hasIdentity();
|
|
392
|
+
if (hasIdentityValue) {
|
|
393
|
+
// Check sync status directly - sync if not explicitly 'true'
|
|
394
|
+
// undefined = not synced yet, 'false' = explicitly not synced, 'true' = synced
|
|
395
|
+
const syncStatus = await storage.getItem('oxy_identity_synced');
|
|
396
|
+
if (syncStatus !== 'true') {
|
|
397
|
+
await syncIdentity();
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
} catch (syncError) {
|
|
401
|
+
// Skip sync silently if username is required (expected when offline onboarding)
|
|
402
|
+
if (syncError?.code === 'USERNAME_REQUIRED' || syncError?.message === 'USERNAME_REQUIRED') {
|
|
403
|
+
if (__DEV__) {
|
|
404
|
+
loggerUtil.debug('Sync skipped - username required', {
|
|
405
|
+
component: 'OxyContext',
|
|
406
|
+
method: 'checkNetworkAndSync'
|
|
407
|
+
}, syncError);
|
|
408
|
+
}
|
|
409
|
+
// Don't log or show error - username will be set later
|
|
410
|
+
} else if (!isTimeoutOrNetworkError(syncError)) {
|
|
411
|
+
// Only log unexpected errors - timeouts/network issues are expected when offline
|
|
412
|
+
logger('Error syncing identity on reconnect', syncError);
|
|
413
|
+
} else if (__DEV__) {
|
|
414
|
+
loggerUtil.debug('Identity sync timeout (expected when offline)', {
|
|
415
|
+
component: 'OxyContext',
|
|
416
|
+
method: 'checkNetworkAndSync'
|
|
417
|
+
}, syncError);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// TanStack Query will automatically retry pending mutations
|
|
423
|
+
// Reset flag immediately after processing (whether logged or not)
|
|
424
|
+
wasOffline = false;
|
|
425
|
+
}
|
|
426
|
+
} catch (error) {
|
|
427
|
+
// Network check failed - we're offline
|
|
428
|
+
wasOffline = true;
|
|
429
|
+
|
|
430
|
+
// Increment failure count and apply exponential backoff
|
|
431
|
+
stateRef.consecutiveFailures++;
|
|
432
|
+
|
|
433
|
+
// Calculate new interval with exponential backoff, capped at maxInterval
|
|
434
|
+
const backoffMultiplier = Math.min(Math.pow(2, stateRef.consecutiveFailures - 1), stateRef.maxInterval / stateRef.baseInterval);
|
|
435
|
+
stateRef.currentInterval = Math.min(stateRef.baseInterval * backoffMultiplier, stateRef.maxInterval);
|
|
436
|
+
|
|
437
|
+
// If we hit the circuit breaker threshold, use max interval
|
|
438
|
+
if (stateRef.consecutiveFailures >= stateRef.maxFailures) {
|
|
439
|
+
stateRef.currentInterval = stateRef.maxInterval;
|
|
440
|
+
}
|
|
441
|
+
} finally {
|
|
442
|
+
// Always schedule next check (will use updated interval)
|
|
443
|
+
scheduleNextCheck();
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
// Check immediately
|
|
448
|
+
checkNetworkAndSync();
|
|
449
|
+
return () => {
|
|
450
|
+
if (checkTimeout) {
|
|
451
|
+
clearTimeout(checkTimeout);
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
}, [oxyServices, storage, syncIdentity, logger]);
|
|
455
|
+
const {
|
|
456
|
+
getDeviceSessions,
|
|
457
|
+
logoutAllDeviceSessions,
|
|
458
|
+
updateDeviceName
|
|
459
|
+
} = useDeviceManagement({
|
|
460
|
+
oxyServices,
|
|
461
|
+
activeSessionId,
|
|
462
|
+
onError,
|
|
463
|
+
clearSessionState,
|
|
464
|
+
logger
|
|
465
|
+
});
|
|
466
|
+
const useFollowHook = loadUseFollowHook();
|
|
544
467
|
const restoreSessionsFromStorage = useCallback(async () => {
|
|
545
|
-
if (!
|
|
468
|
+
if (!storage) {
|
|
469
|
+
return;
|
|
470
|
+
}
|
|
546
471
|
setTokenReady(false);
|
|
547
472
|
try {
|
|
548
|
-
await
|
|
549
|
-
const
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
try {
|
|
555
|
-
await switchSession(activeId);
|
|
556
|
-
} catch (switchError) {
|
|
557
|
-
if (isTimeoutOrNetworkError(switchError)) {
|
|
473
|
+
const storedSessionIdsJson = await storage.getItem(storageKeys.sessionIds);
|
|
474
|
+
const storedSessionIds = storedSessionIdsJson ? JSON.parse(storedSessionIdsJson) : [];
|
|
475
|
+
const storedActiveSessionId = await storage.getItem(storageKeys.activeSessionId);
|
|
476
|
+
const validSessions = [];
|
|
477
|
+
if (storedSessionIds.length > 0) {
|
|
478
|
+
for (const sessionId of storedSessionIds) {
|
|
558
479
|
try {
|
|
559
|
-
const validation = await oxyServices.validateSession(
|
|
560
|
-
useHeaderValidation:
|
|
480
|
+
const validation = await oxyServices.validateSession(sessionId, {
|
|
481
|
+
useHeaderValidation: true
|
|
561
482
|
});
|
|
562
483
|
if (validation?.valid && validation.user) {
|
|
563
|
-
|
|
484
|
+
const now = new Date();
|
|
485
|
+
validSessions.push({
|
|
486
|
+
sessionId,
|
|
487
|
+
deviceId: '',
|
|
488
|
+
expiresAt: new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
489
|
+
lastActive: now.toISOString(),
|
|
490
|
+
userId: validation.user.id?.toString() ?? '',
|
|
491
|
+
isCurrent: sessionId === storedActiveSessionId
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
} catch (validationError) {
|
|
495
|
+
// Silently handle expected errors (invalid sessions, timeouts, network issues) during restoration
|
|
496
|
+
// Only log unexpected errors
|
|
497
|
+
if (!isInvalidSessionError(validationError) && !isTimeoutOrNetworkError(validationError)) {
|
|
498
|
+
logger('Session validation failed during init', validationError);
|
|
499
|
+
} else if (__DEV__ && isTimeoutOrNetworkError(validationError)) {
|
|
500
|
+
// Only log timeouts in dev mode for debugging
|
|
501
|
+
loggerUtil.debug('Session validation timeout (expected when offline)', {
|
|
502
|
+
component: 'OxyContext',
|
|
503
|
+
method: 'restoreSessionsFromStorage'
|
|
504
|
+
}, validationError);
|
|
564
505
|
}
|
|
565
|
-
} catch {
|
|
566
|
-
// Offline - will sync when online
|
|
567
506
|
}
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
|
|
507
|
+
}
|
|
508
|
+
if (validSessions.length > 0) {
|
|
509
|
+
updateSessions(validSessions, {
|
|
510
|
+
merge: false
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
if (storedActiveSessionId) {
|
|
515
|
+
try {
|
|
516
|
+
await switchSession(storedActiveSessionId);
|
|
517
|
+
} catch (switchError) {
|
|
518
|
+
// Silently handle expected errors (invalid sessions, timeouts, network issues)
|
|
519
|
+
if (isInvalidSessionError(switchError)) {
|
|
520
|
+
await storage.removeItem(storageKeys.activeSessionId);
|
|
521
|
+
updateSessions(validSessions.filter(session => session.sessionId !== storedActiveSessionId), {
|
|
522
|
+
merge: false
|
|
523
|
+
});
|
|
524
|
+
// Don't log expected session errors during restoration
|
|
525
|
+
} else if (isTimeoutOrNetworkError(switchError)) {
|
|
526
|
+
// Timeout/network error - non-critical, don't block
|
|
527
|
+
if (__DEV__) {
|
|
528
|
+
loggerUtil.debug('Active session validation timeout (expected when offline)', {
|
|
529
|
+
component: 'OxyContext',
|
|
530
|
+
method: 'restoreSessionsFromStorage'
|
|
531
|
+
}, switchError);
|
|
532
|
+
}
|
|
533
|
+
} else {
|
|
534
|
+
// Only log unexpected errors
|
|
535
|
+
logger('Active session validation error', switchError);
|
|
536
|
+
}
|
|
571
537
|
}
|
|
572
538
|
}
|
|
573
539
|
} catch (error) {
|
|
574
|
-
if (__DEV__)
|
|
540
|
+
if (__DEV__) {
|
|
541
|
+
loggerUtil.error('Auth init error', error instanceof Error ? error : new Error(String(error)), {
|
|
542
|
+
component: 'OxyContext',
|
|
543
|
+
method: 'restoreSessionsFromStorage'
|
|
544
|
+
});
|
|
545
|
+
}
|
|
575
546
|
await clearSessionState();
|
|
576
547
|
} finally {
|
|
577
548
|
setTokenReady(true);
|
|
578
549
|
}
|
|
579
|
-
}, [
|
|
580
|
-
|
|
581
|
-
// Restore sessions when core is initialized
|
|
550
|
+
}, [clearSessionState, logger, oxyServices, storage, storageKeys.activeSessionId, storageKeys.sessionIds, switchSession, updateSessions]);
|
|
582
551
|
useEffect(() => {
|
|
583
|
-
if (
|
|
584
|
-
|
|
552
|
+
if (!storage || initializedRef.current) {
|
|
553
|
+
return;
|
|
585
554
|
}
|
|
586
|
-
|
|
555
|
+
initializedRef.current = true;
|
|
556
|
+
void restoreSessionsFromStorage();
|
|
557
|
+
}, [restoreSessionsFromStorage, storage]);
|
|
587
558
|
const activeSession = activeSessionId ? sessions.find(session => session.sessionId === activeSessionId) : undefined;
|
|
588
559
|
const currentDeviceId = activeSession?.deviceId ?? null;
|
|
589
560
|
|
|
590
|
-
//
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
561
|
+
// Get userId from JWT token (MongoDB ObjectId) for socket room matching
|
|
562
|
+
const userId = oxyServices.getCurrentUserId();
|
|
563
|
+
|
|
564
|
+
// Transfer code storage interface
|
|
565
|
+
|
|
566
|
+
// Store transfer codes in memory for verification (also persisted to storage)
|
|
567
|
+
// Map: transferId -> TransferCodeData
|
|
568
|
+
const transferCodesRef = useRef(new Map());
|
|
569
|
+
const activeTransferIdRef = useRef(null);
|
|
570
|
+
const TRANSFER_CODES_STORAGE_KEY = `${storageKeyPrefix}_transfer_codes`;
|
|
571
|
+
const ACTIVE_TRANSFER_STORAGE_KEY = `${storageKeyPrefix}_active_transfer_id`;
|
|
572
|
+
|
|
573
|
+
// Load transfer codes from storage on startup
|
|
574
|
+
useEffect(() => {
|
|
575
|
+
if (!storage || !isStorageReady) return;
|
|
576
|
+
const loadTransferCodes = async () => {
|
|
577
|
+
try {
|
|
578
|
+
// Load transfer codes
|
|
579
|
+
const storedCodes = await storage.getItem(TRANSFER_CODES_STORAGE_KEY);
|
|
580
|
+
if (storedCodes) {
|
|
581
|
+
const parsed = JSON.parse(storedCodes);
|
|
582
|
+
const now = Date.now();
|
|
583
|
+
const fifteenMinutes = 15 * 60 * 1000;
|
|
584
|
+
|
|
585
|
+
// Only restore non-expired pending transfers
|
|
586
|
+
Object.entries(parsed).forEach(([transferId, data]) => {
|
|
587
|
+
if (data.state === 'pending' && now - data.timestamp < fifteenMinutes) {
|
|
588
|
+
transferCodesRef.current.set(transferId, data);
|
|
589
|
+
if (__DEV__) {
|
|
590
|
+
logger('Restored pending transfer from storage', {
|
|
591
|
+
transferId
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// Load active transfer ID
|
|
599
|
+
const activeTransferId = await storage.getItem(ACTIVE_TRANSFER_STORAGE_KEY);
|
|
600
|
+
if (activeTransferId) {
|
|
601
|
+
// Verify it's still valid
|
|
602
|
+
const transferData = transferCodesRef.current.get(activeTransferId);
|
|
603
|
+
if (transferData && transferData.state === 'pending') {
|
|
604
|
+
activeTransferIdRef.current = activeTransferId;
|
|
605
|
+
if (__DEV__) {
|
|
606
|
+
logger('Restored active transfer ID from storage', {
|
|
607
|
+
transferId: activeTransferId
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
} else {
|
|
611
|
+
// Clear invalid active transfer
|
|
612
|
+
await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
} catch (error) {
|
|
616
|
+
if (__DEV__) {
|
|
617
|
+
logger('Failed to load transfer codes from storage', error);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
};
|
|
621
|
+
loadTransferCodes();
|
|
622
|
+
}, [storage, isStorageReady, logger, storageKeyPrefix]);
|
|
623
|
+
|
|
624
|
+
// Persist transfer codes to storage whenever they change
|
|
625
|
+
const persistTransferCodes = useCallback(async () => {
|
|
626
|
+
if (!storage) return;
|
|
627
|
+
try {
|
|
628
|
+
const codesToStore = {};
|
|
629
|
+
transferCodesRef.current.forEach((value, key) => {
|
|
630
|
+
codesToStore[key] = value;
|
|
631
|
+
});
|
|
632
|
+
await storage.setItem(TRANSFER_CODES_STORAGE_KEY, JSON.stringify(codesToStore));
|
|
633
|
+
} catch (error) {
|
|
634
|
+
if (__DEV__) {
|
|
635
|
+
logger('Failed to persist transfer codes', error);
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}, [storage, logger]);
|
|
639
|
+
|
|
640
|
+
// Cleanup old transfer codes (older than 15 minutes)
|
|
641
|
+
useEffect(() => {
|
|
642
|
+
const cleanup = setInterval(async () => {
|
|
643
|
+
const now = Date.now();
|
|
644
|
+
const fifteenMinutes = 15 * 60 * 1000;
|
|
645
|
+
let needsPersist = false;
|
|
646
|
+
transferCodesRef.current.forEach((value, key) => {
|
|
647
|
+
if (now - value.timestamp > fifteenMinutes) {
|
|
648
|
+
transferCodesRef.current.delete(key);
|
|
649
|
+
needsPersist = true;
|
|
650
|
+
if (__DEV__) {
|
|
651
|
+
logger('Cleaned up expired transfer code', {
|
|
652
|
+
transferId: key
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
// Clear active transfer if it was deleted
|
|
659
|
+
if (activeTransferIdRef.current && !transferCodesRef.current.has(activeTransferIdRef.current)) {
|
|
660
|
+
activeTransferIdRef.current = null;
|
|
661
|
+
if (storage) {
|
|
662
|
+
try {
|
|
663
|
+
await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
|
|
664
|
+
} catch (error) {
|
|
665
|
+
// Ignore storage errors
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
if (needsPersist) {
|
|
670
|
+
await persistTransferCodes();
|
|
671
|
+
}
|
|
672
|
+
}, 60000); // Check every minute
|
|
673
|
+
|
|
674
|
+
return () => clearInterval(cleanup);
|
|
675
|
+
}, [logger, persistTransferCodes, storage]);
|
|
676
|
+
|
|
677
|
+
// Transfer code management functions
|
|
678
|
+
const storeTransferCode = useCallback(async (transferId, code, sourceDeviceId, publicKey) => {
|
|
679
|
+
const transferData = {
|
|
680
|
+
code,
|
|
681
|
+
sourceDeviceId,
|
|
682
|
+
publicKey,
|
|
683
|
+
timestamp: Date.now(),
|
|
684
|
+
state: 'pending'
|
|
685
|
+
};
|
|
686
|
+
transferCodesRef.current.set(transferId, transferData);
|
|
687
|
+
activeTransferIdRef.current = transferId;
|
|
688
|
+
|
|
689
|
+
// Persist to storage
|
|
690
|
+
await persistTransferCodes();
|
|
691
|
+
if (storage) {
|
|
692
|
+
try {
|
|
693
|
+
await storage.setItem(ACTIVE_TRANSFER_STORAGE_KEY, transferId);
|
|
694
|
+
} catch (error) {
|
|
695
|
+
if (__DEV__) {
|
|
696
|
+
logger('Failed to persist active transfer ID', error);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (__DEV__) {
|
|
701
|
+
logger('Stored transfer code', {
|
|
702
|
+
transferId,
|
|
703
|
+
sourceDeviceId,
|
|
704
|
+
publicKey: publicKey.substring(0, 16) + '...'
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
}, [logger, persistTransferCodes, storage]);
|
|
708
|
+
const getTransferCode = useCallback(transferId => {
|
|
709
|
+
return transferCodesRef.current.get(transferId) || null;
|
|
710
|
+
}, []);
|
|
711
|
+
const updateTransferState = useCallback(async (transferId, state) => {
|
|
712
|
+
const transferData = transferCodesRef.current.get(transferId);
|
|
713
|
+
if (transferData) {
|
|
714
|
+
transferData.state = state;
|
|
715
|
+
transferCodesRef.current.set(transferId, transferData);
|
|
716
|
+
|
|
717
|
+
// Clear active transfer if completed or failed
|
|
718
|
+
if (state === 'completed' || state === 'failed') {
|
|
719
|
+
if (activeTransferIdRef.current === transferId) {
|
|
720
|
+
activeTransferIdRef.current = null;
|
|
721
|
+
if (storage) {
|
|
722
|
+
try {
|
|
723
|
+
await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
|
|
724
|
+
} catch (error) {
|
|
725
|
+
// Ignore storage errors
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
await persistTransferCodes();
|
|
731
|
+
if (__DEV__) {
|
|
732
|
+
logger('Updated transfer state', {
|
|
733
|
+
transferId,
|
|
734
|
+
state
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
}, [logger, persistTransferCodes, storage]);
|
|
739
|
+
const clearTransferCode = useCallback(async transferId => {
|
|
740
|
+
transferCodesRef.current.delete(transferId);
|
|
741
|
+
if (activeTransferIdRef.current === transferId) {
|
|
742
|
+
activeTransferIdRef.current = null;
|
|
743
|
+
if (storage) {
|
|
744
|
+
try {
|
|
745
|
+
await storage.removeItem(ACTIVE_TRANSFER_STORAGE_KEY);
|
|
746
|
+
} catch (error) {
|
|
747
|
+
// Ignore storage errors
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
await persistTransferCodes();
|
|
752
|
+
if (__DEV__) {
|
|
753
|
+
logger('Cleared transfer code', {
|
|
754
|
+
transferId
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
}, [logger, persistTransferCodes, storage]);
|
|
758
|
+
const refreshSessionsWithUser = useCallback(() => refreshSessions(userId || undefined), [refreshSessions, userId]);
|
|
759
|
+
const handleSessionRemoved = useCallback(sessionId => {
|
|
760
|
+
trackRemovedSession(sessionId);
|
|
761
|
+
}, [trackRemovedSession]);
|
|
762
|
+
const handleRemoteSignOut = useCallback(() => {
|
|
763
|
+
toast.info('You have been signed out remotely.');
|
|
764
|
+
logout().catch(remoteError => logger('Failed to process remote sign out', remoteError));
|
|
765
|
+
}, [logger, logout]);
|
|
766
|
+
const handleIdentityTransferComplete = useCallback(async data => {
|
|
767
|
+
try {
|
|
768
|
+
logger('Received identity transfer complete notification', {
|
|
769
|
+
transferId: data.transferId,
|
|
770
|
+
sourceDeviceId: data.sourceDeviceId,
|
|
771
|
+
currentDeviceId,
|
|
772
|
+
hasActiveSession: activeSessionId !== null,
|
|
773
|
+
publicKey: data.publicKey.substring(0, 16) + '...'
|
|
774
|
+
});
|
|
775
|
+
const storedTransfer = getTransferCode(data.transferId);
|
|
776
|
+
if (!storedTransfer) {
|
|
777
|
+
logger('Transfer code not found for transferId', {
|
|
778
|
+
transferId: data.transferId
|
|
779
|
+
});
|
|
780
|
+
toast.error('Transfer verification failed: Code not found. Identity will not be deleted.');
|
|
781
|
+
return;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
// Verify publicKey matches first (most important check)
|
|
785
|
+
const publicKeyMatches = data.publicKey === storedTransfer.publicKey;
|
|
786
|
+
if (!publicKeyMatches) {
|
|
787
|
+
logger('Public key mismatch for transfer', {
|
|
788
|
+
transferId: data.transferId,
|
|
789
|
+
receivedPublicKey: data.publicKey.substring(0, 16) + '...',
|
|
790
|
+
storedPublicKey: storedTransfer.publicKey.substring(0, 16) + '...'
|
|
791
|
+
});
|
|
792
|
+
toast.error('Transfer verification failed: Public key mismatch. Identity will not be deleted.');
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// Verify deviceId matches - very lenient since publicKey is the critical check
|
|
797
|
+
// If publicKey matches, we allow deletion even if deviceId doesn't match exactly
|
|
798
|
+
// This handles cases where deviceId might not be available or slightly different
|
|
799
|
+
const deviceIdMatches =
|
|
800
|
+
// Exact match
|
|
801
|
+
data.sourceDeviceId && data.sourceDeviceId === currentDeviceId ||
|
|
802
|
+
// Stored sourceDeviceId matches current deviceId
|
|
803
|
+
storedTransfer.sourceDeviceId && storedTransfer.sourceDeviceId === currentDeviceId;
|
|
804
|
+
|
|
805
|
+
// If publicKey matches, we're very lenient with deviceId - only warn but don't block
|
|
806
|
+
if (!deviceIdMatches && publicKeyMatches) {
|
|
807
|
+
logger('Device ID mismatch for transfer, but publicKey matches - proceeding with deletion', {
|
|
808
|
+
transferId: data.transferId,
|
|
809
|
+
receivedDeviceId: data.sourceDeviceId,
|
|
810
|
+
storedDeviceId: storedTransfer.sourceDeviceId,
|
|
811
|
+
currentDeviceId,
|
|
812
|
+
hasActiveSession: activeSessionId !== null
|
|
813
|
+
});
|
|
814
|
+
// Proceed with deletion - publicKey match is the critical verification
|
|
815
|
+
} else if (!deviceIdMatches && !publicKeyMatches) {
|
|
816
|
+
// Both don't match - this is suspicious, block deletion
|
|
817
|
+
logger('Device ID and publicKey mismatch for transfer', {
|
|
818
|
+
transferId: data.transferId,
|
|
819
|
+
receivedDeviceId: data.sourceDeviceId,
|
|
820
|
+
currentDeviceId
|
|
821
|
+
});
|
|
822
|
+
toast.error('Transfer verification failed: Device and key mismatch. Identity will not be deleted.');
|
|
823
|
+
return;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// Verify transfer code matches (if provided)
|
|
827
|
+
// Transfer code is optional - if not provided, we still proceed if publicKey matches
|
|
828
|
+
if (data.transferCode) {
|
|
829
|
+
const codeMatches = data.transferCode.toUpperCase() === storedTransfer.code.toUpperCase();
|
|
830
|
+
if (!codeMatches) {
|
|
831
|
+
logger('Transfer code mismatch, but publicKey matches - proceeding with deletion', {
|
|
832
|
+
transferId: data.transferId,
|
|
833
|
+
receivedCode: data.transferCode,
|
|
834
|
+
storedCode: storedTransfer.code.substring(0, 2) + '****'
|
|
835
|
+
});
|
|
836
|
+
// Don't block - publicKey match is sufficient, code mismatch might be due to user error
|
|
837
|
+
// Log warning but proceed
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
// Check if transfer is too old (safety timeout - 10 minutes)
|
|
842
|
+
const transferAge = Date.now() - storedTransfer.timestamp;
|
|
843
|
+
const tenMinutes = 10 * 60 * 1000;
|
|
844
|
+
if (transferAge > tenMinutes) {
|
|
845
|
+
logger('Transfer confirmation received too late', {
|
|
846
|
+
transferId: data.transferId,
|
|
847
|
+
age: transferAge,
|
|
848
|
+
ageMinutes: Math.round(transferAge / 60000)
|
|
849
|
+
});
|
|
850
|
+
toast.error('Transfer verification failed: Confirmation received too late. Identity will not be deleted.');
|
|
851
|
+
clearTransferCode(data.transferId);
|
|
852
|
+
return;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
// NOTE: Target device verification already happened server-side when notifyTransferComplete was called
|
|
856
|
+
// The server verified that the target device is authenticated and has the matching public key
|
|
857
|
+
// Additional client-side verification is not necessary and would require source device authentication
|
|
858
|
+
// which may not be available. The existing checks (public key match, transfer code, device ID) are sufficient.
|
|
859
|
+
|
|
860
|
+
logger('All transfer verifications passed, deleting identity from source device', {
|
|
861
|
+
transferId: data.transferId,
|
|
862
|
+
sourceDeviceId: data.sourceDeviceId,
|
|
863
|
+
publicKey: data.publicKey.substring(0, 16) + '...'
|
|
864
|
+
});
|
|
865
|
+
try {
|
|
866
|
+
// Verify identity still exists before deletion (safety check)
|
|
867
|
+
const identityStillExists = await KeyManager.hasIdentity();
|
|
868
|
+
if (!identityStillExists) {
|
|
869
|
+
logger('Identity already deleted - skipping deletion', {
|
|
870
|
+
transferId: data.transferId
|
|
871
|
+
});
|
|
872
|
+
await updateTransferState(data.transferId, 'completed');
|
|
873
|
+
await clearTransferCode(data.transferId);
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
await deleteIdentityAndClearAccount(false, false, true);
|
|
877
|
+
|
|
878
|
+
// Verify identity was actually deleted
|
|
879
|
+
const identityDeleted = !(await KeyManager.hasIdentity());
|
|
880
|
+
if (!identityDeleted) {
|
|
881
|
+
logger('Identity deletion failed - identity still exists', {
|
|
882
|
+
transferId: data.transferId
|
|
883
|
+
});
|
|
884
|
+
await updateTransferState(data.transferId, 'failed');
|
|
885
|
+
throw new Error('Identity deletion failed - identity still exists');
|
|
886
|
+
}
|
|
887
|
+
await updateTransferState(data.transferId, 'completed');
|
|
888
|
+
await clearTransferCode(data.transferId);
|
|
889
|
+
logger('Identity successfully deleted and transfer code cleared', {
|
|
890
|
+
transferId: data.transferId
|
|
891
|
+
});
|
|
892
|
+
toast.success('Identity successfully transferred and removed from this device');
|
|
893
|
+
} catch (deleteError) {
|
|
894
|
+
logger('Error during identity deletion', deleteError);
|
|
895
|
+
await updateTransferState(data.transferId, 'failed');
|
|
896
|
+
throw deleteError;
|
|
897
|
+
}
|
|
898
|
+
} catch (error) {
|
|
899
|
+
logger('Failed to delete identity after transfer', error);
|
|
900
|
+
toast.error(error?.message || 'Failed to remove identity from this device. Please try again manually from Security Settings.');
|
|
901
|
+
}
|
|
902
|
+
}, [deleteIdentityAndClearAccount, logger, getTransferCode, clearTransferCode, updateTransferState, currentDeviceId, activeSessionId, oxyServices]);
|
|
607
903
|
useSessionSocket({
|
|
608
904
|
userId,
|
|
609
905
|
activeSessionId,
|
|
610
906
|
currentDeviceId,
|
|
611
|
-
refreshSessions,
|
|
907
|
+
refreshSessions: refreshSessionsWithUser,
|
|
612
908
|
logout,
|
|
613
909
|
clearSessionState,
|
|
614
910
|
baseURL: oxyServices.getBaseURL(),
|
|
615
911
|
getAccessToken: () => oxyServices.getAccessToken(),
|
|
616
912
|
getTransferCode: getTransferCode,
|
|
617
|
-
onRemoteSignOut:
|
|
618
|
-
|
|
619
|
-
logout().catch(err => logger('Failed to process remote sign out', err));
|
|
620
|
-
},
|
|
621
|
-
onSessionRemoved: sessionId => {
|
|
622
|
-
setSessions(prev => prev.filter(s => s.sessionId !== sessionId));
|
|
623
|
-
if (sessionId === activeSessionId) {
|
|
624
|
-
setActiveSessionId(null);
|
|
625
|
-
}
|
|
626
|
-
},
|
|
913
|
+
onRemoteSignOut: handleRemoteSignOut,
|
|
914
|
+
onSessionRemoved: handleSessionRemoved,
|
|
627
915
|
onIdentityTransferComplete: handleIdentityTransferComplete
|
|
628
916
|
});
|
|
629
|
-
const {
|
|
630
|
-
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
917
|
+
const switchSessionForContext = useCallback(async sessionId => {
|
|
918
|
+
await switchSession(sessionId);
|
|
919
|
+
}, [switchSession]);
|
|
920
|
+
|
|
921
|
+
// Create showBottomSheet function that uses the global function
|
|
922
|
+
const showBottomSheetForContext = useCallback(screenOrConfig => {
|
|
923
|
+
globalShowBottomSheet(screenOrConfig);
|
|
924
|
+
}, []);
|
|
925
|
+
|
|
926
|
+
// Create openAvatarPicker function
|
|
927
|
+
const openAvatarPicker = useCallback(() => {
|
|
928
|
+
showBottomSheetForContext({
|
|
929
|
+
screen: 'FileManagement',
|
|
930
|
+
props: {
|
|
931
|
+
selectMode: true,
|
|
932
|
+
multiSelect: false,
|
|
933
|
+
disabledMimeTypes: ['video/', 'audio/', 'application/pdf'],
|
|
934
|
+
afterSelect: 'none',
|
|
935
|
+
// Don't navigate away - stay on current screen
|
|
936
|
+
onSelect: async file => {
|
|
937
|
+
if (!file.contentType.startsWith('image/')) {
|
|
938
|
+
toast.error(translate(currentLanguage, 'editProfile.toasts.selectImage') || 'Please select an image file');
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
try {
|
|
942
|
+
// Update file visibility to public for avatar
|
|
943
|
+
await updateAvatarVisibility(file.id, oxyServices, 'OxyContext');
|
|
944
|
+
|
|
945
|
+
// Update user profile (handles query invalidation and accountStore update)
|
|
946
|
+
await updateProfileWithAvatar({
|
|
947
|
+
avatar: file.id
|
|
948
|
+
}, oxyServices, activeSessionId, queryClient, {
|
|
949
|
+
syncIdentity,
|
|
950
|
+
deviceId: currentDeviceId || undefined
|
|
951
|
+
});
|
|
952
|
+
toast.success(translate(currentLanguage, 'editProfile.toasts.avatarUpdated') || 'Avatar updated');
|
|
953
|
+
} catch (e) {
|
|
954
|
+
toast.error(e.message || translate(currentLanguage, 'editProfile.toasts.updateAvatarFailed') || 'Failed to update avatar');
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
});
|
|
959
|
+
}, [oxyServices, currentLanguage, showBottomSheetForContext, activeSessionId, queryClient, syncIdentity]);
|
|
638
960
|
const contextValue = useMemo(() => ({
|
|
639
961
|
user,
|
|
640
962
|
sessions,
|
|
641
963
|
activeSessionId,
|
|
642
964
|
currentDeviceId,
|
|
643
|
-
isAuthenticated
|
|
965
|
+
isAuthenticated,
|
|
644
966
|
isLoading,
|
|
645
967
|
isTokenReady: tokenReady,
|
|
646
968
|
isStorageReady,
|
|
@@ -657,15 +979,21 @@ export const OxyProvider = ({
|
|
|
657
979
|
isIdentitySynced,
|
|
658
980
|
syncIdentity,
|
|
659
981
|
deleteIdentityAndClearAccount,
|
|
982
|
+
storeTransferCode,
|
|
983
|
+
getTransferCode,
|
|
984
|
+
clearTransferCode,
|
|
985
|
+
getAllPendingTransfers,
|
|
986
|
+
getActiveTransferId,
|
|
987
|
+
updateTransferState,
|
|
660
988
|
identitySyncState: {
|
|
661
989
|
isSynced: isIdentitySyncedStore ?? true,
|
|
662
990
|
isSyncing: isSyncing ?? false
|
|
663
991
|
},
|
|
664
992
|
logout,
|
|
665
993
|
logoutAll,
|
|
666
|
-
switchSession,
|
|
994
|
+
switchSession: switchSessionForContext,
|
|
667
995
|
removeSession: logout,
|
|
668
|
-
refreshSessions,
|
|
996
|
+
refreshSessions: refreshSessionsWithUser,
|
|
669
997
|
setLanguage,
|
|
670
998
|
getDeviceSessions,
|
|
671
999
|
logoutAllDeviceSessions,
|
|
@@ -673,9 +1001,10 @@ export const OxyProvider = ({
|
|
|
673
1001
|
clearSessionState,
|
|
674
1002
|
clearAllAccountData,
|
|
675
1003
|
oxyServices,
|
|
676
|
-
|
|
1004
|
+
useFollow: useFollowHook,
|
|
1005
|
+
showBottomSheet: showBottomSheetForContext,
|
|
677
1006
|
openAvatarPicker
|
|
678
|
-
}), [activeSessionId, currentDeviceId, createIdentity, importIdentity, signIn, hasIdentity, getPublicKey, isIdentitySynced, syncIdentity, deleteIdentityAndClearAccount, isIdentitySyncedStore, isSyncing, currentLanguage, currentLanguageMetadata, currentLanguageName, currentNativeLanguageName, error, getDeviceSessions,
|
|
1007
|
+
}), [activeSessionId, currentDeviceId, createIdentity, importIdentity, signIn, hasIdentity, getPublicKey, isIdentitySynced, syncIdentity, deleteIdentityAndClearAccount, storeTransferCode, getTransferCode, clearTransferCode, getAllPendingTransfers, getActiveTransferId, updateTransferState, isIdentitySyncedStore, isSyncing, currentLanguage, currentLanguageMetadata, currentLanguageName, currentNativeLanguageName, error, getDeviceSessions, isAuthenticated, isLoading, logout, logoutAll, logoutAllDeviceSessions, oxyServices, refreshSessionsWithUser, sessions, setLanguage, switchSessionForContext, tokenReady, isStorageReady, updateDeviceName, clearAllAccountData, useFollowHook, user, showBottomSheetForContext, openAvatarPicker]);
|
|
679
1008
|
return /*#__PURE__*/_jsx(OxyContext.Provider, {
|
|
680
1009
|
value: contextValue,
|
|
681
1010
|
children: children
|