add i18n where was missing

This commit is contained in:
Sebastian 2022-02-23 15:18:37 -03:00
parent 7647d077e7
commit 41850c9f14
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
50 changed files with 2104 additions and 1303 deletions

View File

@ -18,17 +18,23 @@
* Popup shown to the user when they click * Popup shown to the user when they click
* the Taler browser action button. * the Taler browser action button.
* *
* @author Florian Dold * @author sebasjm
*/ */
/** /**
* Imports. * Imports.
*/ */
import { i18n } from "@gnu-taler/taler-util"; import { i18n, Translate } from "@gnu-taler/taler-util";
import { VNode, h } from "preact"; import { VNode, h } from "preact";
import { JustInDevMode } from "./components/JustInDevMode"; import { JustInDevMode } from "./components/JustInDevMode";
import { NavigationHeader, NavigationHeaderHolder } from "./components/styled"; import { NavigationHeader, NavigationHeaderHolder } from "./components/styled";
/**
* List of pages used by the wallet
*
* @author sebasjm
*/
export enum Pages { export enum Pages {
welcome = "/welcome", welcome = "/welcome",
@ -60,17 +66,15 @@ export function PopupNavBar({ path = "" }: { path?: string }): VNode {
: "#"; : "#";
return ( return (
<NavigationHeader> <NavigationHeader>
<a <a href="/balance" class={path.startsWith("/balance") ? "active" : ""}>
href="/balance" <Translate>Balance</Translate>
class={path.startsWith("/balance") ? "active" : ""} </a>
>{i18n.str`Balance`}</a> <a href="/backup" class={path.startsWith("/backup") ? "active" : ""}>
<a <Translate>Backup</Translate>
href="/backup" </a>
class={path.startsWith("/backup") ? "active" : ""}
>{i18n.str`Backup`}</a>
<a /> <a />
<a href={innerUrl} target="_blank" rel="noreferrer"> <a href={innerUrl} target="_blank" rel="noreferrer">
<div class="settings-icon" title="Settings" /> <div class="settings-icon" title={i18n.str`Settings`} />
</a> </a>
</NavigationHeader> </NavigationHeader>
); );
@ -80,20 +84,17 @@ export function WalletNavBar({ path = "" }: { path?: string }): VNode {
return ( return (
<NavigationHeaderHolder> <NavigationHeaderHolder>
<NavigationHeader> <NavigationHeader>
<a <a href="/balance" class={path.startsWith("/balance") ? "active" : ""}>
href="/balance" <Translate>Balance</Translate>
class={path.startsWith("/balance") ? "active" : ""} </a>
>{i18n.str`Balance`}</a> <a href="/backup" class={path.startsWith("/backup") ? "active" : ""}>
<a <Translate>Backup</Translate>
href="/backup" </a>
class={path.startsWith("/backup") ? "active" : ""}
>{i18n.str`Backup`}</a>
<JustInDevMode> <JustInDevMode>
<a <a href="/dev" class={path.startsWith("/dev") ? "active" : ""}>
href="/dev" <Translate>Dev</Translate>
class={path.startsWith("/dev") ? "active" : ""} </a>
>{i18n.str`Dev`}</a>
</JustInDevMode> </JustInDevMode>
<a /> <a />
@ -101,7 +102,7 @@ export function WalletNavBar({ path = "" }: { path?: string }): VNode {
href="/settings" href="/settings"
class={path.startsWith("/settings") ? "active" : ""} class={path.startsWith("/settings") ? "active" : ""}
> >
<div class="settings-icon" title="Settings" /> <Translate>Settings</Translate>
</a> </a>
</NavigationHeader> </NavigationHeader>
</NavigationHeaderHolder> </NavigationHeaderHolder>

View File

@ -17,7 +17,7 @@
/** /**
* Entry point for the background page. * Entry point for the background page.
* *
* @author Florian Dold * @author sebasjm
*/ */
/** /**

View File

@ -1,43 +0,0 @@
"use strict";
/*
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/>
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.BrowserCryptoWorkerFactory = void 0;
/**
* API to access the Taler crypto worker thread.
* @author Florian Dold
*/
class BrowserCryptoWorkerFactory {
startWorker() {
const workerCtor = Worker;
const workerPath = "/browserWorkerEntry.js";
return new workerCtor(workerPath);
}
getConcurrency() {
let concurrency = 2;
try {
// only works in the browser
// tslint:disable-next-line:no-string-literal
concurrency = navigator["hardwareConcurrency"];
concurrency = Math.max(1, Math.ceil(concurrency / 2));
} catch (e) {
concurrency = 2;
}
return concurrency;
}
}
exports.BrowserCryptoWorkerFactory = BrowserCryptoWorkerFactory;
//# sourceMappingURL=browserCryptoWorkerFactory.js.map

View File

@ -1 +0,0 @@
{"version":3,"file":"browserCryptoWorkerFactory.js","sourceRoot":"","sources":["browserCryptoWorkerFactory.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;AAEH;;;GAGG;AAEH,MAAa,0BAA0B;IACrC,WAAW;QACT,MAAM,UAAU,GAAG,MAAM,CAAC;QAC1B,MAAM,UAAU,GAAG,wBAAwB,CAAC;QAC5C,OAAO,IAAI,UAAU,CAAC,UAAU,CAAiB,CAAC;IACpD,CAAC;IAED,cAAc;QACZ,IAAI,WAAW,GAAG,CAAC,CAAC;QACpB,IAAI;YACF,4BAA4B;YAC5B,6CAA6C;YAC7C,WAAW,GAAI,SAAiB,CAAC,qBAAqB,CAAC,CAAC;YACxD,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC;SACvD;QAAC,OAAO,CAAC,EAAE;YACV,WAAW,GAAG,CAAC,CAAC;SACjB;QACD,OAAO,WAAW,CAAC;IACrB,CAAC;CACF;AAnBD,gEAmBC"}

View File

@ -14,7 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { PaytoUri } from "@gnu-taler/taler-util"; import { PaytoUri, Translate } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { CopiedIcon, CopyIcon } from "../svg"; import { CopiedIcon, CopyIcon } from "../svg";
@ -34,23 +34,23 @@ export function BankDetailsByPaytoType({
amount, amount,
}: BankDetailsProps): VNode { }: BankDetailsProps): VNode {
const firstPart = !payto ? undefined : !payto.isKnown ? ( const firstPart = !payto ? undefined : !payto.isKnown ? (
<Row name="Account" value={payto.targetPath} /> <Row name={<Translate>Account</Translate>} value={payto.targetPath} />
) : payto.targetType === "x-taler-bank" ? ( ) : payto.targetType === "x-taler-bank" ? (
<Fragment> <Fragment>
<Row name="Bank host" value={payto.host} /> <Row name={<Translate>Bank host</Translate>} value={payto.host} />
<Row name="Bank account" value={payto.account} /> <Row name={<Translate>Bank account</Translate>} value={payto.account} />
</Fragment> </Fragment>
) : payto.targetType === "iban" ? ( ) : payto.targetType === "iban" ? (
<Row name="IBAN" value={payto.iban} /> <Row name={<Translate>IBAN</Translate>} value={payto.iban} />
) : undefined; ) : undefined;
return ( return (
<div style={{ textAlign: "left" }}> <div style={{ textAlign: "left" }}>
<p>Bank transfer details</p> <p>Bank transfer details</p>
<table> <table>
{firstPart} {firstPart}
<Row name="Exchange" value={exchangeBaseUrl} /> <Row name={<Translate>Exchange</Translate>} value={exchangeBaseUrl} />
<Row name="Chosen amount" value={amount} /> <Row name={<Translate>Chosen amount</Translate>} value={amount} />
<Row name="Subject" value={subject} literal /> <Row name={<Translate>Subject</Translate>} value={subject} literal />
</table> </table>
</div> </div>
); );
@ -61,7 +61,7 @@ function Row({
value, value,
literal, literal,
}: { }: {
name: string; name: VNode;
value: string; value: string;
literal?: boolean; literal?: boolean;
}): VNode { }): VNode {

View File

@ -19,9 +19,9 @@ import { h, VNode } from "preact";
interface Props { interface Props {
enabled: boolean; enabled: boolean;
onToggle: () => void; onToggle: () => void;
label: string; label: VNode;
name: string; name: string;
description?: string; description?: VNode;
} }
export function Checkbox({ export function Checkbox({
name, name,

View File

@ -20,7 +20,7 @@ import { h, VNode } from "preact";
interface Props { interface Props {
enabled: boolean; enabled: boolean;
onToggle: () => void; onToggle: () => void;
label: string; label: VNode;
name: string; name: string;
} }

View File

@ -14,6 +14,7 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { Translate } from "@gnu-taler/taler-util";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
export function DebugCheckbox({ export function DebugCheckbox({
@ -36,7 +37,7 @@ export function DebugCheckbox({
htmlFor="checkbox-perm" htmlFor="checkbox-perm"
style={{ marginLeft: "0.5em", fontWeight: "bold" }} style={{ marginLeft: "0.5em", fontWeight: "bold" }}
> >
Automatically open wallet based on page content <Translate>Automatically open wallet based on page content</Translate>
</label> </label>
<span <span
style={{ style={{
@ -46,8 +47,12 @@ export function DebugCheckbox({
marginLeft: "2em", marginLeft: "2em",
}} }}
> >
(Enabling this option below will make using the wallet faster, but (
requires more permissions from your browser.) <Translate>
Enabling this option below will make using the wallet faster, but
requires more permissions from your browser.
</Translate>
)
</span> </span>
</div> </div>
); );

View File

@ -14,7 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { WalletDiagnostics } from "@gnu-taler/taler-util"; import { Translate, WalletDiagnostics } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { PageLink } from "../renderHtml"; import { PageLink } from "../renderHtml";
@ -25,7 +25,13 @@ interface Props {
export function Diagnostics({ timedOut, diagnostics }: Props): VNode { export function Diagnostics({ timedOut, diagnostics }: Props): VNode {
if (timedOut) { if (timedOut) {
return <p>Diagnostics timed out. Could not talk to the wallet backend.</p>; return (
<p>
<Translate>
Diagnostics timed out. Could not talk to the wallet backend.
</Translate>
</p>
);
} }
if (diagnostics) { if (diagnostics) {
@ -41,7 +47,9 @@ export function Diagnostics({ timedOut, diagnostics }: Props): VNode {
paddingBottom: "0.2em", paddingBottom: "0.2em",
}} }}
> >
<p>Problems detected:</p> <p>
<Translate>Problems detected:</Translate>
</p>
<ol> <ol>
{diagnostics.errors.map((errMsg) => ( {diagnostics.errors.map((errMsg) => (
<li key={errMsg}>{errMsg}</li> <li key={errMsg}>{errMsg}</li>
@ -49,22 +57,32 @@ export function Diagnostics({ timedOut, diagnostics }: Props): VNode {
</ol> </ol>
{diagnostics.firefoxIdbProblem ? ( {diagnostics.firefoxIdbProblem ? (
<p> <p>
Please check in your <code>about:config</code> settings that you <Translate>
have IndexedDB enabled (check the preference name{" "} Please check in your <code>about:config</code> settings that you
<code>dom.indexedDB.enabled</code>). have IndexedDB enabled (check the preference name{" "}
<code>dom.indexedDB.enabled</code>).
</Translate>
</p> </p>
) : null} ) : null}
{diagnostics.dbOutdated ? ( {diagnostics.dbOutdated ? (
<p> <p>
Your wallet database is outdated. Currently automatic migration is <Translate>
not supported. Please go{" "} Your wallet database is outdated. Currently automatic migration is
<PageLink pageName="/reset-required">here</PageLink> to reset the not supported. Please go{" "}
wallet database. <PageLink pageName="/reset-required">
<Translate>here</Translate>
</PageLink>{" "}
to reset the wallet database.
</Translate>
</p> </p>
) : null} ) : null}
</div> </div>
); );
} }
return <p>Running diagnostics ...</p>; return (
<p>
<Translate>Running diagnostics</Translate> ...
</p>
);
} }

View File

@ -14,6 +14,7 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { Translate } from "@gnu-taler/taler-util";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
import { useRef, useState } from "preact/hooks"; import { useRef, useState } from "preact/hooks";
@ -39,7 +40,9 @@ export function EditableText({
return ( return (
<div style={{ display: "flex", justifyContent: "space-between" }}> <div style={{ display: "flex", justifyContent: "space-between" }}>
<p>{value}</p> <p>{value}</p>
<button onClick={() => setEditing(true)}>edit</button> <button onClick={() => setEditing(true)}>
<Translate>Edit</Translate>
</button>
</div> </div>
); );
}; };
@ -54,7 +57,7 @@ export function EditableText({
onChange(ref.current.value).then(() => setEditing(false)); onChange(ref.current.value).then(() => setEditing(false));
}} }}
> >
confirm <Translate>Confirm</Translate>
</button> </button>
</div> </div>
); );

View File

@ -13,7 +13,7 @@
You should have received a copy of the GNU General Public License along with 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/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { VNode, h } from "preact"; import { VNode, h, ComponentChildren } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import arrowDown from "../../static/img/chevron-down.svg"; import arrowDown from "../../static/img/chevron-down.svg";
import { ErrorBox } from "./styled"; import { ErrorBox } from "./styled";
@ -22,11 +22,10 @@ export function ErrorMessage({
title, title,
description, description,
}: { }: {
title?: string | VNode; title: VNode;
description?: string; description?: string;
}): VNode | null { }): VNode | null {
const [showErrorDetail, setShowErrorDetail] = useState(false); const [showErrorDetail, setShowErrorDetail] = useState(false);
if (!title) return null;
return ( return (
<ErrorBox style={{ paddingTop: 0, paddingBottom: 0 }}> <ErrorBox style={{ paddingTop: 0, paddingBottom: 0 }}>
<div> <div>

View File

@ -24,7 +24,7 @@ export function ErrorTalerOperation({
title, title,
error, error,
}: { }: {
title?: string; title?: VNode;
error?: TalerErrorDetails; error?: TalerErrorDetails;
}): VNode | null { }): VNode | null {
const { devMode } = useDevContext(); const { devMode } = useDevContext();

View File

@ -13,8 +13,13 @@
You should have received a copy of the GNU General Public License along with 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/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { Translate } from "@gnu-taler/taler-util";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
export function Loading(): VNode { export function Loading(): VNode {
return <div>Loading...</div>; return (
<div>
<Translate>Loading</Translate>...
</div>
);
} }

View File

@ -19,7 +19,7 @@ import { ErrorMessage } from "./ErrorMessage";
import { ErrorTalerOperation } from "./ErrorTalerOperation"; import { ErrorTalerOperation } from "./ErrorTalerOperation";
export interface Props { export interface Props {
title: string; title: VNode;
error: HookError; error: HookError;
} }
export function LoadingError({ title, error }: Props): VNode { export function LoadingError({ title, error }: Props): VNode {

View File

@ -4,7 +4,7 @@ import { ButtonBoxPrimary, ButtonPrimary, ParagraphClickable } from "./styled";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
export interface Props { export interface Props {
label: (s: string) => string; label: (s: string) => VNode;
actions: string[]; actions: string[];
onClick: (s: string) => void; onClick: (s: string) => void;
} }

View File

@ -15,11 +15,11 @@
*/ */
import { AmountLike } from "@gnu-taler/taler-util"; import { AmountLike } from "@gnu-taler/taler-util";
import { ExtraLargeText, LargeText, SmallLightText } from "./styled"; import { ExtraLargeText, LargeText, SmallLightText } from "./styled";
import { h } from "preact"; import { h, VNode } from "preact";
export type Kind = "positive" | "negative" | "neutral"; export type Kind = "positive" | "negative" | "neutral";
interface Props { interface Props {
title: string; title: VNode;
text: AmountLike; text: AmountLike;
kind: Kind; kind: Kind;
big?: boolean; big?: boolean;

View File

@ -14,13 +14,14 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { Translate } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { NiceSelect } from "./styled"; import { NiceSelect } from "./styled";
interface Props { interface Props {
value?: string; value?: string;
onChange: (s: string) => void; onChange: (s: string) => void;
label: string; label: VNode;
list: { list: {
[label: string]: string; [label: string]: string;
}; };
@ -58,7 +59,7 @@ export function SelectList({
{value === undefined || {value === undefined ||
(canBeNull && ( (canBeNull && (
<option selected disabled> <option selected disabled>
Select one option <Translate>Select one option</Translate>
</option> </option>
// ) : ( // ) : (
// <option selected>{list[value]}</option> // <option selected>{list[value]}</option>

View File

@ -21,6 +21,7 @@ import {
Timestamp, Timestamp,
Transaction, Transaction,
TransactionType, TransactionType,
Translate,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
import imageBank from "../../static/img/ri-bank-line.svg"; import imageBank from "../../static/img/ri-bank-line.svg";
@ -133,7 +134,7 @@ function TransactionLayout(props: TransactionLayoutProps): VNode {
</LargeText> </LargeText>
{props.pending && ( {props.pending && (
<LightText style={{ marginTop: 5, marginBottom: 5 }}> <LightText style={{ marginTop: 5, marginBottom: 5 }}>
Waiting for confirmation <Translate>Waiting for confirmation</Translate>
</LightText> </LightText>
)} )}
<SmallLightText style={{ marginTop: 5 }}> <SmallLightText style={{ marginTop: 5 }}>
@ -195,7 +196,11 @@ function TransactionAmount(props: TransactionAmountProps): VNode {
{sign} {sign}
{Amounts.stringifyValue(props.amount)} {Amounts.stringifyValue(props.amount)}
</ExtraLargeText> </ExtraLargeText>
{props.pending && <div>PENDING</div>} {props.pending && (
<div>
<Translate>PENDING</Translate>
</div>
)}
</Column> </Column>
); );
} }

View File

@ -35,6 +35,7 @@ import {
NotificationType, NotificationType,
PreparePayResult, PreparePayResult,
PreparePayResultType, PreparePayResultType,
Translate,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { OperationFailedError } from "@gnu-taler/taler-wallet-core"; import { OperationFailedError } from "@gnu-taler/taler-wallet-core";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
@ -108,7 +109,11 @@ export function DepositPage({ talerPayUri, goBack }: Props): VNode {
}, [talerPayUri, foundAmountStr]); }, [talerPayUri, foundAmountStr]);
if (!talerPayUri) { if (!talerPayUri) {
return <span>missing pay uri</span>; return (
<span>
<Translate>missing pay uri</Translate>
</span>
);
} }
if (!payStatus) { if (!payStatus) {
@ -116,10 +121,16 @@ export function DepositPage({ talerPayUri, goBack }: Props): VNode {
return ( return (
<WalletAction> <WalletAction>
<LogoHeader /> <LogoHeader />
<h2>{i18n.str`Digital cash payment`}</h2> <h2>
<Translate>Digital cash payment</Translate>
</h2>
<section> <section>
<ErrorTalerOperation <ErrorTalerOperation
title="Could not get the payment information for this order" title={
<Translate>
Could not get the payment information for this order
</Translate>
}
error={payErrMsg?.operationError} error={payErrMsg?.operationError}
/> />
</section> </section>
@ -130,15 +141,25 @@ export function DepositPage({ talerPayUri, goBack }: Props): VNode {
return ( return (
<WalletAction> <WalletAction>
<LogoHeader /> <LogoHeader />
<h2>{i18n.str`Digital cash payment`}</h2> <h2>
<Translate>Digital cash payment</Translate>
</h2>
<section> <section>
<p>Could not get the payment information for this order</p> <p>
<Translate>
Could not get the payment information for this order
</Translate>
</p>
<ErrorBox>{payErrMsg}</ErrorBox> <ErrorBox>{payErrMsg}</ErrorBox>
</section> </section>
</WalletAction> </WalletAction>
); );
} }
return <span>Loading payment information ...</span>; return (
<span>
<Translate>Loading payment information</Translate> ...
</span>
);
} }
const onClick = async (): Promise<void> => { const onClick = async (): Promise<void> => {
@ -183,20 +204,32 @@ export function PaymentRequestView({
<WalletAction> <WalletAction>
<LogoHeader /> <LogoHeader />
<h2>{i18n.str`Digital cash deposit`}</h2> <h2>
<Translate>Digital cash deposit</Translate>
</h2>
{payStatus.status === PreparePayResultType.AlreadyConfirmed && {payStatus.status === PreparePayResultType.AlreadyConfirmed &&
(payStatus.paid ? ( (payStatus.paid ? (
<SuccessBox> Already paid </SuccessBox> <SuccessBox>
<Translate>Already paid</Translate>
</SuccessBox>
) : ( ) : (
<WarningBox> Already claimed </WarningBox> <WarningBox>
<Translate>Already claimed</Translate>
</WarningBox>
))} ))}
{payResult && payResult.type === ConfirmPayResultType.Done && ( {payResult && payResult.type === ConfirmPayResultType.Done && (
<SuccessBox> <SuccessBox>
<h3>Payment complete</h3> <h3>
<Translate>Payment complete</Translate>
</h3>
<p> <p>
{!payResult.contractTerms.fulfillment_message {!payResult.contractTerms.fulfillment_message ? (
? "You will now be sent back to the merchant you came from." <Translate>
: payResult.contractTerms.fulfillment_message} You will now be sent back to the merchant you came from.
</Translate>
) : (
payResult.contractTerms.fulfillment_message
)}
</p> </p>
</SuccessBox> </SuccessBox>
)} )}
@ -205,7 +238,7 @@ export function PaymentRequestView({
Amounts.isNonZero(totalFees) && ( Amounts.isNonZero(totalFees) && (
<Part <Part
big big
title="Total to pay" title={<Translate>Total to pay</Translate>}
text={amountToPretty( text={amountToPretty(
Amounts.parseOrThrow(payStatus.amountEffective), Amounts.parseOrThrow(payStatus.amountEffective),
)} )}
@ -214,7 +247,7 @@ export function PaymentRequestView({
)} )}
<Part <Part
big big
title="Purchase amount" title={<Translate>Purchase amount</Translate>}
text={amountToPretty(Amounts.parseOrThrow(payStatus.amountRaw))} text={amountToPretty(Amounts.parseOrThrow(payStatus.amountRaw))}
kind="neutral" kind="neutral"
/> />
@ -222,21 +255,25 @@ export function PaymentRequestView({
<Fragment> <Fragment>
<Part <Part
big big
title="Fee" title={<Translate>Fee</Translate>}
text={amountToPretty(totalFees)} text={amountToPretty(totalFees)}
kind="negative" kind="negative"
/> />
</Fragment> </Fragment>
)} )}
<Part <Part
title="Merchant" title={<Translate>Merchant</Translate>}
text={contractTerms.merchant.name} text={contractTerms.merchant.name}
kind="neutral" kind="neutral"
/> />
<Part title="Purchase" text={contractTerms.summary} kind="neutral" /> <Part
title={<Translate>Purchase</Translate>}
text={contractTerms.summary}
kind="neutral"
/>
{contractTerms.order_id && ( {contractTerms.order_id && (
<Part <Part
title="Receipt" title={<Translate>Receipt</Translate>}
text={`#${contractTerms.order_id}`} text={`#${contractTerms.order_id}`}
kind="neutral" kind="neutral"
/> />

View File

@ -37,10 +37,12 @@ import {
PreparePayResult, PreparePayResult,
PreparePayResultType, PreparePayResultType,
Product, Product,
Translate,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { OperationFailedError } from "@gnu-taler/taler-wallet-core"; import { OperationFailedError } from "@gnu-taler/taler-wallet-core";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { ErrorMessage } from "../components/ErrorMessage";
import { Loading } from "../components/Loading"; import { Loading } from "../components/Loading";
import { LoadingError } from "../components/LoadingError"; import { LoadingError } from "../components/LoadingError";
import { LogoHeader } from "../components/LogoHeader"; import { LogoHeader } from "../components/LogoHeader";
@ -106,7 +108,12 @@ export function PayPage({
} }
if (hook.hasError) { if (hook.hasError) {
return <LoadingError title="Could not load pay status" error={hook} />; return (
<LoadingError
title={<Translate>Could not load pay status</Translate>}
error={hook}
/>
);
} }
const foundBalance = hook.response.balance.balances.find( const foundBalance = hook.response.balance.balances.find(
@ -178,9 +185,13 @@ export function PaymentRequestView({
if (!contractTerms) { if (!contractTerms) {
return ( return (
<span> <ErrorMessage
Error: did not get contract terms from merchant or wallet backend. title={
</span> <Translate>
Could not load contract terms from merchant or wallet backend.
</Translate>
}
/>
); );
} }
@ -192,13 +203,6 @@ export function PaymentRequestView({
totalFees = Amounts.sub(amountEffective, amountRaw).amount; totalFees = Amounts.sub(amountEffective, amountRaw).amount;
} }
// let merchantName: VNode;
// if (contractTerms.merchant && contractTerms.merchant.name) {
// merchantName = <strong>{contractTerms.merchant.name}</strong>;
// } else {
// merchantName = <strong>(pub: {contractTerms.merchant_pub})</strong>;
// }
function Alternative(): VNode { function Alternative(): VNode {
const [showQR, setShowQR] = useState<boolean>(false); const [showQR, setShowQR] = useState<boolean>(false);
const privateUri = const privateUri =
@ -209,12 +213,21 @@ export function PaymentRequestView({
return ( return (
<section> <section>
<LinkSuccess upperCased onClick={() => setShowQR((qr) => !qr)}> <LinkSuccess upperCased onClick={() => setShowQR((qr) => !qr)}>
{!showQR ? i18n.str`Pay with a mobile phone` : i18n.str`Hide QR`} {!showQR ? (
<Translate>Pay with a mobile phone</Translate>
) : (
<Translate>Hide QR</Translate>
)}
</LinkSuccess> </LinkSuccess>
{showQR && ( {showQR && (
<div> <div>
<QR text={privateUri} /> <QR text={privateUri} />
Scan the QR code or <a href={privateUri}>click here</a> <Translate>
Scan the QR code or
<a href={privateUri}>
<Translate>click here</Translate>
</a>
</Translate>
</div> </div>
)} )}
</section> </section>
@ -227,7 +240,9 @@ export function PaymentRequestView({
return ( return (
<section> <section>
<div> <div>
<p>Processing...</p> <p>
<Translate>Processing</Translate>...
</p>
</div> </div>
</section> </section>
); );
@ -239,7 +254,9 @@ export function PaymentRequestView({
<Fragment> <Fragment>
<section> <section>
<ButtonSuccess upperCased onClick={onClick}> <ButtonSuccess upperCased onClick={onClick}>
{i18n.str`Pay`} {amountToString(payStatus.amountEffective)} <Translate>
Pay {amountToString(payStatus.amountEffective)}
</Translate>
</ButtonSuccess> </ButtonSuccess>
</section> </section>
<Alternative /> <Alternative />
@ -252,18 +269,22 @@ export function PaymentRequestView({
<section> <section>
{balance ? ( {balance ? (
<WarningBox> <WarningBox>
Your balance of {amountToString(balance)} is not enough to pay <Translate>
for this purchase Your balance of {amountToString(balance)} is not enough to pay
for this purchase
</Translate>
</WarningBox> </WarningBox>
) : ( ) : (
<WarningBox> <WarningBox>
Your balance is not enough to pay for this purchase. <Translate>
Your balance is not enough to pay for this purchase.
</Translate>
</WarningBox> </WarningBox>
)} )}
</section> </section>
<section> <section>
<ButtonSuccess upperCased onClick={goToWalletManualWithdraw}> <ButtonSuccess upperCased onClick={goToWalletManualWithdraw}>
{i18n.str`Withdraw digital cash`} <Translate>Withdraw digital cash</Translate>
</ButtonSuccess> </ButtonSuccess>
</section> </section>
<Alternative /> <Alternative />
@ -276,7 +297,7 @@ export function PaymentRequestView({
<section> <section>
{payStatus.paid && contractTerms.fulfillment_message && ( {payStatus.paid && contractTerms.fulfillment_message && (
<Part <Part
title="Merchant message" title={<Translate>Merchant message</Translate>}
text={contractTerms.fulfillment_message} text={contractTerms.fulfillment_message}
kind="neutral" kind="neutral"
/> />
@ -293,31 +314,48 @@ export function PaymentRequestView({
<WalletAction> <WalletAction>
<LogoHeader /> <LogoHeader />
<h2>{i18n.str`Digital cash payment`}</h2> <h2>
<Translate>Digital cash payment</Translate>
</h2>
{payStatus.status === PreparePayResultType.AlreadyConfirmed && {payStatus.status === PreparePayResultType.AlreadyConfirmed &&
(payStatus.paid ? ( (payStatus.paid ? (
payStatus.contractTerms.fulfillment_url ? ( payStatus.contractTerms.fulfillment_url ? (
<SuccessBox> <SuccessBox>
Already paid, you are going to be redirected to{" "} <Translate>
<a href={payStatus.contractTerms.fulfillment_url}> Already paid, you are going to be redirected to{" "}
{payStatus.contractTerms.fulfillment_url} <a href={payStatus.contractTerms.fulfillment_url}>
</a> {payStatus.contractTerms.fulfillment_url}
</a>
</Translate>
</SuccessBox> </SuccessBox>
) : ( ) : (
<SuccessBox> Already paid </SuccessBox> <SuccessBox>
<Translate>Already paid</Translate>
</SuccessBox>
) )
) : ( ) : (
<WarningBox> Already claimed </WarningBox> <WarningBox>
<Translate>Already claimed</Translate>
</WarningBox>
))} ))}
{payResult && payResult.type === ConfirmPayResultType.Done && ( {payResult && payResult.type === ConfirmPayResultType.Done && (
<SuccessBox> <SuccessBox>
<h3>Payment complete</h3> <h3>
<Translate>Payment complete</Translate>
</h3>
<p> <p>
{!payResult.contractTerms.fulfillment_message {!payResult.contractTerms.fulfillment_message ? (
? payResult.contractTerms.fulfillment_url payResult.contractTerms.fulfillment_url ? (
? `You are going to be redirected to ${payResult.contractTerms.fulfillment_url}` <Translate>
: "You can close this page." You are going to be redirected to $
: payResult.contractTerms.fulfillment_message} {payResult.contractTerms.fulfillment_url}
</Translate>
) : (
<Translate>You can close this page.</Translate>
)
) : (
payResult.contractTerms.fulfillment_message
)}
</p> </p>
</SuccessBox> </SuccessBox>
)} )}
@ -326,14 +364,14 @@ export function PaymentRequestView({
Amounts.isNonZero(totalFees) && ( Amounts.isNonZero(totalFees) && (
<Part <Part
big big
title="Total to pay" title={<Translate>Total to pay</Translate>}
text={amountToString(payStatus.amountEffective)} text={amountToString(payStatus.amountEffective)}
kind="negative" kind="negative"
/> />
)} )}
<Part <Part
big big
title="Purchase amount" title={<Translate>Purchase amount</Translate>}
text={amountToString(payStatus.amountRaw)} text={amountToString(payStatus.amountRaw)}
kind="neutral" kind="neutral"
/> />
@ -341,21 +379,25 @@ export function PaymentRequestView({
<Fragment> <Fragment>
<Part <Part
big big
title="Fee" title={<Translate>Fee</Translate>}
text={amountToString(totalFees)} text={amountToString(totalFees)}
kind="negative" kind="negative"
/> />
</Fragment> </Fragment>
)} )}
<Part <Part
title="Merchant" title={<Translate>Merchant</Translate>}
text={contractTerms.merchant.name} text={contractTerms.merchant.name}
kind="neutral" kind="neutral"
/> />
<Part title="Purchase" text={contractTerms.summary} kind="neutral" /> <Part
title={<Translate>Purchase</Translate>}
text={contractTerms.summary}
kind="neutral"
/>
{contractTerms.order_id && ( {contractTerms.order_id && (
<Part <Part
title="Receipt" title={<Translate>Receipt</Translate>}
text={`#${contractTerms.order_id}`} text={`#${contractTerms.order_id}`}
kind="neutral" kind="neutral"
/> />
@ -373,7 +415,7 @@ function ProductList({ products }: { products: Product[] }): VNode {
return ( return (
<Fragment> <Fragment>
<SmallLightText style={{ margin: ".5em" }}> <SmallLightText style={{ margin: ".5em" }}>
List of products <Translate>List of products</Translate>
</SmallLightText> </SmallLightText>
<dl> <dl>
{products.map((p, i) => { {products.map((p, i) => {
@ -415,15 +457,18 @@ function ProductList({ products }: { products: Product[] }): VNode {
{p.quantity ?? 1} x {p.description} {p.quantity ?? 1} x {p.description}
</dt> </dt>
<dd> <dd>
Total{` `} <Translate>Total</Translate>
{p.price {` `}
? `${Amounts.stringifyValue( {p.price ? (
Amounts.mult( `${Amounts.stringifyValue(
Amounts.parseOrThrow(p.price), Amounts.mult(
p.quantity ?? 1, Amounts.parseOrThrow(p.price),
).amount, p.quantity ?? 1,
)} ${p}` ).amount,
: "free"} )} ${p}`
) : (
<Translate>free</Translate>
)}
</dd> </dd>
</div> </div>
</div> </div>

View File

@ -17,10 +17,10 @@
/** /**
* Page that shows refund status for purchases. * Page that shows refund status for purchases.
* *
* @author Florian Dold * @author sebasjm
*/ */
import { Amounts, ApplyRefundResponse } from "@gnu-taler/taler-util"; import { Amounts, ApplyRefundResponse, Translate } from "@gnu-taler/taler-util";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { AmountView } from "../renderHtml"; import { AmountView } from "../renderHtml";
@ -37,20 +37,28 @@ export function View({ applyResult }: ViewProps): VNode {
<section class="main"> <section class="main">
<h1>GNU Taler Wallet</h1> <h1>GNU Taler Wallet</h1>
<article class="fade"> <article class="fade">
<h2>Refund Status</h2> <h2>
<Translate>Refund Status</Translate>
</h2>
<p> <p>
The product <em>{applyResult.info.summary}</em> has received a total <Translate>
effective refund of{" "} The product <em>{applyResult.info.summary}</em> has received a total
effective refund of{" "}
</Translate>
<AmountView amount={applyResult.amountRefundGranted} />. <AmountView amount={applyResult.amountRefundGranted} />.
</p> </p>
{applyResult.pendingAtExchange ? ( {applyResult.pendingAtExchange ? (
<p>Refund processing is still in progress.</p> <p>
<Translate>Refund processing is still in progress.</Translate>
</p>
) : null} ) : null}
{!Amounts.isZero(applyResult.amountRefundGone) ? ( {!Amounts.isZero(applyResult.amountRefundGone) ? (
<p> <p>
The refund amount of{" "} <Translate>
<AmountView amount={applyResult.amountRefundGone} /> could not be The refund amount of{" "}
applied. <AmountView amount={applyResult.amountRefundGone} /> could not be
applied.
</Translate>
</p> </p>
) : null} ) : null}
</article> </article>
@ -82,15 +90,27 @@ export function RefundPage({ talerRefundUri }: Props): VNode {
console.log("rendering"); console.log("rendering");
if (!talerRefundUri) { if (!talerRefundUri) {
return <span>missing taler refund uri</span>; return (
<span>
<Translate>missing taler refund uri</Translate>
</span>
);
} }
if (errMsg) { if (errMsg) {
return <span>Error: {errMsg}</span>; return (
<span>
<Translate>Error: {errMsg}</Translate>
</span>
);
} }
if (!applyResult) { if (!applyResult) {
return <span>Updating refund status</span>; return (
<span>
<Translate>Updating refund status</Translate>
</span>
);
} }
return <View applyResult={applyResult} />; return <View applyResult={applyResult} />;

View File

@ -1,4 +1,4 @@
import { i18n } from "@gnu-taler/taler-util"; import { i18n, Translate } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { CheckboxOutlined } from "../components/CheckboxOutlined"; import { CheckboxOutlined } from "../components/CheckboxOutlined";
import { ExchangeXmlTos } from "../components/ExchangeToS"; import { ExchangeXmlTos } from "../components/ExchangeToS";
@ -34,7 +34,7 @@ export function TermsOfServiceSection({
{terms.status === "notfound" && ( {terms.status === "notfound" && (
<section> <section>
<WarningText> <WarningText>
{i18n.str`Exchange doesn't have terms of service`} <Translate>Exchange doesn't have terms of service</Translate>
</WarningText> </WarningText>
</section> </section>
)} )}
@ -46,21 +46,21 @@ export function TermsOfServiceSection({
{terms.status === "notfound" && ( {terms.status === "notfound" && (
<section> <section>
<WarningText> <WarningText>
{i18n.str`Exchange doesn't have terms of service`} <Translate>Exchange doesn't have terms of service</Translate>
</WarningText> </WarningText>
</section> </section>
)} )}
{terms.status === "new" && ( {terms.status === "new" && (
<section> <section>
<ButtonSuccess upperCased onClick={() => onReview(true)}> <ButtonSuccess upperCased onClick={() => onReview(true)}>
{i18n.str`Review exchange terms of service`} <Translate>Review exchange terms of service</Translate>
</ButtonSuccess> </ButtonSuccess>
</section> </section>
)} )}
{terms.status === "changed" && ( {terms.status === "changed" && (
<section> <section>
<ButtonWarning upperCased onClick={() => onReview(true)}> <ButtonWarning upperCased onClick={() => onReview(true)}>
{i18n.str`Review new version of terms of service`} <Translate>Review new version of terms of service</Translate>
</ButtonWarning> </ButtonWarning>
</section> </section>
)} )}
@ -72,7 +72,7 @@ export function TermsOfServiceSection({
{onReview && ( {onReview && (
<section> <section>
<LinkSuccess upperCased onClick={() => onReview(true)}> <LinkSuccess upperCased onClick={() => onReview(true)}>
{i18n.str`Show terms of service`} <Translate>Show terms of service</Translate>
</LinkSuccess> </LinkSuccess>
</section> </section>
)} )}
@ -80,7 +80,9 @@ export function TermsOfServiceSection({
<CheckboxOutlined <CheckboxOutlined
name="terms" name="terms"
enabled={reviewed} enabled={reviewed}
label={i18n.str`I accept the exchange terms of service`} label={
<Translate>I accept the exchange terms of service</Translate>
}
onToggle={() => { onToggle={() => {
onAccept(!reviewed); onAccept(!reviewed);
if (onReview) onReview(false); if (onReview) onReview(false);
@ -95,7 +97,9 @@ export function TermsOfServiceSection({
{terms.status !== "notfound" && !terms.content && ( {terms.status !== "notfound" && !terms.content && (
<section> <section>
<WarningBox> <WarningBox>
The exchange reply with a empty terms of service <Translate>
The exchange reply with a empty terms of service
</Translate>
</WarningBox> </WarningBox>
</section> </section>
)} )}
@ -116,7 +120,7 @@ export function TermsOfServiceSection({
)} )}
{terms.content.type === "pdf" && ( {terms.content.type === "pdf" && (
<a href={terms.content.location.toString()} download="tos.pdf"> <a href={terms.content.location.toString()} download="tos.pdf">
Download Terms of Service <Translate>Download Terms of Service</Translate>
</a> </a>
)} )}
</section> </section>
@ -124,7 +128,7 @@ export function TermsOfServiceSection({
{reviewed && onReview && ( {reviewed && onReview && (
<section> <section>
<LinkSuccess upperCased onClick={() => onReview(false)}> <LinkSuccess upperCased onClick={() => onReview(false)}>
{i18n.str`Hide terms of service`} <Translate>Hide terms of service</Translate>
</LinkSuccess> </LinkSuccess>
</section> </section>
)} )}
@ -133,7 +137,9 @@ export function TermsOfServiceSection({
<CheckboxOutlined <CheckboxOutlined
name="terms" name="terms"
enabled={reviewed} enabled={reviewed}
label={i18n.str`I accept the exchange terms of service`} label={
<Translate>I accept the exchange terms of service</Translate>
}
onToggle={() => { onToggle={() => {
onAccept(!reviewed); onAccept(!reviewed);
if (onReview) onReview(false); if (onReview) onReview(false);

View File

@ -17,10 +17,10 @@
/** /**
* Page shown to the user to accept or ignore a tip from a merchant. * Page shown to the user to accept or ignore a tip from a merchant.
* *
* @author Florian Dold <dold@taler.net> * @author sebasjm <dold@taler.net>
*/ */
import { PrepareTipResult } from "@gnu-taler/taler-util"; import { PrepareTipResult, Translate } from "@gnu-taler/taler-util";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { Loading } from "../components/Loading"; import { Loading } from "../components/Loading";
@ -46,21 +46,29 @@ export function View({
<article class="fade"> <article class="fade">
{prepareTipResult.accepted ? ( {prepareTipResult.accepted ? (
<span> <span>
Tip from <code>{prepareTipResult.merchantBaseUrl}</code> accepted. <Translate>
Check your transactions list for more details. Tip from <code>{prepareTipResult.merchantBaseUrl}</code> accepted.
Check your transactions list for more details.
</Translate>
</span> </span>
) : ( ) : (
<div> <div>
<p> <p>
The merchant <code>{prepareTipResult.merchantBaseUrl}</code> is <Translate>
offering you a tip of{" "} The merchant <code>{prepareTipResult.merchantBaseUrl}</code> is
<strong> offering you a tip of{" "}
<AmountView amount={prepareTipResult.tipAmountEffective} /> <strong>
</strong>{" "} <AmountView amount={prepareTipResult.tipAmountEffective} />
via the exchange <code>{prepareTipResult.exchangeBaseUrl}</code> </strong>{" "}
via the exchange <code>{prepareTipResult.exchangeBaseUrl}</code>
</Translate>
</p> </p>
<button onClick={onAccept}>Accept tip</button> <button onClick={onAccept}>
<button onClick={onIgnore}>Ignore</button> <Translate>Accept tip</Translate>
</button>
<button onClick={onIgnore}>
<Translate>Ignore</Translate>
</button>
</div> </div>
)} )}
</article> </article>
@ -98,11 +106,19 @@ export function TipPage({ talerTipUri }: Props): VNode {
}; };
if (!talerTipUri) { if (!talerTipUri) {
return <span>missing tip uri</span>; return (
<span>
<Translate>missing tip uri</Translate>
</span>
);
} }
if (tipIgnored) { if (tipIgnored) {
return <span>You've ignored the tip.</span>; return (
<span>
<Translate>You've ignored the tip.</Translate>
</span>
);
} }
if (!prepareTipResult) { if (!prepareTipResult) {

View File

@ -18,7 +18,7 @@
* Page shown to the user to confirm creation * Page shown to the user to confirm creation
* of a reserve, usually requested by the bank. * of a reserve, usually requested by the bank.
* *
* @author Florian Dold * @author sebasjm
*/ */
import { import {
@ -26,6 +26,7 @@ import {
Amounts, Amounts,
ExchangeListItem, ExchangeListItem,
i18n, i18n,
Translate,
WithdrawUriInfoResponse, WithdrawUriInfoResponse,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { OperationFailedError } from "@gnu-taler/taler-wallet-core"; import { OperationFailedError } from "@gnu-taler/taler-wallet-core";
@ -117,37 +118,46 @@ export function View({
return ( return (
<WalletAction> <WalletAction>
<LogoHeader /> <LogoHeader />
<h2>{i18n.str`Digital cash withdrawal`}</h2> <h2>
<Translate>Digital cash withdrawal</Translate>
</h2>
{withdrawError && ( {withdrawError && (
<ErrorTalerOperation <ErrorTalerOperation
title="Could not finish the withdrawal operation" title={
<Translate>Could not finish the withdrawal operation</Translate>
}
error={withdrawError.operationError} error={withdrawError.operationError}
/> />
)} )}
<section> <section>
<Part <Part
title="Total to withdraw" title={<Translate>Total to withdraw</Translate>}
text={amountToString(Amounts.sub(amount, withdrawalFee).amount)} text={amountToString(Amounts.sub(amount, withdrawalFee).amount)}
kind="positive" kind="positive"
/> />
{Amounts.isNonZero(withdrawalFee) && ( {Amounts.isNonZero(withdrawalFee) && (
<Fragment> <Fragment>
<Part <Part
title="Chosen amount" title={<Translate>Chosen amount</Translate>}
text={amountToString(amount)} text={amountToString(amount)}
kind="neutral" kind="neutral"
/> />
<Part <Part
title="Exchange fee" title={<Translate>Exchange fee</Translate>}
text={amountToString(withdrawalFee)} text={amountToString(withdrawalFee)}
kind="negative" kind="negative"
/> />
</Fragment> </Fragment>
)} )}
{exchangeBaseUrl && ( {exchangeBaseUrl && (
<Part title="Exchange" text={exchangeBaseUrl} kind="neutral" big /> <Part
title={<Translate>Exchange</Translate>}
text={exchangeBaseUrl}
kind="neutral"
big
/>
)} )}
</section> </section>
{!reviewing && ( {!reviewing && (
@ -156,7 +166,7 @@ export function View({
<Fragment> <Fragment>
<div> <div>
<SelectList <SelectList
label="Known exchanges" label={<Translate>Known exchanges</Translate>}
list={exchanges} list={exchanges}
value={nextExchange} value={nextExchange}
name="switchingExchange" name="switchingExchange"
@ -172,14 +182,16 @@ export function View({
setSwitchingExchange(false); setSwitchingExchange(false);
}} }}
> >
{nextExchange === undefined {nextExchange === undefined ? (
? i18n.str`Cancel exchange selection` <Translate>Cancel exchange selection</Translate>
: i18n.str`Confirm exchange selection`} ) : (
<Translate>Confirm exchange selection</Translate>
)}
</LinkSuccess> </LinkSuccess>
</Fragment> </Fragment>
) : ( ) : (
<LinkSuccess upperCased onClick={() => setSwitchingExchange(true)}> <LinkSuccess upperCased onClick={() => setSwitchingExchange(true)}>
{i18n.str`Switch exchange`} <Translate>Switch exchange</Translate>
</LinkSuccess> </LinkSuccess>
)} )}
</section> </section>
@ -198,7 +210,7 @@ export function View({
disabled={!exchangeBaseUrl || confirmDisabled} disabled={!exchangeBaseUrl || confirmDisabled}
onClick={doWithdrawAndCheckError} onClick={doWithdrawAndCheckError}
> >
{i18n.str`Confirm withdrawal`} <Translate>Confirm withdrawal</Translate>
</ButtonSuccess> </ButtonSuccess>
)} )}
{terms.status === "notfound" && ( {terms.status === "notfound" && (
@ -207,7 +219,7 @@ export function View({
disabled={!exchangeBaseUrl} disabled={!exchangeBaseUrl}
onClick={doWithdrawAndCheckError} onClick={doWithdrawAndCheckError}
> >
{i18n.str`Withdraw anyway`} <Translate>Withdraw anyway</Translate>
</ButtonWarning> </ButtonWarning>
)} )}
</section> </section>
@ -270,7 +282,7 @@ export function WithdrawPageWithParsedURI({
if (detailsHook.hasError) { if (detailsHook.hasError) {
return ( return (
<LoadingError <LoadingError
title="Could not load the withdrawal details" title={<Translate>Could not load the withdrawal details</Translate>}
error={detailsHook} error={detailsHook}
/> />
); );
@ -293,9 +305,7 @@ export function WithdrawPageWithParsedURI({
const onWithdraw = async (): Promise<void> => { const onWithdraw = async (): Promise<void> => {
if (!exchange) return; if (!exchange) return;
console.log("accepting exchange", exchange);
const res = await wxApi.acceptWithdrawal(uri, exchange); const res = await wxApi.acceptWithdrawal(uri, exchange);
console.log("accept withdrawal response", res);
if (res.confirmTransferUrl) { if (res.confirmTransferUrl) {
document.location.href = res.confirmTransferUrl; document.location.href = res.confirmTransferUrl;
} }
@ -327,7 +337,7 @@ export function WithdrawPage({ talerWithdrawUri }: Props): VNode {
if (!talerWithdrawUri) { if (!talerWithdrawUri) {
return ( return (
<span> <span>
<i18n.Translate>missing withdraw uri</i18n.Translate> <Translate>missing withdraw uri</Translate>
</span> </span>
); );
} }
@ -337,7 +347,7 @@ export function WithdrawPage({ talerWithdrawUri }: Props): VNode {
if (uriInfoHook.hasError) { if (uriInfoHook.hasError) {
return ( return (
<LoadingError <LoadingError
title="Could not get the info from the URI" title={<Translate>Could not get the info from the URI</Translate>}
error={uriInfoHook} error={uriInfoHook}
/> />
); );

View File

@ -1,31 +0,0 @@
/*
This file is part of TALER
(C) 2017 Inria
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 { h, VNode } from "preact";
/**
* View and edit auditors.
*
* @author Florian Dold
*/
/**
* Imports.
*/
export function makePaybackPage(): VNode {
return <div>not implemented</div>;
}

View File

@ -17,9 +17,10 @@
/** /**
* Page to inform the user when a database reset is required. * Page to inform the user when a database reset is required.
* *
* @author Florian Dold * @author sebasjm
*/ */
import { Translate } from "@gnu-taler/taler-util";
import { Component, h, VNode } from "preact"; import { Component, h, VNode } from "preact";
import * as wxApi from "../wxApi"; import * as wxApi from "../wxApi";
@ -49,14 +50,20 @@ class ResetNotification extends Component<any, State> {
if (this.state.resetRequired) { if (this.state.resetRequired) {
return ( return (
<div> <div>
<h1>Manual Reset Required</h1> <h1>
<Translate>Manual Reset Required</Translate>
</h1>
<p> <p>
The wallet&apos;s database in your browser is incompatible with the{" "} <Translate>
currently installed wallet. Please reset manually. The wallet&apos;s database in your browser is incompatible with
the currently installed wallet. Please reset manually.
</Translate>
</p> </p>
<p> <p>
Once the database format has stabilized, we will provide automatic <Translate>
upgrades. Once the database format has stabilized, we will provide automatic
upgrades.
</Translate>
</p> </p>
<input <input
id="check" id="check"
@ -67,7 +74,7 @@ class ResetNotification extends Component<any, State> {
}} }}
/>{" "} />{" "}
<label htmlFor="check"> <label htmlFor="check">
I understand that I will lose all my data <Translate>I understand that I will lose all my data</Translate>
</label> </label>
<br /> <br />
<button <button
@ -75,15 +82,21 @@ class ResetNotification extends Component<any, State> {
disabled={!this.state.checked} disabled={!this.state.checked}
onClick={() => wxApi.resetDb()} onClick={() => wxApi.resetDb()}
> >
Reset <Translate>Reset</Translate>
</button> </button>
</div> </div>
); );
} }
return ( return (
<div> <div>
<h1>Everything is fine!</h1>A reset is not required anymore, you can <h1>
close this page. <Translate>Everything is fine!</Translate>
</h1>
<p>
<Translate>
A reset is not required anymore, you can close this page.
</Translate>
</p>
</div> </div>
); );
} }

View File

@ -14,16 +14,21 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { Translate } from "@gnu-taler/taler-util";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
/** /**
* Return coins to own bank account. * Return coins to own bank account.
* *
* @author Florian Dold * @author sebasjm
*/ */
/** /**
* Imports. * Imports.
*/ */
export function createReturnCoinsPage(): VNode { export function createReturnCoinsPage(): VNode {
return <span>Not implemented yet.</span>; return (
<span>
<Translate>Not implemented yet.</Translate>
</span>
);
} }

File diff suppressed because it is too large Load Diff

View File

@ -17,275 +17,38 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: src/NavigationBar.tsx:86 #: src/NavigationBar.tsx:71
#, c-format
msgid "Balance"
msgstr ""
#: src/NavigationBar.tsx:87
#, c-format
msgid "Pending"
msgstr ""
#: src/NavigationBar.tsx:88
#, c-format
msgid "Backup"
msgstr ""
#: src/NavigationBar.tsx:89
#, c-format #, c-format
msgid "Settings" msgid "Settings"
msgstr "" msgstr ""
#: src/NavigationBar.tsx:90 #: src/popup/DeveloperPage.tsx:377
#, c-format
msgid "Dev"
msgstr ""
#: src/wallet/BackupPage.tsx:127
#, c-format
msgid "Add provider"
msgstr ""
#: src/wallet/BackupPage.tsx:137
#, c-format
msgid "Sync all backups"
msgstr ""
#: src/wallet/BackupPage.tsx:139
#, c-format
msgid "Sync now"
msgstr ""
#: src/popup/BalancePage.tsx:79
#, c-format
msgid "You have no balance to show. Need some %1$s getting started?"
msgstr ""
#: src/wallet/ProviderAddPage.tsx:145
#, c-format
msgid "&lt; Back"
msgstr ""
#: src/wallet/ProviderAddPage.tsx:156
#, c-format
msgid "Next"
msgstr ""
#: src/wallet/ProviderDetailPage.tsx:57
#, c-format
msgid "Loading..."
msgstr ""
#: src/wallet/ProviderDetailPage.tsx:64
#, c-format
msgid "There was an error loading the provider detail for \"%1$s\""
msgstr ""
#: src/wallet/ProviderDetailPage.tsx:75
#, c-format
msgid "There is not known provider with url \"%1$s\". Redirecting back..."
msgstr ""
#: src/wallet/ProviderDetailPage.tsx:131
#, c-format
msgid "Back up"
msgstr ""
#: src/wallet/ProviderDetailPage.tsx:142
#, c-format
msgid "Extend"
msgstr ""
#: src/wallet/ProviderDetailPage.tsx:148
#, c-format #, c-format
msgid "" msgid ""
"terms has changed, extending the service will imply accepting the new terms of " "Do you want to IRREVOCABLY DESTROY everything inside your wallet and LOSE ALL "
"service" "YOUR COINS?"
msgstr "" msgstr ""
#: src/wallet/ProviderDetailPage.tsx:158 #: src/wallet/CreateManualWithdraw.tsx:102
#, c-format #, c-format
msgid "old" msgid "Manual Withdrawal"
msgstr "" msgstr ""
#: src/wallet/ProviderDetailPage.tsx:162 #: src/wallet/CreateManualWithdraw.tsx:104
#, c-format #, c-format
msgid "new" msgid ""
"Choose a exchange from where the coins will be withdrawn. The exchange\n"
" will send the coins to this wallet after receiving a wire transfer\n"
" with the correct subject."
msgstr "" msgstr ""
#: src/wallet/ProviderDetailPage.tsx:169 #: src/wallet/CreateManualWithdraw.tsx:109
#, c-format #, c-format
msgid "fee" msgid "No exchange configured"
msgstr "" msgstr ""
#: src/wallet/ProviderDetailPage.tsx:177 #: src/wallet/CreateManualWithdraw.tsx:111
#, c-format
msgid "storage"
msgstr ""
#: src/wallet/ProviderDetailPage.tsx:190
#, c-format
msgid "&lt; back"
msgstr ""
#: src/wallet/ProviderDetailPage.tsx:194
#, c-format
msgid "remove provider"
msgstr ""
#: src/wallet/ProviderDetailPage.tsx:213
#, c-format
msgid "There is conflict with another backup from %1$s"
msgstr ""
#: src/wallet/ProviderDetailPage.tsx:228
#, c-format
msgid "Unknown backup problem: %1$s"
msgstr ""
#: src/wallet/ProviderDetailPage.tsx:247
#, c-format
msgid "service paid"
msgstr ""
#: src/popup/Settings.tsx:46
#, c-format
msgid "Permissions"
msgstr ""
#: src/cta/TermsOfServiceSection.tsx:37
#, c-format
msgid "Exchange doesn't have terms of service"
msgstr ""
#: src/cta/TermsOfServiceSection.tsx:56
#, c-format
msgid "Review exchange terms of service"
msgstr ""
#: src/cta/TermsOfServiceSection.tsx:63
#, c-format
msgid "Review new version of terms of service"
msgstr ""
#: src/cta/TermsOfServiceSection.tsx:75
#, c-format
msgid "Show terms of service"
msgstr ""
#: src/cta/TermsOfServiceSection.tsx:83
#, c-format
msgid "I accept the exchange terms of service"
msgstr ""
#: src/cta/TermsOfServiceSection.tsx:127
#, c-format
msgid "Hide terms of service"
msgstr ""
#: src/wallet/ExchangeAddConfirm.tsx:110
#, c-format
msgid "Cancel"
msgstr ""
#: src/wallet/ExchangeAddConfirm.tsx:114
#, c-format
msgid "Loading terms.."
msgstr ""
#: src/wallet/ExchangeAddConfirm.tsx:121
#, c-format #, c-format
msgid "Add exchange" msgid "Add exchange"
msgstr "" msgstr ""
#: src/wallet/ExchangeAddConfirm.tsx:131
#, c-format
msgid "Add exchange anyway"
msgstr ""
#: src/wallet/Settings.tsx:95
#, c-format
msgid "Known exchanges"
msgstr ""
#: src/wallet/Transaction.tsx:159
#, c-format
msgid "retry"
msgstr ""
#: src/wallet/Transaction.tsx:163
#, c-format
msgid "Forget"
msgstr ""
#: src/wallet/Transaction.tsx:198
#, c-format
msgid "Confirm"
msgstr ""
#: src/cta/Pay.tsx:211
#, c-format
msgid "Pay with a mobile phone"
msgstr ""
#: src/cta/Pay.tsx:211
#, c-format
msgid "Hide QR"
msgstr ""
#: src/cta/Pay.tsx:241
#, c-format
msgid "Pay"
msgstr ""
#: src/cta/Pay.tsx:265
#, c-format
msgid "Withdraw digital cash"
msgstr ""
#: src/cta/Pay.tsx:295
#, c-format
msgid "Digital cash payment"
msgstr ""
#: src/cta/Withdraw.tsx:101
#, c-format
msgid "Digital cash withdrawal"
msgstr ""
#: src/cta/Withdraw.tsx:149
#, c-format
msgid "Cancel exchange selection"
msgstr ""
#: src/cta/Withdraw.tsx:150
#, c-format
msgid "Confirm exchange selection"
msgstr ""
#: src/cta/Withdraw.tsx:155
#, c-format
msgid "Switch exchange"
msgstr ""
#: src/cta/Withdraw.tsx:174
#, c-format
msgid "Confirm withdrawal"
msgstr ""
#: src/cta/Withdraw.tsx:183
#, c-format
msgid "Withdraw anyway"
msgstr ""
#: src/cta/Withdraw.tsx:310
#, c-format
msgid "missing withdraw uri"
msgstr ""
#: src/cta/Deposit.tsx:186
#, c-format
msgid "Digital cash deposit"
msgstr ""

View File

@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { Amounts, Balance } from "@gnu-taler/taler-util"; import { Amounts, Balance, Translate } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { BalanceTable } from "../components/BalanceTable"; import { BalanceTable } from "../components/BalanceTable";
@ -47,7 +47,12 @@ export function BalancePage({
} }
if (state.hasError) { if (state.hasError) {
return <LoadingError title="Could not load balance page" error={state} />; return (
<LoadingError
title={<Translate>Could not load balance page</Translate>}
error={state}
/>
);
} }
if (addingAction) { if (addingAction) {
@ -99,17 +104,19 @@ export function BalanceView({
</section> </section>
<footer style={{ justifyContent: "space-between" }}> <footer style={{ justifyContent: "space-between" }}>
<ButtonPrimary onClick={goToWalletManualWithdraw}> <ButtonPrimary onClick={goToWalletManualWithdraw}>
Withdraw <Translate>Withdraw</Translate>
</ButtonPrimary> </ButtonPrimary>
{currencyWithNonZeroAmount.length > 0 && ( {currencyWithNonZeroAmount.length > 0 && (
<MultiActionButton <MultiActionButton
label={(s) => `Deposit ${s}`} label={(s) => <Translate>Deposit {s}</Translate>}
actions={currencyWithNonZeroAmount} actions={currencyWithNonZeroAmount}
onClick={(c) => goToWalletDeposit(c)} onClick={(c) => goToWalletDeposit(c)}
/> />
)} )}
<JustInDevMode> <JustInDevMode>
<ButtonBoxPrimary onClick={goToAddAction}>enter uri</ButtonBoxPrimary> <ButtonBoxPrimary onClick={goToAddAction}>
<Translate>Enter URI</Translate>
</ButtonBoxPrimary>
</JustInDevMode> </JustInDevMode>
</footer> </footer>
</Fragment> </Fragment>

View File

@ -20,6 +20,8 @@ import {
CoinDumpJson, CoinDumpJson,
ExchangeListItem, ExchangeListItem,
NotificationType, NotificationType,
Translate,
i18n,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { PendingTaskInfo } from "@gnu-taler/taler-wallet-core"; import { PendingTaskInfo } from "@gnu-taler/taler-wallet-core";
import { format } from "date-fns"; import { format } from "date-fns";
@ -31,7 +33,6 @@ import { Time } from "../components/Time";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
import { useDiagnostics } from "../hooks/useDiagnostics"; import { useDiagnostics } from "../hooks/useDiagnostics";
import * as wxApi from "../wxApi"; import * as wxApi from "../wxApi";
import BalanceStories from "./Balance.stories";
export function DeveloperPage(): VNode { export function DeveloperPage(): VNode {
const [status, timedOut] = useDiagnostics(); const [status, timedOut] = useDiagnostics();
@ -149,10 +150,16 @@ export function View({
return ( return (
<div> <div>
<p>Debug tools:</p> <p>
<button onClick={confirmReset}>reset</button> <Translate>Debug tools</Translate>:
</p>
<button onClick={confirmReset}>
<Translate>reset</Translate>
</button>
<br /> <br />
<button onClick={() => fileRef?.current?.click()}>import database</button> <button onClick={() => fileRef?.current?.click()}>
<Translate>import database</Translate>
</button>
<input <input
ref={fileRef} ref={fileRef}
style={{ display: "none" }} style={{ display: "none" }}
@ -171,31 +178,36 @@ export function View({
}} }}
/> />
<br /> <br />
<button onClick={onExportDatabase}>export database</button> <button onClick={onExportDatabase}>
<Translate>export database</Translate>
</button>
{downloadedDatabase && ( {downloadedDatabase && (
<div> <div>
Database exported at <Translate>
<Time Database exported at
timestamp={{ t_ms: downloadedDatabase.time.getTime() }} <Time
format="yyyy/MM/dd HH:mm:ss" timestamp={{ t_ms: downloadedDatabase.time.getTime() }}
/> format="yyyy/MM/dd HH:mm:ss"
<a />
href={`data:text/plain;charset=utf-8;base64,${toBase64( <a
downloadedDatabase.content, href={`data:text/plain;charset=utf-8;base64,${toBase64(
)}`} downloadedDatabase.content,
download={`taler-wallet-database-${format( )}`}
downloadedDatabase.time, download={`taler-wallet-database-${format(
"yyyy/MM/dd_HH:mm", downloadedDatabase.time,
)}.json`} "yyyy/MM/dd_HH:mm",
> )}.json`}
{" "} >
click here{" "} <Translate>click here</Translate>
</a> </a>
to download to download
</Translate>
</div> </div>
)} )}
<br /> <br />
<p>Coins:</p> <p>
<Translate>Coins</Translate>:
</p>
{Object.keys(money_by_exchange).map((ex) => { {Object.keys(money_by_exchange).map((ex) => {
const allcoins = money_by_exchange[ex]; const allcoins = money_by_exchange[ex];
allcoins.sort((a, b) => { allcoins.sort((a, b) => {
@ -220,7 +232,9 @@ export function View({
<Diagnostics diagnostics={status} timedOut={timedOut} /> <Diagnostics diagnostics={status} timedOut={timedOut} />
{operations && operations.length > 0 && ( {operations && operations.length > 0 && (
<Fragment> <Fragment>
<p>Pending operations</p> <p>
<Translate>Pending operations</Translate>
</p>
<dl> <dl>
{operations.reverse().map((o) => { {operations.reverse().map((o) => {
return ( return (
@ -257,18 +271,30 @@ function ShowAllCoins({
<b>{ex}</b>: {total} {currencies[ex]} <b>{ex}</b>: {total} {currencies[ex]}
</p> </p>
<p> <p>
<b>usable coins</b> <b>
<Translate>usable coins</Translate>
</b>
</p> </p>
{collapsedUnspent ? ( {collapsedUnspent ? (
<div onClick={() => setCollapsedUnspent(false)}>click to show</div> <div onClick={() => setCollapsedUnspent(false)}>click to show</div>
) : ( ) : (
<table onClick={() => setCollapsedUnspent(true)}> <table onClick={() => setCollapsedUnspent(true)}>
<tr> <tr>
<td>id</td> <td>
<td>denom</td> <Translate>id</Translate>
<td>value</td> </td>
<td>status</td> <td>
<td>from refresh?</td> <Translate>denom</Translate>
</td>
<td>
<Translate>value</Translate>
</td>
<td>
<Translate>status</Translate>
</td>
<td>
<Translate>from refresh?</Translate>
</td>
</tr> </tr>
{coins.usable.map((c) => { {coins.usable.map((c) => {
return ( return (
@ -283,17 +309,31 @@ function ShowAllCoins({
})} })}
</table> </table>
)} )}
<p>spent coins</p> <p>
<Translate>spent coins</Translate>
</p>
{collapsedSpent ? ( {collapsedSpent ? (
<div onClick={() => setCollapsedSpent(false)}>click to show</div> <div onClick={() => setCollapsedSpent(false)}>
<Translate>click to show</Translate>
</div>
) : ( ) : (
<table onClick={() => setCollapsedSpent(true)}> <table onClick={() => setCollapsedSpent(true)}>
<tr> <tr>
<td>id</td> <td>
<td>denom</td> <Translate>id</Translate>
<td>value</td> </td>
<td>status</td> <td>
<td>refresh?</td> <Translate>denom</Translate>
</td>
<td>
<Translate>value</Translate>
</td>
<td>
<Translate>status</Translate>
</td>
<td>
<Translate>from refresh?</Translate>
</td>
</tr> </tr>
{coins.spent.map((c) => { {coins.spent.map((c) => {
return ( return (
@ -335,8 +375,7 @@ function runIntegrationTest() {}
export async function confirmReset(): Promise<void> { export async function confirmReset(): Promise<void> {
if ( if (
confirm( confirm(
"Do you want to IRREVOCABLY DESTROY everything inside your" + i18n.str`Do you want to IRREVOCABLY DESTROY everything inside your wallet and LOSE ALL YOUR COINS?`,
" wallet and LOSE ALL YOUR COINS?",
) )
) { ) {
await wxApi.resetDb(); await wxApi.resetDb();

View File

@ -1,4 +1,4 @@
import { i18n } from "@gnu-taler/taler-util"; import { Translate } from "@gnu-taler/taler-util";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
import { ButtonBoxWarning, WarningBox } from "../components/styled"; import { ButtonBoxWarning, WarningBox } from "../components/styled";
@ -11,16 +11,16 @@ export function NoBalanceHelp({
<WarningBox> <WarningBox>
<p> <p>
<b> <b>
<i18n.Translate>You have no balance to show.</i18n.Translate> <Translate>You have no balance to show.</Translate>
</b> </b>
<br /> <br />
<i18n.Translate> <Translate>
To withdraw money you can start from your bank site or click the To withdraw money you can start from your bank site or click the
"withdraw" button to use a known exchange. "withdraw" button to use a known exchange.
</i18n.Translate> </Translate>
</p> </p>
<ButtonBoxWarning onClick={() => goToWalletManualWithdraw()}> <ButtonBoxWarning onClick={() => goToWalletManualWithdraw()}>
Withdraw <Translate>Withdraw</Translate>
</ButtonBoxWarning> </ButtonBoxWarning>
</WarningBox> </WarningBox>
); );

View File

@ -19,7 +19,11 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { classifyTalerUri, TalerUriType } from "@gnu-taler/taler-util"; import {
classifyTalerUri,
TalerUriType,
Translate,
} from "@gnu-taler/taler-util";
import { Fragment, h } from "preact"; import { Fragment, h } from "preact";
import { ButtonPrimary, ButtonSuccess } from "../components/styled"; import { ButtonPrimary, ButtonSuccess } from "../components/styled";
import { actionForTalerUri } from "../utils/index"; import { actionForTalerUri } from "../utils/index";
@ -52,46 +56,56 @@ export function TalerActionFound({ url, onDismiss }: Props) {
return ( return (
<Fragment> <Fragment>
<section> <section>
<h1>Taler Action </h1> <h1>
<Translate>Taler Action</Translate>
</h1>
{uriType === TalerUriType.TalerPay && ( {uriType === TalerUriType.TalerPay && (
<div> <div>
<p>This page has pay action.</p> <p>
<Translate>This page has pay action.</Translate>
</p>
<ButtonSuccess <ButtonSuccess
onClick={() => { onClick={() => {
navigateTo(actionForTalerUri(uriType, url)); navigateTo(actionForTalerUri(uriType, url));
}} }}
> >
Open pay page <Translate>Open pay page</Translate>
</ButtonSuccess> </ButtonSuccess>
</div> </div>
)} )}
{uriType === TalerUriType.TalerWithdraw && ( {uriType === TalerUriType.TalerWithdraw && (
<div> <div>
<p>This page has a withdrawal action.</p> <p>
<Translate>This page has a withdrawal action.</Translate>
</p>
<ButtonSuccess <ButtonSuccess
onClick={() => { onClick={() => {
navigateTo(actionForTalerUri(uriType, url)); navigateTo(actionForTalerUri(uriType, url));
}} }}
> >
Open withdraw page <Translate>Open withdraw page</Translate>
</ButtonSuccess> </ButtonSuccess>
</div> </div>
)} )}
{uriType === TalerUriType.TalerTip && ( {uriType === TalerUriType.TalerTip && (
<div> <div>
<p>This page has a tip action.</p> <p>
<Translate>This page has a tip action.</Translate>
</p>
<ButtonSuccess <ButtonSuccess
onClick={() => { onClick={() => {
navigateTo(actionForTalerUri(uriType, url)); navigateTo(actionForTalerUri(uriType, url));
}} }}
> >
Open tip page <Translate>Open tip page</Translate>
</ButtonSuccess> </ButtonSuccess>
</div> </div>
)} )}
{uriType === TalerUriType.TalerNotifyReserve && ( {uriType === TalerUriType.TalerNotifyReserve && (
<div> <div>
<p>This page has a notify reserve action.</p> <p>
<Translate>This page has a notify reserve action.</Translate>
</p>
<ButtonSuccess <ButtonSuccess
onClick={() => { onClick={() => {
navigateTo(actionForTalerUri(uriType, url)); navigateTo(actionForTalerUri(uriType, url));
@ -103,26 +117,33 @@ export function TalerActionFound({ url, onDismiss }: Props) {
)} )}
{uriType === TalerUriType.TalerRefund && ( {uriType === TalerUriType.TalerRefund && (
<div> <div>
<p>This page has a refund action.</p> <p>
<Translate>This page has a refund action.</Translate>
</p>
<ButtonSuccess <ButtonSuccess
onClick={() => { onClick={() => {
navigateTo(actionForTalerUri(uriType, url)); navigateTo(actionForTalerUri(uriType, url));
}} }}
> >
Open refund page <Translate>Open refund page</Translate>
</ButtonSuccess> </ButtonSuccess>
</div> </div>
)} )}
{uriType === TalerUriType.Unknown && ( {uriType === TalerUriType.Unknown && (
<div> <div>
<p>This page has a malformed taler uri.</p> <p>
<Translate>This page has a malformed taler uri.</Translate>
</p>
<p>{url}</p> <p>{url}</p>
</div> </div>
)} )}
</section> </section>
<footer> <footer>
<div /> <div />
<ButtonPrimary onClick={() => onDismiss()}> Dismiss </ButtonPrimary> <ButtonPrimary onClick={() => onDismiss()}>
{" "}
<Translate>Dismiss</Translate>{" "}
</ButtonPrimary>
</footer> </footer>
</Fragment> </Fragment>
); );

View File

@ -17,10 +17,10 @@
/** /**
* Main entry point for extension pages. * Main entry point for extension pages.
* *
* @author Florian Dold <dold@taler.net> * @author sebasjm <dold@taler.net>
*/ */
import { setupI18n } from "@gnu-taler/taler-util"; import { setupI18n, Translate } from "@gnu-taler/taler-util";
import { createHashHistory } from "history"; import { createHashHistory } from "history";
import { Fragment, h, render, VNode } from "preact"; import { Fragment, h, render, VNode } from "preact";
import Router, { route, Route } from "preact-router"; import Router, { route, Route } from "preact-router";
@ -87,27 +87,20 @@ function Application(): VNode {
<CheckTalerActionComponent /> <CheckTalerActionComponent />
<PopupBox devMode={devMode}> <PopupBox devMode={devMode}>
<Router history={hash_history}> <Router history={hash_history}>
<Route path={Pages.dev} component={DeveloperPage} />
<Route <Route
path={Pages.balance} path={Pages.balance}
component={BalancePage} component={BalancePage}
goToWalletManualWithdraw={() => goToWalletManualWithdraw={() =>
goToWalletPage( route(Pages.balance_manual_withdraw.replace(":currency?", ""))
Pages.balance_manual_withdraw.replace(":currency?", ""),
)
} }
goToWalletDeposit={(currency: string) => goToWalletDeposit={(currency: string) =>
goToWalletPage( route(Pages.balance_deposit.replace(":currency", currency))
Pages.balance_deposit.replace(":currency", currency),
)
} }
goToWalletHistory={(currency: string) => goToWalletHistory={(currency: string) =>
goToWalletPage( route(Pages.balance_history.replace(":currency", currency))
Pages.balance_history.replace(":currency", currency),
)
} }
/> />
<Route <Route
path={Pages.cta} path={Pages.cta}
component={function Action({ action }: { action: string }) { component={function Action({ action }: { action: string }) {
@ -139,21 +132,29 @@ function Application(): VNode {
route(Pages.backup); route(Pages.backup);
}} }}
/> />
<Route
path={Pages.backup_provider_add}
component={ProviderAddPage}
onBack={() => {
route(Pages.backup);
}}
/>
<Route <Route
path={Pages.settings_exchange_add} path={Pages.balance_manual_withdraw}
component={ExchangeAddPage} component={RedirectToWalletPage}
onBack={() => {
route(Pages.balance);
}}
/> />
<Route
path={Pages.balance_deposit}
component={RedirectToWalletPage}
/>
<Route
path={Pages.balance_history}
component={RedirectToWalletPage}
/>
<Route
path={Pages.backup_provider_add}
component={RedirectToWalletPage}
/>
<Route path={Pages.settings} component={RedirectToWalletPage} />
<Route
path={Pages.settings_exchange_add}
component={RedirectToWalletPage}
/>
<Route path={Pages.dev} component={RedirectToWalletPage} />
<Route default component={Redirect} to={Pages.balance} /> <Route default component={Redirect} to={Pages.balance} />
</Router> </Router>
@ -165,15 +166,26 @@ function Application(): VNode {
); );
} }
async function goToWalletPage(page: Pages | string): Promise<void> { function RedirectToWalletPage(): VNode {
// eslint-disable-next-line no-undef const page = document.location.hash || "#/";
await chrome.tabs.create({ useEffect(() => {
active: true, chrome.tabs
// eslint-disable-next-line no-undef .create({
url: chrome.runtime.getURL(`/static/wallet.html#${page}`), active: true,
// eslint-disable-next-line no-undef
url: chrome.runtime.getURL(`/static/wallet.html${page}`),
})
.then(() => {
window.close();
});
}); });
window.close(); return (
// return null; <span>
<Translate>
this popup is being closed and you are being redirected to {page}
</Translate>
</span>
);
} }
function Redirect({ to }: { to: string }): null { function Redirect({ to }: { to: string }): null {

View File

@ -1,4 +1,8 @@
import { classifyTalerUri, TalerUriType } from "@gnu-taler/taler-util"; import {
classifyTalerUri,
TalerUriType,
Translate,
} from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Button, ButtonSuccess, InputWithLabel } from "../components/styled"; import { Button, ButtonSuccess, InputWithLabel } from "../components/styled";
@ -8,20 +12,20 @@ export interface Props {
onCancel: () => void; onCancel: () => void;
} }
function buttonLabelByTalerType(type: TalerUriType): string { function buttonLabelByTalerType(type: TalerUriType): VNode {
switch (type) { switch (type) {
case TalerUriType.TalerNotifyReserve: case TalerUriType.TalerNotifyReserve:
return "Open reserve page"; return <Translate>Open reserve page</Translate>;
case TalerUriType.TalerPay: case TalerUriType.TalerPay:
return "Open pay page"; return <Translate>Open pay page</Translate>;
case TalerUriType.TalerRefund: case TalerUriType.TalerRefund:
return "Open refund page"; return <Translate>Open refund page</Translate>;
case TalerUriType.TalerTip: case TalerUriType.TalerTip:
return "Open tip page"; return <Translate>Open tip page</Translate>;
case TalerUriType.TalerWithdraw: case TalerUriType.TalerWithdraw:
return "Open withdraw page"; return <Translate>Open withdraw page</Translate>;
} }
return ""; return <Fragment />;
} }
export function AddNewActionView({ onCancel }: Props): VNode { export function AddNewActionView({ onCancel }: Props): VNode {
@ -47,7 +51,9 @@ export function AddNewActionView({ onCancel }: Props): VNode {
</InputWithLabel> </InputWithLabel>
</section> </section>
<footer> <footer>
<Button onClick={onCancel}>Back</Button> <Button onClick={onCancel}>
<Translate>Back</Translate>
</Button>
{uriType !== TalerUriType.Unknown && ( {uriType !== TalerUriType.Unknown && (
<ButtonSuccess <ButtonSuccess
onClick={() => { onClick={() => {

View File

@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { i18n, Timestamp } from "@gnu-taler/taler-util"; import { i18n, Timestamp, Translate } from "@gnu-taler/taler-util";
import { import {
ProviderInfo, ProviderInfo,
ProviderPaymentPaid, ProviderPaymentPaid,
@ -71,7 +71,10 @@ export function BackupPage({ onAddProvider }: Props): VNode {
} }
if (status.hasError) { if (status.hasError) {
return ( return (
<LoadingError title="Could not load backup providers" error={status} /> <LoadingError
title={<Translate>Could not load backup providers</Translate>}
error={status}
/>
); );
} }
@ -122,9 +125,11 @@ export function BackupView({
))} ))}
{!providers.length && ( {!providers.length && (
<Centered style={{ marginTop: 100 }}> <Centered style={{ marginTop: 100 }}>
<BoldLight>No backup providers configured</BoldLight> <BoldLight>
<Translate>No backup providers configured</Translate>
</BoldLight>
<ButtonSuccess onClick={onAddProvider}> <ButtonSuccess onClick={onAddProvider}>
<i18n.Translate>Add provider</i18n.Translate> <Translate>Add provider</Translate>
</ButtonSuccess> </ButtonSuccess>
</Centered> </Centered>
)} )}
@ -135,12 +140,14 @@ export function BackupView({
<div> <div>
<ButtonPrimary onClick={onSyncAll}> <ButtonPrimary onClick={onSyncAll}>
{providers.length > 1 ? ( {providers.length > 1 ? (
<i18n.Translate>Sync all backups</i18n.Translate> <Translate>Sync all backups</Translate>
) : ( ) : (
<i18n.Translate>Sync now</i18n.Translate> <Translate>Sync now</Translate>
)} )}
</ButtonPrimary> </ButtonPrimary>
<ButtonSuccess onClick={onAddProvider}>Add provider</ButtonSuccess> <ButtonSuccess onClick={onAddProvider}>
<Translate>Add provider</Translate>
</ButtonSuccess>
</div> </div>
</footer> </footer>
)} )}
@ -176,10 +183,14 @@ function BackupLayout(props: TransactionLayoutProps): VNode {
</a> </a>
{dateStr && ( {dateStr && (
<SmallText style={{ marginTop: 5 }}>Last synced: {dateStr}</SmallText> <SmallText style={{ marginTop: 5 }}>
<Translate>Last synced</Translate>: {dateStr}
</SmallText>
)} )}
{!dateStr && ( {!dateStr && (
<SmallLightText style={{ marginTop: 5 }}>Not synced</SmallLightText> <SmallLightText style={{ marginTop: 5 }}>
<Translate>Not synced</Translate>
</SmallLightText>
)} )}
</div> </div>
<div> <div>
@ -196,7 +207,9 @@ function BackupLayout(props: TransactionLayoutProps): VNode {
function ExpirationText({ until }: { until: Timestamp }): VNode { function ExpirationText({ until }: { until: Timestamp }): VNode {
return ( return (
<Fragment> <Fragment>
<CenteredText> Expires in </CenteredText> <CenteredText>
<Translate>Expires in</Translate>
</CenteredText>
<CenteredBoldText {...{ color: colorByTimeToExpire(until) }}> <CenteredBoldText {...{ color: colorByTimeToExpire(until) }}>
{" "} {" "}
{daysUntil(until)}{" "} {daysUntil(until)}{" "}

View File

@ -19,7 +19,7 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { AmountJson, Amounts, i18n } from "@gnu-taler/taler-util"; import { AmountJson, Amounts, i18n, Translate } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { ErrorMessage } from "../components/ErrorMessage"; import { ErrorMessage } from "../components/ErrorMessage";
@ -99,16 +99,22 @@ export function CreateManualWithdraw({
if (!initialExchange) { if (!initialExchange) {
return ( return (
<section> <section>
<h2>Manual Withdrawal</h2> <h2>
<Translate>Manual Withdrawal</Translate>
</h2>
<LightText> <LightText>
Choose a exchange from where the coins will be withdrawn. The exchange <Translate>
will send the coins to this wallet after receiving a wire transfer Choose a exchange from where the coins will be withdrawn. The
with the correct subject. exchange will send the coins to this wallet after receiving a wire
transfer with the correct subject.
</Translate>
</LightText> </LightText>
<Centered style={{ marginTop: 100 }}> <Centered style={{ marginTop: 100 }}>
<BoldLight>No exchange configured</BoldLight> <BoldLight>
<Translate>No exchange configured</Translate>
</BoldLight>
<ButtonSuccess onClick={onAddExchange}> <ButtonSuccess onClick={onAddExchange}>
<i18n.Translate>Add exchange</i18n.Translate> <Translate>Add exchange</Translate>
</ButtonSuccess> </ButtonSuccess>
</Centered> </Centered>
</section> </section>
@ -118,20 +124,26 @@ export function CreateManualWithdraw({
return ( return (
<Fragment> <Fragment>
<section> <section>
<ErrorMessage {error && (
title={error && "Can't create the reserve"} <ErrorMessage
description={error} title={<Translate>Can't create the reserve</Translate>}
/> description={error}
<h2>Manual Withdrawal</h2> />
)}
<h2>
<Translate>Manual Withdrawal</Translate>
</h2>
<LightText> <LightText>
Choose a exchange from where the coins will be withdrawn. The exchange <Translate>
will send the coins to this wallet after receiving a wire transfer Choose a exchange from where the coins will be withdrawn. The
with the correct subject. exchange will send the coins to this wallet after receiving a wire
transfer with the correct subject.
</Translate>
</LightText> </LightText>
<p> <p>
<Input> <Input>
<SelectList <SelectList
label="Currency" label={<Translate>Currency</Translate>}
list={currencyMap} list={currencyMap}
name="currency" name="currency"
value={currency} value={currency}
@ -140,7 +152,7 @@ export function CreateManualWithdraw({
</Input> </Input>
<Input> <Input>
<SelectList <SelectList
label="Exchange" label={<Translate>Exchange</Translate>}
list={exchangeMap} list={exchangeMap}
name="currency" name="currency"
value={exchange} value={exchange}
@ -149,12 +161,14 @@ export function CreateManualWithdraw({
</Input> </Input>
<div style={{ display: "flex", justifyContent: "space-between" }}> <div style={{ display: "flex", justifyContent: "space-between" }}>
<LinkPrimary onClick={onAddExchange} style={{ marginLeft: "auto" }}> <LinkPrimary onClick={onAddExchange} style={{ marginLeft: "auto" }}>
<i18n.Translate>Add exchange</i18n.Translate> <Translate>Add Exchange</Translate>
</LinkPrimary> </LinkPrimary>
</div> </div>
{currency && ( {currency && (
<InputWithLabel invalid={!!amount && !parsedAmount}> <InputWithLabel invalid={!!amount && !parsedAmount}>
<label>Amount</label> <label>
<Translate>Amount</Translate>
</label>
<div> <div>
<span>{currency}</span> <span>{currency}</span>
<input <input
@ -173,7 +187,7 @@ export function CreateManualWithdraw({
disabled={!parsedAmount || !exchange} disabled={!parsedAmount || !exchange}
onClick={() => onCreate(exchange, parsedAmount!)} onClick={() => onCreate(exchange, parsedAmount!)}
> >
Start withdrawal <Translate>Start withdrawal</Translate>
</ButtonPrimary> </ButtonPrimary>
</footer> </footer>
</Fragment> </Fragment>

View File

@ -19,6 +19,7 @@ import {
Amounts, Amounts,
AmountString, AmountString,
PaytoUri, PaytoUri,
Translate,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { DepositFee } from "@gnu-taler/taler-wallet-core/src/operations/deposits"; import { DepositFee } from "@gnu-taler/taler-wallet-core/src/operations/deposits";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
@ -132,13 +133,21 @@ export function View({
}, [amount]); }, [amount]);
if (!balance) { if (!balance) {
return <div>no balance</div>; return (
<div>
<Translate>no balance</Translate>
</div>
);
} }
if (!knownBankAccounts || !knownBankAccounts.length) { if (!knownBankAccounts || !knownBankAccounts.length) {
return ( return (
<WarningBox> <WarningBox>
<p>There is no known bank account to send money to</p> <p>
<ButtonBoxWarning>Withdraw</ButtonBoxWarning> <Translate>There is no known bank account to send money to</Translate>
</p>
<ButtonBoxWarning>
<Translate>Withdraw</Translate>
</ButtonBoxWarning>
</WarningBox> </WarningBox>
); );
} }
@ -162,11 +171,13 @@ export function View({
return ( return (
<Fragment> <Fragment>
<h2>Send {currency} to your account</h2> <h2>
<Translate>Send {currency} to your account</Translate>
</h2>
<section> <section>
<Input> <Input>
<SelectList <SelectList
label="Bank account IBAN number" label={<Translate>Bank account IBAN number</Translate>}
list={accountMap} list={accountMap}
name="account" name="account"
value={String(accountIdx)} value={String(accountIdx)}
@ -174,7 +185,9 @@ export function View({
/> />
</Input> </Input>
<InputWithLabel invalid={!!error}> <InputWithLabel invalid={!!error}>
<label>Amount</label> <label>
<Translate>Amount</Translate>
</label>
<div> <div>
<span>{currency}</span> <span>{currency}</span>
<input <input
@ -196,7 +209,9 @@ export function View({
{ {
<Fragment> <Fragment>
<InputWithLabel> <InputWithLabel>
<label>Deposit fee</label> <label>
<Translate>Deposit fee</Translate>
</label>
<div> <div>
<span>{currency}</span> <span>{currency}</span>
<input <input
@ -208,7 +223,9 @@ export function View({
</InputWithLabel> </InputWithLabel>
<InputWithLabel> <InputWithLabel>
<label>Total deposit</label> <label>
<Translate>Total deposit</Translate>
</label>
<div> <div>
<span>{currency}</span> <span>{currency}</span>
<input <input
@ -224,10 +241,14 @@ export function View({
<footer> <footer>
<div /> <div />
{unableToDeposit ? ( {unableToDeposit ? (
<ButtonPrimary disabled>Deposit</ButtonPrimary> <ButtonPrimary disabled>
<Translate>Deposit</Translate>
</ButtonPrimary>
) : ( ) : (
<ButtonPrimary onClick={() => onSend(accountURI, amountStr)}> <ButtonPrimary onClick={() => onSend(accountURI, amountStr)}>
Deposit {Amounts.stringifyValue(totalToDeposit)} {currency} <Translate>
Deposit {Amounts.stringifyValue(totalToDeposit)} {currency}
</Translate>
</ButtonPrimary> </ButtonPrimary>
)} )}
</footer> </footer>

View File

@ -1,4 +1,4 @@
import { i18n } from "@gnu-taler/taler-util"; import { i18n, Translate } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Button, ButtonSuccess, ButtonWarning } from "../components/styled"; import { Button, ButtonSuccess, ButtonWarning } from "../components/styled";
@ -84,9 +84,11 @@ export function View({
return ( return (
<Fragment> <Fragment>
<section> <section>
<h1>Review terms of service</h1> <h1>
<Translate>Review terms of service</Translate>
</h1>
<div> <div>
Exchange URL: <Translate>Exchange URL</Translate>:
<a href={url} target="_blank" rel="noreferrer"> <a href={url} target="_blank" rel="noreferrer">
{url} {url}
</a> </a>
@ -107,28 +109,28 @@ export function View({
<footer> <footer>
<Button onClick={onCancel}> <Button onClick={onCancel}>
<i18n.Translate>Cancel</i18n.Translate> <Translate>Cancel</Translate>
</Button> </Button>
{!terms && ( {!terms && (
<Button disabled> <Button disabled>
<i18n.Translate>Loading terms..</i18n.Translate> <Translate>Loading terms..</Translate>
</Button> </Button>
)} )}
{terms && ( {terms && (
<Fragment> <Fragment>
{needsReview && !reviewed && ( {needsReview && !reviewed && (
<ButtonSuccess disabled upperCased onClick={onConfirm}> <ButtonSuccess disabled upperCased onClick={onConfirm}>
{i18n.str`Add exchange`} <Translate>Add exchange</Translate>
</ButtonSuccess> </ButtonSuccess>
)} )}
{(terms.status === "accepted" || (needsReview && reviewed)) && ( {(terms.status === "accepted" || (needsReview && reviewed)) && (
<ButtonSuccess upperCased onClick={onConfirm}> <ButtonSuccess upperCased onClick={onConfirm}>
{i18n.str`Add exchange`} <Translate>Add exchange</Translate>
</ButtonSuccess> </ButtonSuccess>
)} )}
{terms.status === "notfound" && ( {terms.status === "notfound" && (
<ButtonWarning upperCased onClick={onConfirm}> <ButtonWarning upperCased onClick={onConfirm}>
{i18n.str`Add exchange anyway`} <Translate>Add exchange anyway</Translate>
</ButtonWarning> </ButtonWarning>
)} )}
</Fragment> </Fragment>

View File

@ -2,6 +2,7 @@ import {
canonicalizeBaseUrl, canonicalizeBaseUrl,
i18n, i18n,
TalerConfigResponse, TalerConfigResponse,
Translate,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { Fragment, h } from "preact"; import { Fragment, h } from "preact";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
@ -91,32 +92,46 @@ export function ExchangeSetUrlPage({
<Fragment> <Fragment>
<section> <section>
{!expectedCurrency ? ( {!expectedCurrency ? (
<h1>Add new exchange</h1> <h1>
<Translate>Add new exchange</Translate>
</h1>
) : ( ) : (
<h2>Add exchange for {expectedCurrency}</h2> <h2>
<Translate>Add exchange for {expectedCurrency}</Translate>
</h2>
)} )}
{!result && ( {!result && (
<LightText>Enter the URL of an exchange you trust.</LightText> <LightText>
<Translate>Enter the URL of an exchange you trust.</Translate>
</LightText>
)} )}
{result && ( {result && (
<LightText> <LightText>
An exchange has been found! Review the information and click next <Translate>
An exchange has been found! Review the information and click next
</Translate>
</LightText> </LightText>
)} )}
{result && expectedCurrency && expectedCurrency !== result.currency && ( {result && expectedCurrency && expectedCurrency !== result.currency && (
<WarningBox> <WarningBox>
This exchange doesn't match the expected currency{" "} <Translate>
<b>{expectedCurrency}</b> This exchange doesn't match the expected currency
<b>{expectedCurrency}</b>
</Translate>
</WarningBox> </WarningBox>
)} )}
<ErrorMessage {error && (
title={error && "Unable to add this exchange"} <ErrorMessage
description={error} title={<Translate>Unable to verify this exchange</Translate>}
/> description={error}
<ErrorMessage />
title={confirmationError && "Unable to add this exchange"} )}
description={confirmationError} {confirmationError && (
/> <ErrorMessage
title={<Translate>Unable to add this exchange</Translate>}
description={confirmationError}
/>
)}
<p> <p>
<Input invalid={!!error}> <Input invalid={!!error}>
<label>URL</label> <label>URL</label>
@ -127,15 +142,23 @@ export function ExchangeSetUrlPage({
onInput={(e) => updateEndpoint(e.currentTarget.value)} onInput={(e) => updateEndpoint(e.currentTarget.value)}
/> />
</Input> </Input>
{loading && <div>loading... </div>} {loading && (
<div>
<Translate>loading</Translate>...
</div>
)}
{result && !loading && ( {result && !loading && (
<Fragment> <Fragment>
<Input> <Input>
<label>Version</label> <label>
<Translate>Version</Translate>
</label>
<input type="text" disabled value={result.version} /> <input type="text" disabled value={result.version} />
</Input> </Input>
<Input> <Input>
<label>Currency</label> <label>
<Translate>Currency</Translate>
</label>
<input type="text" disabled value={result.currency} /> <input type="text" disabled value={result.currency} />
</Input> </Input>
</Fragment> </Fragment>
@ -144,7 +167,7 @@ export function ExchangeSetUrlPage({
</section> </section>
<footer> <footer>
<Button onClick={onCancel}> <Button onClick={onCancel}>
<i18n.Translate>Cancel</i18n.Translate> <Translate>Cancel</Translate>
</Button> </Button>
<ButtonPrimary <ButtonPrimary
disabled={ disabled={
@ -160,7 +183,7 @@ export function ExchangeSetUrlPage({
); );
}} }}
> >
<i18n.Translate>Next</i18n.Translate> <Translate>Next</Translate>
</ButtonPrimary> </ButtonPrimary>
</footer> </footer>
</Fragment> </Fragment>

View File

@ -19,6 +19,7 @@ import {
Balance, Balance,
NotificationType, NotificationType,
Transaction, Transaction,
Translate,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
@ -66,7 +67,7 @@ export function HistoryPage({
if (transactionQuery.hasError) { if (transactionQuery.hasError) {
return ( return (
<LoadingError <LoadingError
title="Could not load the list of transactions" title={<Translate>Could not load the list of transactions</Translate>}
error={transactionQuery} error={transactionQuery}
/> />
); );
@ -193,21 +194,23 @@ export function HistoryView({
style={{ marginLeft: 0, marginTop: 8 }} style={{ marginLeft: 0, marginTop: 8 }}
onClick={() => goToWalletManualWithdraw(selectedCurrency)} onClick={() => goToWalletManualWithdraw(selectedCurrency)}
> >
Withdraw <Translate>Withdraw</Translate>
</ButtonPrimary> </ButtonPrimary>
{currencyAmount && Amounts.isNonZero(currencyAmount) && ( {currencyAmount && Amounts.isNonZero(currencyAmount) && (
<ButtonBoxPrimary <ButtonBoxPrimary
style={{ marginLeft: 0, marginTop: 8 }} style={{ marginLeft: 0, marginTop: 8 }}
onClick={() => goToWalletDeposit(selectedCurrency)} onClick={() => goToWalletDeposit(selectedCurrency)}
> >
Deposit <Translate>Deposit</Translate>
</ButtonBoxPrimary> </ButtonBoxPrimary>
)} )}
</div> </div>
</div> </div>
</section> </section>
{datesWithTransaction.length === 0 ? ( {datesWithTransaction.length === 0 ? (
<section>There is no history for this currency</section> <section>
<Translate>There is no history for this currency</Translate>
</section>
) : ( ) : (
<section> <section>
{datesWithTransaction.map((d, i) => { {datesWithTransaction.map((d, i) => {

View File

@ -19,6 +19,7 @@ import {
AmountJson, AmountJson,
Amounts, Amounts,
NotificationType, NotificationType,
Translate,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
@ -95,7 +96,9 @@ export function ManualWithdrawPage({ currency, onCancel }: Props): VNode {
if (state.hasError) { if (state.hasError) {
return ( return (
<LoadingError <LoadingError
title="Could not load the list of known exchanges" title={
<Translate>Could not load the list of known exchanges</Translate>
}
error={state} error={state}
/> />
); );

View File

@ -19,6 +19,7 @@ import {
BackupBackupProviderTerms, BackupBackupProviderTerms,
canonicalizeBaseUrl, canonicalizeBaseUrl,
i18n, i18n,
Translate,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
@ -113,15 +114,23 @@ export function SetUrlView({
return ( return (
<Fragment> <Fragment>
<section> <section>
<h1> Add backup provider</h1> <h1>
<ErrorMessage <Translate>Add backup provider</Translate>
title={error && "Could not get provider information"} </h1>
description={error} {error && (
/> <ErrorMessage
<LightText> Backup providers may charge for their service</LightText> title={<Translate>Could not get provider information</Translate>}
description={error}
/>
)}
<LightText>
<Translate>Backup providers may charge for their service</Translate>
</LightText>
<p> <p>
<Input invalid={urlError}> <Input invalid={urlError}>
<label>URL</label> <label>
<Translate>URL</Translate>
</label>
<input <input
type="text" type="text"
placeholder="https://" placeholder="https://"
@ -130,7 +139,9 @@ export function SetUrlView({
/> />
</Input> </Input>
<Input> <Input>
<label>Name</label> <label>
<Translate>Name</Translate>
</label>
<input <input
type="text" type="text"
disabled={name === undefined} disabled={name === undefined}
@ -142,7 +153,7 @@ export function SetUrlView({
</section> </section>
<footer> <footer>
<Button onClick={onCancel}> <Button onClick={onCancel}>
<i18n.Translate> &lt; Back</i18n.Translate> &lt; <Translate>Back</Translate>
</Button> </Button>
<ButtonPrimary <ButtonPrimary
disabled={!value && !urlError} disabled={!value && !urlError}
@ -153,7 +164,7 @@ export function SetUrlView({
); );
}} }}
> >
<i18n.Translate>Next</i18n.Translate> <Translate>Next</Translate>
</ButtonPrimary> </ButtonPrimary>
</footer> </footer>
</Fragment> </Fragment>
@ -177,29 +188,41 @@ export function ConfirmProviderView({
return ( return (
<Fragment> <Fragment>
<section> <section>
<h1>Review terms of service</h1> <h1>
<Translate>Review terms of service</Translate>
</h1>
<div> <div>
Provider URL:{" "} <Translate>Provider URL</Translate>:{" "}
<a href={url} target="_blank"> <a href={url} target="_blank">
{url} {url}
</a> </a>
</div> </div>
<SmallLightText> <SmallLightText>
Please review and accept this provider's terms of service <Translate>
Please review and accept this provider's terms of service
</Translate>
</SmallLightText> </SmallLightText>
<h2>1. Pricing</h2> <h2>
1. <Translate>Pricing</Translate>
</h2>
<p> <p>
{Amounts.isZero(provider.annual_fee) {Amounts.isZero(provider.annual_fee) ? (
? "free of charge" <Translate>free of charge</Translate>
: `${provider.annual_fee} per year of service`} ) : (
<Translate>{provider.annual_fee} per year of service</Translate>
)}
</p> </p>
<h2>2. Storage</h2> <h2>
2. <Translate>Storage</Translate>
</h2>
<p> <p>
{provider.storage_limit_in_megabytes} megabytes of storage per year of <Translate>
service {provider.storage_limit_in_megabytes} megabytes of storage per year
of service
</Translate>
</p> </p>
<Checkbox <Checkbox
label="Accept terms of service" label={<Translate>Accept terms of service</Translate>}
name="terms" name="terms"
onToggle={() => setAccepted((old) => !old)} onToggle={() => setAccepted((old) => !old)}
enabled={accepted} enabled={accepted}
@ -207,10 +230,10 @@ export function ConfirmProviderView({
</section> </section>
<footer> <footer>
<Button onClick={onCancel}> <Button onClick={onCancel}>
<i18n.Translate> &lt; Back</i18n.Translate> &lt; <Translate>Back</Translate>
</Button> </Button>
<ButtonPrimary disabled={!accepted} onClick={onConfirm}> <ButtonPrimary disabled={!accepted} onClick={onConfirm}>
<i18n.Translate>Add provider</i18n.Translate> <Translate>Add provider</Translate>
</ButtonPrimary> </ButtonPrimary>
</footer> </footer>
</Fragment> </Fragment>

View File

@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { i18n } from "@gnu-taler/taler-util"; import { Translate } from "@gnu-taler/taler-util";
import { import {
ProviderInfo, ProviderInfo,
ProviderPaymentStatus, ProviderPaymentStatus,
@ -22,6 +22,8 @@ import {
} from "@gnu-taler/taler-wallet-core"; } from "@gnu-taler/taler-wallet-core";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { ErrorMessage } from "../components/ErrorMessage"; import { ErrorMessage } from "../components/ErrorMessage";
import { Loading } from "../components/Loading";
import { LoadingError } from "../components/LoadingError";
import { import {
Button, Button,
ButtonDestructive, ButtonDestructive,
@ -52,35 +54,24 @@ export function ProviderDetailPage({ pid: providerURL, onBack }: Props): VNode {
const state = useAsyncAsHook(getProviderInfo); const state = useAsyncAsHook(getProviderInfo);
if (!state) { if (!state) {
return ( return <Loading />;
<div>
<i18n.Translate>Loading...</i18n.Translate>
</div>
);
} }
if (state.hasError) { if (state.hasError) {
return ( return (
<div> <LoadingError
<i18n.Translate> title={
There was an error loading the provider detail for "{providerURL}" <Translate>
</i18n.Translate> There was an error loading the provider detail for "{providerURL}"
</div> </Translate>
}
error={state}
/>
); );
} }
if (state.response === null) {
onBack();
return (
<div>
<i18n.Translate>
There is not known provider with url "{providerURL}". Redirecting
back...
</i18n.Translate>
</div>
);
}
return ( return (
<ProviderView <ProviderView
url={providerURL}
info={state.response} info={state.response}
onSync={async () => wxApi.syncOneProvider(providerURL)} onSync={async () => wxApi.syncOneProvider(providerURL)}
onDelete={async () => wxApi.removeProvider(providerURL).then(onBack)} onDelete={async () => wxApi.removeProvider(providerURL).then(onBack)}
@ -93,7 +84,8 @@ export function ProviderDetailPage({ pid: providerURL, onBack }: Props): VNode {
} }
export interface ViewProps { export interface ViewProps {
info: ProviderInfo; url: string;
info: ProviderInfo | null;
onDelete: () => void; onDelete: () => void;
onSync: () => void; onSync: () => void;
onBack: () => void; onBack: () => void;
@ -102,12 +94,30 @@ export interface ViewProps {
export function ProviderView({ export function ProviderView({
info, info,
url,
onDelete, onDelete,
onSync, onSync,
onBack, onBack,
onExtend, onExtend,
}: ViewProps): VNode { }: ViewProps): VNode {
const lb = info?.lastSuccessfulBackupTimestamp; if (info === null) {
return (
<Fragment>
<section>
<p>
<Translate>There is not known provider with url "{url}".</Translate>
</p>
</section>
<footer>
<Button onClick={onBack}>
&lt; <Translate>Back</Translate>
</Button>
<div />
</footer>
</Fragment>
);
}
const lb = info.lastSuccessfulBackupTimestamp;
const isPaid = const isPaid =
info.paymentStatus.type === ProviderPaymentType.Paid || info.paymentStatus.type === ProviderPaymentType.Paid ||
info.paymentStatus.type === ProviderPaymentType.TermsChanged; info.paymentStatus.type === ProviderPaymentType.TermsChanged;
@ -125,48 +135,55 @@ export function ProviderView({
</header> </header>
<section> <section>
<p> <p>
<b>Last backup:</b> <Time timestamp={lb} format="dd MMMM yyyy" /> <b>
<Translate>Last backup</Translate>:
</b>{" "}
<Time timestamp={lb} format="dd MMMM yyyy" />
</p> </p>
<ButtonPrimary onClick={onSync}> <ButtonPrimary onClick={onSync}>
<i18n.Translate>Back up</i18n.Translate> <Translate>Back up</Translate>
</ButtonPrimary> </ButtonPrimary>
{info.terms && ( {info.terms && (
<Fragment> <Fragment>
<p> <p>
<b>Provider fee:</b> {info.terms && info.terms.annualFee} per year <b>
<Translate>Provider fee</Translate>:
</b>{" "}
{info.terms && info.terms.annualFee}{" "}
<Translate>per year</Translate>
</p> </p>
</Fragment> </Fragment>
)} )}
<p>{descriptionByStatus(info.paymentStatus)}</p> <p>{descriptionByStatus(info.paymentStatus)}</p>
<ButtonPrimary disabled onClick={onExtend}> <ButtonPrimary disabled onClick={onExtend}>
<i18n.Translate>Extend</i18n.Translate> <Translate>Extend</Translate>
</ButtonPrimary> </ButtonPrimary>
{info.paymentStatus.type === ProviderPaymentType.TermsChanged && ( {info.paymentStatus.type === ProviderPaymentType.TermsChanged && (
<div> <div>
<p> <p>
<i18n.Translate> <Translate>
terms has changed, extending the service will imply accepting terms has changed, extending the service will imply accepting
the new terms of service the new terms of service
</i18n.Translate> </Translate>
</p> </p>
<table> <table>
<thead> <thead>
<tr> <tr>
<td>&nbsp;</td> <td>&nbsp;</td>
<td> <td>
<i18n.Translate>old</i18n.Translate> <Translate>old</Translate>
</td> </td>
<td> -&gt;</td> <td> -&gt;</td>
<td> <td>
<i18n.Translate>new</i18n.Translate> <Translate>new</Translate>
</td> </td>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<td> <td>
<i18n.Translate>fee</i18n.Translate> <Translate>fee</Translate>
</td> </td>
<td>{info.paymentStatus.oldTerms.annualFee}</td> <td>{info.paymentStatus.oldTerms.annualFee}</td>
<td>-&gt;</td> <td>-&gt;</td>
@ -174,7 +191,7 @@ export function ProviderView({
</tr> </tr>
<tr> <tr>
<td> <td>
<i18n.Translate>storage</i18n.Translate> <Translate>storage</Translate>
</td> </td>
<td>{info.paymentStatus.oldTerms.storageLimitInMegabytes}</td> <td>{info.paymentStatus.oldTerms.storageLimitInMegabytes}</td>
<td>-&gt;</td> <td>-&gt;</td>
@ -187,11 +204,11 @@ export function ProviderView({
</section> </section>
<footer> <footer>
<Button onClick={onBack}> <Button onClick={onBack}>
<i18n.Translate> &lt; back</i18n.Translate> &lt; <Translate>back</Translate>
</Button> </Button>
<div> <div>
<ButtonDestructive onClick={onDelete}> <ButtonDestructive onClick={onDelete}>
<i18n.Translate>remove provider</i18n.Translate> <Translate>Remove provider</Translate>
</ButtonDestructive> </ButtonDestructive>
</div> </div>
</footer> </footer>
@ -201,7 +218,12 @@ export function ProviderView({
function Error({ info }: { info: ProviderInfo }): VNode { function Error({ info }: { info: ProviderInfo }): VNode {
if (info.lastError) { if (info.lastError) {
return <ErrorMessage title={info.lastError.hint} />; return (
<ErrorMessage
title={<Translate>This provider has reported an error</Translate>}
description={info.lastError.hint}
/>
);
} }
if (info.backupProblem) { if (info.backupProblem) {
switch (info.backupProblem.type) { switch (info.backupProblem.type) {
@ -210,24 +232,26 @@ function Error({ info }: { info: ProviderInfo }): VNode {
<ErrorMessage <ErrorMessage
title={ title={
<Fragment> <Fragment>
<i18n.Translate> <Translate>
There is conflict with another backup from{" "} There is conflict with another backup from{" "}
<b>{info.backupProblem.otherDeviceId}</b> <b>{info.backupProblem.otherDeviceId}</b>
</i18n.Translate> </Translate>
</Fragment> </Fragment>
} }
/> />
); );
case "backup-unreadable": case "backup-unreadable":
return <ErrorMessage title="Backup is not readable" />; return (
<ErrorMessage title={<Translate>Backup is not readable</Translate>} />
);
default: default:
return ( return (
<ErrorMessage <ErrorMessage
title={ title={
<Fragment> <Fragment>
<i18n.Translate> <Translate>
Unknown backup problem: {JSON.stringify(info.backupProblem)} Unknown backup problem: {JSON.stringify(info.backupProblem)}
</i18n.Translate> </Translate>
</Fragment> </Fragment>
} }
/> />
@ -239,16 +263,20 @@ function Error({ info }: { info: ProviderInfo }): VNode {
function descriptionByStatus(status: ProviderPaymentStatus): VNode { function descriptionByStatus(status: ProviderPaymentStatus): VNode {
switch (status.type) { switch (status.type) {
// return i18n.str`no enough balance to make the payment`
// return i18n.str`not paid yet`
case ProviderPaymentType.Paid: case ProviderPaymentType.Paid:
case ProviderPaymentType.TermsChanged: case ProviderPaymentType.TermsChanged:
if (status.paidUntil.t_ms === "never") { if (status.paidUntil.t_ms === "never") {
return <span>{i18n.str`service paid`}</span>; return (
<span>
<Translate>service paid</Translate>
</span>
);
} }
return ( return (
<Fragment> <Fragment>
<b>Backup valid until:</b>{" "} <b>
<Translate>Backup valid until</Translate>:
</b>{" "}
<Time timestamp={status.paidUntil} format="dd MMM yyyy" /> <Time timestamp={status.paidUntil} format="dd MMM yyyy" />
</Fragment> </Fragment>
); );

View File

@ -1,4 +1,9 @@
import { AmountJson, Amounts, parsePaytoUri } from "@gnu-taler/taler-util"; import {
AmountJson,
Amounts,
parsePaytoUri,
Translate,
} from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { BankDetailsByPaytoType } from "../components/BankDetailsByPaytoType"; import { BankDetailsByPaytoType } from "../components/BankDetailsByPaytoType";
import { QR } from "../components/QR"; import { QR } from "../components/QR";
@ -22,15 +27,23 @@ export function ReserveCreated({
const paytoURI = parsePaytoUri(payto); const paytoURI = parsePaytoUri(payto);
// const url = new URL(paytoURI?.targetPath); // const url = new URL(paytoURI?.targetPath);
if (!paytoURI) { if (!paytoURI) {
return <div>could not parse payto uri from exchange {payto}</div>; return (
<div>
<Translate>could not parse payto uri from exchange {payto}</Translate>
</div>
);
} }
return ( return (
<Fragment> <Fragment>
<section> <section>
<h1>Exchange is ready for withdrawal!</h1> <h1>
<Translate>Exchange is ready for withdrawal</Translate>
</h1>
<p> <p>
To complete the process you need to wire{" "} <Translate>
<b>{amountToString(amount)}</b> to the exchange bank account To complete the process you need to wire
<b>{amountToString(amount)}</b> to the exchange bank account
</Translate>
</p> </p>
<BankDetailsByPaytoType <BankDetailsByPaytoType
amount={amountToString(amount)} amount={amountToString(amount)}
@ -40,23 +53,27 @@ export function ReserveCreated({
/> />
<p> <p>
<WarningBox> <WarningBox>
Make sure to use the correct subject, otherwise the money will not <Translate>
arrive in this wallet. Make sure to use the correct subject, otherwise the money will not
arrive in this wallet.
</Translate>
</WarningBox> </WarningBox>
</p> </p>
</section> </section>
<section> <section>
<p> <p>
Alternative, you can also scan this QR code or open{" "} <Translate>
<a href={payto}>this link</a> if you have a banking app installed that Alternative, you can also scan this QR code or open
supports RFC 8905 <a href={payto}>this link</a> if you have a banking app installed
that supports RFC 8905
</Translate>
</p> </p>
<QR text={payto} /> <QR text={payto} />
</section> </section>
<footer> <footer>
<div /> <div />
<ButtonDestructive onClick={onCancel}> <ButtonDestructive onClick={onCancel}>
Cancel withdrawal <Translate>Cancel withdrawal</Translate>
</ButtonDestructive> </ButtonDestructive>
</footer> </footer>
</Fragment> </Fragment>

View File

@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/ */
import { ExchangeListItem, i18n } from "@gnu-taler/taler-util"; import { ExchangeListItem, i18n, Translate } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { Checkbox } from "../components/Checkbox"; import { Checkbox } from "../components/Checkbox";
import { import {
@ -81,29 +81,46 @@ export function SettingsView({
<Fragment> <Fragment>
<section> <section>
<h2> <h2>
<i18n.Translate>Permissions</i18n.Translate> <Translate>Permissions</Translate>
</h2> </h2>
<Checkbox <Checkbox
label="Automatically open wallet based on page content" label={
<Translate>
Automatically open wallet based on page content
</Translate>
}
name="perm" name="perm"
description="(Enabling this option below will make using the wallet faster, but requires more permissions from your browser.)" description={
<Translate>
Enabling this option below will make using the wallet faster, but
requires more permissions from your browser.
</Translate>
}
enabled={permissionsEnabled} enabled={permissionsEnabled}
onToggle={togglePermissions} onToggle={togglePermissions}
/> />
<h2> <h2>
<i18n.Translate>Known exchanges</i18n.Translate> <Translate>Known exchanges</Translate>
</h2> </h2>
{!knownExchanges || !knownExchanges.length ? ( {!knownExchanges || !knownExchanges.length ? (
<div>No exchange yet!</div> <div>
<Translate>No exchange yet</Translate>
</div>
) : ( ) : (
<Fragment> <Fragment>
<table> <table>
<thead> <thead>
<tr> <tr>
<th>currency</th> <th>
<th>url</th> <Translate>Currency</Translate>
<th>term of service</th> </th>
<th>
<Translate>URL</Translate>
</th>
<th>
<Translate>Term of Service</Translate>
</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -116,12 +133,24 @@ export function SettingsView({
); );
switch (status) { switch (status) {
case "accepted": case "accepted":
return <SuccessText>ok</SuccessText>; return (
<SuccessText>
<Translate>ok</Translate>
</SuccessText>
);
case "changed": case "changed":
return <WarningText>changed!</WarningText>; return (
<WarningText>
<Translate>changed</Translate>
</WarningText>
);
case "new": case "new":
case "notfound": case "notfound":
return <DestructiveText>not accepted</DestructiveText>; return (
<DestructiveText>
<Translate>not accepted</Translate>
</DestructiveText>
);
} }
} }
return ( return (
@ -143,15 +172,19 @@ export function SettingsView({
<div style={{ display: "flex", justifyContent: "space-between" }}> <div style={{ display: "flex", justifyContent: "space-between" }}>
<div /> <div />
<LinkPrimary href={Pages.settings_exchange_add}> <LinkPrimary href={Pages.settings_exchange_add}>
Add an exchange <Translate>Add an exchange</Translate>
</LinkPrimary> </LinkPrimary>
</div> </div>
<h2>Config</h2> <h2>Config</h2>
<Checkbox <Checkbox
label="Developer mode" label={<Translate>Developer mode</Translate>}
name="devMode" name="devMode"
description="(More options and information useful for debugging)" description={
<Translate>
(More options and information useful for debugging)
</Translate>
}
enabled={developerMode} enabled={developerMode}
onToggle={toggleDeveloperMode} onToggle={toggleDeveloperMode}
/> />

View File

@ -22,6 +22,7 @@ import {
parsePaytoUri, parsePaytoUri,
Transaction, Transaction,
TransactionType, TransactionType,
Translate,
WithdrawalType, WithdrawalType,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import { differenceInSeconds } from "date-fns"; import { differenceInSeconds } from "date-fns";
@ -47,7 +48,6 @@ import {
} from "../components/styled"; } from "../components/styled";
import { Time } from "../components/Time"; import { Time } from "../components/Time";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook";
import { Pages } from "../NavigationBar";
import * as wxApi from "../wxApi"; import * as wxApi from "../wxApi";
interface Props { interface Props {
@ -76,7 +76,9 @@ export function TransactionPage({ tid, goToWalletHistory }: Props): VNode {
if (state.hasError) { if (state.hasError) {
return ( return (
<LoadingError <LoadingError
title="Could not load the transaction information" title={
<Translate>Could not load the transaction information</Translate>
}
error={state} error={state}
/> />
); );
@ -139,11 +141,17 @@ export function TransactionView({
<Fragment> <Fragment>
<section style={{ padding: 8, textAlign: "center" }}> <section style={{ padding: 8, textAlign: "center" }}>
<ErrorTalerOperation <ErrorTalerOperation
title="There was an error trying to complete the transaction" title={
<Translate>
There was an error trying to complete the transaction
</Translate>
}
error={transaction?.error} error={transaction?.error}
/> />
{transaction.pending && ( {transaction.pending && (
<WarningBox>This transaction is not completed</WarningBox> <WarningBox>
<Translate>This transaction is not completed</Translate>
</WarningBox>
)} )}
</section> </section>
<section> <section>
@ -151,16 +159,16 @@ export function TransactionView({
</section> </section>
<footer> <footer>
<Button onClick={onBack}> <Button onClick={onBack}>
<i18n.Translate> &lt; Back </i18n.Translate> &lt; <Translate> Back </Translate>
</Button> </Button>
<div> <div>
{showRetry ? ( {showRetry ? (
<ButtonPrimary onClick={onRetry}> <ButtonPrimary onClick={onRetry}>
<i18n.Translate>retry</i18n.Translate> <Translate>Retry</Translate>
</ButtonPrimary> </ButtonPrimary>
) : null} ) : null}
<ButtonDestructive onClick={doCheckBeforeForget}> <ButtonDestructive onClick={doCheckBeforeForget}>
<i18n.Translate> Forget </i18n.Translate> <Translate>Forget</Translate>
</ButtonDestructive> </ButtonDestructive>
</div> </div>
</footer> </footer>
@ -184,24 +192,30 @@ export function TransactionView({
{confirmBeforeForget ? ( {confirmBeforeForget ? (
<Overlay> <Overlay>
<CenteredDialog> <CenteredDialog>
<header>Caution!</header> <header>
<Translate>Caution!</Translate>
</header>
<section> <section>
If you have already wired money to the exchange you will loose <Translate>
the chance to get the coins form it. If you have already wired money to the exchange you will loose
the chance to get the coins form it.
</Translate>
</section> </section>
<footer> <footer>
<Button onClick={() => setConfirmBeforeForget(false)}> <Button onClick={() => setConfirmBeforeForget(false)}>
<i18n.Translate> Cancel </i18n.Translate> <Translate>Cancel</Translate>
</Button> </Button>
<ButtonDestructive onClick={onDelete}> <ButtonDestructive onClick={onDelete}>
<i18n.Translate> Confirm </i18n.Translate> <Translate>Confirm</Translate>
</ButtonDestructive> </ButtonDestructive>
</footer> </footer>
</CenteredDialog> </CenteredDialog>
</Overlay> </Overlay>
) : undefined} ) : undefined}
<h2>Withdrawal</h2> <h2>
<Translate>Withdrawal</Translate>
</h2>
<Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" /> <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
{transaction.pending ? ( {transaction.pending ? (
transaction.withdrawalDetails.type === transaction.withdrawalDetails.type ===
@ -217,19 +231,21 @@ export function TransactionView({
/> />
<p> <p>
<WarningBox> <WarningBox>
Make sure to use the correct subject, otherwise the money will <Translate>
not arrive in this wallet. Make sure to use the correct subject, otherwise the money
will not arrive in this wallet.
</Translate>
</WarningBox> </WarningBox>
</p> </p>
<Part <Part
big big
title="Total withdrawn" title={<Translate>Total withdrawn</Translate>}
text={amountToString(transaction.amountEffective)} text={amountToString(transaction.amountEffective)}
kind="positive" kind="positive"
/> />
<Part <Part
big big
title="Exchange fee" title={<Translate>Exchange fee</Translate>}
text={amountToString(fee)} text={amountToString(fee)}
kind="negative" kind="negative"
/> />
@ -239,34 +255,38 @@ export function TransactionView({
{!transaction.withdrawalDetails.confirmed && {!transaction.withdrawalDetails.confirmed &&
transaction.withdrawalDetails.bankConfirmationUrl ? ( transaction.withdrawalDetails.bankConfirmationUrl ? (
<InfoBox> <InfoBox>
The bank is waiting for confirmation. Go to the <Translate>
<a The bank is waiting for confirmation. Go to the
href={transaction.withdrawalDetails.bankConfirmationUrl} <a
target="_blank" href={transaction.withdrawalDetails.bankConfirmationUrl}
rel="noreferrer" target="_blank"
> rel="noreferrer"
bank site >
</a> <Translate>bank site</Translate>
</a>
</Translate>
</InfoBox> </InfoBox>
) : undefined} ) : undefined}
{transaction.withdrawalDetails.confirmed && ( {transaction.withdrawalDetails.confirmed && (
<InfoBox>Waiting for the coins to arrive</InfoBox> <InfoBox>
<Translate>Waiting for the coins to arrive</Translate>
</InfoBox>
)} )}
<Part <Part
big big
title="Total withdrawn" title={<Translate>Total withdrawn</Translate>}
text={amountToString(transaction.amountEffective)} text={amountToString(transaction.amountEffective)}
kind="positive" kind="positive"
/> />
<Part <Part
big big
title="Chosen amount" title={<Translate>Chosen amount</Translate>}
text={amountToString(transaction.amountRaw)} text={amountToString(transaction.amountRaw)}
kind="neutral" kind="neutral"
/> />
<Part <Part
big big
title="Exchange fee" title={<Translate>Exchange fee</Translate>}
text={amountToString(fee)} text={amountToString(fee)}
kind="negative" kind="negative"
/> />
@ -276,26 +296,26 @@ export function TransactionView({
<Fragment> <Fragment>
<Part <Part
big big
title="Total withdrawn" title={<Translate>Total withdrawn</Translate>}
text={amountToString(transaction.amountEffective)} text={amountToString(transaction.amountEffective)}
kind="positive" kind="positive"
/> />
<Part <Part
big big
title="Chosen amount" title={<Translate>Chosen amount</Translate>}
text={amountToString(transaction.amountRaw)} text={amountToString(transaction.amountRaw)}
kind="neutral" kind="neutral"
/> />
<Part <Part
big big
title="Exchange fee" title={<Translate>Exchange fee</Translate>}
text={amountToString(fee)} text={amountToString(fee)}
kind="negative" kind="negative"
/> />
</Fragment> </Fragment>
)} )}
<Part <Part
title="Exchange" title={<Translate>Exchange</Translate>}
text={new URL(transaction.exchangeBaseUrl).hostname} text={new URL(transaction.exchangeBaseUrl).hostname}
kind="neutral" kind="neutral"
/> />
@ -315,30 +335,41 @@ export function TransactionView({
return ( return (
<TransactionTemplate> <TransactionTemplate>
<h2>Payment </h2> <h2>
<Translate>Payment</Translate>
</h2>
<Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" /> <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
<br /> <br />
<Part <Part
big big
title="Total paid" title={<Translate>Total paid</Translate>}
text={amountToString(transaction.amountEffective)} text={amountToString(transaction.amountEffective)}
kind="negative" kind="negative"
/> />
<Part <Part
big big
title="Purchase amount" title={<Translate>Purchase amount</Translate>}
text={amountToString(transaction.amountRaw)} text={amountToString(transaction.amountRaw)}
kind="neutral" kind="neutral"
/> />
<Part big title="Fee" text={amountToString(fee)} kind="negative" />
<Part <Part
title="Merchant" big
title={<Translate>Fee</Translate>}
text={amountToString(fee)}
kind="negative"
/>
<Part
title={<Translate>Merchant</Translate>}
text={transaction.info.merchant.name} text={transaction.info.merchant.name}
kind="neutral" kind="neutral"
/> />
<Part title="Purchase" text={transaction.info.summary} kind="neutral" />
<Part <Part
title="Receipt" title={<Translate>Purchase</Translate>}
text={transaction.info.summary}
kind="neutral"
/>
<Part
title={<Translate>Receipt</Translate>}
text={`#${transaction.info.orderId}`} text={`#${transaction.info.orderId}`}
kind="neutral" kind="neutral"
/> />
@ -375,22 +406,29 @@ export function TransactionView({
).amount; ).amount;
return ( return (
<TransactionTemplate> <TransactionTemplate>
<h2>Deposit </h2> <h2>
<Translate>Deposit</Translate>
</h2>
<Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" /> <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
<br /> <br />
<Part <Part
big big
title="Total send" title={<Translate>Total send</Translate>}
text={amountToString(transaction.amountEffective)} text={amountToString(transaction.amountEffective)}
kind="neutral" kind="neutral"
/> />
<Part <Part
big big
title="Deposit amount" title={<Translate>Deposit amount</Translate>}
text={amountToString(transaction.amountRaw)} text={amountToString(transaction.amountRaw)}
kind="positive" kind="positive"
/> />
<Part big title="Fee" text={amountToString(fee)} kind="negative" /> <Part
big
title={<Translate>Fee</Translate>}
text={amountToString(fee)}
kind="negative"
/>
</TransactionTemplate> </TransactionTemplate>
); );
} }
@ -402,22 +440,29 @@ export function TransactionView({
).amount; ).amount;
return ( return (
<TransactionTemplate> <TransactionTemplate>
<h2>Refresh</h2> <h2>
<Translate>Refresh</Translate>
</h2>
<Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" /> <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
<br /> <br />
<Part <Part
big big
title="Total refresh" title={<Translate>Total refresh</Translate>}
text={amountToString(transaction.amountEffective)} text={amountToString(transaction.amountEffective)}
kind="negative" kind="negative"
/> />
<Part <Part
big big
title="Refresh amount" title={<Translate>Refresh amount</Translate>}
text={amountToString(transaction.amountRaw)} text={amountToString(transaction.amountRaw)}
kind="neutral" kind="neutral"
/> />
<Part big title="Fee" text={amountToString(fee)} kind="negative" /> <Part
big
title={<Translate>Fee</Translate>}
text={amountToString(fee)}
kind="negative"
/>
</TransactionTemplate> </TransactionTemplate>
); );
} }
@ -429,22 +474,29 @@ export function TransactionView({
).amount; ).amount;
return ( return (
<TransactionTemplate> <TransactionTemplate>
<h2>Tip</h2> <h2>
<Translate>Tip</Translate>
</h2>
<Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" /> <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
<br /> <br />
<Part <Part
big big
title="Total tip" title={<Translate>Total tip</Translate>}
text={amountToString(transaction.amountEffective)} text={amountToString(transaction.amountEffective)}
kind="positive" kind="positive"
/> />
<Part <Part
big big
title="Received amount" title={<Translate>Received amount</Translate>}
text={amountToString(transaction.amountRaw)} text={amountToString(transaction.amountRaw)}
kind="neutral" kind="neutral"
/> />
<Part big title="Fee" text={amountToString(fee)} kind="negative" /> <Part
big
title={<Translate>Fee</Translate>}
text={amountToString(fee)}
kind="negative"
/>
</TransactionTemplate> </TransactionTemplate>
); );
} }
@ -456,30 +508,41 @@ export function TransactionView({
).amount; ).amount;
return ( return (
<TransactionTemplate> <TransactionTemplate>
<h2>Refund</h2> <h2>
<Translate>Refund</Translate>
</h2>
<Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" /> <Time timestamp={transaction.timestamp} format="dd MMMM yyyy, HH:mm" />
<br /> <br />
<Part <Part
big big
title="Total refund" title={<Translate>Total refund</Translate>}
text={amountToString(transaction.amountEffective)} text={amountToString(transaction.amountEffective)}
kind="positive" kind="positive"
/> />
<Part <Part
big big
title="Refund amount" title={<Translate>Refund amount</Translate>}
text={amountToString(transaction.amountRaw)} text={amountToString(transaction.amountRaw)}
kind="neutral" kind="neutral"
/> />
<Part big title="Fee" text={amountToString(fee)} kind="negative" />
<Part <Part
title="Merchant" big
title={<Translate>Fee</Translate>}
text={amountToString(fee)}
kind="negative"
/>
<Part
title={<Translate>Merchant</Translate>}
text={transaction.info.merchant.name} text={transaction.info.merchant.name}
kind="neutral" kind="neutral"
/> />
<Part title="Purchase" text={transaction.info.summary} kind="neutral" />
<Part <Part
title="Receipt" title={<Translate>Purchase</Translate>}
text={transaction.info.summary}
kind="neutral"
/>
<Part
title={<Translate>Receipt</Translate>}
text={`#${transaction.info.orderId}`} text={`#${transaction.info.orderId}`}
kind="neutral" kind="neutral"
/> />

View File

@ -17,10 +17,10 @@
/** /**
* Welcome page, shown on first installs. * Welcome page, shown on first installs.
* *
* @author Florian Dold * @author sebasjm
*/ */
import { WalletDiagnostics } from "@gnu-taler/taler-util"; import { Translate, WalletDiagnostics } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { Checkbox } from "../components/Checkbox"; import { Checkbox } from "../components/Checkbox";
import { Diagnostics } from "../components/Diagnostics"; import { Diagnostics } from "../components/Diagnostics";
@ -54,24 +54,41 @@ export function View({
}: ViewProps): VNode { }: ViewProps): VNode {
return ( return (
<Fragment> <Fragment>
<h1>Browser Extension Installed!</h1> <h1>
<Translate>Browser Extension Installed!</Translate>
</h1>
<div> <div>
<p>Thank you for installing the wallet.</p> <p>
<Translate>Thank you for installing the wallet.</Translate>
</p>
<Diagnostics diagnostics={diagnostics} timedOut={timedOut} /> <Diagnostics diagnostics={diagnostics} timedOut={timedOut} />
<h2>Permissions</h2> <h2>
<Translate>Permissions</Translate>
</h2>
<Checkbox <Checkbox
label="Automatically open wallet based on page content" label={
<Translate>
Automatically open wallet based on page content
</Translate>
}
name="perm" name="perm"
description="(Enabling this option below will make using the wallet faster, but requires more permissions from your browser.)" description={
<Translate>
(Enabling this option below will make using the wallet faster, but
requires more permissions from your browser.)
</Translate>
}
enabled={permissionsEnabled} enabled={permissionsEnabled}
onToggle={togglePermissions} onToggle={togglePermissions}
/> />
<h2>Next Steps</h2> <h2>
<Translate>Next Steps</Translate>
</h2>
<a href="https://demo.taler.net/" style={{ display: "block" }}> <a href="https://demo.taler.net/" style={{ display: "block" }}>
Try the demo » <Translate>Try the demo</Translate> »
</a> </a>
<a href="https://demo.taler.net/" style={{ display: "block" }}> <a href="https://demo.taler.net/" style={{ display: "block" }}>
Learn how to top up your wallet balance » <Translate>Learn how to top up your wallet balance</Translate> »
</a> </a>
</div> </div>
</Fragment> </Fragment>

View File

@ -17,10 +17,10 @@
/** /**
* Main entry point for extension pages. * Main entry point for extension pages.
* *
* @author Florian Dold <dold@taler.net> * @author sebasjm <dold@taler.net>
*/ */
import { i18n, setupI18n } from "@gnu-taler/taler-util"; import { i18n, setupI18n, Translate } from "@gnu-taler/taler-util";
import { createHashHistory } from "history"; import { createHashHistory } from "history";
import { Fragment, h, render, VNode } from "preact"; import { Fragment, h, render, VNode } from "preact";
import Router, { route, Route } from "preact-router"; import Router, { route, Route } from "preact-router";
@ -78,7 +78,7 @@ if (document.readyState === "loading") {
function Application(): VNode { function Application(): VNode {
const [globalNotification, setGlobalNotification] = useState< const [globalNotification, setGlobalNotification] = useState<
string | undefined VNode | undefined
>(undefined); >(undefined);
const hash_history = createHashHistory(); const hash_history = createHashHistory();
function clearNotification(): void { function clearNotification(): void {
@ -169,7 +169,9 @@ function Application(): VNode {
onSuccess={(currency: string) => { onSuccess={(currency: string) => {
route(Pages.balance_history.replace(":currency", currency)); route(Pages.balance_history.replace(":currency", currency));
setGlobalNotification( setGlobalNotification(
"All done, your transaction is in progress", <Translate>
All done, your transaction is in progress
</Translate>,
); );
}} }}
/> />

View File

@ -41,6 +41,12 @@ import { DepositFee } from "@gnu-taler/taler-wallet-core/src/operations/deposits
import { ExchangeWithdrawDetails } from "@gnu-taler/taler-wallet-core/src/operations/withdraw"; import { ExchangeWithdrawDetails } from "@gnu-taler/taler-wallet-core/src/operations/withdraw";
import { MessageFromBackend } from "./wxBackend"; import { MessageFromBackend } from "./wxBackend";
/**
*
* @autor Florian Dold
* @autor sebasjm
*/
export interface ExtendedPermissionsResponse { export interface ExtendedPermissionsResponse {
newValue: boolean; newValue: boolean;
} }