@oxy.so/services 1.0.3 → 1.2.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.map +1 -1
- package/lib/commonjs/ui/components/LanguageBridge.js +51 -0
- package/lib/commonjs/ui/components/LanguageBridge.js.map +1 -0
- package/lib/commonjs/ui/components/OxyAuthChooser.js +95 -140
- package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/commonjs/ui/components/OxyConsentScreen.js +132 -2
- package/lib/commonjs/ui/components/OxyConsentScreen.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +19 -1
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js +8 -1
- package/lib/commonjs/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js +35 -25
- package/lib/commonjs/ui/components/authChooser/SignInEntryView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/SignInRequestView.js +2 -3
- package/lib/commonjs/ui/components/authChooser/SignInRequestView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/SignUpView.js +12 -127
- package/lib/commonjs/ui/components/authChooser/SignUpView.js.map +1 -1
- package/lib/commonjs/ui/components/authChooser/signInFailureMessage.js +40 -0
- package/lib/commonjs/ui/components/authChooser/signInFailureMessage.js.map +1 -0
- package/lib/commonjs/ui/components/authChooser/types.js +4 -6
- package/lib/commonjs/ui/components/authChooser/types.js.map +1 -1
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js +15 -2
- package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/commonjs/ui/hooks/accountDialogSnapshot.js +3 -1
- package/lib/commonjs/ui/hooks/accountDialogSnapshot.js.map +1 -1
- package/lib/commonjs/ui/hooks/useProfileEditing.js +14 -0
- package/lib/commonjs/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js +225 -9
- package/lib/commonjs/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/commonjs/ui/screens/ManageAccountScreen.js +10 -0
- package/lib/commonjs/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/components/LanguageBridge.js +47 -0
- package/lib/module/ui/components/LanguageBridge.js.map +1 -0
- package/lib/module/ui/components/OxyAuthChooser.js +97 -142
- package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/module/ui/components/OxyConsentScreen.js +132 -2
- package/lib/module/ui/components/OxyConsentScreen.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +19 -1
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/authChooser/AccountsMenuView.js +8 -1
- package/lib/module/ui/components/authChooser/AccountsMenuView.js.map +1 -1
- package/lib/module/ui/components/authChooser/SignInEntryView.js +35 -25
- package/lib/module/ui/components/authChooser/SignInEntryView.js.map +1 -1
- package/lib/module/ui/components/authChooser/SignInRequestView.js +2 -3
- package/lib/module/ui/components/authChooser/SignInRequestView.js.map +1 -1
- package/lib/module/ui/components/authChooser/SignUpView.js +14 -129
- package/lib/module/ui/components/authChooser/SignUpView.js.map +1 -1
- package/lib/module/ui/components/authChooser/signInFailureMessage.js +36 -0
- package/lib/module/ui/components/authChooser/signInFailureMessage.js.map +1 -0
- package/lib/module/ui/components/authChooser/types.js +4 -6
- package/lib/module/ui/components/authChooser/types.js.map +1 -1
- package/lib/module/ui/context/hooks/useAuthOperations.js +15 -2
- package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
- package/lib/module/ui/hooks/accountDialogSnapshot.js +3 -1
- package/lib/module/ui/hooks/accountDialogSnapshot.js.map +1 -1
- package/lib/module/ui/hooks/useProfileEditing.js +14 -0
- package/lib/module/ui/hooks/useProfileEditing.js.map +1 -1
- package/lib/module/ui/screens/EditProfileFieldScreen.js +225 -9
- package/lib/module/ui/screens/EditProfileFieldScreen.js.map +1 -1
- package/lib/module/ui/screens/ManageAccountScreen.js +12 -2
- package/lib/module/ui/screens/ManageAccountScreen.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +2 -2
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/LanguageBridge.d.ts +19 -0
- package/lib/typescript/commonjs/ui/components/LanguageBridge.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts +3 -4
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyConsentScreen.d.ts +17 -1
- package/lib/typescript/commonjs/ui/components/OxyConsentScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts +14 -0
- package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts +11 -12
- package/lib/typescript/commonjs/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/SignInRequestView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/SignUpView.d.ts +10 -13
- package/lib/typescript/commonjs/ui/components/authChooser/SignUpView.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/authChooser/signInFailureMessage.d.ts +16 -0
- package/lib/typescript/commonjs/ui/components/authChooser/signInFailureMessage.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts +11 -13
- package/lib/typescript/commonjs/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts +3 -2
- package/lib/typescript/commonjs/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +20 -2
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/accountDialogSnapshot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts +3 -0
- package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useProfileEditing.d.ts +1 -0
- package/lib/typescript/commonjs/ui/hooks/useProfileEditing.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/types/navigation.d.ts +29 -0
- package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +2 -2
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/LanguageBridge.d.ts +19 -0
- package/lib/typescript/module/ui/components/LanguageBridge.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts +3 -4
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyConsentScreen.d.ts +17 -1
- package/lib/typescript/module/ui/components/OxyConsentScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyProvider.d.ts +14 -0
- package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/AccountsMenuView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts +11 -12
- package/lib/typescript/module/ui/components/authChooser/SignInEntryView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/SignInRequestView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/SignUpView.d.ts +10 -13
- package/lib/typescript/module/ui/components/authChooser/SignUpView.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/authChooser/signInFailureMessage.d.ts +16 -0
- package/lib/typescript/module/ui/components/authChooser/signInFailureMessage.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/authChooser/types.d.ts +11 -13
- package/lib/typescript/module/ui/components/authChooser/types.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts +3 -2
- package/lib/typescript/module/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +20 -2
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/accountDialogSnapshot.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts +3 -0
- package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useProfileEditing.d.ts +1 -0
- package/lib/typescript/module/ui/hooks/useProfileEditing.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/EditProfileFieldScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/screens/ManageAccountScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/types/navigation.d.ts +29 -0
- package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/index.ts +2 -1
- package/src/ui/components/LanguageBridge.tsx +44 -0
- package/src/ui/components/OxyAuthChooser.tsx +106 -156
- package/src/ui/components/OxyConsentScreen.tsx +127 -0
- package/src/ui/components/OxyProvider.tsx +17 -0
- package/src/ui/components/__tests__/LanguageBridge.test.tsx +81 -0
- package/src/ui/components/__tests__/OxyConsentScreen.test.tsx +32 -0
- package/src/ui/components/authChooser/AccountsMenuView.tsx +7 -0
- package/src/ui/components/authChooser/SignInEntryView.tsx +41 -44
- package/src/ui/components/authChooser/SignInRequestView.tsx +1 -4
- package/src/ui/components/authChooser/SignUpView.tsx +16 -114
- package/src/ui/components/authChooser/signInFailureMessage.ts +39 -0
- package/src/ui/components/authChooser/types.ts +11 -13
- package/src/ui/context/OxyContext.tsx +1 -1
- package/src/ui/context/hooks/useAuthOperations.ts +10 -5
- package/src/ui/context/oxyContextTypes.ts +16 -2
- package/src/ui/hooks/accountDialogSnapshot.ts +2 -0
- package/src/ui/hooks/useProfileEditing.ts +15 -0
- package/src/ui/screens/EditProfileFieldScreen.tsx +202 -7
- package/src/ui/screens/ManageAccountScreen.tsx +12 -0
- package/src/ui/types/navigation.ts +30 -0
- package/lib/commonjs/ui/components/authChooser/useUsernameAvailability.js +0 -50
- package/lib/commonjs/ui/components/authChooser/useUsernameAvailability.js.map +0 -1
- package/lib/module/ui/components/authChooser/useUsernameAvailability.js +0 -46
- package/lib/module/ui/components/authChooser/useUsernameAvailability.js.map +0 -1
- package/lib/typescript/commonjs/ui/components/authChooser/useUsernameAvailability.d.ts +0 -16
- package/lib/typescript/commonjs/ui/components/authChooser/useUsernameAvailability.d.ts.map +0 -1
- package/lib/typescript/module/ui/components/authChooser/useUsernameAvailability.d.ts +0 -16
- package/lib/typescript/module/ui/components/authChooser/useUsernameAvailability.d.ts.map +0 -1
- package/src/ui/components/authChooser/useUsernameAvailability.ts +0 -60
|
@@ -14,6 +14,7 @@ import { createMemoryStorage, createPlatformStorage, type StorageInterface } fro
|
|
|
14
14
|
import { isNetConnectivityOnline } from '../utils/netConnectivity';
|
|
15
15
|
import { KeyboardBoundary } from './KeyboardBoundary';
|
|
16
16
|
import { ProductAnalyticsObserver } from '../analytics/productAnalytics';
|
|
17
|
+
import { LanguageBridge } from './LanguageBridge';
|
|
17
18
|
|
|
18
19
|
const bootStyles = StyleSheet.create({
|
|
19
20
|
providerRoot: {
|
|
@@ -58,6 +59,20 @@ const isWeb = Platform.OS === 'web';
|
|
|
58
59
|
* return <Text>Welcome, {user?.username}!</Text>;
|
|
59
60
|
* }
|
|
60
61
|
* ```
|
|
62
|
+
*
|
|
63
|
+
* An app with its own translated UI passes a language config to follow the account's
|
|
64
|
+
* (or, signed out, the device/guest) resolved locale automatically — see ADR
|
|
65
|
+
* 0022 (`docs/adr/0022-app-i18n-follows-oxy-language.md`):
|
|
66
|
+
* ```tsx
|
|
67
|
+
* <OxyProvider
|
|
68
|
+
* baseURL="https://api.oxy.so"
|
|
69
|
+
* language={{
|
|
70
|
+
* supportedLocales: SUPPORTED_LANGUAGES, // this app's own catalog
|
|
71
|
+
* fallbackLocale: DEFAULT_LANGUAGE,
|
|
72
|
+
* onChange: (locale) => i18n.changeLanguage(locale),
|
|
73
|
+
* }}
|
|
74
|
+
* >
|
|
75
|
+
* ```
|
|
61
76
|
*/
|
|
62
77
|
const OxyProvider: FC<OxyProviderProps> = ({
|
|
63
78
|
oxyServices,
|
|
@@ -76,6 +91,7 @@ const OxyProvider: FC<OxyProviderProps> = ({
|
|
|
76
91
|
requireAuth = 'off',
|
|
77
92
|
backgroundSession = false,
|
|
78
93
|
deviceCredentialStorage = 'persistent',
|
|
94
|
+
language,
|
|
79
95
|
}) => {
|
|
80
96
|
|
|
81
97
|
// Storage + persistence wiring.
|
|
@@ -229,6 +245,7 @@ const OxyProvider: FC<OxyProviderProps> = ({
|
|
|
229
245
|
onAuthStateChange={onAuthStateChange as OxyRuntimeProviderProps['onAuthStateChange']}
|
|
230
246
|
>
|
|
231
247
|
{productAnalytics ? <ProductAnalyticsObserver analytics={productAnalytics} /> : null}
|
|
248
|
+
{language ? <LanguageBridge {...language} /> : null}
|
|
232
249
|
<SurfaceProvider>
|
|
233
250
|
{requireAuth === 'off' ? (
|
|
234
251
|
children
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import { LanguageBridge } from '../LanguageBridge';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* `OxyProvider`'s language config prop, realized: the one place Oxy's
|
|
7
|
+
* resolved language reaches a host app's own i18n library. `useOxy` is mocked here —
|
|
8
|
+
* it is the SDK's own well-tested context — so these tests exercise exactly
|
|
9
|
+
* what this component adds: coercion to the caller's catalog, and calling
|
|
10
|
+
* back only when the RESOLVED locale actually changes.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
let mockCurrentLanguage = 'en-US';
|
|
14
|
+
jest.mock('../../context/OxyContext', () => ({
|
|
15
|
+
useOxy: () => ({ currentLanguage: mockCurrentLanguage }),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
const HOST_CATALOG = ['en-US', 'es-ES', 'fr-FR'] as const;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
mockCurrentLanguage = 'en-US';
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('resolves and reports the account language on mount', () => {
|
|
25
|
+
mockCurrentLanguage = 'es-ES';
|
|
26
|
+
const onChange = jest.fn();
|
|
27
|
+
render(<LanguageBridge supportedLocales={HOST_CATALOG} fallbackLocale="en-US" onChange={onChange} />);
|
|
28
|
+
|
|
29
|
+
expect(onChange).toHaveBeenCalledWith('es-ES');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('coerces a locale the host never shipped a catalog for, by base language', () => {
|
|
33
|
+
mockCurrentLanguage = 'es-MX';
|
|
34
|
+
const onChange = jest.fn();
|
|
35
|
+
render(<LanguageBridge supportedLocales={HOST_CATALOG} fallbackLocale="en-US" onChange={onChange} />);
|
|
36
|
+
|
|
37
|
+
expect(onChange).toHaveBeenCalledWith('es-ES');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('falls back to the caller default when nothing matches, even by base', () => {
|
|
41
|
+
mockCurrentLanguage = 'ja-JP';
|
|
42
|
+
const onChange = jest.fn();
|
|
43
|
+
render(<LanguageBridge supportedLocales={HOST_CATALOG} fallbackLocale="en-US" onChange={onChange} />);
|
|
44
|
+
|
|
45
|
+
expect(onChange).toHaveBeenCalledWith('en-US');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('calls back again only when the RESOLVED locale changes', () => {
|
|
49
|
+
const onChange = jest.fn();
|
|
50
|
+
const { rerender } = render(
|
|
51
|
+
<LanguageBridge supportedLocales={HOST_CATALOG} fallbackLocale="en-US" onChange={onChange} />,
|
|
52
|
+
);
|
|
53
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
54
|
+
|
|
55
|
+
// Same resolved locale, brand-new callback identity (the common case for an
|
|
56
|
+
// inline arrow function) — must not re-fire.
|
|
57
|
+
rerender(<LanguageBridge supportedLocales={HOST_CATALOG} fallbackLocale="en-US" onChange={() => {}} />);
|
|
58
|
+
expect(onChange).toHaveBeenCalledTimes(1);
|
|
59
|
+
|
|
60
|
+
mockCurrentLanguage = 'fr-FR';
|
|
61
|
+
rerender(<LanguageBridge supportedLocales={HOST_CATALOG} fallbackLocale="en-US" onChange={onChange} />);
|
|
62
|
+
expect(onChange).toHaveBeenCalledTimes(2);
|
|
63
|
+
expect(onChange).toHaveBeenLastCalledWith('fr-FR');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('reports a rejected onChange to onError instead of throwing', async () => {
|
|
67
|
+
const failure = new Error('catalog failed to load');
|
|
68
|
+
const onChange = jest.fn().mockRejectedValue(failure);
|
|
69
|
+
const onError = jest.fn();
|
|
70
|
+
render(
|
|
71
|
+
<LanguageBridge
|
|
72
|
+
supportedLocales={HOST_CATALOG}
|
|
73
|
+
fallbackLocale="en-US"
|
|
74
|
+
onChange={onChange}
|
|
75
|
+
onError={onError}
|
|
76
|
+
/>,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
80
|
+
expect(onError).toHaveBeenCalledWith(failure, 'en-US');
|
|
81
|
+
});
|
|
@@ -24,6 +24,7 @@ function renderScreen(props: Partial<OxyConsentScreenProps> = {}) {
|
|
|
24
24
|
application={props.application ?? baseApp}
|
|
25
25
|
scopes={props.scopes ?? ['openid', 'profile']}
|
|
26
26
|
user={props.user}
|
|
27
|
+
resource={props.resource}
|
|
27
28
|
onAllow={props.onAllow ?? onAllow}
|
|
28
29
|
onDeny={props.onDeny ?? onDeny}
|
|
29
30
|
busy={props.busy}
|
|
@@ -138,6 +139,37 @@ describe('OxyConsentScreen', () => {
|
|
|
138
139
|
expect(getByTestId('consent-account-name').textContent).toBe('Alice A');
|
|
139
140
|
});
|
|
140
141
|
|
|
142
|
+
it('renders the exact MCP app, protected resource, account id and catalog-derived write actions', () => {
|
|
143
|
+
const { getByTestId } = renderScreen({
|
|
144
|
+
scopes: ['email.read', 'email.send'],
|
|
145
|
+
user: {
|
|
146
|
+
accountId: 'account-mail-1',
|
|
147
|
+
displayName: 'Oxy Mail',
|
|
148
|
+
handle: 'oxy-mail-account',
|
|
149
|
+
},
|
|
150
|
+
resource: {
|
|
151
|
+
application: { name: 'Inbox', iconUrl: 'inbox-icon' },
|
|
152
|
+
uri: 'https://mcp.inbox.oxy.so',
|
|
153
|
+
writeActions: [{
|
|
154
|
+
name: 'sendEmail',
|
|
155
|
+
version: '1.0.0',
|
|
156
|
+
description: 'Send an email from the selected mailbox.',
|
|
157
|
+
requiredCapabilities: ['email.send'],
|
|
158
|
+
effect: 'external',
|
|
159
|
+
}],
|
|
160
|
+
},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
expect(getByTestId('consent-resource-app').textContent).toBe('Inbox');
|
|
164
|
+
expect(getByTestId('consent-resource-uri').textContent).toBe('https://mcp.inbox.oxy.so');
|
|
165
|
+
expect(getByTestId('consent-account-id').textContent).toContain('account-mail-1');
|
|
166
|
+
expect(getByTestId('consent-scope-email.send').textContent).toContain('email.send');
|
|
167
|
+
expect(getByTestId('consent-write-action-sendEmail').textContent).toContain(
|
|
168
|
+
'Send an email from the selected mailbox.',
|
|
169
|
+
);
|
|
170
|
+
expect(getByTestId('consent-write-action-sendEmail').textContent).toContain('External');
|
|
171
|
+
});
|
|
172
|
+
|
|
141
173
|
it('falls back to the handle when the account has no display name (D5)', () => {
|
|
142
174
|
const { getByTestId } = renderScreen({ user: { handle: 'bob' } });
|
|
143
175
|
expect(getByTestId('consent-account-name').textContent).toBe('bob');
|
|
@@ -477,6 +477,13 @@ const AccountsMenuView: React.FC<AccountsMenuViewProps> = ({
|
|
|
477
477
|
title={t('accountMenu.settings')}
|
|
478
478
|
onPress={menu.onOpenSettings}
|
|
479
479
|
/>
|
|
480
|
+
{menu.onOpenIdentity ? (
|
|
481
|
+
<SettingsListItem
|
|
482
|
+
icon={<MenuIcon name="shield-account-outline" theme={theme} />}
|
|
483
|
+
title={t('accountMenu.identity')}
|
|
484
|
+
onPress={menu.onOpenIdentity}
|
|
485
|
+
/>
|
|
486
|
+
) : null}
|
|
480
487
|
<SettingsListItem
|
|
481
488
|
icon={<MenuIcon name="help-circle-outline" theme={theme} />}
|
|
482
489
|
title={t('accountMenu.help')}
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The sign-in ENTRY (`signin` / `add` views) — ONE primary action.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* Web: "Continue" opens the identity origin (`id.oxy.so`) in a popup, where the
|
|
5
|
+
* person signs in — or creates an account — with a passkey, and the account's
|
|
6
|
+
* self-custody identity is created or unsealed (one identity, two carriers). No
|
|
7
|
+
* philosophy up front: fingerprint, face or device PIN, and they are in. An
|
|
8
|
+
* account that lives on another device is one subordinate link away (Commons
|
|
9
|
+
* QR). Nothing auto-starts: a browser only opens a popup from a user gesture.
|
|
9
10
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* the issue removes.
|
|
11
|
+
* Native: "Continue with Oxy" — the Commons flow (shared keychain, else the
|
|
12
|
+
* request Oxy routes to the person's Commons identity).
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* your phone") IS the primary route there, never something behind a tap.
|
|
14
|
+
* Existing accounts still render ABOVE the CTA: "continue as one of these" is a
|
|
15
|
+
* choice of WHO, not of HOW.
|
|
17
16
|
*/
|
|
18
17
|
|
|
19
18
|
import type React from 'react';
|
|
20
19
|
import { View } from 'react-native';
|
|
21
20
|
import { Button } from '@oxy.so/bloom/button';
|
|
21
|
+
import { Text } from '@oxy.so/bloom/typography';
|
|
22
22
|
import {
|
|
23
23
|
showsPrincipalHeaders,
|
|
24
24
|
type AccountDialogSnapshot,
|
|
@@ -42,7 +42,7 @@ interface SignInEntryViewProps {
|
|
|
42
42
|
theme: Theme;
|
|
43
43
|
t: Translate;
|
|
44
44
|
handlers: OxyAuthChooserHandlers;
|
|
45
|
-
/**
|
|
45
|
+
/** Native's primary action. Starts the Commons flow; Oxy chooses the route from there. */
|
|
46
46
|
onContinueWithOxy: () => void;
|
|
47
47
|
alternatives: SignInAlternatives;
|
|
48
48
|
}
|
|
@@ -56,37 +56,20 @@ const SignInEntryView: React.FC<SignInEntryViewProps> = ({
|
|
|
56
56
|
onContinueWithOxy,
|
|
57
57
|
alternatives,
|
|
58
58
|
}) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
{
|
|
70
|
-
key: 'passkey-signin-link',
|
|
71
|
-
label: alternatives.passkeyPending
|
|
72
|
-
? t('accountSwitcher.passkeySigningIn')
|
|
73
|
-
: t('accountSwitcher.useIdentityOnDevice'),
|
|
74
|
-
onPress: alternatives.onSignInWithPasskey,
|
|
75
|
-
disabled: alternatives.passkeyPending,
|
|
76
|
-
},
|
|
77
|
-
]
|
|
78
|
-
: []),
|
|
79
|
-
{
|
|
80
|
-
key: 'get-commons-link',
|
|
81
|
-
label: t('accountSwitcher.getCommons'),
|
|
82
|
-
onPress: alternatives.onGetCommons,
|
|
83
|
-
},
|
|
84
|
-
];
|
|
59
|
+
const web = alternatives.passkeyAvailable;
|
|
60
|
+
|
|
61
|
+
// Web keeps Commons one link away; native keeps its QR/Commons alternatives
|
|
62
|
+
// behind the disclosure, as before.
|
|
63
|
+
const troubleActions: OxySignInSurfaceAction[] = web
|
|
64
|
+
? [{ key: 'get-commons-link', label: t('accountSwitcher.getCommons'), onPress: alternatives.onGetCommons }]
|
|
65
|
+
: [
|
|
66
|
+
{ key: 'scan-qr-link', label: t('accountSwitcher.scanQr'), onPress: alternatives.onShowQr },
|
|
67
|
+
{ key: 'get-commons-link', label: t('accountSwitcher.getCommons'), onPress: alternatives.onGetCommons },
|
|
68
|
+
];
|
|
85
69
|
|
|
86
70
|
// Whose route this is only needs naming when the list holds more than one
|
|
87
71
|
// person, or somebody with more than one account — the same rule the account
|
|
88
|
-
// menu's group headers follow
|
|
89
|
-
// worth stating.
|
|
72
|
+
// menu's group headers follow.
|
|
90
73
|
const namesTheOperator = showsPrincipalHeaders(principals);
|
|
91
74
|
const rows = principals.flatMap((principal) =>
|
|
92
75
|
principal.contexts.map((context) => ({
|
|
@@ -117,18 +100,23 @@ const SignInEntryView: React.FC<SignInEntryViewProps> = ({
|
|
|
117
100
|
</View>
|
|
118
101
|
) : null}
|
|
119
102
|
|
|
103
|
+
{web ? (
|
|
104
|
+
<Text style={[styles.mutedText, { color: theme.colors.textSecondary }]}>
|
|
105
|
+
{t('accountSwitcher.passkeyHint')}
|
|
106
|
+
</Text>
|
|
107
|
+
) : null}
|
|
108
|
+
|
|
120
109
|
<Button
|
|
121
110
|
variant="primary"
|
|
122
|
-
onPress={onContinueWithOxy}
|
|
111
|
+
onPress={web ? alternatives.onSignInWithPasskey : onContinueWithOxy}
|
|
123
112
|
style={styles.primaryButton}
|
|
124
113
|
testID="continue-with-oxy"
|
|
125
114
|
>
|
|
126
|
-
{t('accountSwitcher.continueWithOxy')}
|
|
115
|
+
{web ? t('accountSwitcher.continueWithPasskey') : t('accountSwitcher.continueWithOxy')}
|
|
127
116
|
</Button>
|
|
128
117
|
|
|
129
118
|
{/* Account CREATION is not an authentication method — it is the way in for
|
|
130
|
-
someone who has no
|
|
131
|
-
rather than hiding behind a troubleshooting affordance. */}
|
|
119
|
+
someone who has no account yet — so it keeps its own subordinate link. */}
|
|
132
120
|
<SubtleLink
|
|
133
121
|
label={t('signin.createAccountLink')}
|
|
134
122
|
theme={theme}
|
|
@@ -136,6 +124,15 @@ const SignInEntryView: React.FC<SignInEntryViewProps> = ({
|
|
|
136
124
|
testID="create-account-link"
|
|
137
125
|
/>
|
|
138
126
|
|
|
127
|
+
{web ? (
|
|
128
|
+
<SubtleLink
|
|
129
|
+
label={t('accountSwitcher.otherDeviceCommons')}
|
|
130
|
+
theme={theme}
|
|
131
|
+
onPress={alternatives.onShowQr}
|
|
132
|
+
testID="scan-qr-link"
|
|
133
|
+
/>
|
|
134
|
+
) : null}
|
|
135
|
+
|
|
139
136
|
<TroubleDisclosure actions={troubleActions} revealed={false} theme={theme} t={t} />
|
|
140
137
|
</View>
|
|
141
138
|
);
|
|
@@ -58,11 +58,8 @@ const SignInRequestView: React.FC<SignInRequestViewProps> = ({
|
|
|
58
58
|
? [
|
|
59
59
|
{
|
|
60
60
|
key: 'passkey-signin-link',
|
|
61
|
-
label:
|
|
62
|
-
? t('accountSwitcher.passkeySigningIn')
|
|
63
|
-
: t('accountSwitcher.useIdentityOnDevice'),
|
|
61
|
+
label: t('accountSwitcher.useIdentityOnDevice'),
|
|
64
62
|
onPress: alternatives.onSignInWithPasskey,
|
|
65
|
-
disabled: alternatives.passkeyPending,
|
|
66
63
|
},
|
|
67
64
|
]
|
|
68
65
|
: [];
|
|
@@ -1,37 +1,31 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Account creation (`signup` view).
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
4
|
+
* Web: the account AND its self-custody identity are created at the identity
|
|
5
|
+
* origin (`id.oxy.so`), in the same window a passkey sign-in uses — never on
|
|
6
|
+
* this page, whatever its origin (one identity, two carriers). The sign-in
|
|
7
|
+
* entry's "create an account" link opens that window directly; this view is
|
|
8
|
+
* what `openAccountDialog('signup')` lands on, and offers the same one action.
|
|
9
|
+
*
|
|
10
|
+
* Native: Commons owns identity creation. Deep-link straight in when Commons is
|
|
11
|
+
* installed, else lead with the "Get Commons" acquisition CTA.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import type React from 'react';
|
|
15
|
-
import {
|
|
16
|
-
import { TextInput, View } from 'react-native';
|
|
15
|
+
import { View } from 'react-native';
|
|
17
16
|
import { Button } from '@oxy.so/bloom/button';
|
|
18
17
|
import { Text } from '@oxy.so/bloom/typography';
|
|
19
|
-
import type { AccountDialogSnapshot
|
|
20
|
-
import {
|
|
21
|
-
import { Dividerish, SubtleLink } from './primitives';
|
|
18
|
+
import type { AccountDialogSnapshot } from '@oxy.so/core';
|
|
19
|
+
import { SubtleLink } from './primitives';
|
|
22
20
|
import { authChooserStyles as styles } from './styles';
|
|
23
21
|
import type { PasskeyMode, Theme, Translate } from './types';
|
|
24
|
-
import { useUsernameAvailability, type UsernameStatus } from './useUsernameAvailability';
|
|
25
22
|
|
|
26
23
|
interface SignUpViewProps {
|
|
27
24
|
snapshot: AccountDialogSnapshot;
|
|
28
25
|
theme: Theme;
|
|
29
26
|
t: Translate;
|
|
30
|
-
oxyServices: OxyServices;
|
|
31
27
|
passkeyMode: PasskeyMode;
|
|
32
|
-
|
|
33
|
-
createPending: boolean;
|
|
34
|
-
/** Open the auth.oxy.so hub popup (b2) — the 'hub' mode's only path. */
|
|
28
|
+
/** Open the identity-origin window — web's only creation path. */
|
|
35
29
|
onOpenHub: () => void;
|
|
36
30
|
/** Deep-link into Commons' own identity-creation screen (installed only). */
|
|
37
31
|
onCreateIdentityInCommons: () => void;
|
|
@@ -44,22 +38,13 @@ const SignUpView: React.FC<SignUpViewProps> = ({
|
|
|
44
38
|
snapshot,
|
|
45
39
|
theme,
|
|
46
40
|
t,
|
|
47
|
-
oxyServices,
|
|
48
41
|
passkeyMode,
|
|
49
|
-
onCreateWithPasskey,
|
|
50
|
-
createPending,
|
|
51
42
|
onOpenHub,
|
|
52
43
|
onCreateIdentityInCommons,
|
|
53
44
|
onGetCommons,
|
|
54
45
|
onBackToSignIn,
|
|
55
46
|
}) => {
|
|
56
|
-
|
|
57
|
-
const { status: usernameStatus, check: checkUsername } = useUsernameAvailability(oxyServices, t);
|
|
58
|
-
const canSubmit = username.trim().length >= 3 && usernameStatus === 'available' && !createPending;
|
|
59
|
-
|
|
60
|
-
// Native — Commons owns identity creation. Deep-link straight in when it is
|
|
61
|
-
// installed, else lead with the same "Get Commons" acquisition CTA.
|
|
62
|
-
if (!isWebBrowser()) {
|
|
47
|
+
if (passkeyMode === 'none') {
|
|
63
48
|
const commonsInstalled = snapshot.commonsAvailability === 'available';
|
|
64
49
|
return (
|
|
65
50
|
<View style={styles.centeredBlock}>
|
|
@@ -75,100 +60,17 @@ const SignUpView: React.FC<SignUpViewProps> = ({
|
|
|
75
60
|
);
|
|
76
61
|
}
|
|
77
62
|
|
|
78
|
-
// Web, first-party Oxy origin. Commons leads with a "Get Commons" CTA; passkey
|
|
79
|
-
// creation is the de-emphasized alternative underneath for someone who doesn't
|
|
80
|
-
// want to install anything — a secondary button introduced by its own "No
|
|
81
|
-
// Commons?" framing, the same subordination the sign-in surfaces use.
|
|
82
|
-
if (passkeyMode === 'direct') {
|
|
83
|
-
return (
|
|
84
|
-
<View style={styles.signInBlock}>
|
|
85
|
-
<Text style={[styles.qrHeadline, { color: theme.colors.text }]}>
|
|
86
|
-
{t('signup.commonsHeadline')}
|
|
87
|
-
</Text>
|
|
88
|
-
<Text style={[styles.mutedText, { color: theme.colors.textSecondary }]}>
|
|
89
|
-
{t('signup.commonsExplainer')}
|
|
90
|
-
</Text>
|
|
91
|
-
<Button variant="primary" onPress={onGetCommons} style={styles.primaryButton}>
|
|
92
|
-
{t('accountSwitcher.getCommons')}
|
|
93
|
-
</Button>
|
|
94
|
-
|
|
95
|
-
<Dividerish theme={theme} label={t('signin.or')} />
|
|
96
|
-
|
|
97
|
-
<Text style={[styles.mutedText, { color: theme.colors.textSecondary }]}>
|
|
98
|
-
{t('signup.passkeyAlternative')}
|
|
99
|
-
</Text>
|
|
100
|
-
<TextInput
|
|
101
|
-
style={[styles.usernameInput, { borderColor: theme.colors.border, color: theme.colors.text }]}
|
|
102
|
-
placeholder={t('signup.usernamePlaceholder')}
|
|
103
|
-
placeholderTextColor={theme.colors.textSecondary}
|
|
104
|
-
value={username}
|
|
105
|
-
autoCapitalize="none"
|
|
106
|
-
autoCorrect={false}
|
|
107
|
-
autoComplete="username"
|
|
108
|
-
onChangeText={(value) => {
|
|
109
|
-
setUsername(value);
|
|
110
|
-
checkUsername(value.trim());
|
|
111
|
-
}}
|
|
112
|
-
testID="signup-username-input"
|
|
113
|
-
/>
|
|
114
|
-
<UsernameStatusText status={usernameStatus} theme={theme} t={t} />
|
|
115
|
-
<Button
|
|
116
|
-
variant="secondary"
|
|
117
|
-
onPress={() => onCreateWithPasskey(username.trim())}
|
|
118
|
-
disabled={!canSubmit}
|
|
119
|
-
style={styles.secondaryButton}
|
|
120
|
-
testID="signup-create-button"
|
|
121
|
-
>
|
|
122
|
-
{createPending ? t('signup.creating') : t('signup.createAccount')}
|
|
123
|
-
</Button>
|
|
124
|
-
<SubtleLink label={t('signup.backToSignInLink')} theme={theme} onPress={onBackToSignIn} />
|
|
125
|
-
</View>
|
|
126
|
-
);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Web, non-Oxy origin (b2): a passkey ceremony cannot run locally here — the
|
|
130
|
-
// same WebAuthn RP-ID boundary the sign-in surfaces hit — so this opens the
|
|
131
|
-
// auth.oxy.so hub popup. Completing EITHER a sign-in OR a fresh sign-up there
|
|
132
|
-
// relays the resulting session back through the SAME poll/socket engine.
|
|
133
63
|
return (
|
|
134
64
|
<View style={styles.centeredBlock}>
|
|
135
65
|
<Text style={[styles.mutedText, { color: theme.colors.textSecondary }]}>
|
|
136
|
-
{t('
|
|
66
|
+
{t('accountSwitcher.passkeyHint')}
|
|
137
67
|
</Text>
|
|
138
|
-
<Button variant="primary" onPress={onOpenHub} style={styles.primaryButton}>
|
|
139
|
-
{t('signup.
|
|
68
|
+
<Button variant="primary" onPress={onOpenHub} style={styles.primaryButton} testID="signup-open-identity">
|
|
69
|
+
{t('signup.createAccount')}
|
|
140
70
|
</Button>
|
|
141
71
|
<SubtleLink label={t('signup.backToSignInLink')} theme={theme} onPress={onBackToSignIn} />
|
|
142
72
|
</View>
|
|
143
73
|
);
|
|
144
74
|
};
|
|
145
75
|
|
|
146
|
-
/** The inline availability verdict. Network FAILURES are toasted, never shown here. */
|
|
147
|
-
const UsernameStatusText: React.FC<{ status: UsernameStatus; theme: Theme; t: Translate }> = ({
|
|
148
|
-
status,
|
|
149
|
-
theme,
|
|
150
|
-
t,
|
|
151
|
-
}) => {
|
|
152
|
-
if (status === 'idle') return null;
|
|
153
|
-
if (status === 'checking') {
|
|
154
|
-
return (
|
|
155
|
-
<Text style={[styles.mutedText, { color: theme.colors.textSecondary }]}>
|
|
156
|
-
{t('signup.usernameChecking')}
|
|
157
|
-
</Text>
|
|
158
|
-
);
|
|
159
|
-
}
|
|
160
|
-
if (status === 'available') {
|
|
161
|
-
return (
|
|
162
|
-
<Text style={[styles.mutedText, { color: theme.colors.success }]}>
|
|
163
|
-
{t('signup.usernameAvailable')}
|
|
164
|
-
</Text>
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
return (
|
|
168
|
-
<Text style={[styles.mutedText, { color: theme.colors.error }]}>
|
|
169
|
-
{t('signup.usernameTaken')}
|
|
170
|
-
</Text>
|
|
171
|
-
);
|
|
172
|
-
};
|
|
173
|
-
|
|
174
76
|
export default SignUpView;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The localized toast for a failed sign-in attempt.
|
|
3
|
+
*
|
|
4
|
+
* One responsibility: turn the controller's machine-readable
|
|
5
|
+
* `signIn.failure` into copy. The controller ships no user-facing prose — its
|
|
6
|
+
* raw `signIn.error` is English or a server string, kept for diagnostics only.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { SignInFailureReason } from '@oxy.so/core';
|
|
10
|
+
import type { Translate } from './types';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The toast for `failure`, or `null` when there is nothing to report: a
|
|
14
|
+
* `'cancelled'` attempt is the user's own choice (they closed the window), and
|
|
15
|
+
* the surface already offers "Try again" without scolding them for it.
|
|
16
|
+
*/
|
|
17
|
+
export function signInFailureMessage(
|
|
18
|
+
failure: SignInFailureReason | null,
|
|
19
|
+
t: Translate,
|
|
20
|
+
): string | null {
|
|
21
|
+
switch (failure) {
|
|
22
|
+
case 'cancelled':
|
|
23
|
+
return null;
|
|
24
|
+
case 'denied':
|
|
25
|
+
return t('accountSwitcher.signInFailures.denied');
|
|
26
|
+
case 'expired':
|
|
27
|
+
return t('accountSwitcher.signInFailures.expired');
|
|
28
|
+
case 'network':
|
|
29
|
+
return t('accountSwitcher.signInFailures.network');
|
|
30
|
+
case 'not-configured':
|
|
31
|
+
return t('accountSwitcher.signInFailures.notConfigured');
|
|
32
|
+
case 'unsupported-flow':
|
|
33
|
+
return t('accountSwitcher.signInFailures.unsupportedFlow');
|
|
34
|
+
case 'claim-failed':
|
|
35
|
+
return t('accountSwitcher.signInFailures.claimFailed');
|
|
36
|
+
default:
|
|
37
|
+
return t('accountSwitcher.signInFailures.generic');
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -95,6 +95,11 @@ export interface AccountsMenuActions {
|
|
|
95
95
|
onPrivacy: () => void;
|
|
96
96
|
onTerms: () => void;
|
|
97
97
|
onSignOut: () => void;
|
|
98
|
+
/**
|
|
99
|
+
* Open the person's identity at the identity origin (recovery phrase,
|
|
100
|
+
* recovery, account deletion). Web only — `undefined` hides the row.
|
|
101
|
+
*/
|
|
102
|
+
onOpenIdentity?: () => void;
|
|
98
103
|
customItems: readonly {
|
|
99
104
|
key: string;
|
|
100
105
|
label: string;
|
|
@@ -104,15 +109,13 @@ export interface AccountsMenuActions {
|
|
|
104
109
|
}
|
|
105
110
|
|
|
106
111
|
/**
|
|
107
|
-
* Where a
|
|
112
|
+
* Where a passkey sign-in or sign-up can run from the current surface.
|
|
108
113
|
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* `'
|
|
112
|
-
* `'hub'` = any other web origin; it runs in the auth.oxy.so popup instead.
|
|
113
|
-
* `'none'` = native, where Commons owns identity.
|
|
114
|
+
* `'hub'` = web: it runs at the identity origin (`id.oxy.so`) in a popup, on
|
|
115
|
+
* every web origin, first-party or not.
|
|
116
|
+
* `'none'` = native, where Commons owns identity.
|
|
114
117
|
*/
|
|
115
|
-
export type PasskeyMode = '
|
|
118
|
+
export type PasskeyMode = 'hub' | 'none';
|
|
116
119
|
|
|
117
120
|
/**
|
|
118
121
|
* Everything that is NOT the sign-in surface's one primary action.
|
|
@@ -125,17 +128,12 @@ export type PasskeyMode = 'direct' | 'hub' | 'none';
|
|
|
125
128
|
export interface SignInAlternatives {
|
|
126
129
|
/** Whether a WebAuthn ceremony is reachable from this origin at all. */
|
|
127
130
|
passkeyAvailable: boolean;
|
|
128
|
-
/**
|
|
129
|
-
* `true` while a DIRECT ceremony is in flight. The hub-popup variant reports
|
|
130
|
-
* its progress through `snapshot.signIn` instead, so it never sets this.
|
|
131
|
-
*/
|
|
132
|
-
passkeyPending: boolean;
|
|
133
131
|
onSignInWithPasskey: () => void;
|
|
134
132
|
/** Fall back to the cross-device QR handoff (restarts the request). */
|
|
135
133
|
onShowQr: () => void;
|
|
136
134
|
/** Open the Commons store listing / landing page for this platform. */
|
|
137
135
|
onGetCommons: () => void;
|
|
138
|
-
/**
|
|
136
|
+
/** Create an account: the identity-origin window on web, the Commons flow on native. */
|
|
139
137
|
onCreateAccount: () => void;
|
|
140
138
|
}
|
|
141
139
|
|
|
@@ -104,7 +104,7 @@ import {
|
|
|
104
104
|
import { queryKeys } from '../hooks/queries/queryKeys';
|
|
105
105
|
import { useOxyAccountGraph } from './useOxyAccountGraph';
|
|
106
106
|
|
|
107
|
-
export type { OxyContextState, OxyRuntimeProviderProps } from './oxyContextTypes';
|
|
107
|
+
export type { LogoutResult, OxyContextState, OxyRuntimeProviderProps } from './oxyContextTypes';
|
|
108
108
|
|
|
109
109
|
const OxyRuntimeContext = createContext<OxyContextState | null>(null);
|
|
110
110
|
|