From d518ede5ffe8d4e44b0194279a9f32839bc1f903 Mon Sep 17 00:00:00 2001 From: Daniel Karbach Date: Sat, 24 Feb 2024 14:38:58 +0100 Subject: [PATCH] limits for huge tournaments --- app/Http/Controllers/ProtocolController.php | 1 + app/Http/Controllers/TournamentController.php | 31 ++++++++++++++++--- resources/js/components/rounds/List.js | 10 ++++-- resources/js/components/rounds/LoadMore.js | 31 +++++++++++++++++++ resources/js/components/tournament/Detail.js | 8 ++++- resources/js/helpers/Tournament.js | 10 ++++++ resources/js/i18n/de.js | 1 + resources/js/i18n/en.js | 1 + resources/js/pages/Tournament.js | 23 +++++++++++++- routes/api.php | 1 + 10 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 resources/js/components/rounds/LoadMore.js diff --git a/app/Http/Controllers/ProtocolController.php b/app/Http/Controllers/ProtocolController.php index e4f0280..6b5e48c 100644 --- a/app/Http/Controllers/ProtocolController.php +++ b/app/Http/Controllers/ProtocolController.php @@ -14,6 +14,7 @@ class ProtocolController extends Controller ->protocols() ->with('user') ->orderBy('created_at', 'desc') + ->limit(150) ->get(); return $protocol->values()->toJson(); } diff --git a/app/Http/Controllers/TournamentController.php b/app/Http/Controllers/TournamentController.php index 9b21736..f53dad4 100644 --- a/app/Http/Controllers/TournamentController.php +++ b/app/Http/Controllers/TournamentController.php @@ -28,21 +28,21 @@ class TournamentController extends Controller $tournament = Tournament::with( 'applications', 'applications.user', - 'rounds', - 'rounds.results', - 'rounds.results.user', 'participants', 'participants.user', )->findOrFail($id); $this->authorize('view', $tournament); - foreach ($tournament->rounds as $round) { + $rounds = $tournament->rounds()->with(['results', 'results.user'])->limit(25)->get(); + foreach ($rounds as $round) { try { $this->authorize('seeResults', $round); } catch (AuthorizationException) { $round->hideResults(); } } - return $tournament->toJson(); + $json = $tournament->toArray(); + $json['rounds'] = $rounds->toArray(); + return $json; } public function discord(Request $request, Tournament $tournament) { @@ -81,6 +81,27 @@ class TournamentController extends Controller return $tournament->toJson(); } + public function moreRounds(Request $request, Tournament $tournament) { + $this->authorize('view', $tournament); + + $validatedData = $request->validate([ + 'last_known' => 'integer|required', + ]); + + $rounds = $tournament->rounds() + ->where('number', '<', $validatedData['last_known']) + ->with(['results', 'results.user']) + ->limit(25)->get(); + foreach ($rounds as $round) { + try { + $this->authorize('seeResults', $round); + } catch (AuthorizationException) { + $round->hideResults(); + } + } + return $rounds->toArray(); + } + public function open(Request $request, Tournament $tournament) { $this->authorize('update', $tournament); $tournament->accept_applications = true; diff --git a/resources/js/components/rounds/List.js b/resources/js/components/rounds/List.js index 2ac8f17..5aa2a4c 100644 --- a/resources/js/components/rounds/List.js +++ b/resources/js/components/rounds/List.js @@ -4,12 +4,14 @@ import { Alert } from 'react-bootstrap'; import { withTranslation } from 'react-i18next'; import Item from './Item'; +import LoadMore from './LoadMore'; import i18n from '../../i18n'; const List = ({ + loadMore, rounds, tournament, -}) => rounds && rounds.length ? +}) => rounds && rounds.length ? <>
    {rounds.map(round => )}
-: + {loadMore ? + + : null} + : {i18n.t('rounds.empty')} ; List.propTypes = { + loadMore: PropTypes.func, rounds: PropTypes.arrayOf(PropTypes.shape({ id: PropTypes.number, })), diff --git a/resources/js/components/rounds/LoadMore.js b/resources/js/components/rounds/LoadMore.js new file mode 100644 index 0000000..d0ea365 --- /dev/null +++ b/resources/js/components/rounds/LoadMore.js @@ -0,0 +1,31 @@ +import PropTypes from 'prop-types'; +import React from 'react'; +import { Button } from 'react-bootstrap'; +import { useTranslation } from 'react-i18next'; + +const LoadMore = ({ loadMore }) => { + const [loading, setLoading] = React.useState(false); + + const { t } = useTranslation(); + + const handleLoadMore = React.useCallback(async () => { + setLoading(true); + try { + await loadMore(); + } finally { + setLoading(false); + } + }, [loadMore]); + + return
+ +
; +}; + +LoadMore.propTypes = { + loadMore: PropTypes.func, +}; + +export default LoadMore; diff --git a/resources/js/components/tournament/Detail.js b/resources/js/components/tournament/Detail.js index 84592f6..318e677 100644 --- a/resources/js/components/tournament/Detail.js +++ b/resources/js/components/tournament/Detail.js @@ -42,6 +42,7 @@ const getClassName = (tournament, user) => { const Detail = ({ addRound, + moreRounds, tournament, }) => { const { t } = useTranslation(); @@ -111,7 +112,11 @@ const Detail = ({ : null} {tournament.rounds ? - + : null} @@ -120,6 +125,7 @@ const Detail = ({ Detail.propTypes = { addRound: PropTypes.func, + moreRounds: PropTypes.func, tournament: PropTypes.shape({ id: PropTypes.number, participants: PropTypes.arrayOf(PropTypes.shape({ diff --git a/resources/js/helpers/Tournament.js b/resources/js/helpers/Tournament.js index 9d2962d..7270351 100644 --- a/resources/js/helpers/Tournament.js +++ b/resources/js/helpers/Tournament.js @@ -30,6 +30,16 @@ export const getRunners = tournament => { .sort(Participant.compareUsername); }; +export const getLastRound = tournament => { + if (!tournament || !tournament.rounds || !tournament.rounds.length) return null; + return tournament.rounds.slice(-1)[0]; +}; + +export const canLoadMoreRounds = tournament => { + const last_round = getLastRound(tournament); + return last_round && last_round.number > 1; +}; + export const hasScoreboard = tournament => !!(tournament && tournament.type === 'signup-async'); export const hasSignup = tournament => !!(tournament && tournament.type === 'signup-async'); diff --git a/resources/js/i18n/de.js b/resources/js/i18n/de.js index 308ca7f..0d17d73 100644 --- a/resources/js/i18n/de.js +++ b/resources/js/i18n/de.js @@ -399,6 +399,7 @@ export default { noSeed: 'Noch kein Seed', numberOfResults_one: '{{ count }} Ergebnis', numberOfResults_other: '{{ count }} Ergebnisse', + loadMore: 'weitere Runden laden', lock: 'Runde sperren', lockDescription: 'Wenn die Runde gesperrt wird, können Runner keine Änderungen an ihrem Ergebnis mehr vornehmen.', locked: 'Die Runde ist für weitere Änderungen am Ergebnis gesperrt.', diff --git a/resources/js/i18n/en.js b/resources/js/i18n/en.js index a146ca2..3722991 100644 --- a/resources/js/i18n/en.js +++ b/resources/js/i18n/en.js @@ -399,6 +399,7 @@ export default { noSeed: 'No seed set', numberOfResults_one: '{{ count }} submission', numberOfResults_other: '{{ count }} submissions', + loadMore: 'load more rounds', lock: 'Lock round', lockDescription: 'When a round is locked, runners cannot submit or change results.', locked: 'Results for this round have been locked.', diff --git a/resources/js/pages/Tournament.js b/resources/js/pages/Tournament.js index 5de5dbe..ecaab6c 100644 --- a/resources/js/pages/Tournament.js +++ b/resources/js/pages/Tournament.js @@ -10,6 +10,8 @@ import Loading from '../components/common/Loading'; import NotFound from '../pages/NotFound'; import Detail from '../components/tournament/Detail'; import { + canLoadMoreRounds, + getLastRound, patchApplication, patchParticipant, patchResult, @@ -97,6 +99,21 @@ export const Component = () => { }; }, [id]); + const moreRounds = React.useCallback(async () => { + const last_round = getLastRound(tournament); + if (!last_round) return; + console.log(last_round); + const last_known = last_round.number; + const rsp = await axios.get( + `/api/tournaments/${id}/more-rounds`, + { params: { last_known } }, + ); + setTournament(tournament => ({ + ...tournament, + rounds: [...tournament.rounds, ...rsp.data], + })); + }, [id, tournament]); + useEffect(() => { const cb = (e) => { if (e.user) { @@ -132,6 +149,10 @@ export const Component = () => { {tournament.title} - + ; }; diff --git a/routes/api.php b/routes/api.php index 2b8b682..ca02e36 100644 --- a/routes/api.php +++ b/routes/api.php @@ -70,6 +70,7 @@ Route::get('tech', 'App\Http\Controllers\TechniqueController@search'); Route::get('tech/{tech:name}', 'App\Http\Controllers\TechniqueController@single'); Route::get('tournaments/{id}', 'App\Http\Controllers\TournamentController@single'); +Route::get('tournaments/{tournament}/more-rounds', 'App\Http\Controllers\TournamentController@moreRounds'); Route::post('tournaments/{tournament}/apply', 'App\Http\Controllers\TournamentController@apply'); Route::post('tournaments/{tournament}/close', 'App\Http\Controllers\TournamentController@close'); Route::post('tournaments/{tournament}/discord', 'App\Http\Controllers\TournamentController@discord'); -- 2.39.2