@oxyhq/services 16.0.0 → 17.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/index.js +21 -51
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/client.js +0 -14
- package/lib/commonjs/ui/client.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAccountDialog.js +764 -0
- package/lib/commonjs/ui/components/OxyAccountDialog.js.map +1 -0
- package/lib/commonjs/ui/components/OxyProvider.js +11 -6
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/OxySignInButton.js +10 -21
- package/lib/commonjs/ui/components/OxySignInButton.js.map +1 -1
- package/lib/commonjs/ui/components/ProfileButton.js +25 -112
- package/lib/commonjs/ui/components/ProfileButton.js.map +1 -1
- package/lib/commonjs/ui/components/RequireOxyAuth.js +299 -0
- package/lib/commonjs/ui/components/RequireOxyAuth.js.map +1 -0
- package/lib/commonjs/ui/context/OxyContext.js +106 -2
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/mutations/useAccountMutations.js +1 -1
- package/lib/commonjs/ui/hooks/useAuth.js +19 -30
- package/lib/commonjs/ui/hooks/useAuth.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js +37 -267
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js.map +1 -1
- package/lib/commonjs/ui/index.js +5 -40
- package/lib/commonjs/ui/index.js.map +1 -1
- package/lib/commonjs/ui/navigation/accountDialogManager.js +91 -0
- package/lib/commonjs/ui/navigation/accountDialogManager.js.map +1 -0
- package/lib/commonjs/ui/navigation/routes.js +0 -2
- package/lib/commonjs/ui/navigation/routes.js.map +1 -1
- package/lib/commonjs/ui/screens/ManageAccountScreen.js +4 -3
- package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/commonjs/ui/session/createSessionClient.js +10 -2
- package/lib/commonjs/ui/session/createSessionClient.js.map +1 -1
- package/lib/module/index.js +18 -15
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/client.js +1 -2
- package/lib/module/ui/client.js.map +1 -1
- package/lib/module/ui/components/OxyAccountDialog.js +761 -0
- package/lib/module/ui/components/OxyAccountDialog.js.map +1 -0
- package/lib/module/ui/components/OxyProvider.js +11 -6
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/OxySignInButton.js +10 -21
- package/lib/module/ui/components/OxySignInButton.js.map +1 -1
- package/lib/module/ui/components/ProfileButton.js +26 -112
- package/lib/module/ui/components/ProfileButton.js.map +1 -1
- package/lib/module/ui/components/RequireOxyAuth.js +294 -0
- package/lib/module/ui/components/RequireOxyAuth.js.map +1 -0
- package/lib/module/ui/context/OxyContext.js +107 -3
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/mutations/useAccountMutations.js +1 -1
- package/lib/module/ui/hooks/useAuth.js +19 -30
- package/lib/module/ui/hooks/useAuth.js.map +1 -1
- package/lib/module/ui/hooks/useSwitchableAccounts.js +38 -267
- package/lib/module/ui/hooks/useSwitchableAccounts.js.map +1 -1
- package/lib/module/ui/index.js +1 -5
- package/lib/module/ui/index.js.map +1 -1
- package/lib/module/ui/navigation/accountDialogManager.js +81 -0
- package/lib/module/ui/navigation/accountDialogManager.js.map +1 -0
- package/lib/module/ui/navigation/routes.js +0 -2
- package/lib/module/ui/navigation/routes.js.map +1 -1
- package/lib/module/ui/screens/ManageAccountScreen.js +4 -3
- package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/module/ui/session/createSessionClient.js +10 -2
- package/lib/module/ui/session/createSessionClient.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +4 -11
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/client.d.ts +0 -4
- package/lib/typescript/commonjs/ui/client.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAccountDialog.d.ts +35 -0
- package/lib/typescript/commonjs/ui/components/OxyAccountDialog.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts +2 -2
- package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxySignInButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts +11 -23
- package/lib/typescript/commonjs/ui/components/ProfileButton.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/RequireOxyAuth.d.ts +55 -0
- package/lib/typescript/commonjs/ui/components/RequireOxyAuth.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +13 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts +13 -152
- package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/index.d.ts +2 -5
- package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts +49 -0
- package/lib/typescript/commonjs/ui/navigation/accountDialogManager.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts +1 -1
- package/lib/typescript/commonjs/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts +1 -1
- package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts +9 -2
- package/lib/typescript/commonjs/ui/session/createSessionClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/types/navigation.d.ts +9 -0
- package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +4 -11
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/client.d.ts +0 -4
- package/lib/typescript/module/ui/client.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAccountDialog.d.ts +35 -0
- package/lib/typescript/module/ui/components/OxyAccountDialog.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/OxyProvider.d.ts +2 -2
- package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxySignInButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/ProfileButton.d.ts +11 -23
- package/lib/typescript/module/ui/components/ProfileButton.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/RequireOxyAuth.d.ts +55 -0
- package/lib/typescript/module/ui/components/RequireOxyAuth.d.ts.map +1 -0
- package/lib/typescript/module/ui/context/OxyContext.d.ts +13 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useAuth.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts +13 -152
- package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
- package/lib/typescript/module/ui/index.d.ts +2 -5
- package/lib/typescript/module/ui/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts +49 -0
- package/lib/typescript/module/ui/navigation/accountDialogManager.d.ts.map +1 -0
- package/lib/typescript/module/ui/navigation/routes.d.ts +1 -1
- package/lib/typescript/module/ui/navigation/routes.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts +1 -1
- package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/createSessionClient.d.ts +9 -2
- package/lib/typescript/module/ui/session/createSessionClient.d.ts.map +1 -1
- package/lib/typescript/module/ui/types/navigation.d.ts +9 -0
- package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +21 -26
- package/src/ui/client.ts +0 -4
- package/src/ui/components/OxyAccountDialog.tsx +760 -0
- package/src/ui/components/OxyProvider.tsx +12 -6
- package/src/ui/components/OxySignInButton.tsx +10 -21
- package/src/ui/components/ProfileButton.tsx +23 -126
- package/src/ui/components/RequireOxyAuth.tsx +266 -0
- package/src/ui/context/OxyContext.tsx +146 -7
- package/src/ui/hooks/mutations/useAccountMutations.ts +1 -1
- package/src/ui/hooks/useAuth.ts +19 -31
- package/src/ui/hooks/useSwitchableAccounts.ts +42 -376
- package/src/ui/index.ts +2 -5
- package/src/ui/navigation/accountDialogManager.ts +84 -0
- package/src/ui/navigation/routes.ts +0 -4
- package/src/ui/screens/ManageAccountScreen.tsx +3 -2
- package/src/ui/session/createSessionClient.ts +9 -1
- package/src/ui/types/navigation.ts +9 -0
- package/lib/commonjs/ui/components/AccountMenu.js +0 -598
- package/lib/commonjs/ui/components/AccountMenu.js.map +0 -1
- package/lib/commonjs/ui/components/AccountMenuButton.js +0 -135
- package/lib/commonjs/ui/components/AccountMenuButton.js.map +0 -1
- package/lib/commonjs/ui/components/AccountSwitcher.js +0 -780
- package/lib/commonjs/ui/components/AccountSwitcher.js.map +0 -1
- package/lib/commonjs/ui/components/AnotherDeviceQR.js +0 -116
- package/lib/commonjs/ui/components/AnotherDeviceQR.js.map +0 -1
- package/lib/commonjs/ui/components/ProfileMenu.js +0 -518
- package/lib/commonjs/ui/components/ProfileMenu.js.map +0 -1
- package/lib/commonjs/ui/components/SignInAccountChooser.js +0 -189
- package/lib/commonjs/ui/components/SignInAccountChooser.js.map +0 -1
- package/lib/commonjs/ui/components/SignInModal.js +0 -600
- package/lib/commonjs/ui/components/SignInModal.js.map +0 -1
- package/lib/commonjs/ui/hooks/useOxyAuthSession.js +0 -501
- package/lib/commonjs/ui/hooks/useOxyAuthSession.js.map +0 -1
- package/lib/commonjs/ui/hooks/usePasswordSignIn.js +0 -170
- package/lib/commonjs/ui/hooks/usePasswordSignIn.js.map +0 -1
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +0 -51
- package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +0 -1
- package/lib/commonjs/ui/screens/OxyAuthScreen.js +0 -312
- package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +0 -1
- package/lib/commonjs/utils/deviceFlowSignIn.js +0 -109
- package/lib/commonjs/utils/deviceFlowSignIn.js.map +0 -1
- package/lib/module/ui/components/AccountMenu.js +0 -593
- package/lib/module/ui/components/AccountMenu.js.map +0 -1
- package/lib/module/ui/components/AccountMenuButton.js +0 -130
- package/lib/module/ui/components/AccountMenuButton.js.map +0 -1
- package/lib/module/ui/components/AccountSwitcher.js +0 -774
- package/lib/module/ui/components/AccountSwitcher.js.map +0 -1
- package/lib/module/ui/components/AnotherDeviceQR.js +0 -110
- package/lib/module/ui/components/AnotherDeviceQR.js.map +0 -1
- package/lib/module/ui/components/ProfileMenu.js +0 -514
- package/lib/module/ui/components/ProfileMenu.js.map +0 -1
- package/lib/module/ui/components/SignInAccountChooser.js +0 -183
- package/lib/module/ui/components/SignInAccountChooser.js.map +0 -1
- package/lib/module/ui/components/SignInModal.js +0 -591
- package/lib/module/ui/components/SignInModal.js.map +0 -1
- package/lib/module/ui/hooks/useOxyAuthSession.js +0 -495
- package/lib/module/ui/hooks/useOxyAuthSession.js.map +0 -1
- package/lib/module/ui/hooks/usePasswordSignIn.js +0 -165
- package/lib/module/ui/hooks/usePasswordSignIn.js.map +0 -1
- package/lib/module/ui/screens/AccountSwitcherScreen.js +0 -48
- package/lib/module/ui/screens/AccountSwitcherScreen.js.map +0 -1
- package/lib/module/ui/screens/OxyAuthScreen.js +0 -306
- package/lib/module/ui/screens/OxyAuthScreen.js.map +0 -1
- package/lib/module/utils/deviceFlowSignIn.js +0 -106
- package/lib/module/utils/deviceFlowSignIn.js.map +0 -1
- package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts +0 -47
- package/lib/typescript/commonjs/ui/components/AccountMenu.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/components/AccountMenuButton.d.ts +0 -26
- package/lib/typescript/commonjs/ui/components/AccountMenuButton.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/components/AccountSwitcher.d.ts +0 -46
- package/lib/typescript/commonjs/ui/components/AccountSwitcher.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/components/AnotherDeviceQR.d.ts +0 -34
- package/lib/typescript/commonjs/ui/components/AnotherDeviceQR.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/components/ProfileMenu.d.ts +0 -49
- package/lib/typescript/commonjs/ui/components/ProfileMenu.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/components/SignInAccountChooser.d.ts +0 -29
- package/lib/typescript/commonjs/ui/components/SignInAccountChooser.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/components/SignInModal.d.ts +0 -28
- package/lib/typescript/commonjs/ui/components/SignInModal.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/hooks/useOxyAuthSession.d.ts +0 -105
- package/lib/typescript/commonjs/ui/hooks/useOxyAuthSession.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/hooks/usePasswordSignIn.d.ts +0 -31
- package/lib/typescript/commonjs/ui/hooks/usePasswordSignIn.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/screens/AccountSwitcherScreen.d.ts +0 -11
- package/lib/typescript/commonjs/ui/screens/AccountSwitcherScreen.d.ts.map +0 -1
- package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts +0 -22
- package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts.map +0 -1
- package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts +0 -97
- package/lib/typescript/commonjs/utils/deviceFlowSignIn.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/AccountMenu.d.ts +0 -47
- package/lib/typescript/module/ui/components/AccountMenu.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/AccountMenuButton.d.ts +0 -26
- package/lib/typescript/module/ui/components/AccountMenuButton.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/AccountSwitcher.d.ts +0 -46
- package/lib/typescript/module/ui/components/AccountSwitcher.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/AnotherDeviceQR.d.ts +0 -34
- package/lib/typescript/module/ui/components/AnotherDeviceQR.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/ProfileMenu.d.ts +0 -49
- package/lib/typescript/module/ui/components/ProfileMenu.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/SignInAccountChooser.d.ts +0 -29
- package/lib/typescript/module/ui/components/SignInAccountChooser.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/SignInModal.d.ts +0 -28
- package/lib/typescript/module/ui/components/SignInModal.d.ts.map +0 -1
- package/lib/typescript/module/ui/hooks/useOxyAuthSession.d.ts +0 -105
- package/lib/typescript/module/ui/hooks/useOxyAuthSession.d.ts.map +0 -1
- package/lib/typescript/module/ui/hooks/usePasswordSignIn.d.ts +0 -31
- package/lib/typescript/module/ui/hooks/usePasswordSignIn.d.ts.map +0 -1
- package/lib/typescript/module/ui/screens/AccountSwitcherScreen.d.ts +0 -11
- package/lib/typescript/module/ui/screens/AccountSwitcherScreen.d.ts.map +0 -1
- package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts +0 -22
- package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts.map +0 -1
- package/lib/typescript/module/utils/deviceFlowSignIn.d.ts +0 -97
- package/lib/typescript/module/utils/deviceFlowSignIn.d.ts.map +0 -1
- package/src/ui/components/AccountMenu.tsx +0 -645
- package/src/ui/components/AccountMenuButton.tsx +0 -126
- package/src/ui/components/AccountSwitcher.tsx +0 -751
- package/src/ui/components/AnotherDeviceQR.tsx +0 -119
- package/src/ui/components/ProfileMenu.tsx +0 -564
- package/src/ui/components/SignInAccountChooser.tsx +0 -162
- package/src/ui/components/SignInModal.tsx +0 -530
- package/src/ui/hooks/useOxyAuthSession.ts +0 -635
- package/src/ui/hooks/usePasswordSignIn.ts +0 -207
- package/src/ui/screens/AccountSwitcherScreen.tsx +0 -43
- package/src/ui/screens/OxyAuthScreen.tsx +0 -324
- package/src/utils/__tests__/deviceFlowSignIn.test.ts +0 -231
- package/src/utils/deviceFlowSignIn.ts +0 -152
|
@@ -9,6 +9,7 @@ import { BloomDialogProvider } from '@oxyhq/bloom';
|
|
|
9
9
|
import { ToastOutlet } from '@oxyhq/bloom/toast';
|
|
10
10
|
import { logger as loggerUtil } from '@oxyhq/core';
|
|
11
11
|
import { setupFonts } from './FontLoader';
|
|
12
|
+
import { RequireOxyAuth } from './RequireOxyAuth';
|
|
12
13
|
import { attachQueryPersistence, createQueryClient } from '../hooks/queryClient';
|
|
13
14
|
import { createPlatformStorage, type StorageInterface } from '../utils/storageHelpers';
|
|
14
15
|
|
|
@@ -63,8 +64,8 @@ const LazyBottomSheetRouter = lazy((): Promise<{ default: ComponentType }> =>
|
|
|
63
64
|
),
|
|
64
65
|
);
|
|
65
66
|
|
|
66
|
-
const
|
|
67
|
-
import('./
|
|
67
|
+
const LazyOxyAccountDialog = lazy((): Promise<{ default: ComponentType }> =>
|
|
68
|
+
import('./OxyAccountDialog').then(
|
|
68
69
|
(mod) => ({ default: mod.default as unknown as ComponentType }),
|
|
69
70
|
() => ({ default: (() => null) as FC }),
|
|
70
71
|
),
|
|
@@ -75,8 +76,8 @@ const LazySignInModal = lazy((): Promise<{ default: ComponentType }> =>
|
|
|
75
76
|
*
|
|
76
77
|
* Provides authentication, session management, query client, and UI overlays.
|
|
77
78
|
* Wraps its own overlay stack in SafeAreaProvider and GestureHandlerRootView so
|
|
78
|
-
* BottomSheetRouter and
|
|
79
|
-
* has not mounted those providers yet.
|
|
79
|
+
* BottomSheetRouter and OxyAccountDialog can safely render even when a consuming
|
|
80
|
+
* app has not mounted those providers yet.
|
|
80
81
|
*
|
|
81
82
|
* Usage:
|
|
82
83
|
* ```tsx
|
|
@@ -114,6 +115,7 @@ const OxyProvider: FC<OxyProviderProps> = ({
|
|
|
114
115
|
authWebUrl,
|
|
115
116
|
authRedirectUri,
|
|
116
117
|
queryClient: providedQueryClient,
|
|
118
|
+
requireAuth = 'off',
|
|
117
119
|
}) => {
|
|
118
120
|
|
|
119
121
|
// Dynamic KeyboardProvider for native. Uses variable indirection
|
|
@@ -310,10 +312,14 @@ const OxyProvider: FC<OxyProviderProps> = ({
|
|
|
310
312
|
clientId={clientId}
|
|
311
313
|
onAuthStateChange={onAuthStateChange as OxyContextProviderProps['onAuthStateChange']}
|
|
312
314
|
>
|
|
313
|
-
{
|
|
315
|
+
{requireAuth === 'off' ? (
|
|
316
|
+
children
|
|
317
|
+
) : (
|
|
318
|
+
<RequireOxyAuth prompt={requireAuth}>{children}</RequireOxyAuth>
|
|
319
|
+
)}
|
|
314
320
|
<Suspense fallback={null}>
|
|
315
321
|
<LazyBottomSheetRouter />
|
|
316
|
-
<
|
|
322
|
+
<LazyOxyAccountDialog />
|
|
317
323
|
</Suspense>
|
|
318
324
|
<ToastOutlet />
|
|
319
325
|
</OxyContextProvider>
|
|
@@ -6,7 +6,7 @@ import { useShallow } from 'zustand/react/shallow';
|
|
|
6
6
|
import { useTheme } from '@oxyhq/bloom/theme';
|
|
7
7
|
import { useOxy } from '../context/OxyContext';
|
|
8
8
|
import OxyLogo from './OxyLogo';
|
|
9
|
-
import {
|
|
9
|
+
import { subscribeToSignInModal } from '../navigation/accountDialogManager';
|
|
10
10
|
|
|
11
11
|
export interface OxySignInButtonProps {
|
|
12
12
|
/**
|
|
@@ -84,37 +84,26 @@ export const OxySignInButton: React.FC<OxySignInButtonProps> = ({
|
|
|
84
84
|
showWhenAuthenticated = false,
|
|
85
85
|
}) => {
|
|
86
86
|
const theme = useTheme();
|
|
87
|
-
const {
|
|
87
|
+
const { openAccountDialog } = useOxy();
|
|
88
88
|
const { isAuthenticated, isLoading } = useAuthStore(
|
|
89
89
|
useShallow((state) => ({ isAuthenticated: state.isAuthenticated, isLoading: state.isLoading }))
|
|
90
90
|
);
|
|
91
|
-
// Tracks
|
|
92
|
-
// "Signing in..." while it
|
|
93
|
-
//
|
|
91
|
+
// Tracks whether the unified account dialog is open so we can show
|
|
92
|
+
// "Signing in..." while it is. The manager reports visibility on every
|
|
93
|
+
// change regardless of platform or what opened/closed it.
|
|
94
94
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
useEffect(() => {
|
|
98
|
-
if (Platform.OS !== 'web') return;
|
|
99
|
-
return subscribeToSignInModal(setIsModalOpen);
|
|
100
|
-
}, []);
|
|
96
|
+
useEffect(() => subscribeToSignInModal(setIsModalOpen), []);
|
|
101
97
|
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
// - Native: bottom sheet (sheet UX fits iOS/Android)
|
|
98
|
+
// Open the unified account dialog on its sign-in view (same surface on web +
|
|
99
|
+
// native), or defer to a caller-supplied handler.
|
|
105
100
|
const handlePress = useCallback(() => {
|
|
106
101
|
if (onPress) {
|
|
107
102
|
onPress();
|
|
108
103
|
return;
|
|
109
104
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
setIsModalOpen(true);
|
|
113
|
-
showSignInModal();
|
|
114
|
-
} else {
|
|
115
|
-
showBottomSheet?.('OxyAuth');
|
|
116
|
-
}
|
|
117
|
-
}, [onPress, showBottomSheet]);
|
|
105
|
+
openAccountDialog('signin');
|
|
106
|
+
}, [onPress, openAccountDialog]);
|
|
118
107
|
|
|
119
108
|
const themedStyles = useMemo(() => StyleSheet.create({
|
|
120
109
|
button: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type React from 'react';
|
|
2
|
-
import { useCallback,
|
|
2
|
+
import { useCallback, useState } from 'react';
|
|
3
3
|
import {
|
|
4
4
|
View,
|
|
5
5
|
Pressable,
|
|
@@ -14,21 +14,11 @@ import { Text } from '@oxyhq/bloom/typography';
|
|
|
14
14
|
import { useTheme } from '@oxyhq/bloom/theme';
|
|
15
15
|
import { getAccountDisplayName, getAccountFallbackHandle } from '@oxyhq/core';
|
|
16
16
|
import { useAuth } from '../hooks/useAuth';
|
|
17
|
+
import { useOxy } from '../context/OxyContext';
|
|
17
18
|
import { useI18n } from '../hooks/useI18n';
|
|
18
|
-
import ProfileMenu, { type ProfileMenuAnchor } from './ProfileMenu';
|
|
19
19
|
|
|
20
20
|
const isWeb = Platform.OS === 'web';
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
* Fixed popover width used by {@link ProfileMenu} on web. `ProfileButton`
|
|
24
|
-
* mirrors this constant when computing the trigger-relative anchor so the
|
|
25
|
-
* left-edge clamp keeps the panel fully on-screen. Keep the two in sync.
|
|
26
|
-
*/
|
|
27
|
-
const MENU_WIDTH = 300;
|
|
28
|
-
|
|
29
|
-
/** Gutter, in px, kept between the popover and the viewport edges on web. */
|
|
30
|
-
const VIEWPORT_GUTTER = 8;
|
|
31
|
-
|
|
32
22
|
/**
|
|
33
23
|
* Web-only Bluesky-style hover animation timings, mirrored from
|
|
34
24
|
* `social-app/src/view/shell/desktop/LeftNav.tsx`. The row background and the
|
|
@@ -67,23 +57,15 @@ export interface ProfileButtonProps {
|
|
|
67
57
|
*/
|
|
68
58
|
avatarSize?: number;
|
|
69
59
|
/** Navigate to the "Manage account" surface (settings). */
|
|
70
|
-
onNavigateManage
|
|
60
|
+
onNavigateManage?: () => void;
|
|
71
61
|
/** Start the add-account / sign-in flow for an additional account. */
|
|
72
|
-
onAddAccount
|
|
62
|
+
onAddAccount?: () => void;
|
|
73
63
|
/** Optional: navigate to the signed-in user's own profile. */
|
|
74
64
|
onNavigateProfile?: () => void;
|
|
75
|
-
/** Called before the active identity changes so apps can clear scoped state. */
|
|
76
|
-
onBeforeSessionChange?: () => void | Promise<void>;
|
|
77
65
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* behavior.
|
|
82
|
-
* - `'down'`: the menu opens DOWNWARD, for a trigger placed at the TOP of a
|
|
83
|
-
* sidebar (the accounts app pattern).
|
|
84
|
-
* - `'auto'`: opens downward when there is more room below the trigger than
|
|
85
|
-
* above, otherwise upward.
|
|
86
|
-
* Native (bottom-sheet) is unaffected — this only influences the web popover.
|
|
66
|
+
* Retained for source compatibility. The trigger now opens the unified
|
|
67
|
+
* `OxyAccountDialog` (a centered / bottom-sheet modal) rather than an
|
|
68
|
+
* anchored popover, so popover placement no longer applies.
|
|
87
69
|
*/
|
|
88
70
|
placement?: 'up' | 'down' | 'auto';
|
|
89
71
|
/**
|
|
@@ -98,36 +80,27 @@ export interface ProfileButtonProps {
|
|
|
98
80
|
|
|
99
81
|
/**
|
|
100
82
|
* Self-contained sidebar account trigger, modeled on Bluesky's ProfileCard and
|
|
101
|
-
* the Oxy inbox's `MailboxDrawer` footer.
|
|
102
|
-
*
|
|
83
|
+
* the Oxy inbox's `MailboxDrawer` footer. Pressing it opens the unified
|
|
84
|
+
* {@link OxyAccountDialog} — the single account switcher + sign-in surface — via
|
|
85
|
+
* `useOxy().openAccountDialog`.
|
|
103
86
|
*
|
|
104
87
|
* Three auth states from {@link useAuth}:
|
|
105
88
|
* - **Undetermined** (`!isAuthResolved || isPrivateApiPending`): a neutral
|
|
106
89
|
* avatar-sized skeleton circle, no press.
|
|
107
90
|
* - **Signed in**: a pressable row — Bloom `Avatar` + display name + `@handle`
|
|
108
|
-
* +
|
|
109
|
-
*
|
|
110
|
-
*
|
|
91
|
+
* + a chevron. Press opens the dialog on its `accounts` view.
|
|
92
|
+
* - **Signed out**: a "Sign in" row that calls `useAuth().signIn()` (which opens
|
|
93
|
+
* the dialog on its `signin` view).
|
|
111
94
|
*
|
|
112
95
|
* Styling uses react-native `StyleSheet` + the Bloom theme (via `useTheme`) so
|
|
113
96
|
* the layout renders identically in EVERY consumer — including apps that do not
|
|
114
97
|
* use NativeWind (e.g. the accounts app). Only the web hover animation keeps
|
|
115
98
|
* dynamic inline `style` (the CSS transition/transform values), which is what
|
|
116
99
|
* the `react-native-web-style.d.ts` augmentation exists for.
|
|
117
|
-
*
|
|
118
|
-
* All display strings resolve through `@oxyhq/core`'s
|
|
119
|
-
* `getAccountDisplayName` / `getAccountFallbackHandle` — no hand-rolled name
|
|
120
|
-
* fallbacks. Avatars pass the bare file id as `source` + `variant="thumb"`, so
|
|
121
|
-
* the app's registered `ImageResolver` builds the thumbnail URL.
|
|
122
100
|
*/
|
|
123
101
|
const ProfileButton: React.FC<ProfileButtonProps> = ({
|
|
124
102
|
expanded = true,
|
|
125
103
|
avatarSize,
|
|
126
|
-
onNavigateManage,
|
|
127
|
-
onAddAccount,
|
|
128
|
-
onNavigateProfile,
|
|
129
|
-
onBeforeSessionChange,
|
|
130
|
-
placement = 'up',
|
|
131
104
|
className,
|
|
132
105
|
style,
|
|
133
106
|
}) => {
|
|
@@ -138,76 +111,20 @@ const ProfileButton: React.FC<ProfileButtonProps> = ({
|
|
|
138
111
|
isPrivateApiPending,
|
|
139
112
|
signIn,
|
|
140
113
|
} = useAuth();
|
|
114
|
+
const { openAccountDialog } = useOxy();
|
|
141
115
|
const { colors } = useTheme();
|
|
142
116
|
const { t, locale } = useI18n();
|
|
143
117
|
|
|
144
|
-
const [menuOpen, setMenuOpen] = useState(false);
|
|
145
|
-
const [anchor, setAnchor] = useState<ProfileMenuAnchor | null>(null);
|
|
146
|
-
|
|
147
118
|
// Web-only hover/focus tracking for the Bluesky-style reveal animation.
|
|
148
119
|
// Native has no hover, so these stay false and the row renders statically.
|
|
149
120
|
const [hovered, setHovered] = useState(false);
|
|
150
121
|
const [focused, setFocused] = useState(false);
|
|
151
122
|
|
|
152
|
-
// Trigger ref for the web anchor measurement. RN-Web exposes `measure()` on
|
|
153
|
-
// host views, so we anchor the popover to the button rect and open upward.
|
|
154
|
-
const triggerRef = useRef<View | null>(null);
|
|
155
|
-
|
|
156
123
|
const resolvedAvatarSize = avatarSize ?? (expanded ? 40 : 32);
|
|
157
124
|
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
setMenuOpen(true);
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
const node = triggerRef.current;
|
|
165
|
-
if (!node || typeof node.measure !== 'function' || typeof window === 'undefined') {
|
|
166
|
-
setAnchor(null);
|
|
167
|
-
setMenuOpen(true);
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
node.measure((_x, _y, _w, height, pageX, pageY) => {
|
|
171
|
-
if (typeof pageX !== 'number' || typeof pageY !== 'number') {
|
|
172
|
-
setAnchor(null);
|
|
173
|
-
} else {
|
|
174
|
-
// Clamp the panel's left edge inside the viewport gutter.
|
|
175
|
-
const maxLeft = Math.max(
|
|
176
|
-
VIEWPORT_GUTTER,
|
|
177
|
-
window.innerWidth - MENU_WIDTH - VIEWPORT_GUTTER,
|
|
178
|
-
);
|
|
179
|
-
const left = Math.min(Math.max(VIEWPORT_GUTTER, pageX), maxLeft);
|
|
180
|
-
// Bottom edge of the trigger in page coordinates. `height` is a
|
|
181
|
-
// number for host views; guard defensively for non-web shims.
|
|
182
|
-
const triggerBottom = pageY + (typeof height === 'number' ? height : 0);
|
|
183
|
-
// Resolve `'auto'` by comparing the room below the trigger with
|
|
184
|
-
// the room above it; pick whichever direction has more space.
|
|
185
|
-
const openDown =
|
|
186
|
-
placement === 'down' ||
|
|
187
|
-
(placement === 'auto' &&
|
|
188
|
-
window.innerHeight - triggerBottom > pageY);
|
|
189
|
-
if (openDown) {
|
|
190
|
-
// Anchor the panel's TOP edge just below the trigger bottom so
|
|
191
|
-
// the menu opens downward from a top-of-sidebar trigger.
|
|
192
|
-
const top = Math.max(VIEWPORT_GUTTER, triggerBottom + VIEWPORT_GUTTER);
|
|
193
|
-
setAnchor({ left, top });
|
|
194
|
-
} else {
|
|
195
|
-
// Anchor the panel's BOTTOM edge just above the trigger top so
|
|
196
|
-
// the menu opens upward from this footer button.
|
|
197
|
-
const bottom = Math.max(
|
|
198
|
-
VIEWPORT_GUTTER,
|
|
199
|
-
window.innerHeight - pageY + VIEWPORT_GUTTER,
|
|
200
|
-
);
|
|
201
|
-
setAnchor({ left, bottom });
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
setMenuOpen(true);
|
|
205
|
-
});
|
|
206
|
-
}, [placement]);
|
|
207
|
-
|
|
208
|
-
const handleClose = useCallback(() => {
|
|
209
|
-
setMenuOpen(false);
|
|
210
|
-
}, []);
|
|
125
|
+
const openDialog = useCallback(() => {
|
|
126
|
+
openAccountDialog('accounts');
|
|
127
|
+
}, [openAccountDialog]);
|
|
211
128
|
|
|
212
129
|
// ── Undetermined: skeleton circle, no interaction. ──────────────────────
|
|
213
130
|
if (!isAuthResolved || isPrivateApiPending) {
|
|
@@ -285,10 +202,10 @@ const ProfileButton: React.FC<ProfileButtonProps> = ({
|
|
|
285
202
|
/>
|
|
286
203
|
);
|
|
287
204
|
|
|
288
|
-
// `active` combines hover
|
|
289
|
-
//
|
|
290
|
-
//
|
|
291
|
-
const active = hovered || focused
|
|
205
|
+
// `active` combines hover and keyboard focus — exactly like Bluesky's
|
|
206
|
+
// `state.hovered || state.focused`. Only web ever sets these, so on native
|
|
207
|
+
// the row renders statically.
|
|
208
|
+
const active = hovered || focused;
|
|
292
209
|
const accountLabel = t('accountSwitcher.switchWhileSignedInAs', { name: displayName })
|
|
293
210
|
|| `Switch account, signed in as ${displayName}`;
|
|
294
211
|
|
|
@@ -318,24 +235,14 @@ const ProfileButton: React.FC<ProfileButtonProps> = ({
|
|
|
318
235
|
return (
|
|
319
236
|
<View className={className} style={style}>
|
|
320
237
|
<Pressable
|
|
321
|
-
ref={triggerRef}
|
|
322
238
|
style={[styles.collapsedTrigger, collapsedBgStyle]}
|
|
323
|
-
onPress={
|
|
239
|
+
onPress={openDialog}
|
|
324
240
|
accessibilityRole="button"
|
|
325
241
|
accessibilityLabel={accountLabel}
|
|
326
242
|
{...webInteractionProps}
|
|
327
243
|
>
|
|
328
244
|
{avatarNode}
|
|
329
245
|
</Pressable>
|
|
330
|
-
<ProfileMenu
|
|
331
|
-
open={menuOpen}
|
|
332
|
-
onClose={handleClose}
|
|
333
|
-
anchor={anchor}
|
|
334
|
-
onNavigateManage={onNavigateManage}
|
|
335
|
-
onAddAccount={onAddAccount}
|
|
336
|
-
onNavigateProfile={onNavigateProfile}
|
|
337
|
-
onBeforeSessionChange={onBeforeSessionChange}
|
|
338
|
-
/>
|
|
339
246
|
</View>
|
|
340
247
|
);
|
|
341
248
|
}
|
|
@@ -402,9 +309,8 @@ const ProfileButton: React.FC<ProfileButtonProps> = ({
|
|
|
402
309
|
return (
|
|
403
310
|
<View className={className} style={[styles.fullWidth, style]}>
|
|
404
311
|
<Pressable
|
|
405
|
-
ref={triggerRef}
|
|
406
312
|
style={[styles.row, styles.rowExpanded, rowStyle]}
|
|
407
|
-
onPress={
|
|
313
|
+
onPress={openDialog}
|
|
408
314
|
accessibilityRole="button"
|
|
409
315
|
accessibilityLabel={accountLabel}
|
|
410
316
|
{...webInteractionProps}
|
|
@@ -434,15 +340,6 @@ const ProfileButton: React.FC<ProfileButtonProps> = ({
|
|
|
434
340
|
/>
|
|
435
341
|
</View>
|
|
436
342
|
</Pressable>
|
|
437
|
-
<ProfileMenu
|
|
438
|
-
open={menuOpen}
|
|
439
|
-
onClose={handleClose}
|
|
440
|
-
anchor={anchor}
|
|
441
|
-
onNavigateManage={onNavigateManage}
|
|
442
|
-
onAddAccount={onAddAccount}
|
|
443
|
-
onNavigateProfile={onNavigateProfile}
|
|
444
|
-
onBeforeSessionChange={onBeforeSessionChange}
|
|
445
|
-
/>
|
|
446
343
|
</View>
|
|
447
344
|
);
|
|
448
345
|
};
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RequireOxyAuth — the optional signed-out gate primitive (React Native).
|
|
3
|
+
*
|
|
4
|
+
* ONE shared way for any Oxy app to opt into a signed-out gate. It is entirely
|
|
5
|
+
* OPTIONAL: a public app renders its children untouched (`prompt="off"`); an
|
|
6
|
+
* auth-required app blocks them behind a signed-out state (`prompt="hard"`); an
|
|
7
|
+
* app that mostly works signed-out but wants a nudge shows a dismissible banner
|
|
8
|
+
* (`prompt="soft"`). Every mode reuses the ONE account dialog the provider
|
|
9
|
+
* already mounts — opening the sign-in surface is always
|
|
10
|
+
* `useOxy().openAccountDialog('signin')`; there is NO second dialog.
|
|
11
|
+
*
|
|
12
|
+
* Readiness gating (CRITICAL): the gate keys on the SDK's own readiness state
|
|
13
|
+
* (`useOxy().canUsePrivateApi` / `isPrivateApiPending`), NEVER on app-local
|
|
14
|
+
* hooks. While auth is still resolving (`isPrivateApiPending`) it renders a
|
|
15
|
+
* neutral loading state so the signed-out wall never flashes before the
|
|
16
|
+
* device-first cold boot concludes (the documented cold-boot race).
|
|
17
|
+
*
|
|
18
|
+
* Styling follows the sibling `OxyAccountDialog`/`OxySignInButton` pattern —
|
|
19
|
+
* `useTheme()` + `StyleSheet` (NOT NativeWind), so the gate renders correctly in
|
|
20
|
+
* EVERY consumer, including apps that do not run NativeWind (e.g. the accounts
|
|
21
|
+
* app).
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type React from 'react';
|
|
25
|
+
import { useState, type ReactNode } from 'react';
|
|
26
|
+
import { ActivityIndicator, Pressable, StyleSheet, View } from 'react-native';
|
|
27
|
+
import { useTheme } from '@oxyhq/bloom/theme';
|
|
28
|
+
import { Text } from '@oxyhq/bloom/typography';
|
|
29
|
+
import { useOxy } from '../context/OxyContext';
|
|
30
|
+
import { OxySignInButton } from './OxySignInButton';
|
|
31
|
+
import OxyLogo from './OxyLogo';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* How `RequireOxyAuth` treats a signed-out (or still-resolving) session.
|
|
35
|
+
* - `off` — always render children (public app; a no-op provided for symmetry).
|
|
36
|
+
* - `soft` — render children, plus a dismissible sign-in banner when signed out.
|
|
37
|
+
* - `hard` — block children behind a centered signed-out state until signed in.
|
|
38
|
+
*/
|
|
39
|
+
export type RequireOxyAuthPrompt = 'off' | 'soft' | 'hard';
|
|
40
|
+
|
|
41
|
+
export interface RequireOxyAuthProps {
|
|
42
|
+
children: ReactNode;
|
|
43
|
+
/** Gate behavior. @default 'hard' */
|
|
44
|
+
prompt?: RequireOxyAuthPrompt;
|
|
45
|
+
/** Replaces the neutral loading UI shown while auth is still resolving. */
|
|
46
|
+
loadingFallback?: ReactNode;
|
|
47
|
+
/** Replaces the entire default signed-out wall (`prompt="hard"`). */
|
|
48
|
+
signedOutFallback?: ReactNode;
|
|
49
|
+
/** Title for the default `prompt="hard"` wall. */
|
|
50
|
+
title?: string;
|
|
51
|
+
/** Subtitle for the default `prompt="hard"` wall. */
|
|
52
|
+
subtitle?: string;
|
|
53
|
+
/** Message for the `prompt="soft"` banner. */
|
|
54
|
+
bannerMessage?: string;
|
|
55
|
+
/** CTA label for the `prompt="soft"` banner. */
|
|
56
|
+
bannerActionLabel?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const DEFAULT_TITLE = 'Sign in to continue';
|
|
60
|
+
const DEFAULT_SUBTITLE = 'Sign in with your Oxy account to continue.';
|
|
61
|
+
const DEFAULT_BANNER_MESSAGE = "You're browsing signed out.";
|
|
62
|
+
const DEFAULT_BANNER_ACTION = 'Sign in';
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Optional signed-out gate. Wrap any subtree (or the whole app via the
|
|
66
|
+
* provider's `requireAuth` prop) to opt into a shared, readiness-safe wall.
|
|
67
|
+
*/
|
|
68
|
+
export const RequireOxyAuth: React.FC<RequireOxyAuthProps> = ({
|
|
69
|
+
children,
|
|
70
|
+
prompt = 'hard',
|
|
71
|
+
loadingFallback,
|
|
72
|
+
signedOutFallback,
|
|
73
|
+
title,
|
|
74
|
+
subtitle,
|
|
75
|
+
bannerMessage,
|
|
76
|
+
bannerActionLabel,
|
|
77
|
+
}) => {
|
|
78
|
+
const { canUsePrivateApi, isPrivateApiPending, openAccountDialog } = useOxy();
|
|
79
|
+
|
|
80
|
+
// Public app: render straight through. Cheap enough to short-circuit before
|
|
81
|
+
// touching any gate UI.
|
|
82
|
+
if (prompt === 'off') {
|
|
83
|
+
return <>{children}</>;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Signed in (and token ready): render the protected subtree in every mode.
|
|
87
|
+
if (canUsePrivateApi) {
|
|
88
|
+
return <>{children}</>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (prompt === 'soft') {
|
|
92
|
+
return (
|
|
93
|
+
<SoftGate
|
|
94
|
+
pending={isPrivateApiPending}
|
|
95
|
+
message={bannerMessage ?? DEFAULT_BANNER_MESSAGE}
|
|
96
|
+
actionLabel={bannerActionLabel ?? DEFAULT_BANNER_ACTION}
|
|
97
|
+
onSignIn={() => openAccountDialog('signin')}
|
|
98
|
+
>
|
|
99
|
+
{children}
|
|
100
|
+
</SoftGate>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// prompt === 'hard'
|
|
105
|
+
if (isPrivateApiPending) {
|
|
106
|
+
return loadingFallback ? <>{loadingFallback}</> : <NeutralLoading />;
|
|
107
|
+
}
|
|
108
|
+
if (signedOutFallback) {
|
|
109
|
+
return <>{signedOutFallback}</>;
|
|
110
|
+
}
|
|
111
|
+
return <HardWall title={title ?? DEFAULT_TITLE} subtitle={subtitle ?? DEFAULT_SUBTITLE} />;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// ---------------------------------------------------------------------------
|
|
115
|
+
// Soft gate — children + a dismissible sign-in banner while signed out.
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
interface SoftGateProps {
|
|
119
|
+
children: ReactNode;
|
|
120
|
+
pending: boolean;
|
|
121
|
+
message: string;
|
|
122
|
+
actionLabel: string;
|
|
123
|
+
onSignIn: () => void;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const SoftGate: React.FC<SoftGateProps> = ({ children, pending, message, actionLabel, onSignIn }) => {
|
|
127
|
+
const theme = useTheme();
|
|
128
|
+
const [dismissed, setDismissed] = useState(false);
|
|
129
|
+
|
|
130
|
+
// Never surface the nudge while auth is still resolving — only once the boot
|
|
131
|
+
// has concluded signed out and the user has not dismissed it.
|
|
132
|
+
const showBanner = !pending && !dismissed;
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<View style={styles.softRoot}>
|
|
136
|
+
{showBanner ? (
|
|
137
|
+
<View style={[styles.banner, { backgroundColor: theme.colors.card, borderColor: theme.colors.border }]}>
|
|
138
|
+
<OxyLogo variant="icon" size={20} fillColor={theme.colors.primary} />
|
|
139
|
+
<Text style={[styles.bannerText, { color: theme.colors.text }]} numberOfLines={2}>
|
|
140
|
+
{message}
|
|
141
|
+
</Text>
|
|
142
|
+
<Pressable
|
|
143
|
+
onPress={onSignIn}
|
|
144
|
+
style={[styles.bannerCta, { backgroundColor: theme.colors.primary }]}
|
|
145
|
+
accessibilityRole="button"
|
|
146
|
+
accessibilityLabel={actionLabel}
|
|
147
|
+
>
|
|
148
|
+
<Text style={styles.bannerCtaText}>{actionLabel}</Text>
|
|
149
|
+
</Pressable>
|
|
150
|
+
<Pressable
|
|
151
|
+
onPress={() => setDismissed(true)}
|
|
152
|
+
style={styles.bannerDismiss}
|
|
153
|
+
accessibilityRole="button"
|
|
154
|
+
accessibilityLabel="Dismiss"
|
|
155
|
+
hitSlop={8}
|
|
156
|
+
>
|
|
157
|
+
<Text style={[styles.bannerDismissText, { color: theme.colors.textSecondary }]}>✕</Text>
|
|
158
|
+
</Pressable>
|
|
159
|
+
</View>
|
|
160
|
+
) : null}
|
|
161
|
+
<View style={styles.softBody}>{children}</View>
|
|
162
|
+
</View>
|
|
163
|
+
);
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// Hard wall — centered signed-out state with the "Sign in with Oxy" CTA.
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
|
|
170
|
+
const HardWall: React.FC<{ title: string; subtitle: string }> = ({ title, subtitle }) => {
|
|
171
|
+
const theme = useTheme();
|
|
172
|
+
return (
|
|
173
|
+
<View style={[styles.wallRoot, { backgroundColor: theme.colors.background }]}>
|
|
174
|
+
<View style={[styles.logoBadge, { backgroundColor: theme.colors.primarySubtle }]}>
|
|
175
|
+
<OxyLogo variant="icon" size={44} fillColor={theme.colors.primary} />
|
|
176
|
+
</View>
|
|
177
|
+
<Text style={[styles.wallTitle, { color: theme.colors.text }]}>{title}</Text>
|
|
178
|
+
<Text style={[styles.wallSubtitle, { color: theme.colors.textSecondary }]}>{subtitle}</Text>
|
|
179
|
+
<OxySignInButton variant="contained" style={styles.wallCta} />
|
|
180
|
+
</View>
|
|
181
|
+
);
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
const NeutralLoading: React.FC = () => {
|
|
185
|
+
const theme = useTheme();
|
|
186
|
+
return (
|
|
187
|
+
<View style={[styles.wallRoot, { backgroundColor: theme.colors.background }]}>
|
|
188
|
+
<ActivityIndicator color={theme.colors.primary} />
|
|
189
|
+
</View>
|
|
190
|
+
);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const styles = StyleSheet.create({
|
|
194
|
+
softRoot: {
|
|
195
|
+
flex: 1,
|
|
196
|
+
},
|
|
197
|
+
softBody: {
|
|
198
|
+
flex: 1,
|
|
199
|
+
},
|
|
200
|
+
banner: {
|
|
201
|
+
flexDirection: 'row',
|
|
202
|
+
alignItems: 'center',
|
|
203
|
+
gap: 10,
|
|
204
|
+
paddingVertical: 8,
|
|
205
|
+
paddingHorizontal: 12,
|
|
206
|
+
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
207
|
+
},
|
|
208
|
+
bannerText: {
|
|
209
|
+
flex: 1,
|
|
210
|
+
fontSize: 13.5,
|
|
211
|
+
},
|
|
212
|
+
bannerCta: {
|
|
213
|
+
paddingVertical: 6,
|
|
214
|
+
paddingHorizontal: 14,
|
|
215
|
+
borderRadius: 999,
|
|
216
|
+
},
|
|
217
|
+
bannerCtaText: {
|
|
218
|
+
color: '#FFFFFF',
|
|
219
|
+
fontSize: 13,
|
|
220
|
+
fontWeight: '700',
|
|
221
|
+
},
|
|
222
|
+
bannerDismiss: {
|
|
223
|
+
width: 28,
|
|
224
|
+
height: 28,
|
|
225
|
+
alignItems: 'center',
|
|
226
|
+
justifyContent: 'center',
|
|
227
|
+
},
|
|
228
|
+
bannerDismissText: {
|
|
229
|
+
fontSize: 15,
|
|
230
|
+
fontWeight: '600',
|
|
231
|
+
},
|
|
232
|
+
wallRoot: {
|
|
233
|
+
flex: 1,
|
|
234
|
+
alignItems: 'center',
|
|
235
|
+
justifyContent: 'center',
|
|
236
|
+
padding: 24,
|
|
237
|
+
gap: 16,
|
|
238
|
+
},
|
|
239
|
+
logoBadge: {
|
|
240
|
+
width: 88,
|
|
241
|
+
height: 88,
|
|
242
|
+
borderRadius: 44,
|
|
243
|
+
alignItems: 'center',
|
|
244
|
+
justifyContent: 'center',
|
|
245
|
+
marginBottom: 4,
|
|
246
|
+
},
|
|
247
|
+
wallTitle: {
|
|
248
|
+
fontSize: 24,
|
|
249
|
+
fontWeight: '800',
|
|
250
|
+
letterSpacing: -0.4,
|
|
251
|
+
textAlign: 'center',
|
|
252
|
+
},
|
|
253
|
+
wallSubtitle: {
|
|
254
|
+
fontSize: 15,
|
|
255
|
+
lineHeight: 21,
|
|
256
|
+
textAlign: 'center',
|
|
257
|
+
maxWidth: 340,
|
|
258
|
+
},
|
|
259
|
+
wallCta: {
|
|
260
|
+
marginTop: 8,
|
|
261
|
+
width: '100%',
|
|
262
|
+
maxWidth: 320,
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
export default RequireOxyAuth;
|