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>
<Translate>
Please check in your <code>about:config</code> settings that you Please check in your <code>about:config</code> settings that you
have IndexedDB enabled (check the preference name{" "} have IndexedDB enabled (check the preference name{" "}
<code>dom.indexedDB.enabled</code>). <code>dom.indexedDB.enabled</code>).
</Translate>
</p> </p>
) : null} ) : null}
{diagnostics.dbOutdated ? ( {diagnostics.dbOutdated ? (
<p> <p>
<Translate>
Your wallet database is outdated. Currently automatic migration is Your wallet database is outdated. Currently automatic migration is
not supported. Please go{" "} not supported. Please go{" "}
<PageLink pageName="/reset-required">here</PageLink> to reset the <PageLink pageName="/reset-required">
wallet database. <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>
<Translate>
Your balance of {amountToString(balance)} is not enough to pay Your balance of {amountToString(balance)} is not enough to pay
for this purchase for this purchase
</Translate>
</WarningBox> </WarningBox>
) : ( ) : (
<WarningBox> <WarningBox>
<Translate>
Your balance is not enough to pay for this purchase. 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>
<Translate>
Already paid, you are going to be redirected to{" "} Already paid, you are going to be redirected to{" "}
<a href={payStatus.contractTerms.fulfillment_url}> <a href={payStatus.contractTerms.fulfillment_url}>
{payStatus.contractTerms.fulfillment_url} {payStatus.contractTerms.fulfillment_url}
</a> </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.stringifyValue(
Amounts.mult( Amounts.mult(
Amounts.parseOrThrow(p.price), Amounts.parseOrThrow(p.price),
p.quantity ?? 1, p.quantity ?? 1,
).amount, ).amount,
)} ${p}` )} ${p}`
: "free"} ) : (
<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>
<Translate>
The product <em>{applyResult.info.summary}</em> has received a total The product <em>{applyResult.info.summary}</em> has received a total
effective refund of{" "} 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>
<Translate>
The refund amount of{" "} The refund amount of{" "}
<AmountView amount={applyResult.amountRefundGone} /> could not be <AmountView amount={applyResult.amountRefundGone} /> could not be
applied. 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>
<Translate>
The exchange reply with a empty terms of service 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>
<Translate>
Tip from <code>{prepareTipResult.merchantBaseUrl}</code> accepted. Tip from <code>{prepareTipResult.merchantBaseUrl}</code> accepted.
Check your transactions list for more details. Check your transactions list for more details.
</Translate>
</span> </span>
) : ( ) : (
<div> <div>
<p> <p>
<Translate>
The merchant <code>{prepareTipResult.merchantBaseUrl}</code> is The merchant <code>{prepareTipResult.merchantBaseUrl}</code> is
offering you a tip of{" "} offering you a tip of{" "}
<strong> <strong>
<AmountView amount={prepareTipResult.tipAmountEffective} /> <AmountView amount={prepareTipResult.tipAmountEffective} />
</strong>{" "} </strong>{" "}
via the exchange <code>{prepareTipResult.exchangeBaseUrl}</code> 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>
<Translate>
Once the database format has stabilized, we will provide automatic Once the database format has stabilized, we will provide automatic
upgrades. 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,9 +178,12 @@ export function View({
}} }}
/> />
<br /> <br />
<button onClick={onExportDatabase}>export database</button> <button onClick={onExportDatabase}>
<Translate>export database</Translate>
</button>
{downloadedDatabase && ( {downloadedDatabase && (
<div> <div>
<Translate>
Database exported at Database exported at
<Time <Time
timestamp={{ t_ms: downloadedDatabase.time.getTime() }} timestamp={{ t_ms: downloadedDatabase.time.getTime() }}
@ -188,14 +198,16 @@ export function View({
"yyyy/MM/dd_HH:mm", "yyyy/MM/dd_HH:mm",
)}.json`} )}.json`}
> >
{" "} <Translate>click here</Translate>
click here{" "}
</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(() => {
chrome.tabs
.create({
active: true, active: true,
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
url: chrome.runtime.getURL(`/static/wallet.html#${page}`), url: chrome.runtime.getURL(`/static/wallet.html${page}`),
}); })
.then(() => {
window.close(); window.close();
// return null; });
});
return (
<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>
{error && (
<ErrorMessage <ErrorMessage
title={error && "Can't create the reserve"} title={<Translate>Can't create the reserve</Translate>}
description={error} 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)}>
<Translate>
Deposit {Amounts.stringifyValue(totalToDeposit)} {currency} 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>
<Translate>
An exchange has been found! Review the information and click next 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>
This exchange doesn't match the expected currency
<b>{expectedCurrency}</b> <b>{expectedCurrency}</b>
</Translate>
</WarningBox> </WarningBox>
)} )}
{error && (
<ErrorMessage <ErrorMessage
title={error && "Unable to add this exchange"} title={<Translate>Unable to verify this exchange</Translate>}
description={error} description={error}
/> />
)}
{confirmationError && (
<ErrorMessage <ErrorMessage
title={confirmationError && "Unable to add this exchange"} title={<Translate>Unable to add this exchange</Translate>}
description={confirmationError} 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>
<Translate>Add backup provider</Translate>
</h1>
{error && (
<ErrorMessage <ErrorMessage
title={error && "Could not get provider information"} title={<Translate>Could not get provider information</Translate>}
description={error} description={error}
/> />
<LightText> Backup providers may charge for their service</LightText> )}
<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>
<Translate>
Please review and accept this provider's terms of service 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={
<Translate>
There was an error loading the provider detail for "{providerURL}" There was an error loading the provider detail for "{providerURL}"
</i18n.Translate> </Translate>
</div> }
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>
To complete the process you need to wire
<b>{amountToString(amount)}</b> to the exchange bank account <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>
<Translate>
Make sure to use the correct subject, otherwise the money will not Make sure to use the correct subject, otherwise the money will not
arrive in this wallet. 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>
<Translate>
If you have already wired money to the exchange you will loose If you have already wired money to the exchange you will loose
the chance to get the coins form it. 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>
<Translate>
The bank is waiting for confirmation. Go to the The bank is waiting for confirmation. Go to the
<a <a
href={transaction.withdrawalDetails.bankConfirmationUrl} href={transaction.withdrawalDetails.bankConfirmationUrl}
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
> >
bank site <Translate>bank site</Translate>
</a> </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;
} }