Skip to content

Update comments UI : Refonte complète des commentaires — threading N-niveaux, éditeur riche, sécurité, validation#37

Merged
raynaldlao merged 30 commits into
masterfrom
update-comments-ui
Jul 14, 2026
Merged

Update comments UI : Refonte complète des commentaires — threading N-niveaux, éditeur riche, sécurité, validation#37
raynaldlao merged 30 commits into
masterfrom
update-comments-ui

Conversation

@raynaldlao

@raynaldlao raynaldlao commented Jul 14, 2026

Copy link
Copy Markdown
Owner

Résumé

Refonte complète du système de commentaires. 30 commits, 37 fichiers modifiés, ~1500 lignes ajoutées.


Commentaires hiérarchiques (N-niveaux)

  • Remplacement du threading plat 2-niveaux par un arbre récursif N-niveaux via une structure CommentNode avec build_comment_nested_tree()
  • Macro Jinja récursive render_comment(node, depth=0), profondeur limitée à 3 avant aplatissement
  • Affichage YouTube : bouton "View replies" / "Hide replies" pour les profondeurs < 3, expansion/réduction en cascade
  • Indentation visuelle CSS (.comment-depth-1 à .comment-depth-4) avec layout flex (avatar, en-tête, contenu)

Éditeur de commentaires — SunEditor

  • Remplacement du <textarea> brut par SunEditor WYSIWYG, synchronisation du HTML vers un champ caché à la soumission
  • Éditeur dédié par réponse (initReplyEditor(commentId))
  • Barre d'outils : gras, souligné, italique, barré, liste, lien, emoji
  • Blocage des images à toutes les entrées (coller, glisser-déposer, boutons d'upload, insertImage neutralisé)
  • Hauteur de ligne 1.4 pour une saisie compacte
  • Thème sombre pour les dialogues, icônes de la barre d'outils, listes déroulantes et chrome
  • Police Inter appliquée à toute l'interface SunEditor

Sélecteur d'emoji

  • Plugin SunEditor déclenchant un CustomEvent('open-emoji-picker')
  • Popup EmojiMart positionné au niveau du bouton, se ferme au scroll extérieur, se repositionne en cas de débordement
  • Sélecteur de teint désactivé, police Inter appliquée
  • Fermeture du picker s'il est déjà ouvert (comportement toggle)

Sécurité

  • Sanitisation : nh3.clean() avec une liste blanche compatible SunEditor bloque <script> et les tags non autorisés
  • Honeypot : champ caché hp_comment attrape les bots ; les soumissions remplies sont silencieusement rejetées
  • Rate limiting : 60 secondes de cooldown par utilisateur via CommentService ; message : "You're posting too fast..."
  • Suppression : deux clics — premier clic remplace le contenu par "Comment removed" / auteur "Anonymous", deuxième clic supprime avec cascade récursive

Limite de caractères et validation

  • maxCharCount: 5000 dans SunEditor, maxlength sur le textarea de secours, validateur Pydantic, VARCHAR(5000) en base
  • Validation des commentaires vides à 4 couches : garde JS avec toast, validateur Pydantic avec strip HTML, vérification après nh3.clean() dans le service, CHECK constraint en base
  • Texte indicatif : "Comment must be between 1 and 5000 characters"
  • 4 nouveaux tests DTO couvrant le rejet HTML-only

Style et UX

  • Compteur dynamique "Comments (N)" dans l'en-tête de l'article
  • Label permanent au-dessus de l'éditeur (.comment-join-title avec margin: 0)
  • .comment button { box-shadow: none } supprime le highlight des boutons
  • Marge haute séparant la section commentaires du contenu article
  • Rendu JSON inline côté serveur pour un chargement plus rapide
  • Politique de cache statique splitée (dist/ immutable, le reste no-cache)

raynaldlao added 30 commits July 7, 2026 16:29
…ve N‑level tree using a CommentNode structure, parent pointing to the direct parent and nested recursive rendering in the frontend, removing the old flat CommentThreadView
…ip <script> and non‑allowed tags, use a SunEditor‑compatible whitelist, move ALLOWED_TAGS to a frozenset on CommentService, update docstrings, and adjust integration tests to match nh3’s stripping. Add the nh3 dependency and update service + tests
…) for N‑level threading (depth capped at 4, deeper levels flattened), remove <hr> separators and update integration test to expect Level 3 rendering. Update article_detail.html with the recursive macro, .comment-depth-N classes, avatar initial + circle, formatted timestamp, and Reply button with hidden form
… .comment-avatar and .comment-content flex layout, depth‑based indentation via .comment-depth‑1..4, header row for author and timestamp, reply toggle and form styles, recursive .comment-replies wrapper, SunEditor dark‑theme variables, and a mobile media query. Remove five obsolete comment‑related classes
…e textarea with SunEditor‑ready IDs, syncing HTML to a hidden input on submit, adding per‑reply editors, loading SunEditor CSS/JS only when comments are present and adding the v2.43.10 bundles plus an initialization script for main and reply editors
…, replacing their content with “Comment removed” and setting the author to Anonymous, including legacy “[deleted]”. They are displayed in italics via .comment-deleted. Reply handling now allows only one reply form at a time. SunEditor initialization binds the form and hidden input before the editor is created. Docstrings are updated, CommentThreadView is removed, the emoji button is dropped, the repository adds get_by_reply_to and tests are adjusted for soft‑delete and the updated threading model
…he plugin triggers a CustomEvent, prompting the popup script to show an EmojiMart picker at the button location and inject the selected emoji into the appropriate editor. CSP is extended for CDN access, required bundles load only on comment pages and minimal CSS styles the floating popup
…avior. The popup now repositions when it overflows the viewport, scroll events outside the picker close it and mobile layouts center the picker with adjusted sizing
…moji picker by setting EmojiMart’s skinTonePosition to 'none' in the Picker options
…r UI by setting EmojiMart’s --font-family variable to var(--font-primary), so labels, categories and search text match the site typography
…ng the emoji button now closes the picker when it is already open instead of reopening it, matching expected UI behavior
…ditor UI by overriding its default font on the root container, so the toolbar, menus, tooltips and editor chrome match the site typography
…atic cache policy. Dark mode uses proper class overrides, Inter is applied to the editor and chrome, spacing around comment forms is adjusted and static assets now use a split cache policy (dist → immutable, others → no‑cache)
…ons and list dropdowns. Toolbar SVGs now use var(--on-surface) instead of black and dark theme overrides are added for the list layer background, border and hover states
…ides unused link‑dialog fields, unifies link color to var(--primary) and preserves target="_blank" with rel="noopener noreferrer" in sanitization. Tests updated accordingly
…e comment template for correct reply overflow. Caps visual depth at 3, moves replies outside the padded header section and updates CSS accordingly
…mpact comment editing. Adds lineHeight: '1.4' to the editor config and CSS overrides for paragraphs and editable text
…eply forms. A hidden input catches bots and submissions with the field filled are silently rejected. Tests cover both comment and reply honeypot redirects
… limit to comments. Honeypot submissions are silently dropped and rate‑limited users get a flash with the remaining wait time. Tests updated for both behaviors
…ve cascade, strips the <!--cmt‑removed--> marker in DTOs and introduces orphan‑children helpers in repository adapters. Template updated for new delete flow, dead CSS removed and tests added for cascade and first‑click behavior
…ies are collapsed by default and expanded recursively on click, with CSS display rules and template/JS updates to place the toggle in .comment-actions
…earer visual separation from the article content
… now computes total comment nodes (including nested and soft‑deleted), passes the count to the template and tests cover zero, nested, and soft‑deleted cases plus an integration check
…ditor comment editor. Capture‑phase filters stop paste and drag‑and‑drop images, upload inputs are disabled, insertImage is neutralized and unused callbacks removed. JS is modernized and image‑related tags are dropped from ALLOWED_TAGS
…r article load and synced comment reveal. useArticle skips fetching when inline data exists, legacy text is wrapped for BlockNote, comments are revealed after the editor renders and vendor chunk preload speeds up the React mount
…browser, application, and database. Validation now runs before the rate‑limit check, preventing false cooldowns and error messages are cleaned for users. Frontend editors use maxlength, Pydantic enforces the limit and the database column is capped accordingly
… comment editors by enabling charCounter and setting maxTextLength to 5000 in the initCommentEditor config

for both comment and reply editors
…to comment editors. CSS adds the hint style and removes default h4 spacing, the template restores the title and places the hint directly above SunEditor and the editor keeps the 5000‑character limit without placeholder or counter
… comment threads. Adds a .comment button rule in article.css to disable the default highlight on Reply, View replies, Cancel, Delete and Post Comment buttons
…idation. The editor strips HTML and shows a toast when the result is empty, hint text now requires 1–5000 chars, backend and validator both reject empty sanitized input, tests cover HTML‑only cases and the migration enforces VARCHAR(5000) with a minimum‑length check
@raynaldlao raynaldlao self-assigned this Jul 14, 2026
@raynaldlao
raynaldlao requested a review from hlargitte July 14, 2026 09:03

@hlargitte hlargitte left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@raynaldlao
raynaldlao merged commit e0ef5db into master Jul 14, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants