Add site connection permissions, approval flow, and active address
Some checks failed
check / check (push) Has been cancelled
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:
@@ -520,6 +520,22 @@
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="bg-well p-3 mx-1 mb-3">
|
||||
<h3 class="font-bold mb-1">Allowed Sites</h3>
|
||||
<p class="text-xs text-muted mb-2">
|
||||
Sites that can connect to your wallet without asking.
|
||||
</p>
|
||||
<div id="settings-allowed-sites"></div>
|
||||
</div>
|
||||
|
||||
<div class="bg-well p-3 mx-1 mb-3">
|
||||
<h3 class="font-bold mb-1">Denied Sites</h3>
|
||||
<p class="text-xs text-muted mb-2">
|
||||
Sites that are blocked from connecting to your wallet.
|
||||
</p>
|
||||
<div id="settings-denied-sites"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ============ TRANSACTION DETAIL ============ -->
|
||||
@@ -559,18 +575,28 @@
|
||||
|
||||
<!-- ============ APPROVAL ============ -->
|
||||
<div id="view-approve" class="view hidden">
|
||||
<h2 class="font-bold mb-2">A website is requesting access</h2>
|
||||
<div class="mb-2">
|
||||
<h2 class="font-bold mb-2">Connection Request</h2>
|
||||
<div class="mb-3">
|
||||
<p class="mb-2">
|
||||
<span id="approve-hostname" class="font-bold"></span>
|
||||
wants to connect to your wallet.
|
||||
</p>
|
||||
<div class="text-xs text-muted mb-1">
|
||||
From:
|
||||
<span id="approve-origin" class="font-bold"></span>
|
||||
Address that will be shared:
|
||||
</div>
|
||||
<div class="font-bold mb-1" id="approve-type"></div>
|
||||
<div
|
||||
id="approve-address"
|
||||
class="text-xs flex items-center mb-2"
|
||||
></div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label
|
||||
class="text-xs flex items-center gap-1 cursor-pointer"
|
||||
>
|
||||
<input type="checkbox" id="approve-remember" checked />
|
||||
Remember my choice for this site
|
||||
</label>
|
||||
</div>
|
||||
<pre
|
||||
id="approve-details"
|
||||
class="border border-border p-2 text-xs overflow-auto mb-3 max-h-64"
|
||||
></pre>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
id="btn-approve"
|
||||
|
||||
@@ -63,9 +63,29 @@ async function init() {
|
||||
|
||||
await loadState();
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
// Check for approval mode
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const approvalId = params.get("approval");
|
||||
if (approvalId) {
|
||||
approval.show(parseInt(approvalId, 10));
|
||||
showView("approve");
|
||||
return;
|
||||
}
|
||||
|
||||
$("btn-settings").addEventListener("click", () => {
|
||||
$("settings-rpc").value = state.rpcUrl;
|
||||
$("settings-blockscout").value = state.blockscoutUrl;
|
||||
settings.renderSiteLists();
|
||||
showView("settings");
|
||||
});
|
||||
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 };
|
||||
|
||||
Reference in New Issue
Block a user