@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,397 +1,63 @@
1
- import { useMemo } from 'react';
2
- import { useQuery } from '@tanstack/react-query';
3
- import {
4
- getAccountDisplayName,
5
- getAccountFallbackHandle,
6
- } from '@oxyhq/core';
7
- import type {
8
- User,
9
- AccountNode,
10
- AccountRelationship,
11
- AccountKind,
12
- AccountMember,
13
- } from '@oxyhq/core';
1
+ import { useCallback, useMemo, useSyncExternalStore } from 'react';
2
+ import type { SwitchableAccount, AccountDialogSnapshot } from '@oxyhq/core';
14
3
  import { useOxy } from '../context/OxyContext';
15
- import { useI18n } from './useI18n';
16
- import { queryKeys } from './queries/queryKeys';
17
-
18
- /**
19
- * The per-account user shape carried by a {@link SwitchableAccount}. The SDK's
20
- * canonical {@link User} document — either the freshest `useOxy().user` (the
21
- * active row), a profile resolved via `oxyServices.getUsersByIds()` (every other
22
- * device row), or the `account` document embedded in an account-graph node.
23
- */
24
- export type SwitchableAccountUser = User;
25
-
26
- /**
27
- * One account the signed-in user can switch INTO, in the uniform switch model.
28
- *
29
- * A switchable account is either a device sign-in, an account-graph node (owned
30
- * org / shared-with-you), or BOTH (an account that has been switched into
31
- * becomes a device session while still being a graph node — the two are deduped
32
- * into a single row). Every row carries a canonical `accountId` (the uniform
33
- * switch key passed to `switchToAccount`); `sessionId` is present IFF the
34
- * account is currently signed in on THIS device.
35
- */
36
- export interface SwitchableAccount {
37
- /**
38
- * Canonical account id (the underlying `User._id`). The single key EVERY
39
- * switch uses — `switchToAccount(accountId)`. Always present.
40
- */
41
- accountId: string;
42
- /**
43
- * Device session id, present IFF this account is signed in on THIS device.
44
- * Absent for a graph account not yet switched into. Used only for
45
- * device-scoped actions (per-account sign-out via `removeSession`); switching
46
- * ALWAYS goes through `switchToAccount(accountId)`.
47
- */
48
- sessionId?: string;
49
- /**
50
- * Device-local refresh-cookie slot index (0..N-1), when the underlying
51
- * `ClientSession` carries one (web silent-switch). Absent on native and for
52
- * graph-only rows.
53
- */
54
- authuser?: number;
55
- /** Whether this account is the currently-active one. */
56
- isCurrent: boolean;
57
- /** Whether this account is signed in on THIS device (has a `sessionId`). */
58
- onDevice: boolean;
59
- /**
60
- * The caller's relationship to this account when it appears in the account
61
- * graph: `self` (the caller's own personal account), `owner` (an org/project/
62
- * bot the caller owns), or `member` (shared with the caller). Absent for an
63
- * independent device sign-in that is NOT in the active account's graph.
64
- */
65
- relationship?: AccountRelationship;
66
- /** Account classification (personal/organization/…). Cosmetic badge only. */
67
- kind?: AccountKind;
68
- /** Parent account id for 2-level tree grouping, or `null` for a root. */
69
- parentAccountId?: string | null;
70
- /**
71
- * The caller's effective membership (role + permissions) in this account when
72
- * it appears in the graph, or `null`/absent otherwise. Use `permissions` to
73
- * gate per-account settings UI.
74
- */
75
- callerMembership?: AccountMember | null;
76
- /** Friendly display name (never blank — falls back to a handle/sentinel). */
77
- displayName: string;
78
- /**
79
- * Real account email, or `null` when the account genuinely has none. NEVER a
80
- * synthesized `username@oxy.so` — a missing email falls back to the `@handle`
81
- * secondary line.
82
- */
83
- email: string | null;
84
- /** Resolved avatar thumbnail URL, or `undefined` when the account has no avatar. */
85
- avatarUrl?: string;
86
- /** Account's preferred Bloom color preset, or `null` when unset. */
87
- color: string | null;
88
- /** The underlying per-account user payload. */
89
- user: SwitchableAccountUser;
90
- }
91
4
 
92
5
  export interface UseSwitchableAccountsResult {
93
6
  /** Every switchable account (device sign-ins + linked graph accounts). */
94
7
  accounts: SwitchableAccount[];
95
- /** True until the per-device-account profile fetch settles. */
8
+ /** True until the initial account-list fetch settles with no data yet. */
96
9
  isLoading: boolean;
97
- /** The currently-active session id (mirrors `useOxy().activeSessionId`). */
10
+ /** The currently-active account's device session id, or `null`. */
98
11
  currentSessionId: string | null;
99
12
  }
100
13
 
101
- /** The minimal device-session shape {@link buildSwitchableAccounts} reads. */
102
- export interface SwitchableSessionInput {
103
- sessionId: string;
104
- userId?: string;
105
- authuser?: number;
106
- }
107
-
108
- export interface BuildSwitchableAccountsInput {
109
- /** Device sessions projected from the server-authoritative `SessionClient`. */
110
- sessions: SwitchableSessionInput[];
111
- /** The currently-active session id. */
112
- activeSessionId: string | null;
113
- /** The freshest copy of the active account's user (`useOxy().user`). */
114
- liveUser: User | null;
115
- isAuthenticated: boolean;
116
- /** The account graph under the active account (`useOxy().accounts`). */
117
- graph: AccountNode[];
118
- /** Per-device-account profiles resolved via `getUsersByIds()`, keyed by id. */
119
- profilesById: Map<string, User>;
120
- locale: string;
121
- /** Resolves a file id to a thumbnail URL (bound to `getFileDownloadUrl`). */
122
- resolveAvatarUrl: (avatar: string | null | undefined) => string | undefined;
123
- }
124
-
125
- /**
126
- * Resolve which entries are the current account, robustly.
127
- *
128
- * Primary signal: `entry.sessionId === activeSessionId` — both are projected
129
- * from the same `SessionClient` state, so this normally matches exactly.
130
- * Fallbacks exist only to bridge the brief window between the two sequential
131
- * `updateSessions` / `setActiveSessionId` calls in `OxyContext.syncFromClient`,
132
- * applied only when the `sessionId` match found nothing AND the user is
133
- * authenticated:
134
- * 1. Match the live `user.id` against each entry's per-account user id.
135
- * 2. If still nothing and there is exactly one account, mark that one current.
136
- *
137
- * At most ONE entry is ever marked current. Runs over DEVICE rows only (graph
138
- * rows are never active — the active account is always a device session), so the
139
- * single-account fallback counts device rows.
140
- *
141
- * Pure & side-effect free so it is unit-testable without rendering React.
142
- */
143
- export function markCurrentAccount(
144
- accounts: SwitchableAccount[],
145
- activeSessionId: string | null | undefined,
146
- liveUserId: string | null | undefined,
147
- isAuthenticated: boolean,
148
- ): SwitchableAccount[] {
149
- const bySession = accounts.map((account): SwitchableAccount => ({
150
- ...account,
151
- isCurrent: Boolean(activeSessionId) && account.sessionId === activeSessionId,
152
- }));
153
-
154
- if (bySession.some((account) => account.isCurrent) || !isAuthenticated) {
155
- return bySession;
156
- }
157
-
158
- // No row matched by session id: fall back to the live user's id, marking at
159
- // most the FIRST matching entry current (never more than one).
160
- if (liveUserId) {
161
- let matched = false;
162
- const byUser = bySession.map((account): SwitchableAccount => {
163
- if (!matched && account.user.id === liveUserId) {
164
- matched = true;
165
- return { ...account, isCurrent: true };
166
- }
167
- return account;
168
- });
169
- if (matched) {
170
- return byUser;
171
- }
172
- }
173
-
174
- // Authenticated, nothing matched, but there is exactly one account → it must
175
- // be the current one.
176
- if (bySession.length === 1) {
177
- return [{ ...bySession[0], isCurrent: true }];
178
- }
179
-
180
- return bySession;
181
- }
182
-
183
14
  /**
184
- * Pure union of device sign-ins and account-graph nodes into the flat
185
- * {@link SwitchableAccount}[] the switchers render. Extracted so the merge /
186
- * dedup semantics are unit-testable without rendering React.
187
- *
188
- * Order: device rows first (in session order, current flagged), then graph-only
189
- * rows (in graph order). An account present as BOTH a device session and a graph
190
- * node is deduped into ONE device row enriched with the graph metadata
191
- * (relationship / kind / parent / membership).
15
+ * Stable empty snapshot for the no-provider loading state, so `useSyncExternalStore`
16
+ * always has a getSnapshot that returns a constant reference before mount.
192
17
  */
193
- export function buildSwitchableAccounts(input: BuildSwitchableAccountsInput): SwitchableAccount[] {
194
- const {
195
- sessions,
196
- activeSessionId,
197
- liveUser,
198
- isAuthenticated,
199
- graph,
200
- profilesById,
201
- locale,
202
- resolveAvatarUrl,
203
- } = input;
204
-
205
- // Nothing is switchable when signed out — never surface a lingering device
206
- // session or graph node once the user is unauthenticated.
207
- if (!isAuthenticated) {
208
- return [];
209
- }
210
-
211
- const toRow = (
212
- accountUser: User,
213
- opts: {
214
- sessionId?: string;
215
- authuser?: number;
216
- relationship?: AccountRelationship;
217
- kind?: AccountKind;
218
- parentAccountId?: string | null;
219
- callerMembership?: AccountMember | null;
220
- },
221
- ): SwitchableAccount => {
222
- const displayName = getAccountDisplayName(accountUser, locale);
223
- const handle = getAccountFallbackHandle(accountUser);
224
- const secondaryHandle = handle ? `@${handle}` : null;
225
- return {
226
- accountId: accountUser.id?.toString() ?? '',
227
- sessionId: opts.sessionId,
228
- authuser: opts.authuser,
229
- isCurrent: Boolean(opts.sessionId) && opts.sessionId === activeSessionId,
230
- onDevice: Boolean(opts.sessionId),
231
- relationship: opts.relationship,
232
- kind: opts.kind,
233
- parentAccountId: opts.parentAccountId,
234
- callerMembership: opts.callerMembership,
235
- displayName,
236
- // Real email, or the `@handle` fallback (NEVER synthesized).
237
- email: accountUser.email ?? secondaryHandle,
238
- avatarUrl: resolveAvatarUrl(accountUser.avatar),
239
- color: accountUser.color ?? null,
240
- user: accountUser,
241
- };
242
- };
243
-
244
- // --- Device rows ---
245
- const built = sessions.flatMap((session): SwitchableAccount[] => {
246
- const isCurrent = session.sessionId === activeSessionId;
247
- // The active row always uses the live `user` — freshest available —
248
- // regardless of whether the batch profile fetch has resolved yet.
249
- const accountUser: User | undefined = isCurrent && liveUser
250
- ? liveUser
251
- : (session.userId ? profilesById.get(session.userId) : undefined);
252
- if (!accountUser) {
253
- return [];
254
- }
255
- return [toRow(accountUser, { sessionId: session.sessionId, authuser: session.authuser })];
256
- });
257
-
258
- const flagged = markCurrentAccount(built, activeSessionId, liveUser?.id ?? null, isAuthenticated);
259
-
260
- // The signed-in user must ALWAYS be represented, even before the device
261
- // session set has synced (e.g. immediately after cold boot). Synthesize a
262
- // single current row from the live `useOxy().user` in that case.
263
- const deviceRows = flagged.length === 0 && isAuthenticated && liveUser && activeSessionId
264
- ? [toRow(liveUser, { sessionId: activeSessionId })]
265
- : flagged;
266
-
267
- // --- Merge graph nodes, deduping by account id ---
268
- const byAccountId = new Map<string, SwitchableAccount>();
269
- const order: string[] = [];
270
- const remember = (row: SwitchableAccount): void => {
271
- if (!row.accountId || byAccountId.has(row.accountId)) {
272
- return;
273
- }
274
- byAccountId.set(row.accountId, row);
275
- order.push(row.accountId);
276
- };
277
-
278
- for (const row of deviceRows) {
279
- remember(row);
280
- }
281
-
282
- for (const node of graph) {
283
- const existing = byAccountId.get(node.accountId);
284
- if (existing) {
285
- // On-device account that is ALSO in the graph: enrich the device row
286
- // with graph metadata; keep its (freshest) profile + sessionId.
287
- byAccountId.set(node.accountId, {
288
- ...existing,
289
- relationship: node.relationship,
290
- kind: node.kind,
291
- parentAccountId: node.parentAccountId,
292
- callerMembership: node.callerMembership,
293
- });
294
- continue;
295
- }
296
- // Graph-only account (owned org / shared, not yet a device session).
297
- remember(toRow(node.account, {
298
- relationship: node.relationship,
299
- kind: node.kind,
300
- parentAccountId: node.parentAccountId,
301
- callerMembership: node.callerMembership,
302
- }));
303
- }
304
-
305
- return order.flatMap((id) => {
306
- const row = byAccountId.get(id);
307
- return row ? [row] : [];
308
- });
309
- }
18
+ const EMPTY_SNAPSHOT: AccountDialogSnapshot = {
19
+ view: 'accounts',
20
+ accounts: [],
21
+ activeAccountId: null,
22
+ loading: false,
23
+ error: null,
24
+ switchingAccountId: null,
25
+ signIn: { phase: 'idle', authorizeCode: null, qrPayload: null, expiresAt: null, error: null },
26
+ };
310
27
 
311
28
  /**
312
- * Resolve every account the signed-in user can switch into — device sign-ins
313
- * AND linked graph accounts (owned orgs + shared-with-you) — as one flat list
314
- * with real per-account name / email / avatar / color, deduped by account id.
315
- *
316
- * ## Data sources
317
- *
318
- * - Device sign-ins: `useOxy().sessions` a `ClientSession[]` projected by
319
- * `OxyContext.syncFromClient` from the server-authoritative `SessionClient`.
320
- * Each session carries only `sessionId` + `userId`, so this hook additionally
321
- * fetches every device account's profile via `oxyServices.getUsersByIds()`.
322
- * The ACTIVE row always uses `useOxy().user` directly (the freshest copy).
323
- * - Linked accounts: `useOxy().accounts` — the `AccountNode[]` graph the context
324
- * loads from `GET /accounts`. Each node already embeds its `account` `User`
325
- * document, so graph-only rows need no extra fetch.
326
- *
327
- * An account present as BOTH a device session and a graph node is deduped into
328
- * ONE row (see {@link buildSwitchableAccounts}). EVERY row switches the same
329
- * way — through `useOxy().switchToAccount(row.accountId)`.
330
- *
331
- * ## Error handling
332
- *
333
- * `getUsersByIds()` resolves to `[]` on a failed chunk (logged internally)
334
- * rather than throwing. Device accounts whose profile could not be resolved are
335
- * omitted until a subsequent fetch succeeds (except the active one, which never
336
- * depends on this fetch); graph accounts are unaffected.
29
+ * Every account the signed-in user can switch into — device sign-ins AND linked
30
+ * graph accounts (owned orgs + shared-with-you) — as one flat, deduped list with
31
+ * real per-account name / email / avatar / color.
32
+ *
33
+ * This is a thin binding over the shared `AccountDialogController` in
34
+ * `@oxyhq/core` (the SAME headless source the {@link OxyAccountDialog} renders),
35
+ * so there is ONE projection (`projectSwitchableAccounts`) and one switch path
36
+ * (`controller.switchTo`) across the whole SDK — the local duplicate projection
37
+ * this hook used to own has been removed. Every switch routes through
38
+ * `useOxy().switchToAccount(accountId)` (or the dialog's row press).
337
39
  */
338
40
  export function useSwitchableAccounts(): UseSwitchableAccountsResult {
339
- const {
340
- oxyServices,
341
- sessions,
342
- activeSessionId,
343
- user,
344
- isAuthenticated,
345
- accounts: graph,
346
- } = useOxy();
347
- const { locale } = useI18n();
41
+ const { accountDialogController: controller } = useOxy();
348
42
 
349
- // Every distinct account id carried by the device's session set, sorted for
350
- // a stable query key regardless of session ordering.
351
- const accountIds = useMemo<string[]>(() => {
352
- const ids = new Set<string>();
353
- for (const session of sessions ?? []) {
354
- if (session.userId) {
355
- ids.add(session.userId);
356
- }
357
- }
358
- return Array.from(ids).sort();
359
- }, [sessions]);
360
-
361
- const profilesQuery = useQuery({
362
- queryKey: queryKeys.users.list(accountIds),
363
- queryFn: () => oxyServices.getUsersByIds(accountIds),
364
- enabled: isAuthenticated && accountIds.length > 0,
365
- staleTime: 5 * 60 * 1000, // 5 minutes — matches useUserProfile's convention
366
- gcTime: 30 * 60 * 1000, // 30 minutes
367
- });
368
-
369
- const profilesById = useMemo<Map<string, User>>(() => {
370
- const map = new Map<string, User>();
371
- for (const profile of profilesQuery.data ?? []) {
372
- map.set(profile.id, profile);
373
- }
374
- return map;
375
- }, [profilesQuery.data]);
376
-
377
- const accounts = useMemo<SwitchableAccount[]>(
378
- () => buildSwitchableAccounts({
379
- sessions: sessions ?? [],
380
- activeSessionId: activeSessionId ?? null,
381
- liveUser: user ?? null,
382
- isAuthenticated,
383
- graph: graph ?? [],
384
- profilesById,
385
- locale,
386
- resolveAvatarUrl: (avatar) =>
387
- (avatar ? oxyServices.getFileDownloadUrl(avatar, 'thumb') : undefined),
388
- }),
389
- [sessions, activeSessionId, user, isAuthenticated, graph, profilesById, locale, oxyServices],
43
+ const subscribe = useCallback(
44
+ (listener: () => void) => (controller ? controller.subscribe(listener) : () => undefined),
45
+ [controller],
390
46
  );
47
+ const getSnapshot = useCallback(
48
+ () => (controller ? controller.getSnapshot() : EMPTY_SNAPSHOT),
49
+ [controller],
50
+ );
51
+ const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
52
+
53
+ const currentSessionId = useMemo<string | null>(() => {
54
+ const active = snapshot.accounts.find((account) => account.accountId === snapshot.activeAccountId);
55
+ return active?.sessionId ?? null;
56
+ }, [snapshot]);
391
57
 
392
58
  return {
393
- accounts,
394
- isLoading: isAuthenticated && accountIds.length > 0 && profilesQuery.isLoading,
395
- currentSessionId: activeSessionId ?? null,
59
+ accounts: snapshot.accounts,
60
+ isLoading: snapshot.loading,
61
+ currentSessionId,
396
62
  };
397
63
  }
package/src/ui/index.ts CHANGED
@@ -21,16 +21,14 @@ export { default as OxySignInButton } from './components/OxySignInButton';
21
21
  export { default as OxyAuthPrompt } from './components/OxyAuthPrompt';
22
22
  export type { OxyAuthPromptProps } from './components/OxyAuthPrompt';
23
23
  export { default as OxyLogo } from './components/OxyLogo';
24
+ export { RequireOxyAuth } from './components/RequireOxyAuth';
25
+ export type { RequireOxyAuthProps, RequireOxyAuthPrompt } from './components/RequireOxyAuth';
24
26
  export { default as Avatar } from './components/Avatar';
25
27
  export { default as FollowButton } from './components/FollowButton';
26
28
  export { default as OxyPayButton } from './components/OxyPayButton';
27
29
  export { FontLoader, setupFonts } from './components/FontLoader';
28
30
  export { default as OxyIcon } from './components/icon/OxyIcon';
29
- export { default as AccountMenu } from './components/AccountMenu';
30
- export { default as AccountMenuButton } from './components/AccountMenuButton';
31
31
  export { default as ProfileButton } from './components/ProfileButton';
32
- export { default as ProfileMenu } from './components/ProfileMenu';
33
- export { default as AccountSwitcher, AccountSwitcherView } from './components/AccountSwitcher';
34
32
 
35
33
  // Context + hooks
36
34
  export { useOxy } from './context/OxyContext';
@@ -42,7 +40,6 @@ export type { UseStorageOptions, UseStorageResult } from './hooks/useStorage';
42
40
  // Screens
43
41
  export { default as ProfileScreen } from './screens/ProfileScreen';
44
42
  export { default as ManageAccountScreen } from './screens/ManageAccountScreen';
45
- export { default as AccountSwitcherScreen } from './screens/AccountSwitcherScreen';
46
43
  export { default as CreateAccountScreen } from './screens/CreateAccountScreen';
47
44
  export { default as AccountMembersScreen } from './screens/AccountMembersScreen';
48
45
  export { default as AccountSettingsScreen } from './screens/AccountSettingsScreen';
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Imperative bridge for the unified {@link OxyAccountDialog}.
3
+ *
4
+ * Mirrors `bottomSheetManager` — a tiny module-level indirection so any caller
5
+ * (even outside React, e.g. an app's imperative "sign in" handler) can open the
6
+ * single account dialog without a ref. `OxyContext` registers the live open/close
7
+ * controls on mount via {@link registerAccountDialogControls} and reports the
8
+ * dialog's visibility here via {@link notifyAccountDialogVisibility}; the exported
9
+ * `openAccountDialog` / `closeAccountDialog` drive those controls.
10
+ *
11
+ * `showSignInModal` / `hideSignInModal` / `subscribeToSignInModal` are the stable
12
+ * public surface the previous `SignInModal` module owned — kept so existing
13
+ * consumers (inbox, accounts) keep working while the sign-in surface is now the
14
+ * unified dialog opened on its `signin` view.
15
+ */
16
+
17
+ import type { AccountDialogView } from '@oxyhq/core';
18
+
19
+ /** Live open/close handles registered by the mounted provider. */
20
+ export interface AccountDialogControls {
21
+ open: (view?: AccountDialogView) => void;
22
+ close: () => void;
23
+ }
24
+
25
+ let controls: AccountDialogControls | null = null;
26
+ const visibilityListeners = new Set<(visible: boolean) => void>();
27
+
28
+ /**
29
+ * Register the provider's live open/close controls. Returns an unregister
30
+ * function that only clears the slot if it still owns it (guards against a late
31
+ * unmount clobbering a newer provider).
32
+ */
33
+ export function registerAccountDialogControls(next: AccountDialogControls): () => void {
34
+ controls = next;
35
+ return () => {
36
+ if (controls === next) {
37
+ controls = null;
38
+ }
39
+ };
40
+ }
41
+
42
+ /** Open the account dialog on `view` (default `accounts`). No-op before mount. */
43
+ export function openAccountDialog(view?: AccountDialogView): void {
44
+ controls?.open(view);
45
+ }
46
+
47
+ /** Close the account dialog. No-op before mount. */
48
+ export function closeAccountDialog(): void {
49
+ controls?.close();
50
+ }
51
+
52
+ /**
53
+ * Report the dialog's current visibility so subscribers (e.g. `OxySignInButton`)
54
+ * can reflect an in-flight "Signing in…" state. Called by the provider whenever
55
+ * the open flag changes, so listeners stay accurate regardless of what triggered
56
+ * the change (imperative open, backdrop dismiss, completed sign-in).
57
+ */
58
+ export function notifyAccountDialogVisibility(visible: boolean): void {
59
+ for (const listener of visibilityListeners) {
60
+ listener(visible);
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Subscribe to dialog visibility changes. Returns an unsubscribe function.
66
+ * Retains the name the old `SignInModal` exposed so `OxySignInButton` keeps its
67
+ * "Signing in…" affordance.
68
+ */
69
+ export function subscribeToSignInModal(listener: (visible: boolean) => void): () => void {
70
+ visibilityListeners.add(listener);
71
+ return () => {
72
+ visibilityListeners.delete(listener);
73
+ };
74
+ }
75
+
76
+ /** Open the unified dialog on its sign-in view. Public back-compat entry point. */
77
+ export function showSignInModal(): void {
78
+ openAccountDialog('signin');
79
+ }
80
+
81
+ /** Close the unified dialog. Public back-compat entry point. */
82
+ export function hideSignInModal(): void {
83
+ closeAccountDialog();
84
+ }
@@ -7,7 +7,6 @@ import type { BaseScreenProps } from '../types/navigation';
7
7
 
8
8
  // Define all available route names
9
9
  export type RouteName =
10
- | 'OxyAuth' // Sign in with Oxy (QR code / deep link to Accounts app)
11
10
  | 'ManageAccount' // Unified "Manage your Oxy Account" surface
12
11
  | 'AccountVerification'
13
12
  | 'PaymentGateway'
@@ -37,7 +36,6 @@ export type RouteName =
37
36
  | 'TrustFAQ'
38
37
  | 'FollowersList' // List of user's followers
39
38
  | 'FollowingList' // List of users being followed
40
- | 'AccountSwitcher' // Unified account switcher (device sign-ins + account graph)
41
39
  | 'CreateAccount' // Create a new account (organization / project / bot)
42
40
  | 'AccountMembers' // Manage an account's members (invite / roles / transfer)
43
41
  | 'AccountSettings' // Per-account profile edit + members + danger zone
@@ -49,7 +47,6 @@ export type RouteName =
49
47
  // Lazy screen loaders - functions that return screen components on-demand
50
48
  // This breaks the require cycle by deferring imports until screens are actually needed
51
49
  const screenLoaders: Record<RouteName, () => ComponentType<BaseScreenProps>> = {
52
- OxyAuth: () => require('../screens/OxyAuthScreen').default,
53
50
  ManageAccount: () => require('../screens/ManageAccountScreen').default,
54
51
  AccountVerification: () => require('../screens/AccountVerificationScreen').default,
55
52
  PaymentGateway: () => require('../screens/PaymentGatewayScreen').default,
@@ -82,7 +79,6 @@ const screenLoaders: Record<RouteName, () => ComponentType<BaseScreenProps>> = {
82
79
  // User list screens (followers/following)
83
80
  FollowersList: () => require('../screens/FollowersListScreen').default,
84
81
  FollowingList: () => require('../screens/FollowingListScreen').default,
85
- AccountSwitcher: () => require('../screens/AccountSwitcherScreen').default,
86
82
  CreateAccount: () => require('../screens/CreateAccountScreen').default,
87
83
  AccountMembers: () => require('../screens/AccountMembersScreen').default,
88
84
  AccountSettings: () => require('../screens/AccountSettingsScreen').default,
@@ -78,7 +78,7 @@ const formatRelative = (dateString?: string): string => {
78
78
  * Replaces AccountOverview + AccountSettings + the per-account half of
79
79
  * SessionManagement. Lists ONLY the active user's profile, sessions on this
80
80
  * device, and security/destructive actions for THIS account. Multi-account
81
- * surface lives in {@link AccountSwitcher} — keep these concerns separate.
81
+ * surface lives in the unified `OxyAccountDialog` — keep these concerns separate.
82
82
  */
83
83
  const ManageAccountScreen: React.FC<BaseScreenProps> = ({
84
84
  onClose,
@@ -95,6 +95,7 @@ const ManageAccountScreen: React.FC<BaseScreenProps> = ({
95
95
  logout,
96
96
  openAvatarPicker,
97
97
  accounts,
98
+ openAccountDialog,
98
99
  } = useOxy();
99
100
 
100
101
  const { data: userFromQuery, isLoading: userLoading } = useCurrentUser({
@@ -700,7 +701,7 @@ const ManageAccountScreen: React.FC<BaseScreenProps> = ({
700
701
  || 'Accounts you own or share'
701
702
  )
702
703
  }
703
- onPress={() => navigate?.('AccountSwitcher')}
704
+ onPress={() => openAccountDialog('accounts')}
704
705
  />
705
706
  <SettingsListItem
706
707
  icon={
@@ -4,6 +4,7 @@ import {
4
4
  createSessionClientHost,
5
5
  type AuthStateStore,
6
6
  type OxyServices,
7
+ type SessionClientOptions,
7
8
  } from '@oxyhq/core';
8
9
  import { createTokenTransport } from './tokenTransport';
9
10
 
@@ -31,12 +32,19 @@ export function createSessionClient(
31
32
  oxyServices: OxyServices,
32
33
  store: AuthStateStore,
33
34
  onUnauthenticated?: () => void,
35
+ /**
36
+ * Optional signed-out realtime wiring: `signedOutSocketAuth` (open the socket
37
+ * while signed out — web returns `true` to ride the `oxy_device` cookie,
38
+ * native returns the shared device token) and `onSessionAppeared` (self-acquire
39
+ * when a sibling signs in on this device).
40
+ */
41
+ extra?: Pick<SessionClientOptions, 'signedOutSocketAuth' | 'onSessionAppeared'>,
34
42
  ): {
35
43
  client: SessionClient;
36
44
  host: ReturnType<typeof createSessionClientHost>;
37
45
  } {
38
46
  const host = createSessionClientHost(oxyServices);
39
47
  const transport = createTokenTransport(oxyServices, store);
40
- const client = new SessionClient(host, { transport, socketFactory: io, onUnauthenticated });
48
+ const client = new SessionClient(host, { transport, socketFactory: io, onUnauthenticated, ...extra });
41
49
  return { client, host };
42
50
  }
@@ -63,4 +63,31 @@ export interface OxyProviderProps {
63
63
  authWebUrl?: string;
64
64
  authRedirectUri?: string;
65
65
  queryClient?: QueryClient;
66
+ /**
67
+ * Convenience: wrap the whole app subtree in `<RequireOxyAuth prompt=...>`.
68
+ * `off` (default) renders children unconditionally; `soft` adds a dismissible
69
+ * sign-in banner while signed out; `hard` blocks the app behind the signed-out
70
+ * wall until the user signs in. For finer control, mount `RequireOxyAuth`
71
+ * yourself around a specific subtree instead.
72
+ * @default 'off'
73
+ */
74
+ requireAuth?: 'off' | 'soft' | 'hard';
75
+ /**
76
+ * Whether this provider acts as the ecosystem's device-first **session
77
+ * authority**. `true` (default) runs the full device-first cold boot on
78
+ * mount (bootstrap-return → stored tokens → shared key / bootstrap hop) and
79
+ * opens the signed-out device-state socket so an idle tab self-acquires when
80
+ * a sibling signs in — the correct behavior for every Relying Party app.
81
+ *
82
+ * Set `false` for the **IdP host** (`auth.oxy.so`): the IdP is NOT a session
83
+ * authority (see the handoff "IdP vs RP" section). With `coldBoot={false}`
84
+ * the provider skips `runSessionColdBoot` entirely and never opens the
85
+ * signed-out device socket — auth resolves immediately as signed out (no
86
+ * boot spinner). Interactive sign-in is unaffected: a user who signs in
87
+ * through this provider (password, 2FA, or the "Sign in with Oxy" QR device
88
+ * flow) still commits a normal session scoped to this origin, which is all
89
+ * the IdP needs to drive its OAuth authorize/consent flow.
90
+ * @default true
91
+ */
92
+ coldBoot?: boolean;
66
93
  }