Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
5 / 5
CRAP
100.00% covered (success)
100.00%
1 / 1
MentionsHtmxController
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
5 / 5
5
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 actionList
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
1
 actionMarkAllRead
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 actionMarkRead
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 renderListResponse
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3declare(strict_types=1);
4
5/** @license For full copyright and license information, please see the LICENSE.md file. */
6
7namespace App\Modules\Comments\Presentation\Htmx;
8
9defined('AMMONLY_APP') || exit('Direct script access is forbidden.');
10
11use App\Core\Engine\Domain\Model\PermissionContext;
12use App\Modules\Comments\Application\Service\CommentMentionsQueryServiceInterface;
13use Nyholm\Psr7\Factory\Psr17Factory;
14use Psr\Http\Message\ResponseInterface;
15use Psr\Http\Message\ServerRequestInterface;
16use Twig\Environment as TwigEnvironment;
17
18/**
19 * HTMX Controller handling dynamic partial updates for Mentions Inbox.
20 *
21 * @package App\Modules\Comments\Presentation\Htmx
22 */
23final readonly class MentionsHtmxController
24{
25    /**
26     * MentionsHtmxController constructor.
27     */
28    public function __construct(
29        private TwigEnvironment $twig,
30        private CommentMentionsQueryServiceInterface $mentionsService,
31        private Psr17Factory $psr17Factory
32    ) {
33    }
34
35    /**
36     * Renders filtered partial list of mentions.
37     */
38    public function actionList(ServerRequestInterface $request, PermissionContext $ctx): ResponseInterface
39    {
40        $params = $request->getQueryParams();
41        $scope = (string) ($params['scope'] ?? 'unread');
42        $query = (string) ($params['q'] ?? '');
43
44        $inbox = $this->mentionsService->getInbox($ctx->actorUserId, $scope, $query);
45
46        return $this->renderListResponse($inbox, $scope, $query);
47    }
48
49    /**
50     * Marks all mentions as read and re-renders stream.
51     */
52    public function actionMarkAllRead(ServerRequestInterface $request, PermissionContext $ctx): ResponseInterface
53    {
54        $this->mentionsService->markAllAsRead($ctx->actorUserId);
55
56        $params = $request->getQueryParams();
57        $scope = (string) ($params['scope'] ?? 'unread');
58        $query = (string) ($params['q'] ?? '');
59
60        $inbox = $this->mentionsService->getInbox($ctx->actorUserId, $scope, $query);
61
62        return $this->renderListResponse($inbox, $scope, $query);
63    }
64
65    /**
66     * Marks a specific mention as read and re-renders stream.
67     */
68    public function actionMarkRead(
69        ServerRequestInterface $request,
70        int $commentId,
71        PermissionContext $ctx
72    ): ResponseInterface {
73        $this->mentionsService->markAsReadUpToComment($ctx->actorUserId, $commentId);
74
75        $params = $request->getQueryParams();
76        $scope = (string) ($params['scope'] ?? 'unread');
77        $query = (string) ($params['q'] ?? '');
78
79        $inbox = $this->mentionsService->getInbox($ctx->actorUserId, $scope, $query);
80
81        return $this->renderListResponse($inbox, $scope, $query);
82    }
83
84    /**
85     * Helper rendering partial list with HTMX trigger header for unread count badge.
86     */
87    private function renderListResponse(
88        mixed $inbox,
89        string $scope,
90        string $query
91    ): ResponseInterface {
92        $html = $this->twig->render('modules/comments/partials/mentions_list.twig', [
93            'inbox'        => $inbox,
94            'active_scope' => $scope,
95            'search_query' => $query,
96        ]);
97
98        $response = $this->psr17Factory->createResponse(200);
99        $response->getBody()->write($html);
100
101        $triggerData = json_encode(['mentionsCountUpdate' => ['count' => $inbox->unreadCount]]);
102
103        return $response
104            ->withHeader('Content-Type', 'text/html; charset=utf-8')
105            ->withHeader('HX-Trigger', (string) $triggerData);
106    }
107}