Like's #16

Merged
pi3c merged 4 commits from feature/likes into dev 2026-05-10 18:25:17 +00:00
3 changed files with 83 additions and 0 deletions
Showing only changes of commit c8e19e3ce5 - Show all commits

View File

@@ -52,6 +52,9 @@
<span class="post-card-meta-item" data-testid="post-date-{{ post.id }}">
{{ post.created_at.strftime('%B %d, %Y') }}
</span>
<span class="post-card-meta-item" data-testid="like-count-{{ post.id }}">
👍 {{ post.like_count }}
</span>
</div>
<div class="post-card-content" data-testid="post-content-preview-{{ post.id }}">

View File

@@ -33,6 +33,13 @@
{% else %}
<span class="badge" data-testid="post-detail-status">{{ _('post.status_draft', current_locale) }}</span>
{% endif %}
<span class="post-card-meta-item" data-testid="post-detail-like-count">
<button id="like-button" class="btn-like" data-testid="like-button"
data-post-slug="{{ post.slug }}"
data-liked="false">
👍 <span id="like-count">{{ post.like_count }}</span>
</button>
</span>
</div>
</header>
@@ -83,3 +90,42 @@
</footer>
</article>
{% endblock %}
{% block extra_js %}
<script data-testid="like-script">
document.addEventListener('DOMContentLoaded', function() {
var likeButton = document.getElementById('like-button');
if (!likeButton) return;
likeButton.addEventListener('click', function() {
var slug = this.getAttribute('data-post-slug');
var countSpan = document.getElementById('like-count');
fetch('/web/posts/' + slug + '/like', {
method: 'POST',
headers: {
'Accept': 'application/json'
}
})
.then(function(response) {
if (response.status === 401) {
window.location.href = '/auth/dev-login';
return;
}
if (!response.ok) {
throw new Error('Like request failed');
}
return response.json();
})
.then(function(data) {
if (data && data.like_count !== undefined) {
countSpan.textContent = data.like_count;
}
})
.catch(function(error) {
console.error('Like error:', error);
});
});
});
</script>
{% endblock %}

View File

@@ -24,6 +24,7 @@ from app.application.use_cases import (
GetPostUseCase,
ListPostsUseCase,
PublishPostUseCase,
TogglePostLikeUseCase,
UpdatePostUseCase,
)
from app.domain.exceptions import (
@@ -523,6 +524,39 @@ async def delete_post(
return RedirectResponse(url="/web/", status_code=303)
@router.post("/posts/{post_slug}/like")
async def toggle_like_web(
post_slug: str,
user: OptionalUserDep,
get_use_case: FromDishka[GetPostUseCase],
toggle_use_case: FromDishka[TogglePostLikeUseCase],
) -> dict[str, object]:
"""Toggle like on a post via web UI.
Args:
post_slug: The URL-friendly slug of the post.
user: Current user from cookie or None.
get_use_case: Use case for retrieving posts.
toggle_use_case: Use case for toggling likes.
Returns:
JSON dict with updated like_count.
Raises:
HTTPException: If post not found or user not authenticated.
"""
if not user:
raise HTTPException(status_code=401, detail="Authentication required")
try:
post = await get_use_case.by_slug(post_slug)
except NotFoundException:
raise HTTPException(status_code=404, detail="Post not found") from None
result = await toggle_use_case.execute(post.id, user.user_id)
return {"like_count": result.like_count}
@router.get("/profile", response_class=HTMLResponse)
async def profile(
request: Request,