gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] 02/02: implement the simplest recovery function


From: gnunet
Subject: [taler-wallet-core] 02/02: implement the simplest recovery function
Date: Thu, 20 Oct 2022 19:55:01 +0200

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

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

commit 414b1b84e8d1805d4bdb8844a6e5bdd50a3613c2
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Thu Oct 20 14:54:29 2022 -0300

    implement the simplest recovery function
---
 .../src/NavigationBar.tsx                          |  1 +
 .../src/cta/Recovery/index.ts                      | 66 ++++++++++++++++
 .../src/cta/Recovery/state.ts                      | 67 ++++++++++++++++
 .../src/cta/Recovery/stories.tsx                   | 28 +++++++
 .../src/cta/Recovery/test.ts                       | 21 +++++
 .../src/cta/Recovery/views.tsx                     | 63 +++++++++++++++
 .../src/platform/chrome.ts                         |  5 ++
 .../src/wallet/Application.tsx                     |  7 ++
 .../src/wallet/Backup.stories.tsx                  |  9 ++-
 .../src/wallet/BackupPage.tsx                      | 89 +++++++++++++++++++++-
 .../src/wallet/ProviderAddPage.tsx                 | 10 ++-
 packages/taler-wallet-webextension/src/wxApi.ts    |  2 +-
 .../taler-wallet-webextension/src/wxBackend.ts     |  5 ++
 13 files changed, 367 insertions(+), 6 deletions(-)

diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx 
b/packages/taler-wallet-webextension/src/NavigationBar.tsx
index f6c56af13..4f105aa10 100644
--- a/packages/taler-wallet-webextension/src/NavigationBar.tsx
+++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx
@@ -110,6 +110,7 @@ export const Pages = {
 
   cta: pageDefinition<{ action: string }>("/cta/:action"),
   ctaPay: "/cta/pay",
+  ctaRecovery: "/cta/recovery",
   ctaRefund: "/cta/refund",
   ctaTips: "/cta/tip",
   ctaWithdraw: "/cta/withdraw",
diff --git a/packages/taler-wallet-webextension/src/cta/Recovery/index.ts 
b/packages/taler-wallet-webextension/src/cta/Recovery/index.ts
new file mode 100644
index 000000000..013e9c041
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/cta/Recovery/index.ts
@@ -0,0 +1,66 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { AmountJson } from "@gnu-taler/taler-util";
+import { Loading } from "../../components/Loading.js";
+import { HookError } from "../../hooks/useAsyncAsHook.js";
+import { ButtonHandler } from "../../mui/handlers.js";
+import { compose, StateViewMap } from "../../utils/index.js";
+import * as wxApi from "../../wxApi.js";
+import { useComponentState } from "./state.js";
+import { LoadingUriView, ReadyView } from "./views.js";
+
+export interface Props {
+  talerRecoveryUri?: string;
+  onCancel: () => Promise<void>;
+  onSuccess: () => Promise<void>;
+}
+
+export type State = State.Loading | State.LoadingUriError | State.Ready;
+
+export namespace State {
+  export interface Loading {
+    status: "loading";
+    error: undefined;
+  }
+
+  export interface LoadingUriError {
+    status: "loading-uri";
+    error: HookError;
+  }
+
+  export interface BaseInfo {
+    error: undefined;
+    cancel: ButtonHandler;
+  }
+
+  export interface Ready extends BaseInfo {
+    status: "ready";
+    accept: ButtonHandler;
+  }
+}
+
+const viewMapping: StateViewMap<State> = {
+  loading: Loading,
+  "loading-uri": LoadingUriView,
+  ready: ReadyView,
+};
+
+export const RecoveryPage = compose(
+  "Recovery",
+  (p: Props) => useComponentState(p, wxApi),
+  viewMapping,
+);
diff --git a/packages/taler-wallet-webextension/src/cta/Recovery/state.ts 
b/packages/taler-wallet-webextension/src/cta/Recovery/state.ts
new file mode 100644
index 000000000..965a64e69
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/cta/Recovery/state.ts
@@ -0,0 +1,67 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { parseRecoveryUri } from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import * as wxApi from "../../wxApi.js";
+import { wxClient } from "../../wxApi.js";
+import { Props, State } from "./index.js";
+
+export function useComponentState(
+  { talerRecoveryUri, onCancel, onSuccess }: Props,
+  api: typeof wxApi,
+): State {
+  if (!talerRecoveryUri) {
+    return {
+      status: "loading-uri",
+      error: {
+        operational: false,
+        hasError: true,
+        message: "Missing URI",
+      },
+    };
+  }
+  const info = parseRecoveryUri(talerRecoveryUri);
+
+  if (!info) {
+    return {
+      status: "loading-uri",
+      error: {
+        operational: false,
+        hasError: true,
+        message: "Could not be read",
+      },
+    };
+  }
+  const recovery = info;
+
+  async function recoverBackup(): Promise<void> {
+    await wxClient.call(WalletApiOperation.ImportBackupRecovery, { recovery });
+    onSuccess();
+  }
+
+  return {
+    status: "ready",
+
+    accept: {
+      onClick: recoverBackup,
+    },
+    cancel: {
+      onClick: onCancel,
+    },
+    error: undefined,
+  };
+}
diff --git a/packages/taler-wallet-webextension/src/cta/Recovery/stories.tsx 
b/packages/taler-wallet-webextension/src/cta/Recovery/stories.tsx
new file mode 100644
index 000000000..e1da860fb
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/cta/Recovery/stories.tsx
@@ -0,0 +1,28 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ *
+ * @author Sebastian Javier Marchano (sebasjm)
+ */
+
+import { Amounts } from "@gnu-taler/taler-util";
+import { createExample } from "../../test-utils.js";
+import { ReadyView } from "./views.js";
+
+export default {
+  title: "cta/recovery",
+};
diff --git a/packages/taler-wallet-webextension/src/cta/Recovery/test.ts 
b/packages/taler-wallet-webextension/src/cta/Recovery/test.ts
new file mode 100644
index 000000000..68c75b380
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/cta/Recovery/test.ts
@@ -0,0 +1,21 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+describe("Backup import CTA states", () => {
+  it.skip("should test something", async () => {
+    return;
+  });
+});
diff --git a/packages/taler-wallet-webextension/src/cta/Recovery/views.tsx 
b/packages/taler-wallet-webextension/src/cta/Recovery/views.tsx
new file mode 100644
index 000000000..371516932
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/cta/Recovery/views.tsx
@@ -0,0 +1,63 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { Fragment, h, VNode } from "preact";
+import { LoadingError } from "../../components/LoadingError.js";
+import { LogoHeader } from "../../components/LogoHeader.js";
+import { SubTitle, WalletAction } from "../../components/styled/index.js";
+import { useTranslationContext } from "../../context/translation.js";
+import { Button } from "../../mui/Button.js";
+import { State } from "./index.js";
+
+export function LoadingUriView({ error }: State.LoadingUriError): VNode {
+  const { i18n } = useTranslationContext();
+
+  return (
+    <LoadingError
+      title={
+        <i18n.Translate>
+          Could not load backup recovery information
+        </i18n.Translate>
+      }
+      error={error}
+    />
+  );
+}
+
+export function ReadyView({ accept, cancel }: State.Ready): VNode {
+  const { i18n } = useTranslationContext();
+  return (
+    <WalletAction>
+      <LogoHeader />
+
+      <SubTitle>
+        <i18n.Translate>Digital wallet recovery</i18n.Translate>
+      </SubTitle>
+
+      <section>
+        <p>
+          <i18n.Translate>Import backup, show info</i18n.Translate>
+        </p>
+        <Button variant="contained" onClick={accept.onClick}>
+          Import
+        </Button>
+        <Button variant="contained" onClick={cancel.onClick}>
+          Cancel
+        </Button>
+      </section>
+    </WalletAction>
+  );
+}
diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts 
b/packages/taler-wallet-webextension/src/platform/chrome.ts
index dfd5ef818..d7af57564 100644
--- a/packages/taler-wallet-webextension/src/platform/chrome.ts
+++ b/packages/taler-wallet-webextension/src/platform/chrome.ts
@@ -236,6 +236,11 @@ function openWalletURIFromPopup(talerUri: string): void {
         `static/wallet.html#/cta/withdraw?talerWithdrawUri=${talerUri}`,
       );
       break;
+    case TalerUriType.TalerRecovery:
+      url = chrome.runtime.getURL(
+        `static/wallet.html#/cta/recovery?talerRecoveryUri=${talerUri}`,
+      );
+      break;
     case TalerUriType.TalerPay:
       url = chrome.runtime.getURL(
         `static/wallet.html#/cta/pay?talerPayUri=${talerUri}`,
diff --git a/packages/taler-wallet-webextension/src/wallet/Application.tsx 
b/packages/taler-wallet-webextension/src/wallet/Application.tsx
index f8b2f3ec8..5934dec00 100644
--- a/packages/taler-wallet-webextension/src/wallet/Application.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Application.tsx
@@ -64,6 +64,7 @@ import { TransferCreatePage } from 
"../cta/TransferCreate/index.js";
 import { InvoiceCreatePage } from "../cta/InvoiceCreate/index.js";
 import { TransferPickupPage } from "../cta/TransferPickup/index.js";
 import { InvoicePayPage } from "../cta/InvoicePay/index.js";
+import { RecoveryPage } from "../cta/Recovery/index.js";
 
 export function Application(): VNode {
   const [globalNotification, setGlobalNotification] = useState<
@@ -328,6 +329,12 @@ export function Application(): VNode {
                   redirectTo(Pages.balanceTransaction({ tid }))
                 }
               />
+              <Route
+                path={Pages.ctaRecovery}
+                component={RecoveryPage}
+                onCancel={() => redirectTo(Pages.balance)}
+                onSuccess={() => redirectTo(Pages.backup)}
+              />
 
               {/**
                * NOT FOUND
diff --git a/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx 
b/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx
index 1b54dbfa5..b12f5e5f6 100644
--- a/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Backup.stories.tsx
@@ -21,7 +21,10 @@
 
 import { ProviderPaymentType } from "@gnu-taler/taler-wallet-core";
 import { addDays } from "date-fns";
-import { BackupView as TestedComponent } from "./BackupPage.js";
+import {
+  BackupView as TestedComponent,
+  ShowRecoveryInfo,
+} from "./BackupPage.js";
 import { createExample } from "../test-utils.js";
 import { TalerProtocolTimestamp } from "@gnu-taler/taler-util";
 
@@ -194,3 +197,7 @@ export const OneProvider = createExample(TestedComponent, {
 export const Empty = createExample(TestedComponent, {
   providers: [],
 });
+
+export const Recovery = createExample(ShowRecoveryInfo, {
+  info: "taler://recovery/ASLDKJASLKDJASD",
+});
diff --git a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
index 3f948d37b..bba8b5964 100644
--- a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
@@ -14,12 +14,17 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { AbsoluteTime } from "@gnu-taler/taler-util";
+import {
+  AbsoluteTime,
+  BackupRecovery,
+  constructRecoveryUri,
+} from "@gnu-taler/taler-util";
 import {
   ProviderInfo,
   ProviderPaymentPaid,
   ProviderPaymentStatus,
   ProviderPaymentType,
+  WalletApiOperation,
 } from "@gnu-taler/taler-wallet-core";
 import {
   differenceInMonths,
@@ -37,20 +42,78 @@ import {
   RowBorderGray,
   SmallLightText,
   SmallText,
+  WarningBox,
 } from "../components/styled/index.js";
 import { useTranslationContext } from "../context/translation.js";
 import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
 import { Button } from "../mui/Button.js";
 import { Pages } from "../NavigationBar.js";
 import * as wxApi from "../wxApi.js";
+import { wxClient } from "../wxApi.js";
+import { useEffect, useState } from "preact/hooks";
+import { QR } from "../components/QR.js";
 
 interface Props {
   onAddProvider: () => Promise<void>;
 }
 
+export function ShowRecoveryInfo({
+  info,
+  onClose,
+}: {
+  info: string;
+  onClose: () => Promise<void>;
+}): VNode {
+  const [display, setDisplay] = useState(false);
+  const [copied, setCopied] = useState(false);
+  async function copyText(): Promise<void> {
+    navigator.clipboard.writeText(info);
+    setCopied(true);
+  }
+  useEffect(() => {
+    if (copied) {
+      setTimeout(() => {
+        setCopied(false);
+      }, 1000);
+    }
+  }, [copied]);
+  return (
+    <Fragment>
+      <h2>Wallet Recovery</h2>
+      <WarningBox>Do not share this QR or URI with anyone</WarningBox>
+      <section>
+        <p>
+          The qr code can be scanned by another wallet to keep synchronized 
with
+          this wallet.
+        </p>
+        <Button variant="contained" onClick={async () => setDisplay((d) => 
!d)}>
+          {display ? "Hide" : "Show"} QR code
+        </Button>
+        {display && <QR text={JSON.stringify(info)} />}
+      </section>
+
+      <section>
+        <p>You can also use the string version</p>
+        <Button variant="contained" disabled={copied} onClick={copyText}>
+          Copy recovery URI
+        </Button>
+      </section>
+      <footer>
+        <div></div>
+        <div>
+          <Button variant="contained" onClick={onClose}>
+            Close
+          </Button>
+        </div>
+      </footer>
+    </Fragment>
+  );
+}
+
 export function BackupPage({ onAddProvider }: Props): VNode {
   const { i18n } = useTranslationContext();
   const status = useAsyncAsHook(wxApi.getBackupInfo);
+  const [recoveryInfo, setRecoveryInfo] = useState<string>("");
   if (!status) {
     return <Loading />;
   }
@@ -63,6 +126,12 @@ export function BackupPage({ onAddProvider }: Props): VNode 
{
     );
   }
 
+  async function getRecoveryInfo(): Promise<void> {
+    const r = await wxClient.call(WalletApiOperation.ExportBackupRecovery, {});
+    const str = constructRecoveryUri(r);
+    setRecoveryInfo(str);
+  }
+
   const providers = status.response.providers.sort((a, b) => {
     if (
       a.paymentStatus.type === ProviderPaymentType.Paid &&
@@ -75,11 +144,21 @@ export function BackupPage({ onAddProvider }: Props): 
VNode {
     );
   });
 
+  if (recoveryInfo) {
+    return (
+      <ShowRecoveryInfo
+        info={recoveryInfo}
+        onClose={async () => setRecoveryInfo("")}
+      />
+    );
+  }
+
   return (
     <BackupView
       providers={providers}
       onAddProvider={onAddProvider}
       onSyncAll={wxApi.syncAllProviders}
+      onShowInfo={getRecoveryInfo}
     />
   );
 }
@@ -88,12 +167,14 @@ export interface ViewProps {
   providers: ProviderInfo[];
   onAddProvider: () => Promise<void>;
   onSyncAll: () => Promise<void>;
+  onShowInfo: () => Promise<void>;
 }
 
 export function BackupView({
   providers,
   onAddProvider,
   onSyncAll,
+  onShowInfo,
 }: ViewProps): VNode {
   const { i18n } = useTranslationContext();
   return (
@@ -128,7 +209,11 @@ export function BackupView({
       </section>
       {!!providers.length && (
         <footer>
-          <div />
+          <div>
+            <Button variant="contained" onClick={onShowInfo}>
+              Show recovery
+            </Button>
+          </div>
           <div>
             <Button variant="contained" onClick={onSyncAll}>
               {providers.length > 1 ? (
diff --git a/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
index 37d54eedc..e0bdeec5f 100644
--- a/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/ProviderAddPage.tsx
@@ -19,6 +19,7 @@ import {
   BackupBackupProviderTerms,
   canonicalizeBaseUrl,
 } from "@gnu-taler/taler-util";
+import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
 import { Fragment, h, VNode } from "preact";
 import { useEffect, useState } from "preact/hooks";
 import { Checkbox } from "../components/Checkbox.js";
@@ -34,6 +35,7 @@ import { useTranslationContext } from 
"../context/translation.js";
 import { Button } from "../mui/Button.js";
 import { queryToSlashConfig } from "../utils/index.js";
 import * as wxApi from "../wxApi.js";
+import { wxClient } from "../wxApi.js";
 
 interface Props {
   currency: string;
@@ -69,8 +71,12 @@ export function ProviderAddPage({ onBack }: Props): VNode {
         setVerifying(undefined);
       }}
       onConfirm={() => {
-        return wxApi
-          .addBackupProvider(verifying.url, verifying.name)
+        return wxClient
+          .call(WalletApiOperation.AddBackupProvider, {
+            backupProviderBaseUrl: verifying.url,
+            name: verifying.name,
+            activate: true,
+          })
           .then(onBack);
       }}
     />
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts 
b/packages/taler-wallet-webextension/src/wxApi.ts
index 8ec16a698..cb333f1dc 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -167,7 +167,7 @@ export class WxWalletCoreApiClient implements 
WalletCoreApiClient {
   }
 }
 
-const wxClient = new WxWalletCoreApiClient();
+export const wxClient = new WxWalletCoreApiClient();
 
 /**
  * Pay for a proposal.
diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts 
b/packages/taler-wallet-webextension/src/wxBackend.ts
index 13cffe747..6f930b788 100644
--- a/packages/taler-wallet-webextension/src/wxBackend.ts
+++ b/packages/taler-wallet-webextension/src/wxBackend.ts
@@ -290,6 +290,11 @@ function parseTalerUriAndRedirect(tabId: number, talerUri: 
string): void {
         tabId,
         `/cta/transfer/pickup?talerPayPushUri=${talerUri}`,
       );
+    case TalerUriType.TalerRecovery:
+      return platform.redirectTabToWalletPage(
+        tabId,
+        `/cta/transfer/recovery?talerBackupUri=${talerUri}`,
+      );
     case TalerUriType.TalerNotifyReserve:
       // FIXME:  Is this still useful?
       // handleNotifyReserve(w);

-- 
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]