From 26a12809605ac8099acf7931676bfbad0298a3f2 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 1 Jul 2021 11:33:26 -0300 Subject: [PATCH] first working version of provider --- .../src/hooks/useProvidersByCurrency.ts | 2 +- .../src/popup/Backup.stories.tsx | 2 +- .../src/popup/BackupPage.tsx | 4 +- .../src/popup/Provider.stories.tsx | 215 ++++++++++++++++++ .../src/popup/ProviderPage.tsx | 174 ++++++++++++++ .../src/popup/Transaction.tsx | 2 +- .../src/popup/popup.tsx | 1 + .../src/popupEntryPoint.tsx | 2 + .../taler-wallet-webextension/src/wxApi.ts | 23 +- 9 files changed, 417 insertions(+), 8 deletions(-) create mode 100644 packages/taler-wallet-webextension/src/popup/Provider.stories.tsx create mode 100644 packages/taler-wallet-webextension/src/popup/ProviderPage.tsx diff --git a/packages/taler-wallet-webextension/src/hooks/useProvidersByCurrency.ts b/packages/taler-wallet-webextension/src/hooks/useProvidersByCurrency.ts index dedaf6f86..8c35705e1 100644 --- a/packages/taler-wallet-webextension/src/hooks/useProvidersByCurrency.ts +++ b/packages/taler-wallet-webextension/src/hooks/useProvidersByCurrency.ts @@ -1,5 +1,5 @@ import { Amounts } from "@gnu-taler/taler-util"; -import { ProviderInfo } from "@gnu-taler/taler-wallet-core/src/operations/backup"; +import { ProviderInfo } from "@gnu-taler/taler-wallet-core"; import { useEffect, useState } from "preact/hooks"; import * as wxApi from "../wxApi"; diff --git a/packages/taler-wallet-webextension/src/popup/Backup.stories.tsx b/packages/taler-wallet-webextension/src/popup/Backup.stories.tsx index 856360ebf..1bd431633 100644 --- a/packages/taler-wallet-webextension/src/popup/Backup.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/Backup.stories.tsx @@ -19,7 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { ProviderPaymentType } from '@gnu-taler/taler-wallet-core/src/operations/backup'; +import { ProviderPaymentType } from '@gnu-taler/taler-wallet-core'; import { FunctionalComponent } from 'preact'; import { BackupView as TestedComponent } from './BackupPage'; diff --git a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx index 9900720d9..968898a63 100644 --- a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx +++ b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx @@ -16,10 +16,10 @@ import { Timestamp } from "@gnu-taler/taler-util"; -// import { ProviderPaymentStatus } from "@gnu-taler/taler-wallet-core/src/operations/backup"; import { formatDuration, intervalToDuration } from "date-fns"; import { JSX, VNode } from "preact"; import { ProvidersByCurrency, useBackupStatus } from "../hooks/useProvidersByCurrency"; +import { Pages } from "./popup"; export function BackupPage(): VNode { const status = useBackupStatus() @@ -99,7 +99,7 @@ function BackupLayout(props: TransactionLayoutProps): JSX.Element { {dateStr &&
{dateStr}
} {!dateStr &&
never synced
}
- {props.title} + {props.title}
{props.subtitle}
diff --git a/packages/taler-wallet-webextension/src/popup/Provider.stories.tsx b/packages/taler-wallet-webextension/src/popup/Provider.stories.tsx new file mode 100644 index 000000000..7b059103b --- /dev/null +++ b/packages/taler-wallet-webextension/src/popup/Provider.stories.tsx @@ -0,0 +1,215 @@ +/* + This file is part of GNU Taler + (C) 2021 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 + */ + +/** +* +* @author Sebastian Javier Marchano (sebasjm) +*/ + +import { ProviderPaymentType } from '@gnu-taler/taler-wallet-core'; +import { FunctionalComponent } from 'preact'; +import { ProviderView as TestedComponent } from './ProviderPage'; + +export default { + title: 'popup/backup/details', + component: TestedComponent, + argTypes: { + onRetry: { action: 'onRetry' }, + onDelete: { action: 'onDelete' }, + onBack: { action: 'onBack' }, + } +}; + + +function createExample(Component: FunctionalComponent, props: Partial) { + const r = (args: any) => + r.args = props + return r +} + +export const NotDefined = createExample(TestedComponent, { + currency: 'ARS', +}); + +export const Active = createExample(TestedComponent, { + currency: 'ARS', + info: { + "active": true, + "syncProviderBaseUrl": "http://sync.taler:9967/", + "lastSuccessfulBackupTimestamp": { + "t_ms": 1625063925078 + }, + "paymentProposalIds": [ + "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG" + ], + "paymentStatus": { + "type": ProviderPaymentType.Paid, + "paidUntil": { + "t_ms": 1656599921000 + } + }, + "terms": { + "annualFee": "ARS:1", + "storageLimitInMegabytes": 16, + "supportedProtocolVersion": "0.0" + } + } +}); + +export const ActiveErrorSync = createExample(TestedComponent, { + currency: 'ARS', + info: { + "active": true, + "syncProviderBaseUrl": "http://sync.taler:9967/", + "lastSuccessfulBackupTimestamp": { + "t_ms": 1625063925078 + }, + "paymentProposalIds": [ + "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG" + ], + "paymentStatus": { + "type": ProviderPaymentType.Paid, + "paidUntil": { + "t_ms": 1656599921000 + } + }, + lastError: { + code: 2002, + details: 'details', + hint: 'error hint from the server', + message: 'message' + }, + "terms": { + "annualFee": "ARS:1", + "storageLimitInMegabytes": 16, + "supportedProtocolVersion": "0.0" + } + } +}); + +export const ActiveBackupProblemUnreadable = createExample(TestedComponent, { + currency: 'ARS', + info: { + "active": true, + "syncProviderBaseUrl": "http://sync.taler:9967/", + "lastSuccessfulBackupTimestamp": { + "t_ms": 1625063925078 + }, + "paymentProposalIds": [ + "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG" + ], + "paymentStatus": { + "type": ProviderPaymentType.Paid, + "paidUntil": { + "t_ms": 1656599921000 + } + }, + backupProblem: { + type: 'backup-unreadable' + }, + "terms": { + "annualFee": "ARS:1", + "storageLimitInMegabytes": 16, + "supportedProtocolVersion": "0.0" + } + } +}); + +export const ActiveBackupProblemDevice = createExample(TestedComponent, { + currency: 'ARS', + info: { + "active": true, + "syncProviderBaseUrl": "http://sync.taler:9967/", + "lastSuccessfulBackupTimestamp": { + "t_ms": 1625063925078 + }, + "paymentProposalIds": [ + "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG" + ], + "paymentStatus": { + "type": ProviderPaymentType.Paid, + "paidUntil": { + "t_ms": 1656599921000 + } + }, + backupProblem: { + type: 'backup-conflicting-device', + myDeviceId: 'my-device-id', + otherDeviceId: 'other-device-id', + backupTimestamp: { + "t_ms": 1656599921000 + } + }, + "terms": { + "annualFee": "ARS:1", + "storageLimitInMegabytes": 16, + "supportedProtocolVersion": "0.0" + } + } +}); + +export const InactiveUnpaid = createExample(TestedComponent, { + currency: 'ARS', + info: { + "active": false, + "syncProviderBaseUrl": "http://sync.demo.taler.net/", + "paymentProposalIds": [], + "paymentStatus": { + "type": ProviderPaymentType.Unpaid, + }, + "terms": { + "annualFee": "ARS:0.1", + "storageLimitInMegabytes": 16, + "supportedProtocolVersion": "0.0" + } + } +}); + +export const InactiveInsufficientBalance = createExample(TestedComponent, { + currency: 'ARS', + info: { + "active": false, + "syncProviderBaseUrl": "http://sync.demo.taler.net/", + "paymentProposalIds": [], + "paymentStatus": { + "type": ProviderPaymentType.InsufficientBalance, + }, + "terms": { + "annualFee": "ARS:0.1", + "storageLimitInMegabytes": 16, + "supportedProtocolVersion": "0.0" + } + } +}); + +export const InactivePending = createExample(TestedComponent, { + currency: 'ARS', + info: { + "active": false, + "syncProviderBaseUrl": "http://sync.demo.taler.net/", + "paymentProposalIds": [], + "paymentStatus": { + "type": ProviderPaymentType.Pending, + }, + "terms": { + "annualFee": "ARS:0.1", + "storageLimitInMegabytes": 16, + "supportedProtocolVersion": "0.0" + } + } +}); + + diff --git a/packages/taler-wallet-webextension/src/popup/ProviderPage.tsx b/packages/taler-wallet-webextension/src/popup/ProviderPage.tsx new file mode 100644 index 000000000..1112017fc --- /dev/null +++ b/packages/taler-wallet-webextension/src/popup/ProviderPage.tsx @@ -0,0 +1,174 @@ +/* + This file is part of TALER + (C) 2016 GNUnet e.V. + + 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. + + 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 + TALER; see the file COPYING. If not, see +*/ + + +import { i18n, Timestamp } from "@gnu-taler/taler-util"; +import { ProviderInfo, ProviderPaymentType } from "@gnu-taler/taler-wallet-core"; +import { formatDuration, intervalToDuration } from "date-fns"; +import { VNode } from "preact"; +import { useRef, useState } from "preact/hooks"; +import { useBackupStatus } from "../hooks/useProvidersByCurrency"; +import * as wxApi from "../wxApi"; + +interface Props { + currency: string; +} + +export function ProviderPage({ currency }: Props): VNode { + const status = useBackupStatus() + const [adding, setAdding] = useState(false) + if (!status) { + return
Loading...
+ } + if (adding) { + return { + console.log(value) + wxApi.addBackupProvider(value).then(_ => history.go(-1)) + setAdding(false) + }} /> + } + const info = status.providers[currency]; + return { null }} + onDelete={() => { null }} + onBack={() => { history.go(-1); }} + onAddProvider={() => { setAdding(true) }} + />; +} + +function AddProviderView({ onConfirm }: { onConfirm: (s: string) => void }) { + const textInput = useRef(null) + return
+ + +
+} + +export interface ViewProps { + currency: string; + info?: ProviderInfo; + onDelete: () => void; + onSync: () => void; + onBack: () => void; + onAddProvider: () => void; +} + +export function ProviderView({ currency, info, onDelete, onSync, onBack, onAddProvider }: ViewProps): VNode { + function Footer() { + return
+ +
+ {info && } + {info && } + {!info && } +
+
+ } + function Error() { + if (info?.lastError) { + return
+

{info.lastError.hint}

+
+ } + if (info?.backupProblem) { + switch (info.backupProblem.type) { + case "backup-conflicting-device": + return
+

There is another backup from {info.backupProblem.otherDeviceId}

+
+ case "backup-unreadable": + return
+

Backup is not readable

+
+ default: + return
+

Unkown backup problem: {JSON.stringify(info.backupProblem)}

+
+ } + } + return null + } + function colorByStatus(status: ProviderPaymentType | undefined) { + switch (status) { + case ProviderPaymentType.InsufficientBalance: + return 'rgb(223, 117, 20)' + case ProviderPaymentType.Unpaid: + return 'rgb(202, 60, 60)' + case ProviderPaymentType.Paid: + return 'rgb(28, 184, 65)' + case ProviderPaymentType.Pending: + return 'gray' + case ProviderPaymentType.InsufficientBalance: + return 'rgb(202, 60, 60)' + case ProviderPaymentType.TermsChanged: + return 'rgb(202, 60, 60)' + default: + break; + } + return undefined + } + + return ( +
+ +
+
+ {info?.paymentStatus.type} + {info && + From {info.syncProviderBaseUrl} + } + + + +
+

{currency}

+ {info &&
{info.terms?.annualFee} / year
} +
+ +
{daysSince(info?.lastSuccessfulBackupTimestamp)}
+
+
+
+
+ ) +} + +function daysSince(d?: Timestamp) { + if (!d || d.t_ms === 'never') return 'never synced' + const duration = intervalToDuration({ + start: d.t_ms, + end: new Date(), + }) + const str = formatDuration(duration, { + delimiter: ', ', + format: [ + duration?.years ? 'years' : ( + duration?.months ? 'months' : ( + duration?.days ? 'days' : ( + duration?.hours ? 'hours' : ( + duration?.minutes ? 'minutes' : 'seconds' + ) + ) + ) + ) + ] + }) + return `synced ${str} ago` +} diff --git a/packages/taler-wallet-webextension/src/popup/Transaction.tsx b/packages/taler-wallet-webextension/src/popup/Transaction.tsx index 7a66d7a0e..d6a85d64d 100644 --- a/packages/taler-wallet-webextension/src/popup/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/popup/Transaction.tsx @@ -97,7 +97,7 @@ export function TransactionView({ transaction, onDelete, onRetry, onBack }: Wall return (
- {transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')} + {transaction.timestamp.t_ms === "never" ? "never" : format(transaction.timestamp.t_ms, 'dd/MM/yyyy HH:mm:ss')} From {transaction.exchangeBaseUrl} diff --git a/packages/taler-wallet-webextension/src/popup/popup.tsx b/packages/taler-wallet-webextension/src/popup/popup.tsx index 288e04477..6b8f110e1 100644 --- a/packages/taler-wallet-webextension/src/popup/popup.tsx +++ b/packages/taler-wallet-webextension/src/popup/popup.tsx @@ -36,6 +36,7 @@ export enum Pages { backup = '/backup', history = '/history', transaction = '/transaction/:tid', + provider = '/provider/:currency', } interface TabProps { diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx index c777e01de..8fb5121e5 100644 --- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx @@ -38,6 +38,7 @@ import { useTalerActionURL } from "./hooks/useTalerActionURL"; import { createHashHistory } from "history"; import { DevContextProvider } from "./context/useDevContext"; import { BackupPage } from "./popup/BackupPage"; +import { ProviderPage } from "./popup/ProviderPage.js"; function main(): void { try { @@ -99,6 +100,7 @@ function Application() { + diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts index 81f418d40..393c41102 100644 --- a/packages/taler-wallet-webextension/src/wxApi.ts +++ b/packages/taler-wallet-webextension/src/wxApi.ts @@ -38,8 +38,8 @@ import { DeleteTransactionRequest, RetryTransactionRequest, } from "@gnu-taler/taler-util"; -import { BackupProviderState, OperationFailedError } from "@gnu-taler/taler-wallet-core"; -import { BackupInfo } from "@gnu-taler/taler-wallet-core/src/operations/backup"; +import { AddBackupProviderRequest, BackupProviderState, OperationFailedError } from "@gnu-taler/taler-wallet-core"; +import { BackupInfo } from "@gnu-taler/taler-wallet-core"; export interface ExtendedPermissionsResponse { newValue: boolean; @@ -166,9 +166,26 @@ export function listKnownCurrencies(): Promise { /** * Get information about the current state of wallet backups. */ - export function getBackupInfo(): Promise { +export function getBackupInfo(): Promise { return callBackend("getBackupInfo", {}) } + +/** + * Add a backup provider and activate it + */ +export function addBackupProvider(backupProviderBaseUrl: string): Promise { + return callBackend("addBackupProvider", { + backupProviderBaseUrl, activate: true + } as AddBackupProviderRequest) +} + +export function syncAllProviders(): Promise { + return callBackend("runBackupCycle", {}) +} + + + + /** * Retry a transaction * @param transactionId