feat(extension): in-app update prompt — popup banner + toolbar badge (#1489)
CI / lint (push) Successful in 3s
extension / lint (push) Successful in 10s
CI / frontend-build (push) Successful in 24s
CI / backend-lint-and-test (push) Successful in 39s
CI / integration (push) Successful in 3m51s
extension / lint (pull_request) Successful in 10s
CI / lint (push) Successful in 3s
extension / lint (push) Successful in 10s
CI / frontend-build (push) Successful in 24s
CI / backend-lint-and-test (push) Successful in 39s
CI / integration (push) Successful in 3m51s
extension / lint (pull_request) Successful in 10s
The extension is installed per-instance from the operator's FC host, so Firefox's
static update_url can't apply (each instance has a different host) and updates
were fully manual. Add a self-hosted-friendly update surface that reuses the
existing public GET /api/extension/manifest ({version, latest_url, sha256}):
- lib/api.js: getExtensionManifest().
- background.js: checkForUpdateInfo() compares the instance's latest published
version against runtime.getManifest().version (dotted-numeric compare so
1.0.10 > 1.0.9); CHECK_UPDATE message handler; refreshUpdateBadge() sets a
toolbar badge via browser.action; a daily browser.alarms check plus on
startup/installed. New 'alarms' permission (non-prompting).
- popup: an 'Update available — vX' banner with an Update button that opens the
signed XPI (web root, /api stripped like OPEN_ARTIST_PAGE) → Firefox's native
install prompt. Never blocks the popup on a failed check.
No backend changes (endpoint already exists). Bump 1.0.8→1.0.9 so this ships;
from here on updates surface themselves instead of needing a manual reinstall.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -72,6 +72,17 @@ body {
|
||||
.btn.block { display: block; width: 100%; margin-top: 8px; }
|
||||
.btn.link { background: none; color: var(--on-surface-variant); padding: 4px; }
|
||||
.btn.link:hover { color: var(--accent); }
|
||||
.btn.small { padding: 6px 12px; font-size: 13px; }
|
||||
|
||||
/* In-app update prompt (accent-tinted so it reads as an actionable notice). */
|
||||
.update-banner {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
margin: 10px 10px 0; padding: 10px 12px;
|
||||
background: rgba(244, 186, 122, 0.12);
|
||||
border: 1px solid rgba(244, 186, 122, 0.4);
|
||||
border-radius: 6px;
|
||||
}
|
||||
#update-text { flex: 1; font-size: 13px; }
|
||||
|
||||
.source-row .play {
|
||||
background: none; border: none; color: var(--on-surface-variant);
|
||||
|
||||
@@ -20,6 +20,11 @@
|
||||
</section>
|
||||
|
||||
<section id="main-content" class="main hidden">
|
||||
<div id="update-banner" class="update-banner hidden">
|
||||
<span id="update-text"></span>
|
||||
<button id="update-btn" class="btn primary small">Update</button>
|
||||
</div>
|
||||
|
||||
<nav class="tabs">
|
||||
<button class="tab active" data-tab="platforms">Platforms</button>
|
||||
<button class="tab" data-tab="sources">Sources</button>
|
||||
|
||||
@@ -14,6 +14,7 @@ async function init() {
|
||||
setupEventListeners();
|
||||
showPlatformsLoading();
|
||||
testConnectionIfNeeded();
|
||||
checkForUpdate();
|
||||
loadPlatformStatus().catch(e => showError(`Failed to load platforms: ${e.message}`));
|
||||
} catch (e) {
|
||||
showSetupRequired();
|
||||
@@ -63,6 +64,26 @@ function updateConnectionDot(connected) {
|
||||
d.title = connected ? 'Connected to FabledCurator' : 'Disconnected';
|
||||
}
|
||||
|
||||
// Nudge to reinstall when the configured instance publishes a newer signed XPI
|
||||
// (the extension is self-hosted, so there's no Firefox auto-update). Never
|
||||
// blocks the popup — a failed check just leaves the banner hidden.
|
||||
async function checkForUpdate() {
|
||||
try {
|
||||
const r = await browser.runtime.sendMessage({ type: 'CHECK_UPDATE' });
|
||||
if (r && r.updateAvailable && r.xpiUrl) showUpdateBanner(r);
|
||||
} catch { /* non-fatal */ }
|
||||
}
|
||||
|
||||
function showUpdateBanner(r) {
|
||||
document.getElementById('update-text').textContent =
|
||||
`Update available — v${r.latestVersion} (installed v${r.currentVersion})`;
|
||||
// Opening the signed XPI triggers Firefox's native install prompt.
|
||||
document.getElementById('update-btn').addEventListener('click', () => {
|
||||
browser.tabs.create({ url: r.xpiUrl });
|
||||
});
|
||||
document.getElementById('update-banner').classList.remove('hidden');
|
||||
}
|
||||
|
||||
async function loadPlatformStatus() {
|
||||
const status = await browser.runtime.sendMessage({ type: 'GET_PLATFORM_STATUS' });
|
||||
const c = document.getElementById('platforms-list');
|
||||
|
||||
Reference in New Issue
Block a user