Skip to content

Commit

Permalink
Team member leaderboard
Browse files Browse the repository at this point in the history
  • Loading branch information
nanaya committed Jan 28, 2025
1 parent edb2bf3 commit ac3404b
Show file tree
Hide file tree
Showing 10 changed files with 160 additions and 5 deletions.
23 changes: 21 additions & 2 deletions app/Http/Controllers/TeamsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@

namespace App\Http\Controllers;

use App\Exceptions\InvariantException;
use App\Models\Beatmap;
use App\Models\Team;
use App\Models\User;
use App\Transformers\UserCompactTransformer;
use Symfony\Component\HttpFoundation\Response;

Expand Down Expand Up @@ -49,15 +52,31 @@ public function part(string $id): Response
return ujs_redirect(route('teams.show', ['team' => $team]));
}

public function show(string $id): Response
public function show(string $id, ?string $ruleset = null): Response
{
$team = Team
::with(array_map(
fn (string $preload): string => "members.user.{$preload}",
UserCompactTransformer::CARD_INCLUDES_PRELOAD,
))->findOrFail($id);

return ext_view('teams.show', compact('team'));
$ruleset ??= Beatmap::modeStr($team->default_ruleset_id);
$statisticsRelationName = User::statisticsRelationName($ruleset);
if ($statisticsRelationName === null) {
throw new InvariantException(osu_trans('beatmaps.invalid_ruleset'));
}
$leaderboard = $team
->members
->loadMissing("user.{$statisticsRelationName}")
->map(fn ($member) =>
(
$member->user->$statisticsRelationName
?? $member->user->$statisticsRelationName()->make()
)->setRelation('user', $member->user))
->sortByDesc(fn ($s) => $s->rank_score)
->values();

return ext_view('teams.show', compact('leaderboard', 'ruleset', 'team'));
}

public function update(string $id): Response
Expand Down
4 changes: 4 additions & 0 deletions app/helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -1294,6 +1294,10 @@ function i18n_date_auto(DateTimeInterface $date, string $skeleton): string

function i18n_number_format($number, $style = null, $pattern = null, $precision = null, $locale = null)
{
if ($number === null) {
return null;
}

if ($style === null && $pattern === null && $precision === null) {
static $formatters = [];
$locale ??= App::getLocale();
Expand Down
2 changes: 2 additions & 0 deletions resources/css/bem-index.less
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,8 @@
@import "bem/team-info-entries";
@import "bem/team-info-entry";
@import "bem/team-members";
@import "bem/team-members-leaderboard";
@import "bem/team-members-leaderboard-item";
@import "bem/team-members-manage";
@import "bem/team-settings";
@import "bem/team-settings-description-preview";
Expand Down
43 changes: 43 additions & 0 deletions resources/css/bem/team-members-leaderboard-item.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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.

.team-members-leaderboard-item {
align-items: center;
background: hsl(var(--hsl-b3));
border-radius: @border-radius--large;
display: grid;
font-size: @font-size--title-small;
gap: 10px;
grid-column: 1 / -1;
grid-template-columns: subgrid;
padding: 4px 10px;

&:hover {
background: hsl(var(--hsl-b2));
}

&__avatar {
.default-border-radius();
align-items: center;
display: flex;
overflow: hidden;
width: 40px;
}

&__number {
font-size: @font-size--title-small-3;
padding: 0 10px;
}

&__number-title {
color: hsl(var(--hsl-c2));
font-size: @font-size--normal;
}

&__username {
display: flex;
align-items: center;
width: max-content;
gap: 10px;
}
}
11 changes: 11 additions & 0 deletions resources/css/bem/team-members-leaderboard.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// 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.

.team-members-leaderboard {
display: grid;
margin: 0;
padding: 0;
list-style: none;
gap: 2px;
grid-template-columns: auto 1fr auto auto;
}
2 changes: 2 additions & 0 deletions resources/lang/en/beatmaps.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
// See the LICENCE file in the repository root for full licence text.

return [
'invalid_ruleset' => 'Invalid ruleset specified.',

'change_owner' => [
'too_many' => 'Too many guest mappers.',
],
Expand Down
12 changes: 11 additions & 1 deletion resources/lang/en/teams.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@
],
],

'header_links' => [
'show' => 'info',
],

'members' => [
'destroy' => [
'success' => 'Team member removed',
Expand Down Expand Up @@ -79,14 +83,20 @@
'website' => 'Website',
],

'leaderboard' => [
'performance' => 'Performance',
'global_rank' => 'Global Rank',
],

'members' => [
'members' => 'Team Members',
'owner' => 'Team Leader',
],

'sections' => [
'members' => 'Members',
'info' => 'Info',
'leaderboard' => 'Members Leaderboard',
'members' => 'Members',
],
],
];
44 changes: 44 additions & 0 deletions resources/views/teams/_members_leaderboard.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{{--
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.
--}}
<ul class="team-members-leaderboard">
@foreach ($leaderboard as $i => $stats)
<li class="team-members-leaderboard-item">
<div class="team-members-leaderboard-item__rank">
#{{ i18n_number_format($i + 1) }}
</div>
<div class="team-members-leaderboard-item__username-container">
<a
class="team-members-leaderboard-item__username js-usercard"
data-user-id="{{ $stats->user_id }}"
href="{{ route('users.show', $stats->user_id) }}"
>
<span class="team-members-leaderboard-item__avatar">
<span
class="avatar avatar--full avatar--guest"
{!! background_image($stats->user->user_avatar) !!}
></span>
</span>

{{ $stats->user->username }}
</a>
</div>
<div class="team-members-leaderboard-item__number">
<div class="team-members-leaderboard-item__number-title">
{{ osu_trans('teams.show.leaderboard.performance') }}
</div>
<div>
{{ i18n_number_format($stats->pp()) ?? '-' }}
</div>
</div>
<div class="team-members-leaderboard-item__number">
<div class="team-members-leaderboard-item__number-title">
{{ osu_trans('teams.show.leaderboard.global_rank') }}
</div>
<div>
{{ i18n_number_format($stats->globalRank()) ?? '-' }}
</div>
</div>
@endforeach
</ul>
21 changes: 20 additions & 1 deletion resources/views/teams/show.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,22 @@
])

@section('content')
@include('layout._page_header_v4', ['params' => [
@component('layout._page_header_v4', ['params' => [
'links' => [[
'active' => true,
'title' => osu_trans('teams.header_links.show'),
'url' => route('teams.show', ['team' => $team->getKey(), 'ruleset' => $ruleset]),
]],
'theme' => 'team',
'backgroundImage' => $headerUrl,
]])
@slot('linksAppend')
@include('objects._ruleset_selector', [
'currentRuleset' => $ruleset,
'urlFn' => fn ($r) => route('teams.show', ['team' => $team->getKey(), 'ruleset' => $r]),
])
@endslot
@endcomponent

<div class="osu-page osu-page--generic-compact">
<div class="profile-info profile-info--cover profile-info--team">
Expand Down Expand Up @@ -157,6 +169,13 @@ class="js-react--user-card u-contents"
</div>
</div>
</div>

<div class="page-extra u-fancy-scrollbar">
<h2 class="title title--page-extra-small title--page-extra-small-top">
{{ osu_trans('teams.show.sections.leaderboard') }}
</h2>
@include('teams._members_leaderboard')
</div>
</div>
</div>
@endsection
3 changes: 2 additions & 1 deletion routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@
Route::post('part', 'TeamsController@part')->name('part');
Route::resource('members', 'Teams\MembersController', ['only' => ['destroy', 'index']]);
});
Route::resource('teams', 'TeamsController', ['only' => ['destroy', 'edit', 'show', 'update']]);
Route::resource('teams', 'TeamsController', ['only' => ['destroy', 'edit', 'update']]);
Route::get('teams/{team}/{ruleset?}', 'TeamsController@show')->name('teams.show');

Route::post('users/check-username-availability', 'UsersController@checkUsernameAvailability')->name('users.check-username-availability');
Route::get('users/lookup', 'Users\LookupController@index')->name('users.lookup');
Expand Down

0 comments on commit ac3404b

Please sign in to comment.