From ef275f4fbf0a1b0223b419d140c4282c7d40b026 Mon Sep 17 00:00:00 2001 From: rimu <3310831+rimu@users.noreply.github.com> Date: Sun, 15 Oct 2023 21:13:32 +1300 Subject: [PATCH] reply to a reply --- app/community/forms.py | 2 +- app/community/routes.py | 52 ++++++++++++--- app/static/images/collapsed.gif | Bin 0 -> 176 bytes app/static/images/expanded.gif | Bin 0 -> 183 bytes app/static/js/coolfieldset.js | 85 +++++++++++++++++++++++++ app/static/js/scripts.js | 8 ++- app/static/structure.css | 11 ++-- app/static/structure.scss | 9 ++- app/static/styles.css | 30 +++++++++ app/static/styles.scss | 30 +++++++++ app/templates/community/add_reply.html | 68 ++++++++++++++++++++ app/templates/community/post.html | 42 ++++++------ 12 files changed, 301 insertions(+), 36 deletions(-) create mode 100644 app/static/images/collapsed.gif create mode 100644 app/static/images/expanded.gif create mode 100644 app/static/js/coolfieldset.js create mode 100644 app/templates/community/add_reply.html diff --git a/app/community/forms.py b/app/community/forms.py index 464a5dda..0b2d3450 100644 --- a/app/community/forms.py +++ b/app/community/forms.py @@ -71,5 +71,5 @@ class CreatePost(FlaskForm): class NewReplyForm(FlaskForm): - body = TextAreaField(_l('Body'), render_kw={'placeholder': 'What are your thoughts?'}) + body = TextAreaField(_l('Body'), render_kw={'placeholder': 'What are your thoughts?', 'rows': 3}) submit = SubmitField(_l('Comment')) diff --git a/app/community/routes.py b/app/community/routes.py index 1b131c7c..281de697 100644 --- a/app/community/routes.py +++ b/app/community/routes.py @@ -29,7 +29,8 @@ def add_local(): private_key, public_key = RsaKeys.generate_keypair() community = Community(title=form.community_name.data, name=form.url.data, description=form.description.data, rules=form.rules.data, nsfw=form.nsfw.data, private_key=private_key, - public_key=public_key, ap_profile_id=current_app.config['SERVER_NAME'] + '/c/' + form.url.data, + public_key=public_key, + ap_profile_id=current_app.config['SERVER_NAME'] + '/c/' + form.url.data, subscriptions_count=1) db.session.add(community) db.session.commit() @@ -71,7 +72,8 @@ def show_community(community: Community): mods = community.moderators() is_moderator = current_user.is_authenticated and any(mod.user_id == current_user.id for mod in mods) - is_owner = current_user.is_authenticated and any(mod.user_id == current_user.id and mod.is_owner == True for mod in mods) + is_owner = current_user.is_authenticated and any( + mod.user_id == current_user.id and mod.is_owner == True for mod in mods) if community.private_mods: mod_list = [] @@ -125,7 +127,7 @@ def subscribe(actor): except Exception as ex: flash('Failed to send request to subscribe: ' + str(ex), 'error') current_app.logger.error("Exception while trying to subscribe" + str(ex)) - else: # for local communities, joining is instant + else: # for local communities, joining is instant banned = CommunityBan.query.filter_by(user_id=current_user.id, community_id=community.id).first() if banned: flash('You cannot join this community') @@ -237,7 +239,8 @@ def show_post(post_id: int): flash('Your comment has been added.') # todo: flush cache # todo: federation - return redirect(url_for('community.show_post', post_id=post_id)) # redirect to current page to avoid refresh resubmitting the form + return redirect(url_for('community.show_post', + post_id=post_id)) # redirect to current page to avoid refresh resubmitting the form else: replies = post_replies(post.id, 'top') return render_template('community/post.html', title=post.title, post=post, is_moderator=is_moderator, @@ -250,25 +253,25 @@ def comment_vote(comment_id, vote_direction): comment = PostReply.query.get_or_404(comment_id) existing_vote = PostReplyVote.query.filter_by(user_id=current_user.id, post_reply_id=comment.id).first() if existing_vote: - if existing_vote.effect > 0: # previous vote was up + if existing_vote.effect > 0: # previous vote was up if vote_direction == 'upvote': # new vote is also up, so remove it db.session.delete(existing_vote) comment.up_votes -= 1 comment.score -= 1 - else: # new vote is down while previous vote was up, so reverse their previous vote + else: # new vote is down while previous vote was up, so reverse their previous vote existing_vote.effect = -1 comment.up_votes -= 1 comment.down_votes += 1 comment.score -= 2 downvoted_class = 'voted_down' - else: # previous vote was down + else: # previous vote was down if vote_direction == 'upvote': # new vote is upvote existing_vote.effect = 1 comment.up_votes += 1 comment.down_votes -= 1 comment.score += 1 upvoted_class = 'voted_up' - else: # reverse a previous downvote + else: # reverse a previous downvote db.session.delete(existing_vote) comment.down_votes -= 1 comment.score += 2 @@ -289,3 +292,36 @@ def comment_vote(comment_id, vote_direction): return render_template('community/_voting_buttons.html', comment=comment, upvoted_class=upvoted_class, downvoted_class=downvoted_class) + + +@bp.route('/post//comment/') +def show_comment(post_id, comment_id): + ... + + +@bp.route('/post//comment//reply', methods=['GET', 'POST']) +def add_reply(post_id: int, comment_id: int): + post = Post.query.get_or_404(post_id) + comment = PostReply.query.get_or_404(comment_id) + mods = post.community.moderators() + is_moderator = current_user.is_authenticated and any(mod.user_id == current_user.id for mod in mods) + form = NewReplyForm() + if form.validate_on_submit(): + reply = PostReply(user_id=current_user.id, post_id=post.id, parent_id=comment.id, depth=comment.depth + 1, + community_id=post.community.id, body=form.body.data, + body_html=markdown_to_html(form.body.data), body_html_safe=True, + from_bot=current_user.bot, up_votes=1, nsfw=post.nsfw, nsfl=post.nsfl) + db.session.add(reply) + db.session.commit() + reply_vote = PostReplyVote(user_id=current_user.id, author_id=current_user.id, post_reply_id=reply.id, + effect=1.0) + db.session.add(reply_vote) + db.session.commit() + form.body.data = '' + flash('Your comment has been added.') + # todo: flush cache + # todo: federation + return redirect(url_for('community.show_post', post_id=post_id, _anchor=f'comment_{reply.id}')) + else: + return render_template('community/add_reply.html', title=_('Discussing %(title)s', title=post.title), post=post, + is_moderator=is_moderator, form=form, comment=comment) diff --git a/app/static/images/collapsed.gif b/app/static/images/collapsed.gif new file mode 100644 index 0000000000000000000000000000000000000000..3dc9ca77087a9c6bea9fdf2457237fbfa5258c5a GIT binary patch literal 176 zcmZ?wbhEHbawQJWNK6!rf)FM+{um+g*~w8;_(dZ+LQ0 JD%6?58USC1P>=us literal 0 HcmV?d00001 diff --git a/app/static/images/expanded.gif b/app/static/images/expanded.gif new file mode 100644 index 0000000000000000000000000000000000000000..126ccc11afd2eb76781bd3f47f2a3709437339b4 GIT binary patch literal 183 zcmZ?wbhEHbiMg8*Kgk5xpU{XZQBkVI`sSZ@2y+6ZrQTs|NsA=zyD?+1QdU=FfuTRFzA33g6w2q zHBnIQOUY!KxWr4)LD%2#Z16{&D-$jR8+A0XrLbjkxUn4Jaa53$X>2$gkfb5PBmA^L Jp_PHb8UUR { + element.style.display = 'none'; + }); + } else { + content.forEach((element) => { + element.style.display = 'none'; + }); + } + fieldset.classList.remove('expanded'); + fieldset.classList.add('collapsed'); + content.forEach((element) => { + element.setAttribute('aria-expanded', 'false'); + }); + if (!options.animation) { + fieldset.dispatchEvent(new Event('update')); + } + } + + function showFieldsetContent(fieldset, options) { + const content = fieldset.querySelectorAll('*:not(legend)'); + if (options.animation) { + content.forEach((element) => { + element.style.display = ''; + }); + } else { + content.forEach((element) => { + element.style.display = ''; + }); + } + fieldset.classList.remove('collapsed'); + fieldset.classList.add('expanded'); + content.forEach((element) => { + element.setAttribute('aria-expanded', 'true'); + }); + if (!options.animation) { + fieldset.dispatchEvent(new Event('update')); + } + } + + function doToggle(fieldset, setting) { + if (fieldset.classList.contains('collapsed')) { + showFieldsetContent(fieldset, setting); + } else if (fieldset.classList.contains('expanded')) { + hideFieldsetContent(fieldset, setting); + } + } + + function coolfieldset(selector, options) { + const fieldsets = document.querySelectorAll(selector); + const setting = { collapsed: false, animation: true, speed: 'medium', ...options }; + + fieldsets.forEach((fieldset) => { + const legend = fieldset.querySelector('legend'); + const content = fieldset.querySelectorAll('*:not(legend)'); + + content.forEach((element) => { + const wrapper = document.createElement('div'); + wrapper.classList.add('wrapper'); + element.parentNode.insertBefore(wrapper, element); + wrapper.appendChild(element); + }); + + if (setting.collapsed) { + hideFieldsetContent(fieldset, { animation: false }); + } else { + fieldset.classList.add('expanded'); + } + + legend.addEventListener('click', () => doToggle(fieldset, setting)); + }); + } + + window.coolfieldset = coolfieldset; +})(); + +// Usage: +// coolfieldset('.coolfieldset', { collapsed: true, animation: true, speed: 'slow' }); + +document.addEventListener('DOMContentLoaded', function () { + coolfieldset('.coolfieldset', { collapsed: true, animation: true, speed: 'slow' }); +}); diff --git a/app/static/js/scripts.js b/app/static/js/scripts.js index b6527e60..85325e94 100644 --- a/app/static/js/scripts.js +++ b/app/static/js/scripts.js @@ -15,7 +15,7 @@ function setupShowMoreLinks() { const comments = document.querySelectorAll('.comment'); comments.forEach(comment => { - const content = comment.querySelector('.comment_body'); + const content = comment.querySelector('.limit_height'); if (content && content.clientHeight > 400) { content.style.overflow = 'hidden'; content.style.maxHeight = '400px'; @@ -33,6 +33,7 @@ function setupShowMoreLinks() { showMoreLink.innerHTML = ''; } else { content.style.overflow = 'hidden'; + content.style.maxHeight = '400px'; showMoreLink.innerHTML = ''; } }); @@ -95,6 +96,11 @@ function setupHideButtons() { hidable.style.display = isHidden ? 'block' : 'none'; }); + const moreHidables = parentElement.parentElement.querySelectorAll('.hidable'); + moreHidables.forEach(hidable => { + hidable.style.display = isHidden ? 'block' : 'none'; + }); + // Toggle the content of hideEl if (isHidden) { hideEl.innerHTML = "[-] hide"; diff --git a/app/static/structure.css b/app/static/structure.css index 503612fd..1fed54fa 100644 --- a/app/static/structure.css +++ b/app/static/structure.css @@ -366,14 +366,13 @@ fieldset legend { clear: both; margin-bottom: 20px; } -.comment .comment_body { - overflow: hidden; +.comment .limit_height { position: relative; } -.comment .comment_body.expanded { +.comment .limit_height.expanded { max-height: none; } -.comment .comment_body.expanded .show-more { +.comment .limit_height.expanded .show-more { display: none; } .comment .show-more { @@ -441,4 +440,8 @@ fieldset legend { text-decoration: none; } +.add_reply .form-control-label { + display: none; +} + /*# sourceMappingURL=structure.css.map */ diff --git a/app/static/structure.scss b/app/static/structure.scss index b707b375..3fc43fc9 100644 --- a/app/static/structure.scss +++ b/app/static/structure.scss @@ -140,8 +140,7 @@ nav, etc which are used site-wide */ clear: both; margin-bottom: 20px; - .comment_body { - overflow: hidden; + .limit_height { position: relative; &.expanded { @@ -232,4 +231,10 @@ nav, etc which are used site-wide */ text-decoration: none; } } +} + +.add_reply { + .form-control-label { + display: none; + } } \ No newline at end of file diff --git a/app/static/styles.css b/app/static/styles.css index d09aaaea..9c3b1355 100644 --- a/app/static/styles.css +++ b/app/static/styles.css @@ -324,4 +324,34 @@ nav.navbar { margin-bottom: 3px; } +.coolfieldset, .coolfieldset.expanded { + border: 1px solid #ddd; + border-radius: 5px; + padding: 0 20px; +} + +.coolfieldset.collapsed { + border: 0; + border-top: 1px solid #bbb; + border-radius: 0; +} + +.coolfieldset legend { + padding-left: 13px; + font-weight: bold; + cursor: pointer; + background-color: white; + display: block; + position: relative; + top: -11px; +} + +.coolfieldset legend, .coolfieldset.expanded legend { + background: whitesmoke url(/static/images/expanded.gif) no-repeat center left; +} + +.coolfieldset.collapsed legend { + background: whitesmoke url(/static/images/collapsed.gif) no-repeat center left; +} + /*# sourceMappingURL=styles.css.map */ diff --git a/app/static/styles.scss b/app/static/styles.scss index 1a43bd87..f05c772e 100644 --- a/app/static/styles.scss +++ b/app/static/styles.scss @@ -91,3 +91,33 @@ nav.navbar { margin-left: 4px; margin-bottom: 3px; } + +.coolfieldset, .coolfieldset.expanded{ + border:1px solid $light-grey; + border-radius: 5px; + padding: 0 20px; +} + +.coolfieldset.collapsed{ + border:0; + border-top:1px solid $grey; + border-radius: 0; +} + +.coolfieldset legend{ + padding-left:13px; + font-weight:bold; + cursor:pointer; + background-color: white; + display: block; + position: relative; + top: -11px; +} + +.coolfieldset legend, .coolfieldset.expanded legend{ + background: whitesmoke url(/static/images/expanded.gif) no-repeat center left; +} + +.coolfieldset.collapsed legend{ + background: whitesmoke url(/static/images/collapsed.gif) no-repeat center left; +} \ No newline at end of file diff --git a/app/templates/community/add_reply.html b/app/templates/community/add_reply.html new file mode 100644 index 00000000..dc384aa8 --- /dev/null +++ b/app/templates/community/add_reply.html @@ -0,0 +1,68 @@ +{% extends "base.html" %} +{% from 'bootstrap/form.html' import render_form %} + +{% block app_content %} + +
+
+
Original post +

{{ post.title }}

+ {{ post.body_html | safe }} +
+
Comment you are replying to + {{ comment.body_html | safe}} +
+ {{ render_form(form) }} +
+
+
+
+
+
+ {% if current_user.is_authenticated and current_user.subscribed(post.community) %} + {{ _('Unsubscribe') }} + {% else %} + {{ _('Subscribe') }} + {% endif %} +
+ +
+
+ +
+
+
+
+
+

{{ _('About community') }}

+
+
+

{{ post.community.description|safe }}

+

{{ post.community.rules|safe }}

+ {% if len(mods) > 0 and not post.community.private_mods %} +

Moderators

+
    + {% for mod in mods %} +
  1. {{ mod.user_name }}
  2. + {% endfor %} +
+ {% endif %} +
+
+ {% if is_moderator %} +
+
+

{{ _('Community Settings') }}

+
+ +
+ {% endif %} +
+
+ +{% endblock %} diff --git a/app/templates/community/post.html b/app/templates/community/post.html index 900fca97..fd93b23f 100644 --- a/app/templates/community/post.html +++ b/app/templates/community/post.html @@ -82,28 +82,30 @@
{% macro render_comment(comment) %} -
-
- {% with comment=comment['comment'] %} - {% include "community/_voting_buttons.html" %} - {% endwith %} -
- -
- {% if comment['comment'].author.avatar_id %} - - Avatar - {% endif %} - - {{ comment['comment'].author.user_name}} - {% if comment['comment'].author.id == post.author.id%}[S]{% endif %} - {{ moment(comment['comment'].posted_at).fromNow(refresh=True) }} -
-
- {{ comment['comment'].body_html | safe }} +
+
+
+ {% with comment=comment['comment'] %} + {% include "community/_voting_buttons.html" %} + {% endwith %} +
+ +
+ {% if comment['comment'].author.avatar_id %} + + Avatar + {% endif %} + + {{ comment['comment'].author.user_name}} + {% if comment['comment'].author.id == post.author.id%}[S]{% endif %} + {{ moment(comment['comment'].posted_at).fromNow(refresh=True) }} +
+
+ {{ comment['comment'].body_html | safe }} +
{% if comment['replies'] %}