Apply full refactor updates plus pipeline/email UX confirmations
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m33s
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m33s
This commit is contained in:
@@ -1,130 +1,130 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef, useCallback, useState } from 'react'
|
||||
|
||||
export interface VoteUpdate {
|
||||
projectId: string
|
||||
totalVotes: number
|
||||
averageScore: number | null
|
||||
latestVote: { score: number; isAudienceVote: boolean; votedAt: string } | null
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
export interface AudienceVoteUpdate {
|
||||
projectId: string
|
||||
audienceVotes: number
|
||||
audienceAverage: number | null
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
export interface SessionStatusUpdate {
|
||||
status: string
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
export interface ProjectChangeUpdate {
|
||||
projectId: string | null
|
||||
projectIndex: number
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
interface SSECallbacks {
|
||||
onVoteUpdate?: (data: VoteUpdate) => void
|
||||
onAudienceVote?: (data: AudienceVoteUpdate) => void
|
||||
onSessionStatus?: (data: SessionStatusUpdate) => void
|
||||
onProjectChange?: (data: ProjectChangeUpdate) => void
|
||||
onConnected?: () => void
|
||||
onError?: (error: Event) => void
|
||||
}
|
||||
|
||||
export function useLiveVotingSSE(
|
||||
sessionId: string | null,
|
||||
callbacks: SSECallbacks
|
||||
) {
|
||||
const [isConnected, setIsConnected] = useState(false)
|
||||
const eventSourceRef = useRef<EventSource | null>(null)
|
||||
const callbacksRef = useRef(callbacks)
|
||||
callbacksRef.current = callbacks
|
||||
|
||||
const connect = useCallback(() => {
|
||||
if (!sessionId) return
|
||||
|
||||
// Close any existing connection
|
||||
if (eventSourceRef.current) {
|
||||
eventSourceRef.current.close()
|
||||
}
|
||||
|
||||
const baseUrl = typeof window !== 'undefined' ? window.location.origin : ''
|
||||
const url = `${baseUrl}/api/live-voting/stream?sessionId=${sessionId}`
|
||||
const es = new EventSource(url)
|
||||
eventSourceRef.current = es
|
||||
|
||||
es.addEventListener('connected', () => {
|
||||
setIsConnected(true)
|
||||
callbacksRef.current.onConnected?.()
|
||||
})
|
||||
|
||||
es.addEventListener('vote_update', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data) as VoteUpdate
|
||||
callbacksRef.current.onVoteUpdate?.(data)
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
})
|
||||
|
||||
es.addEventListener('audience_vote', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data) as AudienceVoteUpdate
|
||||
callbacksRef.current.onAudienceVote?.(data)
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
})
|
||||
|
||||
es.addEventListener('session_status', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data) as SessionStatusUpdate
|
||||
callbacksRef.current.onSessionStatus?.(data)
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
})
|
||||
|
||||
es.addEventListener('project_change', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data) as ProjectChangeUpdate
|
||||
callbacksRef.current.onProjectChange?.(data)
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
})
|
||||
|
||||
es.onerror = (event) => {
|
||||
setIsConnected(false)
|
||||
callbacksRef.current.onError?.(event)
|
||||
|
||||
// Auto-reconnect after 3 seconds
|
||||
setTimeout(() => {
|
||||
if (eventSourceRef.current === es) {
|
||||
connect()
|
||||
}
|
||||
}, 3000)
|
||||
}
|
||||
}, [sessionId])
|
||||
|
||||
const disconnect = useCallback(() => {
|
||||
if (eventSourceRef.current) {
|
||||
eventSourceRef.current.close()
|
||||
eventSourceRef.current = null
|
||||
setIsConnected(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
connect()
|
||||
return () => disconnect()
|
||||
}, [connect, disconnect])
|
||||
|
||||
return { isConnected, reconnect: connect, disconnect }
|
||||
}
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef, useCallback, useState } from 'react'
|
||||
|
||||
export interface VoteUpdate {
|
||||
projectId: string
|
||||
totalVotes: number
|
||||
averageScore: number | null
|
||||
latestVote: { score: number; isAudienceVote: boolean; votedAt: string } | null
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
export interface AudienceVoteUpdate {
|
||||
projectId: string
|
||||
audienceVotes: number
|
||||
audienceAverage: number | null
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
export interface SessionStatusUpdate {
|
||||
status: string
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
export interface ProjectChangeUpdate {
|
||||
projectId: string | null
|
||||
projectIndex: number
|
||||
timestamp: string
|
||||
}
|
||||
|
||||
interface SSECallbacks {
|
||||
onVoteUpdate?: (data: VoteUpdate) => void
|
||||
onAudienceVote?: (data: AudienceVoteUpdate) => void
|
||||
onSessionStatus?: (data: SessionStatusUpdate) => void
|
||||
onProjectChange?: (data: ProjectChangeUpdate) => void
|
||||
onConnected?: () => void
|
||||
onError?: (error: Event) => void
|
||||
}
|
||||
|
||||
export function useLiveVotingSSE(
|
||||
sessionId: string | null,
|
||||
callbacks: SSECallbacks
|
||||
) {
|
||||
const [isConnected, setIsConnected] = useState(false)
|
||||
const eventSourceRef = useRef<EventSource | null>(null)
|
||||
const callbacksRef = useRef(callbacks)
|
||||
callbacksRef.current = callbacks
|
||||
|
||||
const connect = useCallback(() => {
|
||||
if (!sessionId) return
|
||||
|
||||
// Close any existing connection
|
||||
if (eventSourceRef.current) {
|
||||
eventSourceRef.current.close()
|
||||
}
|
||||
|
||||
const baseUrl = typeof window !== 'undefined' ? window.location.origin : ''
|
||||
const url = `${baseUrl}/api/live-voting/stream?sessionId=${sessionId}`
|
||||
const es = new EventSource(url)
|
||||
eventSourceRef.current = es
|
||||
|
||||
es.addEventListener('connected', () => {
|
||||
setIsConnected(true)
|
||||
callbacksRef.current.onConnected?.()
|
||||
})
|
||||
|
||||
es.addEventListener('vote_update', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data) as VoteUpdate
|
||||
callbacksRef.current.onVoteUpdate?.(data)
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
})
|
||||
|
||||
es.addEventListener('audience_vote', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data) as AudienceVoteUpdate
|
||||
callbacksRef.current.onAudienceVote?.(data)
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
})
|
||||
|
||||
es.addEventListener('session_status', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data) as SessionStatusUpdate
|
||||
callbacksRef.current.onSessionStatus?.(data)
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
})
|
||||
|
||||
es.addEventListener('project_change', (event) => {
|
||||
try {
|
||||
const data = JSON.parse(event.data) as ProjectChangeUpdate
|
||||
callbacksRef.current.onProjectChange?.(data)
|
||||
} catch {
|
||||
// Ignore parse errors
|
||||
}
|
||||
})
|
||||
|
||||
es.onerror = (event) => {
|
||||
setIsConnected(false)
|
||||
callbacksRef.current.onError?.(event)
|
||||
|
||||
// Auto-reconnect after 3 seconds
|
||||
setTimeout(() => {
|
||||
if (eventSourceRef.current === es) {
|
||||
connect()
|
||||
}
|
||||
}, 3000)
|
||||
}
|
||||
}, [sessionId])
|
||||
|
||||
const disconnect = useCallback(() => {
|
||||
if (eventSourceRef.current) {
|
||||
eventSourceRef.current.close()
|
||||
eventSourceRef.current = null
|
||||
setIsConnected(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
connect()
|
||||
return () => disconnect()
|
||||
}, [connect, disconnect])
|
||||
|
||||
return { isConnected, reconnect: connect, disconnect }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user