@oxyhq/services 14.1.0 → 16.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 (292) hide show
  1. package/lib/commonjs/index.js +4 -21
  2. package/lib/commonjs/index.js.map +1 -1
  3. package/lib/commonjs/ui/components/SignInAccountChooser.js +189 -0
  4. package/lib/commonjs/ui/components/SignInAccountChooser.js.map +1 -0
  5. package/lib/commonjs/ui/components/SignInModal.js +350 -131
  6. package/lib/commonjs/ui/components/SignInModal.js.map +1 -1
  7. package/lib/commonjs/ui/context/OxyContext.js +340 -1547
  8. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  9. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +61 -64
  10. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  11. package/lib/commonjs/ui/hooks/mutations/mutationKeys.js +1 -1
  12. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +9 -9
  13. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  14. package/lib/commonjs/ui/hooks/queries/queryKeys.js +1 -1
  15. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +7 -8
  16. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +1 -1
  17. package/lib/commonjs/ui/hooks/useAuth.js +14 -33
  18. package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
  19. package/lib/commonjs/ui/hooks/usePasswordSignIn.js +170 -0
  20. package/lib/commonjs/ui/hooks/usePasswordSignIn.js.map +1 -0
  21. package/lib/commonjs/ui/hooks/useProfileEditing.js +2 -5
  22. package/lib/commonjs/ui/hooks/useProfileEditing.js.map +1 -1
  23. package/lib/commonjs/ui/hooks/useSessionManagement.js +2 -2
  24. package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
  25. package/lib/commonjs/ui/navigation/routes.js +1 -0
  26. package/lib/commonjs/ui/navigation/routes.js.map +1 -1
  27. package/lib/commonjs/ui/screens/ConnectedAppsScreen.js +17 -18
  28. package/lib/commonjs/ui/screens/ConnectedAppsScreen.js.map +1 -1
  29. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +93 -68
  30. package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
  31. package/lib/commonjs/ui/screens/EditProfileScreen.js +187 -0
  32. package/lib/commonjs/ui/screens/EditProfileScreen.js.map +1 -0
  33. package/lib/commonjs/ui/screens/ManageAccountScreen.js +3 -7
  34. package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
  35. package/lib/commonjs/ui/screens/OxyAuthScreen.js +231 -95
  36. package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
  37. package/lib/commonjs/ui/screens/ProfileScreen.js +15 -2
  38. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  39. package/lib/commonjs/ui/screens/linkFormat.js +38 -0
  40. package/lib/commonjs/ui/screens/linkFormat.js.map +1 -0
  41. package/lib/commonjs/ui/session/authStore.js +146 -0
  42. package/lib/commonjs/ui/session/authStore.js.map +1 -0
  43. package/lib/commonjs/ui/session/createSessionClient.js +23 -17
  44. package/lib/commonjs/ui/session/createSessionClient.js.map +1 -1
  45. package/lib/commonjs/ui/session/index.js +13 -7
  46. package/lib/commonjs/ui/session/index.js.map +1 -1
  47. package/lib/commonjs/ui/session/tokenTransport.js +19 -31
  48. package/lib/commonjs/ui/session/tokenTransport.js.map +1 -1
  49. package/lib/commonjs/ui/utils/isWebBrowser.js +13 -0
  50. package/lib/commonjs/ui/utils/isWebBrowser.js.map +1 -0
  51. package/lib/commonjs/utils/deviceFlowSignIn.js +13 -16
  52. package/lib/commonjs/utils/deviceFlowSignIn.js.map +1 -1
  53. package/lib/module/index.js +2 -7
  54. package/lib/module/index.js.map +1 -1
  55. package/lib/module/ui/components/SignInAccountChooser.js +183 -0
  56. package/lib/module/ui/components/SignInAccountChooser.js.map +1 -0
  57. package/lib/module/ui/components/SignInModal.js +352 -134
  58. package/lib/module/ui/components/SignInModal.js.map +1 -1
  59. package/lib/module/ui/context/OxyContext.js +342 -1551
  60. package/lib/module/ui/context/OxyContext.js.map +1 -1
  61. package/lib/module/ui/context/hooks/useAuthOperations.js +60 -63
  62. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  63. package/lib/module/ui/hooks/mutations/mutationKeys.js +1 -1
  64. package/lib/module/ui/hooks/mutations/useAccountMutations.js +7 -7
  65. package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  66. package/lib/module/ui/hooks/queries/queryKeys.js +1 -1
  67. package/lib/module/ui/hooks/queries/useAccountQueries.js +5 -6
  68. package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
  69. package/lib/module/ui/hooks/useAuth.js +14 -33
  70. package/lib/module/ui/hooks/useAuth.js.map +1 -1
  71. package/lib/module/ui/hooks/usePasswordSignIn.js +165 -0
  72. package/lib/module/ui/hooks/usePasswordSignIn.js.map +1 -0
  73. package/lib/module/ui/hooks/useProfileEditing.js +2 -5
  74. package/lib/module/ui/hooks/useProfileEditing.js.map +1 -1
  75. package/lib/module/ui/hooks/useSessionManagement.js +1 -1
  76. package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
  77. package/lib/module/ui/navigation/routes.js +1 -0
  78. package/lib/module/ui/navigation/routes.js.map +1 -1
  79. package/lib/module/ui/screens/ConnectedAppsScreen.js +19 -20
  80. package/lib/module/ui/screens/ConnectedAppsScreen.js.map +1 -1
  81. package/lib/module/ui/screens/EditProfileFieldScreen.js +91 -66
  82. package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
  83. package/lib/module/ui/screens/EditProfileScreen.js +182 -0
  84. package/lib/module/ui/screens/EditProfileScreen.js.map +1 -0
  85. package/lib/module/ui/screens/ManageAccountScreen.js +3 -7
  86. package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
  87. package/lib/module/ui/screens/OxyAuthScreen.js +233 -97
  88. package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
  89. package/lib/module/ui/screens/ProfileScreen.js +15 -2
  90. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  91. package/lib/module/ui/screens/linkFormat.js +31 -0
  92. package/lib/module/ui/screens/linkFormat.js.map +1 -0
  93. package/lib/module/ui/session/authStore.js +143 -0
  94. package/lib/module/ui/session/authStore.js.map +1 -0
  95. package/lib/module/ui/session/createSessionClient.js +22 -16
  96. package/lib/module/ui/session/createSessionClient.js.map +1 -1
  97. package/lib/module/ui/session/index.js +9 -9
  98. package/lib/module/ui/session/index.js.map +1 -1
  99. package/lib/module/ui/session/tokenTransport.js +20 -33
  100. package/lib/module/ui/session/tokenTransport.js.map +1 -1
  101. package/lib/module/ui/utils/isWebBrowser.js +11 -0
  102. package/lib/module/ui/utils/isWebBrowser.js.map +1 -0
  103. package/lib/module/utils/deviceFlowSignIn.js +14 -17
  104. package/lib/module/utils/deviceFlowSignIn.js.map +1 -1
  105. package/lib/typescript/commonjs/index.d.ts +2 -3
  106. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  107. package/lib/typescript/commonjs/ui/components/SignInAccountChooser.d.ts +29 -0
  108. package/lib/typescript/commonjs/ui/components/SignInAccountChooser.d.ts.map +1 -0
  109. package/lib/typescript/commonjs/ui/components/SignInModal.d.ts +15 -10
  110. package/lib/typescript/commonjs/ui/components/SignInModal.d.ts.map +1 -1
  111. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +36 -42
  112. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  113. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +18 -23
  114. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  115. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +4 -6
  116. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts +6 -7
  118. package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  119. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
  120. package/lib/typescript/commonjs/ui/hooks/usePasswordSignIn.d.ts +31 -0
  121. package/lib/typescript/commonjs/ui/hooks/usePasswordSignIn.d.ts.map +1 -0
  122. package/lib/typescript/commonjs/ui/hooks/useProfileEditing.d.ts +0 -1
  123. package/lib/typescript/commonjs/ui/hooks/useProfileEditing.d.ts.map +1 -1
  124. package/lib/typescript/commonjs/ui/navigation/routes.d.ts +1 -1
  125. package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
  126. package/lib/typescript/commonjs/ui/screens/ConnectedAppsScreen.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
  128. package/lib/typescript/commonjs/ui/screens/EditProfileScreen.d.ts +14 -0
  129. package/lib/typescript/commonjs/ui/screens/EditProfileScreen.d.ts.map +1 -0
  130. package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts +1 -1
  131. package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  132. package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts +13 -12
  133. package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts.map +1 -1
  134. package/lib/typescript/commonjs/ui/screens/ProfileScreen.d.ts.map +1 -1
  135. package/lib/typescript/commonjs/ui/screens/linkFormat.d.ts +23 -0
  136. package/lib/typescript/commonjs/ui/screens/linkFormat.d.ts.map +1 -0
  137. package/lib/typescript/commonjs/ui/session/authStore.d.ts +33 -0
  138. package/lib/typescript/commonjs/ui/session/authStore.d.ts.map +1 -0
  139. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts +19 -15
  140. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -1
  141. package/lib/typescript/commonjs/ui/session/index.d.ts +9 -9
  142. package/lib/typescript/commonjs/ui/session/index.d.ts.map +1 -1
  143. package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts +13 -19
  144. package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts.map +1 -1
  145. package/lib/typescript/commonjs/ui/utils/isWebBrowser.d.ts +9 -0
  146. package/lib/typescript/commonjs/ui/utils/isWebBrowser.d.ts.map +1 -0
  147. package/lib/typescript/commonjs/ui/utils/storageHelpers.d.ts +7 -7
  148. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts +15 -13
  149. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts.map +1 -1
  150. package/lib/typescript/module/index.d.ts +2 -3
  151. package/lib/typescript/module/index.d.ts.map +1 -1
  152. package/lib/typescript/module/ui/components/SignInAccountChooser.d.ts +29 -0
  153. package/lib/typescript/module/ui/components/SignInAccountChooser.d.ts.map +1 -0
  154. package/lib/typescript/module/ui/components/SignInModal.d.ts +15 -10
  155. package/lib/typescript/module/ui/components/SignInModal.d.ts.map +1 -1
  156. package/lib/typescript/module/ui/context/OxyContext.d.ts +36 -42
  157. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  158. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +18 -23
  159. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  160. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +4 -6
  161. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  162. package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts +6 -7
  163. package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  164. package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
  165. package/lib/typescript/module/ui/hooks/usePasswordSignIn.d.ts +31 -0
  166. package/lib/typescript/module/ui/hooks/usePasswordSignIn.d.ts.map +1 -0
  167. package/lib/typescript/module/ui/hooks/useProfileEditing.d.ts +0 -1
  168. package/lib/typescript/module/ui/hooks/useProfileEditing.d.ts.map +1 -1
  169. package/lib/typescript/module/ui/navigation/routes.d.ts +1 -1
  170. package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
  171. package/lib/typescript/module/ui/screens/ConnectedAppsScreen.d.ts.map +1 -1
  172. package/lib/typescript/module/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
  173. package/lib/typescript/module/ui/screens/EditProfileScreen.d.ts +14 -0
  174. package/lib/typescript/module/ui/screens/EditProfileScreen.d.ts.map +1 -0
  175. package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts +1 -1
  176. package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  177. package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts +13 -12
  178. package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts.map +1 -1
  179. package/lib/typescript/module/ui/screens/ProfileScreen.d.ts.map +1 -1
  180. package/lib/typescript/module/ui/screens/linkFormat.d.ts +23 -0
  181. package/lib/typescript/module/ui/screens/linkFormat.d.ts.map +1 -0
  182. package/lib/typescript/module/ui/session/authStore.d.ts +33 -0
  183. package/lib/typescript/module/ui/session/authStore.d.ts.map +1 -0
  184. package/lib/typescript/module/ui/session/createSessionClient.d.ts +19 -15
  185. package/lib/typescript/module/ui/session/createSessionClient.d.ts.map +1 -1
  186. package/lib/typescript/module/ui/session/index.d.ts +9 -9
  187. package/lib/typescript/module/ui/session/index.d.ts.map +1 -1
  188. package/lib/typescript/module/ui/session/tokenTransport.d.ts +13 -19
  189. package/lib/typescript/module/ui/session/tokenTransport.d.ts.map +1 -1
  190. package/lib/typescript/module/ui/utils/isWebBrowser.d.ts +9 -0
  191. package/lib/typescript/module/ui/utils/isWebBrowser.d.ts.map +1 -0
  192. package/lib/typescript/module/ui/utils/storageHelpers.d.ts +7 -7
  193. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts +15 -13
  194. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts.map +1 -1
  195. package/package.json +4 -4
  196. package/src/index.ts +2 -6
  197. package/src/ui/components/SignInAccountChooser.tsx +162 -0
  198. package/src/ui/components/SignInModal.tsx +309 -139
  199. package/src/ui/context/OxyContext.tsx +532 -1785
  200. package/src/ui/context/hooks/useAuthOperations.ts +65 -76
  201. package/src/ui/hooks/mutations/mutationKeys.ts +1 -1
  202. package/src/ui/hooks/mutations/useAccountMutations.ts +7 -7
  203. package/src/ui/hooks/queries/queryKeys.ts +1 -1
  204. package/src/ui/hooks/queries/useAccountQueries.ts +8 -9
  205. package/src/ui/hooks/useAuth.ts +14 -35
  206. package/src/ui/hooks/usePasswordSignIn.ts +207 -0
  207. package/src/ui/hooks/useProfileEditing.ts +2 -8
  208. package/src/ui/hooks/useSessionManagement.ts +1 -1
  209. package/src/ui/navigation/routes.ts +3 -1
  210. package/src/ui/screens/ConnectedAppsScreen.tsx +23 -24
  211. package/src/ui/screens/EditProfileFieldScreen.tsx +102 -66
  212. package/src/ui/screens/EditProfileScreen.tsx +155 -0
  213. package/src/ui/screens/ManageAccountScreen.tsx +3 -7
  214. package/src/ui/screens/OxyAuthScreen.tsx +259 -112
  215. package/src/ui/screens/ProfileScreen.tsx +15 -2
  216. package/src/ui/screens/__tests__/linkFormat.test.ts +73 -0
  217. package/src/ui/screens/linkFormat.ts +37 -0
  218. package/src/ui/session/__tests__/createSessionClient.test.ts +22 -7
  219. package/src/ui/session/__tests__/tokenTransport.test.ts +37 -49
  220. package/src/ui/session/authStore.ts +164 -0
  221. package/src/ui/session/createSessionClient.ts +30 -17
  222. package/src/ui/session/index.ts +10 -9
  223. package/src/ui/session/tokenTransport.ts +26 -36
  224. package/src/ui/utils/isWebBrowser.ts +8 -0
  225. package/src/ui/utils/storageHelpers.ts +7 -7
  226. package/src/utils/__tests__/deviceFlowSignIn.test.ts +50 -131
  227. package/src/utils/deviceFlowSignIn.ts +19 -26
  228. package/lib/commonjs/ui/context/inSessionTokenRefresh.js +0 -243
  229. package/lib/commonjs/ui/context/inSessionTokenRefresh.js.map +0 -1
  230. package/lib/commonjs/ui/context/silentSessionRestore.js +0 -56
  231. package/lib/commonjs/ui/context/silentSessionRestore.js.map +0 -1
  232. package/lib/commonjs/ui/hooks/useWebSSO.js +0 -28
  233. package/lib/commonjs/ui/hooks/useWebSSO.js.map +0 -1
  234. package/lib/commonjs/ui/session/projectSessionState.js +0 -86
  235. package/lib/commonjs/ui/session/projectSessionState.js.map +0 -1
  236. package/lib/commonjs/ui/session/sessionClientHost.js +0 -30
  237. package/lib/commonjs/ui/session/sessionClientHost.js.map +0 -1
  238. package/lib/commonjs/ui/utils/activeAuthuser.js +0 -142
  239. package/lib/commonjs/ui/utils/activeAuthuser.js.map +0 -1
  240. package/lib/commonjs/utils/crossApex.js +0 -74
  241. package/lib/commonjs/utils/crossApex.js.map +0 -1
  242. package/lib/module/ui/context/inSessionTokenRefresh.js +0 -238
  243. package/lib/module/ui/context/inSessionTokenRefresh.js.map +0 -1
  244. package/lib/module/ui/context/silentSessionRestore.js +0 -53
  245. package/lib/module/ui/context/silentSessionRestore.js.map +0 -1
  246. package/lib/module/ui/hooks/useWebSSO.js +0 -24
  247. package/lib/module/ui/hooks/useWebSSO.js.map +0 -1
  248. package/lib/module/ui/session/projectSessionState.js +0 -79
  249. package/lib/module/ui/session/projectSessionState.js.map +0 -1
  250. package/lib/module/ui/session/sessionClientHost.js +0 -26
  251. package/lib/module/ui/session/sessionClientHost.js.map +0 -1
  252. package/lib/module/ui/utils/activeAuthuser.js +0 -134
  253. package/lib/module/ui/utils/activeAuthuser.js.map +0 -1
  254. package/lib/module/utils/crossApex.js +0 -69
  255. package/lib/module/utils/crossApex.js.map +0 -1
  256. package/lib/typescript/commonjs/ui/context/inSessionTokenRefresh.d.ts +0 -106
  257. package/lib/typescript/commonjs/ui/context/inSessionTokenRefresh.d.ts.map +0 -1
  258. package/lib/typescript/commonjs/ui/context/silentSessionRestore.d.ts +0 -36
  259. package/lib/typescript/commonjs/ui/context/silentSessionRestore.d.ts.map +0 -1
  260. package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts +0 -19
  261. package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts.map +0 -1
  262. package/lib/typescript/commonjs/ui/session/projectSessionState.d.ts +0 -45
  263. package/lib/typescript/commonjs/ui/session/projectSessionState.d.ts.map +0 -1
  264. package/lib/typescript/commonjs/ui/session/sessionClientHost.d.ts +0 -14
  265. package/lib/typescript/commonjs/ui/session/sessionClientHost.d.ts.map +0 -1
  266. package/lib/typescript/commonjs/ui/utils/activeAuthuser.d.ts +0 -67
  267. package/lib/typescript/commonjs/ui/utils/activeAuthuser.d.ts.map +0 -1
  268. package/lib/typescript/commonjs/utils/crossApex.d.ts +0 -55
  269. package/lib/typescript/commonjs/utils/crossApex.d.ts.map +0 -1
  270. package/lib/typescript/module/ui/context/inSessionTokenRefresh.d.ts +0 -106
  271. package/lib/typescript/module/ui/context/inSessionTokenRefresh.d.ts.map +0 -1
  272. package/lib/typescript/module/ui/context/silentSessionRestore.d.ts +0 -36
  273. package/lib/typescript/module/ui/context/silentSessionRestore.d.ts.map +0 -1
  274. package/lib/typescript/module/ui/hooks/useWebSSO.d.ts +0 -19
  275. package/lib/typescript/module/ui/hooks/useWebSSO.d.ts.map +0 -1
  276. package/lib/typescript/module/ui/session/projectSessionState.d.ts +0 -45
  277. package/lib/typescript/module/ui/session/projectSessionState.d.ts.map +0 -1
  278. package/lib/typescript/module/ui/session/sessionClientHost.d.ts +0 -14
  279. package/lib/typescript/module/ui/session/sessionClientHost.d.ts.map +0 -1
  280. package/lib/typescript/module/ui/utils/activeAuthuser.d.ts +0 -67
  281. package/lib/typescript/module/ui/utils/activeAuthuser.d.ts.map +0 -1
  282. package/lib/typescript/module/utils/crossApex.d.ts +0 -55
  283. package/lib/typescript/module/utils/crossApex.d.ts.map +0 -1
  284. package/src/ui/context/inSessionTokenRefresh.ts +0 -264
  285. package/src/ui/context/silentSessionRestore.ts +0 -53
  286. package/src/ui/hooks/useWebSSO.ts +0 -23
  287. package/src/ui/session/__tests__/projectSessionState.test.ts +0 -114
  288. package/src/ui/session/__tests__/sessionClientHost.test.ts +0 -43
  289. package/src/ui/session/projectSessionState.ts +0 -85
  290. package/src/ui/session/sessionClientHost.ts +0 -27
  291. package/src/ui/utils/activeAuthuser.ts +0 -142
  292. package/src/utils/crossApex.ts +0 -75
@@ -1,36 +1,47 @@
1
1
  /**
2
- * SignInModal - Web-first centered sign-in modal (Continue with Oxy)
2
+ * SignInModal web-first centered sign-in modal.
3
3
  *
4
- * A semi-transparent full-screen modal whose primary action is the one-tap
5
- * "Continue with Oxy" approval flow. The QR code is demoted to a collapsed
6
- * "Sign in on another device" disclosure (you can't scan your own screen).
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.
7
13
  *
8
- * ALL of the auth-session machinery (session-token creation, QR data, socket +
9
- * polling, waiting/error/retry state, the open-auth handler, deep-link return,
10
- * and cleanup) lives in the shared `useOxyAuthSession` hook, which the native
11
- * `OxyAuthScreen` also consumes neither container re-implements the transport.
12
- *
13
- * Animates with a fade + scale effect.
14
+ * When there are no accounts the modal opens straight on the sign-in options.
15
+ * The device-flow machinery lives in the shared `useOxyAuthSession` hook (the
16
+ * native `OxyAuthScreen` consumes it too neither container re-implements the
17
+ * transport). Animates with a fade + scale; per-phase content cross-fades and
18
+ * respects reduced motion.
14
19
  */
15
20
 
16
21
  import type React from 'react';
17
22
  import { useState, useEffect, useCallback } from 'react';
18
- import { View, Text, TouchableOpacity, StyleSheet, Modal } from 'react-native';
23
+ import { View, Text, TextInput, TouchableOpacity, StyleSheet, Modal, Linking, AccessibilityInfo } from 'react-native';
19
24
  import Animated, {
20
25
  useSharedValue,
21
26
  useAnimatedStyle,
22
27
  withTiming,
28
+ FadeIn,
23
29
  } from 'react-native-reanimated';
24
30
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
25
31
  import { useTheme } from '@oxyhq/bloom/theme';
26
32
  import { Button } from '@oxyhq/bloom/button';
27
33
  import { Loading } from '@oxyhq/bloom/loading';
28
- import { Linking } from 'react-native';
34
+ import { toast } from '@oxyhq/bloom';
35
+ import { Ionicons } from '@expo/vector-icons';
36
+ import { isDev, logger as loggerUtil } from '@oxyhq/core';
29
37
  import { useOxy } from '../context/OxyContext';
30
38
  import OxyLogo from './OxyLogo';
31
39
  import AnotherDeviceQR from './AnotherDeviceQR';
40
+ import SignInAccountChooser from './SignInAccountChooser';
41
+ import { useSwitchableAccounts, type SwitchableAccount } from '../hooks/useSwitchableAccounts';
42
+ import { useI18n } from '../hooks/useI18n';
32
43
  import { useOxyAuthSession, OXY_ACCOUNTS_WEB_URL } from '../hooks/useOxyAuthSession';
33
- import { isCrossApexWeb } from '../../utils/crossApex';
44
+ import { usePasswordSignIn } from '../hooks/usePasswordSignIn';
34
45
 
35
46
  // Store for modal visibility with subscription support
36
47
  let modalVisible = false;
@@ -59,10 +70,8 @@ export const subscribeToSignInModal = (listener: (visible: boolean) => void): ((
59
70
 
60
71
  const SignInModal: React.FC = () => {
61
72
  const [visible, setVisible] = useState(false);
62
-
63
73
  const insets = useSafeAreaInsets();
64
74
  const theme = useTheme();
65
- const { oxyServices, handleWebSession, clientId } = useOxy();
66
75
 
67
76
  // Register the imperative visibility callback.
68
77
  useEffect(() => {
@@ -74,75 +83,88 @@ const SignInModal: React.FC = () => {
74
83
 
75
84
  if (!visible) return null;
76
85
 
77
- // The hook owns the whole auth-session lifecycle. Mounting it only while the
78
- // modal is visible matches the previous behavior (session created on open,
79
- // cleaned up on close) — the inner component is unmounted when `visible`
80
- // flips false, which runs the hook's unmount cleanup.
81
- return (
82
- <SignInModalContent
83
- theme={theme}
84
- insets={insets}
85
- oxyServices={oxyServices}
86
- handleWebSession={handleWebSession}
87
- clientId={clientId}
88
- />
89
- );
86
+ // Mounting the content only while visible preserves the session-created-on-open
87
+ // / cleaned-up-on-close behaviour and resets the phase state on every reopen.
88
+ return <SignInModalContent theme={theme} insets={insets} />;
90
89
  };
91
90
 
92
91
  interface SignInModalContentProps {
93
92
  theme: ReturnType<typeof useTheme>;
94
93
  insets: ReturnType<typeof useSafeAreaInsets>;
95
- oxyServices: ReturnType<typeof useOxy>['oxyServices'];
96
- handleWebSession: ReturnType<typeof useOxy>['handleWebSession'];
97
- clientId: ReturnType<typeof useOxy>['clientId'];
98
94
  }
99
95
 
100
- const SignInModalContent: React.FC<SignInModalContentProps> = ({
101
- theme,
102
- insets,
103
- oxyServices,
104
- handleWebSession,
105
- clientId,
106
- }) => {
107
- const { qrData, qrPayload, isLoading, error, isWaiting, openAuthApproval, openSameDeviceApproval, retry } = useOxyAuthSession(
96
+ const SignInModalContent: React.FC<SignInModalContentProps> = ({ theme, insets }) => {
97
+ const { oxyServices, handleWebSession, clientId, switchToAccount } = useOxy();
98
+ const { t } = useI18n();
99
+ const { accounts } = useSwitchableAccounts();
100
+
101
+ // Phase: the chooser is the front screen whenever accounts exist AND the user
102
+ // has not tapped "Use another account" this session.
103
+ const [useAnother, setUseAnother] = useState(false);
104
+ const [switchingId, setSwitchingId] = useState<string | null>(null);
105
+ const showChooser = !useAnother && accounts.length > 0;
106
+
107
+ const { qrData, qrPayload, isLoading, error, isWaiting, openSameDeviceApproval, retry } = useOxyAuthSession(
108
108
  oxyServices,
109
109
  clientId,
110
110
  handleWebSession,
111
111
  { onSignedIn: hideSignInModal },
112
112
  );
113
113
 
114
- // On a cross-apex web RP, only the "Continue with Oxy" IdP popup establishes
115
- // a durable `fedcm_session`. The Commons-app handoffs (same-device deep-link
116
- // + cross-device QR) approve OUTSIDE the browser, so they leave no IdP
117
- // session and the user would be logged out on reload — hide them there.
118
- const crossApexWeb = isCrossApexWeb();
114
+ // First-party password sign-in the PRIMARY option once past the chooser.
115
+ const pw = usePasswordSignIn({ onSignedIn: hideSignInModal });
119
116
 
120
- // Entrance animation.
117
+ // Entrance animation (respects reduced motion).
121
118
  const opacity = useSharedValue(0);
122
119
  const scale = useSharedValue(0.96);
123
-
124
- // biome-ignore lint/correctness/useExhaustiveDependencies: opacity/scale are Reanimated SharedValues (stable refs) and must not be listed as deps; this runs once on mount to play the entrance.
120
+ // biome-ignore lint/correctness/useExhaustiveDependencies: opacity/scale are Reanimated SharedValues (stable refs), not deps; runs once on mount.
125
121
  useEffect(() => {
126
- opacity.value = withTiming(1, { duration: 250 });
127
- scale.value = withTiming(1, { duration: 250 });
122
+ let cancelled = false;
123
+ AccessibilityInfo.isReduceMotionEnabled().then((reduced) => {
124
+ if (cancelled) return;
125
+ const duration = reduced ? 0 : 250;
126
+ opacity.value = withTiming(1, { duration });
127
+ scale.value = withTiming(1, { duration });
128
+ });
129
+ return () => {
130
+ cancelled = true;
131
+ };
128
132
  }, []);
129
133
 
130
- const backdropStyle = useAnimatedStyle(() => ({
131
- opacity: opacity.value,
132
- }));
133
-
134
+ const backdropStyle = useAnimatedStyle(() => ({ opacity: opacity.value }));
134
135
  const contentStyle = useAnimatedStyle(() => ({
135
136
  opacity: opacity.value,
136
137
  transform: [{ scale: scale.value }],
137
138
  }));
138
139
 
139
- const handleClose = useCallback(() => {
140
- hideSignInModal();
141
- }, []);
140
+ const handleClose = useCallback(() => hideSignInModal(), []);
141
+ const handleCreateAccount = useCallback(() => Linking.openURL(OXY_ACCOUNTS_WEB_URL), []);
142
142
 
143
- const handleCreateAccount = useCallback(() => {
144
- Linking.openURL(OXY_ACCOUNTS_WEB_URL);
145
- }, []);
143
+ const handleSelectAccount = useCallback(async (account: SwitchableAccount) => {
144
+ // The active account is already signed in — selecting it just continues.
145
+ if (account.isCurrent) {
146
+ hideSignInModal();
147
+ return;
148
+ }
149
+ if (switchingId) return;
150
+ setSwitchingId(account.accountId);
151
+ try {
152
+ await switchToAccount(account.accountId);
153
+ hideSignInModal();
154
+ } catch (switchError) {
155
+ if (isDev()) {
156
+ loggerUtil.warn('SignInModal: switch account failed', { component: 'SignInModal' }, switchError as unknown);
157
+ }
158
+ toast.error(t('accountSwitcher.toasts.switchFailed') || 'Failed to switch account');
159
+ } finally {
160
+ setSwitchingId(null);
161
+ }
162
+ }, [switchingId, switchToAccount, t]);
163
+
164
+ const title = showChooser ? t('signin.chooser.title') : 'Sign in to Oxy';
165
+ const subtitle = showChooser
166
+ ? t('signin.chooser.subtitle')
167
+ : 'Continue with your Oxy identity to sign in securely.';
146
168
 
147
169
  return (
148
170
  <Modal visible transparent animationType="none" statusBarTranslucent onRequestClose={handleClose}>
@@ -152,87 +174,182 @@ const SignInModalContent: React.FC<SignInModalContentProps> = ({
152
174
  <Animated.View
153
175
  style={[
154
176
  styles.card,
155
- { backgroundColor: theme.colors.card, paddingTop: insets.top + 24, paddingBottom: insets.bottom + 24 },
177
+ { backgroundColor: theme.colors.card, paddingTop: insets.top + 28, paddingBottom: insets.bottom + 24 },
156
178
  contentStyle,
157
179
  ]}
158
180
  >
159
- {/* Close button */}
160
181
  <TouchableOpacity
161
182
  style={[styles.closeButton, { backgroundColor: theme.colors.backgroundSecondary }]}
162
183
  onPress={handleClose}
163
184
  accessibilityRole="button"
164
185
  accessibilityLabel="Close sign in"
165
186
  >
166
- <Text style={[styles.closeButtonText, { color: theme.colors.textSecondary }]}>×</Text>
187
+ <Ionicons name="close" size={22} color={theme.colors.textSecondary} />
167
188
  </TouchableOpacity>
168
189
 
169
190
  {/* Branded header */}
170
191
  <View style={styles.header}>
171
- <OxyLogo variant="icon" size={56} />
172
- <Text className="text-foreground" style={styles.title}>Sign in to Oxy</Text>
173
- <Text className="text-muted-foreground" style={styles.subtitle}>
174
- Continue with your Oxy identity to sign in securely
175
- </Text>
192
+ <OxyLogo variant="icon" size={52} />
193
+ <Text className="text-foreground" style={styles.title}>{title}</Text>
194
+ <Text className="text-muted-foreground" style={styles.subtitle}>{subtitle}</Text>
176
195
  </View>
177
196
 
178
- {isLoading ? (
179
- <View style={styles.loadingContainer}>
180
- <Loading size="large" />
181
- <Text className="text-muted-foreground" style={styles.loadingText}>
182
- Preparing sign in...
183
- </Text>
184
- </View>
185
- ) : error ? (
186
- <View style={styles.errorContainer}>
187
- <Text className="text-destructive" style={styles.errorText}>{error}</Text>
188
- <Button variant="primary" onPress={retry} style={styles.primaryButton}>Try Again</Button>
189
- </View>
197
+ {showChooser ? (
198
+ <Animated.View key="chooser" entering={FadeIn.duration(180)} style={styles.phase}>
199
+ <SignInAccountChooser
200
+ accounts={accounts}
201
+ onSelectAccount={handleSelectAccount}
202
+ onUseAnother={() => setUseAnother(true)}
203
+ pendingAccountId={switchingId}
204
+ disabled={switchingId !== null}
205
+ />
206
+ </Animated.View>
190
207
  ) : (
191
- <>
192
- {/* Primary action Continue with Oxy */}
193
- <Button
194
- variant="primary"
195
- onPress={openAuthApproval}
196
- icon={<OxyLogo variant="icon" size={20} fillColor={theme.colors.primaryForeground} style={styles.buttonIcon} />}
197
- style={styles.primaryButton}
198
- >
199
- Continue with Oxy
200
- </Button>
201
-
202
- {/* Same-device "Sign in with Oxy" handoff — deep-links into the
203
- native Oxy app to approve. Shown only when the handoff backend
204
- returned a payload, and never on a cross-apex web RP (the
205
- approval happens outside the browser no durable session). */}
206
- {!crossApexWeb && qrPayload && (
208
+ <Animated.View key="signIn" entering={FadeIn.duration(180)} style={styles.phase}>
209
+ {/* Back to the chooser when accounts exist. */}
210
+ {accounts.length > 0 && (
211
+ <TouchableOpacity
212
+ onPress={() => setUseAnother(false)}
213
+ accessibilityRole="button"
214
+ style={styles.backRow}
215
+ >
216
+ <Ionicons name="chevron-back" size={18} color={theme.colors.textSecondary} />
217
+ <Text style={[styles.backText, { color: theme.colors.textSecondary }]}>{t('signin.chooser.title')}</Text>
218
+ </TouchableOpacity>
219
+ )}
220
+
221
+ {/* PRIMARY first-party password sign-in. Always usable; the
222
+ device-flow loading/error state below never gates it. */}
223
+ <View style={styles.form}>
224
+ {pw.step === 'identifier' && (
225
+ <TextInput
226
+ style={[styles.input, { borderColor: theme.colors.border, color: theme.colors.text, backgroundColor: theme.colors.backgroundSecondary }]}
227
+ value={pw.identifier}
228
+ onChangeText={pw.setIdentifier}
229
+ onSubmitEditing={pw.submitIdentifier}
230
+ placeholder="Username or email"
231
+ placeholderTextColor={theme.colors.textSecondary}
232
+ autoCapitalize="none"
233
+ autoCorrect={false}
234
+ keyboardType="email-address"
235
+ returnKeyType="next"
236
+ accessibilityLabel="Username or email"
237
+ />
238
+ )}
239
+
240
+ {pw.step === 'password' && (
241
+ <>
242
+ <Text style={[styles.contextText, { color: theme.colors.textSecondary }]}>{pw.identifier}</Text>
243
+ <TextInput
244
+ style={[styles.input, { borderColor: theme.colors.border, color: theme.colors.text, backgroundColor: theme.colors.backgroundSecondary }]}
245
+ value={pw.password}
246
+ onChangeText={pw.setPassword}
247
+ onSubmitEditing={pw.submitPassword}
248
+ placeholder="Password"
249
+ placeholderTextColor={theme.colors.textSecondary}
250
+ secureTextEntry
251
+ autoCapitalize="none"
252
+ autoCorrect={false}
253
+ returnKeyType="go"
254
+ accessibilityLabel="Password"
255
+ />
256
+ </>
257
+ )}
258
+
259
+ {pw.step === 'twoFactor' && (
260
+ <TextInput
261
+ style={[styles.input, { borderColor: theme.colors.border, color: theme.colors.text, backgroundColor: theme.colors.backgroundSecondary }]}
262
+ value={pw.code}
263
+ onChangeText={pw.setCode}
264
+ onSubmitEditing={pw.submitTwoFactor}
265
+ placeholder={pw.useBackupCode ? 'Backup code' : '6-digit code'}
266
+ placeholderTextColor={theme.colors.textSecondary}
267
+ autoCapitalize="none"
268
+ autoCorrect={false}
269
+ keyboardType={pw.useBackupCode ? 'default' : 'number-pad'}
270
+ returnKeyType="go"
271
+ accessibilityLabel={pw.useBackupCode ? 'Backup code' : 'Two-factor code'}
272
+ />
273
+ )}
274
+
207
275
  <Button
208
- variant="secondary"
209
- onPress={openSameDeviceApproval}
210
- icon={<OxyLogo variant="icon" size={20} fillColor={theme.colors.text} style={styles.buttonIcon} />}
211
- style={styles.secondaryButton}
276
+ variant="primary"
277
+ onPress={
278
+ pw.step === 'identifier' ? pw.submitIdentifier
279
+ : pw.step === 'password' ? pw.submitPassword
280
+ : pw.submitTwoFactor
281
+ }
282
+ loading={pw.isSubmitting}
283
+ disabled={pw.isSubmitting}
284
+ style={styles.primaryButton}
212
285
  >
213
- Sign in with the Oxy app
286
+ {pw.step === 'identifier' ? 'Continue' : pw.step === 'password' ? 'Sign in' : 'Verify'}
214
287
  </Button>
215
- )}
216
288
 
217
- {/* Waiting status */}
218
- {isWaiting && (
219
- <View style={styles.statusContainer}>
289
+ {pw.error && (
290
+ <Text className="text-destructive" style={styles.formError}>{pw.error}</Text>
291
+ )}
292
+
293
+ {pw.step === 'twoFactor' && (
294
+ <TouchableOpacity onPress={() => pw.setUseBackupCode(!pw.useBackupCode)} accessibilityRole="button" style={styles.linkButton}>
295
+ <Text style={[styles.linkText, { color: theme.colors.primary }]}>
296
+ {pw.useBackupCode ? 'Use authenticator code' : 'Use a backup code'}
297
+ </Text>
298
+ </TouchableOpacity>
299
+ )}
300
+
301
+ {pw.step !== 'identifier' && (
302
+ <TouchableOpacity onPress={pw.back} accessibilityRole="button" style={styles.linkButton}>
303
+ <Text style={[styles.linkText, { color: theme.colors.textSecondary }]}>Back</Text>
304
+ </TouchableOpacity>
305
+ )}
306
+ </View>
307
+
308
+ {/* "or" divider */}
309
+ <View style={styles.dividerRow}>
310
+ <View style={[styles.dividerLine, { backgroundColor: theme.colors.border }]} />
311
+ <Text style={[styles.dividerText, { color: theme.colors.textSecondary }]}>or</Text>
312
+ <View style={[styles.dividerLine, { backgroundColor: theme.colors.border }]} />
313
+ </View>
314
+
315
+ {/* SECONDARY — cross-app device flow. Its loading/error state gates
316
+ ONLY this section; the password form above is always usable. */}
317
+ {isLoading ? (
318
+ <View style={styles.deviceLoading}>
220
319
  <Loading size="small" style={styles.statusSpinner} />
221
- <Text style={[styles.statusText, { color: theme.colors.textSecondary }]}>
222
- Waiting for authorization...
223
- </Text>
320
+ <Text style={[styles.statusText, { color: theme.colors.textSecondary }]}>Preparing sign in…</Text>
224
321
  </View>
225
- )}
226
-
227
- {/* Collapsed "sign in on another device" QR disclosure. Hidden on
228
- a cross-apex web RP: a remote Commons-app approval completes via
229
- the device-flow claim, which plants no `fedcm_session`. */}
230
- {!crossApexWeb && (
231
- <View style={styles.qrSection}>
232
- <AnotherDeviceQR qrData={qrData} qrPayload={qrPayload} />
322
+ ) : error ? (
323
+ <View style={styles.errorContainer}>
324
+ <Text className="text-destructive" style={styles.errorText}>{error}</Text>
325
+ <Button variant="secondary" onPress={retry} style={styles.secondaryButton}>Try Again</Button>
233
326
  </View>
327
+ ) : (
328
+ <>
329
+ {qrPayload && (
330
+ <Button
331
+ variant="secondary"
332
+ onPress={openSameDeviceApproval}
333
+ icon={<OxyLogo variant="icon" size={20} fillColor={theme.colors.text} style={styles.buttonIcon} />}
334
+ style={styles.secondaryButton}
335
+ >
336
+ Sign in with the Oxy app
337
+ </Button>
338
+ )}
339
+
340
+ {isWaiting && (
341
+ <View style={styles.statusContainer}>
342
+ <Loading size="small" style={styles.statusSpinner} />
343
+ <Text style={[styles.statusText, { color: theme.colors.textSecondary }]}>Waiting for authorization…</Text>
344
+ </View>
345
+ )}
346
+
347
+ <View style={styles.qrSection}>
348
+ <AnotherDeviceQR qrData={qrData} qrPayload={qrPayload} />
349
+ </View>
350
+ </>
234
351
  )}
235
- </>
352
+ </Animated.View>
236
353
  )}
237
354
 
238
355
  {/* Footer — create an account */}
@@ -241,9 +358,7 @@ const SignInModalContent: React.FC<SignInModalContentProps> = ({
241
358
  Don't have an Oxy account?{' '}
242
359
  </Text>
243
360
  <TouchableOpacity onPress={handleCreateAccount} accessibilityRole="link">
244
- <Text style={styles.footerLink} className="text-primary">
245
- Create one
246
- </Text>
361
+ <Text style={styles.footerLink} className="text-primary">Create one</Text>
247
362
  </TouchableOpacity>
248
363
  </View>
249
364
  </Animated.View>
@@ -260,42 +375,54 @@ const styles = StyleSheet.create({
260
375
  },
261
376
  card: {
262
377
  width: '100%',
263
- maxWidth: 400,
378
+ maxWidth: 420,
264
379
  alignItems: 'center',
265
380
  paddingHorizontal: 24,
266
- borderRadius: 24,
381
+ borderRadius: 28,
267
382
  },
268
383
  closeButton: {
269
384
  position: 'absolute',
270
385
  top: 16,
271
386
  right: 16,
272
- width: 40,
273
- height: 40,
274
- borderRadius: 20,
387
+ width: 36,
388
+ height: 36,
389
+ borderRadius: 18,
275
390
  justifyContent: 'center',
276
391
  alignItems: 'center',
277
392
  zIndex: 10,
278
393
  },
279
- closeButtonText: {
280
- fontSize: 28,
281
- fontWeight: '300',
282
- lineHeight: 32,
283
- },
284
394
  header: {
285
395
  alignItems: 'center',
286
- marginBottom: 28,
396
+ marginBottom: 24,
287
397
  },
288
398
  title: {
289
399
  fontSize: 26,
290
- fontWeight: 'bold',
400
+ fontWeight: '800',
401
+ letterSpacing: -0.5,
291
402
  marginTop: 16,
292
403
  textAlign: 'center',
293
404
  },
294
405
  subtitle: {
295
406
  fontSize: 15,
407
+ lineHeight: 21,
296
408
  marginTop: 8,
297
409
  textAlign: 'center',
298
410
  },
411
+ phase: {
412
+ width: '100%',
413
+ },
414
+ backRow: {
415
+ flexDirection: 'row',
416
+ alignItems: 'center',
417
+ gap: 4,
418
+ alignSelf: 'flex-start',
419
+ paddingVertical: 6,
420
+ marginBottom: 8,
421
+ },
422
+ backText: {
423
+ fontSize: 14,
424
+ fontWeight: '600',
425
+ },
299
426
  primaryButton: {
300
427
  width: '100%',
301
428
  borderRadius: 12,
@@ -308,6 +435,57 @@ const styles = StyleSheet.create({
308
435
  buttonIcon: {
309
436
  marginRight: 10,
310
437
  },
438
+ form: {
439
+ width: '100%',
440
+ },
441
+ input: {
442
+ width: '100%',
443
+ borderWidth: 1,
444
+ borderRadius: 12,
445
+ paddingHorizontal: 16,
446
+ paddingVertical: 13,
447
+ fontSize: 15,
448
+ marginBottom: 12,
449
+ },
450
+ contextText: {
451
+ fontSize: 14,
452
+ textAlign: 'center',
453
+ marginBottom: 12,
454
+ },
455
+ formError: {
456
+ fontSize: 14,
457
+ textAlign: 'center',
458
+ marginTop: 12,
459
+ },
460
+ linkButton: {
461
+ alignSelf: 'center',
462
+ paddingVertical: 8,
463
+ marginTop: 8,
464
+ },
465
+ linkText: {
466
+ fontSize: 14,
467
+ fontWeight: '600',
468
+ },
469
+ dividerRow: {
470
+ flexDirection: 'row',
471
+ alignItems: 'center',
472
+ width: '100%',
473
+ marginVertical: 20,
474
+ },
475
+ dividerLine: {
476
+ flex: 1,
477
+ height: 1,
478
+ },
479
+ dividerText: {
480
+ marginHorizontal: 12,
481
+ fontSize: 13,
482
+ },
483
+ deviceLoading: {
484
+ flexDirection: 'row',
485
+ alignItems: 'center',
486
+ justifyContent: 'center',
487
+ paddingVertical: 12,
488
+ },
311
489
  statusContainer: {
312
490
  flexDirection: 'row',
313
491
  alignItems: 'center',
@@ -324,14 +502,6 @@ const styles = StyleSheet.create({
324
502
  width: '100%',
325
503
  marginTop: 24,
326
504
  },
327
- loadingContainer: {
328
- alignItems: 'center',
329
- paddingVertical: 40,
330
- },
331
- loadingText: {
332
- marginTop: 16,
333
- fontSize: 14,
334
- },
335
505
  errorContainer: {
336
506
  alignItems: 'center',
337
507
  paddingVertical: 20,