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