diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index e0eecee..38834d7 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -18,5 +18,15 @@ //= require jquery.dataTables.min.js //= require turbolinks // +//= require actioncable +//= require_self //= require_tree . +// Create the Action Cable consumer instance +(function() { + this.App || (this.App = {}); + + App.cable = ActionCable.createConsumer(); + +}).call(this); + diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb new file mode 100644 index 0000000..d672697 --- /dev/null +++ b/app/channels/application_cable/channel.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb new file mode 100644 index 0000000..0ff5442 --- /dev/null +++ b/app/channels/application_cable/connection.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Connection < ActionCable::Connection::Base + end +end diff --git a/app/channels/match_channel.rb b/app/channels/match_channel.rb new file mode 100644 index 0000000..0ae50b3 --- /dev/null +++ b/app/channels/match_channel.rb @@ -0,0 +1,63 @@ +class MatchChannel < ApplicationCable::Channel + def subscribed + @match = Match.find_by(id: params[:match_id]) + Rails.logger.info "[MatchChannel] Client subscribed with match_id: #{params[:match_id]}. Match found: #{@match.present?}" + if @match + stream_for @match + else + Rails.logger.warn "[MatchChannel] Match not found for ID: #{params[:match_id]}. Subscription may fail." + # You might want to reject the subscription if the match isn't found + # reject + end + end + + def unsubscribed + Rails.logger.info "[MatchChannel] Client unsubscribed for match #{@match&.id}" + end + + # Called when client sends data with action: 'send_stat' + def send_stat(data) + # Explicit check for @match at the start + unless @match + Rails.logger.error "[MatchChannel] Error: send_stat called but @match is nil. Client params on sub: #{params[:match_id]}" + return # Stop if no match context + end + + Rails.logger.info "[MatchChannel] Received send_stat for match #{@match.id} with data: #{data.inspect}" + + # Prepare attributes to update + attributes_to_update = {} + attributes_to_update[:w1_stat] = data['new_w1_stat'] if data.key?('new_w1_stat') + attributes_to_update[:w2_stat] = data['new_w2_stat'] if data.key?('new_w2_stat') + + if attributes_to_update.present? + # Persist the changes to the database + # Note: Consider background job or throttling for very high frequency updates + begin + if @match.update(attributes_to_update) + Rails.logger.info "[MatchChannel] Updated match #{@match.id} stats in DB: #{attributes_to_update.keys.join(', ')}" + + # Prepare payload for broadcast (using potentially updated values from @match) + payload = { + w1_stat: @match.w1_stat, + w2_stat: @match.w2_stat + }.compact + + if payload.present? + Rails.logger.info "[MatchChannel] Broadcasting DB-persisted stats to match #{@match.id} with payload: #{payload.inspect}" + MatchChannel.broadcast_to(@match, payload) + else + Rails.logger.info "[MatchChannel] Payload empty after DB update for match #{@match.id}, not broadcasting." + end + else + Rails.logger.error "[MatchChannel] Failed to update match #{@match.id} stats in DB: #{@match.errors.full_messages.join(', ')}" + end + rescue => e + Rails.logger.error "[MatchChannel] Exception during match update for #{@match.id}: #{e.message}" + Rails.logger.error e.backtrace.join("\n") + end + else + Rails.logger.info "[MatchChannel] No new stat data provided in send_stat for match #{@match.id}, not updating DB or broadcasting." + end + end +end diff --git a/app/controllers/matches_controller.rb b/app/controllers/matches_controller.rb index df848e0..1375f1b 100644 --- a/app/controllers/matches_controller.rb +++ b/app/controllers/matches_controller.rb @@ -1,5 +1,5 @@ class MatchesController < ApplicationController - before_action :set_match, only: [:show, :edit, :update, :stat] + before_action :set_match, only: [:show, :edit, :update, :stat, :spectate] before_action :check_access, only: [:edit,:update, :stat] # GET /matches/1 @@ -54,12 +54,42 @@ class MatchesController < ApplicationController session[:error_return_path] = "/matches/#{@match.id}/stat" end + # GET /matches/:id/spectate + def spectate + # Similar to stat, but potentially simplified for read-only view + # We mainly need @match for the view to get the ID + # and maybe initial wrestler names/schools + if @match + @wrestler1_name = @match.w1 ? @match.wrestler1.name : "Not assigned" + @wrestler1_school_name = @match.w1 ? @match.wrestler1.school.name : "N/A" + @wrestler2_name = @match.w2 ? @match.wrestler2.name : "Not assigned" + @wrestler2_school_name = @match.w2 ? @match.wrestler2.school.name : "N/A" + @tournament = @match.tournament + else + # Handle case where match isn't found, perhaps redirect or render error + redirect_to root_path, alert: "Match not found." + end + end # PATCH/PUT /matches/1 # PATCH/PUT /matches/1.json def update respond_to do |format| if @match.update(match_params) + # Broadcast the update + MatchChannel.broadcast_to( + @match, + { + w1_stat: @match.w1_stat, + w2_stat: @match.w2_stat, + score: @match.score, + win_type: @match.win_type, + winner_id: @match.winner_id, + winner_name: @match.winner&.name, + finished: @match.finished + } + ) + if session[:return_path] sanitized_return_path = sanitize_return_path(session[:return_path]) format.html { redirect_to sanitized_return_path, notice: 'Match was successfully updated.' } diff --git a/app/models/match.rb b/app/models/match.rb index 71de101..8c571c1 100644 --- a/app/models/match.rb +++ b/app/models/match.rb @@ -2,6 +2,7 @@ class Match < ApplicationRecord belongs_to :tournament, touch: true belongs_to :weight, touch: true belongs_to :mat, touch: true, optional: true + belongs_to :winner, class_name: 'Wrestler', foreign_key: 'winner_id', optional: true has_many :wrestlers, :through => :weight has_many :schools, :through => :wrestlers validate :score_validation, :win_type_validation, :bracket_position_validation, :overtime_type_validation diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index b4719d7..d3cf640 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -3,6 +3,7 @@ <% if params[:print] %>
<%= csrf_meta_tags %> + <%= action_cable_meta_tag %><%= @match.w1_stat %>+
<%= @match.w2_stat %>+
Winner: <%= @match.winner_id ? @match.winner.name : '-' %>
+Win Type: <%= @match.win_type || '-' %>
+Score: <%= @match.score || '-' %>
+Finished: <%= @match.finished ? 'Yes' : 'No' %>
+<%= match.bout_number %> <%= match.bracket_score_string %>
<%= @winner_place %> Place Winner
<%= link_to match.bout_number, spectate_match_path(match) %> <%= match.bracket_score_string %>
<%= @winner_place %> Place Winner