Add site connection permissions, approval flow, and active address
Some checks failed
check / check (push) Has been cancelled

- Add activeAddress, allowedSites, deniedSites, rememberSiteChoice to
  persisted state
- Replace auto-connect with permission checks: allowed sites connect
  automatically, denied sites are rejected, unknown sites trigger an
  approval popup
- Add approval popup UI with hostname display, active address preview,
  remember checkbox, and allow/deny buttons
- Add ACTIVE/[select] indicator on address rows in the main view to
  set the active web3 address
- Add allowed/denied site list management in settings with delete buttons
- Broadcast accountsChanged to connected dapps when active address changes
- Handle approval window close as implicit denial
This commit is contained in:
2026-02-26 03:40:34 +07:00
parent 9a6e544167
commit 56fa56bc8a
7 changed files with 389 additions and 62 deletions

View File

@@ -1,15 +1,52 @@
const { $, showView } = require("./helpers");
const { $, addressDotHtml } = require("./helpers");
const { state, saveState } = require("../../shared/state");
function init(ctx) {
$("btn-approve").addEventListener("click", () => {
ctx.renderWalletList();
showView("main");
});
const runtime =
typeof browser !== "undefined" ? browser.runtime : chrome.runtime;
$("btn-reject").addEventListener("click", () => {
ctx.renderWalletList();
showView("main");
let approvalId = null;
function show(id) {
approvalId = id;
runtime.sendMessage({ type: "AUTISTMASK_GET_APPROVAL", id }, (details) => {
if (!details) {
window.close();
return;
}
$("approve-hostname").textContent = details.hostname;
const dot = addressDotHtml(state.activeAddress);
$("approve-address").innerHTML = dot + state.activeAddress;
$("approve-remember").checked = state.rememberSiteChoice;
});
}
module.exports = { init };
function init(ctx) {
$("approve-remember").addEventListener("change", async () => {
state.rememberSiteChoice = $("approve-remember").checked;
await saveState();
});
$("btn-approve").addEventListener("click", () => {
const remember = $("approve-remember").checked;
runtime.sendMessage({
type: "AUTISTMASK_APPROVAL_RESPONSE",
id: approvalId,
approved: true,
remember,
});
window.close();
});
$("btn-reject").addEventListener("click", () => {
const remember = $("approve-remember").checked;
runtime.sendMessage({
type: "AUTISTMASK_APPROVAL_RESPONSE",
id: approvalId,
approved: false,
remember,
});
window.close();
});
}
module.exports = { init, show };

View File

@@ -41,7 +41,11 @@ function render(ctx) {
wallet.addresses.forEach((addr, ai) => {
html += `<div class="address-row py-1 border-b border-border-light cursor-pointer hover:bg-hover" data-wallet="${wi}" data-address="${ai}">`;
html += `<div class="text-xs font-bold">Address ${wi + 1}.${ai + 1}</div>`;
const isActive = state.activeAddress === addr.address;
const activeHtml = isActive
? `<span class="font-bold text-xs">ACTIVE</span>`
: `<span class="btn-select-active text-xs underline decoration-dashed cursor-pointer" data-addr="${addr.address}">select</span>`;
html += `<div class="text-xs font-bold flex justify-between items-center"><span>Address ${wi + 1}.${ai + 1}</span>${activeHtml}</div>`;
const dot = addressDotHtml(addr.address);
if (addr.ensName) {
html += `<div class="text-xs font-bold flex items-center">${dot}${addr.ensName}</div>`;
@@ -67,6 +71,20 @@ function render(ctx) {
});
});
container.querySelectorAll(".btn-select-active").forEach((btn) => {
btn.addEventListener("click", async (e) => {
e.stopPropagation();
state.activeAddress = btn.dataset.addr;
await saveState();
render(ctx);
const runtime =
typeof browser !== "undefined"
? browser.runtime
: chrome.runtime;
runtime.sendMessage({ type: "AUTISTMASK_ACTIVE_CHANGED" });
});
});
container.querySelectorAll(".btn-add-address").forEach((btn) => {
btn.addEventListener("click", async (e) => {
e.stopPropagation();

View File

@@ -3,6 +3,44 @@ const { state, saveState } = require("../../shared/state");
const { ETHEREUM_MAINNET_CHAIN_ID } = require("../../shared/constants");
const { log } = require("../../shared/log");
const runtime =
typeof browser !== "undefined" ? browser.runtime : chrome.runtime;
function renderSiteList(containerId, list, stateKey) {
const container = $(containerId);
if (list.length === 0) {
container.innerHTML = '<p class="text-xs text-muted">None</p>';
return;
}
let html = "";
list.forEach((hostname, i) => {
html += `<div class="flex justify-between items-center text-xs py-1 border-b border-border-light">`;
html += `<span>${hostname}</span>`;
html += `<button class="btn-remove-site border border-border px-1 hover:bg-fg hover:text-bg cursor-pointer" data-key="${stateKey}" data-index="${i}">[x]</button>`;
html += `</div>`;
});
container.innerHTML = html;
container.querySelectorAll(".btn-remove-site").forEach((btn) => {
btn.addEventListener("click", async () => {
const key = btn.dataset.key;
const idx = parseInt(btn.dataset.index, 10);
state[key].splice(idx, 1);
await saveState();
runtime.sendMessage({ type: "AUTISTMASK_REMOVE_SITE" });
renderSiteList(containerId, state[key], key);
});
});
}
function renderSiteLists() {
renderSiteList(
"settings-allowed-sites",
state.allowedSites,
"allowedSites",
);
renderSiteList("settings-denied-sites", state.deniedSites, "deniedSites");
}
function init(ctx) {
$("btn-save-rpc").addEventListener("click", async () => {
const url = $("settings-rpc").value.trim();
@@ -77,4 +115,4 @@ function init(ctx) {
});
}
module.exports = { init };
module.exports = { init, renderSiteLists };