Unify create/import into single Add Wallet view
All checks were successful
check / check (push) Successful in 13s
All checks were successful
check / check (push) Successful in 13s
Merge "Create new wallet" and "Import recovery phrase" into one "Add wallet" screen. The recovery phrase textarea starts empty. A clickable die button generates a random phrase and shows a backup warning. Users who already have a phrase just paste it. Welcome screen simplified to two options: "Add wallet" and "Import private key". README updated to match.
This commit is contained in:
parent
8431488849
commit
1a49665210
46
README.md
46
README.md
@ -145,31 +145,31 @@ The popup has the following views, switched via simple show/hide:
|
||||
|
||||
1. **Lock**: Password input + Unlock button. Shown when the wallet is locked or
|
||||
on first open after browser restart.
|
||||
2. **Welcome**: Shown on first use. Three options: "Create a new wallet", "I
|
||||
have a recovery phrase", "I have a private key". Password is set during this
|
||||
first flow.
|
||||
3. **Create**: Displays a generated 12-word recovery phrase with instructions to
|
||||
write it down. User sets a password and confirms.
|
||||
4. **Import recovery phrase**: Paste a 12 or 24 word recovery phrase. Password
|
||||
fields shown only on first use.
|
||||
5. **Import private key**: Paste a private key. Password fields shown only on
|
||||
first use.
|
||||
6. **Main**: All wallets listed, each showing its addresses with truncated
|
||||
address and ETH balance. "+" next to HD wallets to add another address. "+
|
||||
Add wallet" at the bottom. Settings and Lock buttons in the header. Future: a
|
||||
sub-heading showing total portfolio value in USD (and eventually other
|
||||
currencies).
|
||||
7. **Add wallet**: Choose wallet type (new, recovery phrase, private key) — same
|
||||
three options as Welcome but without password setup.
|
||||
8. **Address detail**: Full address (click to copy), ETH balance, USD value
|
||||
2. **Welcome**: Shown on first use. Two options: "Add wallet" (recovery phrase
|
||||
based) and "Import private key". Password is set during the first wallet
|
||||
addition.
|
||||
3. **Add wallet**: A unified view for both creating and importing recovery
|
||||
phrase wallets. The recovery phrase text area starts empty. A clickable die
|
||||
button `[die]` generates a random 12-word phrase and fills it in. If the user
|
||||
already has a phrase, they paste it directly. When the die is clicked, a
|
||||
warning box appears reminding the user to write the phrase down. Password
|
||||
fields are shown only on first use.
|
||||
4. **Import private key**: Paste a private key. This creates a wallet with a
|
||||
single address. Password fields shown only on first use.
|
||||
5. **Main**: All wallets listed, each showing its addresses with truncated
|
||||
address and ETH balance. "+" next to recovery phrase wallets to add another
|
||||
address. "+ Add wallet" and "+ Import private key" buttons at the bottom.
|
||||
Settings and Lock buttons in the header. Future: a sub-heading showing total
|
||||
portfolio value in USD (and eventually other currencies).
|
||||
6. **Address detail**: Full address (click to copy), ETH balance, USD value
|
||||
(future), Send/Receive buttons, token list with "+ Add" button.
|
||||
9. **Send**: Token selector, recipient address, amount. Cancel returns to
|
||||
7. **Send**: Token selector, recipient address, amount. Cancel returns to
|
||||
address detail.
|
||||
10. **Receive**: Full address displayed with "Copy address" button.
|
||||
11. **Add token**: Enter contract address. The extension looks up the token
|
||||
name/symbol automatically.
|
||||
12. **Settings**: Network (RPC endpoint URL) with explanatory text.
|
||||
13. **Approval**: When a website requests wallet access or a signature, shows
|
||||
8. **Receive**: Full address displayed with "Copy address" button.
|
||||
9. **Add token**: Enter contract address. The extension looks up the token
|
||||
name/symbol automatically.
|
||||
10. **Settings**: Network (RPC endpoint URL) with explanatory text.
|
||||
11. **Approval**: When a website requests wallet access or a signature, shows
|
||||
the site origin, request details, and Allow/Deny buttons.
|
||||
|
||||
### External Services
|
||||
|
||||
@ -41,48 +41,59 @@
|
||||
<h1 class="font-bold border-b border-border pb-1 mb-3">
|
||||
Welcome to AutistMask
|
||||
</h1>
|
||||
<p class="mb-3">
|
||||
To get started, add a wallet. You can create a brand new one
|
||||
or bring in one you already have.
|
||||
</p>
|
||||
<p class="mb-3">To get started, add a wallet.</p>
|
||||
<div class="flex flex-col gap-2">
|
||||
<button
|
||||
id="btn-welcome-new"
|
||||
id="btn-welcome-add"
|
||||
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
||||
>
|
||||
Create a new wallet
|
||||
</button>
|
||||
<button
|
||||
id="btn-welcome-recovery"
|
||||
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
||||
>
|
||||
I have a recovery phrase
|
||||
Add wallet
|
||||
</button>
|
||||
<button
|
||||
id="btn-welcome-key"
|
||||
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
||||
>
|
||||
I have a private key
|
||||
Import private key
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ============ CREATE NEW WALLET ============ -->
|
||||
<div id="view-create" class="view hidden">
|
||||
<!-- ============ ADD WALLET (unified create/import) ============ -->
|
||||
<div id="view-add-wallet" class="view hidden">
|
||||
<h1 class="font-bold border-b border-border pb-1 mb-3">
|
||||
Create New Wallet
|
||||
Add Wallet
|
||||
</h1>
|
||||
<p class="mb-2">
|
||||
These 12 words are your recovery phrase. Write them down on
|
||||
Enter your 12 or 24 word recovery phrase below, or press the
|
||||
die to generate a new one.
|
||||
</p>
|
||||
<div class="mb-1 flex justify-end">
|
||||
<button
|
||||
id="btn-generate-phrase"
|
||||
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer text-xs"
|
||||
title="Generate a random recovery phrase"
|
||||
>
|
||||
[⚀]
|
||||
</button>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<textarea
|
||||
id="wallet-mnemonic"
|
||||
rows="3"
|
||||
class="border border-border p-1 w-full font-mono text-sm bg-bg text-fg resize-y"
|
||||
placeholder="word word word ..."
|
||||
></textarea>
|
||||
</div>
|
||||
<div
|
||||
id="add-wallet-phrase-warning"
|
||||
class="text-xs mb-2 border border-border border-dashed p-2 hidden"
|
||||
>
|
||||
These words are your recovery phrase. Write them down on
|
||||
paper and keep them somewhere safe. Anyone with these words
|
||||
can access your funds. If you lose them, your wallet cannot
|
||||
be recovered.
|
||||
</p>
|
||||
<div
|
||||
id="create-mnemonic"
|
||||
class="border border-border p-2 mb-3 break-all select-all leading-relaxed"
|
||||
></div>
|
||||
<div class="mb-2">
|
||||
</div>
|
||||
<div class="mb-2" id="add-wallet-password-section">
|
||||
<label class="block mb-1">Choose a password</label>
|
||||
<p class="text-xs text-muted mb-1">
|
||||
This password locks the wallet on this device. It is not
|
||||
@ -90,90 +101,34 @@
|
||||
</p>
|
||||
<input
|
||||
type="password"
|
||||
id="create-password"
|
||||
id="add-wallet-password"
|
||||
class="border border-border p-1 w-full font-mono text-sm bg-bg text-fg"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<div class="mb-2" id="add-wallet-password-confirm-section">
|
||||
<label class="block mb-1">Confirm password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="create-password-confirm"
|
||||
id="add-wallet-password-confirm"
|
||||
class="border border-border p-1 w-full font-mono text-sm bg-bg text-fg"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
id="btn-create-confirm"
|
||||
id="btn-add-wallet-confirm"
|
||||
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
||||
>
|
||||
I wrote it down — continue
|
||||
Add
|
||||
</button>
|
||||
<button
|
||||
id="btn-create-back"
|
||||
id="btn-add-wallet-back"
|
||||
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
id="create-error"
|
||||
class="mt-2 border border-border border-dashed p-1 hidden"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<!-- ============ IMPORT RECOVERY PHRASE ============ -->
|
||||
<div id="view-import-phrase" class="view hidden">
|
||||
<h1 class="font-bold border-b border-border pb-1 mb-3">
|
||||
Import Recovery Phrase
|
||||
</h1>
|
||||
<p class="mb-2">
|
||||
Enter the 12 or 24 word recovery phrase from your existing
|
||||
wallet. Separate each word with a space.
|
||||
</p>
|
||||
<div class="mb-2">
|
||||
<textarea
|
||||
id="import-mnemonic"
|
||||
rows="3"
|
||||
class="border border-border p-1 w-full font-mono text-sm bg-bg text-fg resize-y"
|
||||
placeholder="word word word ..."
|
||||
></textarea>
|
||||
</div>
|
||||
<div class="mb-2" id="import-phrase-password-section">
|
||||
<label class="block mb-1">Choose a password</label>
|
||||
<p class="text-xs text-muted mb-1">
|
||||
This password locks the wallet on this device.
|
||||
</p>
|
||||
<input
|
||||
type="password"
|
||||
id="import-phrase-password"
|
||||
class="border border-border p-1 w-full font-mono text-sm bg-bg text-fg"
|
||||
/>
|
||||
</div>
|
||||
<div class="mb-2" id="import-phrase-password-confirm-section">
|
||||
<label class="block mb-1">Confirm password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="import-phrase-password-confirm"
|
||||
class="border border-border p-1 w-full font-mono text-sm bg-bg text-fg"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
id="btn-import-phrase-confirm"
|
||||
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
||||
>
|
||||
Import
|
||||
</button>
|
||||
<button
|
||||
id="btn-import-phrase-back"
|
||||
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
||||
>
|
||||
Back
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
id="import-phrase-error"
|
||||
id="add-wallet-error"
|
||||
class="mt-2 border border-border border-dashed p-1 hidden"
|
||||
></div>
|
||||
</div>
|
||||
@ -262,58 +217,18 @@
|
||||
<div id="wallet-list"></div>
|
||||
|
||||
<!-- add wallet button -->
|
||||
<div class="mt-3 border-t border-border pt-2">
|
||||
<div class="mt-3 border-t border-border pt-2 flex gap-2">
|
||||
<button
|
||||
id="btn-add-wallet"
|
||||
id="btn-main-add-wallet"
|
||||
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
||||
>
|
||||
+ Add wallet
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ============ ADD WALLET (from main view) ============ -->
|
||||
<div id="view-add-wallet" class="view hidden">
|
||||
<h1 class="font-bold border-b border-border pb-1 mb-3">
|
||||
Add Wallet
|
||||
</h1>
|
||||
<p class="mb-3">What kind of wallet do you want to add?</p>
|
||||
<div class="flex flex-col gap-2">
|
||||
<button
|
||||
id="btn-add-wallet-new"
|
||||
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer text-left"
|
||||
>
|
||||
Create a new wallet
|
||||
<span class="block text-xs text-muted"
|
||||
>Generates a new recovery phrase with one
|
||||
address</span
|
||||
>
|
||||
</button>
|
||||
<button
|
||||
id="btn-add-wallet-phrase"
|
||||
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer text-left"
|
||||
>
|
||||
Import recovery phrase
|
||||
<span class="block text-xs text-muted"
|
||||
>Use an existing 12 or 24 word recovery phrase</span
|
||||
>
|
||||
</button>
|
||||
<button
|
||||
id="btn-add-wallet-key"
|
||||
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer text-left"
|
||||
>
|
||||
Import private key
|
||||
<span class="block text-xs text-muted"
|
||||
>A single address from a private key</span
|
||||
>
|
||||
</button>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<button
|
||||
id="btn-add-wallet-back"
|
||||
id="btn-main-import-key"
|
||||
class="border border-border px-2 py-1 hover:bg-fg hover:text-bg cursor-pointer"
|
||||
>
|
||||
Back
|
||||
+ Import private key
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4,11 +4,9 @@
|
||||
const VIEWS = [
|
||||
"lock",
|
||||
"welcome",
|
||||
"create",
|
||||
"import-phrase",
|
||||
"add-wallet",
|
||||
"import-key",
|
||||
"main",
|
||||
"add-wallet",
|
||||
"address",
|
||||
"send",
|
||||
"receive",
|
||||
@ -72,6 +70,25 @@ function makeStubAddress() {
|
||||
};
|
||||
}
|
||||
|
||||
function generateStubMnemonic() {
|
||||
// TODO: replace with real BIP-39 generation via background
|
||||
const words = [
|
||||
"abandon",
|
||||
"ability",
|
||||
"able",
|
||||
"about",
|
||||
"above",
|
||||
"absent",
|
||||
"absorb",
|
||||
"abstract",
|
||||
"absurd",
|
||||
"abuse",
|
||||
"access",
|
||||
"accident",
|
||||
];
|
||||
return words.join(" ");
|
||||
}
|
||||
|
||||
// -- render wallet list on main view --
|
||||
function renderWalletList() {
|
||||
const container = $("wallet-list");
|
||||
@ -102,7 +119,6 @@ function renderWalletList() {
|
||||
});
|
||||
container.innerHTML = html;
|
||||
|
||||
// bind clicks on address rows
|
||||
container.querySelectorAll(".address-row").forEach((row) => {
|
||||
row.addEventListener("click", () => {
|
||||
state.selectedWallet = parseInt(row.dataset.wallet, 10);
|
||||
@ -111,11 +127,11 @@ function renderWalletList() {
|
||||
});
|
||||
});
|
||||
|
||||
// bind clicks on + buttons within HD wallets
|
||||
container.querySelectorAll(".btn-add-address").forEach((btn) => {
|
||||
btn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
const wi = parseInt(btn.dataset.wallet, 10);
|
||||
// TODO: derive next address from seed via background
|
||||
state.wallets[wi].addresses.push(makeStubAddress());
|
||||
renderWalletList();
|
||||
});
|
||||
@ -179,39 +195,29 @@ function addWalletAndGoToMain(wallet) {
|
||||
showView("main");
|
||||
}
|
||||
|
||||
function showImportView(type) {
|
||||
if (type === "phrase") {
|
||||
$("import-mnemonic").value = "";
|
||||
hideError("import-phrase-error");
|
||||
const needsPw = state.isFirstSetup;
|
||||
$("import-phrase-password-section").classList.toggle(
|
||||
"hidden",
|
||||
!needsPw,
|
||||
);
|
||||
$("import-phrase-password-confirm-section").classList.toggle(
|
||||
"hidden",
|
||||
!needsPw,
|
||||
);
|
||||
showView("import-phrase");
|
||||
} else {
|
||||
$("import-private-key").value = "";
|
||||
hideError("import-key-error");
|
||||
const needsPw = state.isFirstSetup;
|
||||
$("import-key-password-section").classList.toggle("hidden", !needsPw);
|
||||
$("import-key-password-confirm-section").classList.toggle(
|
||||
"hidden",
|
||||
!needsPw,
|
||||
);
|
||||
showView("import-key");
|
||||
}
|
||||
function showAddWalletView() {
|
||||
$("wallet-mnemonic").value = "";
|
||||
$("add-wallet-phrase-warning").classList.add("hidden");
|
||||
hideError("add-wallet-error");
|
||||
const needsPw = state.isFirstSetup;
|
||||
$("add-wallet-password-section").classList.toggle("hidden", !needsPw);
|
||||
$("add-wallet-password-confirm-section").classList.toggle(
|
||||
"hidden",
|
||||
!needsPw,
|
||||
);
|
||||
showView("add-wallet");
|
||||
}
|
||||
|
||||
function showCreateView() {
|
||||
// TODO: generate real mnemonic via background
|
||||
$("create-mnemonic").textContent =
|
||||
"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
|
||||
hideError("create-error");
|
||||
showView("create");
|
||||
function showImportKeyView() {
|
||||
$("import-private-key").value = "";
|
||||
hideError("import-key-error");
|
||||
const needsPw = state.isFirstSetup;
|
||||
$("import-key-password-section").classList.toggle("hidden", !needsPw);
|
||||
$("import-key-password-confirm-section").classList.toggle(
|
||||
"hidden",
|
||||
!needsPw,
|
||||
);
|
||||
showView("import-key");
|
||||
}
|
||||
|
||||
function validatePasswords(pwId, pw2Id, errorId) {
|
||||
@ -234,6 +240,15 @@ function validatePasswords(pwId, pw2Id, errorId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function backFromWalletAdd() {
|
||||
if (state.isFirstSetup) {
|
||||
showView("welcome");
|
||||
} else {
|
||||
renderWalletList();
|
||||
showView("main");
|
||||
}
|
||||
}
|
||||
|
||||
// -- init --
|
||||
function init() {
|
||||
if (!state.hasWallet) {
|
||||
@ -260,51 +275,29 @@ function init() {
|
||||
});
|
||||
|
||||
// -- Welcome --
|
||||
$("btn-welcome-new").addEventListener("click", showCreateView);
|
||||
$("btn-welcome-recovery").addEventListener("click", () =>
|
||||
showImportView("phrase"),
|
||||
);
|
||||
$("btn-welcome-key").addEventListener("click", () => showImportView("key"));
|
||||
$("btn-welcome-add").addEventListener("click", showAddWalletView);
|
||||
$("btn-welcome-key").addEventListener("click", showImportKeyView);
|
||||
|
||||
// -- Create wallet --
|
||||
$("btn-create-confirm").addEventListener("click", () => {
|
||||
if (
|
||||
!validatePasswords(
|
||||
"create-password",
|
||||
"create-password-confirm",
|
||||
"create-error",
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
hideError("create-error");
|
||||
const walletNum = state.wallets.length + 1;
|
||||
addWalletAndGoToMain({
|
||||
type: "hd",
|
||||
name: "Wallet " + walletNum,
|
||||
mnemonic: $("create-mnemonic").textContent,
|
||||
addresses: [makeStubAddress()],
|
||||
});
|
||||
// -- Add wallet (unified create/import) --
|
||||
$("btn-generate-phrase").addEventListener("click", () => {
|
||||
const phrase = generateStubMnemonic();
|
||||
$("wallet-mnemonic").value = phrase;
|
||||
$("add-wallet-phrase-warning").classList.remove("hidden");
|
||||
});
|
||||
|
||||
$("btn-create-back").addEventListener("click", () => {
|
||||
showView(state.isFirstSetup ? "welcome" : "add-wallet");
|
||||
});
|
||||
|
||||
// -- Import recovery phrase --
|
||||
$("btn-import-phrase-confirm").addEventListener("click", () => {
|
||||
const mnemonic = $("import-mnemonic").value.trim();
|
||||
$("btn-add-wallet-confirm").addEventListener("click", () => {
|
||||
const mnemonic = $("wallet-mnemonic").value.trim();
|
||||
if (!mnemonic) {
|
||||
showError(
|
||||
"import-phrase-error",
|
||||
"Please enter your recovery phrase.",
|
||||
"add-wallet-error",
|
||||
"Please enter a recovery phrase or press the die to generate one.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
const words = mnemonic.split(/\s+/);
|
||||
if (words.length !== 12 && words.length !== 24) {
|
||||
showError(
|
||||
"import-phrase-error",
|
||||
"add-wallet-error",
|
||||
"Recovery phrase must be 12 or 24 words. You entered " +
|
||||
words.length +
|
||||
".",
|
||||
@ -313,14 +306,14 @@ function init() {
|
||||
}
|
||||
if (
|
||||
!validatePasswords(
|
||||
"import-phrase-password",
|
||||
"import-phrase-password-confirm",
|
||||
"import-phrase-error",
|
||||
"add-wallet-password",
|
||||
"add-wallet-password-confirm",
|
||||
"add-wallet-error",
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
hideError("import-phrase-error");
|
||||
hideError("add-wallet-error");
|
||||
const walletNum = state.wallets.length + 1;
|
||||
addWalletAndGoToMain({
|
||||
type: "hd",
|
||||
@ -330,9 +323,7 @@ function init() {
|
||||
});
|
||||
});
|
||||
|
||||
$("btn-import-phrase-back").addEventListener("click", () => {
|
||||
showView(state.isFirstSetup ? "welcome" : "add-wallet");
|
||||
});
|
||||
$("btn-add-wallet-back").addEventListener("click", backFromWalletAdd);
|
||||
|
||||
// -- Import private key --
|
||||
$("btn-import-key-confirm").addEventListener("click", () => {
|
||||
@ -360,9 +351,7 @@ function init() {
|
||||
});
|
||||
});
|
||||
|
||||
$("btn-import-key-back").addEventListener("click", () => {
|
||||
showView(state.isFirstSetup ? "welcome" : "add-wallet");
|
||||
});
|
||||
$("btn-import-key-back").addEventListener("click", backFromWalletAdd);
|
||||
|
||||
// -- Main view --
|
||||
$("btn-lock").addEventListener("click", () => {
|
||||
@ -376,21 +365,8 @@ function init() {
|
||||
showView("settings");
|
||||
});
|
||||
|
||||
$("btn-add-wallet").addEventListener("click", () => {
|
||||
showView("add-wallet");
|
||||
});
|
||||
|
||||
// -- Add wallet menu (from main view) --
|
||||
$("btn-add-wallet-new").addEventListener("click", showCreateView);
|
||||
$("btn-add-wallet-phrase").addEventListener("click", () =>
|
||||
showImportView("phrase"),
|
||||
);
|
||||
$("btn-add-wallet-key").addEventListener("click", () =>
|
||||
showImportView("key"),
|
||||
);
|
||||
$("btn-add-wallet-back").addEventListener("click", () => {
|
||||
showView("main");
|
||||
});
|
||||
$("btn-main-add-wallet").addEventListener("click", showAddWalletView);
|
||||
$("btn-main-import-key").addEventListener("click", showImportKeyView);
|
||||
|
||||
// -- Address detail --
|
||||
$("address-full").addEventListener("click", () => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user