@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.
- package/lib/commonjs/index.js +11 -17
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/components/AccountMenu.js +103 -65
- package/lib/commonjs/ui/components/AccountMenu.js.map +1 -1
- package/lib/commonjs/ui/components/AccountSwitcher.js +84 -126
- package/lib/commonjs/ui/components/AccountSwitcher.js.map +1 -1
- package/lib/commonjs/ui/components/ProfileButton.js +81 -22
- package/lib/commonjs/ui/components/ProfileButton.js.map +1 -1
- package/lib/commonjs/ui/components/ProfileMenu.js +244 -142
- package/lib/commonjs/ui/components/ProfileMenu.js.map +1 -1
- package/lib/commonjs/ui/components/SignInModal.js +4 -4
- package/lib/commonjs/ui/components/SignInModal.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +506 -466
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +59 -57
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/context/inSessionTokenRefresh.js +23 -42
- package/lib/commonjs/ui/context/inSessionTokenRefresh.js.map +1 -1
- package/lib/commonjs/ui/context/silentSessionRestore.js +12 -21
- package/lib/commonjs/ui/context/silentSessionRestore.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAuth.js +4 -3
- package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
- package/lib/commonjs/ui/hooks/useOxyAuthSession.js +12 -7
- package/lib/commonjs/ui/hooks/useOxyAuthSession.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +17 -65
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js +288 -0
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js.map +1 -0
- package/lib/commonjs/ui/hooks/useWebSSO.js +12 -191
- package/lib/commonjs/ui/hooks/useWebSSO.js.map +1 -1
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/screens/OxyAuthScreen.js +2 -2
- package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/commonjs/ui/session/createSessionClient.js +28 -0
- package/lib/commonjs/ui/session/createSessionClient.js.map +1 -0
- package/lib/commonjs/ui/session/index.js +52 -0
- package/lib/commonjs/ui/session/index.js.map +1 -0
- package/lib/commonjs/ui/session/projectSessionState.js +86 -0
- package/lib/commonjs/ui/session/projectSessionState.js.map +1 -0
- package/lib/commonjs/ui/session/sessionClientHost.js +30 -0
- package/lib/commonjs/ui/session/sessionClientHost.js.map +1 -0
- package/lib/commonjs/ui/session/tokenTransport.js +77 -0
- package/lib/commonjs/ui/session/tokenTransport.js.map +1 -0
- package/lib/commonjs/ui/utils/activeAuthuser.js +35 -79
- package/lib/commonjs/ui/utils/activeAuthuser.js.map +1 -1
- package/lib/commonjs/ui/utils/sessionHelpers.js +1 -51
- package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/commonjs/utils/deviceFlowSignIn.js +70 -15
- package/lib/commonjs/utils/deviceFlowSignIn.js.map +1 -1
- package/lib/module/index.js +5 -5
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/components/AccountMenu.js +103 -65
- package/lib/module/ui/components/AccountMenu.js.map +1 -1
- package/lib/module/ui/components/AccountSwitcher.js +85 -127
- package/lib/module/ui/components/AccountSwitcher.js.map +1 -1
- package/lib/module/ui/components/ProfileButton.js +82 -23
- package/lib/module/ui/components/ProfileButton.js.map +1 -1
- package/lib/module/ui/components/ProfileMenu.js +247 -145
- package/lib/module/ui/components/ProfileMenu.js.map +1 -1
- package/lib/module/ui/components/SignInModal.js +4 -4
- package/lib/module/ui/components/SignInModal.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +510 -470
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +61 -60
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/context/inSessionTokenRefresh.js +24 -43
- package/lib/module/ui/context/inSessionTokenRefresh.js.map +1 -1
- package/lib/module/ui/context/silentSessionRestore.js +12 -20
- package/lib/module/ui/context/silentSessionRestore.js.map +1 -1
- package/lib/module/ui/hooks/useAuth.js +4 -3
- package/lib/module/ui/hooks/useAuth.js.map +1 -1
- package/lib/module/ui/hooks/useOxyAuthSession.js +12 -7
- package/lib/module/ui/hooks/useOxyAuthSession.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js +18 -66
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/hooks/useSwitchableAccounts.js +283 -0
- package/lib/module/ui/hooks/useSwitchableAccounts.js.map +1 -0
- package/lib/module/ui/hooks/useWebSSO.js +13 -192
- package/lib/module/ui/hooks/useWebSSO.js.map +1 -1
- package/lib/module/ui/index.js +0 -1
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/screens/OxyAuthScreen.js +2 -2
- package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/module/ui/session/createSessionClient.js +25 -0
- package/lib/module/ui/session/createSessionClient.js.map +1 -0
- package/lib/module/ui/session/index.js +17 -0
- package/lib/module/ui/session/index.js.map +1 -0
- package/lib/module/ui/session/projectSessionState.js +79 -0
- package/lib/module/ui/session/projectSessionState.js.map +1 -0
- package/lib/module/ui/session/sessionClientHost.js +26 -0
- package/lib/module/ui/session/sessionClientHost.js.map +1 -0
- package/lib/module/ui/session/tokenTransport.js +74 -0
- package/lib/module/ui/session/tokenTransport.js.map +1 -0
- package/lib/module/ui/utils/activeAuthuser.js +34 -76
- package/lib/module/ui/utils/activeAuthuser.js.map +1 -1
- package/lib/module/ui/utils/sessionHelpers.js +0 -49
- package/lib/module/ui/utils/sessionHelpers.js.map +1 -1
- package/lib/module/utils/deviceFlowSignIn.js +71 -16
- package/lib/module/utils/deviceFlowSignIn.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +2 -3
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts +4 -2
- package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/AccountSwitcher.d.ts +7 -8
- package/lib/typescript/commonjs/ui/components/AccountSwitcher.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts +11 -1
- package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ProfileMenu.d.ts +12 -9
- package/lib/typescript/commonjs/ui/components/ProfileMenu.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +16 -13
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +27 -3
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/inSessionTokenRefresh.d.ts +22 -17
- package/lib/typescript/commonjs/ui/context/inSessionTokenRefresh.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/silentSessionRestore.d.ts +12 -18
- package/lib/typescript/commonjs/ui/context/silentSessionRestore.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +1 -0
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useOxyAuthSession.d.ts +2 -2
- package/lib/typescript/commonjs/ui/hooks/useOxyAuthSession.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts +162 -0
- package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts +13 -52
- package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts +15 -0
- package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/session/index.d.ts +15 -0
- package/lib/typescript/commonjs/ui/session/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/session/projectSessionState.d.ts +45 -0
- package/lib/typescript/commonjs/ui/session/projectSessionState.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/session/sessionClientHost.d.ts +14 -0
- package/lib/typescript/commonjs/ui/session/sessionClientHost.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts +24 -0
- package/lib/typescript/commonjs/ui/session/tokenTransport.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/utils/activeAuthuser.d.ts +21 -29
- package/lib/typescript/commonjs/ui/utils/activeAuthuser.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts +0 -19
- package/lib/typescript/commonjs/ui/utils/sessionHelpers.d.ts.map +1 -1
- package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts +51 -19
- package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +2 -3
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/AccountMenu.d.ts +4 -2
- package/lib/typescript/module/ui/components/AccountMenu.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/AccountSwitcher.d.ts +7 -8
- package/lib/typescript/module/ui/components/AccountSwitcher.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ProfileButton.d.ts +11 -1
- package/lib/typescript/module/ui/components/ProfileButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ProfileMenu.d.ts +12 -9
- package/lib/typescript/module/ui/components/ProfileMenu.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts +16 -13
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +27 -3
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/inSessionTokenRefresh.d.ts +22 -17
- package/lib/typescript/module/ui/context/inSessionTokenRefresh.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/silentSessionRestore.d.ts +12 -18
- package/lib/typescript/module/ui/context/silentSessionRestore.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +1 -0
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAuth.d.ts +1 -1
- package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useOxyAuthSession.d.ts +2 -2
- package/lib/typescript/module/ui/hooks/useOxyAuthSession.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts +162 -0
- package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/useWebSSO.d.ts +13 -52
- package/lib/typescript/module/ui/hooks/useWebSSO.d.ts.map +1 -1
- package/lib/typescript/module/ui/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/createSessionClient.d.ts +15 -0
- package/lib/typescript/module/ui/session/createSessionClient.d.ts.map +1 -0
- package/lib/typescript/module/ui/session/index.d.ts +15 -0
- package/lib/typescript/module/ui/session/index.d.ts.map +1 -0
- package/lib/typescript/module/ui/session/projectSessionState.d.ts +45 -0
- package/lib/typescript/module/ui/session/projectSessionState.d.ts.map +1 -0
- package/lib/typescript/module/ui/session/sessionClientHost.d.ts +14 -0
- package/lib/typescript/module/ui/session/sessionClientHost.d.ts.map +1 -0
- package/lib/typescript/module/ui/session/tokenTransport.d.ts +24 -0
- package/lib/typescript/module/ui/session/tokenTransport.d.ts.map +1 -0
- package/lib/typescript/module/ui/utils/activeAuthuser.d.ts +21 -29
- package/lib/typescript/module/ui/utils/activeAuthuser.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/sessionHelpers.d.ts +0 -19
- package/lib/typescript/module/ui/utils/sessionHelpers.d.ts.map +1 -1
- package/lib/typescript/module/utils/deviceFlowSignIn.d.ts +51 -19
- package/lib/typescript/module/utils/deviceFlowSignIn.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +9 -9
- package/src/ui/components/AccountMenu.tsx +117 -70
- package/src/ui/components/AccountSwitcher.tsx +88 -134
- package/src/ui/components/ProfileButton.tsx +95 -17
- package/src/ui/components/ProfileMenu.tsx +263 -155
- package/src/ui/components/SignInModal.tsx +5 -5
- package/src/ui/context/OxyContext.tsx +560 -475
- package/src/ui/context/hooks/useAuthOperations.ts +72 -63
- package/src/ui/context/inSessionTokenRefresh.ts +23 -42
- package/src/ui/context/silentSessionRestore.ts +12 -26
- package/src/ui/hooks/useAuth.ts +4 -3
- package/src/ui/hooks/useOxyAuthSession.ts +13 -8
- package/src/ui/hooks/useSessionManagement.ts +16 -87
- package/src/ui/hooks/useSwitchableAccounts.ts +397 -0
- package/src/ui/hooks/useWebSSO.ts +13 -227
- package/src/ui/index.ts +0 -1
- package/src/ui/screens/OxyAuthScreen.tsx +2 -2
- package/src/ui/session/__tests__/createSessionClient.test.ts +41 -0
- package/src/ui/session/__tests__/projectSessionState.test.ts +114 -0
- package/src/ui/session/__tests__/sessionClientHost.test.ts +43 -0
- package/src/ui/session/__tests__/tokenTransport.test.ts +142 -0
- package/src/ui/session/createSessionClient.ts +21 -0
- package/src/ui/session/index.ts +19 -0
- package/src/ui/session/projectSessionState.ts +85 -0
- package/src/ui/session/sessionClientHost.ts +27 -0
- package/src/ui/session/tokenTransport.ts +76 -0
- package/src/ui/utils/activeAuthuser.ts +34 -76
- package/src/ui/utils/sessionHelpers.ts +0 -66
- package/src/utils/__tests__/deviceFlowSignIn.test.ts +243 -34
- package/src/utils/deviceFlowSignIn.ts +98 -21
- package/lib/commonjs/ui/components/accountMenuRows.js +0 -33
- package/lib/commonjs/ui/components/accountMenuRows.js.map +0 -1
- package/lib/commonjs/ui/hooks/useDeviceAccounts.js +0 -280
- package/lib/commonjs/ui/hooks/useDeviceAccounts.js.map +0 -1
- package/lib/commonjs/ui/hooks/useSessionSocket.js +0 -203
- package/lib/commonjs/ui/hooks/useSessionSocket.js.map +0 -1
- package/lib/commonjs/utils/silentGuardKey.js +0 -54
- package/lib/commonjs/utils/silentGuardKey.js.map +0 -1
- package/lib/module/ui/components/accountMenuRows.js +0 -29
- package/lib/module/ui/components/accountMenuRows.js.map +0 -1
- package/lib/module/ui/hooks/useDeviceAccounts.js +0 -276
- package/lib/module/ui/hooks/useDeviceAccounts.js.map +0 -1
- package/lib/module/ui/hooks/useSessionSocket.js +0 -199
- package/lib/module/ui/hooks/useSessionSocket.js.map +0 -1
- package/lib/module/utils/silentGuardKey.js +0 -49
- package/lib/module/utils/silentGuardKey.js.map +0 -1
- package/lib/typescript/commonjs/ui/components/accountMenuRows.d.ts +0 -34
- package/lib/typescript/commonjs/ui/components/accountMenuRows.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/hooks/useDeviceAccounts.d.ts +0 -133
- package/lib/typescript/commonjs/ui/hooks/useDeviceAccounts.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/hooks/useSessionSocket.d.ts +0 -14
- package/lib/typescript/commonjs/ui/hooks/useSessionSocket.d.ts.map +0 -1
- package/lib/typescript/commonjs/utils/silentGuardKey.d.ts +0 -31
- package/lib/typescript/commonjs/utils/silentGuardKey.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/accountMenuRows.d.ts +0 -34
- package/lib/typescript/module/ui/components/accountMenuRows.d.ts.map +0 -1
- package/lib/typescript/module/ui/hooks/useDeviceAccounts.d.ts +0 -133
- package/lib/typescript/module/ui/hooks/useDeviceAccounts.d.ts.map +0 -1
- package/lib/typescript/module/ui/hooks/useSessionSocket.d.ts +0 -14
- package/lib/typescript/module/ui/hooks/useSessionSocket.d.ts.map +0 -1
- package/lib/typescript/module/utils/silentGuardKey.d.ts +0 -31
- package/lib/typescript/module/utils/silentGuardKey.d.ts.map +0 -1
- package/src/ui/components/accountMenuRows.ts +0 -48
- package/src/ui/hooks/useDeviceAccounts.ts +0 -344
- package/src/ui/hooks/useSessionSocket.ts +0 -233
- package/src/ui/utils/__tests__/activeAuthuser.test.ts +0 -75
- package/src/utils/__tests__/silentGuardKey.test.ts +0 -82
- 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 {
|
|
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
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
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
|
-
*
|
|
71
|
-
*
|
|
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
|
|
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
|
-
|
|
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 } =
|
|
93
|
+
const { accounts } = useSwitchableAccounts();
|
|
92
94
|
|
|
93
|
-
const [
|
|
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 =
|
|
101
|
+
const isSwitching = busyAccountId !== null;
|
|
100
102
|
const actionDisabled = isSwitching || removingSessionId !== null || signingOutAll;
|
|
101
103
|
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
118
|
+
setBusyAccountId(account.accountId);
|
|
108
119
|
try {
|
|
109
120
|
await onBeforeSessionChange?.();
|
|
110
|
-
await
|
|
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
|
-
|
|
130
|
+
setBusyAccountId(null);
|
|
120
131
|
}
|
|
121
|
-
}, [
|
|
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 (
|
|
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
|
-
}, [
|
|
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).
|
|
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
|
-
|
|
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
|
-
|
|
179
|
-
|
|
222
|
+
style={styles.scroll}
|
|
223
|
+
contentContainerStyle={styles.scrollContent}
|
|
180
224
|
showsVerticalScrollIndicator={false}
|
|
181
225
|
>
|
|
182
|
-
{/* 1)
|
|
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 =
|
|
186
|
-
const
|
|
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.
|
|
242
|
+
key={`account-${account.accountId}`}
|
|
190
243
|
accessibilityRole="menuitem"
|
|
191
244
|
accessibilityLabel={account.displayName}
|
|
192
245
|
accessibilityState={{ selected: isActive }}
|
|
193
|
-
onPress={() => handleSwitch(account
|
|
246
|
+
onPress={() => handleSwitch(account)}
|
|
194
247
|
disabled={isActive || isBusy || isSwitching}
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
|
207
|
-
<
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
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(
|
|
304
|
+
onPress={() => handleRemove(sessionId)}
|
|
236
305
|
disabled={actionDisabled}
|
|
237
306
|
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
|
238
|
-
|
|
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
|
|
322
|
+
<View style={styles.switchingRow}>
|
|
254
323
|
<ActivityIndicator color={colors.textSecondary} size="small" />
|
|
255
|
-
<Text
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
389
|
-
style={!isWeb ? styles.nativeScrim : undefined}
|
|
406
|
+
style={isWeb ? styles.webOverlay : styles.nativeOverlay}
|
|
390
407
|
>
|
|
391
|
-
|
|
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
|
-
|
|
442
|
+
style={[styles.actionRow, disabled && styles.rowDimmed]}
|
|
434
443
|
>
|
|
435
444
|
<MaterialCommunityIcons name={icon} size={20} color={iconColor} />
|
|
436
|
-
<Text
|
|
445
|
+
<Text style={[styles.actionText, { color: textColor }]}>{label}</Text>
|
|
437
446
|
</Pressable>
|
|
438
447
|
);
|
|
439
448
|
|
|
440
|
-
|
|
441
|
-
//
|
|
442
|
-
//
|
|
443
|
-
|
|
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
|
-
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
}
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
110
|
+
handleWebSession,
|
|
111
111
|
{ onSignedIn: hideSignInModal },
|
|
112
112
|
);
|
|
113
113
|
|