// resources/js/composables/useSyncData.js import { onMounted, onUnmounted } from 'vue'; import { useSyncStore } from '../stores/syncState.js'; /** * Composable to handle real-time data synchronization using SSE or polling. */ export function useSyncData() { const syncStore = useSyncStore(); let eventSource = null; const intervals = {}; /** * Start Server-Sent Events listener. * @param {string} endpoint */ const startSSE = (endpoint = '/api/sync/events') => { if (typeof EventSource === 'undefined') { console.warn('[Sync] EventSource not supported by browser.'); return false; } if (eventSource) eventSource.close(); eventSource = new EventSource(endpoint); eventSource.onmessage = (event) => { try { const data = JSON.parse(event.data); handleSyncMessage(data); } catch (e) { console.error('[Sync] Failed to parse SSE message:', e); } }; eventSource.onerror = (err) => { console.error('[Sync] EventSource connection error, closing.'); eventSource.close(); eventSource = null; }; return true; }; /** * Handle incoming synchronization messages. * @param {object} payload */ const handleSyncMessage = (payload) => { const { type, key, data } = payload; syncStore.updateStatus(key || type, 'synced'); // Notify Pinia or other stores about updates // For example: // switch (type) { // case 'user_update': // useUserStore().fetchCurrentUser(); // break; // } }; /** * Start interval-based polling as a fallback or for specific keys. * @param {string} key * @param {string} url * @param {number} intervalMs * @param {string} method * @param {object|null} data */ const startPolling = (key, url, intervalMs = 60000, method = 'GET', data = null) => { if (intervals[key]) return; const pollFunc = async () => { try { syncStore.updateStatus(key, 'syncing'); const options = { method: method, headers: { 'Accept': 'application/json' } }; if (data && (method === 'POST' || method === 'PUT')) { options.headers['Content-Type'] = 'application/json'; options.body = JSON.stringify(data); } const response = await fetch(url, options); if (!response.ok) throw new Error(`Poll failed: ${response.status}`); const result = await response.json(); handleSyncMessage({ type: 'poll_result', key, data: result }); } catch (err) { syncStore.setError(key, err.message); } }; // Run immediately then start interval pollFunc(); intervals[key] = setInterval(pollFunc, intervalMs); }; /** * Stop polling for a specific key. * @param {string} key */ const stopPolling = (key) => { if (intervals[key]) { clearInterval(intervals[key]); delete intervals[key]; } }; // Cleanup on unmount onUnmounted(() => { if (eventSource) eventSource.close(); Object.values(intervals).forEach(clearInterval); }); return { startSSE, startPolling, stopPolling }; }