Offline-first photo capture app for Nextcloud with: - Camera capture with continuous mode (auto-reopens after each photo) - File browser with fullscreen image gallery, swipe navigation, and rename - Upload queue with background sync engine - Admin panel for Nextcloud user management - Service worker for offline-first caching (v13) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
112 lines
3.3 KiB
JavaScript
112 lines
3.3 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';
|
|
}
|
|
},
|
|
|
|
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;
|