diff --git a/assets/controllers/search_broadcast_controller.js b/assets/controllers/search_broadcast_controller.js new file mode 100644 index 0000000..fca46da --- /dev/null +++ b/assets/controllers/search_broadcast_controller.js @@ -0,0 +1,9 @@ +import { Controller } from '@hotwired/stimulus'; + +export default class extends Controller { + onInput(e) { + const value = e.target.value || ''; + const active = value.trim().length > 0; + window.dispatchEvent(new CustomEvent('search:changed', { detail: { active } })); + } +} diff --git a/assets/controllers/search_visibility_controller.js b/assets/controllers/search_visibility_controller.js new file mode 100644 index 0000000..cc78fc9 --- /dev/null +++ b/assets/controllers/search_visibility_controller.js @@ -0,0 +1,12 @@ +import { Controller } from '@hotwired/stimulus'; + +export default class extends Controller { + static targets = ['list']; + + toggle(event) { + const active = !!event.detail?.active; + if (this.hasListTarget) { + this.listTarget.hidden = active; + } + } +} diff --git a/assets/styles/form.css b/assets/styles/form.css index 0f38c60..ec29103 100644 --- a/assets/styles/form.css +++ b/assets/styles/form.css @@ -67,3 +67,8 @@ textarea, input { #editor { margin: 0; } + +button:disabled { + opacity: 0.5; + cursor: not-allowed; +} diff --git a/assets/styles/utilities.css b/assets/styles/utilities.css index 8bddfb9..59d98ac 100644 --- a/assets/styles/utilities.css +++ b/assets/styles/utilities.css @@ -45,3 +45,5 @@ /* Details/Summary tweaks */ details>summary{cursor:pointer} +/* Text truncation with ellipsis */ +.line-clamp-5 {display: -webkit-box;-webkit-box-orient: vertical;-webkit-line-clamp: 5;overflow: hidden;text-overflow: ellipsis;} diff --git a/config/packages/security.yaml b/config/packages/security.yaml index b32a818..5e91441 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -27,6 +27,6 @@ security: # Note: Only the *first* access control that matches will be used access_control: - { path: ^/admin, roles: ROLE_USER } - - { path: ^/search, roles: ROLE_USER } + # - { path: ^/search, roles: ROLE_USER } # - { path: ^/nzine, roles: ROLE_USER } # - { path: ^/profile, roles: ROLE_USER } diff --git a/src/Command/NostrEventFromYamlDefinitionCommand.php b/src/Command/NostrEventFromYamlDefinitionCommand.php index 9dd51de..b8b341c 100644 --- a/src/Command/NostrEventFromYamlDefinitionCommand.php +++ b/src/Command/NostrEventFromYamlDefinitionCommand.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace App\Command; use App\Enum\IndexStatusEnum; +use App\Enum\KindsEnum; use App\Factory\ArticleFactory; use App\Service\NostrClient; use Doctrine\ORM\EntityManagerInterface; @@ -71,7 +72,7 @@ class NostrEventFromYamlDefinitionCommand extends Command try { // Deserialize YAML content into an Event object $event = new Event(); - $event->setKind(30040); + $event->setKind(KindsEnum::PUBLICATION_INDEX->value); $tags = $yamlContent['tags']; $event->setTags($tags); $items = array_filter($tags, function ($tag) { diff --git a/src/Controller/DefaultController.php b/src/Controller/DefaultController.php index cee4c44..4a6b875 100644 --- a/src/Controller/DefaultController.php +++ b/src/Controller/DefaultController.php @@ -19,6 +19,7 @@ use Psr\Log\LoggerInterface; class DefaultController extends AbstractController { public function __construct( + private readonly FinderInterface $finder, private readonly CacheInterface $redisCache) { } @@ -30,6 +31,11 @@ class DefaultController extends AbstractController #[Route('/', name: 'home')] public function index(): Response { + // get latest articles + $q = new Query(); + $q->setSize(12); + $q->setSort(['createdAt' => ['order' => 'desc']]); + $latest = $this->finder->find($q); // get newsroom index, loop over categories, pick top three from each and display in sections $mag = $this->redisCache->get('magazine-newsroom-magazine-by-newsroom', function (){ return null; @@ -41,7 +47,8 @@ class DefaultController extends AbstractController }); return $this->render('home.html.twig', [ - 'indices' => array_values($cats) + 'indices' => array_values($cats), + 'latest' => $latest ]); } diff --git a/src/Controller/MagazineWizardController.php b/src/Controller/MagazineWizardController.php index 66a91cb..fe0bf24 100644 --- a/src/Controller/MagazineWizardController.php +++ b/src/Controller/MagazineWizardController.php @@ -6,9 +6,11 @@ namespace App\Controller; use App\Dto\CategoryDraft; use App\Dto\MagazineDraft; +use App\Enum\KindsEnum; use App\Form\CategoryArticlesType; use App\Form\MagazineSetupType; use App\Service\RedisCacheService; +use Doctrine\ORM\EntityManagerInterface; use Psr\Cache\CacheItemPoolInterface; use swentel\nostr\Event\Event; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; @@ -164,7 +166,8 @@ class MagazineWizardController extends AbstractController Request $request, CacheItemPoolInterface $redisCache, CsrfTokenManagerInterface $csrfTokenManager, - RedisClient $redis + RedisClient $redis, + EntityManagerInterface $entityManager ): JsonResponse { // Verify CSRF token $csrfToken = $request->headers->get('X-CSRF-TOKEN'); @@ -184,7 +187,7 @@ class MagazineWizardController extends AbstractController $eventObj->setId($signedEvent['id'] ?? ''); $eventObj->setPublicKey($signedEvent['pubkey'] ?? ''); $eventObj->setCreatedAt($signedEvent['created_at'] ?? time()); - $eventObj->setKind($signedEvent['kind'] ?? 30040); + $eventObj->setKind($signedEvent['kind'] ?? KindsEnum::PUBLICATION_INDEX->value); $eventObj->setTags($signedEvent['tags'] ?? []); $eventObj->setContent($signedEvent['content'] ?? ''); $eventObj->setSignature($signedEvent['sig'] ?? ''); @@ -230,6 +233,20 @@ class MagazineWizardController extends AbstractController // non-fatal } + // Save to persistence as Event entity + // Map swentel Event to Event entity, it's always a new event + $event = new \App\Entity\Event(); + $event->setId($eventObj->getId()); + $event->setPubkey($eventObj->getPublicKey()); + $event->setCreatedAt($eventObj->getCreatedAt()); + $event->setKind($eventObj->getKind()); + $event->setTags($eventObj->getTags()); + $event->setContent($eventObj->getContent()); + $event->setSig($eventObj->getSignature()); + // Persist + $entityManager->persist($event); + $entityManager->flush(); + return new JsonResponse(['ok' => true]); } diff --git a/src/Twig/Components/SearchComponent.php b/src/Twig/Components/SearchComponent.php index d074515..70bc41b 100644 --- a/src/Twig/Components/SearchComponent.php +++ b/src/Twig/Components/SearchComponent.php @@ -28,6 +28,7 @@ final class SearchComponent public array $results = []; public bool $interactive = true; + public string $currentRoute; public int $credits = 0; public ?string $npub = null; @@ -55,8 +56,9 @@ final class SearchComponent { } - public function mount(): void + public function mount($currentRoute = 'search'): void { + $this->currentRoute = $currentRoute; $token = $this->tokenStorage->getToken(); $this->npub = $token?->getUserIdentifier(); @@ -71,7 +73,7 @@ final class SearchComponent } // Restore search results from session if available and no query provided - if (empty($this->query)) { + if (empty($this->query) && $this->currentRoute == 'search') { $session = $this->requestStack->getSession(); if ($session->has(self::SESSION_QUERY_KEY)) { $this->query = $session->get(self::SESSION_QUERY_KEY); @@ -237,7 +239,7 @@ final class SearchComponent ]); // Increase minimum score to filter out irrelevant results - $mainQuery->setMinScore(0.3); + $mainQuery->setMinScore(0.35); // Sort by score and createdAt $mainQuery->setSort([ diff --git a/templates/components/Molecules/Card.html.twig b/templates/components/Molecules/Card.html.twig index 21f4008..e6904d0 100644 --- a/templates/components/Molecules/Card.html.twig +++ b/templates/components/Molecules/Card.html.twig @@ -20,7 +20,7 @@
+
{{ article.summary }}
{% endif %} diff --git a/templates/components/Organisms/ZineList.html.twig b/templates/components/Organisms/ZineList.html.twig index 8c867cd..b8eb23c 100644 --- a/templates/components/Organisms/ZineList.html.twig +++ b/templates/components/Organisms/ZineList.html.twig @@ -1,16 +1,22 @@{{ idx.summary }}
-Log in to search articles.
+ Currently only available on desktop. +{{ 'text.noResults'|trans }}
{% endif %} - - {% block aside %} - {% if credits == 0 %} -Log in to access search.
+