14 changed files with 197 additions and 41 deletions
@ -0,0 +1,25 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Util\CommonMark\NostrSchemeExtension; |
||||||
|
|
||||||
|
use League\CommonMark\Node\Inline\AbstractInline; |
||||||
|
|
||||||
|
/** |
||||||
|
* Class NostrEmbeddedCard |
||||||
|
* Represents an embedded HTML card for Nostr events |
||||||
|
*/ |
||||||
|
class NostrEmbeddedCard extends AbstractInline |
||||||
|
{ |
||||||
|
private string $htmlContent; |
||||||
|
|
||||||
|
public function __construct(string $htmlContent) |
||||||
|
{ |
||||||
|
parent::__construct(); |
||||||
|
$this->htmlContent = $htmlContent; |
||||||
|
} |
||||||
|
|
||||||
|
public function getHtmlContent(): string |
||||||
|
{ |
||||||
|
return $this->htmlContent; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,20 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
namespace App\Util\CommonMark\NostrSchemeExtension; |
||||||
|
|
||||||
|
use League\CommonMark\Node\Node; |
||||||
|
use League\CommonMark\Renderer\ChildNodeRendererInterface; |
||||||
|
use League\CommonMark\Renderer\NodeRendererInterface; |
||||||
|
|
||||||
|
class NostrEmbeddedCardRenderer implements NodeRendererInterface |
||||||
|
{ |
||||||
|
public function render(Node $node, ChildNodeRendererInterface $childRenderer) |
||||||
|
{ |
||||||
|
if (!($node instanceof NostrEmbeddedCard)) { |
||||||
|
throw new \InvalidArgumentException('Incompatible inline node type: ' . get_class($node)); |
||||||
|
} |
||||||
|
|
||||||
|
// Return the raw HTML content for the embedded card |
||||||
|
return $node->getHtmlContent(); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,77 @@ |
|||||||
|
{# Embedded event card component #} |
||||||
|
<div class="embedded-event-card" data-nevent="{{ nevent }}"> |
||||||
|
<div class="event-header"> |
||||||
|
{% if author %} |
||||||
|
{% if author.image is defined %} |
||||||
|
<img src="{{ author.image }}" class="avatar-small" alt="{{ author.name }}" onerror="this.style.display = 'none'" /> |
||||||
|
{% endif %} |
||||||
|
<div class="author-info"> |
||||||
|
<strong>{{ author.name ?? 'Anonymous' }}</strong> |
||||||
|
{% if author.nip05 is defined %} |
||||||
|
<span class="nip05">{{ author.nip05 }}</span> |
||||||
|
{% endif %} |
||||||
|
</div> |
||||||
|
{% endif %} |
||||||
|
<div class="event-meta"> |
||||||
|
<span class="event-date">{{ event.created_at|date('M j, Y H:i') }}</span> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
<div class="event-content line-clamp-5"> |
||||||
|
{{ event.content|markdown_to_html|mentionify }} |
||||||
|
</div> |
||||||
|
<div class="event-footer"> |
||||||
|
<a href="/e/{{ nevent }}" class="view-full">View full event</a> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<style> |
||||||
|
.embedded-event-card { |
||||||
|
border: 1px solid #e1e5e9; |
||||||
|
border-radius: 8px; |
||||||
|
padding: 12px; |
||||||
|
margin: 8px 0; |
||||||
|
background: #f8f9fa; |
||||||
|
} |
||||||
|
|
||||||
|
.embedded-event-card .event-header { |
||||||
|
display: flex; |
||||||
|
align-items: center; |
||||||
|
gap: 8px; |
||||||
|
margin-bottom: 8px; |
||||||
|
} |
||||||
|
|
||||||
|
.embedded-event-card .avatar-small { |
||||||
|
width: 24px; |
||||||
|
height: 24px; |
||||||
|
border-radius: 50%; |
||||||
|
} |
||||||
|
|
||||||
|
.embedded-event-card .author-info { |
||||||
|
flex: 1; |
||||||
|
} |
||||||
|
|
||||||
|
.embedded-event-card .nip05 { |
||||||
|
color: #6c757d; |
||||||
|
font-size: 0.85em; |
||||||
|
} |
||||||
|
|
||||||
|
.embedded-event-card .event-meta { |
||||||
|
color: #6c757d; |
||||||
|
font-size: 0.8em; |
||||||
|
} |
||||||
|
|
||||||
|
.embedded-event-card .event-content { |
||||||
|
margin: 8px 0; |
||||||
|
line-height: 1.4; |
||||||
|
} |
||||||
|
|
||||||
|
.embedded-event-card .event-footer { |
||||||
|
text-align: right; |
||||||
|
} |
||||||
|
|
||||||
|
.embedded-event-card .view-full { |
||||||
|
color: #007bff; |
||||||
|
text-decoration: none; |
||||||
|
font-size: 0.85em; |
||||||
|
} |
||||||
|
</style> |
||||||
Loading…
Reference in new issue