fix: a second click on Add while the lookup waits tracks the token once (closes #727)
The "already tracked" check ran only before the contract lookup, so a second click while the first lookup waited tracked the token twice. AddToken and SettingsAddToken now check again when the lookup answers. An answer for a token tracked meanwhile shows the "already tracked" flash message and tracks nothing. Model: opus-5-5
This commit is contained in:
@@ -17,6 +17,11 @@ const { log } = require("../../shared/log");
|
||||
// AddToken was last opened changes neither the screen nor the status line.
|
||||
let addTokenScreenOpenings = 0;
|
||||
|
||||
function findTracked(address) {
|
||||
const lower = address.toLowerCase();
|
||||
return state.trackedTokens.find((t) => t.address.toLowerCase() === lower);
|
||||
}
|
||||
|
||||
function show() {
|
||||
addTokenScreenOpenings++;
|
||||
$("add-token-address").value = "";
|
||||
@@ -50,9 +55,7 @@ function init(ctx) {
|
||||
showFlash("Enter a valid contract address starting with 0x.");
|
||||
return;
|
||||
}
|
||||
const already = state.trackedTokens.find(
|
||||
(t) => t.address.toLowerCase() === contractAddr.toLowerCase(),
|
||||
);
|
||||
const already = findTracked(contractAddr);
|
||||
if (already) {
|
||||
showFlash(already.symbol + " is already being tracked.");
|
||||
return;
|
||||
@@ -85,6 +88,18 @@ function init(ctx) {
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Another click on Add may have tracked this token while this
|
||||
// lookup waited.
|
||||
const trackedMeanwhile = findTracked(contractAddr);
|
||||
if (trackedMeanwhile) {
|
||||
showFlash(
|
||||
trackedMeanwhile.symbol + " is already being tracked.",
|
||||
);
|
||||
if (addTokenScreenOpenings !== opening) return;
|
||||
infoEl.textContent = "";
|
||||
infoEl.style.visibility = "hidden";
|
||||
return;
|
||||
}
|
||||
// The balance list never shows a token that the spoof rule
|
||||
// refuses, so such a token is not tracked.
|
||||
if (isSpoofedSymbol(info.symbol, contractAddr, chainId)) {
|
||||
|
||||
@@ -163,6 +163,14 @@ function init(_ctx) {
|
||||
);
|
||||
return;
|
||||
}
|
||||
// Another click on Add, or a "Top tokens:" button, may have
|
||||
// tracked this token while this lookup waited.
|
||||
if (isTracked(addr)) {
|
||||
showFlash("Already tracked.");
|
||||
infoEl.textContent = "";
|
||||
infoEl.style.visibility = "hidden";
|
||||
return;
|
||||
}
|
||||
// The balance list never shows a token that the spoof rule
|
||||
// refuses, so such a token is not tracked. The rule never refuses
|
||||
// a bundled token, so the "Top tokens:" buttons and the top 100
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
// A contract lookup on AddToken can answer after the user has left AddToken.
|
||||
// The token is still added, and the screen the user is on does not change
|
||||
// (https://git.eeqj.de/sneak/AutistMask/issues/603). An answer while AddToken
|
||||
// still shows goes to AddressDetail. Driven through the real popup over a
|
||||
// stored profile.
|
||||
// still shows goes to AddressDetail. On either add-token screen, an answer for a
|
||||
// token tracked while the lookup waited tracks nothing
|
||||
// (https://git.eeqj.de/sneak/AutistMask/issues/727). Driven through the real
|
||||
// popup over a stored profile.
|
||||
|
||||
const {
|
||||
bootPopup,
|
||||
@@ -43,9 +45,9 @@ async function openAddToken() {
|
||||
return boot;
|
||||
}
|
||||
|
||||
// Presses Add with a contract lookup that answers only when the function
|
||||
// returned here is called. That function waits for Add to finish.
|
||||
function addWithPendingLookup(boot) {
|
||||
// Presses `button` with a contract lookup that answers only when the function
|
||||
// returned here is called. That function waits for the add to finish.
|
||||
function addWithPendingLookup(boot, button = "btn-add-token-confirm") {
|
||||
const { lookupTokenInfo } = require("../src/shared/balances");
|
||||
let answer;
|
||||
lookupTokenInfo.mockImplementationOnce(
|
||||
@@ -54,7 +56,7 @@ function addWithPendingLookup(boot) {
|
||||
answer = resolve;
|
||||
}),
|
||||
);
|
||||
const adding = boot.click("btn-add-token-confirm");
|
||||
const adding = boot.click(button);
|
||||
return async () => {
|
||||
answer({
|
||||
symbol: TOKEN.symbol,
|
||||
@@ -136,3 +138,78 @@ test("an answer while AddToken still shows goes to AddressDetail with the token
|
||||
expect(boot.node("address-balances").innerHTML).toContain("TST");
|
||||
expect(boot.storage.read("autistmask").trackedTokens).toEqual([TOKEN]);
|
||||
});
|
||||
|
||||
test("an answer for a token tracked after AddToken was opened again leaves the status line as it is", async () => {
|
||||
const boot = await openAddToken();
|
||||
const answerFirstLookup = addWithPendingLookup(boot);
|
||||
await boot.click("btn-add-token-back");
|
||||
await boot.click("btn-add-token");
|
||||
boot.node("add-token-address").value = OTHER_TOKEN_ADDRESS;
|
||||
// The second lookup is never answered.
|
||||
addWithPendingLookup(boot);
|
||||
require("../src/popup/views/helpers").trackToken(TOKEN);
|
||||
|
||||
await answerFirstLookup();
|
||||
expect(boot.text("flash-msg")).toBe("TST is already being tracked.");
|
||||
expect(boot.text("add-token-info")).toBe("Looking up token...");
|
||||
});
|
||||
|
||||
// Each screen is opened from AddressDetail by the buttons named, in order.
|
||||
describe.each([
|
||||
[
|
||||
"AddToken",
|
||||
["btn-add-token"],
|
||||
"add-token-address",
|
||||
"btn-add-token-confirm",
|
||||
"add-token-info",
|
||||
"TST is already being tracked.",
|
||||
],
|
||||
[
|
||||
"SettingsAddToken",
|
||||
["btn-settings", "btn-settings-add-token"],
|
||||
"settings-addtoken-address",
|
||||
"btn-settings-addtoken-manual",
|
||||
"settings-addtoken-info",
|
||||
"Already tracked.",
|
||||
],
|
||||
])("%s", (_, opening, field, button, statusLine, alreadyTrackedFlash) => {
|
||||
// The screen open with TOKEN's contract address typed in.
|
||||
async function openScreen() {
|
||||
const boot = await bootPopup(
|
||||
unversionedValidProfile({
|
||||
currentView: "address",
|
||||
selectedWallet: 0,
|
||||
selectedAddress: 0,
|
||||
}),
|
||||
);
|
||||
expect(boot.pageErrors).toEqual([]);
|
||||
for (const id of opening) await boot.click(id);
|
||||
boot.node(field).value = TOKEN.address;
|
||||
return boot;
|
||||
}
|
||||
|
||||
test("a second click on Add while the lookup waits tracks the token once", async () => {
|
||||
const boot = await openScreen();
|
||||
const answerFirstClick = addWithPendingLookup(boot, button);
|
||||
const answerSecondClick = addWithPendingLookup(boot, button);
|
||||
|
||||
await answerFirstClick();
|
||||
await answerSecondClick();
|
||||
expect(boot.text("flash-msg")).toBe(alreadyTrackedFlash);
|
||||
expect(boot.storage.read("autistmask").trackedTokens).toEqual([TOKEN]);
|
||||
});
|
||||
|
||||
test("an answer for a token tracked while the lookup waited clears the status line", async () => {
|
||||
const boot = await openScreen();
|
||||
const answerLookup = addWithPendingLookup(boot, button);
|
||||
expect(boot.text(statusLine)).toBe("Looking up token...");
|
||||
require("../src/popup/views/helpers").trackToken(TOKEN);
|
||||
|
||||
await answerLookup();
|
||||
expect(boot.text("flash-msg")).toBe(alreadyTrackedFlash);
|
||||
expect(boot.text(statusLine)).toBe("");
|
||||
expect(boot.node(statusLine).style.visibility).toBe("hidden");
|
||||
const { state } = require("../src/shared/state");
|
||||
expect(state.trackedTokens).toEqual([TOKEN]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user