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; } }