diff --git a/package.json b/package.json index 3f4c23eb..be329eab 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "url": "git+https://github.com/opencor/webapp.git" }, "type": "module", - "version": "1.20261031.0", + "version": "1.20261102.0", "engines": { "bun": ">=1.2.0" }, diff --git a/src/renderer/package.json b/src/renderer/package.json index f6c67a3c..0b127fbc 100644 --- a/src/renderer/package.json +++ b/src/renderer/package.json @@ -42,7 +42,7 @@ }, "./style.css": "./dist/opencor.css" }, - "version": "1.20261031.0", + "version": "1.20261102.0", "libopencorVersion": "1.20261029.0", "scripts": { "build": "vite build && bun scripts/generate.version.ts", diff --git a/src/renderer/src/common/changelog.ts b/src/renderer/src/common/changelog.ts index 9b893d20..09e77b8a 100644 --- a/src/renderer/src/common/changelog.ts +++ b/src/renderer/src/common/changelog.ts @@ -127,29 +127,55 @@ const changelog = (): IOpenCORChangelogEntry[] => { // scripts/generate.version.ts), i.e. the changelog of the latest deployed version (which may be even newer // than the given version) with cache busting (i.e. by adding a timestamp query parameter to the URL so that // the browser doesn't serve a cached version). -// Note #2: we don't want to bother the user if we cannot fetch the changes, so we just return an empty list in that -// case. +// Note #2: we return null if we cannot fetch the changes (within a given amount of time), so that our update available +// dialog can let the user know about it. +// Note #3: we keep track of the changes that we have fetched (or are fetching) for a given version, so that OpenCOR's +// Web app can fetch them as soon as it knows about a newer version (see OpenCOR.vue) and our update available +// dialog can then list them straightaway. We don't keep track of the changes that we failed to fetch, so that +// we can try again later. -const fetchChangelog = async (toVersion: string): Promise => { +const CHANGELOG_FETCH_TIMEOUT = 3 * 1000; // 3 seconds. + +const fetchedChangelogs = new Map>(); + +const doFetchChangelog = async (toVersion: string): Promise => { const url = electronApi ? `https://raw.githubusercontent.com/opencor/webapp/v${toVersion}/src/renderer/changelog.json` : `./assets/changelog.json?t=${Date.now()}`; try { - const response = await fetch(url); + const response = await fetch(url, { signal: AbortSignal.timeout(CHANGELOG_FETCH_TIMEOUT) }); if (!response.ok) { console.warn(`OpenCOR: failed to fetch the changelog (${response.status}: ${response.statusText}).`); - return []; + return null; } return changelogEntries(sanitisedChangelog(await response.json()), currentVersion, toVersion); } catch (error: unknown) { console.warn('OpenCOR: failed to fetch the changelog:', formatError(error)); - return []; + return null; + } +}; + +const fetchChangelog = (toVersion: string): Promise => { + let res = fetchedChangelogs.get(toVersion); + + if (!res) { + res = doFetchChangelog(toVersion).then((entries: IOpenCORChangelogEntry[] | null) => { + if (entries === null) { + fetchedChangelogs.delete(toVersion); + } + + return entries; + }); + + fetchedChangelogs.set(toVersion, res); } + + return res; }; // Export our changelog functions. diff --git a/src/renderer/src/components/OpenCOR.vue b/src/renderer/src/components/OpenCOR.vue index 5538b65f..aab2b727 100644 --- a/src/renderer/src/components/OpenCOR.vue +++ b/src/renderer/src/components/OpenCOR.vue @@ -845,6 +845,17 @@ const onSettingsMenu = (): void => { }; // Update available dialog (for OpenCOR's Web app). +// Note: we fetch the changes for a newer version as soon as we know about it, so that our update available dialog can +// list them straightaway (see fetchChangelog() in changelog.ts). + +vue.watch( + [version.updateAvailable, version.latestVersion], + ([newUpdateAvailable, newLatestVersion]: [boolean, string]) => { + if (newUpdateAvailable) { + void changelog.fetchChangelog(newLatestVersion); + } + } +); const onUpdateAvailable = () => { updateVersion.value = version.latestVersion.value; diff --git a/src/renderer/src/components/dialogs/UpdateAvailableDialog.vue b/src/renderer/src/components/dialogs/UpdateAvailableDialog.vue index 963ff253..cbc70df0 100644 --- a/src/renderer/src/components/dialogs/UpdateAvailableDialog.vue +++ b/src/renderer/src/components/dialogs/UpdateAvailableDialog.vue @@ -8,9 +8,14 @@
OpenCOR {{ version }} is available!
-
+
What's new:
- +
+ + Retrieving the changes... +
+
The changes could not be retrieved.
+
@@ -54,11 +59,16 @@ defineEmits<{ }>(); // Retrieve what is new between our current version (excluded) and the available version (included). -// Note: our dialog may get closed and shown again (possibly for another version) before the changes that we were -// fetching have been retrieved, in which case we ignore those changes since they may not be for the version that -// is now available. +// Note #1: OpenCOR shows our dialog as soon as it knows about a newer version, so we list the changes as soon as they +// have been fetched. OpenCOR's Web app, on the other hand, fetches them as soon as it knows about a newer +// version (see OpenCOR.vue), so they are normally already fetched by the time our dialog is shown, in which +// case we list them straightaway. +// Note #2: our dialog may get closed and shown again (possibly for another version) before the changes that we were +// fetching have been retrieved, in which case we ignore those changes since they may not be for the version +// that is now available. -const entries = vue.ref([]); +const entries = vue.ref([]); +const fetching = vue.ref(false); let fetchId = 0; @@ -66,10 +76,12 @@ const onShow = (): void => { const crtFetchId = ++fetchId; entries.value = []; + fetching.value = true; - void fetchChangelog(props.version).then((changelogEntries: IOpenCORChangelogEntry[]) => { + void fetchChangelog(props.version).then((changelogEntries: IOpenCORChangelogEntry[] | null) => { if (crtFetchId === fetchId) { entries.value = changelogEntries; + fetching.value = false; } }); };