From e9bb85a212dbd9b86875e89a0aca5d805e2ad61b Mon Sep 17 00:00:00 2001
From: Sebastian <sebasjm@gmail.com>
Date: Fri, 13 Aug 2021 18:04:05 -0300
Subject: new wallet UI and more tests

---
 .../taler-wallet-webextension/src/wallet/Pay.tsx   | 287 ++++++++++++---------
 1 file changed, 158 insertions(+), 129 deletions(-)

(limited to 'packages/taler-wallet-webextension/src/wallet/Pay.tsx')

diff --git a/packages/taler-wallet-webextension/src/wallet/Pay.tsx b/packages/taler-wallet-webextension/src/wallet/Pay.tsx
index bd06656c7..a5849bb28 100644
--- a/packages/taler-wallet-webextension/src/wallet/Pay.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Pay.tsx
@@ -29,7 +29,7 @@ import * as wxApi from "../wxApi";
 
 import { useState, useEffect } from "preact/hooks";
 
-import { getJsonI18n, i18n } from "@gnu-taler/taler-util";
+import { ConfirmPayResultDone, getJsonI18n, i18n } from "@gnu-taler/taler-util";
 import {
   PreparePayResult,
   ConfirmPayResult,
@@ -45,13 +45,54 @@ interface Props {
   talerPayUri?: string
 }
 
+export function AlreadyPaid({ payStatus }: { payStatus: PreparePayResult }) {
+  const fulfillmentUrl = payStatus.contractTerms.fulfillment_url;
+  let message;
+  if (fulfillmentUrl) {
+    message = (
+      <span>
+        You have already paid for this article. Click{" "}
+        <a href={fulfillmentUrl} target="_bank" rel="external">here</a> to view it again.
+      </span>
+    );
+  } else {
+    message = <span>
+      You have already paid for this article:{" "}
+      <em>
+        {payStatus.contractTerms.fulfillment_message ?? "no message given"}
+      </em>
+    </span>;
+  }
+  return <section class="main">
+    <h1>GNU Taler Wallet</h1>
+    <article class="fade">
+      {message}
+    </article>
+  </section>
+}
+
+const doPayment = async (payStatus: PreparePayResult): Promise<ConfirmPayResultDone> => {
+  if (payStatus.status !== "payment-possible") {
+    throw Error(`invalid state: ${payStatus.status}`);
+  }
+  const proposalId = payStatus.proposalId;
+  const res = await wxApi.confirmPay(proposalId, undefined);
+  if (res.type !== ConfirmPayResultType.Done) {
+    throw Error("payment pending");
+  }
+  const fu = res.contractTerms.fulfillment_url;
+  if (fu) {
+    document.location.href = fu;
+  }
+  return res;
+};
+
+
+
 export function PayPage({ talerPayUri }: Props): JSX.Element {
   const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>(undefined);
   const [payResult, setPayResult] = useState<ConfirmPayResult | undefined>(undefined);
   const [payErrMsg, setPayErrMsg] = useState<string | undefined>("");
-  const [numTries, setNumTries] = useState(0);
-  const [loading, setLoading] = useState(false);
-  let totalFees: AmountJson | undefined = undefined;
 
   useEffect(() => {
     if (!talerPayUri) return;
@@ -60,53 +101,67 @@ export function PayPage({ talerPayUri }: Props): JSX.Element {
       setPayStatus(p);
     };
     doFetch();
-  }, [numTries, talerPayUri]);
+  }, [talerPayUri]);
 
   if (!talerPayUri) {
     return <span>missing pay uri</span>
   }
-  
+
   if (!payStatus) {
     return <span>Loading payment information ...</span>;
   }
 
-  let insufficientBalance = false;
-  if (payStatus.status == PreparePayResultType.InsufficientBalance) {
-    insufficientBalance = true;
-  }
-
-  if (payStatus.status === PreparePayResultType.PaymentPossible) {
-    const amountRaw = Amounts.parseOrThrow(payStatus.amountRaw);
-    const amountEffective: AmountJson = Amounts.parseOrThrow(
-      payStatus.amountEffective,
-    );
-    totalFees = Amounts.sub(amountEffective, amountRaw).amount;
-  }
-
-  if (
-    payStatus.status === PreparePayResultType.AlreadyConfirmed &&
-    numTries === 0
-  ) {
-    const fulfillmentUrl = payStatus.contractTerms.fulfillment_url;
-    if (fulfillmentUrl) {
+  if (payResult && payResult.type === ConfirmPayResultType.Done) {
+    if (payResult.contractTerms.fulfillment_message) {
+      const obj = {
+        fulfillment_message: payResult.contractTerms.fulfillment_message,
+        fulfillment_message_i18n:
+          payResult.contractTerms.fulfillment_message_i18n,
+      };
+      const msg = getJsonI18n(obj, "fulfillment_message");
       return (
-        <span>
-          You have already paid for this article. Click{" "}
-          <a href={fulfillmentUrl} target="_bank" rel="external">here</a> to view it again.
-        </span>
+        <div>
+          <p>Payment succeeded.</p>
+          <p>{msg}</p>
+        </div>
       );
     } else {
-      <span>
-        You have already paid for this article:{" "}
-        <em>
-          {payStatus.contractTerms.fulfillment_message ?? "no message given"}
-        </em>
-      </span>;
+      return <span>Redirecting ...</span>;
     }
   }
 
+  const onClick = async () => {
+    try {
+      const res = await doPayment(payStatus)
+      setPayResult(res);
+    } catch (e) {
+      console.error(e);
+      setPayErrMsg(e.message);
+    }
+
+  }
+
+  return <PaymentRequestView payStatus={payStatus} onClick={onClick} payErrMsg={payErrMsg} />;
+}
+
+export interface PaymentRequestViewProps {
+  payStatus: PreparePayResult;
+  onClick: () => void;
+  payErrMsg?: string;
+
+}
+export function PaymentRequestView({ payStatus, onClick, payErrMsg }: PaymentRequestViewProps) {
+  let totalFees: AmountJson | undefined = undefined;
+  let insufficientBalance = false;
+  const [loading, setLoading] = useState(false);
   const contractTerms: ContractTerms = payStatus.contractTerms;
 
+  if (
+    payStatus.status === PreparePayResultType.AlreadyConfirmed
+  ) {
+    return <AlreadyPaid payStatus={payStatus} />
+  }
+
   if (!contractTerms) {
     return (
       <span>
@@ -115,6 +170,18 @@ export function PayPage({ talerPayUri }: Props): JSX.Element {
     );
   }
 
+  if (payStatus.status == PreparePayResultType.InsufficientBalance) {
+    insufficientBalance = true;
+  }
+
+  if (payStatus.status === PreparePayResultType.PaymentPossible) {
+    const amountRaw = Amounts.parseOrThrow(payStatus.amountRaw);
+    const amountEffective: AmountJson = Amounts.parseOrThrow(
+      payStatus.amountEffective,
+    );
+    totalFees = Amounts.sub(amountEffective, amountRaw).amount;
+  }
+
   let merchantName: VNode;
   if (contractTerms.merchant && contractTerms.merchant.name) {
     merchantName = <strong>{contractTerms.merchant.name}</strong>;
@@ -126,99 +193,61 @@ export function PayPage({ talerPayUri }: Props): JSX.Element {
     <strong>{renderAmount(Amounts.parseOrThrow(contractTerms.amount))}</strong>
   );
 
-  const doPayment = async (): Promise<void> => {
-    if (payStatus.status !== "payment-possible") {
-      throw Error(`invalid state: ${payStatus.status}`);
-    }
-    const proposalId = payStatus.proposalId;
-    setNumTries(numTries + 1);
-    try {
-      setLoading(true);
-      const res = await wxApi.confirmPay(proposalId, undefined);
-      if (res.type !== ConfirmPayResultType.Done) {
-        throw Error("payment pending");
-      }
-      const fu = res.contractTerms.fulfillment_url;
-      if (fu) {
-        document.location.href = fu;
-      }
-      setPayResult(res);
-    } catch (e) {
-      console.error(e);
-      setPayErrMsg(e.message);
-    }
-  };
-
-  if (payResult && payResult.type === ConfirmPayResultType.Done) {
-    if (payResult.contractTerms.fulfillment_message) {
-      const obj = {
-        fulfillment_message: payResult.contractTerms.fulfillment_message,
-        fulfillment_message_i18n:
-          payResult.contractTerms.fulfillment_message_i18n,
-      };
-      const msg = getJsonI18n(obj, "fulfillment_message");
-      return (
-        <div>
-          <p>Payment succeeded.</p>
-          <p>{msg}</p>
-        </div>
-      );
-    } else {
-      return <span>Redirecting ...</span>;
-    }
-  }
-
-  return (
-    <div>
-      <p>
-        <i18n.Translate>
-          The merchant <span>{merchantName}</span> offers you to purchase:
-        </i18n.Translate>
-        <div style={{ textAlign: "center" }}>
-          <strong>{contractTerms.summary}</strong>
-        </div>
-        {totalFees ? (
+  return <section class="main">
+    <h1>GNU Taler Wallet</h1>
+    <article class="fade">
+      <div>
+        <p>
           <i18n.Translate>
-            The total price is <span>{amount} </span>
-            (plus <span>{renderAmount(totalFees)}</span> fees).
-          </i18n.Translate>
+            The merchant <span>{merchantName}</span> offers you to purchase:
+      </i18n.Translate>
+          <div style={{ textAlign: "center" }}>
+            <strong>{contractTerms.summary}</strong>
+          </div>
+          {totalFees ? (
+            <i18n.Translate>
+              The total price is <span>{amount} </span>
+        (plus <span>{renderAmount(totalFees)}</span> fees).
+            </i18n.Translate>
+          ) : (
+              <i18n.Translate>
+                The total price is <span>{amount}</span>.
+              </i18n.Translate>
+            )}
+        </p>
+
+        {insufficientBalance ? (
+          <div>
+            <p style={{ color: "red", fontWeight: "bold" }}>
+              Unable to pay: Your balance is insufficient.
+            </p>
+          </div>
+        ) : null}
+
+        {payErrMsg ? (
+          <div>
+            <p>Payment failed: {payErrMsg}</p>
+            <button
+              class="pure-button button-success"
+              onClick={onClick}
+            >
+              {i18n.str`Retry`}
+            </button>
+          </div>
         ) : (
-          <i18n.Translate>
-            The total price is <span>{amount}</span>.
-          </i18n.Translate>
-        )}
-      </p>
-
-      {insufficientBalance ? (
-        <div>
-          <p style={{ color: "red", fontWeight: "bold" }}>
-            Unable to pay: Your balance is insufficient.
-          </p>
-        </div>
-      ) : null}
-
-      {payErrMsg ? (
-        <div>
-          <p>Payment failed: {payErrMsg}</p>
-          <button
-            class="pure-button button-success"
-            onClick={() => doPayment()}
-          >
-            {i18n.str`Retry`}
-          </button>
-        </div>
-      ) : (
-        <div>
-          <ProgressButton
-            isLoading={loading}
-            disabled={insufficientBalance}
-            onClick={() => doPayment()}
-          >
-            {i18n.str`Confirm payment`}
-          </ProgressButton>
-        </div>
-      )}
-    </div>
-  );
-}
-
+            <div>
+              <ProgressButton
+                isLoading={loading}
+                disabled={insufficientBalance}
+                onClick={onClick}
+              >
+                {i18n.str`Confirm payment`}
+              </ProgressButton>
+            </div>
+          )}
+      </div>
+    </article>
+  </section>
+
+
+}
\ No newline at end of file
-- 
cgit v1.2.3

