@oxyhq/services 13.3.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/ProfileMenu.js +140 -153
- 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/ProfileMenu.js +143 -156
- 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/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/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/ProfileMenu.tsx +158 -162
- 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
|
@@ -2,31 +2,52 @@
|
|
|
2
2
|
* Shared, pure orchestration for completing the cross-app device-flow sign-in
|
|
3
3
|
* (the QR-code / "Open Oxy Auth" path used on native and web).
|
|
4
4
|
*
|
|
5
|
-
* THE BUG THIS FIXES (native): once another authenticated device
|
|
6
|
-
* pending AuthSession, the originating client is notified
|
|
7
|
-
* deep-link) with the authorized `sessionId`. Before any
|
|
8
|
-
* code can use it, the client MUST exchange the secret
|
|
9
|
-
* (held only by this client, generated for THIS flow)
|
|
10
|
-
* token via `claimSessionByToken` — the device-flow
|
|
11
|
-
* code-for-token exchange (RFC 8628 §3.4).
|
|
5
|
+
* THE FIRST BUG THIS FIXES (native): once another authenticated device
|
|
6
|
+
* approves the pending AuthSession, the originating client is notified
|
|
7
|
+
* (socket / poll / deep-link) with the authorized `sessionId`. Before any
|
|
8
|
+
* session-management code can use it, the client MUST exchange the secret
|
|
9
|
+
* 128-bit `sessionToken` (held only by this client, generated for THIS flow)
|
|
10
|
+
* for the first access token via `claimSessionByToken` — the device-flow
|
|
11
|
+
* equivalent of OAuth's code-for-token exchange (RFC 8628 §3.4).
|
|
12
|
+
*
|
|
13
|
+
* THE SECOND BUG THIS FIXES (session-sync cutover regression): the
|
|
14
|
+
* freshly-claimed session is NOT yet registered in the device's
|
|
15
|
+
* server-authoritative session set — nothing has run
|
|
16
|
+
* `sessionClient.addCurrentAccount()` for it — so it must NOT be committed
|
|
17
|
+
* through `switchSession`. That path is now an account-SWITCH between
|
|
18
|
+
* accounts already registered on this device (`OxyContext`'s
|
|
19
|
+
* `switchSessionForContext`), and throws `No device account found for
|
|
20
|
+
* session "..."` for anything else, surfacing as "Authorization successful
|
|
21
|
+
* but failed to complete sign in." Instead the claimed session must be
|
|
22
|
+
* committed through the SAME path a fresh password sign-in uses —
|
|
23
|
+
* `useOxy().handleWebSession` (`OxyContext`'s `handleWebSSOSession`) — which
|
|
24
|
+
* registers the account into the device set, persists it durably, and
|
|
25
|
+
* hydrates the full user profile.
|
|
12
26
|
*
|
|
13
27
|
* Skipping the claim leaves the SDK with NO bearer token: the session is
|
|
14
28
|
* authorized server-side but the app never becomes authenticated and the UI
|
|
15
|
-
* sits "Waiting for authorization..." forever. Consolidating the
|
|
16
|
-
* sequence here keeps native and web identical and
|
|
29
|
+
* sits "Waiting for authorization..." forever. Consolidating the
|
|
30
|
+
* claim->commit sequence here keeps native and web identical and
|
|
31
|
+
* unit-testable.
|
|
17
32
|
*/
|
|
18
|
-
import { type User } from '@oxyhq/core';
|
|
33
|
+
import { type EstablishAfterClaimDeps, type SessionLoginResponse, type SsoEstablishClient, type User } from '@oxyhq/core';
|
|
19
34
|
interface DeviceFlowClaimResult {
|
|
20
35
|
accessToken?: string;
|
|
21
36
|
sessionId?: string;
|
|
37
|
+
deviceId?: string;
|
|
38
|
+
expiresAt?: string;
|
|
39
|
+
user?: User;
|
|
22
40
|
}
|
|
23
41
|
/**
|
|
24
42
|
* The minimal `OxyServices` surface this orchestration needs. Kept as a
|
|
25
43
|
* structural type (rather than importing the full client) so the helper is
|
|
26
44
|
* trivially unit-testable with a stub and never pulls the RN/Expo runtime into
|
|
27
45
|
* a test bundle.
|
|
46
|
+
*
|
|
47
|
+
* Extends {@link SsoEstablishClient} (`requestSsoEstablishUrl`) so the WEB
|
|
48
|
+
* post-claim durable-session hop can be driven off the same client.
|
|
28
49
|
*/
|
|
29
|
-
export interface DeviceFlowClient {
|
|
50
|
+
export interface DeviceFlowClient extends SsoEstablishClient {
|
|
30
51
|
/**
|
|
31
52
|
* Exchange the device-flow `sessionToken` for the first access + refresh
|
|
32
53
|
* token, planting them on the client. Single-use; replay is rejected by the
|
|
@@ -45,19 +66,30 @@ export interface CompleteDeviceFlowSignInOptions {
|
|
|
45
66
|
*/
|
|
46
67
|
sessionToken: string;
|
|
47
68
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
69
|
+
* `useOxy().handleWebSession` — commits the freshly-claimed session into
|
|
70
|
+
* context state through the SAME path a password sign-in uses:
|
|
71
|
+
* registers the account into the device's server-authoritative session set,
|
|
72
|
+
* persists it durably, and hydrates the full user profile. Runs AFTER the
|
|
73
|
+
* bearer is planted so its bearer-protected calls succeed.
|
|
74
|
+
*/
|
|
75
|
+
commitSession: (session: SessionLoginResponse) => Promise<void>;
|
|
76
|
+
/**
|
|
77
|
+
* Injectable web seams for the post-claim durable-session establish hop
|
|
78
|
+
* ({@link establishIdpSessionAfterClaim}). Omit in production — the defaults
|
|
79
|
+
* resolve to `window.*` (and no-op off-web / on native). Tests inject fakes to
|
|
80
|
+
* drive the web path deterministically.
|
|
51
81
|
*/
|
|
52
|
-
|
|
82
|
+
establishDeps?: EstablishAfterClaimDeps;
|
|
53
83
|
}
|
|
54
84
|
/**
|
|
55
85
|
* Complete a device-flow sign-in: claim the first access token with the secret
|
|
56
|
-
* `sessionToken` (planting the bearer), then
|
|
57
|
-
* `
|
|
86
|
+
* `sessionToken` (planting the bearer), then commit the resulting session via
|
|
87
|
+
* `commitSession` (registers it into the device's server-authoritative session
|
|
88
|
+
* set and hydrates the full user). Returns the authenticated user.
|
|
58
89
|
*
|
|
59
|
-
* Throws if
|
|
90
|
+
* Throws if the claim did not return a usable session, or if either the claim
|
|
91
|
+
* or the commit fails; callers surface a retry UI.
|
|
60
92
|
*/
|
|
61
|
-
export declare function completeDeviceFlowSignIn({ oxyServices, sessionId, sessionToken,
|
|
93
|
+
export declare function completeDeviceFlowSignIn({ oxyServices, sessionId, sessionToken, commitSession, establishDeps, }: CompleteDeviceFlowSignInOptions): Promise<User>;
|
|
62
94
|
export {};
|
|
63
95
|
//# sourceMappingURL=deviceFlowSignIn.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deviceFlowSignIn.d.ts","sourceRoot":"","sources":["../../../../src/utils/deviceFlowSignIn.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"deviceFlowSignIn.d.ts","sourceRoot":"","sources":["../../../../src/utils/deviceFlowSignIn.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAGL,KAAK,uBAAuB,EAE5B,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,IAAI,EACV,MAAM,aAAa,CAAC;AAErB,UAAU,qBAAqB;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,IAAI,CAAC;CACb;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAiB,SAAQ,kBAAkB;IAC1D;;;;OAIG;IACH,mBAAmB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,GAAG,SAAS,CAAC,CAAC;CAC3F;AAED,MAAM,WAAW,+BAA+B;IAC9C,6EAA6E;IAC7E,WAAW,EAAE,gBAAgB,CAAC;IAC9B,+EAA+E;IAC/E,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,aAAa,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE;;;;;OAKG;IACH,aAAa,CAAC,EAAE,uBAAuB,CAAC;CACzC;AAED;;;;;;;;GAQG;AACH,wBAAsB,wBAAwB,CAAC,EAC7C,WAAW,EACX,SAAS,EACT,YAAY,EACZ,aAAa,EACb,aAAa,GACd,EAAE,+BAA+B,GAAG,OAAO,CAAC,IAAI,CAAC,CA+CjD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxyhq/services",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "14.0.0",
|
|
4
4
|
"description": "OxyHQ Expo/React Native SDK — UI components, screens, and native features",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -106,10 +106,10 @@
|
|
|
106
106
|
}
|
|
107
107
|
},
|
|
108
108
|
"dependencies": {
|
|
109
|
-
"@oxyhq/contracts": "0.
|
|
109
|
+
"@oxyhq/contracts": "0.8.0"
|
|
110
110
|
},
|
|
111
111
|
"devDependencies": {
|
|
112
|
-
"@oxyhq/core": "^5.2
|
|
112
|
+
"@oxyhq/core": "^5.4.2",
|
|
113
113
|
"nativewind": "5.0.0-preview.3",
|
|
114
114
|
"react-native-css": "^3.0.0",
|
|
115
115
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
@@ -153,7 +153,7 @@
|
|
|
153
153
|
"peerDependencies": {
|
|
154
154
|
"@expo/vector-icons": "^15.0.3",
|
|
155
155
|
"@oxyhq/bloom": ">=0.16.0",
|
|
156
|
-
"@oxyhq/core": "^5.2
|
|
156
|
+
"@oxyhq/core": "^5.4.2",
|
|
157
157
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
158
158
|
"@react-native-community/netinfo": "^11.4.1",
|
|
159
159
|
"@tanstack/query-async-storage-persister": "^5.101",
|
package/src/index.ts
CHANGED
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
/// <reference path="./types/react-native-classname.d.ts" />
|
|
25
|
-
/// <reference path="./types/react-native-web-style.d.ts" />
|
|
26
25
|
|
|
27
26
|
import { setPlatformOS, type PlatformOS } from '@oxyhq/core';
|
|
28
27
|
import { Platform } from 'react-native';
|
|
@@ -62,7 +61,6 @@ export {
|
|
|
62
61
|
// ---------------------------------------------------------------------------
|
|
63
62
|
// Session / asset hooks
|
|
64
63
|
// ---------------------------------------------------------------------------
|
|
65
|
-
export { useSessionSocket } from './ui/hooks/useSessionSocket';
|
|
66
64
|
export { useAssets, setOxyAssetInstance } from './ui/hooks/useAssets';
|
|
67
65
|
export { useFileDownloadUrl } from './ui/hooks/useFileDownloadUrl';
|
|
68
66
|
export { useFollow, useFollowerCounts } from './ui/hooks/useFollow';
|
|
@@ -226,14 +224,16 @@ export type { ProfileMenuProps, ProfileMenuAnchor } from './ui/components/Profil
|
|
|
226
224
|
export { default as AccountSwitcher, AccountSwitcherView } from './ui/components/AccountSwitcher';
|
|
227
225
|
export type { AccountSwitcherProps, AccountSwitcherActions } from './ui/components/AccountSwitcher';
|
|
228
226
|
|
|
229
|
-
// Unified
|
|
230
|
-
//
|
|
231
|
-
|
|
227
|
+
// Unified switchable-accounts hook — the single source of everything the user
|
|
228
|
+
// can switch into: device sign-ins AND linked graph accounts (owned orgs +
|
|
229
|
+
// shared-with-you), deduped by account id and hydrated with real
|
|
230
|
+
// name/email/avatar/color. Every switch routes through `switchToAccount`.
|
|
231
|
+
export { useSwitchableAccounts } from './ui/hooks/useSwitchableAccounts';
|
|
232
232
|
export type {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
} from './ui/hooks/
|
|
233
|
+
SwitchableAccount,
|
|
234
|
+
SwitchableAccountUser,
|
|
235
|
+
UseSwitchableAccountsResult,
|
|
236
|
+
} from './ui/hooks/useSwitchableAccounts';
|
|
237
237
|
|
|
238
238
|
// Unified "Manage your Oxy Account" screen (the caller's own personal account)
|
|
239
239
|
export { default as ManageAccountScreen } from './ui/screens/ManageAccountScreen';
|
|
@@ -20,8 +20,7 @@ import Avatar from './Avatar';
|
|
|
20
20
|
import { useOxy } from '../context/OxyContext';
|
|
21
21
|
import { useI18n } from '../hooks/useI18n';
|
|
22
22
|
import { isDev, logger as loggerUtil } from '@oxyhq/core';
|
|
23
|
-
import {
|
|
24
|
-
import { useDeviceAccounts } from '../hooks/useDeviceAccounts';
|
|
23
|
+
import { useSwitchableAccounts, type SwitchableAccount } from '../hooks/useSwitchableAccounts';
|
|
25
24
|
|
|
26
25
|
/**
|
|
27
26
|
* Web-only anchor for the popover panel. Each field anchors the panel against
|
|
@@ -66,9 +65,11 @@ const PANEL_WIDTH = 360;
|
|
|
66
65
|
/**
|
|
67
66
|
* Unified, canonical account switcher for the Oxy ecosystem. Gmail-style: the
|
|
68
67
|
* accounts list sits at the top (current account first, with a checkmark), then
|
|
69
|
-
* "Add another account", "Manage account", and the sign-out actions.
|
|
68
|
+
* "Add another account", "Manage account", and the sign-out actions. Lists EVERY
|
|
69
|
+
* switchable account (device sign-ins AND linked graph accounts) and routes every
|
|
70
|
+
* switch through the single `switchToAccount(accountId)` path.
|
|
70
71
|
*
|
|
71
|
-
* Reads everything it needs from `useOxy()` / `
|
|
72
|
+
* Reads everything it needs from `useOxy()` / `useSwitchableAccounts()` — never
|
|
72
73
|
* receives a session via props. Renders as a popover anchored to the trigger on
|
|
73
74
|
* web, and as a full-width bottom-sheet style modal on native.
|
|
74
75
|
*/
|
|
@@ -81,8 +82,7 @@ const AccountMenu: React.FC<AccountMenuProps> = ({
|
|
|
81
82
|
onBeforeSessionChange,
|
|
82
83
|
}) => {
|
|
83
84
|
const {
|
|
84
|
-
|
|
85
|
-
switchSession,
|
|
85
|
+
switchToAccount,
|
|
86
86
|
logout,
|
|
87
87
|
logoutAll,
|
|
88
88
|
removeSession,
|
|
@@ -91,14 +91,15 @@ const AccountMenu: React.FC<AccountMenuProps> = ({
|
|
|
91
91
|
const bloomTheme = useTheme();
|
|
92
92
|
const colors = bloomTheme.colors;
|
|
93
93
|
|
|
94
|
-
// Source EVERY account
|
|
95
|
-
//
|
|
96
|
-
// `
|
|
97
|
-
// live-user row when the
|
|
98
|
-
//
|
|
99
|
-
|
|
94
|
+
// Source EVERY switchable account — device sign-ins AND linked graph
|
|
95
|
+
// accounts (owned orgs + shared-with-you) — from the single
|
|
96
|
+
// `useSwitchableAccounts` hook, each hydrated with real
|
|
97
|
+
// name/email/avatar/color. It also synthesises a live-user row when the
|
|
98
|
+
// device set has not synced yet, so the signed-in user is always represented
|
|
99
|
+
// (no "Not signed in" false negative).
|
|
100
|
+
const { accounts } = useSwitchableAccounts();
|
|
100
101
|
|
|
101
|
-
const [
|
|
102
|
+
const [switchingAccountId, setSwitchingAccountId] = useState<string | null>(null);
|
|
102
103
|
const [removingSessionId, setRemovingSessionId] = useState<string | null>(null);
|
|
103
104
|
const [signingOut, setSigningOut] = useState(false);
|
|
104
105
|
const [signingOutAll, setSigningOutAll] = useState(false);
|
|
@@ -108,34 +109,44 @@ const AccountMenu: React.FC<AccountMenuProps> = ({
|
|
|
108
109
|
|
|
109
110
|
const containerRef = useRef<View | null>(null);
|
|
110
111
|
|
|
111
|
-
// Current account first, then the
|
|
112
|
-
// the Gmail-style chooser the inbox design ports
|
|
113
|
-
const rows = useMemo<
|
|
114
|
-
const
|
|
115
|
-
const
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
//
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
112
|
+
// Current account first, then the remaining device sign-ins, then the linked
|
|
113
|
+
// graph accounts — matching the Gmail-style chooser the inbox design ports.
|
|
114
|
+
const rows = useMemo<SwitchableAccount[]>(() => {
|
|
115
|
+
const device = accounts.filter((account) => account.onDevice);
|
|
116
|
+
const graph = accounts.filter((account) => !account.onDevice);
|
|
117
|
+
const current = device.filter((account) => account.isCurrent);
|
|
118
|
+
const others = device.filter((account) => !account.isCurrent);
|
|
119
|
+
return [...current, ...others, ...graph];
|
|
120
|
+
}, [accounts]);
|
|
121
|
+
|
|
122
|
+
// "Sign out of all accounts" only applies to real device sessions.
|
|
123
|
+
const deviceCount = useMemo(
|
|
124
|
+
() => accounts.filter((account) => account.onDevice).length,
|
|
125
|
+
[accounts],
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
// Account ids currently listed — used to indent a graph-only row whose parent
|
|
129
|
+
// account is also shown.
|
|
130
|
+
const listedAccountIds = useMemo(
|
|
131
|
+
() => new Set(rows.map((row) => row.accountId)),
|
|
132
|
+
[rows],
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const isSwitching = switchingAccountId !== null;
|
|
136
|
+
|
|
137
|
+
// The ONE uniform switch path: every row (device sign-in or linked graph
|
|
138
|
+
// account) routes through `useOxy().switchToAccount(accountId)`. It reuses
|
|
139
|
+
// the device session when the account is already signed in here, and mints a
|
|
140
|
+
// real session only on the first switch into a graph account — a single
|
|
141
|
+
// source of truth for every switch.
|
|
142
|
+
const handleSwitch = useCallback(async (account: SwitchableAccount) => {
|
|
143
|
+
if (account.isCurrent || switchingAccountId) {
|
|
133
144
|
return;
|
|
134
145
|
}
|
|
135
|
-
|
|
146
|
+
setSwitchingAccountId(account.accountId);
|
|
136
147
|
try {
|
|
137
148
|
await onBeforeSessionChange?.();
|
|
138
|
-
await
|
|
149
|
+
await switchToAccount(account.accountId);
|
|
139
150
|
toast.success(t('accountSwitcher.toasts.switchSuccess') || 'Switched account');
|
|
140
151
|
onClose();
|
|
141
152
|
} catch (error) {
|
|
@@ -144,9 +155,9 @@ const AccountMenu: React.FC<AccountMenuProps> = ({
|
|
|
144
155
|
}
|
|
145
156
|
toast.error(t('accountSwitcher.toasts.switchFailed') || 'Failed to switch account');
|
|
146
157
|
} finally {
|
|
147
|
-
|
|
158
|
+
setSwitchingAccountId(null);
|
|
148
159
|
}
|
|
149
|
-
}, [
|
|
160
|
+
}, [switchingAccountId, switchToAccount, t, onClose, onBeforeSessionChange]);
|
|
150
161
|
|
|
151
162
|
// Sign out a SPECIFIC inactive account from its per-row icon. `removeSession`
|
|
152
163
|
// is the SDK's canonical per-session sign-out: it targets the given session
|
|
@@ -154,7 +165,7 @@ const AccountMenu: React.FC<AccountMenuProps> = ({
|
|
|
154
165
|
// otherwise) and removes ONLY that account without switching/clearing the
|
|
155
166
|
// active session. The menu stays open so the user can keep managing accounts.
|
|
156
167
|
const handleRemove = useCallback(async (sessionId: string) => {
|
|
157
|
-
if (
|
|
168
|
+
if (removingSessionId) {
|
|
158
169
|
return;
|
|
159
170
|
}
|
|
160
171
|
setRemovingSessionId(sessionId);
|
|
@@ -167,7 +178,7 @@ const AccountMenu: React.FC<AccountMenuProps> = ({
|
|
|
167
178
|
} finally {
|
|
168
179
|
setRemovingSessionId(null);
|
|
169
180
|
}
|
|
170
|
-
}, [
|
|
181
|
+
}, [removingSessionId, removeSession, t]);
|
|
171
182
|
|
|
172
183
|
const performSignOut = useCallback(async () => {
|
|
173
184
|
if (signingOut) {
|
|
@@ -272,64 +283,81 @@ const AccountMenu: React.FC<AccountMenuProps> = ({
|
|
|
272
283
|
contentContainerStyle={styles.scrollContent}
|
|
273
284
|
showsVerticalScrollIndicator={false}
|
|
274
285
|
>
|
|
275
|
-
{/* 1) Accounts list — current first (checkmark), then
|
|
286
|
+
{/* 1) Accounts list — current first (checkmark), then the other
|
|
287
|
+
device sign-ins, then linked graph accounts (role badge,
|
|
288
|
+
indented under their parent). Every row switches the same way. */}
|
|
276
289
|
{rows.map((row) => {
|
|
277
|
-
const
|
|
278
|
-
const
|
|
290
|
+
const sessionId = row.sessionId;
|
|
291
|
+
const isBusy = switchingAccountId === row.accountId;
|
|
292
|
+
const isRemoving = sessionId ? removingSessionId === sessionId : false;
|
|
293
|
+
const isChild = Boolean(
|
|
294
|
+
row.parentAccountId && listedAccountIds.has(row.parentAccountId),
|
|
295
|
+
);
|
|
296
|
+
const role = row.callerMembership?.role;
|
|
279
297
|
return (
|
|
280
298
|
<TouchableOpacity
|
|
281
|
-
key={`account-${row.
|
|
299
|
+
key={`account-${row.accountId}`}
|
|
282
300
|
accessibilityRole="menuitem"
|
|
283
301
|
accessibilityLabel={row.displayName}
|
|
284
|
-
accessibilityState={{ selected: row.
|
|
285
|
-
onPress={() => handleSwitch(row
|
|
286
|
-
disabled={row.
|
|
302
|
+
accessibilityState={{ selected: row.isCurrent }}
|
|
303
|
+
onPress={() => handleSwitch(row)}
|
|
304
|
+
disabled={row.isCurrent || isBusy || isSwitching}
|
|
287
305
|
activeOpacity={0.6}
|
|
288
306
|
style={[
|
|
289
307
|
styles.accountRow,
|
|
290
|
-
|
|
291
|
-
|
|
308
|
+
isChild && styles.childRow,
|
|
309
|
+
row.isCurrent && { backgroundColor: colors.primarySubtle },
|
|
310
|
+
isSwitching && !row.isCurrent && styles.rowDisabled,
|
|
292
311
|
]}
|
|
293
312
|
>
|
|
294
313
|
<Avatar
|
|
295
|
-
uri={row.
|
|
314
|
+
uri={row.avatarUrl}
|
|
296
315
|
name={row.displayName}
|
|
297
|
-
size={row.
|
|
316
|
+
size={row.isCurrent ? 40 : isChild ? 28 : 32}
|
|
298
317
|
/>
|
|
299
318
|
<View style={styles.accountInfo}>
|
|
300
|
-
<
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
319
|
+
<View style={styles.nameRow}>
|
|
320
|
+
<Text
|
|
321
|
+
style={[
|
|
322
|
+
styles.accountName,
|
|
323
|
+
{ color: colors.text },
|
|
324
|
+
row.isCurrent && styles.accountNameActive,
|
|
325
|
+
]}
|
|
326
|
+
numberOfLines={1}
|
|
327
|
+
>
|
|
328
|
+
{row.displayName}
|
|
329
|
+
</Text>
|
|
330
|
+
{role ? (
|
|
331
|
+
<View style={[styles.roleBadge, { backgroundColor: colors.card }]}>
|
|
332
|
+
<Text style={[styles.roleBadgeText, { color: colors.textSecondary }]}>
|
|
333
|
+
{t(`accounts.roles.${role}.label`) || role}
|
|
334
|
+
</Text>
|
|
335
|
+
</View>
|
|
336
|
+
) : null}
|
|
337
|
+
</View>
|
|
338
|
+
{row.email ? (
|
|
311
339
|
<Text
|
|
312
340
|
style={[styles.accountEmail, { color: colors.textSecondary }]}
|
|
313
341
|
numberOfLines={1}
|
|
314
342
|
>
|
|
315
|
-
{row.
|
|
343
|
+
{row.email}
|
|
316
344
|
</Text>
|
|
317
345
|
) : null}
|
|
318
346
|
</View>
|
|
319
347
|
{isBusy ? (
|
|
320
348
|
<ActivityIndicator color={colors.primary} size="small" />
|
|
321
|
-
) : row.
|
|
349
|
+
) : row.isCurrent ? (
|
|
322
350
|
<Ionicons name="checkmark" size={20} color={colors.primary} />
|
|
323
351
|
) : isRemoving ? (
|
|
324
352
|
<ActivityIndicator color={colors.textSecondary} size="small" />
|
|
325
|
-
) : (
|
|
353
|
+
) : sessionId ? (
|
|
326
354
|
<TouchableOpacity
|
|
327
355
|
accessibilityRole="button"
|
|
328
356
|
accessibilityLabel={
|
|
329
357
|
t('accountMenu.signOutAccount', { name: row.displayName })
|
|
330
358
|
|| `Sign out ${row.displayName}`
|
|
331
359
|
}
|
|
332
|
-
onPress={() => handleRemove(
|
|
360
|
+
onPress={() => handleRemove(sessionId)}
|
|
333
361
|
disabled={isSwitching || removingSessionId !== null}
|
|
334
362
|
activeOpacity={0.6}
|
|
335
363
|
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
|
@@ -341,7 +369,7 @@ const AccountMenu: React.FC<AccountMenuProps> = ({
|
|
|
341
369
|
color={colors.textSecondary}
|
|
342
370
|
/>
|
|
343
371
|
</TouchableOpacity>
|
|
344
|
-
)}
|
|
372
|
+
) : null}
|
|
345
373
|
</TouchableOpacity>
|
|
346
374
|
);
|
|
347
375
|
})}
|
|
@@ -415,8 +443,8 @@ const AccountMenu: React.FC<AccountMenuProps> = ({
|
|
|
415
443
|
</Text>
|
|
416
444
|
</TouchableOpacity>
|
|
417
445
|
|
|
418
|
-
{/* 6) Sign out of all accounts (only when >1
|
|
419
|
-
{
|
|
446
|
+
{/* 6) Sign out of all accounts (only when >1 device sign-in). */}
|
|
447
|
+
{deviceCount > 1 ? (
|
|
420
448
|
<TouchableOpacity
|
|
421
449
|
accessibilityRole="menuitem"
|
|
422
450
|
accessibilityLabel={t('accountMenu.signOutAll') || 'Sign out of all accounts'}
|
|
@@ -545,6 +573,9 @@ const styles = StyleSheet.create({
|
|
|
545
573
|
paddingVertical: 10,
|
|
546
574
|
gap: 10,
|
|
547
575
|
},
|
|
576
|
+
childRow: {
|
|
577
|
+
paddingLeft: 34,
|
|
578
|
+
},
|
|
548
579
|
rowDisabled: {
|
|
549
580
|
opacity: 0.4,
|
|
550
581
|
},
|
|
@@ -552,13 +583,29 @@ const styles = StyleSheet.create({
|
|
|
552
583
|
flex: 1,
|
|
553
584
|
minWidth: 0,
|
|
554
585
|
},
|
|
586
|
+
nameRow: {
|
|
587
|
+
flexDirection: 'row',
|
|
588
|
+
alignItems: 'center',
|
|
589
|
+
gap: 6,
|
|
590
|
+
},
|
|
555
591
|
accountName: {
|
|
556
592
|
fontSize: 13,
|
|
557
593
|
fontWeight: '500',
|
|
594
|
+
flexShrink: 1,
|
|
558
595
|
},
|
|
559
596
|
accountNameActive: {
|
|
560
597
|
fontWeight: '600',
|
|
561
598
|
},
|
|
599
|
+
roleBadge: {
|
|
600
|
+
paddingHorizontal: 6,
|
|
601
|
+
paddingVertical: 1,
|
|
602
|
+
borderRadius: 8,
|
|
603
|
+
},
|
|
604
|
+
roleBadgeText: {
|
|
605
|
+
fontSize: 10,
|
|
606
|
+
fontWeight: '600',
|
|
607
|
+
textTransform: 'capitalize',
|
|
608
|
+
},
|
|
562
609
|
accountEmail: {
|
|
563
610
|
fontSize: 11,
|
|
564
611
|
marginTop: 1,
|