@oxyhq/services 5.16.39 → 5.16.41

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