@oxyhq/services 16.0.0 → 18.0.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 (278) hide show
  1. package/README.md +77 -98
  2. package/lib/commonjs/index.js +40 -51
  3. package/lib/commonjs/index.js.map +1 -1
  4. package/lib/commonjs/ui/client.js +0 -14
  5. package/lib/commonjs/ui/client.js.map +1 -1
  6. package/lib/commonjs/ui/components/OxyAccountDialog.js +742 -0
  7. package/lib/commonjs/ui/components/OxyAccountDialog.js.map +1 -0
  8. package/lib/commonjs/ui/components/OxyConsentScreen.js +461 -0
  9. package/lib/commonjs/ui/components/OxyConsentScreen.js.map +1 -0
  10. package/lib/commonjs/ui/components/OxyProvider.js +13 -6
  11. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  12. package/lib/commonjs/ui/components/OxySignInButton.js +180 -22
  13. package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
  14. package/lib/commonjs/ui/components/ProfileButton.js +25 -112
  15. package/lib/commonjs/ui/components/ProfileButton.js.map +1 -1
  16. package/lib/commonjs/ui/components/RequireOxyAuth.js +299 -0
  17. package/lib/commonjs/ui/components/RequireOxyAuth.js.map +1 -0
  18. package/lib/commonjs/ui/components/oauthNavigation.js +73 -0
  19. package/lib/commonjs/ui/components/oauthNavigation.js.map +1 -0
  20. package/lib/commonjs/ui/context/OxyContext.js +122 -4
  21. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  22. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +1 -1
  23. package/lib/commonjs/ui/hooks/useAuth.js +19 -30
  24. package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
  25. package/lib/commonjs/ui/hooks/useSwitchableAccounts.js +37 -267
  26. package/lib/commonjs/ui/hooks/useSwitchableAccounts.js.map +1 -1
  27. package/lib/commonjs/ui/index.js +5 -40
  28. package/lib/commonjs/ui/index.js.map +1 -1
  29. package/lib/commonjs/ui/navigation/accountDialogManager.js +91 -0
  30. package/lib/commonjs/ui/navigation/accountDialogManager.js.map +1 -0
  31. package/lib/commonjs/ui/navigation/routes.js +0 -2
  32. package/lib/commonjs/ui/navigation/routes.js.map +1 -1
  33. package/lib/commonjs/ui/screens/ManageAccountScreen.js +4 -3
  34. package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
  35. package/lib/commonjs/ui/session/createSessionClient.js +10 -2
  36. package/lib/commonjs/ui/session/createSessionClient.js.map +1 -1
  37. package/lib/module/index.js +19 -16
  38. package/lib/module/index.js.map +1 -1
  39. package/lib/module/ui/client.js +1 -2
  40. package/lib/module/ui/client.js.map +1 -1
  41. package/lib/module/ui/components/OxyAccountDialog.js +739 -0
  42. package/lib/module/ui/components/OxyAccountDialog.js.map +1 -0
  43. package/lib/module/ui/components/OxyConsentScreen.js +454 -0
  44. package/lib/module/ui/components/OxyConsentScreen.js.map +1 -0
  45. package/lib/module/ui/components/OxyProvider.js +13 -6
  46. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  47. package/lib/module/ui/components/OxySignInButton.js +181 -22
  48. package/lib/module/ui/components/OxySignInButton.js.map +1 -1
  49. package/lib/module/ui/components/ProfileButton.js +26 -112
  50. package/lib/module/ui/components/ProfileButton.js.map +1 -1
  51. package/lib/module/ui/components/RequireOxyAuth.js +294 -0
  52. package/lib/module/ui/components/RequireOxyAuth.js.map +1 -0
  53. package/lib/module/ui/components/oauthNavigation.js +70 -0
  54. package/lib/module/ui/components/oauthNavigation.js.map +1 -0
  55. package/lib/module/ui/context/OxyContext.js +123 -5
  56. package/lib/module/ui/context/OxyContext.js.map +1 -1
  57. package/lib/module/ui/hooks/mutations/useAccountMutations.js +1 -1
  58. package/lib/module/ui/hooks/useAuth.js +19 -30
  59. package/lib/module/ui/hooks/useAuth.js.map +1 -1
  60. package/lib/module/ui/hooks/useSwitchableAccounts.js +38 -267
  61. package/lib/module/ui/hooks/useSwitchableAccounts.js.map +1 -1
  62. package/lib/module/ui/index.js +1 -5
  63. package/lib/module/ui/index.js.map +1 -1
  64. package/lib/module/ui/navigation/accountDialogManager.js +81 -0
  65. package/lib/module/ui/navigation/accountDialogManager.js.map +1 -0
  66. package/lib/module/ui/navigation/routes.js +0 -2
  67. package/lib/module/ui/navigation/routes.js.map +1 -1
  68. package/lib/module/ui/screens/ManageAccountScreen.js +4 -3
  69. package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
  70. package/lib/module/ui/session/createSessionClient.js +10 -2
  71. package/lib/module/ui/session/createSessionClient.js.map +1 -1
  72. package/lib/typescript/commonjs/index.d.ts +8 -12
  73. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  74. package/lib/typescript/commonjs/ui/client.d.ts +0 -4
  75. package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
  76. package/lib/typescript/commonjs/ui/components/OxyAccountDialog.d.ts +51 -0
  77. package/lib/typescript/commonjs/ui/components/OxyAccountDialog.d.ts.map +1 -0
  78. package/lib/typescript/commonjs/ui/components/OxyConsentScreen.d.ts +45 -0
  79. package/lib/typescript/commonjs/ui/components/OxyConsentScreen.d.ts.map +1 -0
  80. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts +2 -2
  81. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
  82. package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts +56 -0
  83. package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts.map +1 -1
  84. package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts +11 -23
  85. package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts.map +1 -1
  86. package/lib/typescript/commonjs/ui/components/RequireOxyAuth.d.ts +55 -0
  87. package/lib/typescript/commonjs/ui/components/RequireOxyAuth.d.ts.map +1 -0
  88. package/lib/typescript/commonjs/ui/components/oauthNavigation.d.ts +36 -0
  89. package/lib/typescript/commonjs/ui/components/oauthNavigation.d.ts.map +1 -0
  90. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +27 -1
  91. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  92. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  93. package/lib/typescript/commonjs/ui/hooks/queries/useServicesQueries.d.ts +1 -1
  94. package/lib/typescript/commonjs/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
  95. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
  96. package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts +13 -152
  97. package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
  98. package/lib/typescript/commonjs/ui/index.d.ts +2 -5
  99. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  100. package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts +49 -0
  101. package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts.map +1 -0
  102. package/lib/typescript/commonjs/ui/navigation/routes.d.ts +1 -1
  103. package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
  104. package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts +1 -1
  105. package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts +9 -2
  107. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -1
  108. package/lib/typescript/commonjs/ui/types/navigation.d.ts +27 -0
  109. package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
  110. package/lib/typescript/module/index.d.ts +8 -12
  111. package/lib/typescript/module/index.d.ts.map +1 -1
  112. package/lib/typescript/module/ui/client.d.ts +0 -4
  113. package/lib/typescript/module/ui/client.d.ts.map +1 -1
  114. package/lib/typescript/module/ui/components/OxyAccountDialog.d.ts +51 -0
  115. package/lib/typescript/module/ui/components/OxyAccountDialog.d.ts.map +1 -0
  116. package/lib/typescript/module/ui/components/OxyConsentScreen.d.ts +45 -0
  117. package/lib/typescript/module/ui/components/OxyConsentScreen.d.ts.map +1 -0
  118. package/lib/typescript/module/ui/components/OxyProvider.d.ts +2 -2
  119. package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
  120. package/lib/typescript/module/ui/components/OxySignInButton.d.ts +56 -0
  121. package/lib/typescript/module/ui/components/OxySignInButton.d.ts.map +1 -1
  122. package/lib/typescript/module/ui/components/ProfileButton.d.ts +11 -23
  123. package/lib/typescript/module/ui/components/ProfileButton.d.ts.map +1 -1
  124. package/lib/typescript/module/ui/components/RequireOxyAuth.d.ts +55 -0
  125. package/lib/typescript/module/ui/components/RequireOxyAuth.d.ts.map +1 -0
  126. package/lib/typescript/module/ui/components/oauthNavigation.d.ts +36 -0
  127. package/lib/typescript/module/ui/components/oauthNavigation.d.ts.map +1 -0
  128. package/lib/typescript/module/ui/context/OxyContext.d.ts +27 -1
  129. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  130. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  131. package/lib/typescript/module/ui/hooks/queries/useServicesQueries.d.ts +1 -1
  132. package/lib/typescript/module/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
  133. package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
  134. package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts +13 -152
  135. package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
  136. package/lib/typescript/module/ui/index.d.ts +2 -5
  137. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  138. package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts +49 -0
  139. package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts.map +1 -0
  140. package/lib/typescript/module/ui/navigation/routes.d.ts +1 -1
  141. package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
  142. package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts +1 -1
  143. package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  144. package/lib/typescript/module/ui/session/createSessionClient.d.ts +9 -2
  145. package/lib/typescript/module/ui/session/createSessionClient.d.ts.map +1 -1
  146. package/lib/typescript/module/ui/types/navigation.d.ts +27 -0
  147. package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
  148. package/package.json +4 -4
  149. package/src/index.ts +33 -27
  150. package/src/ui/client.ts +0 -4
  151. package/src/ui/components/OxyAccountDialog.tsx +735 -0
  152. package/src/ui/components/OxyConsentScreen.tsx +456 -0
  153. package/src/ui/components/OxyProvider.tsx +14 -6
  154. package/src/ui/components/OxySignInButton.tsx +227 -22
  155. package/src/ui/components/ProfileButton.tsx +23 -126
  156. package/src/ui/components/RequireOxyAuth.tsx +266 -0
  157. package/src/ui/components/__tests__/OxyConsentScreen.test.tsx +130 -0
  158. package/src/ui/components/oauthNavigation.ts +89 -0
  159. package/src/ui/context/OxyContext.tsx +176 -9
  160. package/src/ui/context/__tests__/OxyContext.coldBoot.test.tsx +165 -0
  161. package/src/ui/hooks/mutations/useAccountMutations.ts +1 -1
  162. package/src/ui/hooks/useAuth.ts +19 -31
  163. package/src/ui/hooks/useSwitchableAccounts.ts +42 -376
  164. package/src/ui/index.ts +2 -5
  165. package/src/ui/navigation/accountDialogManager.ts +84 -0
  166. package/src/ui/navigation/routes.ts +0 -4
  167. package/src/ui/screens/ManageAccountScreen.tsx +3 -2
  168. package/src/ui/session/createSessionClient.ts +9 -1
  169. package/src/ui/types/navigation.ts +27 -0
  170. package/lib/commonjs/ui/components/AccountMenu.js +0 -598
  171. package/lib/commonjs/ui/components/AccountMenu.js.map +0 -1
  172. package/lib/commonjs/ui/components/AccountMenuButton.js +0 -135
  173. package/lib/commonjs/ui/components/AccountMenuButton.js.map +0 -1
  174. package/lib/commonjs/ui/components/AccountSwitcher.js +0 -780
  175. package/lib/commonjs/ui/components/AccountSwitcher.js.map +0 -1
  176. package/lib/commonjs/ui/components/AnotherDeviceQR.js +0 -116
  177. package/lib/commonjs/ui/components/AnotherDeviceQR.js.map +0 -1
  178. package/lib/commonjs/ui/components/ProfileMenu.js +0 -518
  179. package/lib/commonjs/ui/components/ProfileMenu.js.map +0 -1
  180. package/lib/commonjs/ui/components/SignInAccountChooser.js +0 -189
  181. package/lib/commonjs/ui/components/SignInAccountChooser.js.map +0 -1
  182. package/lib/commonjs/ui/components/SignInModal.js +0 -600
  183. package/lib/commonjs/ui/components/SignInModal.js.map +0 -1
  184. package/lib/commonjs/ui/hooks/useOxyAuthSession.js +0 -501
  185. package/lib/commonjs/ui/hooks/useOxyAuthSession.js.map +0 -1
  186. package/lib/commonjs/ui/hooks/usePasswordSignIn.js +0 -170
  187. package/lib/commonjs/ui/hooks/usePasswordSignIn.js.map +0 -1
  188. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +0 -51
  189. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +0 -1
  190. package/lib/commonjs/ui/screens/OxyAuthScreen.js +0 -312
  191. package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +0 -1
  192. package/lib/commonjs/utils/deviceFlowSignIn.js +0 -109
  193. package/lib/commonjs/utils/deviceFlowSignIn.js.map +0 -1
  194. package/lib/module/ui/components/AccountMenu.js +0 -593
  195. package/lib/module/ui/components/AccountMenu.js.map +0 -1
  196. package/lib/module/ui/components/AccountMenuButton.js +0 -130
  197. package/lib/module/ui/components/AccountMenuButton.js.map +0 -1
  198. package/lib/module/ui/components/AccountSwitcher.js +0 -774
  199. package/lib/module/ui/components/AccountSwitcher.js.map +0 -1
  200. package/lib/module/ui/components/AnotherDeviceQR.js +0 -110
  201. package/lib/module/ui/components/AnotherDeviceQR.js.map +0 -1
  202. package/lib/module/ui/components/ProfileMenu.js +0 -514
  203. package/lib/module/ui/components/ProfileMenu.js.map +0 -1
  204. package/lib/module/ui/components/SignInAccountChooser.js +0 -183
  205. package/lib/module/ui/components/SignInAccountChooser.js.map +0 -1
  206. package/lib/module/ui/components/SignInModal.js +0 -591
  207. package/lib/module/ui/components/SignInModal.js.map +0 -1
  208. package/lib/module/ui/hooks/useOxyAuthSession.js +0 -495
  209. package/lib/module/ui/hooks/useOxyAuthSession.js.map +0 -1
  210. package/lib/module/ui/hooks/usePasswordSignIn.js +0 -165
  211. package/lib/module/ui/hooks/usePasswordSignIn.js.map +0 -1
  212. package/lib/module/ui/screens/AccountSwitcherScreen.js +0 -48
  213. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +0 -1
  214. package/lib/module/ui/screens/OxyAuthScreen.js +0 -306
  215. package/lib/module/ui/screens/OxyAuthScreen.js.map +0 -1
  216. package/lib/module/utils/deviceFlowSignIn.js +0 -106
  217. package/lib/module/utils/deviceFlowSignIn.js.map +0 -1
  218. package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts +0 -47
  219. package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts.map +0 -1
  220. package/lib/typescript/commonjs/ui/components/AccountMenuButton.d.ts +0 -26
  221. package/lib/typescript/commonjs/ui/components/AccountMenuButton.d.ts.map +0 -1
  222. package/lib/typescript/commonjs/ui/components/AccountSwitcher.d.ts +0 -46
  223. package/lib/typescript/commonjs/ui/components/AccountSwitcher.d.ts.map +0 -1
  224. package/lib/typescript/commonjs/ui/components/AnotherDeviceQR.d.ts +0 -34
  225. package/lib/typescript/commonjs/ui/components/AnotherDeviceQR.d.ts.map +0 -1
  226. package/lib/typescript/commonjs/ui/components/ProfileMenu.d.ts +0 -49
  227. package/lib/typescript/commonjs/ui/components/ProfileMenu.d.ts.map +0 -1
  228. package/lib/typescript/commonjs/ui/components/SignInAccountChooser.d.ts +0 -29
  229. package/lib/typescript/commonjs/ui/components/SignInAccountChooser.d.ts.map +0 -1
  230. package/lib/typescript/commonjs/ui/components/SignInModal.d.ts +0 -28
  231. package/lib/typescript/commonjs/ui/components/SignInModal.d.ts.map +0 -1
  232. package/lib/typescript/commonjs/ui/hooks/useOxyAuthSession.d.ts +0 -105
  233. package/lib/typescript/commonjs/ui/hooks/useOxyAuthSession.d.ts.map +0 -1
  234. package/lib/typescript/commonjs/ui/hooks/usePasswordSignIn.d.ts +0 -31
  235. package/lib/typescript/commonjs/ui/hooks/usePasswordSignIn.d.ts.map +0 -1
  236. package/lib/typescript/commonjs/ui/screens/AccountSwitcherScreen.d.ts +0 -11
  237. package/lib/typescript/commonjs/ui/screens/AccountSwitcherScreen.d.ts.map +0 -1
  238. package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts +0 -22
  239. package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts.map +0 -1
  240. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts +0 -97
  241. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts.map +0 -1
  242. package/lib/typescript/module/ui/components/AccountMenu.d.ts +0 -47
  243. package/lib/typescript/module/ui/components/AccountMenu.d.ts.map +0 -1
  244. package/lib/typescript/module/ui/components/AccountMenuButton.d.ts +0 -26
  245. package/lib/typescript/module/ui/components/AccountMenuButton.d.ts.map +0 -1
  246. package/lib/typescript/module/ui/components/AccountSwitcher.d.ts +0 -46
  247. package/lib/typescript/module/ui/components/AccountSwitcher.d.ts.map +0 -1
  248. package/lib/typescript/module/ui/components/AnotherDeviceQR.d.ts +0 -34
  249. package/lib/typescript/module/ui/components/AnotherDeviceQR.d.ts.map +0 -1
  250. package/lib/typescript/module/ui/components/ProfileMenu.d.ts +0 -49
  251. package/lib/typescript/module/ui/components/ProfileMenu.d.ts.map +0 -1
  252. package/lib/typescript/module/ui/components/SignInAccountChooser.d.ts +0 -29
  253. package/lib/typescript/module/ui/components/SignInAccountChooser.d.ts.map +0 -1
  254. package/lib/typescript/module/ui/components/SignInModal.d.ts +0 -28
  255. package/lib/typescript/module/ui/components/SignInModal.d.ts.map +0 -1
  256. package/lib/typescript/module/ui/hooks/useOxyAuthSession.d.ts +0 -105
  257. package/lib/typescript/module/ui/hooks/useOxyAuthSession.d.ts.map +0 -1
  258. package/lib/typescript/module/ui/hooks/usePasswordSignIn.d.ts +0 -31
  259. package/lib/typescript/module/ui/hooks/usePasswordSignIn.d.ts.map +0 -1
  260. package/lib/typescript/module/ui/screens/AccountSwitcherScreen.d.ts +0 -11
  261. package/lib/typescript/module/ui/screens/AccountSwitcherScreen.d.ts.map +0 -1
  262. package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts +0 -22
  263. package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts.map +0 -1
  264. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts +0 -97
  265. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts.map +0 -1
  266. package/src/ui/components/AccountMenu.tsx +0 -645
  267. package/src/ui/components/AccountMenuButton.tsx +0 -126
  268. package/src/ui/components/AccountSwitcher.tsx +0 -751
  269. package/src/ui/components/AnotherDeviceQR.tsx +0 -119
  270. package/src/ui/components/ProfileMenu.tsx +0 -564
  271. package/src/ui/components/SignInAccountChooser.tsx +0 -162
  272. package/src/ui/components/SignInModal.tsx +0 -530
  273. package/src/ui/hooks/useOxyAuthSession.ts +0 -635
  274. package/src/ui/hooks/usePasswordSignIn.ts +0 -207
  275. package/src/ui/screens/AccountSwitcherScreen.tsx +0 -43
  276. package/src/ui/screens/OxyAuthScreen.tsx +0 -324
  277. package/src/utils/__tests__/deviceFlowSignIn.test.ts +0 -231
  278. package/src/utils/deviceFlowSignIn.ts +0 -152
@@ -1,207 +0,0 @@
1
- /**
2
- * `usePasswordSignIn` — the first-party password sign-in state machine.
3
- *
4
- * Drives the in-app "Sign in with Oxy" password flow shared by the web
5
- * `SignInModal` and the native `OxyAuthScreen`: identifier → password → optional
6
- * two-factor. It is a thin UI state machine over the context methods
7
- * `signInWithPassword` + `completeTwoFactorSignIn` (which own the network call,
8
- * the persisted-refresh commit, and the device-set registration) — so both
9
- * surfaces present an identical flow without re-implementing any transport.
10
- *
11
- * On a committed session `onSignedIn` fires (the modal/screen closes); the
12
- * device-first cold boot / SessionClient projection then drive the app into the
13
- * authenticated state.
14
- */
15
- import { useCallback, useMemo, useRef, useState } from 'react';
16
- import { useOxy } from '../context/OxyContext';
17
- import { handleAuthError } from '../utils/errorHandlers';
18
-
19
- export type PasswordSignInStep = 'identifier' | 'password' | 'twoFactor';
20
-
21
- export interface UsePasswordSignInOptions {
22
- /** Fired once a session has been committed (the surface should close). */
23
- onSignedIn?: () => void;
24
- }
25
-
26
- export interface UsePasswordSignInResult {
27
- step: PasswordSignInStep;
28
- identifier: string;
29
- setIdentifier: (value: string) => void;
30
- password: string;
31
- setPassword: (value: string) => void;
32
- /** The current 2FA input — a TOTP code or a backup code, per `useBackupCode`. */
33
- code: string;
34
- setCode: (value: string) => void;
35
- useBackupCode: boolean;
36
- setUseBackupCode: (value: boolean) => void;
37
- error: string | null;
38
- isSubmitting: boolean;
39
- /** Advance identifier → password (validates a non-empty identifier). */
40
- submitIdentifier: () => void;
41
- /** Submit the password: commits a one-step session, or advances to 2FA. */
42
- submitPassword: () => Promise<void>;
43
- /** Submit the 2FA code / backup code and commit the session. */
44
- submitTwoFactor: () => Promise<void>;
45
- /** Step back one screen (2FA → password → identifier), clearing the error. */
46
- back: () => void;
47
- /** Reset every field back to the identifier step. */
48
- reset: () => void;
49
- }
50
-
51
- export function usePasswordSignIn(options: UsePasswordSignInOptions = {}): UsePasswordSignInResult {
52
- const { signInWithPassword, completeTwoFactorSignIn } = useOxy();
53
- const { onSignedIn } = options;
54
-
55
- const [step, setStep] = useState<PasswordSignInStep>('identifier');
56
- const [identifier, setIdentifier] = useState('');
57
- const [password, setPassword] = useState('');
58
- const [code, setCode] = useState('');
59
- const [useBackupCode, setUseBackupCode] = useState(false);
60
- const [error, setError] = useState<string | null>(null);
61
- const [isSubmitting, setIsSubmitting] = useState(false);
62
- const [loginToken, setLoginToken] = useState<string | null>(null);
63
-
64
- // Synchronous in-flight guard. `isSubmitting` state drives the UI, but a rapid
65
- // double-tap fires both handlers in the SAME tick — before React re-renders —
66
- // so a state read would still see `false` on the second call and double-fire
67
- // the network request (rate-limit + race). This ref is set/checked
68
- // synchronously, so the second call is a true no-op.
69
- const submittingRef = useRef(false);
70
-
71
- const surfaceError = useCallback((err: unknown, defaultMessage: string): void => {
72
- setError(handleAuthError(err, { defaultMessage, code: 'PASSWORD_SIGN_IN_ERROR' }));
73
- }, []);
74
-
75
- const submitIdentifier = useCallback(() => {
76
- if (!identifier.trim()) {
77
- setError('Enter your username or email');
78
- return;
79
- }
80
- setError(null);
81
- setStep('password');
82
- }, [identifier]);
83
-
84
- const submitPassword = useCallback(async () => {
85
- // A second call while one is already in flight is a no-op (double-tap guard).
86
- if (submittingRef.current) {
87
- return;
88
- }
89
- if (!password) {
90
- setError('Enter your password');
91
- return;
92
- }
93
- setError(null);
94
- submittingRef.current = true;
95
- setIsSubmitting(true);
96
- try {
97
- const result = await signInWithPassword(identifier.trim(), password);
98
- if (result.status === '2fa_required') {
99
- setLoginToken(result.loginToken);
100
- setCode('');
101
- setUseBackupCode(false);
102
- setStep('twoFactor');
103
- return;
104
- }
105
- onSignedIn?.();
106
- } catch (err) {
107
- surfaceError(err, 'Sign in failed');
108
- } finally {
109
- submittingRef.current = false;
110
- setIsSubmitting(false);
111
- }
112
- }, [identifier, password, signInWithPassword, onSignedIn, surfaceError]);
113
-
114
- const submitTwoFactor = useCallback(async () => {
115
- // A second call while one is already in flight is a no-op (double-tap guard).
116
- if (submittingRef.current) {
117
- return;
118
- }
119
- if (!loginToken) {
120
- setError('Your sign-in session expired. Start again.');
121
- setStep('password');
122
- return;
123
- }
124
- if (!code.trim()) {
125
- setError(useBackupCode ? 'Enter a backup code' : 'Enter your 6-digit code');
126
- return;
127
- }
128
- setError(null);
129
- submittingRef.current = true;
130
- setIsSubmitting(true);
131
- try {
132
- await completeTwoFactorSignIn({
133
- loginToken,
134
- ...(useBackupCode ? { backupCode: code.trim() } : { token: code.trim() }),
135
- });
136
- onSignedIn?.();
137
- } catch (err) {
138
- surfaceError(err, 'Verification failed');
139
- } finally {
140
- submittingRef.current = false;
141
- setIsSubmitting(false);
142
- }
143
- }, [loginToken, code, useBackupCode, completeTwoFactorSignIn, onSignedIn, surfaceError]);
144
-
145
- const back = useCallback(() => {
146
- setError(null);
147
- setStep((current) => {
148
- if (current === 'twoFactor') {
149
- setLoginToken(null);
150
- setCode('');
151
- return 'password';
152
- }
153
- if (current === 'password') {
154
- setPassword('');
155
- return 'identifier';
156
- }
157
- return current;
158
- });
159
- }, []);
160
-
161
- const reset = useCallback(() => {
162
- setStep('identifier');
163
- setIdentifier('');
164
- setPassword('');
165
- setCode('');
166
- setUseBackupCode(false);
167
- setLoginToken(null);
168
- setError(null);
169
- submittingRef.current = false;
170
- setIsSubmitting(false);
171
- }, []);
172
-
173
- return useMemo(
174
- () => ({
175
- step,
176
- identifier,
177
- setIdentifier,
178
- password,
179
- setPassword,
180
- code,
181
- setCode,
182
- useBackupCode,
183
- setUseBackupCode,
184
- error,
185
- isSubmitting,
186
- submitIdentifier,
187
- submitPassword,
188
- submitTwoFactor,
189
- back,
190
- reset,
191
- }),
192
- [
193
- step,
194
- identifier,
195
- password,
196
- code,
197
- useBackupCode,
198
- error,
199
- isSubmitting,
200
- submitIdentifier,
201
- submitPassword,
202
- submitTwoFactor,
203
- back,
204
- reset,
205
- ],
206
- );
207
- }
@@ -1,43 +0,0 @@
1
- import type React from 'react';
2
- import { useCallback } from 'react';
3
- import { View } from 'react-native';
4
- import type { BaseScreenProps } from '../types/navigation';
5
- import Header from '../components/Header';
6
- import { AccountSwitcherView } from '../components/AccountSwitcher';
7
- import { useI18n } from '../hooks/useI18n';
8
-
9
- /**
10
- * Bottom-sheet route wrapper around {@link AccountSwitcherView}. Used when the
11
- * switcher is opened as a sheet (e.g. from the "Your accounts" entry in
12
- * ManageAccount) rather than as the header-chip popover (which uses the
13
- * `AccountSwitcher` modal directly).
14
- */
15
- const AccountSwitcherScreen: React.FC<BaseScreenProps> = ({ onClose, goBack, navigate }) => {
16
- const { t } = useI18n();
17
-
18
- const close = useCallback(() => {
19
- (onClose ?? goBack)?.();
20
- }, [onClose, goBack]);
21
-
22
- return (
23
- <View className="flex-1 bg-bg">
24
- <Header
25
- title={t('accountSwitcher.title') || 'Switch account'}
26
- onBack={goBack}
27
- onClose={onClose}
28
- showBackButton={!!goBack}
29
- showCloseButton={!!onClose}
30
- elevation="subtle"
31
- />
32
- <AccountSwitcherView
33
- onClose={close}
34
- onAddAccount={() => navigate?.('OxyAuth')}
35
- onNavigateManage={() => navigate?.('ManageAccount')}
36
- onCreateAccount={() => navigate?.('CreateAccount')}
37
- onOpenAccountSettings={(accountId) => navigate?.('AccountSettings', { accountId })}
38
- />
39
- </View>
40
- );
41
- };
42
-
43
- export default AccountSwitcherScreen;
@@ -1,324 +0,0 @@
1
- /**
2
- * OxyAuthScreen — Sign in with Oxy (native bottom-sheet container).
3
- *
4
- * Two phases, Google-style:
5
- * 1. Account chooser (FRONT screen, shown when the device/user already has
6
- * accounts): pick an account to continue as — one tap switches through the
7
- * SAME `switchToAccount` path the account switcher uses — or "Use another
8
- * account" to reveal the sign-in options.
9
- * 2. Sign-in options: the first-party password flow (identifier → password →
10
- * optional 2FA, `usePasswordSignIn`) as the PRIMARY action, with the
11
- * cross-app device flow (same-device deep-link + "sign in on another device"
12
- * QR) as a SECONDARY option below an "or" divider.
13
- *
14
- * The device-flow machinery lives in the shared `useOxyAuthSession` hook (the
15
- * web `SignInModal` consumes it too). This screen should NOT be used within the
16
- * Oxy Accounts app itself.
17
- */
18
-
19
- import type React from 'react';
20
- import { useCallback, useState } from 'react';
21
- import { View, TextInput, Linking, type TextStyle } from 'react-native';
22
- import type { BaseScreenProps } from '../types/navigation';
23
- import { useTheme } from '@oxyhq/bloom/theme';
24
- import { Button } from '@oxyhq/bloom/button';
25
- import { Loading } from '@oxyhq/bloom/loading';
26
- import { H4, Text } from '@oxyhq/bloom/typography';
27
- import { IconCircle } from '@oxyhq/bloom/icon-circle';
28
- import * as Icons from '@oxyhq/bloom/icons';
29
- import { toast } from '@oxyhq/bloom';
30
- import { isDev, logger as loggerUtil } from '@oxyhq/core';
31
- import { useOxy } from '../context/OxyContext';
32
- import OxyLogo from '../components/OxyLogo';
33
- import AnotherDeviceQR from '../components/AnotherDeviceQR';
34
- import LoadingState from '../components/LoadingState';
35
- import SignInAccountChooser from '../components/SignInAccountChooser';
36
- import { useSwitchableAccounts, type SwitchableAccount } from '../hooks/useSwitchableAccounts';
37
- import { useI18n } from '../hooks/useI18n';
38
- import { useOxyAuthSession, OXY_ACCOUNTS_WEB_URL } from '../hooks/useOxyAuthSession';
39
- import { usePasswordSignIn } from '../hooks/usePasswordSignIn';
40
-
41
- const OxyAuthScreen: React.FC<BaseScreenProps> = ({ goBack, onAuthenticated }) => {
42
- const bloomTheme = useTheme();
43
- const { oxyServices, handleWebSession, clientId, switchToAccount } = useOxy();
44
- const { t } = useI18n();
45
- const { accounts } = useSwitchableAccounts();
46
-
47
- const [useAnother, setUseAnother] = useState(false);
48
- const [switchingId, setSwitchingId] = useState<string | null>(null);
49
- const showChooser = !useAnother && accounts.length > 0;
50
-
51
- const { qrData, qrPayload, isLoading, error, isWaiting, openSameDeviceApproval, retry } = useOxyAuthSession(
52
- oxyServices,
53
- clientId,
54
- handleWebSession,
55
- { onSignedIn: onAuthenticated },
56
- );
57
-
58
- const pw = usePasswordSignIn({ onSignedIn: onAuthenticated });
59
-
60
- const handleSelectAccount = useCallback(async (account: SwitchableAccount) => {
61
- if (account.isCurrent) {
62
- onAuthenticated?.();
63
- return;
64
- }
65
- if (switchingId) return;
66
- setSwitchingId(account.accountId);
67
- try {
68
- await switchToAccount(account.accountId);
69
- onAuthenticated?.();
70
- } catch (switchError) {
71
- if (isDev()) {
72
- loggerUtil.warn('OxyAuthScreen: switch account failed', { component: 'OxyAuthScreen' }, switchError as unknown);
73
- }
74
- toast.error(t('accountSwitcher.toasts.switchFailed') || 'Failed to switch account');
75
- } finally {
76
- setSwitchingId(null);
77
- }
78
- }, [switchingId, switchToAccount, onAuthenticated, t]);
79
-
80
- const inputStyle: TextStyle = {
81
- width: '100%',
82
- borderWidth: 1,
83
- borderRadius: 12,
84
- paddingHorizontal: 16,
85
- paddingVertical: 13,
86
- fontSize: 15,
87
- marginBottom: 12,
88
- borderColor: bloomTheme.colors.border,
89
- color: bloomTheme.colors.text,
90
- backgroundColor: bloomTheme.colors.backgroundSecondary,
91
- };
92
-
93
- const title = showChooser ? t('signin.chooser.title') : 'Sign in to Oxy';
94
- const subtitle = showChooser
95
- ? t('signin.chooser.subtitle')
96
- : 'Continue with your Oxy identity to sign in securely';
97
-
98
- return (
99
- <View className="flex-1 items-center justify-center bg-bg px-screen-margin">
100
- {/* Branded header */}
101
- <View className="items-center mb-space-24 gap-space-12">
102
- <OxyLogo variant="icon" size={52} />
103
- <H4 className="text-headerBold font-headerBold text-text text-center">{title}</H4>
104
- <Text className="font-sans text-body text-text-secondary text-center">{subtitle}</Text>
105
- </View>
106
-
107
- {showChooser ? (
108
- <SignInAccountChooser
109
- accounts={accounts}
110
- onSelectAccount={handleSelectAccount}
111
- onUseAnother={() => setUseAnother(true)}
112
- pendingAccountId={switchingId}
113
- disabled={switchingId !== null}
114
- />
115
- ) : (
116
- <>
117
- {/* Back to the chooser when accounts exist. */}
118
- {accounts.length > 0 && (
119
- <Button
120
- variant="text"
121
- size="small"
122
- className="self-start mb-space-8"
123
- onPress={() => setUseAnother(false)}
124
- accessibilityLabel="Choose an account"
125
- icon={<Icons.ArrowLeft_Stroke2_Corner0_Rounded size="sm" style={{ color: bloomTheme.colors.textSecondary }} />}
126
- >
127
- Choose an account
128
- </Button>
129
- )}
130
-
131
- {/* PRIMARY — first-party password sign-in. Always usable; the device-flow
132
- loading/error state below never gates it. */}
133
- <View className="w-full">
134
- {pw.step === 'identifier' && (
135
- <TextInput
136
- style={inputStyle}
137
- value={pw.identifier}
138
- onChangeText={pw.setIdentifier}
139
- onSubmitEditing={pw.submitIdentifier}
140
- placeholder="Username or email"
141
- placeholderTextColor={bloomTheme.colors.textSecondary}
142
- autoCapitalize="none"
143
- autoCorrect={false}
144
- keyboardType="email-address"
145
- returnKeyType="next"
146
- accessibilityLabel="Username or email"
147
- />
148
- )}
149
-
150
- {pw.step === 'password' && (
151
- <>
152
- <Text className="font-sans text-body text-text-secondary text-center mb-space-12">
153
- {pw.identifier}
154
- </Text>
155
- <TextInput
156
- style={inputStyle}
157
- value={pw.password}
158
- onChangeText={pw.setPassword}
159
- onSubmitEditing={pw.submitPassword}
160
- placeholder="Password"
161
- placeholderTextColor={bloomTheme.colors.textSecondary}
162
- secureTextEntry
163
- autoCapitalize="none"
164
- autoCorrect={false}
165
- returnKeyType="go"
166
- accessibilityLabel="Password"
167
- />
168
- </>
169
- )}
170
-
171
- {pw.step === 'twoFactor' && (
172
- <TextInput
173
- style={inputStyle}
174
- value={pw.code}
175
- onChangeText={pw.setCode}
176
- onSubmitEditing={pw.submitTwoFactor}
177
- placeholder={pw.useBackupCode ? 'Backup code' : '6-digit code'}
178
- placeholderTextColor={bloomTheme.colors.textSecondary}
179
- autoCapitalize="none"
180
- autoCorrect={false}
181
- keyboardType={pw.useBackupCode ? 'default' : 'number-pad'}
182
- returnKeyType="go"
183
- accessibilityLabel={pw.useBackupCode ? 'Backup code' : 'Two-factor code'}
184
- />
185
- )}
186
-
187
- <Button
188
- variant="primary"
189
- size="large"
190
- fullWidth
191
- className="w-full"
192
- onPress={
193
- pw.step === 'identifier' ? pw.submitIdentifier
194
- : pw.step === 'password' ? pw.submitPassword
195
- : pw.submitTwoFactor
196
- }
197
- loading={pw.isSubmitting}
198
- disabled={pw.isSubmitting}
199
- >
200
- {pw.step === 'identifier' ? 'Continue' : pw.step === 'password' ? 'Sign in' : 'Verify'}
201
- </Button>
202
-
203
- {pw.error && (
204
- <Text className="font-sans text-body text-destructive text-center mt-space-12">
205
- {pw.error}
206
- </Text>
207
- )}
208
-
209
- {pw.step === 'twoFactor' && (
210
- <Button
211
- variant="text"
212
- size="small"
213
- className="mt-space-8"
214
- onPress={() => pw.setUseBackupCode(!pw.useBackupCode)}
215
- >
216
- {pw.useBackupCode ? 'Use authenticator code' : 'Use a backup code'}
217
- </Button>
218
- )}
219
-
220
- {pw.step !== 'identifier' && (
221
- <Button
222
- variant="text"
223
- size="small"
224
- className="mt-space-8"
225
- onPress={pw.back}
226
- accessibilityLabel="Back"
227
- >
228
- Back
229
- </Button>
230
- )}
231
- </View>
232
-
233
- {/* "or" divider — separates the password form from the SECONDARY device flow. */}
234
- <View className="flex-row items-center w-full my-space-16 gap-space-8">
235
- <View className="flex-1" style={{ height: 1, backgroundColor: bloomTheme.colors.border }} />
236
- <Text className="font-sans text-caption text-text-tertiary">or</Text>
237
- <View className="flex-1" style={{ height: 1, backgroundColor: bloomTheme.colors.border }} />
238
- </View>
239
-
240
- {/* SECONDARY — cross-app device flow. Its loading/error state gates ONLY
241
- this section; the password form above is always usable. */}
242
- {isLoading ? (
243
- <LoadingState
244
- size="small"
245
- color={bloomTheme.colors.primary}
246
- message="Preparing sign in…"
247
- />
248
- ) : error ? (
249
- <View className="items-center gap-space-16 w-full">
250
- <IconCircle icon={Icons.Warning_Stroke2_Corner0_Rounded} />
251
- <Text className="font-sans text-body text-text-secondary text-center">{error}</Text>
252
- <Button
253
- variant="secondary"
254
- fullWidth
255
- className="w-full"
256
- onPress={retry}
257
- icon={
258
- <Icons.ArrowRotateClockwise_Stroke2_Corner0_Rounded
259
- size="sm"
260
- style={{ color: bloomTheme.colors.text }}
261
- />
262
- }
263
- >
264
- Try Again
265
- </Button>
266
- </View>
267
- ) : (
268
- <>
269
- {qrPayload && (
270
- <Button
271
- variant="secondary"
272
- size="large"
273
- fullWidth
274
- className="w-full"
275
- onPress={openSameDeviceApproval}
276
- icon={<OxyLogo variant="icon" size={20} fillColor={bloomTheme.colors.text} />}
277
- >
278
- Sign in with the Oxy app
279
- </Button>
280
- )}
281
-
282
- {isWaiting && (
283
- <View className="flex-row items-center mt-space-16 gap-space-8">
284
- <Loading size="small" />
285
- <Text className="font-sans text-body text-text-secondary">
286
- Waiting for authorization…
287
- </Text>
288
- </View>
289
- )}
290
-
291
- <View className="w-full mt-space-24">
292
- <AnotherDeviceQR qrData={qrData} qrPayload={qrPayload} />
293
- </View>
294
- </>
295
- )}
296
- </>
297
- )}
298
-
299
- {/* Footer — create an account */}
300
- <View className="flex-row flex-wrap justify-center items-center mt-space-24">
301
- <Text className="font-sans text-body text-text-secondary">
302
- Don't have an Oxy account?
303
- </Text>
304
- <Button
305
- variant="text"
306
- size="small"
307
- onPress={() => Linking.openURL(OXY_ACCOUNTS_WEB_URL)}
308
- accessibilityLabel="Create an Oxy account"
309
- >
310
- Create one
311
- </Button>
312
- </View>
313
-
314
- {/* Cancel */}
315
- {goBack && (
316
- <Button variant="text" onPress={goBack} className="mt-space-8" accessibilityLabel="Cancel">
317
- Cancel
318
- </Button>
319
- )}
320
- </View>
321
- );
322
- };
323
-
324
- export default OxyAuthScreen;