gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated (9b2d6d76 -> e4f3acfe)


From: gnunet
Subject: [taler-wallet-core] branch master updated (9b2d6d76 -> e4f3acfe)
Date: Sun, 11 Sep 2022 04:23:10 +0200

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a change to branch master
in repository wallet-core.

    from 9b2d6d76 include age commitment proof in refreshed coins
     new dda90b51 find taler action in clipboard and withdraw with mobile
     new e4f3acfe fix #7343

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../src/context/iocContext.ts                      |   7 +-
 .../src/cta/InvoiceCreate/views.tsx                |   4 +-
 .../src/cta/InvoicePay/index.ts                    |  29 ++++-
 .../src/cta/InvoicePay/state.ts                    | 105 +++++++++++++---
 .../src/cta/InvoicePay/stories.tsx                 |   5 +
 .../src/cta/InvoicePay/views.tsx                   |  49 +++++---
 .../src/cta/Payment/index.ts                       |   3 +-
 .../src/cta/Payment/state.ts                       |   4 +-
 .../src/cta/Payment/views.tsx                      | 140 +++++++++++----------
 .../src/cta/Withdraw/index.ts                      |   1 +
 .../src/cta/Withdraw/state.ts                      |   1 +
 .../src/cta/Withdraw/views.tsx                     |  94 ++++++++++----
 .../src/hooks/useTalerActionURL.test.ts            |   8 +-
 .../src/hooks/useTalerActionURL.ts                 |  32 ++++-
 .../taler-wallet-webextension/src/platform/api.ts  |  17 ++-
 .../src/platform/chrome.ts                         |   6 +
 .../taler-wallet-webextension/src/platform/dev.ts  |   1 +
 .../src/popup/Application.tsx                      |  10 +-
 18 files changed, 363 insertions(+), 153 deletions(-)

diff --git a/packages/taler-wallet-webextension/src/context/iocContext.ts 
b/packages/taler-wallet-webextension/src/context/iocContext.ts
index b1a9aa12..c7fee0bc 100644
--- a/packages/taler-wallet-webextension/src/context/iocContext.ts
+++ b/packages/taler-wallet-webextension/src/context/iocContext.ts
@@ -25,9 +25,11 @@ import { platform } from "../platform/api.js";
 
 interface Type {
   findTalerUriInActiveTab: () => Promise<string | undefined>;
+  findTalerUriInClipboard: () => Promise<string | undefined>;
 }
 const Context = createContext<Type>({
   findTalerUriInActiveTab: async () => undefined,
+  findTalerUriInClipboard: async () => undefined,
 });
 
 /**
@@ -56,7 +58,10 @@ export const IoCProviderForRuntime = ({
   children: any;
 }): VNode => {
   return h(Context.Provider, {
-    value: { findTalerUriInActiveTab: platform.findTalerUriInActiveTab },
+    value: {
+      findTalerUriInActiveTab: platform.findTalerUriInActiveTab,
+      findTalerUriInClipboard: platform.findTalerUriInClipboard,
+    },
     children,
   });
 };
diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx 
b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx
index d3d2c68e..18e59328 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/views.tsx
@@ -122,13 +122,13 @@ export function ReadyView({
               }}
             >
               <i18n.Translate>Exchange</i18n.Translate>
-              <Link>
+              {/* <Link>
                 <SvgIcon
                   title="Edit"
                   dangerouslySetInnerHTML={{ __html: editIcon }}
                   color="black"
                 />
-              </Link>
+              </Link> */}
             </div>
           }
           text={<ExchangeDetails exchange={exchangeUrl} />}
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts 
b/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
index 2521ee69..71aedc63 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { AbsoluteTime, AmountJson, TalerErrorDetail } from 
"@gnu-taler/taler-util";
+import { AbsoluteTime, AmountJson, PreparePayResult, TalerErrorDetail } from 
"@gnu-taler/taler-util";
 import { Loading } from "../../components/Loading.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { ButtonHandler } from "../../mui/handlers.js";
@@ -26,11 +26,14 @@ import { LoadingUriView, ReadyView } from "./views.js";
 export interface Props {
   talerPayPullUri: string;
   onClose: () => Promise<void>;
+  goToWalletManualWithdraw: (amount?: string) => Promise<void>;
 }
 
 export type State =
   | State.Loading
   | State.LoadingUriError
+  | State.NoEnoughBalance
+  | State.NoBalanceForCurrency
   | State.Ready;
 
 export namespace State {
@@ -47,22 +50,38 @@ export namespace State {
 
   export interface BaseInfo {
     error: undefined;
+    uri: string;
     cancel: ButtonHandler;
-  }
-  export interface Ready extends BaseInfo {
-    status: "ready";
     amount: AmountJson,
+    goToWalletManualWithdraw: (currency: string) => Promise<void>;
     summary: string | undefined,
     expiration: AbsoluteTime | undefined,
+    operationError?: TalerErrorDetail;
+    payStatus: PreparePayResult;
+  }
+
+  export interface NoBalanceForCurrency extends BaseInfo {
+    status: "no-balance-for-currency"
+    balance: undefined;
+  }
+  export interface NoEnoughBalance extends BaseInfo {
+    status: "no-enough-balance"
+    balance: AmountJson;
+  }
+
+  export interface Ready extends BaseInfo {
+    status: "ready";
     error: undefined;
+    balance: AmountJson;
     accept: ButtonHandler;
-    operationError?: TalerErrorDetail;
   }
 }
 
 const viewMapping: StateViewMap<State> = {
   loading: Loading,
   "loading-uri": LoadingUriView,
+  "no-balance-for-currency": ReadyView,
+  "no-enough-balance": ReadyView,
   "ready": ReadyView,
 };
 
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts 
b/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
index 27be1e42..f87cdf8e 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
@@ -14,22 +14,31 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { AbsoluteTime, Amounts, TalerErrorDetail, TalerProtocolTimestamp } 
from "@gnu-taler/taler-util";
+import { AbsoluteTime, Amounts, NotificationType, PreparePayResult, 
PreparePayResultType, TalerErrorDetail, TalerProtocolTimestamp } from 
"@gnu-taler/taler-util";
 import { TalerError } from "@gnu-taler/taler-wallet-core";
-import { useState } from "preact/hooks";
+import { useEffect, useState } from "preact/hooks";
 import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
 import * as wxApi from "../../wxApi.js";
 import { Props, State } from "./index.js";
 
 export function useComponentState(
-  { talerPayPullUri, onClose }: Props,
+  { talerPayPullUri, onClose, goToWalletManualWithdraw }: Props,
   api: typeof wxApi,
 ): State {
   const hook = useAsyncAsHook(async () => {
-    return await api.checkPeerPullPayment({
+    const p2p = await api.checkPeerPullPayment({
       talerUri: talerPayPullUri
     })
-  }, [])
+    const balance = await api.getBalance();
+    return { p2p, balance }
+  })
+
+  useEffect(() => {
+    api.onUpdateNotification([NotificationType.CoinWithdrawn], () => {
+      hook?.retry();
+    });
+  });
+
   const [operationError, setOperationError] = useState<TalerErrorDetail | 
undefined>(undefined)
 
   if (!hook) {
@@ -45,12 +54,84 @@ export function useComponentState(
     };
   }
 
-  const { amount: purseAmount, contractTerms, peerPullPaymentIncomingId } = 
hook.response
+  // const { payStatus } = hook.response.p2p;
+
+  const { amount: purseAmount, contractTerms, peerPullPaymentIncomingId } = 
hook.response.p2p
 
-  const amount: string = contractTerms?.amount
+
+  const amountStr: string = contractTerms?.amount
+  const amount = Amounts.parseOrThrow(amountStr)
   const summary: string | undefined = contractTerms?.summary
   const expiration: TalerProtocolTimestamp | undefined = 
contractTerms?.purse_expiration
 
+  const foundBalance = hook.response.balance.balances.find(
+    (b) => Amounts.parseOrThrow(b.available).currency === amount.currency,
+  );
+
+  const paymentPossible: PreparePayResult = {
+    status: PreparePayResultType.PaymentPossible,
+    proposalId: "fakeID",
+    contractTerms: {
+    } as any,
+    contractTermsHash: "asd",
+    amountRaw: hook.response.p2p.amount,
+    amountEffective: hook.response.p2p.amount,
+    noncePriv: "",
+  } as PreparePayResult
+
+  const insufficientBalance: PreparePayResult = {
+    status: PreparePayResultType.InsufficientBalance,
+    proposalId: "fakeID",
+    contractTerms: {
+    } as any,
+    amountRaw: hook.response.p2p.amount,
+    noncePriv: "",
+  }
+
+
+  const baseResult = {
+    uri: talerPayPullUri,
+    cancel: {
+      onClick: onClose
+    },
+    amount,
+    goToWalletManualWithdraw,
+    summary,
+    expiration: expiration ? AbsoluteTime.fromTimestamp(expiration) : 
undefined,
+    operationError,
+  }
+
+  if (!foundBalance) {
+    return {
+      status: "no-balance-for-currency",
+      error: undefined,
+      balance: undefined,
+      ...baseResult,
+      payStatus: insufficientBalance,
+    }
+  }
+
+  const foundAmount = Amounts.parseOrThrow(foundBalance.available);
+
+  //FIXME: should use pay result type since it check for coins exceptions
+  if (Amounts.cmp(foundAmount, amount) < 0) { //payStatus.status === 
PreparePayResultType.InsufficientBalance) {
+    return {
+      status: 'no-enough-balance',
+      error: undefined,
+      balance: foundAmount,
+      ...baseResult,
+      payStatus: insufficientBalance,
+    }
+  }
+
+  // if (payStatus.status === PreparePayResultType.AlreadyConfirmed) {
+  //   return {
+  //     status: "confirmed",
+  //     balance: foundAmount,
+  //     ...baseResult,
+  //   };
+  // }
+
   async function accept(): Promise<void> {
     try {
       const resp = await api.acceptPeerPullPayment({
@@ -69,16 +150,12 @@ export function useComponentState(
 
   return {
     status: "ready",
-    amount: Amounts.parseOrThrow(amount),
     error: undefined,
+    ...baseResult,
+    payStatus: paymentPossible,
+    balance: foundAmount,
     accept: {
       onClick: accept
     },
-    summary,
-    expiration: expiration ? AbsoluteTime.fromTimestamp(expiration) : 
undefined,
-    cancel: {
-      onClick: onClose
-    },
-    operationError
   }
 }
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/stories.tsx 
b/packages/taler-wallet-webextension/src/cta/InvoicePay/stories.tsx
index 81b79a20..5a8a5193 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/stories.tsx
@@ -19,6 +19,7 @@
  * @author Sebastian Javier Marchano (sebasjm)
  */
 
+import { PreparePayResult, PreparePayResultType } from "@gnu-taler/taler-util";
 import { createExample } from "../../test-utils.js";
 import { ReadyView } from "./views.js";
 
@@ -32,6 +33,10 @@ export const Ready = createExample(ReadyView, {
     value: 1,
     fraction: 0,
   },
+  payStatus: {
+    status: PreparePayResultType.PaymentPossible,
+    amountEffective: "ARS:1",
+  } as PreparePayResult,
   accept: {},
   cancel: {},
 });
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx 
b/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx
index 71bdb20b..21b666ab 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/views.tsx
@@ -14,16 +14,23 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { h, VNode } from "preact";
+import { Amounts, PreparePayResultType } from "@gnu-taler/taler-util";
+import { Fragment, h, VNode } from "preact";
 import { Amount } from "../../components/Amount.js";
 import { ErrorTalerOperation } from "../../components/ErrorTalerOperation.js";
 import { LoadingError } from "../../components/LoadingError.js";
 import { LogoHeader } from "../../components/LogoHeader.js";
 import { Part } from "../../components/Part.js";
-import { Link, SubTitle, WalletAction } from 
"../../components/styled/index.js";
+import {
+  Link,
+  SubTitle,
+  WalletAction,
+  WarningBox,
+} from "../../components/styled/index.js";
 import { Time } from "../../components/Time.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { Button } from "../../mui/Button.js";
+import { ButtonsSection, PayWithMobile } from "../Payment/views.js";
 import { State } from "./index.js";
 
 export function LoadingUriView({ error }: State.LoadingUriError): VNode {
@@ -37,16 +44,21 @@ export function LoadingUriView({ error }: 
State.LoadingUriError): VNode {
   );
 }
 
-export function ReadyView({
-  operationError,
-  cancel,
-  accept,
-  expiration,
-  summary,
-  amount,
-}: State.Ready): VNode {
+export function ReadyView(
+  state: State.Ready | State.NoBalanceForCurrency | State.NoEnoughBalance,
+): VNode {
   const { i18n } = useTranslationContext();
-
+  const {
+    operationError,
+    summary,
+    amount,
+    expiration,
+    uri,
+    status,
+    balance,
+    payStatus,
+    cancel,
+  } = state;
   return (
     <WalletAction>
       <LogoHeader />
@@ -78,13 +90,14 @@ export function ReadyView({
           kind="neutral"
         />
       </section>
-      <section>
-        <Button variant="contained" color="success" onClick={accept.onClick}>
-          <i18n.Translate>
-            Pay &nbsp; {<Amount value={amount} />}
-          </i18n.Translate>
-        </Button>
-      </section>
+      <ButtonsSection
+        amount={amount}
+        balance={balance}
+        payStatus={payStatus}
+        uri={uri}
+        payHandler={status === "ready" ? state.accept : undefined}
+        goToWalletManualWithdraw={state.goToWalletManualWithdraw}
+      />
       <section>
         <Link upperCased onClick={cancel.onClick}>
           <i18n.Translate>Cancel</i18n.Translate>
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/index.ts 
b/packages/taler-wallet-webextension/src/cta/Payment/index.ts
index 889e532c..6e401f7d 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Payment/index.ts
@@ -15,6 +15,7 @@
  */
 
 import { AmountJson, ConfirmPayResult, PreparePayResult, 
PreparePayResultAlreadyConfirmed, PreparePayResultInsufficientBalance, 
PreparePayResultPaymentPossible } from "@gnu-taler/taler-util";
+import { TalerError } from "@gnu-taler/taler-wallet-core";
 import { Loading } from "../../components/Loading.js";
 import { HookError } from "../../hooks/useAsyncAsHook.js";
 import { ButtonHandler } from "../../mui/handlers.js";
@@ -85,7 +86,7 @@ export namespace State {
     status: "completed";
     payStatus: PreparePayResult;
     payResult: ConfirmPayResult;
-    payHandler: ButtonHandler;
+    paymentError?: TalerError;
     balance: AmountJson;
   }
 }
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/state.ts 
b/packages/taler-wallet-webextension/src/cta/Payment/state.ts
index 842bb7ed..ad4bb700 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Payment/state.ts
@@ -101,9 +101,7 @@ export function useComponentState(
       status: "completed",
       balance: foundAmount,
       payStatus,
-      payHandler: {
-        error: payErrMsg,
-      },
+      paymentError: payErrMsg,
       payResult,
       ...baseResult,
     };
diff --git a/packages/taler-wallet-webextension/src/cta/Payment/views.tsx 
b/packages/taler-wallet-webextension/src/cta/Payment/views.tsx
index c4374590..c799607a 100644
--- a/packages/taler-wallet-webextension/src/cta/Payment/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Payment/views.tsx
@@ -16,9 +16,12 @@
 
 import {
   AbsoluteTime,
+  AmountJson,
   Amounts,
   ConfirmPayResultType,
   ContractTerms,
+  PreparePayResult,
+  PreparePayResultPaymentPossible,
   PreparePayResultType,
   Product,
 } from "@gnu-taler/taler-util";
@@ -42,6 +45,7 @@ import {
 import { Time } from "../../components/Time.js";
 import { useTranslationContext } from "../../context/translation.js";
 import { Button } from "../../mui/Button.js";
+import { ButtonHandler } from "../../mui/handlers.js";
 import { MerchantDetails, PurchaseDetails } from "../../wallet/Transaction.js";
 import { State } from "./index.js";
 
@@ -164,7 +168,11 @@ export function BaseView(state: SupportedStates): VNode {
         )}
       </section>
       <ButtonsSection
-        state={state}
+        amount={state.amount}
+        balance={state.balance}
+        payStatus={state.payStatus}
+        uri={state.uri}
+        payHandler={state.status === "ready" ? state.payHandler : undefined}
         goToWalletManualWithdraw={state.goToWalletManualWithdraw}
       />
       <section>
@@ -276,9 +284,9 @@ function ShowImportantMessage({ state }: { state: 
SupportedStates }): VNode {
   }
 
   if (state.status == "completed") {
-    const { payResult, payHandler } = state;
-    if (payHandler.error) {
-      return <ErrorTalerOperation error={payHandler.error.errorDetail} />;
+    const { payResult, paymentError } = state;
+    if (paymentError) {
+      return <ErrorTalerOperation error={paymentError.errorDetail} />;
     }
     if (payResult.type === ConfirmPayResultType.Done) {
       return (
@@ -307,15 +315,11 @@ function ShowImportantMessage({ state }: { state: 
SupportedStates }): VNode {
   return <Fragment />;
 }
 
-function PayWithMobile({ state }: { state: SupportedStates }): VNode {
+export function PayWithMobile({ uri }: { uri: string }): VNode {
   const { i18n } = useTranslationContext();
 
   const [showQR, setShowQR] = useState<boolean>(false);
 
-  const privateUri =
-    state.payStatus.status !== PreparePayResultType.AlreadyConfirmed
-      ? `${state.uri}&n=${state.payStatus.noncePriv}`
-      : state.uri;
   return (
     <section>
       <LinkSuccess upperCased onClick={() => setShowQR((qr) => !qr)}>
@@ -327,10 +331,10 @@ function PayWithMobile({ state }: { state: 
SupportedStates }): VNode {
       </LinkSuccess>
       {showQR && (
         <div>
-          <QR text={privateUri} />
+          <QR text={uri} />
           <i18n.Translate>
             Scan the QR code or &nbsp;
-            <a href={privateUri}>
+            <a href={uri}>
               <i18n.Translate>click here</i18n.Translate>
             </a>
           </i18n.Translate>
@@ -340,50 +344,60 @@ function PayWithMobile({ state }: { state: 
SupportedStates }): VNode {
   );
 }
 
-function ButtonsSection({
-  state,
-  goToWalletManualWithdraw,
-}: {
-  state: SupportedStates;
+interface ButtonSectionProps {
+  payStatus: PreparePayResult;
+  payHandler: ButtonHandler | undefined;
+  balance: AmountJson | undefined;
+  uri: string;
+  amount: AmountJson;
   goToWalletManualWithdraw: (currency: string) => Promise<void>;
-}): VNode {
+}
+
+export function ButtonsSection({
+  payStatus,
+  uri,
+  payHandler,
+  balance,
+  amount,
+  goToWalletManualWithdraw,
+}: ButtonSectionProps): VNode {
   const { i18n } = useTranslationContext();
-  if (state.status === "ready") {
+  if (payStatus.status === PreparePayResultType.PaymentPossible) {
+    const privateUri = `${uri}&n=${payStatus.noncePriv}`;
+
     return (
       <Fragment>
         <section>
           <Button
             variant="contained"
             color="success"
-            onClick={state.payHandler.onClick}
+            onClick={payHandler?.onClick}
           >
             <i18n.Translate>
               Pay &nbsp;
-              {<Amount value={state.payStatus.amountEffective} />}
+              {<Amount value={amount} />}
             </i18n.Translate>
           </Button>
         </section>
-        <PayWithMobile state={state} />
+        <PayWithMobile uri={privateUri} />
       </Fragment>
     );
   }
-  if (
-    state.status === "no-enough-balance" ||
-    state.status === "no-balance-for-currency"
-  ) {
-    // if (state.payStatus.status === 
PreparePayResultType.InsufficientBalance) {
+
+  if (payStatus.status === PreparePayResultType.InsufficientBalance) {
     let BalanceMessage = "";
-    if (!state.balance) {
+    if (!balance) {
       BalanceMessage = i18n.str`You have no balance for this currency. 
Withdraw digital cash first.`;
     } else {
-      const balanceShouldBeEnough =
-        Amounts.cmp(state.balance, state.amount) !== -1;
+      const balanceShouldBeEnough = Amounts.cmp(balance, amount) !== -1;
       if (balanceShouldBeEnough) {
-        BalanceMessage = i18n.str`Could not find enough coins to pay this 
order. Even if you have enough ${state.balance.currency} some restriction may 
apply.`;
+        BalanceMessage = i18n.str`Could not find enough coins to pay. Even if 
you have enough ${balance.currency} some restriction may apply.`;
       } else {
-        BalanceMessage = i18n.str`Your current balance is not enough for this 
order.`;
+        BalanceMessage = i18n.str`Your current balance is not enough.`;
       }
     }
+    const uriPrivate = `${uri}&n=${payStatus.noncePriv}`;
+
     return (
       <Fragment>
         <section>
@@ -393,51 +407,45 @@ function ButtonsSection({
           <Button
             variant="contained"
             color="success"
-            onClick={() =>
-              goToWalletManualWithdraw(Amounts.stringify(state.amount))
-            }
+            onClick={() => goToWalletManualWithdraw(Amounts.stringify(amount))}
           >
             <i18n.Translate>Get digital cash</i18n.Translate>
           </Button>
         </section>
-        <PayWithMobile state={state} />
+        <PayWithMobile uri={uriPrivate} />
       </Fragment>
     );
-    // }
-  }
-  if (state.status === "confirmed") {
-    if (state.payStatus.status === PreparePayResultType.AlreadyConfirmed) {
-      return (
-        <Fragment>
-          <section>
-            {state.payStatus.paid &&
-              state.payStatus.contractTerms.fulfillment_message && (
-                <Part
-                  title={<i18n.Translate>Merchant message</i18n.Translate>}
-                  text={state.payStatus.contractTerms.fulfillment_message}
-                  kind="neutral"
-                />
-              )}
-          </section>
-          {!state.payStatus.paid && <PayWithMobile state={state} />}
-        </Fragment>
-      );
-    }
   }
-
-  if (state.status === "completed") {
-    if (state.payResult.type === ConfirmPayResultType.Pending) {
-      return (
+  if (payStatus.status === PreparePayResultType.AlreadyConfirmed) {
+    return (
+      <Fragment>
         <section>
-          <div>
-            <p>
-              <i18n.Translate>Processing</i18n.Translate>...
-            </p>
-          </div>
+          {payStatus.paid && payStatus.contractTerms.fulfillment_message && (
+            <Part
+              title={<i18n.Translate>Merchant message</i18n.Translate>}
+              text={payStatus.contractTerms.fulfillment_message}
+              kind="neutral"
+            />
+          )}
         </section>
-      );
-    }
+        {!payStatus.paid && <PayWithMobile uri={uri} />}
+      </Fragment>
+    );
   }
 
+  // if (state.status === "completed") {
+  //   if (state.payResult.type === ConfirmPayResultType.Pending) {
+  //     return (
+  //       <section>
+  //         <div>
+  //           <p>
+  //             <i18n.Translate>Processing</i18n.Translate>...
+  //           </p>
+  //         </div>
+  //       </section>
+  //     );
+  //   }
+  // }
+
   return <Fragment />;
 }
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts 
b/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
index 7425dbd2..7bd66726 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
@@ -85,6 +85,7 @@ export namespace State {
 
     ageRestriction?: SelectFieldHandler;
 
+    talerWithdrawUri?: string;
     cancel: () => Promise<void>;
   };
 }
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts 
b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
index 015c9aac..0d2e150e 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
@@ -410,6 +410,7 @@ export function useComponentStateFromURI(
     toBeReceived,
     withdrawalFee,
     chosenAmount,
+    talerWithdrawUri,
     ageRestriction,
     doWithdrawal: {
       onClick:
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx 
b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
index 850bd6e8..44058634 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
@@ -23,6 +23,7 @@ import { SelectList } from "../../components/SelectList.js";
 import {
   Input,
   Link,
+  LinkSuccess,
   SubTitle,
   SuccessBox,
   SvgIcon,
@@ -35,6 +36,8 @@ import { TermsOfServiceSection } from 
"../TermsOfServiceSection.js";
 import { State } from "./index.js";
 import editIcon from "../../svg/edit_24px.svg";
 import { Amount } from "../../components/Amount.js";
+import { QR } from "../../components/QR.js";
+import { useState } from "preact/hooks";
 
 export function LoadingUriView({ error }: State.LoadingUriError): VNode {
   const { i18n } = useTranslationContext();
@@ -126,13 +129,13 @@ export function SuccessView(state: State.Success): VNode {
               }}
             >
               <i18n.Translate>Exchange</i18n.Translate>
-              <Link>
+              {/* <Link>
                 <SvgIcon
                   title="Edit"
                   dangerouslySetInnerHTML={{ __html: editIcon }}
                   color="black"
                 />
-              </Link>
+              </Link> */}
             </div>
           }
           text={<ExchangeDetails exchange={state.exchangeUrl} />}
@@ -164,31 +167,36 @@ export function SuccessView(state: State.Success): VNode {
       </section>
       {state.tosProps && <TermsOfServiceSection {...state.tosProps} />}
       {state.tosProps ? (
-        <section>
-          {(state.tosProps.terms.status === "accepted" ||
-            (state.mustAcceptFirst && state.tosProps.reviewed)) && (
-            <Button
-              variant="contained"
-              color="success"
-              disabled={!state.doWithdrawal.onClick}
-              onClick={state.doWithdrawal.onClick}
-            >
-              <i18n.Translate>
-                Withdraw &nbsp; <Amount value={state.toBeReceived} />
-              </i18n.Translate>
-            </Button>
-          )}
-          {state.tosProps.terms.status === "notfound" && (
-            <Button
-              variant="contained"
-              color="warning"
-              disabled={!state.doWithdrawal.onClick}
-              onClick={state.doWithdrawal.onClick}
-            >
-              <i18n.Translate>Withdraw anyway</i18n.Translate>
-            </Button>
-          )}
-        </section>
+        <Fragment>
+          <section>
+            {(state.tosProps.terms.status === "accepted" ||
+              (state.mustAcceptFirst && state.tosProps.reviewed)) && (
+              <Button
+                variant="contained"
+                color="success"
+                disabled={!state.doWithdrawal.onClick}
+                onClick={state.doWithdrawal.onClick}
+              >
+                <i18n.Translate>
+                  Withdraw &nbsp; <Amount value={state.toBeReceived} />
+                </i18n.Translate>
+              </Button>
+            )}
+            {state.tosProps.terms.status === "notfound" && (
+              <Button
+                variant="contained"
+                color="warning"
+                disabled={!state.doWithdrawal.onClick}
+                onClick={state.doWithdrawal.onClick}
+              >
+                <i18n.Translate>Withdraw anyway</i18n.Translate>
+              </Button>
+            )}
+          </section>
+          {state.talerWithdrawUri ? (
+            <WithdrawWithMobile talerWithdrawUri={state.talerWithdrawUri} />
+          ) : undefined}
+        </Fragment>
       ) : (
         <section>
           <i18n.Translate>Loading terms of service...</i18n.Translate>
@@ -202,3 +210,35 @@ export function SuccessView(state: State.Success): VNode {
     </WalletAction>
   );
 }
+
+function WithdrawWithMobile({
+  talerWithdrawUri,
+}: {
+  talerWithdrawUri: string;
+}): VNode {
+  const { i18n } = useTranslationContext();
+  const [showQR, setShowQR] = useState<boolean>(false);
+
+  return (
+    <section>
+      <LinkSuccess upperCased onClick={() => setShowQR((qr) => !qr)}>
+        {!showQR ? (
+          <i18n.Translate>Withdraw to a mobile phone</i18n.Translate>
+        ) : (
+          <i18n.Translate>Hide QR</i18n.Translate>
+        )}
+      </LinkSuccess>
+      {showQR && (
+        <div>
+          <QR text={talerWithdrawUri} />
+          <i18n.Translate>
+            Scan the QR code or &nbsp;
+            <a href={talerWithdrawUri}>
+              <i18n.Translate>click here</i18n.Translate>
+            </a>
+          </i18n.Translate>
+        </div>
+      )}
+    </section>
+  );
+}
diff --git 
a/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.test.ts 
b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.test.ts
index c00d6d7f..61fe86e3 100644
--- a/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.test.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.test.ts
@@ -20,11 +20,13 @@ import { h, VNode } from "preact";
 import { expect } from "chai";
 
 describe("useTalerActionURL hook", () => {
+
   it("should be set url to undefined when dismiss", async () => {
     const ctx = ({ children }: { children: any }): VNode => {
       return h(IoCProviderForTesting, {
         value: {
           findTalerUriInActiveTab: async () => "asd",
+          findTalerUriInClipboard: async () => "qwe",
         },
         children,
       });
@@ -42,7 +44,10 @@ describe("useTalerActionURL hook", () => {
 
     {
       const [url, setDismissed] = getLastResultOrThrow();
-      expect(url).equals("asd");
+      expect(url).deep.equals({
+        location: "clipboard",
+        uri: "qwe"
+      });
       setDismissed(true);
     }
 
@@ -53,7 +58,6 @@ describe("useTalerActionURL hook", () => {
       if (url !== undefined) throw Error("invalid");
       expect(url).undefined;
     }
-
     await assertNoPendingUpdate();
   });
 });
diff --git a/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts 
b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts
index 449cb698..e1b08278 100644
--- a/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts
@@ -17,23 +17,43 @@
 import { useEffect, useState } from "preact/hooks";
 import { useIocContext } from "../context/iocContext.js";
 
+export interface UriLocation {
+  uri: string;
+  location: "clipboard" | "activeTab"
+}
+
 export function useTalerActionURL(): [
-  string | undefined,
+  UriLocation | undefined,
   (s: boolean) => void,
 ] {
-  const [talerActionUrl, setTalerActionUrl] = useState<string | undefined>(
+  const [talerActionUrl, setTalerActionUrl] = useState<UriLocation | 
undefined>(
     undefined,
   );
   const [dismissed, setDismissed] = useState(false);
-  const { findTalerUriInActiveTab } = useIocContext();
+  const { findTalerUriInActiveTab, findTalerUriInClipboard } = useIocContext();
 
   useEffect(() => {
     async function check(): Promise<void> {
-      const talerUri = await findTalerUriInActiveTab();
-      setTalerActionUrl(talerUri);
+      const clipUri = await findTalerUriInClipboard();
+      if (clipUri) {
+        setTalerActionUrl({
+          location: "clipboard",
+          uri: clipUri
+        });
+        return;
+      }
+      const tabUri = await findTalerUriInActiveTab();
+      if (tabUri) {
+        setTalerActionUrl({
+          location: "activeTab",
+          uri: tabUri
+        });
+        return;
+      }
     }
     check();
-  });
+  }, [setTalerActionUrl]);
+
   const url = dismissed ? undefined : talerActionUrl;
   return [url, setDismissed];
 }
diff --git a/packages/taler-wallet-webextension/src/platform/api.ts 
b/packages/taler-wallet-webextension/src/platform/api.ts
index 37f52192..23fd80ed 100644
--- a/packages/taler-wallet-webextension/src/platform/api.ts
+++ b/packages/taler-wallet-webextension/src/platform/api.ts
@@ -163,13 +163,22 @@ export interface PlatformAPI {
   findTalerUriInActiveTab(): Promise<string | undefined>;
 
   /**
-   * Used from the frontend to send commands to the wallet
+   * Popup API
    *
-   * @param operation
-   * @param payload
+   * Read the current tab html and try to find any Taler URI or QR code 
present.
    *
-   * @return response from the backend
+   * @return Taler URI if found
    */
+  findTalerUriInClipboard(): Promise<string | undefined>;
+
+  /**
+  * Used from the frontend to send commands to the wallet
+  *
+  * @param operation
+  * @param payload
+  *
+  * @return response from the backend
+  */
   sendMessageToWalletBackground(
     operation: string,
     payload: any,
diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts 
b/packages/taler-wallet-webextension/src/platform/chrome.ts
index 4ce995bd..7311354c 100644
--- a/packages/taler-wallet-webextension/src/platform/chrome.ts
+++ b/packages/taler-wallet-webextension/src/platform/chrome.ts
@@ -30,6 +30,7 @@ import {
 const api: PlatformAPI = {
   isFirefox,
   findTalerUriInActiveTab,
+  findTalerUriInClipboard,
   getPermissionsApi,
   getWalletWebExVersion,
   listenToWalletBackground,
@@ -689,6 +690,11 @@ async function findTalerUriInTab(tabId: number): 
Promise<string | undefined> {
   }
 }
 
+async function findTalerUriInClipboard(): Promise<string | undefined> {
+  const textInClipboard = await window.navigator.clipboard.readText();
+  return textInClipboard.startsWith("taler://") || 
textInClipboard.startsWith("taler+http://";) ? textInClipboard : undefined
+}
+
 async function findTalerUriInActiveTab(): Promise<string | undefined> {
   const tab = await getCurrentTab();
   if (!tab || tab.id === undefined) return;
diff --git a/packages/taler-wallet-webextension/src/platform/dev.ts 
b/packages/taler-wallet-webextension/src/platform/dev.ts
index e5db0c8e..bb7e181c 100644
--- a/packages/taler-wallet-webextension/src/platform/dev.ts
+++ b/packages/taler-wallet-webextension/src/platform/dev.ts
@@ -23,6 +23,7 @@ const api: PlatformAPI = {
   isFirefox: () => false,
   keepAlive: (cb: VoidFunction) => cb(),
   findTalerUriInActiveTab: async () => undefined,
+  findTalerUriInClipboard: async () => undefined,
   containsTalerHeaderListener: () => {
     return true;
   },
diff --git a/packages/taler-wallet-webextension/src/popup/Application.tsx 
b/packages/taler-wallet-webextension/src/popup/Application.tsx
index 2bf09d07..457f26cf 100644
--- a/packages/taler-wallet-webextension/src/popup/Application.tsx
+++ b/packages/taler-wallet-webextension/src/popup/Application.tsx
@@ -42,13 +42,15 @@ import { BalancePage } from "./BalancePage.js";
 import { TalerActionFound } from "./TalerActionFound.js";
 
 function CheckTalerActionComponent(): VNode {
-  const [talerActionUrl] = useTalerActionURL();
+  const [action] = useTalerActionURL();
+
+  const actionUri = action?.uri;
 
   useEffect(() => {
-    if (talerActionUrl) {
-      route(Pages.cta({ action: encodeURIComponent(talerActionUrl) }));
+    if (actionUri) {
+      route(Pages.cta({ action: encodeURIComponent(actionUri) }));
     }
-  }, [talerActionUrl]);
+  }, [actionUri]);
 
   return <Fragment />;
 }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]