@domino-sdk/relay 1.6.0 → 1.9.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 (35) hide show
  1. package/dist/action-9Xl0StBg.d.ts +33 -0
  2. package/dist/{activity-mPUN4v5Q.d.ts → activity-CCe1e_7a.d.ts} +115 -115
  3. package/dist/authoring.d.ts +45 -45
  4. package/dist/authoring.js +1 -1
  5. package/dist/{automatic-outcomes-DmuqzQiH.d.ts → automatic-outcomes-Dyk0VTUs.d.ts} +1 -1
  6. package/dist/browser.d.ts +7 -6
  7. package/dist/browser.js +5 -4
  8. package/dist/{chunk-C25TDFHN.js → chunk-2N464DDE.js} +1 -1
  9. package/dist/{chunk-GMPPLZKH.js → chunk-2V74CHDL.js} +191 -191
  10. package/dist/{chunk-6RBF3UDA.js → chunk-734LYHWP.js} +2 -2
  11. package/dist/chunk-ETPL6AAJ.js +98 -0
  12. package/dist/{chunk-ZOUEEK5L.js → chunk-FWSSMSIA.js} +49 -21
  13. package/dist/{chunk-BFZHJA2B.js → chunk-L3QFIRGO.js} +2 -2
  14. package/dist/{client-CQuTAjSs.d.ts → client-CbASImpi.d.ts} +42 -36
  15. package/dist/{groups-D08JtrZu.d.ts → groups-Dvmd4CYk.d.ts} +1 -1
  16. package/dist/{images-D7en0Awp.d.ts → images-Dekv3DYF.d.ts} +1 -1
  17. package/dist/index.d.ts +8 -7
  18. package/dist/index.js +7 -3
  19. package/dist/internal.d.ts +5 -119
  20. package/dist/internal.js +1 -1
  21. package/dist/{offers-BENZl5l5.d.ts → offers-BEGVSOTD.d.ts} +2 -2
  22. package/dist/{pages-CBa-_u_n.d.ts → pages-HmS6eedC.d.ts} +38 -38
  23. package/dist/react/index.d.ts +197 -10
  24. package/dist/react/index.js +253 -111
  25. package/dist/referral-reward-kinds-Flbb1D2q.d.ts +171 -0
  26. package/dist/{schema-BPdHSCAc.d.ts → schema-BCuiO7eD.d.ts} +16 -16
  27. package/dist/schema.d.ts +8 -8
  28. package/dist/schema.js +3 -3
  29. package/dist/server/index.d.ts +592 -45
  30. package/dist/server/index.js +209 -49
  31. package/dist/{sign-in-options-FPKoZMfo.d.ts → sign-in-options-z3wsfizk.d.ts} +2 -2
  32. package/dist/{submission-input-BzIWutQi.d.ts → submission-input-Bs14jIsu.d.ts} +2 -2
  33. package/package.json +1 -1
  34. package/dist/identity-integration-BPovus6e.d.ts +0 -58
  35. /package/dist/{chunk-QGLHZ2E5.js → chunk-5ECM6AWV.js} +0 -0
@@ -8,16 +8,22 @@ import {
8
8
  discoverSolanaWallets,
9
9
  readSignInMethods,
10
10
  signInOptionsFrom
11
- } from "../chunk-BFZHJA2B.js";
12
- import "../chunk-ZOUEEK5L.js";
13
- import "../chunk-C25TDFHN.js";
11
+ } from "../chunk-L3QFIRGO.js";
12
+ import "../chunk-FWSSMSIA.js";
14
13
  import {
14
+ createRelayAction
15
+ } from "../chunk-ETPL6AAJ.js";
16
+ import "../chunk-2N464DDE.js";
17
+ import {
18
+ RelayError,
15
19
  RelayErrorCode,
16
20
  asRelayError,
17
21
  isRelayError
18
- } from "../chunk-GMPPLZKH.js";
22
+ } from "../chunk-2V74CHDL.js";
19
23
  import "../chunk-6QJ3LADK.js";
20
- import "../chunk-NPGENI7K.js";
24
+ import {
25
+ isRequestError
26
+ } from "../chunk-NPGENI7K.js";
21
27
  import {
22
28
  sessionMember
23
29
  } from "../chunk-6ON4RMEN.js";
@@ -385,13 +391,82 @@ function useActivity(options = {}) {
385
391
  return { ...value, refresh };
386
392
  }
387
393
 
394
+ // src/react/action.ts
395
+ import { useCallback as useCallback2, useRef as useRef2, useState as useState2 } from "react";
396
+ function useRelayAction(scope) {
397
+ const owner = useRef2({ scope, action: createRelayAction() });
398
+ const [state, setState] = useState2({
399
+ owner: owner.current,
400
+ busy: false,
401
+ error: null
402
+ });
403
+ if (owner.current.scope !== scope) {
404
+ owner.current = { scope, action: createRelayAction() };
405
+ }
406
+ const execute = useCallback2(
407
+ async (task) => {
408
+ const current2 = owner.current;
409
+ if (current2.action.busy) {
410
+ return { ok: false };
411
+ }
412
+ const result = task ? current2.action.run(task) : current2.action.retry();
413
+ setState({ owner: current2, busy: true, error: null });
414
+ try {
415
+ const value = await result;
416
+ if (owner.current === current2) {
417
+ setState({ owner: current2, busy: false, error: null });
418
+ }
419
+ return { ok: true, value };
420
+ } catch (caught) {
421
+ if (owner.current === current2) {
422
+ const cause = current2.action.error ?? caught;
423
+ const error = isRequestError(cause) && !isRelayError(cause) ? new RelayError(cause.message, {
424
+ kind: cause.kind,
425
+ status: cause.status,
426
+ code: cause.code,
427
+ uncertain: cause.uncertain,
428
+ operationId: cause.operationId,
429
+ replaySafe: cause.recovery === "replay",
430
+ retryAfter: cause.retryAfter,
431
+ cause
432
+ }) : asRelayError(cause);
433
+ setState({
434
+ owner: current2,
435
+ busy: false,
436
+ error
437
+ });
438
+ }
439
+ return { ok: false };
440
+ }
441
+ },
442
+ []
443
+ );
444
+ const retry = useCallback2(() => execute(), [execute]);
445
+ const settle = useCallback2(() => {
446
+ const current2 = owner.current;
447
+ if (!current2.action.settle()) {
448
+ return false;
449
+ }
450
+ setState({ owner: current2, busy: false, error: null });
451
+ return true;
452
+ }, []);
453
+ const current = state.owner === owner.current ? state : { busy: false, error: null };
454
+ return {
455
+ run: execute,
456
+ retry,
457
+ settle,
458
+ busy: current.busy,
459
+ error: current.error
460
+ };
461
+ }
462
+
388
463
  // src/react/provider.tsx
389
464
  import { useEffect as useEffect3, useMemo as useMemo2 } from "react";
390
465
 
391
466
  // src/react/experience.tsx
392
467
  import {
393
468
  createContext as createContext2,
394
- useCallback as useCallback2,
469
+ useCallback as useCallback3,
395
470
  useContext as useContext2,
396
471
  useMemo,
397
472
  useSyncExternalStore as useSyncExternalStore2
@@ -503,10 +578,10 @@ function watchRoleDeliveries(options) {
503
578
  }
504
579
 
505
580
  // src/react/use-role-deliveries.ts
506
- import { useEffect as useEffect2, useState as useState2 } from "react";
581
+ import { useEffect as useEffect2, useState as useState3 } from "react";
507
582
  var NONE = Object.freeze([]);
508
583
  function useRoleDeliveries(relay, member, completions) {
509
- const [state, setState] = useState2(null);
584
+ const [state, setState] = useState3(null);
510
585
  useEffect2(() => {
511
586
  if (!member || !completions) {
512
587
  return;
@@ -562,7 +637,7 @@ function useExperience() {
562
637
  const store = useStore();
563
638
  const experience = useExperienceSnapshot();
564
639
  const deliveries = useContext2(Deliveries);
565
- const refresh = useCallback2(() => store.refreshExperience(), [store]);
640
+ const refresh = useCallback3(() => store.refreshExperience(), [store]);
566
641
  return useMemo(
567
642
  () => ({ ...experience, refresh, deliveries }),
568
643
  [experience, refresh, deliveries]
@@ -1175,16 +1250,16 @@ function RelayProvider({
1175
1250
 
1176
1251
  // src/react/query.ts
1177
1252
  import {
1178
- useCallback as useCallback3,
1253
+ useCallback as useCallback4,
1179
1254
  useEffect as useEffect4,
1180
1255
  useLayoutEffect as useLayoutEffect2,
1181
1256
  useMemo as useMemo3,
1182
- useRef as useRef2,
1183
- useState as useState3,
1257
+ useRef as useRef3,
1258
+ useState as useState4,
1184
1259
  useSyncExternalStore as useSyncExternalStore3
1185
1260
  } from "react";
1186
1261
  function useLatest(value) {
1187
- const ref = useRef2(value);
1262
+ const ref = useRef3(value);
1188
1263
  useLayoutEffect2(() => {
1189
1264
  ref.current = value;
1190
1265
  });
@@ -1202,7 +1277,7 @@ function useCached(cache, key, read, options) {
1202
1277
  const hash = key ? hashKey(key) : null;
1203
1278
  const { intervalMs, refetchOnFocus } = options;
1204
1279
  const latest = useLatest({ key, read });
1205
- const subscribe = useCallback3(
1280
+ const subscribe = useCallback4(
1206
1281
  (listener) => {
1207
1282
  const { key: current } = latest.current;
1208
1283
  if (!cache || !current) {
@@ -1221,7 +1296,7 @@ function useCached(cache, key, read, options) {
1221
1296
  );
1222
1297
  const get = () => cache && key ? cache.snapshot(key) : LOADING;
1223
1298
  const snapshot = useSyncExternalStore3(subscribe, get, get);
1224
- const refresh = useCallback3(
1299
+ const refresh = useCallback4(
1225
1300
  () => cache && latest.current.key ? cache.refresh(latest.current.key) : Promise.resolve(),
1226
1301
  [cache, hash]
1227
1302
  );
@@ -1269,8 +1344,8 @@ function usePages(cache, key, read, options) {
1269
1344
  (context) => read(context),
1270
1345
  options
1271
1346
  );
1272
- const [more, setMore] = useState3(null);
1273
- const busy = useRef2(null);
1347
+ const [more, setMore] = useState4(null);
1348
+ const busy = useRef3(null);
1274
1349
  const latest = useLatest({ key, read, query });
1275
1350
  useEffect4(
1276
1351
  () => () => {
@@ -1279,7 +1354,7 @@ function usePages(cache, key, read, options) {
1279
1354
  },
1280
1355
  [cache, hash]
1281
1356
  );
1282
- const loadMore = useCallback3(async () => {
1357
+ const loadMore = useCallback4(async () => {
1283
1358
  const { key: current, read: reader, query: shown } = latest.current;
1284
1359
  if (!cache || !current || busy.current || shown.status !== "ready" || shown.data.nextCursor === null) {
1285
1360
  return;
@@ -1353,7 +1428,7 @@ function useMemberPages(key, read, options = {}) {
1353
1428
  }
1354
1429
  function useInvalidate() {
1355
1430
  const store = useStore();
1356
- return useCallback3((keys = []) => store.invalidate(keys), [store]);
1431
+ return useCallback4((keys = []) => store.invalidate(keys), [store]);
1357
1432
  }
1358
1433
  function useSession() {
1359
1434
  const store = useStore();
@@ -1369,22 +1444,22 @@ function useSession() {
1369
1444
  );
1370
1445
  }
1371
1446
  function useAction() {
1372
- const [busy, setBusy] = useState3(false);
1373
- const [actionError, setActionError] = useState3(null);
1374
- const run = useCallback3(async (task) => {
1375
- setBusy(true);
1376
- setActionError(null);
1377
- try {
1378
- await task();
1379
- return true;
1380
- } catch (cause) {
1381
- setActionError(relayError(cause));
1382
- return false;
1383
- } finally {
1384
- setBusy(false);
1385
- }
1386
- }, []);
1387
- return { busy, actionError, run };
1447
+ const session = useScopeSession();
1448
+ const store = useStore();
1449
+ const scope = useMemo3(() => ({ store, session }), [store, session]);
1450
+ const {
1451
+ run: execute,
1452
+ retry: replay,
1453
+ busy,
1454
+ error: actionError,
1455
+ settle
1456
+ } = useRelayAction(scope);
1457
+ const run = useCallback4(
1458
+ async (task) => (await execute(task)).ok,
1459
+ [execute]
1460
+ );
1461
+ const retry = useCallback4(async () => (await replay()).ok, [replay]);
1462
+ return { busy, actionError, run, retry, settle };
1388
1463
  }
1389
1464
  function useScopeSession() {
1390
1465
  const store = useStore();
@@ -1431,26 +1506,27 @@ function questGroups(experience, order = []) {
1431
1506
 
1432
1507
  // src/react/use-quest.ts
1433
1508
  import {
1434
- useCallback as useCallback4,
1509
+ useCallback as useCallback5,
1435
1510
  useEffect as useEffect5,
1436
1511
  useLayoutEffect as useLayoutEffect3,
1437
1512
  useMemo as useMemo4,
1438
- useRef as useRef3,
1439
- useState as useState4
1513
+ useRef as useRef4,
1514
+ useState as useState5
1440
1515
  } from "react";
1441
1516
  function useQuest(observed) {
1442
1517
  const store = useStore();
1443
1518
  const relay = store.client;
1444
- const [latest, setLatest] = useState4(null);
1445
- const [draft, setDraft] = useState4(null);
1446
- const [notice, setNotice] = useState4(null);
1447
- const [error, setError] = useState4(null);
1448
- const [busy, setBusy] = useState4(false);
1449
- const [discardedAttempt, setDiscardedAttempt] = useState4(null);
1450
- const [saved, setSaved] = useState4("none");
1451
- const [recoveryRevision, setRecoveryRevision] = useState4(0);
1452
- const [retired, setRetired] = useState4(false);
1453
- const [receipt, setReceipt] = useState4(null);
1519
+ const [latest, setLatest] = useState5(null);
1520
+ const [draft, setDraft] = useState5(null);
1521
+ const [notice, setNotice] = useState5(null);
1522
+ const [error, setError] = useState5(null);
1523
+ const [busy, setBusy] = useState5(false);
1524
+ const lock = useRef4(false);
1525
+ const [discardedAttempt, setDiscardedAttempt] = useState5(null);
1526
+ const [saved, setSaved] = useState5("none");
1527
+ const [recoveryRevision, setRecoveryRevision] = useState5(0);
1528
+ const [retired, setRetired] = useState5(false);
1529
+ const [receipt, setReceipt] = useState5(null);
1454
1530
  useEffect5(() => setLatest(null), [observed.release]);
1455
1531
  const view = latest ?? observed;
1456
1532
  const { quest, release } = view;
@@ -1474,8 +1550,12 @@ function useQuest(observed) {
1474
1550
  active = false;
1475
1551
  };
1476
1552
  }, [relay, quest, release, completed, attempt, busy, recoveryRevision]);
1477
- const run = useCallback4(
1553
+ const run = useCallback5(
1478
1554
  async (action) => {
1555
+ if (lock.current) {
1556
+ return;
1557
+ }
1558
+ lock.current = true;
1479
1559
  setBusy(true);
1480
1560
  setError(null);
1481
1561
  setNotice(null);
@@ -1494,19 +1574,24 @@ function useQuest(observed) {
1494
1574
  } catch (cause) {
1495
1575
  setError(asRelayError(cause, "Something went wrong."));
1496
1576
  } finally {
1577
+ lock.current = false;
1497
1578
  setBusy(false);
1498
1579
  setRecoveryRevision((revision) => revision + 1);
1499
1580
  }
1500
1581
  },
1501
1582
  [store]
1502
1583
  );
1503
- const shown = useRef3(view);
1584
+ const shown = useRef4(view);
1504
1585
  useLayoutEffect3(() => {
1505
1586
  shown.current = view;
1506
1587
  });
1507
1588
  const actions = useMemo4(
1508
1589
  () => ({
1509
1590
  restart: async () => {
1591
+ if (lock.current) {
1592
+ return;
1593
+ }
1594
+ lock.current = true;
1510
1595
  const current = shown.current;
1511
1596
  setBusy(true);
1512
1597
  setError(null);
@@ -1520,6 +1605,7 @@ function useQuest(observed) {
1520
1605
  } catch (cause) {
1521
1606
  setError(asRelayError(cause, "Could not start a new submission."));
1522
1607
  } finally {
1608
+ lock.current = false;
1523
1609
  setBusy(false);
1524
1610
  setRecoveryRevision((revision) => revision + 1);
1525
1611
  }
@@ -1605,10 +1691,10 @@ function useQuest(observed) {
1605
1691
  }
1606
1692
 
1607
1693
  // src/react/auth.ts
1608
- import { useCallback as useCallback5, useMemo as useMemo6, useRef as useRef4, useState as useState6 } from "react";
1694
+ import { useCallback as useCallback6, useMemo as useMemo6, useRef as useRef5, useState as useState7 } from "react";
1609
1695
 
1610
1696
  // src/react/wallets.ts
1611
- import { useEffect as useEffect6, useMemo as useMemo5, useState as useState5 } from "react";
1697
+ import { useEffect as useEffect6, useMemo as useMemo5, useState as useState6 } from "react";
1612
1698
  function useWallets() {
1613
1699
  const relay = useRelay();
1614
1700
  const invalidate = useInvalidate();
@@ -1618,7 +1704,7 @@ function useWallets() {
1618
1704
  (options) => relay.wallets.list(options),
1619
1705
  { refetchOnFocus: true }
1620
1706
  );
1621
- const { busy, actionError, run } = useAction();
1707
+ const { busy, actionError, run, retry, settle } = useAction();
1622
1708
  const actions = useMemo5(() => {
1623
1709
  const changed = () => void invalidate([relayKeys.wallets, relayKeys.experience]);
1624
1710
  return {
@@ -1637,8 +1723,8 @@ function useWallets() {
1637
1723
  };
1638
1724
  }, [relay, invalidate, session, run]);
1639
1725
  return useMemo5(
1640
- () => ({ ...query, busy, actionError, ...actions }),
1641
- [query, busy, actionError, actions]
1726
+ () => ({ ...query, busy, actionError, retry, settle, ...actions }),
1727
+ [query, busy, actionError, retry, settle, actions]
1642
1728
  );
1643
1729
  }
1644
1730
  function discoverChains(key) {
@@ -1648,7 +1734,7 @@ function discoverChains(key) {
1648
1734
  }
1649
1735
  function useWalletDiscovery(options = {}) {
1650
1736
  const chains = JSON.stringify(cosmosChainList(options.cosmos));
1651
- const [found, setFound] = useState5({ evm: [], solana: [], cosmos: [] });
1737
+ const [found, setFound] = useState6({ evm: [], solana: [], cosmos: [] });
1652
1738
  useEffect6(() => {
1653
1739
  let active = true;
1654
1740
  const solana = discoverSolanaWallets();
@@ -1671,7 +1757,7 @@ function useSignInWallets(networks, options = {}) {
1671
1757
  );
1672
1758
  const key = networks ? `${networks.join(" ")}
1673
1759
  ${chains}` : null;
1674
- const [found, setFound] = useState5(null);
1760
+ const [found, setFound] = useState6(null);
1675
1761
  useEffect6(() => {
1676
1762
  if (key === null) {
1677
1763
  return;
@@ -1705,16 +1791,21 @@ function useMe() {
1705
1791
  }
1706
1792
  function useSignIn() {
1707
1793
  const relay = useRelay();
1708
- const [challenge, setChallengeState] = useState6(null);
1709
- const latest = useRef4(null);
1710
- const [busy, setBusy] = useState6(false);
1711
- const [error, setError] = useState6(null);
1712
- const setChallenge = useCallback5((value) => {
1794
+ const [challenge, setChallengeState] = useState7(null);
1795
+ const latest = useRef5(null);
1796
+ const [busy, setBusy] = useState7(false);
1797
+ const lock = useRef5(false);
1798
+ const [error, setError] = useState7(null);
1799
+ const setChallenge = useCallback6((value) => {
1713
1800
  latest.current = value;
1714
1801
  setChallengeState(value);
1715
1802
  }, []);
1716
- const run = useCallback5(
1803
+ const run = useCallback6(
1717
1804
  async (task, fallback) => {
1805
+ if (lock.current) {
1806
+ return;
1807
+ }
1808
+ lock.current = true;
1718
1809
  setBusy(true);
1719
1810
  setError(null);
1720
1811
  try {
@@ -1722,19 +1813,20 @@ function useSignIn() {
1722
1813
  } catch (cause) {
1723
1814
  setError(asRelayError(cause, fallback));
1724
1815
  } finally {
1816
+ lock.current = false;
1725
1817
  setBusy(false);
1726
1818
  }
1727
1819
  },
1728
1820
  []
1729
1821
  );
1730
- const sendCode = useCallback5(
1822
+ const sendCode = useCallback6(
1731
1823
  (email) => run(async () => {
1732
1824
  setChallenge(null);
1733
1825
  setChallenge((await relay.auth.emailStart(email)).challenge);
1734
1826
  }, "Couldn't send a code. Check the address and try again."),
1735
1827
  [relay, run, setChallenge]
1736
1828
  );
1737
- const verifyCode = useCallback5(
1829
+ const verifyCode = useCallback6(
1738
1830
  (code) => run(async () => {
1739
1831
  const sent = latest.current;
1740
1832
  if (!sent) {
@@ -1745,18 +1837,18 @@ function useSignIn() {
1745
1837
  }, "That code didn't work. Check it, or request a new one."),
1746
1838
  [relay, run, setChallenge]
1747
1839
  );
1748
- const startOver = useCallback5(() => {
1840
+ const startOver = useCallback6(() => {
1749
1841
  setChallenge(null);
1750
1842
  setError(null);
1751
1843
  }, [setChallenge]);
1752
- const withProvider = useCallback5(
1844
+ const withProvider = useCallback6(
1753
1845
  (provider) => run(async () => {
1754
1846
  const { url } = await relay.auth.start(provider, "sign-in");
1755
1847
  window.location.assign(url);
1756
1848
  }, "Couldn't start sign-in. Try again."),
1757
1849
  [relay, run]
1758
1850
  );
1759
- const withWallet = useCallback5(
1851
+ const withWallet = useCallback6(
1760
1852
  (signer) => run(async () => {
1761
1853
  await relay.auth.wallet.signIn(signer);
1762
1854
  setChallenge(null);
@@ -1791,12 +1883,18 @@ function useSignIn() {
1791
1883
  }
1792
1884
  function useSignOut() {
1793
1885
  const store = useStore();
1794
- const [busy, setBusy] = useState6(false);
1795
- const signOut = useCallback5(async () => {
1886
+ const [busy, setBusy] = useState7(false);
1887
+ const lock = useRef5(false);
1888
+ const signOut = useCallback6(async () => {
1889
+ if (lock.current) {
1890
+ return;
1891
+ }
1892
+ lock.current = true;
1796
1893
  setBusy(true);
1797
1894
  try {
1798
1895
  await store.signOut();
1799
1896
  } finally {
1897
+ lock.current = false;
1800
1898
  setBusy(false);
1801
1899
  }
1802
1900
  }, [store]);
@@ -1871,7 +1969,7 @@ function useAccounts() {
1871
1969
  (options) => relay.auth.accounts(options),
1872
1970
  { refetchOnFocus: true }
1873
1971
  );
1874
- const { busy, actionError, run } = useAction();
1972
+ const { busy, actionError, run, retry, settle } = useAction();
1875
1973
  const actions = useMemo7(
1876
1974
  () => ({
1877
1975
  connect: (provider) => run(async () => {
@@ -1897,10 +1995,12 @@ function useAccounts() {
1897
1995
  ...query,
1898
1996
  busy,
1899
1997
  actionError,
1998
+ retry,
1999
+ settle,
1900
2000
  isConnected: (provider) => isConnected(accounts, provider),
1901
2001
  ...actions
1902
2002
  }),
1903
- [query, busy, actionError, accounts, actions]
2003
+ [query, busy, actionError, retry, settle, accounts, actions]
1904
2004
  );
1905
2005
  }
1906
2006
 
@@ -1916,11 +2016,11 @@ function useMyProfile() {
1916
2016
  (options) => relay.profiles.mine(options),
1917
2017
  { refetchOnFocus: true }
1918
2018
  );
1919
- const { busy, actionError, run } = useAction();
2019
+ const { busy, actionError, run, retry, settle } = useAction();
1920
2020
  const actions = useMemo8(() => {
1921
- const change = (task) => run(async () => {
2021
+ const change = (task) => run(async (actionId) => {
1922
2022
  const cache = store.scope?.cache;
1923
- const saved = await task();
2023
+ const saved = await task(actionId);
1924
2024
  cache?.set(MINE, saved);
1925
2025
  void cache?.invalidate([
1926
2026
  [...relayKeys.profiles, "get"],
@@ -1928,19 +2028,21 @@ function useMyProfile() {
1928
2028
  ]);
1929
2029
  });
1930
2030
  return {
1931
- update: (input) => change(() => relay.profiles.update(input, { session })),
2031
+ update: (input) => change(
2032
+ (actionId) => relay.profiles.update({ ...input, actionId }, { session })
2033
+ ),
1932
2034
  /** Uploads a JPEG, PNG or WebP of at most 5 MB as the avatar. */
1933
2035
  uploadAvatar: (file) => change(
1934
- () => relay.profiles.uploadAvatar(file, {
1935
- uploadId: crypto.randomUUID(),
2036
+ (actionId) => relay.profiles.uploadAvatar(file, {
2037
+ actionId,
1936
2038
  session
1937
2039
  })
1938
2040
  )
1939
2041
  };
1940
2042
  }, [relay, store, session, run]);
1941
2043
  return useMemo8(
1942
- () => ({ ...query, busy, actionError, ...actions }),
1943
- [query, busy, actionError, actions]
2044
+ () => ({ ...query, busy, actionError, retry, settle, ...actions }),
2045
+ [query, busy, actionError, retry, settle, actions]
1944
2046
  );
1945
2047
  }
1946
2048
  function useProfile(id, options = {}) {
@@ -1981,7 +2083,7 @@ function useProfileCompletions(profile, options = {}) {
1981
2083
  }
1982
2084
 
1983
2085
  // src/react/referrals.ts
1984
- import { useCallback as useCallback6, useEffect as useEffect7, useMemo as useMemo9, useRef as useRef5, useState as useState7 } from "react";
2086
+ import { useCallback as useCallback7, useEffect as useEffect7, useMemo as useMemo9, useRef as useRef6, useState as useState8 } from "react";
1985
2087
  var PENDING = "domino:pending-referral";
1986
2088
  var CODE = /^[a-zA-Z0-9_-]{1,100}$/;
1987
2089
  function captureReferralCode(param = "ref") {
@@ -2007,33 +2109,68 @@ function forgetReferralCode() {
2007
2109
  function useInvitation(param = "ref") {
2008
2110
  const relay = useRelay();
2009
2111
  const invalidate = useInvalidate();
2010
- const { member } = useSession();
2011
- const [state, setState] = useState7(() => {
2112
+ const signed = useSession();
2113
+ const { member } = signed;
2114
+ const session = signed.data?.id;
2115
+ const [state, setState] = useState8(() => {
2012
2116
  const code = captureReferralCode(param);
2013
2117
  return code ? { status: "pending", code } : { status: "none" };
2014
2118
  });
2015
- const started = useRef5(null);
2016
- const accept = useCallback6(
2119
+ const started = useRef6(null);
2120
+ const pending = useRef6({
2121
+ member,
2122
+ relay,
2123
+ session,
2124
+ code: "",
2125
+ action: createRelayAction()
2126
+ });
2127
+ if (pending.current.member !== member || pending.current.relay !== relay || pending.current.session !== session) {
2128
+ pending.current = {
2129
+ member,
2130
+ relay,
2131
+ session,
2132
+ code: "",
2133
+ action: createRelayAction()
2134
+ };
2135
+ }
2136
+ const accept = useCallback7(
2017
2137
  async (code) => {
2018
- setState({ status: "accepting", code });
2138
+ const owner = pending.current;
2139
+ if (owner.action.busy) {
2140
+ return;
2141
+ }
2142
+ if (!owner.action.error) {
2143
+ owner.code = code;
2144
+ }
2145
+ const logicalCode = owner.code;
2146
+ setState({ status: "accepting", code: logicalCode });
2019
2147
  try {
2020
- await relay.referrals.accept({ code, actionId: `accept-${code}` });
2148
+ await owner.action.run(
2149
+ (actionId) => relay.referrals.accept({ code, actionId }, { session })
2150
+ );
2151
+ if (pending.current !== owner) {
2152
+ return;
2153
+ }
2021
2154
  forgetReferralCode();
2022
- setState({ status: "accepted", code });
2155
+ setState({ status: "accepted", code: logicalCode });
2023
2156
  void invalidate([relayKeys.referrals, relayKeys.profiles]);
2024
- } catch (cause) {
2157
+ } catch (caught) {
2158
+ if (pending.current !== owner) {
2159
+ return;
2160
+ }
2161
+ const cause = owner.action.error ?? caught;
2025
2162
  const refused = isRelayError(cause) && (cause.kind === "http" && cause.status !== void 0 && cause.status >= 400 && cause.status < 500 && cause.status !== 429 || cause.code === RelayErrorCode.InvalidInput);
2026
2163
  if (refused) {
2027
2164
  forgetReferralCode();
2028
2165
  }
2029
2166
  setState({
2030
2167
  status: refused ? "rejected" : "failed",
2031
- code,
2168
+ code: logicalCode,
2032
2169
  error: asRelayError(cause, "That invitation code couldn't be used.")
2033
2170
  });
2034
2171
  }
2035
2172
  },
2036
- [relay, invalidate]
2173
+ [relay, invalidate, session]
2037
2174
  );
2038
2175
  useEffect7(() => {
2039
2176
  if (member && state.status === "pending" && started.current !== `${member}:${state.code}`) {
@@ -2108,7 +2245,7 @@ function useQuestOutcomes(query = {}) {
2108
2245
  }
2109
2246
 
2110
2247
  // src/react/offers.ts
2111
- import { useCallback as useCallback7, useState as useState8 } from "react";
2248
+ import { useCallback as useCallback8, useMemo as useMemo10 } from "react";
2112
2249
  function useOffers() {
2113
2250
  const relay = useRelay();
2114
2251
  return useRelayQuery(
@@ -2119,29 +2256,33 @@ function useOffers() {
2119
2256
  function useRedeemOffer() {
2120
2257
  const relay = useRelay();
2121
2258
  const invalidate = useInvalidate();
2122
- const [busy, setBusy] = useState8(false);
2123
- const [error, setError] = useState8(null);
2124
- const redeem = useCallback7(
2125
- async (offer) => {
2126
- if (busy) {
2127
- return null;
2128
- }
2129
- setBusy(true);
2130
- setError(null);
2131
- try {
2132
- const redemption = await relay.offers.redeem(offer);
2259
+ const session = useScopeSession();
2260
+ const scope = useMemo10(() => ({ relay, session }), [relay, session]);
2261
+ const {
2262
+ run,
2263
+ retry: replay,
2264
+ settle,
2265
+ busy,
2266
+ error
2267
+ } = useRelayAction(scope);
2268
+ const refresh = useCallback8(
2269
+ async (result) => {
2270
+ const redemption = result.ok ? result.value : null;
2271
+ if (redemption) {
2133
2272
  await invalidate([relayKeys.offers, relayKeys.balances]);
2134
- return redemption;
2135
- } catch (cause) {
2136
- setError(asRelayError(cause, "This offer could not be redeemed."));
2137
- return null;
2138
- } finally {
2139
- setBusy(false);
2140
2273
  }
2274
+ return redemption;
2141
2275
  },
2142
- [busy, invalidate, relay]
2276
+ [invalidate]
2277
+ );
2278
+ const redeem = useCallback8(
2279
+ (offer) => run(
2280
+ (actionId) => relay.offers.redeem(offer, { actionId }, { session })
2281
+ ).then(refresh),
2282
+ [run, relay, session, refresh]
2143
2283
  );
2144
- return { redeem, busy, error };
2284
+ const retry = useCallback8(() => replay().then(refresh), [replay, refresh]);
2285
+ return { redeem, retry, settle, busy, error };
2145
2286
  }
2146
2287
 
2147
2288
  // src/react/references.ts
@@ -2200,6 +2341,7 @@ export {
2200
2341
  useRedeemOffer,
2201
2342
  useReferralSummary,
2202
2343
  useRelay,
2344
+ useRelayAction,
2203
2345
  useRelayPages,
2204
2346
  useRelayQuery,
2205
2347
  useSession,