pagination

This commit is contained in:
rimu 2023-12-15 17:35:11 +13:00
parent fb2dc055d3
commit 7b91250e3a
11 changed files with 87 additions and 17 deletions

View file

@ -92,6 +92,8 @@ def show_community(community: Community):
if current_user.is_anonymous and request_etag_matches(current_etag):
return return_304(current_etag)
page = request.args.get('page', 1, type=int)
mods = community.moderators()
is_moderator = current_user.is_authenticated and any(mod.user_id == current_user.id for mod in mods)
@ -105,17 +107,23 @@ def show_community(community: Community):
mod_list = User.query.filter(User.id.in_(mod_user_ids)).all()
if current_user.is_anonymous or current_user.ignore_bots:
posts = community.posts.filter(Post.from_bot == False).order_by(desc(Post.last_active)).all()
posts = community.posts.filter(Post.from_bot == False).order_by(desc(Post.last_active)).paginate(page=page, per_page=100, error_out=False)
else:
posts = community.posts.order_by(desc(Post.last_active)).all()
posts = community.posts.order_by(desc(Post.last_active)).paginate(page=page, per_page=100, error_out=False)
description = shorten_string(community.description, 150) if community.description else None
og_image = community.image.source_url if community.image_id else None
next_url = url_for('activitypub.community_profile', actor=community.ap_id if community.ap_id is not None else community.name,
page=posts.next_num) if posts.has_next else None
prev_url = url_for('activitypub.community_profile', actor=community.ap_id if community.ap_id is not None else community.name,
page=posts.prev_num) if posts.has_prev and page != 1 else None
return render_template('community/community.html', community=community, title=community.title,
is_moderator=is_moderator, is_owner=is_owner, mods=mod_list, posts=posts, description=description,
og_image=og_image, POST_TYPE_IMAGE=POST_TYPE_IMAGE, POST_TYPE_LINK=POST_TYPE_LINK, SUBSCRIPTION_PENDING=SUBSCRIPTION_PENDING,
SUBSCRIPTION_MEMBER=SUBSCRIPTION_MEMBER, etag=f"{community.id}_{hash(community.last_active)}",
next_url=next_url, prev_url=prev_url,
rss_feed=f"https://{current_app.config['SERVER_NAME']}/community/{community.link()}/feed", rss_feed_name=f"{community.title} posts on PieFed")

View file

@ -432,7 +432,7 @@ class Post(db.Model):
mea_culpa = db.Column(db.Boolean, default=False)
has_embed = db.Column(db.Boolean, default=False)
reply_count = db.Column(db.Integer, default=0)
score = db.Column(db.Integer, default=0, index=True)
score = db.Column(db.Integer, default=0, index=True) # used for 'top' ranking
nsfw = db.Column(db.Boolean, default=False)
nsfl = db.Column(db.Boolean, default=False)
sticky = db.Column(db.Boolean, default=False)
@ -445,7 +445,7 @@ class Post(db.Model):
ip = db.Column(db.String(50))
up_votes = db.Column(db.Integer, default=0)
down_votes = db.Column(db.Integer, default=0)
ranking = db.Column(db.Integer, default=0)
ranking = db.Column(db.Integer, default=0) # used for 'hot' ranking
language = db.Column(db.String(10))
edited_at = db.Column(db.DateTime)
@ -507,7 +507,7 @@ class PostReply(db.Model):
body = db.Column(db.Text)
body_html = db.Column(db.Text)
body_html_safe = db.Column(db.Boolean, default=False)
score = db.Column(db.Integer, default=0, index=True)
score = db.Column(db.Integer, default=0, index=True) # used for 'top' sorting
nsfw = db.Column(db.Boolean, default=False)
nsfl = db.Column(db.Boolean, default=False)
notify_author = db.Column(db.Boolean, default=True)
@ -517,7 +517,7 @@ class PostReply(db.Model):
from_bot = db.Column(db.Boolean, default=False)
up_votes = db.Column(db.Integer, default=0)
down_votes = db.Column(db.Integer, default=0)
ranking = db.Column(db.Integer, default=0, index=True)
ranking = db.Column(db.Integer, default=0, index=True) # used for 'hot' sorting
language = db.Column(db.String(10))
edited_at = db.Column(db.DateTime)

View file

@ -15,7 +15,9 @@ class NewReplyForm(FlaskForm):
class ReportPostForm(FlaskForm):
reason_choices = [('1', _l('Breaks community rules')), ('7', _l('Spam')), ('2', _l('Harassment')),
('3', _l('Threatening violence')), ('4', _l('Hate / genocide')),
('6', _l('Sharing personal information')),
('15', _l('Misinformation / disinformation')),
('16', _l('Racism, sexism, transphobia')),
('6', _l('Sharing personal info - doxing')),
('5', _l('Minor abuse or sexualization')),
('8', _l('Non-consensual intimate media')),
('9', _l('Prohibited transaction')), ('10', _l('Impersonation')),

View file

@ -1,6 +1,6 @@
from datetime import datetime
from flask import redirect, url_for, flash, current_app, abort
from flask import redirect, url_for, flash, current_app, abort, request
from flask_login import login_user, logout_user, current_user, login_required
from flask_babel import _
from sqlalchemy import or_, desc
@ -449,6 +449,8 @@ def post_report(post_id: int):
flash(_('Post has been reported, thank you!'))
return redirect(post.community.local_url())
elif request.method == 'GET':
form.report_remote.data = True
return render_template('post/post_report.html', title=_('Report post'), form=form, post=post)

View file

@ -16,6 +16,8 @@ def post_replies(post_id: int, sort_by: str, show_first: int = 0) -> List[PostRe
elif sort_by == 'new':
comments = comments.order_by(desc(PostReply.posted_at))
comments = comments.limit(2000) # paginating indented replies is too hard so just get the first 2000.
comments_dict = {comment.id: {'comment': comment, 'replies': []} for comment in comments.all()}
for comment in comments:

View file

@ -592,7 +592,7 @@ fieldset legend {
list-style-type: none;
padding: 0;
overflow-y: auto;
height: 135px;
height: 230px;
}
.table tr th {

View file

@ -330,7 +330,7 @@ nav, etc which are used site-wide */
list-style-type: none;
padding: 0;
overflow-y: auto;
height: 135px;
height: 230px;
}
.table {

View file

@ -42,6 +42,19 @@
<p>{{ _('No posts in this community yet.') }}</p>
{% endfor %}
</div>
<nav aria-label="Pagination" class="mt-4">
{% if prev_url %}
<a href="{{ prev_url }}" class="btn btn-primary">
<span aria-hidden="true">&larr;</span> {{ _('Previous page') }}
</a>
{% endif %}
{% if next_url %}
<a href="{{ next_url }}" class="btn btn-primary">
{{ _('Next page') }} <span aria-hidden="true">&rarr;</span>
</a>
{% endif %}
</nav>
</div>
<div class="col-12 col-md-4">

View file

@ -17,7 +17,7 @@
{% endif %}
{% if post.user_id == current_user.id and not post.mea_culpa %}
<li><a href="{{ url_for('post.post_mea_culpa', post_id=post.id) }}" class="no-underline"><span class="fe fe-mea-culpa"></span>
{{ _("I changed my mind") }}</a></li>
{{ _("I made a mistake with this post and have changed my mind about the topic") }}</a></li>
{% endif %}
{% if post.user_id != current_user.id %}
<li><a href="{{ url_for('post.post_block_user', post_id=post.id) }}" class="no-underline"><span class="fe fe-block"></span>

View file

@ -38,22 +38,46 @@
{{ user.about_html|safe }}
{% endif %}
{% if len(posts) > 0 %}
{% if posts %}
<h2 class="mt-4">Posts</h2>
<div class="post_list">
{% for post in posts %}
{% include 'post/_post_teaser.html' %}
{% endfor %}
</div>
<nav aria-label="Pagination" class="mt-4">
{% if post_prev_url %}
<a href="{{ post_prev_url }}" class="btn btn-primary">
<span aria-hidden="true">&larr;</span> {{ _('Previous page') }}
</a>
{% endif %}
{% if post_next_url %}
<a href="{{ post_next_url }}" class="btn btn-primary">
{{ _('Next page') }} <span aria-hidden="true">&rarr;</span>
</a>
{% endif %}
</nav>
{% endif %}
{% if len(post_replies) > 0 %}
<h2 class="mt-4">Comments</h2>
{% if post_replies %}
<h2 class="mt-4" id="comments">Comments</h2>
<div class="post_list">
{% for post_reply in post_replies %}
{% include 'post/_post_reply_teaser.html' %}
{% endfor %}
</div>
<nav aria-label="Pagination" class="mt-4">
{% if replies_prev_url %}
<a href="{{ replies_prev_url }}#comments" class="btn btn-primary">
<span aria-hidden="true">&larr;</span> {{ _('Previous page') }}
</a>
{% endif %}
{% if replies_next_url %}
<a href="{{ replies_next_url }}#comments" class="btn btn-primary">
{{ _('Next page') }} <span aria-hidden="true">&rarr;</span>
</a>
{% endif %}
</nav>
{% endif %}
</div>

View file

@ -17,21 +17,40 @@ from sqlalchemy import desc, or_, text
def show_profile(user):
if user.deleted or user.banned and current_user.is_anonymous():
abort(404)
posts = Post.query.filter_by(user_id=user.id).order_by(desc(Post.posted_at)).limit(20).all()
post_page = request.args.get('post_page', 1, type=int)
replies_page = request.args.get('replies_page', 1, type=int)
posts = Post.query.filter_by(user_id=user.id).order_by(desc(Post.posted_at)).paginate(page=post_page, per_page=50, error_out=False)
moderates = Community.query.filter_by(banned=False).join(CommunityMember).filter(CommunityMember.user_id == user.id)\
.filter(or_(CommunityMember.is_moderator, CommunityMember.is_owner))
upvoted = Post.query.join(PostVote).filter(Post.id == PostVote.post_id, PostVote.effect > 0).order_by(desc(Post.posted_at)).limit(10).all()
subscribed = Community.query.filter_by(banned=False).join(CommunityMember).filter(CommunityMember.user_id == user.id).all()
if current_user.is_anonymous or user.id != current_user.id:
moderates = moderates.filter(Community.private_mods == False)
post_replies = PostReply.query.filter_by(user_id=user.id).order_by(desc(PostReply.posted_at)).limit(20).all()
post_replies = PostReply.query.filter_by(user_id=user.id).order_by(desc(PostReply.posted_at)).paginate(page=replies_page, per_page=50, error_out=False)
# profile info
canonical = user.ap_public_url if user.ap_public_url else None
user.about_html = markdown_to_html(user.about)
description = shorten_string(markdown_to_text(user.about), 150) if user.about else None
# pagination urls
post_next_url = url_for('activitypub.user_profile', actor=user.ap_id if user.ap_id is not None else user.user_name,
post_page=posts.next_num) if posts.has_next else None
post_prev_url = url_for('activitypub.user_profile', actor=user.ap_id if user.ap_id is not None else user.user_name,
post_page=posts.prev_num) if posts.has_prev and post_page != 1 else None
replies_next_url = url_for('activitypub.user_profile', actor=user.ap_id if user.ap_id is not None else user.user_name,
replies_page=post_replies.next_num) if post_replies.has_next else None
replies_prev_url = url_for('activitypub.user_profile', actor=user.ap_id if user.ap_id is not None else user.user_name,
replies_page=post_replies.prev_num) if post_replies.has_prev and replies_page != 1 else None
return render_template('user/show_profile.html', user=user, posts=posts, post_replies=post_replies,
moderates=moderates.all(), canonical=canonical, title=_('Posts by %(user_name)s',
user_name=user.user_name),
description=description, subscribed=subscribed, upvoted=upvoted)
description=description, subscribed=subscribed, upvoted=upvoted,
post_next_url=post_next_url, post_prev_url=post_prev_url,
replies_next_url=replies_next_url, replies_prev_url=replies_prev_url)
@bp.route('/u/<actor>/profile', methods=['GET', 'POST'])