Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: migrate player game activity page to React #3006

Merged
8 changes: 4 additions & 4 deletions app/Enums/PlayerGameActivityEventType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@

namespace App\Enums;

abstract class PlayerGameActivityEventType
enum PlayerGameActivityEventType: string
{
public const Unlock = 'unlock';
case Unlock = 'unlock';

public const RichPresence = 'rich-presence';
case RichPresence = 'rich-presence';

public const Custom = 'custom';
case Custom = 'custom';
}
23 changes: 11 additions & 12 deletions app/Enums/PlayerGameActivitySessionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,23 @@

namespace App\Enums;

abstract class PlayerGameActivitySessionType
enum PlayerGameActivitySessionType: string
{
public const Player = 'player-session';
case Player = 'player-session';

public const Reconstructed = 'reconstructed';
case Reconstructed = 'reconstructed';

public const ManualUnlock = 'manual-unlock';
case ManualUnlock = 'manual-unlock';

public const TicketCreated = 'ticket-created';
case TicketCreated = 'ticket-created';

public static function toString(string $type): string
public function label(): string
{
return match ($type) {
PlayerGameActivitySessionType::Player => "Player Session",
PlayerGameActivitySessionType::Reconstructed => "Reconstructed Session",
PlayerGameActivitySessionType::ManualUnlock => "Manual Unlock",
PlayerGameActivitySessionType::TicketCreated => "Ticket Created",
default => $type,
return match ($this) {
self::Player => 'Player Session',
self::Reconstructed => 'Reconstructed Session',
self::ManualUnlock => 'Manual Unlock',
self::TicketCreated => 'Ticket Created',
};
}
}
115 changes: 115 additions & 0 deletions app/Platform/Actions/BuildPlayerGameActivityDataAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php

declare(strict_types=1);

namespace App\Platform\Actions;

use App\Models\Achievement;
use App\Models\Game;
use App\Models\User;
use App\Platform\Data\AchievementData;
use App\Platform\Data\GameHashData;
use App\Platform\Data\ParsedUserAgentData;
use App\Platform\Data\PlayerGameActivityData;
use App\Platform\Data\PlayerGameActivityEventData;
use App\Platform\Data\PlayerGameActivitySessionData;
use App\Platform\Data\PlayerGameActivitySummaryData;
use App\Platform\Data\PlayerGameClientBreakdownData;
use App\Platform\Services\PlayerGameActivityService;
use App\Platform\Services\UserAgentService;

class BuildPlayerGameActivityDataAction
{
public function __construct(
protected PlayerGameActivityService $playerGameActivityService,
protected UserAgentService $userAgentService,
) {
}

public function execute(User $user, Game $game): PlayerGameActivityData
{
$this->playerGameActivityService->initialize($user, $game);
$summary = $this->playerGameActivityService->summarize();

/**
* @var array<int, array{
* events: array<int, array{
* achievement?: array{
* ID: int,
* Title: string,
* Description: string,
* Points: int,
* TrueRatio: float,
* BadgeName: string,
* Flags: int
* }
* }>,
* userAgent?: string,
* playerSession?: array{gameHash?: mixed}
* }> $sessions
*/
$sessions = $this->playerGameActivityService->sessions;

$mappedSessions = array_map(function (array $session): PlayerGameActivitySessionData {
/** @var array<int, array{achievement?: array}> $events */
$events = $session['events'];

$mappedEvents = array_map(function (array $event): PlayerGameActivityEventData {
if (isset($event['achievement'])) {
// Create a temporary Achievement model for the transformation.
$achievement = (new Achievement())->forceFill([
'ID' => $event['achievement']['ID'],
'Title' => $event['achievement']['Title'],
'Description' => $event['achievement']['Description'],
'Points' => $event['achievement']['Points'],
'TrueRatio' => $event['achievement']['Points'],
'BadgeName' => $event['achievement']['BadgeName'],
'Flags' => $event['achievement']['Flags'],
]);

$event['achievement'] = AchievementData::fromAchievement($achievement)->include(
'badgeUnlockedUrl',
'flags',
'points',
);
}

return PlayerGameActivityEventData::from($event);
}, $events);

$parsedUserAgent = isset($session['userAgent']) && is_string($session['userAgent'])
? ParsedUserAgentData::from($this->userAgentService->decode($session['userAgent']))
: null;

$gameHash = isset($session['playerSession']['gameHash'])
? GameHashData::fromGameHash($session['playerSession']['gameHash'])
->include('isMultiDisc')
: null;

return PlayerGameActivitySessionData::from([
...$session,
'events' => $mappedEvents,
'parsedUserAgent' => $parsedUserAgent,
'gameHash' => $gameHash,
]);
}, $sessions);

/** @var array<string, array{clientIdentifier: string}> $clientBreakdownData */
$clientBreakdownData = $this->playerGameActivityService->getClientBreakdown($this->userAgentService);

$clientBreakdown = array_map(
fn (array $client, string $identifier): PlayerGameClientBreakdownData => PlayerGameClientBreakdownData::from([
...$client,
'clientIdentifier' => $identifier,
]),
$clientBreakdownData,
array_keys($clientBreakdownData)
);

return new PlayerGameActivityData(
sessions: $mappedSessions,
clientBreakdown: array_values($clientBreakdown),
summarizedActivity: new PlayerGameActivitySummaryData(...$summary),
);
}
}
28 changes: 28 additions & 0 deletions app/Platform/Controllers/PlayerGameController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,24 @@

namespace App\Platform\Controllers;

use App\Data\UserData;
use App\Http\Controller;
use App\Models\Game;
use App\Models\PlayerGame;
use App\Models\System;
use App\Models\User;
use App\Platform\Actions\BuildPlayerGameActivityDataAction;
use App\Platform\Actions\ResetPlayerProgressAction;
use App\Platform\Data\GameData;
use App\Platform\Data\PlayerGameActivityPagePropsData;
use App\Platform\Data\PlayerGameData;
use App\Platform\Data\PlayerResettableGameAchievementData;
use App\Platform\Data\PlayerResettableGameData;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Inertia\Inertia;
use Inertia\Response as InertiaResponse;

class PlayerGameController extends Controller
{
Expand Down Expand Up @@ -99,6 +106,26 @@ public function destroy(Request $request, Game $game): JsonResponse
return response()->json(['message' => __('legacy.success.reset')]);
}

public function activity(
User $user,
Game $game,
BuildPlayerGameActivityDataAction $buildPlayerGameActivityData
): InertiaResponse {
$playerGame = $user->playerGames()->whereGameId($game->id)->first();
// TODO rename to viewSessionHistory
$this->authorize('viewSessionHistory2', [PlayerGame::class, $playerGame]);

$props = new PlayerGameActivityPagePropsData(
player: UserData::fromUser($user),
game: GameData::from($game)->include('achievementsPublished', 'badgeUrl'),
playerGame: $playerGame ? PlayerGameData::fromPlayerGame($playerGame) : null,
activity: $buildPlayerGameActivityData->execute($user, $game),
);

return Inertia::render('user/[user]/game/[game]/activity', $props);
}

// TODO move to an api controller
public function resettableGames(Request $request): JsonResponse
{
/** @var User $user */
Expand All @@ -125,6 +152,7 @@ public function resettableGames(Request $request): JsonResponse
return response()->json(['results' => $resettableGames]);
}

// TODO move to an api controller
public function resettableGameAchievements(Request $request, Game $game): JsonResponse
{
/** @var User $user */
Expand Down
3 changes: 3 additions & 0 deletions app/Platform/Data/AchievementData.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use App\Models\Achievement;
use App\Models\PlayerAchievement;
use App\Platform\Enums\AchievementFlag;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Lazy;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;
Expand All @@ -19,6 +20,7 @@ public function __construct(
public Lazy|string $description,
public Lazy|string $badgeUnlockedUrl,
public Lazy|string $badgeLockedUrl,
public Lazy|AchievementFlag $flags,
public Lazy|GameData $game,
public Lazy|string $unlockedAt,
public Lazy|string $unlockedHardcoreAt,
Expand All @@ -38,6 +40,7 @@ public static function fromAchievement(
description: Lazy::create(fn () => $achievement->description),
badgeUnlockedUrl: Lazy::create(fn () => $achievement->badge_unlocked_url),
badgeLockedUrl: Lazy::create(fn () => $achievement->badge_locked_url),
flags: Lazy::create(fn () => AchievementFlag::from($achievement->Flags)),
game: Lazy::create(fn () => GameData::fromGame($achievement->game)),
unlockedAt: Lazy::create(fn () => $playerAchievement?->unlocked_at),
unlockedHardcoreAt: Lazy::create(fn () => $playerAchievement?->unlocked_hardcore_at),
Expand Down
3 changes: 3 additions & 0 deletions app/Platform/Data/GameHashData.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use App\Models\GameHash;
use Illuminate\Database\Eloquent\Collection;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Lazy;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;
use Spatie\TypeScriptTransformer\Attributes\TypeScriptType;

Expand All @@ -20,6 +21,7 @@ public function __construct(
#[TypeScriptType('App\\Platform\\Data\\GameHashLabelData[]')]
public array $labels,
public ?string $patchUrl,
public Lazy|bool $isMultiDisc,
) {
}

Expand All @@ -31,6 +33,7 @@ public static function fromGameHash(GameHash $gameHash): self
name: $gameHash->name,
labels: GameHashLabelData::fromLabelsString($gameHash->labels),
patchUrl: $gameHash->patch_url,
isMultiDisc: Lazy::create(fn () => $gameHash->isMultiDiscGameHash()),
);
}

Expand Down
22 changes: 22 additions & 0 deletions app/Platform/Data/ParsedUserAgentData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types=1);

namespace App\Platform\Data;

use Spatie\LaravelData\Data;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript('ParsedUserAgent')]
class ParsedUserAgentData extends Data
{
public function __construct(
public string $client,
public string $clientVersion,
public ?string $os = null,
public ?string $integrationVersion = null,
public ?array $extra = null,
public ?string $clientVariation = null,
) {
}
}
23 changes: 23 additions & 0 deletions app/Platform/Data/PlayerGameActivityData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace App\Platform\Data;

use Spatie\LaravelData\Data;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript('PlayerGameActivity')]
class PlayerGameActivityData extends Data
{
public function __construct(
public PlayerGameActivitySummaryData $summarizedActivity,

/** @var PlayerGameActivitySessionData[] */
public array $sessions,

/** @var PlayerGameClientBreakdownData[] */
public array $clientBreakdown,
) {
}
}
29 changes: 29 additions & 0 deletions app/Platform/Data/PlayerGameActivityEventData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace App\Platform\Data;

use App\Data\UserData;
use App\Enums\PlayerGameActivityEventType;
use Carbon\Carbon;
use Spatie\LaravelData\Data;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript('PlayerGameActivityEvent')]
class PlayerGameActivityEventData extends Data
{
public function __construct(
public PlayerGameActivityEventType $type,
/** Rich presence, etc. */
public ?string $description = null,
public ?string $header = null,
public ?Carbon $when = null,
public ?int $id = null,
public ?bool $hardcore = null,
public ?AchievementData $achievement = null,
public ?UserData $unlocker = null,
public ?bool $hardcoreLater = null,
) {
}
}
21 changes: 21 additions & 0 deletions app/Platform/Data/PlayerGameActivityPagePropsData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace App\Platform\Data;

use App\Data\UserData;
use Spatie\LaravelData\Data;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript('PlayerGameActivityPageProps')]
class PlayerGameActivityPagePropsData extends Data
{
public function __construct(
public UserData $player,
public GameData $game,
public ?PlayerGameData $playerGame,
public PlayerGameActivityData $activity,
) {
}
}
Loading
Loading