diff --git a/src/popup/index.js b/src/popup/index.js
index c2e9069..df17fab 100644
--- a/src/popup/index.js
+++ b/src/popup/index.js
@@ -16,6 +16,7 @@ const addressToken = require("./views/addressToken");
const send = require("./views/send");
const confirmTx = require("./views/confirmTx");
const txStatus = require("./views/txStatus");
+const transactionDetail = require("./views/transactionDetail");
const receive = require("./views/receive");
const addToken = require("./views/addToken");
const settings = require("./views/settings");
@@ -53,6 +54,7 @@ const ctx = {
showAddTokenView: () => addToken.show(),
showConfirmTx: (txInfo) => confirmTx.show(txInfo),
showReceive: () => receive.show(),
+ showTransactionDetail: (tx) => transactionDetail.show(tx),
};
// Views that can be fully re-rendered from persisted state.
@@ -63,26 +65,37 @@ const RESTORABLE_VIEWS = new Set([
"address-token",
"receive",
"settings",
+ "transaction",
+ "success-tx",
+ "error-tx",
]);
+function needsAddress(view) {
+ return (
+ view === "address" ||
+ view === "address-token" ||
+ view === "receive" ||
+ view === "transaction"
+ );
+}
+
+function hasValidAddress() {
+ return (
+ state.selectedWallet !== null &&
+ state.selectedAddress !== null &&
+ state.wallets[state.selectedWallet] &&
+ state.wallets[state.selectedWallet].addresses[state.selectedAddress]
+ );
+}
+
function restoreView() {
const view = state.currentView;
if (!view || !RESTORABLE_VIEWS.has(view)) {
return fallbackView();
}
- // Validate that selectedWallet/selectedAddress still point to valid data
- if (view === "address" || view === "address-token" || view === "receive") {
- if (
- state.selectedWallet === null ||
- state.selectedAddress === null ||
- !state.wallets[state.selectedWallet] ||
- !state.wallets[state.selectedWallet].addresses[
- state.selectedAddress
- ]
- ) {
- return fallbackView();
- }
+ if (needsAddress(view) && !hasValidAddress()) {
+ return fallbackView();
}
if (view === "address-token" && !state.selectedToken) {
@@ -102,6 +115,27 @@ function restoreView() {
case "settings":
settings.show();
break;
+ case "transaction":
+ if (state.viewData && state.viewData.tx) {
+ transactionDetail.render();
+ } else {
+ fallbackView();
+ }
+ break;
+ case "success-tx":
+ if (state.viewData && state.viewData.hash) {
+ txStatus.renderSuccess();
+ } else {
+ fallbackView();
+ }
+ break;
+ case "error-tx":
+ if (state.viewData && state.viewData.message) {
+ txStatus.renderError();
+ } else {
+ fallbackView();
+ }
+ break;
default:
fallbackView();
break;
@@ -169,6 +203,7 @@ async function init() {
send.init(ctx);
confirmTx.init(ctx);
txStatus.init(ctx);
+ transactionDetail.init(ctx);
receive.init(ctx);
addToken.init(ctx);
settings.init(ctx);
diff --git a/src/popup/views/addressDetail.js b/src/popup/views/addressDetail.js
index b3de59f..e1c20cf 100644
--- a/src/popup/views/addressDetail.js
+++ b/src/popup/views/addressDetail.js
@@ -5,7 +5,6 @@ const {
balanceLinesForAddress,
addressDotHtml,
escapeHtml,
- formatAddressHtml,
truncateMiddle,
} = require("./helpers");
const { state, currentAddress, saveState } = require("../../shared/state");
@@ -32,10 +31,6 @@ function etherscanAddressLink(address) {
return `https://etherscan.io/address/${address}`;
}
-function etherscanTxLink(hash) {
- return `https://etherscan.io/tx/${hash}`;
-}
-
function show() {
state.selectedToken = null;
const wallet = state.wallets[state.selectedWallet];
@@ -211,77 +206,15 @@ function renderTransactions(txs) {
list.querySelectorAll(".tx-row").forEach((row) => {
row.addEventListener("click", () => {
const idx = parseInt(row.dataset.tx, 10);
- showTxDetail(loadedTxs[idx]);
+ const tx = loadedTxs[idx];
+ const counterparty = tx.direction === "sent" ? tx.to : tx.from;
+ tx.fromEns = ensNameMap.get(tx.from) || null;
+ tx.toEns = ensNameMap.get(tx.to) || null;
+ ctx.showTransactionDetail(tx);
});
});
}
-function copyableHtml(text, extraClass) {
- const cls =
- "underline decoration-dashed cursor-pointer" +
- (extraClass ? " " + extraClass : "");
- return `${escapeHtml(text)}`;
-}
-
-function blockieHtml(address) {
- const src = makeBlockie(address);
- return `
`;
-}
-
-function txDetailAddressHtml(address) {
- const ensName = ensNameMap.get(address) || null;
- const blockie = blockieHtml(address);
- const dot = addressDotHtml(address);
- const link = etherscanAddressLink(address);
- const extLink = `${EXT_ICON}`;
- let html = `
${blockie}
`;
- if (ensName) {
- html +=
- `${dot}` +
- copyableHtml(ensName, "") +
- extLink +
- `
` +
- `` +
- copyableHtml(address, "break-all") +
- `
`;
- } else {
- html +=
- `${dot}` +
- copyableHtml(address, "break-all") +
- extLink +
- `
`;
- }
- return html;
-}
-
-function txDetailHashHtml(hash) {
- const link = etherscanTxLink(hash);
- const extLink = `${EXT_ICON}`;
- return copyableHtml(hash, "break-all") + extLink;
-}
-
-function showTxDetail(tx) {
- $("tx-detail-hash").innerHTML = txDetailHashHtml(tx.hash);
- $("tx-detail-from").innerHTML = txDetailAddressHtml(tx.from);
- $("tx-detail-to").innerHTML = txDetailAddressHtml(tx.to);
- $("tx-detail-value").textContent = tx.value + " " + tx.symbol;
- $("tx-detail-time").textContent =
- isoDate(tx.timestamp) + " (" + timeAgo(tx.timestamp) + ")";
- $("tx-detail-status").textContent = tx.isError ? "Failed" : "Success";
- showView("transaction");
-
- // Attach copy handlers
- document
- .getElementById("view-transaction")
- .querySelectorAll("[data-copy]")
- .forEach((el) => {
- el.onclick = () => {
- navigator.clipboard.writeText(el.dataset.copy);
- showFlash("Copied!");
- };
- });
-}
-
function init(_ctx) {
ctx = _ctx;
$("address-full").addEventListener("click", () => {
@@ -319,14 +252,6 @@ function init(_ctx) {
});
$("btn-add-token").addEventListener("click", ctx.showAddTokenView);
-
- $("btn-tx-back").addEventListener("click", () => {
- if (state.selectedToken) {
- ctx.showAddressToken();
- } else {
- show();
- }
- });
}
module.exports = { init, show };
diff --git a/src/popup/views/addressToken.js b/src/popup/views/addressToken.js
index 855bb88..43e4161 100644
--- a/src/popup/views/addressToken.js
+++ b/src/popup/views/addressToken.js
@@ -25,6 +25,8 @@ const { updateSendBalance, renderSendTokenSelect } = require("./send");
const { log } = require("../../shared/log");
const makeBlockie = require("ethereum-blockies-base64");
+let ctx;
+
const EXT_ICON =
`` +
`