81 lines
2.2 KiB
TypeScript
81 lines
2.2 KiB
TypeScript
// composables/usePoll.ts
|
|
import { ref, onMounted, onUnmounted } from 'vue';
|
|
import * as Y from 'yjs';
|
|
|
|
export const usePoll = () => {
|
|
// Reactive Vue state to drive the UI
|
|
const pollData = ref<Record<string, number>>({});
|
|
const isConnected = ref(false);
|
|
const connectedPeers = ref(1); // 1 = just you initially
|
|
|
|
let ydoc: Y.Doc;
|
|
let provider: any;
|
|
let yMap: Y.Map<number>;
|
|
|
|
onMounted(async () => {
|
|
// 1. Initialize the Yjs document
|
|
ydoc = new Y.Doc();
|
|
|
|
// 2. Connect via WebRTC.
|
|
// 'nuxt-p2p-poll-demo-room' is the shared room name.
|
|
// Anyone using this exact string will sync together.
|
|
const { WebrtcProvider } = await import('y-webrtc');
|
|
provider = new WebrtcProvider('nuxt-p2p-poll-demo-room', ydoc);
|
|
|
|
// Track connection status
|
|
provider.on('synced', (arg: {synced: boolean}) => {
|
|
isConnected.value = arg.synced;
|
|
});
|
|
|
|
provider.on('peers', (data: any) => {
|
|
// data.webrtcPeers contains the connected peer IDs
|
|
connectedPeers.value = data.webrtcPeers.length + 1; // +1 for self
|
|
});
|
|
|
|
// 3. Define our shared state structure. We'll use a Map
|
|
// where the Key is the Poll Option (string) and Value is the Vote Count (number)
|
|
yMap = ydoc.getMap<number>('shared-poll');
|
|
|
|
// 4. Listen for changes from other peers and update our Vue reactive state
|
|
yMap.observe(() => {
|
|
pollData.value = yMap.toJSON();
|
|
});
|
|
|
|
// Initial load in case data already exists in the room
|
|
pollData.value = yMap.toJSON();
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
// Clean up when the component is destroyed
|
|
if (provider) {
|
|
provider.disconnect();
|
|
}
|
|
if (ydoc) {
|
|
ydoc.destroy();
|
|
}
|
|
});
|
|
|
|
// Action: Add a new option to the poll
|
|
const addOption = (optionName: string) => {
|
|
const trimmedName = optionName.trim();
|
|
if (trimmedName && !yMap.has(trimmedName)) {
|
|
yMap.set(trimmedName, 0); // Initialize with 0 votes
|
|
}
|
|
};
|
|
|
|
// Action: Vote for an existing option
|
|
const vote = (optionName: string) => {
|
|
if (yMap.has(optionName)) {
|
|
const currentVotes = yMap.get(optionName) || 0;
|
|
yMap.set(optionName, currentVotes + 1);
|
|
}
|
|
};
|
|
|
|
return {
|
|
pollData,
|
|
isConnected,
|
|
connectedPeers,
|
|
addOption,
|
|
vote
|
|
};
|
|
}; |