vote for posts

This commit is contained in:
rimu 2023-10-23 17:22:21 +13:00
parent b05531fda3
commit 8737d3cbad
8 changed files with 227 additions and 130 deletions

View file

@ -53,7 +53,7 @@ class CreatePost(FlaskForm):
self.link_url.errors.append(_('URL is required.'))
return False
domain = domain_from_url(self.link_url.data)
if domain.banned:
if domain and domain.banned:
self.link_url.errors.append(_(f"Links to %s are not allowed.".format(domain.name)))
return False
elif self.type.data == 'image':

View file

@ -9,10 +9,10 @@ from app import db, constants
from app.activitypub.signature import RsaKeys, HttpSignature
from app.community.forms import SearchRemoteCommunity, AddLocalCommunity, CreatePost, NewReplyForm
from app.community.util import search_for_community, community_url_exists, actor_to_community, post_replies, \
get_comment_branch
get_comment_branch, post_reply_count
from app.constants import SUBSCRIPTION_MEMBER, SUBSCRIPTION_OWNER, POST_TYPE_LINK, POST_TYPE_ARTICLE, POST_TYPE_IMAGE
from app.models import User, Community, CommunityMember, CommunityJoinRequest, CommunityBan, Post, PostReply, \
PostReplyVote
PostReplyVote, PostVote
from app.community import bp
from app.utils import get_setting, render_template, allowlist_html, markdown_to_html, validation_required
@ -252,6 +252,58 @@ def show_post(post_id: int):
canonical=post.ap_id, form=form, replies=replies, THREAD_CUTOFF_DEPTH=constants.THREAD_CUTOFF_DEPTH)
@bp.route('/post/<int:post_id>/<vote_direction>', methods=['GET', 'POST'])
@login_required
@validation_required
def post_vote(post_id: int, vote_direction):
upvoted_class = downvoted_class = ''
post = Post.query.get_or_404(post_id)
existing_vote = PostVote.query.filter_by(user_id=current_user.id, post_id=post.id).first()
if existing_vote:
post.author.reputation -= existing_vote.effect
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)
post.up_votes -= 1
post.score -= 1
else: # new vote is down while previous vote was up, so reverse their previous vote
existing_vote.effect = -1
post.up_votes -= 1
post.down_votes += 1
post.score -= 2
downvoted_class = 'voted_down'
else: # previous vote was down
if vote_direction == 'upvote': # new vote is upvote
existing_vote.effect = 1
post.up_votes += 1
post.down_votes -= 1
post.score += 1
upvoted_class = 'voted_up'
else: # reverse a previous downvote
db.session.delete(existing_vote)
post.down_votes -= 1
post.score += 2
else:
if vote_direction == 'upvote':
effect = 1
post.up_votes += 1
post.score += 1
upvoted_class = 'voted_up'
else:
effect = -1
post.down_votes += 1
post.score -= 1
downvoted_class = 'voted_down'
vote = PostVote(user_id=current_user.id, post_id=post.id, author_id=post.author.id,
effect=effect)
post.author.reputation += effect
db.session.add(vote)
db.session.commit()
return render_template('community/_post_voting_buttons.html', post=post,
upvoted_class=upvoted_class,
downvoted_class=downvoted_class)
@bp.route('/comment/<int:comment_id>/<vote_direction>', methods=['POST'])
@login_required
@validation_required
@ -293,7 +345,8 @@ def comment_vote(comment_id, vote_direction):
comment.down_votes += 1
comment.score -= 1
downvoted_class = 'voted_down'
vote = PostReplyVote(user_id=current_user.id, post_reply_id=comment_id, author_id=comment.user_id, effect=effect)
vote = PostReplyVote(user_id=current_user.id, post_reply_id=comment_id, author_id=comment.author.id, effect=effect)
comment.author.reputation += effect
db.session.add(vote)
db.session.commit()
return render_template('community/_voting_buttons.html', comment=comment,
@ -331,6 +384,7 @@ def add_reply(post_id: int, comment_id: int):
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)
post.reply_count = post_reply_count(post.id)
db.session.commit()
form.body.data = ''
flash('Your comment has been added.')

View file

@ -126,3 +126,9 @@ def get_comment_branch(post_id: int, comment_id: int, sort_by: str) -> List[Post
parent_comment['replies'].append(comments_dict[comment.id])
return [comment for comment in comments_dict.values() if comment['comment'].id == comment_id]
# The number of replies a post has
def post_reply_count(post_id) -> int:
return db.session.execute('SELECT COUNT(id) as c FROM "post_reply" WHERE post_id = :post_id',
{'post_id': post_id}).scalar()

View file

@ -378,6 +378,44 @@ fieldset legend {
padding-right: 5px;
}
.voting_buttons {
float: right;
display: block;
width: 60px;
padding: 5px;
}
.voting_buttons div {
border: solid 1px #0071CE;
}
.voting_buttons .upvote_button, .voting_buttons .downvote_button {
padding-left: 3px;
border-radius: 3px;
cursor: pointer;
}
.voting_buttons .upvote_button.digits_4, .voting_buttons .downvote_button.digits_4 {
width: 68px;
}
.voting_buttons .upvote_button.digits_5, .voting_buttons .downvote_button.digits_5 {
width: 76px;
}
.voting_buttons .upvote_button.digits_6, .voting_buttons .downvote_button.digits_6 {
width: 84px;
}
.voting_buttons .upvote_button.voted_up, .voting_buttons .downvote_button.voted_up {
color: green;
font-weight: bold;
}
.voting_buttons .upvote_button.voted_down, .voting_buttons .downvote_button.voted_down {
color: darkred;
font-weight: bold;
}
.voting_buttons .downvote_button {
margin-top: 5px;
}
.voting_buttons a {
text-decoration: none;
}
.comment {
clear: both;
margin-bottom: 20px;
@ -413,43 +451,6 @@ fieldset legend {
.comment .hide_button a {
text-decoration: none;
}
.comment .voting_buttons {
float: right;
display: block;
width: 60px;
padding: 5px;
}
.comment .voting_buttons div {
border: solid 1px #0071CE;
}
.comment .voting_buttons .upvote_button, .comment .voting_buttons .downvote_button {
padding-left: 3px;
border-radius: 3px;
cursor: pointer;
}
.comment .voting_buttons .upvote_button.digits_4, .comment .voting_buttons .downvote_button.digits_4 {
width: 68px;
}
.comment .voting_buttons .upvote_button.digits_5, .comment .voting_buttons .downvote_button.digits_5 {
width: 76px;
}
.comment .voting_buttons .upvote_button.digits_6, .comment .voting_buttons .downvote_button.digits_6 {
width: 84px;
}
.comment .voting_buttons .upvote_button.voted_up, .comment .voting_buttons .downvote_button.voted_up {
color: green;
font-weight: bold;
}
.comment .voting_buttons .upvote_button.voted_down, .comment .voting_buttons .downvote_button.voted_down {
color: darkred;
font-weight: bold;
}
.comment .voting_buttons .downvote_button {
margin-top: 5px;
}
.comment .voting_buttons a {
text-decoration: none;
}
.comment .comment_actions {
margin-top: -10px;
}

View file

@ -148,50 +148,6 @@ nav, etc which are used site-wide */
padding-right: 5px;
}
.comment {
clear: both;
margin-bottom: 20px;
margin-left: 20px;
.limit_height {
position: relative;
&.expanded {
max-height: none;
.show-more {
display: none;
}
}
}
.show-more {
text-decoration: none;
display: block;
text-align: center;
background-color: $dark-grey;
color: white;
margin-bottom: 15px;
}
.comment_author {
img {
width: 20px;
height: 20px;
}
}
.hide_button {
float: right;
display: block;
width: 68px;
padding: 5px;
a {
text-decoration: none;
}
}
.voting_buttons {
float: right;
display: block;
@ -238,6 +194,50 @@ nav, etc which are used site-wide */
}
}
.comment {
clear: both;
margin-bottom: 20px;
margin-left: 20px;
.limit_height {
position: relative;
&.expanded {
max-height: none;
.show-more {
display: none;
}
}
}
.show-more {
text-decoration: none;
display: block;
text-align: center;
background-color: $dark-grey;
color: white;
margin-bottom: 15px;
}
.comment_author {
img {
width: 20px;
height: 20px;
}
}
.hide_button {
float: right;
display: block;
width: 68px;
padding: 5px;
a {
text-decoration: none;
}
}
.comment_actions {
margin-top: -10px;
a {

View file

@ -9,6 +9,9 @@
<li class="breadcrumb-item active">{{ post.title|shorten(15) }}</li>
</ol>
</nav>
<div class="voting_buttons">
{% include "community/_post_voting_buttons.html" %}
</div>
<h1 class="mt-2">{{ post.title }}</h1>
{% if post.url %}
<p><small><a href="{{ post.url }}" rel="nofollow ugc">{{ post.url|shorten_url }}</a>
@ -29,6 +32,7 @@
{% endif %}
</div>
{% else %}
<div class="col">
<nav aria-label="breadcrumb" id="breadcrumb_nav" title="Navigation">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="/">{{ _('Home') }}</a></li>
@ -37,6 +41,9 @@
<li class="breadcrumb-item active">{{ post.title|shorten(15) }}</li>
</ol>
</nav>
<div class="voting_buttons">
{% include "community/_post_voting_buttons.html" %}
</div>
<h1 class="mt-2">{{ post.title }}</h1>
{% if post.url %}
<p><small><a href="{{ post.url }}" rel="nofollow ugc">{{ post.url|shorten_url }}
@ -49,6 +56,7 @@
<p class="small">submitted {{ moment(post.posted_at).fromNow() }} by
{{ render_username(post.author) }}
</p>
</div>
{% endif %}
</div>

View file

@ -1,4 +1,6 @@
<div class="post_teaser">
<div class="row">
<div class="col col-md-10">
<div class="row meta_row small">
<div class="col">{{ render_username(post.author) }} · {{ moment(post.posted_at).fromNow() }}</div>
</div>
@ -19,13 +21,18 @@
{% endif %}
</div>
<div class="row utilities_row">
<div class="col-4">
up {{ post.score }} down
</div>
<div class="col-6">
<a href="{{ url_for('community.show_post', post_id=post.id, _anchor='replies') }}">{{ post.reply_count }}</a> additional tools
</div>
<div class="col-2">...</div>
</div>
</div>
<div class="col col-md-2">
<div class="voting_buttons pt-2">
{% include "community/_post_voting_buttons.html" %}
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,21 @@
{% if current_user.is_authenticated and current_user.verified %}
<div class="upvote_button digits_{{ digits(post.up_votes) }} {{ upvoted_class }}"
hx-post="/community/post/{{ post.id }}/upvote" hx-trigger="click throttle:1s" hx-target="closest .voting_buttons">
<span class="fe fe-arrow-up"></span>
{{ post.up_votes }}
</div>
<div class="downvote_button digits_{{ digits(post.down_votes) }} {{ downvoted_class }}"
hx-post="/community/post/{{ post.id }}/downvote" hx-trigger="click throttle:1s" hx-target="closest .voting_buttons">
<span class="fe fe-arrow-down"></span>
{{ post.down_votes }}
</div>
{% else %}
<div class="upvote_button digits_{{ digits(post.up_votes) }} {{ upvoted_class }}">
<span class="fe fe-arrow-up"></span>
{{ post.up_votes }}
</div>
<div class="downvote_button digits_{{ digits(post.down_votes) }} {{ downvoted_class }}">
<span class="fe fe-arrow-down"></span>
{{ post.down_votes }}
</div>
{% endif %}