[PLUGINS][TreeNotes] Feed only shows each note and its respective direct

replies, conversation shows whole tree

[COMPONENTS][Feed] Added request to FormatNoteList event

Every single Note that was provided to FeedController::postProcess is
shown. This means, that even though the Feed is formatted to show only a
Note and its respective direct replies, those same replies are shown
individually again (and they get the chance to show their own direct
replies).

The Note list provided to FormatNoteList is reversed, and for every
index, the respective Note replies are filtered out of the original list.
The replies are then added as leafs of the current Note and added to the tree.
This commit is contained in:
Eliseu Amaro 2021-12-31 23:26:39 +00:00
parent f6311debbf
commit e7940a21ee
Signed by: eliseuamaro
GPG Key ID: 96DA09D4B97BC2D5
6 changed files with 59 additions and 17 deletions

View File

@ -49,7 +49,7 @@ class Reply extends FeedController
* *
* @return array * @return array
*/ */
public function addReply(Request $request, int $note_id, int $actor_id) public function addReply(Request $request, int $note_id)
{ {
$user = Common::ensureLoggedIn(); $user = Common::ensureLoggedIn();

View File

@ -86,9 +86,9 @@ class Conversation extends Component
} }
// Generating URL for reply action route // Generating URL for reply action route
$args = ['note_id' => $note->getId(), 'actor_id' => $note->getActor()->getId()]; $args = ['note_id' => $note->getId()];
$type = Router::ABSOLUTE_PATH; $type = Router::ABSOLUTE_PATH;
$reply_action_url = Router::url('reply_add', $args, $type); $reply_action_url = Router::url('conversation_reply_to', $args, $type);
$query_string = $request->getQueryString(); $query_string = $request->getQueryString();
// Concatenating get parameter to redirect the user to where he came from // Concatenating get parameter to redirect the user to where he came from
@ -109,7 +109,7 @@ class Conversation extends Component
{ {
// If Actor is adding a reply, get parent's Note id // If Actor is adding a reply, get parent's Note id
// Else it's null // Else it's null
$extra_args['reply_to'] = $request->get('_route') === 'reply_add' ? (int) $request->get('note_id') : null; $extra_args['reply_to'] = $request->get('_route') === 'conversation_reply_to' ? (int) $request->get('note_id') : null;
return Event::next; return Event::next;
} }
@ -147,7 +147,7 @@ class Conversation extends Component
public function onAddRoute(RouteLoader $r): bool public function onAddRoute(RouteLoader $r): bool
{ {
$r->connect('reply_add', '/object/note/new?to={actor_id<\d+>}&reply_to={note_id<\d+>}', [ReplyController::class, 'addReply']); $r->connect('conversation_reply_to', '/conversation/reply?reply_to_note={note_id<\d+>}', [ReplyController::class, 'addReply']);
$r->connect('conversation', '/conversation/{conversation_id<\d+>}', [Controller\Conversation::class, 'showConversation']); $r->connect('conversation', '/conversation/{conversation_id<\d+>}', [Controller\Conversation::class, 'showConversation']);
$r->connect('conversation_mute', '/conversation/{conversation_id<\d+>}/mute', [Controller\Conversation::class, 'muteConversation']); $r->connect('conversation_mute', '/conversation/{conversation_id<\d+>}/mute', [Controller\Conversation::class, 'muteConversation']);

View File

@ -38,6 +38,7 @@ use App\Entity\Actor;
use App\Entity\Note; use App\Entity\Note;
use App\Util\Common; use App\Util\Common;
use Functional as F; use Functional as F;
use function App\Core\I18n\_m;
abstract class FeedController extends Controller abstract class FeedController extends Controller
{ {
@ -49,12 +50,11 @@ abstract class FeedController extends Controller
protected function postProcess(array $result): array protected function postProcess(array $result): array
{ {
$actor = Common::actor(); $actor = Common::actor();
if (\array_key_exists('notes', $result)) { if (\array_key_exists('notes', $result)) {
$notes = $result['notes']; $notes = $result['notes'];
self::enforceScope($notes, $actor); self::enforceScope($notes, $actor);
Event::handle('FilterNoteList', [$actor, &$notes, $result['request']]); Event::handle('FilterNoteList', [$actor, &$notes, $result['request']]);
Event::handle('FormatNoteList', [$notes, &$result['notes']]); Event::handle('FormatNoteList', [$notes, &$result['notes'], &$result['request']]);
} }
return $result; return $result;

View File

@ -15,7 +15,7 @@
<summary class="section-title-summary"> <summary class="section-title-summary">
<h2> <h2>
{% set current_path = app.request.get('_route') %} {% set current_path = app.request.get('_route') %}
{% if current_path == 'reply_add' %} {% if current_path == 'conversation_reply_to' %}
{{ "Reply to note" | trans }} {{ "Reply to note" | trans }}
{% else %} {% else %}
{{ "Create a note" | trans }} {{ "Create a note" | trans }}

View File

@ -23,30 +23,72 @@ namespace Plugin\TreeNotes;
use App\Core\Modules\Plugin; use App\Core\Modules\Plugin;
use App\Entity\Note; use App\Entity\Note;
use Symfony\Component\HttpFoundation\Request;
class TreeNotes extends Plugin class TreeNotes extends Plugin
{ {
/** /**
* Format the given $notes_in_trees_out in a list of reply trees * Formatting notes without taking a direct reply out of context
* Show whole conversation in conversation related routes
*
* @param array $notes_in
* @param array $notes_out
* @param \Symfony\Component\HttpFoundation\Request $request
*
* @return void
*/ */
public function onFormatNoteList(array $notes_in, ?array &$notes_out) public function onFormatNoteList(array $notes_in, array &$notes_out, Request $request)
{ {
$roots = array_filter($notes_in, static fn (Note $note) => \is_null($note->getReplyTo())); if (str_starts_with($request->get('_route'), 'conversation')) {
$notes_out = $this->build_tree($roots, $notes_in); $parents = $this->conversationFormat($notes_in);
$notes_out = $this->conversationFormatTree($parents, $notes_in);
} else {
$notes_out = $this->feedFormatTree($notes_in);
}
} }
private function build_tree(array $parents, array $notes) private function feedFormatTree(array $notes): array
{
$tree = [];
$notes = array_reverse($notes);
foreach ($notes as $note) {
if (!is_null($children = $note->getReplies())) {
$notes = array_filter($notes, fn (Note $n) => !in_array($n, $children));
$tree[] = [
'note' => $note,
'replies' => array_map(
function ($n) {
return ['note' => $n, 'replies' => []];
},
$children
),
];
} else {
$tree[] = ['note' => $note, 'replies' => []];
}
}
return array_reverse($tree);
}
private function conversationFormat(array $notes_in)
{
return array_filter($notes_in, static fn (Note $note) => \is_null($note->getReplyTo()));
}
private function conversationFormatTree(array $parents, array $notes)
{ {
$subtree = []; $subtree = [];
foreach ($parents as $p) { foreach ($parents as $p) {
$subtree[] = $this->build_subtree($p, $notes); $subtree[] = $this->conversationFormatSubTree($p, $notes);
} }
return $subtree; return $subtree;
} }
private function build_subtree(Note $parent, array $notes) private function conversationFormatSubTree(Note $parent, array $notes)
{ {
$children = array_filter($notes, fn (Note $note) => $note->getReplyTo() === $parent->getId()); $children = array_filter($notes, fn (Note $note) => $note->getReplyTo() === $parent->getId());
return ['note' => $parent, 'replies' => $this->build_tree($children, $notes)]; return ['note' => $parent, 'replies' => $this->conversationFormatTree($children, $notes)];
} }
} }

View File

@ -352,7 +352,7 @@ class Note extends Entity
*/ */
public function getReplies(): array public function getReplies(): array
{ {
return Cache::getList('note-replies-' . $this->getId(), fn () => DB::findBy('note', ['reply_to' => $this->getId()], order_by: ['created' => 'DESC', 'id' => 'DESC'])); return DB::findBy('note', ['reply_to' => $this->getId()], order_by: ['created' => 'DESC', 'id' => 'DESC']);
} }
/** /**