@oxyhq/services 5.16.44 → 5.17.1

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