| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586 |
- // The startup/login page uses the same process-wide updater as the workspace.
- // It remains usable even when no server is reachable.
- (() => {
- const desktop = window.yaoyaoDesktop
- if (!desktop?.updateState || document.querySelector('[data-desktop-update-inline]') || window.__yaoyaoInlineUpdate) return
- window.__yaoyaoInlineUpdate = true
- let entry = document.getElementById('desktop-update-entry')
- const fallback = !entry
- if (fallback) {
- entry = document.createElement('section')
- entry.id = 'desktop-update-entry'
- entry.className = 'desktop-update-entry desktop-update-fallback'
- entry.hidden = true
- entry.setAttribute('aria-label', 'App 更新')
- entry.innerHTML = '<button id="desktop-update-action" type="button"><svg id="desktop-update-ring" viewBox="0 0 24 24" aria-hidden="true"><circle class="update-track" cx="12" cy="12" r="9"/><circle id="desktop-update-value" cx="12" cy="12" r="9" pathLength="100"/></svg><span id="desktop-update-label" aria-live="polite">检测更新</span><span id="desktop-update-dot" hidden aria-hidden="true"></span></button><p id="desktop-update-error" role="alert" hidden></p>'
- document.body.append(entry)
- }
- const button = document.getElementById('desktop-update-action')
- const label = document.getElementById('desktop-update-label')
- const ring = document.getElementById('desktop-update-ring')
- const value = document.getElementById('desktop-update-value')
- const dot = document.getElementById('desktop-update-dot')
- const error = document.getElementById('desktop-update-error')
- let state, pending = false, closed = false, requested = false, reading = false, localError = ''
- function render() {
- if (!state || closed) return
- const busy = pending || ['checking', 'downloading', 'verifying', 'preparing', 'installing'].includes(state.phase)
- const percent = state.phase === 'downloading' && state.total > 0 ? Math.max(0, Math.min(100, Math.floor(state.received / state.total * 100))) : undefined
- entry.hidden = !requested && !localError && !state.available && !['downloading', 'verifying', 'preparing', 'ready', 'installing', 'failed'].includes(state.phase)
- label.textContent = state.phase === 'ready' ? state.installMode === 'restart' ? '重新启动' : '打开安装包'
- : state.phase === 'installing' ? '正在重启…'
- : state.phase === 'downloading' ? percent === undefined ? '正在下载…' : `正在下载 · ${percent}%`
- : ['verifying', 'preparing'].includes(state.phase) ? '正在准备…'
- : busy ? '正在检测…' : state.retryable === false ? '更新未完成'
- : state.available ? '可更新' : state.phase === 'failed' || localError ? '重试更新'
- : state.phase === 'checked' ? '已是最新版本' : '检测更新'
- button.disabled = busy || state.retryable === false
- button.setAttribute('aria-label', label.textContent)
- button.setAttribute('aria-busy', String(busy))
- button.title = localError || state.error || state.message
- ring.classList.toggle('update-spinning', busy && percent === undefined)
- ring.setAttribute('role', busy ? 'progressbar' : 'img')
- ring.setAttribute('aria-label', label.textContent)
- if (busy) ring.removeAttribute('aria-hidden')
- else ring.setAttribute('aria-hidden', 'true')
- ring.setAttribute('aria-valuemin', '0'); ring.setAttribute('aria-valuemax', '100')
- if (percent === undefined) ring.removeAttribute('aria-valuenow')
- else ring.setAttribute('aria-valuenow', String(percent))
- value.setAttribute('stroke-dasharray', `${busy ? percent ?? 25 : 100} 100`)
- dot.hidden = !state.available || busy
- error.textContent = localError || state.error || ''; error.hidden = !error.textContent
- }
- async function refresh() {
- if (reading || closed) return
- if (fallback && document.querySelector('[data-desktop-update-inline]')) {
- closed = true; clearInterval(timer); detach(); entry.remove()
- return
- }
- if (fallback) {
- const sidebar = document.querySelector('.desktop-sidebar .sidebar-footer')
- if (sidebar && entry.parentElement !== sidebar) {
- sidebar.prepend(entry)
- entry.classList.replace('desktop-update-fallback', 'desktop-update-sidebar')
- }
- }
- reading = true
- try { state = await desktop.updateState(); render() } catch { /* Poll again after navigation settles. */ }
- finally { reading = false }
- }
- button.addEventListener('click', async () => {
- if (pending || !state || button.disabled) return
- const action = state.phase === 'ready' ? state.installMode === 'restart' ? 'install' : 'open' : state.available ? 'download' : 'check'
- pending = true; localError = ''; render()
- try {
- const operation = desktop.updateAction(action)
- void refresh()
- const next = await operation
- if (next) state = next
- } catch (cause) { localError = cause.message || '更新未完成,请重试' }
- finally { pending = false; render() }
- })
- const detach = desktop.onUpdateRequested(() => { requested = true; void refresh() })
- const timer = setInterval(() => void refresh(), 500)
- window.addEventListener('unload', () => { closed = true; clearInterval(timer); detach() })
- void refresh()
- })()
|