Skip to content

Commit

Permalink
refactor: improve type safety of forum Recent Posts page (#2663)
Browse files Browse the repository at this point in the history
  • Loading branch information
wescopeland authored Sep 6, 2024
1 parent a8043da commit d267bd9
Show file tree
Hide file tree
Showing 27 changed files with 108 additions and 127 deletions.
9 changes: 5 additions & 4 deletions app/Community/Controllers/ForumTopicController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace App\Community\Controllers;

use App\Community\Data\RecentPostsPagePropsData;
use App\Community\Requests\ForumTopicRequest;
use App\Data\ForumTopicData;
use App\Data\PaginatedData;
Expand Down Expand Up @@ -120,7 +121,7 @@ private function getTotalRecentForumTopics(int $permissions = Permissions::Unreg
->count();
}

public function recentlyActive(Request $request): InertiaResponse
public function recentPosts(Request $request): InertiaResponse
{
$offset = $request->input('page', 1) - 1;
$count = 25;
Expand Down Expand Up @@ -160,9 +161,9 @@ public function recentlyActive(Request $request): InertiaResponse

$paginatedTopics = PaginatedData::fromLengthAwarePaginator($paginator);

return Inertia::render('forums/recent-posts', [
'paginatedTopics' => $paginatedTopics,
]);
$props = new RecentPostsPagePropsData($paginatedTopics);

return Inertia::render('forums/recent-posts', $props);
}

private function getRecentForumTopics(int $page = 1, int $permissions = Permissions::Unregistered): array
Expand Down
18 changes: 18 additions & 0 deletions app/Community/Data/RecentPostsPagePropsData.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types=1);

namespace App\Community\Data;

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

#[TypeScript('RecentPostsPageProps<TItems = App.Data.ForumTopic>')]
class RecentPostsPagePropsData extends Data
{
public function __construct(
public PaginatedData $paginatedTopics
) {
}
}
4 changes: 2 additions & 2 deletions app/Community/RouteServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ protected function mapWebRoutes(): void
Route::middleware(['web', 'csp'])
->group(function () {
Route::middleware(['inertia'])->group(function () {
Route::get('settings', [UserSettingsController::class, 'show'])->name('settings.show');
Route::get('forums/recent-posts', [ForumTopicController::class, 'recentPosts'])->name('forum.recent-posts');

Route::get('forums/recent-posts', [ForumTopicController::class, 'recentlyActive'])->name('forum.recent-posts');
Route::get('settings', [UserSettingsController::class, 'show'])->name('settings.show');
});

/*
Expand Down
16 changes: 5 additions & 11 deletions app/Data/PaginatedData.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,19 @@

use Illuminate\Pagination\LengthAwarePaginator;
use Spatie\LaravelData\Data;
use Spatie\TypeScriptTransformer\Attributes\LiteralTypeScriptType;
use Spatie\TypeScriptTransformer\Attributes\TypeScript;
use Spatie\TypeScriptTransformer\Attributes\TypeScriptType;

// The TypeScript transformer is not capable of recognizing that this resource
// should accept a generic. We'll fix the type with an override in the front-end.
// We'll give the output type an "__UNSAFE" prefix as a warning that it shouldn't
// be directly used by developers.

/**
* @template T
*/
#[TypeScript('__UNSAFE_PaginatedData')]
#[TypeScript('PaginatedData<TItems>')]
class PaginatedData extends Data
{
public function __construct(
public int $currentPage,
public int $lastPage,
public int $perPage,
public int $total,
#[LiteralTypeScriptType('TItems[]')]
public array $items,

#[TypeScriptType([
Expand All @@ -38,8 +32,8 @@ public function __construct(
}

/**
* @param LengthAwarePaginator<T> $paginator
* @return self<T>
* @template TItems
* @param LengthAwarePaginator<TItems> $paginator
*/
public static function fromLengthAwarePaginator(LengthAwarePaginator $paginator): self
{
Expand Down
9 changes: 9 additions & 0 deletions resources/js/common/hooks/usePageProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { usePage } from '@inertiajs/react';

import type { AppGlobalProps } from '@/common/models';

export function usePageProps<TPageProps = unknown>() {
const { props } = usePage<AppGlobalProps & TPageProps>();

return props;
}
1 change: 0 additions & 1 deletion resources/js/common/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@ export * from './app-global-props.model';
export * from './app-page.model';
export * from './avatar-size.model';
export * from './laravel-validation-error.model';
export * from './paginated-data.model';
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { render, screen } from '@/test';

import { RecentPostsBreadcrumbs } from './RecentPostsBreadcrumbs';
import { ForumBreadcrumbs } from './ForumBreadcrumbs';

describe('Component: RecentPostsBreadcrumbs', () => {
describe('Component: ForumBreadcrumbs', () => {
it('renders without crashing', () => {
// ARRANGE
const { container } = render(<RecentPostsBreadcrumbs />);
const { container } = render(<ForumBreadcrumbs currentPageLabel="Recent Posts" />);

// ASSERT
expect(container).toBeTruthy();
});

it('has a link back to the forum index', () => {
// ARRANGE
render(<RecentPostsBreadcrumbs />);
render(<ForumBreadcrumbs currentPageLabel="Recent Posts" />);

// ASSERT
const forumIndexLinkEl = screen.getByRole('link', { name: /forum index/i });
Expand All @@ -23,7 +23,7 @@ describe('Component: RecentPostsBreadcrumbs', () => {

it('communicates the active link in an accessible manner', () => {
// ARRANGE
render(<RecentPostsBreadcrumbs />);
render(<ForumBreadcrumbs currentPageLabel="Recent Posts" />);

// ASSERT
const activeLinkEl = screen.getByRole('link', { name: /recent posts/i });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@ import {
BaseBreadcrumbSeparator,
} from '@/common/components/+vendor/BaseBreadcrumb';

export const RecentPostsBreadcrumbs: FC = () => {
// TODO support ForumCategory and Forum
interface ForumBreadcrumbsProps {
currentPageLabel: string;
}

export const ForumBreadcrumbs: FC<ForumBreadcrumbsProps> = ({ currentPageLabel }) => {
return (
<div className="navpath">
<div className="navpath mb-3 hidden sm:block">
<BaseBreadcrumb>
<BaseBreadcrumbList>
<BaseBreadcrumbItem>
Expand All @@ -21,7 +26,7 @@ export const RecentPostsBreadcrumbs: FC = () => {
<BaseBreadcrumbSeparator />

<BaseBreadcrumbItem>
<BaseBreadcrumbPage>Recent Posts</BaseBreadcrumbPage>
<BaseBreadcrumbPage>{currentPageLabel}</BaseBreadcrumbPage>
</BaseBreadcrumbItem>
</BaseBreadcrumbList>
</BaseBreadcrumb>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ForumBreadcrumbs';
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createRecentActiveForumTopic } from '@/features/forums/models';
import { render, screen } from '@/test';
import { createRecentActiveForumTopic } from '@/test/factories';

import { AggregateRecentPostLinks } from './AggregateRecentPostLinks';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import type { FC } from 'react';

import type { RecentActiveForumTopic } from '@/features/forums/models';

interface AggregateRecentPostLinksProps {
topic: RecentActiveForumTopic;
topic: App.Data.ForumTopic;
}

export const AggregateRecentPostLinks: FC<AggregateRecentPostLinksProps> = ({ topic }) => {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { createPaginatedData } from '@/common/models';
import { createRecentActiveForumTopic } from '@/features/forums/models';
import { render, screen } from '@/test';
import { createPaginatedData, createRecentActiveForumTopic } from '@/test/factories';

import { RecentPostsCards } from './RecentPostsCards';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,30 @@
import { usePage } from '@inertiajs/react';
import type { FC } from 'react';

import { UserAvatar } from '@/common/components/UserAvatar';
import type { RecentPostsPageProps } from '@/features/forums/models';
import { usePageProps } from '@/common/hooks/usePageProps';

import { AggregateRecentPostLinks } from '../AggregateRecentPostLinks';
import { PostTimestamp } from '../PostTimestamp';

export const RecentPostsCards: FC = () => {
const { props } = usePage<RecentPostsPageProps>();

const { auth, paginatedTopics } = props;
const { auth, paginatedTopics } = usePageProps<App.Community.Data.RecentPostsPageProps>();

return (
<div className="flex flex-col gap-y-2">
{paginatedTopics.items.map((topic) => (
<div key={`card-${topic.latestComment.id}`} className="embedded">
<div key={`card-${topic?.latestComment?.id}`} className="embedded">
<div className="relative flex justify-between">
<div className="flex flex-col gap-y-1">
<UserAvatar displayName={topic.latestComment.user.displayName} size={16} />

<span className="smalldate">
<PostTimestamp
asAbsoluteDate={auth?.user.preferences.prefersAbsoluteDates ?? false}
postedAt={topic.latestComment.createdAt}
/>
</span>
<UserAvatar displayName={topic?.latestComment?.user.displayName ?? ''} size={16} />

{topic.latestComment?.createdAt ? (
<span className="smalldate">
<PostTimestamp
asAbsoluteDate={auth?.user.preferences.prefersAbsoluteDates ?? false}
postedAt={topic.latestComment.createdAt}
/>
</span>
) : null}
</div>

<AggregateRecentPostLinks topic={topic} />
Expand All @@ -35,13 +34,13 @@ export const RecentPostsCards: FC = () => {
<p className="truncate">
in{' '}
<a
href={`/viewtopic.php?t=${topic.id}&c=${topic.latestComment.id}#${topic.latestComment.id}`}
href={`/viewtopic.php?t=${topic.id}&c=${topic.latestComment?.id}#${topic.latestComment?.id}`}
>
{topic.title}
</a>
</p>

<p className="line-clamp-3 text-xs">{topic.latestComment.body}</p>
<p className="line-clamp-3 text-xs">{topic.latestComment?.body}</p>
</div>
</div>
))}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import type { FC } from 'react';

import { RecentPostsBreadcrumbs } from './RecentPostsBreadcrumbs';
import { ForumBreadcrumbs } from '../ForumBreadcrumbs';
import { RecentPostsCards } from './RecentPostsCards';
import { RecentPostsPagination } from './RecentPostsPagination';
import { RecentPostsTable } from './RecentPostsTable';

export const RecentPostsMainRoot: FC = () => {
return (
<div>
<RecentPostsBreadcrumbs />
<ForumBreadcrumbs currentPageLabel="Recent Posts" />

<h1 className="w-full">Recent Posts</h1>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { faker } from '@faker-js/faker';

import { createPaginatedData } from '@/common/models';
import { render, screen } from '@/test';
import { createPaginatedData } from '@/test/factories';

import { RecentPostsPagination } from './RecentPostsPagination';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { usePage } from '@inertiajs/react';
import type { FC } from 'react';

import {
Expand All @@ -8,10 +7,10 @@ import {
BasePaginationNext,
BasePaginationPrevious,
} from '@/common/components/+vendor/BasePagination';
import type { RecentPostsPageProps } from '@/features/forums/models';
import { usePageProps } from '@/common/hooks/usePageProps';

export const RecentPostsPagination: FC = () => {
const { paginatedTopics } = usePage<RecentPostsPageProps>().props;
const { paginatedTopics } = usePageProps<App.Community.Data.RecentPostsPageProps>();

const {
perPage,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { createPaginatedData } from '@/common/models';
import { createRecentActiveForumTopic } from '@/features/forums/models';
import { render, screen } from '@/test';
import { createPaginatedData, createRecentActiveForumTopic } from '@/test/factories';

import { RecentPostsTable } from './RecentPostsTable';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { usePage } from '@inertiajs/react';
import type { FC } from 'react';

import { UserAvatar } from '@/common/components/UserAvatar';
import type { RecentPostsPageProps } from '@/features/forums/models';
import { usePageProps } from '@/common/hooks/usePageProps';

import { AggregateRecentPostLinks } from '../AggregateRecentPostLinks';
import { PostTimestamp } from '../PostTimestamp';

export const RecentPostsTable: FC = () => {
const { props } = usePage<RecentPostsPageProps>();

const { auth, paginatedTopics } = props;
const { auth, paginatedTopics } = usePageProps<App.Community.Data.RecentPostsPageProps>();

return (
<table className="table-highlight">
Expand All @@ -24,28 +21,30 @@ export const RecentPostsTable: FC = () => {

<tbody>
{paginatedTopics.items.map((topic) => (
<tr key={topic.latestComment.id}>
<tr key={topic.latestComment?.id}>
<td className="py-3">
<UserAvatar displayName={topic.latestComment.user.displayName} size={24} />
<UserAvatar displayName={topic.latestComment?.user.displayName ?? ''} size={24} />
</td>

<td>
<p className="flex items-center gap-x-2">
<a
href={`/viewtopic.php?t=${topic.id}&c=${topic.latestComment.id}#${topic.latestComment.id}`}
href={`/viewtopic.php?t=${topic.id}&c=${topic.latestComment?.id}#${topic.latestComment?.id}`}
>
{topic.title}
</a>
<span className="smalldate">
<PostTimestamp
asAbsoluteDate={auth?.user.preferences.prefersAbsoluteDates ?? false}
postedAt={topic.latestComment.createdAt}
/>
{topic.latestComment?.createdAt ? (
<PostTimestamp
asAbsoluteDate={auth?.user.preferences.prefersAbsoluteDates ?? false}
postedAt={topic.latestComment.createdAt}
/>
) : null}
</span>
</p>

<div className="comment text-overflow-wrap">
<p className="lg:line-clamp-2 xl:line-clamp-1">{topic.latestComment.body}</p>
<p className="lg:line-clamp-2 xl:line-clamp-1">{topic.latestComment?.body}</p>
</div>
</td>

Expand Down
2 changes: 0 additions & 2 deletions resources/js/features/forums/models/index.ts

This file was deleted.

Loading

0 comments on commit d267bd9

Please sign in to comment.