7 changed files with 194 additions and 27 deletions
@ -0,0 +1,36 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
declare(strict_types=1); |
||||||
|
|
||||||
|
namespace App\Service; |
||||||
|
|
||||||
|
use App\Entity\Article; |
||||||
|
use App\Repository\ArticleHighlightRepository; |
||||||
|
use App\Util\CommonMark\Converter; |
||||||
|
use League\CommonMark\Exception\CommonMarkException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Markdown → HTML for an {@see Article}, with the same kind-9802 highlight injection as {@see \App\Controller\ArticleController}. |
||||||
|
*/ |
||||||
|
final class ArticleBodyHtmlRenderer |
||||||
|
{ |
||||||
|
public function __construct( |
||||||
|
private readonly Converter $converter, |
||||||
|
private readonly ArticleHighlightRepository $articleHighlightRepository, |
||||||
|
private readonly ArticleBodyHighlightInjector $articleBodyHighlightInjector, |
||||||
|
) { |
||||||
|
} |
||||||
|
|
||||||
|
public function renderForArticle(Article $article): string |
||||||
|
{ |
||||||
|
$raw = (string) ($article->getContent() ?? ''); |
||||||
|
try { |
||||||
|
$html = $this->converter->convertToHTML($raw); |
||||||
|
} catch (CommonMarkException) { |
||||||
|
$html = htmlspecialchars($raw, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); |
||||||
|
} |
||||||
|
$highlights = $this->articleHighlightRepository->findByArticle($article); |
||||||
|
|
||||||
|
return $this->articleBodyHighlightInjector->inject($html, $highlights)['html']; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,48 @@ |
|||||||
|
{# |
||||||
|
NIP-51 30004 home strip: section title from list `title` tag; each item reads like a static landing |
||||||
|
section — illustration (max 400px, natural aspect), headline, then full article body (Markdown + |
||||||
|
highlights as on /p/…/d/…). Body is not wrapped in a single <a> (markdown may contain links). |
||||||
|
#} |
||||||
|
{% if tiles is not empty %} |
||||||
|
<section |
||||||
|
class="home-curation-landmark" |
||||||
|
{% if section_title|default('') != '' %} |
||||||
|
aria-labelledby="home-curation-landmark-heading" |
||||||
|
{% else %} |
||||||
|
aria-label="{{ (website_name ~ ' — curated articles')|e('html_attr') }}" |
||||||
|
{% endif %} |
||||||
|
> |
||||||
|
{% if section_title|default('') != '' %} |
||||||
|
<h2 id="home-curation-landmark-heading" class="home-curation-landmark__title">{{ section_title|e }}</h2> |
||||||
|
{% endif %} |
||||||
|
<div class="home-curation-landmark__articles"> |
||||||
|
{% for tile in tiles %} |
||||||
|
{% set item = tile.article %} |
||||||
|
{% set article_href = (item.pubkey and npub_from_hex(item.pubkey) != '') ? path('article', { npub: npub_from_hex(item.pubkey), slug: item.slug }) : path('article-legacy-redirect', { slug: item.slug }) %} |
||||||
|
<article class="curation-article-display"> |
||||||
|
<div class="curation-article-display__media"> |
||||||
|
<a href="{{ article_href }}" tabindex="-1" aria-hidden="true"> |
||||||
|
<img |
||||||
|
src="{{ article_card_cover(item.image, item.pubkey) }}" |
||||||
|
alt="{{ ('Illustration for ' ~ item.title)|e('html_attr') }}" |
||||||
|
loading="{{ loop.first ? 'eager' : 'lazy' }}" |
||||||
|
decoding="async" |
||||||
|
> |
||||||
|
</a> |
||||||
|
</div> |
||||||
|
<div class="curation-article-display__body"> |
||||||
|
<h3 class="curation-article-display__headline"> |
||||||
|
<a class="curation-article-display__title-link" href="{{ article_href }}">{{ item.title|e }}</a> |
||||||
|
</h3> |
||||||
|
<div |
||||||
|
class="article-main curation-article-display__main" |
||||||
|
data-controller="user-highlight-tooltip" |
||||||
|
> |
||||||
|
{{ tile.body_html|raw }} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</article> |
||||||
|
{% endfor %} |
||||||
|
</div> |
||||||
|
</section> |
||||||
|
{% endif %} |
||||||
Loading…
Reference in new issue