@oxyhq/services 10.2.0 → 10.2.2
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/README.md +9 -13
- package/lib/commonjs/index.js +10 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/components/AccountMenu.js +297 -226
- package/lib/commonjs/ui/components/AccountMenu.js.map +1 -1
- package/lib/commonjs/ui/components/AccountMenuButton.js.map +1 -1
- package/lib/commonjs/ui/components/FollowButton.js +3 -1
- package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
- package/lib/commonjs/ui/components/OxySignInButton.js +1 -1
- package/lib/commonjs/ui/components/SignInModal.js +11 -12
- package/lib/commonjs/ui/components/SignInModal.js.map +1 -1
- package/lib/commonjs/ui/components/accountMenuRows.js +18 -30
- package/lib/commonjs/ui/components/accountMenuRows.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +57 -78
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +7 -13
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/hooks/useAuth.js +10 -40
- package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
- package/lib/commonjs/ui/hooks/useDeviceAccounts.js +285 -0
- package/lib/commonjs/ui/hooks/useDeviceAccounts.js.map +1 -0
- package/lib/commonjs/ui/hooks/useFollow.js +21 -7
- package/lib/commonjs/ui/hooks/useFollow.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionManagement.js +5 -6
- package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSessionSocket.js +4 -5
- package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/commonjs/ui/hooks/useWebSSO.js +1 -1
- package/lib/commonjs/ui/navigation/routes.js +7 -7
- package/lib/commonjs/ui/navigation/routes.js.map +1 -1
- package/lib/commonjs/ui/screens/OxyAuthScreen.js +6 -7
- package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ProfileScreen.js +18 -20
- package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +4 -4
- package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/{karma/KarmaAboutScreen.js → trust/TrustAboutScreen.js} +11 -11
- package/lib/commonjs/ui/screens/trust/TrustAboutScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/{karma/KarmaCenterScreen.js → trust/TrustCenterScreen.js} +91 -41
- package/lib/commonjs/ui/screens/trust/TrustCenterScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/{karma/KarmaFAQScreen.js → trust/TrustFAQScreen.js} +11 -11
- package/lib/commonjs/ui/screens/{karma/KarmaFAQScreen.js.map → trust/TrustFAQScreen.js.map} +1 -1
- package/lib/commonjs/ui/screens/{karma/KarmaLeaderboardScreen.js → trust/TrustLeaderboardScreen.js} +63 -42
- package/lib/commonjs/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/{karma/KarmaRewardsScreen.js → trust/TrustRewardsScreen.js} +54 -54
- package/lib/commonjs/ui/screens/trust/TrustRewardsScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/{karma/KarmaRulesScreen.js → trust/TrustRulesScreen.js} +45 -16
- package/lib/commonjs/ui/screens/trust/TrustRulesScreen.js.map +1 -0
- package/lib/commonjs/ui/screens/trust/trustTier.js +23 -0
- package/lib/commonjs/ui/screens/trust/trustTier.js.map +1 -0
- package/lib/commonjs/utils/deviceFlowSignIn.js +12 -10
- package/lib/commonjs/utils/deviceFlowSignIn.js.map +1 -1
- package/lib/module/index.js +3 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/components/AccountMenu.js +297 -226
- package/lib/module/ui/components/AccountMenu.js.map +1 -1
- package/lib/module/ui/components/AccountMenuButton.js.map +1 -1
- package/lib/module/ui/components/FollowButton.js +3 -1
- package/lib/module/ui/components/FollowButton.js.map +1 -1
- package/lib/module/ui/components/OxySignInButton.js +1 -1
- package/lib/module/ui/components/SignInModal.js +11 -12
- package/lib/module/ui/components/SignInModal.js.map +1 -1
- package/lib/module/ui/components/accountMenuRows.js +18 -30
- package/lib/module/ui/components/accountMenuRows.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +58 -79
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +7 -13
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/hooks/useAuth.js +10 -40
- package/lib/module/ui/hooks/useAuth.js.map +1 -1
- package/lib/module/ui/hooks/useDeviceAccounts.js +281 -0
- package/lib/module/ui/hooks/useDeviceAccounts.js.map +1 -0
- package/lib/module/ui/hooks/useFollow.js +21 -7
- package/lib/module/ui/hooks/useFollow.js.map +1 -1
- package/lib/module/ui/hooks/useSessionManagement.js +5 -6
- package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
- package/lib/module/ui/hooks/useSessionSocket.js +4 -5
- package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
- package/lib/module/ui/hooks/useWebSSO.js +1 -1
- package/lib/module/ui/navigation/routes.js +7 -7
- package/lib/module/ui/navigation/routes.js.map +1 -1
- package/lib/module/ui/screens/OxyAuthScreen.js +6 -7
- package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
- package/lib/module/ui/screens/ProfileScreen.js +18 -20
- package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
- package/lib/module/ui/screens/WelcomeNewUserScreen.js +4 -4
- package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
- package/lib/module/ui/screens/{karma/KarmaAboutScreen.js → trust/TrustAboutScreen.js} +11 -11
- package/lib/module/ui/screens/trust/TrustAboutScreen.js.map +1 -0
- package/lib/module/ui/screens/{karma/KarmaCenterScreen.js → trust/TrustCenterScreen.js} +92 -42
- package/lib/module/ui/screens/trust/TrustCenterScreen.js.map +1 -0
- package/lib/module/ui/screens/{karma/KarmaFAQScreen.js → trust/TrustFAQScreen.js} +11 -11
- package/lib/module/ui/screens/{karma/KarmaFAQScreen.js.map → trust/TrustFAQScreen.js.map} +1 -1
- package/lib/module/ui/screens/{karma/KarmaLeaderboardScreen.js → trust/TrustLeaderboardScreen.js} +63 -42
- package/lib/module/ui/screens/trust/TrustLeaderboardScreen.js.map +1 -0
- package/lib/module/ui/screens/{karma/KarmaRewardsScreen.js → trust/TrustRewardsScreen.js} +54 -54
- package/lib/module/ui/screens/trust/TrustRewardsScreen.js.map +1 -0
- package/lib/module/ui/screens/{karma/KarmaRulesScreen.js → trust/TrustRulesScreen.js} +45 -16
- package/lib/module/ui/screens/trust/TrustRulesScreen.js.map +1 -0
- package/lib/module/ui/screens/trust/trustTier.js +19 -0
- package/lib/module/ui/screens/trust/trustTier.js.map +1 -0
- package/lib/module/utils/deviceFlowSignIn.js +13 -10
- package/lib/module/utils/deviceFlowSignIn.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +3 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts +30 -10
- package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SignInModal.d.ts +1 -1
- package/lib/typescript/commonjs/ui/components/SignInModal.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/accountMenuRows.d.ts +19 -12
- package/lib/typescript/commonjs/ui/components/accountMenuRows.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +3 -3
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +1 -7
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts +3 -7
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useDeviceAccounts.d.ts +133 -0
- package/lib/typescript/commonjs/ui/hooks/useDeviceAccounts.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/hooks/useFollow.d.ts +1 -1
- package/lib/typescript/commonjs/ui/hooks/useFollow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSessionSocket.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts +1 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts +1 -1
- package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ProfileScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustAboutScreen.d.ts +5 -0
- package/lib/typescript/commonjs/ui/screens/{karma/KarmaAboutScreen.d.ts.map → trust/TrustAboutScreen.d.ts.map} +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustCenterScreen.d.ts +5 -0
- package/lib/typescript/commonjs/ui/screens/trust/TrustCenterScreen.d.ts.map +1 -0
- package/lib/typescript/{module/ui/screens/karma/KarmaFAQScreen.d.ts → commonjs/ui/screens/trust/TrustFAQScreen.d.ts} +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustFAQScreen.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/trust/TrustLeaderboardScreen.d.ts +5 -0
- package/lib/typescript/commonjs/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/trust/TrustRewardsScreen.d.ts +5 -0
- package/lib/typescript/commonjs/ui/screens/{karma/KarmaRewardsScreen.d.ts.map → trust/TrustRewardsScreen.d.ts.map} +1 -1
- package/lib/typescript/commonjs/ui/screens/trust/TrustRulesScreen.d.ts +5 -0
- package/lib/typescript/commonjs/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts +9 -0
- package/lib/typescript/commonjs/ui/screens/trust/trustTier.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/types/navigation.d.ts +1 -1
- package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts +11 -9
- package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +3 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/AccountMenu.d.ts +30 -10
- package/lib/typescript/module/ui/components/AccountMenu.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SignInModal.d.ts +1 -1
- package/lib/typescript/module/ui/components/SignInModal.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/accountMenuRows.d.ts +19 -12
- package/lib/typescript/module/ui/components/accountMenuRows.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts +3 -3
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +1 -7
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAuth.d.ts +3 -7
- package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useDeviceAccounts.d.ts +133 -0
- package/lib/typescript/module/ui/hooks/useDeviceAccounts.d.ts.map +1 -0
- package/lib/typescript/module/ui/hooks/useFollow.d.ts +1 -1
- package/lib/typescript/module/ui/hooks/useFollow.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSessionManagement.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSessionSocket.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useWebSSO.d.ts +1 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts +1 -1
- package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ProfileScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustAboutScreen.d.ts +5 -0
- package/lib/typescript/module/ui/screens/{karma/KarmaAboutScreen.d.ts.map → trust/TrustAboutScreen.d.ts.map} +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustCenterScreen.d.ts +5 -0
- package/lib/typescript/module/ui/screens/trust/TrustCenterScreen.d.ts.map +1 -0
- package/lib/typescript/{commonjs/ui/screens/karma/KarmaFAQScreen.d.ts → module/ui/screens/trust/TrustFAQScreen.d.ts} +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustFAQScreen.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/trust/TrustLeaderboardScreen.d.ts +5 -0
- package/lib/typescript/module/ui/screens/trust/TrustLeaderboardScreen.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/trust/TrustRewardsScreen.d.ts +5 -0
- package/lib/typescript/module/ui/screens/{karma/KarmaRewardsScreen.d.ts.map → trust/TrustRewardsScreen.d.ts.map} +1 -1
- package/lib/typescript/module/ui/screens/trust/TrustRulesScreen.d.ts +5 -0
- package/lib/typescript/module/ui/screens/trust/TrustRulesScreen.d.ts.map +1 -0
- package/lib/typescript/module/ui/screens/trust/trustTier.d.ts +9 -0
- package/lib/typescript/module/ui/screens/trust/trustTier.d.ts.map +1 -0
- package/lib/typescript/module/ui/types/navigation.d.ts +1 -1
- package/lib/typescript/module/utils/deviceFlowSignIn.d.ts +11 -9
- package/lib/typescript/module/utils/deviceFlowSignIn.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +10 -1
- package/src/ui/components/AccountMenu.tsx +311 -253
- package/src/ui/components/AccountMenuButton.tsx +2 -2
- package/src/ui/components/FollowButton.tsx +2 -2
- package/src/ui/components/OxySignInButton.tsx +1 -1
- package/src/ui/components/SignInModal.tsx +13 -14
- package/src/ui/components/accountMenuRows.ts +28 -40
- package/src/ui/context/OxyContext.tsx +71 -74
- package/src/ui/context/hooks/useAuthOperations.ts +7 -13
- package/src/ui/hooks/useAuth.ts +12 -49
- package/src/ui/hooks/useDeviceAccounts.ts +348 -0
- package/src/ui/hooks/useFollow.ts +16 -8
- package/src/ui/hooks/useSessionManagement.ts +5 -14
- package/src/ui/hooks/useSessionSocket.ts +4 -5
- package/src/ui/hooks/useWebSSO.ts +1 -1
- package/src/ui/navigation/routes.ts +13 -13
- package/src/ui/screens/OxyAuthScreen.tsx +6 -7
- package/src/ui/screens/ProfileScreen.tsx +15 -17
- package/src/ui/screens/WelcomeNewUserScreen.tsx +2 -2
- package/src/ui/screens/{karma/KarmaAboutScreen.tsx → trust/TrustAboutScreen.tsx} +15 -15
- package/src/ui/screens/{karma/KarmaCenterScreen.tsx → trust/TrustCenterScreen.tsx} +87 -41
- package/src/ui/screens/{karma/KarmaFAQScreen.tsx → trust/TrustFAQScreen.tsx} +10 -10
- package/src/ui/screens/trust/TrustLeaderboardScreen.tsx +101 -0
- package/src/ui/screens/{karma/KarmaRewardsScreen.tsx → trust/TrustRewardsScreen.tsx} +54 -54
- package/src/ui/screens/{karma/KarmaRulesScreen.tsx → trust/TrustRulesScreen.tsx} +27 -13
- package/src/ui/screens/trust/trustTier.ts +20 -0
- package/src/ui/types/navigation.ts +1 -2
- package/src/utils/__tests__/deviceFlowSignIn.test.ts +2 -3
- package/src/utils/deviceFlowSignIn.ts +18 -12
- package/lib/commonjs/ui/screens/karma/KarmaAboutScreen.js.map +0 -1
- package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js.map +0 -1
- package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js.map +0 -1
- package/lib/commonjs/ui/screens/karma/KarmaRewardsScreen.js.map +0 -1
- package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js.map +0 -1
- package/lib/module/ui/screens/karma/KarmaAboutScreen.js.map +0 -1
- package/lib/module/ui/screens/karma/KarmaCenterScreen.js.map +0 -1
- package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js.map +0 -1
- package/lib/module/ui/screens/karma/KarmaRewardsScreen.js.map +0 -1
- package/lib/module/ui/screens/karma/KarmaRulesScreen.js.map +0 -1
- package/lib/typescript/commonjs/ui/screens/karma/KarmaAboutScreen.d.ts +0 -5
- package/lib/typescript/commonjs/ui/screens/karma/KarmaCenterScreen.d.ts +0 -5
- package/lib/typescript/commonjs/ui/screens/karma/KarmaCenterScreen.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/screens/karma/KarmaFAQScreen.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/screens/karma/KarmaLeaderboardScreen.d.ts +0 -5
- package/lib/typescript/commonjs/ui/screens/karma/KarmaLeaderboardScreen.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/screens/karma/KarmaRewardsScreen.d.ts +0 -5
- package/lib/typescript/commonjs/ui/screens/karma/KarmaRulesScreen.d.ts +0 -5
- package/lib/typescript/commonjs/ui/screens/karma/KarmaRulesScreen.d.ts.map +0 -1
- package/lib/typescript/module/ui/screens/karma/KarmaAboutScreen.d.ts +0 -5
- package/lib/typescript/module/ui/screens/karma/KarmaCenterScreen.d.ts +0 -5
- package/lib/typescript/module/ui/screens/karma/KarmaCenterScreen.d.ts.map +0 -1
- package/lib/typescript/module/ui/screens/karma/KarmaFAQScreen.d.ts.map +0 -1
- package/lib/typescript/module/ui/screens/karma/KarmaLeaderboardScreen.d.ts +0 -5
- package/lib/typescript/module/ui/screens/karma/KarmaLeaderboardScreen.d.ts.map +0 -1
- package/lib/typescript/module/ui/screens/karma/KarmaRewardsScreen.d.ts +0 -5
- package/lib/typescript/module/ui/screens/karma/KarmaRulesScreen.d.ts +0 -5
- package/lib/typescript/module/ui/screens/karma/KarmaRulesScreen.d.ts.map +0 -1
- package/src/ui/screens/karma/KarmaLeaderboardScreen.tsx +0 -88
|
@@ -14,31 +14,61 @@ import {
|
|
|
14
14
|
} from 'react-native';
|
|
15
15
|
import { Ionicons } from '@expo/vector-icons';
|
|
16
16
|
import { toast, useDialogControl, Dialog } from '@oxyhq/bloom';
|
|
17
|
+
import { Divider } from '@oxyhq/bloom/divider';
|
|
17
18
|
import { useTheme } from '@oxyhq/bloom/theme';
|
|
18
19
|
import Avatar from './Avatar';
|
|
19
20
|
import { useOxy } from '../context/OxyContext';
|
|
20
21
|
import { useI18n } from '../hooks/useI18n';
|
|
21
22
|
import { logger as loggerUtil } from '@oxyhq/core';
|
|
22
23
|
import { buildAccountRows, type AccountRow } from './accountMenuRows';
|
|
24
|
+
import { useDeviceAccounts } from '../hooks/useDeviceAccounts';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Web-only anchor for the popover panel. Each field anchors the panel against
|
|
28
|
+
* one viewport edge, so the popover can be placed against ANY corner: a
|
|
29
|
+
* top-right avatar chip opens downward/right-aligned (`{ top, right }`), while a
|
|
30
|
+
* bottom-left account button opens upward/left-aligned (`{ bottom, left }`).
|
|
31
|
+
*
|
|
32
|
+
* Callers MUST supply at most one vertical edge (`top` XOR `bottom`) and at most
|
|
33
|
+
* one horizontal edge (`left` XOR `right`). The panel has a fixed width and
|
|
34
|
+
* `maxHeight`, so a single vertical + single horizontal edge fully positions it.
|
|
35
|
+
* Supplying both opposite edges (e.g. `top` AND `bottom`) would stretch the
|
|
36
|
+
* panel on RN-Web and is unsupported.
|
|
37
|
+
*/
|
|
38
|
+
export interface AccountMenuAnchor {
|
|
39
|
+
/** Distance from the viewport top, when anchoring the panel's TOP edge. */
|
|
40
|
+
top?: number;
|
|
41
|
+
/** Distance from the viewport bottom, when anchoring the panel's BOTTOM edge (opens upward). */
|
|
42
|
+
bottom?: number;
|
|
43
|
+
/** Distance from the viewport left, when anchoring the panel's LEFT edge. */
|
|
44
|
+
left?: number;
|
|
45
|
+
/** Distance from the viewport right, when anchoring the panel's RIGHT edge. */
|
|
46
|
+
right?: number;
|
|
47
|
+
}
|
|
23
48
|
|
|
24
49
|
export interface AccountMenuProps {
|
|
25
50
|
open: boolean;
|
|
26
51
|
onClose: () => void;
|
|
27
52
|
onNavigateManage: () => void;
|
|
28
53
|
onAddAccount: () => void;
|
|
29
|
-
/** Optional anchor
|
|
30
|
-
anchor?:
|
|
54
|
+
/** Optional anchor (web only). Native ignores this and uses bottom-sheet style. */
|
|
55
|
+
anchor?: AccountMenuAnchor | null;
|
|
31
56
|
}
|
|
32
57
|
|
|
33
58
|
const isWeb = Platform.OS === 'web';
|
|
34
59
|
|
|
60
|
+
/** Fixed popover width on web. Callers that compute an anchor (e.g. inbox's
|
|
61
|
+
* MailboxDrawer) key their gutter math off this — keep them consistent. */
|
|
62
|
+
const PANEL_WIDTH = 360;
|
|
63
|
+
|
|
35
64
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
65
|
+
* Unified, canonical account switcher for the Oxy ecosystem. Gmail-style: the
|
|
66
|
+
* accounts list sits at the top (current account first, with a checkmark), then
|
|
67
|
+
* "Add another account", "Manage account", and the sign-out actions.
|
|
38
68
|
*
|
|
39
|
-
* Reads everything it needs from `useOxy()` — never
|
|
40
|
-
* props. Renders as a popover anchored to the trigger on
|
|
41
|
-
* full-width bottom-sheet style modal on native.
|
|
69
|
+
* Reads everything it needs from `useOxy()` / `useDeviceAccounts()` — never
|
|
70
|
+
* receives a session via props. Renders as a popover anchored to the trigger on
|
|
71
|
+
* web, and as a full-width bottom-sheet style modal on native.
|
|
42
72
|
*/
|
|
43
73
|
const AccountMenu: React.FC<AccountMenuProps> = ({
|
|
44
74
|
open,
|
|
@@ -48,18 +78,25 @@ const AccountMenu: React.FC<AccountMenuProps> = ({
|
|
|
48
78
|
anchor,
|
|
49
79
|
}) => {
|
|
50
80
|
const {
|
|
51
|
-
user,
|
|
52
|
-
sessions,
|
|
53
81
|
activeSessionId,
|
|
54
82
|
switchSession,
|
|
55
83
|
logout,
|
|
56
84
|
logoutAll,
|
|
57
|
-
|
|
85
|
+
removeSession,
|
|
58
86
|
} = useOxy();
|
|
59
|
-
const { t
|
|
87
|
+
const { t } = useI18n();
|
|
60
88
|
const bloomTheme = useTheme();
|
|
89
|
+
const colors = bloomTheme.colors;
|
|
90
|
+
|
|
91
|
+
// Source EVERY account's real name/email/avatar/color from the unified
|
|
92
|
+
// device-account hook (shared apex `refresh-all` path on `*.oxy.so`, local
|
|
93
|
+
// `useOxy()` fallback on cross-domain web / native). This also synthesises a
|
|
94
|
+
// live-user row when the probe is empty, so the signed-in user is always
|
|
95
|
+
// represented (no "Not signed in" false negative).
|
|
96
|
+
const { accounts: deviceAccounts } = useDeviceAccounts();
|
|
61
97
|
|
|
62
98
|
const [busySessionId, setBusySessionId] = useState<string | null>(null);
|
|
99
|
+
const [removingSessionId, setRemovingSessionId] = useState<string | null>(null);
|
|
63
100
|
const [signingOut, setSigningOut] = useState(false);
|
|
64
101
|
const [signingOutAll, setSigningOutAll] = useState(false);
|
|
65
102
|
|
|
@@ -67,24 +104,27 @@ const AccountMenu: React.FC<AccountMenuProps> = ({
|
|
|
67
104
|
const signOutAllDialog = useDialogControl();
|
|
68
105
|
|
|
69
106
|
const containerRef = useRef<View | null>(null);
|
|
70
|
-
const firstActionRef = useRef<View | null>(null);
|
|
71
|
-
|
|
72
|
-
const rows = useMemo<AccountRow[]>(() => buildAccountRows({
|
|
73
|
-
sessions,
|
|
74
|
-
activeSessionId,
|
|
75
|
-
user,
|
|
76
|
-
locale,
|
|
77
|
-
getAvatarUrl: (avatarId) => oxyServices.getFileDownloadUrl(avatarId, 'thumb'),
|
|
78
|
-
}), [sessions, activeSessionId, user, locale, oxyServices]);
|
|
79
107
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
108
|
+
// Current account first, then the rest in their existing order — matching
|
|
109
|
+
// the Gmail-style chooser the inbox design ports from.
|
|
110
|
+
const rows = useMemo<AccountRow[]>(() => {
|
|
111
|
+
const built = buildAccountRows({ accounts: deviceAccounts });
|
|
112
|
+
const current = built.filter((row) => row.isActive);
|
|
113
|
+
const others = built.filter((row) => !row.isActive);
|
|
114
|
+
return [...current, ...others];
|
|
115
|
+
}, [deviceAccounts]);
|
|
83
116
|
|
|
84
|
-
const
|
|
85
|
-
return rows.filter((r) => !r.isActive);
|
|
86
|
-
}, [rows]);
|
|
117
|
+
const isSwitching = busySessionId !== null;
|
|
87
118
|
|
|
119
|
+
// Switch to a non-active account. We route ALL rows through
|
|
120
|
+
// `useOxy().switchSession(sessionId)` — the SDK's canonical switch path. On
|
|
121
|
+
// WEB it already performs the same silent activation the auth chooser uses:
|
|
122
|
+
// when the target `ClientSession` carries an `authuser` slot, it rotates that
|
|
123
|
+
// slot via `oxyServices.refreshTokenViaCookie({ authuser })` and plants the
|
|
124
|
+
// fresh access token before validating (see `useSessionManagement.switchSession`).
|
|
125
|
+
// On NATIVE it validates the session id directly. There is no separate
|
|
126
|
+
// "activate by authuser" SDK entry point, so reusing `switchSession`
|
|
127
|
+
// (rather than inventing a parallel mechanism) keeps a single source of truth.
|
|
88
128
|
const handleSwitch = useCallback(async (sessionId: string) => {
|
|
89
129
|
if (sessionId === activeSessionId || busySessionId) {
|
|
90
130
|
return;
|
|
@@ -104,6 +144,27 @@ const AccountMenu: React.FC<AccountMenuProps> = ({
|
|
|
104
144
|
}
|
|
105
145
|
}, [activeSessionId, busySessionId, switchSession, t, onClose]);
|
|
106
146
|
|
|
147
|
+
// Sign out a SPECIFIC inactive account from its per-row icon. `removeSession`
|
|
148
|
+
// is the SDK's canonical per-session sign-out: it targets the given session
|
|
149
|
+
// id (cookie-cleared logout via `authuser` slot on web, bearer logout
|
|
150
|
+
// otherwise) and removes ONLY that account without switching/clearing the
|
|
151
|
+
// active session. The menu stays open so the user can keep managing accounts.
|
|
152
|
+
const handleRemove = useCallback(async (sessionId: string) => {
|
|
153
|
+
if (sessionId === activeSessionId || removingSessionId) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
setRemovingSessionId(sessionId);
|
|
157
|
+
try {
|
|
158
|
+
await removeSession(sessionId);
|
|
159
|
+
toast.success(t('common.actions.signedOut') || 'Signed out');
|
|
160
|
+
} catch (error) {
|
|
161
|
+
loggerUtil.warn('Remove account failed', { component: 'AccountMenu' }, error as unknown);
|
|
162
|
+
toast.error(t('common.errors.signOutFailed') || 'Failed to sign out');
|
|
163
|
+
} finally {
|
|
164
|
+
setRemovingSessionId(null);
|
|
165
|
+
}
|
|
166
|
+
}, [activeSessionId, removingSessionId, removeSession, t]);
|
|
167
|
+
|
|
107
168
|
const performSignOut = useCallback(async () => {
|
|
108
169
|
if (signingOut) {
|
|
109
170
|
return;
|
|
@@ -161,200 +222,215 @@ const AccountMenu: React.FC<AccountMenuProps> = ({
|
|
|
161
222
|
? styles.webOverlay
|
|
162
223
|
: styles.nativeOverlay;
|
|
163
224
|
|
|
225
|
+
// Apply ONLY the edges the anchor supplies (so the panel never sets
|
|
226
|
+
// conflicting opposite edges). When no anchor is provided, fall back to the
|
|
227
|
+
// historical top-right placement used by `AccountMenuButton`.
|
|
228
|
+
const anchorStyle: ViewStyle = anchor
|
|
229
|
+
? {
|
|
230
|
+
...(anchor.top !== undefined ? { top: anchor.top } : null),
|
|
231
|
+
...(anchor.bottom !== undefined ? { bottom: anchor.bottom } : null),
|
|
232
|
+
...(anchor.left !== undefined ? { left: anchor.left } : null),
|
|
233
|
+
...(anchor.right !== undefined ? { right: anchor.right } : null),
|
|
234
|
+
}
|
|
235
|
+
: { top: 64, right: 16 };
|
|
236
|
+
|
|
164
237
|
const panelStyles: ViewStyle[] = isWeb
|
|
165
238
|
? [
|
|
166
239
|
styles.panelBase,
|
|
167
240
|
styles.panelWeb,
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
: { top: 64, right: 16 },
|
|
171
|
-
{ backgroundColor: bloomTheme.colors.background, borderColor: bloomTheme.colors.border },
|
|
241
|
+
anchorStyle,
|
|
242
|
+
{ backgroundColor: colors.background, borderColor: colors.border },
|
|
172
243
|
]
|
|
173
244
|
: [
|
|
174
245
|
styles.panelBase,
|
|
175
246
|
styles.panelNative,
|
|
176
|
-
{ backgroundColor:
|
|
247
|
+
{ backgroundColor: colors.background },
|
|
177
248
|
];
|
|
178
249
|
|
|
250
|
+
const actionDisabled = isSwitching || signingOut || signingOutAll;
|
|
251
|
+
|
|
179
252
|
const content = (
|
|
180
|
-
<
|
|
253
|
+
<Pressable
|
|
181
254
|
ref={containerRef}
|
|
255
|
+
// Swallow taps inside the panel so they never reach the overlay's
|
|
256
|
+
// outside-tap-to-close handler. (On web the panel is a direct,
|
|
257
|
+
// absolutely-positioned child of the overlay so the anchor resolves
|
|
258
|
+
// against the viewport.)
|
|
259
|
+
onPress={() => undefined}
|
|
182
260
|
style={panelStyles}
|
|
183
261
|
accessibilityRole="menu"
|
|
184
262
|
accessibilityLabel={t('accountMenu.label') || 'Account menu'}
|
|
185
263
|
>
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
uri={activeRow.avatarUri}
|
|
191
|
-
name={activeRow.displayName}
|
|
192
|
-
size={64}
|
|
193
|
-
/>
|
|
194
|
-
<Text
|
|
195
|
-
style={[styles.headerName, { color: bloomTheme.colors.text }]}
|
|
196
|
-
numberOfLines={1}
|
|
197
|
-
>
|
|
198
|
-
{activeRow.displayName}
|
|
199
|
-
</Text>
|
|
200
|
-
{activeRow.secondary ? (
|
|
201
|
-
<Text
|
|
202
|
-
style={[styles.headerSecondary, { color: bloomTheme.colors.textSecondary }]}
|
|
203
|
-
numberOfLines={1}
|
|
204
|
-
>
|
|
205
|
-
{activeRow.secondary}
|
|
206
|
-
</Text>
|
|
207
|
-
) : null}
|
|
208
|
-
</View>
|
|
209
|
-
) : (
|
|
210
|
-
<View style={styles.header}>
|
|
211
|
-
<Text style={[styles.headerName, { color: bloomTheme.colors.text }]}>
|
|
212
|
-
{t('common.status.notSignedIn') || 'Not signed in'}
|
|
213
|
-
</Text>
|
|
214
|
-
</View>
|
|
215
|
-
)}
|
|
216
|
-
|
|
217
|
-
{/* 2) Manage account */}
|
|
218
|
-
<TouchableOpacity
|
|
219
|
-
ref={firstActionRef as React.RefObject<View>}
|
|
220
|
-
accessibilityRole="menuitem"
|
|
221
|
-
accessibilityLabel={t('accountMenu.manage') || 'Manage your Oxy Account'}
|
|
222
|
-
style={[styles.primaryButton, { borderColor: bloomTheme.colors.border }]}
|
|
223
|
-
onPress={() => {
|
|
224
|
-
onClose();
|
|
225
|
-
onNavigateManage();
|
|
226
|
-
}}
|
|
264
|
+
<ScrollView
|
|
265
|
+
style={styles.scroll}
|
|
266
|
+
contentContainerStyle={styles.scrollContent}
|
|
267
|
+
showsVerticalScrollIndicator={false}
|
|
227
268
|
>
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
269
|
+
{/* 1) Accounts list — current first (checkmark), then others. */}
|
|
270
|
+
{rows.map((row) => {
|
|
271
|
+
const isBusy = busySessionId === row.sessionId;
|
|
272
|
+
const isRemoving = removingSessionId === row.sessionId;
|
|
273
|
+
return (
|
|
274
|
+
<TouchableOpacity
|
|
275
|
+
key={`account-${row.sessionId}`}
|
|
276
|
+
accessibilityRole="menuitem"
|
|
277
|
+
accessibilityLabel={row.displayName}
|
|
278
|
+
accessibilityState={{ selected: row.isActive }}
|
|
279
|
+
onPress={() => handleSwitch(row.sessionId)}
|
|
280
|
+
disabled={row.isActive || isBusy || isSwitching}
|
|
281
|
+
activeOpacity={0.6}
|
|
282
|
+
style={[
|
|
283
|
+
styles.accountRow,
|
|
284
|
+
row.isActive && { backgroundColor: colors.primarySubtle },
|
|
285
|
+
isSwitching && !row.isActive && styles.rowDisabled,
|
|
286
|
+
]}
|
|
287
|
+
>
|
|
288
|
+
<Avatar
|
|
289
|
+
uri={row.avatarUri}
|
|
290
|
+
name={row.displayName}
|
|
291
|
+
size={row.isActive ? 40 : 32}
|
|
292
|
+
/>
|
|
293
|
+
<View style={styles.accountInfo}>
|
|
294
|
+
<Text
|
|
295
|
+
style={[
|
|
296
|
+
styles.accountName,
|
|
297
|
+
{ color: colors.text },
|
|
298
|
+
row.isActive && styles.accountNameActive,
|
|
299
|
+
]}
|
|
300
|
+
numberOfLines={1}
|
|
301
|
+
>
|
|
302
|
+
{row.displayName}
|
|
303
|
+
</Text>
|
|
304
|
+
{row.secondary ? (
|
|
263
305
|
<Text
|
|
264
|
-
style={[styles.
|
|
306
|
+
style={[styles.accountEmail, { color: colors.textSecondary }]}
|
|
265
307
|
numberOfLines={1}
|
|
266
308
|
>
|
|
267
|
-
{row.
|
|
309
|
+
{row.secondary}
|
|
268
310
|
</Text>
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
311
|
+
) : null}
|
|
312
|
+
</View>
|
|
313
|
+
{isBusy ? (
|
|
314
|
+
<ActivityIndicator color={colors.primary} size="small" />
|
|
315
|
+
) : row.isActive ? (
|
|
316
|
+
<Ionicons name="checkmark" size={20} color={colors.primary} />
|
|
317
|
+
) : isRemoving ? (
|
|
318
|
+
<ActivityIndicator color={colors.textSecondary} size="small" />
|
|
319
|
+
) : (
|
|
320
|
+
<TouchableOpacity
|
|
321
|
+
accessibilityRole="button"
|
|
322
|
+
accessibilityLabel={
|
|
323
|
+
t('accountMenu.signOutAccount', { name: row.displayName })
|
|
324
|
+
|| `Sign out ${row.displayName}`
|
|
325
|
+
}
|
|
326
|
+
onPress={() => handleRemove(row.sessionId)}
|
|
327
|
+
disabled={isSwitching || removingSessionId !== null}
|
|
328
|
+
activeOpacity={0.6}
|
|
329
|
+
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
|
330
|
+
style={styles.rowSignOutButton}
|
|
331
|
+
>
|
|
281
332
|
<Ionicons
|
|
282
|
-
name="
|
|
283
|
-
size={
|
|
284
|
-
color={
|
|
333
|
+
name="log-out-outline"
|
|
334
|
+
size={18}
|
|
335
|
+
color={colors.textSecondary}
|
|
285
336
|
/>
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
337
|
+
</TouchableOpacity>
|
|
338
|
+
)}
|
|
339
|
+
</TouchableOpacity>
|
|
340
|
+
);
|
|
341
|
+
})}
|
|
342
|
+
|
|
343
|
+
{/* 2) Switching indicator. */}
|
|
344
|
+
{isSwitching ? (
|
|
345
|
+
<View style={styles.switchingRow}>
|
|
346
|
+
<ActivityIndicator color={colors.textSecondary} size="small" />
|
|
347
|
+
<Text style={[styles.switchingText, { color: colors.textSecondary }]}>
|
|
348
|
+
{t('accountMenu.switching') || 'Switching account…'}
|
|
349
|
+
</Text>
|
|
350
|
+
</View>
|
|
351
|
+
) : null}
|
|
352
|
+
|
|
353
|
+
<Divider color={colors.border} spacing={4} />
|
|
354
|
+
|
|
355
|
+
{/* 3) Add another account. */}
|
|
356
|
+
<TouchableOpacity
|
|
357
|
+
accessibilityRole="menuitem"
|
|
358
|
+
accessibilityLabel={t('accountMenu.addAnother') || 'Add another account'}
|
|
359
|
+
onPress={() => {
|
|
360
|
+
onClose();
|
|
361
|
+
onAddAccount();
|
|
362
|
+
}}
|
|
363
|
+
disabled={actionDisabled}
|
|
364
|
+
activeOpacity={0.6}
|
|
365
|
+
style={[styles.actionRow, actionDisabled && styles.rowDisabled]}
|
|
308
366
|
>
|
|
309
|
-
<Ionicons
|
|
310
|
-
|
|
311
|
-
size={18}
|
|
312
|
-
color={bloomTheme.colors.primary}
|
|
313
|
-
/>
|
|
314
|
-
</View>
|
|
315
|
-
<View style={styles.rowInfo}>
|
|
316
|
-
<Text style={[styles.rowName, { color: bloomTheme.colors.text }]}>
|
|
367
|
+
<Ionicons name="person-add-outline" size={20} color={colors.icon} />
|
|
368
|
+
<Text style={[styles.actionText, { color: colors.text }]}>
|
|
317
369
|
{t('accountMenu.addAnother') || 'Add another account'}
|
|
318
370
|
</Text>
|
|
319
|
-
</
|
|
320
|
-
|
|
371
|
+
</TouchableOpacity>
|
|
372
|
+
|
|
373
|
+
<Divider color={colors.border} spacing={4} />
|
|
374
|
+
|
|
375
|
+
{/* 4) Manage account / Settings. */}
|
|
376
|
+
<TouchableOpacity
|
|
377
|
+
accessibilityRole="menuitem"
|
|
378
|
+
accessibilityLabel={t('accountMenu.manage') || 'Manage your Oxy Account'}
|
|
379
|
+
onPress={() => {
|
|
380
|
+
onClose();
|
|
381
|
+
onNavigateManage();
|
|
382
|
+
}}
|
|
383
|
+
disabled={actionDisabled}
|
|
384
|
+
activeOpacity={0.6}
|
|
385
|
+
style={[styles.actionRow, actionDisabled && styles.rowDisabled]}
|
|
386
|
+
>
|
|
387
|
+
<Ionicons name="settings-outline" size={18} color={colors.icon} />
|
|
388
|
+
<Text style={[styles.actionText, { color: colors.text }]}>
|
|
389
|
+
{t('accountMenu.manage') || 'Manage your Oxy Account'}
|
|
390
|
+
</Text>
|
|
391
|
+
</TouchableOpacity>
|
|
321
392
|
|
|
322
|
-
|
|
323
|
-
<View style={[styles.footer, { borderTopColor: bloomTheme.colors.border }]}>
|
|
393
|
+
{/* 5) Sign out (current). */}
|
|
324
394
|
<TouchableOpacity
|
|
325
395
|
accessibilityRole="menuitem"
|
|
326
396
|
accessibilityLabel={t('common.actions.signOut') || 'Sign out'}
|
|
327
397
|
onPress={() => signOutDialog.open()}
|
|
328
|
-
disabled={
|
|
329
|
-
|
|
398
|
+
disabled={actionDisabled}
|
|
399
|
+
activeOpacity={0.6}
|
|
400
|
+
style={[styles.actionRow, actionDisabled && styles.rowDisabled]}
|
|
330
401
|
>
|
|
331
402
|
{signingOut ? (
|
|
332
|
-
<ActivityIndicator color={
|
|
403
|
+
<ActivityIndicator color={colors.error} size="small" />
|
|
333
404
|
) : (
|
|
334
|
-
<
|
|
335
|
-
{t('common.actions.signOut') || 'Sign out'}
|
|
336
|
-
</Text>
|
|
405
|
+
<Ionicons name="log-out-outline" size={18} color={colors.error} />
|
|
337
406
|
)}
|
|
407
|
+
<Text style={[styles.actionText, { color: colors.error }]}>
|
|
408
|
+
{t('common.actions.signOut') || 'Sign out'}
|
|
409
|
+
</Text>
|
|
338
410
|
</TouchableOpacity>
|
|
411
|
+
|
|
412
|
+
{/* 6) Sign out of all accounts (only when >1 account). */}
|
|
339
413
|
{rows.length > 1 ? (
|
|
340
414
|
<TouchableOpacity
|
|
341
415
|
accessibilityRole="menuitem"
|
|
342
416
|
accessibilityLabel={t('accountMenu.signOutAll') || 'Sign out of all accounts'}
|
|
343
417
|
onPress={() => signOutAllDialog.open()}
|
|
344
|
-
disabled={
|
|
345
|
-
|
|
418
|
+
disabled={actionDisabled}
|
|
419
|
+
activeOpacity={0.6}
|
|
420
|
+
style={[styles.actionRow, actionDisabled && styles.rowDisabled]}
|
|
346
421
|
>
|
|
347
422
|
{signingOutAll ? (
|
|
348
|
-
<ActivityIndicator color={
|
|
423
|
+
<ActivityIndicator color={colors.error} size="small" />
|
|
349
424
|
) : (
|
|
350
|
-
<
|
|
351
|
-
{t('accountMenu.signOutAll') || 'Sign out of all accounts'}
|
|
352
|
-
</Text>
|
|
425
|
+
<Ionicons name="log-out-outline" size={18} color={colors.error} />
|
|
353
426
|
)}
|
|
427
|
+
<Text style={[styles.actionText, { color: colors.error }]}>
|
|
428
|
+
{t('accountMenu.signOutAll') || 'Sign out of all accounts'}
|
|
429
|
+
</Text>
|
|
354
430
|
</TouchableOpacity>
|
|
355
431
|
) : null}
|
|
356
|
-
</
|
|
357
|
-
</
|
|
432
|
+
</ScrollView>
|
|
433
|
+
</Pressable>
|
|
358
434
|
);
|
|
359
435
|
|
|
360
436
|
return (
|
|
@@ -370,10 +446,15 @@ const AccountMenu: React.FC<AccountMenuProps> = ({
|
|
|
370
446
|
onPress={onClose}
|
|
371
447
|
style={overlayStyles}
|
|
372
448
|
>
|
|
373
|
-
{/*
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
449
|
+
{/*
|
|
450
|
+
* Web: the panel is `position: absolute` and rendered as a DIRECT
|
|
451
|
+
* child of the full-viewport overlay, so its anchor edges resolve
|
|
452
|
+
* against the viewport. The panel swallows its own taps.
|
|
453
|
+
*
|
|
454
|
+
* Native: the overlay is `justify-content: flex-end`, so the
|
|
455
|
+
* (statically positioned) panel docks to the bottom as a sheet.
|
|
456
|
+
*/}
|
|
457
|
+
{content}
|
|
377
458
|
</Pressable>
|
|
378
459
|
|
|
379
460
|
<Dialog
|
|
@@ -410,18 +491,19 @@ const styles = StyleSheet.create({
|
|
|
410
491
|
webOverlay: {
|
|
411
492
|
flex: 1,
|
|
412
493
|
backgroundColor: 'transparent',
|
|
494
|
+
// Explicit positioning context: the absolutely-positioned panel is a
|
|
495
|
+
// direct child and resolves its anchor edges (top/bottom/left/right)
|
|
496
|
+
// against this full-viewport overlay — matching the trigger-rect math
|
|
497
|
+
// callers compute from `window.innerWidth` / `window.innerHeight`.
|
|
498
|
+
position: 'relative',
|
|
413
499
|
},
|
|
414
500
|
nativeOverlay: {
|
|
415
501
|
flex: 1,
|
|
416
502
|
backgroundColor: 'rgba(0,0,0,0.32)',
|
|
417
503
|
justifyContent: 'flex-end',
|
|
418
504
|
},
|
|
419
|
-
panelTouchable: {
|
|
420
|
-
// Pressable wrapper that owns no styling — just absorbs taps.
|
|
421
|
-
},
|
|
422
505
|
panelBase: {
|
|
423
|
-
borderRadius:
|
|
424
|
-
paddingVertical: 12,
|
|
506
|
+
borderRadius: 12,
|
|
425
507
|
shadowColor: '#000',
|
|
426
508
|
shadowOpacity: 0.18,
|
|
427
509
|
shadowRadius: 24,
|
|
@@ -431,103 +513,79 @@ const styles = StyleSheet.create({
|
|
|
431
513
|
},
|
|
432
514
|
panelWeb: {
|
|
433
515
|
position: 'absolute',
|
|
434
|
-
width:
|
|
516
|
+
width: PANEL_WIDTH,
|
|
435
517
|
maxHeight: '85%',
|
|
436
518
|
borderWidth: 1,
|
|
437
519
|
},
|
|
438
520
|
panelNative: {
|
|
439
521
|
marginHorizontal: 0,
|
|
440
|
-
borderTopLeftRadius:
|
|
441
|
-
borderTopRightRadius:
|
|
522
|
+
borderTopLeftRadius: 20,
|
|
523
|
+
borderTopRightRadius: 20,
|
|
442
524
|
borderBottomLeftRadius: 0,
|
|
443
525
|
borderBottomRightRadius: 0,
|
|
444
|
-
|
|
445
|
-
paddingBottom: 24,
|
|
526
|
+
paddingBottom: 12,
|
|
446
527
|
maxHeight: '85%',
|
|
447
528
|
},
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
paddingHorizontal: 20,
|
|
451
|
-
paddingVertical: 16,
|
|
452
|
-
},
|
|
453
|
-
headerName: {
|
|
454
|
-
fontSize: 18,
|
|
455
|
-
fontWeight: '600',
|
|
456
|
-
marginTop: 12,
|
|
457
|
-
textAlign: 'center',
|
|
529
|
+
scroll: {
|
|
530
|
+
flexGrow: 0,
|
|
458
531
|
},
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
marginTop: 4,
|
|
462
|
-
textAlign: 'center',
|
|
532
|
+
scrollContent: {
|
|
533
|
+
paddingVertical: 4,
|
|
463
534
|
},
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
marginBottom: 12,
|
|
467
|
-
paddingVertical: 12,
|
|
468
|
-
paddingHorizontal: 16,
|
|
469
|
-
borderRadius: 999,
|
|
470
|
-
borderWidth: 1,
|
|
535
|
+
accountRow: {
|
|
536
|
+
flexDirection: 'row',
|
|
471
537
|
alignItems: 'center',
|
|
538
|
+
paddingHorizontal: 14,
|
|
539
|
+
paddingVertical: 10,
|
|
540
|
+
gap: 10,
|
|
472
541
|
},
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
fontWeight: '600',
|
|
542
|
+
rowDisabled: {
|
|
543
|
+
opacity: 0.4,
|
|
476
544
|
},
|
|
477
|
-
|
|
478
|
-
|
|
545
|
+
accountInfo: {
|
|
546
|
+
flex: 1,
|
|
547
|
+
minWidth: 0,
|
|
479
548
|
},
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
549
|
+
accountName: {
|
|
550
|
+
fontSize: 13,
|
|
551
|
+
fontWeight: '500',
|
|
483
552
|
},
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
alignItems: 'center',
|
|
487
|
-
paddingVertical: 10,
|
|
488
|
-
paddingHorizontal: 12,
|
|
489
|
-
borderRadius: 16,
|
|
490
|
-
marginVertical: 2,
|
|
553
|
+
accountNameActive: {
|
|
554
|
+
fontWeight: '600',
|
|
491
555
|
},
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
marginTop:
|
|
556
|
+
accountEmail: {
|
|
557
|
+
fontSize: 11,
|
|
558
|
+
marginTop: 1,
|
|
495
559
|
},
|
|
496
|
-
|
|
497
|
-
width:
|
|
498
|
-
height:
|
|
499
|
-
borderRadius: 18,
|
|
560
|
+
rowSignOutButton: {
|
|
561
|
+
width: 28,
|
|
562
|
+
height: 28,
|
|
500
563
|
alignItems: 'center',
|
|
501
564
|
justifyContent: 'center',
|
|
565
|
+
borderRadius: 14,
|
|
566
|
+
opacity: 0.6,
|
|
502
567
|
},
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
568
|
+
switchingRow: {
|
|
569
|
+
flexDirection: 'row',
|
|
570
|
+
alignItems: 'center',
|
|
571
|
+
justifyContent: 'center',
|
|
572
|
+
paddingVertical: 8,
|
|
573
|
+
gap: 8,
|
|
506
574
|
},
|
|
507
|
-
|
|
508
|
-
fontSize:
|
|
575
|
+
switchingText: {
|
|
576
|
+
fontSize: 12,
|
|
509
577
|
fontWeight: '500',
|
|
510
578
|
},
|
|
511
|
-
|
|
512
|
-
fontSize: 13,
|
|
513
|
-
marginTop: 2,
|
|
514
|
-
},
|
|
515
|
-
footer: {
|
|
516
|
-
marginTop: 12,
|
|
517
|
-
paddingTop: 8,
|
|
518
|
-
paddingHorizontal: 12,
|
|
519
|
-
borderTopWidth: StyleSheet.hairlineWidth,
|
|
579
|
+
actionRow: {
|
|
520
580
|
flexDirection: 'row',
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
paddingVertical: 10,
|
|
526
|
-
paddingHorizontal: 12,
|
|
581
|
+
alignItems: 'center',
|
|
582
|
+
paddingHorizontal: 14,
|
|
583
|
+
paddingVertical: 12,
|
|
584
|
+
gap: 10,
|
|
527
585
|
},
|
|
528
|
-
|
|
529
|
-
fontSize:
|
|
530
|
-
fontWeight: '
|
|
586
|
+
actionText: {
|
|
587
|
+
fontSize: 13,
|
|
588
|
+
fontWeight: '500',
|
|
531
589
|
},
|
|
532
590
|
});
|
|
533
591
|
|