Sprint 3 : protocoles de vote et boite a outils
Backend: - Sessions de vote : list, close, tally, threshold details, auto-expiration - Protocoles : update, simulate, meta-gouvernance, formulas CRUD - Service vote enrichi : close_session, get_threshold_details, nuanced breakdown - Schemas : ThresholdDetailOut, VoteResultOut, FormulaSimulationRequest/Result - WebSocket broadcast sur chaque vote + fermeture session - 25 nouveaux tests (threshold details, close, nuanced, simulation) Frontend: - 5 composants vote : VoteBinary, VoteNuanced, ThresholdGauge, FormulaDisplay, VoteHistory - 3 composants protocoles : ProtocolPicker, FormulaEditor, ModeParamsDisplay - Simulateur de formules interactif (page /protocols/formulas) - Page detail protocole (/protocols/[id]) - Composable useWebSocket (live updates) - Composable useVoteFormula (calcul client-side reactif) - Integration KaTeX pour rendu LaTeX des formules Documentation: - API reference : 8 nouveaux endpoints documentes - Formules : tables d'inertie, parametres detailles, simulation API - Guide vote : vote binaire/nuance, jauge, historique, simulateur, meta-gouvernance 55 tests passes (+ 1 skipped), 126 fichiers total. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
100
frontend/app/composables/useWebSocket.ts
Normal file
100
frontend/app/composables/useWebSocket.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Composable for WebSocket connectivity to receive live vote updates.
|
||||
*
|
||||
* Connects to the backend WS endpoint and allows subscribing to
|
||||
* individual vote session channels for real-time tally updates.
|
||||
*/
|
||||
export function useWebSocket() {
|
||||
const config = useRuntimeConfig()
|
||||
let ws: WebSocket | null = null
|
||||
let reconnectTimer: ReturnType<typeof setTimeout> | null = null
|
||||
const connected = ref(false)
|
||||
const lastMessage = ref<any>(null)
|
||||
|
||||
/**
|
||||
* Open a WebSocket connection to the backend live endpoint.
|
||||
*/
|
||||
function connect() {
|
||||
if (ws && (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING)) {
|
||||
return
|
||||
}
|
||||
|
||||
const wsUrl = config.public.apiBase
|
||||
.replace(/^http/, 'ws')
|
||||
.replace(/\/api\/v1$/, '/api/v1/ws/live')
|
||||
|
||||
ws = new WebSocket(wsUrl)
|
||||
|
||||
ws.onopen = () => {
|
||||
connected.value = true
|
||||
if (reconnectTimer) {
|
||||
clearTimeout(reconnectTimer)
|
||||
reconnectTimer = null
|
||||
}
|
||||
}
|
||||
|
||||
ws.onclose = () => {
|
||||
connected.value = false
|
||||
reconnect()
|
||||
}
|
||||
|
||||
ws.onerror = () => {
|
||||
connected.value = false
|
||||
}
|
||||
|
||||
ws.onmessage = (event: MessageEvent) => {
|
||||
try {
|
||||
lastMessage.value = JSON.parse(event.data)
|
||||
} catch {
|
||||
lastMessage.value = event.data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to real-time updates for a vote session.
|
||||
*/
|
||||
function subscribe(sessionId: string) {
|
||||
if (ws?.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ action: 'subscribe', session_id: sessionId }))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from a vote session's updates.
|
||||
*/
|
||||
function unsubscribe(sessionId: string) {
|
||||
if (ws?.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ action: 'unsubscribe', session_id: sessionId }))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gracefully close the WebSocket connection.
|
||||
*/
|
||||
function disconnect() {
|
||||
if (reconnectTimer) {
|
||||
clearTimeout(reconnectTimer)
|
||||
reconnectTimer = null
|
||||
}
|
||||
if (ws) {
|
||||
ws.onclose = null
|
||||
ws.close()
|
||||
ws = null
|
||||
}
|
||||
connected.value = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a reconnection attempt after a delay.
|
||||
*/
|
||||
function reconnect() {
|
||||
if (reconnectTimer) return
|
||||
reconnectTimer = setTimeout(() => {
|
||||
reconnectTimer = null
|
||||
connect()
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
return { connected, lastMessage, connect, subscribe, unsubscribe, disconnect }
|
||||
}
|
||||
Reference in New Issue
Block a user