@openreceive/react 0.4.3 → 0.4.5

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/README.md CHANGED
@@ -31,11 +31,12 @@ framework values rather than DOM `CustomEvent`s — `onState` gets the
31
31
  and returns the view model plus the copy/open actions. Create mode belongs to
32
32
  `<Checkout>`, so the hook takes no create options.
33
33
 
34
- ## Icon assets
34
+ ## Images
35
35
 
36
- The checkout loads its payment-method icons by URL at runtime; your app must
37
- serve them where the resolution lands. See
38
- [Icon assets in `@openreceive/browser`](https://github.com/openreceive/openreceive/blob/master/packages/js/browser/README.md#icon-assets)
39
- for the per-bundler recipes.
36
+ Everything the checkout draws — payment-method icons, wallet logos, pay
37
+ tutorials — ships inside the JavaScript; nothing to copy, serve or configure,
38
+ under any bundler. If your Content-Security-Policy has a strict `img-src`,
39
+ allow `data:`. See
40
+ [Images in `@openreceive/browser`](https://github.com/openreceive/openreceive/blob/master/packages/js/browser/README.md#images).
40
41
 
41
42
  Part of [OpenReceive](https://openreceive.org). Start with the [Node quickstart](https://github.com/openreceive/openreceive/blob/master/docs/guides/quickstart-node.md); the full API is in the [API reference](https://github.com/openreceive/openreceive/blob/master/docs/guides/api-reference.md).
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { CheckoutComponentProps, CheckoutState, QrEncoder, BrowserLoggerOption, CheckoutStatusRefresh, Status, SwapRefundStaging, CheckoutSnapshot, AssetUrlResolver, ThemePreference, BrowserLogContext, CheckoutInvoiceSnapshot, ResolvedTheme, ThemeModel, CheckoutPhase, CheckoutStatusModel, UnixSeconds, BrowserLogger, TransactionDetailsSource } from '@openreceive/browser/headless';
2
+ import { CheckoutComponentProps, CheckoutState, QrEncoder, BrowserLoggerOption, CheckoutStatusRefresh, Status, SwapRefundStaging, CheckoutSnapshot, ThemePreference, BrowserLogContext, CheckoutInvoiceSnapshot, ResolvedTheme, ThemeModel, CheckoutPhase, CheckoutStatusModel, UnixSeconds, BrowserLogger, TransactionDetailsSource } from '@openreceive/browser/headless';
3
3
  export { TransactionDetailsSource, resolveTransactionDetailRows } from '@openreceive/browser/headless';
4
4
 
5
5
  interface CheckoutData {
@@ -46,6 +46,8 @@ interface UseCheckoutOptions extends CheckoutData, Omit<CheckoutEventHandlers, "
46
46
  * polling — there is nowhere to poll.
47
47
  */
48
48
  readonly prefix?: string;
49
+ /** Header name the page's `<meta name="csrf-token">` is sent under; default `X-CSRF-Token`. */
50
+ readonly csrfHeader?: string;
49
51
  readonly polling?: boolean;
50
52
  readonly pollIntervalMs?: number;
51
53
  }
@@ -162,8 +164,6 @@ interface CheckoutProps extends CheckoutComponentProps, CheckoutEventHandlers, O
162
164
  * `useCheckout` model.
163
165
  */
164
166
  readonly children?: CheckoutChildren;
165
- /** Passed through to the payment wizard. See {@link PaymentWizardProps.resolveAssetUrl}. */
166
- readonly resolveAssetUrl?: AssetUrlResolver;
167
167
  /**
168
168
  * Lock the checkout to one theme. Wins over the stored preference and any
169
169
  * ancestor ThemeScope, and hides the toggle: a host embedding the checkout in
@@ -233,25 +233,12 @@ interface PaymentWizardProps {
233
233
  */
234
234
  readonly prefix?: string;
235
235
  readonly fetch?: typeof globalThis.fetch;
236
+ /** Header name the page's `<meta name="csrf-token">` is sent under; default `X-CSRF-Token`. */
237
+ readonly csrfHeader?: string;
236
238
  readonly clipboard?: Pick<Clipboard, "writeText">;
237
239
  readonly qrEncoder?: QrEncoder;
238
240
  /** Base URL of an external bolt11 decoder; omitted, no "Decode" link is rendered. */
239
241
  readonly decodeLinkUrl?: string;
240
- /**
241
- * Rewrite a packaged asset path (`assets/provider-icons/strike.png`,
242
- * `assets/pay_tutorials/strike-1.webp`) into a URL this host can serve. The
243
- * packaged URLs only resolve under Vite/Rollup; every other bundler needs
244
- * this, or the provider logos and pay tutorials come out as dead `file://`
245
- * links. The payment-method icons are compiled in as `data:` URIs and need
246
- * nothing; given a resolver they go through it too (`assets/icons/btc.svg`).
247
- * See docs/guides/provider-registry.md.
248
- */
249
- readonly resolveAssetUrl?: AssetUrlResolver;
250
- /**
251
- * The string form of the same seam: where this host serves the packages'
252
- * `dist/assets` trees. `resolveAssetUrl` wins when both are set.
253
- */
254
- readonly assetBaseUrl?: string;
255
242
  /**
256
243
  * The two-step refund, from whoever owns the checkout controller —
257
244
  * `<Checkout>` passes its `useCheckout` model. Given one, the wizard stages
package/dist/index.js CHANGED
@@ -47,6 +47,7 @@ function useCheckoutSession(options) {
47
47
  },
48
48
  prefix: () => optionsRef.current.swap?.prefix(),
49
49
  fetch: () => optionsRef.current.swap?.fetch(),
50
+ csrfHeader: () => optionsRef.current.swap?.csrfHeader?.(),
50
51
  onStarted: (invoice) => optionsRef.current.swap?.onStarted?.(invoice)
51
52
  },
52
53
  get logger() {
@@ -769,6 +770,7 @@ function useCheckout(options) {
769
770
  refreshStatus: async (reference) => await refreshStatusRef.current?.(reference) ?? null
770
771
  } : {},
771
772
  ...prefix === void 0 ? {} : { prefix },
773
+ ...options.csrfHeader === void 0 ? {} : { csrfHeader: options.csrfHeader },
772
774
  pollIntervalMs: options.pollIntervalMs,
773
775
  // Omit logger when unset so @openreceive/browser attaches its default console sink.
774
776
  // Pass `false` through to disable; wrap custom sinks so inline host callbacks stay stable.
@@ -796,7 +798,7 @@ function useCheckout(options) {
796
798
  controller.stop();
797
799
  if (controllerRef.current === controller) controllerRef.current = null;
798
800
  };
799
- }, [checkoutIdentity, polls, prefix, options.pollIntervalMs]);
801
+ }, [checkoutIdentity, polls, prefix, options.csrfHeader, options.pollIntervalMs]);
800
802
  const publicStatus = deriveStatus(state);
801
803
  const richStatus = createCheckoutStatusModel2(state);
802
804
  React6.useEffect(() => {
@@ -944,7 +946,6 @@ import {
944
946
  buildMethodGridEntries,
945
947
  checkoutLabels as checkoutLabels5,
946
948
  copyInvoice as copyInvoiceHelper3,
947
- createAssetBaseUrlResolver,
948
949
  createMethodGridDisplay,
949
950
  createPaymentWizardController,
950
951
  createPaymentWizardModel,
@@ -957,6 +958,7 @@ import {
957
958
  getPaymentMethodIcon,
958
959
  getSwapOptionIcon,
959
960
  getWizardEmptyMessage,
961
+ loadPayTutorialImages,
960
962
  networkButtonClasses,
961
963
  networkCheckClasses,
962
964
  networkMobileRevealClasses,
@@ -1142,10 +1144,12 @@ function ProviderTutorialModal(options) {
1142
1144
  {
1143
1145
  className: orClasses4.tutorialFrame
1144
1146
  },
1145
- React7.createElement("img", {
1146
- alt: tutorial?.caption ?? "",
1147
+ // No `<img>` until the lazily loaded screenshot is in hand: an
1148
+ // empty src is a request for the page itself.
1149
+ tutorial?.image === void 0 ? null : React7.createElement("img", {
1150
+ alt: tutorial.caption,
1147
1151
  className: orClasses4.tutorialImage,
1148
- src: tutorial?.image ?? ""
1152
+ src: tutorial.image
1149
1153
  })
1150
1154
  ),
1151
1155
  React7.createElement(
@@ -2020,6 +2024,7 @@ function PaymentWizard(props) {
2020
2024
  },
2021
2025
  prefix: () => props.prefix,
2022
2026
  fetch: () => fetcher,
2027
+ csrfHeader: () => props.csrfHeader,
2023
2028
  onStarted: (invoice) => props.onSwapStarted?.(invoice)
2024
2029
  },
2025
2030
  ...props.logger === void 0 ? {} : { logger: props.logger },
@@ -2073,6 +2078,7 @@ function PaymentWizard(props) {
2073
2078
  ) : await requestSwapRefund({
2074
2079
  fetch: fetcher,
2075
2080
  prefix,
2081
+ ...props.csrfHeader === void 0 ? {} : { csrfHeader: props.csrfHeader },
2076
2082
  reference,
2077
2083
  paymentHash: resolveAttemptPaymentHash(
2078
2084
  [startedSwapInvoice, ...checkout?.invoices ?? []],
@@ -2090,6 +2096,7 @@ function PaymentWizard(props) {
2090
2096
  },
2091
2097
  [
2092
2098
  props.prefix,
2099
+ props.csrfHeader,
2093
2100
  reference,
2094
2101
  fetcher,
2095
2102
  props.onError,
@@ -2107,14 +2114,25 @@ function PaymentWizard(props) {
2107
2114
  );
2108
2115
  const model = createPaymentWizardModel(selection);
2109
2116
  const { wizard } = model;
2110
- const resolveAssetUrl = props.resolveAssetUrl ?? (props.assetBaseUrl === void 0 || props.assetBaseUrl.trim() === "" ? void 0 : createAssetBaseUrlResolver(props.assetBaseUrl));
2117
+ const tutorialOpen = activeTutorial !== null;
2118
+ const [tutorialImagesReady, setTutorialImagesReady] = React9.useState(false);
2119
+ React9.useEffect(() => {
2120
+ if (!tutorialOpen || tutorialImagesReady) return;
2121
+ let cancelled = false;
2122
+ loadPayTutorialImages().then(
2123
+ () => {
2124
+ if (!cancelled) setTutorialImagesReady(true);
2125
+ },
2126
+ () => void 0
2127
+ );
2128
+ return () => {
2129
+ cancelled = true;
2130
+ };
2131
+ }, [tutorialOpen, tutorialImagesReady]);
2111
2132
  const routeAssetDisplays = createWizardRouteAssetDisplays(model.routeAssets, {
2112
- selectedRoute: model.selectedRoute,
2113
- ...resolveAssetUrl === void 0 ? {} : { resolveAssetUrl }
2114
- });
2115
- const routeDisplays = createWizardRouteDisplays(wizard.routes, {
2116
- ...resolveAssetUrl === void 0 ? {} : { resolveAssetUrl }
2133
+ selectedRoute: model.selectedRoute
2117
2134
  });
2135
+ const routeDisplays = createWizardRouteDisplays(wizard.routes);
2118
2136
  const showRoutePicker = routeAssetDisplays.length > 0 && (model.selectedRoute === null || routeDisplays.length === 0);
2119
2137
  const activeTutorialProvider = activeTutorial === null ? void 0 : routeDisplays.flatMap((route) => route.providers).find((provider) => provider.id === activeTutorial.providerId);
2120
2138
  const swapAssetOptions = swapOptions.enabled ? swapOptions.options.filter((option) => option.provider.length > 0) : [];
@@ -2258,8 +2276,7 @@ function PaymentWizard(props) {
2258
2276
  void props.onRequestLightning?.();
2259
2277
  }
2260
2278
  },
2261
- onContinueSwap: selectSwapAsset,
2262
- ...resolveAssetUrl === void 0 ? {} : { resolveAssetUrl }
2279
+ onContinueSwap: selectSwapAsset
2263
2280
  }) : null,
2264
2281
  // On the grid the note is a section of its own, under the tiles: there is no
2265
2282
  // body to put it in until a method is chosen.
@@ -2532,7 +2549,7 @@ function renderCompactPaymentMethodSelector(options) {
2532
2549
  React9.createElement("img", {
2533
2550
  alt: "",
2534
2551
  className: orClasses6.methodNetworkIcon,
2535
- src: getNetworkIcon(option.network_label, options.resolveAssetUrl)
2552
+ src: getNetworkIcon(option.network_label)
2536
2553
  })
2537
2554
  ),
2538
2555
  React9.createElement("span", { className: "truncate" }, option.network_label),
@@ -2627,7 +2644,7 @@ function renderCompactPaymentMethodSelector(options) {
2627
2644
  React9.createElement("img", {
2628
2645
  alt: "",
2629
2646
  className: orClasses6.methodIcon,
2630
- src: getPaymentMethodIcon(method.id, options.resolveAssetUrl)
2647
+ src: getPaymentMethodIcon(method.id)
2631
2648
  })
2632
2649
  ),
2633
2650
  React9.createElement(
@@ -2693,7 +2710,7 @@ function renderCompactPaymentMethodSelector(options) {
2693
2710
  }) : React9.createElement("img", {
2694
2711
  alt: "",
2695
2712
  className: orClasses6.methodIcon,
2696
- src: getSwapOptionIcon(displayOption, options.resolveAssetUrl)
2713
+ src: getSwapOptionIcon(displayOption)
2697
2714
  })
2698
2715
  ),
2699
2716
  React9.createElement(
@@ -2920,6 +2937,7 @@ function CheckoutSnapshotMode(props) {
2920
2937
  function CheckoutCreate(props) {
2921
2938
  const reference = props.reference;
2922
2939
  const resolvedPrefix = props.prefix ?? OPENRECEIVE_DEFAULT_PREFIX;
2940
+ const csrfHeader = props.csrfHeader;
2923
2941
  const {
2924
2942
  onError,
2925
2943
  metadata,
@@ -2952,6 +2970,7 @@ function CheckoutCreate(props) {
2952
2970
  requestCheckout: (id) => requestCheckout({
2953
2971
  prefix: resolvedPrefix,
2954
2972
  reference: id,
2973
+ ...csrfHeader === void 0 ? {} : { csrfHeader },
2955
2974
  ...metadataRef.current === void 0 ? {} : { metadata: metadataRef.current },
2956
2975
  ...createFetchRef.current === void 0 ? {} : { fetch: createFetchRef.current }
2957
2976
  }),
@@ -2974,11 +2993,13 @@ function CheckoutCreate(props) {
2974
2993
  prepareCheckout({
2975
2994
  prefix: resolvedPrefix,
2976
2995
  reference,
2996
+ ...csrfHeader === void 0 ? {} : { csrfHeader },
2977
2997
  ...createFetchRef.current === void 0 ? {} : { fetch: createFetchRef.current }
2978
2998
  }).then(
2979
2999
  (checkout) => resumePaymentHash === void 0 ? checkout : resumeSwapAttempt({
2980
3000
  fetch: createFetchRef.current ?? globalThis.fetch,
2981
3001
  prefix: resolvedPrefix,
3002
+ ...csrfHeader === void 0 ? {} : { csrfHeader },
2982
3003
  reference,
2983
3004
  paymentHash: resumePaymentHash,
2984
3005
  snapshot: checkout
@@ -2996,7 +3017,7 @@ function CheckoutCreate(props) {
2996
3017
  return () => {
2997
3018
  cancelled = true;
2998
3019
  };
2999
- }, [reference, resolvedPrefix, resumePaymentHash, attempt]);
3020
+ }, [reference, resolvedPrefix, csrfHeader, resumePaymentHash, attempt]);
3000
3021
  const onSwapStarted = React10.useCallback(
3001
3022
  (invoice) => {
3002
3023
  setCreated((current) => {
@@ -3085,6 +3106,7 @@ function CheckoutView(props) {
3085
3106
  // The dispatcher and CheckoutCreate both resolve this before rendering the
3086
3107
  // view, so the default below only guards a direct CheckoutView call.
3087
3108
  prefix = OPENRECEIVE_DEFAULT_PREFIX,
3109
+ csrfHeader,
3088
3110
  metadata: _metadata,
3089
3111
  createFetch: _createFetch,
3090
3112
  // Not `_`-prefixed like its neighbours: the view reads it, to infer
@@ -3121,8 +3143,6 @@ function CheckoutView(props) {
3121
3143
  components,
3122
3144
  classNames,
3123
3145
  children,
3124
- resolveAssetUrl,
3125
- assetBaseUrl,
3126
3146
  className,
3127
3147
  ...sectionProps
3128
3148
  } = props;
@@ -3134,6 +3154,7 @@ function CheckoutView(props) {
3134
3154
  onError,
3135
3155
  refreshStatus,
3136
3156
  prefix,
3157
+ csrfHeader,
3137
3158
  onState,
3138
3159
  onSettled,
3139
3160
  polling,
@@ -3374,6 +3395,7 @@ function CheckoutView(props) {
3374
3395
  onError,
3375
3396
  onSwapFocusChange: setSwapFocused,
3376
3397
  prefix,
3398
+ csrfHeader,
3377
3399
  qrEncoder,
3378
3400
  decodeLinkUrl,
3379
3401
  logContext: getCheckoutLogContext({
@@ -3385,8 +3407,6 @@ function CheckoutView(props) {
3385
3407
  onCopy,
3386
3408
  onRequestLightning,
3387
3409
  onSwapStarted,
3388
- resolveAssetUrl,
3389
- assetBaseUrl,
3390
3410
  // Whether a payer who closes the tab has a URL to come back
3391
3411
  // to. Explicit wins — only the host knows about a per-order
3392
3412
  // route of its own; otherwise infer it from the two props that