TreeNotes.php 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. <?php
  2. declare(strict_types = 1);
  3. // {{{ License
  4. // This file is part of GNU social - https://www.gnu.org/software/social
  5. //
  6. // GNU social is free software: you can redistribute it and/or modify
  7. // it under the terms of the GNU Affero General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // GNU social is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Affero General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Affero General Public License
  17. // along with GNU social. If not, see <http://www.gnu.org/licenses/>.
  18. // }}}
  19. namespace Plugin\TreeNotes;
  20. use App\Core\Modules\Plugin;
  21. use App\Entity\Note;
  22. use Symfony\Component\HttpFoundation\Request;
  23. class TreeNotes extends Plugin
  24. {
  25. /**
  26. * Formatting notes without taking a direct reply out of context
  27. * Show whole conversation in conversation related routes.
  28. */
  29. public function onFormatNoteList(array $notes_in, array &$notes_out, Request $request)
  30. {
  31. if (str_starts_with($request->get('_route'), 'conversation')) {
  32. $parents = $this->conversationFormat($notes_in);
  33. $notes_out = $this->conversationFormatTree($parents, $notes_in);
  34. } else {
  35. $notes_out = $this->feedFormatTree($notes_in);
  36. }
  37. }
  38. /**
  39. * Formats general Feed view, allowing users to see a Note and its direct replies.
  40. * These replies are then, shown independently of parent note, making sure that every single Note is shown at least once to users.
  41. *
  42. * The list is transversed in reverse to prevent any parent Note from being processed twice. At the same time, this allows all direct replies to be rendered inside the same, respective, parent Note.
  43. * Moreover, this implies the Entity\Note::getReplies() query will only be performed once, for every Note.
  44. *
  45. * @param array $notes The Note list to be formatted, each element has two keys: 'note' (parent/current note), and 'replies' (array of notes in the same format)
  46. */
  47. private function feedFormatTree(array $notes): array
  48. {
  49. $tree = [];
  50. $notes = array_reverse($notes);
  51. foreach ($notes as $note) {
  52. if (!\is_null($children = $note->getReplies())) {
  53. $notes = array_filter($notes, fn (Note $n) => !\in_array($n, $children));
  54. $tree[] = [
  55. 'note' => $note,
  56. 'replies' => array_map(
  57. fn ($n) => ['note' => $n, 'replies' => []],
  58. $children,
  59. ),
  60. ];
  61. } else {
  62. $tree[] = ['note' => $note, 'replies' => []];
  63. }
  64. }
  65. return array_reverse($tree);
  66. }
  67. /**
  68. * Filters given Note list off any children, returning only initial Notes of a Conversation.
  69. *
  70. * @param array $notes_in Notes to be filtered
  71. *
  72. * @return array All initial Conversation Notes in given list
  73. */
  74. private function conversationFormat(array $notes_in): array
  75. {
  76. return array_filter($notes_in, static fn (Note $note) => \is_null($note->getReplyTo()));
  77. }
  78. private function conversationFormatTree(array $parents, array $notes): array
  79. {
  80. $subtree = [];
  81. foreach ($parents as $p) {
  82. $subtree[] = $this->conversationFormatSubTree($p, $notes);
  83. }
  84. return $subtree;
  85. }
  86. private function conversationFormatSubTree(Note $parent, array $notes)
  87. {
  88. $children = array_filter($notes, fn (Note $note) => $note->getReplyTo() === $parent->getId());
  89. return ['note' => $parent, 'replies' => $this->conversationFormatTree($children, $notes)];
  90. }
  91. }