@oxyhq/services 13.2.0 → 14.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 (260) hide show
  1. package/lib/commonjs/index.js +11 -17
  2. package/lib/commonjs/index.js.map +1 -1
  3. package/lib/commonjs/ui/components/AccountMenu.js +103 -65
  4. package/lib/commonjs/ui/components/AccountMenu.js.map +1 -1
  5. package/lib/commonjs/ui/components/AccountSwitcher.js +84 -126
  6. package/lib/commonjs/ui/components/AccountSwitcher.js.map +1 -1
  7. package/lib/commonjs/ui/components/ProfileButton.js +81 -22
  8. package/lib/commonjs/ui/components/ProfileButton.js.map +1 -1
  9. package/lib/commonjs/ui/components/ProfileMenu.js +244 -142
  10. package/lib/commonjs/ui/components/ProfileMenu.js.map +1 -1
  11. package/lib/commonjs/ui/components/SignInModal.js +4 -4
  12. package/lib/commonjs/ui/components/SignInModal.js.map +1 -1
  13. package/lib/commonjs/ui/context/OxyContext.js +506 -466
  14. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  15. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +59 -57
  16. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  17. package/lib/commonjs/ui/context/inSessionTokenRefresh.js +23 -42
  18. package/lib/commonjs/ui/context/inSessionTokenRefresh.js.map +1 -1
  19. package/lib/commonjs/ui/context/silentSessionRestore.js +12 -21
  20. package/lib/commonjs/ui/context/silentSessionRestore.js.map +1 -1
  21. package/lib/commonjs/ui/hooks/useAuth.js +4 -3
  22. package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
  23. package/lib/commonjs/ui/hooks/useOxyAuthSession.js +12 -7
  24. package/lib/commonjs/ui/hooks/useOxyAuthSession.js.map +1 -1
  25. package/lib/commonjs/ui/hooks/useSessionManagement.js +17 -65
  26. package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
  27. package/lib/commonjs/ui/hooks/useSwitchableAccounts.js +288 -0
  28. package/lib/commonjs/ui/hooks/useSwitchableAccounts.js.map +1 -0
  29. package/lib/commonjs/ui/hooks/useWebSSO.js +12 -191
  30. package/lib/commonjs/ui/hooks/useWebSSO.js.map +1 -1
  31. package/lib/commonjs/ui/index.js.map +1 -1
  32. package/lib/commonjs/ui/screens/OxyAuthScreen.js +2 -2
  33. package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
  34. package/lib/commonjs/ui/session/createSessionClient.js +28 -0
  35. package/lib/commonjs/ui/session/createSessionClient.js.map +1 -0
  36. package/lib/commonjs/ui/session/index.js +52 -0
  37. package/lib/commonjs/ui/session/index.js.map +1 -0
  38. package/lib/commonjs/ui/session/projectSessionState.js +86 -0
  39. package/lib/commonjs/ui/session/projectSessionState.js.map +1 -0
  40. package/lib/commonjs/ui/session/sessionClientHost.js +30 -0
  41. package/lib/commonjs/ui/session/sessionClientHost.js.map +1 -0
  42. package/lib/commonjs/ui/session/tokenTransport.js +77 -0
  43. package/lib/commonjs/ui/session/tokenTransport.js.map +1 -0
  44. package/lib/commonjs/ui/utils/activeAuthuser.js +35 -79
  45. package/lib/commonjs/ui/utils/activeAuthuser.js.map +1 -1
  46. package/lib/commonjs/ui/utils/sessionHelpers.js +1 -51
  47. package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -1
  48. package/lib/commonjs/utils/deviceFlowSignIn.js +70 -15
  49. package/lib/commonjs/utils/deviceFlowSignIn.js.map +1 -1
  50. package/lib/module/index.js +5 -5
  51. package/lib/module/index.js.map +1 -1
  52. package/lib/module/ui/components/AccountMenu.js +103 -65
  53. package/lib/module/ui/components/AccountMenu.js.map +1 -1
  54. package/lib/module/ui/components/AccountSwitcher.js +85 -127
  55. package/lib/module/ui/components/AccountSwitcher.js.map +1 -1
  56. package/lib/module/ui/components/ProfileButton.js +82 -23
  57. package/lib/module/ui/components/ProfileButton.js.map +1 -1
  58. package/lib/module/ui/components/ProfileMenu.js +247 -145
  59. package/lib/module/ui/components/ProfileMenu.js.map +1 -1
  60. package/lib/module/ui/components/SignInModal.js +4 -4
  61. package/lib/module/ui/components/SignInModal.js.map +1 -1
  62. package/lib/module/ui/context/OxyContext.js +510 -470
  63. package/lib/module/ui/context/OxyContext.js.map +1 -1
  64. package/lib/module/ui/context/hooks/useAuthOperations.js +61 -60
  65. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  66. package/lib/module/ui/context/inSessionTokenRefresh.js +24 -43
  67. package/lib/module/ui/context/inSessionTokenRefresh.js.map +1 -1
  68. package/lib/module/ui/context/silentSessionRestore.js +12 -20
  69. package/lib/module/ui/context/silentSessionRestore.js.map +1 -1
  70. package/lib/module/ui/hooks/useAuth.js +4 -3
  71. package/lib/module/ui/hooks/useAuth.js.map +1 -1
  72. package/lib/module/ui/hooks/useOxyAuthSession.js +12 -7
  73. package/lib/module/ui/hooks/useOxyAuthSession.js.map +1 -1
  74. package/lib/module/ui/hooks/useSessionManagement.js +18 -66
  75. package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
  76. package/lib/module/ui/hooks/useSwitchableAccounts.js +283 -0
  77. package/lib/module/ui/hooks/useSwitchableAccounts.js.map +1 -0
  78. package/lib/module/ui/hooks/useWebSSO.js +13 -192
  79. package/lib/module/ui/hooks/useWebSSO.js.map +1 -1
  80. package/lib/module/ui/index.js +0 -1
  81. package/lib/module/ui/index.js.map +1 -1
  82. package/lib/module/ui/screens/OxyAuthScreen.js +2 -2
  83. package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
  84. package/lib/module/ui/session/createSessionClient.js +25 -0
  85. package/lib/module/ui/session/createSessionClient.js.map +1 -0
  86. package/lib/module/ui/session/index.js +17 -0
  87. package/lib/module/ui/session/index.js.map +1 -0
  88. package/lib/module/ui/session/projectSessionState.js +79 -0
  89. package/lib/module/ui/session/projectSessionState.js.map +1 -0
  90. package/lib/module/ui/session/sessionClientHost.js +26 -0
  91. package/lib/module/ui/session/sessionClientHost.js.map +1 -0
  92. package/lib/module/ui/session/tokenTransport.js +74 -0
  93. package/lib/module/ui/session/tokenTransport.js.map +1 -0
  94. package/lib/module/ui/utils/activeAuthuser.js +34 -76
  95. package/lib/module/ui/utils/activeAuthuser.js.map +1 -1
  96. package/lib/module/ui/utils/sessionHelpers.js +0 -49
  97. package/lib/module/ui/utils/sessionHelpers.js.map +1 -1
  98. package/lib/module/utils/deviceFlowSignIn.js +71 -16
  99. package/lib/module/utils/deviceFlowSignIn.js.map +1 -1
  100. package/lib/typescript/commonjs/index.d.ts +2 -3
  101. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  102. package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts +4 -2
  103. package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts.map +1 -1
  104. package/lib/typescript/commonjs/ui/components/AccountSwitcher.d.ts +7 -8
  105. package/lib/typescript/commonjs/ui/components/AccountSwitcher.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts +11 -1
  107. package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts.map +1 -1
  108. package/lib/typescript/commonjs/ui/components/ProfileMenu.d.ts +12 -9
  109. package/lib/typescript/commonjs/ui/components/ProfileMenu.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +16 -13
  111. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  112. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +27 -3
  113. package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  114. package/lib/typescript/commonjs/ui/context/inSessionTokenRefresh.d.ts +22 -17
  115. package/lib/typescript/commonjs/ui/context/inSessionTokenRefresh.d.ts.map +1 -1
  116. package/lib/typescript/commonjs/ui/context/silentSessionRestore.d.ts +12 -18
  117. package/lib/typescript/commonjs/ui/context/silentSessionRestore.d.ts.map +1 -1
  118. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +1 -0
  119. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  120. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts +1 -1
  121. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
  122. package/lib/typescript/commonjs/ui/hooks/useOxyAuthSession.d.ts +2 -2
  123. package/lib/typescript/commonjs/ui/hooks/useOxyAuthSession.d.ts.map +1 -1
  124. package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
  125. package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts +162 -0
  126. package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts.map +1 -0
  127. package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts +13 -52
  128. package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  130. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts +15 -0
  131. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -0
  132. package/lib/typescript/commonjs/ui/session/index.d.ts +15 -0
  133. package/lib/typescript/commonjs/ui/session/index.d.ts.map +1 -0
  134. package/lib/typescript/commonjs/ui/session/projectSessionState.d.ts +45 -0
  135. package/lib/typescript/commonjs/ui/session/projectSessionState.d.ts.map +1 -0
  136. package/lib/typescript/commonjs/ui/session/sessionClientHost.d.ts +14 -0
  137. package/lib/typescript/commonjs/ui/session/sessionClientHost.d.ts.map +1 -0
  138. package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts +24 -0
  139. package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts.map +1 -0
  140. package/lib/typescript/commonjs/ui/utils/activeAuthuser.d.ts +21 -29
  141. package/lib/typescript/commonjs/ui/utils/activeAuthuser.d.ts.map +1 -1
  142. package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts +0 -19
  143. package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts.map +1 -1
  144. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts +51 -19
  145. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts.map +1 -1
  146. package/lib/typescript/module/index.d.ts +2 -3
  147. package/lib/typescript/module/index.d.ts.map +1 -1
  148. package/lib/typescript/module/ui/components/AccountMenu.d.ts +4 -2
  149. package/lib/typescript/module/ui/components/AccountMenu.d.ts.map +1 -1
  150. package/lib/typescript/module/ui/components/AccountSwitcher.d.ts +7 -8
  151. package/lib/typescript/module/ui/components/AccountSwitcher.d.ts.map +1 -1
  152. package/lib/typescript/module/ui/components/ProfileButton.d.ts +11 -1
  153. package/lib/typescript/module/ui/components/ProfileButton.d.ts.map +1 -1
  154. package/lib/typescript/module/ui/components/ProfileMenu.d.ts +12 -9
  155. package/lib/typescript/module/ui/components/ProfileMenu.d.ts.map +1 -1
  156. package/lib/typescript/module/ui/context/OxyContext.d.ts +16 -13
  157. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  158. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +27 -3
  159. package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  160. package/lib/typescript/module/ui/context/inSessionTokenRefresh.d.ts +22 -17
  161. package/lib/typescript/module/ui/context/inSessionTokenRefresh.d.ts.map +1 -1
  162. package/lib/typescript/module/ui/context/silentSessionRestore.d.ts +12 -18
  163. package/lib/typescript/module/ui/context/silentSessionRestore.d.ts.map +1 -1
  164. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +1 -0
  165. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  166. package/lib/typescript/module/ui/hooks/useAuth.d.ts +1 -1
  167. package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
  168. package/lib/typescript/module/ui/hooks/useOxyAuthSession.d.ts +2 -2
  169. package/lib/typescript/module/ui/hooks/useOxyAuthSession.d.ts.map +1 -1
  170. package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
  171. package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts +162 -0
  172. package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts.map +1 -0
  173. package/lib/typescript/module/ui/hooks/useWebSSO.d.ts +13 -52
  174. package/lib/typescript/module/ui/hooks/useWebSSO.d.ts.map +1 -1
  175. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  176. package/lib/typescript/module/ui/session/createSessionClient.d.ts +15 -0
  177. package/lib/typescript/module/ui/session/createSessionClient.d.ts.map +1 -0
  178. package/lib/typescript/module/ui/session/index.d.ts +15 -0
  179. package/lib/typescript/module/ui/session/index.d.ts.map +1 -0
  180. package/lib/typescript/module/ui/session/projectSessionState.d.ts +45 -0
  181. package/lib/typescript/module/ui/session/projectSessionState.d.ts.map +1 -0
  182. package/lib/typescript/module/ui/session/sessionClientHost.d.ts +14 -0
  183. package/lib/typescript/module/ui/session/sessionClientHost.d.ts.map +1 -0
  184. package/lib/typescript/module/ui/session/tokenTransport.d.ts +24 -0
  185. package/lib/typescript/module/ui/session/tokenTransport.d.ts.map +1 -0
  186. package/lib/typescript/module/ui/utils/activeAuthuser.d.ts +21 -29
  187. package/lib/typescript/module/ui/utils/activeAuthuser.d.ts.map +1 -1
  188. package/lib/typescript/module/ui/utils/sessionHelpers.d.ts +0 -19
  189. package/lib/typescript/module/ui/utils/sessionHelpers.d.ts.map +1 -1
  190. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts +51 -19
  191. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts.map +1 -1
  192. package/package.json +4 -4
  193. package/src/index.ts +9 -9
  194. package/src/ui/components/AccountMenu.tsx +117 -70
  195. package/src/ui/components/AccountSwitcher.tsx +88 -134
  196. package/src/ui/components/ProfileButton.tsx +95 -17
  197. package/src/ui/components/ProfileMenu.tsx +263 -155
  198. package/src/ui/components/SignInModal.tsx +5 -5
  199. package/src/ui/context/OxyContext.tsx +560 -475
  200. package/src/ui/context/hooks/useAuthOperations.ts +72 -63
  201. package/src/ui/context/inSessionTokenRefresh.ts +23 -42
  202. package/src/ui/context/silentSessionRestore.ts +12 -26
  203. package/src/ui/hooks/useAuth.ts +4 -3
  204. package/src/ui/hooks/useOxyAuthSession.ts +13 -8
  205. package/src/ui/hooks/useSessionManagement.ts +16 -87
  206. package/src/ui/hooks/useSwitchableAccounts.ts +397 -0
  207. package/src/ui/hooks/useWebSSO.ts +13 -227
  208. package/src/ui/index.ts +0 -1
  209. package/src/ui/screens/OxyAuthScreen.tsx +2 -2
  210. package/src/ui/session/__tests__/createSessionClient.test.ts +41 -0
  211. package/src/ui/session/__tests__/projectSessionState.test.ts +114 -0
  212. package/src/ui/session/__tests__/sessionClientHost.test.ts +43 -0
  213. package/src/ui/session/__tests__/tokenTransport.test.ts +142 -0
  214. package/src/ui/session/createSessionClient.ts +21 -0
  215. package/src/ui/session/index.ts +19 -0
  216. package/src/ui/session/projectSessionState.ts +85 -0
  217. package/src/ui/session/sessionClientHost.ts +27 -0
  218. package/src/ui/session/tokenTransport.ts +76 -0
  219. package/src/ui/utils/activeAuthuser.ts +34 -76
  220. package/src/ui/utils/sessionHelpers.ts +0 -66
  221. package/src/utils/__tests__/deviceFlowSignIn.test.ts +243 -34
  222. package/src/utils/deviceFlowSignIn.ts +98 -21
  223. package/lib/commonjs/ui/components/accountMenuRows.js +0 -33
  224. package/lib/commonjs/ui/components/accountMenuRows.js.map +0 -1
  225. package/lib/commonjs/ui/hooks/useDeviceAccounts.js +0 -280
  226. package/lib/commonjs/ui/hooks/useDeviceAccounts.js.map +0 -1
  227. package/lib/commonjs/ui/hooks/useSessionSocket.js +0 -203
  228. package/lib/commonjs/ui/hooks/useSessionSocket.js.map +0 -1
  229. package/lib/commonjs/utils/silentGuardKey.js +0 -54
  230. package/lib/commonjs/utils/silentGuardKey.js.map +0 -1
  231. package/lib/module/ui/components/accountMenuRows.js +0 -29
  232. package/lib/module/ui/components/accountMenuRows.js.map +0 -1
  233. package/lib/module/ui/hooks/useDeviceAccounts.js +0 -276
  234. package/lib/module/ui/hooks/useDeviceAccounts.js.map +0 -1
  235. package/lib/module/ui/hooks/useSessionSocket.js +0 -199
  236. package/lib/module/ui/hooks/useSessionSocket.js.map +0 -1
  237. package/lib/module/utils/silentGuardKey.js +0 -49
  238. package/lib/module/utils/silentGuardKey.js.map +0 -1
  239. package/lib/typescript/commonjs/ui/components/accountMenuRows.d.ts +0 -34
  240. package/lib/typescript/commonjs/ui/components/accountMenuRows.d.ts.map +0 -1
  241. package/lib/typescript/commonjs/ui/hooks/useDeviceAccounts.d.ts +0 -133
  242. package/lib/typescript/commonjs/ui/hooks/useDeviceAccounts.d.ts.map +0 -1
  243. package/lib/typescript/commonjs/ui/hooks/useSessionSocket.d.ts +0 -14
  244. package/lib/typescript/commonjs/ui/hooks/useSessionSocket.d.ts.map +0 -1
  245. package/lib/typescript/commonjs/utils/silentGuardKey.d.ts +0 -31
  246. package/lib/typescript/commonjs/utils/silentGuardKey.d.ts.map +0 -1
  247. package/lib/typescript/module/ui/components/accountMenuRows.d.ts +0 -34
  248. package/lib/typescript/module/ui/components/accountMenuRows.d.ts.map +0 -1
  249. package/lib/typescript/module/ui/hooks/useDeviceAccounts.d.ts +0 -133
  250. package/lib/typescript/module/ui/hooks/useDeviceAccounts.d.ts.map +0 -1
  251. package/lib/typescript/module/ui/hooks/useSessionSocket.d.ts +0 -14
  252. package/lib/typescript/module/ui/hooks/useSessionSocket.d.ts.map +0 -1
  253. package/lib/typescript/module/utils/silentGuardKey.d.ts +0 -31
  254. package/lib/typescript/module/utils/silentGuardKey.d.ts.map +0 -1
  255. package/src/ui/components/accountMenuRows.ts +0 -48
  256. package/src/ui/hooks/useDeviceAccounts.ts +0 -344
  257. package/src/ui/hooks/useSessionSocket.ts +0 -233
  258. package/src/ui/utils/__tests__/activeAuthuser.test.ts +0 -75
  259. package/src/utils/__tests__/silentGuardKey.test.ts +0 -82
  260. package/src/utils/silentGuardKey.ts +0 -46
@@ -1,5 +1,5 @@
1
1
  import type React from 'react';
2
- import { useCallback, useEffect, useState } from 'react';
2
+ import { useCallback, useEffect, useMemo, useState } from 'react';
3
3
  import {
4
4
  View,
5
5
  Pressable,
@@ -7,6 +7,7 @@ import {
7
7
  ScrollView,
8
8
  ActivityIndicator,
9
9
  Platform,
10
+ StyleSheet,
10
11
  type ViewStyle,
11
12
  } from 'react-native';
12
13
  import { MaterialCommunityIcons } from '@expo/vector-icons';
@@ -18,7 +19,7 @@ import { Divider } from '@oxyhq/bloom/divider';
18
19
  import { isDev, logger as loggerUtil } from '@oxyhq/core';
19
20
  import { useOxy } from '../context/OxyContext';
20
21
  import { useI18n } from '../hooks/useI18n';
21
- import { useDeviceAccounts } from '../hooks/useDeviceAccounts';
22
+ import { useSwitchableAccounts, type SwitchableAccount } from '../hooks/useSwitchableAccounts';
22
23
 
23
24
  const isWeb = Platform.OS === 'web';
24
25
 
@@ -57,57 +58,67 @@ export interface ProfileMenuProps {
57
58
  onBeforeSessionChange?: () => void | Promise<void>;
58
59
  }
59
60
 
60
- /** Everything `ProfileMenuContent` needs, minus the `open` flag (always open). */
61
- type ProfileMenuContentProps = Omit<ProfileMenuProps, 'open'>;
62
-
63
61
  /**
64
- * Clean device-account switcher, modeled on the inbox `AccountMenu` but written
65
- * fresh with NativeWind classNames + Bloom primitives. Lists every account
66
- * signed in on this device (from {@link useDeviceAccounts}); tapping a row
67
- * switches, and each inactive row carries a sign-out icon. Below the list:
68
- * Add account, Manage account, optional View profile, and Sign out of all.
62
+ * Clean account switcher, written with react-native `StyleSheet` + Bloom
63
+ * theme colors (the package convention no NativeWind, which is only an
64
+ * optional peer and absent in the primary `accounts` consumer). Lists EVERY
65
+ * switchable account from {@link useSwitchableAccounts}
66
+ * device sign-ins AND linked graph accounts (owned orgs + shared-with-you)
67
+ * and routes EVERY switch through the context's single
68
+ * `switchToAccount(accountId)` dispatcher (there is no separate device-only
69
+ * path). Each on-device inactive row carries a per-account sign-out icon;
70
+ * graph-only rows carry a role badge and indent under their parent. Below the
71
+ * list: Add account, Manage account, optional View profile, and Sign out of all.
69
72
  *
70
- * Heavy hooks (device accounts / refresh-all, Bloom dialog/toast controls, the
71
- * escape-key listener) live here so they run ONLY while the menu is open — the
72
- * outer {@link ProfileMenu} mounts this only when `open`. This component may
73
- * therefore assume it is always open.
73
+ * Renders as an upward-opening popover anchored to the trigger on web, and a
74
+ * bottom-sheet style modal on native.
74
75
  */
75
- const ProfileMenuContent: React.FC<ProfileMenuContentProps> = ({
76
+ const ProfileMenu: React.FC<ProfileMenuProps> = ({
77
+ open,
76
78
  onClose,
79
+ anchor,
77
80
  onNavigateManage,
78
81
  onAddAccount,
79
82
  onNavigateProfile,
80
83
  onBeforeSessionChange,
81
84
  }) => {
82
85
  const {
83
- activeSessionId,
84
- switchSession,
86
+ switchToAccount,
85
87
  logoutAll,
86
88
  removeSession,
87
89
  } = useOxy();
88
90
  const { t } = useI18n();
89
91
  const { colors } = useTheme();
90
92
 
91
- const { accounts } = useDeviceAccounts();
93
+ const { accounts } = useSwitchableAccounts();
92
94
 
93
- const [busySessionId, setBusySessionId] = useState<string | null>(null);
95
+ const [busyAccountId, setBusyAccountId] = useState<string | null>(null);
94
96
  const [removingSessionId, setRemovingSessionId] = useState<string | null>(null);
95
97
  const [signingOutAll, setSigningOutAll] = useState(false);
96
98
 
97
99
  const signOutAllDialog = useDialogControl();
98
100
 
99
- const isSwitching = busySessionId !== null;
101
+ const isSwitching = busyAccountId !== null;
100
102
  const actionDisabled = isSwitching || removingSessionId !== null || signingOutAll;
101
103
 
102
- // Switch to a non-active account through the SDK's canonical switch path.
103
- const handleSwitch = useCallback(async (sessionId: string) => {
104
- if (sessionId === activeSessionId || busySessionId) {
104
+ // Account ids currently in the list used to decide whether a graph-only
105
+ // row is a CHILD (its parent is also shown) so it can be indented.
106
+ const listedAccountIds = useMemo(
107
+ () => new Set(accounts.map((account) => account.accountId)),
108
+ [accounts],
109
+ );
110
+
111
+ // The ONE uniform switch path: every row (device or linked) routes through
112
+ // `switchToAccount(accountId)`. It reuses the device session when the account
113
+ // is already signed in here, and mints one only on first entry.
114
+ const handleSwitch = useCallback(async (account: SwitchableAccount) => {
115
+ if (account.isCurrent || busyAccountId) {
105
116
  return;
106
117
  }
107
- setBusySessionId(sessionId);
118
+ setBusyAccountId(account.accountId);
108
119
  try {
109
120
  await onBeforeSessionChange?.();
110
- await switchSession(sessionId);
121
+ await switchToAccount(account.accountId);
111
122
  toast.success(t('accountSwitcher.toasts.switchSuccess') || 'Switched account');
112
123
  onClose();
113
124
  } catch (error) {
@@ -116,14 +127,14 @@ const ProfileMenuContent: React.FC<ProfileMenuContentProps> = ({
116
127
  }
117
128
  toast.error(t('accountSwitcher.toasts.switchFailed') || 'Failed to switch account');
118
129
  } finally {
119
- setBusySessionId(null);
130
+ setBusyAccountId(null);
120
131
  }
121
- }, [activeSessionId, busySessionId, switchSession, t, onClose, onBeforeSessionChange]);
132
+ }, [busyAccountId, switchToAccount, t, onClose, onBeforeSessionChange]);
122
133
 
123
134
  // Sign out a specific inactive account without switching/clearing the active
124
135
  // one. The menu stays open so the user can keep managing accounts.
125
136
  const handleRemove = useCallback(async (sessionId: string) => {
126
- if (sessionId === activeSessionId || removingSessionId) {
137
+ if (removingSessionId) {
127
138
  return;
128
139
  }
129
140
  setRemovingSessionId(sessionId);
@@ -136,7 +147,7 @@ const ProfileMenuContent: React.FC<ProfileMenuContentProps> = ({
136
147
  } finally {
137
148
  setRemovingSessionId(null);
138
149
  }
139
- }, [activeSessionId, removingSessionId, removeSession, t]);
150
+ }, [removingSessionId, removeSession, t]);
140
151
 
141
152
  const performSignOutAll = useCallback(async () => {
142
153
  if (signingOutAll) {
@@ -156,10 +167,9 @@ const ProfileMenuContent: React.FC<ProfileMenuContentProps> = ({
156
167
  }
157
168
  }, [signingOutAll, logoutAll, t, onClose, onBeforeSessionChange]);
158
169
 
159
- // Escape-to-close (web only). This component only mounts while open, so the
160
- // listener is attached exactly for the menu's lifetime.
170
+ // Escape-to-close (web only).
161
171
  useEffect(() => {
162
- if (!isWeb || typeof document === 'undefined') {
172
+ if (!open || !isWeb || typeof document === 'undefined') {
163
173
  return undefined;
164
174
  }
165
175
  const onKey = (event: KeyboardEvent) => {
@@ -170,49 +180,108 @@ const ProfileMenuContent: React.FC<ProfileMenuContentProps> = ({
170
180
  };
171
181
  document.addEventListener('keydown', onKey, true);
172
182
  return () => document.removeEventListener('keydown', onKey, true);
173
- }, [onClose]);
183
+ }, [open, onClose]);
174
184
 
175
- return (
176
- <>
185
+ if (!open) {
186
+ return null;
187
+ }
188
+
189
+ // Web: anchor the panel to the measured trigger. When the anchor pins `top`
190
+ // the panel opens DOWNWARD from the trigger; otherwise it pins `bottom` and
191
+ // opens UPWARD (the default footer behavior). With no anchor captured, fall
192
+ // back to a bottom-left placement. Native ignores this (the panel docks to
193
+ // the bottom via the overlay's flex-end).
194
+ const panelAnchorStyle: ViewStyle | undefined = isWeb
195
+ ? {
196
+ position: 'absolute',
197
+ width: MENU_WIDTH,
198
+ left: anchor?.left ?? 8,
199
+ ...(typeof anchor?.top === 'number'
200
+ ? { top: anchor.top }
201
+ : { bottom: anchor?.bottom ?? 8 }),
202
+ }
203
+ : undefined;
204
+
205
+ const content = (
206
+ <Pressable
207
+ // Swallow taps inside the panel so they never reach the overlay's
208
+ // outside-tap-to-close handler.
209
+ onPress={() => undefined}
210
+ style={[
211
+ isWeb ? styles.panelWeb : styles.panelNative,
212
+ { backgroundColor: colors.background },
213
+ isWeb ? { borderColor: colors.border } : null,
214
+ panelAnchorStyle,
215
+ styles.panelBounds,
216
+ styles.shadow,
217
+ ]}
218
+ accessibilityRole="menu"
219
+ accessibilityLabel={t('accountMenu.label') || 'Account menu'}
220
+ >
177
221
  <ScrollView
178
- className="grow-0"
179
- contentContainerClassName="py-1"
222
+ style={styles.scroll}
223
+ contentContainerStyle={styles.scrollContent}
180
224
  showsVerticalScrollIndicator={false}
181
225
  >
182
- {/* 1) Device accountsactive first (checkmark), others switchable. */}
226
+ {/* 1) Every switchable account device sign-ins AND linked graph
227
+ accounts. Active first (checkmark); on-device rows carry a
228
+ per-account sign-out icon; graph-only rows show a role badge
229
+ and indent under their parent. Every row switches through the
230
+ SAME `switchToAccount(accountId)` path. */}
183
231
  {accounts.map((account) => {
184
232
  const isActive = account.isCurrent;
185
- const isBusy = busySessionId === account.sessionId;
186
- const isRemoving = removingSessionId === account.sessionId;
233
+ const isBusy = busyAccountId === account.accountId;
234
+ const sessionId = account.sessionId;
235
+ const isRemoving = sessionId ? removingSessionId === sessionId : false;
236
+ const isChild = Boolean(
237
+ account.parentAccountId && listedAccountIds.has(account.parentAccountId),
238
+ );
239
+ const role = account.callerMembership?.role;
187
240
  return (
188
241
  <Pressable
189
- key={`account-${account.sessionId}`}
242
+ key={`account-${account.accountId}`}
190
243
  accessibilityRole="menuitem"
191
244
  accessibilityLabel={account.displayName}
192
245
  accessibilityState={{ selected: isActive }}
193
- onPress={() => handleSwitch(account.sessionId)}
246
+ onPress={() => handleSwitch(account)}
194
247
  disabled={isActive || isBusy || isSwitching}
195
- className={`flex-row items-center gap-3 px-4 py-2.5 ${
196
- isActive ? 'bg-secondary' : ''
197
- } ${isSwitching && !isActive ? 'opacity-40' : ''}`}
248
+ style={[
249
+ styles.accountRow,
250
+ isChild && styles.childRow,
251
+ isActive && { backgroundColor: colors.backgroundSecondary },
252
+ isSwitching && !isActive && styles.rowDimmed,
253
+ ]}
198
254
  >
199
255
  <Avatar
200
256
  source={account.user.avatar ?? undefined}
201
257
  uri={account.avatarUrl}
202
258
  variant="thumb"
203
259
  name={account.displayName}
204
- size={isActive ? 40 : 32}
260
+ size={isActive ? 40 : isChild ? 28 : 32}
205
261
  />
206
- <View className="min-w-0 flex-1">
207
- <Text
208
- className={`text-foreground ${isActive ? 'font-semibold' : 'font-medium'}`}
209
- numberOfLines={1}
210
- >
211
- {account.displayName}
212
- </Text>
262
+ <View style={styles.accountInfo}>
263
+ <View style={styles.nameRow}>
264
+ <Text
265
+ style={[
266
+ styles.accountName,
267
+ { color: colors.text },
268
+ isActive && styles.accountNameActive,
269
+ ]}
270
+ numberOfLines={1}
271
+ >
272
+ {account.displayName}
273
+ </Text>
274
+ {role ? (
275
+ <View style={[styles.roleBadge, { backgroundColor: colors.card }]}>
276
+ <Text style={[styles.roleBadgeText, { color: colors.textSecondary }]}>
277
+ {t(`accounts.roles.${role}.label`) || role}
278
+ </Text>
279
+ </View>
280
+ ) : null}
281
+ </View>
213
282
  {account.email ? (
214
283
  <Text
215
- className="text-xs text-muted-foreground"
284
+ style={[styles.accountEmail, { color: colors.textSecondary }]}
216
285
  numberOfLines={1}
217
286
  >
218
287
  {account.email}
@@ -225,17 +294,17 @@ const ProfileMenuContent: React.FC<ProfileMenuContentProps> = ({
225
294
  <MaterialCommunityIcons name="check" size={20} color={colors.primary} />
226
295
  ) : isRemoving ? (
227
296
  <ActivityIndicator color={colors.textSecondary} size="small" />
228
- ) : (
297
+ ) : sessionId ? (
229
298
  <Pressable
230
299
  accessibilityRole="button"
231
300
  accessibilityLabel={
232
301
  t('accountMenu.signOutAccount', { name: account.displayName })
233
302
  || `Sign out ${account.displayName}`
234
303
  }
235
- onPress={() => handleRemove(account.sessionId)}
304
+ onPress={() => handleRemove(sessionId)}
236
305
  disabled={actionDisabled}
237
306
  hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
238
- className="h-7 w-7 items-center justify-center rounded-full opacity-60"
307
+ style={styles.rowSignOutButton}
239
308
  >
240
309
  <MaterialCommunityIcons
241
310
  name="logout"
@@ -243,16 +312,16 @@ const ProfileMenuContent: React.FC<ProfileMenuContentProps> = ({
243
312
  color={colors.textSecondary}
244
313
  />
245
314
  </Pressable>
246
- )}
315
+ ) : null}
247
316
  </Pressable>
248
317
  );
249
318
  })}
250
319
 
251
320
  {/* 2) Switching indicator. */}
252
321
  {isSwitching ? (
253
- <View className="flex-row items-center justify-center gap-2 py-2">
322
+ <View style={styles.switchingRow}>
254
323
  <ActivityIndicator color={colors.textSecondary} size="small" />
255
- <Text className="text-xs font-medium text-muted-foreground">
324
+ <Text style={[styles.switchingText, { color: colors.textSecondary }]}>
256
325
  {t('accountMenu.switching') || 'Switching account…'}
257
326
  </Text>
258
327
  </View>
@@ -264,6 +333,7 @@ const ProfileMenuContent: React.FC<ProfileMenuContentProps> = ({
264
333
  <ActionRow
265
334
  icon="account-plus-outline"
266
335
  iconColor={colors.icon}
336
+ textColor={colors.text}
267
337
  label={t('accountMenu.addAnother') || 'Add another account'}
268
338
  disabled={actionDisabled}
269
339
  onPress={() => {
@@ -276,6 +346,7 @@ const ProfileMenuContent: React.FC<ProfileMenuContentProps> = ({
276
346
  <ActionRow
277
347
  icon="cog-outline"
278
348
  iconColor={colors.icon}
349
+ textColor={colors.text}
279
350
  label={t('accountMenu.manage') || 'Manage your Oxy Account'}
280
351
  disabled={actionDisabled}
281
352
  onPress={() => {
@@ -289,6 +360,7 @@ const ProfileMenuContent: React.FC<ProfileMenuContentProps> = ({
289
360
  <ActionRow
290
361
  icon="account-outline"
291
362
  iconColor={colors.icon}
363
+ textColor={colors.text}
292
364
  label={t('accountMenu.viewProfile') || 'View profile'}
293
365
  disabled={actionDisabled}
294
366
  onPress={() => {
@@ -305,74 +377,20 @@ const ProfileMenuContent: React.FC<ProfileMenuContentProps> = ({
305
377
  accessibilityLabel={t('accountMenu.signOutAll') || 'Sign out of all accounts'}
306
378
  onPress={() => signOutAllDialog.open()}
307
379
  disabled={actionDisabled}
308
- className={`flex-row items-center gap-3 px-4 py-3 ${actionDisabled ? 'opacity-40' : ''}`}
380
+ style={[styles.actionRow, actionDisabled && styles.rowDimmed]}
309
381
  >
310
382
  {signingOutAll ? (
311
383
  <ActivityIndicator color={colors.error} size="small" />
312
384
  ) : (
313
385
  <MaterialCommunityIcons name="logout" size={18} color={colors.error} />
314
386
  )}
315
- <Text className="font-medium" style={{ color: colors.error }}>
387
+ <Text style={[styles.actionText, { color: colors.error }]}>
316
388
  {t('accountMenu.signOutAll') || 'Sign out of all accounts'}
317
389
  </Text>
318
390
  </Pressable>
319
391
  </ScrollView>
320
-
321
- <Dialog
322
- control={signOutAllDialog}
323
- title={t('accountMenu.signOutAll') || 'Sign out of all accounts'}
324
- description={t('common.confirms.signOutAll') || 'Are you sure you want to sign out of all accounts?'}
325
- actions={[
326
- {
327
- label: t('accountMenu.signOutAll') || 'Sign out of all accounts',
328
- color: 'destructive',
329
- onPress: performSignOutAll,
330
- },
331
- { label: t('common.cancel') || 'Cancel', color: 'cancel' },
332
- ]}
333
- />
334
- </>
392
+ </Pressable>
335
393
  );
336
- };
337
-
338
- /**
339
- * Public wrapper. The RN `Modal` shell is ALWAYS mounted (only `visible` toggles)
340
- * so the react-native-web portal is created up front — mounting a web `Modal`
341
- * already-`visible` is fragile (the first interaction can be eaten / the button
342
- * "does nothing"). Only the heavy {@link ProfileMenuContent} mounts when open,
343
- * keeping the account hooks off the render path when the menu is closed. On
344
- * native that closed render is `<Modal visible={false}>{null}</Modal>` → no heavy
345
- * hooks run (preserving the native-drawer crash fix).
346
- *
347
- * This outer component calls ONLY light, always-safe hooks (`useTheme`,
348
- * `useI18n` for the overlay a11y label) and computes the panel/overlay layout.
349
- */
350
- const ProfileMenu: React.FC<ProfileMenuProps> = ({
351
- open,
352
- onClose,
353
- anchor,
354
- onNavigateManage,
355
- onAddAccount,
356
- onNavigateProfile,
357
- onBeforeSessionChange,
358
- }) => {
359
- const { t } = useI18n();
360
-
361
- // Web: anchor the panel to the measured trigger. When the anchor pins `top`
362
- // the panel opens DOWNWARD from the trigger; otherwise it pins `bottom` and
363
- // opens UPWARD (the default footer behavior). With no anchor captured, fall
364
- // back to a bottom-left placement. Native ignores this (the panel docks to
365
- // the bottom via the overlay's flex-end).
366
- const panelAnchorStyle: ViewStyle | undefined = isWeb
367
- ? {
368
- position: 'absolute',
369
- width: MENU_WIDTH,
370
- left: anchor?.left ?? 8,
371
- ...(typeof anchor?.top === 'number'
372
- ? { top: anchor.top }
373
- : { bottom: anchor?.bottom ?? 8 }),
374
- }
375
- : undefined;
376
394
 
377
395
  return (
378
396
  <Modal
@@ -385,34 +403,24 @@ const ProfileMenu: React.FC<ProfileMenuProps> = ({
385
403
  accessibilityRole="button"
386
404
  accessibilityLabel={t('common.actions.close') || 'Close'}
387
405
  onPress={onClose}
388
- className={isWeb ? 'flex-1 relative' : 'flex-1 justify-end'}
389
- style={!isWeb ? styles.nativeScrim : undefined}
406
+ style={isWeb ? styles.webOverlay : styles.nativeOverlay}
390
407
  >
391
- <Pressable
392
- // Swallow taps inside the panel so they never reach the overlay's
393
- // outside-tap-to-close handler.
394
- onPress={() => undefined}
395
- className={
396
- isWeb
397
- ? 'overflow-hidden rounded-2xl border border-border bg-background'
398
- : 'overflow-hidden rounded-t-3xl bg-background pb-3'
399
- }
400
- style={[panelAnchorStyle, { maxHeight: '85%' }, styles.shadow]}
401
- accessibilityRole="menu"
402
- accessibilityLabel={t('accountMenu.label') || 'Account menu'}
403
- >
404
- {open ? (
405
- <ProfileMenuContent
406
- onClose={onClose}
407
- anchor={anchor}
408
- onNavigateManage={onNavigateManage}
409
- onAddAccount={onAddAccount}
410
- onNavigateProfile={onNavigateProfile}
411
- onBeforeSessionChange={onBeforeSessionChange}
412
- />
413
- ) : null}
414
- </Pressable>
408
+ {content}
415
409
  </Pressable>
410
+
411
+ <Dialog
412
+ control={signOutAllDialog}
413
+ title={t('accountMenu.signOutAll') || 'Sign out of all accounts'}
414
+ description={t('common.confirms.signOutAll') || 'Are you sure you want to sign out of all accounts?'}
415
+ actions={[
416
+ {
417
+ label: t('accountMenu.signOutAll') || 'Sign out of all accounts',
418
+ color: 'destructive',
419
+ onPress: performSignOutAll,
420
+ },
421
+ { label: t('common.cancel') || 'Cancel', color: 'cancel' },
422
+ ]}
423
+ />
416
424
  </Modal>
417
425
  );
418
426
  };
@@ -421,36 +429,136 @@ const ProfileMenu: React.FC<ProfileMenuProps> = ({
421
429
  const ActionRow: React.FC<{
422
430
  icon: React.ComponentProps<typeof MaterialCommunityIcons>['name'];
423
431
  iconColor: string;
432
+ textColor: string;
424
433
  label: string;
425
434
  disabled: boolean;
426
435
  onPress: () => void;
427
- }> = ({ icon, iconColor, label, disabled, onPress }) => (
436
+ }> = ({ icon, iconColor, textColor, label, disabled, onPress }) => (
428
437
  <Pressable
429
438
  accessibilityRole="menuitem"
430
439
  accessibilityLabel={label}
431
440
  onPress={onPress}
432
441
  disabled={disabled}
433
- className={`flex-row items-center gap-3 px-4 py-3 ${disabled ? 'opacity-40' : ''}`}
442
+ style={[styles.actionRow, disabled && styles.rowDimmed]}
434
443
  >
435
444
  <MaterialCommunityIcons name={icon} size={20} color={iconColor} />
436
- <Text className="font-medium text-foreground">{label}</Text>
445
+ <Text style={[styles.actionText, { color: textColor }]}>{label}</Text>
437
446
  </Pressable>
438
447
  );
439
448
 
440
- // The panel's drop shadow and the native scrim are the only values with no
441
- // NativeWind class equivalent in this package (dynamic elevation + rgba scrim),
442
- // so they stay as small inline objects rather than raw class-replaceable styles.
443
- const styles = {
449
+ const styles = StyleSheet.create({
450
+ // Overlay: web is a positioning context for the anchored panel; native dims
451
+ // the backdrop and docks the sheet to the bottom.
452
+ webOverlay: {
453
+ flex: 1,
454
+ position: 'relative',
455
+ },
456
+ nativeOverlay: {
457
+ flex: 1,
458
+ justifyContent: 'flex-end',
459
+ backgroundColor: 'rgba(0,0,0,0.32)',
460
+ },
461
+ // Panel shell — web popover / native bottom sheet.
462
+ panelWeb: {
463
+ overflow: 'hidden',
464
+ borderRadius: 16,
465
+ borderWidth: 1,
466
+ },
467
+ panelNative: {
468
+ overflow: 'hidden',
469
+ borderTopLeftRadius: 24,
470
+ borderTopRightRadius: 24,
471
+ paddingBottom: 12,
472
+ },
473
+ // Shared height cap applied to both panel variants.
474
+ panelBounds: {
475
+ maxHeight: '85%',
476
+ },
477
+ // The panel's drop shadow (dynamic elevation).
444
478
  shadow: {
445
479
  shadowColor: '#000',
446
480
  shadowOpacity: 0.18,
447
481
  shadowRadius: 24,
448
482
  shadowOffset: { width: 0, height: 8 },
449
483
  elevation: 12,
450
- } satisfies ViewStyle,
451
- nativeScrim: {
452
- backgroundColor: 'rgba(0,0,0,0.32)',
453
- } satisfies ViewStyle,
454
- };
484
+ },
485
+ scroll: {
486
+ flexGrow: 0,
487
+ },
488
+ scrollContent: {
489
+ paddingVertical: 4,
490
+ },
491
+ accountRow: {
492
+ flexDirection: 'row',
493
+ alignItems: 'center',
494
+ gap: 12,
495
+ paddingHorizontal: 16,
496
+ paddingVertical: 10,
497
+ },
498
+ childRow: {
499
+ paddingLeft: 40,
500
+ },
501
+ rowDimmed: {
502
+ opacity: 0.4,
503
+ },
504
+ accountInfo: {
505
+ flex: 1,
506
+ minWidth: 0,
507
+ },
508
+ nameRow: {
509
+ flexDirection: 'row',
510
+ alignItems: 'center',
511
+ gap: 6,
512
+ },
513
+ accountName: {
514
+ fontWeight: '500',
515
+ flexShrink: 1,
516
+ },
517
+ accountNameActive: {
518
+ fontWeight: '600',
519
+ },
520
+ accountEmail: {
521
+ fontSize: 12,
522
+ },
523
+ roleBadge: {
524
+ paddingHorizontal: 6,
525
+ paddingVertical: 1,
526
+ borderRadius: 8,
527
+ },
528
+ roleBadgeText: {
529
+ fontSize: 10,
530
+ fontWeight: '600',
531
+ textTransform: 'capitalize',
532
+ },
533
+ rowSignOutButton: {
534
+ width: 28,
535
+ height: 28,
536
+ alignItems: 'center',
537
+ justifyContent: 'center',
538
+ borderRadius: 9999,
539
+ opacity: 0.6,
540
+ },
541
+ switchingRow: {
542
+ flexDirection: 'row',
543
+ alignItems: 'center',
544
+ justifyContent: 'center',
545
+ gap: 8,
546
+ paddingVertical: 8,
547
+ },
548
+ switchingText: {
549
+ fontSize: 12,
550
+ fontWeight: '500',
551
+ },
552
+ actionRow: {
553
+ flexDirection: 'row',
554
+ alignItems: 'center',
555
+ gap: 12,
556
+ paddingHorizontal: 16,
557
+ paddingVertical: 12,
558
+ },
559
+ actionText: {
560
+ fontWeight: '500',
561
+ },
562
+ });
455
563
 
456
564
  export default ProfileMenu;
@@ -62,7 +62,7 @@ const SignInModal: React.FC = () => {
62
62
 
63
63
  const insets = useSafeAreaInsets();
64
64
  const theme = useTheme();
65
- const { oxyServices, switchSession, clientId } = useOxy();
65
+ const { oxyServices, handleWebSession, clientId } = useOxy();
66
66
 
67
67
  // Register the imperative visibility callback.
68
68
  useEffect(() => {
@@ -83,7 +83,7 @@ const SignInModal: React.FC = () => {
83
83
  theme={theme}
84
84
  insets={insets}
85
85
  oxyServices={oxyServices}
86
- switchSession={switchSession}
86
+ handleWebSession={handleWebSession}
87
87
  clientId={clientId}
88
88
  />
89
89
  );
@@ -93,7 +93,7 @@ interface SignInModalContentProps {
93
93
  theme: ReturnType<typeof useTheme>;
94
94
  insets: ReturnType<typeof useSafeAreaInsets>;
95
95
  oxyServices: ReturnType<typeof useOxy>['oxyServices'];
96
- switchSession: ReturnType<typeof useOxy>['switchSession'];
96
+ handleWebSession: ReturnType<typeof useOxy>['handleWebSession'];
97
97
  clientId: ReturnType<typeof useOxy>['clientId'];
98
98
  }
99
99
 
@@ -101,13 +101,13 @@ const SignInModalContent: React.FC<SignInModalContentProps> = ({
101
101
  theme,
102
102
  insets,
103
103
  oxyServices,
104
- switchSession,
104
+ handleWebSession,
105
105
  clientId,
106
106
  }) => {
107
107
  const { qrData, qrPayload, isLoading, error, isWaiting, openAuthApproval, openSameDeviceApproval, retry } = useOxyAuthSession(
108
108
  oxyServices,
109
109
  clientId,
110
- switchSession,
110
+ handleWebSession,
111
111
  { onSignedIn: hideSignInModal },
112
112
  );
113
113