16 changed files with 87 additions and 376 deletions
@ -1,58 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
declare(strict_types=1); |
|
||||||
|
|
||||||
namespace App\Util; |
|
||||||
|
|
||||||
use App\Enum\KindsEnum; |
|
||||||
|
|
||||||
/** |
|
||||||
* NIP-51 kind 30004 (curation set) for the home strip: ordered `a` tags for kind **30023** only. |
|
||||||
* Other address kinds and `e` tags are ignored. |
|
||||||
*/ |
|
||||||
final class CurationSet30004Home |
|
||||||
{ |
|
||||||
/** |
|
||||||
* @param iterable<int, mixed> $tags Event tag rows (Nostr JSON shape) |
|
||||||
* |
|
||||||
* @return array{title: string, items: list<array{type: 'article', pk: string, slug: string}>} |
|
||||||
*/ |
|
||||||
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]; |
|
||||||
} |
|
||||||
} |
|
||||||
@ -1,19 +1,18 @@ |
|||||||
{# |
{# |
||||||
NIP-51 30004 home strip: section title from list `title` tag; each item reads like a static landing |
Home: magazine root kind 30040 — long-form `a` tags in index order (top to bottom). Section title from |
||||||
section — illustration (max 400px, natural aspect), headline, then full article body (Markdown + |
root `title` tag. Same layout as before: illustration, headline, full article body (Markdown + highlights). |
||||||
highlights as on /p/…/d/…). Body is not wrapped in a single <a> (markdown may contain links). |
|
||||||
#} |
#} |
||||||
{% if tiles is not empty %} |
{% if tiles is not empty %} |
||||||
<section |
<section |
||||||
class="home-curation-landmark" |
class="home-curation-landmark" |
||||||
{% if section_title|default('') != '' %} |
{% if section_title|default('') != '' %} |
||||||
aria-labelledby="home-curation-landmark-heading" |
aria-labelledby="home-magazine-strip-heading" |
||||||
{% else %} |
{% else %} |
||||||
aria-label="{{ (website_name ~ ' — curated articles')|e('html_attr') }}" |
aria-label="{{ (website_name ~ ' — featured articles')|e('html_attr') }}" |
||||||
{% endif %} |
{% endif %} |
||||||
> |
> |
||||||
{% if section_title|default('') != '' %} |
{% if section_title|default('') != '' %} |
||||||
<h2 id="home-curation-landmark-heading" class="home-curation-landmark__title">{{ section_title|e }}</h2> |
<h2 id="home-magazine-strip-heading" class="home-curation-landmark__title">{{ section_title|e }}</h2> |
||||||
{% endif %} |
{% endif %} |
||||||
<div class="home-curation-landmark__articles"> |
<div class="home-curation-landmark__articles"> |
||||||
{% for tile in tiles %} |
{% for tile in tiles %} |
||||||
@ -1,44 +0,0 @@ |
|||||||
<?php |
|
||||||
|
|
||||||
declare(strict_types=1); |
|
||||||
|
|
||||||
namespace App\Tests\Util; |
|
||||||
|
|
||||||
use App\Util\CurationSet30004Home; |
|
||||||
use PHPUnit\Framework\TestCase; |
|
||||||
|
|
||||||
final class CurationSet30004HomeTest extends TestCase |
|
||||||
{ |
|
||||||
public function testParsesTitleAndOrdered30023RefsOnly(): void |
|
||||||
{ |
|
||||||
$eid = 'd78ba0d5dce22bfff9db0a9e996c9ef27e2c91051de0c4e1da340e0326b4941e'; |
|
||||||
$tags = [ |
|
||||||
['d', 'jvdy9i4'], |
|
||||||
['title', 'Yaks'], |
|
||||||
['image', 'https://example.com/y.jpg'], |
|
||||||
['a', '30023:26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:slug-one'], |
|
||||||
['e', $eid], |
|
||||||
['a', '30024:26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:draft-only'], |
|
||||||
['a', '30023:26dc95542e18b8b7aec2f14610f55c335abebec76f3db9e58c254661d0593a0c:slug-two'], |
|
||||||
]; |
|
||||||
$out = CurationSet30004Home::parseTitleAndOrderedRefs($tags); |
|
||||||
self::assertSame('Yaks', $out['title']); |
|
||||||
self::assertSame([ |
|
||||||
['type' => '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']); |
|
||||||
} |
|
||||||
} |
|
||||||
Loading…
Reference in new issue