@oxyhq/services 5.16.1 → 5.16.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/lib/commonjs/core/mixins/OxyServices.user.js +14 -13
- package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/commonjs/crypto/keyManager.js +164 -3
- package/lib/commonjs/crypto/keyManager.js.map +1 -1
- package/lib/commonjs/crypto/signatureService.js +26 -0
- package/lib/commonjs/crypto/signatureService.js.map +1 -1
- package/lib/commonjs/ui/components/GroupedSection.js +1 -1
- package/lib/commonjs/ui/components/GroupedSection.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +71 -24
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/profile/EditDisplayNameModal.js +1 -4
- package/lib/commonjs/ui/components/profile/EditDisplayNameModal.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +177 -4
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +148 -49
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useSessionManagement.js +22 -2
- package/lib/commonjs/ui/context/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/hooks/mutations/index.js +28 -0
- package/lib/commonjs/ui/hooks/mutations/index.js.map +1 -0
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +314 -0
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -0
- package/lib/commonjs/ui/hooks/mutations/useServicesMutations.js +193 -0
- package/lib/commonjs/ui/hooks/mutations/useServicesMutations.js.map +1 -0
- package/lib/commonjs/ui/hooks/queries/index.js +39 -0
- package/lib/commonjs/ui/hooks/queries/index.js.map +1 -0
- package/lib/commonjs/ui/hooks/queries/queryKeys.js +85 -0
- package/lib/commonjs/ui/hooks/queries/queryKeys.js.map +1 -0
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +145 -0
- package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -0
- package/lib/commonjs/ui/hooks/queries/useServicesQueries.js +138 -0
- package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -0
- package/lib/commonjs/ui/hooks/queryClient.js +117 -0
- package/lib/commonjs/ui/hooks/queryClient.js.map +1 -0
- package/lib/commonjs/ui/hooks/useIdentityMutations.js +111 -0
- package/lib/commonjs/ui/hooks/useIdentityMutations.js.map +1 -0
- package/lib/commonjs/ui/hooks/useProfileEditing.js +42 -58
- package/lib/commonjs/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/commonjs/ui/hooks/useQueryClient.js +20 -0
- package/lib/commonjs/ui/hooks/useQueryClient.js.map +1 -0
- package/lib/commonjs/ui/hooks/useSessionManagement.js +22 -2
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js +43 -42
- package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js +63 -58
- package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +6 -6
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/commonjs/ui/stores/accountStore.js +57 -42
- package/lib/commonjs/ui/stores/accountStore.js.map +1 -1
- package/lib/commonjs/ui/stores/authStore.js +4 -25
- package/lib/commonjs/ui/stores/authStore.js.map +1 -1
- package/lib/module/core/mixins/OxyServices.user.js +14 -13
- package/lib/module/core/mixins/OxyServices.user.js.map +1 -1
- package/lib/module/crypto/keyManager.js +164 -3
- package/lib/module/crypto/keyManager.js.map +1 -1
- package/lib/module/crypto/signatureService.js +26 -0
- package/lib/module/crypto/signatureService.js.map +1 -1
- package/lib/module/ui/components/GroupedSection.js +1 -1
- package/lib/module/ui/components/GroupedSection.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +72 -25
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/profile/EditDisplayNameModal.js +1 -4
- package/lib/module/ui/components/profile/EditDisplayNameModal.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +176 -4
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +148 -49
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/context/hooks/useSessionManagement.js +22 -2
- package/lib/module/ui/context/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/hooks/mutations/index.js +6 -0
- package/lib/module/ui/hooks/mutations/index.js.map +1 -0
- package/lib/module/ui/hooks/mutations/useAccountMutations.js +308 -0
- package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -0
- package/lib/module/ui/hooks/mutations/useServicesMutations.js +185 -0
- package/lib/module/ui/hooks/mutations/useServicesMutations.js.map +1 -0
- package/lib/module/ui/hooks/queries/index.js +7 -0
- package/lib/module/ui/hooks/queries/index.js.map +1 -0
- package/lib/module/ui/hooks/queries/queryKeys.js +78 -0
- package/lib/module/ui/hooks/queries/queryKeys.js.map +1 -0
- package/lib/module/ui/hooks/queries/useAccountQueries.js +136 -0
- package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -0
- package/lib/module/ui/hooks/queries/useServicesQueries.js +130 -0
- package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -0
- package/lib/module/ui/hooks/queryClient.js +110 -0
- package/lib/module/ui/hooks/queryClient.js.map +1 -0
- package/lib/module/ui/hooks/useIdentityMutations.js +105 -0
- package/lib/module/ui/hooks/useIdentityMutations.js.map +1 -0
- package/lib/module/ui/hooks/useProfileEditing.js +43 -59
- package/lib/module/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/module/ui/hooks/useQueryClient.js +15 -0
- package/lib/module/ui/hooks/useQueryClient.js.map +1 -0
- package/lib/module/ui/hooks/useSessionManagement.js +22 -2
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/screens/AccountOverviewScreen.js +43 -42
- package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
- package/lib/module/ui/screens/AccountSettingsScreen.js +63 -58
- package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
- package/lib/module/ui/screens/WelcomeNewUserScreen.js +6 -6
- package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/module/ui/stores/accountStore.js +57 -42
- package/lib/module/ui/stores/accountStore.js.map +1 -1
- package/lib/module/ui/stores/authStore.js +4 -25
- package/lib/module/ui/stores/authStore.js.map +1 -1
- package/lib/typescript/core/mixins/OxyServices.user.d.ts +4 -5
- package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -1
- package/lib/typescript/core/mixins/index.d.ts +0 -1
- package/lib/typescript/core/mixins/index.d.ts.map +1 -1
- package/lib/typescript/crypto/keyManager.d.ts +19 -2
- package/lib/typescript/crypto/keyManager.d.ts.map +1 -1
- package/lib/typescript/crypto/signatureService.d.ts +5 -0
- package/lib/typescript/crypto/signatureService.d.ts.map +1 -1
- package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/ui/components/profile/EditDisplayNameModal.d.ts.map +1 -1
- package/lib/typescript/ui/context/OxyContext.d.ts +4 -0
- package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/ui/context/hooks/useSessionManagement.d.ts +3 -1
- package/lib/typescript/ui/context/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/mutations/index.d.ts +3 -0
- package/lib/typescript/ui/hooks/mutations/index.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts +25 -0
- package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/mutations/useServicesMutations.d.ts +23 -0
- package/lib/typescript/ui/hooks/mutations/useServicesMutations.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/queries/index.d.ts +4 -0
- package/lib/typescript/ui/hooks/queries/index.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/queries/queryKeys.d.ts +56 -0
- package/lib/typescript/ui/hooks/queries/queryKeys.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts +41 -0
- package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts +34 -0
- package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/queryClient.d.ts +19 -0
- package/lib/typescript/ui/hooks/queryClient.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/useIdentityMutations.d.ts +29 -0
- package/lib/typescript/ui/hooks/useIdentityMutations.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/useProfileEditing.d.ts.map +1 -1
- package/lib/typescript/ui/hooks/useQueryClient.d.ts +7 -0
- package/lib/typescript/ui/hooks/useQueryClient.d.ts.map +1 -0
- package/lib/typescript/ui/hooks/useSessionManagement.d.ts +3 -1
- package/lib/typescript/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
- package/lib/typescript/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
- package/lib/typescript/ui/stores/accountStore.d.ts.map +1 -1
- package/lib/typescript/ui/stores/authStore.d.ts +0 -4
- package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/core/mixins/OxyServices.user.ts +17 -10
- package/src/crypto/keyManager.ts +177 -2
- package/src/crypto/signatureService.ts +30 -0
- package/src/ui/components/GroupedSection.tsx +1 -1
- package/src/ui/components/OxyProvider.tsx +91 -37
- package/src/ui/components/profile/EditDisplayNameModal.tsx +1 -3
- package/src/ui/context/OxyContext.tsx +185 -2
- package/src/ui/context/hooks/useAuthOperations.ts +171 -58
- package/src/ui/context/hooks/useSessionManagement.ts +24 -1
- package/src/ui/hooks/mutations/index.ts +4 -0
- package/src/ui/hooks/mutations/useAccountMutations.ts +277 -0
- package/src/ui/hooks/mutations/useServicesMutations.ts +164 -0
- package/src/ui/hooks/queries/index.ts +5 -0
- package/src/ui/hooks/queries/queryKeys.ts +73 -0
- package/src/ui/hooks/queries/useAccountQueries.ts +126 -0
- package/src/ui/hooks/queries/useServicesQueries.ts +121 -0
- package/src/ui/hooks/queryClient.ts +112 -0
- package/src/ui/hooks/useIdentityMutations.ts +115 -0
- package/src/ui/hooks/useProfileEditing.ts +46 -60
- package/src/ui/hooks/useQueryClient.ts +17 -0
- package/src/ui/hooks/useSessionManagement.ts +24 -1
- package/src/ui/screens/AccountOverviewScreen.tsx +38 -46
- package/src/ui/screens/AccountSettingsScreen.tsx +54 -54
- package/src/ui/screens/WelcomeNewUserScreen.tsx +13 -12
- package/src/ui/stores/accountStore.ts +54 -43
- package/src/ui/stores/authStore.ts +3 -17
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TanStack Query mutations for identity operations
|
|
3
|
+
* Provides offline-first mutations for identity creation, import, and sync
|
|
4
|
+
* Never deletes identity on errors - preserves user data
|
|
5
|
+
*/
|
|
6
|
+
import type { User } from '../../models/interfaces';
|
|
7
|
+
export interface CreateIdentityResult {
|
|
8
|
+
recoveryPhrase: string[];
|
|
9
|
+
synced: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface ImportIdentityResult {
|
|
12
|
+
synced: boolean;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Hook for creating a new identity with offline support
|
|
16
|
+
* Never deletes identity on error - preserves user data
|
|
17
|
+
*/
|
|
18
|
+
export declare function useCreateIdentity(createIdentityFn: () => Promise<CreateIdentityResult>): import("@tanstack/react-query").UseMutationResult<CreateIdentityResult, Error, void, unknown>;
|
|
19
|
+
/**
|
|
20
|
+
* Hook for importing an identity from recovery phrase
|
|
21
|
+
* Never deletes identity on error - preserves user data
|
|
22
|
+
*/
|
|
23
|
+
export declare function useImportIdentity(importIdentityFn: (phrase: string) => Promise<ImportIdentityResult>): import("@tanstack/react-query").UseMutationResult<ImportIdentityResult, Error, string, unknown>;
|
|
24
|
+
/**
|
|
25
|
+
* Hook for syncing identity with server
|
|
26
|
+
* Never deletes identity on error - only logs and allows retry
|
|
27
|
+
*/
|
|
28
|
+
export declare function useSyncIdentity(syncIdentityFn: () => Promise<User>): import("@tanstack/react-query").UseMutationResult<User, Error, void, unknown>;
|
|
29
|
+
//# sourceMappingURL=useIdentityMutations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIdentityMutations.d.ts","sourceRoot":"","sources":["../../../../src/ui/hooks/useIdentityMutations.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,MAAM,WAAW,oBAAoB;IACnC,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,gBAAgB,EAAE,MAAM,OAAO,CAAC,oBAAoB,CAAC,iGAqBtD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,oBAAoB,CAAC,mGAmBpE;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,iFAoCpC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useProfileEditing.d.ts","sourceRoot":"","sources":["../../../../src/ui/hooks/useProfileEditing.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useProfileEditing.d.ts","sourceRoot":"","sources":["../../../../src/ui/hooks/useProfileEditing.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,iBAAiB;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,KAAK,CAAC;QACd,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC;KAC9C,CAAC,CAAC;IACH,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,aAAa,CAAC,EAAE,KAAK,CAAC;QAClB,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,EAAE,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,eAAO,MAAM,iBAAiB;2BAOsB,iBAAiB;yBAqDnB,MAAM,SAAS,GAAG;;CAmCnE,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { QueryClient } from '@tanstack/react-query';
|
|
2
|
+
/**
|
|
3
|
+
* Custom hook to access the QueryClient
|
|
4
|
+
* Provides type safety and ensures client is available
|
|
5
|
+
*/
|
|
6
|
+
export declare const useQueryClient: () => QueryClient;
|
|
7
|
+
//# sourceMappingURL=useQueryClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useQueryClient.d.ts","sourceRoot":"","sources":["../../../../src/ui/hooks/useQueryClient.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD;;;GAGG;AACH,eAAO,MAAM,cAAc,QAAO,WAQjC,CAAC"}
|
|
@@ -2,6 +2,7 @@ import type { ApiError, User } from '../../models/interfaces';
|
|
|
2
2
|
import type { ClientSession } from '../../models/session';
|
|
3
3
|
import { getStorageKeys, type StorageInterface } from '../utils/storageHelpers';
|
|
4
4
|
import type { OxyServices } from '../../core';
|
|
5
|
+
import type { QueryClient } from '@tanstack/react-query';
|
|
5
6
|
export interface UseSessionManagementOptions {
|
|
6
7
|
oxyServices: OxyServices;
|
|
7
8
|
storage: StorageInterface | null;
|
|
@@ -14,6 +15,7 @@ export interface UseSessionManagementOptions {
|
|
|
14
15
|
setAuthError?: (message: string | null) => void;
|
|
15
16
|
logger?: (message: string, error?: unknown) => void;
|
|
16
17
|
setTokenReady?: (ready: boolean) => void;
|
|
18
|
+
queryClient?: QueryClient | null;
|
|
17
19
|
}
|
|
18
20
|
export interface UseSessionManagementResult {
|
|
19
21
|
sessions: ClientSession[];
|
|
@@ -35,5 +37,5 @@ export interface UseSessionManagementResult {
|
|
|
35
37
|
*
|
|
36
38
|
* @param options - Session management configuration
|
|
37
39
|
*/
|
|
38
|
-
export declare const useSessionManagement: ({ oxyServices, storage, storageKeyPrefix, loginSuccess, logoutStore, applyLanguagePreference, onAuthStateChange, onError, setAuthError, logger, setTokenReady, }: UseSessionManagementOptions) => UseSessionManagementResult;
|
|
40
|
+
export declare const useSessionManagement: ({ oxyServices, storage, storageKeyPrefix, loginSuccess, logoutStore, applyLanguagePreference, onAuthStateChange, onError, setAuthError, logger, setTokenReady, queryClient, }: UseSessionManagementOptions) => UseSessionManagementResult;
|
|
39
41
|
//# sourceMappingURL=useSessionManagement.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSessionManagement.d.ts","sourceRoot":"","sources":["../../../../src/ui/hooks/useSessionManagement.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG1D,OAAO,EAAE,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"useSessionManagement.d.ts","sourceRoot":"","sources":["../../../../src/ui/hooks/useSessionManagement.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAG1D,OAAO,EAAE,cAAc,EAAE,KAAK,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAEhF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,uBAAuB,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAChD,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACpD,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,WAAW,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IACvD,cAAc,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,CAAC;IACnF,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,eAAe,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,iBAAiB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,mBAAmB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,WAAW,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;IAC/C,iBAAiB,EAAE,OAAO,CAAC;CAC5B;AAKD;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,GAAI,+KAalC,2BAA2B,KAAG,0BAiVhC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountOverviewScreen.d.ts","sourceRoot":"","sources":["../../../../src/ui/screens/AccountOverviewScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAcjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;;
|
|
1
|
+
{"version":3,"file":"AccountOverviewScreen.d.ts","sourceRoot":"","sources":["../../../../src/ui/screens/AccountOverviewScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAcjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;;AA8yB3D,wBAAiD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AccountSettingsScreen.d.ts","sourceRoot":"","sources":["../../../../src/ui/screens/AccountSettingsScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAcjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;;
|
|
1
|
+
{"version":3,"file":"AccountSettingsScreen.d.ts","sourceRoot":"","sources":["../../../../src/ui/screens/AccountSettingsScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAcjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;;mBA8Cc,MAAM;qBAAmB,MAAM;;AAwgExG,wBAAiD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WelcomeNewUserScreen.d.ts","sourceRoot":"","sources":["../../../../src/ui/screens/WelcomeNewUserScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAGjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"WelcomeNewUserScreen.d.ts","sourceRoot":"","sources":["../../../../src/ui/screens/WelcomeNewUserScreen.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAGjF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AA+C3D;;;;;GAKG;AACH,QAAA,MAAM,oBAAoB,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,GAAG;IAAE,OAAO,CAAC,EAAE,GAAG,CAAA;CAAE,CA0MvE,CAAC;AAgGF,eAAe,oBAAoB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accountStore.d.ts","sourceRoot":"","sources":["../../../../src/ui/stores/accountStore.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,YAAY;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,YAAY;IAElB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACvC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,EAAE,YAAY,EAAE,CAAC;IAG9B,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAG/B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAGrB,WAAW,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IAChD,UAAU,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IAC3E,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAG9C,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAGjE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAGzC,YAAY,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,CAAC,EAAE,YAAY,EAAE,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAG5I,KAAK,EAAE,MAAM,IAAI,CAAC;CACrB;AA4CD,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"accountStore.d.ts","sourceRoot":"","sources":["../../../../src/ui/stores/accountStore.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,YAAY;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,YAAY;IAElB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACvC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,aAAa,EAAE,YAAY,EAAE,CAAC;IAG9B,OAAO,EAAE,OAAO,CAAC;IACjB,iBAAiB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAG/B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAGrB,WAAW,EAAE,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,IAAI,CAAC;IAChD,UAAU,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,IAAI,CAAC;IAC5C,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,CAAC;IAC3E,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,gBAAgB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAG9C,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IACvC,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;IAGjE,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAGzC,YAAY,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,CAAC,EAAE,YAAY,EAAE,EAAE,aAAa,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAG5I,KAAK,EAAE,MAAM,IAAI,CAAC;CACrB;AA4CD,eAAO,MAAM,eAAe,2EAmMzB,CAAC;AAGJ,eAAO,MAAM,WAAW,QAAO,YAAY,EAE1C,CAAC;AAEF,eAAO,MAAM,iBAAiB,eAAwC,CAAC;AACvE,eAAO,MAAM,eAAe,qBAAsC,CAAC;AACnE,eAAO,MAAM,wBAAwB,GAAI,WAAW,MAAM,YACE,CAAC"}
|
|
@@ -13,10 +13,6 @@ export interface AuthState {
|
|
|
13
13
|
fetchUser: (oxyServices: {
|
|
14
14
|
getCurrentUser: () => Promise<User>;
|
|
15
15
|
}, forceRefresh?: boolean) => Promise<void>;
|
|
16
|
-
updateUser: (updates: Partial<User>, oxyServices: {
|
|
17
|
-
updateProfile: (updates: Partial<User>) => Promise<User>;
|
|
18
|
-
getCurrentUser: () => Promise<User>;
|
|
19
|
-
}) => Promise<void>;
|
|
20
16
|
setUser: (user: User) => void;
|
|
21
17
|
setIdentitySynced: (synced: boolean) => void;
|
|
22
18
|
setSyncing: (syncing: boolean) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authStore.d.ts","sourceRoot":"","sources":["../../../../src/ui/stores/authStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAG7B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IAEnB,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,SAAS,EAAE,CAAC,WAAW,EAAE;QAAE,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,EAAE,YAAY,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3G,
|
|
1
|
+
{"version":3,"file":"authStore.d.ts","sourceRoot":"","sources":["../../../../src/ui/stores/authStore.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAEpD,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,eAAe,EAAE,OAAO,CAAC;IACzB,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAG7B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IAEnB,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IACnC,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACtC,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,SAAS,EAAE,CAAC,WAAW,EAAE;QAAE,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,EAAE,YAAY,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3G,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAC;IAG9B,iBAAiB,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7C,UAAU,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACxC;AAED,eAAO,MAAM,YAAY,wEA0DtB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/services",
|
|
3
|
-
"version": "5.16.
|
|
3
|
+
"version": "5.16.2",
|
|
4
4
|
"description": "Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -90,6 +90,7 @@
|
|
|
90
90
|
},
|
|
91
91
|
"dependencies": {
|
|
92
92
|
"@react-native-async-storage/async-storage": "2.1.2",
|
|
93
|
+
"@react-native-community/netinfo": "^11.4.1",
|
|
93
94
|
"@tanstack/react-query": "^5.59.0",
|
|
94
95
|
"axios": "^1.9.0",
|
|
95
96
|
"bip39": "^3.1.0",
|
|
@@ -111,13 +112,14 @@
|
|
|
111
112
|
"zustand": "^5.0.6"
|
|
112
113
|
},
|
|
113
114
|
"devDependencies": {
|
|
114
|
-
"@types/color": "^3.0.6",
|
|
115
|
-
"@types/elliptic": "^6.4.18",
|
|
116
115
|
"@biomejs/biome": "^1.9.4",
|
|
117
116
|
"@commitlint/cli": "^17.6.5",
|
|
118
117
|
"@commitlint/config-conventional": "^17.6.5",
|
|
118
|
+
"@react-native/babel-preset": "^0.77.0",
|
|
119
119
|
"@release-it/conventional-changelog": "^10.0.2",
|
|
120
120
|
"@testing-library/jest-dom": "^6.4.2",
|
|
121
|
+
"@types/color": "^3.0.6",
|
|
122
|
+
"@types/elliptic": "^6.4.18",
|
|
121
123
|
"@types/invariant": "^2.2.34",
|
|
122
124
|
"@types/jest": "^29.5.14",
|
|
123
125
|
"@types/jwt-decode": "^2.2.1",
|
|
@@ -132,7 +134,6 @@
|
|
|
132
134
|
"husky": "^4.3.8",
|
|
133
135
|
"jest": "~29.7.0",
|
|
134
136
|
"lint-staged": "^15.2.4",
|
|
135
|
-
"@react-native/babel-preset": "^0.77.0",
|
|
136
137
|
"react-native-builder-bob": "^0.40.16",
|
|
137
138
|
"react-native-gesture-handler": "~2.28.0",
|
|
138
139
|
"react-native-reanimated": "~4.1.1",
|
|
@@ -129,11 +129,28 @@ export function OxyServicesUserMixin<T extends typeof OxyServicesBase>(Base: T)
|
|
|
129
129
|
|
|
130
130
|
/**
|
|
131
131
|
* Update user profile
|
|
132
|
+
* TanStack Query handles offline queuing automatically
|
|
132
133
|
*/
|
|
133
134
|
async updateProfile(updates: Record<string, any>): Promise<User> {
|
|
134
135
|
try {
|
|
135
136
|
return await this.makeRequest<User>('PUT', '/api/users/me', updates, { cache: false });
|
|
136
137
|
} catch (error) {
|
|
138
|
+
const errorAny = error as any;
|
|
139
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
140
|
+
const status = errorAny?.status || errorAny?.response?.status;
|
|
141
|
+
|
|
142
|
+
// Check if it's an authentication error (401)
|
|
143
|
+
const isAuthError = status === 401 ||
|
|
144
|
+
errorMessage.includes('Authentication required') ||
|
|
145
|
+
errorMessage.includes('Invalid or missing authorization header');
|
|
146
|
+
|
|
147
|
+
// If authentication error and we don't have a token, this might be an offline session
|
|
148
|
+
// The caller should handle syncing the session before retrying
|
|
149
|
+
if (isAuthError && !this.hasValidToken()) {
|
|
150
|
+
// Re-throw with a specific message so the caller knows to sync
|
|
151
|
+
throw new Error('AUTH_REQUIRED_OFFLINE_SESSION: Session needs to be synced to get a token');
|
|
152
|
+
}
|
|
153
|
+
|
|
137
154
|
throw this.handleError(error);
|
|
138
155
|
}
|
|
139
156
|
}
|
|
@@ -219,16 +236,6 @@ export function OxyServicesUserMixin<T extends typeof OxyServicesBase>(Base: T)
|
|
|
219
236
|
}
|
|
220
237
|
}
|
|
221
238
|
|
|
222
|
-
/**
|
|
223
|
-
* Update user by ID (admin function)
|
|
224
|
-
*/
|
|
225
|
-
async updateUser(userId: string, updates: Record<string, any>): Promise<User> {
|
|
226
|
-
try {
|
|
227
|
-
return await this.makeRequest<User>('PUT', `/api/users/${userId}`, updates, { cache: false });
|
|
228
|
-
} catch (error) {
|
|
229
|
-
throw this.handleError(error);
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
239
|
|
|
233
240
|
/**
|
|
234
241
|
* Follow a user
|
package/src/crypto/keyManager.ts
CHANGED
|
@@ -17,6 +17,9 @@ const ec = new EC('secp256k1');
|
|
|
17
17
|
const STORAGE_KEYS = {
|
|
18
18
|
PRIVATE_KEY: 'oxy_identity_private_key',
|
|
19
19
|
PUBLIC_KEY: 'oxy_identity_public_key',
|
|
20
|
+
BACKUP_PRIVATE_KEY: 'oxy_identity_backup_private_key',
|
|
21
|
+
BACKUP_PUBLIC_KEY: 'oxy_identity_backup_public_key',
|
|
22
|
+
BACKUP_TIMESTAMP: 'oxy_identity_backup_timestamp',
|
|
20
23
|
} as const;
|
|
21
24
|
|
|
22
25
|
/**
|
|
@@ -183,12 +186,184 @@ export class KeyManager {
|
|
|
183
186
|
|
|
184
187
|
/**
|
|
185
188
|
* Delete the stored identity (both keys)
|
|
186
|
-
* Use with caution - this is irreversible without a recovery phrase
|
|
189
|
+
* Use with EXTREME caution - this is irreversible without a recovery phrase
|
|
190
|
+
* This should ONLY be called when explicitly requested by the user
|
|
191
|
+
* @param skipBackup - If true, skip backup before deletion (default: false)
|
|
192
|
+
* @param force - If true, skip confirmation checks (default: false)
|
|
193
|
+
* @param userConfirmed - If true, user has explicitly confirmed deletion (default: false)
|
|
187
194
|
*/
|
|
188
|
-
static async deleteIdentity(
|
|
195
|
+
static async deleteIdentity(
|
|
196
|
+
skipBackup: boolean = false,
|
|
197
|
+
force: boolean = false,
|
|
198
|
+
userConfirmed: boolean = false
|
|
199
|
+
): Promise<void> {
|
|
200
|
+
// CRITICAL SAFEGUARD: Require explicit user confirmation unless force is true
|
|
201
|
+
if (!force && !userConfirmed) {
|
|
202
|
+
throw new Error('Identity deletion requires explicit user confirmation. This is a safety measure to prevent accidental data loss.');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if (!force) {
|
|
206
|
+
const hasIdentity = await KeyManager.hasIdentity();
|
|
207
|
+
if (!hasIdentity) {
|
|
208
|
+
return; // Nothing to delete
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
189
212
|
const store = await initSecureStore();
|
|
213
|
+
|
|
214
|
+
// ALWAYS create backup before deletion unless explicitly skipped
|
|
215
|
+
if (!skipBackup) {
|
|
216
|
+
try {
|
|
217
|
+
const backupSuccess = await KeyManager.backupIdentity();
|
|
218
|
+
if (!backupSuccess && typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
219
|
+
console.warn('[KeyManager] Failed to backup identity before deletion - proceeding anyway');
|
|
220
|
+
}
|
|
221
|
+
} catch (backupError) {
|
|
222
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
223
|
+
console.warn('[KeyManager] Failed to backup identity before deletion:', backupError);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
190
228
|
await store.deleteItemAsync(STORAGE_KEYS.PRIVATE_KEY);
|
|
191
229
|
await store.deleteItemAsync(STORAGE_KEYS.PUBLIC_KEY);
|
|
230
|
+
|
|
231
|
+
// Also clear backup if force deletion
|
|
232
|
+
if (force) {
|
|
233
|
+
try {
|
|
234
|
+
await store.deleteItemAsync(STORAGE_KEYS.BACKUP_PRIVATE_KEY);
|
|
235
|
+
await store.deleteItemAsync(STORAGE_KEYS.BACKUP_PUBLIC_KEY);
|
|
236
|
+
await store.deleteItemAsync(STORAGE_KEYS.BACKUP_TIMESTAMP);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
// Ignore backup deletion errors
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Backup identity to SecureStore (separate backup storage)
|
|
245
|
+
* This provides a recovery mechanism if primary storage fails
|
|
246
|
+
*/
|
|
247
|
+
static async backupIdentity(): Promise<boolean> {
|
|
248
|
+
try {
|
|
249
|
+
const store = await initSecureStore();
|
|
250
|
+
const privateKey = await KeyManager.getPrivateKey();
|
|
251
|
+
const publicKey = await KeyManager.getPublicKey();
|
|
252
|
+
|
|
253
|
+
if (!privateKey || !publicKey) {
|
|
254
|
+
return false; // Nothing to backup
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Store backup in SecureStore (still secure, but separate from primary storage)
|
|
258
|
+
await store.setItemAsync(STORAGE_KEYS.BACKUP_PRIVATE_KEY, privateKey, {
|
|
259
|
+
keychainAccessible: store.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
|
|
260
|
+
});
|
|
261
|
+
await store.setItemAsync(STORAGE_KEYS.BACKUP_PUBLIC_KEY, publicKey);
|
|
262
|
+
await store.setItemAsync(STORAGE_KEYS.BACKUP_TIMESTAMP, Date.now().toString());
|
|
263
|
+
|
|
264
|
+
return true;
|
|
265
|
+
} catch (error) {
|
|
266
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
267
|
+
console.error('[KeyManager] Failed to backup identity:', error);
|
|
268
|
+
}
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Verify identity integrity - checks if keys are valid and accessible
|
|
275
|
+
*/
|
|
276
|
+
static async verifyIdentityIntegrity(): Promise<boolean> {
|
|
277
|
+
try {
|
|
278
|
+
const privateKey = await KeyManager.getPrivateKey();
|
|
279
|
+
const publicKey = await KeyManager.getPublicKey();
|
|
280
|
+
|
|
281
|
+
if (!privateKey || !publicKey) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Validate private key format
|
|
286
|
+
if (!KeyManager.isValidPrivateKey(privateKey)) {
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Validate public key format
|
|
291
|
+
if (!KeyManager.isValidPublicKey(publicKey)) {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Verify public key can be derived from private key
|
|
296
|
+
const derivedPublicKey = KeyManager.derivePublicKey(privateKey);
|
|
297
|
+
if (derivedPublicKey !== publicKey) {
|
|
298
|
+
return false; // Keys don't match
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Verify we can create a key pair object (tests elliptic curve operations)
|
|
302
|
+
const keyPair = await KeyManager.getKeyPairObject();
|
|
303
|
+
if (!keyPair) {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return true;
|
|
308
|
+
} catch (error) {
|
|
309
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
310
|
+
console.error('[KeyManager] Identity integrity check failed:', error);
|
|
311
|
+
}
|
|
312
|
+
return false;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Restore identity from backup if primary storage is corrupted
|
|
318
|
+
*/
|
|
319
|
+
static async restoreIdentityFromBackup(): Promise<boolean> {
|
|
320
|
+
try {
|
|
321
|
+
const store = await initSecureStore();
|
|
322
|
+
|
|
323
|
+
// Check if backup exists
|
|
324
|
+
const backupPrivateKey = await store.getItemAsync(STORAGE_KEYS.BACKUP_PRIVATE_KEY);
|
|
325
|
+
const backupPublicKey = await store.getItemAsync(STORAGE_KEYS.BACKUP_PUBLIC_KEY);
|
|
326
|
+
|
|
327
|
+
if (!backupPrivateKey || !backupPublicKey) {
|
|
328
|
+
return false; // No backup available
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Verify backup integrity
|
|
332
|
+
if (!KeyManager.isValidPrivateKey(backupPrivateKey)) {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (!KeyManager.isValidPublicKey(backupPublicKey)) {
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Verify keys match
|
|
341
|
+
const derivedPublicKey = KeyManager.derivePublicKey(backupPrivateKey);
|
|
342
|
+
if (derivedPublicKey !== backupPublicKey) {
|
|
343
|
+
return false; // Backup keys don't match
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Restore from backup
|
|
347
|
+
await store.setItemAsync(STORAGE_KEYS.PRIVATE_KEY, backupPrivateKey, {
|
|
348
|
+
keychainAccessible: store.WHEN_UNLOCKED_THIS_DEVICE_ONLY,
|
|
349
|
+
});
|
|
350
|
+
await store.setItemAsync(STORAGE_KEYS.PUBLIC_KEY, backupPublicKey);
|
|
351
|
+
|
|
352
|
+
// Verify restoration was successful
|
|
353
|
+
const restored = await KeyManager.verifyIdentityIntegrity();
|
|
354
|
+
if (restored) {
|
|
355
|
+
// Update backup timestamp
|
|
356
|
+
await store.setItemAsync(STORAGE_KEYS.BACKUP_TIMESTAMP, Date.now().toString());
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return false;
|
|
361
|
+
} catch (error) {
|
|
362
|
+
if (typeof __DEV__ !== 'undefined' && __DEV__) {
|
|
363
|
+
console.error('[KeyManager] Failed to restore identity from backup:', error);
|
|
364
|
+
}
|
|
365
|
+
return false;
|
|
366
|
+
}
|
|
192
367
|
}
|
|
193
368
|
|
|
194
369
|
/**
|
|
@@ -82,6 +82,36 @@ export interface AuthChallenge {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
export class SignatureService {
|
|
85
|
+
/**
|
|
86
|
+
* Generate a random challenge string (for offline use)
|
|
87
|
+
* Uses expo-crypto in React Native, crypto.randomBytes in Node.js
|
|
88
|
+
*/
|
|
89
|
+
static async generateChallenge(): Promise<string> {
|
|
90
|
+
if (isReactNative() || !isNodeJS()) {
|
|
91
|
+
// Use expo-crypto for React Native (expo-random is deprecated)
|
|
92
|
+
const Crypto = await initExpoCrypto();
|
|
93
|
+
const randomBytes = await Crypto.getRandomBytesAsync(32);
|
|
94
|
+
return Array.from(randomBytes)
|
|
95
|
+
.map((b: number) => b.toString(16).padStart(2, '0'))
|
|
96
|
+
.join('');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Node.js fallback
|
|
100
|
+
try {
|
|
101
|
+
// eslint-disable-next-line @typescript-eslint/no-implied-eval
|
|
102
|
+
const getCrypto = new Function('return require("crypto")');
|
|
103
|
+
const crypto = getCrypto();
|
|
104
|
+
return crypto.randomBytes(32).toString('hex');
|
|
105
|
+
} catch (error) {
|
|
106
|
+
// Fallback to expo-crypto if Node crypto fails
|
|
107
|
+
const Crypto = await initExpoCrypto();
|
|
108
|
+
const randomBytes = await Crypto.getRandomBytesAsync(32);
|
|
109
|
+
return Array.from(randomBytes)
|
|
110
|
+
.map((b: number) => b.toString(16).padStart(2, '0'))
|
|
111
|
+
.join('');
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
85
115
|
/**
|
|
86
116
|
* Hash a message using SHA-256
|
|
87
117
|
*/
|
|
@@ -27,7 +27,7 @@ const GroupedSectionComponent = ({ items }: GroupedSectionProps) => {
|
|
|
27
27
|
return (
|
|
28
28
|
<View style={{ width: '100%' }}>
|
|
29
29
|
{items.map((item, index) => (
|
|
30
|
-
<View key={item.id} style={{ marginBottom: index < items.length - 1 ? 4 : 0 }}>
|
|
30
|
+
<View key={`${item.id}-${index}`} style={{ marginBottom: index < items.length - 1 ? 4 : 0 }}>
|
|
31
31
|
<GroupedItem
|
|
32
32
|
icon={item.icon}
|
|
33
33
|
iconColor={item.iconColor}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import { useEffect, useRef, type FC } from 'react';
|
|
1
|
+
import { useEffect, useRef, useState, type FC } from 'react';
|
|
2
2
|
import { AppState } from 'react-native';
|
|
3
3
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
|
4
4
|
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
|
5
5
|
import { KeyboardProvider } from 'react-native-keyboard-controller';
|
|
6
6
|
import type { OxyProviderProps } from '../types/navigation';
|
|
7
7
|
import { OxyContextProvider } from '../context/OxyContext';
|
|
8
|
-
import {
|
|
8
|
+
import { QueryClientProvider, focusManager, onlineManager } from '@tanstack/react-query';
|
|
9
9
|
import { setupFonts } from './FontLoader';
|
|
10
10
|
import BottomSheetRouter from './BottomSheetRouter';
|
|
11
11
|
import { Toaster } from '../../lib/sonner';
|
|
12
|
+
import { createQueryClient } from '../hooks/queryClient';
|
|
13
|
+
import { useStorage } from '../hooks/useStorage';
|
|
12
14
|
|
|
13
15
|
// Initialize fonts automatically
|
|
14
16
|
setupFonts();
|
|
@@ -26,31 +28,32 @@ const OxyProvider: FC<OxyProviderProps> = ({
|
|
|
26
28
|
onAuthStateChange,
|
|
27
29
|
storageKeyPrefix,
|
|
28
30
|
baseURL,
|
|
29
|
-
queryClient,
|
|
31
|
+
queryClient: providedQueryClient,
|
|
30
32
|
}) => {
|
|
31
33
|
// contextOnly is retained for backwards compatibility while the UI-only
|
|
32
34
|
// bottom sheet experience is removed. At the moment both modes behave the same.
|
|
33
35
|
void contextOnly;
|
|
34
36
|
|
|
35
|
-
//
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
37
|
+
// Get storage for query persistence
|
|
38
|
+
const { storage, isReady: isStorageReady } = useStorage();
|
|
39
|
+
|
|
40
|
+
// Initialize React Query Client with persistence
|
|
41
|
+
const queryClientRef = useRef<ReturnType<typeof createQueryClient> | null>(null);
|
|
42
|
+
const [queryClient, setQueryClient] = useState<ReturnType<typeof createQueryClient> | null>(null);
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
if (providedQueryClient) {
|
|
46
|
+
queryClientRef.current = providedQueryClient;
|
|
47
|
+
setQueryClient(providedQueryClient);
|
|
48
|
+
} else if (isStorageReady) {
|
|
49
|
+
// Create query client with persistence once storage is ready
|
|
50
|
+
if (!queryClientRef.current) {
|
|
51
|
+
const client = createQueryClient(storage);
|
|
52
|
+
queryClientRef.current = client;
|
|
53
|
+
setQueryClient(client);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}, [providedQueryClient, isStorageReady, storage]);
|
|
54
57
|
|
|
55
58
|
// Hook React Query focus manager into React Native AppState
|
|
56
59
|
useEffect(() => {
|
|
@@ -62,28 +65,79 @@ const OxyProvider: FC<OxyProviderProps> = ({
|
|
|
62
65
|
};
|
|
63
66
|
}, []);
|
|
64
67
|
|
|
68
|
+
// Setup network status monitoring for offline detection
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
let cleanup: (() => void) | undefined;
|
|
71
|
+
|
|
72
|
+
const setupNetworkMonitoring = async () => {
|
|
73
|
+
try {
|
|
74
|
+
// For React Native, try to use NetInfo
|
|
75
|
+
if (typeof window === 'undefined' || (typeof navigator !== 'undefined' && navigator.product === 'ReactNative')) {
|
|
76
|
+
try {
|
|
77
|
+
const NetInfo = await import('@react-native-community/netinfo');
|
|
78
|
+
const state = await NetInfo.default.fetch();
|
|
79
|
+
onlineManager.setOnline(state.isConnected ?? true);
|
|
80
|
+
|
|
81
|
+
const unsubscribe = NetInfo.default.addEventListener((state: { isConnected: boolean | null }) => {
|
|
82
|
+
onlineManager.setOnline(state.isConnected ?? true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
cleanup = () => unsubscribe();
|
|
86
|
+
} catch {
|
|
87
|
+
// NetInfo not available, default to online
|
|
88
|
+
onlineManager.setOnline(true);
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
// For web, use navigator.onLine
|
|
92
|
+
onlineManager.setOnline(navigator.onLine);
|
|
93
|
+
const handleOnline = () => onlineManager.setOnline(true);
|
|
94
|
+
const handleOffline = () => onlineManager.setOnline(false);
|
|
95
|
+
|
|
96
|
+
window.addEventListener('online', handleOnline);
|
|
97
|
+
window.addEventListener('offline', handleOffline);
|
|
98
|
+
|
|
99
|
+
cleanup = () => {
|
|
100
|
+
window.removeEventListener('online', handleOnline);
|
|
101
|
+
window.removeEventListener('offline', handleOffline);
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
} catch (error) {
|
|
105
|
+
// Default to online if detection fails
|
|
106
|
+
onlineManager.setOnline(true);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
setupNetworkMonitoring();
|
|
111
|
+
|
|
112
|
+
return () => {
|
|
113
|
+
cleanup?.();
|
|
114
|
+
};
|
|
115
|
+
}, []);
|
|
116
|
+
|
|
65
117
|
// Ensure we have a valid QueryClient
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
118
|
+
if (!queryClient) {
|
|
119
|
+
// Return loading state or fallback
|
|
120
|
+
return null;
|
|
69
121
|
}
|
|
70
122
|
|
|
71
123
|
return (
|
|
72
124
|
<SafeAreaProvider>
|
|
73
125
|
<GestureHandlerRootView style={{ flex: 1 }}>
|
|
74
126
|
<KeyboardProvider>
|
|
75
|
-
|
|
76
|
-
<
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
127
|
+
{queryClient && (
|
|
128
|
+
<QueryClientProvider client={queryClient}>
|
|
129
|
+
<OxyContextProvider
|
|
130
|
+
oxyServices={oxyServices as any}
|
|
131
|
+
baseURL={baseURL}
|
|
132
|
+
storageKeyPrefix={storageKeyPrefix}
|
|
133
|
+
onAuthStateChange={onAuthStateChange as any}
|
|
134
|
+
>
|
|
135
|
+
{children}
|
|
136
|
+
<BottomSheetRouter />
|
|
137
|
+
<Toaster />
|
|
138
|
+
</OxyContextProvider>
|
|
139
|
+
</QueryClientProvider>
|
|
140
|
+
)}
|
|
87
141
|
</KeyboardProvider>
|
|
88
142
|
</GestureHandlerRootView>
|
|
89
143
|
</SafeAreaProvider>
|
|
@@ -36,7 +36,7 @@ export const EditDisplayNameModal: React.FC<EditDisplayNameModalProps> = ({
|
|
|
36
36
|
const colorScheme = useColorScheme();
|
|
37
37
|
const themeStyles = useThemeStyles(theme || 'light', colorScheme);
|
|
38
38
|
const colors = themeStyles.colors;
|
|
39
|
-
const {
|
|
39
|
+
const { saveProfile, isSaving } = useProfileEditing();
|
|
40
40
|
|
|
41
41
|
const [displayName, setDisplayName] = useState(initialDisplayName);
|
|
42
42
|
const [lastName, setLastName] = useState(initialLastName);
|
|
@@ -48,8 +48,6 @@ export const EditDisplayNameModal: React.FC<EditDisplayNameModalProps> = ({
|
|
|
48
48
|
}
|
|
49
49
|
}, [visible, initialDisplayName, initialLastName]);
|
|
50
50
|
|
|
51
|
-
const { saveProfile } = useProfileEditing();
|
|
52
|
-
|
|
53
51
|
const handleSave = async () => {
|
|
54
52
|
const updates = {
|
|
55
53
|
displayName,
|