mirror of
https://codeberg.org/rimu/pyfedi
synced 2025-01-23 19:36:56 -08:00
vote for posts
This commit is contained in:
parent
b05531fda3
commit
8737d3cbad
8 changed files with 227 additions and 130 deletions
|
@ -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':
|
||||
|
|
|
@ -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.')
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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>
|
21
app/templates/community/_post_voting_buttons.html
Normal file
21
app/templates/community/_post_voting_buttons.html
Normal 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 %}
|
Loading…
Reference in a new issue