diff --git a/assets/styles/app.css b/assets/styles/app.css index 830ff33..0f8b5b2 100644 --- a/assets/styles/app.css +++ b/assets/styles/app.css @@ -207,11 +207,6 @@ div:nth-child(odd) .featured-list { border-bottom: 1px solid var(--color-border); } -.featured-list .card-header img { - max-height: 500px; - aspect-ratio: 1; -} - .article-list .metadata { display: flex; flex-direction: row; diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index 1f87470..96c028d 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -4,6 +4,10 @@ declare(strict_types=1); namespace App\Controller; +use App\Entity\Event; +use App\Enum\KindsEnum; +use App\Service\RedisCacheService; +use Doctrine\ORM\EntityManagerInterface; use Elastica\Collapse; use Elastica\Query; use Elastica\Query\Terms; @@ -32,22 +36,7 @@ class DefaultController extends AbstractController #[Route('/', name: 'home')] public function index(): Response { - $cacheKey = 'home-latest-articles'; - $latest = $this->redisCache->get($cacheKey, function (ItemInterface $item) { - $item->expiresAfter(13600); // about 4 hours - // get latest articles - $q = new Query(); - $q->setSize(12); - $q->setSort(['createdAt' => ['order' => 'desc']]); - $col = new Collapse(); - $col->setFieldname('pubkey'); - $q->setCollapse($col); - return $this->finder->find($q); - }); - - return $this->render('home.html.twig', [ - 'latest' => $latest - ]); + return $this->render('home.html.twig'); } /** @@ -87,18 +76,25 @@ class DefaultController extends AbstractController return $this->finder->find($q); }); - return $this->render('home.html.twig', [ + return $this->render('pages/latest.html.twig', [ 'latest' => $latest ]); } /** + * Magazine front page: title, summary, category links, featured list. * @throws InvalidArgumentException */ #[Route('/mag/{mag}', name: 'magazine-index')] - public function magIndex($mag) : Response + public function magIndex(string $mag, RedisCacheService $redisCache) : Response { - return new Response('Not implemented yet', 501); + // redis cache lookup of magazine index by slug + $magazine = $redisCache->getMagazineIndex($mag); + + return $this->render('magazine/magazine-front.html.twig', [ + 'magazine' => $magazine, + 'mag' => $mag, + ]); } /** @@ -106,9 +102,12 @@ class DefaultController extends AbstractController */ #[Route('/mag/{mag}/cat/{slug}', name: 'magazine-category')] public function magCategory($mag, $slug, CacheInterface $redisCache, + RedisCacheService $redisCacheService, FinderInterface $finder, LoggerInterface $logger): Response { + $magazine = $redisCacheService->getMagazineIndex($mag); + $catIndex = $redisCache->get('magazine-' . $slug, function (){ throw new Exception('Not found'); }); @@ -211,6 +210,8 @@ class DefaultController extends AbstractController } return $this->render('pages/category.html.twig', [ + 'mag' => $mag, + 'magazine' => $magazine, 'list' => $list, 'category' => $category, 'index' => $catIndex diff --git a/src/Service/RedisCacheService.php b/src/Service/RedisCacheService.php index 53b9e47..e80b9b0 100644 --- a/src/Service/RedisCacheService.php +++ b/src/Service/RedisCacheService.php @@ -2,8 +2,12 @@ namespace App\Service; +use App\Entity\Event; +use App\Enum\KindsEnum; +use Doctrine\ORM\EntityManagerInterface; use Psr\Cache\InvalidArgumentException; use Psr\Log\LoggerInterface; +use Symfony\Component\HttpFoundation\Response; use Symfony\Contracts\Cache\CacheInterface; use Symfony\Contracts\Cache\ItemInterface; @@ -13,6 +17,7 @@ readonly class RedisCacheService public function __construct( private NostrClient $nostrClient, private CacheInterface $redisCache, + private EntityManagerInterface $entityManager, private LoggerInterface $logger ) { @@ -70,18 +75,37 @@ readonly class RedisCacheService /** * Get a magazine index object by key. - * @param string $key + * @param string $slug * @return object|null + * @throws InvalidArgumentException */ - public function getMagazineIndex(string $key): ?object + public function getMagazineIndex(string $slug): ?object { - try { - $item = $this->redisCache->getItem($key); - return $item->isHit() ? $item->get() : null; - } catch (\Exception $e) { - $this->logger->error('Error fetching magazine index.', ['exception' => $e]); - return null; - } + // redis cache lookup of magazine index by slug + $key = 'magazine-index-' . $slug; + return $this->redisCache->get($key, function (ItemInterface $item) use ($slug) { + $item->expiresAfter(3600); // 1 hour + + $nzines = $this->entityManager->getRepository(Event::class)->findBy(['kind' => KindsEnum::PUBLICATION_INDEX]); + // filter, only keep type === magazine and slug === $mag + $nzines = array_filter($nzines, function ($index) use ($slug) { + // look for slug + return $index->getSlug() === $slug; + }); + + if (count($nzines) === 0) { + return new Response('Magazine not found', 404); + } + // sort by createdAt, keep newest + usort($nzines, function ($a, $b) { + return $b->getCreatedAt() <=> $a->getCreatedAt(); + }); + $nzine = array_pop($nzines); + + $this->logger->info('Magazine lookup', ['mag' => $slug, 'found' => json_encode($nzine)]); + + return $nzine; + }); } /** diff --git a/templates/layout.html.twig b/templates/layout.html.twig index 35ba9e3..c50f900 100644 --- a/templates/layout.html.twig +++ b/templates/layout.html.twig @@ -17,6 +17,9 @@
  • {{ 'heading.search'|trans }}
  • +
  • + Latest +
  • {% block nav %}{% endblock %} diff --git a/templates/magazine/magazine-front.html.twig b/templates/magazine/magazine-front.html.twig new file mode 100644 index 0000000..d1b1df6 --- /dev/null +++ b/templates/magazine/magazine-front.html.twig @@ -0,0 +1,37 @@ +{% extends 'layout.html.twig' %} + +{% block body %} +
    +
    +

    {{ magazine.title|default(magazine.slug|default(mag)) }}

    + {% if magazine.summary is defined and magazine.summary %} +

    {{ magazine.summary }}

    + {% endif %} + {# Category links (from 'a' tags) #} + {% set categoryTags = [] %} + {% if magazine.tags is defined %} + {% for tag in magazine.tags %} + {% if tag[0] is defined and tag[0] == 'a' %} + {% set categoryTags = categoryTags|merge([tag]) %} + {% endif %} + {% endfor %} + {% endif %} + {% if categoryTags is not empty %} +
      + {% for catTag in categoryTags %} +
    • + {% endfor %} +
    + {% else %} +

    No categories yet.

    + {% endif %} +
    +
    +
    + {% if categoryTags is not empty %} + {% for cat in categoryTags %} + + {% endfor %} + {% endif %} +
    +{% endblock %} diff --git a/templates/pages/category.html.twig b/templates/pages/category.html.twig index 68c9994..f9dcf92 100644 --- a/templates/pages/category.html.twig +++ b/templates/pages/category.html.twig @@ -12,6 +12,32 @@ {% endblock %} {% block body %} +
    +
    +

    {{ magazine.title|default(magazine.slug|default(mag)) }}

    + {% if magazine.summary is defined and magazine.summary %} +

    {{ magazine.summary }}

    + {% endif %} + {# Category links (from 'a' tags) #} + {% set categoryTags = [] %} + {% if magazine.tags is defined %} + {% for tag in magazine.tags %} + {% if tag[0] is defined and tag[0] == 'a' %} + {% set categoryTags = categoryTags|merge([tag]) %} + {% endif %} + {% endfor %} + {% endif %} + {% if categoryTags is not empty %} +
      + {% for catTag in categoryTags %} +
    • + {% endfor %} +
    + {% else %} +

    No categories yet.

    + {% endif %} +
    +
    {% endblock %} diff --git a/templates/pages/latest.html.twig b/templates/pages/latest.html.twig index 286fb17..d7f8099 100644 --- a/templates/pages/latest.html.twig +++ b/templates/pages/latest.html.twig @@ -1,5 +1,7 @@ {% extends 'layout.html.twig' %} {% block body %} - +
    + +
    {% endblock %}