From 9303e51bc50d730a8026f58984e83b840360ee88 Mon Sep 17 00:00:00 2001 From: Yassine Doghri <yassine@doghri.fr> Date: Fri, 14 Jan 2022 17:42:55 +0000 Subject: [PATCH] feat: add task to housekeeping setting for resetting all instance counts set two toggle switches to run housekeeping tasks seperately if needed --- app/Controllers/PostController.php | 13 +- app/Models/EpisodeCommentModel.php | 36 ++++- app/Models/EpisodeModel.php | 52 ++++++ app/Models/PostModel.php | 25 +++ .../Admin/Controllers/SettingsController.php | 151 ++++++++++-------- modules/Admin/Language/en/Settings.php | 6 +- modules/Admin/Language/fr/Settings.php | 4 + .../Fediverse/Controllers/PostController.php | 5 +- modules/Fediverse/Models/ActorModel.php | 41 +++++ modules/Fediverse/Models/PostModel.php | 67 +++++++- themes/cp_admin/settings/general.php | 3 + .../episode/_partials/comment_actions.php | 4 +- .../_partials/comment_actions_from_post.php | 16 +- .../cp_app/episode/_partials/comment_card.php | 4 +- .../_partials/comment_reply_actions.php | 4 +- themes/cp_app/post/_partials/card.php | 2 +- 16 files changed, 348 insertions(+), 85 deletions(-) diff --git a/app/Controllers/PostController.php b/app/Controllers/PostController.php index ee5320418e..430a1ca061 100644 --- a/app/Controllers/PostController.php +++ b/app/Controllers/PostController.php @@ -22,7 +22,6 @@ use CodeIgniter\HTTP\URI; use CodeIgniter\I18n\Time; use Modules\Analytics\AnalyticsTrait; use Modules\Fediverse\Controllers\PostController as FediversePostController; -use Modules\Fediverse\Entities\Post as FediversePost; use Modules\Fediverse\Models\FavouriteModel; class PostController extends FediversePostController @@ -33,6 +32,11 @@ class PostController extends FediversePostController protected Actor $actor; + /** + * @var CastopodPost + */ + protected $post; + /** * @var string[] */ @@ -53,6 +57,7 @@ class PostController extends FediversePostController count($params) > 1 && ($post = (new PostModel())->getPostById($params[1])) !== null ) { + /** @var CastopodPost $post */ $this->post = $post; unset($params[0]); @@ -163,7 +168,7 @@ class PostController extends FediversePostController ->with('errors', $this->validator->getErrors()); } - $newPost = new FediversePost([ + $newPost = new CastopodPost([ 'actor_id' => interact_as_actor_id(), 'in_reply_to_id' => $this->post->id, 'message' => $this->request->getPost('message'), @@ -171,6 +176,10 @@ class PostController extends FediversePostController 'created_by' => user_id(), ]); + if ($this->post->in_reply_to_id === null && $this->post->episode_id !== null) { + $newPost->episode_id = $this->post->episode_id; + } + $postModel = new PostModel(); if (! $postModel->addReply($newPost)) { return redirect() diff --git a/app/Models/EpisodeCommentModel.php b/app/Models/EpisodeCommentModel.php index 7102e5e456..8310f02c3d 100644 --- a/app/Models/EpisodeCommentModel.php +++ b/app/Models/EpisodeCommentModel.php @@ -149,7 +149,10 @@ class EpisodeCommentModel extends UuidModel ->whereIn('in_reply_to_id', function (BaseBuilder $builder) use (&$episodeId): BaseBuilder { return $builder->select('id') ->from(config('Fediverse')->tablesPrefix . 'posts') - ->where('episode_id', $episodeId); + ->where([ + 'episode_id' => $episodeId, + 'in_reply_to_id' => null, + ]); }) ->where('`created_at` <= NOW()', null, false) ->getCompiledSelect(); @@ -179,6 +182,37 @@ class EpisodeCommentModel extends UuidModel ->findAll(); } + public function resetLikesCount(): int | false + { + $commentsLikesCount = $this->db->table('likes') + ->select('comment_id as id, COUNT(*) as `likes_count`') + ->groupBy('id') + ->get() + ->getResultArray(); + + if ($commentsLikesCount !== []) { + $this->uuidUseBytes = false; + return $this->updateBatch($commentsLikesCount, 'id'); + } + return 0; + } + + public function resetRepliesCount(): int | false + { + $commentsRepliesCount = $this->select('episode_comments.id, COUNT(*) as `replies_count`') + ->join('episode_comments as c2', 'episode_comments.id = c2.in_reply_to_id') + ->groupBy('episode_comments.id') + ->get() + ->getResultArray(); + + if ($commentsRepliesCount !== []) { + $this->uuidUseBytes = false; + return $this->updateBatch($commentsRepliesCount, 'id'); + } + + return 0; + } + /** * @param array<string, array<string|int, mixed>> $data * @return array<string, array<string|int, mixed>> diff --git a/app/Models/EpisodeModel.php b/app/Models/EpisodeModel.php index 0e4fa9cf39..10277643b6 100644 --- a/app/Models/EpisodeModel.php +++ b/app/Models/EpisodeModel.php @@ -322,6 +322,58 @@ class EpisodeModel extends Model return $stats; } + public function resetCommentsCount(): int | false + { + $episodeCommentsCount = $this->select('episodes.id, COUNT(*) as `comments_count`') + ->join('episode_comments', 'episodes.id = episode_comments.episode_id') + ->where('in_reply_to_id', null) + ->groupBy('episodes.id') + ->getCompiledSelect(); + + $episodePostsRepliesCount = $this + ->select('episodes.id, COUNT(*) as `comments_count`') + ->join( + config('Fediverse') + ->tablesPrefix . 'posts', + 'episodes.id = ' . config('Fediverse')->tablesPrefix . 'posts.episode_id' + ) + ->where('in_reply_to_id IS NOT', null) + ->groupBy('episodes.id') + ->getCompiledSelect(); + + $query = $this->db->query( + 'SELECT `id`, SUM(`comments_count`) as `comments_count` FROM (' . $episodeCommentsCount . ' UNION ALL ' . $episodePostsRepliesCount . ') x GROUP BY `id`' + ); + + $countsPerEpisodeId = $query->getResultArray(); + + if ($countsPerEpisodeId !== []) { + return $this->updateBatch($countsPerEpisodeId, 'id'); + } + + return 0; + } + + public function resetPostsCount(): int | false + { + $episodePostsCount = $this->select('episodes.id, COUNT(*) as `posts_count`') + ->join( + config('Fediverse') + ->tablesPrefix . 'posts', + 'episodes.id = ' . config('Fediverse')->tablesPrefix . 'posts.episode_id' + ) + ->where('in_reply_to_id', null) + ->groupBy('episodes.id') + ->get() + ->getResultArray(); + + if ($episodePostsCount !== []) { + return $this->updateBatch($episodePostsCount, 'id'); + } + + return 0; + } + /** * @param mixed[] $data * diff --git a/app/Models/PostModel.php b/app/Models/PostModel.php index 9d6cd911a1..12d0e82600 100644 --- a/app/Models/PostModel.php +++ b/app/Models/PostModel.php @@ -49,8 +49,33 @@ class PostModel extends FediversePostModel return $this->where([ 'episode_id' => $episodeId, ]) + ->where('in_reply_to_id', null) ->where('`published_at` <= NOW()', null, false) ->orderBy('published_at', 'DESC') ->findAll(); } + + public function setEpisodeIdForRepliesOfEpisodePosts(): int | false + { + // make sure that posts in reply to episode activities have an episode id + $postsToUpdate = $this->db->table(config('Fediverse')->tablesPrefix . 'posts as p1') + ->join(config('Fediverse')->tablesPrefix . 'posts as p2', 'p1.id = p2.in_reply_to_id') + ->select('p2.id, p1.episode_id') + ->where([ + 'p1.in_reply_to_id' => null, + 'p2.in_reply_to_id IS NOT' => null, + 'p2.episode_id' => null, + 'p1.episode_id IS NOT' => null, + ]) + ->get() + ->getResultArray(); + + if ($postsToUpdate !== []) { + $postModel = new self(); + $postModel->uuidUseBytes = false; + return $postModel->updateBatch($postsToUpdate, 'id'); + } + + return 0; + } } diff --git a/modules/Admin/Controllers/SettingsController.php b/modules/Admin/Controllers/SettingsController.php index 9ec90e7c06..7054b0e0d1 100644 --- a/modules/Admin/Controllers/SettingsController.php +++ b/modules/Admin/Controllers/SettingsController.php @@ -10,11 +10,13 @@ declare(strict_types=1); namespace Modules\Admin\Controllers; -use App\Entities\Media\Image; use App\Models\ActorModel; +use App\Models\EpisodeCommentModel; +use App\Models\EpisodeModel; use App\Models\MediaModel; use App\Models\PersonModel; use App\Models\PodcastModel; +use App\Models\PostModel; use CodeIgniter\Files\File; use CodeIgniter\HTTP\RedirectResponse; use PHP_ICO; @@ -158,93 +160,112 @@ class SettingsController extends BaseController public function runHousekeeping(): RedirectResponse { + if ($this->request->getPost('reset_counts') === 'yes') { + // recalculate fediverse counts + (new ActorModel())->resetFollowersCount(); + (new ActorModel())->resetPostsCount(); + (new PostModel())->setEpisodeIdForRepliesOfEpisodePosts(); + (new PostModel())->resetFavouritesCount(); + (new PostModel())->resetReblogsCount(); + (new PostModel())->resetRepliesCount(); + (new EpisodeModel())->resetCommentsCount(); + (new EpisodeModel())->resetPostsCount(); + (new EpisodeCommentModel())->resetLikesCount(); + (new EpisodeCommentModel())->resetRepliesCount(); + } helper('media'); - // Delete all podcast image sizes to recreate them - $allPodcasts = (new PodcastModel())->findAll(); - foreach ($allPodcasts as $podcast) { - $podcastImages = glob(media_path("/podcasts/{$podcast->handle}/*_*{jpg,png,webp}"), GLOB_BRACE); + if ($this->request->getPost('rewrite_media') === 'yes') { - if ($podcastImages) { - foreach ($podcastImages as $podcastImage) { - if (is_file($podcastImage)) { - unlink($podcastImage); + // Delete all podcast image sizes to recreate them + $allPodcasts = (new PodcastModel())->findAll(); + foreach ($allPodcasts as $podcast) { + $podcastImages = glob(media_path("/podcasts/{$podcast->handle}/*_*{jpg,png,webp}"), GLOB_BRACE); + + if ($podcastImages) { + foreach ($podcastImages as $podcastImage) { + if (is_file($podcastImage)) { + unlink($podcastImage); + } } } } - } - // Delete all person image sizes to recreate them - $personsImages = glob(media_path('/persons/*_*{jpg,png,webp}'), GLOB_BRACE); - if ($personsImages) { - foreach ($personsImages as $personsImage) { - if (is_file($personsImage)) { - unlink($personsImage); + // Delete all person image sizes to recreate them + $personsImages = glob(media_path('/persons/*_*{jpg,png,webp}'), GLOB_BRACE); + if ($personsImages) { + foreach ($personsImages as $personsImage) { + if (is_file($personsImage)) { + unlink($personsImage); + } } } - } - $allImages = (new MediaModel('image'))->getAllOfType(); - foreach ($allImages as $image) { - if (str_starts_with($image->file_path, 'podcasts')) { - if (str_ends_with($image->file_path, 'banner.jpg') || str_ends_with($image->file_path, 'banner.png')) { - $image->sizes = config('Images') - ->podcastBannerSizes; - } else { + $allImages = (new MediaModel('image'))->getAllOfType(); + foreach ($allImages as $image) { + if (str_starts_with($image->file_path, 'podcasts')) { + if (str_ends_with($image->file_path, 'banner.jpg') || str_ends_with( + $image->file_path, + 'banner.png' + )) { + $image->sizes = config('Images') + ->podcastBannerSizes; + } else { + $image->sizes = config('Images') + ->podcastCoverSizes; + } + } elseif (str_starts_with($image->file_path, 'persons')) { $image->sizes = config('Images') - ->podcastCoverSizes; + ->personAvatarSizes; } - } elseif (str_starts_with($image->file_path, 'persons')) { - $image->sizes = config('Images') - ->personAvatarSizes; + $image->setFile(new File(media_path($image->file_path))); + + (new MediaModel('image'))->updateMedia($image); } - $image->setFile(new File(media_path($image->file_path))); - (new MediaModel('image'))->updateMedia($image); - } + $allAudio = (new MediaModel('audio'))->getAllOfType(); + foreach ($allAudio as $audio) { + $audio->setFile(new File(media_path($audio->file_path))); + + (new MediaModel('audio'))->updateMedia($audio); + } - $allAudio = (new MediaModel('audio'))->getAllOfType(); - foreach ($allAudio as $audio) { - $audio->setFile(new File(media_path($audio->file_path))); + $allTranscripts = (new MediaModel('transcript'))->getAllOfType(); + foreach ($allTranscripts as $transcript) { + $transcript->setFile(new File(media_path($transcript->file_path))); - (new MediaModel('audio'))->updateMedia($audio); - } + (new MediaModel('transcript'))->updateMedia($transcript); + } - $allTranscripts = (new MediaModel('transcript'))->getAllOfType(); - foreach ($allTranscripts as $transcript) { - $transcript->setFile(new File(media_path($transcript->file_path))); + $allChapters = (new MediaModel('chapters'))->getAllOfType(); + foreach ($allChapters as $chapters) { + $chapters->setFile(new File(media_path($chapters->file_path))); - (new MediaModel('transcript'))->updateMedia($transcript); - } + (new MediaModel('chapters'))->updateMedia($chapters); + } - $allChapters = (new MediaModel('chapters'))->getAllOfType(); - foreach ($allChapters as $chapters) { - $chapters->setFile(new File(media_path($chapters->file_path))); + $allVideos = (new MediaModel('video'))->getAllOfType(); + foreach ($allVideos as $video) { + $video->setFile(new File(media_path($video->file_path))); - (new MediaModel('chapters'))->updateMedia($chapters); - } + (new MediaModel('video'))->updateMedia($video); + } - $allVideos = (new MediaModel('video'))->getAllOfType(); - foreach ($allVideos as $video) { - $video->setFile(new File(media_path($video->file_path))); + // reset avatar and banner image urls for each podcast actor + foreach ($allPodcasts as $podcast) { + $actorModel = new ActorModel(); + $actor = $actorModel->getActorById($podcast->actor_id); - (new MediaModel('video'))->updateMedia($video); - } + if ($actor !== null) { + // update values + $actor->avatar_image_url = $podcast->cover->federation_url; + $actor->avatar_image_mimetype = $podcast->cover->file_mimetype; + $actor->cover_image_url = $podcast->banner->federation_url; + $actor->cover_image_mimetype = $podcast->banner->file_mimetype; - // reset avatar and banner image urls for each podcast actor - foreach ($allPodcasts as $podcast) { - $actorModel = new ActorModel(); - $actor = $actorModel->getActorById($podcast->actor_id); - - if ($actor !== null) { - // update values - $actor->avatar_image_url = $podcast->cover->federation_url; - $actor->avatar_image_mimetype = $podcast->cover->file_mimetype; - $actor->cover_image_url = $podcast->banner->federation_url; - $actor->cover_image_mimetype = $podcast->banner->file_mimetype; - - if ($actor->hasChanged()) { - $actorModel->update($actor->id, $actor); + if ($actor->hasChanged()) { + $actorModel->update($actor->id, $actor); + } } } } diff --git a/modules/Admin/Language/en/Settings.php b/modules/Admin/Language/en/Settings.php index 8d0ece529e..ea79101b72 100644 --- a/modules/Admin/Language/en/Settings.php +++ b/modules/Admin/Language/en/Settings.php @@ -30,7 +30,11 @@ return [ ], 'housekeeping' => [ 'title' => 'Housekeeping', - 'subtitle' => 'Runs various housekeeping tasks, such as rewriting media files metadata (images, audio files, transcripts, chapters, …).', + 'subtitle' => 'Runs various housekeeping tasks, such as resetting counts and rewriting media files metadata. This may take a while.', + 'reset_counts' => 'Reset counts', + 'reset_counts_helper' => 'This option will recalculate and reset all data counts (number of followers, posts, comments, …).', + 'rewrite_media' => 'Rewrite media metadata', + 'rewrite_media_helper' => 'This option will delete all superfluous media files and recreate them (images, audio files, transcripts, chapters, …)', 'run' => 'Run housekeeping', 'runSuccess' => 'Housekeeping has been run successfully!', ], diff --git a/modules/Admin/Language/fr/Settings.php b/modules/Admin/Language/fr/Settings.php index 0d0fe8df0f..7c52f2d408 100644 --- a/modules/Admin/Language/fr/Settings.php +++ b/modules/Admin/Language/fr/Settings.php @@ -31,6 +31,10 @@ return [ 'housekeeping' => [ 'title' => 'Ménage', 'subtitle' => 'Exécute un nombre de tâches de nettoyage, comme la réécriture de métadonnées des fichiers media (images, fichiers audio, transcript, chapitres, …).', + 'reset_counts' => 'Réinitialiser les compteurs', + 'reset_counts_helper' => 'Cette option recalcul et réinitialise les compteurs de données (nombre d’abonné·e·s, de publications, de commentaires, …).', + 'rewrite_media' => 'Réécrire les métadonnées des fichiers média', + 'rewrite_media_helper' => 'Cette option supprimera tous les fichiers média superflus et les recréera (images, fichiers audio, transcripts, chapitrages, …)', 'run' => 'Faire le ménage', 'runSuccess' => 'Le ménage a été effectué avec succès !', ], diff --git a/modules/Fediverse/Controllers/PostController.php b/modules/Fediverse/Controllers/PostController.php index c375111c8e..8d47006582 100644 --- a/modules/Fediverse/Controllers/PostController.php +++ b/modules/Fediverse/Controllers/PostController.php @@ -28,7 +28,10 @@ class PostController extends Controller */ protected $helpers = ['fediverse']; - protected Post $post; + /** + * @var Post + */ + protected $post; protected Fediverse $config; diff --git a/modules/Fediverse/Models/ActorModel.php b/modules/Fediverse/Models/ActorModel.php index 628cca0560..2608df67bf 100644 --- a/modules/Fediverse/Models/ActorModel.php +++ b/modules/Fediverse/Models/ActorModel.php @@ -272,6 +272,47 @@ class ActorModel extends BaseModel return $found; } + public function resetFollowersCount(): int | false + { + $tablePrefix = config('Fediverse') + ->tablesPrefix; + + $actorsFollowersCount = $this->db->table($tablePrefix . 'follows')->select( + 'target_actor_id as id, COUNT(*) as `followers_count`' + ) + ->groupBy('id') + ->get() + ->getResultArray(); + + if ($actorsFollowersCount !== []) { + return $this->updateBatch($actorsFollowersCount, 'id'); + } + + return 0; + } + + public function resetPostsCount(): int | false + { + $tablePrefix = config('Fediverse') + ->tablesPrefix; + + $actorsFollowersCount = $this->db->table($tablePrefix . 'posts')->select( + 'actor_id as id, COUNT(*) as `posts_count`' + ) + ->where([ + 'in_reply_to_id' => null, + ]) + ->groupBy('actor_id') + ->get() + ->getResultArray(); + + if ($actorsFollowersCount !== []) { + return $this->updateBatch($actorsFollowersCount, 'id'); + } + + return 0; + } + public function clearCache(Actor $actor): void { $cachePrefix = config('Fediverse') diff --git a/modules/Fediverse/Models/PostModel.php b/modules/Fediverse/Models/PostModel.php index b3f162f8e7..dd1a9f4b99 100644 --- a/modules/Fediverse/Models/PostModel.php +++ b/modules/Fediverse/Models/PostModel.php @@ -276,9 +276,12 @@ class PostModel extends BaseUuidModel } } - model('ActorModel', false) - ->where('id', $post->actor_id) - ->increment('posts_count'); + if ($post->in_reply_to_id === null) { + // increment posts_count only if not reply + model('ActorModel', false) + ->where('id', $post->actor_id) + ->increment('posts_count'); + } if ($registerActivity) { // set post id and uri to construct NoteObject @@ -619,6 +622,64 @@ class PostModel extends BaseUuidModel return $found; } + public function resetFavouritesCount(): int | false + { + $tablePrefix = config('Fediverse') + ->tablesPrefix; + + $postsFavouritesCount = $this->db->table($tablePrefix . 'favourites')->select( + 'post_id as id, COUNT(*) as `favourites_count`' + ) + ->groupBy('id') + ->get() + ->getResultArray(); + + if ($postsFavouritesCount !== []) { + $this->uuidUseBytes = false; + return $this->updateBatch($postsFavouritesCount, 'id'); + } + + return 0; + } + + public function resetReblogsCount(): int | false + { + $tablePrefix = config('Fediverse') + ->tablesPrefix; + + $postsReblogsCount = $this->select($tablePrefix . 'posts.id, COUNT(*) as `replies_count`') + ->join($tablePrefix . 'posts as p2', $tablePrefix . 'posts.id = p2.reblog_of_id') + ->groupBy($tablePrefix . 'posts.id') + ->get() + ->getResultArray(); + + if ($postsReblogsCount !== []) { + $this->uuidUseBytes = false; + return $this->updateBatch($postsReblogsCount, 'id'); + } + + return 0; + } + + public function resetRepliesCount(): int | false + { + $tablePrefix = config('Fediverse') + ->tablesPrefix; + + $postsRepliesCount = $this->select($tablePrefix . 'posts.id, COUNT(*) as `replies_count`') + ->join($tablePrefix . 'posts as p2', $tablePrefix . 'posts.id = p2.in_reply_to_id') + ->groupBy($tablePrefix . 'posts.id') + ->get() + ->getResultArray(); + + if ($postsRepliesCount !== []) { + $this->uuidUseBytes = false; + return $this->updateBatch($postsRepliesCount, 'id'); + } + + return 0; + } + public function clearCache(Post $post): void { $cachePrefix = config('Fediverse') diff --git a/themes/cp_admin/settings/general.php b/themes/cp_admin/settings/general.php index bd806aa240..3394731fed 100644 --- a/themes/cp_admin/settings/general.php +++ b/themes/cp_admin/settings/general.php @@ -77,6 +77,9 @@ title="<?= lang('Settings.housekeeping.title') ?>" subtitle="<?= lang('Settings.housekeeping.subtitle') ?>" > + <Forms.Toggler name="reset_counts" value="yes" size="small" checked="true" hint="<?= lang('Settings.housekeeping.reset_counts_helper') ?>"><?= lang('Settings.housekeeping.reset_counts') ?></Forms.Toggler> + <Forms.Toggler name="rewrite_media" value="yes" size="small" checked="true" hint="<?= lang('Settings.housekeeping.rewrite_media_helper') ?>"><?= lang('Settings.housekeeping.rewrite_media') ?></Forms.Toggler> + <Button variant="primary" type="submit" iconLeft="home-gear"><?= lang('Settings.housekeeping.run') ?></Button> </Forms.Section> diff --git a/themes/cp_app/episode/_partials/comment_actions.php b/themes/cp_app/episode/_partials/comment_actions.php index b0630b74ef..0ca8c8c5a1 100644 --- a/themes/cp_app/episode/_partials/comment_actions.php +++ b/themes/cp_app/episode/_partials/comment_actions.php @@ -1,12 +1,12 @@ <footer> <?php if (can_user_interact()): ?> - <form action="<?= route_to('comment-attempt-like', interact_as_actor()->username, $comment->episode->slug, $comment->id) ?>" method="POST" class="flex items-center gap-x-4"> + <form action="<?= route_to('episode-comment-attempt-like', interact_as_actor()->username, $comment->episode->slug, $comment->id) ?>" method="POST" class="flex items-center gap-x-4"> <button type="submit" name="action" class="inline-flex items-center hover:underline group" title="<?= lang( 'Comment.likes', [ 'numberOfLikes' => $comment->likes_count, ], -) ?>"><?= icon('heart', 'text-xl mr-1 opacity-40 group-hover:text-red-600 group-hover:opacity-100') . $comment->likes_count ?></button> +) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-400 group-hover:text-red-600') . $comment->likes_count ?></button> <Button uri="<?= route_to('episode-comment', $comment->episode->podcast->handle, $comment->episode->slug, $comment->id) ?>" size="small"><?= lang('Comment.reply') ?></Button> </form> <?php if ($comment->replies_count): ?> diff --git a/themes/cp_app/episode/_partials/comment_actions_from_post.php b/themes/cp_app/episode/_partials/comment_actions_from_post.php index f45e112a5d..bba0f41242 100644 --- a/themes/cp_app/episode/_partials/comment_actions_from_post.php +++ b/themes/cp_app/episode/_partials/comment_actions_from_post.php @@ -6,7 +6,7 @@ [ 'numberOfLikes' => $comment->likes_count, ], -) ?>"><?= icon('heart', 'text-xl mr-1 opacity-40 group-hover:text-red-600 group-hover:opacity-100') . $comment->likes_count ?></button> +) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-400 group-hover:text-red-600') . $comment->likes_count ?></button> <Button uri="<?= route_to('post', $podcast->handle, $comment->id) ?>" size="small"><?= lang('Comment.reply') ?></Button> </form> <?php if ($comment->replies_count): ?> @@ -21,12 +21,18 @@ ) ?> <?php endif; ?> <?php else: ?> - <button class="inline-flex items-center opacity-50 cursor-not-allowed hover:underline" title="<?= lang( - 'Comment.like', + <?= anchor_popup( + route_to('post-remote-action', $podcast->handle, $comment->id, 'favourite'), + icon('heart', 'text-xl mr-1 opacity-40') . $comment->likes_count, [ - 'numberOfLikes' => $comment->likes_count, + 'class' => 'inline-flex items-center hover:underline', + 'width' => 420, + 'height' => 620, + 'title' => lang('Post.favourites', [ + 'numberOfFavourites' => $comment->likes_count, + ]), ], -) ?>"><?= icon('heart', 'text-xl mr-1 text-skin-muted') . $comment->likes_count ?></button> +) ?> <?php if ($comment->replies_count): ?> <?= anchor( route_to('post', $podcast->handle, $comment->id), diff --git a/themes/cp_app/episode/_partials/comment_card.php b/themes/cp_app/episode/_partials/comment_card.php index 9cea928d3d..1bfb091c05 100644 --- a/themes/cp_app/episode/_partials/comment_card.php +++ b/themes/cp_app/episode/_partials/comment_card.php @@ -21,13 +21,13 @@ <?php else: ?> <footer> <?php if (can_user_interact()): ?> - <form action="<?= route_to('comment-attempt-like', interact_as_actor()->username, $episode->slug, $comment->id) ?>" method="POST" class="flex items-center gap-x-4"> + <form action="<?= route_to('episode-comment-attempt-like', interact_as_actor()->username, $episode->slug, $comment->id) ?>" method="POST" class="flex items-center gap-x-4"> <button type="submit" name="action" class="inline-flex items-center hover:underline group" title="<?= lang( 'Comment.likes', [ 'numberOfLikes' => $comment->likes_count, ], - ) ?>"><?= icon('heart', 'text-xl mr-1 opacity-40 group-hover:text-red-600 group-hover:opacity-100') . lang( + ) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-400 group-hover:text-red-600') . lang( 'Comment.likes', [ 'numberOfLikes' => $comment->likes_count, diff --git a/themes/cp_app/episode/_partials/comment_reply_actions.php b/themes/cp_app/episode/_partials/comment_reply_actions.php index d878ec7e1f..a727b66619 100644 --- a/themes/cp_app/episode/_partials/comment_reply_actions.php +++ b/themes/cp_app/episode/_partials/comment_reply_actions.php @@ -1,12 +1,12 @@ <footer> <?php if (can_user_interact()): ?> - <form action="<?= route_to('comment-attempt-like', interact_as_actor()->username, $reply->episode->slug, $reply->id) ?>" method="POST" class="flex items-center gap-x-4"> + <form action="<?= route_to('episode-comment-attempt-like', interact_as_actor()->username, $reply->episode->slug, $reply->id) ?>" method="POST" class="flex items-center gap-x-4"> <button type="submit" name="action" class="inline-flex items-center hover:underline group" title="<?= lang( 'Comment.likes', [ 'numberOfLikes' => $reply->likes_count, ], -) ?>"><?= icon('heart', 'text-lg mr-1 opacity-40 group-hover:text-red-600') . $reply->likes_count ?></button> +) ?>"><?= icon('heart', 'text-lg mr-1 text-gray-400 group-hover:text-red-600') . $reply->likes_count ?></button> <Button uri="<?= route_to('episode-comment', $reply->episode->podcast->handle, $reply->episode->slug, $reply->id) ?>" size="small"><?= lang('Comment.reply') ?></Button> </form> <?php else: ?> diff --git a/themes/cp_app/post/_partials/card.php b/themes/cp_app/post/_partials/card.php index 0a6c45d893..efe7a952b1 100644 --- a/themes/cp_app/post/_partials/card.php +++ b/themes/cp_app/post/_partials/card.php @@ -27,7 +27,7 @@ <?php else: ?> <div class="px-6 mb-4 post-content"><?= $post->message_html ?></div> <?php endif; ?> - <?php if ($post->episode_id): ?> + <?php if ($post->episode_id && $post->in_reply_to_id === null): ?> <?= view('episode/_partials/preview_card', [ 'index' => $index, 'episode' => $post->episode, -- GitLab