From: Daniel Karbach Date: Wed, 7 May 2025 13:45:40 +0000 (+0200) Subject: options when to reveal round results X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=a9f0cf3d993ca5ff878193a98c1ff435cdd4d502;p=alttp.git options when to reveal round results --- diff --git a/app/Events/RoundChanged.php b/app/Events/RoundChanged.php index a4df199..926e2e2 100644 --- a/app/Events/RoundChanged.php +++ b/app/Events/RoundChanged.php @@ -23,6 +23,7 @@ class RoundChanged implements ShouldBroadcast public function __construct(Round $round) { $this->round = $round; + $this->round->setRelations([]); } /** diff --git a/app/Http/Controllers/TournamentController.php b/app/Http/Controllers/TournamentController.php index 2dc7e51..5ce63f4 100644 --- a/app/Http/Controllers/TournamentController.php +++ b/app/Http/Controllers/TournamentController.php @@ -105,11 +105,15 @@ class TournamentController extends Controller public function settings(Request $request, Tournament $tournament) { $this->authorize('update', $tournament); $validatedData = $request->validate([ + 'result_reveal' => 'string|nullable|in:always,finishers,never,participants', 'show_numbers' => 'boolean|nullable', ]); if (array_key_exists('show_numbers', $validatedData)) { $tournament->show_numbers = $validatedData['show_numbers']; } + if (isset($validatedData['result_reveal'])) { + $tournament->result_reveal = $validatedData['result_reveal']; + } $tournament->save(); if ($tournament->wasChanged()) { TournamentChanged::dispatch($tournament); diff --git a/app/Models/Round.php b/app/Models/Round.php index 7daa779..e5ce703 100644 --- a/app/Models/Round.php +++ b/app/Models/Round.php @@ -12,6 +12,7 @@ class Round extends Model public function isComplete() { if (count($this->tournament->participants) == 0) return false; + if ($this->tournament->type == 'open-async') return false; if (count($this->results) == 0) return false; foreach ($this->tournament->getRunners() as $participant) { $result = $participant->findResult($this); diff --git a/app/Models/Tournament.php b/app/Models/Tournament.php index 96e89b8..2ba33f1 100644 --- a/app/Models/Tournament.php +++ b/app/Models/Tournament.php @@ -2,7 +2,6 @@ namespace App\Models; -use App\Events\ParticipantChanged; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -48,7 +47,6 @@ class Tournament extends Model } usort($runners, [Participant::class, 'compareScore']); - $reversed = array_reverse($runners); $placement = count($runners); $skipped = 0; $lastScore = $runners[0]->score; diff --git a/app/Policies/RoundPolicy.php b/app/Policies/RoundPolicy.php index 1f872c5..5bb46c0 100644 --- a/app/Policies/RoundPolicy.php +++ b/app/Policies/RoundPolicy.php @@ -101,11 +101,20 @@ class RoundPolicy */ public function seeResults(?User $user, Round $round) { - return + if ( $round->locked || - ($user && $user->hasFinished($round)) || ($user && $user->isTournamentMonitor($round->tournament)) || - $round->isComplete(); + $round->isComplete() + ) { + return true; + } + if ($round->tournament->result_reveal == 'finishers') { + return ($user && $user->hasFinished($round)); + } + if ($round->tournament->result_reveal == 'participants') { + return ($user && $user->isRunner($round->tournament)); + } + return false; } /** diff --git a/database/migrations/2025_05_07_123722_add_tournament_result_reveal_column.php b/database/migrations/2025_05_07_123722_add_tournament_result_reveal_column.php new file mode 100644 index 0000000..5c60b81 --- /dev/null +++ b/database/migrations/2025_05_07_123722_add_tournament_result_reveal_column.php @@ -0,0 +1,28 @@ +string('result_reveal')->default('finishers'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('tournaments', function(Blueprint $table) { + $table->dropColumn('result_reveal'); + }); + } +}; diff --git a/resources/js/components/tournament/SettingsDialog.js b/resources/js/components/tournament/SettingsDialog.js index 46fbfd8..a40a0a1 100644 --- a/resources/js/components/tournament/SettingsDialog.js +++ b/resources/js/components/tournament/SettingsDialog.js @@ -1,7 +1,7 @@ import axios from 'axios'; import PropTypes from 'prop-types'; import React from 'react'; -import { Button, Col, Modal, Row } from 'react-bootstrap'; +import { Button, Col, Form, Modal, Row } from 'react-bootstrap'; import { withTranslation } from 'react-i18next'; import toastr from 'toastr'; @@ -113,6 +113,27 @@ const SettingsDialog = ({ value={tournament.show_numbers} /> +
+ + {i18n.t('tournaments.resultReveal')} + + + settings(tournament, { result_reveal: value })} + style={{ width: '50%' }} + value={tournament.result_reveal} + > + {['never', 'finishers', 'participants', 'always'].map((key) => + + )} + +

{i18n.t('tournaments.discord')}

@@ -157,6 +178,7 @@ SettingsDialog.propTypes = { accept_applications: PropTypes.bool, discord: PropTypes.string, locked: PropTypes.bool, + result_reveal: PropTypes.string, show_numbers: PropTypes.bool, }), }; diff --git a/resources/js/helpers/Round.js b/resources/js/helpers/Round.js index c7bb811..429d9fa 100644 --- a/resources/js/helpers/Round.js +++ b/resources/js/helpers/Round.js @@ -3,6 +3,7 @@ import Tournament from './Tournament'; export const isComplete = (tournament, round) => { if (!tournament || !tournament.participants) return false; + if (tournament.type === 'open-async') return false; if (!round || !round.results) return false; const runners = Tournament.getRunners(tournament); if (!runners.length) return false; diff --git a/resources/js/helpers/permissions.js b/resources/js/helpers/permissions.js index 23aba69..c24d75a 100644 --- a/resources/js/helpers/permissions.js +++ b/resources/js/helpers/permissions.js @@ -106,14 +106,14 @@ export const isTournamentAdmin = (user, tournament) => { return p && p.roles && p.roles.includes('admin'); }; -export const isTournamentCrew = (user, tournament) => - isTournamentAdmin(user, tournament) || isTournamentMonitor(user, tournament); - export const isTournamentMonitor = (user, tournament) => { const p = isParticipant(user, tournament); return p && p.roles && p.roles.includes('monitor'); }; +export const isTournamentCrew = (user, tournament) => + isTournamentAdmin(user, tournament) || isTournamentMonitor(user, tournament); + export const hasFinished = (user, round) => user && round && round.results && round.results.find(r => r.user_id == user.id && r.has_finished); @@ -151,11 +151,22 @@ export const mayUpdateTournament = (user, tournament) => export const mayViewProtocol = (user, tournament) => isTournamentCrew(user, tournament); -export const maySeeResults = (user, tournament, round) => - round.locked || - hasFinished(user, round) || - isTournamentMonitor(user, tournament) || - Round.isComplete(tournament, round); +export const maySeeResults = (user, tournament, round) => { + if ( + round.locked || + isTournamentMonitor(user, tournament) || + Round.isComplete(tournament, round) + ) { + return true; + } + if (tournament.result_reveal === 'finishers') { + return hasFinished(user, round); + } + if (tournament.result_reveal === 'participants') { + return isRunner(user, tournament); + } + return false; +}; // Twitch diff --git a/resources/js/i18n/de.js b/resources/js/i18n/de.js index 90922a6..658a2f6 100644 --- a/resources/js/i18n/de.js +++ b/resources/js/i18n/de.js @@ -690,6 +690,20 @@ export default { open: 'Anmeldung geöffnet', openError: 'Fehler beim Öffnen der Anmledung', openSuccess: 'Anmeldung geöffnet', + resultReveal: 'Frühzeitige Ergebnisse', + resultRevealDescription: 'Ob und wann Ergebnisse von offenen Runden angezeigt werden sollen.', + resultRevealOption: { + always: 'Immer', + finishers: 'Absolventen', + never: 'Nie', + participants: 'Teilnehmern', + }, + resultRevealOptionDescription: { + always: 'Ergebnis wird allen angezeigt, sobald es eingetragen wird.', + finishers: 'Ergebnis wird allen Teilnehmern angezeigt, die eine Zeit (oder DNF) für die Runde eingetragen haben.', + never: 'Ergebnis wird erst mit Abschluss der Runde veröffentlicht.', + participants: 'Ergebnis wird allen registrierten Teilnehmern des Turniers angezeigt.', + }, scoreboard: 'Scoreboard', scoreChart: 'Turnierverlauf', settings: 'Einstellungen', diff --git a/resources/js/i18n/en.js b/resources/js/i18n/en.js index f961bf0..1740cf6 100644 --- a/resources/js/i18n/en.js +++ b/resources/js/i18n/en.js @@ -690,6 +690,20 @@ export default { open: 'Open registration', openError: 'Error opening registration', openSuccess: 'Registration opened', + resultReveal: 'Early results', + resultRevealDescription: 'When to show results for open rounds.', + resultRevealOption: { + always: 'Always', + finishers: 'Finishers', + never: 'Never', + participants: 'Participants', + }, + resultRevealOptionDescription: { + always: 'Show all results as they come in.', + finishers: 'Show results to participants who submitted a time (or DNF).', + never: 'Only reveal results once the round has closed down.', + participants: 'Show results to only registered tournament members.', + }, scoreboard: 'Scoreboard', scoreChart: 'Score chart', settings: 'Settings',