diff --git a/content/mail-2925.js b/content/mail-2925.js index 0197ce7..404477c 100644 --- a/content/mail-2925.js +++ b/content/mail-2925.js @@ -11,10 +11,22 @@ if (!isTopFrame) { } else { let seenCodes = new Set(); +let seenCodeSessionKey = ''; +let seenCodesReadyPromise = null; async function loadSeenCodes() { try { - const data = await chrome.storage.session.get('seen2925Codes'); + const data = await chrome.storage.session.get(['seen2925CodeState', 'seen2925Codes']); + const state = data?.seen2925CodeState || null; + if (state && typeof state === 'object') { + seenCodeSessionKey = String(state.sessionKey || ''); + if (Array.isArray(state.codes)) { + seenCodes = new Set(state.codes); + } + console.log(MAIL2925_PREFIX, `Loaded ${seenCodes.size} seen codes for session ${seenCodeSessionKey || 'default'}`); + return; + } + if (data.seen2925Codes && Array.isArray(data.seen2925Codes)) { seenCodes = new Set(data.seen2925Codes); console.log(MAIL2925_PREFIX, `Loaded ${seenCodes.size} previously seen codes`); @@ -24,16 +36,46 @@ async function loadSeenCodes() { } } -loadSeenCodes(); +seenCodesReadyPromise = loadSeenCodes(); async function persistSeenCodes() { try { - await chrome.storage.session.set({ seen2925Codes: [...seenCodes] }); + await chrome.storage.session.set({ + seen2925Codes: [...seenCodes], + seen2925CodeState: { + sessionKey: seenCodeSessionKey, + codes: [...seenCodes], + }, + }); } catch (err) { console.warn(MAIL2925_PREFIX, 'Could not persist seen codes, continuing in-memory only:', err?.message || err); } } +function buildSeenCodeSessionKey(step, payload = {}) { + const timestamp = Number(payload?.filterAfterTimestamp); + if (Number.isFinite(timestamp) && timestamp > 0) { + return `${step}:${timestamp}`; + } + return `${step}:default`; +} + +async function ensureSeenCodesSession(step, payload = {}) { + if (seenCodesReadyPromise) { + await seenCodesReadyPromise; + seenCodesReadyPromise = null; + } + + const nextSessionKey = buildSeenCodeSessionKey(step, payload); + if (seenCodeSessionKey === nextSessionKey) { + return; + } + + seenCodeSessionKey = nextSessionKey; + seenCodes = new Set(); + await persistSeenCodes(); +} + chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { if (message.type === 'POLL_EMAIL') { resetStopState(); @@ -473,6 +515,7 @@ async function refreshInbox() { } async function handlePollEmail(step, payload) { + await ensureSeenCodesSession(step, payload); const { senderFilters, subjectFilters, diff --git a/tests/mail-2925-content.test.js b/tests/mail-2925-content.test.js index ad54311..30df390 100644 --- a/tests/mail-2925-content.test.js +++ b/tests/mail-2925-content.test.js @@ -116,6 +116,7 @@ async function openMailAndDeleteAfterRead(item) { return item.id === 'new' ? 'Your ChatGPT code is 654321' : 'No code here'; } +async function ensureSeenCodesSession() {} function persistSeenCodes() {} function log() {} @@ -199,6 +200,7 @@ async function openMailAndDeleteAfterRead(item) { return item.text; } +async function ensureSeenCodesSession() {} function persistSeenCodes() {} function log() {} @@ -224,6 +226,58 @@ return { assert.deepEqual(api.getReadAndDeleteCalls(), ['mail-1']); }); +test('ensureSeenCodesSession resets tried codes only when a new verification step session starts', async () => { + const bundle = [ + extractFunction('buildSeenCodeSessionKey'), + extractFunction('ensureSeenCodesSession'), + ].join('\n'); + + const api = new Function(` +let seenCodes = new Set(['111111']); +let seenCodeSessionKey = ''; +let seenCodesReadyPromise = Promise.resolve(); +const persisted = []; + +async function persistSeenCodes() { + persisted.push({ + sessionKey: seenCodeSessionKey, + codes: [...seenCodes], + }); +} + +${bundle} + +return { + ensureSeenCodesSession, + getSeenCodes() { + return [...seenCodes]; + }, + getSessionKey() { + return seenCodeSessionKey; + }, + getPersisted() { + return persisted.slice(); + }, +}; +`)(); + + await api.ensureSeenCodesSession(4, { filterAfterTimestamp: 1000 }); + assert.equal(api.getSessionKey(), '4:1000'); + assert.deepEqual(api.getSeenCodes(), []); + + await api.ensureSeenCodesSession(4, { filterAfterTimestamp: 1000 }); + assert.deepEqual(api.getPersisted(), [ + { sessionKey: '4:1000', codes: [] }, + ]); + + await api.ensureSeenCodesSession(8, { filterAfterTimestamp: 2000 }); + assert.equal(api.getSessionKey(), '8:2000'); + assert.deepEqual(api.getPersisted(), [ + { sessionKey: '4:1000', codes: [] }, + { sessionKey: '8:2000', codes: [] }, + ]); +}); + test('openMailAndGetMessageText always returns to inbox after opening a 2925 message', async () => { const bundle = [ extractFunction('returnToInbox'),