From 08124cc75e016ce1d6bae15a7751d548930b9ec2 Mon Sep 17 00:00:00 2001 From: Krzysztof Gutkowski Date: Sun, 19 Apr 2026 21:59:06 +0200 Subject: [PATCH 1/3] Split quick/ranked play rankings into separate tabs --- .../Ranking/MatchmakingController.php | 16 ++++++++++++--- app/Http/Controllers/RankingController.php | 6 +++++- app/Models/MatchmakingPool.php | 2 ++ .../js/components/basic-select-options.tsx | 3 ++- resources/lang/en/rankings.php | 6 +++++- .../rankings/_pool_type_selector.blade.php | 20 +++++++++++++++++++ .../views/rankings/matchmaking.blade.php | 19 +++++++++++------- routes/web.php | 2 +- 8 files changed, 60 insertions(+), 14 deletions(-) create mode 100644 resources/views/rankings/_pool_type_selector.blade.php diff --git a/app/Http/Controllers/Ranking/MatchmakingController.php b/app/Http/Controllers/Ranking/MatchmakingController.php index a32973dce93..ca06ba27a96 100644 --- a/app/Http/Controllers/Ranking/MatchmakingController.php +++ b/app/Http/Controllers/Ranking/MatchmakingController.php @@ -18,17 +18,27 @@ class MatchmakingController extends Controller 'rating' => [['rating', 'DESC'], ['total_points', 'DESC']], ]; - public function show(?string $rulesetName = null, ?string $poolId = null) + public function show(?string $poolType = null, ?string $rulesetName = null, ?string $poolId = null) { + $poolType ??= MatchmakingPool::TYPES[0]; + $rulesetName ??= default_mode(); $rulesetId = Beatmap::MODES[$rulesetName] ?? abort(422, 'invalid ruleset parameter'); - $poolsQuery = MatchmakingPool::where(['ruleset_id' => $rulesetId])->orderByDesc('active')->orderByDesc('id'); + + $poolsQuery = MatchmakingPool::where([ + 'type' => $poolType, + 'ruleset_id' => $rulesetId, + ])->orderByDesc('active')->orderByDesc('id'); if ($poolId === null) { $pool = $poolsQuery->firstOrFail(); - return ujs_redirect(route('rankings.matchmaking', ['mode' => $rulesetName, 'pool' => $pool->getKey()])); + return ujs_redirect(route('rankings.matchmaking', [ + 'poolType' => $poolType, + 'mode' => $rulesetName, + 'pool' => $pool->getKey(), + ])); } $pools = $poolsQuery->get(); diff --git a/app/Http/Controllers/RankingController.php b/app/Http/Controllers/RankingController.php index 6d7ed518356..f17261c1849 100644 --- a/app/Http/Controllers/RankingController.php +++ b/app/Http/Controllers/RankingController.php @@ -8,6 +8,7 @@ use App\Models\Beatmap; use App\Models\Country; use App\Models\CountryStatistics; +use App\Models\MatchmakingPool; use App\Models\Model; use App\Models\Spotlight; use App\Models\TeamStatistics; @@ -64,7 +65,10 @@ public static function url( 'type' => $params['type'], ]), 'kudosu' => route('rankings.kudosu'), - 'matchmaking' => route('rankings.matchmaking', ['mode' => $params['mode'] ?? default_mode()]), + 'matchmaking' => route('rankings.matchmaking', [ + 'poolType' => $params['poolType'] ?? MatchmakingPool::TYPES[0], + 'mode' => $params['mode'] ?? default_mode(), + ]), 'playlists' => match ($params['list'] ?? 'seasons') { 'charts' => route('rankings', [ 'mode' => $params['mode'] ?? default_mode(), diff --git a/app/Models/MatchmakingPool.php b/app/Models/MatchmakingPool.php index 51eba55723e..788e3bf6060 100644 --- a/app/Models/MatchmakingPool.php +++ b/app/Models/MatchmakingPool.php @@ -24,6 +24,8 @@ */ class MatchmakingPool extends Model { + const array TYPES = ['ranked_play', 'quick_play']; + protected $casts = [ 'active' => 'bool', ]; diff --git a/resources/js/components/basic-select-options.tsx b/resources/js/components/basic-select-options.tsx index 3b0e61188b4..a3357716204 100644 --- a/resources/js/components/basic-select-options.tsx +++ b/resources/js/components/basic-select-options.tsx @@ -19,6 +19,7 @@ interface PropsBase { type Props = PropsBase & ({ type: 'daily_challenge' | 'download' | 'multiplayer' | 'seasons' | 'spotlight'; } | { + poolType: 'ranked_play' | 'quick_play'; ruleset: Ruleset; type: 'matchmaking'; }); @@ -52,7 +53,7 @@ export default class BasicSelectOptions extends React.PureComponent { case 'download': return route('download', { platform: id }); case 'matchmaking': - return route('rankings.matchmaking', { mode: this.props.ruleset, pool: id }); + return route('rankings.matchmaking', { mode: this.props.ruleset, pool: id, poolType: this.props.poolType }); case 'multiplayer': return route('multiplayer.rooms.show', { room: id ?? 'latest' }); case 'seasons': diff --git a/resources/lang/en/rankings.php b/resources/lang/en/rankings.php index e0520194e83..4af8a62bc4f 100644 --- a/resources/lang/en/rankings.php +++ b/resources/lang/en/rankings.php @@ -34,6 +34,10 @@ ], 'matchmaking' => [ + 'pool_types' => [ + 'quick_play' => 'quick play', + 'ranked_play' => 'ranked play', + ], 'plays' => 'Plays', 'points' => 'Points', 'provisional' => 'Not enough matches played to accurately determine rating', @@ -66,7 +70,7 @@ 'daily_challenge' => 'daily challenge', 'global' => 'global', 'kudosu' => 'kudosu', - 'matchmaking' => 'quick play', + 'matchmaking' => 'matchmaking', 'playlists' => 'playlists', 'team' => 'team', 'top_plays' => 'top plays', diff --git a/resources/views/rankings/_pool_type_selector.blade.php b/resources/views/rankings/_pool_type_selector.blade.php new file mode 100644 index 00000000000..11146a6f2ca --- /dev/null +++ b/resources/views/rankings/_pool_type_selector.blade.php @@ -0,0 +1,20 @@ +{{-- + Copyright (c) ppy Pty Ltd . Licensed under the GNU Affero General Public License v3.0. + See the LICENCE file in the repository root for full licence text. +--}} +@php + use App\Models\MatchmakingPool; +@endphp +
+ @foreach(MatchmakingPool::TYPES as $tab) + + {{ osu_trans("rankings.matchmaking.pool_types.{$tab}") }} + + @endforeach +
diff --git a/resources/views/rankings/matchmaking.blade.php b/resources/views/rankings/matchmaking.blade.php index 1866a11b5af..8087e60eeb3 100644 --- a/resources/views/rankings/matchmaking.blade.php +++ b/resources/views/rankings/matchmaking.blade.php @@ -5,29 +5,34 @@ @php use App\Http\Controllers\Ranking\MatchmakingController; - $params = ['mode' => $rulesetName]; + $params = ['poolType' => $pool->type, 'mode' => $rulesetName]; @endphp @extends('rankings.index', [ 'hasPager' => $scores !== null, 'params' => [...$params, 'type' => 'matchmaking'], 'rulesetSelectorUrlFn' => fn (string $r): string => route('rankings.matchmaking', [...$params, 'mode' => $r, 'sort' => $sort]), - 'titlePrepend' => osu_trans('rankings.type.matchmaking').': '.$pool->getDisplayName(), + 'titlePrepend' => osu_trans("rankings.matchmaking.pool_types.{$pool->type}").': '.$pool->getDisplayName(), ]) -@if (count($pools) > 1) - @section('ranking-header') -
+@section('ranking-header') +
+ @include('rankings._pool_type_selector', compact('params')) +
+ + @if (count($pools) > 1) +
@include('objects._basic_select_options', ['selectOptions' => [ ...json_options($pool, $pools, fn ($pool) => [ 'id' => $pool->getKey(), 'text' => $pool->getDisplayName(), ]), + 'poolType' => $pool->type, 'ruleset' => $rulesetName, 'type' => 'matchmaking', ]])
- @endsection -@endif + @endif +@endsection @section('scores-header')
diff --git a/routes/web.php b/routes/web.php index 30a9202644c..c3e21b85b72 100644 --- a/routes/web.php +++ b/routes/web.php @@ -307,7 +307,7 @@ Route::get('rankings/kudosu', 'RankingController@kudosu')->name('rankings.kudosu'); Route::resource('rankings/daily-challenge', 'Ranking\DailyChallengeController', ['only' => ['index', 'show']]); - Route::get('rankings/quickplay/{mode?}/{pool?}', 'Ranking\MatchmakingController@show')->name('rankings.matchmaking'); + Route::get('rankings/matchmaking/{poolType?}/{mode?}/{pool?}', 'Ranking\MatchmakingController@show')->name('rankings.matchmaking'); Route::get('rankings/top-plays/{mode?}', 'Ranking\TopPlaysController@show')->name('rankings.top-plays'); Route::get('rankings/{mode?}/{type?}/{sort?}', 'RankingController@index')->name('rankings'); From 9cb0ef5a16ec860b3cc04f7ddd56b597f3592515 Mon Sep 17 00:00:00 2001 From: Krzysztof Gutkowski Date: Sun, 19 Apr 2026 22:09:00 +0200 Subject: [PATCH 2/3] Don't show points in ranked play rankings --- .../Ranking/MatchmakingController.php | 2 +- app/Models/MatchmakingPool.php | 5 ++ resources/css/bem/ranking-page-grid.less | 10 ++++ .../views/rankings/matchmaking.blade.php | 52 +++++++++++-------- 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/app/Http/Controllers/Ranking/MatchmakingController.php b/app/Http/Controllers/Ranking/MatchmakingController.php index ca06ba27a96..2163b3308d3 100644 --- a/app/Http/Controllers/Ranking/MatchmakingController.php +++ b/app/Http/Controllers/Ranking/MatchmakingController.php @@ -47,7 +47,7 @@ public function show(?string $poolType = null, ?string $rulesetName = null, ?str $query = $pool->allUserStats()->with('user.team')->default(); $sort = get_string(request('sort')); - if (!array_key_exists($sort, static::SORTS)) { + if (!array_key_exists($sort, static::SORTS) || !$pool->hasPoints()) { $sort = 'rating'; } foreach (static::SORTS[$sort] as $dbSort) { diff --git a/app/Models/MatchmakingPool.php b/app/Models/MatchmakingPool.php index 788e3bf6060..4acff44203d 100644 --- a/app/Models/MatchmakingPool.php +++ b/app/Models/MatchmakingPool.php @@ -46,4 +46,9 @@ public function getDisplayName(): string return $prefix.$name; } + + public function hasPoints(): bool + { + return $this->type === 'quick_play'; + } } diff --git a/resources/css/bem/ranking-page-grid.less b/resources/css/bem/ranking-page-grid.less index bbcc9e8e997..766233b4e7c 100644 --- a/resources/css/bem/ranking-page-grid.less +++ b/resources/css/bem/ranking-page-grid.less @@ -11,4 +11,14 @@ --item-padding: 6px 20px; grid-template-columns: auto 10fr 1fr 1fr 1fr 1fr; } + + &--quick_play { + --item-padding: 6px 20px; + grid-template-columns: auto 10fr 1fr 1fr 1fr 1fr; + } + + &--ranked_play { + --item-padding: 6px 20px; + grid-template-columns: auto 10fr 1fr 1fr 1fr; + } } diff --git a/resources/views/rankings/matchmaking.blade.php b/resources/views/rankings/matchmaking.blade.php index 8087e60eeb3..87816ccca4b 100644 --- a/resources/views/rankings/matchmaking.blade.php +++ b/resources/views/rankings/matchmaking.blade.php @@ -35,25 +35,27 @@ @endsection @section('scores-header') -
-
-
- {{ osu_trans('sort._') }} + @if ($pool->hasPoints()) +
+
+
+ {{ osu_trans('sort._') }} +
+ @foreach (MatchmakingController::SORTS as $newSort => $_dbColumns) + + {{ osu_trans("rankings.matchmaking.{$newSort}") }} + + @endforeach
- @foreach (MatchmakingController::SORTS as $newSort => $_dbColumns) - - {{ osu_trans("rankings.matchmaking.{$newSort}") }} - - @endforeach
-
+ @endif @endsection @section('scores') -
+
@@ -66,9 +68,11 @@ class="{{ class_with_modifiers('sort__item', 'button', ['active' => $newSort ===
{{ osu_trans('rankings.matchmaking.plays') }}
-
- {{ osu_trans('rankings.matchmaking.points') }} -
+ @if ($pool->hasPoints()) +
+ {{ osu_trans('rankings.matchmaking.points') }} +
+ @endif
{{ osu_trans('rankings.matchmaking.rating') }}
@@ -92,12 +96,14 @@ class="{{ class_with_modifiers('sort__item', 'button', ['active' => $newSort ===
{{ i18n_number_format($score->elo_data['contest_count']) }}
-
- {{ i18n_number_format($score->total_points) }} -
+ @if ($pool->hasPoints()) +
+ {{ i18n_number_format($score->total_points) }} +
+ @endif