aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-webextension')
-rw-r--r--packages/taler-wallet-webextension/manifest-common.json4
-rw-r--r--packages/taler-wallet-webextension/package.json2
-rw-r--r--packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx12
-rw-r--r--packages/taler-wallet-webextension/src/components/Modal.tsx3
-rw-r--r--packages/taler-wallet-webextension/src/components/TermsOfService/index.ts9
-rw-r--r--packages/taler-wallet-webextension/src/components/TermsOfService/state.ts78
-rw-r--r--packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx90
-rw-r--r--packages/taler-wallet-webextension/src/components/WalletActivity.tsx158
-rw-r--r--packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts1
-rw-r--r--packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts11
-rw-r--r--packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts1
-rw-r--r--packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts5
-rw-r--r--packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts7
-rw-r--r--packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts5
-rw-r--r--packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts9
-rw-r--r--packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts5
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw/index.ts16
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw/state.ts58
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx108
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw/test.ts6
-rw-r--r--packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx21
-rw-r--r--packages/taler-wallet-webextension/src/i18n/de.po12
-rw-r--r--packages/taler-wallet-webextension/src/platform/api.ts1
-rw-r--r--packages/taler-wallet-webextension/src/platform/chrome.ts62
-rw-r--r--packages/taler-wallet-webextension/src/platform/dev.ts5
-rw-r--r--packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts145
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx20
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Transaction.tsx12
-rw-r--r--packages/taler-wallet-webextension/src/wxApi.ts44
-rw-r--r--packages/taler-wallet-webextension/src/wxBackend.ts65
30 files changed, 582 insertions, 393 deletions
diff --git a/packages/taler-wallet-webextension/manifest-common.json b/packages/taler-wallet-webextension/manifest-common.json
index 88f152d50..01248e964 100644
--- a/packages/taler-wallet-webextension/manifest-common.json
+++ b/packages/taler-wallet-webextension/manifest-common.json
@@ -2,7 +2,7 @@
"name": "GNU Taler Wallet (git)",
"description": "Privacy preserving and transparent payments",
"author": "GNU Taler Developers",
- "version": "0.11.4",
+ "version": "0.12.2",
"icons": {
"16": "static/img/taler-logo-16.png",
"19": "static/img/taler-logo-19.png",
@@ -14,5 +14,5 @@
"256": "static/img/taler-logo-256.png",
"512": "static/img/taler-logo-512.png"
},
- "version_name": "0.11.4"
+ "version_name": "0.12.2"
}
diff --git a/packages/taler-wallet-webextension/package.json b/packages/taler-wallet-webextension/package.json
index 90679cfdd..5c622da70 100644
--- a/packages/taler-wallet-webextension/package.json
+++ b/packages/taler-wallet-webextension/package.json
@@ -1,6 +1,6 @@
{
"name": "@gnu-taler/taler-wallet-webextension",
- "version": "0.11.4",
+ "version": "0.12.2",
"description": "GNU Taler Wallet browser extension",
"main": "./build/index.js",
"types": "./build/index.d.ts",
diff --git a/packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx b/packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx
index b1ed3b02c..38d1b6b6b 100644
--- a/packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx
+++ b/packages/taler-wallet-webextension/src/components/CurrentAlerts.tsx
@@ -22,6 +22,8 @@ import {
} from "../context/alert.js";
import { Alert } from "../mui/Alert.js";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { ButtonHandler } from "../mui/handlers.js";
+import { Button } from "../mui/Button.js";
/**
*
@@ -99,13 +101,23 @@ function AlertContext({
export function ErrorAlertView({
error,
+ retry,
onClose,
}: {
error: AlertNotification;
+ retry?: ButtonHandler;
onClose?: () => Promise<void>;
}): VNode {
+ const { i18n } = useTranslationContext();
return (
<Wrapper>
+ {!retry ? undefined : (
+ <section>
+ <Button variant="contained" color="success" onClick={retry.onClick}>
+ <i18n.Translate>Retry operation</i18n.Translate>
+ </Button>
+ </section>
+ )}
<AlertView alert={error} onClose={onClose} />
</Wrapper>
);
diff --git a/packages/taler-wallet-webextension/src/components/Modal.tsx b/packages/taler-wallet-webextension/src/components/Modal.tsx
index f8c0f1651..c5f716c76 100644
--- a/packages/taler-wallet-webextension/src/components/Modal.tsx
+++ b/packages/taler-wallet-webextension/src/components/Modal.tsx
@@ -52,8 +52,7 @@ const Body = styled.div`
export function Modal({ title, children, onClose }: Props): VNode {
return (
- <div style={{ top: 0, width: "100%", height: "100%" }}>
-
+ <div style={{ top: 0, position: "fixed", width: "100%", height: "100%" }}>
<FullSize onClick={onClose?.onClick}>
<div
onClick={(e) => e.stopPropagation()}
diff --git a/packages/taler-wallet-webextension/src/components/TermsOfService/index.ts b/packages/taler-wallet-webextension/src/components/TermsOfService/index.ts
index 1585e3992..7ef5b95a1 100644
--- a/packages/taler-wallet-webextension/src/components/TermsOfService/index.ts
+++ b/packages/taler-wallet-webextension/src/components/TermsOfService/index.ts
@@ -17,7 +17,11 @@
import { ComponentChildren } from "preact";
import { Loading } from "../../components/Loading.js";
import { ErrorAlert } from "../../context/alert.js";
-import { SelectFieldHandler, ToggleHandler } from "../../mui/handlers.js";
+import {
+ ButtonHandler,
+ SelectFieldHandler,
+ ToggleHandler,
+} from "../../mui/handlers.js";
import { StateViewMap, compose } from "../../utils/index.js";
import { ErrorAlertView } from "../CurrentAlerts.js";
import { useComponentState } from "./state.js";
@@ -61,6 +65,7 @@ export namespace State {
status: "show-content";
termsAccepted: ToggleHandler;
showingTermsOfService?: ToggleHandler;
+ skipTos: ButtonHandler;
tosLang: SelectFieldHandler;
tosFormat: SelectFieldHandler;
}
@@ -68,7 +73,7 @@ export namespace State {
status: "show-buttons-accepted";
termsAccepted: ToggleHandler;
showingTermsOfService: ToggleHandler;
- children: ComponentChildren,
+ children: ComponentChildren;
}
export interface ShowButtonsNotAccepted extends BaseInfo {
status: "show-buttons-not-accepted";
diff --git a/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts b/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts
index 76524f0f4..96d14dadf 100644
--- a/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts
+++ b/packages/taler-wallet-webextension/src/components/TermsOfService/state.ts
@@ -25,22 +25,28 @@ import { buildTermsOfServiceState } from "./utils.js";
const supportedFormats = {
"text/html": "HTML",
- "text/xml" : "XML",
- "text/markdown" : "Markdown",
- "text/plain" : "Plain text",
- "text/pdf" : "PDF",
-}
-
-export function useComponentState({ showEvenIfaccepted, exchangeUrl, readOnly, children }: Props): State {
+ "text/xml": "XML",
+ "text/markdown": "Markdown",
+ "text/plain": "Plain text",
+ "text/pdf": "PDF",
+};
+
+export function useComponentState({
+ showEvenIfaccepted,
+ exchangeUrl,
+ readOnly,
+ children,
+}: Props): State {
const api = useBackendContext();
const [showContent, setShowContent] = useState<boolean>(!!readOnly);
const { i18n, lang } = useTranslationContext();
- const [tosLang, setTosLang] = useState<string>()
+ const [forceAccepted, setForceAccepted] = useState<boolean>();
+ const [tosLang, setTosLang] = useState<string>();
const { pushAlertOnError } = useAlertContext();
- const [format, setFormat] = useState("text/html")
+ const [format, setFormat] = useState("text/html");
- const acceptedLang = tosLang ?? lang
+ const acceptedLang = tosLang ?? lang;
/**
* For the exchange selected, bring the status of the terms of service
*/
@@ -54,10 +60,13 @@ export function useComponentState({ showEvenIfaccepted, exchangeUrl, readOnly, c
},
);
- const supportedLangs = exchangeTos.tosAvailableLanguages.reduce((prev, cur) => {
- prev[cur] = cur
- return prev;
- }, {} as Record<string, string>)
+ const supportedLangs = exchangeTos.tosAvailableLanguages.reduce(
+ (prev, cur) => {
+ prev[cur] = cur;
+ return prev;
+ },
+ {} as Record<string, string>,
+ );
const state = buildTermsOfServiceState(exchangeTos);
@@ -92,30 +101,35 @@ export function useComponentState({ showEvenIfaccepted, exchangeUrl, readOnly, c
} else {
// mark as not accepted
}
- terms?.retry()
+ terms?.retry();
}
- const accepted = state.status === "accepted";
+ const accepted = state.status === "accepted" || forceAccepted;
const base = {
error: undefined,
showingTermsOfService: {
value: showContent && (!accepted || showEvenIfaccepted),
button: {
- onClick: accepted && !showEvenIfaccepted ? undefined : pushAlertOnError(async () => {
- setShowContent(!showContent);
- }),
+ onClick:
+ accepted && !showEvenIfaccepted
+ ? undefined
+ : pushAlertOnError(async () => {
+ setShowContent(!showContent);
+ }),
},
},
terms: state,
termsAccepted: {
value: accepted,
button: {
- onClick: readOnly ? undefined : pushAlertOnError(async () => {
- const newValue = !accepted; //toggle
- await onUpdate(newValue);
- setShowContent(false);
- }),
+ onClick: readOnly
+ ? undefined
+ : pushAlertOnError(async () => {
+ const newValue = !accepted; //toggle
+ await onUpdate(newValue);
+ setShowContent(false);
+ }),
},
},
};
@@ -135,20 +149,25 @@ export function useComponentState({ showEvenIfaccepted, exchangeUrl, readOnly, c
terms: state,
showingTermsOfService: readOnly ? undefined : base.showingTermsOfService,
termsAccepted: base.termsAccepted,
+ skipTos: {
+ onClick: pushAlertOnError(async () => {
+ setForceAccepted(true);
+ }),
+ },
tosFormat: {
onChange: pushAlertOnError(async (s) => {
- setFormat(s)
+ setFormat(s);
}),
list: supportedFormats,
- value: format ?? ""
+ value: format ?? "",
},
tosLang: {
onChange: pushAlertOnError(async (s) => {
- setTosLang(s)
+ setTosLang(s);
}),
list: supportedLangs,
- value: tosLang ?? lang
- }
+ value: tosLang ?? lang,
+ },
};
}
//showing buttons
@@ -156,5 +175,4 @@ export function useComponentState({ showEvenIfaccepted, exchangeUrl, readOnly, c
status: "show-buttons-not-accepted",
...base,
};
-
}
diff --git a/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx b/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx
index 40cfba3bc..f3172a741 100644
--- a/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx
+++ b/packages/taler-wallet-webextension/src/components/TermsOfService/views.tsx
@@ -23,7 +23,7 @@ import {
Input,
LinkSuccess,
TermsOfServiceStyle,
- WarningBox
+ WarningBox,
} from "../../components/styled/index.js";
import { Button } from "../../mui/Button.js";
import { State } from "./index.js";
@@ -50,7 +50,9 @@ export function ShowButtonsAcceptedTosView({
</LinkSuccess>
</section>
{termsAccepted.button.onClick !== undefined && (
- <section style={{ justifyContent: "space-around", display: "flex" }}>
+ <section
+ style={{ justifyContent: "space-around", display: "flex" }}
+ >
<CheckboxOutlined
name="terms"
enabled={termsAccepted.value}
@@ -75,36 +77,9 @@ export function ShowButtonsNonAcceptedTosView({
terms,
}: State.ShowButtonsNotAccepted): VNode {
const { i18n } = useTranslationContext();
- // const ableToReviewTermsOfService =
- // showingTermsOfService.button.onClick !== undefined;
-
- // if (!ableToReviewTermsOfService) {
- // return (
- // <Fragment>
- // {terms.status === ExchangeTosStatus.Pending && (
- // <section style={{ justifyContent: "space-around", display: "flex" }}>
- // <WarningText>
- // <i18n.Translate>
- // Exchange doesn&apos;t have terms of service
- // </i18n.Translate>
- // </WarningText>
- // </section>
- // )}
- // </Fragment>
- // );
- // }
return (
<Fragment>
- {/* {terms.status === ExchangeTosStatus.NotFound && (
- <section style={{ justifyContent: "space-around", display: "flex" }}>
- <WarningText>
- <i18n.Translate>
- Exchange doesn&apos;t have terms of service
- </i18n.Translate>
- </WarningText>
- </section>
- )} */}
<section style={{ justifyContent: "space-around", display: "flex" }}>
<Button
variant="contained"
@@ -124,11 +99,35 @@ export function ShowTosContentView({
terms,
tosLang,
tosFormat,
+ skipTos,
}: State.ShowContent): VNode {
const { i18n } = useTranslationContext();
- const ableToReviewTermsOfService =
- termsAccepted.button.onClick !== undefined;
+ const ableToReviewTermsOfService = termsAccepted.button.onClick !== undefined;
+
+ if (terms.status === ExchangeTosStatus.MissingTos) {
+ return (
+ <section>
+ <section style={{ justifyContent: "space-around", display: "flex" }}>
+ <WarningBox>
+ <i18n.Translate>
+ The exchange doesn't have a terms of service.
+ </i18n.Translate>
+ </WarningBox>
+ </section>
+ <section style={{ justifyContent: "space-around", display: "flex" }}>
+ <Button
+ variant="contained"
+ color="success"
+ disabled={!skipTos.onClick}
+ onClick={skipTos.onClick}
+ >
+ <i18n.Translate>Skip it for now.</i18n.Translate>
+ </Button>
+ </section>
+ </section>
+ );
+ }
return (
<section>
<Input style={{ display: "flex", justifyContent: "end" }}>
@@ -206,20 +205,21 @@ export function ShowTosContentView({
</LinkSuccess>
</section>
)}
- {termsAccepted.button.onClick && terms.status !== ExchangeTosStatus.Accepted && (
- <section style={{ justifyContent: "space-around", display: "flex" }}>
- <CheckboxOutlined
- name="terms"
- enabled={termsAccepted.value}
- label={
- <i18n.Translate>
- I accept the exchange terms of service
- </i18n.Translate>
- }
- onToggle={termsAccepted.button.onClick}
- />
- </section>
- )}
+ {termsAccepted.button.onClick &&
+ terms.status !== ExchangeTosStatus.Accepted && (
+ <section style={{ justifyContent: "space-around", display: "flex" }}>
+ <CheckboxOutlined
+ name="terms"
+ enabled={termsAccepted.value}
+ label={
+ <i18n.Translate>
+ I accept the exchange terms of service
+ </i18n.Translate>
+ }
+ onToggle={termsAccepted.button.onClick}
+ />
+ </section>
+ )}
</section>
);
}
diff --git a/packages/taler-wallet-webextension/src/components/WalletActivity.tsx b/packages/taler-wallet-webextension/src/components/WalletActivity.tsx
index f29d0b0f7..c0bc5532b 100644
--- a/packages/taler-wallet-webextension/src/components/WalletActivity.tsx
+++ b/packages/taler-wallet-webextension/src/components/WalletActivity.tsx
@@ -39,6 +39,7 @@ import { WxApiType } from "../wxApi.js";
import { WalletActivityTrack } from "../wxBackend.js";
import { Modal } from "./Modal.js";
import { Time } from "./Time.js";
+import { Checkbox } from "./Checkbox.js";
const OPEN_ACTIVITY_HEIGHT_PX = 250;
const CLOSE_ACTIVITY_HEIGHT_PX = 40;
@@ -212,7 +213,10 @@ function ShowBackupOperationError({ events, onClick }: MoreInfoPRops): VNode {
<dd>{error.hint ?? "--"}</dd>
<dt>Time</dt>
<dd>
- <Time timestamp={error.when} format="yyyy/MM/dd HH:mm:ss" />
+ <Time
+ timestamp={error.when}
+ format="yyyy/MM/dd HH:mm:ss.SSS"
+ />
</dd>
</dl>
<pre
@@ -360,34 +364,44 @@ function ShowObservabilityEvent({ events, onClick }: MoreInfoPRops): VNode {
const title = (function () {
switch (not.event.type) {
- case ObservabilityEventType.HttpFetchFinishError:
- case ObservabilityEventType.HttpFetchFinishSuccess:
case ObservabilityEventType.HttpFetchStart:
return "HTTP Request";
- case ObservabilityEventType.DbQueryFinishSuccess:
- case ObservabilityEventType.DbQueryFinishError:
+ case ObservabilityEventType.HttpFetchFinishSuccess:
+ return "HTTP Request (o)";
+ case ObservabilityEventType.HttpFetchFinishError:
+ return "HTTP Request (x)";
case ObservabilityEventType.DbQueryStart:
return "Database";
- case ObservabilityEventType.RequestFinishSuccess:
- case ObservabilityEventType.RequestFinishError:
+ case ObservabilityEventType.DbQueryFinishSuccess:
+ return "Database (o)";
+ case ObservabilityEventType.DbQueryFinishError:
+ return "Database (x)";
case ObservabilityEventType.RequestStart:
return "Wallet";
- case ObservabilityEventType.CryptoFinishSuccess:
- case ObservabilityEventType.CryptoFinishError:
+ case ObservabilityEventType.RequestFinishSuccess:
+ return "Wallet (o)";
+ case ObservabilityEventType.RequestFinishError:
+ return "Wallet (x)";
case ObservabilityEventType.CryptoStart:
return "Crypto";
+ case ObservabilityEventType.CryptoFinishSuccess:
+ return "Crypto (o)";
+ case ObservabilityEventType.CryptoFinishError:
+ return "Crypto (x)";
case ObservabilityEventType.TaskStart:
- return "Task start";
+ return "Task";
case ObservabilityEventType.TaskStop:
- return "Task stop";
+ return "Task (s)";
case ObservabilityEventType.TaskReset:
- return "Task reset";
+ return "Task (r)";
case ObservabilityEventType.ShepherdTaskResult:
return "Schedule";
case ObservabilityEventType.DeclareTaskDependency:
return "Task dependency";
case ObservabilityEventType.Message:
return "Message";
+ case ObservabilityEventType.DeclareConcernsTransaction:
+ return "DeclareConcernsTransaction";
}
})();
@@ -401,12 +415,11 @@ function ShowObservabilityEvent({ events, onClick }: MoreInfoPRops): VNode {
);
});
return (
- <table>
+ <table style={{ width: "100%" }}>
<thead>
<td>Event</td>
<td>Info</td>
- <td>Start</td>
- <td>End</td>
+ <td>When</td>
</thead>
<tbody>{asd}</tbody>
</table>
@@ -417,11 +430,9 @@ function ShowObervavilityDetails({
title,
notif,
onClick,
- prev,
}: {
title: string;
notif: ObservaNotifWithTime;
- prev?: ObservaNotifWithTime;
onClick: (content: VNode) => void;
}): VNode {
switch (notif.event.type) {
@@ -443,7 +454,7 @@ function ShowObervavilityDetails({
wordBreak: "break-word",
}}
>
- {JSON.stringify({ event: notif, prev }, undefined, 2)}
+ {JSON.stringify({ event: notif }, undefined, 2)}
</pre>
</Fragment>,
);
@@ -454,21 +465,21 @@ function ShowObervavilityDetails({
</td>
<td>
{notif.event.url}{" "}
- {prev?.event.type ===
+ {notif?.event.type ===
ObservabilityEventType.HttpFetchFinishSuccess ? (
- `(${prev.event.status})`
- ) : prev?.event.type ===
+ `(${notif.event.status})`
+ ) : notif?.event.type ===
ObservabilityEventType.HttpFetchFinishError ? (
<a
href="#"
onClick={(e) => {
e.preventDefault();
if (
- prev.event.type !==
+ notif.event.type !==
ObservabilityEventType.HttpFetchFinishError
)
return;
- const error = prev.event.error;
+ const error = notif.event.error;
onClick(
<Fragment>
<dl>
@@ -482,7 +493,7 @@ function ShowObervavilityDetails({
<dd>
<Time
timestamp={error.when}
- format="yyyy/MM/dd HH:mm:ss"
+ format="yyyy/MM/dd HH:mm:ss.SSS"
/>
</dd>
</dl>
@@ -504,11 +515,7 @@ function ShowObervavilityDetails({
</td>
<td>
{" "}
- <Time timestamp={notif.when} format="yyyy/MM/dd HH:mm:ss" />
- </td>
- <td>
- {" "}
- <Time timestamp={prev?.when} format="yyyy/MM/dd HH:mm:ss" />
+ <Time timestamp={notif.when} format="yyyy/MM/dd HH:mm:ss.SSS" />
</td>
</tr>
);
@@ -531,7 +538,7 @@ function ShowObervavilityDetails({
wordBreak: "break-word",
}}
>
- {JSON.stringify({ event: notif, prev }, undefined, 2)}
+ {JSON.stringify({ event: notif }, undefined, 2)}
</pre>
</Fragment>,
);
@@ -544,10 +551,7 @@ function ShowObervavilityDetails({
{notif.event.location} {notif.event.name}
</td>
<td>
- <Time timestamp={notif.when} format="yyyy/MM/dd HH:mm:ss" />
- </td>
- <td>
- <Time timestamp={prev?.when} format="yyyy/MM/dd HH:mm:ss" />
+ <Time timestamp={notif.when} format="yyyy/MM/dd HH:mm:ss.SSS" />
</td>
</tr>
);
@@ -572,7 +576,7 @@ function ShowObervavilityDetails({
wordBreak: "break-word",
}}
>
- {JSON.stringify({ event: notif, prev }, undefined, 2)}
+ {JSON.stringify({ event: notif }, undefined, 2)}
</pre>
</Fragment>,
);
@@ -583,10 +587,7 @@ function ShowObervavilityDetails({
</td>
<td>{notif.event.taskId}</td>
<td>
- <Time timestamp={notif.when} format="yyyy/MM/dd HH:mm:ss" />
- </td>
- <td>
- <Time timestamp={prev?.when} format="yyyy/MM/dd HH:mm:ss" />
+ <Time timestamp={notif.when} format="yyyy/MM/dd HH:mm:ss.SSS" />
</td>
</tr>
);
@@ -607,7 +608,7 @@ function ShowObervavilityDetails({
wordBreak: "break-word",
}}
>
- {JSON.stringify({ event: notif, prev }, undefined, 2)}
+ {JSON.stringify({ event: notif }, undefined, 2)}
</pre>
</Fragment>,
);
@@ -618,10 +619,7 @@ function ShowObervavilityDetails({
</td>
<td>{notif.event.resultType}</td>
<td>
- <Time timestamp={notif.when} format="yyyy/MM/dd HH:mm:ss" />
- </td>
- <td>
- <Time timestamp={prev?.when} format="yyyy/MM/dd HH:mm:ss" />
+ <Time timestamp={notif.when} format="yyyy/MM/dd HH:mm:ss.SSS" />
</td>
</tr>
);
@@ -644,7 +642,7 @@ function ShowObervavilityDetails({
wordBreak: "break-word",
}}
>
- {JSON.stringify({ event: notif, prev }, undefined, 2)}
+ {JSON.stringify({ event: notif }, undefined, 2)}
</pre>
</Fragment>,
);
@@ -655,10 +653,7 @@ function ShowObervavilityDetails({
</td>
<td>{notif.event.operation}</td>
<td>
- <Time timestamp={notif.when} format="yyyy/MM/dd HH:mm:ss" />
- </td>
- <td>
- <Time timestamp={prev?.when} format="yyyy/MM/dd HH:mm:ss" />
+ <Time timestamp={notif.when} format="yyyy/MM/dd HH:mm:ss.SSS" />
</td>
</tr>
);
@@ -681,7 +676,7 @@ function ShowObervavilityDetails({
wordBreak: "break-word",
}}
>
- {JSON.stringify({ event: notif, prev }, undefined, 2)}
+ {JSON.stringify({ event: notif }, undefined, 2)}
</pre>
</Fragment>,
);
@@ -692,27 +687,30 @@ function ShowObervavilityDetails({
</td>
<td>{notif.event.type}</td>
<td>
- <Time timestamp={notif.when} format="yyyy/MM/dd HH:mm:ss" />
- </td>
- <td>
- <Time timestamp={prev?.when} format="yyyy/MM/dd HH:mm:ss" />
+ <Time timestamp={notif.when} format="yyyy/MM/dd HH:mm:ss.SSS" />
</td>
</tr>
);
}
+ case ObservabilityEventType.DeclareConcernsTransaction:
case ObservabilityEventType.Message:
// FIXME
return <></>;
}
}
+function createTabId(tab: chrome.tabs.Tab | undefined) {
+ return !tab ? "popup" : `${tab.windowId}:${tab.id}`;
+}
+
function refresh(
api: WxApiType,
onUpdate: (list: WalletActivityTrack[]) => void,
filter: string,
+ fromView?: chrome.tabs.Tab,
) {
api.background
- .call("getNotifications", { filter })
+ .call("getNotifications", { filter, operationsFrom: createTabId(fromView) })
.then((notif) => {
onUpdate(notif);
})
@@ -721,6 +719,15 @@ function refresh(
});
}
+let currentTab: chrome.tabs.Tab | undefined;
+// Allow running outside the extension for testing
+// tslint:disable-next-line:no-string-literal
+if (typeof chrome !== "undefined") {
+ chrome.tabs.getCurrent().then((d) => {
+ currentTab = d;
+ });
+}
+
export function ObservabilityEventsTable(): VNode {
const { i18n } = useTranslationContext();
const api = useBackendContext();
@@ -728,11 +735,17 @@ export function ObservabilityEventsTable(): VNode {
const [notifications, setNotifications] = useState<WalletActivityTrack[]>([]);
const [showDetails, setShowDetails] = useState<VNode>();
const [filter, onChangeFilter] = useState("");
+ const [onlyThisScreen, setOnlyThisScreen] = useState(true);
useEffect(() => {
let lastTimeout: ReturnType<typeof setTimeout>;
function periodicRefresh() {
- refresh(api, setNotifications, filter);
+ refresh(
+ api,
+ setNotifications,
+ filter,
+ onlyThisScreen ? currentTab : undefined,
+ );
lastTimeout = setTimeout(() => {
periodicRefresh();
@@ -743,7 +756,7 @@ export function ObservabilityEventsTable(): VNode {
};
}
return periodicRefresh();
- }, [filter]);
+ }, [filter, onlyThisScreen]);
return (
<div>
@@ -754,6 +767,12 @@ export function ObservabilityEventsTable(): VNode {
value={filter}
onChange={onChangeFilter}
/>
+ <Checkbox
+ label={i18n.str`All events`}
+ name="terms"
+ onToggle={async () => setOnlyThisScreen((v) => !v)}
+ enabled={!onlyThisScreen}
+ />
<div
style={{
padding: 4,
@@ -763,7 +782,12 @@ export function ObservabilityEventsTable(): VNode {
}}
onClick={() => {
api.background.call("clearNotifications", undefined).then(() => {
- refresh(api, setNotifications, filter);
+ refresh(
+ api,
+ setNotifications,
+ filter,
+ onlyThisScreen ? currentTab : undefined,
+ );
});
}}
>
@@ -784,7 +808,7 @@ export function ObservabilityEventsTable(): VNode {
)}
{notifications.map((not) => {
return (
- <details key={not.id}>
+ <details key={not.groupId}>
<summary>
<div
style={{
@@ -829,10 +853,13 @@ export function ObservabilityEventsTable(): VNode {
})()}
</div>
<div style={{ padding: 4 }}>
- <Time timestamp={not.start} format="yyyy/MM/dd HH:mm:ss" />
+ <Time
+ timestamp={not.start}
+ format="yyyy/MM/dd HH:mm:ss.SSS"
+ />
</div>
<div style={{ padding: 4 }}>
- <Time timestamp={not.end} format="yyyy/MM/dd HH:mm:ss" />
+ <Time timestamp={not.end} format="yyyy/MM/dd HH:mm:ss.SSS" />
</div>
</div>
</summary>
@@ -936,7 +963,7 @@ function ErroDetailModal({
<dd>{error.hint ?? "--"}</dd>
<dt>Time</dt>
<dd>
- <Time timestamp={error.when} format="yyyy/MM/dd HH:mm:ss" />
+ <Time timestamp={error.when} format="yyyy/MM/dd HH:mm:ss.SSS" />
</dd>
</dl>
<pre style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}>
@@ -1009,11 +1036,14 @@ export function ActiveTasksTable(): VNode {
<td>
<Time
timestamp={task.firstTry}
- format="yyyy/MM/dd HH:mm:ss"
+ format="yyyy/MM/dd HH:mm:ss.SSS"
/>
</td>
<td>
- <Time timestamp={task.nextTry} format="yyyy/MM/dd HH:mm:ss" />
+ <Time
+ timestamp={task.nextTry}
+ format="yyyy/MM/dd HH:mm:ss.SSS"
+ />
</td>
<td>
{!task.lastError?.code ? (
diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts
index fd3fb52f8..1ca7481be 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/index.ts
@@ -47,6 +47,7 @@ export namespace State {
export interface LoadingUriError {
status: "error";
+ retry: ButtonHandler;
error: ErrorAlert;
}
diff --git a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
index daa3ee76d..baaa9a3dd 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoiceCreate/state.ts
@@ -34,6 +34,7 @@ export function useComponentState({
}: Props): RecursiveState<State> {
const amount = Amounts.parseOrThrow(amountStr);
const api = useBackendContext();
+ const { pushAlertOnError } = useAlertContext();
const hook = useAsyncAsHook(() =>
api.wallet.call(WalletApiOperation.ListExchanges, {}),
@@ -49,6 +50,11 @@ export function useComponentState({
if (hook.hasError) {
return {
status: "error",
+ retry: {
+ onClick: pushAlertOnError(async () => {
+ hook.retry();
+ }),
+ },
error: alertFromError(
i18n,
i18n.str`Could not load the list of exchanges`,
@@ -103,6 +109,11 @@ export function useComponentState({
if (hook.hasError) {
return {
status: "error",
+ retry: {
+ onClick: pushAlertOnError(async () => {
+ hook.retry();
+ }),
+ },
error: alertFromError(
i18n,
i18n.str`Could not load the invoice status`,
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts b/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
index f0cd63fbe..dcb1f827b 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/index.ts
@@ -50,6 +50,7 @@ export namespace State {
export interface LoadingUriError {
status: "error";
+ retry: ButtonHandler;
error: ErrorAlert;
}
diff --git a/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts b/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
index 99de03d2d..deee83751 100644
--- a/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/InvoicePay/state.ts
@@ -63,6 +63,11 @@ export function useComponentState({
if (hook.hasError) {
return {
status: "error",
+ retry: {
+ onClick: pushAlertOnError(async () => {
+ hook.retry();
+ }),
+ },
error: alertFromError(
i18n,
i18n.str`Could not load the transfer payment status`,
diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts b/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts
index 794d2ad1c..539ca207c 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/index.ts
@@ -14,7 +14,11 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { AmountJson, AmountString, TalerErrorDetail } from "@gnu-taler/taler-util";
+import {
+ AmountJson,
+ AmountString,
+ TalerErrorDetail,
+} from "@gnu-taler/taler-util";
import { ErrorAlertView } from "../../components/CurrentAlerts.js";
import { Loading } from "../../components/Loading.js";
import { ErrorAlert } from "../../context/alert.js";
@@ -39,6 +43,7 @@ export namespace State {
export interface LoadingUriError {
status: "error";
+ retry: ButtonHandler;
error: ErrorAlert;
}
diff --git a/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts b/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
index f092801ed..f15d48c23 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferCreate/state.ts
@@ -57,6 +57,11 @@ export function useComponentState({
if (hook.hasError) {
return {
status: "error",
+ retry: {
+ onClick: pushAlertOnError(async () => {
+ hook.retry();
+ }),
+ },
error: alertFromError(
i18n,
i18n.str`Could not load the max amount to transfer`,
diff --git a/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts b/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts
index 4e1301d6a..a7bb0b67a 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferPickup/index.ts
@@ -14,16 +14,12 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import {
- AbsoluteTime,
- AmountJson,
- TalerErrorDetail,
-} from "@gnu-taler/taler-util";
+import { AbsoluteTime, AmountJson } from "@gnu-taler/taler-util";
import { ErrorAlertView } from "../../components/CurrentAlerts.js";
import { Loading } from "../../components/Loading.js";
import { ErrorAlert } from "../../context/alert.js";
import { ButtonHandler } from "../../mui/handlers.js";
-import { compose, StateViewMap } from "../../utils/index.js";
+import { StateViewMap, compose } from "../../utils/index.js";
import { useComponentState } from "./state.js";
import { ReadyView } from "./views.js";
@@ -43,6 +39,7 @@ export namespace State {
export interface LoadingUriError {
status: "error";
+ retry: ButtonHandler;
error: ErrorAlert;
}
diff --git a/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts b/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts
index 67f6d9113..28d8c9e70 100644
--- a/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/TransferPickup/state.ts
@@ -49,6 +49,11 @@ export function useComponentState({
if (hook.hasError) {
return {
status: "error",
+ retry: {
+ onClick: pushAlertOnError(async () => {
+ hook.retry();
+ }),
+ },
error: alertFromError(
i18n,
i18n.str`Could not load the invoice payment status`,
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
index af1ef213b..418fef505 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/index.ts
@@ -18,7 +18,7 @@ import {
AmountJson,
AmountString,
CurrencySpecification,
- ExchangeListItem
+ ExchangeListItem,
} from "@gnu-taler/taler-util";
import { Loading } from "../../components/Loading.js";
import { State as SelectExchangeState } from "../../hooks/useSelectedExchange.js";
@@ -85,7 +85,7 @@ export namespace State {
operationState: "confirmed" | "aborted" | "selected";
thisWallet: boolean;
redirectToTx: () => void;
- confirmTransferUrl?: string,
+ confirmTransferUrl?: string;
error: undefined;
}
@@ -99,8 +99,8 @@ export namespace State {
editableAmount: boolean;
bankFee: AmountJson;
- withdrawalFee: AmountJson;
toBeReceived: AmountJson;
+ toBeSent: AmountJson;
doWithdrawal: ButtonHandler;
doSelectExchange: ButtonHandler;
@@ -109,10 +109,12 @@ export namespace State {
chooseCurrencies: string[];
selectedCurrency: string;
changeCurrency: (s: string) => void;
- conversionInfo: {
- spec: CurrencySpecification,
- amount: AmountJson,
- } | undefined;
+ conversionInfo:
+ | {
+ spec: CurrencySpecification;
+ amount: AmountJson;
+ }
+ | undefined;
ageRestriction?: SelectFieldHandler;
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
index f8e27e688..0541bbf3f 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/state.ts
@@ -391,7 +391,6 @@ function exchangeSelectionState(
const safeAmount = wInfo.amount
? wInfo.amount
: Amounts.zeroOfCurrency(wInfo.currency);
- const [choosenAmount, setChoosenAmount] = useState(safeAmount);
if (selectedExchange.status !== "ready") {
return selectedExchange;
@@ -403,8 +402,9 @@ function exchangeSelectionState(
| State.Loading => {
const { i18n } = useTranslationContext();
const { pushAlertOnError } = useAlertContext();
+
+ const [choosenAmount, setChoosenAmount] = useState(safeAmount);
const [ageRestricted, setAgeRestricted] = useState(0);
- const currentExchange = selectedExchange.selected;
const [selectedCurrency, setSelectedCurrency] = useState<string>(
wInfo.currency,
@@ -417,7 +417,7 @@ function exchangeSelectionState(
const info = await api.wallet.call(
WalletApiOperation.GetWithdrawalDetailsForAmount,
{
- exchangeBaseUrl: currentExchange.exchangeBaseUrl,
+ exchangeBaseUrl: selectedExchange.selected.exchangeBaseUrl,
amount: Amounts.stringify(choosenAmount),
restrictAge: ageRestricted,
},
@@ -430,13 +430,33 @@ function exchangeSelectionState(
return {
amount: withdrawAmount,
+ currentExchange: selectedExchange.selected,
ageRestrictionOptions: info.ageRestrictionOptions,
accounts: info.withdrawalAccountsList,
};
- }, []);
+ }, [choosenAmount, selectedExchange.selected, ageRestricted]);
const [doingWithdraw, setDoingWithdraw] = useState<boolean>(false);
+ if (!amountHook) {
+ return { status: "loading", error: undefined };
+ }
+ if (amountHook.hasError) {
+ return {
+ status: "error",
+ error: alertFromError(
+ i18n,
+ i18n.str`Could not load the withdrawal details`,
+ amountHook,
+ ),
+ };
+ }
+ if (!amountHook.response) {
+ return { status: "loading", error: undefined };
+ }
+
+ const currentExchange = amountHook.response.currentExchange;
+
async function doWithdrawAndCheckError(): Promise<void> {
try {
setDoingWithdraw(true);
@@ -458,30 +478,10 @@ function exchangeSelectionState(
setDoingWithdraw(false);
}
- if (!amountHook) {
- return { status: "loading", error: undefined };
- }
- if (amountHook.hasError) {
- return {
- status: "error",
- error: alertFromError(
- i18n,
- i18n.str`Could not load the withdrawal details`,
- amountHook,
- ),
- };
- }
- if (!amountHook.response) {
- return { status: "loading", error: undefined };
- }
-
- const withdrawalFee = Amounts.sub(
- amountHook.response.amount.raw,
- amountHook.response.amount.effective,
- ).amount;
+ const toBeSent = amountHook.response.amount.raw;
const toBeReceived = amountHook.response.amount.effective;
- const bankFee = wInfo.amount;
+ const bankFee = wInfo.bankFee;
const ageRestrictionOptions =
amountHook.response.ageRestrictionOptions?.reduce(
@@ -544,6 +544,7 @@ function exchangeSelectionState(
editableExchange: wInfo.editableExchange,
currentExchange,
toBeReceived,
+ toBeSent,
chooseCurrencies,
bankFee,
selectedCurrency,
@@ -551,7 +552,6 @@ function exchangeSelectionState(
setSelectedCurrency(s);
},
conversionInfo,
- withdrawalFee,
amount: {
value: choosenAmount,
onInput: wInfo.editableAmount
@@ -565,11 +565,11 @@ function exchangeSelectionState(
ageRestriction,
doWithdrawal: {
onClick:
- doingWithdraw && !amountError
+ doingWithdraw || amountError
? undefined
: pushAlertOnError(doWithdrawAndCheckError),
},
cancel,
};
- }, []);
+ }, [selectedExchange.selected]);
}
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx
index 1bfafb231..d9b7c380e 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/stories.tsx
@@ -48,14 +48,20 @@ export const TermsOfServiceNotYetLoaded = tests.createExample(SuccessView, {
currency: "USD",
value: 2,
fraction: 10000000,
- }
+ },
},
+ bankFee: {
+ currency: "EUR",
+ fraction: 0,
+ value: 1,
+ },
+
doWithdrawal: { onClick: nullFunction },
currentExchange: {
exchangeBaseUrl: "https://exchange.demo.taler.net",
tos: {},
} as Partial<ExchangeListItem> as any,
- withdrawalFee: {
+ toBeSent: {
currency: "USD",
fraction: 10000000,
value: 1,
@@ -72,20 +78,19 @@ export const TermsOfServiceNotYetLoaded = tests.createExample(SuccessView, {
export const AlreadyAborted = tests.createExample(FinalStateOperation, {
error: undefined,
status: "already-completed",
- operationState: "aborted"
+ operationState: "aborted",
});
export const AlreadySelected = tests.createExample(FinalStateOperation, {
error: undefined,
status: "already-completed",
- operationState: "selected"
+ operationState: "selected",
});
export const AlreadyConfirmed = tests.createExample(FinalStateOperation, {
error: undefined,
status: "already-completed",
- operationState: "confirmed"
+ operationState: "confirmed",
});
-
export const WithSomeFee = tests.createExample(SuccessView, {
error: undefined,
status: "success",
@@ -94,14 +99,20 @@ export const WithSomeFee = tests.createExample(SuccessView, {
currency: "USD",
value: 2,
fraction: 10000000,
- }
+ },
},
+ bankFee: {
+ currency: "EUR",
+ fraction: 0,
+ value: 1,
+ },
+
doWithdrawal: { onClick: nullFunction },
currentExchange: {
exchangeBaseUrl: "https://exchange.demo.taler.net",
tos: {},
} as Partial<ExchangeListItem> as any,
- withdrawalFee: {
+ toBeSent: {
currency: "USD",
fraction: 10000000,
value: 1,
@@ -123,14 +134,20 @@ export const WithoutFee = tests.createExample(SuccessView, {
currency: "USD",
value: 2,
fraction: 0,
- }
+ },
},
+ bankFee: {
+ currency: "EUR",
+ fraction: 0,
+ value: 1,
+ },
+
doWithdrawal: { onClick: nullFunction },
currentExchange: {
exchangeBaseUrl: "https://exchange.demo.taler.net",
tos: {},
} as Partial<ExchangeListItem> as any,
- withdrawalFee: {
+ toBeSent: {
currency: "USD",
fraction: 0,
value: 0,
@@ -152,14 +169,20 @@ export const EditExchangeUntouched = tests.createExample(SuccessView, {
currency: "USD",
value: 2,
fraction: 10000000,
- }
+ },
},
+ bankFee: {
+ currency: "EUR",
+ fraction: 0,
+ value: 1,
+ },
+
doWithdrawal: { onClick: nullFunction },
currentExchange: {
exchangeBaseUrl: "https://exchange.demo.taler.net",
tos: {},
} as Partial<ExchangeListItem> as any,
- withdrawalFee: {
+ toBeSent: {
currency: "USD",
fraction: 0,
value: 0,
@@ -181,14 +204,20 @@ export const EditExchangeModified = tests.createExample(SuccessView, {
currency: "USD",
value: 2,
fraction: 10000000,
- }
+ },
+ },
+ bankFee: {
+ currency: "EUR",
+ fraction: 0,
+ value: 1,
},
+
doWithdrawal: { onClick: nullFunction },
currentExchange: {
exchangeBaseUrl: "https://exchange.demo.taler.net",
tos: {},
} as Partial<ExchangeListItem> as any,
- withdrawalFee: {
+ toBeSent: {
currency: "USD",
fraction: 0,
value: 0,
@@ -211,15 +240,21 @@ export const WithAgeRestriction = tests.createExample(SuccessView, {
currency: "USD",
value: 2,
fraction: 10000000,
- }
+ },
},
+ bankFee: {
+ currency: "EUR",
+ fraction: 0,
+ value: 1,
+ },
+
doSelectExchange: {},
doWithdrawal: { onClick: nullFunction },
currentExchange: {
exchangeBaseUrl: "https://exchange.demo.taler.net",
tos: {},
} as Partial<ExchangeListItem> as any,
- withdrawalFee: {
+ toBeSent: {
currency: "USD",
fraction: 0,
value: 0,
@@ -240,8 +275,14 @@ export const WithAlternateCurrenciesNETZBON = tests.createExample(SuccessView, {
currency: "NETZBON",
value: 2,
fraction: 10000000,
- }
+ },
+ },
+ bankFee: {
+ currency: "EUR",
+ fraction: 0,
+ value: 1,
},
+
chooseCurrencies: ["NETZBON", "EUR"],
selectedCurrency: "NETZBON",
doWithdrawal: { onClick: nullFunction },
@@ -249,7 +290,7 @@ export const WithAlternateCurrenciesNETZBON = tests.createExample(SuccessView, {
exchangeBaseUrl: "https://exchange.netzbon.ch",
tos: {},
} as Partial<ExchangeListItem> as any,
- withdrawalFee: {
+ toBeSent: {
currency: "NETZBON",
fraction: 10000000,
value: 1,
@@ -270,27 +311,33 @@ export const WithAlternateCurrenciesEURO = tests.createExample(SuccessView, {
currency: "NETZBON",
value: 2,
fraction: 10000000,
- }
+ },
+ },
+ bankFee: {
+ currency: "EUR",
+ fraction: 0,
+ value: 1,
},
+
chooseCurrencies: ["NETZBON", "EUR"],
selectedCurrency: "EUR",
- changeCurrency: () => { },
+ changeCurrency: () => {},
conversionInfo: {
spec: {
- name: "EUR"
+ name: "EUR",
} as CurrencySpecification,
amount: {
currency: "EUR",
fraction: 10000000,
value: 1,
- }
+ },
},
doWithdrawal: { onClick: nullFunction },
currentExchange: {
exchangeBaseUrl: "https://exchange.netzbon.ch",
tos: {},
} as Partial<ExchangeListItem> as any,
- withdrawalFee: {
+ toBeSent: {
currency: "NETZBON",
fraction: 10000000,
value: 1,
@@ -311,27 +358,32 @@ export const WithAlternateCurrenciesEURO11 = tests.createExample(SuccessView, {
currency: "NETZBON",
value: 2,
fraction: 10000000,
- }
+ },
},
chooseCurrencies: ["NETZBON", "EUR"],
selectedCurrency: "EUR",
- changeCurrency: () => { },
+ changeCurrency: () => {},
+ bankFee: {
+ currency: "EUR",
+ fraction: 0,
+ value: 1,
+ },
conversionInfo: {
spec: {
- name: "EUR"
+ name: "EUR",
} as CurrencySpecification,
amount: {
currency: "EUR",
fraction: 10000000,
value: 2,
- }
+ },
},
doWithdrawal: { onClick: nullFunction },
currentExchange: {
exchangeBaseUrl: "https://exchange.netzbon.ch",
tos: {},
} as Partial<ExchangeListItem> as any,
- withdrawalFee: {
+ toBeSent: {
currency: "NETZBON",
fraction: 10000000,
value: 1,
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
index bce5f71e3..5a75cb4be 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/test.ts
@@ -123,7 +123,7 @@ describe("Withdraw CTA states", () => {
editableExchange: false,
maxAmount: "ARS:1",
wireFee: "ARS:0",
- },
+ },
},
);
@@ -208,7 +208,7 @@ describe("Withdraw CTA states", () => {
if (state.status !== "success") return;
expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
- expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0"));
+ expect(state.toBeSent).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.amount.value).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.doWithdrawal.onClick).not.undefined;
@@ -302,7 +302,7 @@ describe("Withdraw CTA states", () => {
if (state.status !== "success") return;
expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
- expect(state.withdrawalFee).deep.equal(Amounts.parseOrThrow("ARS:0"));
+ expect(state.toBeSent).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.amount.value).deep.equal(Amounts.parseOrThrow("ARS:2"));
expect(state.doWithdrawal.onClick).not.undefined;
diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
index 86d7248a4..b6a356de8 100644
--- a/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
+++ b/packages/taler-wallet-webextension/src/cta/Withdraw/views.tsx
@@ -39,6 +39,7 @@ import {
getAmountWithFee,
} from "../../wallet/Transaction.js";
import { State } from "./index.js";
+import { Amounts } from "@gnu-taler/taler-util";
export function FinalStateOperation(state: State.AlreadyCompleted): VNode {
const { i18n } = useTranslationContext();
@@ -143,8 +144,6 @@ export function FinalStateOperation(state: State.AlreadyCompleted): VNode {
export function SuccessView(state: State.Success): VNode {
const { i18n } = useTranslationContext();
- // const currentTosVersionIsAccepted =
- // state.currentExchange.tosStatus === ExchangeTosStatus.Accepted;
return (
<Fragment>
<section style={{ textAlign: "left" }}>
@@ -212,9 +211,10 @@ export function SuccessView(state: State.Success): VNode {
conversion={state.conversionInfo?.amount}
amount={getAmountWithFee(
state.toBeReceived,
- state.amount.value,
+ state.toBeSent,
"credit",
)}
+ bankFee={state.bankFee}
/>
}
/>
@@ -232,7 +232,6 @@ export function SuccessView(state: State.Success): VNode {
</section>
<section>
- {/* <div> */}
<TermsOfService exchangeUrl={state.currentExchange.exchangeBaseUrl}>
<Button
variant="contained"
@@ -245,20 +244,6 @@ export function SuccessView(state: State.Success): VNode {
</i18n.Translate>
</Button>
</TermsOfService>
- {/* </div>
- <div style={{ marginTop: 20 }}>
- <Button
- variant="text"
- color="success"
-
- disabled={!state.doAbort.onClick}
- onClick={state.doAbort.onClick}
- >
- <i18n.Translate>
- Cancel
- </i18n.Translate>
- </Button>
- </div> */}
</section>
{state.talerWithdrawUri ? (
<WithdrawWithMobile talerWithdrawUri={state.talerWithdrawUri} />
diff --git a/packages/taler-wallet-webextension/src/i18n/de.po b/packages/taler-wallet-webextension/src/i18n/de.po
index bc66f2136..e54c3067d 100644
--- a/packages/taler-wallet-webextension/src/i18n/de.po
+++ b/packages/taler-wallet-webextension/src/i18n/de.po
@@ -17,8 +17,8 @@ msgstr ""
"Project-Id-Version: Taler Wallet\n"
"Report-Msgid-Bugs-To: languages@taler.net\n"
"POT-Creation-Date: 2016-11-23 00:00+0100\n"
-"PO-Revision-Date: 2024-05-07 14:32+0000\n"
-"Last-Translator: Stefan Kügel <skuegel@web.de>\n"
+"PO-Revision-Date: 2024-07-01 22:17+0000\n"
+"Last-Translator: Stefan Kügel <stefan.kuegel@taler.net>\n"
"Language-Team: German <https://weblate.taler.net/projects/gnu-taler/"
"webextensions/de/>\n"
"Language: de\n"
@@ -26,7 +26,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Weblate 5.4.3\n"
+"X-Generator: Weblate 5.5.5\n"
#: src/NavigationBar.tsx:139
#, c-format
@@ -1365,7 +1365,7 @@ msgstr "Allgemeine Geschäftsbedingungen (AGB) ansehen"
#: src/wallet/ExchangeAddConfirm.tsx:45
#, c-format
msgid "Exchange URL"
-msgstr ""
+msgstr "URL des Exchange"
#: src/wallet/ExchangeAddConfirm.tsx:70
#, c-format
@@ -1735,8 +1735,8 @@ msgid ""
"Please check in your %1$s settings that you have IndexedDB enabled (check "
"the preference name %2$s)."
msgstr ""
-"Bitte prüfen Sie ihre %1$s Einstellungen, für die Sie IndexedDB verwenden ("
-"preference name %2$s prüfen)."
+"Bitte prüfen Sie in Ihren %1$s-Einstellungen, dass Sie IndexedDB aktiviert "
+"haben, und überprüfen Sie %2$s."
#: src/components/Diagnostics.tsx:70
#, c-format
diff --git a/packages/taler-wallet-webextension/src/platform/api.ts b/packages/taler-wallet-webextension/src/platform/api.ts
index 3c116fab2..2388647c1 100644
--- a/packages/taler-wallet-webextension/src/platform/api.ts
+++ b/packages/taler-wallet-webextension/src/platform/api.ts
@@ -227,6 +227,7 @@ export interface BackgroundPlatformAPI {
listenToAllChannels(
notifyNewMessage: <Op extends WalletOperations | BackgroundOperations>(
message: MessageFromFrontend<Op> & { id: string },
+ from: string,
) => Promise<MessageResponse>,
): void;
diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts b/packages/taler-wallet-webextension/src/platform/chrome.ts
index 056351e3f..276d464a0 100644
--- a/packages/taler-wallet-webextension/src/platform/chrome.ts
+++ b/packages/taler-wallet-webextension/src/platform/chrome.ts
@@ -53,7 +53,7 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
redirectTabToWalletPage,
registerAllIncomingConnections,
registerOnInstalled,
- listenToAllChannels ,
+ listenToAllChannels,
registerReloadOnNewVersion,
sendMessageToAllChannels,
openNewURLFromPopup,
@@ -276,18 +276,20 @@ async function sendMessageToBackground<
Op extends WalletOperations | BackgroundOperations,
>(message: MessageFromFrontend<Op>): Promise<MessageResponse> {
nextMessageIndex = (nextMessageIndex + 1) % (Number.MAX_SAFE_INTEGER - 100);
- const messageWithId = { ...message, id: `id_${nextMessageIndex}` };
+ const messageWithId = { ...message, id: `fg:${nextMessageIndex}` };
return new Promise<MessageResponse>((resolve, reject) => {
logger.trace("send operation to the wallet background", message);
let timedout = false;
const timerId = setTimeout(() => {
timedout = true;
- reject(TalerError.fromDetail(TalerErrorCode.GENERIC_TIMEOUT, {
- requestMethod: "wallet",
- requestUrl: message.operation,
- timeoutMs: 20 * 1000,
- }));
+ reject(
+ TalerError.fromDetail(TalerErrorCode.GENERIC_TIMEOUT, {
+ requestMethod: "wallet",
+ requestUrl: message.operation,
+ timeoutMs: 20 * 1000,
+ }),
+ );
}, 20 * 1000);
chrome.runtime.sendMessage(messageWithId, (backgroundResponse) => {
if (timedout) {
@@ -309,7 +311,9 @@ async function sendMessageToBackground<
* To be used by the foreground
*/
let notificationPort: chrome.runtime.Port | undefined;
-function listenToWalletBackground(listener: (message: MessageFromBackend) => void): () => void {
+function listenToWalletBackground(
+ listener: (message: MessageFromBackend) => void,
+): () => void {
if (notificationPort === undefined) {
notificationPort = chrome.runtime.connect({ name: "notifications" });
}
@@ -380,13 +384,18 @@ function registerAllIncomingConnections(): void {
});
}
+function createTabId(tab: chrome.tabs.Tab | undefined) {
+ return !tab ? "popup" : `${tab.windowId}:${tab.id}`;
+}
+
function listenToAllChannels(
notifyNewMessage: <Op extends WalletOperations | BackgroundOperations>(
message: MessageFromFrontend<Op> & { id: string },
+ id: string,
) => Promise<MessageResponse>,
): void {
chrome.runtime.onMessage.addListener((message, sender, reply) => {
- notifyNewMessage(message)
+ notifyNewMessage(message, createTabId(sender.tab))
.then((apiResponse) => {
try {
reply(apiResponse);
@@ -483,26 +492,26 @@ function setAlertedIcon(): void {
interface OffscreenCanvasRenderingContext2D
extends CanvasState,
- CanvasTransform,
- CanvasCompositing,
- CanvasImageSmoothing,
- CanvasFillStrokeStyles,
- CanvasShadowStyles,
- CanvasFilters,
- CanvasRect,
- CanvasDrawPath,
- CanvasUserInterface,
- CanvasText,
- CanvasDrawImage,
- CanvasImageData,
- CanvasPathDrawingStyles,
- CanvasTextDrawingStyles,
- CanvasPath {
+ CanvasTransform,
+ CanvasCompositing,
+ CanvasImageSmoothing,
+ CanvasFillStrokeStyles,
+ CanvasShadowStyles,
+ CanvasFilters,
+ CanvasRect,
+ CanvasDrawPath,
+ CanvasUserInterface,
+ CanvasText,
+ CanvasDrawImage,
+ CanvasImageData,
+ CanvasPathDrawingStyles,
+ CanvasTextDrawingStyles,
+ CanvasPath {
readonly canvas: OffscreenCanvas;
}
declare const OffscreenCanvasRenderingContext2D: {
prototype: OffscreenCanvasRenderingContext2D;
- new(): OffscreenCanvasRenderingContext2D;
+ new (): OffscreenCanvasRenderingContext2D;
};
interface OffscreenCanvas extends EventTarget {
@@ -515,7 +524,7 @@ interface OffscreenCanvas extends EventTarget {
}
declare const OffscreenCanvas: {
prototype: OffscreenCanvas;
- new(width: number, height: number): OffscreenCanvas;
+ new (width: number, height: number): OffscreenCanvas;
};
function createCanvas(size: number): OffscreenCanvas {
@@ -760,7 +769,6 @@ function listenNetworkConnectionState(
};
}
-
function runningOnPrivateMode(): boolean {
return chrome.extension.inIncognitoContext;
}
diff --git a/packages/taler-wallet-webextension/src/platform/dev.ts b/packages/taler-wallet-webextension/src/platform/dev.ts
index b53e8f3c4..844a5c517 100644
--- a/packages/taler-wallet-webextension/src/platform/dev.ts
+++ b/packages/taler-wallet-webextension/src/platform/dev.ts
@@ -95,7 +95,7 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
useServiceWorkerAsBackgroundProcess: () => false,
listenToAllChannels: (
- notifyNewMessage: (message: any) => Promise<MessageResponse>,
+ notifyNewMessage: (message: any, from: string) => Promise<MessageResponse>,
) => {
window.addEventListener(
"message",
@@ -103,7 +103,7 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
if (event.data.type !== "command") return;
const sender = event.data.header.replyMe;
- notifyNewMessage(event.data.body as any).then((resp) => {
+ notifyNewMessage(event.data.body as any, sender).then((resp) => {
logger.trace(`listenToAllChannels: from ${sender}`, event);
if (event.source) {
const msg: IframeMessageResponse = {
@@ -199,4 +199,3 @@ interface IframeMessageCommand {
}
export default api;
-
diff --git a/packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts b/packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts
index 3b7cbcbb7..5e781121b 100644
--- a/packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts
+++ b/packages/taler-wallet-webextension/src/taler-wallet-interaction-loader.ts
@@ -14,7 +14,11 @@
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { CoreApiResponse, TalerError, TalerErrorCode } from "@gnu-taler/taler-util";
+import {
+ CoreApiResponse,
+ TalerError,
+ TalerErrorCode,
+} from "@gnu-taler/taler-util";
import type { MessageFromBackend } from "./platform/api.js";
/**
@@ -51,8 +55,6 @@ const rootElementIsHTML =
// "meta[name=taler-support]",
// );
-
-
function validateTalerUri(uri: string): boolean {
return (
!!uri && (uri.startsWith("taler://") || uri.startsWith("taler+http://"))
@@ -61,7 +63,9 @@ function validateTalerUri(uri: string): boolean {
function convertURIToWebExtensionPath(uri: string) {
const url = new URL(
- chrome.runtime.getURL(`static/wallet.html#/taler-uri/${encodeURIComponent(uri)}`),
+ chrome.runtime.getURL(
+ `static/wallet.html#/taler-uri/${encodeURIComponent(uri)}`,
+ ),
);
return url.href;
}
@@ -75,7 +79,7 @@ const shouldNotInject =
!rootElementIsHTML;
const logger = {
- debug: (...msg: any[]) => { },
+ debug: (...msg: any[]) => {},
info: (...msg: any[]) =>
console.log(`${new Date().toISOString()} TALER`, ...msg),
error: (...msg: any[]) =>
@@ -87,7 +91,7 @@ const logger = {
/**
*/
function redirectToTalerActionHandler(element: HTMLMetaElement) {
- const name = element.getAttribute("name")
+ const name = element.getAttribute("name");
if (!name) return;
if (name !== "taler-uri") return;
const uri = element.getAttribute("content");
@@ -98,20 +102,20 @@ function redirectToTalerActionHandler(element: HTMLMetaElement) {
return;
}
- const walletPage = convertURIToWebExtensionPath(uri)
- window.location.replace(walletPage)
+ const walletPage = convertURIToWebExtensionPath(uri);
+ window.location.replace(walletPage);
}
function injectTalerSupportScript(head: HTMLHeadElement, trusted: boolean) {
- const meta = head.querySelector("meta[name=taler-support]")
+ const meta = head.querySelector("meta[name=taler-support]");
if (!meta) return;
const content = meta.getAttribute("content");
if (!content) return;
- const features = content.split(",")
+ const features = content.split(",");
const debugEnabled = meta.getAttribute("debug") === "true";
- const hijackEnabled = features.indexOf("uri") !== -1
- const talerApiEnabled = features.indexOf("api") !== -1 && trusted
+ const hijackEnabled = features.indexOf("uri") !== -1;
+ const talerApiEnabled = features.indexOf("api") !== -1 && trusted;
const scriptTag = document.createElement("script");
scriptTag.setAttribute("async", "false");
@@ -131,14 +135,16 @@ function injectTalerSupportScript(head: HTMLHeadElement, trusted: boolean) {
scriptTag.src = url.href;
try {
- head.insertBefore(scriptTag, head.children.length ? head.children[0] : null);
+ head.insertBefore(
+ scriptTag,
+ head.children.length ? head.children[0] : null,
+ );
} catch (e) {
logger.info("inserting link handler failed!");
logger.error(e);
}
}
-
export interface ExtensionOperations {
isAutoOpenEnabled: {
request: void;
@@ -177,31 +183,38 @@ async function callBackground<Op extends keyof ExtensionOperations>(
return response.result as any;
}
-
let nextMessageIndex = 0;
/**
- *
- * @param message
- * @returns
+ *
+ * @param message
+ * @returns
*/
async function sendMessageToBackground<Op extends keyof ExtensionOperations>(
message: MessageFromExtension<Op>,
): Promise<MessageResponse> {
- const messageWithId = { ...message, id: `id_${nextMessageIndex++ % 1000}` };
+ const messageWithId = { ...message, id: `ld:${nextMessageIndex++ % 1000}` };
if (!chrome.runtime.id) {
- return Promise.reject(TalerError.fromDetail(TalerErrorCode.WALLET_CORE_NOT_AVAILABLE, {}))
+ return Promise.reject(
+ TalerError.fromDetail(TalerErrorCode.WALLET_CORE_NOT_AVAILABLE, {}),
+ );
}
return new Promise<any>((resolve, reject) => {
- logger.debug("send operation to the wallet background", message, chrome.runtime.id);
+ logger.debug(
+ "send operation to the wallet background",
+ message,
+ chrome.runtime.id,
+ );
let timedout = false;
const timerId = setTimeout(() => {
timedout = true;
- reject(TalerError.fromDetail(TalerErrorCode.GENERIC_TIMEOUT, {
- requestMethod: "wallet",
- requestUrl: message.operation,
- timeoutMs: 20 * 1000,
- }))
+ reject(
+ TalerError.fromDetail(TalerErrorCode.GENERIC_TIMEOUT, {
+ requestMethod: "wallet",
+ requestUrl: message.operation,
+ timeoutMs: 20 * 1000,
+ }),
+ );
}, 20 * 1000); //five seconds
try {
chrome.runtime.sendMessage(messageWithId, (backgroundResponse) => {
@@ -218,7 +231,7 @@ async function sendMessageToBackground<Op extends keyof ExtensionOperations>(
return true;
});
} catch (e) {
- console.log(e)
+ console.log(e);
}
});
}
@@ -240,71 +253,76 @@ function listenToWalletBackground(listener: (m: any) => void): () => void {
const loaderSettings = {
isAutoOpenEnabled: false,
isDomainTrusted: false,
-}
+};
function start(
onTalerMetaTagFound: (listener: (el: HTMLMetaElement) => void) => void,
- onHeadReady: (listener: (el: HTMLHeadElement) => void) => void
+ onHeadReady: (listener: (el: HTMLHeadElement) => void) => void,
) {
// do not run everywhere, this is just expected to run on site
// that are aware of taler
if (shouldNotInject) return;
- const isAutoOpenEnabled_promise = callBackground("isAutoOpenEnabled", undefined).then(result => {
+ const isAutoOpenEnabled_promise = callBackground(
+ "isAutoOpenEnabled",
+ undefined,
+ ).then((result) => {
loaderSettings.isAutoOpenEnabled = result;
return result;
- })
+ });
const isDomainTrusted_promise = callBackground("isDomainTrusted", {
- domain: window.location.origin
- }).then(result => {
+ domain: window.location.origin,
+ }).then((result) => {
loaderSettings.isDomainTrusted = result;
return result;
- })
+ });
onTalerMetaTagFound(async (el) => {
await isAutoOpenEnabled_promise;
if (!loaderSettings.isAutoOpenEnabled) {
return;
}
- redirectToTalerActionHandler(el)
- })
+ redirectToTalerActionHandler(el);
+ });
onHeadReady(async (el) => {
- const trusted = await isDomainTrusted_promise
- injectTalerSupportScript(el, trusted)
- })
+ const trusted = await isDomainTrusted_promise;
+ injectTalerSupportScript(el, trusted);
+ });
listenToWalletBackground((e: MessageFromBackend) => {
- if (e.type === "web-extension" && e.notification.type === "settings-change") {
- const settings = e.notification.currentValue
- loaderSettings.isAutoOpenEnabled = settings.autoOpen
+ if (
+ e.type === "web-extension" &&
+ e.notification.type === "settings-change"
+ ) {
+ const settings = e.notification.currentValue;
+ loaderSettings.isAutoOpenEnabled = settings.autoOpen;
}
- })
-
+ });
}
function isCorrectMetaElement(el: HTMLMetaElement): boolean {
- const name = el.getAttribute("name")
+ const name = el.getAttribute("name");
if (!name) return false;
if (name !== "taler-uri") return false;
const uri = el.getAttribute("content");
if (!uri) return false;
- return true
+ return true;
}
/**
* Tries to find taler meta tag ASAP and report
- * @param notify
- * @returns
+ * @param notify
+ * @returns
*/
function notifyWhenTalerUriIsFound(notify: (el: HTMLMetaElement) => void) {
if (document.head) {
- const element = document.head.querySelector("meta[name=taler-uri]")
+ const element = document.head.querySelector("meta[name=taler-uri]");
if (!element) return;
if (!(element instanceof HTMLMetaElement)) return;
if (isCorrectMetaElement(element)) {
- notify(element)
+ notify(element);
}
return;
}
@@ -315,34 +333,33 @@ function notifyWhenTalerUriIsFound(notify: (el: HTMLMetaElement) => void) {
mut.addedNodes.forEach((added) => {
if (added instanceof HTMLMetaElement) {
if (isCorrectMetaElement(added)) {
- notify(added)
- obs.disconnect()
+ notify(added);
+ obs.disconnect();
}
}
});
}
});
} catch (e) {
- console.error(e)
+ console.error(e);
}
- })
+ });
obs.observe(document, {
childList: true,
subtree: true,
attributes: false,
- })
-
+ });
}
/**
* Tries to find HEAD tag ASAP and report
- * @param notify
- * @returns
+ * @param notify
+ * @returns
*/
function notifyWhenHeadIsFound(notify: (el: HTMLHeadElement) => void) {
if (document.head) {
- notify(document.head)
+ notify(document.head);
return;
}
const obs = new MutationObserver(async function (mutations) {
@@ -351,22 +368,22 @@ function notifyWhenHeadIsFound(notify: (el: HTMLHeadElement) => void) {
if (mut.type === "childList") {
mut.addedNodes.forEach((added) => {
if (added instanceof HTMLHeadElement) {
- notify(added)
- obs.disconnect()
+ notify(added);
+ obs.disconnect();
}
});
}
});
} catch (e) {
- console.error(e)
+ console.error(e);
}
- })
+ });
obs.observe(document, {
childList: true,
subtree: true,
attributes: false,
- })
+ });
}
start(notifyWhenTalerUriIsFound, notifyWhenHeadIsFound);
diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
index 8f23c0685..9feb03714 100644
--- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
@@ -22,7 +22,7 @@ import {
LogLevel,
NotificationType,
ScopeType,
- stringifyWithdrawExchange
+ stringifyWithdrawExchange,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
@@ -112,21 +112,21 @@ export function DeveloperPage(): VNode {
const currencies: { [ex: string]: string } = {};
const money_by_exchange = coins.reduce(
(prev, cur) => {
- const denom = Amounts.parseOrThrow(cur.denom_value);
- if (!prev[cur.exchange_base_url]) {
- prev[cur.exchange_base_url] = [];
- currencies[cur.exchange_base_url] = denom.currency;
+ const denom = Amounts.parseOrThrow(cur.denomValue);
+ if (!prev[cur.exchangeBaseUrl]) {
+ prev[cur.exchangeBaseUrl] = [];
+ currencies[cur.exchangeBaseUrl] = denom.currency;
}
- prev[cur.exchange_base_url].push({
+ prev[cur.exchangeBaseUrl].push({
// ageKeysCount: cur.ageCommitmentProof?.proof.privateKeys.length,
denom_value: denom.value,
denom_fraction: denom.fraction,
// remain_value: parseFloat(
// Amounts.stringifyValue(Amounts.parseOrThrow(cur.remaining_value)),
// ),
- status: cur.coin_status,
- from_refresh: cur.refresh_parent_coin_pub !== undefined,
- id: cur.coin_pub,
+ status: cur.coinStatus,
+ from_refresh: cur.refreshParentCoinPub !== undefined,
+ id: cur.coinPub,
});
return prev;
},
@@ -351,7 +351,7 @@ export function DeveloperPage(): VNode {
<a
href={new URL(`/keys`, e.exchangeBaseUrl).href}
target="_blank"
- rel="noreferrer"
+ rel="noreferrer"
>
{e.exchangeBaseUrl}
</a>
diff --git a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
index 1f0293352..339ded173 100644
--- a/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Transaction.tsx
@@ -1416,9 +1416,11 @@ export function TransferPickupDetails({
export function WithdrawDetails({
conversion,
amount,
+ bankFee,
}: {
conversion?: AmountJson;
amount: AmountWithFee;
+ bankFee?: AmountJson;
}): VNode {
const { i18n } = useTranslationContext();
@@ -1481,6 +1483,16 @@ export function WithdrawDetails({
</tr>
</Fragment>
)}
+ {!bankFee || Amounts.isZero(bankFee) ? undefined : (
+ <tr>
+ <td>
+ <i18n.Translate>Bank fee</i18n.Translate>
+ </td>
+ <td>
+ <Amount value={bankFee} maxFracSize={amount.maxFrac} />
+ </td>
+ </tr>
+ )}
</PurchaseDetailsTable>
);
}
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts
index 47b466fcd..8361a098d 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -31,7 +31,7 @@ import {
TalerError,
TalerErrorCode,
TalerErrorDetail,
- WalletNotification
+ WalletNotification,
} from "@gnu-taler/taler-util";
import {
WalletCoreApiClient,
@@ -55,7 +55,7 @@ import { WalletActivityTrack } from "./wxBackend.js";
const logger = new Logger("wxApi");
-export const WALLET_CORE_SUPPORTED_VERSION = "5:0:0"
+export const WALLET_CORE_SUPPORTED_VERSION = "7:0:0";
export interface ExtendedPermissionsResponse {
newValue: boolean;
@@ -77,6 +77,7 @@ export interface BackgroundOperations {
getNotifications: {
request: {
filter: string;
+ operationsFrom?: string;
};
response: WalletActivityTrack[];
};
@@ -93,7 +94,10 @@ export interface BackgroundOperations {
};
}
-export type WalletEvent = { notification: WalletNotification, when: AbsoluteTime }
+export type WalletEvent = {
+ notification: WalletNotification;
+ when: AbsoluteTime;
+};
export interface BackgroundApiClient {
call<Op extends keyof BackgroundOperations>(
@@ -139,10 +143,14 @@ class BackgroundApiClientImpl implements BackgroundApiClient {
response = await platform.sendMessageToBackground(message);
} catch (error) {
if (error instanceof Error) {
- throw new BackgroundError(operation, {
- code: TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR,
- when: AbsoluteTime.now(),
- }, error);
+ throw new BackgroundError(
+ operation,
+ {
+ code: TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR,
+ when: AbsoluteTime.now(),
+ },
+ error,
+ );
}
throw error;
}
@@ -168,6 +176,8 @@ class WalletApiClientImpl implements WalletCoreApiClient {
): Promise<WalletCoreResponseType<Op>> {
let response: CoreApiResponse;
try {
+ // FIXME: This type must be fixed and needs documentation!
+ // @ts-ignore
const message: MessageFromFrontendWallet<Op> = {
channel: "wallet",
operation,
@@ -176,10 +186,14 @@ class WalletApiClientImpl implements WalletCoreApiClient {
response = await platform.sendMessageToBackground(message);
} catch (error) {
if (error instanceof Error) {
- throw new BackgroundError(operation, {
- code: TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR,
- when: AbsoluteTime.now(),
- }, error);
+ throw new BackgroundError(
+ operation,
+ {
+ code: TalerErrorCode.GENERIC_UNEXPECTED_REQUEST_ERROR,
+ when: AbsoluteTime.now(),
+ },
+ error,
+ );
}
throw error;
}
@@ -187,7 +201,7 @@ class WalletApiClientImpl implements WalletCoreApiClient {
throw new BackgroundError(
`Wallet operation "${operation}" failed`,
response.error,
- TalerError.fromUncheckedDetail(response.error)
+ TalerError.fromUncheckedDetail(response.error),
);
}
logger.trace("got response", response);
@@ -205,7 +219,9 @@ function onUpdateNotification(
return;
};
const onNewMessage = (message: MessageFromBackend): void => {
- const shouldNotify = message.type === "wallet" && messageTypes.includes(message.notification.type);
+ const shouldNotify =
+ message.type === "wallet" &&
+ messageTypes.includes(message.notification.type);
if (shouldNotify) {
doCallback(message.notification);
}
@@ -226,7 +242,7 @@ function trigger(w: ExtensionNotification) {
platform.triggerWalletEvent({
type: "web-extension",
notification: w,
- })
+ });
}
export const wxApi = {
diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts b/packages/taler-wallet-webextension/src/wxBackend.ts
index a0b9f2908..ab3c465c4 100644
--- a/packages/taler-wallet-webextension/src/wxBackend.ts
+++ b/packages/taler-wallet-webextension/src/wxBackend.ts
@@ -91,19 +91,15 @@ async function resetDb(): Promise<void> {
}
export type WalletActivityTrack = {
- id: number;
+ // id: number;
events: (WalletNotification & { when: AbsoluteTime })[];
start: AbsoluteTime;
type: NotificationType;
end: AbsoluteTime;
groupId: string;
+ requestId?: string; //only for request event
};
-let counter = 0;
-function getUniqueId(): number {
- return counter++;
-}
-
//FIXME: maybe circular buffer
const activity: WalletActivityTrack[] = [];
@@ -120,10 +116,9 @@ function convertWalletActivityNotification(
if (found) {
found.end = event.when;
found.events.unshift(event);
- return found;
+ return undefined;
}
return {
- id: getUniqueId(),
type: event.type,
start: event.when,
end: AbsoluteTime.never(),
@@ -134,7 +129,6 @@ function convertWalletActivityNotification(
case NotificationType.BackupOperationError: {
const groupId = "";
return {
- id: getUniqueId(),
type: event.type,
start: event.when,
end: AbsoluteTime.never(),
@@ -148,10 +142,9 @@ function convertWalletActivityNotification(
if (found) {
found.end = event.when;
found.events.unshift(event);
- return found;
+ return undefined;
}
return {
- id: getUniqueId(),
type: event.type,
start: event.when,
end: AbsoluteTime.never(),
@@ -168,10 +161,9 @@ function convertWalletActivityNotification(
if (found) {
found.end = event.when;
found.events.unshift(event);
- return found;
+ return undefined;
}
return {
- id: getUniqueId(),
type: event.type,
start: event.when,
end: AbsoluteTime.never(),
@@ -181,14 +173,13 @@ function convertWalletActivityNotification(
}
case NotificationType.Idle: {
const groupId = "";
- return({
- id: getUniqueId(),
+ return {
type: event.type,
start: event.when,
end: AbsoluteTime.never(),
events: [event],
groupId,
- });
+ };
}
case NotificationType.TaskObservabilityEvent: {
const groupId = `${event.type}:${event.taskId}`;
@@ -196,16 +187,15 @@ function convertWalletActivityNotification(
if (found) {
found.end = event.when;
found.events.unshift(event);
- return found;
+ return undefined;
}
- return({
- id: getUniqueId(),
+ return {
type: event.type,
start: event.when,
end: AbsoluteTime.never(),
events: [event],
groupId,
- });
+ };
}
case NotificationType.RequestObservabilityEvent: {
const groupId = `${event.type}:${event.operation}:${event.requestId}`;
@@ -213,16 +203,16 @@ function convertWalletActivityNotification(
if (found) {
found.end = event.when;
found.events.unshift(event);
- return found;
+ return undefined;
}
- return({
- id: getUniqueId(),
+ return {
type: event.type,
start: event.when,
end: AbsoluteTime.never(),
events: [event],
groupId,
- });
+ requestId: event.requestId,
+ };
}
}
}
@@ -241,14 +231,24 @@ function addNewWalletActivityNotification(
async function getNotifications({
filter,
+ operationFrom,
}: {
filter: string;
+ operationFrom?: string;
}): Promise<WalletActivityTrack[]> {
- if (!filter) return activity;
-
const rg = new RegExp(`.*${filter}.*`);
return activity.filter((event) => {
- return rg.test(event.groupId.toLowerCase());
+ if (operationFrom) {
+ if (event.type !== NotificationType.RequestObservabilityEvent) {
+ return false;
+ }
+ if (event.requestId) {
+ return event.requestId.startsWith(operationFrom);
+ }
+ }
+ if (!filter) return true;
+ const testFilter = rg.test(event.groupId.toLowerCase());
+ return testFilter;
});
}
@@ -318,7 +318,10 @@ let nextMessageIndex = 0;
async function dispatch<
Op extends WalletOperations | BackgroundOperations | ExtensionOperations,
->(req: MessageFromFrontend<Op> & { id: string }): Promise<MessageResponse> {
+>(
+ req: MessageFromFrontend<Op> & { id: string },
+ from: string,
+): Promise<MessageResponse> {
nextMessageIndex = (nextMessageIndex + 1) % (Number.MAX_SAFE_INTEGER - 100);
switch (req.channel) {
@@ -402,7 +405,7 @@ async function dispatch<
};
}
//multiple client can create the same id, send the wallet an unique key
- const newId = `${req.id}_${nextMessageIndex}`;
+ const newId = `${from}:${req.id}`;
const resp = await w.handleCoreApiRequest(
req.operation,
newId,
@@ -516,10 +519,10 @@ export async function wxMain(): Promise<void> {
// Handlers for messages coming directly from the content
// script on the page
logger.trace("listen all channels");
- platform.listenToAllChannels(async (message) => {
+ platform.listenToAllChannels(async (message, from) => {
//wait until wallet is initialized
await afterWalletIsInitialized;
- const result = await dispatch(message);
+ const result = await dispatch(message, from);
return result;
});