@oxyhq/services 16.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 (251) hide show
  1. package/lib/commonjs/index.js +21 -51
  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/useAccountMutations.js +1 -1
  18. package/lib/commonjs/ui/hooks/useAuth.js +19 -30
  19. package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
  20. package/lib/commonjs/ui/hooks/useSwitchableAccounts.js +37 -267
  21. package/lib/commonjs/ui/hooks/useSwitchableAccounts.js.map +1 -1
  22. package/lib/commonjs/ui/index.js +5 -40
  23. package/lib/commonjs/ui/index.js.map +1 -1
  24. package/lib/commonjs/ui/navigation/accountDialogManager.js +91 -0
  25. package/lib/commonjs/ui/navigation/accountDialogManager.js.map +1 -0
  26. package/lib/commonjs/ui/navigation/routes.js +0 -2
  27. package/lib/commonjs/ui/navigation/routes.js.map +1 -1
  28. package/lib/commonjs/ui/screens/ManageAccountScreen.js +4 -3
  29. package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
  30. package/lib/commonjs/ui/session/createSessionClient.js +10 -2
  31. package/lib/commonjs/ui/session/createSessionClient.js.map +1 -1
  32. package/lib/module/index.js +18 -15
  33. package/lib/module/index.js.map +1 -1
  34. package/lib/module/ui/client.js +1 -2
  35. package/lib/module/ui/client.js.map +1 -1
  36. package/lib/module/ui/components/OxyAccountDialog.js +761 -0
  37. package/lib/module/ui/components/OxyAccountDialog.js.map +1 -0
  38. package/lib/module/ui/components/OxyProvider.js +11 -6
  39. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  40. package/lib/module/ui/components/OxySignInButton.js +10 -21
  41. package/lib/module/ui/components/OxySignInButton.js.map +1 -1
  42. package/lib/module/ui/components/ProfileButton.js +26 -112
  43. package/lib/module/ui/components/ProfileButton.js.map +1 -1
  44. package/lib/module/ui/components/RequireOxyAuth.js +294 -0
  45. package/lib/module/ui/components/RequireOxyAuth.js.map +1 -0
  46. package/lib/module/ui/context/OxyContext.js +107 -3
  47. package/lib/module/ui/context/OxyContext.js.map +1 -1
  48. package/lib/module/ui/hooks/mutations/useAccountMutations.js +1 -1
  49. package/lib/module/ui/hooks/useAuth.js +19 -30
  50. package/lib/module/ui/hooks/useAuth.js.map +1 -1
  51. package/lib/module/ui/hooks/useSwitchableAccounts.js +38 -267
  52. package/lib/module/ui/hooks/useSwitchableAccounts.js.map +1 -1
  53. package/lib/module/ui/index.js +1 -5
  54. package/lib/module/ui/index.js.map +1 -1
  55. package/lib/module/ui/navigation/accountDialogManager.js +81 -0
  56. package/lib/module/ui/navigation/accountDialogManager.js.map +1 -0
  57. package/lib/module/ui/navigation/routes.js +0 -2
  58. package/lib/module/ui/navigation/routes.js.map +1 -1
  59. package/lib/module/ui/screens/ManageAccountScreen.js +4 -3
  60. package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
  61. package/lib/module/ui/session/createSessionClient.js +10 -2
  62. package/lib/module/ui/session/createSessionClient.js.map +1 -1
  63. package/lib/typescript/commonjs/index.d.ts +4 -11
  64. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  65. package/lib/typescript/commonjs/ui/client.d.ts +0 -4
  66. package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
  67. package/lib/typescript/commonjs/ui/components/OxyAccountDialog.d.ts +35 -0
  68. package/lib/typescript/commonjs/ui/components/OxyAccountDialog.d.ts.map +1 -0
  69. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts +2 -2
  70. package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
  71. package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts.map +1 -1
  72. package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts +11 -23
  73. package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts.map +1 -1
  74. package/lib/typescript/commonjs/ui/components/RequireOxyAuth.d.ts +55 -0
  75. package/lib/typescript/commonjs/ui/components/RequireOxyAuth.d.ts.map +1 -0
  76. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +13 -1
  77. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  78. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  79. package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
  80. package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts +13 -152
  81. package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
  82. package/lib/typescript/commonjs/ui/index.d.ts +2 -5
  83. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  84. package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts +49 -0
  85. package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts.map +1 -0
  86. package/lib/typescript/commonjs/ui/navigation/routes.d.ts +1 -1
  87. package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
  88. package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts +1 -1
  89. package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  90. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts +9 -2
  91. package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -1
  92. package/lib/typescript/commonjs/ui/types/navigation.d.ts +9 -0
  93. package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
  94. package/lib/typescript/module/index.d.ts +4 -11
  95. package/lib/typescript/module/index.d.ts.map +1 -1
  96. package/lib/typescript/module/ui/client.d.ts +0 -4
  97. package/lib/typescript/module/ui/client.d.ts.map +1 -1
  98. package/lib/typescript/module/ui/components/OxyAccountDialog.d.ts +35 -0
  99. package/lib/typescript/module/ui/components/OxyAccountDialog.d.ts.map +1 -0
  100. package/lib/typescript/module/ui/components/OxyProvider.d.ts +2 -2
  101. package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
  102. package/lib/typescript/module/ui/components/OxySignInButton.d.ts.map +1 -1
  103. package/lib/typescript/module/ui/components/ProfileButton.d.ts +11 -23
  104. package/lib/typescript/module/ui/components/ProfileButton.d.ts.map +1 -1
  105. package/lib/typescript/module/ui/components/RequireOxyAuth.d.ts +55 -0
  106. package/lib/typescript/module/ui/components/RequireOxyAuth.d.ts.map +1 -0
  107. package/lib/typescript/module/ui/context/OxyContext.d.ts +13 -1
  108. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  109. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  110. package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
  111. package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts +13 -152
  112. package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
  113. package/lib/typescript/module/ui/index.d.ts +2 -5
  114. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  115. package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts +49 -0
  116. package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts.map +1 -0
  117. package/lib/typescript/module/ui/navigation/routes.d.ts +1 -1
  118. package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
  119. package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts +1 -1
  120. package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
  121. package/lib/typescript/module/ui/session/createSessionClient.d.ts +9 -2
  122. package/lib/typescript/module/ui/session/createSessionClient.d.ts.map +1 -1
  123. package/lib/typescript/module/ui/types/navigation.d.ts +9 -0
  124. package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
  125. package/package.json +2 -2
  126. package/src/index.ts +21 -26
  127. package/src/ui/client.ts +0 -4
  128. package/src/ui/components/OxyAccountDialog.tsx +760 -0
  129. package/src/ui/components/OxyProvider.tsx +12 -6
  130. package/src/ui/components/OxySignInButton.tsx +10 -21
  131. package/src/ui/components/ProfileButton.tsx +23 -126
  132. package/src/ui/components/RequireOxyAuth.tsx +266 -0
  133. package/src/ui/context/OxyContext.tsx +146 -7
  134. package/src/ui/hooks/mutations/useAccountMutations.ts +1 -1
  135. package/src/ui/hooks/useAuth.ts +19 -31
  136. package/src/ui/hooks/useSwitchableAccounts.ts +42 -376
  137. package/src/ui/index.ts +2 -5
  138. package/src/ui/navigation/accountDialogManager.ts +84 -0
  139. package/src/ui/navigation/routes.ts +0 -4
  140. package/src/ui/screens/ManageAccountScreen.tsx +3 -2
  141. package/src/ui/session/createSessionClient.ts +9 -1
  142. package/src/ui/types/navigation.ts +9 -0
  143. package/lib/commonjs/ui/components/AccountMenu.js +0 -598
  144. package/lib/commonjs/ui/components/AccountMenu.js.map +0 -1
  145. package/lib/commonjs/ui/components/AccountMenuButton.js +0 -135
  146. package/lib/commonjs/ui/components/AccountMenuButton.js.map +0 -1
  147. package/lib/commonjs/ui/components/AccountSwitcher.js +0 -780
  148. package/lib/commonjs/ui/components/AccountSwitcher.js.map +0 -1
  149. package/lib/commonjs/ui/components/AnotherDeviceQR.js +0 -116
  150. package/lib/commonjs/ui/components/AnotherDeviceQR.js.map +0 -1
  151. package/lib/commonjs/ui/components/ProfileMenu.js +0 -518
  152. package/lib/commonjs/ui/components/ProfileMenu.js.map +0 -1
  153. package/lib/commonjs/ui/components/SignInAccountChooser.js +0 -189
  154. package/lib/commonjs/ui/components/SignInAccountChooser.js.map +0 -1
  155. package/lib/commonjs/ui/components/SignInModal.js +0 -600
  156. package/lib/commonjs/ui/components/SignInModal.js.map +0 -1
  157. package/lib/commonjs/ui/hooks/useOxyAuthSession.js +0 -501
  158. package/lib/commonjs/ui/hooks/useOxyAuthSession.js.map +0 -1
  159. package/lib/commonjs/ui/hooks/usePasswordSignIn.js +0 -170
  160. package/lib/commonjs/ui/hooks/usePasswordSignIn.js.map +0 -1
  161. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +0 -51
  162. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +0 -1
  163. package/lib/commonjs/ui/screens/OxyAuthScreen.js +0 -312
  164. package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +0 -1
  165. package/lib/commonjs/utils/deviceFlowSignIn.js +0 -109
  166. package/lib/commonjs/utils/deviceFlowSignIn.js.map +0 -1
  167. package/lib/module/ui/components/AccountMenu.js +0 -593
  168. package/lib/module/ui/components/AccountMenu.js.map +0 -1
  169. package/lib/module/ui/components/AccountMenuButton.js +0 -130
  170. package/lib/module/ui/components/AccountMenuButton.js.map +0 -1
  171. package/lib/module/ui/components/AccountSwitcher.js +0 -774
  172. package/lib/module/ui/components/AccountSwitcher.js.map +0 -1
  173. package/lib/module/ui/components/AnotherDeviceQR.js +0 -110
  174. package/lib/module/ui/components/AnotherDeviceQR.js.map +0 -1
  175. package/lib/module/ui/components/ProfileMenu.js +0 -514
  176. package/lib/module/ui/components/ProfileMenu.js.map +0 -1
  177. package/lib/module/ui/components/SignInAccountChooser.js +0 -183
  178. package/lib/module/ui/components/SignInAccountChooser.js.map +0 -1
  179. package/lib/module/ui/components/SignInModal.js +0 -591
  180. package/lib/module/ui/components/SignInModal.js.map +0 -1
  181. package/lib/module/ui/hooks/useOxyAuthSession.js +0 -495
  182. package/lib/module/ui/hooks/useOxyAuthSession.js.map +0 -1
  183. package/lib/module/ui/hooks/usePasswordSignIn.js +0 -165
  184. package/lib/module/ui/hooks/usePasswordSignIn.js.map +0 -1
  185. package/lib/module/ui/screens/AccountSwitcherScreen.js +0 -48
  186. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +0 -1
  187. package/lib/module/ui/screens/OxyAuthScreen.js +0 -306
  188. package/lib/module/ui/screens/OxyAuthScreen.js.map +0 -1
  189. package/lib/module/utils/deviceFlowSignIn.js +0 -106
  190. package/lib/module/utils/deviceFlowSignIn.js.map +0 -1
  191. package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts +0 -47
  192. package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts.map +0 -1
  193. package/lib/typescript/commonjs/ui/components/AccountMenuButton.d.ts +0 -26
  194. package/lib/typescript/commonjs/ui/components/AccountMenuButton.d.ts.map +0 -1
  195. package/lib/typescript/commonjs/ui/components/AccountSwitcher.d.ts +0 -46
  196. package/lib/typescript/commonjs/ui/components/AccountSwitcher.d.ts.map +0 -1
  197. package/lib/typescript/commonjs/ui/components/AnotherDeviceQR.d.ts +0 -34
  198. package/lib/typescript/commonjs/ui/components/AnotherDeviceQR.d.ts.map +0 -1
  199. package/lib/typescript/commonjs/ui/components/ProfileMenu.d.ts +0 -49
  200. package/lib/typescript/commonjs/ui/components/ProfileMenu.d.ts.map +0 -1
  201. package/lib/typescript/commonjs/ui/components/SignInAccountChooser.d.ts +0 -29
  202. package/lib/typescript/commonjs/ui/components/SignInAccountChooser.d.ts.map +0 -1
  203. package/lib/typescript/commonjs/ui/components/SignInModal.d.ts +0 -28
  204. package/lib/typescript/commonjs/ui/components/SignInModal.d.ts.map +0 -1
  205. package/lib/typescript/commonjs/ui/hooks/useOxyAuthSession.d.ts +0 -105
  206. package/lib/typescript/commonjs/ui/hooks/useOxyAuthSession.d.ts.map +0 -1
  207. package/lib/typescript/commonjs/ui/hooks/usePasswordSignIn.d.ts +0 -31
  208. package/lib/typescript/commonjs/ui/hooks/usePasswordSignIn.d.ts.map +0 -1
  209. package/lib/typescript/commonjs/ui/screens/AccountSwitcherScreen.d.ts +0 -11
  210. package/lib/typescript/commonjs/ui/screens/AccountSwitcherScreen.d.ts.map +0 -1
  211. package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts +0 -22
  212. package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts.map +0 -1
  213. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts +0 -97
  214. package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts.map +0 -1
  215. package/lib/typescript/module/ui/components/AccountMenu.d.ts +0 -47
  216. package/lib/typescript/module/ui/components/AccountMenu.d.ts.map +0 -1
  217. package/lib/typescript/module/ui/components/AccountMenuButton.d.ts +0 -26
  218. package/lib/typescript/module/ui/components/AccountMenuButton.d.ts.map +0 -1
  219. package/lib/typescript/module/ui/components/AccountSwitcher.d.ts +0 -46
  220. package/lib/typescript/module/ui/components/AccountSwitcher.d.ts.map +0 -1
  221. package/lib/typescript/module/ui/components/AnotherDeviceQR.d.ts +0 -34
  222. package/lib/typescript/module/ui/components/AnotherDeviceQR.d.ts.map +0 -1
  223. package/lib/typescript/module/ui/components/ProfileMenu.d.ts +0 -49
  224. package/lib/typescript/module/ui/components/ProfileMenu.d.ts.map +0 -1
  225. package/lib/typescript/module/ui/components/SignInAccountChooser.d.ts +0 -29
  226. package/lib/typescript/module/ui/components/SignInAccountChooser.d.ts.map +0 -1
  227. package/lib/typescript/module/ui/components/SignInModal.d.ts +0 -28
  228. package/lib/typescript/module/ui/components/SignInModal.d.ts.map +0 -1
  229. package/lib/typescript/module/ui/hooks/useOxyAuthSession.d.ts +0 -105
  230. package/lib/typescript/module/ui/hooks/useOxyAuthSession.d.ts.map +0 -1
  231. package/lib/typescript/module/ui/hooks/usePasswordSignIn.d.ts +0 -31
  232. package/lib/typescript/module/ui/hooks/usePasswordSignIn.d.ts.map +0 -1
  233. package/lib/typescript/module/ui/screens/AccountSwitcherScreen.d.ts +0 -11
  234. package/lib/typescript/module/ui/screens/AccountSwitcherScreen.d.ts.map +0 -1
  235. package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts +0 -22
  236. package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts.map +0 -1
  237. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts +0 -97
  238. package/lib/typescript/module/utils/deviceFlowSignIn.d.ts.map +0 -1
  239. package/src/ui/components/AccountMenu.tsx +0 -645
  240. package/src/ui/components/AccountMenuButton.tsx +0 -126
  241. package/src/ui/components/AccountSwitcher.tsx +0 -751
  242. package/src/ui/components/AnotherDeviceQR.tsx +0 -119
  243. package/src/ui/components/ProfileMenu.tsx +0 -564
  244. package/src/ui/components/SignInAccountChooser.tsx +0 -162
  245. package/src/ui/components/SignInModal.tsx +0 -530
  246. package/src/ui/hooks/useOxyAuthSession.ts +0 -635
  247. package/src/ui/hooks/usePasswordSignIn.ts +0 -207
  248. package/src/ui/screens/AccountSwitcherScreen.tsx +0 -43
  249. package/src/ui/screens/OxyAuthScreen.tsx +0 -324
  250. package/src/utils/__tests__/deviceFlowSignIn.test.ts +0 -231
  251. package/src/utils/deviceFlowSignIn.ts +0 -152
@@ -1,751 +0,0 @@
1
- import type React from 'react';
2
- import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
- import {
4
- View,
5
- Text,
6
- TextInput,
7
- TouchableOpacity,
8
- StyleSheet,
9
- ScrollView,
10
- Modal,
11
- Pressable,
12
- Platform,
13
- ActivityIndicator,
14
- type ViewStyle,
15
- } from 'react-native';
16
- import { Ionicons } from '@expo/vector-icons';
17
- import { toast, useDialogControl, Dialog } from '@oxyhq/bloom';
18
- import { Divider } from '@oxyhq/bloom/divider';
19
- import { useTheme } from '@oxyhq/bloom/theme';
20
- import { isDev, logger as loggerUtil } from '@oxyhq/core';
21
- import Avatar from './Avatar';
22
- import { useOxy } from '../context/OxyContext';
23
- import { useI18n } from '../hooks/useI18n';
24
- import { useSwitchableAccounts, type SwitchableAccount } from '../hooks/useSwitchableAccounts';
25
- import type { AccountMenuAnchor } from './AccountMenu';
26
-
27
- const isWeb = Platform.OS === 'web';
28
- const PANEL_WIDTH = 380;
29
-
30
- /**
31
- * The set of action callbacks the switcher body needs. Supplied by whichever
32
- * chrome wraps it — the {@link AccountSwitcher} modal/popover (web header chip,
33
- * native sheet) or the {@link AccountSwitcherScreen} bottom-sheet route.
34
- */
35
- export interface AccountSwitcherActions {
36
- /** Close the surface (dismiss the modal, or pop the bottom sheet). */
37
- onClose: () => void;
38
- /** Start adding another device sign-in. */
39
- onAddAccount: () => void;
40
- /** Open "Manage your Oxy Account" (the caller's own personal account). */
41
- onNavigateManage: () => void;
42
- /** Open the create-account flow. */
43
- onCreateAccount: () => void;
44
- /** Open the per-account settings surface for a non-personal account. */
45
- onOpenAccountSettings?: (accountId: string) => void;
46
- /** Called before the active device session changes so apps can clear tenant state. */
47
- onBeforeSessionChange?: () => void | Promise<void>;
48
- }
49
-
50
- interface TreeEntry {
51
- root: SwitchableAccount;
52
- children: SwitchableAccount[];
53
- }
54
-
55
- /**
56
- * Group flat account rows into a 2-level tree (org → direct children). A row
57
- * whose parent is not in the set is treated as a root; any deeper row not
58
- * captured under a root is promoted to its own root row so nothing is hidden.
59
- */
60
- function toTree(nodes: SwitchableAccount[]): TreeEntry[] {
61
- const byId = new Map(nodes.map((node) => [node.accountId, node]));
62
- const childrenOf = new Map<string, SwitchableAccount[]>();
63
- for (const node of nodes) {
64
- if (node.parentAccountId && byId.has(node.parentAccountId)) {
65
- const arr = childrenOf.get(node.parentAccountId) ?? [];
66
- arr.push(node);
67
- childrenOf.set(node.parentAccountId, arr);
68
- }
69
- }
70
- const rendered = new Set<string>();
71
- const result: TreeEntry[] = [];
72
- for (const node of nodes) {
73
- const isRoot = !node.parentAccountId || !byId.has(node.parentAccountId);
74
- if (!isRoot) continue;
75
- const children = childrenOf.get(node.accountId) ?? [];
76
- result.push({ root: node, children });
77
- rendered.add(node.accountId);
78
- for (const child of children) rendered.add(child.accountId);
79
- }
80
- for (const node of nodes) {
81
- if (!rendered.has(node.accountId)) {
82
- result.push({ root: node, children: [] });
83
- rendered.add(node.accountId);
84
- }
85
- }
86
- return result;
87
- }
88
-
89
- /**
90
- * The presentational, chrome-agnostic body of the unified account switcher.
91
- *
92
- * Two levels, relationship-aware, both sourced from the single
93
- * {@link useSwitchableAccounts} hook and switched through the SINGLE
94
- * `switchToAccount(accountId)` path:
95
- * - Section A — accounts signed in on THIS device (`onDevice` rows). Tap to
96
- * switch; per-row sign-out; "Add another account".
97
- * - Section B — the account GRAPH not yet signed in here: "Your accounts"
98
- * (self / owned) and "Shared with you" (member). 2-level tree (org →
99
- * children), role badge, search, one-tap to switch.
100
- */
101
- export const AccountSwitcherView: React.FC<AccountSwitcherActions> = ({
102
- onClose,
103
- onAddAccount,
104
- onNavigateManage,
105
- onCreateAccount,
106
- onOpenAccountSettings,
107
- onBeforeSessionChange,
108
- }) => {
109
- const {
110
- switchToAccount,
111
- removeSession,
112
- logout,
113
- logoutAll,
114
- } = useOxy();
115
- const { t } = useI18n();
116
- const { colors } = useTheme();
117
-
118
- const { accounts } = useSwitchableAccounts();
119
-
120
- const [switchingAccountId, setSwitchingAccountId] = useState<string | null>(null);
121
- const [removingSessionId, setRemovingSessionId] = useState<string | null>(null);
122
- const [signingOut, setSigningOut] = useState(false);
123
- const [signingOutAll, setSigningOutAll] = useState(false);
124
- const [query, setQuery] = useState('');
125
-
126
- const signOutDialog = useDialogControl();
127
- const signOutAllDialog = useDialogControl();
128
-
129
- // Section A — accounts signed in on THIS device, current one first.
130
- const deviceRows = useMemo<SwitchableAccount[]>(() => {
131
- const onDevice = accounts.filter((account) => account.onDevice);
132
- const current = onDevice.filter((account) => account.isCurrent);
133
- const others = onDevice.filter((account) => !account.isCurrent);
134
- return [...current, ...others];
135
- }, [accounts]);
136
-
137
- // Section B — linked graph accounts NOT already signed in on this device.
138
- // (An account switched into becomes a device session, so it moves to Section
139
- // A automatically — the unified hook already deduped it there.)
140
- const graphAccounts = useMemo<SwitchableAccount[]>(
141
- () => accounts.filter((account) => !account.onDevice),
142
- [accounts],
143
- );
144
-
145
- const isSwitching = switchingAccountId !== null;
146
- const actionDisabled = isSwitching || signingOut || signingOutAll;
147
-
148
- const filtered = useMemo<SwitchableAccount[]>(() => {
149
- const q = query.trim().toLowerCase();
150
- if (!q) return graphAccounts;
151
- return graphAccounts.filter((account) => {
152
- const name = account.displayName.toLowerCase();
153
- const username = (account.user?.username ?? '').toLowerCase();
154
- return name.includes(q) || username.includes(q);
155
- });
156
- }, [graphAccounts, query]);
157
-
158
- const yourAccounts = useMemo(
159
- () => toTree(filtered.filter((account) => account.relationship !== 'member')),
160
- [filtered],
161
- );
162
- const sharedAccounts = useMemo(
163
- () => toTree(filtered.filter((account) => account.relationship === 'member')),
164
- [filtered],
165
- );
166
-
167
- // The ONE uniform switch path. Device rows AND graph rows both switch by
168
- // account id: `switchToAccount` reuses an existing device session (no mint)
169
- // and mints a real session only on the FIRST entry into a graph account.
170
- const handleSwitch = useCallback(async (account: SwitchableAccount) => {
171
- if (switchingAccountId) return;
172
- // Tapping the already-active account just closes — it IS the current one.
173
- if (account.isCurrent) {
174
- onClose();
175
- return;
176
- }
177
- setSwitchingAccountId(account.accountId);
178
- try {
179
- await onBeforeSessionChange?.();
180
- await switchToAccount(account.accountId);
181
- toast.success(t('accountSwitcher.toasts.switchSuccess') || 'Switched account');
182
- onClose();
183
- } catch (error) {
184
- if (!isDev()) {
185
- loggerUtil.warn('Switch account failed', { component: 'AccountSwitcher' }, error as unknown);
186
- }
187
- toast.error(t('accountSwitcher.toasts.switchFailed') || 'Failed to switch account');
188
- } finally {
189
- setSwitchingAccountId(null);
190
- }
191
- }, [switchingAccountId, switchToAccount, t, onClose, onBeforeSessionChange]);
192
-
193
- const handleRemoveDevice = useCallback(async (sessionId: string) => {
194
- if (removingSessionId) return;
195
- setRemovingSessionId(sessionId);
196
- try {
197
- await removeSession(sessionId);
198
- toast.success(t('common.actions.signedOut') || 'Signed out');
199
- } catch (error) {
200
- loggerUtil.warn('Remove account failed', { component: 'AccountSwitcher' }, error as unknown);
201
- toast.error(t('common.errors.signOutFailed') || 'Failed to sign out');
202
- } finally {
203
- setRemovingSessionId(null);
204
- }
205
- }, [removingSessionId, removeSession, t]);
206
-
207
- const performSignOut = useCallback(async () => {
208
- if (signingOut) return;
209
- setSigningOut(true);
210
- try {
211
- await onBeforeSessionChange?.();
212
- await logout();
213
- toast.success(t('common.actions.signedOut') || 'Signed out');
214
- onClose();
215
- } catch (error) {
216
- loggerUtil.warn('Sign out failed', { component: 'AccountSwitcher' }, error as unknown);
217
- toast.error(t('common.errors.signOutFailed') || 'Failed to sign out');
218
- } finally {
219
- setSigningOut(false);
220
- }
221
- }, [signingOut, logout, t, onClose, onBeforeSessionChange]);
222
-
223
- const performSignOutAll = useCallback(async () => {
224
- if (signingOutAll) return;
225
- setSigningOutAll(true);
226
- try {
227
- await onBeforeSessionChange?.();
228
- await logoutAll();
229
- toast.success(t('accountSwitcher.toasts.signOutAllSuccess') || 'Signed out of all accounts');
230
- onClose();
231
- } catch (error) {
232
- loggerUtil.warn('Sign out all failed', { component: 'AccountSwitcher' }, error as unknown);
233
- toast.error(t('common.errors.signOutAllFailed') || 'Failed to sign out of all accounts');
234
- } finally {
235
- setSigningOutAll(false);
236
- }
237
- }, [signingOutAll, logoutAll, t, onClose, onBeforeSessionChange]);
238
-
239
- const renderAccountNode = useCallback((account: SwitchableAccount, isChild: boolean) => {
240
- const displayName = account.displayName;
241
- const secondary = account.email;
242
- const active = account.isCurrent;
243
- const isNodeSwitching = switchingAccountId === account.accountId;
244
- const role = account.callerMembership?.role;
245
- const avatarUri = account.avatarUrl;
246
- const permissions = account.callerMembership?.permissions ?? [];
247
- const canManage = account.relationship !== 'member'
248
- || permissions.includes('account:update')
249
- || permissions.includes('members:read');
250
- const showSettings = account.relationship !== 'self' && !!onOpenAccountSettings && canManage;
251
-
252
- return (
253
- <TouchableOpacity
254
- key={`node-${account.accountId}`}
255
- accessibilityRole="menuitem"
256
- accessibilityLabel={displayName}
257
- accessibilityState={{ selected: active }}
258
- onPress={() => handleSwitch(account)}
259
- disabled={isSwitching}
260
- activeOpacity={0.6}
261
- style={[
262
- styles.accountRow,
263
- isChild && styles.childRow,
264
- active && { backgroundColor: colors.primarySubtle },
265
- isSwitching && !isNodeSwitching && styles.rowDisabled,
266
- ]}
267
- >
268
- <Avatar uri={avatarUri} name={displayName} size={isChild ? 28 : 34} />
269
- <View style={styles.accountInfo}>
270
- <View style={styles.nameRow}>
271
- <Text style={[styles.accountName, { color: colors.text }]} numberOfLines={1}>
272
- {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>
282
- {secondary ? (
283
- <Text style={[styles.accountEmail, { color: colors.textSecondary }]} numberOfLines={1}>
284
- {secondary}
285
- </Text>
286
- ) : null}
287
- </View>
288
- {showSettings ? (
289
- <TouchableOpacity
290
- accessibilityRole="button"
291
- accessibilityLabel={t('accounts.settings.title') || 'Account settings'}
292
- onPress={() => onOpenAccountSettings?.(account.accountId)}
293
- hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
294
- style={styles.settingsButton}
295
- >
296
- <Ionicons name="settings-outline" size={16} color={colors.textSecondary} />
297
- </TouchableOpacity>
298
- ) : null}
299
- {isNodeSwitching ? (
300
- <ActivityIndicator color={colors.primary} size="small" />
301
- ) : active ? (
302
- <Ionicons name="checkmark" size={20} color={colors.primary} />
303
- ) : null}
304
- </TouchableOpacity>
305
- );
306
- }, [switchingAccountId, isSwitching, colors, handleSwitch, onOpenAccountSettings, t]);
307
-
308
- const renderTree = useCallback((entries: TreeEntry[]) => (
309
- entries.map((entry) => (
310
- <View key={`tree-${entry.root.accountId}`}>
311
- {renderAccountNode(entry.root, false)}
312
- {entry.children.map((child) => renderAccountNode(child, true))}
313
- </View>
314
- ))
315
- ), [renderAccountNode]);
316
-
317
- return (
318
- <ScrollView
319
- style={styles.scroll}
320
- contentContainerStyle={styles.scrollContent}
321
- showsVerticalScrollIndicator={false}
322
- keyboardShouldPersistTaps="handled"
323
- >
324
- {/* Section A — device sign-ins */}
325
- <Text style={[styles.sectionLabel, { color: colors.textSecondary }]}>
326
- {t('accountSwitcher.sections.thisDevice') || 'On this device'}
327
- </Text>
328
- {deviceRows.map((account) => {
329
- const sessionId = account.sessionId;
330
- const isBusy = switchingAccountId === account.accountId;
331
- const isRemoving = sessionId ? removingSessionId === sessionId : false;
332
- // The active device session IS the current account — there is no
333
- // separate acting-as concept; switching makes it the whole app.
334
- const isCurrentAccount = account.isCurrent;
335
- return (
336
- <TouchableOpacity
337
- key={`device-${account.accountId}`}
338
- accessibilityRole="menuitem"
339
- accessibilityLabel={account.displayName}
340
- accessibilityState={{ selected: isCurrentAccount }}
341
- onPress={() => handleSwitch(account)}
342
- disabled={isCurrentAccount || isBusy || isSwitching}
343
- activeOpacity={0.6}
344
- style={[
345
- styles.accountRow,
346
- isCurrentAccount && { backgroundColor: colors.primarySubtle },
347
- isSwitching && !isCurrentAccount && styles.rowDisabled,
348
- ]}
349
- >
350
- <Avatar uri={account.avatarUrl} name={account.displayName} size={isCurrentAccount ? 40 : 32} />
351
- <View style={styles.accountInfo}>
352
- <Text
353
- style={[styles.accountName, { color: colors.text }, isCurrentAccount && styles.accountNameActive]}
354
- numberOfLines={1}
355
- >
356
- {account.displayName}
357
- </Text>
358
- {account.email ? (
359
- <Text style={[styles.accountEmail, { color: colors.textSecondary }]} numberOfLines={1}>
360
- {account.email}
361
- </Text>
362
- ) : null}
363
- </View>
364
- {isBusy ? (
365
- <ActivityIndicator color={colors.primary} size="small" />
366
- ) : isCurrentAccount ? (
367
- <Ionicons name="checkmark" size={20} color={colors.primary} />
368
- ) : isRemoving ? (
369
- <ActivityIndicator color={colors.textSecondary} size="small" />
370
- ) : sessionId ? (
371
- <TouchableOpacity
372
- accessibilityRole="button"
373
- accessibilityLabel={
374
- t('accountMenu.signOutAccount', { name: account.displayName })
375
- || `Sign out ${account.displayName}`
376
- }
377
- onPress={() => handleRemoveDevice(sessionId)}
378
- disabled={isSwitching || removingSessionId !== null}
379
- hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
380
- style={styles.settingsButton}
381
- >
382
- <Ionicons name="log-out-outline" size={18} color={colors.textSecondary} />
383
- </TouchableOpacity>
384
- ) : null}
385
- </TouchableOpacity>
386
- );
387
- })}
388
-
389
- {isSwitching ? (
390
- <View style={styles.switchingRow}>
391
- <ActivityIndicator color={colors.textSecondary} size="small" />
392
- <Text style={[styles.switchingText, { color: colors.textSecondary }]}>
393
- {t('accountMenu.switching') || 'Switching account…'}
394
- </Text>
395
- </View>
396
- ) : null}
397
-
398
- <TouchableOpacity
399
- accessibilityRole="menuitem"
400
- accessibilityLabel={t('accountMenu.addAnother') || 'Add another account'}
401
- onPress={() => { onClose(); onAddAccount(); }}
402
- disabled={actionDisabled}
403
- activeOpacity={0.6}
404
- style={[styles.actionRow, actionDisabled && styles.rowDisabled]}
405
- >
406
- <Ionicons name="person-add-outline" size={20} color={colors.icon} />
407
- <Text style={[styles.actionText, { color: colors.text }]}>
408
- {t('accountMenu.addAnother') || 'Add another account'}
409
- </Text>
410
- </TouchableOpacity>
411
-
412
- {/* Section B — account graph (switchable accounts not already a device session) */}
413
- {graphAccounts.length > 0 ? (
414
- <>
415
- <Divider color={colors.border} spacing={4} />
416
-
417
- {graphAccounts.length > 6 ? (
418
- <View style={[styles.searchBox, { backgroundColor: colors.card, borderColor: colors.border }]}>
419
- <Ionicons name="search" size={16} color={colors.textSecondary} />
420
- <TextInput
421
- value={query}
422
- onChangeText={setQuery}
423
- placeholder={t('accountSwitcher.searchPlaceholder') || 'Search accounts'}
424
- placeholderTextColor={colors.textSecondary}
425
- style={[styles.searchInput, { color: colors.text }]}
426
- autoCapitalize="none"
427
- autoCorrect={false}
428
- />
429
- </View>
430
- ) : null}
431
-
432
- {yourAccounts.length > 0 ? (
433
- <>
434
- <Text style={[styles.sectionLabel, { color: colors.textSecondary }]}>
435
- {t('accountSwitcher.sections.yourAccounts') || 'Your accounts'}
436
- </Text>
437
- {renderTree(yourAccounts)}
438
- </>
439
- ) : null}
440
-
441
- {sharedAccounts.length > 0 ? (
442
- <>
443
- <Text style={[styles.sectionLabel, { color: colors.textSecondary }]}>
444
- {t('accountSwitcher.sections.sharedWithYou') || 'Shared with you'}
445
- </Text>
446
- {renderTree(sharedAccounts)}
447
- </>
448
- ) : null}
449
- </>
450
- ) : null}
451
-
452
- <Divider color={colors.border} spacing={4} />
453
-
454
- {/* Footer actions */}
455
- <TouchableOpacity
456
- accessibilityRole="menuitem"
457
- accessibilityLabel={t('accounts.create.title') || 'Create account'}
458
- onPress={() => { onClose(); onCreateAccount(); }}
459
- disabled={actionDisabled}
460
- activeOpacity={0.6}
461
- style={[styles.actionRow, actionDisabled && styles.rowDisabled]}
462
- >
463
- <Ionicons name="add-circle-outline" size={20} color={colors.icon} />
464
- <Text style={[styles.actionText, { color: colors.text }]}>
465
- {t('accounts.create.title') || 'Create account'}
466
- </Text>
467
- </TouchableOpacity>
468
-
469
- <TouchableOpacity
470
- accessibilityRole="menuitem"
471
- accessibilityLabel={t('accountMenu.manage') || 'Manage your Oxy Account'}
472
- onPress={() => { onClose(); onNavigateManage(); }}
473
- disabled={actionDisabled}
474
- activeOpacity={0.6}
475
- style={[styles.actionRow, actionDisabled && styles.rowDisabled]}
476
- >
477
- <Ionicons name="settings-outline" size={18} color={colors.icon} />
478
- <Text style={[styles.actionText, { color: colors.text }]}>
479
- {t('accountMenu.manage') || 'Manage your Oxy Account'}
480
- </Text>
481
- </TouchableOpacity>
482
-
483
- <TouchableOpacity
484
- accessibilityRole="menuitem"
485
- accessibilityLabel={t('common.actions.signOut') || 'Sign out'}
486
- onPress={() => signOutDialog.open()}
487
- disabled={actionDisabled}
488
- activeOpacity={0.6}
489
- style={[styles.actionRow, actionDisabled && styles.rowDisabled]}
490
- >
491
- {signingOut ? (
492
- <ActivityIndicator color={colors.error} size="small" />
493
- ) : (
494
- <Ionicons name="log-out-outline" size={18} color={colors.error} />
495
- )}
496
- <Text style={[styles.actionText, { color: colors.error }]}>
497
- {t('common.actions.signOut') || 'Sign out'}
498
- </Text>
499
- </TouchableOpacity>
500
-
501
- {deviceRows.length > 1 ? (
502
- <TouchableOpacity
503
- accessibilityRole="menuitem"
504
- accessibilityLabel={t('accountMenu.signOutAll') || 'Sign out of all accounts'}
505
- onPress={() => signOutAllDialog.open()}
506
- disabled={actionDisabled}
507
- activeOpacity={0.6}
508
- style={[styles.actionRow, actionDisabled && styles.rowDisabled]}
509
- >
510
- {signingOutAll ? (
511
- <ActivityIndicator color={colors.error} size="small" />
512
- ) : (
513
- <Ionicons name="log-out-outline" size={18} color={colors.error} />
514
- )}
515
- <Text style={[styles.actionText, { color: colors.error }]}>
516
- {t('accountMenu.signOutAll') || 'Sign out of all accounts'}
517
- </Text>
518
- </TouchableOpacity>
519
- ) : null}
520
-
521
- <Dialog
522
- control={signOutDialog}
523
- title={t('common.actions.signOut') || 'Sign out'}
524
- description={t('common.confirms.signOut') || 'Are you sure you want to sign out?'}
525
- actions={[
526
- { label: t('common.actions.signOut') || 'Sign out', color: 'destructive', onPress: performSignOut },
527
- { label: t('common.cancel') || 'Cancel', color: 'cancel' },
528
- ]}
529
- />
530
- <Dialog
531
- control={signOutAllDialog}
532
- title={t('accountMenu.signOutAll') || 'Sign out of all accounts'}
533
- description={t('common.confirms.signOutAll') || 'Are you sure you want to sign out of all accounts?'}
534
- actions={[
535
- { label: t('accountMenu.signOutAll') || 'Sign out of all accounts', color: 'destructive', onPress: performSignOutAll },
536
- { label: t('common.cancel') || 'Cancel', color: 'cancel' },
537
- ]}
538
- />
539
- </ScrollView>
540
- );
541
- };
542
-
543
- export interface AccountSwitcherProps extends AccountSwitcherActions {
544
- open: boolean;
545
- /** Optional anchor (web only). Native ignores this and docks as a bottom sheet. */
546
- anchor?: AccountMenuAnchor | null;
547
- }
548
-
549
- /**
550
- * Unified account switcher presented as a popover (web) / bottom-sheet style
551
- * modal (native). The canonical entry point opened by {@link AccountMenuButton}.
552
- */
553
- const AccountSwitcher: React.FC<AccountSwitcherProps> = ({ open, anchor, ...actions }) => {
554
- const { t } = useI18n();
555
- const { colors } = useTheme();
556
- const containerRef = useRef<View | null>(null);
557
- const { onClose } = actions;
558
-
559
- useEffect(() => {
560
- if (!open || !isWeb || typeof document === 'undefined') {
561
- return undefined;
562
- }
563
- const onKey = (event: KeyboardEvent) => {
564
- if (event.key === 'Escape') {
565
- event.stopPropagation();
566
- onClose();
567
- }
568
- };
569
- document.addEventListener('keydown', onKey, true);
570
- return () => document.removeEventListener('keydown', onKey, true);
571
- }, [open, onClose]);
572
-
573
- if (!open) {
574
- return null;
575
- }
576
-
577
- const anchorStyle: ViewStyle = anchor
578
- ? {
579
- ...(anchor.top !== undefined ? { top: anchor.top } : null),
580
- ...(anchor.bottom !== undefined ? { bottom: anchor.bottom } : null),
581
- ...(anchor.left !== undefined ? { left: anchor.left } : null),
582
- ...(anchor.right !== undefined ? { right: anchor.right } : null),
583
- }
584
- : { top: 64, right: 16 };
585
-
586
- const panelStyles: ViewStyle[] = isWeb
587
- ? [styles.panelBase, styles.panelWeb, anchorStyle, { backgroundColor: colors.background, borderColor: colors.border }]
588
- : [styles.panelBase, styles.panelNative, { backgroundColor: colors.background }];
589
-
590
- return (
591
- <Modal visible={open} transparent animationType={isWeb ? 'fade' : 'slide'} onRequestClose={actions.onClose}>
592
- <Pressable
593
- accessibilityRole="button"
594
- accessibilityLabel={t('common.actions.close') || 'Close'}
595
- onPress={actions.onClose}
596
- style={isWeb ? styles.webOverlay : styles.nativeOverlay}
597
- >
598
- <Pressable
599
- ref={containerRef}
600
- onPress={() => undefined}
601
- style={panelStyles}
602
- accessibilityRole="menu"
603
- accessibilityLabel={t('accountSwitcher.label') || 'Account switcher'}
604
- >
605
- <AccountSwitcherView {...actions} />
606
- </Pressable>
607
- </Pressable>
608
- </Modal>
609
- );
610
- };
611
-
612
- const styles = StyleSheet.create({
613
- webOverlay: {
614
- flex: 1,
615
- backgroundColor: 'transparent',
616
- position: 'relative',
617
- },
618
- nativeOverlay: {
619
- flex: 1,
620
- backgroundColor: 'rgba(0,0,0,0.32)',
621
- justifyContent: 'flex-end',
622
- },
623
- panelBase: {
624
- borderRadius: 12,
625
- shadowColor: '#000',
626
- shadowOpacity: 0.18,
627
- shadowRadius: 24,
628
- shadowOffset: { width: 0, height: 8 },
629
- elevation: 12,
630
- overflow: 'hidden',
631
- },
632
- panelWeb: {
633
- position: 'absolute',
634
- width: PANEL_WIDTH,
635
- maxHeight: '85%',
636
- borderWidth: 1,
637
- },
638
- panelNative: {
639
- marginHorizontal: 0,
640
- borderTopLeftRadius: 20,
641
- borderTopRightRadius: 20,
642
- paddingBottom: 12,
643
- maxHeight: '85%',
644
- },
645
- scroll: {
646
- flexGrow: 0,
647
- },
648
- scrollContent: {
649
- paddingVertical: 4,
650
- },
651
- sectionLabel: {
652
- fontSize: 11,
653
- fontWeight: '600',
654
- textTransform: 'uppercase',
655
- letterSpacing: 0.5,
656
- paddingHorizontal: 14,
657
- paddingTop: 12,
658
- paddingBottom: 4,
659
- },
660
- accountRow: {
661
- flexDirection: 'row',
662
- alignItems: 'center',
663
- paddingHorizontal: 14,
664
- paddingVertical: 10,
665
- gap: 10,
666
- },
667
- childRow: {
668
- paddingLeft: 34,
669
- },
670
- rowDisabled: {
671
- opacity: 0.4,
672
- },
673
- accountInfo: {
674
- flex: 1,
675
- minWidth: 0,
676
- },
677
- nameRow: {
678
- flexDirection: 'row',
679
- alignItems: 'center',
680
- gap: 6,
681
- },
682
- accountName: {
683
- fontSize: 13,
684
- fontWeight: '500',
685
- flexShrink: 1,
686
- },
687
- accountNameActive: {
688
- fontWeight: '600',
689
- },
690
- accountEmail: {
691
- fontSize: 11,
692
- marginTop: 1,
693
- },
694
- roleBadge: {
695
- paddingHorizontal: 6,
696
- paddingVertical: 1,
697
- borderRadius: 8,
698
- },
699
- roleBadgeText: {
700
- fontSize: 10,
701
- fontWeight: '600',
702
- textTransform: 'capitalize',
703
- },
704
- settingsButton: {
705
- width: 28,
706
- height: 28,
707
- alignItems: 'center',
708
- justifyContent: 'center',
709
- borderRadius: 14,
710
- },
711
- switchingRow: {
712
- flexDirection: 'row',
713
- alignItems: 'center',
714
- justifyContent: 'center',
715
- paddingVertical: 8,
716
- gap: 8,
717
- },
718
- switchingText: {
719
- fontSize: 12,
720
- fontWeight: '500',
721
- },
722
- searchBox: {
723
- flexDirection: 'row',
724
- alignItems: 'center',
725
- gap: 8,
726
- marginHorizontal: 14,
727
- marginVertical: 6,
728
- paddingHorizontal: 10,
729
- height: 36,
730
- borderRadius: 10,
731
- borderWidth: 1,
732
- },
733
- searchInput: {
734
- flex: 1,
735
- fontSize: 13,
736
- padding: 0,
737
- },
738
- actionRow: {
739
- flexDirection: 'row',
740
- alignItems: 'center',
741
- paddingHorizontal: 14,
742
- paddingVertical: 12,
743
- gap: 10,
744
- },
745
- actionText: {
746
- fontSize: 13,
747
- fontWeight: '500',
748
- },
749
- });
750
-
751
- export default AccountSwitcher;