Check for Updates

This commit is contained in:
Sedat ÖZTÜRK 2026-07-14 17:16:08 +03:00
parent 4805e8c4d9
commit 535ed6835f

View file

@ -1,7 +1,8 @@
import { registerSW } from 'virtual:pwa-register'
import { store } from '@/store'
const ACTIVATION_RETRY_DELAY = 5_000
const ACTIVATION_TIMEOUT = 30_000
const ACTIVATION_TIMEOUT = 20_000
let registrationStarted = false
let activationInProgress = false
@ -11,10 +12,104 @@ let activateUpdateHandler: ((reloadPage?: boolean) => Promise<void>) | undefined
let registrationInitialization: Promise<void> | undefined
let activationRetryTimer: number | undefined
let activationTimeoutTimer: number | undefined
let overlayStoreUnsubscribe: (() => void) | undefined
const watchedInstallingWorkers = new WeakSet<ServiceWorker>()
export type ManualUpdateResult = 'up-to-date' | 'updating' | 'unsupported'
type Release = {
version: string
changeLog?: string[]
}
const getText = (value: unknown) =>
value === undefined || value === null ? undefined : String(value)
function updateOverlayApiVersion(overlay: HTMLElement) {
const apiConfig = store.getState().abpConfig.config?.extraProperties
const apiEnvironment = getText(apiConfig?.environment)
const apiVersion = getText(apiConfig?.version)
if (!apiEnvironment && !apiVersion) return false
const apiValue = overlay.querySelector<HTMLElement>('[data-sw-api-version]')
if (apiValue) {
apiValue.textContent = `${apiEnvironment ?? '-'}:${apiVersion ?? '-'}`
}
return true
}
function watchOverlayApiVersion(overlay: HTMLElement) {
overlayStoreUnsubscribe?.()
if (updateOverlayApiVersion(overlay)) return
overlayStoreUnsubscribe = store.subscribe(() => {
if (!overlay.isConnected || updateOverlayApiVersion(overlay)) {
overlayStoreUnsubscribe?.()
overlayStoreUnsubscribe = undefined
}
})
}
async function loadUpdateOverlayDetails(overlay: HTMLElement) {
const state = store.getState()
const uiEnvironment = import.meta.env.MODE
let uiVersion = state.locale.currentUiVersion
let latestRelease: Release | undefined
try {
const response = await fetch(`/version.json?ts=${Date.now()}`, { cache: 'no-store' })
if (!response.ok) throw new Error(`Version request failed with ${response.status}`)
const releases = ((await response.json()) as { releases?: Release[] }).releases
latestRelease = releases?.[0]
uiVersion = latestRelease?.version ?? uiVersion
} catch (error) {
console.warn('Update details could not be loaded.', error)
}
// The overlay may have been removed while version.json was loading.
if (!overlay.isConnected) return
const uiValue = overlay.querySelector<HTMLElement>('[data-sw-ui-version]')
const releaseTitle = overlay.querySelector<HTMLElement>('[data-sw-release-title]')
const releaseList = overlay.querySelector<HTMLUListElement>('[data-sw-release-list]')
if (uiValue) uiValue.textContent = `${uiEnvironment}:${uiVersion ?? '-'}`
watchOverlayApiVersion(overlay)
if (!releaseList || !releaseTitle) return
const changes = latestRelease?.changeLog?.filter(Boolean) ?? []
releaseTitle.textContent = latestRelease?.version
? `What's new in v${latestRelease.version}`
: "What's new"
releaseList.replaceChildren()
if (changes.length === 0) {
const item = document.createElement('li')
item.className = 'sw-update-empty'
item.textContent = 'Release notes are not available.'
releaseList.appendChild(item)
return
}
changes.forEach((change) => {
const item = document.createElement('li')
const icon = document.createElement('span')
const text = document.createElement('span')
icon.className = 'sw-update-check'
icon.setAttribute('aria-hidden', 'true')
icon.textContent = '\u2713'
text.textContent = change.replace(/^\s*-\s*/, '')
item.append(icon, text)
releaseList.appendChild(item)
})
}
function showUpdateOverlay() {
if (document.getElementById('sw-update-overlay')) return
@ -36,13 +131,14 @@ function showUpdateOverlay() {
display: flex;
flex-direction: column;
align-items: center;
gap: 20px;
gap: 16px;
background: #fff;
border-radius: 16px;
padding: 40px 56px;
padding: 32px 40px;
box-shadow: 0 8px 40px rgba(0,0,0,0.25);
text-align: center;
max-width: 360px;
max-width: 560px;
max-height: min(90vh, 720px);
width: 90%;
}
#sw-update-overlay .sw-update-spinner {
@ -67,22 +163,109 @@ function showUpdateOverlay() {
color: #6b7280;
margin: 0;
}
#sw-update-overlay .sw-update-versions {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 6px;
width: 100%;
padding: 10px 12px;
border-radius: 8px;
background: #f3f4f6;
color: #4b5563;
font-size: 13px;
text-transform: capitalize;
}
#sw-update-overlay .sw-update-separator {
color: #9ca3af;
margin: 0 4px;
}
#sw-update-overlay .sw-update-release {
width: 100%;
min-height: 0;
overflow-y: auto;
text-align: left;
border-top: 1px solid #e5e7eb;
padding-top: 14px;
}
#sw-update-overlay .sw-update-release-title {
margin: 0 0 10px;
color: #374151;
font-size: 14px;
font-weight: 700;
}
#sw-update-overlay .sw-update-list {
display: flex;
flex-direction: column;
gap: 8px;
list-style: none;
padding: 0;
margin: 0;
color: #6b7280;
font-size: 13px;
line-height: 1.45;
}
#sw-update-overlay .sw-update-list li {
display: flex;
align-items: flex-start;
gap: 8px;
}
#sw-update-overlay .sw-update-check {
display: inline-flex;
flex: 0 0 16px;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
margin-top: 1px;
border-radius: 50%;
background: #d1fae5;
color: #059669;
font-size: 11px;
font-weight: 700;
}
#sw-update-overlay .sw-update-empty {
display: block;
color: #9ca3af;
}
@media (max-width: 640px) {
#sw-update-overlay .sw-update-card {
padding: 24px 20px;
}
}
`
document.head.appendChild(style)
const overlay = document.createElement('div')
overlay.id = 'sw-update-overlay'
overlay.setAttribute('role', 'status')
overlay.setAttribute('aria-live', 'polite')
overlay.innerHTML = `
<div class="sw-update-card">
<div class="sw-update-spinner"></div>
<p class="sw-update-title">System Updating</p>
<p class="sw-update-title">System Updating</p>
<p class="sw-update-desc">Loading new version, please wait...<br/>The page will reload automatically.</p>
<div class="sw-update-versions">
<span><strong>UI:</strong> <span data-sw-ui-version>Loading...</span></span>
<span class="sw-update-separator">|</span>
<span><strong>API:</strong> <span data-sw-api-version>Loading...</span></span>
</div>
<div class="sw-update-release">
<p class="sw-update-release-title" data-sw-release-title>What's new</p>
<ul class="sw-update-list" data-sw-release-list>
<li class="sw-update-empty">Loading release notes...</li>
</ul>
</div>
</div>
`
document.body.appendChild(overlay)
void loadUpdateOverlayDetails(overlay)
}
function hideUpdateOverlay() {
overlayStoreUnsubscribe?.()
overlayStoreUnsubscribe = undefined
document.getElementById('sw-update-overlay')?.remove()
document.getElementById('sw-update-overlay-style')?.remove()
}