clone of github.com/decent-newsroom/newsroom
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

194 lines
6.9 KiB

<?php
declare(strict_types=1);
namespace App\Controller;
use App\Dto\CategoryDraft;
use App\Form\CategoryArticlesType;
use App\Form\CategoryType;
use App\Service\ReadingListManager;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class ReadingListWizardController extends AbstractController
{
private const SESSION_KEY = 'read_wizard';
#[Route('/reading-list/wizard/setup', name: 'read_wizard_setup')]
public function setup(Request $request): Response
{
$draft = $this->getDraft($request) ?? new CategoryDraft();
$form = $this->createForm(CategoryType::class, $draft);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** @var CategoryDraft $draft */
$draft = $form->getData();
if (!$draft->slug) {
$draft->slug = $this->slugifyWithRandom($draft->title);
}
$this->saveDraft($request, $draft);
return $this->redirectToRoute('read_wizard_articles');
}
return $this->render('reading_list/reading_setup.html.twig', [
'form' => $form->createView(),
]);
}
#[Route('/reading-list/wizard/articles', name: 'read_wizard_articles')]
public function articles(Request $request, ReadingListManager $readingListManager): Response
{
$draft = $this->getDraft($request);
$loadSlug = $request->query->get('load');
if ($loadSlug) {
$draft = $readingListManager->loadPublishedListIntoDraft($loadSlug);
$this->saveDraft($request, $draft);
} elseif (!$draft) {
return $this->redirectToRoute('read_wizard_setup');
}
// Ensure at least one input is visible initially
if (empty($draft->articles)) {
$draft->articles = [''];
}
$form = $this->createForm(CategoryArticlesType::class, $draft);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** @var CategoryDraft $draft */
$draft = $form->getData();
// ensure slug exists
if (!$draft->slug) {
$draft->slug = $this->slugifyWithRandom($draft->title);
}
// If draft articles is still empty, remove the empty string we added
if (count($draft->articles) === 1 && $draft->articles[0] === '') {
$draft->articles = [];
}
$this->saveDraft($request, $draft);
return $this->redirectToRoute('read_wizard_review');
}
return $this->render('reading_list/reading_articles.html.twig', [
'form' => $form->createView(),
]);
}
#[Route('/reading-list/add-article', name: 'read_wizard_add_article')]
public function addArticle(Request $request, ReadingListManager $readingListManager): Response
{
// Get the coordinate from the query parameter
$coordinate = $request->query->get('coordinate');
if (!$coordinate) {
$this->addFlash('error', 'No article coordinate provided.');
return $this->redirectToRoute('reading_list_compose');
}
// Get available reading lists
$availableLists = $readingListManager->getUserReadingLists();
$currentDraft = $readingListManager->getCurrentDraft();
// Handle form submission
if ($request->isMethod('POST')) {
$selectedSlug = $request->request->get('selected_list');
// Load or create the selected list
if ($selectedSlug === '__new__' || !$selectedSlug) {
$draft = $readingListManager->createNewDraft();
} else {
$draft = $readingListManager->loadPublishedListIntoDraft($selectedSlug);
}
// Add the article to the draft
if (!in_array($coordinate, $draft->articles, true)) {
$draft->articles[] = $coordinate;
$session = $request->getSession();
$session->set('read_wizard', $draft);
}
// Redirect to compose page with success message
return $this->redirectToRoute('reading_list_compose', [
'add' => $coordinate,
'list' => $selectedSlug ?? '__new__'
]);
}
return $this->render('reading_list/add_article_confirm.html.twig', [
'coordinate' => $coordinate,
'availableLists' => $availableLists,
'currentDraft' => $currentDraft,
]);
}
#[Route('/reading-list/wizard/review', name: 'read_wizard_review')]
public function review(Request $request): Response
{
$draft = $this->getDraft($request);
if (!$draft) {
return $this->redirectToRoute('read_wizard_setup');
}
// Build a single category event skeleton
$tags = [];
$tags[] = ['d', $draft->slug];
$tags[] = ['type', 'reading-list'];
if ($draft->title) { $tags[] = ['title', $draft->title]; }
if ($draft->summary) { $tags[] = ['summary', $draft->summary]; }
foreach ($draft->tags as $t) { $tags[] = ['t', $t]; }
foreach ($draft->articles as $a) {
if (is_string($a) && $a !== '') { $tags[] = ['a', $a]; }
}
$event = [
'kind' => 30040,
'created_at' => time(),
'tags' => $tags,
'content' => '',
];
return $this->render('reading_list/reading_review.html.twig', [
'draft' => $draft,
'eventJson' => json_encode($event, JSON_UNESCAPED_SLASHES),
'csrfToken' => $this->container->get('security.csrf.token_manager')->getToken('nostr_publish')->getValue(),
]);
}
#[Route('/reading-list/wizard/cancel', name: 'read_wizard_cancel', methods: ['GET'])]
public function cancel(Request $request): Response
{
$this->clearDraft($request);
$this->addFlash('info', 'Reading list creation canceled.');
return $this->redirectToRoute('home');
}
private function getDraft(Request $request): ?CategoryDraft
{
$data = $request->getSession()->get(self::SESSION_KEY);
return $data instanceof CategoryDraft ? $data : null;
}
private function saveDraft(Request $request, CategoryDraft $draft): void
{
$request->getSession()->set(self::SESSION_KEY, $draft);
}
private function clearDraft(Request $request): void
{
$request->getSession()->remove(self::SESSION_KEY);
}
private function slugifyWithRandom(string $title): string
{
$slug = strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $title)) ?? '');
$slug = trim(preg_replace('/-+/', '-', $slug) ?? '', '-');
$rand = substr(bin2hex(random_bytes(4)), 0, 6);
return $slug !== '' ? ($slug . '-' . $rand) : $rand;
}
}