// composables/usePoll.ts import { ref, watch, onUnmounted } from 'vue'; import * as Y from 'yjs'; export const usePoll = (pollId: Ref) => { const pollData = ref({}); const isConnected = ref(false); const connectionAttempFailed = ref(false); const connectedPeers = ref(1); let ydoc: Y.Doc | null = null; let provider: any = null; let yMap: Y.Map[]> | null = null; const cleanup = () => { if (provider) provider.disconnect(); if (ydoc) ydoc.destroy(); isConnected.value = false; pollData.value = {}; }; const initPoll = async (id: string) => { cleanup(); // Clear previous session ydoc = new Y.Doc(); // 1. Fetch Snapshot from Nuxt API try { const response = await $fetch<{ update: number[] | null }>(`/api/polls/${id}`); if (response?.update) { Y.applyUpdate(ydoc, new Uint8Array(response.update)); } } catch (err) { console.error('Persistence fetch failed', err); } yMap = ydoc.getMap[]>('shared-poll'); // 2. Local State Sync yMap.observe(() => { pollData.value = yMap!.toJSON(); saveStateToServer(id); }); pollData.value = yMap.toJSON(); // 3. P2P Connection const { WebrtcProvider } = await import('y-webrtc'); provider = new WebrtcProvider(`nuxt-p2p-${id}`, ydoc, { signaling: ["ws:localhost:4444", "ws:lynxpi.ddns.net:4444"] }); provider.on('synced', (arg: {synced: boolean}) => isConnected.value = arg.synced); provider.on('peers', (data: any) => connectedPeers.value = data.webrtcPeers.length + 1); }; const saveStateToServer = async (id: string) => { if (!ydoc) return; const stateUpdate = Y.encodeStateAsUpdate(ydoc); await $fetch(`/api/polls/${id}`, { method: 'POST', body: { update: Array.from(stateUpdate) } }).catch(() => {}); }; // Watch for ID changes (e.g., user clicks a link or goes back) watch(pollId, (newId) => { if (newId && import.meta.client) { initPoll(newId); } else { cleanup(); } }, { immediate: true }); onUnmounted(cleanup); const addOption = (optionName: string) => { if (yMap && !yMap.has(optionName)) yMap.set(optionName, []); }; const vote = (optionName: string, uuid: string) => { if (yMap?.has(optionName)) { var voteData : SignedData[] | undefined = yMap.get(optionName) if(voteData != undefined){ var unsignedVoteData : VoteData = { userid: uuid, timestamp: new Date().toISOString() } var newVote : SignedData = { data: unsignedVoteData, signature: "", } voteData?.push(newVote) yMap.set(optionName, voteData); } } }; return { pollData, isConnected, connectionAttempFailed, connectedPeers, addOption, vote }; };