Skip to content

Conversation

bassgeta
Copy link
Contributor

@bassgeta bassgeta commented Sep 18, 2025

Problem

When testing the widget on a fresh Vite app I've encountered multiple difficulties.
The most jarring one was the fact that when you initialize ShadCN, it creates a tailwind config with okch colours, which breaks html2pdf.js 🤯

Solution

Make rendering of PDFs done with jsPDF and html2canvas-pro, which has oklch support.
And just generally reconfigured everything so that the linter isn't angry when you are using vite.

Changes

  • use html2canvas-pro and jsPDF instead of html2pdf.js
  • split up payment widget context into 3 files so vite's fast reload is working
  • check if process is defined before overriding the API URL (vite uses import.meta.env)

Summary by CodeRabbit

  • Bug Fixes
    • Improved receipt PDF generation for higher-quality, consistent A4 downloads.
    • Safer API URL handling to avoid runtime issues in environments without process.
  • Style
    • Increased receipt layout padding for clearer spacing.
  • Refactor
    • Restructured payment widget context and separated hook/provider for cleaner usage across components.
  • Chores
    • Replaced html2pdf.js with html2canvas-pro and jspdf in dependencies.

@bassgeta bassgeta self-assigned this Sep 18, 2025
Copy link
Contributor

coderabbitai bot commented Sep 18, 2025

Walkthrough

Replaces html2pdf.js with html2canvas-pro + jspdf, updates dependencies and registry entries, restructures payment-widget context into index/provider/hook, adjusts component imports, changes receipt PDF generation logic, adds an env guard for RN_API_URL, tightens TypeScript typings in utils, tweaks receipt padding, and removes html2pdf typings.

Changes

Cohort / File(s) Summary of changes
Dependencies & registry declarations
package.json, registry.json, public/r/registry.json
Removed html2pdf.js; added html2canvas-pro and jspdf. Updated public registry entries to reflect new context index/provider/hook paths and removed public exposure of html2pdf.d.ts.
Context restructure (index/provider/hook)
registry/default/payment-widget/context/payment-widget-context/index.ts, .../payment-widget-provider.tsx, .../use-payment-widget-context.ts
Introduced PaymentWidgetContextValue and exported PaymentWidgetContext in index.ts. Provider now consumes the shared context from index. Added standalone usePaymentWidgetContext hook that validates provider presence. Removed duplicate local context/type declarations.
Components import path updates
registry/default/payment-widget/components/currency-select.tsx, .../payment-confirmation.tsx, .../payment-modal.tsx, registry/default/payment-widget/payment-widget.tsx
Updated imports to use the new context paths (.../payment-widget-context/use-payment-widget-context and provider path). No other logic changes.
Receipt PDF generation
registry/default/payment-widget/components/payment-success.tsx
Replaced html2pdf.js usage with html2canvas-pro (render to canvas) and jspdf (A4 PDF generation). Added canvas scale/CORS/background options, computed image sizing, and explicit hidden preview styles; saves PDF named by receipt number.
Constants env guard
registry/default/payment-widget/constants.ts
RN_API_URL now guards against undefined process before reading process.env.NEXT_PUBLIC_REQUEST_API_URL, with same fallback URL.
Utils typing refinements
registry/default/payment-widget/utils/payment.ts, registry/default/payment-widget/utils/wagmi.ts
Strengthened typings: isPaymentError(error: unknown) with null check; TransactionReceipt imported as type-only; connectors typed as CreateConnectorFn[] and removed as any.
Styles adjustment
registry/default/payment-widget/components/receipt/styles.css
Increased .receipt-container padding from 20px to 32px.
Type declarations removal
registry/default/payment-widget/types/html2pdf.d.ts
Deleted the html2pdf.js TypeScript declaration module and related interfaces/exports.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant PaymentSuccess as PaymentSuccess component
  participant DOM as Receipt DOM Element
  participant Canvas as html2canvas-pro
  participant PDF as jsPDF
  participant Browser

  User->>PaymentSuccess: Click "Download receipt"
  PaymentSuccess->>DOM: Select hidden receipt element
  PaymentSuccess->>Canvas: render(element, { scale:2, useCORS:true, background:"white" })
  Canvas-->>PaymentSuccess: HTMLCanvasElement
  PaymentSuccess->>PDF: new jsPDF('p','mm','a4')
  PaymentSuccess->>PDF: addImage(canvas PNG, width=210mm, preserve aspect)
  PaymentSuccess->>Browser: save("<receipt-number>.pdf")
  note over PaymentSuccess,PDF: New flow replacing html2pdf.js
Loading
sequenceDiagram
  autonumber
  participant App
  participant Provider as PaymentWidgetProvider
  participant ComponentA as Component A
  participant ComponentB as Component B
  participant Ctx as PaymentWidgetContext

  App->>Provider: Render with props
  Provider->>Ctx: Provide value (config, wallet, callbacks)
  ComponentA->>Ctx: usePaymentWidgetContext()
  Ctx-->>ComponentA: Context value
  ComponentB->>Ctx: usePaymentWidgetContext()
  Ctx-->>ComponentB: Context value
  note over ComponentA,ComponentB: Hook throws if no Provider present
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Feat/payment widget #1 — Mirrors the same payment-widget refactor (context split into index/provider/hook) and replaces html2pdf.js with html2canvas-pro + jspdf for receipt generation.

Suggested reviewers

  • rodrigopavezi
  • aimensahnoun

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Fix: Vite issues" is a concise, single-sentence summary that directly reflects the PR objectives and changes (addressing Vite compatibility by replacing html2pdf.js with jsPDF/html2canvas-pro, splitting the payment-widget context, and guarding process usage). It clearly communicates the primary intent to reviewers scanning the history without extraneous detail.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/vite-issues

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb0da6d and 741c361.

📒 Files selected for processing (2)
  • package.json (1 hunks)
  • registry/default/payment-widget/components/payment-success.tsx (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • package.json
  • registry/default/payment-widget/components/payment-success.tsx

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
registry/default/payment-widget/context/payment-widget-context/payment-widget-provider.tsx (1)

41-45: Fix fallback for connectedWalletAddress when walletAccount lacks an address

If a custom WalletClient is provided without account.address, connectedWalletAddress becomes undefined. Prior context used a fallback to address from wagmi. Use nullish coalescing.

Apply:

-  const isWalletOverride = walletAccount !== undefined;
-  const connectedWalletAddress = walletAccount
-    ? walletAccount.account?.address
-    : address;
+  const isWalletOverride = walletAccount !== undefined;
+  const connectedWalletAddress =
+    walletAccount?.account?.address ?? address;
🧹 Nitpick comments (15)
package.json (1)

22-23: Switch to html2canvas-pro + jsPDF looks good.

No functional concerns from this diff. Ensure you tree‑shake unused jsPDF plugins to keep bundles lean.

registry/default/payment-widget/utils/wagmi.ts (1)

1-1: Nice typing hardening for connectors.

Importing CreateConnectorFn and typing connectors removes the any escape hatch. Looks good.

Minor: mark the array const for clearer intent:

-  const connectors: CreateConnectorFn[] = [
+  const connectors: readonly CreateConnectorFn[] = [

Also applies to: 19-19, 41-41

registry/default/payment-widget/utils/payment.ts (2)

55-62: Type guard is safer with unknown + null check.

Good improvement. Consider also asserting the inner error is an Error for downstream consistency.

 export const isPaymentError = (error: unknown): error is PaymentError => {
   return (
     error !== null &&
     typeof error === "object" &&
     "type" in error &&
-    "error" in error
+    "error" in error &&
+    (error as any).error instanceof Error
   );
 };

200-207: Normalize unknown throws to Error before wrapping.

Some runtimes throw strings or plain objects; normalize for consistency with your PaymentError contract.

  } catch (error) {
    console.error("Error in payment flow:", error);
    if (isPaymentError(error)) {
      throw error;
    } else {
-      throw { type: "unknown", error: error as Error } as PaymentError;
+      const err = error instanceof Error ? error : new Error(String(error));
+      throw { type: "unknown", error: err } as PaymentError;
    }
  }
registry/default/payment-widget/constants.ts (1)

2-3: Guard prevents process reference errors, but Vite env isn’t read.

Vite uses import.meta.env.VITE_*. Add a non‑crashing fallback to read that too.

-export const RN_API_URL =
-  (typeof process !== "undefined" && process.env.NEXT_PUBLIC_REQUEST_API_URL) ||
-  "https://api.request.network";
+// Prefer Next.js env, then Vite's import.meta.env, then default.
+const viteUrl =
+  // `import.meta` exists in ESM; `.env` will be undefined outside Vite.
+  (import.meta as any)?.env?.VITE_REQUEST_API_URL as string | undefined;
+export const RN_API_URL =
+  (typeof process !== "undefined"
+    ? process.env?.NEXT_PUBLIC_REQUEST_API_URL
+    : undefined) ||
+  viteUrl ||
+  "https://api.request.network";

Please confirm this file is transpiled as ESM (so import.meta parses). If not, we can gate with a try/catch or a typeof check via a helper.

registry/default/payment-widget/context/payment-widget-context/index.ts (1)

5-31: Tighten address types with viem’s Address.

Use the Address branded type to prevent invalid hex strings at compile time.

-import type { TransactionReceipt, WalletClient } from "viem";
+import type { Address, TransactionReceipt, WalletClient } from "viem";

 export interface PaymentWidgetContextValue {
   amountInUsd: string;
-  recipientWallet: string;
+  recipientWallet: Address;

   walletAccount?: WalletClient;
-  connectedWalletAddress?: string;
+  connectedWalletAddress?: Address;
   isWalletOverride: boolean;

Ensure callers pass checksummed/0x‑prefixed addresses; otherwise add a runtime guard where the context is populated.

registry/default/payment-widget/components/currency-select.tsx (2)

3-3: Nit: include useMemo for derived arrays.

Avoid recomputing on every render.

-import { useState } from "react";
+import { useMemo, useState } from "react";

63-66: Nit: memoize lowerCaseSupportedCurrencies.

Minor render perf tidy‑up.

-  const lowerCaseSupportedCurrencies = supportedCurrencies.map((currency) =>
-    currency.toLowerCase(),
-  );
+  const lowerCaseSupportedCurrencies = useMemo(
+    () => supportedCurrencies.map((c) => c.toLowerCase()),
+    [supportedCurrencies],
+  );
registry/default/payment-widget/components/payment-modal.tsx (1)

69-74: Reset txHash when resetting modal state.

Prevents stale hash on subsequent payments after a success close.

   const reset = () => {
     setActiveStep("currency-select");
     setSelectedCurrency(null);
     setBuyerInfo(receiptInfo.buyerInfo || undefined);
     setRequestId("");
+    setTxHash("");
   };
registry/default/payment-widget/payment-widget.tsx (1)

24-38: Simplify disabled logic; keep errors.

Cleaner derivation without mutable flag.

-  let isButtonDisabled = false;
-
-  if (!rnApiClientId || rnApiClientId === "") {
-    console.error("PaymentWidget: rnApiClientId is required in paymentConfig");
-
-    isButtonDisabled = true;
-  }
-
-  if (supportedCurrencies.length === 0) {
-    console.error(
-      "PaymentWidget: supportedCurrencies is required in paymentConfig",
-    );
-
-    isButtonDisabled = true;
-  }
+  const missingClientId = !rnApiClientId || rnApiClientId === "";
+  const missingCurrencies = supportedCurrencies.length === 0;
+  if (missingClientId) {
+    console.error("PaymentWidget: rnApiClientId is required in paymentConfig");
+  }
+  if (missingCurrencies) {
+    console.error(
+      "PaymentWidget: supportedCurrencies is required in paymentConfig",
+    );
+  }
+  const isButtonDisabled = missingClientId || missingCurrencies;
registry/default/payment-widget/components/payment-confirmation.tsx (2)

49-53: Guard UX: show an actionable error when wallet not connected.

Early return is silent; inform the user.

-    if (!connectedWalletAddress) return;
+    if (!connectedWalletAddress) {
+      setLocalError("Please connect your wallet to continue.");
+      return;
+    }

178-185: Disable Pay when wallet not connected.

Prevents a no-op click path.

-        <Button
+        <Button
           type="button"
           onClick={handleExecutePayment}
           className="flex-1"
-          disabled={isExecuting}
+          disabled={isExecuting || !connectedWalletAddress}
         >
registry/default/payment-widget/components/payment-success.tsx (2)

75-83: Improve canvas quality safely.

Use devicePixelRatio (capped) and silence logs.

-      const canvas = await html2canvas(element, {
-        scale: 2,
+      const canvas = await html2canvas(element, {
+        scale: Math.min(2, window.devicePixelRatio || 1),
         useCORS: true,
+        logging: false,
         backgroundColor: "#ffffff",
         width: element.scrollWidth,
         height: element.scrollHeight,
         windowWidth: element.scrollWidth,
         windowHeight: element.scrollHeight,
       });

97-98: Optional: replace alert with toast.

Use your UI toast for non‑blocking feedback.

registry/default/payment-widget/context/payment-widget-context/payment-widget-provider.tsx (1)

66-81: Optional: simplify deps; reduce maintenance risk

Tracking individual nested fields is brittle. If parent props are stable, consider depending on the objects instead (or pre‑memoize them in parent). If not, keep as is.

Possible tweak:

-    [
-      amountInUsd,
-      recipientWallet,
-      walletAccount,
-      connectedWalletAddress,
-      isWalletOverride,
-      paymentConfig.rnApiClientId,
-      paymentConfig.feeInfo,
-      paymentConfig.supportedCurrencies,
-      uiConfig?.showReceiptDownload,
-      uiConfig?.showRequestScanUrl,
-      receiptInfo,
-      onSuccess,
-      onError,
-    ],
+    [
+      amountInUsd,
+      recipientWallet,
+      walletAccount,
+      connectedWalletAddress,
+      isWalletOverride,
+      paymentConfig,
+      uiConfig,
+      receiptInfo,
+      onSuccess,
+      onError,
+    ],
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b17ecaa and eb0da6d.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (16)
  • package.json (2 hunks)
  • public/r/registry.json (2 hunks)
  • registry.json (2 hunks)
  • registry/default/payment-widget/components/currency-select.tsx (1 hunks)
  • registry/default/payment-widget/components/payment-confirmation.tsx (1 hunks)
  • registry/default/payment-widget/components/payment-modal.tsx (1 hunks)
  • registry/default/payment-widget/components/payment-success.tsx (4 hunks)
  • registry/default/payment-widget/components/receipt/styles.css (1 hunks)
  • registry/default/payment-widget/constants.ts (1 hunks)
  • registry/default/payment-widget/context/payment-widget-context/index.ts (1 hunks)
  • registry/default/payment-widget/context/payment-widget-context/payment-widget-provider.tsx (1 hunks)
  • registry/default/payment-widget/context/payment-widget-context/use-payment-widget-context.ts (1 hunks)
  • registry/default/payment-widget/payment-widget.tsx (1 hunks)
  • registry/default/payment-widget/types/html2pdf.d.ts (0 hunks)
  • registry/default/payment-widget/utils/payment.ts (3 hunks)
  • registry/default/payment-widget/utils/wagmi.ts (3 hunks)
💤 Files with no reviewable changes (1)
  • registry/default/payment-widget/types/html2pdf.d.ts
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-09-17T12:08:58.198Z
Learnt from: bassgeta
PR: RequestNetwork/ui-registry#1
File: registry/default/payment-widget/components/payment-confirmation.tsx:31-41
Timestamp: 2025-09-17T12:08:58.198Z
Learning: In the payment widget context (registry/default/payment-widget/context/payment-widget-context.tsx), connectedWalletAddress is properly derived as `walletAccount?.account?.address || address` which handles both custom WalletClient scenarios and wagmi-managed wallets, so components should use connectedWalletAddress from context rather than address from usePayment hook.

Applied to files:

  • registry/default/payment-widget/context/payment-widget-context/use-payment-widget-context.ts
  • registry/default/payment-widget/context/payment-widget-context/index.ts
  • registry/default/payment-widget/components/currency-select.tsx
  • registry/default/payment-widget/components/payment-modal.tsx
  • registry/default/payment-widget/utils/wagmi.ts
  • registry/default/payment-widget/components/payment-confirmation.tsx
  • registry/default/payment-widget/payment-widget.tsx
  • registry/default/payment-widget/components/payment-success.tsx
  • registry.json
  • public/r/registry.json
  • registry/default/payment-widget/context/payment-widget-context/payment-widget-provider.tsx
📚 Learning: 2025-09-17T12:08:58.198Z
Learnt from: bassgeta
PR: RequestNetwork/ui-registry#1
File: registry/default/payment-widget/components/payment-confirmation.tsx:31-41
Timestamp: 2025-09-17T12:08:58.198Z
Learning: In the payment widget context (registry/default/payment-widget/context/payment-widget-context.tsx), connectedWalletAddress is properly derived as `walletAccount ? walletAccount.account?.address : address` which handles both custom WalletClient scenarios and wagmi-managed wallets, so components should use connectedWalletAddress from context rather than address from usePayment hook.

Applied to files:

  • registry/default/payment-widget/context/payment-widget-context/use-payment-widget-context.ts
  • registry/default/payment-widget/context/payment-widget-context/index.ts
  • registry/default/payment-widget/components/currency-select.tsx
  • registry/default/payment-widget/components/payment-modal.tsx
  • registry/default/payment-widget/utils/wagmi.ts
  • registry/default/payment-widget/components/payment-confirmation.tsx
  • registry/default/payment-widget/payment-widget.tsx
  • registry/default/payment-widget/components/payment-success.tsx
  • registry.json
  • public/r/registry.json
  • registry/default/payment-widget/context/payment-widget-context/payment-widget-provider.tsx
📚 Learning: 2025-09-17T15:33:04.220Z
Learnt from: bassgeta
PR: RequestNetwork/ui-registry#1
File: app/components/payment-widget-wrapper.tsx:15-18
Timestamp: 2025-09-17T15:33:04.220Z
Learning: In the payment widget, walletConnectProjectId is optional and can be empty, but rnApiClientId is required. The PaymentWidgetInner component already disables the button when rnApiClientId is missing, providing existing validation.

Applied to files:

  • registry/default/payment-widget/context/payment-widget-context/index.ts
📚 Learning: 2025-09-17T09:30:27.515Z
Learnt from: bassgeta
PR: RequestNetwork/ui-registry#1
File: app/components/viem-account-demo.tsx:45-46
Timestamp: 2025-09-17T09:30:27.515Z
Learning: Wagmi v2 useConnect hook returns isPending (derived from TanStack Query mutation status) and connectors have .uid property as shown in official documentation at https://wagmi.sh/react/guides/connect-wallet

Applied to files:

  • registry/default/payment-widget/utils/wagmi.ts
🧬 Code graph analysis (3)
registry/default/payment-widget/context/payment-widget-context/use-payment-widget-context.ts (1)
registry/default/payment-widget/context/payment-widget-context/index.ts (2)
  • PaymentWidgetContextValue (5-31)
  • PaymentWidgetContext (33-34)
registry/default/payment-widget/context/payment-widget-context/index.ts (1)
registry/default/payment-widget/types/index.ts (3)
  • FeeInfo (1-4)
  • ReceiptInfo (67-73)
  • PaymentError (8-11)
registry/default/payment-widget/utils/payment.ts (1)
registry/default/payment-widget/types/index.ts (1)
  • PaymentError (8-11)
🔇 Additional comments (18)
registry/default/payment-widget/components/receipt/styles.css (1)

3-3: Padding increase acknowledged.

The 32px padding should still fit within the 800px canvas width used during PDF capture. No issues.

If you notice clipping on narrow viewports when rendering to canvas, consider setting a fixed inner wrapper width (e.g., 736px) to match 800px − 2×32px.

registry/default/payment-widget/utils/payment.ts (1)

1-1: Type‑only import is correct.

This reduces emitted JS and tightens typings.

public/r/registry.json (2)

24-26: Dependency mapping updated.

html2canvas-pro + jsPDF entries align with package.json.


34-47: New context module exports registered correctly.

Paths and targets look consistent with the new foldered context structure.

Run a quick smoke test with shadcn add payment-widget into a Vite app to ensure these entries resolve correctly on install.

registry.json (2)

24-26: Mirrored dependency updates match public/r.

Keeps both registries in sync.


34-47: Context split registered here as well.

Good consistency with public/r/registry.json. No issues.

registry/default/payment-widget/components/currency-select.tsx (1)

13-13: Path update LGTM.

Importing the hook from the new context module is consistent with the refactor.

registry/default/payment-widget/components/payment-modal.tsx (2)

19-19: Path update LGTM.

Hook import aligns with the new context structure.


79-86: Confirm intended UX for early closes.

Currently state resets only when closing from "payment-success". If the user closes earlier, the step/buyer info persist. Confirm that this is desired.

registry/default/payment-widget/payment-widget.tsx (1)

8-12: Path updates LGTM.

Provider and hook imports match the refactor.

registry/default/payment-widget/components/payment-confirmation.tsx (1)

13-13: Path update LGTM.

Hook import matches the new context module.

registry/default/payment-widget/context/payment-widget-context/use-payment-widget-context.ts (1)

1-14: Hook implementation LGTM.

Correctly enforces provider presence and returns typed context.

registry/default/payment-widget/components/payment-success.tsx (4)

14-14: Path update LGTM.

Hook import aligns with the refactor.


75-83: Cross‑origin assets caveat.

If logos/images in the receipt come from a CDN, ensure those tags have crossOrigin="anonymous" and the server sends CORS headers, or html2canvas will taint the canvas.


126-134: Good: hidden render target with fixed width and white bg.

This stabilizes layout and color for capture.

Confirm that 800px corresponds to the intended A4 scaling after the fit‑to‑page change above; adjust if text looks too small.


48-48: Type check: receipt amount shape — OK (string)

PaymentInfo.amount (and therefore CreateReceiptParams.payment.amount) is declared as string in registry/default/payment-widget/utils/receipt.ts; amountInUsd is a string in the widget/context, so no coercion required.

registry/default/payment-widget/context/payment-widget-context/payment-widget-provider.tsx (2)

83-87: Provider wiring looks good

Memoized value, stable shape, and defaults for UI flags are sensible.


41-61: Resolved — consumers handle undefined connectedWalletAddress

connectedWalletAddress is provided by payment-widget-provider and is only consumed in:

  • registry/default/payment-widget/components/payment-confirmation.tsx — guarded (if (!connectedWalletAddress) return) and Back button disabled when missing.
  • registry/default/payment-widget/components/payment-success.tsx — coalesced with || "" when used.

No unguarded string/property operations on connectedWalletAddress were found.

@bassgeta bassgeta merged commit f5e67bf into main Sep 18, 2025
1 check passed
@bassgeta bassgeta deleted the fix/vite-issues branch September 18, 2025 11:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants