Fix comment tree display (Issue #10)

- Add build_comment_tree() to organize comments hierarchically
- Create recursive Jinja macro to render nested comments
- Add visual styling with left border and indentation
- Comments now display as threaded tree structure

Fixes #10
This commit is contained in:
2025-10-11 23:36:27 -05:00
parent b84ebce8f1
commit 63fa44ed2c
2 changed files with 79 additions and 28 deletions

35
app.py
View File

@@ -575,6 +575,33 @@ def api_content_timestamp():
return jsonify({'error': 'Failed to get content timestamp'}), 500 return jsonify({'error': 'Failed to get content timestamp'}), 500
def build_comment_tree(comments):
"""Build a hierarchical comment tree from flat comment list"""
# Create lookup dict by UUID
comment_dict = {c['uuid']: {**c, 'replies': []} for c in comments}
# Build tree structure
root_comments = []
for comment in comments:
parent_uuid = comment.get('parent_comment_uuid')
if parent_uuid and parent_uuid in comment_dict:
# Add as reply to parent
comment_dict[parent_uuid]['replies'].append(comment_dict[comment['uuid']])
else:
# Top-level comment
root_comments.append(comment_dict[comment['uuid']])
# Sort at each level by timestamp
def sort_tree(comments_list):
comments_list.sort(key=lambda x: x.get('timestamp', 0))
for comment in comments_list:
if comment.get('replies'):
sort_tree(comment['replies'])
sort_tree(root_comments)
return root_comments
@app.route('/post/<post_id>') @app.route('/post/<post_id>')
def post_detail(post_id): def post_detail(post_id):
"""Serve individual post detail page with modern theme""" """Serve individual post detail page with modern theme"""
@@ -598,11 +625,11 @@ def post_detail(post_id):
) )
# Get comments from cache # Get comments from cache
comments = cached_comments.get(post_id, []) comments_flat = cached_comments.get(post_id, [])
logger.info(f"Loading post {post_id}: found {len(comments)} comments") logger.info(f"Loading post {post_id}: found {len(comments_flat)} comments")
# Sort comments by timestamp # Build comment tree
comments.sort(key=lambda x: x.get('timestamp', 0)) comments = build_comment_tree(comments_flat)
# Load user settings if authenticated # Load user settings if authenticated
user_settings = {} user_settings = {}

View File

@@ -136,10 +136,8 @@
<section class="comments-section"> <section class="comments-section">
<h2>Comments ({{ comments|length }})</h2> <h2>Comments ({{ comments|length }})</h2>
{% if comments %} {% macro render_comment(comment, depth=0) %}
<div class="comments-list"> <div class="comment" style="margin-left: {{ depth * 24 }}px;">
{% for comment in comments %}
<div class="comment">
<div class="comment-header"> <div class="comment-header">
<span class="comment-author">{{ comment.author }}</span> <span class="comment-author">{{ comment.author }}</span>
<span class="comment-separator"></span> <span class="comment-separator"></span>
@@ -153,7 +151,21 @@
<span>▲ {{ comment.score or 0 }}</span> <span>▲ {{ comment.score or 0 }}</span>
</div> </div>
</div> </div>
{% if comment.replies %}
<div class="comment-replies">
{% for reply in comment.replies %}
{{ render_comment(reply, depth + 1) }}
{% endfor %}
</div> </div>
{% endif %}
</div>
{% endmacro %}
{% if comments %}
<div class="comments-list">
{% for comment in comments %}
{{ render_comment(comment) }}
{% endfor %} {% endfor %}
</div> </div>
{% else %} {% else %}
@@ -554,12 +566,24 @@
.comment { .comment {
padding: 20px 0; padding: 20px 0;
border-bottom: 1px solid #f1f5f9; border-bottom: 1px solid #f1f5f9;
position: relative;
} }
.comment:last-child { .comment:last-child {
border-bottom: none; border-bottom: none;
} }
/* Threaded comment styling */
.comment[style*="margin-left"] {
padding-left: 16px;
border-left: 2px solid #e2e8f0;
border-bottom: none;
}
.comment-replies {
margin-top: 8px;
}
.comment-header { .comment-header {
display: flex; display: flex;
align-items: center; align-items: center;