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.'))
|
self.link_url.errors.append(_('URL is required.'))
|
||||||
return False
|
return False
|
||||||
domain = domain_from_url(self.link_url.data)
|
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)))
|
self.link_url.errors.append(_(f"Links to %s are not allowed.".format(domain.name)))
|
||||||
return False
|
return False
|
||||||
elif self.type.data == 'image':
|
elif self.type.data == 'image':
|
||||||
|
|
|
@ -9,10 +9,10 @@ from app import db, constants
|
||||||
from app.activitypub.signature import RsaKeys, HttpSignature
|
from app.activitypub.signature import RsaKeys, HttpSignature
|
||||||
from app.community.forms import SearchRemoteCommunity, AddLocalCommunity, CreatePost, NewReplyForm
|
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, \
|
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.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, \
|
from app.models import User, Community, CommunityMember, CommunityJoinRequest, CommunityBan, Post, PostReply, \
|
||||||
PostReplyVote
|
PostReplyVote, PostVote
|
||||||
from app.community import bp
|
from app.community import bp
|
||||||
from app.utils import get_setting, render_template, allowlist_html, markdown_to_html, validation_required
|
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)
|
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'])
|
@bp.route('/comment/<int:comment_id>/<vote_direction>', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@validation_required
|
@validation_required
|
||||||
|
@ -293,7 +345,8 @@ def comment_vote(comment_id, vote_direction):
|
||||||
comment.down_votes += 1
|
comment.down_votes += 1
|
||||||
comment.score -= 1
|
comment.score -= 1
|
||||||
downvoted_class = 'voted_down'
|
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.add(vote)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return render_template('community/_voting_buttons.html', comment=comment,
|
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,
|
reply_vote = PostReplyVote(user_id=current_user.id, author_id=current_user.id, post_reply_id=reply.id,
|
||||||
effect=1.0)
|
effect=1.0)
|
||||||
db.session.add(reply_vote)
|
db.session.add(reply_vote)
|
||||||
|
post.reply_count = post_reply_count(post.id)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
form.body.data = ''
|
form.body.data = ''
|
||||||
flash('Your comment has been added.')
|
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])
|
parent_comment['replies'].append(comments_dict[comment.id])
|
||||||
|
|
||||||
return [comment for comment in comments_dict.values() if comment['comment'].id == 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;
|
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 {
|
.comment {
|
||||||
clear: both;
|
clear: both;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
@ -413,43 +451,6 @@ fieldset legend {
|
||||||
.comment .hide_button a {
|
.comment .hide_button a {
|
||||||
text-decoration: none;
|
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 {
|
.comment .comment_actions {
|
||||||
margin-top: -10px;
|
margin-top: -10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,6 +148,52 @@ nav, etc which are used site-wide */
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.voting_buttons {
|
||||||
|
float: right;
|
||||||
|
display: block;
|
||||||
|
width: 60px;
|
||||||
|
padding: 5px;
|
||||||
|
|
||||||
|
div {
|
||||||
|
border: solid 1px $primary-colour;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upvote_button, .downvote_button {
|
||||||
|
padding-left: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&.digits_4 {
|
||||||
|
width: 68px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.digits_5 {
|
||||||
|
width: 76px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.digits_6 {
|
||||||
|
width: 84px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.voted_up {
|
||||||
|
color: green;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
&.voted_down {
|
||||||
|
color: darkred;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.downvote_button {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.comment {
|
.comment {
|
||||||
clear: both;
|
clear: both;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
@ -192,52 +238,6 @@ nav, etc which are used site-wide */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.voting_buttons {
|
|
||||||
float: right;
|
|
||||||
display: block;
|
|
||||||
width: 60px;
|
|
||||||
padding: 5px;
|
|
||||||
|
|
||||||
div {
|
|
||||||
border: solid 1px $primary-colour;
|
|
||||||
}
|
|
||||||
|
|
||||||
.upvote_button, .downvote_button {
|
|
||||||
padding-left: 3px;
|
|
||||||
border-radius: 3px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&.digits_4 {
|
|
||||||
width: 68px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.digits_5 {
|
|
||||||
width: 76px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.digits_6 {
|
|
||||||
width: 84px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.voted_up {
|
|
||||||
color: green;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
&.voted_down {
|
|
||||||
color: darkred;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.downvote_button {
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.comment_actions {
|
.comment_actions {
|
||||||
margin-top: -10px;
|
margin-top: -10px;
|
||||||
a {
|
a {
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
<li class="breadcrumb-item active">{{ post.title|shorten(15) }}</li>
|
<li class="breadcrumb-item active">{{ post.title|shorten(15) }}</li>
|
||||||
</ol>
|
</ol>
|
||||||
</nav>
|
</nav>
|
||||||
|
<div class="voting_buttons">
|
||||||
|
{% include "community/_post_voting_buttons.html" %}
|
||||||
|
</div>
|
||||||
<h1 class="mt-2">{{ post.title }}</h1>
|
<h1 class="mt-2">{{ post.title }}</h1>
|
||||||
{% if post.url %}
|
{% if post.url %}
|
||||||
<p><small><a href="{{ post.url }}" rel="nofollow ugc">{{ post.url|shorten_url }}</a>
|
<p><small><a href="{{ post.url }}" rel="nofollow ugc">{{ post.url|shorten_url }}</a>
|
||||||
|
@ -29,26 +32,31 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
<nav aria-label="breadcrumb" id="breadcrumb_nav" title="Navigation">
|
<div class="col">
|
||||||
<ol class="breadcrumb">
|
<nav aria-label="breadcrumb" id="breadcrumb_nav" title="Navigation">
|
||||||
<li class="breadcrumb-item"><a href="/">{{ _('Home') }}</a></li>
|
<ol class="breadcrumb">
|
||||||
<li class="breadcrumb-item"><a href="/communities">{{ _('Communities') }}</a></li>
|
<li class="breadcrumb-item"><a href="/">{{ _('Home') }}</a></li>
|
||||||
<li class="breadcrumb-item"><a href="/c/{{ post.community.link() }}">{{ post.community.title }}</a></li>
|
<li class="breadcrumb-item"><a href="/communities">{{ _('Communities') }}</a></li>
|
||||||
<li class="breadcrumb-item active">{{ post.title|shorten(15) }}</li>
|
<li class="breadcrumb-item"><a href="/c/{{ post.community.link() }}">{{ post.community.title }}</a></li>
|
||||||
</ol>
|
<li class="breadcrumb-item active">{{ post.title|shorten(15) }}</li>
|
||||||
</nav>
|
</ol>
|
||||||
<h1 class="mt-2">{{ post.title }}</h1>
|
</nav>
|
||||||
{% if post.url %}
|
<div class="voting_buttons">
|
||||||
<p><small><a href="{{ post.url }}" rel="nofollow ugc">{{ post.url|shorten_url }}
|
{% include "community/_post_voting_buttons.html" %}
|
||||||
<img src="/static/images/external_link_black.svg" class="external_link_icon" alt="External link" /></a>
|
</div>
|
||||||
{% if post.type == post_type_link %}
|
<h1 class="mt-2">{{ post.title }}</h1>
|
||||||
<span class="domain_link">(<a href="/d/{{ post.domain_id }}">domain</a>)</span>
|
{% if post.url %}
|
||||||
|
<p><small><a href="{{ post.url }}" rel="nofollow ugc">{{ post.url|shorten_url }}
|
||||||
|
<img src="/static/images/external_link_black.svg" class="external_link_icon" alt="External link" /></a>
|
||||||
|
{% if post.type == post_type_link %}
|
||||||
|
<span class="domain_link">(<a href="/d/{{ post.domain_id }}">domain</a>)</span>
|
||||||
|
{% endif %}
|
||||||
|
</small></p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</small></p>
|
<p class="small">submitted {{ moment(post.posted_at).fromNow() }} by
|
||||||
{% endif %}
|
{{ render_username(post.author) }}
|
||||||
<p class="small">submitted {{ moment(post.posted_at).fromNow() }} by
|
</p>
|
||||||
{{ render_username(post.author) }}
|
</div>
|
||||||
</p>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -1,31 +1,38 @@
|
||||||
<div class="post_teaser">
|
<div class="post_teaser">
|
||||||
<div class="row meta_row small">
|
<div class="row">
|
||||||
<div class="col">{{ render_username(post.author) }} · {{ moment(post.posted_at).fromNow() }}</div>
|
<div class="col col-md-10">
|
||||||
</div>
|
<div class="row meta_row small">
|
||||||
<div class="row main_row">
|
<div class="col">{{ render_username(post.author) }} · {{ moment(post.posted_at).fromNow() }}</div>
|
||||||
<div class="col{% if post.image_id %}-8{% endif %}">
|
</div>
|
||||||
<h3>
|
<div class="row main_row">
|
||||||
<a href="{{ url_for('community.show_post', post_id=post.id) }}">{{ post.title }}</a>
|
<div class="col{% if post.image_id %}-8{% endif %}">
|
||||||
{% if post.type == post_type_link %}
|
<h3>
|
||||||
<span class="domain_link">(<a href="/d/{{ post.domain_id }}">domain</a>)</span>
|
<a href="{{ url_for('community.show_post', post_id=post.id) }}">{{ post.title }}</a>
|
||||||
{% endif %}
|
{% if post.type == post_type_link %}
|
||||||
</h3>
|
<span class="domain_link">(<a href="/d/{{ post.domain_id }}">domain</a>)</span>
|
||||||
</div>
|
{% endif %}
|
||||||
{% if post.image_id %}
|
</h3>
|
||||||
<div class="col-4">
|
</div>
|
||||||
<img src="{{ post.image.source_url}}" alt="{{ post.image.alt_text }}"
|
{% if post.image_id %}
|
||||||
width="100" />
|
<div class="col-4">
|
||||||
|
<img src="{{ post.image.source_url}}" alt="{{ post.image.alt_text }}"
|
||||||
|
width="100" />
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="row utilities_row">
|
||||||
|
<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>
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
<div class="row utilities_row">
|
|
||||||
<div class="col-4">
|
|
||||||
up {{ post.score }} down
|
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col col-md-2">
|
||||||
<a href="{{ url_for('community.show_post', post_id=post.id, _anchor='replies') }}">{{post.reply_count}}</a> additional tools
|
<div class="voting_buttons pt-2">
|
||||||
|
{% include "community/_post_voting_buttons.html" %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-2">...</div>
|
|
||||||
</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…
Add table
Reference in a new issue