diff --git a/app/community/routes.py b/app/community/routes.py index f034baae..f2057c98 100644 --- a/app/community/routes.py +++ b/app/community/routes.py @@ -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") diff --git a/app/models.py b/app/models.py index a8e2e275..a3b3744a 100644 --- a/app/models.py +++ b/app/models.py @@ -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) diff --git a/app/post/forms.py b/app/post/forms.py index d488a3b2..bf85d6c7 100644 --- a/app/post/forms.py +++ b/app/post/forms.py @@ -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')), diff --git a/app/post/routes.py b/app/post/routes.py index 5b60ed04..d8d95abc 100644 --- a/app/post/routes.py +++ b/app/post/routes.py @@ -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) diff --git a/app/post/util.py b/app/post/util.py index 87b3c1d6..1773c83b 100644 --- a/app/post/util.py +++ b/app/post/util.py @@ -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: diff --git a/app/static/structure.css b/app/static/structure.css index cb7bcbf3..32ab2041 100644 --- a/app/static/structure.css +++ b/app/static/structure.css @@ -592,7 +592,7 @@ fieldset legend { list-style-type: none; padding: 0; overflow-y: auto; - height: 135px; + height: 230px; } .table tr th { diff --git a/app/static/structure.scss b/app/static/structure.scss index 573d5915..de5c42ca 100644 --- a/app/static/structure.scss +++ b/app/static/structure.scss @@ -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 { diff --git a/app/templates/community/community.html b/app/templates/community/community.html index 8fcd9844..cbb261de 100644 --- a/app/templates/community/community.html +++ b/app/templates/community/community.html @@ -42,6 +42,19 @@

{{ _('No posts in this community yet.') }}

{% endfor %} + +
diff --git a/app/templates/post/post_options.html b/app/templates/post/post_options.html index b3ca824e..78bbb99f 100644 --- a/app/templates/post/post_options.html +++ b/app/templates/post/post_options.html @@ -17,7 +17,7 @@ {% endif %} {% if post.user_id == current_user.id and not post.mea_culpa %}
  • - {{ _("I changed my mind") }}
  • + {{ _("I made a mistake with this post and have changed my mind about the topic") }} {% endif %} {% if post.user_id != current_user.id %}
  • diff --git a/app/templates/user/show_profile.html b/app/templates/user/show_profile.html index fe1eb24c..c1875814 100644 --- a/app/templates/user/show_profile.html +++ b/app/templates/user/show_profile.html @@ -38,22 +38,46 @@ {{ user.about_html|safe }} {% endif %} - {% if len(posts) > 0 %} + {% if posts %}

    Posts

    {% for post in posts %} {% include 'post/_post_teaser.html' %} {% endfor %}
    +
    {% endif %} - {% if len(post_replies) > 0 %} -

    Comments

    + {% if post_replies %} +

    Comments

    {% for post_reply in post_replies %} {% include 'post/_post_reply_teaser.html' %} {% endfor %}
    + {% endif %}
  • diff --git a/app/user/routes.py b/app/user/routes.py index 0500e40e..50fdb52b 100644 --- a/app/user/routes.py +++ b/app/user/routes.py @@ -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//profile', methods=['GET', 'POST'])