fix(comments): add comment view partials for public pages

This commit is contained in:
Yassine Doghri 2021-08-16 15:45:36 +00:00
parent 0c187ef7a9
commit fcecbe1c68
24 changed files with 338 additions and 80 deletions

View File

@ -426,6 +426,14 @@ $routes->group(
'filter' => 'permission:podcast-manage_publications',
]
);
$routes->post(
'(:uuid)/reply',
'EpisodeController::attemptCommentReply/$1/$2/$3',
[
'as' => 'comment-attempt-reply',
'filter' => 'permission:podcast-manage_publications',
]
);
$routes->post(
'delete',
'EpisodeController::attemptCommentDelete/$1/$2',

View File

@ -821,4 +821,45 @@ class EpisodeController extends BaseController
// Comment has been successfully created
return redirect()->back();
}
public function attemptCommentReply(string $commentId): RedirectResponse
{
// var_dump($commentId);
// die();
$rules = [
'message' => 'required|max_length[500]',
];
if (! $this->validate($rules)) {
return redirect()
->back()
->withInput()
->with('errors', $this->validator->getErrors());
}
$message = $this->request->getPost('message');
$newReply = new EpisodeComment([
'actor_id' => interact_as_actor_id(),
'episode_id' => $this->episode->id,
'message' => $message,
'in_reply_to_id' => $commentId,
'created_at' => new Time('now'),
'created_by' => user_id(),
]);
$commentModel = new EpisodeCommentModel();
if (
! $commentModel->addComment($newReply, true)
) {
return redirect()
->back()
->withInput()
->with('errors', $commentModel->errors());
}
// Reply has been successfully created
return redirect()->back();
}
}

View File

@ -170,4 +170,12 @@ class EpisodeCommentController extends BaseController
return redirect()->back();
}
public function attemptReply(): RedirectResponse
{
model('LikeModel')
->toggleLike(interact_as_actor(), $this->comment);
return redirect()->back();
}
}

View File

@ -118,6 +118,20 @@ class EpisodeComment extends UuidEntity
return $this->replies;
}
public function getReplyToComment(): ?self
{
if ($this->in_reply_to_id === null) {
throw new RuntimeException('Comment is not a reply.');
}
if ($this->reply_to_comment === null) {
$this->reply_to_comment = model('EpisodeCommentModel', false)
->getCommentById($this->in_reply_to_id);
}
return $this->reply_to_comment;
}
public function setMessage(string $message): static
{
helper('activitypub');

View File

@ -9,12 +9,22 @@ declare(strict_types=1);
*/
return [
'title' => "{actorDisplayName}'s comment for {episodeTitle}",
'back_to_episode' => 'Back to {episodeTitle}',
'form' => [
'episode_message_placeholder' => 'Write a comment...',
'reply_to_placeholder' => 'Reply to @{actorUsername}',
'submit' => 'Send!',
'submit_reply' => 'Reply',
],
'likes' => '{numberOfLikes, plural,
one {# like}
other {# likes}
}',
'replies' => '{numberOfReplies, plural,
one {# reply}
other {# replies}
}',
'like' => 'Like',
'reply' => 'Reply',
'view_replies' => 'View replies ({numberOfReplies})',

View File

@ -80,9 +80,15 @@ class EpisodeCommentModel extends UuidModel
return false;
}
(new EpisodeModel())
->where('id', $comment->episode_id)
->increment('comments_count');
if ($comment->in_reply_to_id === null) {
(new EpisodeModel())
->where('id', $comment->episode_id)
->increment('comments_count');
} else {
(new self())
->where('id', service('uuid')->fromString($comment->in_reply_to_id)->getBytes())
->increment('replies_count');
}
if ($registerActivity) {
// set post id and uri to construct NoteObject
@ -129,7 +135,10 @@ class EpisodeCommentModel extends UuidModel
{
// TODO: merge with replies from posts linked to episode linked
$episodeComments = $this->select('*, 0 as is_from_post')
->where('episode_id', $episodeId)
->where([
'episode_id' => $episodeId,
'in_reply_to_id' => null,
])
->getCompiledSelect();
$episodePostsReplies = $this->db->table('activitypub_posts')

View File

@ -20,7 +20,7 @@
<?php if ($comment->is_from_post): ?>
<?= $this->include('podcast/_partials/comment_actions_from_post') ?>
<?php else: ?>
<?= $this->include('podcast/_partials/comment_actions') ?>
<?= $this->include('podcast/_partials/comment_actions') ?>
<?php endif; ?>
</div>
</article>

View File

@ -1,19 +1,10 @@
<footer>
<form action="<?= route_to('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" value="favourite" class="inline-flex items-center hover:underline group" title="<?= lang(
'Comment.like',
[
'numberOfLikes' => $comment->likes_count,
],
) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-400 group-hover:text-red-600') . $comment->likes_count ?></button>
<?= button(
lang('Comment.reply'),
route_to('comment', $podcast->handle, $episode->slug, $comment->id),
[
'size' => 'small',
],
) ?>
</form>
<button class="inline-flex items-center opacity-50 cursor-not-allowed hover:underline" title="<?= lang(
'Comment.like',
[
'numberOfLikes' => $comment->likes_count,
],
) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-500') . $comment->likes_count ?></button>
<?php if($comment->replies_count): ?>
<?= anchor(
route_to('comment', $podcast->handle, $episode->slug, $comment->id),

View File

@ -0,0 +1,24 @@
<footer>
<form action="<?= route_to('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 text-gray-400 group-hover:text-red-600') . $comment->likes_count ?></button>
<?= button(
lang('Comment.reply'),
route_to('comment', $podcast->handle, $episode->slug, $comment->id),
[
'size' => 'small',
],
) ?>
</form>
<?php if($comment->replies_count): ?>
<?= anchor(
route_to('comment', $podcast->handle, $episode->slug, $comment->id),
icon('caret-down', 'text-xl mr-1') . lang('Comment.view_replies', ['numberOfReplies' => $comment->replies_count]),
['class' => 'inline-flex items-center text-xs hover:underline']
) ?>
<?php endif; ?>
</footer>

View File

@ -1,19 +1,10 @@
<footer>
<form action="<?= route_to('post-attempt-action', interact_as_actor()->username, $comment->id) ?>" method="POST" class="flex items-center gap-x-4">
<button type="submit" name="action" value="favourite" class="inline-flex items-center hover:underline group" title="<?= lang(
'Comment.like',
[
'numberOfLikes' => $comment->likes_count,
],
) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-400 group-hover:text-red-600') . $comment->likes_count ?></button>
<?= button(
lang('Comment.reply'),
route_to('post', $podcast->handle, $comment->id),
[
'size' => 'small',
],
) ?>
</form>
<button class="inline-flex items-center opacity-50 cursor-not-allowed hover:underline" title="<?= lang(
'Comment.like',
[
'numberOfLikes' => $comment->likes_count,
],
) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-500') . $comment->likes_count ?></button>
<?php if($comment->replies_count): ?>
<?= anchor(
route_to('post', $podcast->handle, $comment->id),

View File

@ -0,0 +1,24 @@
<footer>
<form action="<?= route_to('post-attempt-action', interact_as_actor()->username, $comment->id) ?>" method="POST" class="flex items-center gap-x-4">
<button type="submit" name="action" value="favourite" class="inline-flex items-center hover:underline group" title="<?= lang(
'Comment.likes',
[
'numberOfLikes' => $comment->likes_count,
],
) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-400 group-hover:text-red-600') . $comment->likes_count ?></button>
<?= button(
lang('Comment.reply'),
route_to('post', $podcast->handle, $comment->id),
[
'size' => 'small',
],
) ?>
</form>
<?php if($comment->replies_count): ?>
<?= anchor(
route_to('post', $podcast->handle, $comment->id),
icon('caret-down', 'text-xl mr-1') . lang('Comment.view_replies', ['numberOfReplies' => $comment->replies_count]),
['class' => 'inline-flex items-center text-xs hover:underline']
) ?>
<?php endif; ?>
</footer>

View File

@ -1,13 +1,11 @@
<article class="relative z-10 w-full bg-white shadow-md rounded-2xl">
<header class="flex px-6 py-4">
<img src="<?= $comment->actor
->avatar_image_url ?>" alt="<?= $comment->actor->display_name ?>" class="w-12 h-12 mr-4 rounded-full" />
<div class="flex flex-col min-w-0">
<article class="relative z-10 flex w-full px-4 py-2 rounded-2xl">
<img src="<?= $comment->actor->avatar_image_url ?>" alt="<?= $comment->display_name ?>" class="w-12 h-12 mr-4 rounded-full" />
<div class="flex-1">
<header class="w-full mb-2 text-sm">
<a href="<?= $comment->actor
->uri ?>" class="flex items-baseline hover:underline" <?= $comment
->actor->is_local
? ''
: 'target="_blank" rel="noopener noreferrer"' ?>>
->uri ?>" class="flex items-baseline hover:underline" <?= $comment->actor->is_local
? ''
: 'target="_blank" rel="noopener noreferrer"' ?>>
<span class="mr-2 font-semibold truncate"><?= $comment->actor
->display_name ?></span>
<span class="text-sm text-gray-500 truncate">@<?= $comment->actor
@ -15,13 +13,14 @@
($comment->actor->is_local
? ''
: '@' . $comment->actor->domain) ?></span>
<?= relative_time($comment->created_at, 'text-xs text-gray-500 ml-auto') ?>
</a>
<a href="<?= route_to('comment', $podcast->handle, $episode->slug, $comment->id) ?>"
class="text-xs text-gray-500">
<?= relative_time($comment->created_at) ?>
</a>
</div>
</header>
<div class="px-6 mb-4 post-content"><?= $comment->message_html ?></div>
<?= $this->include('podcast/_partials/comment_actions') ?>
</header>
<div class="mb-2 post-content"><?= $comment->message_html ?></div>
<?php if ($comment->is_from_post): ?>
<?= $this->include('podcast/_partials/comment_actions_from_post_authenticated') ?>
<?php else: ?>
<?= $this->include('podcast/_partials/comment_actions_authenticated') ?>
<?php endif; ?>
</div>
</article>

View File

@ -0,0 +1,38 @@
<article class="relative z-10 flex w-full p-4 bg-white shadow rounded-2xl">
<img src="<?= $comment->actor->avatar_image_url ?>" alt="<?= $comment->display_name ?>" class="w-12 h-12 mr-4 rounded-full" />
<div class="flex-1">
<header class="w-full mb-2 text-sm">
<a href="<?= $comment->actor
->uri ?>" class="flex items-baseline hover:underline" <?= $comment->actor->is_local
? ''
: 'target="_blank" rel="noopener noreferrer"' ?>>
<span class="mr-2 font-semibold truncate"><?= $comment->actor
->display_name ?></span>
<span class="text-sm text-gray-500 truncate">@<?= $comment->actor
->username .
($comment->actor->is_local
? ''
: '@' . $comment->actor->domain) ?></span>
<?= relative_time($comment->created_at, 'text-xs text-gray-500 ml-auto') ?>
</a>
</header>
<div class="mb-2 post-content"><?= $comment->message_html ?></div>
<?php if ($comment->is_from_post): ?>
<?= $this->include('podcast/_partials/comment_actions_from_post') ?>
<?php else: ?>
<footer>
<button class="inline-flex items-center opacity-50 cursor-not-allowed" title="<?= lang(
'Comment.likes',
[
'numberOfLikes' => $comment->likes_count,
],
) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-500') . lang(
'Comment.likes',
[
'numberOfLikes' => $comment->likes_count,
],
) ?></button>
</footer>
<?php endif; ?>
</div>
</article>

View File

@ -0,0 +1,40 @@
<article class="relative z-10 flex w-full p-4 bg-white shadow rounded-2xl">
<img src="<?= $comment->actor->avatar_image_url ?>" alt="<?= $comment->display_name ?>" class="w-12 h-12 mr-4 rounded-full" />
<div class="flex-1">
<header class="w-full mb-2 text-sm">
<a href="<?= $comment->actor
->uri ?>" class="flex items-baseline hover:underline" <?= $comment->actor->is_local
? ''
: 'target="_blank" rel="noopener noreferrer"' ?>>
<span class="mr-2 font-semibold truncate"><?= $comment->actor
->display_name ?></span>
<span class="text-sm text-gray-500 truncate">@<?= $comment->actor
->username .
($comment->actor->is_local
? ''
: '@' . $comment->actor->domain) ?></span>
<?= relative_time($comment->created_at, 'text-xs text-gray-500 ml-auto') ?>
</a>
</header>
<div class="mb-2 post-content"><?= $comment->message_html ?></div>
<?php if ($comment->is_from_post): ?>
<?= $this->include('podcast/_partials/comment_actions_from_post_authenticated') ?>
<?php else: ?>
<footer>
<form action="<?= route_to('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 text-gray-400 group-hover:text-red-600') . lang(
'Comment.likes',
[
'numberOfLikes' => $comment->likes_count,
],
) ?></button>
</form>
</footer>
<?php endif; ?>
</div>
</article>

View File

@ -0,0 +1,19 @@
<article class="flex px-6 py-4 bg-gray-50">
<img src="<?= $reply->actor->avatar_image_url ?>" alt="<?= $reply->actor
->display_name ?>" class="w-12 h-12 mr-4 rounded-full ring-gray-50 ring-2" />
<div class="flex flex-col flex-1 min-w-0">
<header class="flex items-center mb-2">
<a href="<?= $reply->actor
->uri ?>" class="mr-2 text-base font-semibold truncate hover:underline" <?= $reply
->actor->is_local
? ''
: 'target="_blank" rel="noopener noreferrer"' ?>><?= $reply->actor
->display_name ?><span class="ml-1 text-sm font-normal text-gray-600">@<?= $reply
->actor->username .
($reply->actor->is_local ? '' : '@' . $reply->actor->domain) ?></span></a>
<?= relative_time($reply->created_at, 'flex-shrink-0 ml-auto text-xs text-gray-600') ?>
</header>
<p class="mb-2 post-content"><?= $reply->message_html ?></p>
<?= $this->include('podcast/_partials/comment_reply_actions') ?>
</div>
</article>

View File

@ -0,0 +1,20 @@
<footer class="flex items-center gap-x-4">
<button type="submit" name="action" class="inline-flex items-center opacity-50 cursor-not-allowed" disabled="disabled" title="<?= lang(
'Comment.likes',
[
'numberOfLikes' => $reply->likes_count,
],
) ?>"><?= icon('heart', 'text-xl mr-1 text-gray-500') . $reply->likes_count ?></button>
<?php if($reply->replies_count): ?>
<?= anchor(
route_to('comment', $podcast->handle, $episode->slug, $reply->id),
icon('chat', 'text-2xl mr-1 text-gray-400') . $reply->replies_count,
[
'class' => 'inline-flex items-center hover:underline',
'title' => lang('Comment.replies', [
'numberOfReplies' => $reply->replies_count,
]),
],
) ?>
<?php endif; ?>
</footer>

View File

@ -0,0 +1,17 @@
<footer>
<form action="<?= route_to('comment-attempt-like', interact_as_actor()->username, $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-xl mr-1 text-gray-400 group-hover:text-red-600') . $reply->likes_count ?></button>
<?= button(
lang('Comment.reply'),
route_to('comment', $podcast->handle, $episode->slug, $reply->id),
[
'size' => 'small',
],
) ?>
</form>
</footer>

View File

@ -0,0 +1,19 @@
<article class="flex px-6 py-4 bg-gray-50">
<img src="<?= $reply->actor->avatar_image_url ?>" alt="<?= $reply->actor
->display_name ?>" class="w-12 h-12 mr-4 rounded-full ring-gray-50 ring-2" />
<div class="flex flex-col flex-1 min-w-0">
<header class="flex items-center mb-2">
<a href="<?= $reply->actor
->uri ?>" class="mr-2 text-base font-semibold truncate hover:underline" <?= $reply
->actor->is_local
? ''
: 'target="_blank" rel="noopener noreferrer"' ?>><?= $reply->actor
->display_name ?><span class="ml-1 text-sm font-normal text-gray-600">@<?= $reply
->actor->username .
($reply->actor->is_local ? '' : '@' . $reply->actor->domain) ?></span></a>
<?= relative_time($reply->created_at, 'flex-shrink-0 ml-auto text-xs text-gray-600') ?>
</header>
<p class="mb-2 post-content"><?= $reply->message_html ?></p>
<?= $this->include('podcast/_partials/comment_reply_actions_authenticated') ?>
</div>
</article>

View File

@ -1,22 +1,8 @@
<?= $this->include('podcast/_partials/comment') ?>
<div class="-mt-2 overflow-hidden border-b border-l border-r comment-replies rounded-b-xl">
<div class="px-6 pt-8 pb-4 bg-gray-50">
<?= anchor_popup(
route_to('comment-remote-action', $podcast->handle, $comment->id, 'reply'),
lang('comment.reply_to', ['actorUsername' => $comment->actor->username]),
[
'class' =>
'text-center justify-center font-semibold rounded-full shadow relative z-10 px-4 py-2 w-full bg-rose-600 text-white inline-flex items-center hover:bg-rose-700',
'width' => 420,
'height' => 620,
],
) ?>
</div>
<?= $this->include('podcast/_partials/comment_card') ?>
<div class="-mt-2 overflow-hidden border-b border-l border-r post-replies rounded-b-xl">
<?php foreach ($comment->replies as $reply): ?>
<?= view('podcast/_partials/comment', ['comment' => $reply]) ?>
<?= view('podcast/_partials/comment_reply', ['reply' => $reply]) ?>
<?php endforeach; ?>
</div>

View File

@ -1,7 +1,7 @@
<?= $this->include('podcast/_partials/comment_authenticated') ?>
<?= $this->include('podcast/_partials/comment_card_authenticated') ?>
<div class="-mt-2 overflow-hidden border-b border-l border-r post-replies rounded-b-xl">
<?= form_open(
route_to('comment-attempt-action', interact_as_actor()->username, $episode->slug, $comment->id),
route_to('comment-attempt-reply', $podcast->id, $episode->id, $comment->id),
[
'class' => 'bg-gray-50 flex px-6 pt-8 pb-4',
],
@ -40,8 +40,8 @@
<?= form_close() ?>
<?php foreach ($comment->replies as $reply): ?>
<?= view('podcast/_partials/comment_authenticated', [
'comment' => $reply,
<?= view('podcast/_partials/comment_reply_authenticated', [
'reply' => $reply,
]) ?>
<?php endforeach; ?>
</div>

View File

@ -1,4 +1,4 @@
<article class="relative z-10 w-full bg-white shadow-md rounded-2xl">
<article class="relative z-10 w-full bg-white shadow rounded-2xl">
<header class="flex px-6 py-4">
<img src="<?= $post->actor
->avatar_image_url ?>" alt="<?= $post->actor->display_name ?>" class="w-12 h-12 mr-4 rounded-full" />

View File

@ -26,7 +26,7 @@
'mr-2 text-lg',
) .
lang('Comment.back_to_episode', [
'actor' => $comment->actor->display_name,
'episodeTitle' => $episode->title,
]) ?></a>
</nav>
<div class="pb-12">

View File

@ -3,11 +3,12 @@
<?= $this->section('meta-tags') ?>
<title><?= lang('Comment.title', [
'actorDisplayName' => $comment->actor->display_name,
'episodeTitle' => $episode->title
]) ?></title>
<meta name="description" content="<?= $comment->message ?>"/>
<meta property="og:title" content="<?= lang('Comment.title', [
'actorDisplayName' => $comment->actor->display_name,
]) ?>"/>
'episodeTitle' => $episode->title ]) ?>"/>
<meta property="og:locale" content="<?= service(
'request',
)->getLocale() ?>" />
@ -26,7 +27,7 @@
'mr-2 text-lg',
) .
lang('Comment.back_to_episode', [
'actor' => $comment->actor->display_name,
'episodeTitle' => $episode->title,
]) ?></a>
</nav>
<div class="pb-12">
@ -36,5 +37,4 @@
</div>
</div>
<?= $this->endSection()
?>
<?= $this->endSection() ?>

View File

@ -119,7 +119,7 @@
</div>
<?= form_close() ?>
<?php foreach ($episode->comments as $comment): ?>
<?= view('podcast/_partials/comment', ['comment' => $comment]) ?>
<?= view('podcast/_partials/comment_authenticated', ['comment' => $comment]) ?>
<?php endforeach; ?>
</section>
<section id="activity" class="space-y-8 tab-panel">