Browse Source

Revise routing, part 5

imwald
Nuša Pukšič 4 months ago
parent
commit
3101bd0fbb
  1. 35
      src/Controller/DefaultController.php
  2. 75
      src/Controller/ReadingListController.php
  3. 6
      templates/components/Organisms/MagazineHero.html.twig
  4. 15
      templates/components/Organisms/ReadingListList.html.twig
  5. 3
      templates/layout.html.twig
  6. 13
      templates/pages/latest.html.twig
  7. 21
      templates/pages/list.html.twig
  8. 2
      templates/pages/lists.html.twig
  9. 4
      templates/pages/newsstand.html.twig
  10. 4
      templates/reading_list/index.html.twig
  11. 2
      templates/reading_list/reading_setup.html.twig

35
src/Controller/DefaultController.php

@ -61,30 +61,6 @@ class DefaultController extends AbstractController @@ -61,30 +61,6 @@ class DefaultController extends AbstractController
return $this->render('pages/lists.html.twig');
}
/**
* @throws InvalidArgumentException
*/
#[Route('/latest', name: 'latest')]
public function latest() : 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('pages/latest.html.twig', [
'latest' => $latest
]);
}
/**
* Magazine front page: title, summary, category links, featured list.
* @throws InvalidArgumentException
@ -286,17 +262,6 @@ class DefaultController extends AbstractController @@ -286,17 +262,6 @@ class DefaultController extends AbstractController
]);
}
/**
* @throws InvalidArgumentException
*/
#[Route('/list/{slug}', name: 'reading-list')]
public function readingList($slug, CacheInterface $redisCache,
FinderInterface $finder,
LoggerInterface $logger): Response
{
return new Response('Not implemented yet', 501);
}
/**
* OG Preview endpoint for URLs

75
src/Controller/ReadingListController.php

@ -5,11 +5,19 @@ declare(strict_types=1); @@ -5,11 +5,19 @@ declare(strict_types=1);
namespace App\Controller;
use App\Entity\Event;
use App\Enum\KindsEnum;
use Doctrine\ORM\EntityManagerInterface;
use Elastica\Query;
use Elastica\Query\BoolQuery;
use Elastica\Query\Term;
use FOS\ElasticaBundle\Finder\FinderInterface;
use Psr\Cache\InvalidArgumentException;
use Psr\Log\LoggerInterface;
use swentel\nostr\Key\Key;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Contracts\Cache\CacheInterface;
class ReadingListController extends AbstractController
{
@ -19,7 +27,7 @@ class ReadingListController extends AbstractController @@ -19,7 +27,7 @@ class ReadingListController extends AbstractController
$lists = [];
$user = $this->getUser();
$pubkeyHex = null;
if ($user && method_exists($user, 'getUserIdentifier')) {
if ($user) {
try {
$key = new Key();
$pubkeyHex = $key->convertToHex($user->getUserIdentifier());
@ -59,6 +67,7 @@ class ReadingListController extends AbstractController @@ -59,6 +67,7 @@ class ReadingListController extends AbstractController
'summary' => $summary,
'slug' => $slug,
'createdAt' => $ev->getCreatedAt(),
'pubkey' => $ev->getPubkey(),
];
}
}
@ -74,4 +83,68 @@ class ReadingListController extends AbstractController @@ -74,4 +83,68 @@ class ReadingListController extends AbstractController
{
return $this->render('reading_list/compose.html.twig');
}
/**
*
* @throws InvalidArgumentException
*/
#[Route('/p/{pubkey}/list/{slug}', name: 'reading-list')]
public function readingList($pubkey, $slug, CacheInterface $redisCache,
EntityManagerInterface $em,
FinderInterface $finder,
LoggerInterface $logger): Response
{
$key = 'single-reading-list-' . $pubkey . '-' . $slug;
$logger->info(sprintf('Reading list: %s', $key));
$list = $redisCache->get($key, function() use ($em, $pubkey, $slug) {
// find reading list by pubkey+slug, kind 30040
$lists = $em->getRepository(Event::class)->findBy(['pubkey' => $pubkey, 'kind' => KindsEnum::PUBLICATION_INDEX]);
// filter by tag d = $slug
$lists = array_filter($lists, function($ev) use ($slug) {
return $ev->getSlug() === $slug;
});
// sort revisions and keep latest
usort($lists, function($a, $b) {
return $b->getCreatedAt() <=> $a->getCreatedAt();
});
return array_pop($lists);
});
// fetch articles listed in the list's a tags
$coordinates = []; // Store full coordinates (kind:author:slug)
// Extract category metadata and article coordinates
foreach ($list->getTags() as $tag) {
if ($tag[0] === 'a') {
$coordinates[] = $tag[1]; // Store the full coordinate
}
}
$articles = [];
if (count($coordinates) > 0) {
$boolQuery = new BoolQuery();
foreach ($coordinates as $coord) {
$parts = explode(':', $coord, 3);
[$kind, $author, $slug] = $parts;
$termQuery = new BoolQuery();
$termQuery->addMust(new Term(['kind' => (int)$kind]));
$termQuery->addMust(new Term(['pubkey' => strtolower($author)]));
$termQuery->addMust(new Term(['slug' => $slug]));
$boolQuery->addShould($termQuery);
}
$finalQuery = new Query($boolQuery);
$finalQuery->setSize(100); // Limit to 100 results
$results = $finder->find($finalQuery);
// Index results by their full coordinate for easy lookup
foreach ($results as $result) {
if ($result instanceof Event) {
$coordKey = sprintf('%d:%s:%s', $result->getKind(), strtolower($result->getPubkey()), $result->getSlug());
$articles[$coordKey] = $result;
}
}
}
return $this->render('pages/list.html.twig', [
'list' => $list,
'articles' => $articles,
]);
}
}

6
templates/components/Organisms/MagazineHero.html.twig

@ -1,11 +1,13 @@ @@ -1,11 +1,13 @@
<div {{ attributes }}>
<section class="d-flex gap-3 center ln-section--newsstand mb-3">
<div class="container mt-5 mb-5">
<div class="container mt-5">
<h1><a href="{{ path('magazine-index', {mag: mag}) }}">{{ magazine.title|default(magazine.slug|default(mag)) }}</a></h1>
{% if magazine.summary is defined and magazine.summary %}
<p class="eyebrow">{{ magazine.summary }}</p>
{% endif %}
<div>by <twig:Molecules:UserFromNpub :ident="magazine.pubkey" /></div>
</div>
<div class="container mb-5">
{# Use computed property via this.categoryTags #}
{% if this.categoryTags is not empty %}
<ul class="list-unstyled d-flex flex-row gap-3 justify-content-center mt-3">

15
templates/components/Organisms/ReadingListList.html.twig

@ -1,16 +1,17 @@ @@ -1,16 +1,17 @@
<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('reading-list', { slug: item.slug }) }}">{{ item.title }}</a>
<div><small class="text-muted">{{ item.createdAt|date('Y-m-d') }}</small></div>
</li>
<section class="d-flex gap-3 center">
<h1><a href="{{ path('reading-list', { slug: item.slug, pubkey: item.pubkey }) }}">{{ item.title }}</a></h1>
<p>by <twig:Molecules:UserFromNpub :ident="item.pubkey" /></p>
<small class="text-muted">{{ item.createdAt|date('Y-m-d') }}</small>
{% if item.summary is defined and item.summary %}
<p class="eyebrow">{{ item.summary }}</p>
{% endif %}
</section>
{% endfor %}
</ul>
{% else %}
<p><small>No reading lists yet.</small></p>
{% endif %}
</div>

3
templates/layout.html.twig

@ -17,9 +17,6 @@ @@ -17,9 +17,6 @@
<li>
<a href="{{ path('app_search_index') }}">{{ 'heading.search'|trans }}</a>
</li>
<li>
<a href="{{ path('latest') }}">Latest</a>
</li>
</ul>
<twig:UserMenu />
{% block nav %}{% endblock %}

13
templates/pages/latest.html.twig

@ -1,13 +0,0 @@ @@ -1,13 +0,0 @@
{% extends 'layout.html.twig' %}
{% block body %}
<section class="d-flex gap-3 center ln-section--newsstand mb-3">
<div class="container mt-5 mb-5">
<h1>Hot</h1>
<p class="eyebrow">off the presses</p>
</div>
</section>
<div class="w-container">
<twig:Organisms:CardList :list="latest" />
</div>
{% endblock %}

21
templates/pages/list.html.twig

@ -0,0 +1,21 @@ @@ -0,0 +1,21 @@
{% extends 'layout.html.twig' %}
{% block ogtags %}
<meta property="og:title" content="{{ list.title }} - Newsroom">
<meta property="og:type" content="website">
<meta property="og:url" content="{{ app.request.uri }}">
<meta property="og:description" content="{{ list.summary ?? '' }}">
<meta property="og:site_name" content="Newsroom">
{% endblock %}
{% block body %}
<section class="d-flex gap-3 center ln-section--newsstand mb-3">
<div class="container mt-5 mb-5">
<h1>{{ list.title }}</h1>
<p class="eyebrow">{{ list.summary }}</p>
</div>
</section>
<twig:Organisms:CardList :list="articles" class="article-list" />
{% endblock %}

2
templates/pages/lists.html.twig

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
{% extends 'layout.html.twig' %}
{% block body %}
<section class="d-flex gap-3 center ln-section--newsstand">
<section class="d-flex gap-3 center ln-section--newsstand mb-3">
<div class="container mt-5 mb-5">
<h1>Reading Lists</h1>
<p class="eyebrow">for collections, curations, courses and more</p>

4
templates/pages/newsstand.html.twig

@ -20,6 +20,6 @@ @@ -20,6 +20,6 @@
{% endblock %}
{% block aside %}
<h6>Lists</h6>
<twig:Organisms:ReadingListList />
{# <h6>Lists</h6>#}
{# <twig:Organisms:ReadingListList />#}
{% endblock %}

4
templates/reading_list/index.html.twig

@ -24,9 +24,9 @@ @@ -24,9 +24,9 @@
<div class="d-flex flex-row gap-2">
<a class="btn btn-sm btn-primary" href="{{ path('reading_list_compose') }}">Open Composer</a>
{% if item.slug %}
<a class="btn btn-sm btn-outline-primary" href="{{ path('reading-list', { slug: item.slug }) }}">View</a>
<a class="btn btn-sm btn-outline-primary" href="{{ path('reading-list', { slug: item.slug, pubkey: item.pubkey }) }}">View</a>
<span data-controller="copy-to-clipboard">
<span class="hidden" data-copy-to-clipboard-target="textToCopy">{{ absolute_url(path('reading-list', { slug: item.slug })) }}</span>
<span class="hidden" data-copy-to-clipboard-target="textToCopy">{{ absolute_url(path('reading-list', { slug: item.slug, pubkey: item.pubkey })) }}</span>
<button class="btn btn-sm btn-secondary"
data-copy-to-clipboard-target="copyButton"
data-action="click->copy-to-clipboard#copyToClipboard">Copy link</button>

2
templates/reading_list/reading_setup.html.twig

@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
{{ form_row(form.summary) }}
{{ form_row(form.tags) }}
<div class="mt-3 d-flex flex-row gap-2">
<div class="mt-3 d-flex flex-row gap-2 actions">
<a class="btn btn-secondary" href="{{ path('read_wizard_cancel') }}">Cancel</a>
<button class="btn btn-primary">Add articles</button>
</div>

Loading…
Cancel
Save