@oxyhq/services 5.16.0 → 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.
Files changed (190) hide show
  1. package/lib/commonjs/core/mixins/OxyServices.assets.js +15 -0
  2. package/lib/commonjs/core/mixins/OxyServices.assets.js.map +1 -1
  3. package/lib/commonjs/core/mixins/OxyServices.user.js +14 -13
  4. package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -1
  5. package/lib/commonjs/crypto/keyManager.js +164 -3
  6. package/lib/commonjs/crypto/keyManager.js.map +1 -1
  7. package/lib/commonjs/crypto/signatureService.js +26 -0
  8. package/lib/commonjs/crypto/signatureService.js.map +1 -1
  9. package/lib/commonjs/index.js.map +1 -1
  10. package/lib/commonjs/ui/components/GroupedSection.js +1 -1
  11. package/lib/commonjs/ui/components/GroupedSection.js.map +1 -1
  12. package/lib/commonjs/ui/components/OxyProvider.js +71 -24
  13. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  14. package/lib/commonjs/ui/components/profile/EditDisplayNameModal.js +1 -4
  15. package/lib/commonjs/ui/components/profile/EditDisplayNameModal.js.map +1 -1
  16. package/lib/commonjs/ui/context/OxyContext.js +177 -4
  17. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  18. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +148 -49
  19. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  20. package/lib/commonjs/ui/context/hooks/useSessionManagement.js +22 -2
  21. package/lib/commonjs/ui/context/hooks/useSessionManagement.js.map +1 -1
  22. package/lib/commonjs/ui/hooks/mutations/index.js +28 -0
  23. package/lib/commonjs/ui/hooks/mutations/index.js.map +1 -0
  24. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +314 -0
  25. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -0
  26. package/lib/commonjs/ui/hooks/mutations/useServicesMutations.js +193 -0
  27. package/lib/commonjs/ui/hooks/mutations/useServicesMutations.js.map +1 -0
  28. package/lib/commonjs/ui/hooks/queries/index.js +39 -0
  29. package/lib/commonjs/ui/hooks/queries/index.js.map +1 -0
  30. package/lib/commonjs/ui/hooks/queries/queryKeys.js +85 -0
  31. package/lib/commonjs/ui/hooks/queries/queryKeys.js.map +1 -0
  32. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +145 -0
  33. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -0
  34. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js +138 -0
  35. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -0
  36. package/lib/commonjs/ui/hooks/queryClient.js +117 -0
  37. package/lib/commonjs/ui/hooks/queryClient.js.map +1 -0
  38. package/lib/commonjs/ui/hooks/useIdentityMutations.js +111 -0
  39. package/lib/commonjs/ui/hooks/useIdentityMutations.js.map +1 -0
  40. package/lib/commonjs/ui/hooks/useProfileEditing.js +42 -58
  41. package/lib/commonjs/ui/hooks/useProfileEditing.js.map +1 -1
  42. package/lib/commonjs/ui/hooks/useQueryClient.js +20 -0
  43. package/lib/commonjs/ui/hooks/useQueryClient.js.map +1 -0
  44. package/lib/commonjs/ui/hooks/useSessionManagement.js +22 -2
  45. package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
  46. package/lib/commonjs/ui/screens/AccountOverviewScreen.js +43 -42
  47. package/lib/commonjs/ui/screens/AccountOverviewScreen.js.map +1 -1
  48. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +63 -58
  49. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  50. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +6 -6
  51. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  52. package/lib/commonjs/ui/stores/accountStore.js +57 -42
  53. package/lib/commonjs/ui/stores/accountStore.js.map +1 -1
  54. package/lib/commonjs/ui/stores/authStore.js +4 -25
  55. package/lib/commonjs/ui/stores/authStore.js.map +1 -1
  56. package/lib/module/core/mixins/OxyServices.assets.js +15 -0
  57. package/lib/module/core/mixins/OxyServices.assets.js.map +1 -1
  58. package/lib/module/core/mixins/OxyServices.user.js +14 -13
  59. package/lib/module/core/mixins/OxyServices.user.js.map +1 -1
  60. package/lib/module/crypto/keyManager.js +164 -3
  61. package/lib/module/crypto/keyManager.js.map +1 -1
  62. package/lib/module/crypto/signatureService.js +26 -0
  63. package/lib/module/crypto/signatureService.js.map +1 -1
  64. package/lib/module/index.js.map +1 -1
  65. package/lib/module/ui/components/GroupedSection.js +1 -1
  66. package/lib/module/ui/components/GroupedSection.js.map +1 -1
  67. package/lib/module/ui/components/OxyProvider.js +72 -25
  68. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  69. package/lib/module/ui/components/profile/EditDisplayNameModal.js +1 -4
  70. package/lib/module/ui/components/profile/EditDisplayNameModal.js.map +1 -1
  71. package/lib/module/ui/context/OxyContext.js +176 -4
  72. package/lib/module/ui/context/OxyContext.js.map +1 -1
  73. package/lib/module/ui/context/hooks/useAuthOperations.js +148 -49
  74. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  75. package/lib/module/ui/context/hooks/useSessionManagement.js +22 -2
  76. package/lib/module/ui/context/hooks/useSessionManagement.js.map +1 -1
  77. package/lib/module/ui/hooks/mutations/index.js +6 -0
  78. package/lib/module/ui/hooks/mutations/index.js.map +1 -0
  79. package/lib/module/ui/hooks/mutations/useAccountMutations.js +308 -0
  80. package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -0
  81. package/lib/module/ui/hooks/mutations/useServicesMutations.js +185 -0
  82. package/lib/module/ui/hooks/mutations/useServicesMutations.js.map +1 -0
  83. package/lib/module/ui/hooks/queries/index.js +7 -0
  84. package/lib/module/ui/hooks/queries/index.js.map +1 -0
  85. package/lib/module/ui/hooks/queries/queryKeys.js +78 -0
  86. package/lib/module/ui/hooks/queries/queryKeys.js.map +1 -0
  87. package/lib/module/ui/hooks/queries/useAccountQueries.js +136 -0
  88. package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -0
  89. package/lib/module/ui/hooks/queries/useServicesQueries.js +130 -0
  90. package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -0
  91. package/lib/module/ui/hooks/queryClient.js +110 -0
  92. package/lib/module/ui/hooks/queryClient.js.map +1 -0
  93. package/lib/module/ui/hooks/useIdentityMutations.js +105 -0
  94. package/lib/module/ui/hooks/useIdentityMutations.js.map +1 -0
  95. package/lib/module/ui/hooks/useProfileEditing.js +43 -59
  96. package/lib/module/ui/hooks/useProfileEditing.js.map +1 -1
  97. package/lib/module/ui/hooks/useQueryClient.js +15 -0
  98. package/lib/module/ui/hooks/useQueryClient.js.map +1 -0
  99. package/lib/module/ui/hooks/useSessionManagement.js +22 -2
  100. package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
  101. package/lib/module/ui/screens/AccountOverviewScreen.js +43 -42
  102. package/lib/module/ui/screens/AccountOverviewScreen.js.map +1 -1
  103. package/lib/module/ui/screens/AccountSettingsScreen.js +63 -58
  104. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  105. package/lib/module/ui/screens/WelcomeNewUserScreen.js +6 -6
  106. package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  107. package/lib/module/ui/stores/accountStore.js +57 -42
  108. package/lib/module/ui/stores/accountStore.js.map +1 -1
  109. package/lib/module/ui/stores/authStore.js +4 -25
  110. package/lib/module/ui/stores/authStore.js.map +1 -1
  111. package/lib/typescript/core/mixins/OxyServices.assets.d.ts +7 -1
  112. package/lib/typescript/core/mixins/OxyServices.assets.d.ts.map +1 -1
  113. package/lib/typescript/core/mixins/OxyServices.user.d.ts +4 -5
  114. package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -1
  115. package/lib/typescript/core/mixins/index.d.ts +1 -1
  116. package/lib/typescript/core/mixins/index.d.ts.map +1 -1
  117. package/lib/typescript/crypto/keyManager.d.ts +19 -2
  118. package/lib/typescript/crypto/keyManager.d.ts.map +1 -1
  119. package/lib/typescript/crypto/signatureService.d.ts +5 -0
  120. package/lib/typescript/crypto/signatureService.d.ts.map +1 -1
  121. package/lib/typescript/index.d.ts +1 -1
  122. package/lib/typescript/index.d.ts.map +1 -1
  123. package/lib/typescript/models/interfaces.d.ts +21 -0
  124. package/lib/typescript/models/interfaces.d.ts.map +1 -1
  125. package/lib/typescript/ui/components/OxyProvider.d.ts.map +1 -1
  126. package/lib/typescript/ui/components/profile/EditDisplayNameModal.d.ts.map +1 -1
  127. package/lib/typescript/ui/context/OxyContext.d.ts +4 -0
  128. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  129. package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  130. package/lib/typescript/ui/context/hooks/useSessionManagement.d.ts +3 -1
  131. package/lib/typescript/ui/context/hooks/useSessionManagement.d.ts.map +1 -1
  132. package/lib/typescript/ui/hooks/mutations/index.d.ts +3 -0
  133. package/lib/typescript/ui/hooks/mutations/index.d.ts.map +1 -0
  134. package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts +25 -0
  135. package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -0
  136. package/lib/typescript/ui/hooks/mutations/useServicesMutations.d.ts +23 -0
  137. package/lib/typescript/ui/hooks/mutations/useServicesMutations.d.ts.map +1 -0
  138. package/lib/typescript/ui/hooks/queries/index.d.ts +4 -0
  139. package/lib/typescript/ui/hooks/queries/index.d.ts.map +1 -0
  140. package/lib/typescript/ui/hooks/queries/queryKeys.d.ts +56 -0
  141. package/lib/typescript/ui/hooks/queries/queryKeys.d.ts.map +1 -0
  142. package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts +41 -0
  143. package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -0
  144. package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts +34 -0
  145. package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts.map +1 -0
  146. package/lib/typescript/ui/hooks/queryClient.d.ts +19 -0
  147. package/lib/typescript/ui/hooks/queryClient.d.ts.map +1 -0
  148. package/lib/typescript/ui/hooks/useIdentityMutations.d.ts +29 -0
  149. package/lib/typescript/ui/hooks/useIdentityMutations.d.ts.map +1 -0
  150. package/lib/typescript/ui/hooks/useProfileEditing.d.ts.map +1 -1
  151. package/lib/typescript/ui/hooks/useQueryClient.d.ts +7 -0
  152. package/lib/typescript/ui/hooks/useQueryClient.d.ts.map +1 -0
  153. package/lib/typescript/ui/hooks/useSessionManagement.d.ts +3 -1
  154. package/lib/typescript/ui/hooks/useSessionManagement.d.ts.map +1 -1
  155. package/lib/typescript/ui/screens/AccountOverviewScreen.d.ts.map +1 -1
  156. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  157. package/lib/typescript/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
  158. package/lib/typescript/ui/stores/accountStore.d.ts.map +1 -1
  159. package/lib/typescript/ui/stores/authStore.d.ts +0 -4
  160. package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
  161. package/package.json +6 -5
  162. package/src/core/mixins/OxyServices.assets.ts +16 -1
  163. package/src/core/mixins/OxyServices.user.ts +17 -10
  164. package/src/crypto/keyManager.ts +177 -2
  165. package/src/crypto/signatureService.ts +30 -0
  166. package/src/index.ts +4 -1
  167. package/src/models/interfaces.ts +23 -0
  168. package/src/ui/components/GroupedSection.tsx +1 -1
  169. package/src/ui/components/OxyProvider.tsx +91 -37
  170. package/src/ui/components/profile/EditDisplayNameModal.tsx +1 -3
  171. package/src/ui/context/OxyContext.tsx +185 -2
  172. package/src/ui/context/hooks/useAuthOperations.ts +171 -58
  173. package/src/ui/context/hooks/useSessionManagement.ts +24 -1
  174. package/src/ui/hooks/mutations/index.ts +4 -0
  175. package/src/ui/hooks/mutations/useAccountMutations.ts +277 -0
  176. package/src/ui/hooks/mutations/useServicesMutations.ts +164 -0
  177. package/src/ui/hooks/queries/index.ts +5 -0
  178. package/src/ui/hooks/queries/queryKeys.ts +73 -0
  179. package/src/ui/hooks/queries/useAccountQueries.ts +126 -0
  180. package/src/ui/hooks/queries/useServicesQueries.ts +121 -0
  181. package/src/ui/hooks/queryClient.ts +112 -0
  182. package/src/ui/hooks/useIdentityMutations.ts +115 -0
  183. package/src/ui/hooks/useProfileEditing.ts +46 -60
  184. package/src/ui/hooks/useQueryClient.ts +17 -0
  185. package/src/ui/hooks/useSessionManagement.ts +24 -1
  186. package/src/ui/screens/AccountOverviewScreen.tsx +38 -46
  187. package/src/ui/screens/AccountSettingsScreen.tsx +54 -54
  188. package/src/ui/screens/WelcomeNewUserScreen.tsx +13 -12
  189. package/src/ui/stores/accountStore.ts +54 -43
  190. package/src/ui/stores/authStore.ts +3 -17
@@ -0,0 +1,164 @@
1
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
2
+ import type { User } from '../../../models/interfaces';
3
+ import { queryKeys, invalidateSessionQueries } from '../queries/queryKeys';
4
+ import { useOxy } from '../../context/OxyContext';
5
+ import { toast } from '../../../lib/sonner';
6
+
7
+ /**
8
+ * Switch active session
9
+ */
10
+ export const useSwitchSession = () => {
11
+ const { switchSession, activeSessionId } = useOxy();
12
+ const queryClient = useQueryClient();
13
+
14
+ return useMutation({
15
+ mutationFn: async (sessionId: string) => {
16
+ return await switchSession(sessionId);
17
+ },
18
+ onSuccess: (user) => {
19
+ // Invalidate all session queries
20
+ invalidateSessionQueries(queryClient);
21
+
22
+ // Update current user query
23
+ queryClient.setQueryData(queryKeys.accounts.current(), user);
24
+
25
+ // Invalidate account queries
26
+ queryClient.invalidateQueries({ queryKey: queryKeys.accounts.all });
27
+ },
28
+ onError: (error) => {
29
+ toast.error(error instanceof Error ? error.message : 'Failed to switch session');
30
+ },
31
+ });
32
+ };
33
+
34
+ /**
35
+ * Logout from a session
36
+ */
37
+ export const useLogoutSession = () => {
38
+ const { oxyServices, activeSessionId, sessions } = useOxy();
39
+ const queryClient = useQueryClient();
40
+
41
+ return useMutation({
42
+ mutationFn: async (targetSessionId?: string) => {
43
+ if (!activeSessionId) {
44
+ throw new Error('No active session');
45
+ }
46
+
47
+ const sessionToLogout = targetSessionId || activeSessionId;
48
+ await oxyServices.logoutSession(activeSessionId, sessionToLogout);
49
+
50
+ return sessionToLogout;
51
+ },
52
+ onMutate: async (targetSessionId) => {
53
+ // Cancel outgoing queries
54
+ await queryClient.cancelQueries({ queryKey: queryKeys.sessions.all });
55
+
56
+ // Snapshot previous sessions
57
+ const previousSessions = queryClient.getQueryData(queryKeys.sessions.list());
58
+
59
+ // Optimistically remove session
60
+ if (previousSessions) {
61
+ const sessionToLogout = targetSessionId || activeSessionId;
62
+ const updatedSessions = (previousSessions as any[]).filter(
63
+ (s: any) => s.sessionId !== sessionToLogout
64
+ );
65
+ queryClient.setQueryData(queryKeys.sessions.list(), updatedSessions);
66
+ }
67
+
68
+ return { previousSessions };
69
+ },
70
+ onError: (error, targetSessionId, context) => {
71
+ // Rollback on error
72
+ if (context?.previousSessions) {
73
+ queryClient.setQueryData(queryKeys.sessions.list(), context.previousSessions);
74
+ }
75
+ toast.error(error instanceof Error ? error.message : 'Failed to logout');
76
+ },
77
+ onSuccess: () => {
78
+ // Invalidate all session queries
79
+ invalidateSessionQueries(queryClient);
80
+ },
81
+ onSettled: () => {
82
+ queryClient.invalidateQueries({ queryKey: queryKeys.sessions.all });
83
+ },
84
+ });
85
+ };
86
+
87
+ /**
88
+ * Logout from all sessions
89
+ */
90
+ export const useLogoutAll = () => {
91
+ const { oxyServices, activeSessionId, clearSessionState } = useOxy();
92
+ const queryClient = useQueryClient();
93
+
94
+ return useMutation({
95
+ mutationFn: async () => {
96
+ if (!activeSessionId) {
97
+ throw new Error('No active session');
98
+ }
99
+
100
+ await oxyServices.logoutAllSessions(activeSessionId);
101
+ await clearSessionState();
102
+ },
103
+ onSuccess: () => {
104
+ // Clear all queries
105
+ queryClient.clear();
106
+ toast.success('Logged out from all sessions');
107
+ },
108
+ onError: (error) => {
109
+ toast.error(error instanceof Error ? error.message : 'Failed to logout from all sessions');
110
+ },
111
+ });
112
+ };
113
+
114
+ /**
115
+ * Update device name
116
+ */
117
+ export const useUpdateDeviceName = () => {
118
+ const { oxyServices, activeSessionId } = useOxy();
119
+ const queryClient = useQueryClient();
120
+
121
+ return useMutation({
122
+ mutationFn: async (deviceName: string) => {
123
+ if (!activeSessionId) {
124
+ throw new Error('No active session');
125
+ }
126
+
127
+ return await oxyServices.updateDeviceName(activeSessionId, deviceName);
128
+ },
129
+ onSuccess: () => {
130
+ // Invalidate device and session queries
131
+ queryClient.invalidateQueries({ queryKey: queryKeys.devices.all });
132
+ queryClient.invalidateQueries({ queryKey: queryKeys.sessions.all });
133
+ toast.success('Device name updated');
134
+ },
135
+ onError: (error) => {
136
+ toast.error(error instanceof Error ? error.message : 'Failed to update device name');
137
+ },
138
+ });
139
+ };
140
+
141
+ /**
142
+ * Remove a device
143
+ */
144
+ export const useRemoveDevice = () => {
145
+ const { oxyServices } = useOxy();
146
+ const queryClient = useQueryClient();
147
+
148
+ return useMutation({
149
+ mutationFn: async (deviceId: string) => {
150
+ await oxyServices.removeDevice(deviceId);
151
+ return deviceId;
152
+ },
153
+ onSuccess: () => {
154
+ // Invalidate device queries
155
+ queryClient.invalidateQueries({ queryKey: queryKeys.devices.all });
156
+ queryClient.invalidateQueries({ queryKey: queryKeys.sessions.all });
157
+ toast.success('Device removed');
158
+ },
159
+ onError: (error) => {
160
+ toast.error(error instanceof Error ? error.message : 'Failed to remove device');
161
+ },
162
+ });
163
+ };
164
+
@@ -0,0 +1,5 @@
1
+ // Export all query hooks
2
+ export * from './useAccountQueries';
3
+ export * from './useServicesQueries';
4
+ export * from './queryKeys';
5
+
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Centralized query keys for TanStack Query
3
+ *
4
+ * Following best practices:
5
+ * - Use arrays for hierarchical keys
6
+ * - Include all parameters in the key
7
+ * - Use consistent naming conventions
8
+ */
9
+
10
+ export const queryKeys = {
11
+ // Account queries
12
+ accounts: {
13
+ all: ['accounts'] as const,
14
+ lists: () => [...queryKeys.accounts.all, 'list'] as const,
15
+ list: (sessionIds: string[]) => [...queryKeys.accounts.lists(), sessionIds] as const,
16
+ details: () => [...queryKeys.accounts.all, 'detail'] as const,
17
+ detail: (sessionId: string) => [...queryKeys.accounts.details(), sessionId] as const,
18
+ current: () => [...queryKeys.accounts.all, 'current'] as const,
19
+ settings: () => [...queryKeys.accounts.all, 'settings'] as const,
20
+ },
21
+
22
+ // User queries
23
+ users: {
24
+ all: ['users'] as const,
25
+ lists: () => [...queryKeys.users.all, 'list'] as const,
26
+ list: (userIds: string[]) => [...queryKeys.users.lists(), userIds] as const,
27
+ details: () => [...queryKeys.users.all, 'detail'] as const,
28
+ detail: (userId: string) => [...queryKeys.users.details(), userId] as const,
29
+ profile: (sessionId: string) => [...queryKeys.users.details(), sessionId, 'profile'] as const,
30
+ },
31
+
32
+ // Session queries
33
+ sessions: {
34
+ all: ['sessions'] as const,
35
+ lists: () => [...queryKeys.sessions.all, 'list'] as const,
36
+ list: (userId?: string) => [...queryKeys.sessions.lists(), userId] as const,
37
+ details: () => [...queryKeys.sessions.all, 'detail'] as const,
38
+ detail: (sessionId: string) => [...queryKeys.sessions.details(), sessionId] as const,
39
+ active: () => [...queryKeys.sessions.all, 'active'] as const,
40
+ device: (deviceId: string) => [...queryKeys.sessions.all, 'device', deviceId] as const,
41
+ },
42
+
43
+ // Device queries
44
+ devices: {
45
+ all: ['devices'] as const,
46
+ lists: () => [...queryKeys.devices.all, 'list'] as const,
47
+ list: (userId?: string) => [...queryKeys.devices.lists(), userId] as const,
48
+ details: () => [...queryKeys.devices.all, 'detail'] as const,
49
+ detail: (deviceId: string) => [...queryKeys.devices.details(), deviceId] as const,
50
+ },
51
+ } as const;
52
+
53
+ /**
54
+ * Helper to invalidate all account-related queries
55
+ */
56
+ export const invalidateAccountQueries = (queryClient: any) => {
57
+ queryClient.invalidateQueries({ queryKey: queryKeys.accounts.all });
58
+ };
59
+
60
+ /**
61
+ * Helper to invalidate all user-related queries
62
+ */
63
+ export const invalidateUserQueries = (queryClient: any) => {
64
+ queryClient.invalidateQueries({ queryKey: queryKeys.users.all });
65
+ };
66
+
67
+ /**
68
+ * Helper to invalidate all session-related queries
69
+ */
70
+ export const invalidateSessionQueries = (queryClient: any) => {
71
+ queryClient.invalidateQueries({ queryKey: queryKeys.sessions.all });
72
+ };
73
+
@@ -0,0 +1,126 @@
1
+ import { useQuery, useQueries } from '@tanstack/react-query';
2
+ import type { User } from '../../../models/interfaces';
3
+ import type { OxyServices } from '../../../core';
4
+ import { queryKeys } from './queryKeys';
5
+ import { useOxy } from '../../context/OxyContext';
6
+
7
+ /**
8
+ * Get user profile by session ID
9
+ */
10
+ export const useUserProfile = (sessionId: string | null, options?: { enabled?: boolean }) => {
11
+ const { oxyServices } = useOxy();
12
+
13
+ return useQuery({
14
+ queryKey: queryKeys.users.profile(sessionId || ''),
15
+ queryFn: async () => {
16
+ if (!sessionId) {
17
+ throw new Error('Session ID is required');
18
+ }
19
+ return await oxyServices.getUserBySession(sessionId);
20
+ },
21
+ enabled: (options?.enabled !== false) && !!sessionId,
22
+ staleTime: 5 * 60 * 1000, // 5 minutes
23
+ gcTime: 30 * 60 * 1000, // 30 minutes
24
+ });
25
+ };
26
+
27
+ /**
28
+ * Get multiple user profiles by session IDs (batch query)
29
+ */
30
+ export const useUserProfiles = (sessionIds: string[], options?: { enabled?: boolean }) => {
31
+ const { oxyServices } = useOxy();
32
+
33
+ return useQueries({
34
+ queries: sessionIds.map((sessionId) => ({
35
+ queryKey: queryKeys.users.profile(sessionId),
36
+ queryFn: async () => {
37
+ const results = await oxyServices.getUsersBySessions([sessionId]);
38
+ return results[0]?.user || null;
39
+ },
40
+ enabled: (options?.enabled !== false) && !!sessionId,
41
+ staleTime: 5 * 60 * 1000,
42
+ gcTime: 30 * 60 * 1000,
43
+ })),
44
+ });
45
+ };
46
+
47
+ /**
48
+ * Get current authenticated user
49
+ */
50
+ export const useCurrentUser = (options?: { enabled?: boolean }) => {
51
+ const { oxyServices, activeSessionId, isAuthenticated } = useOxy();
52
+
53
+ return useQuery({
54
+ queryKey: queryKeys.accounts.current(),
55
+ queryFn: async () => {
56
+ if (!activeSessionId) {
57
+ throw new Error('No active session');
58
+ }
59
+ return await oxyServices.getUserBySession(activeSessionId);
60
+ },
61
+ enabled: (options?.enabled !== false) && isAuthenticated && !!activeSessionId,
62
+ staleTime: 1 * 60 * 1000, // 1 minute for current user
63
+ gcTime: 30 * 60 * 1000,
64
+ });
65
+ };
66
+
67
+ /**
68
+ * Get user by ID
69
+ */
70
+ export const useUserById = (userId: string | null, options?: { enabled?: boolean }) => {
71
+ const { oxyServices } = useOxy();
72
+
73
+ return useQuery({
74
+ queryKey: queryKeys.users.detail(userId || ''),
75
+ queryFn: async () => {
76
+ if (!userId) {
77
+ throw new Error('User ID is required');
78
+ }
79
+ return await oxyServices.getUserById(userId);
80
+ },
81
+ enabled: (options?.enabled !== false) && !!userId,
82
+ staleTime: 5 * 60 * 1000,
83
+ gcTime: 30 * 60 * 1000,
84
+ });
85
+ };
86
+
87
+ /**
88
+ * Get user profile by username
89
+ */
90
+ export const useUserByUsername = (username: string | null, options?: { enabled?: boolean }) => {
91
+ const { oxyServices } = useOxy();
92
+
93
+ return useQuery({
94
+ queryKey: [...queryKeys.users.details(), 'username', username || ''],
95
+ queryFn: async () => {
96
+ if (!username) {
97
+ throw new Error('Username is required');
98
+ }
99
+ return await oxyServices.getProfileByUsername(username);
100
+ },
101
+ enabled: (options?.enabled !== false) && !!username,
102
+ staleTime: 5 * 60 * 1000,
103
+ gcTime: 30 * 60 * 1000,
104
+ });
105
+ };
106
+
107
+ /**
108
+ * Batch get users by session IDs (optimized single API call)
109
+ */
110
+ export const useUsersBySessions = (sessionIds: string[], options?: { enabled?: boolean }) => {
111
+ const { oxyServices } = useOxy();
112
+
113
+ return useQuery({
114
+ queryKey: queryKeys.accounts.list(sessionIds),
115
+ queryFn: async () => {
116
+ if (sessionIds.length === 0) {
117
+ return [];
118
+ }
119
+ return await oxyServices.getUsersBySessions(sessionIds);
120
+ },
121
+ enabled: (options?.enabled !== false) && sessionIds.length > 0,
122
+ staleTime: 5 * 60 * 1000,
123
+ gcTime: 30 * 60 * 1000,
124
+ });
125
+ };
126
+
@@ -0,0 +1,121 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import type { ClientSession } from '../../../models/session';
3
+ import { queryKeys } from './queryKeys';
4
+ import { useOxy } from '../../context/OxyContext';
5
+ import { fetchSessionsWithFallback, mapSessionsToClient } from '../../context/utils/sessionHelpers';
6
+
7
+ /**
8
+ * Get all active sessions for the current user
9
+ */
10
+ export const useSessions = (userId?: string, options?: { enabled?: boolean }) => {
11
+ const { oxyServices, activeSessionId } = useOxy();
12
+
13
+ return useQuery({
14
+ queryKey: queryKeys.sessions.list(userId),
15
+ queryFn: async () => {
16
+ if (!activeSessionId) {
17
+ throw new Error('No active session');
18
+ }
19
+
20
+ const sessions = await fetchSessionsWithFallback(oxyServices, activeSessionId, {
21
+ fallbackDeviceId: undefined,
22
+ fallbackUserId: userId,
23
+ });
24
+
25
+ return mapSessionsToClient(sessions, activeSessionId);
26
+ },
27
+ enabled: (options?.enabled !== false) && !!activeSessionId,
28
+ staleTime: 2 * 60 * 1000, // 2 minutes (sessions change frequently)
29
+ gcTime: 10 * 60 * 1000, // 10 minutes
30
+ });
31
+ };
32
+
33
+ /**
34
+ * Get specific session by ID
35
+ */
36
+ export const useSession = (sessionId: string | null, options?: { enabled?: boolean }) => {
37
+ const { oxyServices } = useOxy();
38
+
39
+ return useQuery({
40
+ queryKey: queryKeys.sessions.detail(sessionId || ''),
41
+ queryFn: async () => {
42
+ if (!sessionId) {
43
+ throw new Error('Session ID is required');
44
+ }
45
+
46
+ const validation = await oxyServices.validateSession(sessionId, { useHeaderValidation: true });
47
+ if (!validation?.valid || !validation.user) {
48
+ throw new Error('Session not found or invalid');
49
+ }
50
+
51
+ const now = new Date();
52
+ return {
53
+ sessionId,
54
+ deviceId: '', // Device ID not available from validation response
55
+ expiresAt: validation.expiresAt || new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(),
56
+ lastActive: validation.lastActivity || now.toISOString(),
57
+ userId: validation.user.id?.toString() ?? '',
58
+ isCurrent: false,
59
+ } as ClientSession;
60
+ },
61
+ enabled: (options?.enabled !== false) && !!sessionId,
62
+ staleTime: 2 * 60 * 1000,
63
+ gcTime: 10 * 60 * 1000,
64
+ });
65
+ };
66
+
67
+ /**
68
+ * Get device sessions for the current active session
69
+ */
70
+ export const useDeviceSessions = (options?: { enabled?: boolean }) => {
71
+ const { oxyServices, activeSessionId } = useOxy();
72
+
73
+ return useQuery({
74
+ queryKey: queryKeys.sessions.active(),
75
+ queryFn: async () => {
76
+ if (!activeSessionId) {
77
+ throw new Error('No active session');
78
+ }
79
+
80
+ return await oxyServices.getDeviceSessions(activeSessionId);
81
+ },
82
+ enabled: (options?.enabled !== false) && !!activeSessionId,
83
+ staleTime: 2 * 60 * 1000,
84
+ gcTime: 10 * 60 * 1000,
85
+ });
86
+ };
87
+
88
+ /**
89
+ * Get user devices
90
+ */
91
+ export const useUserDevices = (options?: { enabled?: boolean }) => {
92
+ const { oxyServices, isAuthenticated } = useOxy();
93
+
94
+ return useQuery({
95
+ queryKey: queryKeys.devices.list(),
96
+ queryFn: async () => {
97
+ return await oxyServices.getUserDevices();
98
+ },
99
+ enabled: (options?.enabled !== false) && isAuthenticated,
100
+ staleTime: 5 * 60 * 1000,
101
+ gcTime: 30 * 60 * 1000,
102
+ });
103
+ };
104
+
105
+ /**
106
+ * Get security information
107
+ */
108
+ export const useSecurityInfo = (options?: { enabled?: boolean }) => {
109
+ const { oxyServices, isAuthenticated } = useOxy();
110
+
111
+ return useQuery({
112
+ queryKey: [...queryKeys.devices.all, 'security'],
113
+ queryFn: async () => {
114
+ return await oxyServices.getSecurityInfo();
115
+ },
116
+ enabled: (options?.enabled !== false) && isAuthenticated,
117
+ staleTime: 5 * 60 * 1000,
118
+ gcTime: 30 * 60 * 1000,
119
+ });
120
+ };
121
+
@@ -0,0 +1,112 @@
1
+ import { QueryClient } from '@tanstack/react-query';
2
+ import type { StorageInterface } from '../utils/storageHelpers';
3
+
4
+ const QUERY_CACHE_KEY = 'oxy_query_cache';
5
+ const QUERY_CACHE_VERSION = '1';
6
+
7
+ /**
8
+ * Custom persistence adapter for TanStack Query using our StorageInterface
9
+ */
10
+ export const createPersistenceAdapter = (storage: StorageInterface) => {
11
+ return {
12
+ persistClient: async (client: any) => {
13
+ try {
14
+ const serialized = JSON.stringify({
15
+ clientState: client,
16
+ timestamp: Date.now(),
17
+ version: QUERY_CACHE_VERSION,
18
+ });
19
+ await storage.setItem(QUERY_CACHE_KEY, serialized);
20
+ } catch (error) {
21
+ if (__DEV__) {
22
+ console.warn('[QueryClient] Failed to persist cache:', error);
23
+ }
24
+ }
25
+ },
26
+ restoreClient: async () => {
27
+ try {
28
+ const cached = await storage.getItem(QUERY_CACHE_KEY);
29
+ if (!cached) return undefined;
30
+
31
+ const parsed = JSON.parse(cached);
32
+
33
+ // Check version compatibility
34
+ if (parsed.version !== QUERY_CACHE_VERSION) {
35
+ // Clear old cache on version mismatch
36
+ await storage.removeItem(QUERY_CACHE_KEY);
37
+ return undefined;
38
+ }
39
+
40
+ // Check if cache is too old (30 days)
41
+ const maxAge = 30 * 24 * 60 * 60 * 1000;
42
+ if (parsed.timestamp && Date.now() - parsed.timestamp > maxAge) {
43
+ await storage.removeItem(QUERY_CACHE_KEY);
44
+ return undefined;
45
+ }
46
+
47
+ return parsed.clientState;
48
+ } catch (error) {
49
+ if (__DEV__) {
50
+ console.warn('[QueryClient] Failed to restore cache:', error);
51
+ }
52
+ return undefined;
53
+ }
54
+ },
55
+ removeClient: async () => {
56
+ try {
57
+ await storage.removeItem(QUERY_CACHE_KEY);
58
+ } catch (error) {
59
+ if (__DEV__) {
60
+ console.warn('[QueryClient] Failed to remove cache:', error);
61
+ }
62
+ }
63
+ },
64
+ };
65
+ };
66
+
67
+ /**
68
+ * Create a QueryClient with offline-first configuration
69
+ */
70
+ export const createQueryClient = (storage?: StorageInterface | null): QueryClient => {
71
+ const client = new QueryClient({
72
+ defaultOptions: {
73
+ queries: {
74
+ // Data is fresh for 5 minutes
75
+ staleTime: 5 * 60 * 1000,
76
+ // Keep unused data in cache for 30 minutes
77
+ gcTime: 30 * 60 * 1000,
78
+ // Retry 3 times with exponential backoff
79
+ retry: 3,
80
+ retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
81
+ // Refetch on reconnect
82
+ refetchOnReconnect: true,
83
+ // Don't refetch on window focus (better for mobile)
84
+ refetchOnWindowFocus: false,
85
+ // Offline-first: use cache when offline
86
+ networkMode: 'offlineFirst',
87
+ },
88
+ mutations: {
89
+ // Retry once for mutations
90
+ retry: 1,
91
+ // Offline-first: queue mutations when offline
92
+ networkMode: 'offlineFirst',
93
+ },
94
+ },
95
+ });
96
+
97
+ // Note: Persistence is handled by TanStack Query's built-in persistence
98
+ // For now, we rely on the query client's default behavior with networkMode: 'offlineFirst'
99
+ // The cache will be available in memory and queries will use cached data when offline
100
+ // Full persistence to AsyncStorage can be added later with @tanstack/react-query-persist-client if needed
101
+
102
+ return client;
103
+ };
104
+
105
+ /**
106
+ * Clear persisted query cache
107
+ */
108
+ export const clearQueryCache = async (storage: StorageInterface): Promise<void> => {
109
+ const adapter = createPersistenceAdapter(storage);
110
+ await adapter.removeClient();
111
+ };
112
+