diff --git a/files/assets/js/gif_modal.js b/files/assets/js/gif_modal.js
deleted file mode 100644
index 94746bf09..000000000
--- a/files/assets/js/gif_modal.js
+++ /dev/null
@@ -1,85 +0,0 @@
-let commentFormID;
-
-function commentForm(form) {
- commentFormID = form;
-};
-
-async function getGif(searchTerm) {
-
- if (searchTerm !== undefined) {
- document.getElementById('gifSearch').value = searchTerm;
- }
- else {
- document.getElementById('gifSearch').value = null;
- }
-
- var loadGIFs = document.getElementById('gifs-load-more');
-
- var noGIFs = document.getElementById('no-gifs-found');
-
- var container = document.getElementById('GIFs');
-
- var backBtn = document.getElementById('gifs-back-btn');
-
- var cancelBtn = document.getElementById('gifs-cancel-btn');
-
- container.innerHTML = '';
-
- if (searchTerm == undefined) {
- container.innerHTML = '
Aw shucks. No GIFs found...
';
- container.innerHTML = null;
- loadGIFs.innerHTML = null;
- }
- else {
- for (var i = 0; i < 48; i++) {
- gifURL[i] = "https://media.giphy.com/media/" + data[i].id + "/giphy.webp";
- if (data[i].username==''){
- container.innerHTML += ('
Thou've reached the end of the list!
';
- }
- }
- }
-}
-
-function insertGIF(url,form) {
-
- // https://github.com/themotte/rDrama/issues/139
- // when MULTIMEDIA_EMBEDDING_ENABLED == False, we want to insert an anchor, NOT an img
- //var gif = "\n\n";
- var gif = '\n\n[' + url + '](' + url + ')';
-
- var commentBox = document.getElementById(form);
-
- var old = commentBox.value;
-
- commentBox.value = old + gif;
-
- if (typeof checkForRequired === "function") checkForRequired();
-}
diff --git a/files/classes/comment.py b/files/classes/comment.py
index d90893473..88f5df1db 100644
--- a/files/classes/comment.py
+++ b/files/classes/comment.py
@@ -1,7 +1,7 @@
from os import environ
import re
import time
-from typing import Optional
+from typing import Literal, Optional
from urllib.parse import urlencode, urlparse, parse_qs
from flask import *
from sqlalchemy import *
@@ -16,6 +16,8 @@ from random import randint
from .votes import CommentVote
from math import floor
+CommentRenderContext = Literal['comments', 'volunteer']
+
class Comment(Base):
__tablename__ = "comments"
@@ -87,10 +89,33 @@ class Comment(Base):
comment_age_seconds = int(time.time()) - self.created_utc
comment_age_hours = comment_age_seconds / (60*60)
return comment_age_hours < app.config['SCORE_HIDING_TIME_HOURS']
+
+ @lazy
+ def _score_context_str(self, score_type:Literal['score', 'upvotes', 'downvotes'],
+ context:CommentRenderContext) -> str:
+ if self.is_message: return '' # don't show scores for messages
+ if context == 'volunteer': return '' # volunteer: hide scores
+ if self.should_hide_score: return '' # hide scores for new comments
+
+ if score_type == 'upvotes': return str(self.upvotes)
+ if score_type == 'score': return str(self.score)
+ if score_type == 'downvotes': return str(self.downvotes)
+
+ @lazy
+ def upvotes_str(self, context:CommentRenderContext) -> str:
+ return self._score_context_str('upvotes', context)
+
+ @lazy
+ def score_str(self, context:CommentRenderContext) -> str:
+ return self._score_context_str('score', context)
+
+ @lazy
+ def downvotes_str(self, context:CommentRenderContext) -> str:
+ return self._score_context_str('downvotes', context)
@property
@lazy
- def top_comment(self):
+ def top_comment(self) -> Optional["Comment"]:
return g.db.query(Comment).filter_by(id=self.top_comment_id).one_or_none()
@lazy
@@ -385,20 +410,121 @@ class Comment(Base):
@lazy
def collapse_for_user(self, v, path):
if v and self.author_id == v.id: return False
-
if path == '/admin/removed/comments': return False
-
if self.over_18 and not (v and v.over_18) and not (self.post and self.post.over_18): return True
-
if self.is_banned: return True
-
if v and v.filter_words and self.body and any(x in self.body for x in v.filter_words): return True
-
return False
@property
@lazy
- def is_op(self): return self.author_id==self.post.author_id
+ def is_op(self):
+ return self.author_id == self.post.author_id
+
+ @property
+ @lazy
+ def is_comment(self) -> bool:
+ '''
+ Returns whether this is an actual comment (i.e. not a private message)
+ '''
+ return bool(self.parent_submission)
+
+ @property
+ @lazy
+ def is_message(self) -> bool:
+ '''
+ Returns whether this is a private message or modmail
+ '''
+ return not self.is_comment
+
+ @property
+ @lazy
+ def is_strict_message(self) -> bool:
+ '''
+ Returns whether this is a private message or modmail
+ but is not a notification
+ '''
+ return self.is_message and not self.is_notification
+
+ @property
+ @lazy
+ def is_modmail(self) -> bool:
+ '''
+ Returns whether this is a modmail message
+ '''
+ if not self.is_message: return False
+ if self.sentto == MODMAIL_ID: return True
+
+ top_comment: Optional["Comment"] = self.top_comment
+ return bool(top_comment.sentto == MODMAIL_ID)
+
+ @property
+ @lazy
+ def is_notification(self) -> bool:
+ '''
+ Returns whether this is a notification
+ '''
+ return self.is_message and not self.sentto
+
+ @lazy
+ def header_msg(self, v, is_notification_page:bool, reply_count:int) -> str:
+ '''
+ Returns a message that is in the header for a comment, usually for
+ display on a notification page.
+ '''
+ if self.post:
+ post_html:str = f"
{% set standalone=False %}
{% for reply in replies %}
@@ -106,56 +56,21 @@
-
{% else %}
-{% if v %}
- {% set voted=c.voted %}
- {% if not voted and v.id == c.author_id %}
- {% set voted=1 %}
- {% endif %}
-{% else %}
- {% set voted=-2 %}
-{% endif %}
+{%- set voted = c.voted_display(v) -%}
{% if standalone and level==1 %}
-
+
{% if c.post and c.post.over_18 %}{% endif %}
+ {{c.header_msg(v, is_notification_page, replies | length) | safe}}
- {% if c.post %}
- {% if c.author_id==v.id and replies and is_notification_page%}
- Comment {{'Replies' if (replies | length)>1 else 'Reply'}}: {{c.post.realtitle(v) | safe}}
- {% elif c.post.author_id==v.id and c.level == 1 and is_notification_page%}
- Post Reply: {{c.post.realtitle(v) | safe}}
- {% elif is_notification_page and c.parent_submission in v.subscribed_idlist() %}
- Subscribed Thread: {{c.post.realtitle(v) | safe}}
- {% elif is_notification_page %}
- Username Mention: {{c.post.realtitle(v) | safe}}
- {% else %}
- {{c.post.realtitle(v) | safe}}
- {% endif %}
-
- {% if c.post.sub %}
- in /h/{{c.post.sub}}
- {% endif %}
- {% elif c.author_id==NOTIFICATIONS_ID or c.author_id==AUTOJANNY_ID %}
- Notification
- {% else %}
- {% if c.sentto == MODMAIL_ID %}
- Sent to admins
- {% else %}
- Sent to @{{c.senttouser.username}}
- {% endif %}
- {% endif %}
-
+
{{c.header_msg(v, is_notification_page, replies | length) | safe}}
+
{% endif %}
-{% if not standalone and c.parent_comment and c.parent_comment.sentto %}
- {% set isreply = True %}
-{% else %}
- {% set isreply = False %}
-{% endif %}
+{% set isreply = not standalone and c.parent_comment and c.parent_comment.sentto %}
{% if not isreply %}
@@ -164,89 +79,12 @@
{% endif %}
-
+ {%- include 'component/comment/user_info.html' -%}
-
-
-
- {% if v and c.filter_state == 'reported' and v.can_manage_reports() %}
-
- {% endif %}
-
- {% if c.is_banned and c.ban_reason %}
+ {%- include 'component/comment/reports.html'-%}
+ {% if c.is_banned and c.ban_reason %} {# TODO: shouldn't be visible. See #359 #}
{% endif %}
@@ -254,550 +92,90 @@
{{c.realbody(v) | safe}}
{% if c.parent_submission %}
-
- {% if v and v.id==c.author_id %}
-
- {% endif %}
+ {%- include 'component/comment/editbox_comment.html' -%}
+
+
+
+ -
+ {%- include 'component/comment/voting_desktop.html' -%}
+ {%- include 'component/comment/actions_desktop.html' -%}
{% endif %}
-
-
- {% if v and v.id != c.author_id and c.body %}
-
- {% endif %}
-
-
-
- {% for reply in replies %}
- {{single_comment(reply, level=level+1)}}
- {% endfor %}
-
- {% elif replies %}
-
- {% endif %}
-
- {% if request.path == '/notifications' and c.level == 1 and c.sentto and not c.parent_submission and c.author_id not in (NOTIFICATIONS_ID, AUTOJANNY_ID) %}
-
+ {% for reply in replies %}
+ {{single_comment(reply, level=level+1)}}
+ {% endfor %}
+
+ {% elif replies %}
+
+ {% endif %}
+
+ {% if is_notification_page and c.level == 1 and c.sentto and not c.parent_submission and c.author_id not in (NOTIFICATIONS_ID, AUTOJANNY_ID) %}
+ {%- include 'components/comment/replybox_message.html' -%}
+ {% endif %}
{% else %}
-