Browse Source

Demo page for events

imwald
Nuša Pukšič 7 months ago
parent
commit
07fc1fc422
  1. 98
      src/Controller/EventController.php
  2. 145
      templates/event/index.html.twig

98
src/Controller/EventController.php

@ -0,0 +1,98 @@ @@ -0,0 +1,98 @@
<?php
declare(strict_types=1);
namespace App\Controller;
use App\Service\NostrClient;
use App\Service\RedisCacheService;
use Exception;
use nostriphant\NIP19\Bech32;
use nostriphant\NIP19\Data;
use Psr\Log\LoggerInterface;
use swentel\nostr\Key\Key;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Attribute\Route;
class EventController extends AbstractController
{
/**
* @throws Exception
*/
#[Route('/e/{nevent}', name: 'nevent', requirements: ['nevent' => '^nevent1.*'])]
public function index($nevent, NostrClient $nostrClient, RedisCacheService $redisCacheService, LoggerInterface $logger): Response
{
$logger->info('Accessing event page', ['nevent' => $nevent]);
try {
// Decode nevent - nevent1... is a NIP-19 encoded event identifier
$decoded = new Bech32($nevent);
$logger->info('Decoded event', ['decoded' => json_encode($decoded)]);
// Get the event using the event ID
/** @var Data $data */
$data = $decoded->data;
$logger->info('Event data', ['data' => json_encode($data)]);
// Sort which event type this is using $data->type
switch ($decoded->type) {
case 'note':
// Handle note (regular event)
$relays = $data->relays ?? [];
$event = $nostrClient->getEventById($data->identifier, $relays);
break;
case 'nprofile':
// Redirect to author profile if it's a profile identifier
$logger->info('Redirecting to author profile', ['pubkey' => $data->pubkey]);
return $this->redirectToRoute('author-redirect', ['pubkey' => $data->pubkey]);
case 'nevent':
// Handle nevent identifier (event with additional metadata)
$relays = $data->relays ?? [];
$event = $nostrClient->getEventById($data->id, $relays);
break;
case 'naddr':
// Handle naddr (parameterized replaceable event)
$decodedData = [
'kind' => $data->kind,
'pubkey' => $data->pubkey,
'identifier' => $data->identifier,
'relays' => $data->relays ?? []
];
$event = $nostrClient->getEventByNaddr($decodedData);
break;
default:
$logger->error('Unsupported event type', ['type' => $decoded->type]);
throw new NotFoundHttpException('Unsupported event type: ' . $decoded->type);
}
if (!$event) {
$logger->warning('Event not found', ['data' => $data]);
throw new NotFoundHttpException('Event not found');
}
// If author is included in the event, get metadata
$authorMetadata = null;
if (isset($event->pubkey)) {
$key = new Key();
$npub = $key->convertPublicKeyToBech32($event->pubkey);
$authorMetadata = $redisCacheService->getMetadata($npub);
}
// Render template with the event data
return $this->render('event/index.html.twig', [
'event' => $event,
'author' => $authorMetadata
]);
} catch (Exception $e) {
$logger->error('Error processing event', ['error' => $e->getMessage()]);
throw $e;
}
}
}

145
templates/event/index.html.twig

@ -0,0 +1,145 @@ @@ -0,0 +1,145 @@
{% extends 'base.html.twig' %}
{% block title %}Nostr Event{% endblock %}
{% block body %}
<div class="container">
<div class="event-container">
<div class="event-header">
{% if author %}
{% if author.image is defined %}
<img src="{{ author.image }}" class="avatar" alt="{{ author.name }}" onerror="this.style.display = 'none'" />
{% endif %}
<twig:Molecules:UserFromNpub ident="{{ event.pubkey }}" />
<div>
{% if author.about is defined %}
{{ author.about|markdown_to_html|mentionify }}
{% endif %}
</div>
<hr />
{% endif %}
<div class="event-meta">
<span class="event-date">{{ event.created_at|date('F j, Y - H:i') }}</span>
</div>
</div>
<div class="event-content">
<twig:Atoms:Content :content="event.content" />
</div>
<div class="event-footer">
<div class="event-tags">
{% if event.tags is defined and event.tags|length > 0 %}
<h4>Tags:</h4>
<ul>
{% for tag in event.tags %}
{% if tag[0] != 'e' and tag[0] != 'p' %}
<li>
<strong>{{ tag[0] }}:</strong> {{ tag[1] }}
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}
</div>
<div class="event-references">
{% if event.tags is defined %}
{% set references = [] %}
{% for tag in event.tags %}
{% if tag[0] == 'e' %}
{% set references = references|merge([tag[1]]) %}
{% endif %}
{% endfor %}
{% if references|length > 0 %}
<h4>References:</h4>
<ul>
{% for ref in references %}
<li>
<a href="{{ path('nevent', {nevent: 'nevent1' ~ ref}) }}">{{ ref|slice(0, 8) }}...</a>
</li>
{% endfor %}
</ul>
{% endif %}
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}
{% block stylesheets %}
{{ parent() }}
<style>
.event-container {
max-width: 800px;
margin: 2rem auto;
padding: 1.5rem;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}
.event-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 1.5rem;
border-bottom: 1px solid #eee;
padding-bottom: 1rem;
}
.author-info {
display: flex;
align-items: center;
gap: 1rem;
}
.author-picture {
width: 50px;
height: 50px;
border-radius: 50%;
object-fit: cover;
}
.event-content {
font-size: 1.1rem;
line-height: 1.6;
margin-bottom: 2rem;
white-space: pre-wrap;
}
.event-footer {
display: flex;
justify-content: space-between;
margin-top: 1.5rem;
padding-top: 1rem;
border-top: 1px solid #eee;
}
.event-tags, .event-references {
flex: 1;
}
.event-tags ul, .event-references ul {
list-style-type: none;
padding-left: 0;
}
.event-tags li, .event-references li {
margin-bottom: 0.5rem;
}
.event-id {
margin-top: 1.5rem;
color: #888;
text-align: right;
}
.nip05 {
color: #0066cc;
font-size: 0.9rem;
}
</style>
{% endblock %}
Loading…
Cancel
Save