diff options
author | Sebastian <sebasjm@gmail.com> | 2021-11-16 13:59:53 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2021-11-16 14:01:38 -0300 |
commit | a994009d2f094c4d9c12da68dac3abb28bdef4b3 (patch) | |
tree | e403a58663f81889982635ffb324f9739e6976b3 | |
parent | c33ed919719845f518d6491ef37df6ae16820dd0 (diff) |
reserveCreated new design
37 files changed, 736 insertions, 558 deletions
diff --git a/packages/taler-util/src/payto.ts b/packages/taler-util/src/payto.ts index 504db533b..fc3380555 100644 --- a/packages/taler-util/src/payto.ts +++ b/packages/taler-util/src/payto.ts @@ -16,12 +16,31 @@ import { URLSearchParams } from "./url.js"; -interface PaytoUri { +export type PaytoUri = PaytoUriUnknown | PaytoUriIBAN | PaytoUriTalerBank; + +interface PaytoUriGeneric { targetType: string; targetPath: string; params: { [name: string]: string }; } +interface PaytoUriUnknown extends PaytoUriGeneric { + isKnown: false; +} + +interface PaytoUriIBAN extends PaytoUriGeneric { + isKnown: true; + targetType: 'iban', + iban: string; +} + +interface PaytoUriTalerBank extends PaytoUriGeneric { + isKnown: true; + targetType: 'x-taler-bank', + host: string; + account: string; +} + const paytoPfx = "payto://"; /** @@ -63,9 +82,33 @@ export function parsePaytoUri(s: string): PaytoUri | undefined { params[v] = k; }); + if (targetType === 'x-taler-bank') { + const parts = targetPath.split('/') + const host = parts[0] + const account = parts[1] + return { + targetPath, + targetType, + params, + isKnown: true, + host, account, + }; + + } + if (targetType === 'iban') { + return { + isKnown: true, + targetPath, + targetType, + params, + iban: targetPath + }; + + } return { targetPath, targetType, params, + isKnown: false }; } diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx b/packages/taler-wallet-webextension/src/NavigationBar.tsx index f206fa2dd..56704fb57 100644 --- a/packages/taler-wallet-webextension/src/NavigationBar.tsx +++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx @@ -25,10 +25,10 @@ * Imports. */ import { i18n } from "@gnu-taler/taler-util"; -import { ComponentChildren, JSX, h } from "preact"; +import { ComponentChildren, h, VNode } from "preact"; import Match from "preact-router/match"; -import { useDevContext } from "./context/devContext"; import { PopupNavigation } from "./components/styled"; +import { useDevContext } from "./context/devContext"; export enum Pages { welcome = "/welcome", @@ -59,7 +59,7 @@ interface TabProps { children?: ComponentChildren; } -function Tab(props: TabProps): JSX.Element { +function Tab(props: TabProps): VNode { let cssClass = ""; if (props.current?.startsWith(props.target)) { cssClass = "active"; diff --git a/packages/taler-wallet-webextension/src/components/Checkbox.tsx b/packages/taler-wallet-webextension/src/components/Checkbox.tsx index 276ac9ff0..59e84f4b0 100644 --- a/packages/taler-wallet-webextension/src/components/Checkbox.tsx +++ b/packages/taler-wallet-webextension/src/components/Checkbox.tsx @@ -14,8 +14,7 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { JSX } from "preact/jsx-runtime"; -import { h } from "preact"; +import { h, VNode } from "preact"; interface Props { enabled: boolean; @@ -30,7 +29,7 @@ export function Checkbox({ onToggle, label, description, -}: Props): JSX.Element { +}: Props): VNode { return ( <div> <input diff --git a/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx b/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx index 2fc8316f5..3b9519f39 100644 --- a/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx +++ b/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx @@ -14,9 +14,8 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { JSX } from "preact/jsx-runtime"; import { Outlined, StyledCheckboxLabel } from "./styled/index"; -import { h } from "preact"; +import { h, VNode } from "preact"; interface Props { enabled: boolean; @@ -47,7 +46,7 @@ export function CheckboxOutlined({ enabled, onToggle, label, -}: Props): JSX.Element { +}: Props): VNode { return ( <Outlined> <StyledCheckboxLabel onClick={onToggle}> diff --git a/packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx b/packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx index 952df15ae..b57075805 100644 --- a/packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx +++ b/packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx @@ -14,7 +14,7 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { JSX, h } from "preact"; +import { h, VNode } from "preact"; export function DebugCheckbox({ enabled, @@ -22,7 +22,7 @@ export function DebugCheckbox({ }: { enabled: boolean; onToggle: () => void; -}): JSX.Element { +}): VNode { return ( <div> <input diff --git a/packages/taler-wallet-webextension/src/components/Diagnostics.tsx b/packages/taler-wallet-webextension/src/components/Diagnostics.tsx index 0f8afd525..d368a10bf 100644 --- a/packages/taler-wallet-webextension/src/components/Diagnostics.tsx +++ b/packages/taler-wallet-webextension/src/components/Diagnostics.tsx @@ -15,8 +15,7 @@ */ import { WalletDiagnostics } from "@gnu-taler/taler-util"; -import { h } from "preact"; -import { JSX } from "preact/jsx-runtime"; +import { Fragment, h, VNode } from "preact"; import { PageLink } from "../renderHtml"; interface Props { @@ -24,51 +23,47 @@ interface Props { diagnostics: WalletDiagnostics | undefined; } -export function Diagnostics({ - timedOut, - diagnostics, -}: Props): JSX.Element | null { +export function Diagnostics({ timedOut, diagnostics }: Props): VNode { if (timedOut) { return <p>Diagnostics timed out. Could not talk to the wallet backend.</p>; } if (diagnostics) { if (diagnostics.errors.length === 0) { - return null; - } else { - return ( - <div - style={{ - borderLeft: "0.5em solid red", - paddingLeft: "1em", - paddingTop: "0.2em", - paddingBottom: "0.2em", - }} - > - <p>Problems detected:</p> - <ol> - {diagnostics.errors.map((errMsg) => ( - <li key={errMsg}>{errMsg}</li> - ))} - </ol> - {diagnostics.firefoxIdbProblem ? ( - <p> - Please check in your <code>about:config</code> settings that you - have IndexedDB enabled (check the preference name{" "} - <code>dom.indexedDB.enabled</code>). - </p> - ) : null} - {diagnostics.dbOutdated ? ( - <p> - Your wallet database is outdated. Currently automatic migration is - not supported. Please go{" "} - <PageLink pageName="/reset-required">here</PageLink> to reset the - wallet database. - </p> - ) : null} - </div> - ); + return <Fragment />; } + return ( + <div + style={{ + borderLeft: "0.5em solid red", + paddingLeft: "1em", + paddingTop: "0.2em", + paddingBottom: "0.2em", + }} + > + <p>Problems detected:</p> + <ol> + {diagnostics.errors.map((errMsg) => ( + <li key={errMsg}>{errMsg}</li> + ))} + </ol> + {diagnostics.firefoxIdbProblem ? ( + <p> + Please check in your <code>about:config</code> settings that you + have IndexedDB enabled (check the preference name{" "} + <code>dom.indexedDB.enabled</code>). + </p> + ) : null} + {diagnostics.dbOutdated ? ( + <p> + Your wallet database is outdated. Currently automatic migration is + not supported. Please go{" "} + <PageLink pageName="/reset-required">here</PageLink> to reset the + wallet database. + </p> + ) : null} + </div> + ); } return <p>Running diagnostics ...</p>; diff --git a/packages/taler-wallet-webextension/src/components/EditableText.tsx b/packages/taler-wallet-webextension/src/components/EditableText.tsx index 8b3e6d375..72bfbe809 100644 --- a/packages/taler-wallet-webextension/src/components/EditableText.tsx +++ b/packages/taler-wallet-webextension/src/components/EditableText.tsx @@ -14,9 +14,8 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { h } from "preact"; +import { h, VNode } from "preact"; import { useRef, useState } from "preact/hooks"; -import { JSX } from "preact/jsx-runtime"; interface Props { value: string; @@ -31,31 +30,35 @@ export function EditableText({ onChange, label, description, -}: Props): JSX.Element { +}: Props): VNode { const [editing, setEditing] = useState(false); const ref = useRef<HTMLInputElement>(null); let InputText; if (!editing) { - InputText = () => ( - <div style={{ display: "flex", justifyContent: "space-between" }}> - <p>{value}</p> - <button onClick={() => setEditing(true)}>edit</button> - </div> - ); + InputText = function InputToEdit(): VNode { + return ( + <div style={{ display: "flex", justifyContent: "space-between" }}> + <p>{value}</p> + <button onClick={() => setEditing(true)}>edit</button> + </div> + ); + }; } else { - InputText = () => ( - <div style={{ display: "flex", justifyContent: "space-between" }}> - <input value={value} ref={ref} type="text" id={`text-${name}`} /> - <button - onClick={() => { - if (ref.current) - onChange(ref.current.value).then((r) => setEditing(false)); - }} - > - confirm - </button> - </div> - ); + InputText = function InputEditing(): VNode { + return ( + <div style={{ display: "flex", justifyContent: "space-between" }}> + <input value={value} ref={ref} type="text" id={`text-${name}`} /> + <button + onClick={() => { + if (ref.current) + onChange(ref.current.value).then(() => setEditing(false)); + }} + > + confirm + </button> + </div> + ); + }; } return ( <div> diff --git a/packages/taler-wallet-webextension/src/components/ExchangeToS.tsx b/packages/taler-wallet-webextension/src/components/ExchangeToS.tsx index 6d2731cd8..a71108c50 100644 --- a/packages/taler-wallet-webextension/src/components/ExchangeToS.tsx +++ b/packages/taler-wallet-webextension/src/components/ExchangeToS.tsx @@ -13,12 +13,10 @@ You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { Fragment, VNode } from "preact"; +import { Fragment, VNode, h } from "preact"; import { useState } from "preact/hooks"; -import { JSXInternal } from "preact/src/jsx"; -import { h } from "preact"; -export function ExchangeXmlTos({ doc }: { doc: Document }) { +export function ExchangeXmlTos({ doc }: { doc: Document }): VNode { const termsNode = doc.querySelector("[ids=terms-of-service]"); if (!termsNode) { return ( @@ -70,7 +68,7 @@ function renderChild(child: Element): VNode { default: return ( <div style={{ color: "red", display: "hidden" }}> - unknown tag {child.nodeName} <a></a> + unknown tag {child.nodeName} </div> ); } @@ -81,10 +79,10 @@ function renderChild(child: Element): VNode { * @returns */ function AnchorWithOpenState( - props: JSXInternal.HTMLAttributes<HTMLAnchorElement>, -) { + props: h.JSX.HTMLAttributes<HTMLAnchorElement>, +): VNode { const [open, setOpen] = useState<boolean>(false); - function doClick(e: JSXInternal.TargetedMouseEvent<HTMLAnchorElement>) { + function doClick(e: h.JSX.TargetedMouseEvent<HTMLAnchorElement>): void { setOpen(!open); e.preventDefault(); } diff --git a/packages/taler-wallet-webextension/src/components/SelectList.tsx b/packages/taler-wallet-webextension/src/components/SelectList.tsx index f89ba19b2..78dd2feb4 100644 --- a/packages/taler-wallet-webextension/src/components/SelectList.tsx +++ b/packages/taler-wallet-webextension/src/components/SelectList.tsx @@ -14,9 +14,8 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { JSX } from "preact/jsx-runtime"; +import { Fragment, h, VNode } from "preact"; import { NiceSelect } from "./styled/index"; -import { h } from "preact"; interface Props { value?: string; @@ -34,13 +33,12 @@ export function SelectList({ name, value, list, - canBeNull, onChange, label, description, -}: Props): JSX.Element { +}: Props): VNode { return ( - <div> + <Fragment> <label htmlFor={`text-${name}`} style={{ marginLeft: "0.5em", fontWeight: "bold" }} @@ -84,6 +82,6 @@ export function SelectList({ {description} </span> )} - </div> + </Fragment> ); } diff --git a/packages/taler-wallet-webextension/src/components/Time.tsx b/packages/taler-wallet-webextension/src/components/Time.tsx new file mode 100644 index 000000000..452b08334 --- /dev/null +++ b/packages/taler-wallet-webextension/src/components/Time.tsx @@ -0,0 +1,41 @@ +/* + 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 { Timestamp } from "@gnu-taler/taler-util"; +import { formatISO, format } from "date-fns"; +import { h, VNode } from "preact"; + +export function Time({ + timestamp, + format: formatString, +}: { + timestamp: Timestamp | undefined; + format: string; +}): VNode { + return ( + <time + dateTime={ + !timestamp || timestamp.t_ms === "never" + ? undefined + : formatISO(timestamp.t_ms) + } + > + {!timestamp || timestamp.t_ms === "never" + ? "never" + : format(timestamp.t_ms, formatString)} + </time> + ); +} diff --git a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx index 1917d5627..99ca86385 100644 --- a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx +++ b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx @@ -20,8 +20,7 @@ import { Transaction, TransactionType, } from "@gnu-taler/taler-util"; -import { format, formatDistance } from "date-fns"; -import { h } from "preact"; +import { h, VNode } from "preact"; import imageBank from "../../static/img/ri-bank-line.svg"; import imageHandHeart from "../../static/img/ri-hand-heart-line.svg"; import imageRefresh from "../../static/img/ri-refresh-line.svg"; @@ -36,11 +35,12 @@ import { LargeText, LightText, } from "./styled/index"; +import { Time } from "./Time"; export function TransactionItem(props: { tx: Transaction; multiCurrency: boolean; -}): JSX.Element { +}): VNode { const tx = props.tx; switch (tx.type) { case TransactionType.Withdrawal: @@ -125,10 +125,7 @@ export function TransactionItem(props: { } } -function TransactionLayout(props: TransactionLayoutProps): JSX.Element { - const date = new Date(props.timestamp.t_ms); - const dateStr = format(date, "dd MMM, hh:mm"); - +function TransactionLayout(props: TransactionLayoutProps): VNode { return ( <HistoryRow href={Pages.transaction.replace(":tid", props.id)}> <img src={props.iconPath} /> @@ -146,7 +143,9 @@ function TransactionLayout(props: TransactionLayoutProps): JSX.Element { Waiting for confirmation </LightText> )} - <SmallLightText style={{ marginTop: 5 }}>{dateStr}</SmallLightText> + <SmallLightText style={{ marginTop: 5 }}> + <Time timestamp={props.timestamp} format="dd MMM, hh:mm" /> + </SmallLightText> </Column> <TransactionAmount pending={props.pending} @@ -177,7 +176,7 @@ interface TransactionAmountProps { multiCurrency: boolean; } -function TransactionAmount(props: TransactionAmountProps): JSX.Element { +function TransactionAmount(props: TransactionAmountProps): VNode { const [currency, amount] = props.amount.split(":"); let sign: string; switch (props.debitCreditIndicator) { diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx b/packages/taler-wallet-webextension/src/components/styled/index.tsx index 8b36dbd31..2db7c61f8 100644 --- a/packages/taler-wallet-webextension/src/components/styled/index.tsx +++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx @@ -85,7 +85,7 @@ export const WalletBox = styled.div<{ noPadding?: boolean }>` overflow: auto; table td { - padding: 5px 10px; + padding: 5px 5px; } table tr { border-bottom: 1px solid black; @@ -328,7 +328,8 @@ const ButtonVariant = styled(Button)` text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); `; -export const ButtonPrimary = styled(ButtonVariant)` +export const ButtonPrimary = styled(ButtonVariant)<{ small?: boolean }>` + font-size: ${({ small }) => (small ? "small" : "inherit")}; background-color: rgb(66, 184, 221); `; export const ButtonBoxPrimary = styled(ButtonBox)` @@ -501,29 +502,40 @@ export const Input = styled.div<{ invalid?: boolean }>` `; export const InputWithLabel = styled.div<{ invalid?: boolean }>` + /* display: flex; */ + & label { display: block; + font-weight: bold; + margin-left: 0.5em; padding: 5px; color: ${({ invalid }) => (!invalid ? "inherit" : "red")}; } - & > div { - position: relative; - display: flex; - top: 0px; - bottom: 0px; - - & > div { - position: absolute; - background-color: lightgray; - padding: 5px; - margin: 2px; - } - & > input { - flex: 1; - padding: 5px; - border-color: ${({ invalid }) => (!invalid ? "inherit" : "red")}; - } + & div { + line-height: 24px; + display: flex; + } + & div > span { + background-color: lightgray; + box-sizing: border-box; + border-bottom-left-radius: 0.25em; + border-top-left-radius: 0.25em; + height: 2em; + display: inline-block; + padding-left: 0.5em; + padding-right: 0.5em; + align-items: center; + display: flex; + } + & input { + border-width: 1px; + box-sizing: border-box; + height: 2em; + /* border-color: lightgray; */ + border-bottom-right-radius: 0.25em; + border-top-right-radius: 0.25em; + border-color: ${({ invalid }) => (!invalid ? "lightgray" : "red")}; } `; @@ -535,6 +547,7 @@ export const ErrorBox = styled.div` flex-direction: column; /* margin: 0.5em; */ padding: 1em; + margin: 1em; /* width: 100%; */ color: #721c24; background: #f8d7da; @@ -592,6 +605,8 @@ export const PopupNavigation = styled.div<{ devMode?: boolean }>` } `; +const image = `url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e")`; + export const NiceSelect = styled.div` & > select { -webkit-appearance: none; @@ -600,11 +615,18 @@ export const NiceSelect = styled.div` appearance: none; outline: 0; box-shadow: none; - background-image: none; + + background-image: ${image}; + background-position: right 0.5rem center; + background-repeat: no-repeat; + background-size: 1.5em 1.5em; + padding-right: 2.5rem; + background-color: white; - flex: 1; - padding: 0.5em 1em; + border-radius: 0.25rem; + font-size: 1em; + padding: 0.5em 3em 0.5em 1em; cursor: pointer; } @@ -613,27 +635,6 @@ export const NiceSelect = styled.div` /* width: 10em; */ overflow: hidden; border-radius: 0.25em; - - &::after { - content: "\u25BC"; - position: absolute; - top: 0; - right: 0; - padding: 0.5em 1em; - cursor: pointer; - pointer-events: none; - -webkit-transition: 0.25s all ease; - -o-transition: 0.25s all ease; - transition: 0.25s all ease; - } - - &:hover::after { - /* color: #f39c12; */ - } - - &::-ms-expand { - display: none; - } `; export const Outlined = styled.div` diff --git a/packages/taler-wallet-webextension/src/cta/Pay.tsx b/packages/taler-wallet-webextension/src/cta/Pay.tsx index 1023013d2..d5861c47c 100644 --- a/packages/taler-wallet-webextension/src/cta/Pay.tsx +++ b/packages/taler-wallet-webextension/src/cta/Pay.tsx @@ -36,7 +36,7 @@ import { PreparePayResult, PreparePayResultType, } from "@gnu-taler/taler-util"; -import { h, Fragment, JSX, VNode } from "preact"; +import { Fragment, h, VNode } from "preact"; import { useEffect, useState } from "preact/hooks"; import { LogoHeader } from "../components/LogoHeader"; import { Part } from "../components/Part"; @@ -100,7 +100,7 @@ const doPayment = async ( return res; }; -export function PayPage({ talerPayUri }: Props): JSX.Element { +export function PayPage({ talerPayUri }: Props): VNode { const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>( undefined, ); @@ -159,7 +159,7 @@ export function PayPage({ talerPayUri }: Props): JSX.Element { return <span>Loading payment information ...</span>; } - const onClick = async () => { + const onClick = async (): Promise<void> => { try { const res = await doPayment(payStatus); setPayResult(res); @@ -198,7 +198,7 @@ export function PaymentRequestView({ onClick, payErrMsg, balance, -}: PaymentRequestViewProps) { +}: PaymentRequestViewProps): VNode { let totalFees: AmountJson = Amounts.getZero(payStatus.amountRaw); const contractTerms: ContractTerms = payStatus.contractTerms; @@ -225,7 +225,7 @@ export function PaymentRequestView({ merchantName = <strong>(pub: {contractTerms.merchant_pub})</strong>; } - function Alternative() { + function Alternative(): VNode { const [showQR, setShowQR] = useState<boolean>(false); const privateUri = payStatus.status !== PreparePayResultType.AlreadyConfirmed @@ -246,7 +246,7 @@ export function PaymentRequestView({ ); } - function ButtonsSection() { + function ButtonsSection(): VNode { if (payResult) { if (payResult.type === ConfirmPayResultType.Pending) { return ( @@ -257,7 +257,7 @@ export function PaymentRequestView({ </section> ); } - return null; + return <Fragment />; } if (payErrMsg) { return ( @@ -392,7 +392,7 @@ export function PaymentRequestView({ ); } -function amountToString(text: AmountLike) { +function amountToString(text: AmountLike): string { const aj = Amounts.jsonifyAmount(text); const amount = Amounts.stringifyValue(aj, 2); return `${amount} ${aj.currency}`; diff --git a/packages/taler-wallet-webextension/src/cta/Refund.tsx b/packages/taler-wallet-webextension/src/cta/Refund.tsx index aa11dca6a..cecd1ac00 100644 --- a/packages/taler-wallet-webextension/src/cta/Refund.tsx +++ b/packages/taler-wallet-webextension/src/cta/Refund.tsx @@ -20,12 +20,11 @@ * @author Florian Dold */ -import * as wxApi from "../wxApi"; -import { AmountView } from "../renderHtml"; -import { ApplyRefundResponse, Amounts } from "@gnu-taler/taler-util"; +import { Amounts, ApplyRefundResponse } from "@gnu-taler/taler-util"; +import { h, VNode } from "preact"; import { useEffect, useState } from "preact/hooks"; -import { JSX } from "preact/jsx-runtime"; -import { h } from "preact"; +import { AmountView } from "../renderHtml"; +import * as wxApi from "../wxApi"; interface Props { talerRefundUri?: string; @@ -33,7 +32,7 @@ interface Props { export interface ViewProps { applyResult: ApplyRefundResponse; } -export function View({ applyResult }: ViewProps) { +export function View({ applyResult }: ViewProps): VNode { return ( <section class="main"> <h1>GNU Taler Wallet</h1> @@ -58,7 +57,7 @@ export function View({ applyResult }: ViewProps) { </section> ); } -export function RefundPage({ talerRefundUri }: Props): JSX.Element { +export function RefundPage({ talerRefundUri }: Props): VNode { const [applyResult, setApplyResult] = useState< ApplyRefundResponse | undefined >(undefined); @@ -71,9 +70,10 @@ export function RefundPage({ talerRefundUri }: Props): JSX.Element { const result = await wxApi.applyRefund(talerRefundUri); setApplyResult(result); } catch (e) { - console.error(e); - setErrMsg(e.message); - console.log("err message", e.message); + if (e instanceof Error) { + setErrMsg(e.message); + console.log("err message", e.message); + } } }; doFetch(); diff --git a/packages/taler-wallet-webextension/src/cta/Tip.tsx b/packages/taler-wallet-webextension/src/cta/Tip.tsx index 0a1c1238c..5a9ab720d 100644 --- a/packages/taler-wallet-webextension/src/cta/Tip.tsx +++ b/packages/taler-wallet-webextension/src/cta/Tip.tsx @@ -20,12 +20,11 @@ * @author Florian Dold <dold@taler.net> */ -import { useEffect, useState } from "preact/hooks"; import { PrepareTipResult } from "@gnu-taler/taler-util"; +import { h, VNode } from "preact"; +import { useEffect, useState } from "preact/hooks"; import { AmountView } from "../renderHtml"; import * as wxApi from "../wxApi"; -import { JSX } from "preact/jsx-runtime"; -import { h } from "preact"; interface Props { talerTipUri?: string; @@ -35,7 +34,11 @@ export interface ViewProps { onAccept: () => void; onIgnore: () => void; } -export function View({ prepareTipResult, onAccept, onIgnore }: ViewProps) { +export function View({ + prepareTipResult, + onAccept, + onIgnore, +}: ViewProps): VNode { return ( <section class="main"> <h1>GNU Taler Wallet</h1> @@ -64,7 +67,7 @@ export function View({ prepareTipResult, onAccept, onIgnore }: ViewProps) { ); } -export function TipPage({ talerTipUri }: Props): JSX.Element { +export function TipPage({ talerTipUri }: Props): VNode { const [updateCounter, setUpdateCounter] = useState<number>(0); const [prepareTipResult, setPrepareTipResult] = useState< PrepareTipResult | undefined diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx index 90df2a27e..54ae19c61 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx @@ -19,10 +19,7 @@ * @author Sebastian Javier Marchano (sebasjm) */ -import { amountFractionalBase, Amounts } from "@gnu-taler/taler-util"; -import { ExchangeRecord } from "@gnu-taler/taler-wallet-core"; -import { ExchangeWithdrawDetails } from "@gnu-taler/taler-wallet-core/src/operations/withdraw"; -import { getMaxListeners } from "process"; +import { amountFractionalBase } from "@gnu-taler/taler-util"; import { createExample } from "../test-utils"; import { View as TestedComponent } from "./Withdraw"; @@ -793,12 +790,6 @@ export const NewTerms = createExample(TestedComponent, { }, ], exchangeBaseUrl: "exchange.demo.taler.net", - details: { - content: "", - contentType: "", - currentEtag: "", - acceptedEtag: undefined, - }, withdrawalFee: { currency: "USD", fraction: 0, @@ -810,7 +801,9 @@ export const NewTerms = createExample(TestedComponent, { fraction: 10000000, }, - onSwitchExchange: async () => {}, + onSwitchExchange: async () => { + null; + }, terms: { value: { type: "xml", @@ -834,12 +827,6 @@ export const TermsReviewingPLAIN = createExample(TestedComponent, { }, ], exchangeBaseUrl: "exchange.demo.taler.net", - details: { - content: "", - contentType: "", - currentEtag: "", - acceptedEtag: undefined, - }, withdrawalFee: { currency: "USD", fraction: 0, @@ -851,7 +838,9 @@ export const TermsReviewingPLAIN = createExample(TestedComponent, { fraction: 10000000, }, - onSwitchExchange: async () => {}, + onSwitchExchange: async () => { + null; + }, terms: { value: { type: "plain", @@ -876,12 +865,6 @@ export const TermsReviewingHTML = createExample(TestedComponent, { }, ], exchangeBaseUrl: "exchange.demo.taler.net", - details: { - content: "", - contentType: "", - currentEtag: "", - acceptedEtag: undefined, - }, withdrawalFee: { currency: "USD", fraction: 0, @@ -893,7 +876,9 @@ export const TermsReviewingHTML = createExample(TestedComponent, { fraction: 10000000, }, - onSwitchExchange: async () => {}, + onSwitchExchange: async () => { + null; + }, terms: { value: { type: "html", @@ -935,12 +920,6 @@ export const TermsReviewingPDF = createExample(TestedComponent, { }, ], exchangeBaseUrl: "exchange.demo.taler.net", - details: { - content: "", - contentType: "", - currentEtag: "", - acceptedEtag: undefined, - }, withdrawalFee: { currency: "USD", fraction: 0, @@ -952,7 +931,9 @@ export const TermsReviewingPDF = createExample(TestedComponent, { fraction: 10000000, }, - onSwitchExchange: async () => {}, + onSwitchExchange: async () => { + null; + }, terms: { value: { type: "pdf", @@ -979,12 +960,6 @@ export const TermsReviewingXML = createExample(TestedComponent, { }, ], exchangeBaseUrl: "exchange.demo.taler.net", - details: { - content: "", - contentType: "", - currentEtag: "", - acceptedEtag: undefined, - }, withdrawalFee: { currency: "USD", fraction: 0, @@ -996,7 +971,9 @@ export const TermsReviewingXML = createExample(TestedComponent, { fraction: 10000000, }, - onSwitchExchange: async () => {}, + onSwitchExchange: async () => { + null; + }, terms: { value: { type: "xml", @@ -1021,12 +998,6 @@ export const NewTermsAccepted = createExample(TestedComponent, { }, ], exchangeBaseUrl: "exchange.demo.taler.net", - details: { - content: "", - contentType: "", - currentEtag: "", - acceptedEtag: undefined, - }, withdrawalFee: { currency: "USD", fraction: 0, @@ -1037,7 +1008,9 @@ export const NewTermsAccepted = createExample(TestedComponent, { value: 2, fraction: 10000000, }, - onSwitchExchange: async () => {}, + onSwitchExchange: async () => { + null; + }, terms: { value: { type: "xml", @@ -1062,12 +1035,6 @@ export const TermsShowAgainXML = createExample(TestedComponent, { }, ], exchangeBaseUrl: "exchange.demo.taler.net", - details: { - content: "", - contentType: "", - currentEtag: "", - acceptedEtag: undefined, - }, withdrawalFee: { currency: "USD", fraction: 0, @@ -1079,7 +1046,9 @@ export const TermsShowAgainXML = createExample(TestedComponent, { fraction: 10000000, }, - onSwitchExchange: async () => {}, + onSwitchExchange: async () => { + null; + }, terms: { value: { type: "xml", @@ -1105,12 +1074,6 @@ export const TermsChanged = createExample(TestedComponent, { }, ], exchangeBaseUrl: "exchange.demo.taler.net", - details: { - content: "", - contentType: "", - currentEtag: "", - acceptedEtag: undefined, - }, withdrawalFee: { currency: "USD", fraction: 0, @@ -1122,7 +1085,9 @@ export const TermsChanged = createExample(TestedComponent, { fraction: 10000000, }, - onSwitchExchange: async () => {}, + onSwitchExchange: async () => { + null; + }, terms: { value: { type: "xml", @@ -1146,12 +1111,6 @@ export const TermsNotFound = createExample(TestedComponent, { }, ], exchangeBaseUrl: "exchange.demo.taler.net", - details: { - content: "", - contentType: "", - currentEtag: "", - acceptedEtag: undefined, - }, withdrawalFee: { currency: "USD", fraction: 0, @@ -1163,7 +1122,9 @@ export const TermsNotFound = createExample(TestedComponent, { fraction: 10000000, }, - onSwitchExchange: async () => {}, + onSwitchExchange: async () => { + null; + }, terms: { status: "notfound", }, @@ -1183,12 +1144,6 @@ export const TermsAlreadyAccepted = createExample(TestedComponent, { }, ], exchangeBaseUrl: "exchange.demo.taler.net", - details: { - content: "", - contentType: "", - currentEtag: "", - acceptedEtag: undefined, - }, withdrawalFee: { currency: "USD", fraction: amountFractionalBase * 0.5, @@ -1200,7 +1155,9 @@ export const TermsAlreadyAccepted = createExample(TestedComponent, { fraction: 10000000, }, - onSwitchExchange: async () => {}, + onSwitchExchange: async () => { + null; + }, terms: { status: "accepted", }, @@ -1220,12 +1177,6 @@ export const WithoutFee = createExample(TestedComponent, { }, ], exchangeBaseUrl: "exchange.demo.taler.net", - details: { - content: "", - contentType: "", - currentEtag: "", - acceptedEtag: undefined, - }, withdrawalFee: { currency: "USD", fraction: 0, @@ -1237,7 +1188,9 @@ export const WithoutFee = createExample(TestedComponent, { fraction: 10000000, }, - onSwitchExchange: async () => {}, + onSwitchExchange: async () => { + null; + }, terms: { value: { type: "xml", diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx index 603dafcde..8258717bd 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx @@ -29,9 +29,8 @@ import { i18n, WithdrawUriInfoResponse, } from "@gnu-taler/taler-util"; -import { VNode, h } from "preact"; +import { VNode, h, Fragment } from "preact"; import { useState } from "preact/hooks"; -import { Fragment } from "preact/jsx-runtime"; import { CheckboxOutlined } from "../components/CheckboxOutlined"; import { ExchangeXmlTos } from "../components/ExchangeToS"; import { LogoHeader } from "../components/LogoHeader"; @@ -60,7 +59,6 @@ interface Props { } export interface ViewProps { - details: GetExchangeTosResult; withdrawalFee: AmountJson; exchangeBaseUrl: string; amount: AmountJson; @@ -112,14 +110,13 @@ interface TermsDocumentPdf { location: URL; } -function amountToString(text: AmountJson) { +function amountToString(text: AmountJson): string { const aj = Amounts.jsonifyAmount(text); const amount = Amounts.stringifyValue(aj); return `${amount} ${aj.currency}`; } export function View({ - details, withdrawalFee, exchangeBaseUrl, knownExchanges, @@ -132,7 +129,7 @@ export function View({ onAccept, reviewed, confirmed, -}: ViewProps) { +}: ViewProps): VNode { const needsReview = terms.status === "changed" || terms.status === "new"; const [switchingExchange, setSwitchingExchange] = useState< @@ -309,7 +306,7 @@ export function WithdrawPageWithParsedURI({ }: { uri: string; uriInfo: WithdrawUriInfoResponse; -}) { +}): VNode { const [customExchange, setCustomExchange] = useState<string | undefined>( undefined, ); @@ -407,7 +404,7 @@ export function WithdrawPageWithParsedURI({ return ( <View onWithdraw={onWithdraw} - details={details.tos} + // details={details.tos} amount={withdrawAmount} exchangeBaseUrl={exchange} withdrawalFee={details.info.withdrawFee} //FIXME diff --git a/packages/taler-wallet-webextension/src/cta/payback.tsx b/packages/taler-wallet-webextension/src/cta/payback.tsx index 60cb8c513..1c81b48a0 100644 --- a/packages/taler-wallet-webextension/src/cta/payback.tsx +++ b/packages/taler-wallet-webextension/src/cta/payback.tsx @@ -14,8 +14,7 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { JSX } from "preact/jsx-runtime"; -import { h } from "preact"; +import { h, VNode } from "preact"; /** * View and edit auditors. @@ -27,6 +26,6 @@ import { h } from "preact"; * Imports. */ -export function makePaybackPage(): JSX.Element { +export function makePaybackPage(): VNode { return <div>not implemented</div>; } diff --git a/packages/taler-wallet-webextension/src/cta/reset-required.tsx b/packages/taler-wallet-webextension/src/cta/reset-required.tsx index 3949318c4..75c4c1962 100644 --- a/packages/taler-wallet-webextension/src/cta/reset-required.tsx +++ b/packages/taler-wallet-webextension/src/cta/reset-required.tsx @@ -20,7 +20,7 @@ * @author Florian Dold */ -import { Component, JSX, h } from "preact"; +import { Component, h, VNode } from "preact"; import * as wxApi from "../wxApi"; interface State { @@ -45,7 +45,7 @@ class ResetNotification extends Component<any, State> { const res = await wxApi.checkUpgrade(); this.setState({ resetRequired: res.dbResetRequired }); } - render(): JSX.Element { + render(): VNode { if (this.state.resetRequired) { return ( <div> @@ -92,6 +92,6 @@ class ResetNotification extends Component<any, State> { /** * @deprecated to be removed */ -export function createResetRequiredPage(): JSX.Element { +export function createResetRequiredPage(): VNode { return <ResetNotification />; } diff --git a/packages/taler-wallet-webextension/src/cta/return-coins.tsx b/packages/taler-wallet-webextension/src/cta/return-coins.tsx index 548202cab..55f0297d4 100644 --- a/packages/taler-wallet-webextension/src/cta/return-coins.tsx +++ b/packages/taler-wallet-webextension/src/cta/return-coins.tsx @@ -14,8 +14,7 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { JSX } from "preact/jsx-runtime"; -import { h } from "preact"; +import { h, VNode } from "preact"; /** * Return coins to own bank account. * @@ -25,6 +24,6 @@ import { h } from "preact"; /** * Imports. */ -export function createReturnCoinsPage(): JSX.Element { +export function createReturnCoinsPage(): VNode { return <span>Not implemented yet.</span>; } diff --git a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx index 894c8a791..ae93f8a40 100644 --- a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx +++ b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx @@ -24,18 +24,18 @@ import { formatDuration, intervalToDuration, } from "date-fns"; -import { Fragment, JSX, VNode, h } from "preact"; +import { Fragment, h, VNode } from "preact"; import { BoldLight, ButtonPrimary, ButtonSuccess, Centered, - CenteredText, CenteredBoldText, + CenteredText, PopupBox, RowBorderGray, - SmallText, SmallLightText, + SmallText, } from "../components/styled"; import { useBackupStatus } from "../hooks/useBackupStatus"; import { Pages } from "../NavigationBar"; @@ -72,8 +72,9 @@ export function BackupView({ return ( <PopupBox> <section> - {providers.map((provider) => ( + {providers.map((provider, idx) => ( <BackupLayout + key={idx} status={provider.paymentStatus} timestamp={provider.lastSuccessfulBackupTimestamp} id={provider.syncProviderBaseUrl} @@ -117,7 +118,7 @@ interface TransactionLayoutProps { active: boolean; } -function BackupLayout(props: TransactionLayoutProps): JSX.Element { +function BackupLayout(props: TransactionLayoutProps): VNode { const date = !props.timestamp ? undefined : new Date(props.timestamp.t_ms); const dateStr = date?.toLocaleString([], { dateStyle: "medium", diff --git a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx index 2913f60e0..a23c81cd1 100644 --- a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx +++ b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx @@ -18,17 +18,14 @@ import { amountFractionalBase, Amounts, Balance, - BalancesResponse, i18n, } from "@gnu-taler/taler-util"; -import { JSX, h, Fragment } from "preact"; -import { ErrorMessage } from "../components/ErrorMessage"; +import { h, VNode } from "preact"; import { - PopupBox, - Centered, ButtonPrimary, ErrorBox, Middle, + PopupBox, } from "../components/styled/index"; import { BalancesHook, useBalances } from "../hooks/useBalances"; import { PageLink, renderAmount } from "../renderHtml"; @@ -37,7 +34,7 @@ export function BalancePage({ goToWalletManualWithdraw, }: { goToWalletManualWithdraw: () => void; -}) { +}): VNode { const balance = useBalances(); return ( <BalanceView @@ -53,11 +50,11 @@ export interface BalanceViewProps { goToWalletManualWithdraw: () => void; } -function formatPending(entry: Balance): JSX.Element { - let incoming: JSX.Element | undefined; - let payment: JSX.Element | undefined; +function formatPending(entry: Balance): VNode { + let incoming: VNode | undefined; + let payment: VNode | undefined; - const available = Amounts.parseOrThrow(entry.available); + // const available = Amounts.parseOrThrow(entry.available); const pendingIncoming = Amounts.parseOrThrow(entry.pendingIncoming); const pendingOutgoing = Amounts.parseOrThrow(entry.pendingOutgoing); @@ -105,8 +102,8 @@ export function BalanceView({ balance, Linker, goToWalletManualWithdraw, -}: BalanceViewProps) { - function Content() { +}: BalanceViewProps): VNode { + function Content(): VNode { if (!balance) { return <span />; } @@ -139,7 +136,7 @@ export function BalanceView({ return ( <section data-expanded data-centered> <table style={{ width: "100%" }}> - {balance.response.balances.map((entry) => { + {balance.response.balances.map((entry, idx) => { const av = Amounts.parseOrThrow(entry.available); // Create our number formatter. let formatter; @@ -168,7 +165,7 @@ export function BalanceView({ const fontSize = v.length < 8 ? "3em" : v.length < 13 ? "2em" : "1em"; return ( - <tr> + <tr key={idx}> <td style={{ height: 50, diff --git a/packages/taler-wallet-webextension/src/popup/Debug.tsx b/packages/taler-wallet-webextension/src/popup/Debug.tsx index 8722c1cf8..b0e8543fc 100644 --- a/packages/taler-wallet-webextension/src/popup/Debug.tsx +++ b/packages/taler-wallet-webextension/src/popup/Debug.tsx @@ -14,12 +14,12 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { JSX, h } from "preact"; +import { h, VNode } from "preact"; import { Diagnostics } from "../components/Diagnostics"; import { useDiagnostics } from "../hooks/useDiagnostics.js"; import * as wxApi from "../wxApi"; -export function DeveloperPage(props: any): JSX.Element { +export function DeveloperPage(): VNode { const [status, timedOut] = useDiagnostics(); return ( <div> @@ -36,6 +36,7 @@ export function DeveloperPage(props: any): JSX.Element { export function reload(): void { try { + // eslint-disable-next-line no-undef chrome.runtime.reload(); window.close(); } catch (e) { @@ -57,7 +58,9 @@ export async function confirmReset(): Promise<void> { export function openExtensionPage(page: string) { return () => { + // eslint-disable-next-line no-undef chrome.tabs.create({ + // eslint-disable-next-line no-undef url: chrome.extension.getURL(page), }); }; diff --git a/packages/taler-wallet-webextension/src/popup/History.tsx b/packages/taler-wallet-webextension/src/popup/History.tsx index 8fe6de16c..2228271dc 100644 --- a/packages/taler-wallet-webextension/src/popup/History.tsx +++ b/packages/taler-wallet-webextension/src/popup/History.tsx @@ -21,14 +21,14 @@ import { Transaction, TransactionsResponse, } from "@gnu-taler/taler-util"; -import { h, JSX } from "preact"; +import { 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 * as wxApi from "../wxApi"; -export function HistoryPage(props: any): JSX.Element { +export function HistoryPage(): VNode { const [transactions, setTransactions] = useState< TransactionsResponse | undefined >(undefined); diff --git a/packages/taler-wallet-webextension/src/renderHtml.tsx b/packages/taler-wallet-webextension/src/renderHtml.tsx index 9c2a794dd..15986d5d1 100644 --- a/packages/taler-wallet-webextension/src/renderHtml.tsx +++ b/packages/taler-wallet-webextension/src/renderHtml.tsx @@ -28,13 +28,13 @@ import { Amounts, amountFractionalBase, } from "@gnu-taler/taler-util"; -import { Component, ComponentChildren, JSX, h } from "preact"; +import { Component, ComponentChildren, h, VNode } from "preact"; /** * Render amount as HTML, which non-breaking space between * decimal value and currency. */ -export function renderAmount(amount: AmountJson | string): JSX.Element { +export function renderAmount(amount: AmountJson | string): VNode { let a; if (typeof amount === "string") { a = Amounts.parse(amount); @@ -56,13 +56,13 @@ export const AmountView = ({ amount, }: { amount: AmountJson | string; -}): JSX.Element => renderAmount(amount); +}): VNode => renderAmount(amount); /** * Abbreviate a string to a given length, and show the full * string on hover as a tooltip. */ -export function abbrev(s: string, n = 5): JSX.Element { +export function abbrev(s: string, n = 5): VNode { let sAbbrev = s; if (s.length > n) { sAbbrev = s.slice(0, n) + ".."; @@ -92,7 +92,7 @@ export class Collapsible extends Component<CollapsibleProps, CollapsibleState> { super(props); this.state = { collapsed: props.initiallyCollapsed }; } - render(): JSX.Element { + render(): VNode { const doOpen = (e: any): void => { this.setState({ collapsed: false }); e.preventDefault(); @@ -132,19 +132,19 @@ interface ExpanderTextProps { /** * Show a heading with a toggle to show/hide the expandable content. */ -export function ExpanderText({ text }: ExpanderTextProps): JSX.Element { +export function ExpanderText({ text }: ExpanderTextProps): VNode { return <span>{text}</span>; } export interface LoadingButtonProps - extends JSX.HTMLAttributes<HTMLButtonElement> { + extends h.JSX.HTMLAttributes<HTMLButtonElement> { isLoading: boolean; } export function ProgressButton({ isLoading, ...rest -}: LoadingButtonProps): JSX.Element { +}: LoadingButtonProps): VNode { return ( <button class="pure-button pure-button-primary" type="button" {...rest}> {isLoading ? ( @@ -160,7 +160,8 @@ export function ProgressButton({ export function PageLink(props: { pageName: string; children?: ComponentChildren; -}): JSX.Element { +}): VNode { + // eslint-disable-next-line no-undef const url = chrome.extension.getURL(`/static/wallet.html#/${props.pageName}`); return ( <a class="actionLink" href={url} target="_blank" rel="noopener noreferrer"> diff --git a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx index c3be0203e..f0ae38e0f 100644 --- a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx @@ -24,18 +24,17 @@ import { formatDuration, intervalToDuration, } from "date-fns"; -import { Fragment, JSX, VNode, h } from "preact"; +import { Fragment, h, VNode } from "preact"; import { BoldLight, ButtonPrimary, ButtonSuccess, Centered, - CenteredText, CenteredBoldText, - PopupBox, + CenteredText, RowBorderGray, - SmallText, SmallLightText, + SmallText, WalletBox, } from "../components/styled"; import { useBackupStatus } from "../hooks/useBackupStatus"; @@ -73,8 +72,9 @@ export function BackupView({ return ( <WalletBox> <section> - {providers.map((provider) => ( + {providers.map((provider, idx) => ( <BackupLayout + key={idx} status={provider.paymentStatus} timestamp={provider.lastSuccessfulBackupTimestamp} id={provider.syncProviderBaseUrl} @@ -118,7 +118,7 @@ interface TransactionLayoutProps { active: boolean; } -function BackupLayout(props: TransactionLayoutProps): JSX.Element { +function BackupLayout(props: TransactionLayoutProps): VNode { const date = !props.timestamp ? undefined : new Date(props.timestamp.t_ms); const dateStr = date?.toLocaleString([], { dateStyle: "medium", diff --git a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx index f3c08a3e8..9a2847670 100644 --- a/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/BalancePage.tsx @@ -21,7 +21,7 @@ import { BalancesResponse, i18n, } from "@gnu-taler/taler-util"; -import { JSX, h } from "preact"; +import { h, VNode } from "preact"; import { ButtonPrimary, Centered, WalletBox } from "../components/styled/index"; import { BalancesHook, useBalances } from "../hooks/useBalances"; import { PageLink, renderAmount } from "../renderHtml"; @@ -30,7 +30,7 @@ export function BalancePage({ goToWalletManualWithdraw, }: { goToWalletManualWithdraw: () => void; -}) { +}): VNode { const balance = useBalances(); return ( <BalanceView @@ -51,7 +51,7 @@ export function BalanceView({ balance, Linker, goToWalletManualWithdraw, -}: BalanceViewProps) { +}: BalanceViewProps): VNode { if (!balance) { return <span />; } @@ -85,13 +85,13 @@ export function BalanceView({ ); } -function formatPending(entry: Balance): JSX.Element { - let incoming: JSX.Element | undefined; - let payment: JSX.Element | undefined; +function formatPending(entry: Balance): VNode { + let incoming: VNode | undefined; + let payment: VNode | undefined; - const available = Amounts.parseOrThrow(entry.available); + // const available = Amounts.parseOrThrow(entry.available); const pendingIncoming = Amounts.parseOrThrow(entry.pendingIncoming); - const pendingOutgoing = Amounts.parseOrThrow(entry.pendingOutgoing); + // const pendingOutgoing = Amounts.parseOrThrow(entry.pendingOutgoing); if (!Amounts.isZero(pendingIncoming)) { incoming = ( @@ -128,7 +128,7 @@ function ShowBalances({ }: { wallet: BalancesResponse; onWithdraw: () => void; -}) { +}): VNode { return ( <WalletBox> <section> diff --git a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx index 6eab8dc3a..300e9cd57 100644 --- a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.stories.tsx @@ -28,26 +28,27 @@ export default { argTypes: {}, }; -export const InitialState = createExample(TestedComponent, {}); +// , +const exchangeList = { + "http://exchange.taler:8081": "COL", + "http://exchange.tal": "EUR", +}; -export const WithExchangeFilled = createExample(TestedComponent, { - currency: "COL", - initialExchange: "http://exchange.taler:8081", +export const InitialState = createExample(TestedComponent, { + exchangeList, }); -export const WithExchangeAndAmountFilled = createExample(TestedComponent, { - currency: "COL", - initialExchange: "http://exchange.taler:8081", +export const WithAmountInitialized = createExample(TestedComponent, { initialAmount: "10", + exchangeList, }); export const WithExchangeError = createExample(TestedComponent, { - initialExchange: "http://exchange.tal", error: "The exchange url seems invalid", + exchangeList, }); export const WithAmountError = createExample(TestedComponent, { - currency: "COL", - initialExchange: "http://exchange.taler:8081", initialAmount: "e", + exchangeList, }); diff --git a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx index b48dcbaf2..140ac2d40 100644 --- a/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx +++ b/packages/taler-wallet-webextension/src/wallet/CreateManualWithdraw.tsx @@ -20,9 +20,10 @@ */ import { AmountJson, Amounts } from "@gnu-taler/taler-util"; -import { VNode, h } from "preact"; -import { useEffect, useRef, useState } from "preact/hooks"; +import { h, VNode } from "preact"; +import { useState } from "preact/hooks"; import { ErrorMessage } from "../components/ErrorMessage"; +import { SelectList } from "../components/SelectList"; import { ButtonPrimary, Input, @@ -33,32 +34,56 @@ import { export interface Props { error: string | undefined; - currency: string | undefined; - initialExchange?: string; initialAmount?: string; - onExchangeChange: (exchange: string) => void; + exchangeList: Record<string, string>; onCreate: (exchangeBaseUrl: string, amount: AmountJson) => Promise<void>; } export function CreateManualWithdraw({ - onExchangeChange, - initialExchange, initialAmount, + exchangeList, error, - currency, onCreate, }: Props): VNode { + const exchangeSelectList = Object.keys(exchangeList); + const currencySelectList = Object.values(exchangeList); + const exchangeMap = exchangeSelectList.reduce( + (p, c) => ({ ...p, [c]: `${c} (${exchangeList[c]})` }), + {} as Record<string, string>, + ); + const currencyMap = currencySelectList.reduce( + (p, c) => ({ ...p, [c]: c }), + {} as Record<string, string>, + ); + + const initialExchange = + exchangeSelectList.length > 0 ? exchangeSelectList[0] : ""; + const [exchange, setExchange] = useState(initialExchange || ""); + const [currency, setCurrency] = useState(exchangeList[initialExchange] ?? ""); + const [amount, setAmount] = useState(initialAmount || ""); const parsedAmount = Amounts.parse(`${currency}:${amount}`); - let timeout = useRef<number | undefined>(undefined); - useEffect(() => { - if (timeout) window.clearTimeout(timeout.current); - timeout.current = window.setTimeout(async () => { - onExchangeChange(exchange); - }, 1000); - }, [exchange]); + function changeExchange(exchange: string): void { + setExchange(exchange); + setCurrency(exchangeList[exchange]); + } + + function changeCurrency(currency: string): void { + setCurrency(currency); + const found = Object.entries(exchangeList).find((e) => e[1] === currency); + + if (found) { + setExchange(found[0]); + } else { + setExchange(""); + } + } + + if (!initialExchange) { + return <div>There is no known exchange where to withdraw, add one</div>; + } return ( <WalletBox> @@ -73,26 +98,38 @@ export function CreateManualWithdraw({ withdraw the coins </LightText> <p> - <Input invalid={!!exchange && !currency}> - <label>Exchange</label> - <input - type="text" - placeholder="https://" + <Input> + <SelectList + label="Currency" + list={currencyMap} + name="currency" + value={currency} + onChange={changeCurrency} + /> + </Input> + <Input> + <SelectList + label="Exchange" + list={exchangeMap} + name="currency" value={exchange} - onChange={(e) => setExchange(e.currentTarget.value)} + onChange={changeExchange} /> - <small>http://exchange.taler:8081</small> </Input> + {/* <p style={{ display: "flex", justifyContent: "right" }}> + <a href="" style={{ marginLeft: "auto" }}> + Add new exchange + </a> + </p> */} {currency && ( <InputWithLabel invalid={!!amount && !parsedAmount}> <label>Amount</label> <div> - <div>{currency}</div> + <span>{currency}</span> <input type="number" - style={{ paddingLeft: `${currency.length}em` }} value={amount} - onChange={(e) => setAmount(e.currentTarget.value)} + onInput={(e) => setAmount(e.currentTarget.value)} /> </div> </InputWithLabel> @@ -105,7 +142,7 @@ export function CreateManualWithdraw({ disabled={!parsedAmount || !exchange} onClick={() => onCreate(exchange, parsedAmount!)} > - Create + Start withdrawal </ButtonPrimary> </footer> </WalletBox> diff --git a/packages/taler-wallet-webextension/src/wallet/History.tsx b/packages/taler-wallet-webextension/src/wallet/History.tsx index aabe50a29..6b1a21852 100644 --- a/packages/taler-wallet-webextension/src/wallet/History.tsx +++ b/packages/taler-wallet-webextension/src/wallet/History.tsx @@ -20,15 +20,15 @@ import { Transaction, TransactionsResponse, } from "@gnu-taler/taler-util"; -import { format } from "date-fns"; -import { Fragment, h, JSX } from "preact"; +import { Fragment, h, VNode } from "preact"; import { useEffect, useState } from "preact/hooks"; import { DateSeparator, WalletBox } from "../components/styled"; +import { Time } from "../components/Time"; import { TransactionItem } from "../components/TransactionItem"; import { useBalances } from "../hooks/useBalances"; import * as wxApi from "../wxApi"; -export function HistoryPage(props: any): JSX.Element { +export function HistoryPage(): VNode { const [transactions, setTransactions] = useState< TransactionsResponse | undefined >(undefined); @@ -57,24 +57,30 @@ export function HistoryPage(props: any): JSX.Element { ); } -function amountToString(c: AmountString) { +function amountToString(c: AmountString): string { const idx = c.indexOf(":"); return `${c.substring(idx + 1)} ${c.substring(0, idx)}`; } +const term = 1000 * 60 * 60 * 24; +function normalizeToDay(x: number): number { + return Math.round(x / term) * term; +} + export function HistoryView({ list, balances, }: { list: Transaction[]; balances: Balance[]; -}) { - const byDate = list.reduce(function (rv, x) { +}): VNode { + const byDate = list.reduce((rv, x) => { const theDate = - x.timestamp.t_ms === "never" - ? "never" - : format(x.timestamp.t_ms, "dd MMMM yyyy"); - (rv[theDate] = rv[theDate] || []).push(x); + x.timestamp.t_ms === "never" ? 0 : normalizeToDay(x.timestamp.t_ms); + if (theDate) { + (rv[theDate] = rv[theDate] || []).push(x); + } + return rv; }, {} as { [x: string]: Transaction[] }); @@ -93,8 +99,8 @@ export function HistoryView({ <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> @@ -105,7 +111,12 @@ export function HistoryView({ {Object.keys(byDate).map((d, i) => { return ( <Fragment key={i}> - <DateSeparator>{d}</DateSeparator> + <DateSeparator> + <Time + timestamp={{ t_ms: Number.parseInt(d, 10) }} + format="dd MMMM yyyy" + /> + </DateSeparator> {byDate[d].map((tx, i) => ( <TransactionItem key={i} diff --git a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx index 102978f9e..1af4e8d8d 100644 --- a/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ManualWithdrawPage.tsx @@ -26,44 +26,31 @@ import { import { ReserveCreated } from "./ReserveCreated.js"; import { route } from "preact-router"; import { Pages } from "../NavigationBar.js"; +import { useAsyncAsHook } from "../hooks/useAsyncAsHook"; -interface Props {} - -export function ManualWithdrawPage({}: Props): VNode { +export function ManualWithdrawPage(): VNode { const [success, setSuccess] = useState< - AcceptManualWithdrawalResult | undefined + | { + response: AcceptManualWithdrawalResult; + exchangeBaseUrl: string; + amount: AmountJson; + } + | undefined >(undefined); - const [currency, setCurrency] = useState<string | undefined>(undefined); const [error, setError] = useState<string | undefined>(undefined); - async function onExchangeChange(exchange: string | undefined): Promise<void> { - if (!exchange) return; - try { - const r = await fetch(`${exchange}/keys`); - const j = await r.json(); - if (j.currency) { - await wxApi.addExchange({ - exchangeBaseUrl: `${exchange}/`, - forceUpdate: true, - }); - setCurrency(j.currency); - } - } catch (e) { - setError("The exchange url seems invalid"); - setCurrency(undefined); - } - } + const knownExchangesHook = useAsyncAsHook(() => wxApi.listExchanges()); async function doCreate( exchangeBaseUrl: string, amount: AmountJson, ): Promise<void> { try { - const resp = await wxApi.acceptManualWithdrawal( + const response = await wxApi.acceptManualWithdrawal( exchangeBaseUrl, Amounts.stringify(amount), ); - setSuccess(resp); + setSuccess({ exchangeBaseUrl, response, amount }); } catch (e) { if (e instanceof Error) { setError(e.message); @@ -77,8 +64,10 @@ export function ManualWithdrawPage({}: Props): VNode { if (success) { return ( <ReserveCreated - reservePub={success.reservePub} - paytos={success.exchangePaytoUris} + reservePub={success.response.reservePub} + payto={success.response.exchangePaytoUris[0]} + exchangeBaseUrl={success.exchangeBaseUrl} + amount={success.amount} onBack={() => { route(Pages.balance); }} @@ -86,12 +75,22 @@ export function ManualWithdrawPage({}: Props): VNode { ); } + if (!knownExchangesHook || knownExchangesHook.hasError) { + return <div>No Known exchanges</div>; + } + const exchangeList = knownExchangesHook.response.exchanges.reduce( + (p, c) => ({ + ...p, + [c.exchangeBaseUrl]: c.currency, + }), + {} as Record<string, string>, + ); + return ( <CreateManualWithdraw error={error} - currency={currency} + exchangeList={exchangeList} onCreate={doCreate} - onExchangeChange={onExchangeChange} /> ); } diff --git a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx index bd64b0760..1c14c6e0a 100644 --- a/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ProviderDetailPage.tsx @@ -14,23 +14,23 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { i18n, Timestamp } from "@gnu-taler/taler-util"; +import { i18n } 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 { Fragment, h, VNode } from "preact"; import { ErrorMessage } from "../components/ErrorMessage"; import { Button, ButtonDestructive, ButtonPrimary, PaymentStatus, - WalletBox, SmallLightText, + WalletBox, } from "../components/styled"; +import { Time } from "../components/Time"; import { useProviderStatus } from "../hooks/useProviderStatus"; interface Props { @@ -97,10 +97,7 @@ export function ProviderView({ </header> <section> <p> - <b>Last backup:</b>{" "} - {lb == null || lb.t_ms == "never" - ? "never" - : format(lb.t_ms, "dd MMM yyyy")}{" "} + <b>Last backup:</b> <Time timestamp={lb} format="dd MMMM yyyy" /> </p> <ButtonPrimary onClick={onSync}> <i18n.Translate>Back up</i18n.Translate> @@ -128,7 +125,7 @@ export function ProviderView({ <table> <thead> <tr> - <td></td> + <td> </td> <td> <i18n.Translate>old</i18n.Translate> </td> @@ -174,32 +171,32 @@ export function ProviderView({ ); } -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 daysSince(d?: Timestamp): string { +// 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 }) { +function Error({ info }: { info: ProviderInfo }): VNode { if (info.lastError) { return <ErrorMessage title={info.lastError.hint} />; } @@ -234,45 +231,45 @@ function Error({ info }: { info: ProviderInfo }) { ); } } - return null; + return <Fragment />; } -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 colorByStatus(status: ProviderPaymentType): string { +// 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) { +function descriptionByStatus(status: ProviderPaymentStatus): VNode { 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> - ); + return <span>{i18n.str`service paid`}</span>; } + return ( + <Fragment> + <b>Backup valid until:</b>{" "} + <Time timestamp={status.paidUntil} format="dd MMM yyyy" /> + </Fragment> + ); + case ProviderPaymentType.Unpaid: case ProviderPaymentType.InsufficientBalance: case ProviderPaymentType.Pending: - return ""; + return <span />; } } diff --git a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx index c552b19ba..8d7b65b3c 100644 --- a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.stories.tsx @@ -28,10 +28,26 @@ export default { argTypes: {}, }; -export const InitialState = createExample(TestedComponent, { - reservePub: "ASLKDJQWLKEJASLKDJSADLKASJDLKSADJ", - paytos: [ +export const TalerBank = createExample(TestedComponent, { + reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG", + payto: "payto://x-taler-bank/bank.taler:5882/exchangeminator?amount=COL%3A1&message=Taler+Withdrawal+A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG", - "payto://x-taler-bank/international-bank.com/myaccount?amount=COL%3A1&message=Taler+Withdrawal+TYQTE7VA4M9GZQ4TR06YBNGA05AJGMFNSK4Q62NXR2FKNDB1J4EX", - ], + amount: { + currency: "USD", + value: 10, + fraction: 0, + }, + exchangeBaseUrl: "https://exchange.demo.taler.net", +}); + +export const IBAN = createExample(TestedComponent, { + reservePub: "A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG", + payto: + "payto://iban/ASDQWEASDZXCASDQWE?amount=COL%3A1&message=Taler+Withdrawal+A05AJGMFNSK4Q62NXR2FKNDB1J4EXTYQTE7VA4M9GZQ4TR06YBNG", + amount: { + currency: "USD", + value: 10, + fraction: 0, + }, + exchangeBaseUrl: "https://exchange.demo.taler.net", }); diff --git a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx index 9008e9751..a72026ab8 100644 --- a/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx +++ b/packages/taler-wallet-webextension/src/wallet/ReserveCreated.tsx @@ -1,66 +1,155 @@ -import { h, Fragment, VNode } from "preact"; -import { useState } from "preact/hooks"; +import { + AmountJson, + Amounts, + parsePaytoUri, + PaytoUri, +} from "@gnu-taler/taler-util"; +import { Fragment, h, VNode } from "preact"; +import { useEffect, useState } from "preact/hooks"; import { QR } from "../components/QR"; -import { ButtonBox, FontIcon, WalletBox } from "../components/styled"; +import { + ButtonDestructive, + ButtonPrimary, + WalletBox, + WarningBox, +} from "../components/styled"; export interface Props { reservePub: string; - paytos: string[]; + payto: string; + exchangeBaseUrl: string; + amount: AmountJson; onBack: () => void; } -export function ReserveCreated({ reservePub, paytos, onBack }: Props): VNode { - const [opened, setOpened] = useState(-1); +interface BankDetailsProps { + payto: PaytoUri; + exchangeBaseUrl: string; + subject: string; + amount: string; +} + +function Row({ + name, + value, + literal, +}: { + name: string; + value: string; + literal?: boolean; +}): VNode { + const [copied, setCopied] = useState(false); + function copyText(): void { + navigator.clipboard.writeText(value); + setCopied(true); + } + useEffect(() => { + setTimeout(() => { + setCopied(false); + }, 1000); + }, [copied]); + return ( + <tr> + <td> + {!copied ? ( + <ButtonPrimary small onClick={copyText}> + Copy + </ButtonPrimary> + ) : ( + <ButtonPrimary small disabled> + Copied + </ButtonPrimary> + )} + </td> + <td> + <b>{name}</b> + </td> + {literal ? ( + <td> + <pre style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}> + {value} + </pre> + </td> + ) : ( + <td>{value}</td> + )} + </tr> + ); +} + +function BankDetailsByPaytoType({ + payto, + subject, + exchangeBaseUrl, + amount, +}: BankDetailsProps): VNode { + const firstPart = !payto.isKnown ? ( + <Fragment> + <Row name="Account" value={payto.targetPath} /> + <Row name="Exchange" value={exchangeBaseUrl} /> + </Fragment> + ) : payto.targetType === "x-taler-bank" ? ( + <Fragment> + <Row name="Bank host" value={payto.host} /> + <Row name="Bank account" value={payto.account} /> + <Row name="Exchange" value={exchangeBaseUrl} /> + </Fragment> + ) : payto.targetType === "iban" ? ( + <Fragment> + <Row name="IBAN" value={payto.iban} /> + <Row name="Exchange" value={exchangeBaseUrl} /> + </Fragment> + ) : undefined; + return ( + <table> + {firstPart} + <Row name="Amount" value={amount} /> + <Row name="Subject" value={subject} literal /> + </table> + ); +} +export function ReserveCreated({ + reservePub, + payto, + onBack, + exchangeBaseUrl, + amount, +}: Props): VNode { + const paytoURI = parsePaytoUri(payto); + // const url = new URL(paytoURI?.targetPath); + if (!paytoURI) { + return <div>could not parse payto uri from exchange {payto}</div>; + } return ( <WalletBox> <section> - <h2>Reserve created!</h2> - <p> - Now you need to send money to the exchange to one of the following - accounts - </p> + <h1>Bank transfer details</h1> <p> - To complete the setup of the reserve, you must now initiate a wire - transfer using the given wire transfer subject and crediting the - specified amount to the indicated account of the exchange. + Please wire <b>{Amounts.stringify(amount)}</b> to: </p> + <BankDetailsByPaytoType + amount={Amounts.stringify(amount)} + exchangeBaseUrl={exchangeBaseUrl} + payto={paytoURI} + subject={reservePub} + /> </section> <section> - <ul> - {paytos.map((href, idx) => { - const url = new URL(href); - return ( - <li key={idx}> - <p> - <a - href="" - onClick={(e) => { - setOpened((o) => (o === idx ? -1 : idx)); - e.preventDefault(); - }} - > - {url.pathname} - </a> - {opened === idx && ( - <Fragment> - <p> - If your system supports RFC 8905, you can do this by - opening <a href={href}>this URI</a> or scan the QR with - your wallet - </p> - <QR text={href} /> - </Fragment> - )} - </p> - </li> - ); - })} - </ul> + <p> + <WarningBox> + Make sure to use the correct subject, otherwise the money will not + arrive in this wallet. + </WarningBox> + </p> + <p> + Alternative, you can also scan this QR code or open{" "} + <a href={payto}>this link</a> if you have a banking app installed that + supports RFC 8905 + </p> + <QR text={payto} /> </section> <footer> - <ButtonBox onClick={onBack}> - <FontIcon>←</FontIcon> - </ButtonBox> <div /> + <ButtonDestructive onClick={onBack}>Cancel withdraw</ButtonDestructive> </footer> </WalletBox> ); diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx index 7de6982e7..1472efb40 100644 --- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx @@ -21,28 +21,27 @@ import { Transaction, TransactionType, } from "@gnu-taler/taler-util"; -import { format } from "date-fns"; -import { JSX, VNode, h } from "preact"; +import { h, VNode } from "preact"; import { route } from "preact-router"; import { useEffect, useState } from "preact/hooks"; import emptyImg from "../../static/img/empty.png"; import { ErrorMessage } from "../components/ErrorMessage"; import { Part } from "../components/Part"; import { - ButtonBox, - ButtonBoxDestructive, + Button, + ButtonDestructive, ButtonPrimary, - FontIcon, ListOfProducts, RowBorderGray, SmallLightText, WalletBox, WarningBox, } from "../components/styled"; +import { Time } from "../components/Time"; import { Pages } from "../NavigationBar"; import * as wxApi from "../wxApi"; -export function TransactionPage({ tid }: { tid: string }): JSX.Element { +export function TransactionPage({ tid }: { tid: string }): VNode { const [transaction, setTransaction] = useState<Transaction | undefined>( undefined, ); @@ -70,8 +69,8 @@ export function TransactionPage({ tid }: { tid: string }): JSX.Element { return ( <TransactionView transaction={transaction} - onDelete={() => wxApi.deleteTransaction(tid).then((_) => history.go(-1))} - onRetry={() => wxApi.retryTransaction(tid).then((_) => history.go(-1))} + onDelete={() => wxApi.deleteTransaction(tid).then(() => history.go(-1))} + onRetry={() => wxApi.retryTransaction(tid).then(() => history.go(-1))} onBack={() => { route(Pages.history); }} @@ -91,42 +90,42 @@ export function TransactionView({ onDelete, onRetry, onBack, -}: WalletTransactionProps) { - function TransactionTemplate({ children }: { children: VNode[] }) { +}: WalletTransactionProps): VNode { + function TransactionTemplate({ children }: { children: VNode[] }): VNode { return ( <WalletBox> <section style={{ padding: 8, textAlign: "center" }}> <ErrorMessage title={transaction?.error?.hint} /> {transaction.pending && ( - <WarningBox>This transaction is not completed</WarningBox> + <WarningBox> + This transaction is not completed + <a href="">more info...</a> + </WarningBox> )} </section> <section> <div style={{ textAlign: "center" }}>{children}</div> </section> <footer> - <ButtonBox onClick={onBack}> - <i18n.Translate> - {" "} - <FontIcon>←</FontIcon>{" "} - </i18n.Translate> - </ButtonBox> + <Button onClick={onBack}> + <i18n.Translate> < Back </i18n.Translate> + </Button> <div> {transaction?.error ? ( <ButtonPrimary onClick={onRetry}> <i18n.Translate>retry</i18n.Translate> </ButtonPrimary> ) : null} - <ButtonBoxDestructive onClick={onDelete}> - <i18n.Translate>🗑</i18n.Translate> - </ButtonBoxDestructive> + <ButtonDestructive onClick={onDelete}> + <i18n.Translate> Forget </i18n.Translate> + </ButtonDestructive> </div> </footer> </WalletBox> ); } - function amountToString(text: AmountLike) { + function amountToString(text: AmountLike): string { const aj = Amounts.jsonifyAmount(text); const amount = Amounts.stringifyValue(aj); return `${amount} ${aj.currency}`; @@ -140,23 +139,26 @@ export function TransactionView({ return ( <TransactionTemplate> <h2>Withdrawal</h2> - <div> - {transaction.timestamp.t_ms === "never" - ? "never" - : format(transaction.timestamp.t_ms, "dd MMMM yyyy, HH:mm")} - </div> + <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" /> <br /> <Part + big title="Total withdrawn" text={amountToString(transaction.amountEffective)} kind="positive" /> <Part + big title="Chosen amount" text={amountToString(transaction.amountRaw)} kind="neutral" /> - <Part title="Exchange fee" text={amountToString(fee)} kind="negative" /> + <Part + big + title="Exchange fee" + text={amountToString(fee)} + kind="negative" + /> <Part title="Exchange" text={new URL(transaction.exchangeBaseUrl).hostname} @@ -166,7 +168,9 @@ export function TransactionView({ ); } - const showLargePic = () => {}; + const showLargePic = (): void => { + return; + }; if (transaction.type === TransactionType.Payment) { const fee = Amounts.sub( @@ -177,11 +181,7 @@ export function TransactionView({ return ( <TransactionTemplate> <h2>Payment </h2> - <div> - {transaction.timestamp.t_ms === "never" - ? "never" - : format(transaction.timestamp.t_ms, "dd MMMM yyyy, HH:mm")} - </div> + <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" /> <br /> <Part big @@ -241,11 +241,7 @@ export function TransactionView({ return ( <TransactionTemplate> <h2>Deposit </h2> - <div> - {transaction.timestamp.t_ms === "never" - ? "never" - : format(transaction.timestamp.t_ms, "dd MMMM yyyy, HH:mm")} - </div> + <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" /> <br /> <Part big @@ -272,11 +268,7 @@ export function TransactionView({ return ( <TransactionTemplate> <h2>Refresh</h2> - <div> - {transaction.timestamp.t_ms === "never" - ? "never" - : format(transaction.timestamp.t_ms, "dd MMMM yyyy, HH:mm")} - </div> + <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" /> <br /> <Part big @@ -303,11 +295,7 @@ export function TransactionView({ return ( <TransactionTemplate> <h2>Tip</h2> - <div> - {transaction.timestamp.t_ms === "never" - ? "never" - : format(transaction.timestamp.t_ms, "dd MMMM yyyy, HH:mm")} - </div> + <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" /> <br /> <Part big @@ -334,11 +322,7 @@ export function TransactionView({ return ( <TransactionTemplate> <h2>Refund</h2> - <div> - {transaction.timestamp.t_ms === "never" - ? "never" - : format(transaction.timestamp.t_ms, "dd MMMM yyyy, HH:mm")} - </div> + <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" /> <br /> <Part big @@ -391,5 +375,5 @@ export function TransactionView({ ); } - return <div></div>; + return <div />; } diff --git a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx index 0b8e5c609..a6dd040e4 100644 --- a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx +++ b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx @@ -20,16 +20,15 @@ * @author Florian Dold */ -import { JSX } from "preact/jsx-runtime"; import { Checkbox } from "../components/Checkbox"; import { useExtendedPermissions } from "../hooks/useExtendedPermissions"; import { Diagnostics } from "../components/Diagnostics"; import { WalletBox } from "../components/styled"; import { useDiagnostics } from "../hooks/useDiagnostics"; import { WalletDiagnostics } from "@gnu-taler/taler-util"; -import { h } from "preact"; +import { h, VNode } from "preact"; -export function WelcomePage() { +export function WelcomePage(): VNode { const [permissionsEnabled, togglePermissions] = useExtendedPermissions(); const [diagnostics, timedOut] = useDiagnostics(); return ( @@ -53,7 +52,7 @@ export function View({ togglePermissions, diagnostics, timedOut, -}: ViewProps): JSX.Element { +}: ViewProps): VNode { return ( <WalletBox> <h1>Browser Extension Installed!</h1> diff --git a/packages/taler-wallet-webextension/static/wallet.html b/packages/taler-wallet-webextension/static/wallet.html index a1c069d74..f9dd8a19b 100644 --- a/packages/taler-wallet-webextension/static/wallet.html +++ b/packages/taler-wallet-webextension/static/wallet.html @@ -2,11 +2,27 @@ <html> <head> <meta charset="utf-8" /> - <link rel="stylesheet" type="text/css" href="/static/style/pure.css" /> - <link rel="stylesheet" type="text/css" href="/static/style/wallet.css" /> <link rel="stylesheet" type="text/css" href="/dist/popupEntryPoint.css" /> <link rel="icon" href="/static/img/icon.png" /> <script src="/dist/walletEntryPoint.js"></script> + <style> + html { + font-family: sans-serif; /* 1 */ + } + h1 { + font-size: 2em; + } + input { + font: inherit; + } + body { + margin: 0; + font-size: 100%; + padding: 0; + background-color: #f8faf7; + font-family: Arial, Helvetica, sans-serif; + } + </style> </head> <body> |