@oxyhq/services 15.0.0 → 17.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 (280) hide show
  1. package/lib/commonjs/index.js +25 -55
  2. package/lib/commonjs/index.js.map +1 -1
  3. package/lib/commonjs/ui/client.js +0 -14
  4. package/lib/commonjs/ui/client.js.map +1 -1
  5. package/lib/commonjs/ui/components/OxyAccountDialog.js +764 -0
  6. package/lib/commonjs/ui/components/OxyAccountDialog.js.map +1 -0
  7. package/lib/commonjs/ui/components/OxyProvider.js +11 -6
  8. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  9. package/lib/commonjs/ui/components/OxySignInButton.js +10 -21
  10. package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
  11. package/lib/commonjs/ui/components/ProfileButton.js +25 -112
  12. package/lib/commonjs/ui/components/ProfileButton.js.map +1 -1
  13. package/lib/commonjs/ui/components/RequireOxyAuth.js +299 -0
  14. package/lib/commonjs/ui/components/RequireOxyAuth.js.map +1 -0
  15. package/lib/commonjs/ui/context/OxyContext.js +106 -2
  16. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  17. package/lib/commonjs/ui/hooks/mutations/mutationKeys.js +1 -1
  18. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +10 -10
  19. package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  20. package/lib/commonjs/ui/hooks/queries/queryKeys.js +1 -1
  21. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js +7 -8
  22. package/lib/commonjs/ui/hooks/queries/useAccountQueries.js.map +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/ConnectedAppsScreen.js +17 -18
  34. package/lib/commonjs/ui/screens/ConnectedAppsScreen.js.map +1 -1
  35. package/lib/commonjs/ui/screens/ManageAccountScreen.js +4 -3
  36. package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
  37. package/lib/commonjs/ui/session/createSessionClient.js +10 -2
  38. package/lib/commonjs/ui/session/createSessionClient.js.map +1 -1
  39. package/lib/module/index.js +20 -17
  40. package/lib/module/index.js.map +1 -1
  41. package/lib/module/ui/client.js +1 -2
  42. package/lib/module/ui/client.js.map +1 -1
  43. package/lib/module/ui/components/OxyAccountDialog.js +761 -0
  44. package/lib/module/ui/components/OxyAccountDialog.js.map +1 -0
  45. package/lib/module/ui/components/OxyProvider.js +11 -6
  46. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  47. package/lib/module/ui/components/OxySignInButton.js +10 -21
  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/context/OxyContext.js +107 -3
  54. package/lib/module/ui/context/OxyContext.js.map +1 -1
  55. package/lib/module/ui/hooks/mutations/mutationKeys.js +1 -1
  56. package/lib/module/ui/hooks/mutations/useAccountMutations.js +8 -8
  57. package/lib/module/ui/hooks/mutations/useAccountMutations.js.map +1 -1
  58. package/lib/module/ui/hooks/queries/queryKeys.js +1 -1
  59. package/lib/module/ui/hooks/queries/useAccountQueries.js +5 -6
  60. package/lib/module/ui/hooks/queries/useAccountQueries.js.map +1 -1
  61. package/lib/module/ui/hooks/useAuth.js +19 -30
  62. package/lib/module/ui/hooks/useAuth.js.map +1 -1
  63. package/lib/module/ui/hooks/useSwitchableAccounts.js +38 -267
  64. package/lib/module/ui/hooks/useSwitchableAccounts.js.map +1 -1
  65. package/lib/module/ui/index.js +1 -5
  66. package/lib/module/ui/index.js.map +1 -1
  67. package/lib/module/ui/navigation/accountDialogManager.js +81 -0
  68. package/lib/module/ui/navigation/accountDialogManager.js.map +1 -0
  69. package/lib/module/ui/navigation/routes.js +0 -2
  70. package/lib/module/ui/navigation/routes.js.map +1 -1
  71. package/lib/module/ui/screens/ConnectedAppsScreen.js +19 -20
  72. package/lib/module/ui/screens/ConnectedAppsScreen.js.map +1 -1
  73. package/lib/module/ui/screens/ManageAccountScreen.js +4 -3
  74. package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
  75. package/lib/module/ui/session/createSessionClient.js +10 -2
  76. package/lib/module/ui/session/createSessionClient.js.map +1 -1
  77. package/lib/typescript/commonjs/index.d.ts +6 -13
  78. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  79. package/lib/typescript/commonjs/ui/client.d.ts +0 -4
  80. package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
  81. package/lib/typescript/commonjs/ui/components/OxyAccountDialog.d.ts +35 -0
  82. package/lib/typescript/commonjs/ui/components/OxyAccountDialog.d.ts.map +1 -0
  83. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts +2 -2
  84. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
  85. package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts.map +1 -1
  86. package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts +11 -23
  87. package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts.map +1 -1
  88. package/lib/typescript/commonjs/ui/components/RequireOxyAuth.d.ts +55 -0
  89. package/lib/typescript/commonjs/ui/components/RequireOxyAuth.d.ts.map +1 -0
  90. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +13 -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 +4 -4
  93. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  94. package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts +6 -7
  95. package/lib/typescript/commonjs/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  96. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
  97. package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts +13 -152
  98. package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
  99. package/lib/typescript/commonjs/ui/index.d.ts +2 -5
  100. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  101. package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts +49 -0
  102. package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts.map +1 -0
  103. package/lib/typescript/commonjs/ui/navigation/routes.d.ts +1 -1
  104. package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
  105. package/lib/typescript/commonjs/ui/screens/ConnectedAppsScreen.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts +1 -1
  107. package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  108. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts +9 -2
  109. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/ui/types/navigation.d.ts +9 -0
  111. package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
  112. package/lib/typescript/commonjs/ui/utils/storageHelpers.d.ts +7 -7
  113. package/lib/typescript/module/index.d.ts +6 -13
  114. package/lib/typescript/module/index.d.ts.map +1 -1
  115. package/lib/typescript/module/ui/client.d.ts +0 -4
  116. package/lib/typescript/module/ui/client.d.ts.map +1 -1
  117. package/lib/typescript/module/ui/components/OxyAccountDialog.d.ts +35 -0
  118. package/lib/typescript/module/ui/components/OxyAccountDialog.d.ts.map +1 -0
  119. package/lib/typescript/module/ui/components/OxyProvider.d.ts +2 -2
  120. package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
  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/context/OxyContext.d.ts +13 -1
  127. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  128. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +4 -4
  129. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  130. package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts +6 -7
  131. package/lib/typescript/module/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  132. package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
  133. package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts +13 -152
  134. package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
  135. package/lib/typescript/module/ui/index.d.ts +2 -5
  136. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  137. package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts +49 -0
  138. package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts.map +1 -0
  139. package/lib/typescript/module/ui/navigation/routes.d.ts +1 -1
  140. package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
  141. package/lib/typescript/module/ui/screens/ConnectedAppsScreen.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 +9 -0
  147. package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
  148. package/lib/typescript/module/ui/utils/storageHelpers.d.ts +7 -7
  149. package/package.json +4 -4
  150. package/src/index.ts +23 -28
  151. package/src/ui/client.ts +0 -4
  152. package/src/ui/components/OxyAccountDialog.tsx +760 -0
  153. package/src/ui/components/OxyProvider.tsx +12 -6
  154. package/src/ui/components/OxySignInButton.tsx +10 -21
  155. package/src/ui/components/ProfileButton.tsx +23 -126
  156. package/src/ui/components/RequireOxyAuth.tsx +266 -0
  157. package/src/ui/context/OxyContext.tsx +146 -7
  158. package/src/ui/hooks/mutations/mutationKeys.ts +1 -1
  159. package/src/ui/hooks/mutations/useAccountMutations.ts +8 -8
  160. package/src/ui/hooks/queries/queryKeys.ts +1 -1
  161. package/src/ui/hooks/queries/useAccountQueries.ts +8 -9
  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 +1 -5
  167. package/src/ui/screens/ConnectedAppsScreen.tsx +23 -24
  168. package/src/ui/screens/ManageAccountScreen.tsx +3 -2
  169. package/src/ui/session/createSessionClient.ts +9 -1
  170. package/src/ui/types/navigation.ts +9 -0
  171. package/src/ui/utils/storageHelpers.ts +7 -7
  172. package/lib/commonjs/ui/components/AccountMenu.js +0 -598
  173. package/lib/commonjs/ui/components/AccountMenu.js.map +0 -1
  174. package/lib/commonjs/ui/components/AccountMenuButton.js +0 -135
  175. package/lib/commonjs/ui/components/AccountMenuButton.js.map +0 -1
  176. package/lib/commonjs/ui/components/AccountSwitcher.js +0 -780
  177. package/lib/commonjs/ui/components/AccountSwitcher.js.map +0 -1
  178. package/lib/commonjs/ui/components/AnotherDeviceQR.js +0 -116
  179. package/lib/commonjs/ui/components/AnotherDeviceQR.js.map +0 -1
  180. package/lib/commonjs/ui/components/ProfileMenu.js +0 -518
  181. package/lib/commonjs/ui/components/ProfileMenu.js.map +0 -1
  182. package/lib/commonjs/ui/components/SignInAccountChooser.js +0 -189
  183. package/lib/commonjs/ui/components/SignInAccountChooser.js.map +0 -1
  184. package/lib/commonjs/ui/components/SignInModal.js +0 -600
  185. package/lib/commonjs/ui/components/SignInModal.js.map +0 -1
  186. package/lib/commonjs/ui/hooks/useOxyAuthSession.js +0 -501
  187. package/lib/commonjs/ui/hooks/useOxyAuthSession.js.map +0 -1
  188. package/lib/commonjs/ui/hooks/usePasswordSignIn.js +0 -170
  189. package/lib/commonjs/ui/hooks/usePasswordSignIn.js.map +0 -1
  190. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +0 -51
  191. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +0 -1
  192. package/lib/commonjs/ui/screens/OxyAuthScreen.js +0 -312
  193. package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +0 -1
  194. package/lib/commonjs/utils/deviceFlowSignIn.js +0 -109
  195. package/lib/commonjs/utils/deviceFlowSignIn.js.map +0 -1
  196. package/lib/module/ui/components/AccountMenu.js +0 -593
  197. package/lib/module/ui/components/AccountMenu.js.map +0 -1
  198. package/lib/module/ui/components/AccountMenuButton.js +0 -130
  199. package/lib/module/ui/components/AccountMenuButton.js.map +0 -1
  200. package/lib/module/ui/components/AccountSwitcher.js +0 -774
  201. package/lib/module/ui/components/AccountSwitcher.js.map +0 -1
  202. package/lib/module/ui/components/AnotherDeviceQR.js +0 -110
  203. package/lib/module/ui/components/AnotherDeviceQR.js.map +0 -1
  204. package/lib/module/ui/components/ProfileMenu.js +0 -514
  205. package/lib/module/ui/components/ProfileMenu.js.map +0 -1
  206. package/lib/module/ui/components/SignInAccountChooser.js +0 -183
  207. package/lib/module/ui/components/SignInAccountChooser.js.map +0 -1
  208. package/lib/module/ui/components/SignInModal.js +0 -591
  209. package/lib/module/ui/components/SignInModal.js.map +0 -1
  210. package/lib/module/ui/hooks/useOxyAuthSession.js +0 -495
  211. package/lib/module/ui/hooks/useOxyAuthSession.js.map +0 -1
  212. package/lib/module/ui/hooks/usePasswordSignIn.js +0 -165
  213. package/lib/module/ui/hooks/usePasswordSignIn.js.map +0 -1
  214. package/lib/module/ui/screens/AccountSwitcherScreen.js +0 -48
  215. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +0 -1
  216. package/lib/module/ui/screens/OxyAuthScreen.js +0 -306
  217. package/lib/module/ui/screens/OxyAuthScreen.js.map +0 -1
  218. package/lib/module/utils/deviceFlowSignIn.js +0 -106
  219. package/lib/module/utils/deviceFlowSignIn.js.map +0 -1
  220. package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts +0 -47
  221. package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts.map +0 -1
  222. package/lib/typescript/commonjs/ui/components/AccountMenuButton.d.ts +0 -26
  223. package/lib/typescript/commonjs/ui/components/AccountMenuButton.d.ts.map +0 -1
  224. package/lib/typescript/commonjs/ui/components/AccountSwitcher.d.ts +0 -46
  225. package/lib/typescript/commonjs/ui/components/AccountSwitcher.d.ts.map +0 -1
  226. package/lib/typescript/commonjs/ui/components/AnotherDeviceQR.d.ts +0 -34
  227. package/lib/typescript/commonjs/ui/components/AnotherDeviceQR.d.ts.map +0 -1
  228. package/lib/typescript/commonjs/ui/components/ProfileMenu.d.ts +0 -49
  229. package/lib/typescript/commonjs/ui/components/ProfileMenu.d.ts.map +0 -1
  230. package/lib/typescript/commonjs/ui/components/SignInAccountChooser.d.ts +0 -29
  231. package/lib/typescript/commonjs/ui/components/SignInAccountChooser.d.ts.map +0 -1
  232. package/lib/typescript/commonjs/ui/components/SignInModal.d.ts +0 -28
  233. package/lib/typescript/commonjs/ui/components/SignInModal.d.ts.map +0 -1
  234. package/lib/typescript/commonjs/ui/hooks/useOxyAuthSession.d.ts +0 -105
  235. package/lib/typescript/commonjs/ui/hooks/useOxyAuthSession.d.ts.map +0 -1
  236. package/lib/typescript/commonjs/ui/hooks/usePasswordSignIn.d.ts +0 -31
  237. package/lib/typescript/commonjs/ui/hooks/usePasswordSignIn.d.ts.map +0 -1
  238. package/lib/typescript/commonjs/ui/screens/AccountSwitcherScreen.d.ts +0 -11
  239. package/lib/typescript/commonjs/ui/screens/AccountSwitcherScreen.d.ts.map +0 -1
  240. package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts +0 -22
  241. package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts.map +0 -1
  242. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts +0 -97
  243. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts.map +0 -1
  244. package/lib/typescript/module/ui/components/AccountMenu.d.ts +0 -47
  245. package/lib/typescript/module/ui/components/AccountMenu.d.ts.map +0 -1
  246. package/lib/typescript/module/ui/components/AccountMenuButton.d.ts +0 -26
  247. package/lib/typescript/module/ui/components/AccountMenuButton.d.ts.map +0 -1
  248. package/lib/typescript/module/ui/components/AccountSwitcher.d.ts +0 -46
  249. package/lib/typescript/module/ui/components/AccountSwitcher.d.ts.map +0 -1
  250. package/lib/typescript/module/ui/components/AnotherDeviceQR.d.ts +0 -34
  251. package/lib/typescript/module/ui/components/AnotherDeviceQR.d.ts.map +0 -1
  252. package/lib/typescript/module/ui/components/ProfileMenu.d.ts +0 -49
  253. package/lib/typescript/module/ui/components/ProfileMenu.d.ts.map +0 -1
  254. package/lib/typescript/module/ui/components/SignInAccountChooser.d.ts +0 -29
  255. package/lib/typescript/module/ui/components/SignInAccountChooser.d.ts.map +0 -1
  256. package/lib/typescript/module/ui/components/SignInModal.d.ts +0 -28
  257. package/lib/typescript/module/ui/components/SignInModal.d.ts.map +0 -1
  258. package/lib/typescript/module/ui/hooks/useOxyAuthSession.d.ts +0 -105
  259. package/lib/typescript/module/ui/hooks/useOxyAuthSession.d.ts.map +0 -1
  260. package/lib/typescript/module/ui/hooks/usePasswordSignIn.d.ts +0 -31
  261. package/lib/typescript/module/ui/hooks/usePasswordSignIn.d.ts.map +0 -1
  262. package/lib/typescript/module/ui/screens/AccountSwitcherScreen.d.ts +0 -11
  263. package/lib/typescript/module/ui/screens/AccountSwitcherScreen.d.ts.map +0 -1
  264. package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts +0 -22
  265. package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts.map +0 -1
  266. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts +0 -97
  267. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts.map +0 -1
  268. package/src/ui/components/AccountMenu.tsx +0 -645
  269. package/src/ui/components/AccountMenuButton.tsx +0 -126
  270. package/src/ui/components/AccountSwitcher.tsx +0 -751
  271. package/src/ui/components/AnotherDeviceQR.tsx +0 -119
  272. package/src/ui/components/ProfileMenu.tsx +0 -564
  273. package/src/ui/components/SignInAccountChooser.tsx +0 -162
  274. package/src/ui/components/SignInModal.tsx +0 -530
  275. package/src/ui/hooks/useOxyAuthSession.ts +0 -635
  276. package/src/ui/hooks/usePasswordSignIn.ts +0 -207
  277. package/src/ui/screens/AccountSwitcherScreen.tsx +0 -43
  278. package/src/ui/screens/OxyAuthScreen.tsx +0 -324
  279. package/src/utils/__tests__/deviceFlowSignIn.test.ts +0 -231
  280. package/src/utils/deviceFlowSignIn.ts +0 -152
@@ -9,6 +9,7 @@ import { BloomDialogProvider } from '@oxyhq/bloom';
9
9
  import { ToastOutlet } from '@oxyhq/bloom/toast';
10
10
  import { logger as loggerUtil } from '@oxyhq/core';
11
11
  import { setupFonts } from './FontLoader';
12
+ import { RequireOxyAuth } from './RequireOxyAuth';
12
13
  import { attachQueryPersistence, createQueryClient } from '../hooks/queryClient';
13
14
  import { createPlatformStorage, type StorageInterface } from '../utils/storageHelpers';
14
15
 
@@ -63,8 +64,8 @@ const LazyBottomSheetRouter = lazy((): Promise<{ default: ComponentType }> =>
63
64
  ),
64
65
  );
65
66
 
66
- const LazySignInModal = lazy((): Promise<{ default: ComponentType }> =>
67
- import('./SignInModal').then(
67
+ const LazyOxyAccountDialog = lazy((): Promise<{ default: ComponentType }> =>
68
+ import('./OxyAccountDialog').then(
68
69
  (mod) => ({ default: mod.default as unknown as ComponentType }),
69
70
  () => ({ default: (() => null) as FC }),
70
71
  ),
@@ -75,8 +76,8 @@ const LazySignInModal = lazy((): Promise<{ default: ComponentType }> =>
75
76
  *
76
77
  * Provides authentication, session management, query client, and UI overlays.
77
78
  * Wraps its own overlay stack in SafeAreaProvider and GestureHandlerRootView so
78
- * BottomSheetRouter and SignInModal can safely render even when a consuming app
79
- * has not mounted those providers yet.
79
+ * BottomSheetRouter and OxyAccountDialog can safely render even when a consuming
80
+ * app has not mounted those providers yet.
80
81
  *
81
82
  * Usage:
82
83
  * ```tsx
@@ -114,6 +115,7 @@ const OxyProvider: FC<OxyProviderProps> = ({
114
115
  authWebUrl,
115
116
  authRedirectUri,
116
117
  queryClient: providedQueryClient,
118
+ requireAuth = 'off',
117
119
  }) => {
118
120
 
119
121
  // Dynamic KeyboardProvider for native. Uses variable indirection
@@ -310,10 +312,14 @@ const OxyProvider: FC<OxyProviderProps> = ({
310
312
  clientId={clientId}
311
313
  onAuthStateChange={onAuthStateChange as OxyContextProviderProps['onAuthStateChange']}
312
314
  >
313
- {children}
315
+ {requireAuth === 'off' ? (
316
+ children
317
+ ) : (
318
+ <RequireOxyAuth prompt={requireAuth}>{children}</RequireOxyAuth>
319
+ )}
314
320
  <Suspense fallback={null}>
315
321
  <LazyBottomSheetRouter />
316
- <LazySignInModal />
322
+ <LazyOxyAccountDialog />
317
323
  </Suspense>
318
324
  <ToastOutlet />
319
325
  </OxyContextProvider>
@@ -6,7 +6,7 @@ import { useShallow } from 'zustand/react/shallow';
6
6
  import { useTheme } from '@oxyhq/bloom/theme';
7
7
  import { useOxy } from '../context/OxyContext';
8
8
  import OxyLogo from './OxyLogo';
9
- import { showSignInModal, subscribeToSignInModal } from './SignInModal';
9
+ import { subscribeToSignInModal } from '../navigation/accountDialogManager';
10
10
 
11
11
  export interface OxySignInButtonProps {
12
12
  /**
@@ -84,37 +84,26 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
84
84
  showWhenAuthenticated = false,
85
85
  }) => {
86
86
  const theme = useTheme();
87
- const { showBottomSheet } = useOxy();
87
+ const { openAccountDialog } = useOxy();
88
88
  const { isAuthenticated, isLoading } = useAuthStore(
89
89
  useShallow((state) => ({ isAuthenticated: state.isAuthenticated, isLoading: state.isLoading }))
90
90
  );
91
- // Tracks visibility of the web full-screen sign-in modal so we can show
92
- // "Signing in..." while it's open. On native we open a bottom sheet whose
93
- // visibility is owned by the sheet manager — no subscription needed.
91
+ // Tracks whether the unified account dialog is open so we can show
92
+ // "Signing in..." while it is. The manager reports visibility on every
93
+ // change regardless of platform or what opened/closed it.
94
94
  const [isModalOpen, setIsModalOpen] = useState(false);
95
95
 
96
- // Subscribe to web modal visibility only — bottom sheet manages its own state.
97
- useEffect(() => {
98
- if (Platform.OS !== 'web') return;
99
- return subscribeToSignInModal(setIsModalOpen);
100
- }, []);
96
+ useEffect(() => subscribeToSignInModal(setIsModalOpen), []);
101
97
 
102
- // Handle button press
103
- // - Web: full-screen modal (dialog UX fits desktop / browser)
104
- // - Native: bottom sheet (sheet UX fits iOS/Android)
98
+ // Open the unified account dialog on its sign-in view (same surface on web +
99
+ // native), or defer to a caller-supplied handler.
105
100
  const handlePress = useCallback(() => {
106
101
  if (onPress) {
107
102
  onPress();
108
103
  return;
109
104
  }
110
-
111
- if (Platform.OS === 'web') {
112
- setIsModalOpen(true);
113
- showSignInModal();
114
- } else {
115
- showBottomSheet?.('OxyAuth');
116
- }
117
- }, [onPress, showBottomSheet]);
105
+ openAccountDialog('signin');
106
+ }, [onPress, openAccountDialog]);
118
107
 
119
108
  const themedStyles = useMemo(() => StyleSheet.create({
120
109
  button: {
@@ -1,5 +1,5 @@
1
1
  import type React from 'react';
2
- import { useCallback, useRef, useState } from 'react';
2
+ import { useCallback, useState } from 'react';
3
3
  import {
4
4
  View,
5
5
  Pressable,
@@ -14,21 +14,11 @@ import { Text } from '@oxyhq/bloom/typography';
14
14
  import { useTheme } from '@oxyhq/bloom/theme';
15
15
  import { getAccountDisplayName, getAccountFallbackHandle } from '@oxyhq/core';
16
16
  import { useAuth } from '../hooks/useAuth';
17
+ import { useOxy } from '../context/OxyContext';
17
18
  import { useI18n } from '../hooks/useI18n';
18
- import ProfileMenu, { type ProfileMenuAnchor } from './ProfileMenu';
19
19
 
20
20
  const isWeb = Platform.OS === 'web';
21
21
 
22
- /**
23
- * Fixed popover width used by {@link ProfileMenu} on web. `ProfileButton`
24
- * mirrors this constant when computing the trigger-relative anchor so the
25
- * left-edge clamp keeps the panel fully on-screen. Keep the two in sync.
26
- */
27
- const MENU_WIDTH = 300;
28
-
29
- /** Gutter, in px, kept between the popover and the viewport edges on web. */
30
- const VIEWPORT_GUTTER = 8;
31
-
32
22
  /**
33
23
  * Web-only Bluesky-style hover animation timings, mirrored from
34
24
  * `social-app/src/view/shell/desktop/LeftNav.tsx`. The row background and the
@@ -67,23 +57,15 @@ export interface ProfileButtonProps {
67
57
  */
68
58
  avatarSize?: number;
69
59
  /** Navigate to the "Manage account" surface (settings). */
70
- onNavigateManage: () => void;
60
+ onNavigateManage?: () => void;
71
61
  /** Start the add-account / sign-in flow for an additional account. */
72
- onAddAccount: () => void;
62
+ onAddAccount?: () => void;
73
63
  /** Optional: navigate to the signed-in user's own profile. */
74
64
  onNavigateProfile?: () => void;
75
- /** Called before the active identity changes so apps can clear scoped state. */
76
- onBeforeSessionChange?: () => void | Promise<void>;
77
65
  /**
78
- * Web-only popover direction relative to the trigger:
79
- * - `'up'` (default): the menu opens UPWARD, for a trigger placed at the
80
- * BOTTOM of a sidebar (the footer pattern). Pixel-identical to prior
81
- * behavior.
82
- * - `'down'`: the menu opens DOWNWARD, for a trigger placed at the TOP of a
83
- * sidebar (the accounts app pattern).
84
- * - `'auto'`: opens downward when there is more room below the trigger than
85
- * above, otherwise upward.
86
- * Native (bottom-sheet) is unaffected — this only influences the web popover.
66
+ * Retained for source compatibility. The trigger now opens the unified
67
+ * `OxyAccountDialog` (a centered / bottom-sheet modal) rather than an
68
+ * anchored popover, so popover placement no longer applies.
87
69
  */
88
70
  placement?: 'up' | 'down' | 'auto';
89
71
  /**
@@ -98,36 +80,27 @@ export interface ProfileButtonProps {
98
80
 
99
81
  /**
100
82
  * Self-contained sidebar account trigger, modeled on Bluesky's ProfileCard and
101
- * the Oxy inbox's `MailboxDrawer` footer. Owns its own open state + anchor
102
- * measurement, and renders {@link ProfileMenu} (the device-account switcher).
83
+ * the Oxy inbox's `MailboxDrawer` footer. Pressing it opens the unified
84
+ * {@link OxyAccountDialog} the single account switcher + sign-in surface — via
85
+ * `useOxy().openAccountDialog`.
103
86
  *
104
87
  * Three auth states from {@link useAuth}:
105
88
  * - **Undetermined** (`!isAuthResolved || isPrivateApiPending`): a neutral
106
89
  * avatar-sized skeleton circle, no press.
107
90
  * - **Signed in**: a pressable row — Bloom `Avatar` + display name + `@handle`
108
- * + an `unfold-more-horizontal` chevron. Press opens the menu upward (web)
109
- * against the measured trigger, or as a bottom sheet (native).
110
- * - **Signed out**: a "Sign in" row that calls `useAuth().signIn()`.
91
+ * + a chevron. Press opens the dialog on its `accounts` view.
92
+ * - **Signed out**: a "Sign in" row that calls `useAuth().signIn()` (which opens
93
+ * the dialog on its `signin` view).
111
94
  *
112
95
  * Styling uses react-native `StyleSheet` + the Bloom theme (via `useTheme`) so
113
96
  * the layout renders identically in EVERY consumer — including apps that do not
114
97
  * use NativeWind (e.g. the accounts app). Only the web hover animation keeps
115
98
  * dynamic inline `style` (the CSS transition/transform values), which is what
116
99
  * the `react-native-web-style.d.ts` augmentation exists for.
117
- *
118
- * All display strings resolve through `@oxyhq/core`'s
119
- * `getAccountDisplayName` / `getAccountFallbackHandle` — no hand-rolled name
120
- * fallbacks. Avatars pass the bare file id as `source` + `variant="thumb"`, so
121
- * the app's registered `ImageResolver` builds the thumbnail URL.
122
100
  */
123
101
  const ProfileButton: React.FC<ProfileButtonProps> = ({
124
102
  expanded = true,
125
103
  avatarSize,
126
- onNavigateManage,
127
- onAddAccount,
128
- onNavigateProfile,
129
- onBeforeSessionChange,
130
- placement = 'up',
131
104
  className,
132
105
  style,
133
106
  }) => {
@@ -138,76 +111,20 @@ const ProfileButton: React.FC<ProfileButtonProps> = ({
138
111
  isPrivateApiPending,
139
112
  signIn,
140
113
  } = useAuth();
114
+ const { openAccountDialog } = useOxy();
141
115
  const { colors } = useTheme();
142
116
  const { t, locale } = useI18n();
143
117
 
144
- const [menuOpen, setMenuOpen] = useState(false);
145
- const [anchor, setAnchor] = useState<ProfileMenuAnchor | null>(null);
146
-
147
118
  // Web-only hover/focus tracking for the Bluesky-style reveal animation.
148
119
  // Native has no hover, so these stay false and the row renders statically.
149
120
  const [hovered, setHovered] = useState(false);
150
121
  const [focused, setFocused] = useState(false);
151
122
 
152
- // Trigger ref for the web anchor measurement. RN-Web exposes `measure()` on
153
- // host views, so we anchor the popover to the button rect and open upward.
154
- const triggerRef = useRef<View | null>(null);
155
-
156
123
  const resolvedAvatarSize = avatarSize ?? (expanded ? 40 : 32);
157
124
 
158
- const openMenu = useCallback(() => {
159
- if (!isWeb) {
160
- setAnchor(null);
161
- setMenuOpen(true);
162
- return;
163
- }
164
- const node = triggerRef.current;
165
- if (!node || typeof node.measure !== 'function' || typeof window === 'undefined') {
166
- setAnchor(null);
167
- setMenuOpen(true);
168
- return;
169
- }
170
- node.measure((_x, _y, _w, height, pageX, pageY) => {
171
- if (typeof pageX !== 'number' || typeof pageY !== 'number') {
172
- setAnchor(null);
173
- } else {
174
- // Clamp the panel's left edge inside the viewport gutter.
175
- const maxLeft = Math.max(
176
- VIEWPORT_GUTTER,
177
- window.innerWidth - MENU_WIDTH - VIEWPORT_GUTTER,
178
- );
179
- const left = Math.min(Math.max(VIEWPORT_GUTTER, pageX), maxLeft);
180
- // Bottom edge of the trigger in page coordinates. `height` is a
181
- // number for host views; guard defensively for non-web shims.
182
- const triggerBottom = pageY + (typeof height === 'number' ? height : 0);
183
- // Resolve `'auto'` by comparing the room below the trigger with
184
- // the room above it; pick whichever direction has more space.
185
- const openDown =
186
- placement === 'down' ||
187
- (placement === 'auto' &&
188
- window.innerHeight - triggerBottom > pageY);
189
- if (openDown) {
190
- // Anchor the panel's TOP edge just below the trigger bottom so
191
- // the menu opens downward from a top-of-sidebar trigger.
192
- const top = Math.max(VIEWPORT_GUTTER, triggerBottom + VIEWPORT_GUTTER);
193
- setAnchor({ left, top });
194
- } else {
195
- // Anchor the panel's BOTTOM edge just above the trigger top so
196
- // the menu opens upward from this footer button.
197
- const bottom = Math.max(
198
- VIEWPORT_GUTTER,
199
- window.innerHeight - pageY + VIEWPORT_GUTTER,
200
- );
201
- setAnchor({ left, bottom });
202
- }
203
- }
204
- setMenuOpen(true);
205
- });
206
- }, [placement]);
207
-
208
- const handleClose = useCallback(() => {
209
- setMenuOpen(false);
210
- }, []);
125
+ const openDialog = useCallback(() => {
126
+ openAccountDialog('accounts');
127
+ }, [openAccountDialog]);
211
128
 
212
129
  // ── Undetermined: skeleton circle, no interaction. ──────────────────────
213
130
  if (!isAuthResolved || isPrivateApiPending) {
@@ -285,10 +202,10 @@ const ProfileButton: React.FC<ProfileButtonProps> = ({
285
202
  />
286
203
  );
287
204
 
288
- // `active` combines hover, keyboard focus, and menu-open — exactly like
289
- // Bluesky's `state.hovered || state.focused || control.isOpen`. Only web
290
- // ever sets `hovered`/`focused`, so on native this reduces to `menuOpen`.
291
- const active = hovered || focused || menuOpen;
205
+ // `active` combines hover and keyboard focus — exactly like Bluesky's
206
+ // `state.hovered || state.focused`. Only web ever sets these, so on native
207
+ // the row renders statically.
208
+ const active = hovered || focused;
292
209
  const accountLabel = t('accountSwitcher.switchWhileSignedInAs', { name: displayName })
293
210
  || `Switch account, signed in as ${displayName}`;
294
211
 
@@ -318,24 +235,14 @@ const ProfileButton: React.FC<ProfileButtonProps> = ({
318
235
  return (
319
236
  <View className={className} style={style}>
320
237
  <Pressable
321
- ref={triggerRef}
322
238
  style={[styles.collapsedTrigger, collapsedBgStyle]}
323
- onPress={openMenu}
239
+ onPress={openDialog}
324
240
  accessibilityRole="button"
325
241
  accessibilityLabel={accountLabel}
326
242
  {...webInteractionProps}
327
243
  >
328
244
  {avatarNode}
329
245
  </Pressable>
330
- <ProfileMenu
331
- open={menuOpen}
332
- onClose={handleClose}
333
- anchor={anchor}
334
- onNavigateManage={onNavigateManage}
335
- onAddAccount={onAddAccount}
336
- onNavigateProfile={onNavigateProfile}
337
- onBeforeSessionChange={onBeforeSessionChange}
338
- />
339
246
  </View>
340
247
  );
341
248
  }
@@ -402,9 +309,8 @@ const ProfileButton: React.FC<ProfileButtonProps> = ({
402
309
  return (
403
310
  <View className={className} style={[styles.fullWidth, style]}>
404
311
  <Pressable
405
- ref={triggerRef}
406
312
  style={[styles.row, styles.rowExpanded, rowStyle]}
407
- onPress={openMenu}
313
+ onPress={openDialog}
408
314
  accessibilityRole="button"
409
315
  accessibilityLabel={accountLabel}
410
316
  {...webInteractionProps}
@@ -434,15 +340,6 @@ const ProfileButton: React.FC<ProfileButtonProps> = ({
434
340
  />
435
341
  </View>
436
342
  </Pressable>
437
- <ProfileMenu
438
- open={menuOpen}
439
- onClose={handleClose}
440
- anchor={anchor}
441
- onNavigateManage={onNavigateManage}
442
- onAddAccount={onAddAccount}
443
- onNavigateProfile={onNavigateProfile}
444
- onBeforeSessionChange={onBeforeSessionChange}
445
- />
446
343
  </View>
447
344
  );
448
345
  };
@@ -0,0 +1,266 @@
1
+ /**
2
+ * RequireOxyAuth — the optional signed-out gate primitive (React Native).
3
+ *
4
+ * ONE shared way for any Oxy app to opt into a signed-out gate. It is entirely
5
+ * OPTIONAL: a public app renders its children untouched (`prompt="off"`); an
6
+ * auth-required app blocks them behind a signed-out state (`prompt="hard"`); an
7
+ * app that mostly works signed-out but wants a nudge shows a dismissible banner
8
+ * (`prompt="soft"`). Every mode reuses the ONE account dialog the provider
9
+ * already mounts — opening the sign-in surface is always
10
+ * `useOxy().openAccountDialog('signin')`; there is NO second dialog.
11
+ *
12
+ * Readiness gating (CRITICAL): the gate keys on the SDK's own readiness state
13
+ * (`useOxy().canUsePrivateApi` / `isPrivateApiPending`), NEVER on app-local
14
+ * hooks. While auth is still resolving (`isPrivateApiPending`) it renders a
15
+ * neutral loading state so the signed-out wall never flashes before the
16
+ * device-first cold boot concludes (the documented cold-boot race).
17
+ *
18
+ * Styling follows the sibling `OxyAccountDialog`/`OxySignInButton` pattern —
19
+ * `useTheme()` + `StyleSheet` (NOT NativeWind), so the gate renders correctly in
20
+ * EVERY consumer, including apps that do not run NativeWind (e.g. the accounts
21
+ * app).
22
+ */
23
+
24
+ import type React from 'react';
25
+ import { useState, type ReactNode } from 'react';
26
+ import { ActivityIndicator, Pressable, StyleSheet, View } from 'react-native';
27
+ import { useTheme } from '@oxyhq/bloom/theme';
28
+ import { Text } from '@oxyhq/bloom/typography';
29
+ import { useOxy } from '../context/OxyContext';
30
+ import { OxySignInButton } from './OxySignInButton';
31
+ import OxyLogo from './OxyLogo';
32
+
33
+ /**
34
+ * How `RequireOxyAuth` treats a signed-out (or still-resolving) session.
35
+ * - `off` — always render children (public app; a no-op provided for symmetry).
36
+ * - `soft` — render children, plus a dismissible sign-in banner when signed out.
37
+ * - `hard` — block children behind a centered signed-out state until signed in.
38
+ */
39
+ export type RequireOxyAuthPrompt = 'off' | 'soft' | 'hard';
40
+
41
+ export interface RequireOxyAuthProps {
42
+ children: ReactNode;
43
+ /** Gate behavior. @default 'hard' */
44
+ prompt?: RequireOxyAuthPrompt;
45
+ /** Replaces the neutral loading UI shown while auth is still resolving. */
46
+ loadingFallback?: ReactNode;
47
+ /** Replaces the entire default signed-out wall (`prompt="hard"`). */
48
+ signedOutFallback?: ReactNode;
49
+ /** Title for the default `prompt="hard"` wall. */
50
+ title?: string;
51
+ /** Subtitle for the default `prompt="hard"` wall. */
52
+ subtitle?: string;
53
+ /** Message for the `prompt="soft"` banner. */
54
+ bannerMessage?: string;
55
+ /** CTA label for the `prompt="soft"` banner. */
56
+ bannerActionLabel?: string;
57
+ }
58
+
59
+ const DEFAULT_TITLE = 'Sign in to continue';
60
+ const DEFAULT_SUBTITLE = 'Sign in with your Oxy account to continue.';
61
+ const DEFAULT_BANNER_MESSAGE = "You're browsing signed out.";
62
+ const DEFAULT_BANNER_ACTION = 'Sign in';
63
+
64
+ /**
65
+ * Optional signed-out gate. Wrap any subtree (or the whole app via the
66
+ * provider's `requireAuth` prop) to opt into a shared, readiness-safe wall.
67
+ */
68
+ export const RequireOxyAuth: React.FC<RequireOxyAuthProps> = ({
69
+ children,
70
+ prompt = 'hard',
71
+ loadingFallback,
72
+ signedOutFallback,
73
+ title,
74
+ subtitle,
75
+ bannerMessage,
76
+ bannerActionLabel,
77
+ }) => {
78
+ const { canUsePrivateApi, isPrivateApiPending, openAccountDialog } = useOxy();
79
+
80
+ // Public app: render straight through. Cheap enough to short-circuit before
81
+ // touching any gate UI.
82
+ if (prompt === 'off') {
83
+ return <>{children}</>;
84
+ }
85
+
86
+ // Signed in (and token ready): render the protected subtree in every mode.
87
+ if (canUsePrivateApi) {
88
+ return <>{children}</>;
89
+ }
90
+
91
+ if (prompt === 'soft') {
92
+ return (
93
+ <SoftGate
94
+ pending={isPrivateApiPending}
95
+ message={bannerMessage ?? DEFAULT_BANNER_MESSAGE}
96
+ actionLabel={bannerActionLabel ?? DEFAULT_BANNER_ACTION}
97
+ onSignIn={() => openAccountDialog('signin')}
98
+ >
99
+ {children}
100
+ </SoftGate>
101
+ );
102
+ }
103
+
104
+ // prompt === 'hard'
105
+ if (isPrivateApiPending) {
106
+ return loadingFallback ? <>{loadingFallback}</> : <NeutralLoading />;
107
+ }
108
+ if (signedOutFallback) {
109
+ return <>{signedOutFallback}</>;
110
+ }
111
+ return <HardWall title={title ?? DEFAULT_TITLE} subtitle={subtitle ?? DEFAULT_SUBTITLE} />;
112
+ };
113
+
114
+ // ---------------------------------------------------------------------------
115
+ // Soft gate — children + a dismissible sign-in banner while signed out.
116
+ // ---------------------------------------------------------------------------
117
+
118
+ interface SoftGateProps {
119
+ children: ReactNode;
120
+ pending: boolean;
121
+ message: string;
122
+ actionLabel: string;
123
+ onSignIn: () => void;
124
+ }
125
+
126
+ const SoftGate: React.FC<SoftGateProps> = ({ children, pending, message, actionLabel, onSignIn }) => {
127
+ const theme = useTheme();
128
+ const [dismissed, setDismissed] = useState(false);
129
+
130
+ // Never surface the nudge while auth is still resolving — only once the boot
131
+ // has concluded signed out and the user has not dismissed it.
132
+ const showBanner = !pending && !dismissed;
133
+
134
+ return (
135
+ <View style={styles.softRoot}>
136
+ {showBanner ? (
137
+ <View style={[styles.banner, { backgroundColor: theme.colors.card, borderColor: theme.colors.border }]}>
138
+ <OxyLogo variant="icon" size={20} fillColor={theme.colors.primary} />
139
+ <Text style={[styles.bannerText, { color: theme.colors.text }]} numberOfLines={2}>
140
+ {message}
141
+ </Text>
142
+ <Pressable
143
+ onPress={onSignIn}
144
+ style={[styles.bannerCta, { backgroundColor: theme.colors.primary }]}
145
+ accessibilityRole="button"
146
+ accessibilityLabel={actionLabel}
147
+ >
148
+ <Text style={styles.bannerCtaText}>{actionLabel}</Text>
149
+ </Pressable>
150
+ <Pressable
151
+ onPress={() => setDismissed(true)}
152
+ style={styles.bannerDismiss}
153
+ accessibilityRole="button"
154
+ accessibilityLabel="Dismiss"
155
+ hitSlop={8}
156
+ >
157
+ <Text style={[styles.bannerDismissText, { color: theme.colors.textSecondary }]}>✕</Text>
158
+ </Pressable>
159
+ </View>
160
+ ) : null}
161
+ <View style={styles.softBody}>{children}</View>
162
+ </View>
163
+ );
164
+ };
165
+
166
+ // ---------------------------------------------------------------------------
167
+ // Hard wall — centered signed-out state with the "Sign in with Oxy" CTA.
168
+ // ---------------------------------------------------------------------------
169
+
170
+ const HardWall: React.FC<{ title: string; subtitle: string }> = ({ title, subtitle }) => {
171
+ const theme = useTheme();
172
+ return (
173
+ <View style={[styles.wallRoot, { backgroundColor: theme.colors.background }]}>
174
+ <View style={[styles.logoBadge, { backgroundColor: theme.colors.primarySubtle }]}>
175
+ <OxyLogo variant="icon" size={44} fillColor={theme.colors.primary} />
176
+ </View>
177
+ <Text style={[styles.wallTitle, { color: theme.colors.text }]}>{title}</Text>
178
+ <Text style={[styles.wallSubtitle, { color: theme.colors.textSecondary }]}>{subtitle}</Text>
179
+ <OxySignInButton variant="contained" style={styles.wallCta} />
180
+ </View>
181
+ );
182
+ };
183
+
184
+ const NeutralLoading: React.FC = () => {
185
+ const theme = useTheme();
186
+ return (
187
+ <View style={[styles.wallRoot, { backgroundColor: theme.colors.background }]}>
188
+ <ActivityIndicator color={theme.colors.primary} />
189
+ </View>
190
+ );
191
+ };
192
+
193
+ const styles = StyleSheet.create({
194
+ softRoot: {
195
+ flex: 1,
196
+ },
197
+ softBody: {
198
+ flex: 1,
199
+ },
200
+ banner: {
201
+ flexDirection: 'row',
202
+ alignItems: 'center',
203
+ gap: 10,
204
+ paddingVertical: 8,
205
+ paddingHorizontal: 12,
206
+ borderBottomWidth: StyleSheet.hairlineWidth,
207
+ },
208
+ bannerText: {
209
+ flex: 1,
210
+ fontSize: 13.5,
211
+ },
212
+ bannerCta: {
213
+ paddingVertical: 6,
214
+ paddingHorizontal: 14,
215
+ borderRadius: 999,
216
+ },
217
+ bannerCtaText: {
218
+ color: '#FFFFFF',
219
+ fontSize: 13,
220
+ fontWeight: '700',
221
+ },
222
+ bannerDismiss: {
223
+ width: 28,
224
+ height: 28,
225
+ alignItems: 'center',
226
+ justifyContent: 'center',
227
+ },
228
+ bannerDismissText: {
229
+ fontSize: 15,
230
+ fontWeight: '600',
231
+ },
232
+ wallRoot: {
233
+ flex: 1,
234
+ alignItems: 'center',
235
+ justifyContent: 'center',
236
+ padding: 24,
237
+ gap: 16,
238
+ },
239
+ logoBadge: {
240
+ width: 88,
241
+ height: 88,
242
+ borderRadius: 44,
243
+ alignItems: 'center',
244
+ justifyContent: 'center',
245
+ marginBottom: 4,
246
+ },
247
+ wallTitle: {
248
+ fontSize: 24,
249
+ fontWeight: '800',
250
+ letterSpacing: -0.4,
251
+ textAlign: 'center',
252
+ },
253
+ wallSubtitle: {
254
+ fontSize: 15,
255
+ lineHeight: 21,
256
+ textAlign: 'center',
257
+ maxWidth: 340,
258
+ },
259
+ wallCta: {
260
+ marginTop: 8,
261
+ width: '100%',
262
+ maxWidth: 320,
263
+ },
264
+ });
265
+
266
+ export default RequireOxyAuth;