diff --git a/assets/styles/app.css b/assets/styles/app.css
index 14c02d7..3a5097b 100644
--- a/assets/styles/app.css
+++ b/assets/styles/app.css
@@ -144,7 +144,7 @@ svg.icon {
background-color: var(--color-bg);
color: var(--color-text);
padding: 0;
- margin: 0 0 50px 0;
+ margin: 0 0 2rem 0;
border-radius: 0; /* Sharp edges */
}
diff --git a/src/Controller/ArticleController.php b/src/Controller/ArticleController.php
index 27dac1f..1792f2c 100644
--- a/src/Controller/ArticleController.php
+++ b/src/Controller/ArticleController.php
@@ -100,10 +100,10 @@ class ArticleController extends AbstractController
$cacheKey = 'article_' . $article->getEventId();
$cacheItem = $articlesCache->getItem($cacheKey);
- if (!$cacheItem->isHit()) {
+ //if (!$cacheItem->isHit()) {
$cacheItem->set($converter->convertToHTML($article->getContent()));
$articlesCache->save($cacheItem);
- }
+ //}
$key = new Key();
$npub = $key->convertPublicKeyToBech32($article->getPubkey());
diff --git a/src/Controller/EventController.php b/src/Controller/EventController.php
index 003e051..4a6a3d1 100644
--- a/src/Controller/EventController.php
+++ b/src/Controller/EventController.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Controller;
+use App\Enum\KindsEnum;
use App\Service\NostrClient;
use App\Service\NostrLinkParser;
use App\Service\RedisCacheService;
@@ -52,7 +53,7 @@ class EventController extends AbstractController
case 'nevent':
// Handle nevent identifier (event with additional metadata)
- $relays = $data->relays ?? [];
+ $relays = $data->relays ?? null;
$event = $nostrClient->getEventById($data->id, $relays);
break;
@@ -65,6 +66,11 @@ class EventController extends AbstractController
'relays' => $data->relays ?? []
];
$event = $nostrClient->getEventByNaddr($decodedData);
+ if ($data->kind === KindsEnum::LONGFORM->value) {
+ // If it's a long-form content, redirect to the article page
+ $logger->info('Redirecting to article', ['identifier' => $data->identifier]);
+ return $this->redirectToRoute('article-slug', ['slug' => $data->identifier]);
+ }
break;
default:
diff --git a/src/Service/NostrClient.php b/src/Service/NostrClient.php
index a352a34..2a961b6 100644
--- a/src/Service/NostrClient.php
+++ b/src/Service/NostrClient.php
@@ -276,7 +276,7 @@ class NostrClient
// Create request using the helper method
$request = $this->createNostrRequest(
- kinds: [], // Leave empty to accept any kind
+ kinds: [],
filters: ['ids' => [$eventId]],
relaySet: $relaySet
);
@@ -691,7 +691,9 @@ class NostrClient
{
$subscription = new Subscription();
$filter = new Filter();
- $filter->setKinds($kinds);
+ if (!empty($kinds)) {
+ $filter->setKinds($kinds);
+ }
foreach ($filters as $key => $value) {
$method = 'set' . ucfirst($key);
diff --git a/src/Util/CommonMark/Converter.php b/src/Util/CommonMark/Converter.php
index b037b6c..e09f7f8 100644
--- a/src/Util/CommonMark/Converter.php
+++ b/src/Util/CommonMark/Converter.php
@@ -2,6 +2,7 @@
namespace App\Util\CommonMark;
+use App\Service\NostrClient;
use App\Service\RedisCacheService;
use App\Util\CommonMark\ImagesExtension\RawImageLinkExtension;
use App\Util\CommonMark\NostrSchemeExtension\NostrSchemeExtension;
@@ -21,11 +22,14 @@ use League\CommonMark\Extension\Table\TableExtension;
use League\CommonMark\Extension\TableOfContents\TableOfContentsExtension;
use League\CommonMark\MarkdownConverter;
use League\CommonMark\Renderer\HtmlDecorator;
+use Twig\Environment as TwigEnvironment;
readonly class Converter
{
public function __construct(
- private RedisCacheService $redisCacheService
+ private RedisCacheService $redisCacheService,
+ private NostrClient $nostrClient,
+ private TwigEnvironment $twig
){}
/**
@@ -64,7 +68,7 @@ readonly class Converter
$environment->addExtension(new TableExtension());
$environment->addExtension(new StrikethroughExtension());
// create a custom extension, that handles nostr mentions
- $environment->addExtension(new NostrSchemeExtension($this->redisCacheService));
+ $environment->addExtension(new NostrSchemeExtension($this->redisCacheService, $this->nostrClient, $this->twig));
$environment->addExtension(new SmartPunctExtension());
$environment->addExtension(new EmbedExtension());
$environment->addRenderer(Embed::class, new HtmlDecorator(new EmbedRenderer(), 'div', ['class' => 'embedded-content']));
diff --git a/src/Util/CommonMark/ImagesExtension/RawImageLinkExtension.php b/src/Util/CommonMark/ImagesExtension/RawImageLinkExtension.php
index 049b247..55cd1aa 100644
--- a/src/Util/CommonMark/ImagesExtension/RawImageLinkExtension.php
+++ b/src/Util/CommonMark/ImagesExtension/RawImageLinkExtension.php
@@ -9,6 +9,6 @@ class RawImageLinkExtension implements ExtensionInterface
{
public function register(EnvironmentBuilderInterface $environment): void
{
- $environment->addInlineParser(new RawImageLinkParser());
+ $environment->addInlineParser(new RawImageLinkParser(),201);
}
}
diff --git a/src/Util/CommonMark/ImagesExtension/RawImageLinkParser.php b/src/Util/CommonMark/ImagesExtension/RawImageLinkParser.php
index ad89224..2295c57 100644
--- a/src/Util/CommonMark/ImagesExtension/RawImageLinkParser.php
+++ b/src/Util/CommonMark/ImagesExtension/RawImageLinkParser.php
@@ -2,10 +2,8 @@
namespace App\Util\CommonMark\ImagesExtension;
-use League\CommonMark\Node\Block\Paragraph;
use League\CommonMark\Parser\Inline\InlineParserInterface;
use League\CommonMark\Extension\CommonMark\Node\Inline\Image;
-use League\CommonMark\Extension\CommonMark\Node\Inline\SoftBreak;
use League\CommonMark\Parser\Inline\InlineParserMatch;
use League\CommonMark\Parser\InlineParserContext;
@@ -21,13 +19,12 @@ class RawImageLinkParser implements InlineParserInterface
{
$cursor = $inlineContext->getCursor();
$match = $inlineContext->getFullMatch();
- // Create an element instead of a text link
- $image = new Image($match, '');
- $paragraph = new Paragraph();
- $paragraph->appendChild($image);
- $inlineContext->getContainer()->appendChild($paragraph);
- // Advance the cursor to consume the matched part (important!)
+ // Create an inline Image element directly (not wrapped in a paragraph)
+ $image = new Image($match, '', $match);
+ $inlineContext->getContainer()->appendChild($image);
+
+ // Advance the cursor to consume the matched part
$cursor->advanceBy(strlen($match));
return true;
diff --git a/src/Util/CommonMark/NostrSchemeExtension/NostrEmbeddedCard.php b/src/Util/CommonMark/NostrSchemeExtension/NostrEmbeddedCard.php
new file mode 100644
index 0000000..8d35900
--- /dev/null
+++ b/src/Util/CommonMark/NostrSchemeExtension/NostrEmbeddedCard.php
@@ -0,0 +1,25 @@
+htmlContent = $htmlContent;
+ }
+
+ public function getHtmlContent(): string
+ {
+ return $this->htmlContent;
+ }
+}
diff --git a/src/Util/CommonMark/NostrSchemeExtension/NostrEmbeddedCardRenderer.php b/src/Util/CommonMark/NostrSchemeExtension/NostrEmbeddedCardRenderer.php
new file mode 100644
index 0000000..150114d
--- /dev/null
+++ b/src/Util/CommonMark/NostrSchemeExtension/NostrEmbeddedCardRenderer.php
@@ -0,0 +1,20 @@
+getHtmlContent();
+ }
+}
diff --git a/src/Util/CommonMark/NostrSchemeExtension/NostrSchemeExtension.php b/src/Util/CommonMark/NostrSchemeExtension/NostrSchemeExtension.php
index 65e767e..3441716 100644
--- a/src/Util/CommonMark/NostrSchemeExtension/NostrSchemeExtension.php
+++ b/src/Util/CommonMark/NostrSchemeExtension/NostrSchemeExtension.php
@@ -2,25 +2,31 @@
namespace App\Util\CommonMark\NostrSchemeExtension;
+use App\Service\NostrClient;
use App\Service\RedisCacheService;
use League\CommonMark\Environment\EnvironmentBuilderInterface;
use League\CommonMark\Extension\ExtensionInterface;
+use Twig\Environment;
class NostrSchemeExtension implements ExtensionInterface
{
- public function __construct(private readonly RedisCacheService $redisCacheService)
- {
+ public function __construct(
+ private readonly RedisCacheService $redisCacheService,
+ private readonly NostrClient $nostrClient,
+ private readonly Environment $twig
+ ) {
}
public function register(EnvironmentBuilderInterface $environment): void
{
$environment
->addInlineParser(new NostrMentionParser($this->redisCacheService), 200)
- ->addInlineParser(new NostrSchemeParser($this->redisCacheService), 199)
+ ->addInlineParser(new NostrSchemeParser($this->redisCacheService, $this->nostrClient, $this->twig), 199)
->addInlineParser(new NostrRawNpubParser($this->redisCacheService), 198)
->addRenderer(NostrSchemeData::class, new NostrEventRenderer(), 2)
+ ->addRenderer(NostrEmbeddedCard::class, new NostrEmbeddedCardRenderer(), 3)
->addRenderer(NostrMentionLink::class, new NostrMentionRenderer(), 1)
;
}
diff --git a/src/Util/CommonMark/NostrSchemeExtension/NostrSchemeParser.php b/src/Util/CommonMark/NostrSchemeExtension/NostrSchemeParser.php
index c221d19..f86a999 100644
--- a/src/Util/CommonMark/NostrSchemeExtension/NostrSchemeParser.php
+++ b/src/Util/CommonMark/NostrSchemeExtension/NostrSchemeParser.php
@@ -2,6 +2,7 @@
namespace App\Util\CommonMark\NostrSchemeExtension;
+use App\Service\NostrClient;
use App\Service\RedisCacheService;
use League\CommonMark\Parser\Inline\InlineParserInterface;
use League\CommonMark\Parser\Inline\InlineParserMatch;
@@ -11,16 +12,22 @@ use nostriphant\NIP19\Data\NAddr;
use nostriphant\NIP19\Data\NEvent;
use nostriphant\NIP19\Data\NProfile;
use nostriphant\NIP19\Data\NPub;
+use Twig\Environment;
+use swentel\nostr\Key\Key;
class NostrSchemeParser implements InlineParserInterface
{
private RedisCacheService $redisCacheService;
+ private NostrClient $nostrClient;
+ private Environment $twig;
- public function __construct(RedisCacheService $redisCacheService)
+ public function __construct(RedisCacheService $redisCacheService, NostrClient $nostrClient, Environment $twig)
{
$this->redisCacheService = $redisCacheService;
+ $this->nostrClient = $nostrClient;
+ $this->twig = $twig;
}
public function getMatchDefinition(): InlineParserMatch
@@ -45,8 +52,8 @@ class NostrSchemeParser implements InlineParserInterface
/** @var NPub $object */
$object = $decoded->data;
$profile = $this->redisCacheService->getMetadata($bechEncoded);
- if (isset($profile['name'])) {
- $inlineContext->getContainer()->appendChild(new NostrMentionLink($profile['name'], $bechEncoded));
+ if (isset($profile->name)) {
+ $inlineContext->getContainer()->appendChild(new NostrMentionLink($profile->name, $bechEncoded));
} else {
$inlineContext->getContainer()->appendChild(new NostrMentionLink(null, $bechEncoded));
}
@@ -59,11 +66,32 @@ class NostrSchemeParser implements InlineParserInterface
case 'nevent':
/** @var NEvent $decodedEvent */
$decodedEvent = $decoded->data;
- $eventId = $decodedEvent->id;
- $relays = $decodedEvent->relays;
- $author = $decodedEvent->author;
- $kind = $decodedEvent->kind;
- $inlineContext->getContainer()->appendChild(new NostrSchemeData('nevent', $bechEncoded, $relays, $author, $kind));
+
+ // Fetch the actual event data using the same logic as EventController
+ $event = $this->nostrClient->getEventById($decodedEvent->id, $decodedEvent->relays);
+
+ if ($event) {
+ // Get author metadata if available
+ $authorMetadata = null;
+ if (isset($event->pubkey)) {
+ $key = new Key();
+ $npub = $key->convertPublicKeyToBech32($event->pubkey);
+ $authorMetadata = $this->redisCacheService->getMetadata($npub);
+ }
+
+ // Render the embedded event card
+ $eventCardHtml = $this->twig->render('components/event_card.html.twig', [
+ 'event' => $event,
+ 'author' => $authorMetadata,
+ 'nevent' => $bechEncoded
+ ]);
+
+ // Create a new node type for embedded HTML content
+ $inlineContext->getContainer()->appendChild(new NostrEmbeddedCard($eventCardHtml));
+ } else {
+ // Fallback to simple link if event not found
+ $inlineContext->getContainer()->appendChild(new NostrSchemeData('nevent', $bechEncoded, $decodedEvent->relays, $decodedEvent->author, $decodedEvent->kind));
+ }
break;
case 'naddr':
/** @var NAddr $decodedEvent */
diff --git a/templates/components/event_card.html.twig b/templates/components/event_card.html.twig
new file mode 100644
index 0000000..5af927d
--- /dev/null
+++ b/templates/components/event_card.html.twig
@@ -0,0 +1,77 @@
+{# Embedded event card component #}
+