Implement periodic health checks every 15 seconds to monitor connection status, pausing when the tab is hidden and resuming on visibility. Update UI bindings to use data store for connection status instead of global store. Add destroy method to clean up timers on component teardown.
138 lines
5.3 KiB
JavaScript
138 lines
5.3 KiB
JavaScript
/**
|
|
* App Initialization (Non-module version)
|
|
* This must load BEFORE Alpine initializes
|
|
*/
|
|
|
|
document.addEventListener('alpine:init', () => {
|
|
// App component registration
|
|
|
|
// Main App Controller
|
|
Alpine.data('app', () => ({
|
|
// Re-expose store properties for easier access in navbar
|
|
get connectionStatus() {
|
|
return Alpine.store('data').connectionStatus;
|
|
},
|
|
get loading() {
|
|
return Alpine.store('data').loading;
|
|
},
|
|
|
|
init() {
|
|
// App component initialization
|
|
|
|
// Theme setup
|
|
document.documentElement.setAttribute('data-theme', 'black');
|
|
document.documentElement.classList.add('dark');
|
|
|
|
// Chart Defaults
|
|
if (typeof Chart !== 'undefined') {
|
|
Chart.defaults.color = window.utils.getThemeColor('--color-text-dim');
|
|
Chart.defaults.borderColor = window.utils.getThemeColor('--color-space-border');
|
|
Chart.defaults.font.family = '"JetBrains Mono", monospace';
|
|
}
|
|
|
|
// Start Data Polling
|
|
this.startAutoRefresh();
|
|
document.addEventListener('refresh-interval-changed', () => this.startAutoRefresh());
|
|
|
|
// Initial Data Fetch (separate from health check)
|
|
Alpine.store('data').fetchData();
|
|
},
|
|
|
|
refreshTimer: null,
|
|
isTabVisible: true,
|
|
|
|
fetchData() {
|
|
Alpine.store('data').fetchData();
|
|
},
|
|
|
|
startAutoRefresh() {
|
|
if (this.refreshTimer) clearInterval(this.refreshTimer);
|
|
const baseInterval = parseInt(Alpine.store('settings').refreshInterval);
|
|
if (baseInterval > 0) {
|
|
// Setup visibility change listener (only once)
|
|
if (!this._visibilitySetup) {
|
|
this._visibilitySetup = true;
|
|
document.addEventListener('visibilitychange', () => {
|
|
this.isTabVisible = !document.hidden;
|
|
if (this.isTabVisible) {
|
|
// Tab became visible - fetch immediately and restart timer
|
|
Alpine.store('data').fetchData();
|
|
this.startAutoRefresh();
|
|
}
|
|
});
|
|
}
|
|
|
|
// Schedule next refresh with jitter
|
|
const scheduleNext = () => {
|
|
// Add ±20% random jitter to prevent synchronized requests
|
|
const jitter = (Math.random() - 0.5) * 0.4; // -0.2 to +0.2
|
|
const interval = baseInterval * (1 + jitter);
|
|
|
|
// Slow down when tab is hidden (reduce frequency by 3x)
|
|
const actualInterval = this.isTabVisible
|
|
? interval
|
|
: interval * 3;
|
|
|
|
this.refreshTimer = setTimeout(() => {
|
|
Alpine.store('data').fetchData();
|
|
scheduleNext(); // Reschedule with new jitter
|
|
}, actualInterval * 1000);
|
|
};
|
|
|
|
scheduleNext();
|
|
}
|
|
},
|
|
|
|
// Translation helper for modal (not in a component scope)
|
|
t(key) {
|
|
return Alpine.store('global').t(key);
|
|
},
|
|
|
|
// Add account handler for modal
|
|
async addAccountWeb(reAuthEmail = null) {
|
|
const password = Alpine.store('global').webuiPassword;
|
|
try {
|
|
const urlPath = reAuthEmail
|
|
? `/api/auth/url?email=${encodeURIComponent(reAuthEmail)}`
|
|
: '/api/auth/url';
|
|
|
|
const { response, newPassword } = await window.utils.request(urlPath, {}, password);
|
|
if (newPassword) Alpine.store('global').webuiPassword = newPassword;
|
|
|
|
const data = await response.json();
|
|
|
|
if (data.status === 'ok') {
|
|
const width = 600;
|
|
const height = 700;
|
|
const left = (screen.width - width) / 2;
|
|
const top = (screen.height - height) / 2;
|
|
|
|
window.open(
|
|
data.url,
|
|
'google_oauth',
|
|
`width=${width},height=${height},top=${top},left=${left},scrollbars=yes`
|
|
);
|
|
|
|
const messageHandler = (event) => {
|
|
if (event.data?.type === 'oauth-success') {
|
|
const action = reAuthEmail ? 're-authenticated' : 'added';
|
|
Alpine.store('global').showToast(`Account ${event.data.email} ${action} successfully`, 'success');
|
|
Alpine.store('data').fetchData();
|
|
|
|
const modal = document.getElementById('add_account_modal');
|
|
if (modal) modal.close();
|
|
}
|
|
};
|
|
|
|
window.addEventListener('message', messageHandler);
|
|
setTimeout(() => window.removeEventListener('message', messageHandler), 300000);
|
|
} else {
|
|
Alpine.store('global').showToast(data.error || 'Failed to get auth URL', 'error');
|
|
}
|
|
} catch (e) {
|
|
Alpine.store('global').showToast('Failed to start OAuth flow: ' + e.message, 'error');
|
|
}
|
|
}
|
|
}));
|
|
});
|