implement the simplest recovery function
This commit is contained in:
parent
8390c60031
commit
414b1b84e8
@ -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",
|
||||
|
66
packages/taler-wallet-webextension/src/cta/Recovery/index.ts
Normal file
66
packages/taler-wallet-webextension/src/cta/Recovery/index.ts
Normal file
@ -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,
|
||||
);
|
67
packages/taler-wallet-webextension/src/cta/Recovery/state.ts
Normal file
67
packages/taler-wallet-webextension/src/cta/Recovery/state.ts
Normal file
@ -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,
|
||||
};
|
||||
}
|
@ -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",
|
||||
};
|
21
packages/taler-wallet-webextension/src/cta/Recovery/test.ts
Normal file
21
packages/taler-wallet-webextension/src/cta/Recovery/test.ts
Normal file
@ -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;
|
||||
});
|
||||
});
|
@ -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>
|
||||
);
|
||||
}
|
@ -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}`,
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
});
|
||||
|
@ -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 ? (
|
||||
|
@ -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);
|
||||
}}
|
||||
/>
|
||||
|
@ -167,7 +167,7 @@ export class WxWalletCoreApiClient implements WalletCoreApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
const wxClient = new WxWalletCoreApiClient();
|
||||
export const wxClient = new WxWalletCoreApiClient();
|
||||
|
||||
/**
|
||||
* Pay for a proposal.
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user