diff --git a/assets/styles/app.css b/assets/styles/app.css index 074be9a..55f112f 100644 --- a/assets/styles/app.css +++ b/assets/styles/app.css @@ -162,7 +162,7 @@ svg.icon { } } -/* Home: NIP-51 30004 “headlines” — editorial section title + full-width article stack (not masonry). */ +/* Home: magazine root index headline strip — section title + full-width article stack (not masonry). */ .home-curation-landmark { width: 100%; min-width: 0; diff --git a/assets/styles/article.css b/assets/styles/article.css index 8de5f9f..3b9637a 100644 --- a/assets/styles/article.css +++ b/assets/styles/article.css @@ -1,4 +1,4 @@ -/* Shared “reading pane” surface: full article pages and home NIP-51 30004 headline bodies. */ +/* Shared “reading pane” surface: full article pages and home magazine headline strip bodies. */ :root { --article-reading-pane-bg: color-mix(in srgb, var(--color-bg) 70%, #ffffff 30%); --article-reading-prose-color: color-mix(in srgb, var(--color-text-mid) 35%, var(--color-text) 65%); diff --git a/config/unfold.yaml b/config/unfold.yaml index 8eea8c6..b1f2451 100644 --- a/config/unfold.yaml +++ b/config/unfold.yaml @@ -55,8 +55,6 @@ parameters: # Kind 30040 magazine root #d (NIP-33). Exposed as `d_tag` for backward compatibility. d_tag_magazine: 'newsroom-magazine-on-imwald-by-laeserin' d_tag: '%d_tag_magazine%' - # NIP-51 kind 30004 curation set #d for `npub` (home landing stack): optional `title` tag = section heading; ordered `a` for kind 30023 only (full-width article blocks). Other `a` kinds and `e` tags ignored. Empty or `d-tag-goes-here` disables. - d_tag_curation_set: 'nostr-curated-headlines' # Whether to show community articles on the home page community_articles: true # Domain for site-assigned NIP-05 for featured (magazine category) authors; must match the host serving /.well-known/nostr.json diff --git a/src/Command/PrewarmCommand.php b/src/Command/PrewarmCommand.php index 48955f6..6416849 100644 --- a/src/Command/PrewarmCommand.php +++ b/src/Command/PrewarmCommand.php @@ -64,7 +64,7 @@ final class PrewarmCommand extends Command { $this ->addOption('no-magazine', null, InputOption::VALUE_NONE, 'Skip magazine 30040 index fetch') - ->addOption('no-deletions', null, InputOption::VALUE_NONE, 'Skip NIP-09 kind 5 deletion sync (articles + event rows: magazine, curation 30004, cached curation notes, profiles, etc.)') + ->addOption('no-deletions', null, InputOption::VALUE_NONE, 'Skip NIP-09 kind 5 deletion sync (articles + magazine 30040 rows, profiles, relay lists, etc.)') ->addOption('deletion-since', null, InputOption::VALUE_REQUIRED, 'strtotime() window start for kind 5 fetch', '-2 month') ->addOption('no-metadata', null, InputOption::VALUE_NONE, 'Skip batched kind-0 profile prewarm (MySQL event table)') ->addOption('no-comments', null, InputOption::VALUE_NONE, 'Skip comment thread cache') @@ -236,7 +236,7 @@ final class PrewarmCommand extends Command $this->disableCliExecutionTimeLimit(); if (!$input->getOption('no-deletions')) { - $io->section('NIP-09 deletions (kind 5 → 30023/30024 / 30040 / 30004)'); + $io->section('NIP-09 deletions (kind 5 → 30023/30024 / 30040 / legacy 30004 rows)'); $sinceStr = (string) $input->getOption('deletion-since'); $since = strtotime($sinceStr); if ($since === false) { @@ -284,7 +284,7 @@ final class PrewarmCommand extends Command try { $st = $this->nip09DeletionApplier->apply($kind5); $io->writeln(sprintf( - 'Kind 5 events: %d (deduped). NIP-23 long-form in DB (30023/30024) removed: %d. Magazine index in cache (30040) removed: root %d, category %d. Home curation (30004) rows removed: %d.', + 'Kind 5 events: %d (deduped). NIP-23 long-form in DB (30023/30024) removed: %d. Magazine index in cache (30040) removed: root %d, category %d. Legacy home curation (30004) rows removed: %d.', \count($kind5), $st['articles_removed'], $st['magazine_roots'], diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index 39bcd9e..70675e8 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -25,11 +25,11 @@ class DefaultController extends AbstractController public function index(): Response { $categoryATags = $this->magazineContent->getHomeCategoryAIndexTagsFromStoreOnly(); - $curation = $this->magazineContent->buildHomeCurationWallData(); + $magazineStrip = $this->magazineContent->buildHomeMagazineRootHeadlineStripData(); return $this->render('home.html.twig', [ - 'home_curation_heading' => $curation['heading'], - 'home_curation_tiles' => $curation['tiles'], + 'home_magazine_strip_heading' => $magazineStrip['heading'], + 'home_magazine_strip_tiles' => $magazineStrip['tiles'], 'home_featured_tiles' => $this->magazineContent->buildHomeMixedFeaturedWallTiles($categoryATags), 'home_sidebar_category_recent' => $this->magazineContent->buildHomeSidebarCategorizedRecent($categoryATags), 'home_highlights' => $this->articleHighlightRepository->findRecentForHome(40), diff --git a/src/Nostr/MagazineEventKeys.php b/src/Nostr/MagazineEventKeys.php index 8eef0e4..f5b1e35 100644 --- a/src/Nostr/MagazineEventKeys.php +++ b/src/Nostr/MagazineEventKeys.php @@ -7,7 +7,8 @@ namespace App\Nostr; use App\Service\NostrKeyHelper; /** - * Stable keys for {@see Event} rows: magazine root/category indices, kind 30004 curation set, and kind-0 profiles in MySQL. + * Stable keys for {@see Event} rows: magazine root/category indices, kind-0 profiles, and legacy kind-30004 + * curation keys still used by {@see \App\Service\Nip09DeletionApplier} to clean old MySQL rows. */ final class MagazineEventKeys { diff --git a/src/Service/MagazineContentService.php b/src/Service/MagazineContentService.php index 2cb9763..f5e8560 100644 --- a/src/Service/MagazineContentService.php +++ b/src/Service/MagazineContentService.php @@ -10,7 +10,6 @@ use App\Entity\Event; use App\Enum\EventStatusEnum; use App\Enum\KindsEnum; use App\Repository\ArticleRepository; -use App\Util\CurationSet30004Home; use App\Util\NostrEventTags; use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; use Symfony\Component\HttpFoundation\RequestStack; @@ -649,51 +648,6 @@ final class MagazineContentService } } - /** - * One relay-backed pass per HTTP request to pull 30004-listed **30023** coordinates into {@see Article} - * (see {@see NostrClient::persistCuration30004ReferencedItems}). - */ - private function maybeHydrateCuration30004ReferencedOncePerRequest(Event $stored): void - { - $r = $this->requestStack->getCurrentRequest(); - if ($r === null) { - return; - } - if ($r->attributes->get('_curation_30004_refs_hydrated')) { - return; - } - $r->attributes->set('_curation_30004_refs_hydrated', true); - try { - $this->nostrClient->persistCuration30004ReferencedItems($stored); - } catch (\Throwable) { - } - } - - private function ensureCuration30004FromRelays(string $npub, string $dTag): void - { - $r = $this->requestStack->getCurrentRequest(); - if ($r !== null && $r->attributes->get('_curation_30004_ensured')) { - return; - } - try { - $e = $this->nostrClient->getCurationSet30004($npub, $dTag); - if ($e !== null) { - $this->store->putCuration30004($npub, $dTag, $e); - try { - $this->nostrClient->persistCuration30004ReferencedItems($e); - } catch (\Throwable) { - } - if ($r !== null) { - $r->attributes->set('_curation_30004_refs_hydrated', true); - } - } - } catch (\Throwable) { - } - if ($r !== null) { - $r->attributes->set('_curation_30004_ensured', true); - } - } - private function ensureCategory30040FromRelays(string $slug): void { if (trim($slug) === '') { @@ -755,75 +709,102 @@ final class MagazineContentService } /** - * Home strip from NIP-51 kind 30004 (curation set): `d_tag_curation_set` on `npub`, ordered `a` tags for - * kind **30023** only (other kinds and `e` tags are ignored). Tiles resolve from the local `article` table. + * Home headline strip: kind **30040** magazine root (`npub` + `d_tag`), walking `a` tags **top to bottom**. + * Only kind **30023** / **30024** addresses become tiles; nested **30040** category `a` tags are skipped. + * Section heading comes from the root index `title` tag when present. * * @return array{heading: string, tiles: list} */ - public function buildHomeCurationWallData(): array + public function buildHomeMagazineRootHeadlineStripData(): array { - $d = trim((string) $this->params->get('d_tag_curation_set')); - if ($d === '' || strcasecmp($d, 'd-tag-goes-here') === 0) { - return ['heading' => '', 'tiles' => []]; - } $npub = (string) $this->params->get('npub'); - $stored = $this->store->getCuration30004($npub, $d); - if ($stored === null) { - $this->ensureCuration30004FromRelays($npub, $d); - $stored = $this->store->getCuration30004($npub, $d); + $dTag = (string) $this->params->get('d_tag'); + $mag = $this->store->getRoot($npub, $dTag); + if ($mag === null) { + $this->ensureRoot30040FromRelays($npub, $dTag); + $mag = $this->store->getRoot($npub, $dTag); } - if ($stored === null) { + if ($mag === null) { return ['heading' => '', 'tiles' => []]; } - $this->maybeHydrateCuration30004ReferencedOncePerRequest($stored); - /** @var list> $tagRows */ - $tagRows = $stored->getTags(); - $parsed = CurationSet30004Home::parseTitleAndOrderedRefs($tagRows); - if ($parsed['items'] === []) { - return ['heading' => '', 'tiles' => []]; + + $heading = ''; + $orderedCoords = []; + $seenAddr = []; + foreach ($mag->getTags() as $tagRow) { + $seq = NostrEventTags::rowToStringList($tagRow); + if ($seq === null) { + continue; + } + $name = strtolower((string) ($seq[0] ?? '')); + if ($name === 'title' && isset($seq[1]) && trim((string) $seq[1]) !== '') { + $heading = trim((string) $seq[1]); + } + if ($name !== 'a' || !isset($seq[1]) || (string) $seq[1] === '') { + continue; + } + $coord = trim((string) $seq[1]); + $parts = explode(':', $coord, 3); + if (\count($parts) < 3) { + continue; + } + $kind = (int) ($parts[0] ?? 0); + if (!\in_array($kind, [KindsEnum::LONGFORM->value, KindsEnum::LONGFORM_DRAFT->value], true)) { + continue; + } + $pk = strtolower(trim((string) $parts[1])); + $slug = trim((string) $parts[2]); + if (64 !== \strlen($pk) || !ctype_xdigit($pk) || $slug === '') { + continue; + } + $dedupe = $pk."\0".$slug; + if (isset($seenAddr[$dedupe])) { + continue; + } + $seenAddr[$dedupe] = true; + $orderedCoords[] = $kind.':'.$pk.':'.$slug; } + + if ($orderedCoords === []) { + return ['heading' => $heading, 'tiles' => []]; + } + $pairsArg = []; - foreach ($parsed['items'] as $it) { - $pairsArg[] = ['pubkey' => $it['pk'], 'slug' => $it['slug']]; + foreach ($orderedCoords as $coord) { + $parts = explode(':', $coord, 3); + $pairsArg[] = ['pubkey' => strtolower((string) $parts[1]), 'slug' => trim((string) $parts[2])]; } $indexed = $this->articleRepository->findByAuthorAndSlugIndexed($pairsArg); - $missingPairs = []; - foreach ($pairsArg as $pair) { + $missingCoords = []; + foreach ($pairsArg as $i => $pair) { $k = strtolower((string) $pair['pubkey'])."\0".trim((string) $pair['slug']); if (!isset($indexed[$k])) { - $missingPairs[] = $pair; + $missingCoords[] = $orderedCoords[$i]; } } - if ($missingPairs !== []) { + if ($missingCoords !== []) { try { - $this->nostrClient->ingestLongformForCategoryCoordinates(array_values(array_unique(array_map( - static fn (array $p): string => (string) KindsEnum::LONGFORM->value.':'.strtolower((string) $p['pubkey']).':'.trim((string) $p['slug']), - $missingPairs - )))); + $this->nostrClient->ingestLongformForCategoryCoordinates(array_values(array_unique($missingCoords))); } catch (\Throwable) { } $indexed = $this->articleRepository->findByAuthorAndSlugIndexed($pairsArg); } - $heading = trim($parsed['title']); + $tiles = []; - $seenArticle = []; - foreach ($parsed['items'] as $it) { - $key = $it['pk']."\0".$it['slug']; - if (isset($seenArticle[$key])) { - continue; - } - $article = $indexed[$key] ?? null; + foreach ($pairsArg as $pair) { + $k = strtolower((string) $pair['pubkey'])."\0".trim((string) $pair['slug']); + $article = $indexed[$k] ?? null; if ($article === null) { continue; } - $seenArticle[$key] = true; $tiles[] = [ 'article' => FeaturedArticleCard::fromArticle($article), 'body_html' => $this->articleBodyHtmlRenderer->renderForArticle($article), ]; } + if ($tiles === []) { - return ['heading' => '', 'tiles' => []]; + return ['heading' => $heading, 'tiles' => []]; } return ['heading' => $heading, 'tiles' => $tiles]; diff --git a/src/Service/MagazineIndexStore.php b/src/Service/MagazineIndexStore.php index 1a009bf..6fbe72c 100644 --- a/src/Service/MagazineIndexStore.php +++ b/src/Service/MagazineIndexStore.php @@ -10,7 +10,7 @@ use App\Repository\EventRepository; use Doctrine\ORM\EntityManagerInterface; /** - * Magazine Nostr index events (kind 30040) and the site’s NIP-51 kind 30004 curation set in MySQL {@see Event}. + * Magazine Nostr index events (kind 30040) in MySQL {@see Event}. * Updated by {@see MagazineRefresher} (`app:prewarm` / cron). */ final class MagazineIndexStore @@ -44,20 +44,6 @@ final class MagazineIndexStore return $this->eventRepository->findOneByCoreRowKey($key); } - public function getCuration30004(string $npub, string $dTag): ?Event - { - $dTag = trim($dTag); - if ($dTag === '') { - return null; - } - $key = MagazineEventKeys::magazineCuration30004($npub, $dTag); - if ($key === '') { - return null; - } - - return $this->eventRepository->findOneByCoreRowKey($key); - } - public function putRoot(string $npub, string $dTag, Event $event): void { if ($dTag === '') { @@ -79,19 +65,6 @@ final class MagazineIndexStore $this->replaceByCoreKey($key, Event::STORAGE_MAGAZINE_CATEGORY, $event); } - public function putCuration30004(string $npub, string $dTag, Event $event): void - { - $dTag = trim($dTag); - if ($dTag === '') { - return; - } - $key = MagazineEventKeys::magazineCuration30004($npub, $dTag); - if ($key === '') { - return; - } - $this->replaceByCoreKey($key, Event::STORAGE_MAGAZINE_CURATION_30004, $event); - } - public function deleteCategory(string $slug): void { if ($slug === '') { diff --git a/src/Service/MagazineRefresher.php b/src/Service/MagazineRefresher.php index 318a73a..0cb726b 100644 --- a/src/Service/MagazineRefresher.php +++ b/src/Service/MagazineRefresher.php @@ -91,8 +91,6 @@ final class MagazineRefresher $this->store->putRoot($npub, $dTag, $root); - $this->refreshCuration30004FromRelays($npub); - $deadline = microtime(true) + $budgetSeconds; $mergedPrefer = $this->mergePreferSlugsInOrder($preferSlugs, $preferFromEnv); @@ -165,34 +163,6 @@ final class MagazineRefresher $this->touchLastRelayTime(); } - /** - * Persists NIP-51 kind 30004 (home curation strip) when `d_tag_curation_set` is configured. - */ - private function refreshCuration30004FromRelays(string $npub): void - { - $d = trim((string) $this->params->get('d_tag_curation_set')); - if ($d === '' || strcasecmp($d, 'd-tag-goes-here') === 0) { - return; - } - try { - $ev = $this->nostrClient->getCurationSet30004($npub, $d); - if ($ev !== null) { - $this->store->putCuration30004($npub, $d, $ev); - try { - $this->nostrClient->persistCuration30004ReferencedItems($ev); - } catch (\Throwable $e2) { - $this->logger->warning('MagazineRefresher: curation 30004 referenced ingest failed', [ - 'message' => $e2->getMessage(), - ]); - } - } - } catch (\Throwable $e) { - $this->logger->warning('MagazineRefresher: curation set 30004 fetch failed', [ - 'message' => $e->getMessage(), - ]); - } - } - /** * @throws InvalidArgumentException */ diff --git a/src/Service/Nip09DeletionApplier.php b/src/Service/Nip09DeletionApplier.php index a0cf9e4..a806d7c 100644 --- a/src/Service/Nip09DeletionApplier.php +++ b/src/Service/Nip09DeletionApplier.php @@ -16,7 +16,7 @@ use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface; /** * Applies NIP-09 (kind 5) deletion requests to: * - MySQL: long-form articles ({@see KindsEnum::LONGFORM} 30023, {@see KindsEnum::LONGFORM_DRAFT} 30024) - * - MySQL {@see Event} rows: kind 30040 magazine indices (root + category), kind 30004 home curation set, + * - MySQL {@see Event} rows: kind 30040 magazine indices (root + category), legacy kind 30004 curation rows, * kind 0 profile, 10002 relay list, 10133 payto * * Handled for `e` tags (with `k` when present) and for NIP-33 `a` tags. diff --git a/src/Service/NostrClient.php b/src/Service/NostrClient.php index 6c66c95..4804f5f 100644 --- a/src/Service/NostrClient.php +++ b/src/Service/NostrClient.php @@ -5,7 +5,6 @@ namespace App\Service; use App\Entity\Article; use App\Entity\Event as PublicationEventEntity; use App\Enum\KindsEnum; -use App\Util\CurationSet30004Home; use App\Factory\ArticleFactory; use Doctrine\ORM\EntityManagerInterface; use Doctrine\Persistence\ManagerRegistry; @@ -1633,83 +1632,6 @@ class NostrClient return $this->queryMagazineIndex($npub, $dTag, $pfSet, $relaysForLog2); } - /** - * Latest NIP-51 kind 30004 curation set for this author and #d (parameterized replaceable). - * - * Relay strategy matches {@see getMagazineIndex}: article relays first, then profile relays only - * if nothing matched. - */ - public function getCurationSet30004(mixed $npub, string $dTag): ?PublicationEventEntity - { - $dTag = trim($dTag); - if ($dTag === '') { - return null; - } - $urls = $this->relayListFactory->getConfiguredArticleRelayUrlList(); - $relaysForLog = implode(', ', array_map(NostrRelayQuery::relayLogLabel(...), $urls)); - $result = $this->queryCurationSet30004($npub, $dTag, $this->defaultRelaySet, $relaysForLog); - if ($result !== null) { - return $result; - } - $profileExtra = $this->relayListFactory->getProfileRelayUrlsExcludedFromArticleRelays(); - if ($profileExtra === []) { - return null; - } - $pfSet = $this->relayListFactory->createRelaySetFromUrlsOnly($profileExtra); - $relaysForLog2 = implode(', ', array_map(NostrRelayQuery::relayLogLabel(...), $profileExtra)).' (profile_relays)'; - - return $this->queryCurationSet30004($npub, $dTag, $pfSet, $relaysForLog2); - } - - private function queryCurationSet30004(mixed $npub, string $dTag, RelaySet $relaySet, string $relaysForLog): ?PublicationEventEntity - { - $authorHex = $this->wireMerge->npubToHexPubkey($npub); - if ($authorHex === null) { - $this->logger->warning('Curation set 30004: could not resolve npub to hex pubkey', [ - 'npub' => $npub, - 'dTag' => $dTag, - ]); - - return null; - } - $request = $this->nostrRelayQuery->createNostrRequest( - defaultRelaySet: $this->defaultRelaySet, - relaySet: $relaySet, - kinds: [KindsEnum::CURATION_SET], - filters: ['authors' => [(string) $npub], 'tag' => ['#d', [$dTag]]], - ); - $this->logger->info(sprintf('Curation set 30004 query (relays: %s)', $relaysForLog), [ - 'npub' => $npub, - 'dTag' => $dTag, - 'relays' => $relaysForLog, - ]); - $response = $request->send(); - $events = $this->nostrRelayQuery->processResponse($response, function ($received) { - return $received; - }); - if ($events === []) { - return null; - } - $raw = $this->wireMerge->pickLatestNip33ParameterizedForQuery( - $events, - KindsEnum::CURATION_SET->value, - $authorHex, - $dTag - ); - if ($raw === null) { - $this->logger->warning('Curation set 30004: no event matched NIP-33 address (kind:pubkey:d) after merge', [ - 'npub' => $npub, - 'dTag' => $dTag, - 'relays' => $relaysForLog, - 'event_count' => \count($events), - ]); - - return null; - } - - return $this->wireMerge->magazineEventToPublicationEntity($raw); - } - private function queryMagazineIndex(mixed $npub, mixed $dTag, RelaySet $relaySet, string $relaysForLog): ?PublicationEventEntity { $authorHex = $this->wireMerge->npubToHexPubkey($npub); @@ -2091,35 +2013,4 @@ class NostrClient } $this->logger->info('[longform_ingest] ingestLongform: done (all groups)'); } - - /** - * After persisting NIP-51 kind 30004, ingest each listed **30023** `a` coordinate into {@see Article}. - * Non-30023 `a` tags and `e` tags are ignored at parse time ({@see CurationSet30004Home}). - */ - public function persistCuration30004ReferencedItems(PublicationEventEntity $curation30004): void - { - try { - $parsed = CurationSet30004Home::parseTitleAndOrderedRefs($curation30004->getTags()); - } catch (\Throwable $e) { - $this->logger->warning('[curation_30004] parse refs failed', ['message' => $e->getMessage()]); - - return; - } - $addresses = []; - foreach ($parsed['items'] as $it) { - $addresses[] = (string) KindsEnum::LONGFORM->value.':'.strtolower((string) $it['pk']).':'.trim((string) $it['slug']); - } - $addresses = array_values(array_unique($addresses)); - if ($addresses === []) { - return; - } - try { - $this->ingestLongformForCategoryCoordinates($addresses); - } catch (\Throwable $e) { - $this->logger->warning('[curation_30004] longform ingest for curated list failed', [ - 'message' => $e->getMessage(), - 'address_count' => \count($addresses), - ]); - } - } } diff --git a/src/Service/NostrKind5DeletionFilter.php b/src/Service/NostrKind5DeletionFilter.php index 3f719b4..a88c9d8 100644 --- a/src/Service/NostrKind5DeletionFilter.php +++ b/src/Service/NostrKind5DeletionFilter.php @@ -8,7 +8,7 @@ use App\Enum\KindsEnum; /** * NIP-09 kind-5: keep deletion events that may affect MySQL-backed rows (profile, relay list, payto, - * long-form, magazine 30040, home curation 30004). Skips thread/reply deletions to reduce relay payload. + * long-form, magazine 30040, legacy kind 30004). Skips thread/reply deletions to reduce relay payload. */ final class NostrKind5DeletionFilter { diff --git a/src/Util/CurationSet30004Home.php b/src/Util/CurationSet30004Home.php deleted file mode 100644 index 06d05cc..0000000 --- a/src/Util/CurationSet30004Home.php +++ /dev/null @@ -1,58 +0,0 @@ - $tags Event tag rows (Nostr JSON shape) - * - * @return array{title: string, items: list} - */ - public static function parseTitleAndOrderedRefs(iterable $tags): array - { - $title = ''; - $items = []; - foreach ($tags as $tag) { - if (!\is_array($tag) || $tag === []) { - continue; - } - $name = isset($tag[0]) ? strtolower((string) $tag[0]) : ''; - $v = isset($tag[1]) ? (string) $tag[1] : ''; - if ($v === '') { - continue; - } - if ($name === 'title' && $title === '') { - $title = trim($v); - continue; - } - if ($name !== 'a') { - continue; - } - $parts = explode(':', $v, 3); - if (\count($parts) < 3) { - continue; - } - $kind = (int) $parts[0]; - if ($kind !== KindsEnum::LONGFORM->value) { - continue; - } - $pk = strtolower(trim($parts[1])); - $slug = trim($parts[2]); - if (64 !== \strlen($pk) || !ctype_xdigit($pk) || $slug === '') { - continue; - } - $items[] = ['type' => 'article', 'pk' => $pk, 'slug' => $slug]; - } - - return ['title' => $title, 'items' => $items]; - } -} diff --git a/templates/components/Organisms/HomeCurationHeadlines.html.twig b/templates/components/Organisms/HomeMagazineArticleStrip.html.twig similarity index 78% rename from templates/components/Organisms/HomeCurationHeadlines.html.twig rename to templates/components/Organisms/HomeMagazineArticleStrip.html.twig index 413d883..48ab3b0 100644 --- a/templates/components/Organisms/HomeCurationHeadlines.html.twig +++ b/templates/components/Organisms/HomeMagazineArticleStrip.html.twig @@ -1,19 +1,18 @@ {# - 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 (markdown may contain links). + Home: magazine root kind 30040 — long-form `a` tags in index order (top to bottom). Section title from + root `title` tag. Same layout as before: illustration, headline, full article body (Markdown + highlights). #} {% if tiles is not empty %}
{% if section_title|default('') != '' %} -

{{ section_title|e }}

+

{{ section_title|e }}

{% endif %}
{% for tile in tiles %} diff --git a/templates/home.html.twig b/templates/home.html.twig index 0085bfe..65146c4 100644 --- a/templates/home.html.twig +++ b/templates/home.html.twig @@ -40,10 +40,10 @@ {% block body %}
- {% if home_curation_tiles|default([]) is not empty %} - {% include 'components/Organisms/HomeCurationHeadlines.html.twig' with { - tiles: home_curation_tiles, - section_title: home_curation_heading|default(''), + {% if home_magazine_strip_tiles|default([]) is not empty %} + {% include 'components/Organisms/HomeMagazineArticleStrip.html.twig' with { + tiles: home_magazine_strip_tiles, + section_title: home_magazine_strip_heading|default(''), } only %} {% endif %} {% include 'components/Organisms/FeaturedWall.html.twig' with { tiles: home_featured_tiles|default([]) } only %} diff --git a/tests/Util/CurationSet30004HomeTest.php b/tests/Util/CurationSet30004HomeTest.php deleted file mode 100644 index 8996716..0000000 --- a/tests/Util/CurationSet30004HomeTest.php +++ /dev/null @@ -1,44 +0,0 @@ - 'article', 'pk' => '26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c', 'slug' => 'slug-one'], - ['type' => 'article', 'pk' => '26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c', 'slug' => 'slug-two'], - ], $out['items']); - } - - public function testSkipsNon30023ATags(): void - { - $tags = [ - ['a', '30040:26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:mag'], - ['a', '30023:26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:ok'], - ]; - $out = CurationSet30004Home::parseTitleAndOrderedRefs($tags); - self::assertSame('', $out['title']); - self::assertCount(1, $out['items']); - self::assertSame('article', $out['items'][0]['type']); - self::assertSame('ok', $out['items'][0]['slug']); - } -}