feat: allow ignoring current update prompt
This commit is contained in:
@@ -511,6 +511,13 @@ header {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.update-card-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.update-card-version {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
|
||||
@@ -98,7 +98,10 @@
|
||||
<div id="update-card-version" class="update-card-version"></div>
|
||||
<p id="update-card-summary" class="update-card-summary"></p>
|
||||
</div>
|
||||
<button id="btn-open-release" class="btn btn-primary btn-sm" type="button">前往更新</button>
|
||||
<div class="update-card-actions">
|
||||
<button id="btn-ignore-release" class="btn btn-outline btn-sm" type="button" hidden>忽略本次更新</button>
|
||||
<button id="btn-open-release" class="btn btn-primary btn-sm" type="button">前往更新</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="update-card-reminder">一定请先导出配置,再执行更新</p>
|
||||
<div id="update-release-list" class="update-release-list"></div>
|
||||
|
||||
@@ -31,6 +31,7 @@ const btnReleaseLog = document.getElementById('btn-release-log');
|
||||
const updateCardVersion = document.getElementById('update-card-version');
|
||||
const updateCardSummary = document.getElementById('update-card-summary');
|
||||
const updateReleaseList = document.getElementById('update-release-list');
|
||||
const btnIgnoreRelease = document.getElementById('btn-ignore-release');
|
||||
const btnOpenRelease = document.getElementById('btn-open-release');
|
||||
const settingsCard = document.getElementById('settings-card');
|
||||
const contributionModePanel = document.getElementById('contribution-mode-panel');
|
||||
@@ -7071,6 +7072,24 @@ function openReleaseListPage() {
|
||||
openExternalUrl(getReleaseListUrl());
|
||||
}
|
||||
|
||||
function ignoreCurrentReleaseUpdate() {
|
||||
if (!sidepanelUpdateService?.ignoreReleaseSnapshot) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ignoredVersion = sidepanelUpdateService.ignoreReleaseSnapshot(currentReleaseSnapshot);
|
||||
if (!ignoredVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
renderReleaseSnapshot({
|
||||
...currentReleaseSnapshot,
|
||||
status: 'ignored',
|
||||
ignoredVersion,
|
||||
});
|
||||
showToast(`已忽略 ${ignoredVersion} 更新,有新版本时会再次提醒。`, 'info', 2200);
|
||||
}
|
||||
|
||||
function openCloudflareTempEmailUsageGuidePage() {
|
||||
const targetUrl = getContributionPortalUrl();
|
||||
if (!targetUrl) {
|
||||
@@ -7165,6 +7184,10 @@ function resetUpdateCard() {
|
||||
btnOpenRelease.hidden = true;
|
||||
btnOpenRelease.onclick = null;
|
||||
}
|
||||
if (btnIgnoreRelease) {
|
||||
btnIgnoreRelease.hidden = true;
|
||||
btnIgnoreRelease.onclick = null;
|
||||
}
|
||||
}
|
||||
|
||||
function renderReleaseSnapshot(snapshot) {
|
||||
@@ -7212,6 +7235,17 @@ function renderReleaseSnapshot(snapshot) {
|
||||
btnOpenRelease.textContent = '前往更新';
|
||||
btnOpenRelease.onclick = () => openExternalUrl(logUrl);
|
||||
}
|
||||
if (btnIgnoreRelease) {
|
||||
btnIgnoreRelease.hidden = false;
|
||||
btnIgnoreRelease.onclick = ignoreCurrentReleaseUpdate;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'ignored': {
|
||||
extensionUpdateStatus.textContent = localVersionText || 'Ultra0.0';
|
||||
extensionUpdateStatus.classList.add('is-version-label');
|
||||
resetUpdateCard();
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
const RELEASES_PAGE_URL = `https://github.com/${GITHUB_OWNER}/${GITHUB_REPO}/releases`;
|
||||
const RELEASES_API_URL = `https://api.github.com/repos/${GITHUB_OWNER}/${GITHUB_REPO}/releases?per_page=10`;
|
||||
const CACHE_KEY = 'multipage-release-snapshot-v1';
|
||||
const IGNORED_UPDATE_VERSION_KEY = 'multipage-ignored-release-version-v1';
|
||||
const CACHE_TTL_MS = 60 * 60 * 1000;
|
||||
const FETCH_TIMEOUT_MS = 8000;
|
||||
const MAX_RELEASES = 10;
|
||||
@@ -257,6 +258,32 @@
|
||||
}
|
||||
}
|
||||
|
||||
function getIgnoredUpdateVersion() {
|
||||
try {
|
||||
return String(localStorage.getItem(IGNORED_UPDATE_VERSION_KEY) || '').trim();
|
||||
} catch (error) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function setIgnoredUpdateVersion(version) {
|
||||
const normalized = formatDisplayVersion(version, VERSION_FAMILY_ULTRA) || String(version || '').trim();
|
||||
try {
|
||||
if (normalized) {
|
||||
localStorage.setItem(IGNORED_UPDATE_VERSION_KEY, normalized);
|
||||
} else {
|
||||
localStorage.removeItem(IGNORED_UPDATE_VERSION_KEY);
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore localStorage failures.
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function clearIgnoredUpdateVersion() {
|
||||
return setIgnoredUpdateVersion('');
|
||||
}
|
||||
|
||||
async function fetchReleases() {
|
||||
const controller = new AbortController();
|
||||
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
||||
@@ -326,18 +353,39 @@
|
||||
}
|
||||
|
||||
const newerReleases = releases.filter((release) => compareVersions(release.displayVersion, localVersion) > 0);
|
||||
const ignoredVersion = getIgnoredUpdateVersion();
|
||||
const newestUpdateVersion = newerReleases[0]?.displayVersion || '';
|
||||
const updateIgnored = Boolean(newestUpdateVersion)
|
||||
&& Boolean(ignoredVersion)
|
||||
&& compareVersions(ignoredVersion, newestUpdateVersion) === 0;
|
||||
return {
|
||||
status: newerReleases.length > 0 ? 'update-available' : 'latest',
|
||||
status: newerReleases.length > 0
|
||||
? (updateIgnored ? 'ignored' : 'update-available')
|
||||
: 'latest',
|
||||
localVersion,
|
||||
latestVersion: latestRelease.displayVersion,
|
||||
latestRelease,
|
||||
newerReleases,
|
||||
ignoredVersion: updateIgnored ? ignoredVersion : '',
|
||||
logUrl: latestRelease.url || RELEASES_PAGE_URL,
|
||||
releasesPageUrl: RELEASES_PAGE_URL,
|
||||
checkedAt: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
function getSnapshotUpdateVersion(snapshot = {}) {
|
||||
const newerReleases = Array.isArray(snapshot?.newerReleases) ? snapshot.newerReleases : [];
|
||||
return newerReleases[0]?.displayVersion || snapshot?.latestVersion || '';
|
||||
}
|
||||
|
||||
function ignoreReleaseSnapshot(snapshot = {}) {
|
||||
const targetVersion = getSnapshotUpdateVersion(snapshot);
|
||||
if (!targetVersion) {
|
||||
return '';
|
||||
}
|
||||
return setIgnoredUpdateVersion(targetVersion);
|
||||
}
|
||||
|
||||
function getLocalVersionLabel(manifest = chrome.runtime.getManifest()) {
|
||||
const versionName = formatDisplayVersion(manifest?.version_name, VERSION_FAMILY_ULTRA);
|
||||
if (versionName) {
|
||||
@@ -389,8 +437,11 @@
|
||||
compareVersions,
|
||||
formatDisplayVersion,
|
||||
formatReleaseDate,
|
||||
clearIgnoredUpdateVersion,
|
||||
getIgnoredUpdateVersion,
|
||||
getLocalVersionLabel,
|
||||
getReleaseSnapshot,
|
||||
ignoreReleaseSnapshot,
|
||||
releasesPageUrl: RELEASES_PAGE_URL,
|
||||
stripVersionPrefix,
|
||||
};
|
||||
|
||||
@@ -23,6 +23,9 @@ test('update card highlights exporting config before upgrade', () => {
|
||||
const html = fs.readFileSync('sidepanel/sidepanel.html', 'utf8');
|
||||
const css = fs.readFileSync('sidepanel/sidepanel.css', 'utf8');
|
||||
|
||||
assert.match(html, /id="btn-ignore-release"/);
|
||||
assert.match(html, /class="update-card-actions"/);
|
||||
assert.match(css, /\.update-card-actions\s*\{/);
|
||||
assert.match(html, /<p class="update-card-reminder">一定请先导出配置,再执行更新<\/p>/);
|
||||
assert.match(css, /\.update-card-reminder\s*\{/);
|
||||
assert.match(css, /font-weight:\s*700;/);
|
||||
|
||||
@@ -20,6 +20,9 @@ function createUpdateService(options = {}) {
|
||||
setItem(key, value) {
|
||||
cache.set(key, String(value));
|
||||
},
|
||||
removeItem(key) {
|
||||
cache.delete(key);
|
||||
},
|
||||
};
|
||||
|
||||
if (options.cachedSnapshot) {
|
||||
@@ -180,3 +183,54 @@ test('getReleaseSnapshot reorders cached releases before choosing latest version
|
||||
['Ultra1.1']
|
||||
);
|
||||
});
|
||||
|
||||
test('getReleaseSnapshot suppresses an ignored latest update until a newer release appears', async () => {
|
||||
let releases = [
|
||||
{
|
||||
tag_name: 'Ultra1.1',
|
||||
name: 'Ultra1.1',
|
||||
html_url: 'https://example.com/Ultra1.1',
|
||||
published_at: '2026-04-19T00:00:00.000Z',
|
||||
body: '- current release',
|
||||
draft: false,
|
||||
prerelease: false,
|
||||
},
|
||||
];
|
||||
const { api } = createUpdateService({
|
||||
manifest: {
|
||||
version: '1.0',
|
||||
version_name: 'Ultra1.0',
|
||||
},
|
||||
fetchImpl: async () => ({
|
||||
ok: true,
|
||||
async json() {
|
||||
return releases;
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
const firstSnapshot = await api.getReleaseSnapshot({ force: true });
|
||||
assert.equal(firstSnapshot.status, 'update-available');
|
||||
assert.equal(api.ignoreReleaseSnapshot(firstSnapshot), 'Ultra1.1');
|
||||
|
||||
const ignoredSnapshot = await api.getReleaseSnapshot({ force: true });
|
||||
assert.equal(ignoredSnapshot.status, 'ignored');
|
||||
assert.equal(ignoredSnapshot.ignoredVersion, 'Ultra1.1');
|
||||
|
||||
releases = [
|
||||
{
|
||||
tag_name: 'Ultra1.2',
|
||||
name: 'Ultra1.2',
|
||||
html_url: 'https://example.com/Ultra1.2',
|
||||
published_at: '2026-04-20T00:00:00.000Z',
|
||||
body: '- next release',
|
||||
draft: false,
|
||||
prerelease: false,
|
||||
},
|
||||
...releases,
|
||||
];
|
||||
|
||||
const newerSnapshot = await api.getReleaseSnapshot({ force: true });
|
||||
assert.equal(newerSnapshot.status, 'update-available');
|
||||
assert.equal(newerSnapshot.latestVersion, 'Ultra1.2');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user