| |
| |
| |
| |
|
|
| (function() { |
| 'use strict'; |
| |
| const OFFLINE_QUEUE_KEY = 'bbplease_offline_feedback_queue'; |
| const SYNC_INTERVAL = 30000; |
| let syncIntervalId = null; |
| |
| |
| |
| |
| function init() { |
| |
| window.addEventListener('online', handleOnline); |
| window.addEventListener('offline', handleOffline); |
| |
| |
| if (navigator.onLine) { |
| syncPendingFeedback(); |
| startSyncInterval(); |
| } else { |
| handleOffline(); |
| } |
| |
| |
| syncPendingFeedback(); |
| } |
| |
| |
| |
| |
| function handleOnline() { |
| console.log('๐ถ Online - syncing pending feedback...'); |
| syncPendingFeedback(); |
| startSyncInterval(); |
| |
| |
| const queue = getQueue(); |
| if (queue.length > 0) { |
| showToast(`Syncing ${queue.length} pending feedback...`); |
| } |
| } |
| |
| |
| |
| |
| function handleOffline() { |
| console.log('๐ด Offline - feedback will be queued'); |
| stopSyncInterval(); |
| showToast('You are offline. Feedback will be saved and synced when online.'); |
| } |
| |
| |
| |
| |
| function startSyncInterval() { |
| if (syncIntervalId) { |
| clearInterval(syncIntervalId); |
| } |
| syncIntervalId = setInterval(() => { |
| if (navigator.onLine) { |
| syncPendingFeedback(); |
| } |
| }, SYNC_INTERVAL); |
| } |
| |
| |
| |
| |
| function stopSyncInterval() { |
| if (syncIntervalId) { |
| clearInterval(syncIntervalId); |
| syncIntervalId = null; |
| } |
| } |
| |
| |
| |
| |
| function getQueue() { |
| try { |
| const queueJson = localStorage.getItem(OFFLINE_QUEUE_KEY); |
| return queueJson ? JSON.parse(queueJson) : []; |
| } catch (e) { |
| console.error('Error reading offline queue:', e); |
| return []; |
| } |
| } |
| |
| |
| |
| |
| function saveQueue(queue) { |
| try { |
| localStorage.setItem(OFFLINE_QUEUE_KEY, JSON.stringify(queue)); |
| } catch (e) { |
| console.error('Error saving offline queue:', e); |
| } |
| } |
| |
| |
| |
| |
| function queueFeedback(feedbackData) { |
| const queue = getQueue(); |
| const feedbackItem = { |
| ...feedbackData, |
| queued_at: new Date().toISOString(), |
| id: feedbackData.audio_id || Date.now().toString() |
| }; |
| queue.push(feedbackItem); |
| saveQueue(queue); |
| console.log('๐ฆ Feedback queued offline:', feedbackItem.id); |
| return feedbackItem.id; |
| } |
| |
| |
| |
| |
| function removeFromQueue(feedbackId) { |
| const queue = getQueue(); |
| const filtered = queue.filter(item => item.id !== feedbackId); |
| saveQueue(filtered); |
| return queue.length - filtered.length; |
| } |
| |
| |
| |
| |
| async function syncPendingFeedback() { |
| if (!navigator.onLine) { |
| return { synced: 0, failed: 0 }; |
| } |
| |
| const queue = getQueue(); |
| if (queue.length === 0) { |
| return { synced: 0, failed: 0 }; |
| } |
| |
| console.log(`๐ Syncing ${queue.length} queued feedback items...`); |
| |
| let synced = 0; |
| let failed = 0; |
| const failedItems = []; |
| |
| for (const item of queue) { |
| try { |
| const url = (window.APP_CONFIG && window.APP_CONFIG.getApiUrl) |
| ? window.APP_CONFIG.getApiUrl('/feedback') |
| : '/feedback'; |
| |
| const response = await fetch(url, { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| audio_id: item.audio_id, |
| predicted_label: item.predicted_label, |
| correct_label: item.correct_label, |
| is_correct: item.is_correct |
| }) |
| }); |
| |
| if (response.ok) { |
| removeFromQueue(item.id); |
| synced++; |
| console.log('โ
Synced feedback:', item.id); |
| } else { |
| failed++; |
| failedItems.push(item); |
| console.warn('โ Failed to sync feedback:', item.id, response.status); |
| } |
| } catch (error) { |
| failed++; |
| failedItems.push(item); |
| console.error('โ Error syncing feedback:', item.id, error); |
| } |
| } |
| |
| |
| if (failed > 0 && synced > 0) { |
| |
| const remaining = queue.filter(item => |
| failedItems.some(failed => failed.id === item.id) |
| ); |
| saveQueue(remaining); |
| } |
| |
| if (synced > 0) { |
| console.log(`โ
Synced ${synced} feedback items`); |
| if (typeof showToast === 'function') { |
| showToast(`Synced ${synced} feedback items`); |
| } |
| } |
| |
| return { synced, failed }; |
| } |
| |
| |
| |
| |
| function isOnline() { |
| return navigator.onLine !== false; |
| } |
| |
| |
| |
| |
| function getQueueStatus() { |
| const queue = getQueue(); |
| return { |
| count: queue.length, |
| items: queue, |
| isOnline: isOnline() |
| }; |
| } |
| |
| |
| |
| |
| function clearQueue() { |
| saveQueue([]); |
| console.log('๐๏ธ Offline queue cleared'); |
| } |
| |
| |
| window.OfflineManager = { |
| init: init, |
| queueFeedback: queueFeedback, |
| syncPendingFeedback: syncPendingFeedback, |
| getQueueStatus: getQueueStatus, |
| clearQueue: clearQueue, |
| isOnline: isOnline |
| }; |
| |
| |
| if (document.readyState === 'loading') { |
| document.addEventListener('DOMContentLoaded', init); |
| } else { |
| init(); |
| } |
| })(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|