Files
nextsnap/app/static/js/app.js
kamaji 36a53301a7 Show offline mode UI: hide online-only nav items, show offline label
When offline, hide Files and Admin from the bottom nav and display
"Offline Mode" next to the status dot. The SW offline fallback page
also only shows Capture and Queue nav items. Online-only pages
(browser, admin, reviewer) are never cached and go straight to the
offline fallback when the network is unavailable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-07 23:00:34 -06:00

123 lines
3.7 KiB
JavaScript

// NextSnap - Main application logic
'use strict';
const NextSnap = {
version: '1.0.0',
isOnline: navigator.onLine,
init() {
console.log(`NextSnap v${this.version} initializing...`);
this.setupConnectivityMonitoring();
this.setupServiceWorkerMessaging();
this.checkHealth();
},
setupConnectivityMonitoring() {
// Update online status on events
window.addEventListener('online', () => {
console.log('Network: Online');
this.isOnline = true;
this.updateConnectivityIndicator();
// Sync is handled by SyncEngine's own online listener in sync.js
});
window.addEventListener('offline', () => {
console.log('Network: Offline');
this.isOnline = false;
this.updateConnectivityIndicator();
});
// Initial status
this.updateConnectivityIndicator();
},
updateConnectivityIndicator() {
const indicator = document.querySelector('.connectivity-indicator');
if (!indicator) return;
indicator.classList.remove('online', 'offline', 'syncing');
if (this.isOnline) {
indicator.classList.add('online');
indicator.title = 'Online';
} else {
indicator.classList.add('offline');
indicator.title = 'Offline';
}
// Show/hide "Offline Mode" label
const offlineLabel = document.getElementById('offlineLabel');
if (offlineLabel) {
offlineLabel.style.display = this.isOnline ? 'none' : '';
}
// Show/hide online-only nav items (Files, Admin)
document.querySelectorAll('[data-online-only]').forEach(el => {
el.style.display = this.isOnline ? '' : 'none';
});
},
setSyncingStatus(isSyncing) {
const indicator = document.querySelector('.connectivity-indicator');
if (!indicator) return;
if (isSyncing) {
indicator.classList.add('syncing');
indicator.title = 'Syncing...';
} else {
indicator.classList.remove('syncing');
indicator.title = this.isOnline ? 'Online' : 'Offline';
}
},
setupServiceWorkerMessaging() {
if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
// Listen for messages from service worker
navigator.serviceWorker.addEventListener('message', (event) => {
console.log('Message from SW:', event.data);
});
}
},
// Force service worker update
async updateServiceWorker() {
if ('serviceWorker' in navigator) {
const registration = await navigator.serviceWorker.getRegistration();
if (registration) {
await registration.update();
console.log('Service worker update check triggered');
}
}
},
// Clear all caches (for debugging)
async clearCache() {
if ('serviceWorker' in navigator && navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage({
type: 'CLEAR_CACHE'
});
console.log('Cache clear requested');
}
},
async checkHealth() {
try {
const response = await fetch('/api/health');
const data = await response.json();
console.log('Health check:', data);
} catch (error) {
console.error('Health check failed:', error);
}
}
};
// Initialize when DOM is ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => NextSnap.init());
} else {
NextSnap.init();
}
// Make NextSnap globally available
window.NextSnap = NextSnap;