mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-23 14:12:11 -03:00
chore(ui): improve notification center accessibility
This commit is contained in:
@@ -12,6 +12,73 @@
|
||||
border-bottom: 1px solid var(--lora-border);
|
||||
}
|
||||
|
||||
.notification-tabs {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
margin-bottom: var(--space-3);
|
||||
}
|
||||
|
||||
.notification-tab {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: var(--space-1);
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: var(--lora-surface);
|
||||
border: 1px solid var(--lora-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
color: var(--text-color);
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease, border-color 0.2s ease;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.notification-tab:hover,
|
||||
.notification-tab.active {
|
||||
background: var(--lora-accent-light, rgba(0, 148, 255, 0.12));
|
||||
border-color: var(--lora-accent);
|
||||
color: var(--lora-accent-text, var(--text-color));
|
||||
}
|
||||
|
||||
.notification-tab-badge {
|
||||
display: none;
|
||||
min-width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
padding: 0 0.4rem;
|
||||
border-radius: 999px;
|
||||
background: var(--lora-accent);
|
||||
color: #fff;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.notification-tab-badge.is-dot {
|
||||
min-width: 0.5rem;
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.notification-tab-badge.visible {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.notification-panels {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
|
||||
.notification-panel {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.notification-panel.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.update-icon {
|
||||
font-size: 1.8em;
|
||||
color: var(--lora-accent);
|
||||
@@ -165,6 +232,137 @@
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.banner-history {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.banner-history h3 {
|
||||
margin: 0;
|
||||
font-size: 1.05rem;
|
||||
color: var(--lora-accent);
|
||||
}
|
||||
|
||||
.banner-history-empty {
|
||||
margin: 0;
|
||||
padding: var(--space-3);
|
||||
background: var(--lora-surface);
|
||||
border: 1px dashed var(--lora-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
text-align: center;
|
||||
color: var(--text-muted, rgba(0, 0, 0, 0.6));
|
||||
}
|
||||
|
||||
.banner-history-list {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
}
|
||||
|
||||
.banner-history-item {
|
||||
border: 1px solid var(--lora-border);
|
||||
border-radius: var(--border-radius-sm);
|
||||
padding: var(--space-2);
|
||||
background: var(--card-bg, #fff);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-1);
|
||||
}
|
||||
|
||||
[data-theme="dark"] .banner-history-item {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
}
|
||||
|
||||
.banner-history-title {
|
||||
margin: 0;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.banner-history-description {
|
||||
margin: 0;
|
||||
color: var(--text-color);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.banner-history-meta {
|
||||
display: flex;
|
||||
gap: var(--space-2);
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-muted, rgba(0, 0, 0, 0.6));
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.banner-history-time {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.banner-history-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.banner-history-status.active {
|
||||
color: var(--lora-success);
|
||||
}
|
||||
|
||||
.banner-history-status.dismissed {
|
||||
color: var(--lora-error);
|
||||
}
|
||||
|
||||
.banner-history-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--space-2);
|
||||
margin-top: var(--space-1);
|
||||
}
|
||||
|
||||
.banner-history-action {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding: 0.35rem 0.65rem;
|
||||
border-radius: var(--border-radius-sm);
|
||||
border: 1px solid var(--lora-border);
|
||||
text-decoration: none;
|
||||
font-size: 0.85rem;
|
||||
transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.banner-history-action i {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.banner-history-action.banner-history-action-primary {
|
||||
background: var(--lora-accent);
|
||||
border-color: var(--lora-accent);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.banner-history-action.banner-history-action-secondary {
|
||||
background: var(--lora-surface);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
.banner-history-action.banner-history-action-tertiary {
|
||||
background: transparent;
|
||||
border-style: dashed;
|
||||
}
|
||||
|
||||
.banner-history-action:hover {
|
||||
background: var(--lora-accent-light, rgba(0, 148, 255, 0.12));
|
||||
border-color: var(--lora-accent);
|
||||
color: var(--lora-accent-text, var(--text-color));
|
||||
}
|
||||
|
||||
/* Override toggle switch styles for update preferences */
|
||||
.update-preferences .toggle-switch {
|
||||
position: relative;
|
||||
|
||||
@@ -9,6 +9,10 @@ const COMMUNITY_SUPPORT_BANNER_DELAY_MS = 5 * 24 * 60 * 60 * 1000; // 5 days
|
||||
const COMMUNITY_SUPPORT_FIRST_SEEN_AT_KEY = 'community_support_banner_first_seen_at';
|
||||
const COMMUNITY_SUPPORT_SHOWN_KEY = 'community_support_banner_shown';
|
||||
const KO_FI_URL = 'https://ko-fi.com/pixelpawsai';
|
||||
const BANNER_HISTORY_KEY = 'banner_history';
|
||||
const BANNER_HISTORY_VIEWED_AT_KEY = 'banner_history_viewed_at';
|
||||
const BANNER_HISTORY_LIMIT = 20;
|
||||
const HISTORY_EXCLUDED_IDS = new Set(['version-mismatch']);
|
||||
|
||||
/**
|
||||
* Banner Service for managing notification banners
|
||||
@@ -20,6 +24,8 @@ class BannerService {
|
||||
this.initialized = false;
|
||||
this.communitySupportBannerTimer = null;
|
||||
this.communitySupportBannerRegistered = false;
|
||||
this.recentHistory = this.loadBannerHistory();
|
||||
this.bannerHistoryViewedAt = this.loadBannerHistoryViewedAt();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,6 +126,8 @@ class BannerService {
|
||||
this.updateContainerVisibility();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
this.markBannerDismissed(bannerId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,7 +186,9 @@ class BannerService {
|
||||
`;
|
||||
|
||||
this.container.appendChild(bannerElement);
|
||||
|
||||
|
||||
this.recordBannerAppearance(banner);
|
||||
|
||||
// Call onRegister callback if provided
|
||||
if (typeof banner.onRegister === 'function') {
|
||||
banner.onRegister(bannerElement);
|
||||
@@ -296,6 +306,95 @@ class BannerService {
|
||||
|
||||
this.updateContainerVisibility();
|
||||
}
|
||||
|
||||
loadBannerHistory() {
|
||||
const stored = getStorageItem(BANNER_HISTORY_KEY, []);
|
||||
if (!Array.isArray(stored)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return stored.slice(0, BANNER_HISTORY_LIMIT).map(entry => ({
|
||||
...entry,
|
||||
timestamp: typeof entry.timestamp === 'number' ? entry.timestamp : Date.now(),
|
||||
dismissedAt: typeof entry.dismissedAt === 'number' ? entry.dismissedAt : null,
|
||||
actions: Array.isArray(entry.actions) ? entry.actions : []
|
||||
}));
|
||||
}
|
||||
|
||||
loadBannerHistoryViewedAt() {
|
||||
const stored = getStorageItem(BANNER_HISTORY_VIEWED_AT_KEY, 0);
|
||||
return typeof stored === 'number' ? stored : 0;
|
||||
}
|
||||
|
||||
saveBannerHistory() {
|
||||
setStorageItem(BANNER_HISTORY_KEY, this.recentHistory.slice(0, BANNER_HISTORY_LIMIT));
|
||||
}
|
||||
|
||||
notifyBannerHistoryUpdated() {
|
||||
window.dispatchEvent(new CustomEvent('lm:banner-history-updated'));
|
||||
}
|
||||
|
||||
recordBannerAppearance(banner) {
|
||||
if (!banner?.id || HISTORY_EXCLUDED_IDS.has(banner.id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const sanitizedActions = Array.isArray(banner.actions)
|
||||
? banner.actions.map(action => ({
|
||||
text: action.text,
|
||||
icon: action.icon,
|
||||
url: action.url || null,
|
||||
type: action.type || 'secondary'
|
||||
}))
|
||||
: [];
|
||||
|
||||
const entry = {
|
||||
id: banner.id,
|
||||
title: banner.title,
|
||||
content: banner.content,
|
||||
actions: sanitizedActions,
|
||||
timestamp: Date.now(),
|
||||
dismissedAt: null
|
||||
};
|
||||
|
||||
this.recentHistory.unshift(entry);
|
||||
if (this.recentHistory.length > BANNER_HISTORY_LIMIT) {
|
||||
this.recentHistory.length = BANNER_HISTORY_LIMIT;
|
||||
}
|
||||
|
||||
this.saveBannerHistory();
|
||||
this.notifyBannerHistoryUpdated();
|
||||
}
|
||||
|
||||
markBannerDismissed(bannerId) {
|
||||
if (!bannerId || HISTORY_EXCLUDED_IDS.has(bannerId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const entry of this.recentHistory) {
|
||||
if (entry.id === bannerId && !entry.dismissedAt) {
|
||||
entry.dismissedAt = Date.now();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.saveBannerHistory();
|
||||
this.notifyBannerHistoryUpdated();
|
||||
}
|
||||
|
||||
getRecentBanners() {
|
||||
return this.recentHistory.slice();
|
||||
}
|
||||
|
||||
getUnreadBannerCount() {
|
||||
return this.recentHistory.filter(entry => entry.timestamp > this.bannerHistoryViewedAt).length;
|
||||
}
|
||||
|
||||
markBannerHistoryViewed() {
|
||||
this.bannerHistoryViewedAt = Date.now();
|
||||
setStorageItem(BANNER_HISTORY_VIEWED_AT_KEY, this.bannerHistoryViewedAt);
|
||||
this.notifyBannerHistoryUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
// Create and export singleton instance
|
||||
|
||||
@@ -28,6 +28,9 @@ export class UpdateService {
|
||||
this.nightlyMode = getStorageItem('nightly_updates', false);
|
||||
this.currentVersionInfo = null;
|
||||
this.versionMismatch = false;
|
||||
this.activeNotificationTab = 'updates';
|
||||
this.handleBannerHistoryUpdated = this.handleBannerHistoryUpdated.bind(this);
|
||||
this.handleNotificationTabKeydown = this.handleNotificationTabKeydown.bind(this);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
@@ -61,6 +64,10 @@ export class UpdateService {
|
||||
});
|
||||
this.updateNightlyWarning();
|
||||
}
|
||||
|
||||
this.setupNotificationCenter();
|
||||
window.addEventListener('lm:banner-history-updated', this.handleBannerHistoryUpdated);
|
||||
this.updateTabBadges();
|
||||
|
||||
// Perform update check if needed
|
||||
this.checkForUpdates().then(() => {
|
||||
@@ -81,6 +88,272 @@ export class UpdateService {
|
||||
warning.style.display = this.nightlyMode ? 'flex' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
setupNotificationCenter() {
|
||||
const modal = document.getElementById('updateModal');
|
||||
if (!modal) {
|
||||
this.notificationTabs = [];
|
||||
this.notificationPanels = [];
|
||||
return;
|
||||
}
|
||||
|
||||
this.notificationTabs = Array.from(modal.querySelectorAll('[data-notification-tab]'));
|
||||
this.notificationPanels = Array.from(modal.querySelectorAll('[data-notification-panel]'));
|
||||
|
||||
this.notificationTabs.forEach(tab => {
|
||||
tab.addEventListener('click', () => {
|
||||
const tabName = tab.getAttribute('data-notification-tab');
|
||||
this.switchNotificationTab(tabName, { markRead: true });
|
||||
});
|
||||
tab.addEventListener('keydown', this.handleNotificationTabKeydown);
|
||||
});
|
||||
|
||||
this.renderRecentBanners();
|
||||
this.switchNotificationTab(this.activeNotificationTab);
|
||||
}
|
||||
|
||||
switchNotificationTab(tabName, { markRead = false } = {}) {
|
||||
if (!tabName) return;
|
||||
|
||||
this.activeNotificationTab = tabName;
|
||||
|
||||
if (Array.isArray(this.notificationTabs)) {
|
||||
this.notificationTabs.forEach(tab => {
|
||||
const isActive = tab.getAttribute('data-notification-tab') === tabName;
|
||||
tab.classList.toggle('active', isActive);
|
||||
tab.setAttribute('aria-selected', isActive ? 'true' : 'false');
|
||||
tab.setAttribute('tabindex', isActive ? '0' : '-1');
|
||||
});
|
||||
}
|
||||
|
||||
if (Array.isArray(this.notificationPanels)) {
|
||||
this.notificationPanels.forEach(panel => {
|
||||
const isActive = panel.getAttribute('data-notification-panel') === tabName;
|
||||
panel.classList.toggle('active', isActive);
|
||||
panel.setAttribute('aria-hidden', isActive ? 'false' : 'true');
|
||||
panel.setAttribute('tabindex', isActive ? '0' : '-1');
|
||||
});
|
||||
}
|
||||
|
||||
if (tabName === 'banners') {
|
||||
this.renderRecentBanners();
|
||||
if (markRead && typeof bannerService.markBannerHistoryViewed === 'function') {
|
||||
bannerService.markBannerHistoryViewed();
|
||||
}
|
||||
}
|
||||
|
||||
this.updateTabBadges();
|
||||
}
|
||||
|
||||
handleNotificationTabKeydown(event) {
|
||||
if (!Array.isArray(this.notificationTabs) || this.notificationTabs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { key } = event;
|
||||
const supportedKeys = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'];
|
||||
|
||||
if (!supportedKeys.includes(key)) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const currentIndex = this.notificationTabs.indexOf(event.currentTarget);
|
||||
if (currentIndex === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
let targetIndex = currentIndex;
|
||||
|
||||
if (key === 'ArrowLeft' || key === 'ArrowUp') {
|
||||
targetIndex = (currentIndex - 1 + this.notificationTabs.length) % this.notificationTabs.length;
|
||||
} else if (key === 'ArrowRight' || key === 'ArrowDown') {
|
||||
targetIndex = (currentIndex + 1) % this.notificationTabs.length;
|
||||
} else if (key === 'Home') {
|
||||
targetIndex = 0;
|
||||
} else if (key === 'End') {
|
||||
targetIndex = this.notificationTabs.length - 1;
|
||||
}
|
||||
|
||||
const nextTab = this.notificationTabs[targetIndex];
|
||||
if (!nextTab) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tabName = nextTab.getAttribute('data-notification-tab');
|
||||
nextTab.focus();
|
||||
this.switchNotificationTab(tabName, { markRead: true });
|
||||
}
|
||||
|
||||
isNotificationModalOpen() {
|
||||
const updateModal = modalManager.getModal('updateModal');
|
||||
return !!(updateModal && updateModal.isOpen);
|
||||
}
|
||||
|
||||
handleBannerHistoryUpdated() {
|
||||
this.updateBadgeVisibility();
|
||||
|
||||
if (this.isNotificationModalOpen() && this.activeNotificationTab === 'banners') {
|
||||
this.renderRecentBanners();
|
||||
}
|
||||
}
|
||||
|
||||
updateTabBadges() {
|
||||
const updatesBadge = document.getElementById('updatesTabBadge');
|
||||
const bannerBadge = document.getElementById('bannerTabBadge');
|
||||
const hasUpdate = this.updateNotificationsEnabled && this.updateAvailable;
|
||||
const unreadBanners = typeof bannerService.getUnreadBannerCount === 'function'
|
||||
? bannerService.getUnreadBannerCount()
|
||||
: 0;
|
||||
|
||||
if (updatesBadge) {
|
||||
updatesBadge.classList.toggle('visible', hasUpdate);
|
||||
updatesBadge.classList.toggle('is-dot', hasUpdate);
|
||||
updatesBadge.textContent = '';
|
||||
}
|
||||
|
||||
if (bannerBadge) {
|
||||
if (unreadBanners > 0) {
|
||||
bannerBadge.textContent = unreadBanners > 9 ? '9+' : unreadBanners.toString();
|
||||
} else {
|
||||
bannerBadge.textContent = '';
|
||||
}
|
||||
bannerBadge.classList.toggle('visible', unreadBanners > 0);
|
||||
bannerBadge.classList.remove('is-dot');
|
||||
}
|
||||
}
|
||||
|
||||
renderRecentBanners() {
|
||||
const list = document.getElementById('bannerHistoryList');
|
||||
const emptyState = document.getElementById('bannerHistoryEmpty');
|
||||
|
||||
if (!list || !emptyState) return;
|
||||
|
||||
const banners = typeof bannerService.getRecentBanners === 'function'
|
||||
? bannerService.getRecentBanners()
|
||||
: [];
|
||||
|
||||
list.innerHTML = '';
|
||||
|
||||
if (!banners.length) {
|
||||
emptyState.style.display = 'block';
|
||||
return;
|
||||
}
|
||||
|
||||
emptyState.style.display = 'none';
|
||||
|
||||
banners.forEach(banner => {
|
||||
const item = document.createElement('li');
|
||||
item.className = 'banner-history-item';
|
||||
|
||||
const title = document.createElement('h4');
|
||||
title.className = 'banner-history-title';
|
||||
title.textContent = banner.title || translate('update.banners.recent', {}, 'Recent banners');
|
||||
item.appendChild(title);
|
||||
|
||||
if (banner.content) {
|
||||
const description = document.createElement('p');
|
||||
description.className = 'banner-history-description';
|
||||
description.textContent = banner.content;
|
||||
item.appendChild(description);
|
||||
}
|
||||
|
||||
const meta = document.createElement('div');
|
||||
meta.className = 'banner-history-meta';
|
||||
|
||||
const status = document.createElement('span');
|
||||
status.className = 'banner-history-status';
|
||||
if (banner.dismissedAt) {
|
||||
status.classList.add('dismissed');
|
||||
const dismissedRelative = this.formatRelativeTime(banner.dismissedAt);
|
||||
status.textContent = translate('update.banners.dismissed', {
|
||||
time: dismissedRelative
|
||||
}, `Dismissed ${dismissedRelative}`);
|
||||
} else {
|
||||
status.classList.add('active');
|
||||
status.textContent = translate('update.banners.active', {}, 'Active');
|
||||
}
|
||||
meta.appendChild(status);
|
||||
|
||||
const shownRelative = this.formatRelativeTime(banner.timestamp);
|
||||
const timestamp = document.createElement('span');
|
||||
timestamp.className = 'banner-history-time';
|
||||
timestamp.textContent = translate('update.banners.shown', {
|
||||
time: shownRelative
|
||||
}, `Shown ${shownRelative}`);
|
||||
meta.appendChild(timestamp);
|
||||
|
||||
item.appendChild(meta);
|
||||
|
||||
if (Array.isArray(banner.actions) && banner.actions.length > 0) {
|
||||
const actionsContainer = document.createElement('div');
|
||||
actionsContainer.className = 'banner-history-actions';
|
||||
|
||||
banner.actions.forEach(action => {
|
||||
if (!action?.url) {
|
||||
return;
|
||||
}
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.className = `banner-history-action banner-history-action-${action.type || 'secondary'}`;
|
||||
link.href = action.url;
|
||||
link.target = '_blank';
|
||||
link.rel = 'noopener noreferrer';
|
||||
link.textContent = action.text || action.url;
|
||||
|
||||
if (action.icon) {
|
||||
const icon = document.createElement('i');
|
||||
icon.className = action.icon;
|
||||
link.prepend(icon);
|
||||
}
|
||||
|
||||
actionsContainer.appendChild(link);
|
||||
});
|
||||
|
||||
if (actionsContainer.children.length > 0) {
|
||||
item.appendChild(actionsContainer);
|
||||
}
|
||||
}
|
||||
|
||||
list.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
formatRelativeTime(timestamp) {
|
||||
if (!timestamp) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const locale = window?.i18n?.getCurrentLocale?.() || navigator.language || 'en';
|
||||
|
||||
try {
|
||||
const formatter = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' });
|
||||
const divisions = [
|
||||
{ amount: 60, unit: 'second' },
|
||||
{ amount: 60, unit: 'minute' },
|
||||
{ amount: 24, unit: 'hour' },
|
||||
{ amount: 7, unit: 'day' },
|
||||
{ amount: 4.34524, unit: 'week' },
|
||||
{ amount: 12, unit: 'month' },
|
||||
{ amount: Infinity, unit: 'year' }
|
||||
];
|
||||
|
||||
let duration = (timestamp - Date.now()) / 1000;
|
||||
|
||||
for (const division of divisions) {
|
||||
if (Math.abs(duration) < division.amount) {
|
||||
return formatter.format(Math.round(duration), division.unit);
|
||||
}
|
||||
duration /= division.amount;
|
||||
}
|
||||
|
||||
return formatter.format(Math.round(duration), 'year');
|
||||
} catch (error) {
|
||||
console.warn('RelativeTimeFormat not available, falling back to locale string.', error);
|
||||
return new Date(timestamp).toLocaleString(locale);
|
||||
}
|
||||
}
|
||||
|
||||
async checkForUpdates({ force = false } = {}) {
|
||||
if (!force && !this.updateNotificationsEnabled) {
|
||||
@@ -167,20 +440,29 @@ export class UpdateService {
|
||||
updateBadgeVisibility() {
|
||||
const updateToggle = document.querySelector('.update-toggle');
|
||||
const updateBadge = document.querySelector('.update-toggle .update-badge');
|
||||
|
||||
const unreadBanners = typeof bannerService.getUnreadBannerCount === 'function'
|
||||
? bannerService.getUnreadBannerCount()
|
||||
: 0;
|
||||
|
||||
if (updateToggle) {
|
||||
updateToggle.title = this.updateNotificationsEnabled && this.updateAvailable
|
||||
? translate('update.updateAvailable')
|
||||
: translate('update.title');
|
||||
let tooltipKey = 'header.actions.notifications';
|
||||
if (this.updateNotificationsEnabled && this.updateAvailable) {
|
||||
tooltipKey = 'update.updateAvailable';
|
||||
} else if (unreadBanners > 0) {
|
||||
tooltipKey = 'update.tabs.messages';
|
||||
}
|
||||
updateToggle.title = translate(tooltipKey);
|
||||
}
|
||||
|
||||
|
||||
// Force updating badges visibility based on current state
|
||||
const shouldShow = this.updateNotificationsEnabled && this.updateAvailable;
|
||||
|
||||
const shouldShowUpdate = this.updateNotificationsEnabled && this.updateAvailable;
|
||||
const shouldShow = shouldShowUpdate || unreadBanners > 0;
|
||||
|
||||
if (updateBadge) {
|
||||
updateBadge.classList.toggle('visible', shouldShow);
|
||||
console.log("Update badge visibility:", shouldShow ? "visible" : "hidden");
|
||||
}
|
||||
|
||||
this.updateTabBadges();
|
||||
}
|
||||
|
||||
updateModalContent() {
|
||||
@@ -190,9 +472,9 @@ export class UpdateService {
|
||||
// Update title based on update availability
|
||||
const headerTitle = modal.querySelector('.update-header h2');
|
||||
if (headerTitle) {
|
||||
headerTitle.textContent = this.updateAvailable ?
|
||||
translate('update.updateAvailable') :
|
||||
translate('update.title');
|
||||
headerTitle.textContent = this.updateAvailable ?
|
||||
translate('update.updateAvailable') :
|
||||
translate('update.notificationsTitle');
|
||||
}
|
||||
|
||||
// Always update version information, even if updateInfo is null
|
||||
@@ -418,23 +700,32 @@ export class UpdateService {
|
||||
|
||||
toggleUpdateModal() {
|
||||
const updateModal = modalManager.getModal('updateModal');
|
||||
|
||||
|
||||
// If modal is already open, just close it
|
||||
if (updateModal && updateModal.isOpen) {
|
||||
modalManager.closeModal('updateModal');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!Array.isArray(this.notificationTabs) || !this.notificationTabs.length) {
|
||||
this.setupNotificationCenter();
|
||||
}
|
||||
|
||||
// Update the modal content immediately with current data
|
||||
this.updateModalContent();
|
||||
|
||||
this.renderRecentBanners();
|
||||
|
||||
// Show the modal with current data
|
||||
modalManager.showModal('updateModal');
|
||||
|
||||
this.switchNotificationTab(this.activeNotificationTab, { markRead: true });
|
||||
|
||||
// Then check for updates in the background
|
||||
this.manualCheckForUpdates().then(() => {
|
||||
// Update the modal content again after the check completes
|
||||
this.updateModalContent();
|
||||
if (this.activeNotificationTab === 'banners' && this.isNotificationModalOpen()) {
|
||||
this.renderRecentBanners();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user