web extension WIP
This commit is contained in:
parent
599c8380f2
commit
cbe325cb0f
@ -26,6 +26,7 @@ import { createWithdrawPage } from "./pages/withdraw";
|
||||
import { createWelcomePage } from "./pages/welcome";
|
||||
import { createPayPage } from "./pages/pay";
|
||||
import { createRefundPage } from "./pages/refund";
|
||||
import { setupI18n } from "taler-wallet-core";
|
||||
|
||||
function main(): void {
|
||||
try {
|
||||
@ -65,6 +66,8 @@ function main(): void {
|
||||
}
|
||||
}
|
||||
|
||||
setupI18n("en-US");
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", main);
|
||||
} else {
|
||||
|
@ -397,19 +397,19 @@ function actionForTalerUri(talerUri: string): string | undefined {
|
||||
const uriType = classifyTalerUri(talerUri);
|
||||
switch (uriType) {
|
||||
case TalerUriType.TalerWithdraw:
|
||||
return makeExtensionUrlWithParams("withdraw.html", {
|
||||
return makeExtensionUrlWithParams("static/withdraw.html", {
|
||||
talerWithdrawUri: talerUri,
|
||||
});
|
||||
case TalerUriType.TalerPay:
|
||||
return makeExtensionUrlWithParams("pay.html", {
|
||||
return makeExtensionUrlWithParams("static/pay.html", {
|
||||
talerPayUri: talerUri,
|
||||
});
|
||||
case TalerUriType.TalerTip:
|
||||
return makeExtensionUrlWithParams("tip.html", {
|
||||
return makeExtensionUrlWithParams("static/tip.html", {
|
||||
talerTipUri: talerUri,
|
||||
});
|
||||
case TalerUriType.TalerRefund:
|
||||
return makeExtensionUrlWithParams("refund.html", {
|
||||
return makeExtensionUrlWithParams("static/refund.html", {
|
||||
talerRefundUri: talerUri,
|
||||
});
|
||||
case TalerUriType.TalerNotifyReserve:
|
||||
|
@ -29,11 +29,13 @@ import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
acceptWithdrawal,
|
||||
onUpdateNotification,
|
||||
getWithdrawalDetailsForUri,
|
||||
} from "../wxApi";
|
||||
import { WithdrawUriInfoResponse } from "taler-wallet-core";
|
||||
|
||||
function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element {
|
||||
const [details, setDetails] = useState<
|
||||
any | undefined
|
||||
WithdrawUriInfoResponse | undefined
|
||||
>();
|
||||
const [selectedExchange, setSelectedExchange] = useState<
|
||||
string | undefined
|
||||
@ -54,55 +56,12 @@ function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element {
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async (): Promise<void> => {
|
||||
// FIXME: re-implement with new API
|
||||
// console.log("getting from", talerWithdrawUri);
|
||||
// let d: WithdrawalDetailsResponse | undefined = undefined;
|
||||
// try {
|
||||
// d = await getWithdrawDetails(talerWithdrawUri, selectedExchange);
|
||||
// } catch (e) {
|
||||
// console.error(
|
||||
// `error getting withdraw details for uri ${talerWithdrawUri}, exchange ${selectedExchange}`,
|
||||
// e,
|
||||
// );
|
||||
// setErrMsg(e.message);
|
||||
// return;
|
||||
// }
|
||||
// console.log("got withdrawDetails", d);
|
||||
// if (!selectedExchange && d.bankWithdrawDetails.suggestedExchange) {
|
||||
// console.log("setting selected exchange");
|
||||
// setSelectedExchange(d.bankWithdrawDetails.suggestedExchange);
|
||||
// }
|
||||
// setDetails(d);
|
||||
const res = await getWithdrawalDetailsForUri({talerWithdrawUri: props.talerWithdrawUri});
|
||||
setDetails(res);
|
||||
};
|
||||
fetchData();
|
||||
}, [selectedExchange, errMsg, selecting, talerWithdrawUri, updateCounter]);
|
||||
|
||||
if (errMsg) {
|
||||
return (
|
||||
<div>
|
||||
<i18n.Translate wrap="p">
|
||||
Could not get details for withdraw operation:
|
||||
</i18n.Translate>
|
||||
<p style={{ color: "red" }}>{errMsg}</p>
|
||||
<p>
|
||||
<span
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
style={{ textDecoration: "underline", cursor: "pointer" }}
|
||||
onClick={() => {
|
||||
setSelecting(true);
|
||||
setErrMsg(undefined);
|
||||
setSelectedExchange(undefined);
|
||||
setDetails(undefined);
|
||||
}}
|
||||
>
|
||||
{i18n.str`Chose different exchange provider`}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!details) {
|
||||
return <span>Loading...</span>;
|
||||
}
|
||||
@ -111,51 +70,6 @@ function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element {
|
||||
return <span>Withdraw operation has been cancelled.</span>;
|
||||
}
|
||||
|
||||
if (selecting) {
|
||||
const bankSuggestion =
|
||||
details && details.bankWithdrawDetails.suggestedExchange;
|
||||
return (
|
||||
<div>
|
||||
{i18n.str`Please select an exchange. You can review the details before after your selection.`}
|
||||
{bankSuggestion && (
|
||||
<div>
|
||||
<h2>Bank Suggestion</h2>
|
||||
<button
|
||||
className="pure-button button-success"
|
||||
onClick={() => {
|
||||
setDetails(undefined);
|
||||
setSelectedExchange(bankSuggestion);
|
||||
setSelecting(false);
|
||||
}}
|
||||
>
|
||||
<i18n.Translate wrap="span">
|
||||
Select <strong>{bankSuggestion}</strong>
|
||||
</i18n.Translate>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<h2>Custom Selection</h2>
|
||||
<p>
|
||||
<input
|
||||
type="text"
|
||||
onChange={(e) => setCustomUrl(e.target.value)}
|
||||
value={customUrl}
|
||||
/>
|
||||
</p>
|
||||
<button
|
||||
className="pure-button button-success"
|
||||
onClick={() => {
|
||||
setDetails(undefined);
|
||||
setSelectedExchange(customUrl);
|
||||
setSelecting(false);
|
||||
}}
|
||||
>
|
||||
<i18n.Translate wrap="span">Select custom exchange</i18n.Translate>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const accept = async (): Promise<void> => {
|
||||
if (!selectedExchange) {
|
||||
throw Error("can't accept, no exchange selected");
|
||||
@ -173,7 +87,7 @@ function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element {
|
||||
<h1>Digital Cash Withdrawal</h1>
|
||||
<i18n.Translate wrap="p">
|
||||
You are about to withdraw{" "}
|
||||
<strong>{renderAmount(details.bankWithdrawDetails.amount)}</strong> from
|
||||
<strong>{renderAmount(details.amount)}</strong> from
|
||||
your bank account into your wallet.
|
||||
</i18n.Translate>
|
||||
{selectedExchange ? (
|
||||
@ -211,9 +125,9 @@ function WithdrawalDialog(props: { talerWithdrawUri: string }): JSX.Element {
|
||||
</span>
|
||||
</p>
|
||||
|
||||
{details.exchangeWithdrawDetails ? (
|
||||
{/* {details.exchangeWithdrawDetails ? (
|
||||
<WithdrawDetailView rci={details.exchangeWithdrawDetails} />
|
||||
) : null}
|
||||
) : null} */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -21,14 +21,26 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { AmountJson, ConfirmPayResult, BalancesResponse, PurchaseDetails, TipStatus, BenchmarkResult, PreparePayResult, AcceptWithdrawalResponse, WalletDiagnostics } from "taler-wallet-core";
|
||||
|
||||
import {
|
||||
AmountJson,
|
||||
ConfirmPayResult,
|
||||
BalancesResponse,
|
||||
PurchaseDetails,
|
||||
TipStatus,
|
||||
BenchmarkResult,
|
||||
PreparePayResult,
|
||||
AcceptWithdrawalResponse,
|
||||
WalletDiagnostics,
|
||||
CoreApiResponse,
|
||||
OperationFailedError,
|
||||
GetWithdrawalDetailsForUriRequest,
|
||||
WithdrawUriInfoResponse,
|
||||
} from "taler-wallet-core";
|
||||
|
||||
export interface ExtendedPermissionsResponse {
|
||||
newValue: boolean;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Response with information about available version upgrades.
|
||||
*/
|
||||
@ -50,23 +62,9 @@ export interface UpgradeResponse {
|
||||
oldDbVersion: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error thrown when the function from the backend (via RPC) threw an error.
|
||||
*/
|
||||
export class WalletApiError extends Error {
|
||||
constructor(message: string, public detail: any) {
|
||||
super(message);
|
||||
// restore prototype chain
|
||||
Object.setPrototypeOf(this, new.target.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
async function callBackend(
|
||||
type: string,
|
||||
detail: any,
|
||||
): Promise<any> {
|
||||
async function callBackend(operation: string, payload: any): Promise<any> {
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
chrome.runtime.sendMessage({ type, detail }, (resp) => {
|
||||
chrome.runtime.sendMessage({ operation, payload, id: "(none)" }, (resp) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
console.log("Error calling backend");
|
||||
reject(
|
||||
@ -75,19 +73,17 @@ async function callBackend(
|
||||
),
|
||||
);
|
||||
}
|
||||
if (typeof resp === "object" && resp && resp.error) {
|
||||
console.warn("response error:", resp);
|
||||
const e = new WalletApiError(resp.error.message, resp.error);
|
||||
reject(e);
|
||||
} else {
|
||||
resolve(resp);
|
||||
console.log("got response", resp);
|
||||
const r = resp as CoreApiResponse;
|
||||
if (r.type === "error") {
|
||||
reject(new OperationFailedError(r.error));
|
||||
return;
|
||||
}
|
||||
resolve(r.result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Start refreshing a coin.
|
||||
*/
|
||||
@ -123,7 +119,7 @@ export function resetDb(): Promise<void> {
|
||||
* Get balances for all currencies/exchanges.
|
||||
*/
|
||||
export function getBalance(): Promise<BalancesResponse> {
|
||||
return callBackend("balances", {});
|
||||
return callBackend("getBalances", {});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -227,6 +223,15 @@ export function getExtendedPermissions(): Promise<ExtendedPermissionsResponse> {
|
||||
return callBackend("get-extended-permissions", {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get diagnostics information
|
||||
*/
|
||||
export function getWithdrawalDetailsForUri(
|
||||
req: GetWithdrawalDetailsForUriRequest,
|
||||
): Promise<WithdrawUriInfoResponse> {
|
||||
return callBackend("getWithdrawalDetailsForUri", req);
|
||||
}
|
||||
|
||||
export function onUpdateNotification(f: () => void): () => void {
|
||||
const port = chrome.runtime.connect({ name: "notifications" });
|
||||
const listener = (): void => {
|
||||
|
@ -28,7 +28,21 @@ import * as wxApi from "./wxApi";
|
||||
import MessageSender = chrome.runtime.MessageSender;
|
||||
import { extendedPermissions } from "./permissions";
|
||||
|
||||
import { Wallet, OpenedPromise, openPromise, deleteTalerDatabase, WALLET_DB_MINOR_VERSION, WalletDiagnostics, openTalerDatabase, Database, classifyTalerUri, TalerUriType } from "taler-wallet-core";
|
||||
import {
|
||||
Wallet,
|
||||
OpenedPromise,
|
||||
openPromise,
|
||||
deleteTalerDatabase,
|
||||
WALLET_DB_MINOR_VERSION,
|
||||
WalletDiagnostics,
|
||||
openTalerDatabase,
|
||||
Database,
|
||||
classifyTalerUri,
|
||||
TalerUriType,
|
||||
makeErrorDetails,
|
||||
TalerErrorCode,
|
||||
handleCoreApiRequest,
|
||||
} from "taler-wallet-core";
|
||||
import { BrowserHttpLib } from "./browserHttpLib";
|
||||
import { BrowserCryptoWorkerFactory } from "./browserCryptoWorkerFactory";
|
||||
|
||||
@ -51,255 +65,29 @@ const walletInit: OpenedPromise<void> = openPromise<void>();
|
||||
|
||||
const notificationPorts: chrome.runtime.Port[] = [];
|
||||
|
||||
async function handleMessage(
|
||||
sender: MessageSender,
|
||||
type: string,
|
||||
detail: any,
|
||||
): Promise<any> {
|
||||
function needsWallet(): Wallet {
|
||||
if (!currentWallet) {
|
||||
throw NeedsWallet;
|
||||
}
|
||||
return currentWallet;
|
||||
}
|
||||
switch (type) {
|
||||
case "balances": {
|
||||
return needsWallet().getBalances();
|
||||
}
|
||||
case "dump-db": {
|
||||
const db = needsWallet().db;
|
||||
return db.exportDatabase();
|
||||
}
|
||||
case "import-db": {
|
||||
const db = needsWallet().db;
|
||||
return db.importDatabase(detail.dump);
|
||||
}
|
||||
case "ping": {
|
||||
return Promise.resolve();
|
||||
}
|
||||
case "reset-db": {
|
||||
deleteTalerDatabase(indexedDB);
|
||||
setBadgeText({ text: "" });
|
||||
console.log("reset done");
|
||||
if (!currentWallet) {
|
||||
reinitWallet();
|
||||
}
|
||||
return Promise.resolve({});
|
||||
}
|
||||
case "confirm-pay": {
|
||||
if (typeof detail.proposalId !== "string") {
|
||||
throw Error("proposalId must be string");
|
||||
}
|
||||
return needsWallet().confirmPay(detail.proposalId, detail.sessionId);
|
||||
}
|
||||
case "exchange-info": {
|
||||
if (!detail.baseUrl) {
|
||||
return Promise.resolve({ error: "bad url" });
|
||||
}
|
||||
return needsWallet().updateExchangeFromUrl(detail.baseUrl);
|
||||
}
|
||||
case "get-exchanges": {
|
||||
return needsWallet().getExchangeRecords();
|
||||
}
|
||||
case "get-currencies": {
|
||||
return needsWallet().getCurrencies();
|
||||
}
|
||||
case "update-currency": {
|
||||
return needsWallet().updateCurrency(detail.currencyRecord);
|
||||
}
|
||||
case "get-reserves": {
|
||||
if (typeof detail.exchangeBaseUrl !== "string") {
|
||||
return Promise.reject(Error("exchangeBaseUrl missing"));
|
||||
}
|
||||
return needsWallet().getReserves(detail.exchangeBaseUrl);
|
||||
}
|
||||
case "get-coins": {
|
||||
if (typeof detail.exchangeBaseUrl !== "string") {
|
||||
return Promise.reject(Error("exchangBaseUrl missing"));
|
||||
}
|
||||
return needsWallet().getCoinsForExchange(detail.exchangeBaseUrl);
|
||||
}
|
||||
case "get-denoms": {
|
||||
if (typeof detail.exchangeBaseUrl !== "string") {
|
||||
return Promise.reject(Error("exchangBaseUrl missing"));
|
||||
}
|
||||
return needsWallet().getDenoms(detail.exchangeBaseUrl);
|
||||
}
|
||||
case "refresh-coin": {
|
||||
if (typeof detail.coinPub !== "string") {
|
||||
return Promise.reject(Error("coinPub missing"));
|
||||
}
|
||||
return needsWallet().refresh(detail.coinPub);
|
||||
}
|
||||
case "get-sender-wire-infos": {
|
||||
return needsWallet().getSenderWireInfos();
|
||||
}
|
||||
case "return-coins": {
|
||||
const d = {
|
||||
amount: detail.amount,
|
||||
exchange: detail.exchange,
|
||||
senderWire: detail.senderWire,
|
||||
};
|
||||
return needsWallet().returnCoins(d);
|
||||
}
|
||||
case "check-upgrade": {
|
||||
let dbResetRequired = false;
|
||||
if (!currentWallet) {
|
||||
dbResetRequired = true;
|
||||
}
|
||||
const resp: wxApi.UpgradeResponse = {
|
||||
currentDbVersion: WALLET_DB_MINOR_VERSION.toString(),
|
||||
dbResetRequired,
|
||||
oldDbVersion: (outdatedDbVersion || "unknown").toString(),
|
||||
};
|
||||
return resp;
|
||||
}
|
||||
case "get-purchase-details": {
|
||||
const proposalId = detail.proposalId;
|
||||
if (!proposalId) {
|
||||
throw Error("proposalId missing");
|
||||
}
|
||||
if (typeof proposalId !== "string") {
|
||||
throw Error("proposalId must be a string");
|
||||
}
|
||||
return needsWallet().getPurchaseDetails(proposalId);
|
||||
}
|
||||
case "accept-refund":
|
||||
return needsWallet().applyRefund(detail.refundUrl);
|
||||
case "get-tip-status": {
|
||||
return needsWallet().getTipStatus(detail.talerTipUri);
|
||||
}
|
||||
case "accept-tip": {
|
||||
return needsWallet().acceptTip(detail.talerTipUri);
|
||||
}
|
||||
case "abort-failed-payment": {
|
||||
if (!detail.contractTermsHash) {
|
||||
throw Error("contracTermsHash not given");
|
||||
}
|
||||
return needsWallet().abortFailedPayment(detail.contractTermsHash);
|
||||
}
|
||||
case "benchmark-crypto": {
|
||||
if (!detail.repetitions) {
|
||||
throw Error("repetitions not given");
|
||||
}
|
||||
return needsWallet().benchmarkCrypto(detail.repetitions);
|
||||
}
|
||||
case "accept-withdrawal": {
|
||||
return needsWallet().acceptWithdrawal(
|
||||
detail.talerWithdrawUri,
|
||||
detail.selectedExchange,
|
||||
);
|
||||
}
|
||||
case "get-diagnostics": {
|
||||
const manifestData = chrome.runtime.getManifest();
|
||||
const errors: string[] = [];
|
||||
let firefoxIdbProblem = false;
|
||||
let dbOutdated = false;
|
||||
try {
|
||||
await walletInit.promise;
|
||||
} catch (e) {
|
||||
errors.push("Error during wallet initialization: " + e);
|
||||
if (
|
||||
currentDatabase === undefined &&
|
||||
outdatedDbVersion === undefined &&
|
||||
isFirefox()
|
||||
) {
|
||||
firefoxIdbProblem = true;
|
||||
}
|
||||
}
|
||||
if (!currentWallet) {
|
||||
errors.push("Could not create wallet backend.");
|
||||
}
|
||||
if (!currentDatabase) {
|
||||
errors.push("Could not open database");
|
||||
}
|
||||
if (outdatedDbVersion !== undefined) {
|
||||
errors.push(`Outdated DB version: ${outdatedDbVersion}`);
|
||||
dbOutdated = true;
|
||||
}
|
||||
const diagnostics: WalletDiagnostics = {
|
||||
walletManifestDisplayVersion:
|
||||
manifestData.version_name || "(undefined)",
|
||||
walletManifestVersion: manifestData.version,
|
||||
errors,
|
||||
firefoxIdbProblem,
|
||||
dbOutdated,
|
||||
};
|
||||
return diagnostics;
|
||||
}
|
||||
case "prepare-pay":
|
||||
return needsWallet().preparePayForUri(detail.talerPayUri);
|
||||
case "set-extended-permissions": {
|
||||
const newVal = detail.value;
|
||||
console.log("new extended permissions value", newVal);
|
||||
if (newVal) {
|
||||
setupHeaderListener();
|
||||
return { newValue: true };
|
||||
} else {
|
||||
await new Promise((resolve, reject) => {
|
||||
getPermissionsApi().remove(extendedPermissions, (rem) => {
|
||||
console.log("permissions removed:", rem);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
return { newVal: false };
|
||||
}
|
||||
}
|
||||
case "get-extended-permissions": {
|
||||
const res = await new Promise((resolve, reject) => {
|
||||
getPermissionsApi().contains(extendedPermissions, (result: boolean) => {
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
return { newValue: res };
|
||||
}
|
||||
default:
|
||||
console.error(`Request type ${type} unknown`);
|
||||
console.error(`Request detail was ${detail}`);
|
||||
return {
|
||||
error: {
|
||||
message: `request type ${type} unknown`,
|
||||
requestType: type,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function dispatch(
|
||||
req: any,
|
||||
sender: any,
|
||||
sendResponse: any,
|
||||
): Promise<void> {
|
||||
try {
|
||||
const p = handleMessage(sender, req.type, req.detail);
|
||||
const r = await p;
|
||||
const w = currentWallet;
|
||||
if (!w) {
|
||||
sendResponse(
|
||||
makeErrorDetails(
|
||||
TalerErrorCode.WALLET_CORE_NOT_AVAILABLE,
|
||||
"wallet core not available",
|
||||
{},
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const r = await handleCoreApiRequest(w, req.operation, req.id, req.payload);
|
||||
try {
|
||||
sendResponse(r);
|
||||
} catch (e) {
|
||||
// might fail if tab disconnected
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`exception during wallet handler for '${req.type}'`);
|
||||
console.log("request", req);
|
||||
console.error(e);
|
||||
let stack;
|
||||
try {
|
||||
stack = e.stack.toString();
|
||||
} catch (e) {
|
||||
// might fail
|
||||
}
|
||||
try {
|
||||
sendResponse({
|
||||
error: {
|
||||
message: e.message,
|
||||
stack,
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
// might fail if tab disconnected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getTab(tabId: number): Promise<chrome.tabs.Tab> {
|
||||
@ -436,7 +224,7 @@ function headerListener(
|
||||
switch (uriType) {
|
||||
case TalerUriType.TalerWithdraw:
|
||||
return makeSyncWalletRedirect(
|
||||
"withdraw.html",
|
||||
"/static/withdraw.html",
|
||||
details.tabId,
|
||||
details.url,
|
||||
{
|
||||
@ -445,7 +233,7 @@ function headerListener(
|
||||
);
|
||||
case TalerUriType.TalerPay:
|
||||
return makeSyncWalletRedirect(
|
||||
"pay.html",
|
||||
"/static/pay.html",
|
||||
details.tabId,
|
||||
details.url,
|
||||
{
|
||||
@ -454,7 +242,7 @@ function headerListener(
|
||||
);
|
||||
case TalerUriType.TalerTip:
|
||||
return makeSyncWalletRedirect(
|
||||
"tip.html",
|
||||
"/static/tip.html",
|
||||
details.tabId,
|
||||
details.url,
|
||||
{
|
||||
@ -463,7 +251,7 @@ function headerListener(
|
||||
);
|
||||
case TalerUriType.TalerRefund:
|
||||
return makeSyncWalletRedirect(
|
||||
"refund.html",
|
||||
"/static/refund.html",
|
||||
details.tabId,
|
||||
details.url,
|
||||
{
|
||||
|
@ -3,9 +3,9 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Taler Wallet: Benchmarks</title>
|
||||
<link rel="stylesheet" type="text/css" href="/style/wallet.css" />
|
||||
<link rel="icon" href="/img/icon.png" />
|
||||
<script src="/pageEntryPoint.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/static/style/wallet.css" />
|
||||
<link rel="icon" href="/static/img/icon.png" />
|
||||
<script src="/dist/pageEntryPoint.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<section id="main">
|
||||
|
@ -4,10 +4,10 @@
|
||||
<meta charset="UTF-8" />
|
||||
<title>Taler Wallet: Confirm Contract</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="/style/pure.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/style/wallet.css" />
|
||||
<link rel="icon" href="/img/icon.png" />
|
||||
<script src="/pageEntryPoint.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/static/style/pure.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/style/wallet.css" />
|
||||
<link rel="icon" href="/static/img/icon.png" />
|
||||
<script src="/dist/pageEntryPoint.js"></script>
|
||||
|
||||
<style>
|
||||
button.accept {
|
||||
|
@ -2,11 +2,11 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="stylesheet" type="text/css" href="/style/pure.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/style/wallet.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/style/popup.css" />
|
||||
<link rel="icon" href="/img/icon.png" />
|
||||
<script src="/pageEntryPoint.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/static/style/pure.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/style/wallet.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/style/popup.css" />
|
||||
<link rel="icon" href="/static/img/icon.png" />
|
||||
<script src="/dist/pageEntryPoint.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -3,10 +3,10 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Taler Wallet: Withdraw</title>
|
||||
<link rel="icon" href="/img/icon.png" />
|
||||
<link rel="stylesheet" type="text/css" href="/style/pure.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/style/wallet.css" />
|
||||
<script src="/pageEntryPoint.js"></script>
|
||||
<link rel="icon" href="/static/img/icon.png" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/style/pure.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/style/wallet.css" />
|
||||
<script src="/dist/pageEntryPoint.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
Loading…
Reference in New Issue
Block a user