update notification
This commit is contained in:
parent
48439af5b7
commit
6bbd52fda7
2 changed files with 108 additions and 7 deletions
|
|
@ -12090,10 +12090,26 @@ async function checkForUpdates() {
|
||||||
if (data.update_available) {
|
if (data.update_available) {
|
||||||
const dismissed = localStorage.getItem('soulsync-update-dismissed');
|
const dismissed = localStorage.getItem('soulsync-update-dismissed');
|
||||||
if (dismissed !== data.latest_sha) {
|
if (dismissed !== data.latest_sha) {
|
||||||
|
// Add glow class
|
||||||
btn.classList.add('update-available');
|
btn.classList.add('update-available');
|
||||||
|
// Add UPDATE badge if not already present
|
||||||
|
if (!btn.querySelector('.update-badge')) {
|
||||||
|
const badge = document.createElement('span');
|
||||||
|
badge.className = 'update-badge';
|
||||||
|
badge.textContent = 'UPDATE';
|
||||||
|
btn.appendChild(badge);
|
||||||
|
}
|
||||||
|
// Show toast on first detection (not if already notified this session)
|
||||||
|
const notified = sessionStorage.getItem('soulsync-update-notified');
|
||||||
|
if (notified !== data.latest_sha) {
|
||||||
|
sessionStorage.setItem('soulsync-update-notified', data.latest_sha);
|
||||||
|
showToast('A new SoulSync update is available!', 'info');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
btn.classList.remove('update-available');
|
btn.classList.remove('update-available');
|
||||||
|
const badge = btn.querySelector('.update-badge');
|
||||||
|
if (badge) badge.remove();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.debug('Update check failed:', e);
|
console.debug('Update check failed:', e);
|
||||||
|
|
@ -12101,13 +12117,25 @@ async function checkForUpdates() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function showVersionInfo() {
|
async function showVersionInfo() {
|
||||||
// Dismiss update glow when user opens the modal (fire-and-forget, don't block modal)
|
// Check update status before dismissing so we can pass it to the modal
|
||||||
|
let updateInfo = null;
|
||||||
const btn = document.querySelector('.version-button');
|
const btn = document.querySelector('.version-button');
|
||||||
if (btn && btn.classList.contains('update-available')) {
|
const hadUpdate = btn && btn.classList.contains('update-available');
|
||||||
|
|
||||||
|
// Dismiss update glow when user opens the modal
|
||||||
|
if (hadUpdate) {
|
||||||
btn.classList.remove('update-available');
|
btn.classList.remove('update-available');
|
||||||
fetch('/api/update-check').then(r => r.json()).then(data => {
|
const badge = btn.querySelector('.update-badge');
|
||||||
if (data.latest_sha) localStorage.setItem('soulsync-update-dismissed', data.latest_sha);
|
if (badge) badge.remove();
|
||||||
}).catch(() => {});
|
try {
|
||||||
|
const updateRes = await fetch('/api/update-check');
|
||||||
|
if (updateRes.ok) {
|
||||||
|
updateInfo = await updateRes.json();
|
||||||
|
if (updateInfo.latest_sha) {
|
||||||
|
localStorage.setItem('soulsync-update-dismissed', updateInfo.latest_sha);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -12123,7 +12151,7 @@ async function showVersionInfo() {
|
||||||
console.log('Version data received:', versionData);
|
console.log('Version data received:', versionData);
|
||||||
|
|
||||||
// Populate modal content
|
// Populate modal content
|
||||||
populateVersionModal(versionData);
|
populateVersionModal(versionData, hadUpdate ? updateInfo : null);
|
||||||
|
|
||||||
// Show modal
|
// Show modal
|
||||||
const modalOverlay = document.getElementById('version-modal-overlay');
|
const modalOverlay = document.getElementById('version-modal-overlay');
|
||||||
|
|
@ -12143,7 +12171,7 @@ function closeVersionModal() {
|
||||||
console.log('Version modal closed');
|
console.log('Version modal closed');
|
||||||
}
|
}
|
||||||
|
|
||||||
function populateVersionModal(versionData) {
|
function populateVersionModal(versionData, updateInfo) {
|
||||||
const container = document.getElementById('version-content-container');
|
const container = document.getElementById('version-content-container');
|
||||||
if (!container) {
|
if (!container) {
|
||||||
console.error('Version content container not found');
|
console.error('Version content container not found');
|
||||||
|
|
@ -12160,6 +12188,20 @@ function populateVersionModal(versionData) {
|
||||||
// Clear existing content
|
// Clear existing content
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
|
// Show update banner if an update was available when modal was opened
|
||||||
|
if (updateInfo && updateInfo.update_available) {
|
||||||
|
const banner = document.createElement('div');
|
||||||
|
banner.className = 'version-update-banner';
|
||||||
|
banner.innerHTML = `
|
||||||
|
<div class="version-update-banner-icon">⬆</div>
|
||||||
|
<div class="version-update-banner-text">
|
||||||
|
<strong>New update available</strong>
|
||||||
|
<span>Your version: ${updateInfo.current_sha || 'unknown'} → Latest: ${updateInfo.latest_sha || 'unknown'}</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
container.appendChild(banner);
|
||||||
|
}
|
||||||
|
|
||||||
// Create sections
|
// Create sections
|
||||||
versionData.sections.forEach(section => {
|
versionData.sections.forEach(section => {
|
||||||
const sectionDiv = document.createElement('div');
|
const sectionDiv = document.createElement('div');
|
||||||
|
|
|
||||||
|
|
@ -872,6 +872,26 @@ body {
|
||||||
color: rgb(var(--accent-light-rgb));
|
color: rgb(var(--accent-light-rgb));
|
||||||
border-color: rgba(var(--accent-rgb), 0.4);
|
border-color: rgba(var(--accent-rgb), 0.4);
|
||||||
animation: version-glow 2s ease-in-out infinite;
|
animation: version-glow 2s ease-in-out infinite;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-button.update-available .update-badge {
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 5px;
|
||||||
|
font-size: 7px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
background: rgb(var(--accent-rgb));
|
||||||
|
color: #fff;
|
||||||
|
padding: 1px 5px;
|
||||||
|
border-radius: 8px;
|
||||||
|
vertical-align: middle;
|
||||||
|
animation: badge-pulse 2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes badge-pulse {
|
||||||
|
0%, 100% { opacity: 0.85; }
|
||||||
|
50% { opacity: 1; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes version-glow {
|
@keyframes version-glow {
|
||||||
|
|
@ -885,6 +905,45 @@ body {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Update banner inside version modal */
|
||||||
|
.version-update-banner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
margin: 0 0 20px 0;
|
||||||
|
background: rgba(var(--accent-rgb), 0.08);
|
||||||
|
border: 1px solid rgba(var(--accent-rgb), 0.25);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: rgb(var(--accent-light-rgb));
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 0.1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-update-banner-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-update-banner-text {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-update-banner-text strong {
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.version-update-banner-text span {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
/* Status Section - Premium Glassmorphic Design */
|
/* Status Section - Premium Glassmorphic Design */
|
||||||
.status-section {
|
.status-section {
|
||||||
height: fit-content;
|
height: fit-content;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue