mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
feat: implement version mismatch handling and banner registration in UpdateService
This commit is contained in:
@@ -62,6 +62,12 @@ class BannerService {
|
||||
*/
|
||||
registerBanner(id, bannerConfig) {
|
||||
this.banners.set(id, bannerConfig);
|
||||
|
||||
// If already initialized, render the banner immediately
|
||||
if (this.initialized && !this.isBannerDismissed(id) && this.container) {
|
||||
this.renderBanner(bannerConfig);
|
||||
this.updateContainerVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -88,6 +94,12 @@ class BannerService {
|
||||
// Remove banner from DOM
|
||||
const bannerElement = document.querySelector(`[data-banner-id="${bannerId}"]`);
|
||||
if (bannerElement) {
|
||||
// Call onRemove callback if provided
|
||||
const banner = this.banners.get(bannerId);
|
||||
if (banner && typeof banner.onRemove === 'function') {
|
||||
banner.onRemove(bannerElement);
|
||||
}
|
||||
|
||||
bannerElement.style.animation = 'banner-slide-up 0.3s ease-in-out forwards';
|
||||
setTimeout(() => {
|
||||
bannerElement.remove();
|
||||
@@ -122,12 +134,16 @@ class BannerService {
|
||||
bannerElement.className = 'banner-item';
|
||||
bannerElement.setAttribute('data-banner-id', banner.id);
|
||||
|
||||
const actionsHtml = banner.actions ? banner.actions.map(action =>
|
||||
`<a href="${action.url}" target="_blank" class="banner-action banner-action-${action.type}" rel="noopener noreferrer">
|
||||
const actionsHtml = banner.actions ? banner.actions.map(action => {
|
||||
const actionAttribute = action.action ? `data-action="${action.action}"` : '';
|
||||
const href = action.url ? `href="${action.url}"` : '#';
|
||||
const target = action.url ? 'target="_blank" rel="noopener noreferrer"' : '';
|
||||
|
||||
return `<a ${href ? `href="${href}"` : ''} ${target} class="banner-action banner-action-${action.type}" ${actionAttribute}>
|
||||
<i class="${action.icon}"></i>
|
||||
<span>${action.text}</span>
|
||||
</a>`
|
||||
).join('') : '';
|
||||
</a>`;
|
||||
}).join('') : '';
|
||||
|
||||
const dismissButtonHtml = banner.dismissible ?
|
||||
`<button class="banner-dismiss" onclick="bannerService.dismissBanner('${banner.id}')" title="Dismiss">
|
||||
@@ -148,6 +164,11 @@ class BannerService {
|
||||
`;
|
||||
|
||||
this.container.appendChild(bannerElement);
|
||||
|
||||
// Call onRegister callback if provided
|
||||
if (typeof banner.onRegister === 'function') {
|
||||
banner.onRegister(bannerElement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import { modalManager } from './ModalManager.js';
|
||||
import { getStorageItem, setStorageItem } from '../utils/storageHelpers.js';
|
||||
import {
|
||||
getStorageItem,
|
||||
setStorageItem,
|
||||
getStoredVersionInfo,
|
||||
setStoredVersionInfo,
|
||||
isVersionMatch,
|
||||
resetDismissedBanner
|
||||
} from '../utils/storageHelpers.js';
|
||||
import { bannerService } from './BannerService.js';
|
||||
|
||||
export class UpdateService {
|
||||
constructor() {
|
||||
@@ -17,6 +25,8 @@ export class UpdateService {
|
||||
this.lastCheckTime = parseInt(getStorageItem('last_update_check') || '0');
|
||||
this.isUpdating = false;
|
||||
this.nightlyMode = getStorageItem('nightly_updates', false);
|
||||
this.currentVersionInfo = null;
|
||||
this.versionMismatch = false;
|
||||
}
|
||||
|
||||
initialize() {
|
||||
@@ -59,6 +69,9 @@ export class UpdateService {
|
||||
|
||||
// Immediately update modal content with current values (even if from default)
|
||||
this.updateModalContent();
|
||||
|
||||
// Check version info for mismatch after loading basic info
|
||||
this.checkVersionInfo();
|
||||
}
|
||||
|
||||
updateNightlyWarning() {
|
||||
@@ -424,6 +437,110 @@ export class UpdateService {
|
||||
// Ensure badge visibility is updated after manual check
|
||||
this.updateBadgeVisibility();
|
||||
}
|
||||
|
||||
async checkVersionInfo() {
|
||||
try {
|
||||
// Call API to get current version info
|
||||
const response = await fetch('/api/version-info');
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
this.currentVersionInfo = data.version;
|
||||
|
||||
// Check if version matches stored version
|
||||
this.versionMismatch = !isVersionMatch(this.currentVersionInfo);
|
||||
|
||||
if (this.versionMismatch) {
|
||||
console.log('Version mismatch detected:', {
|
||||
current: this.currentVersionInfo,
|
||||
stored: getStoredVersionInfo()
|
||||
});
|
||||
|
||||
// Reset dismissed status for version mismatch banner
|
||||
resetDismissedBanner('version-mismatch');
|
||||
|
||||
// Register and show the version mismatch banner
|
||||
this.registerVersionMismatchBanner();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to check version info:', error);
|
||||
}
|
||||
}
|
||||
|
||||
registerVersionMismatchBanner() {
|
||||
// Get stored and current version for display
|
||||
const storedVersion = getStoredVersionInfo() || 'unknown';
|
||||
const currentVersion = this.currentVersionInfo || 'unknown';
|
||||
|
||||
bannerService.registerBanner('version-mismatch', {
|
||||
id: 'version-mismatch',
|
||||
title: 'Application Update Detected',
|
||||
content: `Your browser is running an outdated version of LoRA Manager (${storedVersion}). The server has been updated to version ${currentVersion}. Please refresh to ensure proper functionality.`,
|
||||
actions: [
|
||||
{
|
||||
text: 'Refresh Now',
|
||||
icon: 'fas fa-sync',
|
||||
action: 'hardRefresh',
|
||||
type: 'primary'
|
||||
}
|
||||
],
|
||||
dismissible: false,
|
||||
priority: 10,
|
||||
countdown: 15,
|
||||
onRegister: (bannerElement) => {
|
||||
// Add countdown element
|
||||
const countdownEl = document.createElement('div');
|
||||
countdownEl.className = 'banner-countdown';
|
||||
countdownEl.innerHTML = `<span>Refreshing in <strong>15</strong> seconds...</span>`;
|
||||
bannerElement.querySelector('.banner-content').appendChild(countdownEl);
|
||||
|
||||
// Start countdown
|
||||
let seconds = 15;
|
||||
const countdownInterval = setInterval(() => {
|
||||
seconds--;
|
||||
const strongEl = countdownEl.querySelector('strong');
|
||||
if (strongEl) strongEl.textContent = seconds;
|
||||
|
||||
if (seconds <= 0) {
|
||||
clearInterval(countdownInterval);
|
||||
this.performHardRefresh();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Store interval ID for cleanup
|
||||
bannerElement.dataset.countdownInterval = countdownInterval;
|
||||
|
||||
// Add action button event handler
|
||||
const actionBtn = bannerElement.querySelector('.banner-action[data-action="hardRefresh"]');
|
||||
if (actionBtn) {
|
||||
actionBtn.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
clearInterval(countdownInterval);
|
||||
this.performHardRefresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
onRemove: (bannerElement) => {
|
||||
// Clear any existing interval
|
||||
const intervalId = bannerElement.dataset.countdownInterval;
|
||||
if (intervalId) {
|
||||
clearInterval(parseInt(intervalId));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
performHardRefresh() {
|
||||
// Update stored version info before refreshing
|
||||
setStoredVersionInfo(this.currentVersionInfo);
|
||||
|
||||
// Force a hard refresh by adding cache-busting parameter
|
||||
const cacheBuster = new Date().getTime();
|
||||
window.location.href = window.location.pathname +
|
||||
(window.location.search ? window.location.search + '&' : '?') +
|
||||
`cache=${cacheBuster}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Create and export singleton instance
|
||||
|
||||
@@ -213,4 +213,45 @@ export function getMapFromStorage(key) {
|
||||
console.error(`Error loading Map from localStorage (${key}):`, error);
|
||||
return new Map();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stored version info from localStorage
|
||||
* @returns {string|null} The stored version string or null if not found
|
||||
*/
|
||||
export function getStoredVersionInfo() {
|
||||
return getStorageItem('version_info', null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store version info to localStorage
|
||||
* @param {string} versionInfo - The version info string to store
|
||||
*/
|
||||
export function setStoredVersionInfo(versionInfo) {
|
||||
setStorageItem('version_info', versionInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if version info matches between stored and current
|
||||
* @param {string} currentVersionInfo - The current version info from server
|
||||
* @returns {boolean} True if versions match or no stored version exists
|
||||
*/
|
||||
export function isVersionMatch(currentVersionInfo) {
|
||||
const storedVersion = getStoredVersionInfo();
|
||||
// If we have no stored version yet, consider it a match
|
||||
if (storedVersion === null) {
|
||||
setStoredVersionInfo(currentVersionInfo);
|
||||
return true;
|
||||
}
|
||||
return storedVersion === currentVersionInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the dismissed status of a specific banner
|
||||
* @param {string} bannerId - The ID of the banner to un-dismiss
|
||||
*/
|
||||
export function resetDismissedBanner(bannerId) {
|
||||
const dismissedBanners = getStorageItem('dismissed_banners', []);
|
||||
const updatedBanners = dismissedBanners.filter(id => id !== bannerId);
|
||||
setStorageItem('dismissed_banners', updatedBanners);
|
||||
}
|
||||
Reference in New Issue
Block a user