All checks were successful
check / check (push) Successful in 36s
Address rows on Home gain an [x] control, on wallets that derive addresses from an extended key and hold more than one, opening a DeleteAddress confirmation screen. Removal cannot destroy anything: the key material stays. Derivation indices are not renumbered, so the next "+" derives the next unused index rather than resurrecting the removed one. The confirmation states the real route back -- delete the whole wallet in Settings, which asks for the password and destroys the stored recovery phrase, then import it again -- and notes that the scan which follows only finds addresses with on-chain activity. The copy varies by wallet type, since an xprv wallet has no recovery phrase. Removing an address that holds a balance is allowed, with a warning naming no figure; the funds are at the address on-chain and stay there either way. Selection and active address move only when the removed address was the one selected, and site permissions are dropped for it alone. The state transition shares its address comparison, permission cleanup and active-changed broadcast with the wallet-level removal.
277 lines
7.3 KiB
JavaScript
277 lines
7.3 KiB
JavaScript
// AutistMask popup entry point.
|
|
// Loads state, initializes views, triggers first render.
|
|
|
|
const { state, saveState, loadState } = require("../shared/state");
|
|
const { setRuntimeDebug } = require("../shared/log");
|
|
const { refreshPrices } = require("../shared/prices");
|
|
const { refreshBalances } = require("../shared/balances");
|
|
const {
|
|
$,
|
|
showView,
|
|
updateDebugBanner,
|
|
setRenderMain,
|
|
pushCurrentView,
|
|
goBack,
|
|
clearViewStack,
|
|
} = require("./views/helpers");
|
|
const { applyTheme } = require("./theme");
|
|
// Views that can be fully re-rendered from persisted state. All others fall
|
|
// back to the nearest restorable parent; see the module for why the
|
|
// secret-bearing views are absent.
|
|
const { RESTORABLE_VIEWS } = require("./restorableViews");
|
|
|
|
const home = require("./views/home");
|
|
const welcome = require("./views/welcome");
|
|
const addWallet = require("./views/addWallet");
|
|
const addressDetail = require("./views/addressDetail");
|
|
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");
|
|
const settingsAddToken = require("./views/settingsAddToken");
|
|
const deleteAddress = require("./views/deleteAddress");
|
|
const approval = require("./views/approval");
|
|
|
|
function renderWalletList() {
|
|
home.render(ctx);
|
|
}
|
|
|
|
let refreshInFlight = false;
|
|
|
|
async function doRefreshAndRender() {
|
|
if (refreshInFlight) return;
|
|
refreshInFlight = true;
|
|
try {
|
|
await Promise.all([
|
|
refreshPrices(),
|
|
refreshBalances(
|
|
state.wallets,
|
|
state.rpcUrl,
|
|
state.blockscoutUrl,
|
|
state.trackedTokens,
|
|
),
|
|
]);
|
|
state.lastBalanceRefresh = Date.now();
|
|
await saveState();
|
|
renderWalletList();
|
|
} finally {
|
|
refreshInFlight = false;
|
|
}
|
|
}
|
|
|
|
const ctx = {
|
|
renderWalletList,
|
|
doRefreshAndRender,
|
|
showAddWalletView: () => {
|
|
pushCurrentView();
|
|
addWallet.show();
|
|
},
|
|
showAddressDetail: () => {
|
|
pushCurrentView();
|
|
addressDetail.show();
|
|
},
|
|
showAddressToken: () => {
|
|
pushCurrentView();
|
|
addressToken.show();
|
|
},
|
|
showAddTokenView: () => {
|
|
pushCurrentView();
|
|
addToken.show();
|
|
},
|
|
showConfirmTx: (txInfo) => {
|
|
pushCurrentView();
|
|
confirmTx.show(txInfo);
|
|
},
|
|
showReceive: () => {
|
|
pushCurrentView();
|
|
receive.show();
|
|
},
|
|
showTransactionDetail: (tx) => {
|
|
pushCurrentView();
|
|
transactionDetail.show(tx);
|
|
},
|
|
showSettingsView: () => {
|
|
pushCurrentView();
|
|
settings.show();
|
|
},
|
|
showSettingsAddTokenView: () => {
|
|
pushCurrentView();
|
|
settingsAddToken.show();
|
|
},
|
|
showDeleteAddress: (walletIdx, addrIdx) => {
|
|
pushCurrentView();
|
|
deleteAddress.show(walletIdx, addrIdx);
|
|
},
|
|
};
|
|
|
|
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();
|
|
}
|
|
|
|
if (needsAddress(view) && !hasValidAddress()) {
|
|
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;
|
|
case "settings-addtoken":
|
|
settingsAddToken.show();
|
|
break;
|
|
case "confirm-tx":
|
|
if (state.viewData && state.viewData.pendingTx) {
|
|
confirmTx.restore();
|
|
} else {
|
|
fallbackView();
|
|
}
|
|
break;
|
|
case "transaction":
|
|
if (state.viewData && state.viewData.tx) {
|
|
transactionDetail.render();
|
|
} else {
|
|
fallbackView();
|
|
}
|
|
break;
|
|
case "wait-tx":
|
|
// Resumes the receipt poll from the persisted broadcast time.
|
|
if (!txStatus.restoreWait()) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
function fallbackView() {
|
|
renderWalletList();
|
|
showView("main");
|
|
}
|
|
|
|
async function init() {
|
|
await loadState();
|
|
applyTheme(state.theme);
|
|
|
|
// Sync runtime debug flag from persisted state before first render
|
|
setRuntimeDebug(state.debugMode);
|
|
|
|
// Create the debug/testnet banner if needed (uses runtime debug state)
|
|
updateDebugBanner();
|
|
|
|
// Auto-default active address
|
|
if (
|
|
state.activeAddress === null &&
|
|
state.wallets.length > 0 &&
|
|
state.wallets[0].addresses.length > 0
|
|
) {
|
|
state.activeAddress = state.wallets[0].addresses[0].address;
|
|
await saveState();
|
|
}
|
|
|
|
// Always init approval and txStatus — they may run in the approval popup window
|
|
approval.init(ctx);
|
|
txStatus.init(ctx);
|
|
|
|
// Check for approval mode
|
|
const params = new URLSearchParams(window.location.search);
|
|
const approvalId = params.get("approval");
|
|
if (approvalId) {
|
|
approval.show(approvalId);
|
|
showView("approve-site");
|
|
return;
|
|
}
|
|
|
|
$("btn-settings").addEventListener("click", () => {
|
|
if (
|
|
!document
|
|
.getElementById("view-settings")
|
|
.classList.contains("hidden")
|
|
) {
|
|
goBack();
|
|
return;
|
|
}
|
|
pushCurrentView();
|
|
settings.show();
|
|
});
|
|
|
|
setRenderMain(renderWalletList);
|
|
|
|
welcome.init(ctx);
|
|
addWallet.init(ctx);
|
|
home.init(ctx);
|
|
addressDetail.init(ctx);
|
|
addressToken.init(ctx);
|
|
send.init(ctx);
|
|
confirmTx.init(ctx);
|
|
transactionDetail.init(ctx);
|
|
receive.init(ctx);
|
|
addToken.init(ctx);
|
|
settings.init(ctx);
|
|
settingsAddToken.init(ctx);
|
|
deleteAddress.init(ctx);
|
|
|
|
if (!state.hasWallet) {
|
|
showView("welcome");
|
|
} else {
|
|
renderWalletList();
|
|
restoreView();
|
|
doRefreshAndRender();
|
|
setInterval(doRefreshAndRender, 10000);
|
|
}
|
|
}
|
|
|
|
document.addEventListener("DOMContentLoaded", init);
|