7 changed files with 169 additions and 4 deletions
@ -0,0 +1,59 @@
@@ -0,0 +1,59 @@
|
||||
<?php |
||||
|
||||
namespace App\Twig\Components; |
||||
|
||||
use App\Entity\Event; |
||||
use Doctrine\ORM\EntityManagerInterface; |
||||
use Symfony\UX\TwigComponent\Attribute\AsTwigComponent; |
||||
|
||||
#[AsTwigComponent('Organisms:ReadingListList')] |
||||
final class ReadingListList |
||||
{ |
||||
public int $limit = 10; |
||||
|
||||
public function __construct(private readonly EntityManagerInterface $em) |
||||
{ |
||||
} |
||||
|
||||
/** |
||||
* @return array<int, array{title:string, slug:string, createdAt:int, pubkey:string}> |
||||
*/ |
||||
public function getLists(): array |
||||
{ |
||||
$repo = $this->em->getRepository(Event::class); |
||||
// Fetch more than we need to allow collapsing by slug |
||||
/** @var Event[] $events */ |
||||
$events = $repo->findBy(['kind' => 30040], ['created_at' => 'DESC'], 200); |
||||
|
||||
$out = []; |
||||
$seen = []; |
||||
foreach ($events as $ev) { |
||||
$tags = $ev->getTags(); |
||||
$isReadingList = false; |
||||
$title = null; $slug = null; |
||||
foreach ($tags as $t) { |
||||
if (!is_array($t)) continue; |
||||
if (($t[0] ?? null) === 'type' && ($t[1] ?? null) === 'reading-list') { $isReadingList = true; } |
||||
if (($t[0] ?? null) === 'title') { $title = (string)$t[1]; } |
||||
if (($t[0] ?? null) === 'd') { $slug = (string)$t[1]; } |
||||
} |
||||
if (!$isReadingList) continue; |
||||
// Require slug; skip malformed events without slug |
||||
if (!$slug) continue; |
||||
|
||||
// Collapse newest by slug |
||||
if (isset($seen[$slug])) continue; |
||||
$seen[$slug] = true; |
||||
|
||||
$out[] = [ |
||||
'title' => $title ?: '(untitled)', |
||||
'slug' => $slug, |
||||
'createdAt' => $ev->getCreatedAt(), |
||||
'pubkey' => $ev->getPubkey(), |
||||
]; |
||||
if (count($out) >= $this->limit) break; |
||||
} |
||||
|
||||
return $out; |
||||
} |
||||
} |
||||
@ -0,0 +1,16 @@
@@ -0,0 +1,16 @@
|
||||
<div {{ attributes }}> |
||||
{% set items = this.lists %} |
||||
{% if items is not empty %} |
||||
<ul class="list-unstyled small d-grid gap-2"> |
||||
{% for item in items %} |
||||
<li> |
||||
<a href="{{ path('magazine-category', { slug: item.slug }) }}">{{ item.title }}</a> |
||||
<div><small class="text-muted">{{ item.createdAt|date('Y-m-d') }}</small></div> |
||||
</li> |
||||
{% endfor %} |
||||
</ul> |
||||
{% else %} |
||||
<p><small>No reading lists yet.</small></p> |
||||
{% endif %} |
||||
</div> |
||||
|
||||
Loading…
Reference in new issue