138 lines
4.9 KiB
JavaScript
138 lines
4.9 KiB
JavaScript
export class TimeoutError extends Error {
|
|
constructor(message) {
|
|
super(message);
|
|
this.name = 'TimeoutError';
|
|
}
|
|
}
|
|
|
|
export function _(key, substitutions) {
|
|
try {
|
|
return chrome.i18n.getMessage(key, substitutions);
|
|
} catch (e) {
|
|
return key;
|
|
}
|
|
}
|
|
|
|
export function showNotification(message, type = 'info', duration = 3000) {
|
|
const notificationContainer = document.getElementById('notification-container');
|
|
const template = document.getElementById('notification-template');
|
|
|
|
if (!notificationContainer || !template) {
|
|
alert(`${type.toUpperCase()}: ${message}`);
|
|
return;
|
|
}
|
|
|
|
const notificationClone = template.querySelector('.notification').cloneNode(true);
|
|
notificationClone.className = `notification ${type}`;
|
|
|
|
const icon = notificationClone.querySelector('i');
|
|
const span = notificationClone.querySelector('span');
|
|
span.textContent = message;
|
|
|
|
let iconClass = 'fa-info-circle';
|
|
if (type === 'success') iconClass = 'fa-check-circle';
|
|
else if (type === 'error') iconClass = 'fa-exclamation-triangle';
|
|
else if (type === 'warning') iconClass = 'fa-exclamation-circle';
|
|
icon.className = `fas ${iconClass}`;
|
|
|
|
notificationContainer.prepend(notificationClone);
|
|
|
|
setTimeout(() => {
|
|
notificationClone.classList.add('show');
|
|
}, 50);
|
|
|
|
setTimeout(() => {
|
|
notificationClone.classList.remove('show');
|
|
notificationClone.addEventListener('transitionend', () => notificationClone.remove(), { once: true });
|
|
}, duration);
|
|
}
|
|
|
|
export function debounce(func, wait) {
|
|
let timeout;
|
|
return function executedFunction(...args) {
|
|
const later = () => {
|
|
clearTimeout(timeout);
|
|
func.apply(this, args);
|
|
};
|
|
clearTimeout(timeout);
|
|
timeout = setTimeout(later, wait);
|
|
};
|
|
}
|
|
|
|
export function getRelativeTime(timestamp) {
|
|
const now = Date.now();
|
|
const diffSeconds = Math.round((now - timestamp) / 1000);
|
|
const diffMinutes = Math.round(diffSeconds / 60);
|
|
const diffHours = Math.round(diffMinutes / 60);
|
|
const diffDays = Math.round(diffHours / 24);
|
|
|
|
if (diffSeconds < 60) return _('relativeTime_justNow');
|
|
if (diffMinutes < 60) return _('relativeTime_minutesAgo', String(diffMinutes));
|
|
if (diffHours < 24) return _('relativeTime_hoursAgo', String(diffHours));
|
|
if (diffDays === 1) return _('relativeTime_yesterday');
|
|
if (diffDays < 7) return _('relativeTime_daysAgo', String(diffDays));
|
|
|
|
return (new Date(timestamp)).toLocaleDateString(_('appLocaleCode'), { day: 'numeric', month: 'short' });
|
|
}
|
|
|
|
export function mostrarSpinner() {
|
|
const spinner = document.getElementById('spinner');
|
|
if (spinner) spinner.style.display = 'block';
|
|
}
|
|
|
|
export function ocultarSpinner() {
|
|
const spinner = document.getElementById('spinner');
|
|
if (spinner) spinner.style.display = 'none';
|
|
}
|
|
|
|
export function logToConsole(message) {
|
|
const consoleOutput = document.getElementById('consoleOutput');
|
|
if (!consoleOutput) return;
|
|
|
|
const logEntry = document.createElement('div');
|
|
logEntry.className = 'console-log-entry';
|
|
const time = new Date().toLocaleTimeString(_('appLocaleCode'), { hour12: false });
|
|
|
|
const timeSpan = `<span class="log-time">[${time}]</span>`;
|
|
const messageSpan = `<span class="log-message">${message.replace(/</g, "<").replace(/>/g, ">")}</span>`;
|
|
|
|
if (message.toLowerCase().includes("error")) {
|
|
logEntry.classList.add('log-error');
|
|
} else if (message.toLowerCase().includes("advertencia") || message.toLowerCase().includes("warning") || message.toLowerCase().includes("pendiente")) {
|
|
logEntry.classList.add('log-warning');
|
|
} else if (message.toLowerCase().includes("success") || message.toLowerCase().includes("finalizado") || message.toLowerCase().includes("éxito") || message.toLowerCase().includes("limpiadas")) {
|
|
logEntry.classList.add('log-success');
|
|
}
|
|
|
|
logEntry.innerHTML = `${timeSpan} ${messageSpan}`;
|
|
consoleOutput.appendChild(logEntry);
|
|
consoleOutput.scrollTop = consoleOutput.scrollHeight;
|
|
}
|
|
|
|
export function emitirEventoActualizacion() {
|
|
const event = new CustomEvent('indexedDBUpdated');
|
|
window.dispatchEvent(event);
|
|
}
|
|
|
|
export async function fetchWithTimeout(url, options, timeout = 7000) {
|
|
return new Promise((resolve, reject) => {
|
|
const controller = new AbortController();
|
|
const signal = controller.signal;
|
|
|
|
const timer = setTimeout(() => {
|
|
controller.abort();
|
|
reject(new TimeoutError(`Timeout(${timeout}ms)`));
|
|
}, timeout);
|
|
|
|
fetch(url, { ...options, signal })
|
|
.then(response => { clearTimeout(timer); resolve(response); })
|
|
.catch(err => {
|
|
clearTimeout(timer);
|
|
if (err.name === 'AbortError') {
|
|
reject(new TimeoutError(`Timeout(${timeout}ms)`));
|
|
} else {
|
|
reject(err);
|
|
}
|
|
});
|
|
});
|
|
} |