aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-02-27 12:30:24 -0300
committerSebastian <sebasjm@gmail.com>2024-02-27 12:30:24 -0300
commit3a889c177dd35a114d2c95efd296274cd185ce52 (patch)
treed140b74495c7169996b6ddae6835d107a00271c0 /packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
parentff46a080e55bff821f823256bed1bcebdcc1efe9 (diff)
downloadwallet-core-3a889c177dd35a114d2c95efd296274cd185ce52.tar.xz
fix #8469
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx')
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx362
1 files changed, 226 insertions, 136 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
index 0db7a07eb..d19fef155 100644
--- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
@@ -20,8 +20,12 @@ import {
CoinDumpJson,
CoinStatus,
ExchangeListItem,
+ ExchangeTosStatus,
LogLevel,
NotificationType,
+ ScopeType,
+ parseWithdrawUri,
+ stringifyWithdrawExchange,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
@@ -31,7 +35,7 @@ import { useEffect, useRef, useState } from "preact/hooks";
import { Checkbox } from "../components/Checkbox.js";
import { SelectList } from "../components/SelectList.js";
import { Time } from "../components/Time.js";
-import { NotifyUpdateFadeOut } from "../components/styled/index.js";
+import { DestructiveText, LinkPrimary, NotifyUpdateFadeOut, SubTitle, SuccessText, WarningText } from "../components/styled/index.js";
import { useAlertContext } from "../context/alert.js";
import { useBackendContext } from "../context/backend.js";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
@@ -40,50 +44,8 @@ import { Button } from "../mui/Button.js";
import { Grid } from "../mui/Grid.js";
import { Paper } from "../mui/Paper.js";
import { TextField } from "../mui/TextField.js";
-
-export function DeveloperPage(): VNode {
- const listenAllEvents = Array.from<NotificationType>({ length: 1 });
-
- const api = useBackendContext();
-
- const response = useAsyncAsHook(async () => {
- const op = await api.wallet.call(
- WalletApiOperation.GetPendingOperations,
- {},
- );
- const c = await api.wallet.call(WalletApiOperation.DumpCoins, {});
- const ex = await api.wallet.call(WalletApiOperation.ListExchanges, {});
- return {
- operations: op.pendingOperations,
- coins: c.coins,
- exchanges: ex.exchanges,
- };
- });
-
- useEffect(() => {
- return api.listener.onUpdateNotification(listenAllEvents, response?.retry);
- });
-
- const nonResponse = { operations: [], coins: [], exchanges: [] };
- const { operations, coins, exchanges } =
- response === undefined
- ? nonResponse
- : response.hasError
- ? nonResponse
- : response.response;
-
- return (
- <View
- operations={operations}
- coins={coins}
- exchanges={exchanges}
- onDownloadDatabase={async () => {
- const db = await api.wallet.call(WalletApiOperation.ExportDb, {});
- return JSON.stringify(db);
- }}
- />
- );
-}
+import { Pages } from "../NavigationBar.js";
+import { CoinInfo } from "@gnu-taler/taler-wallet-core/dbless";
type CoinsInfo = CoinDumpJson["coins"];
type CalculatedCoinfInfo = {
@@ -103,23 +65,20 @@ type SplitedCoinInfo = {
export interface Props {
// FIXME: Pending operations don't exist anymore.
- operations: any[];
- coins: CoinsInfo;
- exchanges: ExchangeListItem[];
- onDownloadDatabase: () => Promise<string>;
}
function hashObjectId(o: any): string {
return JSON.stringify(o);
}
-export function View({ operations, coins, onDownloadDatabase }: Props): VNode {
+export function DeveloperPage({ }: Props): VNode {
const { i18n } = useTranslationContext();
const [downloadedDatabase, setDownloadedDatabase] = useState<
{ time: Date; content: string } | undefined
>(undefined);
async function onExportDatabase(): Promise<void> {
- const content = await onDownloadDatabase();
+ const db = await api.wallet.call(WalletApiOperation.ExportDb, {});
+ const content = JSON.stringify(db);
setDownloadedDatabase({
time: new Date(),
content,
@@ -136,10 +95,28 @@ export function View({ operations, coins, onDownloadDatabase }: Props): VNode {
const [settings, updateSettings] = useSettings();
const { safely } = useAlertContext();
- const hook = useAsyncAsHook(() =>
- api.wallet.call(WalletApiOperation.ListExchanges, {}),
- );
+ // const hook = useAsyncAsHook(() =>
+ // api.wallet.call(WalletApiOperation.ListExchanges, {}),
+ // );
+ const listenAllEvents = Array.from<NotificationType>({ length: 1 });
+
+ const hook = useAsyncAsHook(async () => {
+ const list = await api.wallet.call(WalletApiOperation.ListExchanges, {});
+ const version = await api.wallet.call(WalletApiOperation.GetVersion, {});
+ const operations: any[] = await api.wallet.call(
+ WalletApiOperation.GetPendingOperations,
+ {},
+ );
+ const coins = await api.wallet.call(WalletApiOperation.DumpCoins, {});
+ return { exchanges: list.exchanges, version, coins, operations };
+ });
const exchangeList = hook && !hook.hasError ? hook.response.exchanges : [];
+ const coins = hook && !hook.hasError ? hook.response.coins.coins : [];
+ const operations = hook && !hook.hasError ? hook.response.operations : [];
+
+ useEffect(() => {
+ return api.listener.onUpdateNotification(listenAllEvents, hook?.retry);
+ });
const currencies: { [ex: string]: string } = {};
const money_by_exchange = coins.reduce(
@@ -205,30 +182,6 @@ export function View({ operations, coins, onDownloadDatabase }: Props): VNode {
<Grid item>
<Button
variant="contained"
- onClick={() => {
- return api.background.call("sum", [1, 2, 3]).then((r) => {
- console.log("SUM", r);
- });
- }}
- >
- <i18n.Translate>sum 123</i18n.Translate>
- </Button>
- </Grid>
- <Grid item>
- <Button
- variant="contained"
- onClick={() => {
- return api.background.call("freeze", 4000).then(() => {
- console.log("WAIT");
- });
- }}
- >
- <i18n.Translate>freeze 4000</i18n.Translate>
- </Button>
- </Grid>
- <Grid item>
- <Button
- variant="contained"
onClick={async () => fileRef?.current?.click()}
>
<i18n.Translate>import database</i18n.Translate>
@@ -260,64 +213,6 @@ export function View({ operations, coins, onDownloadDatabase }: Props): VNode {
<Button
variant="contained"
onClick={async () => {
- navigator.registerProtocolHandler(
- "taler",
- `${window.location.origin}/static/wallet.html#/cta/withdraw?talerWithdrawUri=%s`,
- );
- }}
- >
- <i18n.Translate>Register taler:// handler</i18n.Translate>
- </Button>
- </Grid>
- <Grid item>
- <Button
- variant="contained"
- onClick={async () => {
- const n = navigator as any;
- if ("unregisterProtocolHandler" in n) {
- n.unregisterProtocolHandler(
- "taler",
- `${window.location.origin}/static/wallet.html#/cta/withdraw?talerWithdrawUri=%s`,
- );
- }
- }}
- >
- <i18n.Translate>Remove taler:// handler</i18n.Translate>
- </Button>
- </Grid>{" "}
- <Grid item>
- <Button
- variant="contained"
- onClick={async () => {
- navigator.registerProtocolHandler(
- "ext+taler",
- `${window.location.origin}/static/wallet.html#/cta/withdraw?talerWithdrawUri=%s`,
- );
- }}
- >
- <i18n.Translate>Register ext+taler:// handler</i18n.Translate>
- </Button>
- </Grid>
- <Grid item>
- <Button
- variant="contained"
- onClick={async () => {
- const n = navigator as any;
- if ("unregisterProtocolHandler" in n) {
- n.unregisterProtocolHandler(
- "ext+taler",
- `${window.location.origin}/static/wallet.html#/cta/withdraw?talerWithdrawUri=%s`,
- );
- }
- }}
- >
- <i18n.Translate>Remove ext+taler:// handler</i18n.Translate>
- </Button>
- </Grid>
- <Grid item>
- <Button
- variant="contained"
- onClick={async () => {
const result = await Promise.all(
exchangeList.map(async (exchange) => {
const url = exchange.exchangeBaseUrl;
@@ -359,6 +254,201 @@ export function View({ operations, coins, onDownloadDatabase }: Props): VNode {
})}
/>
+
+ <SubTitle>
+ <i18n.Translate>Exchange Entries</i18n.Translate>
+ </SubTitle>
+ {!exchangeList || !exchangeList.length ? (
+ <div>
+ <i18n.Translate>No exchange yet</i18n.Translate>
+ </div>
+ ) : (
+ <Fragment>
+ <table>
+ <thead>
+ <tr>
+ <th>
+ <i18n.Translate>Currency</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>URL</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>Status</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>Terms of Service</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>Last Update</i18n.Translate>
+ </th>
+ <th>
+ <i18n.Translate>Actions</i18n.Translate>
+ </th>
+ </tr>
+ </thead>
+ <tbody>
+ {exchangeList.map((e, idx) => {
+ function TosStatus(): VNode {
+ switch (e.tosStatus) {
+ case ExchangeTosStatus.Accepted:
+ return (
+ <SuccessText>
+ <i18n.Translate>ok</i18n.Translate>
+ </SuccessText>
+ );
+ case ExchangeTosStatus.Pending:
+ return (
+ <WarningText>
+ <i18n.Translate>pending</i18n.Translate>
+ </WarningText>
+ );
+ case ExchangeTosStatus.Proposed:
+ return <i18n.Translate>proposed</i18n.Translate>;
+ default:
+ return (
+ <DestructiveText>
+ <i18n.Translate>
+ unknown (exchange status should be updated)
+ </i18n.Translate>
+ </DestructiveText>
+ );
+ }
+ }
+ const uri = !e.masterPub ? undefined : stringifyWithdrawExchange({
+ exchangeBaseUrl: e.exchangeBaseUrl,
+ exchangePub: e.masterPub,
+ });
+ return (
+ <tr key={idx}>
+ <td>
+ <a href={!uri? undefined: Pages.defaultCta({ uri })}>
+ {e.scopeInfo ? `${e.scopeInfo.currency} (${e.scopeInfo.type === ScopeType.Global ? "global" : "regional"})` : e.currency}
+ </a>
+ </td>
+ <td>
+ <a href={new URL(`/keys`, e.exchangeBaseUrl).href} target="_blank">{e.exchangeBaseUrl}</a>
+ </td>
+ <td>
+ {e.exchangeEntryStatus} / {e.exchangeUpdateStatus}
+ </td>
+ <td>
+ <TosStatus />
+ </td>
+ <td>
+ {e.lastUpdateTimestamp
+ ? AbsoluteTime.toIsoString(
+ AbsoluteTime.fromPreciseTimestamp(
+ e.lastUpdateTimestamp,
+ ),
+ )
+ : "never"}
+ </td>
+ <td>
+ <button
+ onClick={() => {
+ api.wallet.call(
+ WalletApiOperation.UpdateExchangeEntry,
+ {
+ exchangeBaseUrl: e.exchangeBaseUrl,
+ force: true,
+ },
+ );
+ }}
+ >
+ Reload
+ </button>
+ <button
+ onClick={() => {
+ api.wallet.call(
+ WalletApiOperation.DeleteExchange,
+ {
+ exchangeBaseUrl: e.exchangeBaseUrl,
+ },
+ );
+ }}
+ >
+ Delete
+ </button>
+ <button
+ onClick={() => {
+ api.wallet.call(
+ WalletApiOperation.DeleteExchange,
+ {
+ exchangeBaseUrl: e.exchangeBaseUrl,
+ purge: true,
+ },
+ );
+ }}
+ >
+ Purge
+ </button>
+ {e.scopeInfo && e.masterPub && e.currency ?
+ (e.scopeInfo.type === ScopeType.Global ?
+ <button
+ onClick={() => {
+ api.wallet.call(
+ WalletApiOperation.RemoveGlobalCurrencyExchange,
+ {
+ exchangeBaseUrl: e.exchangeBaseUrl,
+ currency: e.currency!,
+ exchangeMasterPub: e.masterPub!,
+ },
+ );
+ }}
+ >
+
+ Make regional
+ </button>
+ : e.scopeInfo.type === ScopeType.Auditor ?
+ undefined
+
+ : e.scopeInfo.type === ScopeType.Exchange ?
+ <button
+ onClick={() => {
+ api.wallet.call(
+ WalletApiOperation.AddGlobalCurrencyExchange,
+ {
+ exchangeBaseUrl: e.exchangeBaseUrl,
+ currency: e.currency!,
+ exchangeMasterPub: e.masterPub!,
+ },
+ );
+ }}
+ >
+
+ Make global
+ </button>
+ : undefined) : undefined
+ }
+ <button
+ onClick={() => {
+ api.wallet.call(
+ WalletApiOperation.SetExchangeTosForgotten,
+ {
+ exchangeBaseUrl: e.exchangeBaseUrl,
+ },
+ );
+ }}
+ >
+ Forget ToS
+ </button>
+ </td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ </Fragment>
+ )}
+ <div style={{ display: "flex", justifyContent: "space-between" }}>
+ <div />
+ <LinkPrimary href={Pages.settingsExchangeAdd({})}>
+ <i18n.Translate>Add an exchange</i18n.Translate>
+ </LinkPrimary>
+ </div>
+
+
<Paper style={{ padding: 10, margin: 10 }}>
<h3>Logging</h3>
<div>