diff --git a/extension/package.json b/extension/package.json index ea442ca..42233b8 100644 --- a/extension/package.json +++ b/extension/package.json @@ -1,7 +1,7 @@ { "name": "ontracker", "private": true, - "version": "1.12.1", + "version": "1.13.0", "type": "module", "scripts": { "dev": "vite", diff --git a/extension/public/background.js b/extension/public/background.js index d8d3e59..e63eaf9 100644 --- a/extension/public/background.js +++ b/extension/public/background.js @@ -3,7 +3,25 @@ importScripts("config.js"); const ONTRACK_URL = "https://ontrack.deakin.edu.au"; -chrome.runtime.onInstalled.addListener(() => { +// Chrome only runs content_scripts on NEW navigations after install/update — a +// tab that was already open on OnTrack (very likely, since checking tasks is +// why a student would have it open) never gets content.js/injected.js without +// a manual reload. Backfill it into any already-open matching tab so a fresh +// install captures data immediately instead of silently doing nothing until +// the student happens to reload or renavigate. +chrome.runtime.onInstalled.addListener(async () => { + let tabs; + try { + tabs = await chrome.tabs.query({ url: "https://ontrack.deakin.edu.au/*" }); + } catch { + return; // host permission not yet granted or tabs API unavailable + } + for (const tab of tabs) { + if (!tab.id) continue; + chrome.scripting + .executeScript({ target: { tabId: tab.id }, files: ["config.js", "content.js"] }) + .catch(() => {}); // e.g. a chrome:// or restricted tab matched unexpectedly + } }); // Read the durable refresh_token cookie (HttpOnly — only chrome.cookies can see @@ -114,12 +132,14 @@ chrome.runtime.onMessage.addListener((msg, _sender, sendResponse) => { payload: msg.payload, }), }) - .then((r) => r.json()) - .then((d) => { - // Only cache once the server actually stored it — a `skipped` response - // (e.g. rejected as an inactive project) must not be treated as sent, or - // this dedup would permanently suppress a push that never landed. - if (!d || !d.skipped) lastIngestHash.set(dedupKey, hash); + .then((r) => r.json().then((d) => ({ httpOk: r.ok, d }))) + .then(({ httpOk, d }) => { + // Only cache once the server actually stored it. fetch() doesn't reject on + // HTTP error status (e.g. 404 "not subscribed" while the account is still + // being linked), and a `skipped` response (e.g. rejected as an inactive + // project) isn't a store either — either one must not be treated as sent, + // or this dedup would permanently suppress a push that never landed. + if (httpOk && d && d.ok && !d.skipped) lastIngestHash.set(dedupKey, hash); sendResponse({ ok: true }); }) .catch(() => { diff --git a/extension/public/manifest.json b/extension/public/manifest.json index 0b547aa..64eb7b7 100644 --- a/extension/public/manifest.json +++ b/extension/public/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 3, "key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqdNZfW6IzfrG6mYRZiKk5nmeWybcEmfSAkn4pCkfjY5KMkei/sBeXgwXCb2wjpsAVg9y8nlYIBHVhv7WgEKQasmQ5r4kXe0aiX/tiiZpOu2ha8PIG9GK0jszmWieAeVMSA2mcd/SDYzDDDl2iStxas9LRjOQSE6znZHkQbGLw6GjcH3oFg7in0dV3YgGhxmjlUtdJFKr/5vVCrVHJ7t+91uwGtWeT8wDtGlLNWiWV9iD4leidCcMRvcwHWDIduDDbU9d/BHGEPf8i7+Hf318exjOtJ6j89ISKSB36xmQgcvXMVRgh4APHXw7LejzjfVYwwCmFturkS0miMYnq/mIZwIDAQAB", "name": "Ontracker", - "version": "1.12.1", + "version": "1.13.0", "description": "Keeps your OnTrack Brief token fresh automatically.", "icons": { "16": "icons/icon16.png", @@ -10,7 +10,7 @@ "48": "icons/icon48.png", "128": "icons/icon128.png" }, - "permissions": ["storage", "cookies"], + "permissions": ["storage", "cookies", "scripting"], "host_permissions": [ "https://ontrack.deakin.edu.au/*", "https://on-tracker.com/*",