@oxyhq/services 5.16.44 → 5.17.0

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