diff --git a/app/NativeComponents/Home.php b/app/NativeComponents/Home.php index 8326991..a5ee02f 100644 --- a/app/NativeComponents/Home.php +++ b/app/NativeComponents/Home.php @@ -49,17 +49,20 @@ class Home extends NativeComponent * de comunidade dev. Vem de docs/plans/2026-09-22-api-mobile-jwt.md * (feature "Timeline"). * - * @var array{author: string, timeAgo: string, body: string, codeFile: string, codeLang: string, codeLines: list, tags: list, likes: int, comments: int} + * @var array{author: string, role: ?string, timeAgo: string, body: string, code: ?array{file: string, lang: string, lines: list}, tags: list, likes: int, comments: int} */ public array $featuredPost = [ 'author' => 'time He4rt', + 'role' => 'CORE', 'timeAgo' => 'há 2h', 'body' => 'Refatoramos o pool de conexões do Postgres no gateway da API do heartdevs.com.', - 'codeFile' => 'config/database.php', - 'codeLang' => 'php', - 'codeLines' => [ - "'pool' => ['min' => 5, 'max' => 50],", - '// timeout ajustado pra reduzir o P99', + 'code' => [ + 'file' => 'config/database.php', + 'lang' => 'php', + 'lines' => [ + "'pool' => ['min' => 5, 'max' => 50],", + '// timeout ajustado pra reduzir o P99', + ], ], 'tags' => ['#backend', '#postgres', '#performance'], 'likes' => 84, diff --git a/app/NativeComponents/Timeline.php b/app/NativeComponents/Timeline.php index 082b26f..2136a9b 100644 --- a/app/NativeComponents/Timeline.php +++ b/app/NativeComponents/Timeline.php @@ -9,6 +9,99 @@ class Timeline extends NativeComponent { + public string $activeFilter = 'todos'; + + /** + * Mock — a Timeline real vem de docs/plans/2026-09-22-api-mobile-jwt.md + * no heartdevs.com (feature "Timeline"), ainda não existe API pra isso. + * + * @var list}, tags: list, likes: int, comments: int, category: string}> + */ + public array $posts = [ + [ + 'author' => 'time He4rt', + 'role' => 'CORE', + 'timeAgo' => 'há 2h', + 'body' => 'Refatoramos o pool de conexões do Postgres no gateway da API do heartdevs.com.', + 'code' => [ + 'file' => 'config/database.php', + 'lang' => 'php', + 'lines' => [ + "'pool' => ['min' => 5, 'max' => 50],", + '// timeout ajustado pra reduzir o P99', + ], + ], + 'tags' => ['#backend', '#postgres', '#performance'], + 'likes' => 84, + 'comments' => 23, + 'category' => 'projetos', + ], + [ + 'author' => 'maria.dev', + 'role' => null, + 'timeAgo' => 'há 5h', + 'body' => 'A turma de mentoria de Setembro está com vagas abertas! Inscrições abertas até o dia 30.', + 'code' => null, + 'tags' => ['#mentoria', '#carreira'], + 'likes' => 41, + 'comments' => 12, + 'category' => 'mentorias', + ], + [ + 'author' => 'time He4rt', + 'role' => 'CORE', + 'timeAgo' => 'ontem', + 'body' => 'Como fomos de 0 a 9 mil devs na comunidade — um resumo da jornada até aqui.', + 'code' => null, + 'tags' => [], + 'likes' => 156, + 'comments' => 37, + 'category' => 'geral', + ], + [ + 'author' => 'pedro.silva', + 'role' => null, + 'timeAgo' => 'há 2 dias', + 'body' => 'Lancei meu primeiro projeto open source usando Laravel + Livewire. Feedback é bem-vindo.', + 'code' => null, + 'tags' => ['#laravel', '#livewire', '#opensource'], + 'likes' => 62, + 'comments' => 19, + 'category' => 'projetos', + ], + [ + 'author' => 'time He4rt', + 'role' => 'CORE', + 'timeAgo' => 'há 3 dias', + 'body' => 'A He4rt Conf 2025 está confirmada pra Outubro, com inscrições abertas em breve.', + 'code' => null, + 'tags' => ['#he4rtconf'], + 'likes' => 203, + 'comments' => 58, + 'category' => 'eventos', + ], + ]; + + public function setFilter(string $filter): void + { + $this->activeFilter = $filter; + } + + /** + * @return list}, tags: list, likes: int, comments: int, category: string}> + */ + public function filteredPosts(): array + { + if ($this->activeFilter === 'todos') { + return $this->posts; + } + + return array_values(array_filter( + $this->posts, + fn (array $post): bool => $post['category'] === $this->activeFilter, + )); + } + public function navTitle(): string { return 'Timeline'; @@ -16,6 +109,8 @@ public function navTitle(): string public function render(): View { - return view('native.timeline'); + return view('native.timeline', [ + 'posts' => $this->filteredPosts(), + ]); } } diff --git a/resources/views/native/home.blade.php b/resources/views/native/home.blade.php index 91d3ecd..80aa273 100644 --- a/resources/views/native/home.blade.php +++ b/resources/views/native/home.blade.php @@ -89,56 +89,8 @@ - {{-- Post em destaque --}} - - - - - - - - {{ $featuredPost['author'] }} - - CORE - - - {{ $featuredPost['timeAgo'] }} - - - - {{ $featuredPost['body'] }} - - - - {{ $featuredPost['codeFile'] }} - {{ $featuredPost['codeLang'] }} - - @foreach ($featuredPost['codeLines'] as $line) - {{ $line }} - @endforeach - - - - @foreach ($featuredPost['tags'] as $tag) - - {{ $tag }} - - @endforeach - - - - - - {{ $featuredPost['likes'] }} - - - - {{ $featuredPost['comments'] }} - - - - - + {{-- Post em destaque — mesmo card reutilizável da Timeline --}} + @include('native.partials.post-card', ['post' => $featuredPost]) @foreach ($latestPosts as $post) diff --git a/resources/views/native/partials/post-card.blade.php b/resources/views/native/partials/post-card.blade.php new file mode 100644 index 0000000..55d81ef --- /dev/null +++ b/resources/views/native/partials/post-card.blade.php @@ -0,0 +1,63 @@ +@use('App\Icons\Ios') +@use('App\Icons\Android') + +{{-- + Card de post reutilizável da Timeline. Espera $post com as chaves: + author, role (nullable), timeAgo, body, code (nullable: file/lang/lines), + tags (array), likes, comments. +--}} + + + + + + + + {{ $post['author'] }} + @if (! empty($post['role'])) + + {{ $post['role'] }} + + @endif + + {{ $post['timeAgo'] }} + + + + {{ $post['body'] }} + + @if (! empty($post['code'])) + + + {{ $post['code']['file'] }} + {{ $post['code']['lang'] }} + + @foreach ($post['code']['lines'] as $line) + {{ $line }} + @endforeach + + @endif + + @if (! empty($post['tags'])) + + @foreach ($post['tags'] as $tag) + + {{ $tag }} + + @endforeach + + @endif + + + + + {{ $post['likes'] }} + + + + {{ $post['comments'] }} + + + + + diff --git a/resources/views/native/timeline.blade.php b/resources/views/native/timeline.blade.php index 54efa17..6e99597 100644 --- a/resources/views/native/timeline.blade.php +++ b/resources/views/native/timeline.blade.php @@ -1,10 +1,27 @@ @use('App\Icons\Ios') @use('App\Icons\Android') -@include('native.partials.coming-soon', [ - 'icon' => 'bubble.left.and.bubble.right.fill', - 'iosIcon' => Ios::BubbleLeftAndBubbleRightFill, - 'androidIcon' => Android::Forum, - 'title' => 'Timeline chegando em breve', - 'description' => "A gente tá construindo a timeline da comunidade — publicações, projetos e novidades dos devs He4rt, direto no app.", -]) + + + + + + + + + + + + + + @forelse ($posts as $post) + @include('native.partials.post-card', ['post' => $post]) + @empty + + + Nada por aqui ainda nessa categoria. + + @endforelse + + + diff --git a/tests/Feature/TimelineTest.php b/tests/Feature/TimelineTest.php new file mode 100644 index 0000000..7781cbf --- /dev/null +++ b/tests/Feature/TimelineTest.php @@ -0,0 +1,30 @@ +navTitle())->toBe('Timeline'); +}); + +it('shows every mocked post by default', function () { + Native::visit('/timeline') + ->assertSee('Refatoramos o pool de conexões do Postgres') + ->assertSee('turma de mentoria de Setembro') + ->assertSee('He4rt Conf 2025'); +}); + +it('narrows the feed when a category filter is selected', function () { + Native::visit('/timeline') + ->call('setFilter', 'mentorias') + ->assertSee('turma de mentoria de Setembro') + ->assertDontSee('He4rt Conf 2025'); +}); + +it('shows an empty state when a filter has no posts', function () { + Native::visit('/timeline') + ->call('setFilter', 'inexistente') + ->assertSee('Nada por aqui ainda nessa categoria'); +});