From 1e9120f45a71fe5c5c36451d4dac1fe76fcaf71d Mon Sep 17 00:00:00 2001 From: Doug Richar Date: Sat, 11 Jan 2025 22:54:22 -0500 Subject: [PATCH 1/2] fix(ui): prevent incorrect update notifications (#335) * fix(ui): enhance version check reliability and debugging - Add cache-busting and no-cache headers to `version.json` fetch - Add detailed logging for version mismatches - Improve error handling in version check * fix(ui): improve version comparison logic - Add `isNewerVersion` helper to properly compare semver versions - Only show update toast when deployed version is newer - Update logging message to be more accurate - Handle versions with or without 'v' prefix --- ui/src/hooks/useCheckForUpdates.ts | 46 +++++++++++++++++++++++++++--- ui/vite.config.mjs | 17 +++++++---- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/ui/src/hooks/useCheckForUpdates.ts b/ui/src/hooks/useCheckForUpdates.ts index a1b5e6bf..5c788b1c 100644 --- a/ui/src/hooks/useCheckForUpdates.ts +++ b/ui/src/hooks/useCheckForUpdates.ts @@ -1,6 +1,24 @@ import * as React from 'react' import { toast } from 'sonner' +// Helper function to compare versions +function isNewerVersion(current: string, deployed: string): boolean { + // Remove 'v' prefix if present + const cleanCurrent = current.replace(/^v/, '') + const cleanDeployed = deployed.replace(/^v/, '') + + const currentParts = cleanCurrent.split('.').map(Number) + const deployedParts = cleanDeployed.split('.').map(Number) + + // Compare major.minor.patch + for (let i = 0; i < 3; i++) { + if (deployedParts[i] > currentParts[i]) return true + if (deployedParts[i] < currentParts[i]) return false + } + + return false +} + export function useCheckForUpdates() { React.useEffect(() => { if (import.meta.env.MODE !== 'production') { @@ -9,11 +27,31 @@ export function useCheckForUpdates() { const checkForUpdates = async () => { try { - const response = await fetch('/version.json') + const response = await fetch(`/version.json?_=${Date.now()}`, { + cache: 'no-store', + headers: { + 'Cache-Control': 'no-cache, no-store, must-revalidate', + Pragma: 'no-cache', + Expires: '0', + }, + }) + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`) + } + const data = await response.json() const deployedVersion = data.version - if (deployedVersion !== __APP_VERSION__) { + // Only show toast if deployed version is newer + if (isNewerVersion(__APP_VERSION__, deployedVersion)) { + // eslint-disable-next-line no-console + console.log('New version detected:', { + current: __APP_VERSION__, + deployed: deployedVersion, + timestamp: new Date().toISOString(), + }) + toast(`A new version is available! v${deployedVersion}`, { description: 'Click the Reload button to update the app.', action: { @@ -29,15 +67,15 @@ export function useCheckForUpdates() { } } - const delay = Number(import.meta.env.VITE_UPDATE_CHECK_INTERVAL || 1000 * 60) + checkForUpdates() + const delay = Number(import.meta.env.VITE_UPDATE_CHECK_INTERVAL || 1000 * 60) if (Number.isNaN(delay)) { console.error('Invalid update check interval:', import.meta.env.VITE_UPDATE_CHECK_INTERVAL) return } const interval = setInterval(checkForUpdates, delay) - return () => clearInterval(interval) }, []) } diff --git a/ui/vite.config.mjs b/ui/vite.config.mjs index e4d7f2a8..929abde1 100644 --- a/ui/vite.config.mjs +++ b/ui/vite.config.mjs @@ -20,11 +20,18 @@ const replaceVersionPlugin = () => { outDir = config.build.outDir }, generateBundle() { - const filePath = path.resolve(__dirname, 'public/version.json') - const content = fs.readFileSync(filePath, 'utf-8') - const updatedContent = content.replace('__APP_VERSION__', version) - const newFilePath = path.resolve(outDir, 'version.json') - fs.writeFileSync(newFilePath, updatedContent, 'utf-8') + try { + const filePath = path.resolve(__dirname, 'public/version.json') + if (!fs.existsSync(filePath)) { + throw new Error(`version.json not found at ${filePath}`) + } + const content = fs.readFileSync(filePath, 'utf-8') + const updatedContent = content.replace('__APP_VERSION__', version) + const newFilePath = path.resolve(outDir, 'version.json') + fs.writeFileSync(newFilePath, updatedContent, 'utf-8') + } catch (error) { + console.error('Failed to replace version in version.json file:', error) + } }, } } From 8f1f41a1e016f4a1c3b22572aa48961d77278902 Mon Sep 17 00:00:00 2001 From: Doug Richar Date: Sat, 11 Jan 2025 22:56:12 -0500 Subject: [PATCH 2/2] chore: release v0.12.1 --- contracts/bootstrap/package.json | 2 +- contracts/package.json | 2 +- ui/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/bootstrap/package.json b/contracts/bootstrap/package.json index c400a14b..f586ca49 100644 --- a/contracts/bootstrap/package.json +++ b/contracts/bootstrap/package.json @@ -1,6 +1,6 @@ { "name": "bootstrap", - "version": "0.12.0", + "version": "0.12.1", "description": "", "main": "index.ts", "scripts": { diff --git a/contracts/package.json b/contracts/package.json index 96dd2162..1a042153 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,6 +1,6 @@ { "name": "reti-contracts", - "version": "0.12.0", + "version": "0.12.1", "license": "MIT", "scripts": { "generate-client": "algokit generate client contracts/artifacts/ --language typescript --output contracts/clients/{contract_name}Client.ts && ./update_contract_artifacts.sh", diff --git a/ui/package.json b/ui/package.json index 466d4a59..0e3da0ec 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "reti-ui", - "version": "0.12.0", + "version": "0.12.1", "private": true, "type": "module", "engines": {