@oxyhq/services 21.0.1 → 22.1.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 +12 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/ui/boot/runProviderColdBoot.js +12 -2
- package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAccountDialog.js +27 -546
- package/lib/commonjs/ui/components/OxyAccountDialog.js.map +1 -1
- package/lib/commonjs/ui/components/OxyAuthChooser.js +881 -0
- package/lib/commonjs/ui/components/OxyAuthChooser.js.map +1 -0
- package/lib/commonjs/ui/components/OxyProvider.js +2 -0
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/components/oxyAuthChooserStyles.js +158 -0
- package/lib/commonjs/ui/components/oxyAuthChooserStyles.js.map +1 -0
- package/lib/commonjs/ui/context/OxyContext.js +50 -72
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/context/passkeyFlow.js +9 -8
- package/lib/commonjs/ui/context/passkeyFlow.js.map +1 -1
- package/lib/commonjs/ui/context/useOxyAccountGraph.js +6 -2
- package/lib/commonjs/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js +2 -1
- package/lib/commonjs/ui/hooks/useSwitchableAccounts.js.map +1 -1
- package/lib/commonjs/ui/utils/commonsStoreLinks.js +42 -0
- package/lib/commonjs/ui/utils/commonsStoreLinks.js.map +1 -0
- package/lib/commonjs/ui/utils/crossOriginRestore.js +26 -0
- package/lib/commonjs/ui/utils/crossOriginRestore.js.map +1 -1
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/ui/boot/runProviderColdBoot.js +13 -3
- package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/module/ui/components/OxyAccountDialog.js +29 -550
- package/lib/module/ui/components/OxyAccountDialog.js.map +1 -1
- package/lib/module/ui/components/OxyAuthChooser.js +878 -0
- package/lib/module/ui/components/OxyAuthChooser.js.map +1 -0
- package/lib/module/ui/components/OxyProvider.js +2 -0
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/components/oxyAuthChooserStyles.js +155 -0
- package/lib/module/ui/components/oxyAuthChooserStyles.js.map +1 -0
- package/lib/module/ui/context/OxyContext.js +50 -72
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/context/passkeyFlow.js +9 -8
- package/lib/module/ui/context/passkeyFlow.js.map +1 -1
- package/lib/module/ui/context/useOxyAccountGraph.js +6 -2
- package/lib/module/ui/context/useOxyAccountGraph.js.map +1 -1
- package/lib/module/ui/hooks/useSwitchableAccounts.js +2 -1
- package/lib/module/ui/hooks/useSwitchableAccounts.js.map +1 -1
- package/lib/module/ui/utils/commonsStoreLinks.js +37 -0
- package/lib/module/ui/utils/commonsStoreLinks.js.map +1 -0
- package/lib/module/ui/utils/crossOriginRestore.js +26 -1
- package/lib/module/ui/utils/crossOriginRestore.js.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +3 -1
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts +6 -0
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAccountDialog.d.ts +10 -20
- package/lib/typescript/commonjs/ui/components/OxyAccountDialog.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts +60 -0
- package/lib/typescript/commonjs/ui/components/OxyAuthChooser.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/oxyAuthChooserStyles.d.ts +148 -0
- package/lib/typescript/commonjs/ui/components/oxyAuthChooserStyles.d.ts.map +1 -0
- 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/oxyContextTypes.d.ts +22 -27
- package/lib/typescript/commonjs/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/passkeyFlow.d.ts +15 -4
- package/lib/typescript/commonjs/ui/context/passkeyFlow.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts +1 -0
- package/lib/typescript/commonjs/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/types/navigation.d.ts +8 -0
- package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/utils/commonsStoreLinks.d.ts +24 -0
- package/lib/typescript/commonjs/ui/utils/commonsStoreLinks.d.ts.map +1 -0
- package/lib/typescript/commonjs/ui/utils/crossOriginRestore.d.ts +19 -0
- package/lib/typescript/commonjs/ui/utils/crossOriginRestore.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +3 -1
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts +6 -0
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAccountDialog.d.ts +10 -20
- package/lib/typescript/module/ui/components/OxyAccountDialog.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts +60 -0
- package/lib/typescript/module/ui/components/OxyAuthChooser.d.ts.map +1 -0
- package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/oxyAuthChooserStyles.d.ts +148 -0
- package/lib/typescript/module/ui/components/oxyAuthChooserStyles.d.ts.map +1 -0
- 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/oxyContextTypes.d.ts +22 -27
- package/lib/typescript/module/ui/context/oxyContextTypes.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/passkeyFlow.d.ts +15 -4
- package/lib/typescript/module/ui/context/passkeyFlow.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts +1 -0
- package/lib/typescript/module/ui/context/useOxyAccountGraph.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/useSwitchableAccounts.d.ts.map +1 -1
- package/lib/typescript/module/ui/types/navigation.d.ts +8 -0
- package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
- package/lib/typescript/module/ui/utils/commonsStoreLinks.d.ts +24 -0
- package/lib/typescript/module/ui/utils/commonsStoreLinks.d.ts.map +1 -0
- package/lib/typescript/module/ui/utils/crossOriginRestore.d.ts +19 -0
- package/lib/typescript/module/ui/utils/crossOriginRestore.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +8 -1
- package/src/ui/boot/runProviderColdBoot.ts +24 -1
- package/src/ui/components/OxyAccountDialog.tsx +33 -571
- package/src/ui/components/OxyAuthChooser.tsx +883 -0
- package/src/ui/components/OxyProvider.tsx +2 -0
- package/src/ui/components/oxyAuthChooserStyles.ts +152 -0
- package/src/ui/context/OxyContext.tsx +56 -82
- package/src/ui/context/__tests__/passkeyFlow.test.ts +22 -15
- package/src/ui/context/oxyContextTypes.ts +27 -26
- package/src/ui/context/passkeyFlow.ts +17 -9
- package/src/ui/context/useOxyAccountGraph.ts +4 -2
- package/src/ui/hooks/useSwitchableAccounts.ts +1 -0
- package/src/ui/types/navigation.ts +8 -0
- package/src/ui/utils/__tests__/crossOriginRestore.test.ts +143 -0
- package/src/ui/utils/commonsStoreLinks.ts +38 -0
- package/src/ui/utils/crossOriginRestore.ts +34 -0
|
@@ -3,24 +3,13 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* OxyAccountDialog — the ONE unified account dialog for `@oxyhq/services`.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* (the uniform switch), the active row is flagged, and a "+ Add account" row
|
|
14
|
-
* opens the sign-in entry. This is what `ProfileButton` opens.
|
|
15
|
-
* - `add` / `signin` — the sign-in entry: primary "Sign in with Oxy" (device
|
|
16
|
-
* flow), "Scan a QR", and a secondary "Use a password" hand-off that opens
|
|
17
|
-
* auth.oxy.so (password + 2FA are NOT in the SDK).
|
|
18
|
-
* - `qr` — the cross-device QR handoff + its waiting state.
|
|
19
|
-
*
|
|
20
|
-
* Per-account color re-theming uses Bloom's `APP_COLOR_PRESETS` + `BloomColorScope`
|
|
21
|
-
* (same visual language auth.oxy.so uses). Base theming is `useTheme()` + a
|
|
22
|
-
* `StyleSheet`, so the dialog renders correctly in EVERY consumer — including apps
|
|
23
|
-
* that do not use NativeWind (e.g. the accounts app).
|
|
6
|
+
* Bloom `<Dialog>` chrome (header, back button, placement, scroll body) around
|
|
7
|
+
* the headless chooser logic, which lives in `OxyAuthChooser` — the account
|
|
8
|
+
* switcher, sign-in, sign-up, and QR views are ALL there, extracted so the
|
|
9
|
+
* same chooser can be mounted bare by a future host with no Dialog chrome
|
|
10
|
+
* (e.g. an auth.oxy.so hub page driving the cross-origin passkey popup, b2).
|
|
11
|
+
* This file owns ONLY the chrome: the title/subtitle per view, the back
|
|
12
|
+
* button, and the Bloom `<Dialog>` surface itself.
|
|
24
13
|
*
|
|
25
14
|
* The surface is Bloom's `<Dialog>` (`@oxyhq/bloom/dialog`) with a responsive
|
|
26
15
|
* `placement` — a bottom sheet on narrow viewports, a centered card on wide ones.
|
|
@@ -36,50 +25,22 @@
|
|
|
36
25
|
* Backdrop / swipe-to-dismiss is disabled (`dismissOnBackdrop={false}`) on purpose:
|
|
37
26
|
* Bloom's controlled `bottom` placement does not fire `onClose` on a gesture or
|
|
38
27
|
* backdrop dismissal, so allowing it would desync `isAccountDialogOpen` from the
|
|
39
|
-
* sheet and block reopening. The header close button (and
|
|
40
|
-
* drive `closeAccountDialog`, which flips `open` and runs the exit
|
|
28
|
+
* sheet and block reopening. The header close button (and `OxyAuthChooser`'s
|
|
29
|
+
* `onComplete`) drive `closeAccountDialog`, which flips `open` and runs the exit
|
|
30
|
+
* animation.
|
|
41
31
|
*/
|
|
42
32
|
|
|
43
|
-
import { useCallback,
|
|
33
|
+
import { useCallback, useSyncExternalStore } from 'react';
|
|
44
34
|
import { Pressable, ScrollView, StyleSheet, View } from 'react-native';
|
|
45
35
|
import { MaterialCommunityIcons } from '@expo/vector-icons';
|
|
46
|
-
import QRCode from 'react-native-qrcode-svg';
|
|
47
|
-
import { Avatar } from '@oxyhq/bloom/avatar';
|
|
48
|
-
import { Button } from '@oxyhq/bloom/button';
|
|
49
36
|
import { Dialog } from '@oxyhq/bloom/dialog';
|
|
50
37
|
import { Text } from '@oxyhq/bloom/typography';
|
|
51
|
-
import { useTheme
|
|
52
|
-
import { isOxyRpOrigin } from '@oxyhq/core';
|
|
53
|
-
import { useQueryClient } from '@tanstack/react-query';
|
|
38
|
+
import { useTheme } from '@oxyhq/bloom/theme';
|
|
54
39
|
import { useOxy } from "../context/OxyContext.js";
|
|
55
40
|
import { useI18n } from "../hooks/useI18n.js";
|
|
56
|
-
import
|
|
41
|
+
import OxyAuthChooser from "./OxyAuthChooser.js";
|
|
57
42
|
import { LogoIcon } from "./logo/LogoIcon.js";
|
|
58
|
-
|
|
59
|
-
/** Diameter of a row avatar. */
|
|
60
43
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
61
|
-
const ROW_AVATAR_SIZE = 40;
|
|
62
|
-
/** High-contrast QR colors — intentionally fixed (NOT themed) for scan reliability. */
|
|
63
|
-
const QR_PLATE_BG = '#FFFFFF';
|
|
64
|
-
const QR_FOREGROUND = '#000000';
|
|
65
|
-
const QR_SIZE = 196;
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Resolve an account's stored color (a named Bloom preset, e.g. `'purple'`) to
|
|
69
|
-
* a concrete brand hex for the row accent. Falls back to the theme primary when
|
|
70
|
-
* the account has no color or the value is not a recognized preset, so the accent
|
|
71
|
-
* renders in EVERY consumer regardless of NativeWind availability.
|
|
72
|
-
*/
|
|
73
|
-
function resolveAccentHex(color, fallback) {
|
|
74
|
-
const preset = toPreset(color);
|
|
75
|
-
return preset ? APP_COLOR_PRESETS[preset].hex : fallback;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** Narrow a stored color string to a known `AppColorName`, or `undefined`. */
|
|
79
|
-
function toPreset(color) {
|
|
80
|
-
if (!color) return undefined;
|
|
81
|
-
return APP_COLOR_NAMES.includes(color) ? color : undefined;
|
|
82
|
-
}
|
|
83
44
|
/**
|
|
84
45
|
* The unified account dialog. Mounted once by `OxyProvider`; opened imperatively
|
|
85
46
|
* via `useOxy().openAccountDialog(view?)` or imperative `openAccountDialog('signin')`.
|
|
@@ -88,93 +49,26 @@ const OxyAccountDialog = () => {
|
|
|
88
49
|
const {
|
|
89
50
|
accountDialogController: controller,
|
|
90
51
|
isAccountDialogOpen,
|
|
91
|
-
closeAccountDialog
|
|
92
|
-
showBottomSheet,
|
|
93
|
-
logoutAll,
|
|
94
|
-
refreshAccounts,
|
|
95
|
-
signInWithPasskey
|
|
52
|
+
closeAccountDialog
|
|
96
53
|
} = useOxy();
|
|
97
54
|
const theme = useTheme();
|
|
98
55
|
const {
|
|
99
56
|
t
|
|
100
57
|
} = useI18n();
|
|
101
|
-
const queryClient = useQueryClient();
|
|
102
58
|
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
// for arbitrary web origins is a later phase). This is environment-static.
|
|
107
|
-
const passkeyAvailable = useMemo(() => isWebBrowser() && isOxyRpOrigin(), []);
|
|
108
|
-
const [passkeyPending, setPasskeyPending] = useState(false);
|
|
109
|
-
const [passkeyError, setPasskeyError] = useState(null);
|
|
110
|
-
const handleSignInWithPasskey = useCallback(async () => {
|
|
111
|
-
if (passkeyPending) return;
|
|
112
|
-
setPasskeyPending(true);
|
|
113
|
-
setPasskeyError(null);
|
|
114
|
-
try {
|
|
115
|
-
await signInWithPasskey();
|
|
116
|
-
closeAccountDialog();
|
|
117
|
-
} catch (error) {
|
|
118
|
-
// A cancelled/failed ceremony keeps the dialog open so the user can retry
|
|
119
|
-
// or fall back to another method — surface a concise message, never swallow.
|
|
120
|
-
setPasskeyError(error instanceof Error && error.message ? error.message : 'Passkey sign-in failed.');
|
|
121
|
-
} finally {
|
|
122
|
-
setPasskeyPending(false);
|
|
123
|
-
}
|
|
124
|
-
}, [passkeyPending, signInWithPasskey, closeAccountDialog]);
|
|
125
|
-
|
|
126
|
-
// Bind the headless controller. `getSnapshot` returns a stable reference
|
|
127
|
-
// between changes, so it is `useSyncExternalStore`-safe. Guard the no-provider
|
|
128
|
-
// loading state (`controller` is `null`) with an inert store.
|
|
59
|
+
// A lightweight, header-only binding to the same controller `OxyAuthChooser`
|
|
60
|
+
// binds independently — cheap, and the established pattern here (
|
|
61
|
+
// `useSwitchableAccounts` also binds to this controller on its own).
|
|
129
62
|
const subscribe = useCallback(listener => controller ? controller.subscribe(listener) : () => undefined, [controller]);
|
|
130
63
|
const getSnapshot = useCallback(() => controller ? controller.getSnapshot() : EMPTY_SNAPSHOT, [controller]);
|
|
131
64
|
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
132
|
-
const [switching, setSwitching] = useState(false);
|
|
133
|
-
const handleSwitch = useCallback(async accountId => {
|
|
134
|
-
if (!controller || switching) return;
|
|
135
|
-
if (accountId === snapshot.activeAccountId) {
|
|
136
|
-
closeAccountDialog();
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
setSwitching(true);
|
|
140
|
-
try {
|
|
141
|
-
await controller.switchTo(accountId);
|
|
142
|
-
// A switch succeeds unless the controller recorded an error. On success
|
|
143
|
-
// reload the app's account graph and drop cached account-scoped data so
|
|
144
|
-
// the new active identity re-fetches — the same side effects the
|
|
145
|
-
// context's own `switchToAccount` performs.
|
|
146
|
-
if (!controller.getSnapshot().error) {
|
|
147
|
-
void refreshAccounts();
|
|
148
|
-
queryClient.invalidateQueries();
|
|
149
|
-
closeAccountDialog();
|
|
150
|
-
}
|
|
151
|
-
} finally {
|
|
152
|
-
setSwitching(false);
|
|
153
|
-
}
|
|
154
|
-
}, [controller, switching, snapshot.activeAccountId, closeAccountDialog, refreshAccounts, queryClient]);
|
|
155
|
-
const handleManage = useCallback(() => {
|
|
156
|
-
closeAccountDialog();
|
|
157
|
-
showBottomSheet?.('ManageAccount');
|
|
158
|
-
}, [closeAccountDialog, showBottomSheet]);
|
|
159
|
-
const handleSignOutAll = useCallback(() => {
|
|
160
|
-
closeAccountDialog();
|
|
161
|
-
void logoutAll();
|
|
162
|
-
}, [closeAccountDialog, logoutAll]);
|
|
163
|
-
const handlers = useMemo(() => ({
|
|
164
|
-
onSwitch: accountId => {
|
|
165
|
-
void handleSwitch(accountId);
|
|
166
|
-
},
|
|
167
|
-
onAdd: () => controller?.add(),
|
|
168
|
-
onManage: handleManage,
|
|
169
|
-
onClose: closeAccountDialog
|
|
170
|
-
}), [handleSwitch, controller, handleManage, closeAccountDialog]);
|
|
171
65
|
if (!controller) {
|
|
172
66
|
return null;
|
|
173
67
|
}
|
|
174
68
|
const {
|
|
175
69
|
view
|
|
176
70
|
} = snapshot;
|
|
177
|
-
const showBack = view === 'qr' || view === 'add' && snapshot.accounts.length > 0;
|
|
71
|
+
const showBack = view === 'qr' || view === 'signup' || view === 'add' && snapshot.accounts.length > 0;
|
|
178
72
|
return /*#__PURE__*/_jsxs(Dialog, {
|
|
179
73
|
open: isAccountDialogOpen,
|
|
180
74
|
onClose: closeAccountDialog,
|
|
@@ -196,27 +90,8 @@ const OxyAccountDialog = () => {
|
|
|
196
90
|
style: styles.body,
|
|
197
91
|
contentContainerStyle: styles.bodyContent,
|
|
198
92
|
showsVerticalScrollIndicator: false,
|
|
199
|
-
children:
|
|
200
|
-
|
|
201
|
-
theme: theme,
|
|
202
|
-
t: t,
|
|
203
|
-
handlers: handlers
|
|
204
|
-
}) : view === 'qr' ? /*#__PURE__*/_jsx(QrView, {
|
|
205
|
-
snapshot: snapshot,
|
|
206
|
-
theme: theme,
|
|
207
|
-
t: t,
|
|
208
|
-
onRetry: () => void controller.showQr()
|
|
209
|
-
}) : /*#__PURE__*/_jsx(SignInView, {
|
|
210
|
-
snapshot: snapshot,
|
|
211
|
-
theme: theme,
|
|
212
|
-
t: t,
|
|
213
|
-
handlers: handlers,
|
|
214
|
-
onSignInWithOxy: () => void controller.signInWithOxy(),
|
|
215
|
-
onScanQr: () => void controller.showQr(),
|
|
216
|
-
onUsePassword: () => void controller.openPasswordAtOxyAuth(),
|
|
217
|
-
onSignInWithPasskey: passkeyAvailable ? () => void handleSignInWithPasskey() : undefined,
|
|
218
|
-
passkeyPending: passkeyPending,
|
|
219
|
-
passkeyError: passkeyError
|
|
93
|
+
children: /*#__PURE__*/_jsx(OxyAuthChooser, {
|
|
94
|
+
onComplete: closeAccountDialog
|
|
220
95
|
})
|
|
221
96
|
})]
|
|
222
97
|
});
|
|
@@ -288,291 +163,6 @@ const DialogHeader = ({
|
|
|
288
163
|
});
|
|
289
164
|
};
|
|
290
165
|
|
|
291
|
-
// ---------------------------------------------------------------------------
|
|
292
|
-
// Accounts view
|
|
293
|
-
// ---------------------------------------------------------------------------
|
|
294
|
-
|
|
295
|
-
const AccountsView = ({
|
|
296
|
-
snapshot,
|
|
297
|
-
theme,
|
|
298
|
-
t,
|
|
299
|
-
handlers
|
|
300
|
-
}) => {
|
|
301
|
-
if (snapshot.loading && snapshot.accounts.length === 0) {
|
|
302
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
303
|
-
style: styles.centeredBlock,
|
|
304
|
-
children: [/*#__PURE__*/_jsx(MaterialCommunityIcons, {
|
|
305
|
-
name: "loading",
|
|
306
|
-
size: 24,
|
|
307
|
-
color: theme.colors.textSecondary
|
|
308
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
309
|
-
style: [styles.mutedText, {
|
|
310
|
-
color: theme.colors.textSecondary
|
|
311
|
-
}],
|
|
312
|
-
children: t('accountSwitcher.loading') || 'Loading accounts…'
|
|
313
|
-
})]
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
317
|
-
style: styles.rows,
|
|
318
|
-
children: [snapshot.accounts.map(account => /*#__PURE__*/_jsx(AccountRow, {
|
|
319
|
-
account: account,
|
|
320
|
-
theme: theme,
|
|
321
|
-
switching: snapshot.switchingAccountId === account.accountId,
|
|
322
|
-
disabled: snapshot.switchingAccountId !== null,
|
|
323
|
-
onPress: () => handlers.onSwitch(account.accountId)
|
|
324
|
-
}, account.accountId)), /*#__PURE__*/_jsxs(Pressable, {
|
|
325
|
-
style: [styles.addRow, {
|
|
326
|
-
borderColor: theme.colors.border
|
|
327
|
-
}],
|
|
328
|
-
onPress: handlers.onAdd,
|
|
329
|
-
accessibilityRole: "button",
|
|
330
|
-
accessibilityLabel: t('signin.addAccountTitle') || 'Add another account',
|
|
331
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
332
|
-
style: [styles.addBadge, {
|
|
333
|
-
borderColor: theme.colors.border
|
|
334
|
-
}],
|
|
335
|
-
children: /*#__PURE__*/_jsx(MaterialCommunityIcons, {
|
|
336
|
-
name: "plus",
|
|
337
|
-
size: 20,
|
|
338
|
-
color: theme.colors.textSecondary
|
|
339
|
-
})
|
|
340
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
341
|
-
style: [styles.rowName, {
|
|
342
|
-
color: theme.colors.textSecondary
|
|
343
|
-
}],
|
|
344
|
-
children: t('signin.addAccountTitle') || 'Add another account'
|
|
345
|
-
})]
|
|
346
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
347
|
-
style: styles.footerLinks,
|
|
348
|
-
children: /*#__PURE__*/_jsx(Pressable, {
|
|
349
|
-
onPress: handlers.onManage,
|
|
350
|
-
accessibilityRole: "button",
|
|
351
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
352
|
-
style: [styles.linkText, {
|
|
353
|
-
color: theme.colors.primary
|
|
354
|
-
}],
|
|
355
|
-
children: t('accountMenu.manage') || 'Manage accounts'
|
|
356
|
-
})
|
|
357
|
-
})
|
|
358
|
-
})]
|
|
359
|
-
});
|
|
360
|
-
};
|
|
361
|
-
const AccountRow = ({
|
|
362
|
-
account,
|
|
363
|
-
theme,
|
|
364
|
-
switching,
|
|
365
|
-
disabled,
|
|
366
|
-
onPress
|
|
367
|
-
}) => {
|
|
368
|
-
const accent = resolveAccentHex(account.color, theme.colors.primary);
|
|
369
|
-
const rowStyle = [styles.accountRow, {
|
|
370
|
-
borderColor: account.isCurrent ? accent : theme.colors.border,
|
|
371
|
-
backgroundColor: theme.colors.card
|
|
372
|
-
}, disabled && !switching ? styles.rowDisabled : null];
|
|
373
|
-
return /*#__PURE__*/_jsx(BloomColorScope, {
|
|
374
|
-
colorPreset: toPreset(account.color),
|
|
375
|
-
asChild: true,
|
|
376
|
-
children: /*#__PURE__*/_jsxs(Pressable, {
|
|
377
|
-
style: rowStyle,
|
|
378
|
-
onPress: onPress,
|
|
379
|
-
disabled: disabled,
|
|
380
|
-
accessibilityRole: "button",
|
|
381
|
-
accessibilityState: {
|
|
382
|
-
selected: account.isCurrent,
|
|
383
|
-
disabled
|
|
384
|
-
},
|
|
385
|
-
accessibilityLabel: account.displayName,
|
|
386
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
387
|
-
style: [styles.avatarRing, {
|
|
388
|
-
borderColor: account.isCurrent ? accent : 'transparent'
|
|
389
|
-
}],
|
|
390
|
-
children: /*#__PURE__*/_jsx(Avatar, {
|
|
391
|
-
source: account.avatarUrl ?? undefined,
|
|
392
|
-
variant: "thumb",
|
|
393
|
-
name: account.displayName,
|
|
394
|
-
size: ROW_AVATAR_SIZE
|
|
395
|
-
})
|
|
396
|
-
}), /*#__PURE__*/_jsxs(View, {
|
|
397
|
-
style: styles.rowMeta,
|
|
398
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
399
|
-
style: [styles.rowName, {
|
|
400
|
-
color: theme.colors.text
|
|
401
|
-
}],
|
|
402
|
-
numberOfLines: 1,
|
|
403
|
-
children: account.displayName
|
|
404
|
-
}), account.email ? /*#__PURE__*/_jsx(Text, {
|
|
405
|
-
style: [styles.rowHandle, {
|
|
406
|
-
color: theme.colors.textSecondary
|
|
407
|
-
}],
|
|
408
|
-
numberOfLines: 1,
|
|
409
|
-
children: account.email
|
|
410
|
-
}) : null]
|
|
411
|
-
}), switching ? /*#__PURE__*/_jsx(MaterialCommunityIcons, {
|
|
412
|
-
name: "loading",
|
|
413
|
-
size: 20,
|
|
414
|
-
color: accent
|
|
415
|
-
}) : account.isCurrent ? /*#__PURE__*/_jsx(MaterialCommunityIcons, {
|
|
416
|
-
name: "check-circle",
|
|
417
|
-
size: 20,
|
|
418
|
-
color: accent
|
|
419
|
-
}) : /*#__PURE__*/_jsx(MaterialCommunityIcons, {
|
|
420
|
-
name: "chevron-right",
|
|
421
|
-
size: 20,
|
|
422
|
-
color: theme.colors.textSecondary
|
|
423
|
-
})]
|
|
424
|
-
})
|
|
425
|
-
});
|
|
426
|
-
};
|
|
427
|
-
|
|
428
|
-
// ---------------------------------------------------------------------------
|
|
429
|
-
// Sign-in view (add / signin)
|
|
430
|
-
// ---------------------------------------------------------------------------
|
|
431
|
-
|
|
432
|
-
const SignInView = ({
|
|
433
|
-
snapshot,
|
|
434
|
-
theme,
|
|
435
|
-
t,
|
|
436
|
-
handlers,
|
|
437
|
-
onSignInWithOxy,
|
|
438
|
-
onScanQr,
|
|
439
|
-
onUsePassword,
|
|
440
|
-
onSignInWithPasskey,
|
|
441
|
-
passkeyPending,
|
|
442
|
-
passkeyError
|
|
443
|
-
}) => /*#__PURE__*/_jsxs(View, {
|
|
444
|
-
style: styles.signInBlock,
|
|
445
|
-
children: [snapshot.accounts.length > 0 ? /*#__PURE__*/_jsxs(View, {
|
|
446
|
-
style: styles.rows,
|
|
447
|
-
children: [snapshot.accounts.map(account => /*#__PURE__*/_jsx(AccountRow, {
|
|
448
|
-
account: account,
|
|
449
|
-
theme: theme,
|
|
450
|
-
switching: snapshot.switchingAccountId === account.accountId,
|
|
451
|
-
disabled: snapshot.switchingAccountId !== null,
|
|
452
|
-
onPress: () => handlers.onSwitch(account.accountId)
|
|
453
|
-
}, account.accountId)), /*#__PURE__*/_jsx(Dividerish, {
|
|
454
|
-
theme: theme,
|
|
455
|
-
label: t('signin.or') || 'or'
|
|
456
|
-
})]
|
|
457
|
-
}) : null, /*#__PURE__*/_jsx(Button, {
|
|
458
|
-
variant: "primary",
|
|
459
|
-
onPress: onSignInWithOxy,
|
|
460
|
-
style: styles.primaryButton,
|
|
461
|
-
children: "Sign in with Oxy"
|
|
462
|
-
}), onSignInWithPasskey ? /*#__PURE__*/_jsx(Button, {
|
|
463
|
-
variant: "secondary",
|
|
464
|
-
onPress: onSignInWithPasskey,
|
|
465
|
-
disabled: passkeyPending,
|
|
466
|
-
style: styles.secondaryButton,
|
|
467
|
-
testID: "passkey-signin-button",
|
|
468
|
-
children: passkeyPending ? t('accountSwitcher.passkeySigningIn') || 'Signing in…' : t('accountSwitcher.signInWithPasskey') || 'Sign in with a passkey'
|
|
469
|
-
}) : null, passkeyError ? /*#__PURE__*/_jsx(Text, {
|
|
470
|
-
style: [styles.errorText, {
|
|
471
|
-
color: theme.colors.error
|
|
472
|
-
}],
|
|
473
|
-
children: passkeyError
|
|
474
|
-
}) : null, /*#__PURE__*/_jsx(Button, {
|
|
475
|
-
variant: "secondary",
|
|
476
|
-
onPress: onScanQr,
|
|
477
|
-
style: styles.secondaryButton,
|
|
478
|
-
children: t('accountSwitcher.scanQr') || 'Scan a QR from another device'
|
|
479
|
-
}), /*#__PURE__*/_jsx(Pressable, {
|
|
480
|
-
onPress: onUsePassword,
|
|
481
|
-
accessibilityRole: "button",
|
|
482
|
-
style: styles.passwordLink,
|
|
483
|
-
children: /*#__PURE__*/_jsx(Text, {
|
|
484
|
-
style: [styles.linkText, {
|
|
485
|
-
color: theme.colors.textSecondary
|
|
486
|
-
}],
|
|
487
|
-
children: "Use a password instead"
|
|
488
|
-
})
|
|
489
|
-
})]
|
|
490
|
-
});
|
|
491
|
-
const Dividerish = ({
|
|
492
|
-
theme,
|
|
493
|
-
label
|
|
494
|
-
}) => /*#__PURE__*/_jsxs(View, {
|
|
495
|
-
style: styles.dividerRow,
|
|
496
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
497
|
-
style: [styles.dividerLine, {
|
|
498
|
-
backgroundColor: theme.colors.border
|
|
499
|
-
}]
|
|
500
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
501
|
-
style: [styles.dividerText, {
|
|
502
|
-
color: theme.colors.textSecondary
|
|
503
|
-
}],
|
|
504
|
-
children: label
|
|
505
|
-
}), /*#__PURE__*/_jsx(View, {
|
|
506
|
-
style: [styles.dividerLine, {
|
|
507
|
-
backgroundColor: theme.colors.border
|
|
508
|
-
}]
|
|
509
|
-
})]
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
// ---------------------------------------------------------------------------
|
|
513
|
-
// QR view
|
|
514
|
-
// ---------------------------------------------------------------------------
|
|
515
|
-
|
|
516
|
-
const QrView = ({
|
|
517
|
-
snapshot,
|
|
518
|
-
theme,
|
|
519
|
-
t,
|
|
520
|
-
onRetry
|
|
521
|
-
}) => {
|
|
522
|
-
const {
|
|
523
|
-
signIn
|
|
524
|
-
} = snapshot;
|
|
525
|
-
if (signIn.phase === 'error') {
|
|
526
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
527
|
-
style: styles.centeredBlock,
|
|
528
|
-
children: [/*#__PURE__*/_jsx(Text, {
|
|
529
|
-
style: [styles.errorText, {
|
|
530
|
-
color: theme.colors.error
|
|
531
|
-
}],
|
|
532
|
-
children: signIn.error || 'Sign-in failed. Please try again.'
|
|
533
|
-
}), /*#__PURE__*/_jsx(Button, {
|
|
534
|
-
variant: "secondary",
|
|
535
|
-
onPress: onRetry,
|
|
536
|
-
style: styles.secondaryButton,
|
|
537
|
-
children: t('common.actions.tryAgain') || 'Try again'
|
|
538
|
-
})]
|
|
539
|
-
});
|
|
540
|
-
}
|
|
541
|
-
if (signIn.phase === 'starting' || signIn.phase === 'authorized' || !signIn.qrPayload) {
|
|
542
|
-
const label = signIn.phase === 'authorized' ? t('signin.status.signingIn') || 'Signing in…' : t('accountSwitcher.loading') || 'Preparing sign-in…';
|
|
543
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
544
|
-
style: styles.centeredBlock,
|
|
545
|
-
children: [/*#__PURE__*/_jsx(MaterialCommunityIcons, {
|
|
546
|
-
name: "loading",
|
|
547
|
-
size: 26,
|
|
548
|
-
color: theme.colors.primary
|
|
549
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
550
|
-
style: [styles.mutedText, {
|
|
551
|
-
color: theme.colors.textSecondary
|
|
552
|
-
}],
|
|
553
|
-
children: label
|
|
554
|
-
})]
|
|
555
|
-
});
|
|
556
|
-
}
|
|
557
|
-
return /*#__PURE__*/_jsxs(View, {
|
|
558
|
-
style: styles.centeredBlock,
|
|
559
|
-
children: [/*#__PURE__*/_jsx(View, {
|
|
560
|
-
style: styles.qrPlate,
|
|
561
|
-
children: /*#__PURE__*/_jsx(QRCode, {
|
|
562
|
-
value: signIn.qrPayload,
|
|
563
|
-
size: QR_SIZE,
|
|
564
|
-
backgroundColor: QR_PLATE_BG,
|
|
565
|
-
color: QR_FOREGROUND
|
|
566
|
-
})
|
|
567
|
-
}), /*#__PURE__*/_jsx(Text, {
|
|
568
|
-
style: [styles.mutedText, {
|
|
569
|
-
color: theme.colors.textSecondary
|
|
570
|
-
}],
|
|
571
|
-
children: t('accountSwitcher.scanWithOxy') || 'Scan with any Oxy app and approve.'
|
|
572
|
-
})]
|
|
573
|
-
});
|
|
574
|
-
};
|
|
575
|
-
|
|
576
166
|
// ---------------------------------------------------------------------------
|
|
577
167
|
// Copy + helpers
|
|
578
168
|
// ---------------------------------------------------------------------------
|
|
@@ -586,8 +176,13 @@ function headerCopy(view, accountCount, t) {
|
|
|
586
176
|
};
|
|
587
177
|
case 'qr':
|
|
588
178
|
return {
|
|
589
|
-
title: t('accountSwitcher.scanTitle') || '
|
|
590
|
-
subtitle: t('accountSwitcher.scanSubtitle') || '
|
|
179
|
+
title: t('accountSwitcher.scanTitle') || 'Sign in with Oxy',
|
|
180
|
+
subtitle: t('accountSwitcher.scanSubtitle') || 'Scan with Commons on your phone.'
|
|
181
|
+
};
|
|
182
|
+
case 'signup':
|
|
183
|
+
return {
|
|
184
|
+
title: t('signup.title') || 'Create your account',
|
|
185
|
+
subtitle: t('signup.subtitle') || 'One identity for the whole ecosystem.'
|
|
591
186
|
};
|
|
592
187
|
default:
|
|
593
188
|
return accountCount > 0 ? {
|
|
@@ -599,12 +194,6 @@ function headerCopy(view, accountCount, t) {
|
|
|
599
194
|
};
|
|
600
195
|
}
|
|
601
196
|
}
|
|
602
|
-
|
|
603
|
-
/** Current document URL on web; `undefined` where `location` is absent. */
|
|
604
|
-
function currentHref() {
|
|
605
|
-
const location = globalThis.location;
|
|
606
|
-
return typeof location?.href === 'string' ? location.href : undefined;
|
|
607
|
-
}
|
|
608
197
|
const EMPTY_SNAPSHOT = {
|
|
609
198
|
view: 'accounts',
|
|
610
199
|
accounts: [],
|
|
@@ -618,7 +207,8 @@ const EMPTY_SNAPSHOT = {
|
|
|
618
207
|
qrPayload: null,
|
|
619
208
|
expiresAt: null,
|
|
620
209
|
error: null
|
|
621
|
-
}
|
|
210
|
+
},
|
|
211
|
+
commonsAvailability: 'unknown'
|
|
622
212
|
};
|
|
623
213
|
const styles = StyleSheet.create({
|
|
624
214
|
header: {
|
|
@@ -658,117 +248,6 @@ const styles = StyleSheet.create({
|
|
|
658
248
|
bodyContent: {
|
|
659
249
|
paddingTop: 4,
|
|
660
250
|
paddingBottom: 4
|
|
661
|
-
},
|
|
662
|
-
rows: {
|
|
663
|
-
width: '100%',
|
|
664
|
-
gap: 8
|
|
665
|
-
},
|
|
666
|
-
accountRow: {
|
|
667
|
-
flexDirection: 'row',
|
|
668
|
-
alignItems: 'center',
|
|
669
|
-
gap: 12,
|
|
670
|
-
borderWidth: StyleSheet.hairlineWidth,
|
|
671
|
-
borderRadius: 16,
|
|
672
|
-
paddingVertical: 10,
|
|
673
|
-
paddingHorizontal: 12
|
|
674
|
-
},
|
|
675
|
-
rowDisabled: {
|
|
676
|
-
opacity: 0.6
|
|
677
|
-
},
|
|
678
|
-
avatarRing: {
|
|
679
|
-
borderRadius: 9999,
|
|
680
|
-
borderWidth: 2,
|
|
681
|
-
padding: 1
|
|
682
|
-
},
|
|
683
|
-
rowMeta: {
|
|
684
|
-
flex: 1,
|
|
685
|
-
minWidth: 0
|
|
686
|
-
},
|
|
687
|
-
rowName: {
|
|
688
|
-
fontSize: 15,
|
|
689
|
-
fontWeight: '600'
|
|
690
|
-
},
|
|
691
|
-
rowHandle: {
|
|
692
|
-
fontSize: 12.5,
|
|
693
|
-
marginTop: 1
|
|
694
|
-
},
|
|
695
|
-
addRow: {
|
|
696
|
-
flexDirection: 'row',
|
|
697
|
-
alignItems: 'center',
|
|
698
|
-
gap: 12,
|
|
699
|
-
borderWidth: StyleSheet.hairlineWidth,
|
|
700
|
-
borderStyle: 'dashed',
|
|
701
|
-
borderRadius: 16,
|
|
702
|
-
paddingVertical: 10,
|
|
703
|
-
paddingHorizontal: 12,
|
|
704
|
-
marginTop: 8
|
|
705
|
-
},
|
|
706
|
-
addBadge: {
|
|
707
|
-
width: ROW_AVATAR_SIZE,
|
|
708
|
-
height: ROW_AVATAR_SIZE,
|
|
709
|
-
borderRadius: 9999,
|
|
710
|
-
borderWidth: StyleSheet.hairlineWidth,
|
|
711
|
-
alignItems: 'center',
|
|
712
|
-
justifyContent: 'center'
|
|
713
|
-
},
|
|
714
|
-
footerLinks: {
|
|
715
|
-
alignItems: 'center',
|
|
716
|
-
marginTop: 16
|
|
717
|
-
},
|
|
718
|
-
linkText: {
|
|
719
|
-
fontSize: 14,
|
|
720
|
-
fontWeight: '600'
|
|
721
|
-
},
|
|
722
|
-
signInBlock: {
|
|
723
|
-
width: '100%'
|
|
724
|
-
},
|
|
725
|
-
primaryButton: {
|
|
726
|
-
width: '100%',
|
|
727
|
-
borderRadius: 14,
|
|
728
|
-
marginTop: 8
|
|
729
|
-
},
|
|
730
|
-
secondaryButton: {
|
|
731
|
-
width: '100%',
|
|
732
|
-
borderRadius: 14,
|
|
733
|
-
marginTop: 10
|
|
734
|
-
},
|
|
735
|
-
passwordLink: {
|
|
736
|
-
alignSelf: 'center',
|
|
737
|
-
paddingVertical: 10,
|
|
738
|
-
marginTop: 12
|
|
739
|
-
},
|
|
740
|
-
dividerRow: {
|
|
741
|
-
flexDirection: 'row',
|
|
742
|
-
alignItems: 'center',
|
|
743
|
-
width: '100%',
|
|
744
|
-
marginVertical: 14
|
|
745
|
-
},
|
|
746
|
-
dividerLine: {
|
|
747
|
-
flex: 1,
|
|
748
|
-
height: StyleSheet.hairlineWidth
|
|
749
|
-
},
|
|
750
|
-
dividerText: {
|
|
751
|
-
marginHorizontal: 12,
|
|
752
|
-
fontSize: 13
|
|
753
|
-
},
|
|
754
|
-
centeredBlock: {
|
|
755
|
-
alignItems: 'center',
|
|
756
|
-
justifyContent: 'center',
|
|
757
|
-
gap: 12,
|
|
758
|
-
paddingVertical: 24
|
|
759
|
-
},
|
|
760
|
-
mutedText: {
|
|
761
|
-
fontSize: 14,
|
|
762
|
-
textAlign: 'center'
|
|
763
|
-
},
|
|
764
|
-
errorText: {
|
|
765
|
-
fontSize: 14,
|
|
766
|
-
textAlign: 'center'
|
|
767
|
-
},
|
|
768
|
-
qrPlate: {
|
|
769
|
-
padding: 16,
|
|
770
|
-
borderRadius: 16,
|
|
771
|
-
backgroundColor: QR_PLATE_BG
|
|
772
251
|
}
|
|
773
252
|
});
|
|
774
253
|
export default OxyAccountDialog;
|