From f832f8e17f6b6e0166ec5e72b640ee4bc50bd98c Mon Sep 17 00:00:00 2001 From: Arpi Date: Tue, 6 Aug 2024 18:31:47 +0300 Subject: [PATCH 01/11] extension page for WM tools integration --- app/components/footer.tsx | 14 ++ app/components/presets.tsx | 66 +++++++ app/components/quoteDialog.tsx | 4 +- app/lib/open-payments.server.ts | 3 +- app/routes/extension.tsx | 341 ++++++++++++++++++++++++++++++++ app/routes/finish.tsx | 5 +- app/utils/helpers.ts | 10 + 7 files changed, 439 insertions(+), 4 deletions(-) create mode 100644 app/components/footer.tsx create mode 100644 app/components/presets.tsx create mode 100644 app/routes/extension.tsx diff --git a/app/components/footer.tsx b/app/components/footer.tsx new file mode 100644 index 0000000..eabc062 --- /dev/null +++ b/app/components/footer.tsx @@ -0,0 +1,14 @@ +import { Link } from "@remix-run/react"; +import { Logo } from "./ui/logo"; + +export const Footer = () => { + return ( + + by + + Interledger + Pay + + ); +}; +Footer.displayName = "Footer"; diff --git a/app/components/presets.tsx b/app/components/presets.tsx new file mode 100644 index 0000000..709814e --- /dev/null +++ b/app/components/presets.tsx @@ -0,0 +1,66 @@ +import { useEffect } from "react"; +import { cn } from "~/lib/cn"; +import { useDialPadContext } from "~/lib/context/dialpad"; + +const presetPadKeyClasses = "cursor-pointer flex flex-wrap content-center justify-center items-center text-lg aspect-square rounded-full border border-gray-300 w-14"; + +type PresetPadKeyProps = { + label: string; + currency: string; + id: string; +}; +const PresetPadKey = ({ label, id, currency }: PresetPadKeyProps) => { + const { amountValue, setAmountValue } = useDialPadContext(); + + return ( +
  • { + const formattedNumber = Number(id || 0).toFixed(2); + setAmountValue(String(formattedNumber)); + }} + > + {currency} + {label} +
  • + ); +}; +PresetPadKey.displayName = "PresetPadKey"; + +type PresetPadProps = { + values: string[]; + currency: string; + onMore: () => void +}; +export const PresetPad = ({ values, currency, onMore }: PresetPadProps) => { + + return ( + + ); +}; +PresetPad.displayName = "PresetPad"; diff --git a/app/components/quoteDialog.tsx b/app/components/quoteDialog.tsx index e14b1f1..c7dd32d 100644 --- a/app/components/quoteDialog.tsx +++ b/app/components/quoteDialog.tsx @@ -66,12 +66,12 @@ export default function Quote({
    diff --git a/app/lib/open-payments.server.ts b/app/lib/open-payments.server.ts index 210dbdd..f2b434f 100644 --- a/app/lib/open-payments.server.ts +++ b/app/lib/open-payments.server.ts @@ -322,7 +322,8 @@ export async function finishPayment( }, } ) - .catch(() => { + .catch((error) => { + console.log({error}); throw new Error("Could not create outgoing payment."); }); diff --git a/app/routes/extension.tsx b/app/routes/extension.tsx new file mode 100644 index 0000000..c10b894 --- /dev/null +++ b/app/routes/extension.tsx @@ -0,0 +1,341 @@ +import { conform, useForm } from "@conform-to/react"; +import { getFieldsetConstraint, parse } from "@conform-to/zod"; +import { type WalletAddress } from "@interledger/open-payments"; +import { + type ActionFunctionArgs, + json, + redirect, + type LoaderFunctionArgs, +} from "@remix-run/node"; +import { + Form, + Link, + MetaFunction, + useActionData, + useLoaderData, + useNavigation, +} from "@remix-run/react"; +import { z } from "zod"; +import { AmountDisplay, DialPad } from "~/components/dialpad"; +import { PresetPad } from "~/components/presets"; +import { Header } from "~/components/header"; +import Quote from "~/components/quoteDialog"; +import { Button } from "~/components/ui/button"; +import { Field } from "~/components/ui/form/form"; +import { useDialogContext } from "~/lib/context/dialog"; +import { useDialPadContext } from "~/lib/context/dialpad"; +import { fetchQuote, initializePayment } from "~/lib/open-payments.server"; +import { getValidWalletAddress } from "~/lib/validators.server"; +import { commitSession, destroySession, getSession } from "~/session"; +import { formatAmount, objectToUrlParams } from "~/utils/helpers"; +import { useEffect, useState } from "react"; +import { Footer } from "~/components/footer"; +import { cn } from "~/lib/cn"; + +export const meta: MetaFunction = () => { + return [ + { title: "Interledger Pay" }, + { name: "description", content: "Transaction by Interledger Pay!" }, + ]; +}; + +export async function loader({ request }: LoaderFunctionArgs) { + const session = await getSession(request.headers.get("Cookie")); + // const walletAddressInfo = session.get("wallet-address"); + + const params = new URL(request.url).searchParams; + const receiver = params.get("receiver"); + const amount = params.get("amount"); + const asset = params.get("asset"); + const note = params.get("note"); + const action = params.get("action"); + + const isQuote = params.get("quote") || false; + let receiverName = ""; + let receiveAmount = null; + let debitAmount = null; + + let isValidRequest = receiver !== undefined; + let walletAddressInfo; + + try { + if (receiver) { + const formattedReceiver = String(receiver).replace("$", "https://"); + walletAddressInfo = await getValidWalletAddress(formattedReceiver); + } + } catch (error) { + console.log(error); + isValidRequest = false; + } + + const assetScale = 2; + const formattedAmount = formatAmount({ + value: amount ? String(Number(amount) * Math.pow(10, assetScale)) : "0", + assetCode: asset || "usd", + assetScale, + }); + + if (isQuote && isValidRequest && walletAddressInfo) { + const quote = session.get("quote"); + + if (quote === undefined) { + throw new Error("Payment session expired."); + } + + receiverName = + walletAddressInfo.publicName === undefined + ? "Recepient" + : walletAddressInfo.publicName; + + receiveAmount = formatAmount({ + value: quote.receiveAmount.value, + assetCode: quote.receiveAmount.assetCode, + assetScale: quote.receiveAmount.assetScale, + }); + + debitAmount = formatAmount({ + value: quote.debitAmount.value, + assetCode: quote.debitAmount.assetCode, + assetScale: quote.debitAmount.assetScale, + }); + } + + return json({ + receiverWalletAddress: walletAddressInfo ? walletAddressInfo.id : undefined, + amount: formattedAmount, + currency: asset ? asset : "usd", + note: note ? note : null, + action: action ? action : null, + isValidRequest: isValidRequest, + receiveAmount: receiveAmount ? receiveAmount.amountWithCurrency : null, + debitAmount: debitAmount ? debitAmount.amountWithCurrency : null, + receiverName: receiverName, + isQuote: isQuote, + } as const); +} + +const schema = z.object({ + walletAddress: z.string(), + receiver: z + .string() + .transform((val) => val.replace("$", "https://")) + .pipe(z.string().url({ message: "The input is not a wallet address." })), + amount: z.coerce.number(), + note: z.string().optional(), +}); + +export default function Extension() { + const data = useLoaderData(); + const actionData = useActionData(); + const navigation = useNavigation(); + const isSubmitting = navigation.state === "submitting"; + + const { amountValue, setAmountValue, setAssetCode } = useDialPadContext(); + const { setOpen } = useDialogContext(); + const [displayDialPad, setDisplayDialPad] = useState(false); + const [form, fields] = useForm({ + id: "extension-pay-form", + constraint: getFieldsetConstraint(schema), + lastSubmission: actionData, + shouldRevalidate: "onSubmit", + }); + + data.isQuote ? setOpen(true) : setOpen(false); + + useEffect(() => { + setAssetCode(data.currency); + const formattedNumber = data.amount.amount.toFixed(2); + setAmountValue(String(formattedNumber)); + }, [data.amount]); + + return ( + <> +
    + {data.isValidRequest ? ( + <> +
    + +
    +
    + +
    +
    +
    +
    + +
    + setDisplayDialPad(true)} + /> +
    + +
    + + + + +
    + +
    +
    + +
    +
    + + + ) : ( +
    +
    + Invalid request +
    + + + +
    + )} +
    + + ); +} + +export async function action({ request }: ActionFunctionArgs) { + const session = await getSession(request.headers.get("Cookie")); + + let walletAddress; + let receiver = {} as WalletAddress; + + const formData = await request.formData(); + const intent = formData.get("intent"); + + if (intent === "pay") { + const submission = await parse(formData, { + schema: schema.superRefine(async (data, context) => { + try { + data.walletAddress = String(data.walletAddress).replace( + "$", + "https://" + ); + + walletAddress = await getValidWalletAddress(data.walletAddress); + receiver = await getValidWalletAddress(data.receiver); + + session.set("wallet-address", { + walletAddress: walletAddress, + }); + session.set("receiver-wallet-address", receiver); + + return data; + } catch (error) { + context.addIssue({ + path: ["walletAddress"], + code: z.ZodIssueCode.custom, + message: "Wallet address is not valid.", + }); + } + }), + async: true, + }); + + if (!submission.value || submission.intent !== "submit") { + return json(submission); + } + + const quote = await fetchQuote(submission.value, receiver); + session.set("quote", quote); + session.set("submission", submission); + + return redirect( + `/extension?quote=true&${objectToUrlParams(submission.value)}`, + { + headers: { "Set-Cookie": await commitSession(session) }, + } + ); + } else if (intent === "confirm") { + const quote = session.get("quote"); + walletAddress = session.get("wallet-address"); + + if (quote === undefined || walletAddress === undefined) { + throw new Error("Payment session expired."); + } + + const grant = await initializePayment({ + walletAddress: walletAddress.walletAddress.id, + quote: quote, + }); + + session.set("payment-grant", grant); + return redirect(grant.interact.redirect, { + headers: { "Set-Cookie": await commitSession(session) }, + }); + } else { + const submission = session.get("submission"); + const params = submission?.value + ? objectToUrlParams(submission?.value) + : ""; + return redirect(`/extension?${params}`, { + headers: { "Set-Cookie": await destroySession(session) }, + }); + } +} diff --git a/app/routes/finish.tsx b/app/routes/finish.tsx index 499ef32..d5f7e69 100644 --- a/app/routes/finish.tsx +++ b/app/routes/finish.tsx @@ -56,7 +56,10 @@ export async function loader({ request }: LoaderFunctionArgs) { if (quote === undefined) { throw new Error("Payment session expired."); } - +console.log({grant, + quote, + wallet: walletAddressInfo.walletAddress, + interactRef}); const finishPaymentResponse = await finishPayment( grant, quote, diff --git a/app/utils/helpers.ts b/app/utils/helpers.ts index 69526db..7f8e8cb 100644 --- a/app/utils/helpers.ts +++ b/app/utils/helpers.ts @@ -65,3 +65,13 @@ export const formatAmount = (args: FormatAmountArgs): FormattedAmount => { symbol, }; }; + +export const objectToUrlParams = (obj: { [key: string]: number | string }) => { + const params = []; + + for (const key in obj) { + params.push(`${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`); + } + + return params.join("&"); +}; From 71f26b3c6367acf7aae384736debedcceae88442 Mon Sep 17 00:00:00 2001 From: Radu-Cristian Popa Date: Tue, 6 Aug 2024 18:52:39 +0300 Subject: [PATCH 02/11] Use correct access token --- app/lib/open-payments.server.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/lib/open-payments.server.ts b/app/lib/open-payments.server.ts index f2b434f..fb8d680 100644 --- a/app/lib/open-payments.server.ts +++ b/app/lib/open-payments.server.ts @@ -5,6 +5,7 @@ import { type WalletAddress, createAuthenticatedClient, isPendingGrant, + isFinalizedGrant, } from "@interledger/open-payments"; import { createId } from "@paralleldrive/cuid2"; import { randomUUID } from "crypto"; @@ -306,13 +307,17 @@ export async function finishPayment( } ); + if (!isFinalizedGrant(continuation)) { + throw new Error('Expected finalized grant. Received non-finalized grant.') + } + const url = new URL(walletAddress.id).origin; const outgoingPayment = await opClient.outgoingPayment .create( { url: url, - accessToken: continuation.continue.access_token.value, + accessToken: continuation.access_token.value, }, { walletAddress: walletAddress.id, From 31c7de8d91927805471bdbf497074ea6248fa082 Mon Sep 17 00:00:00 2001 From: Arpi Date: Wed, 7 Aug 2024 16:16:40 +0300 Subject: [PATCH 03/11] small fixes --- app/components/dialpad.tsx | 4 +- app/components/footer.tsx | 8 +- app/components/presets.tsx | 13 ++-- app/lib/cn.ts | 6 +- app/lib/open-payments.server.ts | 7 +- app/routes/extension.tsx | 3 +- app/routes/finish.tsx | 5 +- app/tailwind.css | 4 +- package.json | 7 +- pnpm-lock.yaml | 129 +++++++++++++++++++++++++++++--- 10 files changed, 147 insertions(+), 39 deletions(-) diff --git a/app/components/dialpad.tsx b/app/components/dialpad.tsx index 8f91eb4..e2f5a7e 100644 --- a/app/components/dialpad.tsx +++ b/app/components/dialpad.tsx @@ -103,7 +103,7 @@ const DialPadKey = ({ label, id }: DialPadKeyProps) => { setAmountValue(`${amountValue.substring(0, amountValue.length - 1)}`); } else if (amountValue === "0" && id !== DialPadIds.Dot) { setAmountValue( - `${amountValue.substring(0, amountValue.length - 1)}${label}` + `${amountValue.substring(0, amountValue.length - 1)}${label}`, ); } else if ( (id === DialPadIds.Dot && @@ -120,7 +120,7 @@ const DialPadKey = ({ label, id }: DialPadKeyProps) => {
  • { return ( - - by + + by Interledger Pay diff --git a/app/components/presets.tsx b/app/components/presets.tsx index 709814e..6ccf9dd 100644 --- a/app/components/presets.tsx +++ b/app/components/presets.tsx @@ -2,7 +2,8 @@ import { useEffect } from "react"; import { cn } from "~/lib/cn"; import { useDialPadContext } from "~/lib/context/dialpad"; -const presetPadKeyClasses = "cursor-pointer flex flex-wrap content-center justify-center items-center text-lg aspect-square rounded-full border border-gray-300 w-14"; +const presetPadKeyClasses = + "cursor-pointer flex flex-wrap content-center justify-center items-center text-lg aspect-square rounded-full border border-gray-300 w-14"; type PresetPadKeyProps = { label: string; @@ -14,10 +15,7 @@ const PresetPadKey = ({ label, id, currency }: PresetPadKeyProps) => { return (
  • { const formattedNumber = Number(id || 0).toFixed(2); @@ -34,10 +32,9 @@ PresetPadKey.displayName = "PresetPadKey"; type PresetPadProps = { values: string[]; currency: string; - onMore: () => void + onMore: () => void; }; export const PresetPad = ({ values, currency, onMore }: PresetPadProps) => { - return (
      @@ -53,7 +50,7 @@ export const PresetPad = ({ values, currency, onMore }: PresetPadProps) => { className={cn( presetPadKeyClasses, "rotate-90 pb-2.5", - "hover:bg-green-2" + "hover:bg-green-2", )} onClick={() => onMore()} > diff --git a/app/lib/cn.ts b/app/lib/cn.ts index 7637fb9..a5ef193 100644 --- a/app/lib/cn.ts +++ b/app/lib/cn.ts @@ -1,6 +1,6 @@ -import { clsx, type ClassValue } from 'clsx'; -import { twMerge } from 'tailwind-merge'; +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)); + return twMerge(clsx(inputs)); } diff --git a/app/lib/open-payments.server.ts b/app/lib/open-payments.server.ts index fb8d680..a255df3 100644 --- a/app/lib/open-payments.server.ts +++ b/app/lib/open-payments.server.ts @@ -78,7 +78,7 @@ export async function fetchQuote( ) .catch(() => { throw new Error( - `Could not create quote for receiver ${receiver.publicName}.` + `Could not create quote for receiver ${receiver.publicName}.`, ); }); @@ -308,7 +308,7 @@ export async function finishPayment( ); if (!isFinalizedGrant(continuation)) { - throw new Error('Expected finalized grant. Received non-finalized grant.') + throw new Error("Expected finalized grant. Received non-finalized grant."); } const url = new URL(walletAddress.id).origin; @@ -328,13 +328,12 @@ export async function finishPayment( } ) .catch((error) => { - console.log({error}); throw new Error("Could not create outgoing payment."); }); return { url: outgoingPayment.id, - accessToken: continuation.continue.access_token.value, + accessToken: continuation.access_token.value }; } diff --git a/app/routes/extension.tsx b/app/routes/extension.tsx index c10b894..a20f024 100644 --- a/app/routes/extension.tsx +++ b/app/routes/extension.tsx @@ -7,10 +7,11 @@ import { redirect, type LoaderFunctionArgs, } from "@remix-run/node"; +import type { + MetaFunction} from "@remix-run/react"; import { Form, Link, - MetaFunction, useActionData, useLoaderData, useNavigation, diff --git a/app/routes/finish.tsx b/app/routes/finish.tsx index d5f7e69..499ef32 100644 --- a/app/routes/finish.tsx +++ b/app/routes/finish.tsx @@ -56,10 +56,7 @@ export async function loader({ request }: LoaderFunctionArgs) { if (quote === undefined) { throw new Error("Payment session expired."); } -console.log({grant, - quote, - wallet: walletAddressInfo.walletAddress, - interactRef}); + const finishPaymentResponse = await finishPayment( grant, quote, diff --git a/app/tailwind.css b/app/tailwind.css index 05148cc..7ed0045 100644 --- a/app/tailwind.css +++ b/app/tailwind.css @@ -42,7 +42,9 @@ body { @apply bg-background text-foreground; - font-feature-settings: "rlig" 1, "calt" 1; + font-feature-settings: + "rlig" 1, + "calt" 1; #nprogress { @apply pointer-events-none; diff --git a/package.json b/package.json index b8f89cd..61a2e52 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,9 @@ "typecheck": "tsc" }, "dependencies": { - "@conform-to/dom": "0.9.1", - "@conform-to/react": "^0.9.1", - "@conform-to/zod": "^0.9.1", + "@conform-to/dom": "0.9.2", + "@conform-to/react": "^0.9.2", + "@conform-to/zod": "^0.9.2", "@headlessui/react": "^1.7.18", "@interledger/open-payments": "6.13.1", "@paralleldrive/cuid2": "^2.2.2", @@ -41,6 +41,7 @@ "@flydotio/dockerfile": "^0.4.11", "@remix-run/dev": "^2.8.1", "@remix-run/eslint-config": "^2.8.1", + "@types/koa": "^2.15.0", "@types/nprogress": "^0.2.3", "@types/react": "^18.2.65", "@types/react-dom": "^18.2.21", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bd9e48e..ca0c119 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,14 +6,14 @@ settings: dependencies: '@conform-to/dom': - specifier: 0.9.1 - version: 0.9.1 + specifier: 0.9.2 + version: 0.9.2 '@conform-to/react': - specifier: ^0.9.1 + specifier: ^0.9.2 version: 0.9.2(react@18.3.1) '@conform-to/zod': - specifier: ^0.9.1 - version: 0.9.2(@conform-to/dom@0.9.1)(zod@3.23.8) + specifier: ^0.9.2 + version: 0.9.2(@conform-to/dom@0.9.2)(zod@3.23.8) '@headlessui/react': specifier: ^1.7.18 version: 1.7.19(react-dom@18.3.1)(react@18.3.1) @@ -88,6 +88,9 @@ devDependencies: '@remix-run/eslint-config': specifier: ^2.8.1 version: 2.9.2(eslint@8.57.0)(react@18.3.1)(typescript@5.4.5) + '@types/koa': + specifier: ^2.15.0 + version: 2.15.0 '@types/nprogress': specifier: ^0.2.3 version: 0.2.3 @@ -539,10 +542,6 @@ packages: to-fast-properties: 2.0.0 dev: true - /@conform-to/dom@0.9.1: - resolution: {integrity: sha512-+T6QgpLDPZ29j4y5nWW61WMe8qj0cQBymyzbi5sq1f3CcBO3wBbR0VPI7mbm+rLoErHWM3ghhdNlIkw0UPoLLA==} - dev: false - /@conform-to/dom@0.9.2: resolution: {integrity: sha512-vYJvXMRxa9Ieslv5eVcak5+0QbcTv+HwdLqRPZ8lhSFZx5qQWJwdou+Iz+ERzsSQHh7QS3DDYG5HmzaN4evf9g==} dev: false @@ -556,13 +555,13 @@ packages: react: 18.3.1 dev: false - /@conform-to/zod@0.9.2(@conform-to/dom@0.9.1)(zod@3.23.8): + /@conform-to/zod@0.9.2(@conform-to/dom@0.9.2)(zod@3.23.8): resolution: {integrity: sha512-treG9ZcuNuRERQ1uYvJSWT0zZuqHnYTzRwucg20+/WdjgKNSb60Br+Cy6BAHvVQ8dN6wJsGkHenkX2mSVw3xOA==} peerDependencies: '@conform-to/dom': 0.9.2 zod: ^3.21.0 dependencies: - '@conform-to/dom': 0.9.1 + '@conform-to/dom': 0.9.2 zod: 3.23.8 dev: false @@ -1740,6 +1739,12 @@ packages: pretty-format: 27.5.1 dev: true + /@types/accepts@1.3.7: + resolution: {integrity: sha512-Pay9fq2lM2wXPWbteBsRAGiWH2hig4ZE2asK+mm7kUzlxRTfL961rj89I6zV/E3PcIkDqyuBEcMxFT7rccugeQ==} + dependencies: + '@types/node': 20.12.12 + dev: true + /@types/acorn@4.0.6: resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==} dependencies: @@ -1750,9 +1755,35 @@ packages: resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} dev: true + /@types/body-parser@1.19.5: + resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} + dependencies: + '@types/connect': 3.4.38 + '@types/node': 20.12.12 + dev: true + + /@types/connect@3.4.38: + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + dependencies: + '@types/node': 20.12.12 + dev: true + + /@types/content-disposition@0.5.8: + resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==} + dev: true + /@types/cookie@0.6.0: resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + /@types/cookies@0.9.0: + resolution: {integrity: sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==} + dependencies: + '@types/connect': 3.4.38 + '@types/express': 4.17.21 + '@types/keygrip': 1.0.6 + '@types/node': 20.12.12 + dev: true + /@types/debug@4.1.12: resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} dependencies: @@ -1769,12 +1800,38 @@ packages: resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} dev: true + /@types/express-serve-static-core@4.19.5: + resolution: {integrity: sha512-y6W03tvrACO72aijJ5uF02FRq5cgDR9lUxddQ8vyF+GvmjJQqbzDcJngEjURc+ZsG31VI3hODNZJ2URj86pzmg==} + dependencies: + '@types/node': 20.12.12 + '@types/qs': 6.9.15 + '@types/range-parser': 1.2.7 + '@types/send': 0.17.4 + dev: true + + /@types/express@4.17.21: + resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} + dependencies: + '@types/body-parser': 1.19.5 + '@types/express-serve-static-core': 4.19.5 + '@types/qs': 6.9.15 + '@types/serve-static': 1.15.7 + dev: true + /@types/hast@2.3.10: resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} dependencies: '@types/unist': 2.0.10 dev: true + /@types/http-assert@1.5.5: + resolution: {integrity: sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==} + dev: true + + /@types/http-errors@2.0.4: + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + dev: true + /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1782,6 +1839,29 @@ packages: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} dev: true + /@types/keygrip@1.0.6: + resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} + dev: true + + /@types/koa-compose@3.2.8: + resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==} + dependencies: + '@types/koa': 2.15.0 + dev: true + + /@types/koa@2.15.0: + resolution: {integrity: sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==} + dependencies: + '@types/accepts': 1.3.7 + '@types/content-disposition': 0.5.8 + '@types/cookies': 0.9.0 + '@types/http-assert': 1.5.5 + '@types/http-errors': 2.0.4 + '@types/keygrip': 1.0.6 + '@types/koa-compose': 3.2.8 + '@types/node': 20.12.12 + dev: true + /@types/lodash.clonedeep@4.5.9: resolution: {integrity: sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==} dependencies: @@ -1802,6 +1882,10 @@ packages: resolution: {integrity: sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw==} dev: true + /@types/mime@1.3.5: + resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + dev: true + /@types/ms@0.7.34: resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==} dev: true @@ -1819,6 +1903,14 @@ packages: /@types/prop-types@15.7.12: resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + /@types/qs@6.9.15: + resolution: {integrity: sha512-uXHQKES6DQKKCLh441Xv/dwxOq1TVS3JPUMlEqoEglvlhR6Mxnlew/Xq/LRVHpLyk7iK3zODe1qYHIMltO7XGg==} + dev: true + + /@types/range-parser@1.2.7: + resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} + dev: true + /@types/react-dom@18.3.0: resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} dependencies: @@ -1834,6 +1926,21 @@ packages: resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} dev: true + /@types/send@0.17.4: + resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} + dependencies: + '@types/mime': 1.3.5 + '@types/node': 20.12.12 + dev: true + + /@types/serve-static@1.15.7: + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + dependencies: + '@types/http-errors': 2.0.4 + '@types/node': 20.12.12 + '@types/send': 0.17.4 + dev: true + /@types/unist@2.0.10: resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==} dev: true From 3a4e7799887bf71e5aad5c9e0c30b192665409cd Mon Sep 17 00:00:00 2001 From: Arpi Date: Thu, 8 Aug 2024 23:03:35 +0300 Subject: [PATCH 04/11] close button on finish page if request is from extension page & cleanup --- app/routes/extension.tsx | 37 +++++++++++++++++++++--------------- app/routes/finish.tsx | 41 ++++++++++++++++++++++++++++++++-------- app/utils/helpers.ts | 2 ++ 3 files changed, 57 insertions(+), 23 deletions(-) diff --git a/app/routes/extension.tsx b/app/routes/extension.tsx index a20f024..1aba102 100644 --- a/app/routes/extension.tsx +++ b/app/routes/extension.tsx @@ -7,8 +7,7 @@ import { redirect, type LoaderFunctionArgs, } from "@remix-run/node"; -import type { - MetaFunction} from "@remix-run/react"; +import type { MetaFunction } from "@remix-run/react"; import { Form, Link, @@ -19,7 +18,6 @@ import { import { z } from "zod"; import { AmountDisplay, DialPad } from "~/components/dialpad"; import { PresetPad } from "~/components/presets"; -import { Header } from "~/components/header"; import Quote from "~/components/quoteDialog"; import { Button } from "~/components/ui/button"; import { Field } from "~/components/ui/form/form"; @@ -28,7 +26,7 @@ import { useDialPadContext } from "~/lib/context/dialpad"; import { fetchQuote, initializePayment } from "~/lib/open-payments.server"; import { getValidWalletAddress } from "~/lib/validators.server"; import { commitSession, destroySession, getSession } from "~/session"; -import { formatAmount, objectToUrlParams } from "~/utils/helpers"; +import { formatAmount, objectToUrlParams, predefinedPaymentValues } from "~/utils/helpers"; import { useEffect, useState } from "react"; import { Footer } from "~/components/footer"; import { cn } from "~/lib/cn"; @@ -42,7 +40,6 @@ export const meta: MetaFunction = () => { export async function loader({ request }: LoaderFunctionArgs) { const session = await getSession(request.headers.get("Cookie")); - // const walletAddressInfo = session.get("wallet-address"); const params = new URL(request.url).searchParams; const receiver = params.get("receiver"); @@ -57,15 +54,16 @@ export async function loader({ request }: LoaderFunctionArgs) { let debitAmount = null; let isValidRequest = receiver !== undefined; - let walletAddressInfo; + let receiverWalletAddressInfo; try { if (receiver) { const formattedReceiver = String(receiver).replace("$", "https://"); - walletAddressInfo = await getValidWalletAddress(formattedReceiver); + receiverWalletAddressInfo = await getValidWalletAddress( + formattedReceiver + ); } } catch (error) { - console.log(error); isValidRequest = false; } @@ -76,7 +74,7 @@ export async function loader({ request }: LoaderFunctionArgs) { assetScale, }); - if (isQuote && isValidRequest && walletAddressInfo) { + if (isQuote && isValidRequest && receiverWalletAddressInfo) { const quote = session.get("quote"); if (quote === undefined) { @@ -84,9 +82,9 @@ export async function loader({ request }: LoaderFunctionArgs) { } receiverName = - walletAddressInfo.publicName === undefined + receiverWalletAddressInfo.publicName === undefined ? "Recepient" - : walletAddressInfo.publicName; + : receiverWalletAddressInfo.publicName; receiveAmount = formatAmount({ value: quote.receiveAmount.value, @@ -102,7 +100,9 @@ export async function loader({ request }: LoaderFunctionArgs) { } return json({ - receiverWalletAddress: walletAddressInfo ? walletAddressInfo.id : undefined, + receiverWalletAddress: receiverWalletAddressInfo + ? receiverWalletAddressInfo.id + : undefined, amount: formattedAmount, currency: asset ? asset : "usd", note: note ? note : null, @@ -123,6 +123,7 @@ const schema = z.object({ .pipe(z.string().url({ message: "The input is not a wallet address." })), amount: z.coerce.number(), note: z.string().optional(), + action: z.string().optional(), }); export default function Extension() { @@ -164,7 +165,7 @@ export default function Extension() {
      +
      @@ -263,6 +269,7 @@ export default function Extension() { export async function action({ request }: ActionFunctionArgs) { const session = await getSession(request.headers.get("Cookie")); + session.set("fromExtension", true); let walletAddress; let receiver = {} as WalletAddress; diff --git a/app/routes/finish.tsx b/app/routes/finish.tsx index 499ef32..139e3d5 100644 --- a/app/routes/finish.tsx +++ b/app/routes/finish.tsx @@ -15,6 +15,7 @@ import { checkOutgoingPayment, } from "~/lib/open-payments.server"; import { destroySession, getSession } from "~/session"; +import { objectToUrlParams } from "~/utils/helpers"; export async function loader({ request }: LoaderFunctionArgs) { const searchParams = new URL(request.url).searchParams; @@ -52,6 +53,7 @@ export async function loader({ request }: LoaderFunctionArgs) { const grant = session.get("payment-grant"); const walletAddressInfo = session.get("wallet-address"); const isRequestPayment = session.get("isRequestPayment"); + const isFromExtension = session.get("fromExtension"); if (quote === undefined) { throw new Error("Payment session expired."); @@ -72,10 +74,16 @@ export async function loader({ request }: LoaderFunctionArgs) { isRequestPayment ); - return defer({ checkOutgoingPayment: checkOutgoingPaymentPromise }); + return defer({ + checkOutgoingPayment: checkOutgoingPaymentPromise, + isFromExtension, + }); } - return defer({ checkOutgoingPayment: Promise.resolve(paymentResult) }); + return defer({ + checkOutgoingPayment: Promise.resolve(paymentResult), + isFromExtension: false, + }); } export default function Finish() { @@ -93,7 +101,7 @@ export default function Finish() { return ( <> -
      + {!data.isFromExtension &&
      }
      }> @@ -111,8 +119,13 @@ export default function Finish() { {outgoingPaymentCheck.message}
      -
      @@ -130,8 +143,13 @@ export default function Finish() { {outgoingPaymentCheck.message}
      -
      @@ -147,8 +165,15 @@ export default function Finish() { export async function action({ request }: LoaderFunctionArgs) { const session = await getSession(request.headers.get("Cookie")); + const isFromExtension = session.get("fromExtension"); + const submission = session.get("submission"); + if (submission?.value?.walletAddress) { + delete submission.value.walletAddress; + } + const params = submission?.value ? objectToUrlParams(submission?.value) : ""; + const path = isFromExtension ? `/extension?${params}` : `/`; - return redirect("/", { + return redirect(path, { headers: { "Set-Cookie": await destroySession(session) }, }); } diff --git a/app/utils/helpers.ts b/app/utils/helpers.ts index 7f8e8cb..c3f881d 100644 --- a/app/utils/helpers.ts +++ b/app/utils/helpers.ts @@ -1,5 +1,7 @@ import { type Amount } from "~/lib/open-payments.server"; +export const predefinedPaymentValues = ["1", "5", "10"]; + export const getCurrencySymbol = (assetCode: string): string => { return new Intl.NumberFormat("en-US", { currency: assetCode, From 59826ceb9d751efb9e9af11a0745131d56dec802 Mon Sep 17 00:00:00 2001 From: Arpi Date: Mon, 12 Aug 2024 13:22:46 +0300 Subject: [PATCH 05/11] adjustments after integration test w wm tools --- app/components/dialpad.tsx | 2 +- app/components/footer.tsx | 18 ------------------ app/components/presets.tsx | 12 ++++-------- app/root.tsx | 25 ++++++++++++++++++++++--- app/routes/extension.tsx | 38 ++++++++++++++++++++++++++++++++------ app/utils/helpers.ts | 30 ++++++++++++++++++++++++++++++ 6 files changed, 89 insertions(+), 36 deletions(-) delete mode 100644 app/components/footer.tsx diff --git a/app/components/dialpad.tsx b/app/components/dialpad.tsx index e2f5a7e..20b2989 100644 --- a/app/components/dialpad.tsx +++ b/app/components/dialpad.tsx @@ -144,7 +144,7 @@ export const AmountDisplay = (args: AmountDisplayProps) => { : `${getCurrencySymbol(assetCode)} ${amountValue}`; return ( -
      +
      {value}
      ); diff --git a/app/components/footer.tsx b/app/components/footer.tsx deleted file mode 100644 index 318a509..0000000 --- a/app/components/footer.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { Link } from "@remix-run/react"; -import { Logo } from "./ui/logo"; - -export const Footer = () => { - return ( - - by - - Interledger - Pay - - ); -}; -Footer.displayName = "Footer"; diff --git a/app/components/presets.tsx b/app/components/presets.tsx index 6ccf9dd..0b26e87 100644 --- a/app/components/presets.tsx +++ b/app/components/presets.tsx @@ -11,11 +11,11 @@ type PresetPadKeyProps = { id: string; }; const PresetPadKey = ({ label, id, currency }: PresetPadKeyProps) => { - const { amountValue, setAmountValue } = useDialPadContext(); + const { setAmountValue } = useDialPadContext(); return (
    • { const formattedNumber = Number(id || 0).toFixed(2); @@ -37,7 +37,7 @@ type PresetPadProps = { export const PresetPad = ({ values, currency, onMore }: PresetPadProps) => { return (
        -
        +
        {values.map((value) => ( { /> ))}
      • onMore()} > ... diff --git a/app/root.tsx b/app/root.tsx index 8207beb..4dac3f8 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -9,6 +9,7 @@ import { Scripts, ScrollRestoration, isRouteErrorResponse, + useLocation, useNavigation, useRouteError, } from "@remix-run/react"; @@ -21,6 +22,7 @@ import { Button } from "./components/ui/button"; import { FinishError } from "./components/icons"; import { DialogProvider } from "./components/providers/dialogProvider"; import { BackdropProvider } from "./components/providers/backdropProvider"; +import { cn } from "./lib/cn"; export const links: LinksFunction = () => [ { @@ -35,6 +37,10 @@ export const links: LinksFunction = () => [ export default function App() { const navigation = useNavigation(); + const location = useLocation(); + + // detect if it's loaded in wm tools + const isEmbeded = location.pathname.indexOf("extension") !== -1; useEffect(() => { if (navigation.state === "loading" || navigation.state === "submitting") { @@ -45,7 +51,10 @@ export default function App() { }, [navigation.state]); return ( - + @@ -53,8 +62,18 @@ export default function App() { - -
        + +
        diff --git a/app/routes/extension.tsx b/app/routes/extension.tsx index 1aba102..85c4fbd 100644 --- a/app/routes/extension.tsx +++ b/app/routes/extension.tsx @@ -26,9 +26,14 @@ import { useDialPadContext } from "~/lib/context/dialpad"; import { fetchQuote, initializePayment } from "~/lib/open-payments.server"; import { getValidWalletAddress } from "~/lib/validators.server"; import { commitSession, destroySession, getSession } from "~/session"; -import { formatAmount, objectToUrlParams, predefinedPaymentValues } from "~/utils/helpers"; +import { + isMessageEvent, + formatAmount, + objectToUrlParams, + predefinedPaymentValues, + sanitizeAndAddCss, +} from "~/utils/helpers"; import { useEffect, useState } from "react"; -import { Footer } from "~/components/footer"; import { cn } from "~/lib/cn"; export const meta: MetaFunction = () => { @@ -126,6 +131,21 @@ const schema = z.object({ action: z.string().optional(), }); +const addCssFromMessage = (e: Event) => { + if (!isMessageEvent(e)) { + throw new Error("not a message event"); + } + + // check if it's received in expected format + if (e.data?.configCss) { + const existingStyle = document.getElementById("wm_tools_styles"); + if (existingStyle) { + existingStyle.remove(); + } + sanitizeAndAddCss(e.data.configCss); + } +}; + export default function Extension() { const data = useLoaderData(); const actionData = useActionData(); @@ -150,6 +170,13 @@ export default function Extension() { setAmountValue(String(formattedNumber)); }, [data.amount]); + useEffect(() => { + window.addEventListener("message", addCssFromMessage); + return () => { + window.removeEventListener("message", addCssFromMessage); + }; + }, []); + return ( <>
        @@ -157,12 +184,12 @@ export default function Extension() { <>
        -
        +
        -
        +
        -
        { return params.join("&"); }; + +// ensure received css is non corupted, valid css +export const sanitizeAndAddCss = (css: string) => { + const iframe = document.createElement("iframe"); + iframe.style.display = "none"; + iframe.style.width = "10px"; + iframe.style.height = "10px"; + document.body.appendChild(iframe); + if (iframe && iframe.contentDocument) { + const style = iframe.contentDocument.createElement("style"); + style.innerHTML = css; + iframe.contentDocument.head.appendChild(style); + const sheet = style.sheet as CSSStyleSheet; + const result = Array.from(sheet.cssRules) + .map((rule) => rule.cssText || "") + .join("\n"); + iframe.remove(); + + const sanitizedCss = document.createElement('style'); + sanitizedCss.setAttribute("id", "wm_tools_styles"); + + sanitizedCss.innerHTML = result || ""; + document.head.appendChild(sanitizedCss); + } +}; + +export const isMessageEvent = (event: Event): event is MessageEvent => { + return "data" in event; +}; + From 826201f5deba6579d7750240653aa0a8369c2350 Mon Sep 17 00:00:00 2001 From: Arpi Date: Sun, 25 Aug 2024 23:03:45 +0300 Subject: [PATCH 06/11] local https & debugging --- .dockerignore | 3 +- .gitignore | 3 +- Dockerfile.dev | 38 +++++ app/components/quoteDialog.tsx | 6 +- app/routes/extension.tsx | 36 ++++- app/session.ts | 4 +- docker-compose.dev.yml | 31 ++++ nginx.conf | 48 ++++++ pnpm-lock.yaml | 265 ++++++++++++++++++++++++++++++++- 9 files changed, 419 insertions(+), 15 deletions(-) create mode 100644 Dockerfile.dev create mode 100644 docker-compose.dev.yml create mode 100644 nginx.conf diff --git a/.dockerignore b/.dockerignore index 50478a7..78c1975 100644 --- a/.dockerignore +++ b/.dockerignore @@ -5,4 +5,5 @@ build /.vscode Dockerfile .dockerignore -.git \ No newline at end of file +.git +/.cert \ No newline at end of file diff --git a/.gitignore b/.gitignore index 45faad6..69a573f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ node_modules .env build /public/build -/.vscode \ No newline at end of file +/.vscode +/.cert \ No newline at end of file diff --git a/Dockerfile.dev b/Dockerfile.dev new file mode 100644 index 0000000..35cbb9c --- /dev/null +++ b/Dockerfile.dev @@ -0,0 +1,38 @@ +# syntax = docker/dockerfile:1 + +ARG NODE_VERSION=20.6.1 +FROM node:${NODE_VERSION}-slim as base + +# Remix app lives here +WORKDIR /app + +# Install pnpm +ARG PNPM_VERSION=8.15.5 +RUN npm install -g pnpm@$PNPM_VERSION + + +# Throw-away build stage to reduce size of final image +FROM base as build + +# Install packages needed to build node modules +RUN apt-get update -qq && \ + apt-get install -y build-essential pkg-config python-is-python3 + +# Install node modules +COPY --link package.json pnpm-lock.yaml ./ +RUN pnpm install --frozen-lockfile --prod=false + +# Copy application code +COPY --link . . + +# Build application +RUN pnpm run build + +# Final stage for app image +FROM base + +# Copy built application +COPY --from=build /app /app + +EXPOSE 5200 +CMD [ "pnpm", "run", "start" ] diff --git a/app/components/quoteDialog.tsx b/app/components/quoteDialog.tsx index c7dd32d..fd272eb 100644 --- a/app/components/quoteDialog.tsx +++ b/app/components/quoteDialog.tsx @@ -35,7 +35,11 @@ export default function Quote({ return ( - setOpen(false)}> + setOpen(false)} + > { export async function loader({ request }: LoaderFunctionArgs) { const session = await getSession(request.headers.get("Cookie")); + const submission = session.get("submission"); const params = new URL(request.url).searchParams; const receiver = params.get("receiver"); @@ -63,12 +64,16 @@ export async function loader({ request }: LoaderFunctionArgs) { try { if (receiver) { - const formattedReceiver = String(receiver).replace("$", "https://"); + const formattedReceiver = decodeURI(String(receiver)).replace( + "$", + "https://" + ); receiverWalletAddressInfo = await getValidWalletAddress( formattedReceiver ); } } catch (error) { + console.log(error) isValidRequest = false; } @@ -117,6 +122,7 @@ export async function loader({ request }: LoaderFunctionArgs) { debitAmount: debitAmount ? debitAmount.amountWithCurrency : null, receiverName: receiverName, isQuote: isQuote, + submission, } as const); } @@ -155,13 +161,13 @@ export default function Extension() { const { amountValue, setAmountValue, setAssetCode } = useDialPadContext(); const { setOpen } = useDialogContext(); const [displayDialPad, setDisplayDialPad] = useState(false); + const [wmToolsData, setWmToolsData] = useState(); const [form, fields] = useForm({ id: "extension-pay-form", constraint: getFieldsetConstraint(schema), lastSubmission: actionData, shouldRevalidate: "onSubmit", }); - data.isQuote ? setOpen(true) : setOpen(false); useEffect(() => { @@ -171,12 +177,33 @@ export default function Extension() { }, [data.amount]); useEffect(() => { + // if (wmToolsDataWindow) { + // console.log(wmToolsDataWindow); + // setWmToolsData(wmToolsDataWindow); + // } + window.addEventListener("message", addCssFromMessage); return () => { window.removeEventListener("message", addCssFromMessage); }; }, []); + // useEffect(() => { + // data.isQuote ? setOpen(true) : setOpen(false); + // if (data.isQuote) { + // console.log("here"); + // window.open( + // `/extension?quote=true&${objectToUrlParams(data.submission.value)}`, + // "_blank" + // ); + // } + // }, [data.isQuote]); + + // if (typeof window ! == 'undefined') { + // const wmToolsDataWindow = (window as any).parent; + // console.log(wmToolsDataWindow._wm_tools_data) + // } + return ( <>
        @@ -188,7 +215,7 @@ export default function Extension() { displayDialPad ? "" : "hidden" )} > - + {displayDialPad && }