feat: add comments feature with nested replies and recursive rendering
All checks were successful
ci/woodpecker/pr/pipeline Pipeline was successful
All checks were successful
ci/woodpecker/pr/pipeline Pipeline was successful
Implement full comments system: domain entities (Comment, CommentLike), value objects (CommentContent), use cases (CRUD, like toggle), SQLAlchemy repository, API v1 endpoints, web UI with comment form and nested replies, i18n translations (EN/RU/FR/DE), and E2E tests. Fix nested reply (reply-to-reply) not displaying — the flat reply_comments dict was only queried for top-level comment IDs, so deeply nested replies were saved to DB (incrementing comment count) but never rendered. Switch to a recursive Jinja2 macro that renders any nesting depth.
This commit is contained in:
47
app/domain/value_objects/comment_content.py
Normal file
47
app/domain/value_objects/comment_content.py
Normal file
@@ -0,0 +1,47 @@
|
||||
"""Value object for comment content.
|
||||
|
||||
This module defines the CommentContent value object that validates
|
||||
and encapsulates comment text content.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from app.domain.value_objects.base import ValueObject
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class CommentContent(ValueObject[str]):
|
||||
"""Comment content value object.
|
||||
|
||||
Wraps and validates comment content ensuring it meets length
|
||||
requirements and is not empty.
|
||||
|
||||
Attributes:
|
||||
value: The comment content string.
|
||||
MAX_LENGTH: Maximum allowed content length (5000 characters).
|
||||
|
||||
Raises:
|
||||
ValueError: If content is empty or too long.
|
||||
|
||||
Example:
|
||||
>>> content = CommentContent("This is a **bold** comment.")
|
||||
>>> content.value
|
||||
'This is a **bold** comment.'
|
||||
"""
|
||||
|
||||
MAX_LENGTH: int = 5000
|
||||
|
||||
def _validate(self) -> None:
|
||||
"""Validate comment content.
|
||||
|
||||
Checks that content is a non-empty string within length bounds.
|
||||
|
||||
Raises:
|
||||
ValueError: If content fails validation criteria.
|
||||
"""
|
||||
if not isinstance(self.value, str):
|
||||
raise ValueError("Comment content must be a string")
|
||||
if not self.value.strip():
|
||||
raise ValueError("Comment content cannot be empty")
|
||||
if len(self.value) > self.MAX_LENGTH:
|
||||
raise ValueError(f"Comment content must be at most {self.MAX_LENGTH} characters")
|
||||
Reference in New Issue
Block a user