Skip to content

Commit

Permalink
Merge pull request #11469 from nanaya/daily-ranking-page
Browse files Browse the repository at this point in the history
Add daily ranking page
  • Loading branch information
notbakaneko authored Sep 11, 2024
2 parents d6625a6 + 1209a4f commit 36dc9cb
Show file tree
Hide file tree
Showing 16 changed files with 219 additions and 13 deletions.
9 changes: 7 additions & 2 deletions app/Http/Controllers/Multiplayer/RoomsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
namespace App\Http\Controllers\Multiplayer;

use App\Exceptions\InvariantException;
use App\Http\Controllers\Controller as BaseController;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Ranking\DailyChallengeController;
use App\Models\Model;
use App\Models\Multiplayer\Room;
use App\Transformers\Multiplayer\RoomTransformer;

class RoomsController extends BaseController
class RoomsController extends Controller
{
public function __construct()
{
Expand Down Expand Up @@ -177,6 +178,10 @@ public function show($id)
);
}

if ($room->category === 'daily_challenge') {
return ujs_redirect(route('daily-challenge.show', DailyChallengeController::roomId($room)));
}

$playlistItemsQuery = $room->playlist();
if ($room->isRealtime()) {
$playlistItemsQuery->whereHas('scoreLinks');
Expand Down
70 changes: 70 additions & 0 deletions app/Http/Controllers/Ranking/DailyChallengeController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

declare(strict_types=1);

namespace App\Http\Controllers\Ranking;

use App\Http\Controllers\Controller;
use App\Models\Multiplayer\Room;
use Carbon\CarbonImmutable;
use Carbon\Exceptions\InvalidFormatException;

class DailyChallengeController extends Controller
{
private const string DATE_FORMAT = 'Y-m-d';

public static function roomId(Room $room): string
{
return $room->starts_at->format(static::DATE_FORMAT);
}

public function index()
{
$room = Room::dailyChallenges()->last() ?? abort(404);

return ujs_redirect(route('daily-challenge.show', ['daily_challenge' => static::roomId($room)]));
}

public function show(string $dateString)
{
try {
$date = CarbonImmutable::createFromFormat(static::DATE_FORMAT, $dateString);
} catch (InvalidFormatException) {
abort(404, 'invalid date');
}

$room = Room::dailyChallengeFor($date) ?? abort(404);
$playlist = $room->playlist[0];

$currentRoomOption = [
'id' => $dateString,
'text' => $dateString,
];
$roomOptions = Room::dailyChallenges()
->orderBy('id')
->get()
->map(static::roomId(...))
->map(fn (string $roomName): array => [
'id' => $roomName,
'text' => $roomName,
]);

$scores = $room->topScores()->paginate();

$userScore = ($currentUser = \Auth::user()) === null
? null
: $room->topScores()->whereBelongsTo($currentUser)->first();

return ext_view('rankings.daily_challenge', compact(
'currentRoomOption',
'playlist',
'room',
'roomOptions',
'scores',
'userScore',
));
}
}
12 changes: 11 additions & 1 deletion app/Http/Controllers/RankingController.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,16 @@ class RankingController extends Controller
const RANKING_TYPES = ['performance', 'charts', 'score', 'country'];
const SPOTLIGHT_TYPES = ['charts'];
// in display order
const TYPES = ['performance', 'score', 'country', 'multiplayer', 'seasons', 'charts', 'kudosu'];
const TYPES = [
'performance',
'score',
'country',
'multiplayer',
'daily_challenge',
'seasons',
'charts',
'kudosu',
];

public function __construct()
{
Expand Down Expand Up @@ -114,6 +123,7 @@ public static function url(
): string {
return match ($type) {
'country' => route('rankings', ['mode' => $rulesetName, 'type' => $type]),
'daily_challenge' => route('daily-challenge.index'),
'kudosu' => route('rankings.kudosu'),
'multiplayer' => route('multiplayer.rooms.show', ['room' => 'latest']),
'seasons' => route('seasons.show', ['season' => 'latest']),
Expand Down
7 changes: 6 additions & 1 deletion app/Models/Multiplayer/Room.php
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ public function currentPlaylistItem()
public function macroDailyChallengeFor(): \Closure
{
return fn (Builder $query, CarbonImmutable $date): ?static
=> static::where('category', 'daily_challenge')
=> static::dailyChallenges()
->whereBetween('starts_at', [$date->startOfDay(), $date->endOfDay()])
->last();
}
Expand Down Expand Up @@ -265,6 +265,11 @@ public function scopeActive($query)
});
}

public function scopeDailyChallenges(Builder $query): Builder
{
return $query->where('category', 'daily_challenge');
}

public function scopeEnded($query)
{
return $query->where('ends_at', '<', Carbon::now());
Expand Down
3 changes: 3 additions & 0 deletions app/Singletons/RouteSection.php
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ class RouteSection
'passport' => [
'_' => 'user',
],
'ranking' => [
'_' => 'rankings',
],
'store' => [
'_' => 'store',
],
Expand Down
1 change: 1 addition & 0 deletions app/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,7 @@ function page_title()
'main.artist_tracks_controller._' => 'main.artists_controller._',
'main.store_controller._' => 'store._',
'multiplayer.rooms_controller._' => 'main.ranking_controller._',
'ranking.daily_challenge_controller._' => 'main.ranking_controller._',
default => $controllerKey,
};
$namespaceKey = "{$currentRoute['namespace']}._";
Expand Down
4 changes: 4 additions & 0 deletions resources/css/bem/rankings-beatmapsets.less
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
gap: 10px;
grid-template-columns: 1fr 1fr;

&--daily-challenge {
margin-top: 0;
}

&--single {
grid-template-columns: 1fr;
}
Expand Down
5 changes: 4 additions & 1 deletion resources/js/components/basic-select-options.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import SelectOptions, { OptionRenderProps } from 'components/select-options';
import SelectOptionJson from 'interfaces/select-option-json';
import { route } from 'laroute';
import * as React from 'react';
import { fail } from 'utils/fail';
import { navigate } from 'utils/turbolinks';

interface Props {
currentItem: SelectOptionJson;
items: SelectOptionJson[];
type: 'judge_results' | 'multiplayer' | 'seasons';
type: 'daily_challenge' | 'judge_results' | 'multiplayer' | 'seasons';
}

export default class BasicSelectOptions extends React.PureComponent<Props> {
Expand All @@ -32,6 +33,8 @@ export default class BasicSelectOptions extends React.PureComponent<Props> {

private href(id: number | null) {
switch (this.props.type) {
case 'daily_challenge':
return route('daily-challenge.show', { daily_challenge: id ?? fail('missing id parameter') });
case 'judge_results':
return route('contest-entries.judge-results', { contest_entry: id ?? 0 });
case 'multiplayer':
Expand Down
5 changes: 1 addition & 4 deletions resources/js/core/spoilerbox.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

import { fail } from 'utils/fail';
import { htmlElementOrNull } from 'utils/html';

function fail(message: string): never {
throw new Error(message);
}

function expand(e: JQuery.ClickEvent) {
e.stopPropagation();
e.preventDefault();
Expand Down
6 changes: 6 additions & 0 deletions resources/js/utils/fail.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
// See the LICENCE file in the repository root for full licence text.

export function fail(message: string): never {
throw new Error(message);
}
7 changes: 7 additions & 0 deletions resources/lang/en/rankings.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
'title' => 'Country',
],

'daily_challenge' => [
'beatmap' => 'Difficulty',
'percentile_10' => '10th Percentile Score',
'percentile_50' => '50th Percentile Score',
],

'filter' => [
'title' => 'Show',

Expand All @@ -30,6 +36,7 @@
'type' => [
'charts' => 'spotlights (old)',
'country' => 'country',
'daily_challenge' => 'daily challenge',
'kudosu' => 'kudosu',
'multiplayer' => 'multiplayer',
'performance' => 'performance',
Expand Down
6 changes: 4 additions & 2 deletions resources/views/rankings/_beatmapsets.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
See the LICENCE file in the repository root for full licence text.
--}}

<div class="{{ class_with_modifiers('rankings-beatmapsets', ['single' => count($beatmapsets) === 1]) }}">
<div class="{{ class_with_modifiers('rankings-beatmapsets', $modifiers ?? null, ['single' => count($beatmapsets) === 1]) }}">
@foreach ($beatmapsets as $beatmapset)
<div
class="js-react--beatmapset-panel u-contents"
data-beatmapset-panel="{{ json_encode(['beatmapset' => json_item($beatmapset, 'Beatmapset', ['beatmaps'])]) }}"
></div>
>
<div class="beatmapset-panel beatmapset-panel--size-normal"></div>
</div>
@endforeach
</div>
79 changes: 79 additions & 0 deletions resources/views/rankings/daily_challenge.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{{--
Copyright (c) ppy Pty Ltd <[email protected]>. Licensed under the GNU Affero General Public License v3.0.
See the LICENCE file in the repository root for full licence text.
--}}
@extends('rankings.index', [
'hasMode' => false,
'hasPager' => true,
'type' => 'daily_challenge',
'titlePrepend' => osu_trans('rankings.type.daily_challenge').': '.$currentRoomOption['text'],
])

@php
$percentile = $playlist->scorePercentile();
@endphp
@section('ranking-header')
<div class="osu-page osu-page--ranking-info">
<div class="js-react--basic-select-options">
<div class="select-options select-options--spotlight">
<div class="select-options__select">
<span class="select-options__option">
{{ $currentRoomOption['text'] }}
</span>
</div>
</div>
</div>

<script id="json-basic-select-options" type="application/json">
{!! json_encode([
'currentItem' => $currentRoomOption,
'items' => $roomOptions,
'type' => 'daily_challenge',
]) !!}
</script>

<div class="grid-items grid-items--ranking-info-bar">
<div class="counter-box counter-box--ranking">
<div class="counter-box__title">
{{ osu_trans('rankings.daily_challenge.beatmap') }}
</div>
<div class="counter-box__count">
<span class="fal fa-extra-mode-{{ $playlist->beatmap->mode }}"></span>
{{ $playlist->beatmap->version }}
</div>
</div>
<div class="counter-box counter-box--ranking">
<div class="counter-box__title">
{{ osu_trans('rankings.spotlight.participants') }}
</div>
<div class="counter-box__count">
{{ i18n_number_format($scores->total()) }}
</div>
</div>
<div class="counter-box counter-box--ranking">
<div class="counter-box__title">
{{ osu_trans('rankings.daily_challenge.percentile_10') }}
</div>
<div class="counter-box__count">
{{ i18n_number_format($percentile['10p']) }}
</div>
</div>
<div class="counter-box counter-box--ranking">
<div class="counter-box__title">
{{ osu_trans('rankings.daily_challenge.percentile_50') }}
</div>
<div class="counter-box__count">
{{ i18n_number_format($percentile['50p']) }}
</div>
</div>
</div>
</div>
@endsection

@section('scores-header')
@include('rankings._beatmapsets', ['beatmapsets' => [$playlist->beatmap->beatmapset], 'modifiers' => 'daily-challenge'])
@endsection

@section('scores')
@include('multiplayer.rooms._rankings_table')
@endsection
9 changes: 7 additions & 2 deletions resources/views/rankings/index.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,24 @@
'links' => $links,
'theme' => 'rankings',
]])
@slot('linksAppend')
@slot('contentAppend')
@if($hasMode)
@include('rankings._mode_selector')
@endif
@endslot

@slot('linksAppend')
@yield('additionalHeaderLinks')
@endslot
@endcomponent

@yield('ranking-header')

@if ($hasScores)
<div class="osu-page osu-page--generic" id="scores">
<div class="osu-page osu-page--generic">
@yield('scores-header')

<div id="scores"></div>
@if ($hasPager)
@include('objects._pagination_v2', [
'object' => $scores
Expand Down
1 change: 1 addition & 0 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@
});

Route::get('rankings/kudosu', 'RankingController@kudosu')->name('rankings.kudosu');
Route::resource('rankings/daily-challenge', 'Ranking\DailyChallengeController', ['only' => ['index', 'show']]);
Route::get('rankings/{mode?}/{type?}', 'RankingController@index')->name('rankings');

Route::resource('reports', 'ReportsController', ['only' => ['store']]);
Expand Down
Loading

0 comments on commit 36dc9cb

Please sign in to comment.