@oxy.so/services 6.1.0 → 6.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/ui/components/OxyAuthChooser.js +7 -6
- package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/commonjs/ui/components/SurfaceScreen.js +21 -12
- package/lib/commonjs/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxyAuthScreen.js +11 -5
- package/lib/commonjs/ui/components/signIn/OxyAuthScreen.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/OxySignInPanel.js +45 -32
- package/lib/commonjs/ui/components/signIn/OxySignInPanel.js.map +1 -1
- package/lib/commonjs/ui/components/signIn/signInMethods.js +19 -18
- package/lib/commonjs/ui/components/signIn/signInMethods.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +4 -4
- package/lib/commonjs/ui/hooks/useSurfaceFrameWidth.js +28 -0
- package/lib/commonjs/ui/hooks/useSurfaceFrameWidth.js.map +1 -0
- package/lib/module/ui/components/OxyAuthChooser.js +7 -6
- package/lib/module/ui/components/OxyAuthChooser.js.map +1 -1
- package/lib/module/ui/components/SurfaceScreen.js +21 -12
- package/lib/module/ui/components/SurfaceScreen.js.map +1 -1
- package/lib/module/ui/components/signIn/OxyAuthScreen.js +11 -5
- package/lib/module/ui/components/signIn/OxyAuthScreen.js.map +1 -1
- package/lib/module/ui/components/signIn/OxySignInPanel.js +47 -32
- package/lib/module/ui/components/signIn/OxySignInPanel.js.map +1 -1
- package/lib/module/ui/components/signIn/signInMethods.js +19 -18
- package/lib/module/ui/components/signIn/signInMethods.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +4 -4
- package/lib/module/ui/hooks/useSurfaceFrameWidth.js +24 -0
- package/lib/module/ui/hooks/useSurfaceFrameWidth.js.map +1 -0
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts +3 -2
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/SurfaceScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyAuthScreen.d.ts +7 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/OxySignInPanel.d.ts +12 -12
- package/lib/typescript/commonjs/ui/components/signIn/OxySignInPanel.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/signIn/signInMethods.d.ts +18 -15
- package/lib/typescript/commonjs/ui/components/signIn/signInMethods.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts +4 -4
- package/lib/typescript/commonjs/ui/hooks/useSurfaceFrameWidth.d.ts +12 -0
- package/lib/typescript/commonjs/ui/hooks/useSurfaceFrameWidth.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts +3 -2
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/SurfaceScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxyAuthScreen.d.ts +7 -1
- package/lib/typescript/module/ui/components/signIn/OxyAuthScreen.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/OxySignInPanel.d.ts +12 -12
- package/lib/typescript/module/ui/components/signIn/OxySignInPanel.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/signIn/signInMethods.d.ts +18 -15
- package/lib/typescript/module/ui/components/signIn/signInMethods.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts +4 -4
- package/lib/typescript/module/ui/hooks/useSurfaceFrameWidth.d.ts +12 -0
- package/lib/typescript/module/ui/hooks/useSurfaceFrameWidth.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/ui/components/OxyAuthChooser.tsx +7 -6
- package/src/ui/components/SurfaceScreen.tsx +19 -11
- package/src/ui/components/signIn/OxyAuthScreen.tsx +14 -5
- package/src/ui/components/signIn/OxySignInPanel.tsx +56 -37
- package/src/ui/components/signIn/signInMethods.ts +21 -17
- package/src/ui/context/OxyContext.tsx +4 -4
- package/src/ui/context/oxyContextTypes.ts +4 -4
- package/src/ui/hooks/useSurfaceFrameWidth.ts +21 -0
|
@@ -66,20 +66,29 @@ export const OxyAuthScreen: React.FC<{ children: React.ReactNode; className?: st
|
|
|
66
66
|
<View className={`w-full max-w-[448px] self-center gap-6 ${className ?? ''}`}>{children}</View>
|
|
67
67
|
);
|
|
68
68
|
|
|
69
|
-
/**
|
|
70
|
-
|
|
69
|
+
/**
|
|
70
|
+
* The split card: `children` on the left and, from `md`, `aside` on the right.
|
|
71
|
+
* `bare` inside a surface that already is the card (the account dialog): no
|
|
72
|
+
* border of its own, and the right column is a rounded tile within the
|
|
73
|
+
* dialog's margins.
|
|
74
|
+
*/
|
|
75
|
+
export const OxyAuthSplit: React.FC<{ children: React.ReactNode; aside: React.ReactNode; bare?: boolean }> = ({
|
|
76
|
+
children,
|
|
77
|
+
aside,
|
|
78
|
+
bare = false,
|
|
79
|
+
}) => {
|
|
71
80
|
const theme = useTheme();
|
|
72
81
|
return (
|
|
73
82
|
<View
|
|
74
|
-
className=
|
|
83
|
+
className={`w-full self-center md:max-w-[880px] md:flex-row ${bare ? 'md:gap-8' : 'md:rounded-3xl md:border md:border-border md:bg-card md:overflow-hidden'}`}
|
|
75
84
|
>
|
|
76
85
|
{/* Padding on an inner box: a padded flex item cannot shrink its base
|
|
77
86
|
size below its padding, which breaks the two equal tracks. */}
|
|
78
87
|
<View className="md:flex-1 md:basis-0 md:min-w-0">
|
|
79
|
-
<View className=
|
|
88
|
+
<View className={bare ? '' : 'md:p-8'}>{children}</View>
|
|
80
89
|
</View>
|
|
81
90
|
<View
|
|
82
|
-
className=
|
|
91
|
+
className={`hidden md:flex md:flex-1 md:basis-0 md:min-w-0 items-center justify-center overflow-hidden p-8 ${bare ? 'rounded-[20px]' : ''}`}
|
|
83
92
|
style={{ backgroundColor: theme.colors.backgroundSecondary }}
|
|
84
93
|
>
|
|
85
94
|
{aside}
|
|
@@ -4,18 +4,18 @@
|
|
|
4
4
|
* differs (`host`).
|
|
5
5
|
*
|
|
6
6
|
* picker a returning device: "Choose an account", then "Use another account"
|
|
7
|
-
* entry the header, the Commons way in, and the way in for
|
|
8
|
-
* account. On
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
7
|
+
* entry the header, the Commons way in, the passkey, and the way in for
|
|
8
|
+
* someone with no account. On the web, from `md`, the screen is
|
|
9
|
+
* Bloom `AuthCard`'s split card and the Commons way in is the
|
|
10
|
+
* embedded QR over its photo carousel, in the right column (the
|
|
11
|
+
* account dialog grows to that card); below `md` it is "Continue
|
|
12
|
+
* with Oxy" at the top. The passkey belongs to `oxy.so`: on
|
|
13
|
+
* auth.oxy.so (`page`) it runs here — the username with its Continue,
|
|
14
|
+
* a username-first ceremony that takes a hardware security key with
|
|
15
|
+
* no resident credential too, and the discoverable passkey — and in
|
|
16
|
+
* an app's dialog it opens auth.oxy.so's window for that one step, as
|
|
17
|
+
* does "Create account". On native it is "Continue with Oxy" ("Get
|
|
18
|
+
* Commons" without Commons).
|
|
19
19
|
*
|
|
20
20
|
* `signInMethods.ts` owns which blocks a platform gets.
|
|
21
21
|
*/
|
|
@@ -39,6 +39,7 @@ import { useOxy } from '../../context/OxyContext';
|
|
|
39
39
|
import { useAccountDialogSnapshot } from '../../hooks/accountDialogSnapshot';
|
|
40
40
|
import { useDeviceSwitcher } from '../../hooks/useDeviceSwitcher';
|
|
41
41
|
import { useI18n } from '../../hooks/useI18n';
|
|
42
|
+
import { useSurfaceFrameWidth } from '../../hooks/useSurfaceFrameWidth';
|
|
42
43
|
import { getCommonsAcquisitionUrl } from '../../utils/commonsStoreLinks';
|
|
43
44
|
import { isWebBrowser } from '../../utils/isWebBrowser';
|
|
44
45
|
import { SubtleLink } from '../authChooser/primitives';
|
|
@@ -49,6 +50,9 @@ import { OxyAuthScreen, OxyAuthScreenHeader, OxyAuthSplit, OxyAuthTerms } from '
|
|
|
49
50
|
import { RATE_LIMIT_SECONDS, describePasskeyError, isRateLimited } from './passkeyError';
|
|
50
51
|
import { resolveSignInMethods } from './signInMethods';
|
|
51
52
|
|
|
53
|
+
/** Bloom `AuthCard`'s split card width, which the account dialog grows to for it. */
|
|
54
|
+
const SPLIT_WIDTH = 880;
|
|
55
|
+
|
|
52
56
|
export interface OxySignInPanelProps {
|
|
53
57
|
/** This screen signed the origin in. */
|
|
54
58
|
onSignedIn: () => void;
|
|
@@ -124,7 +128,8 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
124
128
|
void controller?.signInWithOxy();
|
|
125
129
|
showRequest();
|
|
126
130
|
};
|
|
127
|
-
|
|
131
|
+
// The passkey is `oxy.so`'s: in an app it runs in auth.oxy.so's window.
|
|
132
|
+
const passkeyOnOxy = () => {
|
|
128
133
|
void continueOnAuth('signin').then((result) => {
|
|
129
134
|
if (result.status === 'signed-in') onSignedIn();
|
|
130
135
|
else if (result.status === 'failed') toast.error(t('signin.errors.failed'));
|
|
@@ -189,7 +194,7 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
189
194
|
return;
|
|
190
195
|
case 'failed':
|
|
191
196
|
// The pair could not be activated as it stands: sign in as it explicitly.
|
|
192
|
-
if (methods.passkey && context.handle) reauthenticate(context);
|
|
197
|
+
if (methods.passkey === 'here' && context.handle) reauthenticate(context);
|
|
193
198
|
else toast.error(t('accountSwitcher.toasts.activateFailed'));
|
|
194
199
|
return;
|
|
195
200
|
default:
|
|
@@ -198,8 +203,11 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
198
203
|
};
|
|
199
204
|
|
|
200
205
|
const showsPicker = pickerAllowed && !showForm && principals.length > 0;
|
|
201
|
-
//
|
|
206
|
+
// The web entry is the split card from `md`. In the account dialog it grows
|
|
207
|
+
// the dialog to that card; below `md` the dialog is a bottom sheet, which a
|
|
208
|
+
// width does not touch.
|
|
202
209
|
const splits = methods.commons === 'qr';
|
|
210
|
+
useSurfaceFrameWidth(host === 'dialog' && !showsPicker && splits ? SPLIT_WIDTH : null);
|
|
203
211
|
|
|
204
212
|
if (showsPicker) {
|
|
205
213
|
return (
|
|
@@ -241,7 +249,7 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
241
249
|
</Text>
|
|
242
250
|
);
|
|
243
251
|
// One solid action per screen: the username's Continue when there is one.
|
|
244
|
-
const commonsAppearance = methods.passkey ? 'outline' : 'solid';
|
|
252
|
+
const commonsAppearance = methods.passkey === 'here' ? 'outline' : 'solid';
|
|
245
253
|
|
|
246
254
|
const continueWithOxyButton = (
|
|
247
255
|
<Button
|
|
@@ -249,13 +257,28 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
249
257
|
tone={commonsAppearance === 'solid' ? 'action' : 'neutral'}
|
|
250
258
|
size="lg"
|
|
251
259
|
fullWidth
|
|
252
|
-
onPress={
|
|
260
|
+
onPress={continueWithOxy}
|
|
253
261
|
testID="continue-with-oxy"
|
|
254
262
|
>
|
|
255
263
|
{t('accountSwitcher.continueWithOxy')}
|
|
256
264
|
</Button>
|
|
257
265
|
);
|
|
258
266
|
|
|
267
|
+
const passkeyButton = (
|
|
268
|
+
<Button
|
|
269
|
+
appearance="outline"
|
|
270
|
+
tone="neutral"
|
|
271
|
+
size="lg"
|
|
272
|
+
fullWidth
|
|
273
|
+
leadingIcon={RiKey2Line}
|
|
274
|
+
disabled={methods.passkey === 'here' && blocked}
|
|
275
|
+
onPress={methods.passkey === 'here' ? () => void runPasskey(undefined, t('signin.errors.failed')) : passkeyOnOxy}
|
|
276
|
+
testID="passkey-sign-in"
|
|
277
|
+
>
|
|
278
|
+
{t('signin.methods.passkey')}
|
|
279
|
+
</Button>
|
|
280
|
+
);
|
|
281
|
+
|
|
259
282
|
const form = (
|
|
260
283
|
<OxyAuthScreen className={splits ? 'md:max-w-none' : undefined}>
|
|
261
284
|
<OxyAuthScreenHeader title={title} description={description} />
|
|
@@ -263,7 +286,7 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
263
286
|
{/* Below `md` this screen is the phone a QR would be scanned with. */}
|
|
264
287
|
{splits ? <CssView className="md:hidden">{continueWithOxyButton}</CssView> : null}
|
|
265
288
|
|
|
266
|
-
{methods.commons === 'continue'
|
|
289
|
+
{methods.commons === 'continue' ? continueWithOxyButton : null}
|
|
267
290
|
{methods.commons === 'get-commons' ? (
|
|
268
291
|
<View style={styles.stack}>
|
|
269
292
|
<Text style={[styles.note, { color: theme.colors.textSecondary }]}>{t('accountSwitcher.commonsNotInstalled')}</Text>
|
|
@@ -273,7 +296,7 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
273
296
|
</View>
|
|
274
297
|
) : null}
|
|
275
298
|
|
|
276
|
-
{methods.passkey ? (
|
|
299
|
+
{methods.passkey === 'here' ? (
|
|
277
300
|
<View style={styles.stack}>
|
|
278
301
|
<View style={styles.field}>
|
|
279
302
|
<TextFieldLabel nativeID="username-label">{t('signin.username.label')}</TextFieldLabel>
|
|
@@ -314,24 +337,17 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
314
337
|
</View>
|
|
315
338
|
) : null}
|
|
316
339
|
|
|
317
|
-
{/* The alternatives to the screen's primary way in, always under it.
|
|
318
|
-
|
|
319
|
-
{methods.
|
|
340
|
+
{/* The alternatives to the screen's primary way in, always under it. In an
|
|
341
|
+
app's dialog, from `md`, the QR beside it IS the primary way in. */}
|
|
342
|
+
{methods.passkey === 'window' ? (
|
|
343
|
+
<CssView className="md:hidden">
|
|
344
|
+
<Divider>{t('signin.orContinueWith')}</Divider>
|
|
345
|
+
</CssView>
|
|
346
|
+
) : (
|
|
347
|
+
<Divider>{t('signin.orContinueWith')}</Divider>
|
|
348
|
+
)}
|
|
320
349
|
|
|
321
|
-
{methods.passkey ? (
|
|
322
|
-
<Button
|
|
323
|
-
appearance="outline"
|
|
324
|
-
tone="neutral"
|
|
325
|
-
size="lg"
|
|
326
|
-
fullWidth
|
|
327
|
-
leadingIcon={RiKey2Line}
|
|
328
|
-
disabled={blocked}
|
|
329
|
-
onPress={() => void runPasskey(undefined, t('signin.errors.failed'))}
|
|
330
|
-
testID="passkey-sign-in"
|
|
331
|
-
>
|
|
332
|
-
{t('signin.methods.passkey')}
|
|
333
|
-
</Button>
|
|
334
|
-
) : methods.commons === 'window' ? null : (
|
|
350
|
+
{methods.passkey === 'none' ? (
|
|
335
351
|
<Button
|
|
336
352
|
appearance="outline"
|
|
337
353
|
tone="neutral"
|
|
@@ -343,9 +359,11 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
343
359
|
>
|
|
344
360
|
{t('accountSwitcher.scanQr')}
|
|
345
361
|
</Button>
|
|
362
|
+
) : (
|
|
363
|
+
passkeyButton
|
|
346
364
|
)}
|
|
347
365
|
|
|
348
|
-
{methods.passkey ? null : noAccount}
|
|
366
|
+
{methods.passkey === 'here' ? null : noAccount}
|
|
349
367
|
{onRecover ? (
|
|
350
368
|
<SubtleLink label={t('signin.recoverLink')} theme={theme} onPress={onRecover} testID="recover-link" />
|
|
351
369
|
) : null}
|
|
@@ -356,6 +374,7 @@ export const OxySignInPanel: React.FC<OxySignInPanelProps> = ({
|
|
|
356
374
|
if (!splits) return form;
|
|
357
375
|
return (
|
|
358
376
|
<OxyAuthSplit
|
|
377
|
+
bare={host === 'dialog'}
|
|
359
378
|
aside={
|
|
360
379
|
<>
|
|
361
380
|
<AuthMediaCarousel slides={SIGN_IN_SLIDES} style={StyleSheet.absoluteFill} />
|
|
@@ -1,34 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Which sign-in methods a surface offers, from where it runs.
|
|
3
3
|
*
|
|
4
|
-
* One screen everywhere
|
|
4
|
+
* One screen everywhere, and sign-in happens IN it (ADR 0029 D1):
|
|
5
5
|
*
|
|
6
|
-
* -
|
|
7
|
-
*
|
|
8
|
-
* the
|
|
9
|
-
* on
|
|
10
|
-
* -
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* device that has no Commons to continue with.
|
|
6
|
+
* - the COMMONS way in — on the web the embedded QR (the split card's right
|
|
7
|
+
* column from `md`, with "Continue with Oxy" below `md`, where the screen
|
|
8
|
+
* is the phone that would scan it); on native "Continue with Oxy", and
|
|
9
|
+
* "Get Commons" on a device that has no Commons to continue with;
|
|
10
|
+
* - the PASSKEY — it belongs to `oxy.so`, so it runs right here on auth.oxy.so
|
|
11
|
+
* (`page`: the username with its Continue, and the discoverable passkey), and
|
|
12
|
+
* in an app's dialog on the web it opens auth.oxy.so's window for that one
|
|
13
|
+
* step (with account creation); native has none, Commons holds the identity.
|
|
15
14
|
*/
|
|
16
15
|
|
|
17
16
|
import type { CommonsAvailability } from '@oxy.so/core';
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
|
-
* `qr` —
|
|
21
|
-
* `window` — an app on the web: "Continue with Oxy" opens auth.oxy.so's window.
|
|
19
|
+
* `qr` — the web: the embedded QR from `md`, "Continue with Oxy" below.
|
|
22
20
|
* `continue` — native "Continue with Oxy": Oxy picks the route (shared
|
|
23
21
|
* keychain, Commons on this device, a push, or the QR view).
|
|
24
22
|
* `get-commons` — native, and Commons is not installed here.
|
|
25
23
|
*/
|
|
26
|
-
export type CommonsEntry = 'qr' | '
|
|
24
|
+
export type CommonsEntry = 'qr' | 'continue' | 'get-commons';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* `here` — auth.oxy.so: the username and the passkey run on this page.
|
|
28
|
+
* `window` — an app's dialog on the web: the passkey opens auth.oxy.so's window.
|
|
29
|
+
* `none` — native.
|
|
30
|
+
*/
|
|
31
|
+
export type PasskeyEntry = 'here' | 'window' | 'none';
|
|
27
32
|
|
|
28
33
|
export interface SignInMethods {
|
|
29
34
|
commons: CommonsEntry;
|
|
30
|
-
|
|
31
|
-
passkey: boolean;
|
|
35
|
+
passkey: PasskeyEntry;
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
export interface SignInSurfaceFacts {
|
|
@@ -42,10 +46,10 @@ export interface SignInSurfaceFacts {
|
|
|
42
46
|
|
|
43
47
|
export function resolveSignInMethods(facts: SignInSurfaceFacts): SignInMethods {
|
|
44
48
|
if (facts.web) {
|
|
45
|
-
return
|
|
49
|
+
return { commons: 'qr', passkey: facts.host === 'page' ? 'here' : 'window' };
|
|
46
50
|
}
|
|
47
51
|
return {
|
|
48
52
|
commons: facts.commonsAvailability === 'unavailable' ? 'get-commons' : 'continue',
|
|
49
|
-
passkey:
|
|
53
|
+
passkey: 'none',
|
|
50
54
|
};
|
|
51
55
|
}
|
|
@@ -785,10 +785,10 @@ export const OxyRuntimeProvider: React.FC<OxyRuntimeProviderProps> = ({
|
|
|
785
785
|
[webAuthMode, oxyServices, clientId, authorizeBaseUrl, isIdentityBound, user?.id],
|
|
786
786
|
);
|
|
787
787
|
|
|
788
|
-
//
|
|
789
|
-
//
|
|
790
|
-
//
|
|
791
|
-
//
|
|
788
|
+
// What only auth.oxy.so can do for an Oxy app on the web — assert the
|
|
789
|
+
// `oxy.so` passkey, create or recover a passkey account — runs in its window
|
|
790
|
+
// over the app, for that one step. A blocked window falls back to the same
|
|
791
|
+
// page in this tab.
|
|
792
792
|
const continueOnAuth = useCallback(
|
|
793
793
|
(screen: OxyAuthScreen): Promise<WebOAuthSignInResult> =>
|
|
794
794
|
startWebOAuthSignInForContext({
|
|
@@ -131,10 +131,10 @@ export interface OxyContextState {
|
|
|
131
131
|
startWebOAuthSignIn: (options: StartWebOAuthSignInOptions) => Promise<WebOAuthSignInResult>;
|
|
132
132
|
|
|
133
133
|
/**
|
|
134
|
-
*
|
|
135
|
-
* (`recover`) in auth.oxy.so's window over the app
|
|
136
|
-
*
|
|
137
|
-
* opens with the gesture.
|
|
134
|
+
* The passkey sign-in (`signin`), account creation (`signup`) or recovery
|
|
135
|
+
* (`recover`) in auth.oxy.so's window over the app — the one step the
|
|
136
|
+
* account dialog cannot run itself, since the passkey belongs to `oxy.so`.
|
|
137
|
+
* Web only; call it from the press, so the window opens with the gesture.
|
|
138
138
|
*/
|
|
139
139
|
continueOnAuth: (screen: OxyAuthScreen) => Promise<WebOAuthSignInResult>;
|
|
140
140
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createContext, useContext, useLayoutEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A screen's say in how wide its surface is.
|
|
5
|
+
*
|
|
6
|
+
* `SurfaceScreen` is the ONE host that declares the surface's dialog frame, so
|
|
7
|
+
* a screen whose views need different widths (the account dialog: the 420 menu,
|
|
8
|
+
* the sign-in's 880 split card) hands its width to that host rather than
|
|
9
|
+
* declaring a second frame. A change morphs the centered card to it.
|
|
10
|
+
*/
|
|
11
|
+
export const SurfaceFrameWidthContext = createContext<((maxWidth: number | null) => void) | null>(null);
|
|
12
|
+
|
|
13
|
+
/** Ask the surface for `maxWidth` (px) while this is mounted; `null` keeps the surface's own. */
|
|
14
|
+
export function useSurfaceFrameWidth(maxWidth: number | null): void {
|
|
15
|
+
const setWidth = useContext(SurfaceFrameWidthContext);
|
|
16
|
+
useLayoutEffect(() => {
|
|
17
|
+
if (!setWidth) return;
|
|
18
|
+
setWidth(maxWidth);
|
|
19
|
+
return () => setWidth(null);
|
|
20
|
+
}, [setWidth, maxWidth]);
|
|
21
|
+
}
|