Skip to content

Commit

Permalink
WIP: front controller path/routing adjustment
Browse files Browse the repository at this point in the history
the new filter ui changes also introduces a couple more filtering
parameters in the form of controller path params, for threads/entries
listing it looks fine and relatively staightforward, but for microblog
posts listing, what used to be a simple `/microblog` is now something
like `/all/hot/∞/all/all/microblog`, which hinders the user experience
on url readability and memorability compared to the previous scheme, for
those who still care

this changes tries to adjust the path/routing of these front controller
especially for the microblog part, to make it more simpler and could
omit some default filter parameters if they aren't set, similar to the
previous path scheme

note that this patch main focus is just the path routing, there's
probably more to adjust/polish in front controller and related areas but
those would likely be a separate follow up patch
  • Loading branch information
asdfzdfj committed Apr 27, 2024
1 parent 6694f84 commit 0e5de43
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 146 deletions.
63 changes: 37 additions & 26 deletions config/kbin_routes/front.yaml
Original file line number Diff line number Diff line change
@@ -1,44 +1,55 @@
front:
controller: App\Controller\Entry\EntryFrontController::front
defaults: { subscription: home, sortBy: hot, time: '∞', type: all, federation: all, content: threads }
path: /{subscription}/{sortBy}/{time}/{type}/{federation}/{content}
defaults: &front_defaults { subscription: home, content: threads, sortBy: hot, time: '∞', type: all, federation: all }
path: /{subscription}/{content}/{sortBy}/{time}/{type}/{federation}
methods: [GET]
requirements:
requirements: &front_requirement
subscription: "%default_subscription_options%"
sortBy: "%default_sort_options%"
time: "%default_time_options%"
type: "%default_type_options%"
federation: "%default_federation_options%"
content: "%default_content_options%"

front_redirect:
controller: App\Controller\Entry\EntryFrontController::front_redirect
defaults: { sortBy: hot, time: '∞', type: all, federation: all, content: threads }
path: /{sortBy}/{time}/{type}/{federation}/{content}
front_sub:
controller: App\Controller\Entry\EntryFrontController::front
defaults: *front_defaults
path: /{subscription}/{sortBy}/{time}/{type}/{federation}
methods: [GET]
requirements:
sortBy: "%default_sort_options%"
time: "%default_time_options%"
type: "%default_type_options%"
federation: "%default_federation_options%"
content: "%default_content_options%"
requirements: *front_requirement

front_content:
controller: App\Controller\Entry\EntryFrontController::front
defaults: *front_defaults
path: /{content}/{sortBy}/{time}/{type}/{federation}
methods: [GET]
requirements: *front_requirement

front_short:
controller: App\Controller\Entry\EntryFrontController::front
defaults: *front_defaults
path: /{sortBy}/{time}/{type}/{federation}
methods: [GET]
requirements: *front_requirement

front_magazine:
controller: App\Controller\Entry\EntryFrontController::magazine
defaults: { sortBy: hot, time: '∞', type: all, federation: all, content: threads }
path: /m/{name}/{sortBy}/{time}/{type}/{federation}/{content}
defaults: &front_magazine_defaults { content: threads, sortBy: hot, time: '∞', type: all, federation: all }
path: /m/{name}/{content}/{sortBy}/{time}/{type}/{federation}
methods: [GET]
requirements:
sortBy: "%default_sort_options%"
time: "%default_time_options%"
type: "%default_type_options%"
federation: "%default_federation_options%"
content: "%default_content_options%"
requirements: *front_requirement

front_magazine_short:
controller: App\Controller\Entry\EntryFrontController::magazine
defaults: &front_magazine_defaults
path: /m/{name}/{sortBy}/{time}/{type}/{federation}
methods: [GET]
requirements: *front_requirement

# Microblog compatibility stuff, redirects from the old routes' URLs

posts_front:
controller: App\Controller\Entry\EntryFrontController::front_redirect
controller: App\Controller\Entry\EntryFrontController::frontRedirect
defaults: { sortBy: hot, time: '∞', type: all, federation: all, content: microblog }
path: /microblog/{sortBy}/{time}
methods: [ GET ]
Expand All @@ -47,7 +58,7 @@ posts_front:
time: "%default_time_options%"

posts_subscribed:
controller: App\Controller\Entry\EntryFrontController::front_redirect
controller: App\Controller\Entry\EntryFrontController::frontRedirect
defaults: { sortBy: hot, time: '∞', type: all, federation: all, content: microblog, subscription: 'sub' }
path: /sub/microblog/{sortBy}/{time}
methods: [ GET ]
Expand All @@ -56,7 +67,7 @@ posts_subscribed:
time: "%default_time_options%"

posts_moderated:
controller: App\Controller\Entry\EntryFrontController::front_redirect
controller: App\Controller\Entry\EntryFrontController::frontRedirect
defaults: { sortBy: hot, time: '∞', type: all, federation: all, content: microblog, subscription: 'mod' }
path: /mod/microblog/{sortBy}/{time}
methods: [ GET ]
Expand All @@ -65,7 +76,7 @@ posts_moderated:
time: "%default_time_options%"

posts_favourite:
controller: App\Controller\Entry\EntryFrontController::front_redirect
controller: App\Controller\Entry\EntryFrontController::frontRedirect
defaults: { sortBy: hot, time: '∞', type: all, federation: all, content: microblog, subscription: 'fav' }
path: /fav/microblog/{sortBy}/{time}
methods: [ GET ]
Expand All @@ -74,7 +85,7 @@ posts_favourite:
time: "%default_time_options%"

magazine_posts:
controller: App\Controller\Entry\EntryFrontController::front_redirect
controller: App\Controller\Entry\EntryFrontController::magazineRedirect
defaults: { sortBy: hot, time: '∞', type: all, federation: all, content: microblog }
path: /m/{name}/microblog/{sortBy}/{time}/{type}/{federation}
methods: [ GET ]
Expand Down
157 changes: 103 additions & 54 deletions src/Controller/Entry/EntryFrontController.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,21 @@

class EntryFrontController extends AbstractController
{
public function __construct(private readonly EntryRepository $entryRepository, private readonly PostRepository $postRepository)
{
public function __construct(
private readonly EntryRepository $entryRepository,
private readonly PostRepository $postRepository
) {
}

public function front(?string $sortBy, ?string $time, ?string $type, string $subscription, string $federation, string $content, Request $request): Response
{
public function front(
string $subscription,
string $content,
?string $sortBy,
?string $time,
?string $type,
string $federation,
Request $request
): Response {
$user = $this->getUser();

$criteria = $this->createCriteria($content, $request);
Expand All @@ -39,57 +48,61 @@ public function front(?string $sortBy, ?string $time, ?string $type, string $sub
if ('home' === $subscription) {
$subscription = $this->subscriptionFor($user);
}
$this->handleSubscription($subscription, $user, $criteria);
$this->handleSubscription($subscription, $criteria);

$this->setUserPreferences($user, $criteria);

$entities = ('threads' === $content) ? $this->entryRepository->findByCriteria($criteria) : $this->postRepository->findByCriteria($criteria);
if ('threads' === $content) {
$entities = $this->entryRepository->findByCriteria($criteria);
$entities = $this->handleCrossposts($entities);
$templatePath = 'entry/';
$dataKey = 'entries';
} elseif ('microblog' === $content) {
$entities = $this->postRepository->findByCriteria($criteria);
$templatePath = 'post/';
$dataKey = 'posts';
} else {
throw new \LogicException("Invalid content filter '{$content}'");
}

$templatePath = ('threads' === $content) ? 'entry/' : 'post/';
$dataKey = ('threads' === $content) ? 'entries' : 'posts';

return $this->renderResponse($request, $content, $criteria, [$dataKey => $entities], $templatePath);
return $this->renderResponse(
$request,
$content,
$criteria,
[$dataKey => $entities],
$templatePath
);
}

// $name is magazine name, for compatibility
public function front_redirect(?string $sortBy, ?string $time, ?string $type, string $federation, string $content, ?string $name, Request $request): Response
{
$user = $this->getUser(); // Fetch the user
$subscription = $this->subscriptionFor($user); // Determine the subscription filter based on the user

if ($name) {
return $this->redirectToRoute('front_magazine', [
'name' => $name,
'subscription' => $subscription,
'sortBy' => $sortBy,
'time' => $time,
'type' => $type,
'federation' => $federation,
'content' => $content,
]);
} else {
return $this->redirectToRoute('front', [
'subscription' => $subscription,
'sortBy' => $sortBy,
'time' => $time,
'type' => $type,
'federation' => $federation,
'content' => $content,
]);
}
public function frontRedirect(
string $content,
?string $sortBy,
?string $time,
?string $type,
string $federation,
Request $request
): Response {
$user = $this->getUser();
$subscription = $this->subscriptionFor($user);

return $this->redirectToRoute('front', [
'subscription' => $subscription,
'sortBy' => $sortBy,
'time' => $time,
'type' => $type,
'federation' => $federation,
'content' => $content,
]);
}

public function magazine(
#[MapEntity(expr: 'repository.findOneByName(name)')]
Magazine $magazine,
string $content,
?string $sortBy,
?string $time,
?string $type,
string $federation,
string $content,
Request $request
): Response {
$user = $this->getUser();
Expand All @@ -106,21 +119,57 @@ public function magazine(
$criteria->magazine = $magazine;
$criteria->stickiesFirst = true;

$subscription = $request->query->get('subscription');
if (!$subscription) {
$subscription = 'all';
}
$this->handleSubscription($subscription, $user, $criteria);
$subscription = $request->query->get('subscription') ?: 'all';
$this->handleSubscription($subscription, $criteria);

$this->setUserPreferences($user, $criteria);

$entities = ('threads' === $content) ? $this->entryRepository->findByCriteria($criteria) : $this->postRepository->findByCriteria($criteria);
// Note no crosspost handling
if ('threads' === $content) {
$entities = $this->entryRepository->findByCriteria($criteria);
// Note no crosspost handling
$templatePath = 'entry/';
$dataKey = 'entries';
} elseif ('microblog' === $content) {
$entities = $this->postRepository->findByCriteria($criteria);
$templatePath = 'post/';
$dataKey = 'posts';
} else {
throw new \LogicException("Invalid content filter '{$content}'");
}

$templatePath = ('threads' === $content) ? 'entry/' : 'post/';
$dataKey = ('threads' === $content) ? 'entries' : 'posts';
return $this->renderResponse(
$request,
$content,
$criteria,
[$dataKey => $entities, 'magazine' => $magazine],
$templatePath
);
}

return $this->renderResponse($request, $content, $criteria, [$dataKey => $entities, 'magazine' => $magazine], $templatePath);
/**
* @param string $name magazine name
*/
public function magazineRedirect(
string $name,
string $content,
?string $sortBy,
?string $time,
?string $type,
string $federation,
Request $request
): Response {
$user = $this->getUser(); // Fetch the user
$subscription = $this->subscriptionFor($user); // Determine the subscription filter based on the user

return $this->redirectToRoute('front_magazine', [
'name' => $name,
'subscription' => $subscription,
'sortBy' => $sortBy,
'time' => $time,
'type' => $type,
'federation' => $federation,
'content' => $content,
]);
}

private function createCriteria(string $content, Request $request)
Expand All @@ -136,19 +185,18 @@ private function createCriteria(string $content, Request $request)
return $criteria->setContent($content);
}

private function handleSubscription(string $subscription, $user, &$criteria)
private function handleSubscription(string $subscription, &$criteria)
{
if ('sub' === $subscription) {
if (\in_array($subscription, ['sub', 'mod', 'fav'])) {
$this->denyAccessUnlessGranted('ROLE_USER');
$this->getUserOrThrow();
}

if ('sub' === $subscription) {
$criteria->subscribed = true;
} elseif ('mod' === $subscription) {
$this->denyAccessUnlessGranted('ROLE_USER');
$this->getUserOrThrow();
$criteria->moderated = true;
} elseif ('fav' === $subscription) {
$this->denyAccessUnlessGranted('ROLE_USER');
$this->getUserOrThrow();
$criteria->favourite = true;
} elseif ($subscription && 'all' !== $subscription) {
throw new \LogicException('Invalid subscription filter '.$subscription);
Expand All @@ -164,7 +212,8 @@ private function setUserPreferences(?User $user, &$criteria)

private function renderResponse(Request $request, $content, $criteria, $data, $templatePath)
{
$baseData = ['criteria' => $criteria] + $data;
$baseData = array_merge(['criteria' => $criteria], $data);

if ('microblog' === $content) {
$dto = new PostDto();
if (isset($data['magazine'])) {
Expand Down
23 changes: 7 additions & 16 deletions src/Repository/Criteria.php
Original file line number Diff line number Diff line change
Expand Up @@ -230,22 +230,13 @@ public function resolveTime(?string $value, bool $reverse = false): ?string

public function resolveType(?string $value): ?string
{
// @todo
$routes = [
'all' => 'all',
'article' => Entry::ENTRY_TYPE_ARTICLE,
'articles' => Entry::ENTRY_TYPE_ARTICLE,
'link' => Entry::ENTRY_TYPE_LINK,
'links' => Entry::ENTRY_TYPE_LINK,
'video' => Entry::ENTRY_TYPE_VIDEO,
'videos' => Entry::ENTRY_TYPE_VIDEO,
'photo' => Entry::ENTRY_TYPE_IMAGE,
'photos' => Entry::ENTRY_TYPE_IMAGE,
'image' => Entry::ENTRY_TYPE_IMAGE,
'images' => Entry::ENTRY_TYPE_IMAGE,
];

return $routes[$value] ?? 'all';
return match ($value) {
'article', 'articles' => Entry::ENTRY_TYPE_ARTICLE,
'link', 'links' => Entry::ENTRY_TYPE_LINK,
'video', 'videos' => Entry::ENTRY_TYPE_VIDEO,
'photo', 'photos', 'image', 'images' => Entry::ENTRY_TYPE_IMAGE,
default => 'all'
};
}

public function translateType(): string
Expand Down
19 changes: 19 additions & 0 deletions src/Twig/Extension/FrontExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

declare(strict_types=1);

namespace App\Twig\Extension;

use App\Twig\Runtime\FrontExtensionRuntime;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

final class FrontExtension extends AbstractExtension
{
public function getFunctions(): array
{
return [
new TwigFunction('front_options_url', [FrontExtensionRuntime::class, 'frontOptionsUrl']),
];
}
}
Loading

0 comments on commit 0e5de43

Please sign in to comment.