1
0
mirror of https://github.com/jcwimer/wrestlingApp synced 2026-03-25 01:14:43 +00:00
Files
wrestlingdev.com/app/assets/javascripts/controllers/match_data_controller.js

384 lines
12 KiB
JavaScript

import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [
"w1Stat", "w2Stat", "statusIndicator"
]
static values = {
tournamentId: Number,
boutNumber: Number,
matchId: Number
}
connect() {
console.log("Match data controller connected")
this.w1 = {
name: "w1",
stats: "",
updated_at: null,
timers: {
"injury": { time: 0, startTime: null, interval: null },
"blood": { time: 0, startTime: null, interval: null }
}
}
this.w2 = {
name: "w2",
stats: "",
updated_at: null,
timers: {
"injury": { time: 0, startTime: null, interval: null },
"blood": { time: 0, startTime: null, interval: null }
}
}
// Initial values
this.updateJsValues()
// Set up debounced handlers for text areas
this.debouncedW1Handler = this.debounce((el) => this.handleTextAreaInput(el, this.w1), 400)
this.debouncedW2Handler = this.debounce((el) => this.handleTextAreaInput(el, this.w2), 400)
// Set up text area event listeners
this.w1StatTarget.addEventListener('input', (event) => this.debouncedW1Handler(event.target))
this.w2StatTarget.addEventListener('input', (event) => this.debouncedW2Handler(event.target))
// Initialize from local storage
this.initializeFromLocalStorage()
// Setup ActionCable
if (this.matchIdValue) {
this.setupSubscription(this.matchIdValue)
}
}
disconnect() {
this.cleanupSubscription()
}
// Match stats core functionality
updateStats(wrestler, text) {
if (!wrestler) {
console.error("updateStats called with undefined wrestler")
return
}
wrestler.stats += text + " "
wrestler.updated_at = new Date().toISOString()
this.updateHtmlValues()
this.saveToLocalStorage(wrestler)
// Send the update via Action Cable if subscribed
if (this.matchSubscription) {
let payload = {}
if (wrestler.name === 'w1') payload.new_w1_stat = wrestler.stats
else if (wrestler.name === 'w2') payload.new_w2_stat = wrestler.stats
if (Object.keys(payload).length > 0) {
console.log('[ActionCable] updateStats performing send_stat:', payload)
this.matchSubscription.perform('send_stat', payload)
}
} else {
console.warn('[ActionCable] updateStats called but matchSubscription is null.')
}
}
// Specific methods for updating each wrestler
updateW1Stats(event) {
const text = event.currentTarget.dataset.matchDataText || ''
this.updateStats(this.w1, text)
}
updateW2Stats(event) {
const text = event.currentTarget.dataset.matchDataText || ''
this.updateStats(this.w2, text)
}
// End period action
endPeriod() {
this.updateStats(this.w1, '|End Period|')
this.updateStats(this.w2, '|End Period|')
}
handleTextAreaInput(textAreaElement, wrestler) {
const newValue = textAreaElement.value
console.log(`Text area input detected for ${wrestler.name}:`, newValue.substring(0, 50) + "...")
// Update the internal JS object
wrestler.stats = newValue
wrestler.updated_at = new Date().toISOString()
// Save to localStorage
this.saveToLocalStorage(wrestler)
// Send the update via Action Cable if subscribed
if (this.matchSubscription) {
let payload = {}
if (wrestler.name === 'w1') {
payload.new_w1_stat = wrestler.stats
} else if (wrestler.name === 'w2') {
payload.new_w2_stat = wrestler.stats
}
if (Object.keys(payload).length > 0) {
console.log('[ActionCable] Performing send_stat from textarea with payload:', payload)
this.matchSubscription.perform('send_stat', payload)
}
}
}
// Timer functions
startTimer(wrestler, timerKey) {
const timer = wrestler.timers[timerKey]
if (timer.interval) return // Prevent multiple intervals
timer.startTime = Date.now()
timer.interval = setInterval(() => {
const elapsedSeconds = Math.floor((Date.now() - timer.startTime) / 1000)
this.updateTimerDisplay(wrestler, timerKey, timer.time + elapsedSeconds)
}, 1000)
}
stopTimer(wrestler, timerKey) {
const timer = wrestler.timers[timerKey]
if (!timer.interval || !timer.startTime) return
clearInterval(timer.interval)
const elapsedSeconds = Math.floor((Date.now() - timer.startTime) / 1000)
timer.time += elapsedSeconds
timer.interval = null
timer.startTime = null
this.saveToLocalStorage(wrestler)
this.updateTimerDisplay(wrestler, timerKey, timer.time)
this.updateStatsBox(wrestler, timerKey, elapsedSeconds)
}
resetTimer(wrestler, timerKey) {
const timer = wrestler.timers[timerKey]
this.stopTimer(wrestler, timerKey)
timer.time = 0
this.updateTimerDisplay(wrestler, timerKey, 0)
this.saveToLocalStorage(wrestler)
}
// Timer control methods for W1
startW1InjuryTimer() {
this.startTimer(this.w1, 'injury')
}
stopW1InjuryTimer() {
this.stopTimer(this.w1, 'injury')
}
resetW1InjuryTimer() {
this.resetTimer(this.w1, 'injury')
}
startW1BloodTimer() {
this.startTimer(this.w1, 'blood')
}
stopW1BloodTimer() {
this.stopTimer(this.w1, 'blood')
}
resetW1BloodTimer() {
this.resetTimer(this.w1, 'blood')
}
// Timer control methods for W2
startW2InjuryTimer() {
this.startTimer(this.w2, 'injury')
}
stopW2InjuryTimer() {
this.stopTimer(this.w2, 'injury')
}
resetW2InjuryTimer() {
this.resetTimer(this.w2, 'injury')
}
startW2BloodTimer() {
this.startTimer(this.w2, 'blood')
}
stopW2BloodTimer() {
this.stopTimer(this.w2, 'blood')
}
resetW2BloodTimer() {
this.resetTimer(this.w2, 'blood')
}
updateTimerDisplay(wrestler, timerKey, totalTime) {
const elementId = `${wrestler.name}-${timerKey}-time`
const element = document.getElementById(elementId)
if (element) {
element.innerText = `${Math.floor(totalTime / 60)}m ${totalTime % 60}s`
}
}
updateStatsBox(wrestler, timerKey, elapsedSeconds) {
const timerType = timerKey.includes("injury") ? "Injury Time" : "Blood Time"
const formattedTime = `${Math.floor(elapsedSeconds / 60)}m ${elapsedSeconds % 60}s`
this.updateStats(wrestler, `${timerType}: ${formattedTime}`)
}
// Utility functions
generateKey(wrestler_name) {
return `${wrestler_name}-${this.tournamentIdValue}-${this.boutNumberValue}`
}
loadFromLocalStorage(wrestler_name) {
const key = this.generateKey(wrestler_name)
const data = localStorage.getItem(key)
return data ? JSON.parse(data) : null
}
saveToLocalStorage(person) {
const key = this.generateKey(person.name)
const data = {
stats: person.stats,
updated_at: person.updated_at,
timers: person.timers
}
localStorage.setItem(key, JSON.stringify(data))
}
updateHtmlValues() {
if (this.w1StatTarget) this.w1StatTarget.value = this.w1.stats
if (this.w2StatTarget) this.w2StatTarget.value = this.w2.stats
}
updateJsValues() {
if (this.w1StatTarget) this.w1.stats = this.w1StatTarget.value
if (this.w2StatTarget) this.w2.stats = this.w2StatTarget.value
}
debounce(func, wait) {
let timeout
return (...args) => {
clearTimeout(timeout)
timeout = setTimeout(() => func(...args), wait)
}
}
initializeTimers(wrestler) {
for (const timerKey in wrestler.timers) {
this.updateTimerDisplay(wrestler, timerKey, wrestler.timers[timerKey].time)
}
}
initializeFromLocalStorage() {
const w1Data = this.loadFromLocalStorage('w1')
if (w1Data) {
this.w1.stats = w1Data.stats || ''
this.w1.updated_at = w1Data.updated_at
if (w1Data.timers) this.w1.timers = w1Data.timers
this.initializeTimers(this.w1)
}
const w2Data = this.loadFromLocalStorage('w2')
if (w2Data) {
this.w2.stats = w2Data.stats || ''
this.w2.updated_at = w2Data.updated_at
if (w2Data.timers) this.w2.timers = w2Data.timers
this.initializeTimers(this.w2)
}
this.updateHtmlValues()
}
cleanupSubscription() {
if (this.matchSubscription) {
console.log(`[Stats AC Cleanup] Unsubscribing from match channel.`)
try {
this.matchSubscription.unsubscribe()
} catch (e) {
console.error(`[Stats AC Cleanup] Error during unsubscribe:`, e)
}
this.matchSubscription = null
}
}
setupSubscription(matchId) {
this.cleanupSubscription()
console.log(`[Stats AC Setup] Attempting subscription for match ID: ${matchId}`)
// Update status indicator
if (this.statusIndicatorTarget) {
this.statusIndicatorTarget.innerText = "Connecting to server for real-time stat updates..."
this.statusIndicatorTarget.classList.remove('alert-success', 'alert-warning', 'alert-danger')
this.statusIndicatorTarget.classList.add('alert-info')
}
// Exit if we don't have App.cable
if (!window.App || !window.App.cable) {
console.error(`[Stats AC Setup] Error: App.cable is not available.`)
if (this.statusIndicatorTarget) {
this.statusIndicatorTarget.innerText = "Error: WebSockets unavailable. Stats won't update in real-time."
this.statusIndicatorTarget.classList.remove('alert-info', 'alert-success', 'alert-warning')
this.statusIndicatorTarget.classList.add('alert-danger')
}
return
}
this.matchSubscription = App.cable.subscriptions.create(
{
channel: "MatchChannel",
match_id: matchId
},
{
connected: () => {
console.log(`[Stats AC] Connected to MatchStatsChannel for match ID: ${matchId}`)
if (this.statusIndicatorTarget) {
this.statusIndicatorTarget.innerText = "Connected: Stats will update in real-time."
this.statusIndicatorTarget.classList.remove('alert-info', 'alert-warning', 'alert-danger')
this.statusIndicatorTarget.classList.add('alert-success')
}
},
disconnected: () => {
console.log(`[Stats AC] Disconnected from MatchStatsChannel`)
if (this.statusIndicatorTarget) {
this.statusIndicatorTarget.innerText = "Disconnected: Stats updates paused."
this.statusIndicatorTarget.classList.remove('alert-info', 'alert-success', 'alert-danger')
this.statusIndicatorTarget.classList.add('alert-warning')
}
},
received: (data) => {
console.log(`[Stats AC] Received data:`, data)
// Update w1 stats
if (data.w1_stat !== undefined && this.w1StatTarget) {
console.log(`[Stats AC] Updating w1_stat: ${data.w1_stat.substring(0, 30)}...`)
this.w1.stats = data.w1_stat
this.w1StatTarget.value = data.w1_stat
}
// Update w2 stats
if (data.w2_stat !== undefined && this.w2StatTarget) {
console.log(`[Stats AC] Updating w2_stat: ${data.w2_stat.substring(0, 30)}...`)
this.w2.stats = data.w2_stat
this.w2StatTarget.value = data.w2_stat
}
},
receive_error: (error) => {
console.error(`[Stats AC] Error:`, error)
this.matchSubscription = null
if (this.statusIndicatorTarget) {
this.statusIndicatorTarget.innerText = "Error: Connection issue. Stats won't update in real-time."
this.statusIndicatorTarget.classList.remove('alert-info', 'alert-success', 'alert-warning')
this.statusIndicatorTarget.classList.add('alert-danger')
}
}
}
)
}
}