Skip to content

Commit

Permalink
feat: migrate forum post Edit page to React (#3021)
Browse files Browse the repository at this point in the history
  • Loading branch information
wescopeland authored Jan 17, 2025
1 parent 82e048b commit 229f3c0
Show file tree
Hide file tree
Showing 121 changed files with 4,984 additions and 69 deletions.
100 changes: 100 additions & 0 deletions app/Community/Actions/FetchDynamicShortcodeContentAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

declare(strict_types=1);

namespace App\Community\Actions;

use App\Data\UserData;
use App\Models\Achievement;
use App\Models\Game;
use App\Models\Ticket;
use App\Models\User;
use App\Platform\Data\AchievementData;
use App\Platform\Data\GameData;
use App\Platform\Data\TicketData;
use Illuminate\Support\Collection;

class FetchDynamicShortcodeContentAction
{
public function execute(
array $usernames = [],
array $ticketIds = [],
array $achievementIds = [],
array $gameIds = [],
): array {
$results = collect([
'users' => $this->fetchUsers($usernames),
'tickets' => $this->fetchTickets($ticketIds),
'achievements' => $this->fetchAchievements($achievementIds),
'games' => $this->fetchGames($gameIds),
]);

return $results->toArray();
}

/**
* @return Collection<int, UserData>
*/
private function fetchUsers(array $usernames): Collection
{
if (empty($usernames)) {
return collect();
}

$users = User::query()
->where(function ($query) use ($usernames) {
$query->whereIn('User', $usernames)
->orWhereIn('display_name', $usernames);
})
->get();

return $users->map(fn (User $user) => UserData::fromUser($user));
}

/**
* @return Collection<int, TicketData>
*/
private function fetchTickets(array $ticketIds): Collection
{
if (empty($ticketIds)) {
return collect();
}

return Ticket::with('achievement')
->whereIn('ID', $ticketIds)
->get()
->map(fn (Ticket $ticket) => TicketData::fromTicket($ticket)->include('state', 'ticketable'));
}

/**
* @return Collection<int, AchievementData>
*/
private function fetchAchievements(array $achievementIds): Collection
{
if (empty($achievementIds)) {
return collect();
}

return Achievement::whereIn('ID', $achievementIds)
->get()
->map(fn (Achievement $achievement) => AchievementData::fromAchievement($achievement)->include(
'badgeUnlockedUrl',
'points'
));
}

/**
* @return Collection<int, GameData>
*/
private function fetchGames(array $gameIds): Collection
{
if (empty($gameIds)) {
return collect();
}

return Game::with('system')
->whereIn('ID', $gameIds)
->get()
->map(fn (Game $game) => GameData::fromGame($game)->include('badgeUrl', 'system.name'));
}
}
57 changes: 57 additions & 0 deletions app/Community/Controllers/Api/ForumTopicCommentApiController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

declare(strict_types=1);

namespace App\Community\Controllers\Api;

use App\Community\Actions\FetchDynamicShortcodeContentAction;
use App\Community\Requests\PreviewForumPostRequest;
use App\Community\Requests\UpdateForumTopicCommentRequest;
use App\Http\Controller;
use App\Models\ForumTopicComment;
use App\Support\Shortcode\Shortcode;
use Illuminate\Http\JsonResponse;

class ForumTopicCommentApiController extends Controller
{
public function store(): void
{
}

public function update(
UpdateForumTopicCommentRequest $request,
ForumTopicComment $comment
): JsonResponse {
$this->authorize('update', $comment);

// Take any RA links and convert them to relevant shortcodes.
// eg: "https://retroachievements.org/game/1" --> "[game=1]"
$newPayload = normalize_shortcodes($request->input('body'));

// Convert [user=$user->username] to [user=$user->id].
$newPayload = Shortcode::convertUserShortcodesToUseIds($newPayload);

$comment->body = $newPayload;
$comment->save();

return response()->json(['success' => true]);
}

public function destroy(): void
{
}

public function preview(
PreviewForumPostRequest $request,
FetchDynamicShortcodeContentAction $action
): JsonResponse {
$entities = $action->execute(
usernames: $request->input('usernames'),
ticketIds: $request->input('ticketIds'),
achievementIds: $request->input('achievementIds'),
gameIds: $request->input('gameIds'),
);

return response()->json($entities);
}
}
22 changes: 18 additions & 4 deletions app/Community/Controllers/ForumTopicCommentController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@

use App\Community\Actions\AddCommentAction;
use App\Community\Actions\GetUrlToCommentDestinationAction;
use App\Community\Actions\ReplaceUserShortcodesWithUsernamesAction;
use App\Community\Requests\ForumTopicCommentRequest;
use App\Data\EditForumTopicCommentPagePropsData;
use App\Data\ForumTopicCommentData;
use App\Models\ForumTopic;
use App\Models\ForumTopicComment;
use Illuminate\Contracts\View\View;
use Illuminate\Http\RedirectResponse;
use Inertia\Inertia;
use Inertia\Response as InertiaResponse;

class ForumTopicCommentController extends CommentController
{
Expand Down Expand Up @@ -44,12 +48,22 @@ public function store(
// ->with('success', $this->resourceActionSuccessMessage('comment', 'create'));
}

public function edit(ForumTopicComment $comment): View
public function edit(ForumTopicComment $comment): InertiaResponse
{
$this->authorize('update', $comment);

return view('forum-topic-comment.edit')
->with('comment', $comment);
// "[user=1]" -> "[user=Scott]"
$comment->body = (new ReplaceUserShortcodesWithUsernamesAction())->execute($comment->body);

$props = new EditForumTopicCommentPagePropsData(
forumTopicComment: ForumTopicCommentData::from($comment)->include(
'forumTopic',
'forumTopic.forum',
'forumTopic.forum.category',
),
);

return Inertia::render('forums/post/[comment]/edit', $props);
}

protected function update(
Expand Down
3 changes: 3 additions & 0 deletions app/Community/Enums/TicketState.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

namespace App\Community\Enums;

use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript]
abstract class TicketState
{
public const Closed = 0;
Expand Down
24 changes: 24 additions & 0 deletions app/Community/Requests/PreviewForumPostRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

declare(strict_types=1);

namespace App\Community\Requests;

use Illuminate\Foundation\Http\FormRequest;

class PreviewForumPostRequest extends FormRequest
{
public function rules(): array
{
return [
'usernames' => 'present|array',
'usernames.*' => 'string',
'ticketIds' => 'present|array',
'ticketIds.*' => 'integer',
'achievementIds' => 'present|array',
'achievementIds.*' => 'integer',
'gameIds' => 'present|array',
'gameIds.*' => 'integer',
];
}
}
23 changes: 23 additions & 0 deletions app/Community/Requests/UpdateForumTopicCommentRequest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace App\Community\Requests;

use App\Support\Rules\ContainsRegularCharacter;
use Illuminate\Foundation\Http\FormRequest;

class UpdateForumTopicCommentRequest extends FormRequest
{
public function rules(): array
{
return [
'body' => [
'required',
'string',
'max:60000',
new ContainsRegularCharacter(),
],
];
}
}
5 changes: 5 additions & 0 deletions app/Community/RouteServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use App\Community\Controllers\AchievementSetClaimController;
use App\Community\Controllers\Api\AchievementCommentApiController;
use App\Community\Controllers\Api\ActivePlayersApiController;
use App\Community\Controllers\Api\ForumTopicCommentApiController;
use App\Community\Controllers\Api\GameClaimsCommentApiController;
use App\Community\Controllers\Api\GameCommentApiController;
use App\Community\Controllers\Api\GameHashesCommentApiController;
Expand Down Expand Up @@ -68,6 +69,9 @@ protected function mapWebRoutes(): void
Route::group(['prefix' => 'internal-api'], function () {
Route::post('achievement/{achievement}/comment', [AchievementCommentApiController::class, 'store'])->name('api.achievement.comment.store');

Route::post('forums/post/preview', [ForumTopicCommentApiController::class, 'preview'])->name('api.forum-topic-comment.preview');
Route::patch('forums/post/{comment}', [ForumTopicCommentApiController::class, 'update'])->name('api.forum-topic-comment.update');

Route::post('game/{game}/claims/comment', [GameClaimsCommentApiController::class, 'store'])->name('api.game.claims.comment.store');
Route::post('game/{game}/comment', [GameCommentApiController::class, 'store'])->name('api.game.comment.store');
Route::post('game/{game}/hashes/comment', [GameHashesCommentApiController::class, 'store'])->name('api.game.hashes.comment.store');
Expand Down Expand Up @@ -114,6 +118,7 @@ protected function mapWebRoutes(): void
Route::get('user/{user}/moderation-comments', [UserModerationCommentController::class, 'index'])->name('user.moderation-comment.index');

Route::get('forums/recent-posts', [ForumTopicController::class, 'recentPosts'])->name('forum.recent-posts');
Route::get('forums/post/{comment}/edit2', [ForumTopicCommentController::class, 'edit'])->name('forum-topic-comment.edit');

Route::get('user/{user}/posts', [UserForumTopicCommentController::class, 'index'])->name('user.posts.index');
Route::get('user/{user}/achievement-checklist', [UserAchievementChecklistController::class, 'index'])->name('user.achievement-checklist');
Expand Down
17 changes: 17 additions & 0 deletions app/Data/EditForumTopicCommentPagePropsData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace App\Data;

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

#[TypeScript('EditForumTopicCommentPageProps')]
class EditForumTopicCommentPagePropsData extends Data
{
public function __construct(
public ForumTopicCommentData $forumTopicComment,
) {
}
}
32 changes: 32 additions & 0 deletions app/Data/ForumCategoryData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

declare(strict_types=1);

namespace App\Data;

use App\Models\ForumCategory;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Lazy;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript('ForumCategory')]
class ForumCategoryData extends Data
{
public function __construct(
public int $id,
public string $title,
public Lazy|string $description,
public Lazy|int $orderColumn,
) {
}

public static function fromForumCategory(ForumCategory $category): self
{
return new self(
id: $category->id,
title: $category->title,
description: Lazy::create(fn () => $category->description),
orderColumn: Lazy::create(fn () => $category->order_column),
);
}
}
34 changes: 34 additions & 0 deletions app/Data/ForumData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

declare(strict_types=1);

namespace App\Data;

use App\Models\Forum;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Lazy;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;

#[TypeScript('Forum')]
class ForumData extends Data
{
public function __construct(
public int $id,
public string $title,
public Lazy|string $description,
public Lazy|int $orderColumn,
public Lazy|ForumCategoryData $category,
) {
}

public static function fromForum(Forum $forum): self
{
return new self(
id: $forum->id,
title: $forum->title,
description: Lazy::create(fn () => $forum->description),
orderColumn: Lazy::create(fn () => $forum->order_column),
category: Lazy::create(fn () => ForumCategoryData::fromForumCategory($forum->category)),
);
}
}
Loading

0 comments on commit 229f3c0

Please sign in to comment.