diff --git a/src/popup/index.js b/src/popup/index.js
index b28bc23..c2e9069 100644
--- a/src/popup/index.js
+++ b/src/popup/index.js
@@ -52,8 +52,67 @@ const ctx = {
showAddressToken: () => addressToken.show(),
showAddTokenView: () => addToken.show(),
showConfirmTx: (txInfo) => confirmTx.show(txInfo),
+ showReceive: () => receive.show(),
};
+// Views that can be fully re-rendered from persisted state.
+// All others fall back to the nearest restorable parent.
+const RESTORABLE_VIEWS = new Set([
+ "main",
+ "address",
+ "address-token",
+ "receive",
+ "settings",
+]);
+
+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 (view === "address-token" && !state.selectedToken) {
+ return fallbackView();
+ }
+
+ switch (view) {
+ case "address":
+ addressDetail.show();
+ break;
+ case "address-token":
+ addressToken.show();
+ break;
+ case "receive":
+ receive.show();
+ break;
+ case "settings":
+ settings.show();
+ break;
+ default:
+ fallbackView();
+ break;
+ }
+}
+
+function fallbackView() {
+ renderWalletList();
+ showView("main");
+}
+
async function init() {
if (DEBUG) {
const banner = document.createElement("div");
@@ -98,10 +157,7 @@ async function init() {
showView("main");
return;
}
- $("settings-rpc").value = state.rpcUrl;
- $("settings-blockscout").value = state.blockscoutUrl;
- settings.renderSiteLists();
- showView("settings");
+ settings.show();
});
welcome.init(ctx);
@@ -121,7 +177,7 @@ async function init() {
showView("welcome");
} else {
renderWalletList();
- showView("main");
+ restoreView();
doRefreshAndRender();
setInterval(doRefreshAndRender, 10000);
}
diff --git a/src/popup/views/addressDetail.js b/src/popup/views/addressDetail.js
index dfffec8..b3de59f 100644
--- a/src/popup/views/addressDetail.js
+++ b/src/popup/views/addressDetail.js
@@ -17,7 +17,6 @@ const {
const { resolveEnsNames } = require("../../shared/ens");
const { updateSendBalance, renderSendTokenSelect } = require("./send");
const { log } = require("../../shared/log");
-const QRCode = require("qrcode");
const makeBlockie = require("ethereum-blockies-base64");
let ctx;
@@ -316,23 +315,7 @@ function init(_ctx) {
});
$("btn-receive").addEventListener("click", () => {
- const addr = currentAddress();
- const address = addr ? addr.address : "";
- $("receive-dot").innerHTML = address ? addressDotHtml(address) : "";
- $("receive-address").textContent = address;
- const link = address ? etherscanAddressLink(address) : "";
- $("receive-etherscan-link").innerHTML = link
- ? `${EXT_ICON}`
- : "";
- if (address) {
- QRCode.toCanvas($("receive-qr"), address, {
- width: 200,
- margin: 2,
- color: { dark: "#000000", light: "#ffffff" },
- });
- }
- $("receive-erc20-warning").classList.add("hidden");
- showView("receive");
+ ctx.showReceive();
});
$("btn-add-token").addEventListener("click", ctx.showAddTokenView);
diff --git a/src/popup/views/addressToken.js b/src/popup/views/addressToken.js
index cba0ee3..855bb88 100644
--- a/src/popup/views/addressToken.js
+++ b/src/popup/views/addressToken.js
@@ -23,7 +23,6 @@ const {
const { resolveEnsNames } = require("../../shared/ens");
const { updateSendBalance, renderSendTokenSelect } = require("./send");
const { log } = require("../../shared/log");
-const QRCode = require("qrcode");
const makeBlockie = require("ethereum-blockies-base64");
const EXT_ICON =
@@ -366,32 +365,7 @@ function init(ctx) {
});
$("btn-address-token-receive").addEventListener("click", () => {
- const addr = currentAddress();
- const address = addr ? addr.address : "";
- $("receive-dot").innerHTML = address ? addressDotHtml(address) : "";
- $("receive-address").textContent = address;
- const addrLink = address ? etherscanAddressLink(address) : "";
- $("receive-etherscan-link").innerHTML = addrLink
- ? `${EXT_ICON}`
- : "";
- if (address) {
- QRCode.toCanvas($("receive-qr"), address, {
- width: 200,
- margin: 2,
- color: { dark: "#000000", light: "#ffffff" },
- });
- }
- const warningEl = $("receive-erc20-warning");
- if (state.selectedToken && state.selectedToken !== "ETH") {
- warningEl.textContent =
- "This is an ERC-20 token. Only send " +
- currentSymbol +
- " on the Ethereum network to this address. Sending tokens on other networks will result in permanent loss.";
- warningEl.classList.remove("hidden");
- } else {
- warningEl.classList.add("hidden");
- }
- showView("receive");
+ ctx.showReceive();
});
}
diff --git a/src/popup/views/helpers.js b/src/popup/views/helpers.js
index 0d37291..82f92d4 100644
--- a/src/popup/views/helpers.js
+++ b/src/popup/views/helpers.js
@@ -6,6 +6,7 @@ const {
getPrice,
getAddressValueUsd,
} = require("../../shared/prices");
+const { state, saveState } = require("../../shared/state");
const VIEWS = [
"welcome",
@@ -49,6 +50,8 @@ function showView(name) {
}
}
clearFlash();
+ state.currentView = name;
+ saveState();
if (DEBUG) {
const banner = document.getElementById("debug-banner");
if (banner) {
diff --git a/src/popup/views/home.js b/src/popup/views/home.js
index a8267eb..13aa6f6 100644
--- a/src/popup/views/home.js
+++ b/src/popup/views/home.js
@@ -9,7 +9,6 @@ const {
} = require("./helpers");
const { state, saveState, currentAddress } = require("../../shared/state");
const { updateSendBalance, renderSendTokenSelect } = require("./send");
-const QRCode = require("qrcode");
const { deriveAddressFromXpub } = require("../../shared/wallet");
const {
formatUsd,
@@ -415,23 +414,7 @@ function init(ctx) {
showFlash("No active address selected.");
return;
}
- const addr = currentAddress();
- const address = addr ? addr.address : "";
- $("receive-dot").innerHTML = address ? addressDotHtml(address) : "";
- $("receive-address").textContent = address;
- const link = address ? `https://etherscan.io/address/${address}` : "";
- $("receive-etherscan-link").innerHTML = link
- ? `${EXT_ICON}`
- : "";
- if (address) {
- QRCode.toCanvas($("receive-qr"), address, {
- width: 200,
- margin: 2,
- color: { dark: "#000000", light: "#ffffff" },
- });
- }
- $("receive-erc20-warning").classList.add("hidden");
- showView("receive");
+ ctx.showReceive();
});
}
diff --git a/src/popup/views/receive.js b/src/popup/views/receive.js
index 40d5b8b..c986fef 100644
--- a/src/popup/views/receive.js
+++ b/src/popup/views/receive.js
@@ -1,5 +1,53 @@
-const { $, showFlash } = require("./helpers");
-const { state } = require("../../shared/state");
+const { $, showView, showFlash, addressDotHtml } = require("./helpers");
+const { state, currentAddress } = require("../../shared/state");
+const QRCode = require("qrcode");
+
+const EXT_ICON =
+ `` +
+ ``;
+
+function show() {
+ const addr = currentAddress();
+ const address = addr ? addr.address : "";
+ $("receive-dot").innerHTML = address ? addressDotHtml(address) : "";
+ $("receive-address").textContent = address;
+ const link = address ? `https://etherscan.io/address/${address}` : "";
+ $("receive-etherscan-link").innerHTML = link
+ ? `${EXT_ICON}`
+ : "";
+ if (address) {
+ QRCode.toCanvas($("receive-qr"), address, {
+ width: 200,
+ margin: 2,
+ color: { dark: "#000000", light: "#ffffff" },
+ });
+ }
+ const warningEl = $("receive-erc20-warning");
+ if (state.selectedToken && state.selectedToken !== "ETH") {
+ // Look up symbol from address token balances
+ const addrObj = currentAddress();
+ let symbol = state.selectedToken;
+ if (addrObj) {
+ const tb = (addrObj.tokenBalances || []).find(
+ (t) =>
+ t.address.toLowerCase() ===
+ state.selectedToken.toLowerCase(),
+ );
+ if (tb) symbol = tb.symbol;
+ }
+ warningEl.textContent =
+ "This is an ERC-20 token. Only send " +
+ symbol +
+ " on the Ethereum network to this address. Sending tokens on other networks will result in permanent loss.";
+ warningEl.classList.remove("hidden");
+ } else {
+ warningEl.classList.add("hidden");
+ }
+ showView("receive");
+}
function init(ctx) {
$("btn-receive-copy").addEventListener("click", () => {
@@ -19,4 +67,4 @@ function init(ctx) {
});
}
-module.exports = { init };
+module.exports = { init, show };
diff --git a/src/popup/views/settings.js b/src/popup/views/settings.js
index 7c7fb27..01e0340 100644
--- a/src/popup/views/settings.js
+++ b/src/popup/views/settings.js
@@ -38,6 +38,13 @@ function renderSiteList(containerId, siteMap, stateKey) {
});
}
+function show() {
+ $("settings-rpc").value = state.rpcUrl;
+ $("settings-blockscout").value = state.blockscoutUrl;
+ renderSiteLists();
+ showView("settings");
+}
+
function renderSiteLists() {
renderSiteList(
"settings-allowed-sites",
@@ -154,4 +161,4 @@ function init(ctx) {
});
}
-module.exports = { init, renderSiteLists };
+module.exports = { init, show, renderSiteLists };
diff --git a/src/shared/state.js b/src/shared/state.js
index 1f47b00..86d2728 100644
--- a/src/shared/state.js
+++ b/src/shared/state.js
@@ -29,6 +29,7 @@ const DEFAULT_STATE = {
const state = {
...DEFAULT_STATE,
+ currentView: null,
selectedWallet: null,
selectedAddress: null,
selectedToken: null,
@@ -53,6 +54,10 @@ async function saveState() {
dustThresholdGwei: state.dustThresholdGwei,
fraudContracts: state.fraudContracts,
tokenHolderCache: state.tokenHolderCache,
+ currentView: state.currentView,
+ selectedWallet: state.selectedWallet,
+ selectedAddress: state.selectedAddress,
+ selectedToken: state.selectedToken,
};
await storageApi.set({ autistmask: persisted });
}
@@ -103,6 +108,12 @@ async function loadState() {
: 100000;
state.fraudContracts = saved.fraudContracts || [];
state.tokenHolderCache = saved.tokenHolderCache || {};
+ state.currentView = saved.currentView || null;
+ state.selectedWallet =
+ saved.selectedWallet !== undefined ? saved.selectedWallet : null;
+ state.selectedAddress =
+ saved.selectedAddress !== undefined ? saved.selectedAddress : null;
+ state.selectedToken = saved.selectedToken || null;
}
}