aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/popup
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2021-11-19 14:51:27 -0300
committerSebastian <sebasjm@gmail.com>2021-11-19 14:51:35 -0300
commita35604fd562a72e4e266bf6a4255d89d3c1374a1 (patch)
treed0c4df01a89dc78c412be6da3aba3cec343937ff /packages/taler-wallet-webextension/src/popup
parent60cfb0e78f3afed92f315c1394da717329db9564 (diff)
downloadwallet-core-a35604fd562a72e4e266bf6a4255d89d3c1374a1.tar.xz
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
Diffstat (limited to 'packages/taler-wallet-webextension/src/popup')
-rw-r--r--packages/taler-wallet-webextension/src/popup/Backup.stories.tsx198
-rw-r--r--packages/taler-wallet-webextension/src/popup/BackupPage.tsx197
-rw-r--r--packages/taler-wallet-webextension/src/popup/Balance.stories.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/popup/BalancePage.tsx195
-rw-r--r--packages/taler-wallet-webextension/src/popup/Debug.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/popup/History.stories.tsx1
-rw-r--r--packages/taler-wallet-webextension/src/popup/History.tsx22
-rw-r--r--packages/taler-wallet-webextension/src/popup/ProviderAddConfirmProvider.stories.tsx51
-rw-r--r--packages/taler-wallet-webextension/src/popup/ProviderAddPage.tsx244
-rw-r--r--packages/taler-wallet-webextension/src/popup/ProviderAddSetUrl.stories.tsx51
-rw-r--r--packages/taler-wallet-webextension/src/popup/ProviderDetail.stories.tsx235
-rw-r--r--packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx278
-rw-r--r--packages/taler-wallet-webextension/src/popup/Settings.tsx83
-rw-r--r--packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx12
14 files changed, 116 insertions, 1455 deletions
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 <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @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 <http://www.gnu.org/licenses/>
-*/
-
-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 <div>Loading...</div>;
- }
- return (
- <BackupView
- providers={status.providers}
- onAddProvider={onAddProvider}
- onSyncAll={status.sync}
- />
- );
-}
-
-export interface ViewProps {
- providers: ProviderInfo[];
- onAddProvider: () => void;
- onSyncAll: () => Promise<void>;
-}
-
-export function BackupView({
- providers,
- onAddProvider,
- onSyncAll,
-}: ViewProps): VNode {
- return (
- <PopupBox>
- <section>
- {providers.map((provider, idx) => (
- <BackupLayout
- key={idx}
- status={provider.paymentStatus}
- timestamp={provider.lastSuccessfulBackupTimestamp}
- id={provider.syncProviderBaseUrl}
- active={provider.active}
- title={provider.name}
- />
- ))}
- {!providers.length && (
- <Centered style={{ marginTop: 100 }}>
- <BoldLight>No backup providers configured</BoldLight>
- <ButtonSuccess onClick={onAddProvider}>
- <i18n.Translate>Add provider</i18n.Translate>
- </ButtonSuccess>
- </Centered>
- )}
- </section>
- {!!providers.length && (
- <footer>
- <div />
- <div>
- <ButtonPrimary onClick={onSyncAll}>
- {providers.length > 1 ? (
- <i18n.Translate>Sync all backups</i18n.Translate>
- ) : (
- <i18n.Translate>Sync now</i18n.Translate>
- )}
- </ButtonPrimary>
- <ButtonSuccess onClick={onAddProvider}>Add provider</ButtonSuccess>
- </div>
- </footer>
- )}
- </PopupBox>
- );
-}
-
-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 (
- <RowBorderGray>
- <div style={{ color: !props.active ? "grey" : undefined }}>
- <a
- href={Pages.provider_detail.replace(
- ":pid",
- encodeURIComponent(props.id),
- )}
- >
- <span>{props.title}</span>
- </a>
-
- {dateStr && (
- <SmallText style={{ marginTop: 5 }}>Last synced: {dateStr}</SmallText>
- )}
- {!dateStr && (
- <SmallLightText style={{ marginTop: 5 }}>Not synced</SmallLightText>
- )}
- </div>
- <div>
- {props.status?.type === "paid" ? (
- <ExpirationText until={props.status.paidUntil} />
- ) : (
- <div>{props.status.type}</div>
- )}
- </div>
- </RowBorderGray>
- );
-}
-
-function ExpirationText({ until }: { until: Timestamp }) {
- return (
- <Fragment>
- <CenteredText> Expires in </CenteredText>
- <CenteredBoldText {...{ color: colorByTimeToExpire(until) }}>
- {" "}
- {daysUntil(until)}{" "}
- </CenteredBoldText>
- </Fragment>
- );
-}
-
-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 <http://www.gnu.org/licenses/>
*/
-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 (
<BalanceView
- balance={balance}
+ balance={state}
Linker={PageLink}
goToWalletManualWithdraw={goToWalletManualWithdraw}
/>
);
}
export interface BalanceViewProps {
- balance: BalancesHook;
+ balance: HookResponse<BalancesResponse>;
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 = (
- <span>
- <i18n.Translate>
- <span style={{ color: "darkgreen" }} title="incoming amount">
- {"+"}
- {renderAmount(entry.pendingIncoming)}
- </span>{" "}
- </i18n.Translate>
- </span>
- );
- }
- if (!Amounts.isZero(pendingOutgoing)) {
- payment = (
- <span>
- <i18n.Translate>
- <span style={{ color: "darkred" }} title="outgoing amount">
- {"-"}
- {renderAmount(entry.pendingOutgoing)}
- </span>{" "}
- </i18n.Translate>
- </span>
- );
- }
-
- const l = [incoming, payment].filter((x) => x !== undefined);
- if (l.length === 0) {
- return <span />;
- }
-
- if (l.length === 1) {
- return <span>{l}</span>;
- }
- return (
- <span>
- {l[0]}, {l[1]}
- </span>
- );
-}
-
export function BalanceView({
balance,
Linker,
goToWalletManualWithdraw,
}: BalanceViewProps): VNode {
- function Content(): VNode {
- if (!balance) {
- return <span />;
- }
+ if (!balance) {
+ return <div>Loading...</div>;
+ }
- if (balance.hasError) {
- return (
- <section>
- <ErrorBox>{balance.message}</ErrorBox>
- <p>
- Click <Linker pageName="welcome">here</Linker> for help and
- diagnostics.
- </p>
- </section>
- );
- }
- if (balance.response.balances.length === 0) {
- return (
- <section data-expanded>
- <Middle>
- <p>
- <i18n.Translate>
- You have no balance to show. Need some{" "}
- <Linker pageName="/welcome">help</Linker> getting started?
- </i18n.Translate>
- </p>
- </Middle>
- </section>
- );
- }
+ if (balance.hasError) {
return (
- <section data-expanded data-centered>
- <table style={{ width: "100%" }}>
- {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 (
- <tr key={idx}>
- <td
- style={{
- height: 50,
- fontSize,
- width: "60%",
- textAlign: "right",
- padding: 0,
- }}
- >
- {v}
- </td>
- <td style={{ maxWidth: "2em", overflowX: "hidden" }}>
- {av.currency}
- </td>
- <td style={{ fontSize: "small", color: "gray" }}>
- {formatPending(entry)}
- </td>
- </tr>
- );
- })}
- </table>
- </section>
+ <Fragment>
+ <ErrorBox>{balance.message}</ErrorBox>
+ <p>
+ Click <Linker pageName="welcome">here</Linker> for help and
+ diagnostics.
+ </p>
+ </Fragment>
+ );
+ }
+ if (balance.response.balances.length === 0) {
+ return (
+ <Fragment>
+ <p>
+ <i18n.Translate>
+ You have no balance to show. Need some{" "}
+ <Linker pageName="/welcome">help</Linker> getting started?
+ </i18n.Translate>
+ </p>
+ </Fragment>
);
}
return (
- <PopupBox>
- {/* <section> */}
- <Content />
- {/* </section> */}
- <footer>
- <div />
+ <Fragment>
+ <section>
+ <BalanceTable balances={balance.response.balances} />
+ </section>
+ <footer style={{ justifyContent: "space-around" }}>
<ButtonPrimary onClick={goToWalletManualWithdraw}>
Withdraw
</ButtonPrimary>
</footer>
- </PopupBox>
+ </Fragment>
);
}
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 (
- <PopupBox noPadding>
+ <Fragment>
{balances.length > 0 && (
<header>
{multiCurrency ? (
<div class="title">
Balance:{" "}
<ul style={{ margin: 0 }}>
- {balances.map((b) => (
- <li>{b.available}</li>
+ {balances.map((b, i) => (
+ <li key={i}>{b.available}</li>
))}
</ul>
</div>
@@ -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({
</a>
)}
</footer>
- </PopupBox>
+ </Fragment>
);
}
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 <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @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 <http://www.gnu.org/licenses/>
- */
-
-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<BackupBackupProviderTerms> {
- return fetch(`${url}config`)
- .catch((e) => {
- throw new Error(`Network error`);
- })
- .then(getJsonIfOk);
- }
-
- if (!verifying) {
- return (
- <SetUrlView
- onCancel={onBack}
- onVerify={(url) => getProviderInfo(url)}
- onConfirm={(url, name) =>
- getProviderInfo(url)
- .then((provider) => {
- setVerifying({ url, name, provider });
- })
- .catch((e) => e.message)
- }
- />
- );
- }
- return (
- <ConfirmProviderView
- provider={verifying.provider}
- url={verifying.url}
- onCancel={() => {
- setVerifying(undefined);
- }}
- onConfirm={() => {
- wxApi.addBackupProvider(verifying.url, verifying.name).then(onBack);
- }}
- />
- );
-}
-
-export interface SetUrlViewProps {
- initialValue?: string;
- onCancel: () => void;
- onVerify: (s: string) => Promise<BackupBackupProviderTerms | undefined>;
- onConfirm: (url: string, name: string) => Promise<string | undefined>;
- withError?: string;
-}
-
-export function SetUrlView({
- initialValue,
- onCancel,
- onVerify,
- onConfirm,
- withError,
-}: SetUrlViewProps) {
- const [value, setValue] = useState<string>(initialValue || "");
- const [urlError, setUrlError] = useState(false);
- const [name, setName] = useState<string | undefined>(undefined);
- const [error, setError] = useState<string | undefined>(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 (
- <PopupBox>
- <section>
- <h1> Add backup provider</h1>
- <ErrorMessage
- title={error && "Could not get provider information"}
- description={error}
- />
- <LightText> Backup providers may charge for their service</LightText>
- <p>
- <Input invalid={urlError}>
- <label>URL</label>
- <input
- type="text"
- placeholder="https://"
- value={value}
- onChange={(e) => setValue(e.currentTarget.value)}
- />
- </Input>
- <Input>
- <label>Name</label>
- <input
- type="text"
- disabled={name === undefined}
- value={name}
- onChange={(e) => setName(e.currentTarget.value)}
- />
- </Input>
- </p>
- </section>
- <footer>
- <Button onClick={onCancel}>
- <i18n.Translate> &lt; Back</i18n.Translate>
- </Button>
- <ButtonPrimary
- disabled={!value && !urlError}
- onClick={() => {
- const url = canonicalizeBaseUrl(value);
- return onConfirm(url, name!).then((r) =>
- r ? setError(r) : undefined,
- );
- }}
- >
- <i18n.Translate>Next</i18n.Translate>
- </ButtonPrimary>
- </footer>
- </PopupBox>
- );
-}
-
-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 (
- <PopupBox>
- <section>
- <h1>Review terms of service</h1>
- <div>
- Provider URL:{" "}
- <a href={url} target="_blank">
- {url}
- </a>
- </div>
- <SmallLightText>
- Please review and accept this provider's terms of service
- </SmallLightText>
- <h2>1. Pricing</h2>
- <p>
- {Amounts.isZero(provider.annual_fee)
- ? "free of charge"
- : `${provider.annual_fee} per year of service`}
- </p>
- <h2>2. Storage</h2>
- <p>
- {provider.storage_limit_in_megabytes} megabytes of storage per year of
- service
- </p>
- <Checkbox
- label="Accept terms of service"
- name="terms"
- onToggle={() => setAccepted((old) => !old)}
- enabled={accepted}
- />
- </section>
- <footer>
- <Button onClick={onCancel}>
- <i18n.Translate> &lt; Back</i18n.Translate>
- </Button>
- <ButtonPrimary disabled={!accepted} onClick={onConfirm}>
- <i18n.Translate>Add provider</i18n.Translate>
- </ButtonPrimary>
- </footer>
- </PopupBox>
- );
-}
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 <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @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 <http://www.gnu.org/licenses/>
- */
-
-/**
- *
- * @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 <http://www.gnu.org/licenses/>
-*/
-
-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 (
- <div>
- <i18n.Translate>Loading...</i18n.Translate>
- </div>
- );
- }
- if (!status.info) {
- onBack();
- return <div />;
- }
- return (
- <ProviderView
- info={status.info}
- onSync={status.sync}
- onDelete={() => 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 (
- <PopupBox>
- <Error info={info} />
- <header>
- <h3>
- {info.name}{" "}
- <SmallLightText>{info.syncProviderBaseUrl}</SmallLightText>
- </h3>
- <PaymentStatus color={isPaid ? "rgb(28, 184, 65)" : "rgb(202, 60, 60)"}>
- {isPaid ? "Paid" : "Unpaid"}
- </PaymentStatus>
- </header>
- <section>
- <p>
- <b>Last backup:</b>{" "}
- {lb == null || lb.t_ms == "never"
- ? "never"
- : format(lb.t_ms, "dd MMM yyyy")}{" "}
- </p>
- <ButtonPrimary onClick={onSync}>
- <i18n.Translate>Back up</i18n.Translate>
- </ButtonPrimary>
- {info.terms && (
- <Fragment>
- <p>
- <b>Provider fee:</b> {info.terms && info.terms.annualFee} per year
- </p>
- </Fragment>
- )}
- <p>{descriptionByStatus(info.paymentStatus)}</p>
- <ButtonPrimary disabled onClick={onExtend}>
- <i18n.Translate>Extend</i18n.Translate>
- </ButtonPrimary>
-
- {info.paymentStatus.type === ProviderPaymentType.TermsChanged && (
- <div>
- <p>
- <i18n.Translate>
- terms has changed, extending the service will imply accepting
- the new terms of service
- </i18n.Translate>
- </p>
- <table>
- <thead>
- <tr>
- <td></td>
- <td>
- <i18n.Translate>old</i18n.Translate>
- </td>
- <td> -&gt;</td>
- <td>
- <i18n.Translate>new</i18n.Translate>
- </td>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>
- <i18n.Translate>fee</i18n.Translate>
- </td>
- <td>{info.paymentStatus.oldTerms.annualFee}</td>
- <td>-&gt;</td>
- <td>{info.paymentStatus.newTerms.annualFee}</td>
- </tr>
- <tr>
- <td>
- <i18n.Translate>storage</i18n.Translate>
- </td>
- <td>{info.paymentStatus.oldTerms.storageLimitInMegabytes}</td>
- <td>-&gt;</td>
- <td>{info.paymentStatus.newTerms.storageLimitInMegabytes}</td>
- </tr>
- </tbody>
- </table>
- </div>
- )}
- </section>
- <footer>
- <Button onClick={onBack}>
- <i18n.Translate> &lt; back</i18n.Translate>
- </Button>
- <div>
- <ButtonDestructive onClick={onDelete}>
- <i18n.Translate>remove provider</i18n.Translate>
- </ButtonDestructive>
- </div>
- </footer>
- </PopupBox>
- );
-}
-
-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 <ErrorMessage title={info.lastError.hint} />;
- }
- if (info.backupProblem) {
- switch (info.backupProblem.type) {
- case "backup-conflicting-device":
- return (
- <ErrorMessage
- title={
- <Fragment>
- <i18n.Translate>
- There is conflict with another backup from{" "}
- <b>{info.backupProblem.otherDeviceId}</b>
- </i18n.Translate>
- </Fragment>
- }
- />
- );
- case "backup-unreadable":
- return <ErrorMessage title="Backup is not readable" />;
- default:
- return (
- <ErrorMessage
- title={
- <Fragment>
- <i18n.Translate>
- Unknown backup problem: {JSON.stringify(info.backupProblem)}
- </i18n.Translate>
- </Fragment>
- }
- />
- );
- }
- }
- 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 (
- <Fragment>
- <b>Backup valid until:</b>{" "}
- {format(status.paidUntil.t_ms, "dd MMM yyyy")}
- </Fragment>
- );
- }
- 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 <http://www.gnu.org/licenses/>
*/
-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 (
<SettingsView
lang={lang}
changeLang={changeLang}
+ knownExchanges={
+ !exchangesHook || exchangesHook.hasError
+ ? []
+ : exchangesHook.response.exchanges
+ }
deviceName={name}
setDeviceName={update}
permissionsEnabled={permissionsEnabled}
@@ -53,36 +61,59 @@ export interface ViewProps {
togglePermissions: () => void;
developerMode: boolean;
toggleDeveloperMode: () => void;
+ knownExchanges: Array<ExchangeListItem>;
}
-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 (
- <PopupBox>
+ <Fragment>
<section>
+ <h2>
+ <i18n.Translate>Known exchanges</i18n.Translate>
+ </h2>
+ {!knownExchanges || !knownExchanges.length ? (
+ <div>No exchange yet!</div>
+ ) : (
+ <Fragment>
+ <table>
+ {knownExchanges.map((e, idx) => (
+ <tr key={idx}>
+ <td>{e.currency}</td>
+ <td>
+ <a href={e.exchangeBaseUrl}>{e.exchangeBaseUrl}</a>
+ </td>
+ </tr>
+ ))}
+ </table>
+ </Fragment>
+ )}
+ <div style={{ display: "flex", justifyContent: "space-between" }}>
+ <div />
+ <ButtonPrimary>Manage exchange</ButtonPrimary>
+ </div>
{/* <h2><i18n.Translate>Wallet</i18n.Translate></h2> */}
{/* <SelectList
value={lang}
@@ -124,14 +155,16 @@ export function SettingsView({
rel="noopener noreferrer"
style={{ color: "darkgreen", textDecoration: "none" }}
href={
+ // eslint-disable-next-line no-undef
chrome.extension
- ? chrome.extension.getURL(`/static/wallet.html#/settings`)
+ ? // eslint-disable-next-line no-undef
+ chrome.extension.getURL(`/static/wallet.html#/settings`)
: "#"
}
>
VIEW MORE SETTINGS
</a>
</footer>
- </PopupBox>
+ </Fragment>
);
}
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 (
- <PopupBox>
+ <Fragment>
<section>
<h1>Taler Action </h1>
{uriType === TalerUriType.TalerPay && (
@@ -109,7 +105,7 @@ export function TalerActionFound({ url, onDismiss }: Props) {
<div />
<ButtonPrimary onClick={() => onDismiss()}> Dismiss </ButtonPrimary>
</footer>
- </PopupBox>
+ </Fragment>
);
}