Centralize view state into app ctx with viewData persistence
All checks were successful
check / check (push) Successful in 17s

Creates a centralized transactionDetail.js view module, replacing
the duplicated showTxDetail/copyableHtml/blockieHtml/txDetailAddressHtml
code that was in both addressDetail.js and addressToken.js (~120 lines
removed). Transaction data is stored in state.viewData and persisted,
so the transaction detail view survives popup close/reopen.

Adds viewData to persisted state. Each view that needs data for
restore stores it in state.viewData before rendering. The ctx object
now has showTransactionDetail() alongside all other show methods.

Restorable views expanded to include: transaction (via viewData.tx),
success-tx (via viewData.hash/blockNumber), error-tx (via
viewData.message). txStatus.js split into show (sets data) + render
(reads data) for each screen, enabling restore.

Non-restorable views (send, confirm-tx, wait-tx, add-wallet,
import-key, add-token) fall back to the nearest parent since they
involve active form state or network polling.
This commit is contained in:
2026-02-27 12:16:33 +07:00
parent 034253077c
commit 2467dfd09c
6 changed files with 254 additions and 178 deletions

View File

@@ -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);