17 changed files with 297 additions and 37 deletions
@ -0,0 +1,45 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Service; |
||||||
|
|
||||||
|
use App\Repository\FeaturedAuthorRepository; |
||||||
|
use swentel\nostr\Key\Key; |
||||||
|
|
||||||
|
/** |
||||||
|
* NIP-05 / listed featured author rows (same shape as {@see \App\Controller\FeaturedAuthorsController}). |
||||||
|
*/ |
||||||
|
final class FeaturedAuthorListedRows |
||||||
|
{ |
||||||
|
public function __construct( |
||||||
|
private readonly FeaturedAuthorRepository $featuredAuthorRepository, |
||||||
|
private readonly CacheService $cacheService, |
||||||
|
) { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* @return list<array{npub: string, pubkey: string, display_name: string, picture: string, local_part: string}> |
||||||
|
*/ |
||||||
|
public function buildListedByLocalPartPage(int $limit, int $offset = 0): array |
||||||
|
{ |
||||||
|
$keys = new Key(); |
||||||
|
$authors = []; |
||||||
|
foreach ($this->featuredAuthorRepository->findListedOrderByLocalPartPaginated($limit, $offset) as $fa) { |
||||||
|
$npub = $keys->convertPublicKeyToBech32($fa->getPubkeyHex()); |
||||||
|
$bundle = $this->cacheService->getMetadataBundle($npub); |
||||||
|
$author = $bundle['content']; |
||||||
|
$displayName = trim((string) ($author->display_name ?? $author->name ?? '')); |
||||||
|
$picture = trim((string) ($author->picture ?? '')); |
||||||
|
$authors[] = [ |
||||||
|
'npub' => $npub, |
||||||
|
'pubkey' => strtolower($fa->getPubkeyHex()), |
||||||
|
'display_name' => $displayName, |
||||||
|
'picture' => $picture, |
||||||
|
'local_part' => $fa->getLocalPart(), |
||||||
|
]; |
||||||
|
} |
||||||
|
|
||||||
|
return $authors; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,27 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Twig; |
||||||
|
|
||||||
|
use App\Service\FeaturedAuthorListedRows; |
||||||
|
use Twig\Extension\AbstractExtension; |
||||||
|
use Twig\TwigFunction; |
||||||
|
|
||||||
|
/** Twig function for the left nav featured-author avatars (avoids Symfony UX component context quirks). */ |
||||||
|
final class SidebarFeaturedAuthorsExtension extends AbstractExtension |
||||||
|
{ |
||||||
|
public function __construct( |
||||||
|
private readonly FeaturedAuthorListedRows $featuredAuthorListedRows, |
||||||
|
) { |
||||||
|
} |
||||||
|
|
||||||
|
public function getFunctions(): array |
||||||
|
{ |
||||||
|
return [ |
||||||
|
new TwigFunction('sidebar_featured_author_rows', function (int $limit = 12): array { |
||||||
|
return $this->featuredAuthorListedRows->buildListedByLocalPartPage($limit, 0); |
||||||
|
}), |
||||||
|
]; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
{% if rows is defined and rows is not empty %} |
||||||
|
<section class="sidebar-featured-authors" aria-label="{{ 'sidebar.featured_authors'|trans }}"> |
||||||
|
<h2 class="sidebar-featured-authors__title">{{ 'sidebar.featured_authors'|trans }}</h2> |
||||||
|
<ul class="sidebar-featured-authors__grid" role="list"> |
||||||
|
{% for row in rows %} |
||||||
|
{% set _name = row.display_name|default('')|trim %} |
||||||
|
{% set _label = _name != '' ? _name : (row.npub|shortenNpub) %} |
||||||
|
{% set _avatar_src = row.picture|default('')|trim != '' ? row.picture : asset('icons/favicon-96x96.png') %} |
||||||
|
<li class="sidebar-featured-authors__item"> |
||||||
|
<a |
||||||
|
class="sidebar-featured-authors__link" |
||||||
|
href="{{ path('author-profile', { npub: row.npub }) }}" |
||||||
|
title="{{ _label|e('html_attr') }}" |
||||||
|
> |
||||||
|
<span class="sidebar-featured-authors__avatar"> |
||||||
|
<img |
||||||
|
src="{{ _avatar_src|e('html_attr') }}" |
||||||
|
alt="" |
||||||
|
width="40" |
||||||
|
height="40" |
||||||
|
loading="lazy" |
||||||
|
decoding="async" |
||||||
|
onerror="this.onerror=null;this.src='{{ asset('icons/favicon-96x96.png')|e('js') }}';" |
||||||
|
> |
||||||
|
</span> |
||||||
|
<span class="visually-hidden">{{ _label }}</span> |
||||||
|
</a> |
||||||
|
</li> |
||||||
|
{% endfor %} |
||||||
|
</ul> |
||||||
|
</section> |
||||||
|
{% endif %} |
||||||
Loading…
Reference in new issue