From a35604fd562a72e4e266bf6a4255d89d3c1374a1 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 19 Nov 2021 14:51:27 -0300 Subject: some changes: - simplify design to reuse more components (from wallet instead of popup) - simplify hooks (useAsyncAsHook) - updateNotification from backend now filter events by type - new balance design proposed by Belen - more information when the withdrawal is in process - manual withdrawal implementation - some bugs killed --- .../src/popup/Backup.stories.tsx | 198 --------------- .../src/popup/BackupPage.tsx | 197 --------------- .../src/popup/Balance.stories.tsx | 2 +- .../src/popup/BalancePage.tsx | 195 +++------------ .../taler-wallet-webextension/src/popup/Debug.tsx | 2 +- .../src/popup/History.stories.tsx | 1 + .../src/popup/History.tsx | 22 +- .../popup/ProviderAddConfirmProvider.stories.tsx | 51 ---- .../src/popup/ProviderAddPage.tsx | 244 ------------------ .../src/popup/ProviderAddSetUrl.stories.tsx | 51 ---- .../src/popup/ProviderDetail.stories.tsx | 235 ----------------- .../src/popup/ProviderDetailPage.tsx | 278 --------------------- .../src/popup/Settings.tsx | 83 ++++-- .../src/popup/TalerActionFound.tsx | 12 +- 14 files changed, 116 insertions(+), 1455 deletions(-) delete mode 100644 packages/taler-wallet-webextension/src/popup/Backup.stories.tsx delete mode 100644 packages/taler-wallet-webextension/src/popup/BackupPage.tsx delete mode 100644 packages/taler-wallet-webextension/src/popup/ProviderAddConfirmProvider.stories.tsx delete mode 100644 packages/taler-wallet-webextension/src/popup/ProviderAddPage.tsx delete mode 100644 packages/taler-wallet-webextension/src/popup/ProviderAddSetUrl.stories.tsx delete mode 100644 packages/taler-wallet-webextension/src/popup/ProviderDetail.stories.tsx delete mode 100644 packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx (limited to 'packages/taler-wallet-webextension/src/popup') diff --git a/packages/taler-wallet-webextension/src/popup/Backup.stories.tsx b/packages/taler-wallet-webextension/src/popup/Backup.stories.tsx deleted file mode 100644 index 232b0da73..000000000 --- a/packages/taler-wallet-webextension/src/popup/Backup.stories.tsx +++ /dev/null @@ -1,198 +0,0 @@ -/* - 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 { addDays } from "date-fns"; -import { BackupView as TestedComponent } from "./BackupPage"; -import { createExample } from "../test-utils"; - -export default { - title: "popup/backup/list", - component: TestedComponent, - argTypes: { - onRetry: { action: "onRetry" }, - onDelete: { action: "onDelete" }, - onBack: { action: "onBack" }, - }, -}; - -export const LotOfProviders = createExample(TestedComponent, { - providers: [ - { - active: true, - name: "sync.demo", - 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", - }, - }, - { - active: true, - name: "sync.demo", - syncProviderBaseUrl: "http://sync.taler:9967/", - lastSuccessfulBackupTimestamp: { - t_ms: 1625063925078, - }, - paymentProposalIds: [ - "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG", - ], - paymentStatus: { - type: ProviderPaymentType.Paid, - paidUntil: { - t_ms: addDays(new Date(), 13).getTime(), - }, - }, - terms: { - annualFee: "ARS:1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, - }, - { - active: false, - name: "sync.demo", - syncProviderBaseUrl: "http://sync.demo.taler.net/", - paymentProposalIds: [], - paymentStatus: { - type: ProviderPaymentType.Pending, - }, - terms: { - annualFee: "KUDOS:0.1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, - }, - { - active: false, - name: "sync.demo", - syncProviderBaseUrl: "http://sync.demo.taler.net/", - paymentProposalIds: [], - paymentStatus: { - type: ProviderPaymentType.InsufficientBalance, - }, - terms: { - annualFee: "KUDOS:0.1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, - }, - { - active: false, - name: "sync.demo", - syncProviderBaseUrl: "http://sync.demo.taler.net/", - paymentProposalIds: [], - paymentStatus: { - type: ProviderPaymentType.TermsChanged, - newTerms: { - annualFee: "USD:2", - storageLimitInMegabytes: 8, - supportedProtocolVersion: "2", - }, - oldTerms: { - annualFee: "USD:1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "1", - }, - paidUntil: { - t_ms: "never", - }, - }, - terms: { - annualFee: "KUDOS:0.1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, - }, - { - active: false, - name: "sync.demo", - syncProviderBaseUrl: "http://sync.demo.taler.net/", - paymentProposalIds: [], - paymentStatus: { - type: ProviderPaymentType.Unpaid, - }, - terms: { - annualFee: "KUDOS:0.1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, - }, - { - active: false, - name: "sync.demo", - syncProviderBaseUrl: "http://sync.demo.taler.net/", - paymentProposalIds: [], - paymentStatus: { - type: ProviderPaymentType.Unpaid, - }, - terms: { - annualFee: "KUDOS:0.1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, - }, - ], -}); - -export const OneProvider = createExample(TestedComponent, { - providers: [ - { - active: true, - name: "sync.demo", - 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 Empty = createExample(TestedComponent, { - providers: [], -}); diff --git a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx deleted file mode 100644 index ae93f8a40..000000000 --- a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx +++ /dev/null @@ -1,197 +0,0 @@ -/* - 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, - ProviderPaymentStatus, -} from "@gnu-taler/taler-wallet-core"; -import { - differenceInMonths, - formatDuration, - intervalToDuration, -} from "date-fns"; -import { Fragment, h, VNode } from "preact"; -import { - BoldLight, - ButtonPrimary, - ButtonSuccess, - Centered, - CenteredBoldText, - CenteredText, - PopupBox, - RowBorderGray, - SmallLightText, - SmallText, -} from "../components/styled"; -import { useBackupStatus } from "../hooks/useBackupStatus"; -import { Pages } from "../NavigationBar"; - -interface Props { - onAddProvider: () => void; -} - -export function BackupPage({ onAddProvider }: Props): VNode { - const status = useBackupStatus(); - if (!status) { - return
Loading...
; - } - return ( - - ); -} - -export interface ViewProps { - providers: ProviderInfo[]; - onAddProvider: () => void; - onSyncAll: () => Promise; -} - -export function BackupView({ - providers, - onAddProvider, - onSyncAll, -}: ViewProps): VNode { - return ( - -
- {providers.map((provider, idx) => ( - - ))} - {!providers.length && ( - - No backup providers configured - - Add provider - - - )} -
- {!!providers.length && ( -
-
-
- - {providers.length > 1 ? ( - Sync all backups - ) : ( - Sync now - )} - - Add provider -
-
- )} -
- ); -} - -interface TransactionLayoutProps { - status: ProviderPaymentStatus; - timestamp?: Timestamp; - title: string; - id: string; - active: boolean; -} - -function BackupLayout(props: TransactionLayoutProps): VNode { - const date = !props.timestamp ? undefined : new Date(props.timestamp.t_ms); - const dateStr = date?.toLocaleString([], { - dateStyle: "medium", - timeStyle: "short", - } as any); - - return ( - -
- - {props.title} - - - {dateStr && ( - Last synced: {dateStr} - )} - {!dateStr && ( - Not synced - )} -
-
- {props.status?.type === "paid" ? ( - - ) : ( -
{props.status.type}
- )} -
-
- ); -} - -function ExpirationText({ until }: { until: Timestamp }) { - return ( - - Expires in - - {" "} - {daysUntil(until)}{" "} - - - ); -} - -function colorByTimeToExpire(d: Timestamp) { - if (d.t_ms === "never") return "rgb(28, 184, 65)"; - const months = differenceInMonths(d.t_ms, new Date()); - return months > 1 ? "rgb(28, 184, 65)" : "rgb(223, 117, 20)"; -} - -function daysUntil(d: Timestamp) { - if (d.t_ms === "never") return undefined; - 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" - : "minutes", - ], - }); - return `${str}`; -} diff --git a/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx b/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx index 80203f6d3..a4988cf2d 100644 --- a/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx @@ -158,7 +158,7 @@ export const SomeCoinsInTreeCurrencies = createExample(TestedComponent, { requiresUserInput: false, }, { - available: "COL:2000", + available: "TESTKUDOS:2000", hasPendingTransactions: false, pendingIncoming: "USD:0", pendingOutgoing: "USD:0", diff --git a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx index a23c81cd1..008f30cb6 100644 --- a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx +++ b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx @@ -14,194 +14,77 @@ TALER; see the file COPYING. If not, see */ -import { - amountFractionalBase, - Amounts, - Balance, - i18n, -} from "@gnu-taler/taler-util"; -import { h, VNode } from "preact"; -import { - ButtonPrimary, - ErrorBox, - Middle, - PopupBox, -} from "../components/styled/index"; -import { BalancesHook, useBalances } from "../hooks/useBalances"; -import { PageLink, renderAmount } from "../renderHtml"; +import { BalancesResponse, i18n } from "@gnu-taler/taler-util"; +import { Fragment, h, VNode } from "preact"; +import { BalanceTable } from "../components/BalanceTable"; +import { ButtonPrimary, ErrorBox } from "../components/styled/index"; +import { HookResponse, useAsyncAsHook } from "../hooks/useAsyncAsHook"; +import { PageLink } from "../renderHtml"; +import * as wxApi from "../wxApi"; export function BalancePage({ goToWalletManualWithdraw, }: { goToWalletManualWithdraw: () => void; }): VNode { - const balance = useBalances(); + const state = useAsyncAsHook(wxApi.getBalance); return ( ); } export interface BalanceViewProps { - balance: BalancesHook; + balance: HookResponse; Linker: typeof PageLink; goToWalletManualWithdraw: () => void; } -function formatPending(entry: Balance): VNode { - let incoming: VNode | undefined; - let payment: VNode | undefined; - - // const available = Amounts.parseOrThrow(entry.available); - const pendingIncoming = Amounts.parseOrThrow(entry.pendingIncoming); - const pendingOutgoing = Amounts.parseOrThrow(entry.pendingOutgoing); - - if (!Amounts.isZero(pendingIncoming)) { - incoming = ( - - - - {"+"} - {renderAmount(entry.pendingIncoming)} - {" "} - - - ); - } - if (!Amounts.isZero(pendingOutgoing)) { - payment = ( - - - - {"-"} - {renderAmount(entry.pendingOutgoing)} - {" "} - - - ); - } - - const l = [incoming, payment].filter((x) => x !== undefined); - if (l.length === 0) { - return ; - } - - if (l.length === 1) { - return {l}; - } - return ( - - {l[0]}, {l[1]} - - ); -} - export function BalanceView({ balance, Linker, goToWalletManualWithdraw, }: BalanceViewProps): VNode { - function Content(): VNode { - if (!balance) { - return ; - } + if (!balance) { + return
Loading...
; + } - if (balance.hasError) { - return ( -
- {balance.message} -

- Click here for help and - diagnostics. -

-
- ); - } - if (balance.response.balances.length === 0) { - return ( -
- -

- - You have no balance to show. Need some{" "} - help getting started? - -

-
-
- ); - } + if (balance.hasError) { return ( -
- - {balance.response.balances.map((entry, idx) => { - const av = Amounts.parseOrThrow(entry.available); - // Create our number formatter. - let formatter; - try { - formatter = new Intl.NumberFormat("en-US", { - style: "currency", - currency: av.currency, - currencyDisplay: "symbol", - // These options are needed to round to whole numbers if that's what you want. - //minimumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1) - //maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501) - }); - } catch { - formatter = new Intl.NumberFormat("en-US", { - // style: 'currency', - // currency: av.currency, - // These options are needed to round to whole numbers if that's what you want. - //minimumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1) - //maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501) - }); - } - - const v = formatter.format( - av.value + av.fraction / amountFractionalBase, - ); - const fontSize = - v.length < 8 ? "3em" : v.length < 13 ? "2em" : "1em"; - return ( - - - - - - ); - })} -
- {v} - - {av.currency} - - {formatPending(entry)} -
-
+ + {balance.message} +

+ Click here for help and + diagnostics. +

+
+ ); + } + if (balance.response.balances.length === 0) { + return ( + +

+ + You have no balance to show. Need some{" "} + help getting started? + +

+
); } return ( - - {/*
*/} - - {/*
*/} -
-
+ +
+ +
+
Withdraw
- +
); } diff --git a/packages/taler-wallet-webextension/src/popup/Debug.tsx b/packages/taler-wallet-webextension/src/popup/Debug.tsx index b0e8543fc..8b5d41657 100644 --- a/packages/taler-wallet-webextension/src/popup/Debug.tsx +++ b/packages/taler-wallet-webextension/src/popup/Debug.tsx @@ -16,7 +16,7 @@ import { h, VNode } from "preact"; import { Diagnostics } from "../components/Diagnostics"; -import { useDiagnostics } from "../hooks/useDiagnostics.js"; +import { useDiagnostics } from "../hooks/useDiagnostics"; import * as wxApi from "../wxApi"; export function DeveloperPage(): VNode { diff --git a/packages/taler-wallet-webextension/src/popup/History.stories.tsx b/packages/taler-wallet-webextension/src/popup/History.stories.tsx index 95f4a547a..43d39da82 100644 --- a/packages/taler-wallet-webextension/src/popup/History.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/History.stories.tsx @@ -55,6 +55,7 @@ const exampleData = { type: TransactionType.Withdrawal, exchangeBaseUrl: "http://exchange.demo.taler.net", withdrawalDetails: { + reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG", confirmed: false, exchangePaytoUris: ["payto://x-taler-bank/bank/account"], type: WithdrawalType.ManualTransfer, diff --git a/packages/taler-wallet-webextension/src/popup/History.tsx b/packages/taler-wallet-webextension/src/popup/History.tsx index 2228271dc..b23b4781f 100644 --- a/packages/taler-wallet-webextension/src/popup/History.tsx +++ b/packages/taler-wallet-webextension/src/popup/History.tsx @@ -21,18 +21,18 @@ import { Transaction, TransactionsResponse, } from "@gnu-taler/taler-util"; -import { h, VNode } from "preact"; +import { Fragment, h, VNode } from "preact"; import { useEffect, useState } from "preact/hooks"; import { PopupBox } from "../components/styled"; import { TransactionItem } from "../components/TransactionItem"; -import { useBalances } from "../hooks/useBalances"; +import { useAsyncAsHook } from "../hooks/useAsyncAsHook"; import * as wxApi from "../wxApi"; export function HistoryPage(): VNode { const [transactions, setTransactions] = useState< TransactionsResponse | undefined >(undefined); - const balance = useBalances(); + const balance = useAsyncAsHook(wxApi.getBalance); const balanceWithoutError = balance?.hasError ? [] : balance?.response.balances || []; @@ -57,7 +57,7 @@ export function HistoryPage(): VNode { ); } -function amountToString(c: AmountString) { +function amountToString(c: AmountString): string { const idx = c.indexOf(":"); return `${c.substring(idx + 1)} ${c.substring(0, idx)}`; } @@ -68,18 +68,18 @@ export function HistoryView({ }: { list: Transaction[]; balances: Balance[]; -}) { +}): VNode { const multiCurrency = balances.length > 1; return ( - + {balances.length > 0 && (
{multiCurrency ? (
Balance:{" "}
    - {balances.map((b) => ( -
  • {b.available}
  • + {balances.map((b, i) => ( +
  • {b.available}
  • ))}
@@ -113,8 +113,10 @@ export function HistoryView({ rel="noopener noreferrer" style={{ color: "darkgreen", textDecoration: "none" }} href={ + // eslint-disable-next-line no-undef chrome.extension - ? chrome.extension.getURL(`/static/wallet.html#/history`) + ? // eslint-disable-next-line no-undef + chrome.extension.getURL(`/static/wallet.html#/history`) : "#" } > @@ -122,6 +124,6 @@ export function HistoryView({ )}
-
+ ); } diff --git a/packages/taler-wallet-webextension/src/popup/ProviderAddConfirmProvider.stories.tsx b/packages/taler-wallet-webextension/src/popup/ProviderAddConfirmProvider.stories.tsx deleted file mode 100644 index 0cff7f75f..000000000 --- a/packages/taler-wallet-webextension/src/popup/ProviderAddConfirmProvider.stories.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - 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 { createExample } from "../test-utils"; -import { ConfirmProviderView as TestedComponent } from "./ProviderAddPage"; - -export default { - title: "popup/backup/confirm", - component: TestedComponent, - argTypes: { - onRetry: { action: "onRetry" }, - onDelete: { action: "onDelete" }, - onBack: { action: "onBack" }, - }, -}; - -export const DemoService = createExample(TestedComponent, { - url: "https://sync.demo.taler.net/", - provider: { - annual_fee: "KUDOS:0.1", - storage_limit_in_megabytes: 20, - supported_protocol_version: "1", - }, -}); - -export const FreeService = createExample(TestedComponent, { - url: "https://sync.taler:9667/", - provider: { - annual_fee: "ARS:0", - storage_limit_in_megabytes: 20, - supported_protocol_version: "1", - }, -}); diff --git a/packages/taler-wallet-webextension/src/popup/ProviderAddPage.tsx b/packages/taler-wallet-webextension/src/popup/ProviderAddPage.tsx deleted file mode 100644 index 55686ee97..000000000 --- a/packages/taler-wallet-webextension/src/popup/ProviderAddPage.tsx +++ /dev/null @@ -1,244 +0,0 @@ -/* - 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 - */ - -import { - Amounts, - BackupBackupProviderTerms, - canonicalizeBaseUrl, - i18n, -} from "@gnu-taler/taler-util"; -import { VNode, h } from "preact"; -import { useEffect, useState } from "preact/hooks"; -import { Checkbox } from "../components/Checkbox"; -import { ErrorMessage } from "../components/ErrorMessage"; -import { - Button, - ButtonPrimary, - Input, - LightText, - PopupBox, - SmallLightText, -} from "../components/styled/index"; -import * as wxApi from "../wxApi"; - -interface Props { - currency: string; - onBack: () => void; -} - -function getJsonIfOk(r: Response) { - if (r.ok) { - return r.json(); - } else { - if (r.status >= 400 && r.status < 500) { - throw new Error(`URL may not be right: (${r.status}) ${r.statusText}`); - } else { - throw new Error( - `Try another server: (${r.status}) ${ - r.statusText || "internal server error" - }`, - ); - } - } -} - -export function ProviderAddPage({ onBack }: Props): VNode { - const [verifying, setVerifying] = useState< - | { url: string; name: string; provider: BackupBackupProviderTerms } - | undefined - >(undefined); - - async function getProviderInfo( - url: string, - ): Promise { - return fetch(`${url}config`) - .catch((e) => { - throw new Error(`Network error`); - }) - .then(getJsonIfOk); - } - - if (!verifying) { - return ( - getProviderInfo(url)} - onConfirm={(url, name) => - getProviderInfo(url) - .then((provider) => { - setVerifying({ url, name, provider }); - }) - .catch((e) => e.message) - } - /> - ); - } - return ( - { - setVerifying(undefined); - }} - onConfirm={() => { - wxApi.addBackupProvider(verifying.url, verifying.name).then(onBack); - }} - /> - ); -} - -export interface SetUrlViewProps { - initialValue?: string; - onCancel: () => void; - onVerify: (s: string) => Promise; - onConfirm: (url: string, name: string) => Promise; - withError?: string; -} - -export function SetUrlView({ - initialValue, - onCancel, - onVerify, - onConfirm, - withError, -}: SetUrlViewProps) { - const [value, setValue] = useState(initialValue || ""); - const [urlError, setUrlError] = useState(false); - const [name, setName] = useState(undefined); - const [error, setError] = useState(withError); - useEffect(() => { - try { - const url = canonicalizeBaseUrl(value); - onVerify(url) - .then((r) => { - setUrlError(false); - setName(new URL(url).hostname); - }) - .catch(() => { - setUrlError(true); - setName(undefined); - }); - } catch { - setUrlError(true); - setName(undefined); - } - }, [value]); - return ( - -
-

Add backup provider

- - Backup providers may charge for their service -

- - - setValue(e.currentTarget.value)} - /> - - - - setName(e.currentTarget.value)} - /> - -

-
-
- - { - const url = canonicalizeBaseUrl(value); - return onConfirm(url, name!).then((r) => - r ? setError(r) : undefined, - ); - }} - > - Next - -
-
- ); -} - -export interface ConfirmProviderViewProps { - provider: BackupBackupProviderTerms; - url: string; - onCancel: () => void; - onConfirm: () => void; -} -export function ConfirmProviderView({ - url, - provider, - onCancel, - onConfirm, -}: ConfirmProviderViewProps) { - const [accepted, setAccepted] = useState(false); - - return ( - -
-

Review terms of service

-
- Provider URL:{" "} - - {url} - -
- - Please review and accept this provider's terms of service - -

1. Pricing

-

- {Amounts.isZero(provider.annual_fee) - ? "free of charge" - : `${provider.annual_fee} per year of service`} -

-

2. Storage

-

- {provider.storage_limit_in_megabytes} megabytes of storage per year of - service -

- setAccepted((old) => !old)} - enabled={accepted} - /> -
-
- - - Add provider - -
-
- ); -} diff --git a/packages/taler-wallet-webextension/src/popup/ProviderAddSetUrl.stories.tsx b/packages/taler-wallet-webextension/src/popup/ProviderAddSetUrl.stories.tsx deleted file mode 100644 index 9a2f97051..000000000 --- a/packages/taler-wallet-webextension/src/popup/ProviderAddSetUrl.stories.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - 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 { createExample } from "../test-utils"; -import { SetUrlView as TestedComponent } from "./ProviderAddPage"; - -export default { - title: "popup/backup/add", - component: TestedComponent, - argTypes: { - onRetry: { action: "onRetry" }, - onDelete: { action: "onDelete" }, - onBack: { action: "onBack" }, - }, -}; - -export const Initial = createExample(TestedComponent, {}); - -export const WithValue = createExample(TestedComponent, { - initialValue: "sync.demo.taler.net", -}); - -export const WithConnectionError = createExample(TestedComponent, { - withError: "Network error", -}); - -export const WithClientError = createExample(TestedComponent, { - withError: "URL may not be right: (404) Not Found", -}); - -export const WithServerError = createExample(TestedComponent, { - withError: "Try another server: (500) Internal Server Error", -}); diff --git a/packages/taler-wallet-webextension/src/popup/ProviderDetail.stories.tsx b/packages/taler-wallet-webextension/src/popup/ProviderDetail.stories.tsx deleted file mode 100644 index fab21398a..000000000 --- a/packages/taler-wallet-webextension/src/popup/ProviderDetail.stories.tsx +++ /dev/null @@ -1,235 +0,0 @@ -/* - 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 { createExample } from "../test-utils"; -import { ProviderView as TestedComponent } from "./ProviderDetailPage"; - -export default { - title: "popup/backup/details", - component: TestedComponent, - argTypes: { - onRetry: { action: "onRetry" }, - onDelete: { action: "onDelete" }, - onBack: { action: "onBack" }, - }, -}; - -export const Active = createExample(TestedComponent, { - info: { - active: true, - name: "sync.demo", - syncProviderBaseUrl: "http://sync.taler:9967/", - lastSuccessfulBackupTimestamp: { - t_ms: 1625063925078, - }, - paymentProposalIds: [ - "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG", - ], - paymentStatus: { - type: ProviderPaymentType.Paid, - paidUntil: { - t_ms: 1656599921000, - }, - }, - terms: { - annualFee: "EUR:1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, - }, -}); - -export const ActiveErrorSync = createExample(TestedComponent, { - info: { - active: true, - name: "sync.demo", - syncProviderBaseUrl: "http://sync.taler:9967/", - lastSuccessfulBackupTimestamp: { - t_ms: 1625063925078, - }, - lastAttemptedBackupTimestamp: { - 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: "EUR:1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, - }, -}); - -export const ActiveBackupProblemUnreadable = createExample(TestedComponent, { - info: { - active: true, - name: "sync.demo", - 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: "EUR:1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, - }, -}); - -export const ActiveBackupProblemDevice = createExample(TestedComponent, { - info: { - active: true, - name: "sync.demo", - 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: "EUR:1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, - }, -}); - -export const InactiveUnpaid = createExample(TestedComponent, { - info: { - active: false, - name: "sync.demo", - syncProviderBaseUrl: "http://sync.demo.taler.net/", - paymentProposalIds: [], - paymentStatus: { - type: ProviderPaymentType.Unpaid, - }, - terms: { - annualFee: "EUR:0.1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, - }, -}); - -export const InactiveInsufficientBalance = createExample(TestedComponent, { - info: { - active: false, - name: "sync.demo", - syncProviderBaseUrl: "http://sync.demo.taler.net/", - paymentProposalIds: [], - paymentStatus: { - type: ProviderPaymentType.InsufficientBalance, - }, - terms: { - annualFee: "EUR:0.1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, - }, -}); - -export const InactivePending = createExample(TestedComponent, { - info: { - active: false, - name: "sync.demo", - syncProviderBaseUrl: "http://sync.demo.taler.net/", - paymentProposalIds: [], - paymentStatus: { - type: ProviderPaymentType.Pending, - }, - terms: { - annualFee: "EUR:0.1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, - }, -}); - -export const ActiveTermsChanged = createExample(TestedComponent, { - info: { - active: true, - name: "sync.demo", - syncProviderBaseUrl: "http://sync.demo.taler.net/", - paymentProposalIds: [], - paymentStatus: { - type: ProviderPaymentType.TermsChanged, - paidUntil: { - t_ms: 1656599921000, - }, - newTerms: { - annualFee: "EUR:10", - storageLimitInMegabytes: 8, - supportedProtocolVersion: "0.0", - }, - oldTerms: { - annualFee: "EUR:0.1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, - }, - terms: { - annualFee: "EUR:0.1", - storageLimitInMegabytes: 16, - supportedProtocolVersion: "0.0", - }, - }, -}); diff --git a/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx b/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx deleted file mode 100644 index 9617c9a41..000000000 --- a/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx +++ /dev/null @@ -1,278 +0,0 @@ -/* - 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, - ProviderPaymentStatus, - ProviderPaymentType, -} from "@gnu-taler/taler-wallet-core"; -import { format, formatDuration, intervalToDuration } from "date-fns"; -import { Fragment, VNode, h } from "preact"; -import { ErrorMessage } from "../components/ErrorMessage"; -import { - Button, - ButtonDestructive, - ButtonPrimary, - PaymentStatus, - PopupBox, - SmallLightText, -} from "../components/styled"; -import { useProviderStatus } from "../hooks/useProviderStatus"; - -interface Props { - pid: string; - onBack: () => void; -} - -export function ProviderDetailPage({ pid, onBack }: Props): VNode { - const status = useProviderStatus(pid); - if (!status) { - return ( -
- Loading... -
- ); - } - if (!status.info) { - onBack(); - return
; - } - return ( - status.remove().then(onBack)} - onBack={onBack} - onExtend={() => { - null; - }} - /> - ); -} - -export interface ViewProps { - info: ProviderInfo; - onDelete: () => void; - onSync: () => void; - onBack: () => void; - onExtend: () => void; -} - -export function ProviderView({ - info, - onDelete, - onSync, - onBack, - onExtend, -}: ViewProps): VNode { - const lb = info?.lastSuccessfulBackupTimestamp; - const isPaid = - info.paymentStatus.type === ProviderPaymentType.Paid || - info.paymentStatus.type === ProviderPaymentType.TermsChanged; - return ( - - -
-

- {info.name}{" "} - {info.syncProviderBaseUrl} -

- - {isPaid ? "Paid" : "Unpaid"} - -
-
-

- Last backup:{" "} - {lb == null || lb.t_ms == "never" - ? "never" - : format(lb.t_ms, "dd MMM yyyy")}{" "} -

- - Back up - - {info.terms && ( - -

- Provider fee: {info.terms && info.terms.annualFee} per year -

-
- )} -

{descriptionByStatus(info.paymentStatus)}

- - Extend - - - {info.paymentStatus.type === ProviderPaymentType.TermsChanged && ( -
-

- - terms has changed, extending the service will imply accepting - the new terms of service - -

- - - - - - - - - - - - - - - - - - - - - - - -
- old - -> - new -
- fee - {info.paymentStatus.oldTerms.annualFee}->{info.paymentStatus.newTerms.annualFee}
- storage - {info.paymentStatus.oldTerms.storageLimitInMegabytes}->{info.paymentStatus.newTerms.storageLimitInMegabytes}
-
- )} -
-
- -
- - remove provider - -
-
-
- ); -} - -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 - ? i18n.str`years` - : duration?.months - ? i18n.str`months` - : duration?.days - ? i18n.str`days` - : duration?.hours - ? i18n.str`hours` - : duration?.minutes - ? i18n.str`minutes` - : i18n.str`seconds`, - ], - }); - return `synced ${str} ago`; -} - -function Error({ info }: { info: ProviderInfo }) { - if (info.lastError) { - return ; - } - if (info.backupProblem) { - switch (info.backupProblem.type) { - case "backup-conflicting-device": - return ( - - - There is conflict with another backup from{" "} - {info.backupProblem.otherDeviceId} - - - } - /> - ); - case "backup-unreadable": - return ; - default: - return ( - - - Unknown backup problem: {JSON.stringify(info.backupProblem)} - - - } - /> - ); - } - } - return null; -} - -function colorByStatus(status: ProviderPaymentType) { - 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)"; - } -} - -function descriptionByStatus(status: ProviderPaymentStatus) { - switch (status.type) { - // return i18n.str`no enough balance to make the payment` - // return i18n.str`not paid yet` - case ProviderPaymentType.Paid: - case ProviderPaymentType.TermsChanged: - if (status.paidUntil.t_ms === "never") { - return i18n.str`service paid`; - } else { - return ( - - Backup valid until:{" "} - {format(status.paidUntil.t_ms, "dd MMM yyyy")} - - ); - } - case ProviderPaymentType.Unpaid: - case ProviderPaymentType.InsufficientBalance: - case ProviderPaymentType.Pending: - return ""; - } -} diff --git a/packages/taler-wallet-webextension/src/popup/Settings.tsx b/packages/taler-wallet-webextension/src/popup/Settings.tsx index 3b83f0762..0a3f777d5 100644 --- a/packages/taler-wallet-webextension/src/popup/Settings.tsx +++ b/packages/taler-wallet-webextension/src/popup/Settings.tsx @@ -14,26 +14,34 @@ TALER; see the file COPYING. If not, see */ -import { i18n } from "@gnu-taler/taler-util"; -import { VNode, h } from "preact"; +import { ExchangeListItem, i18n } from "@gnu-taler/taler-util"; +import { Fragment, h, VNode } from "preact"; import { Checkbox } from "../components/Checkbox"; -import { EditableText } from "../components/EditableText"; -import { SelectList } from "../components/SelectList"; -import { PopupBox } from "../components/styled"; +import { ButtonPrimary } from "../components/styled"; import { useDevContext } from "../context/devContext"; +import { useAsyncAsHook } from "../hooks/useAsyncAsHook"; import { useBackupDeviceName } from "../hooks/useBackupDeviceName"; import { useExtendedPermissions } from "../hooks/useExtendedPermissions"; import { useLang } from "../hooks/useLang"; +// import { strings as messages } from "../i18n/strings"; +import * as wxApi from "../wxApi"; export function SettingsPage(): VNode { const [permissionsEnabled, togglePermissions] = useExtendedPermissions(); const { devMode, toggleDevMode } = useDevContext(); const { name, update } = useBackupDeviceName(); const [lang, changeLang] = useLang(); + const exchangesHook = useAsyncAsHook(wxApi.listExchanges); + return ( void; developerMode: boolean; toggleDeveloperMode: () => void; + knownExchanges: Array; } -import { strings as messages } from "../i18n/strings"; - -type LangsNames = { - [P in keyof typeof messages]: string; -}; +// type LangsNames = { +// [P in keyof typeof messages]: string; +// }; -const names: LangsNames = { - es: "Español [es]", - en: "English [en]", - fr: "Français [fr]", - de: "Deutsch [de]", - sv: "Svenska [sv]", - it: "Italiano [it]", -}; +// const names: LangsNames = { +// es: "Español [es]", +// en: "English [en]", +// fr: "Français [fr]", +// de: "Deutsch [de]", +// sv: "Svenska [sv]", +// it: "Italiano [it]", +// }; export function SettingsView({ - lang, - changeLang, - deviceName, - setDeviceName, + knownExchanges, + // lang, + // changeLang, + // deviceName, + // setDeviceName, permissionsEnabled, togglePermissions, developerMode, toggleDeveloperMode, }: ViewProps): VNode { return ( - +
+

+ Known exchanges +

+ {!knownExchanges || !knownExchanges.length ? ( +
No exchange yet!
+ ) : ( + + + {knownExchanges.map((e, idx) => ( + + + + + ))} +
{e.currency} + {e.exchangeBaseUrl} +
+
+ )} +
+
+ Manage exchange +
{/*

Wallet

*/} {/* VIEW MORE SETTINGS - + ); } diff --git a/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx b/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx index cbdcbeb15..b2220e37b 100644 --- a/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx +++ b/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx @@ -20,12 +20,8 @@ */ import { classifyTalerUri, TalerUriType } from "@gnu-taler/taler-util"; -import { - ButtonPrimary, - ButtonSuccess, - PopupBox, -} from "../components/styled/index"; -import { h } from "preact"; +import { Fragment, h } from "preact"; +import { ButtonPrimary, ButtonSuccess } from "../components/styled/index"; export interface Props { url: string; @@ -35,7 +31,7 @@ export interface Props { export function TalerActionFound({ url, onDismiss }: Props) { const uriType = classifyTalerUri(url); return ( - +

Taler Action

{uriType === TalerUriType.TalerPay && ( @@ -109,7 +105,7 @@ export function TalerActionFound({ url, onDismiss }: Props) {
onDismiss()}> Dismiss - + ); } -- cgit v1.2.3