Auto refresh feature on public scoreboards
All checks were successful
CI / scan_ruby (push) Successful in 19s
CI / scan_js (push) Successful in 13s
CI / lint (push) Successful in 13s
CI / test (push) Successful in 38s

#12
#13
This commit is contained in:
sto
2025-12-08 17:11:47 +01:00
parent 1fc05bea63
commit ee250b96ad
8 changed files with 89 additions and 56 deletions

View File

@@ -123,20 +123,19 @@ class ContestsController < ApplicationController
@title = I18n.t("contests.scoreboard.title", name: @contest.name)
@contestants = ranked_contestants(@contest)
filter_contestants_per_category
if params.key?(:category)
@category = params[:category]
filter_contestants_per_category
end
if params.key?(:hide_offline) && params[:hide_offline] == "true"
@contestants = @contestants.select { |contestant| !contestant.offline.present? }
@hide_offline = true
end
if params.key?(:autorefresh)
@autorefresh = true
end
@puzzles = @contest.puzzles.where(hidden: false).or(@contest.puzzles.where(hidden: nil)).order(:id)
if params.key?(:hide_offline) && params.key?(:category)
@action_path = "/public/#{@contest.friendly_id}?hide_offline=#{params[:hide_offline]}&category=#{params[:category]}"
elsif params.key?(:category)
@action_path = "/public/#{@contest.friendly_id}?category=#{params[:category]}"
elsif params.key?(:hide_offline)
@action_path = "/public/#{@contest.friendly_id}?hide_offline=#{params[:hide_offline]}"
else
@action_path = "/public/#{@contest.friendly_id}"
end
@action_path = "/public/#{@contest.friendly_id}"
@space = " "
render :scoreboard
end
@@ -254,11 +253,11 @@ class ContestsController < ApplicationController
end
def filter_contestants_per_category
if params.key?(:category) && params[:category] != "-1"
if params[:category] == "-2"
if @category != "-1"
if @category == "-2"
@contestants = @contestants.select { |contestant| contestant.categories.size == 0 }
else
@contestants = @contestants.select { |contestant| contestant.categories.where(id: params[:category]).any? }
@contestants = @contestants.select { |contestant| contestant.categories.where(id: @category).any? }
end
end
end

View File

@@ -1,5 +1,3 @@
= render "selectors"
.row
.mt-3.col-6.d-flex.flex-column style="height: calc(100vh - 250px)"
.d-flex.flex-column style="overflow-y: auto"

View File

@@ -9,8 +9,6 @@ css:
=> "#{@puzzles[0].name} -"
= "#{@puzzles[0].brand} #{@puzzles[0].pieces}p"
= render "selectors"
.row
.mt-3.d-flex.flex-column style="height: calc(100vh - 250px)"
.d-flex.flex-column style="overflow-y: auto"

View File

@@ -1,15 +1,3 @@
javascript:
function updateParams() {
categorySelectEl = document.getElementById('categories');
offlineInputEl = document.getElementById('offline');
if (categorySelectEl && !offlineInputEl) {
window.location.replace(`/public/#{@contest.slug}?category=${categorySelectEl.value}`);
} else if (!categorySelectEl) {
window.location.replace(`/public/#{@contest.slug}?hide_offline=${offlineInputEl.checked}`);
} else {
window.location.replace(`/public/#{@contest.slug}?category=${categorySelectEl.value}&hide_offline=${offlineInputEl.checked}`);
}
}
- if @contest.categories.size > 0
.row
.col
@@ -17,29 +5,29 @@ javascript:
option value=-1
= t("contests.scoreboard.all_categories")
- @contest.categories.each do |category|
option value=category.id
= category.name
- if @category == category.id.to_s
option value=category.id selected=true
= category.name
- else
option value=category.id
= category.name
javascript:
categorySelectEl = document.getElementById('categories');
urlParams = new URLSearchParams(window.location.search);
selectedCategory = urlParams.get('category');
Array.from(categorySelectEl.children).forEach((option) => {
if (option.value == selectedCategory) option.selected = true;
});
categorySelectEl.addEventListener('change', (e) => {
updateParams();
document.getElementById('categories').addEventListener('change', (e) => {
addParam('category', e.target.value);
})
- if @contest.offline_form && @contest.puzzles.length < 2
.row
.col
input type="checkbox" id="offline" style="padding: 5px;"
- if @hide_offline
input type="checkbox" id="offline" style="padding: 5px;" checked=true
- else
input type="checkbox" id="offline" style="padding: 5px;"
label for="offline"
.ms-2
= t("contests.scoreboard.hide_offline")
javascript:
offlineInputEl = document.getElementById('offline');
urlParams = new URLSearchParams(window.location.search);
offlineInputEl.checked = urlParams.get('hide_offline') == "true";
offlineInputEl.addEventListener('change', (e) => {
updateParams();
document.getElementById('offline').addEventListener('change', (e) => {
console.log('changed');
if (e.target.checked) addParam('hide_offline', e.target.checked);
else removeParam('hide_offline');
})

View File

@@ -2,15 +2,20 @@ css:
@media (max-width: 800px) {
.mobile-single { display: block !important; }
.desktop-single { display: none; }
#scoreboard-switches { display: none; }
}
- if @contest.puzzles.size < 2
= render "selectors"
- if @contest.puzzles.size < 2
.mobile-single style="display: none;"
= render "scoreboard_mobile_single"
.desktop-single
= render "scoreboard_desktop_single"
turbo-frame id="scoreboard"
a.btn.btn-primary href="" id="refresh-button" style="display: none;"
.mobile-single style="display: none;"
= render "scoreboard_mobile_single"
.desktop-single
= render "scoreboard_desktop_single"
- else
= render "scoreboard_desktop_marathon"
turbo-frame id="scoreboard"
a.btn.btn-primary href="" id="refresh-button" style="display: none;"
= render "scoreboard_desktop_marathon"

View File

@@ -43,19 +43,62 @@ html
.toast-body
= msg
h1.mb-5
h1.mb-4
- if @contest && @contest.id.present?
= @contest.name
- if active_page("/public") == "active" && @action_path
a.ms-4.btn.btn-primary href=@action_path style="margin-top: -6px"
= t("helpers.buttons.refresh")
- if active_page("/contests") == "active"
= @contest.name
.float-end style="margin-top: -5px;" id="scoreboard-switches"
.d-inline-flex.align-items-center
.ms-4.form-check.form-switch style="font-size: 16px; font-weight: 300;"
input.form-check-input type="checkbox" id="refresh-checkbox"
label.ms-1 style="font-size: 16px; font-weight: 300;"
= t("contests.scoreboard.auto_refresh")
.js data-turbo="false"
javascript:
function refresh() {
if (document.getElementById('refresh-checkbox').checked) {
addParam('autorefresh', 1);
setTimeout(refresh, 30000);
}
}
function addParam(key, value) {
const urlParams = new URLSearchParams(window.location.search);
urlParams.delete(key);
urlParams.append(key, value);
const refreshBtn = document.getElementById('refresh-button')
refreshBtn.href = `/public/#{@contest.friendly_id}?${urlParams.toString()}`;
refreshBtn.click();
}
function removeParam(key) {
const urlParams = new URLSearchParams(window.location.search);
urlParams.delete(key);
const refreshBtn = document.getElementById('refresh-button')
refreshBtn.href = `/public/#{@contest.friendly_id}?${urlParams.toString()}`;
refreshBtn.click();
}
function autoRefresh() {
if (document.getElementById('refresh-checkbox').checked) setTimeout(refresh, 30000);
document.getElementById('refresh-checkbox').addEventListener('change', (e) => {
if (e.target.checked) refresh();
else removeParam('autorefresh');
});
}
async function startAutoRefresh(count) {
if (count == 0) return;
if (document.getElementById('refresh-button') && document.getElementById('refresh-checkbox')) autoRefresh();
else setTimeout(() => startAutoRefresh(count - 1), 10);
}
startAutoRefresh(200);
- elsif active_page("/contests") == "active"
= @contest.name
- if @contest.public
a.ms-4.btn.btn-success href="/public/#{@contest.slug}" style="margin-top: -6px;"
= t("contests.show.open_public_scoreboard")
- else
a.ms-4.btn.btn-success.disabled style="margin-top: -6px;"
= t("contests.show.public_scoreboard_disabled")
- else
= @contest.name
- else
= @title

View File

@@ -211,6 +211,7 @@ en:
title: New jigsaw puzzle contest
scoreboard:
all_categories: All categories
auto_refresh: Auto refresh
hide_offline: Hide offline participants
refresh: Activate auto-refresh (every 5s)
title: "%{name}"

View File

@@ -182,6 +182,7 @@ fr:
title: Nouveau concours
scoreboard:
all_categories: Toutes les catégories
auto_refresh: Auto-rafraichissement
hide_offline: Cacher les participant.e.s hors-ligne
refresh: Activer le rafraichissement automatique de la page (toutes les 5s)
title: "%{name}"