diff options
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx')
-rw-r--r-- | packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx | 362 |
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> |