From 4a6492a15ca56a298b3f32a804becaf40e131a77 Mon Sep 17 00:00:00 2001 From: rimu <3310831+rimu@users.noreply.github.com> Date: Fri, 22 Dec 2023 14:05:39 +1300 Subject: [PATCH] refactoring and bug fixes --- app/activitypub/routes.py | 245 ++++++++----------------- app/activitypub/util.py | 131 ++++++++++++- app/community/routes.py | 3 +- app/community/util.py | 4 +- app/static/structure.css | 60 +++--- app/static/structure.scss | 56 ++++-- app/static/styles.css | 36 +++- app/static/styles.scss | 36 +++- app/templates/base.html | 2 +- app/templates/community/community.html | 8 +- app/templates/list_communities.html | 2 +- app/templates/post/_post_full.html | 17 +- app/templates/post/_post_teaser.html | 25 ++- 13 files changed, 374 insertions(+), 251 deletions(-) diff --git a/app/activitypub/routes.py b/app/activitypub/routes.py index 811122f5..df0a1f0b 100644 --- a/app/activitypub/routes.py +++ b/app/activitypub/routes.py @@ -13,7 +13,8 @@ from app.models import User, Community, CommunityJoinRequest, CommunityMember, C PostReply, Instance, PostVote, PostReplyVote, File, AllowedInstances, BannedInstances, utcnow from app.activitypub.util import public_key, users_total, active_half_year, active_month, local_posts, local_comments, \ post_to_activity, find_actor_or_create, default_context, instance_blocked, find_reply_parent, find_liked_object, \ - lemmy_site_data, instance_weight, is_activitypub_request + lemmy_site_data, instance_weight, is_activitypub_request, downvote_post_reply, downvote_post, upvote_post_reply, \ + upvote_post from app.utils import gibberish, get_setting, is_image_url, allowlist_html, html_to_markdown, render_template, \ domain_from_url, markdown_to_html, community_membership, ap_datetime import werkzeug.exceptions @@ -254,7 +255,7 @@ def community_profile(actor): @bp.route('/inbox', methods=['GET', 'POST']) def shared_inbox(): if request.method == 'POST': - # save all incoming data to aid in debugging and development + # save all incoming data to aid in debugging and development. Set result to 'success' if things go well activity_log = ActivityPubLog(direction='in', activity_json=request.data, result='failure') try: @@ -456,78 +457,86 @@ def shared_inbox(): db.session.commit() else: post_id, parent_comment_id, root_id = find_reply_parent(in_reply_to) - post_reply = PostReply(user_id=user.id, community_id=community.id, - post_id=post_id, parent_id=parent_comment_id, - root_id=root_id, - nsfw=community.nsfw, - nsfl=community.nsfl, - ap_id=request_json['object']['object']['id'], - ap_create_id=request_json['object']['id'], - ap_announce_id=request_json['id']) - if 'source' in request_json['object']['object'] and \ - request_json['object']['object']['source'][ - 'mediaType'] == 'text/markdown': - post_reply.body = request_json['object']['object']['source']['content'] - post_reply.body_html = markdown_to_html(post_reply.body) - elif 'content' in request_json['object']['object']: - post_reply.body_html = allowlist_html( - request_json['object']['object']['content']) - post_reply.body = html_to_markdown(post_reply.body_html) + if post_id or parent_comment_id or root_id: + post_reply = PostReply(user_id=user.id, community_id=community.id, + post_id=post_id, parent_id=parent_comment_id, + root_id=root_id, + nsfw=community.nsfw, + nsfl=community.nsfl, + ap_id=request_json['object']['object']['id'], + ap_create_id=request_json['object']['id'], + ap_announce_id=request_json['id']) + if 'source' in request_json['object']['object'] and \ + request_json['object']['object']['source'][ + 'mediaType'] == 'text/markdown': + post_reply.body = request_json['object']['object']['source']['content'] + post_reply.body_html = markdown_to_html(post_reply.body) + elif 'content' in request_json['object']['object']: + post_reply.body_html = allowlist_html( + request_json['object']['object']['content']) + post_reply.body = html_to_markdown(post_reply.body_html) - if post_reply is not None: - post = Post.query.get(post_id) - if post.comments_enabled: - db.session.add(post_reply) - community.post_reply_count += 1 - community.last_active = utcnow() - post.last_active = utcnow() - activity_log.result = 'success' - db.session.commit() - else: - activity_log.exception_message = 'Comments disabled' + if post_reply is not None: + post = Post.query.get(post_id) + if post.comments_enabled: + db.session.add(post_reply) + community.post_reply_count += 1 + community.last_active = utcnow() + post.last_active = utcnow() + activity_log.result = 'success' + db.session.commit() + else: + activity_log.exception_message = 'Comments disabled' + else: + activity_log.exception_message = 'Parent not found' else: activity_log.exception_message = 'Unacceptable type: ' + object_type - elif request_json['object']['type'] == 'Like' or request_json['object']['type'] == 'Dislike': + elif request_json['object']['type'] == 'Like': activity_log.activity_type = request_json['object']['type'] - vote_effect = 1.0 if request_json['object']['type'] == 'Like' else -1.0 - if vote_effect < 0 and get_setting('allow_dislike', True) is False: + user_ap_id = request_json['object']['actor'] + liked_ap_id = request_json['object']['object'] + user = find_actor_or_create(user_ap_id) + if user: + liked = find_liked_object(liked_ap_id) + # insert into voted table + if liked is None: + activity_log.exception_message = 'Liked object not found' + elif liked is not None and isinstance(liked, Post): + upvote_post(liked, user) + activity_log.result = 'success' + elif liked is not None and isinstance(liked, PostReply): + upvote_post_reply(liked, user) + activity_log.result = 'success' + else: + activity_log.exception_message = 'Could not detect type of like' + if activity_log.result == 'success': + ... # todo: recalculate 'hotness' of liked post/reply + # todo: if vote was on content in local community, federate the vote out to followers + elif request_json['object']['type'] == 'Dislike': + activity_log.activity_type = request_json['object']['type'] + if g.site.enable_downvotes is False: activity_log.exception_message = 'Dislike ignored because of allow_dislike setting' else: user_ap_id = request_json['object']['actor'] liked_ap_id = request_json['object']['object'] user = find_actor_or_create(user_ap_id) if user: - vote_weight = instance_weight(user.ap_domain) - liked = find_liked_object(liked_ap_id) + disliked = find_liked_object(liked_ap_id) # insert into voted table - if liked is None: + if disliked is None: activity_log.exception_message = 'Liked object not found' - elif liked is not None and isinstance(liked, Post): - existing_vote = PostVote.query.filter_by(user_id=user.id, post_id=liked.id).first() - if existing_vote: - existing_vote.effect = vote_effect * vote_weight - else: - vote = PostVote(user_id=user.id, author_id=liked.user_id, post_id=liked.id, - effect=vote_effect * vote_weight) - db.session.add(vote) - db.session.commit() + elif disliked is not None and isinstance(disliked, Post): + downvote_post(disliked, user) activity_log.result = 'success' - elif liked is not None and isinstance(liked, PostReply): - existing_vote = PostVote.query.filter_by(user_id=user.id, post_id=liked.id).first() - if existing_vote: - existing_vote.effect = vote_effect * vote_weight - else: - vote = PostReplyVote(user_id=user.id, author_id=liked.user_id, post_reply_id=liked.id, - effect=vote_effect * vote_weight) - db.session.add(vote) - db.session.commit() + elif disliked is not None and isinstance(disliked, PostReply): + downvote_post_reply(disliked, user) activity_log.result = 'success' else: activity_log.exception_message = 'Could not detect type of like' if activity_log.result == 'success': - ... # todo: recalculate 'hotness' of liked post/reply - # todo: if vote was on content in local community, federate the vote out to followers + ... # todo: recalculate 'hotness' of liked post/reply + # todo: if vote was on content in local community, federate the vote out to followers # Follow: remote user wants to join/follow one of our communities elif request_json['type'] == 'Follow': # Follow is when someone wants to join a community @@ -726,65 +735,15 @@ def shared_inbox(): target_ap_id = request_json['object'] post = None comment = None - effect = instance_weight(user.ap_domain) if '/comment/' in target_ap_id: comment = PostReply.query.filter_by(ap_id=target_ap_id).first() if '/post/' in target_ap_id: post = Post.query.filter_by(ap_id=target_ap_id).first() if user and post: - user.last_seen = utcnow() - existing_vote = PostVote.query.filter_by(user_id=user.id, post_id=post.id).first() - if not existing_vote: - post.up_votes += 1 - post.score += effect - vote = PostVote(user_id=user.id, post_id=post.id, author_id=post.author.id, - effect=effect) - post.author.reputation += effect - db.session.add(vote) - else: - # remove previous cast downvote - if existing_vote.effect < 0: - post.author.reputation -= existing_vote.effect - post.down_votes -= 1 - post.score -= existing_vote.effect - db.session.delete(existing_vote) - - # apply up vote - post.up_votes += 1 - post.score += effect - vote = PostVote(user_id=user.id, post_id=post.id, author_id=post.author.id, - effect=effect) - post.author.reputation += effect - db.session.add(vote) + upvote_post(post, user) activity_log.result = 'success' elif user and comment: - user.last_seen = utcnow() - existing_vote = PostReplyVote.query.filter_by(user_id=user.id, - post_reply_id=comment.id).first() - if not existing_vote: - comment.up_votes += 1 - comment.score += effect - vote = PostReplyVote(user_id=user.id, post_reply_id=comment.id, - author_id=comment.author.id, effect=effect) - comment.author.reputation += effect - db.session.add(vote) - else: - # remove previously cast downvote - if existing_vote.effect < 0: - comment.author.reputation -= existing_vote.effect - comment.down_votes -= 1 - comment.score -= existing_vote.effect - db.session.delete(existing_vote) - - # apply up vote - comment.up_votes += 1 - comment.score += effect - vote = PostReplyVote(user_id=user.id, post_reply_id=comment.id, - author_id=comment.author.id, effect=effect) - comment.author.reputation += effect - db.session.add(vote) - else: - pass # they have already upvoted this reply + upvote_post_reply(comment, user) activity_log.result = 'success' elif request_json['type'] == 'Dislike': # Downvote @@ -802,65 +761,10 @@ def shared_inbox(): if '/post/' in target_ap_id: post = Post.query.filter_by(ap_id=target_ap_id).first() if user and comment: - user.last_seen = utcnow() - existing_vote = PostReplyVote.query.filter_by(user_id=user.id, - post_reply_id=comment.id).first() - if not existing_vote: - effect = -1.0 - comment.down_votes += 1 - comment.score -= 1.0 - vote = PostReplyVote(user_id=user.id, post_reply_id=comment.id, - author_id=comment.author.id, effect=effect) - comment.author.reputation += effect - db.session.add(vote) - else: - # remove previously cast upvote - if existing_vote.effect > 0: - comment.author.reputation -= existing_vote.effect - comment.up_votes -= 1 - comment.score -= existing_vote.effect - db.session.delete(existing_vote) - - # apply down vote - effect = -1.0 - comment.down_votes += 1 - comment.score -= 1.0 - vote = PostReplyVote(user_id=user.id, post_reply_id=comment.id, - author_id=comment.author.id, effect=effect) - comment.author.reputation += effect - db.session.add(vote) - else: - pass # they have already downvoted this reply + downvote_post_reply(comment, user) activity_log.result = 'success' elif user and post: - user.last_seen = utcnow() - existing_vote = PostVote.query.filter_by(user_id=user.id, post_id=post.id).first() - if not existing_vote: - effect = -1.0 - post.down_votes += 1 - post.score -= 1.0 - vote = PostVote(user_id=user.id, post_id=post.id, author_id=post.author.id, - effect=effect) - post.author.reputation += effect - db.session.add(vote) - else: - # remove previously cast upvote - if existing_vote.effect > 0: - post.author.reputation -= existing_vote.effect - post.up_votes -= 1 - post.score -= existing_vote.effect - db.session.delete(existing_vote) - - # apply down vote - effect = -1.0 - post.down_votes += 1 - post.score -= 1.0 - vote = PostVote(user_id=user.id, post_id=post.id, author_id=post.author.id, - effect=effect) - post.author.reputation += effect - db.session.add(vote) - else: - pass # they have already downvoted this post + downvote_post(post, user) activity_log.result = 'success' else: activity_log.exception_message = 'Could not find user or content for vote' @@ -885,6 +789,8 @@ def shared_inbox(): return '' + + @bp.route('/c//outbox', methods=['GET']) def community_outbox(actor): actor = actor.strip() @@ -1003,3 +909,12 @@ def post_ap(post_id): return resp else: return show_post(post_id) + + +@bp.route('/activities//') +def activities_json(type, id): + activity = ActivityPubLog.query.filter_by(activity_id=f"https://{current_app.config['SERVER_NAME']}/activities/{type}/{id}").first() + if activity: + ... + else: + abort(404) diff --git a/app/activitypub/util.py b/app/activitypub/util.py index 59490162..adefa6cd 100644 --- a/app/activitypub/util.py +++ b/app/activitypub/util.py @@ -5,7 +5,8 @@ from typing import Union, Tuple from flask import current_app, request from sqlalchemy import text from app import db, cache, constants -from app.models import User, Post, Community, BannedInstances, File, PostReply, AllowedInstances, Instance, utcnow, Site +from app.models import User, Post, Community, BannedInstances, File, PostReply, AllowedInstances, Instance, utcnow, \ + Site, PostVote, PostReplyVote import time import base64 import requests @@ -439,12 +440,16 @@ def default_context(): def find_reply_parent(in_reply_to: str) -> Tuple[int, int, int]: if 'comment' in in_reply_to: parent_comment = PostReply.get_by_ap_id(in_reply_to) + if not parent_comment: + return (None, None, None) parent_comment_id = parent_comment.id post_id = parent_comment.post_id root_id = parent_comment.root_id elif 'post' in in_reply_to: parent_comment_id = None post = Post.get_by_ap_id(in_reply_to) + if not post: + return (None, None, None) post_id = post.id root_id = None else: @@ -460,6 +465,8 @@ def find_reply_parent(in_reply_to: str) -> Tuple[int, int, int]: parent_comment_id = parent_comment.id post_id = parent_comment.post_id root_id = parent_comment.root_id + else: + return (None, None, None) return post_id, parent_comment_id, root_id @@ -523,6 +530,128 @@ def is_activitypub_request(): return 'application/ld+json' in request.headers.get('Accept', '') or 'application/activity+json' in request.headers.get('Accept', '') +def downvote_post(post, user): + user.last_seen = utcnow() + existing_vote = PostVote.query.filter_by(user_id=user.id, post_id=post.id).first() + if not existing_vote: + effect = -1.0 + post.down_votes += 1 + post.score -= 1.0 + vote = PostVote(user_id=user.id, post_id=post.id, author_id=post.author.id, + effect=effect) + post.author.reputation += effect + db.session.add(vote) + else: + # remove previously cast upvote + if existing_vote.effect > 0: + post.author.reputation -= existing_vote.effect + post.up_votes -= 1 + post.score -= existing_vote.effect + db.session.delete(existing_vote) + + # apply down vote + effect = -1.0 + post.down_votes += 1 + post.score -= 1.0 + vote = PostVote(user_id=user.id, post_id=post.id, author_id=post.author.id, + effect=effect) + post.author.reputation += effect + db.session.add(vote) + else: + pass # they have already downvoted this post + + +def downvote_post_reply(comment, user): + user.last_seen = utcnow() + existing_vote = PostReplyVote.query.filter_by(user_id=user.id, + post_reply_id=comment.id).first() + if not existing_vote: + effect = -1.0 + comment.down_votes += 1 + comment.score -= 1.0 + vote = PostReplyVote(user_id=user.id, post_reply_id=comment.id, + author_id=comment.author.id, effect=effect) + comment.author.reputation += effect + db.session.add(vote) + else: + # remove previously cast upvote + if existing_vote.effect > 0: + comment.author.reputation -= existing_vote.effect + comment.up_votes -= 1 + comment.score -= existing_vote.effect + db.session.delete(existing_vote) + + # apply down vote + effect = -1.0 + comment.down_votes += 1 + comment.score -= 1.0 + vote = PostReplyVote(user_id=user.id, post_reply_id=comment.id, + author_id=comment.author.id, effect=effect) + comment.author.reputation += effect + db.session.add(vote) + else: + pass # they have already downvoted this reply + + +def upvote_post_reply(comment, user): + user.last_seen = utcnow() + effect = instance_weight(user.ap_domain) + existing_vote = PostReplyVote.query.filter_by(user_id=user.id, + post_reply_id=comment.id).first() + if not existing_vote: + comment.up_votes += 1 + comment.score += effect + vote = PostReplyVote(user_id=user.id, post_reply_id=comment.id, + author_id=comment.author.id, effect=effect) + comment.author.reputation += effect + db.session.add(vote) + else: + # remove previously cast downvote + if existing_vote.effect < 0: + comment.author.reputation -= existing_vote.effect + comment.down_votes -= 1 + comment.score -= existing_vote.effect + db.session.delete(existing_vote) + + # apply up vote + comment.up_votes += 1 + comment.score += effect + vote = PostReplyVote(user_id=user.id, post_reply_id=comment.id, + author_id=comment.author.id, effect=effect) + comment.author.reputation += effect + db.session.add(vote) + else: + pass # they have already upvoted this reply + + +def upvote_post(post, user): + user.last_seen = utcnow() + effect = instance_weight(user.ap_domain) + existing_vote = PostVote.query.filter_by(user_id=user.id, post_id=post.id).first() + if not existing_vote: + post.up_votes += 1 + post.score += effect + vote = PostVote(user_id=user.id, post_id=post.id, author_id=post.author.id, + effect=effect) + post.author.reputation += effect + db.session.add(vote) + else: + # remove previous cast downvote + if existing_vote.effect < 0: + post.author.reputation -= existing_vote.effect + post.down_votes -= 1 + post.score -= existing_vote.effect + db.session.delete(existing_vote) + + # apply up vote + post.up_votes += 1 + post.score += effect + vote = PostVote(user_id=user.id, post_id=post.id, author_id=post.author.id, + effect=effect) + post.author.reputation += effect + db.session.add(vote) + + def lemmy_site_data(): site = Site.query.get(1) data = { diff --git a/app/community/routes.py b/app/community/routes.py index eb3dfaa5..cf38b438 100644 --- a/app/community/routes.py +++ b/app/community/routes.py @@ -100,6 +100,7 @@ def show_community(community: Community): is_moderator = current_user.is_authenticated and any(mod.user_id == current_user.id for mod in mods) is_owner = current_user.is_authenticated and any( mod.user_id == current_user.id and mod.is_owner == True for mod in mods) + is_admin = current_user.is_authenticated and current_user.is_admin() if community.private_mods: mod_list = [] @@ -121,7 +122,7 @@ def show_community(community: Community): 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, + is_moderator=is_moderator, is_owner=is_owner, is_admin=is_admin, 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, diff --git a/app/community/util.py b/app/community/util.py index 71eefcdf..ffb1c543 100644 --- a/app/community/util.py +++ b/app/community/util.py @@ -101,7 +101,9 @@ def retrieve_mods_and_backfill_thread(community: Community, app): activities_processed += 1 if activities_processed >= 50: break - community.post_count = activities_processed # todo: figure out why this value is not being saved + c = Community.query.get(community.id) + c.post_count = activities_processed + c.last_active = utcnow() db.session.commit() diff --git a/app/static/structure.css b/app/static/structure.css index 32ab2041..572fe847 100644 --- a/app/static/structure.css +++ b/app/static/structure.css @@ -388,27 +388,36 @@ fieldset legend { background-position: center center; background-size: cover; border-radius: 5px; + height: 176px; +} +@media (min-width: 992px) { + .community_header { + height: 240px; + } } @media (min-width: 992px) { .community_header #breadcrumb_nav { padding-left: 20px; padding-top: 13px; } - .community_header #breadcrumb_nav .breadcrumb { - padding: 0; - margin-bottom: 0; - background-color: inherit; - } - .community_header #breadcrumb_nav .breadcrumb .breadcrumb-item { - color: white; - } - .community_header #breadcrumb_nav .breadcrumb .breadcrumb-item a { - color: white; - } - .community_header #breadcrumb_nav .breadcrumb .breadcrumb-item + .breadcrumb-item::before { - content: ">"; - color: white; - } +} +.community_header #breadcrumb_nav .breadcrumb { + background-color: rgba(0, 0, 0, 0.2); + display: inline-block; + padding: 5px 10px; + border-radius: 6px; + margin-bottom: 0; +} +.community_header #breadcrumb_nav .breadcrumb .breadcrumb-item { + color: white; + display: inline-block; +} +.community_header #breadcrumb_nav .breadcrumb .breadcrumb-item a { + color: white; +} +.community_header #breadcrumb_nav .breadcrumb .breadcrumb-item + .breadcrumb-item::before { + content: ">"; + color: white; } .community_header_no_background .community_icon, .community_header .community_icon { @@ -451,14 +460,22 @@ fieldset legend { text-decoration: none; } .post_list .post_teaser .thumbnail { + float: right; padding-left: 0; padding-right: 0; } .post_list .post_teaser .thumbnail img { - position: absolute; - right: 70px; - height: 70px; - margin-top: -47px; + height: 60px; + width: 60px; + border-radius: 5px; + object-fit: cover; + margin-left: 5px; +} +@media (min-width: 992px) { + .post_list .post_teaser .thumbnail img { + height: 70px; + width: 133px; + } } .url_thumbnail { @@ -498,8 +515,9 @@ fieldset legend { float: right; display: block; width: 55px; - padding: 5px; - padding-right: 0; + padding: 0 0 5px 5px; + line-height: 22px; + font-size: 14px; } .voting_buttons div { border: solid 1px #0071CE; diff --git a/app/static/structure.scss b/app/static/structure.scss index de5c42ca..bdf7957d 100644 --- a/app/static/structure.scss +++ b/app/static/structure.scss @@ -77,28 +77,35 @@ nav, etc which are used site-wide */ background-position: center center; background-size: cover; border-radius: 5px; + height: 176px; @include breakpoint(tablet) { - #breadcrumb_nav { + height: 240px; + } + + #breadcrumb_nav { + @include breakpoint(tablet) { padding-left: 20px; padding-top: 13px; + } + .breadcrumb { + background-color: rgba(0,0,0,0.2); + display: inline-block; + padding: 5px 10px; + border-radius: 6px; + margin-bottom: 0; - .breadcrumb { - padding: 0; - margin-bottom: 0; - background-color: inherit; - - .breadcrumb-item { + .breadcrumb-item { + color: white; + display: inline-block; + a { color: white; - a { - color: white; - } } + } - .breadcrumb-item + .breadcrumb-item::before { - content: ">"; - color: white; - } + .breadcrumb-item + .breadcrumb-item::before { + content: ">"; + color: white; } } } @@ -155,13 +162,21 @@ nav, etc which are used site-wide */ } .thumbnail { + float: right; padding-left: 0; padding-right: 0; + img { - position: absolute; - right: 70px; - height: 70px; - margin-top: -47px; + height: 60px; + width: 60px; + border-radius: 5px; + object-fit: cover; + margin-left: 5px; + @include breakpoint(tablet) { + height: 70px; + width: 133px; + + } } } @@ -215,8 +230,9 @@ nav, etc which are used site-wide */ float: right; display: block; width: 55px; - padding: 5px; - padding-right: 0; + padding: 0 0 5px 5px; + line-height: 22px; + font-size: 14px; div { border: solid 1px $primary-colour; diff --git a/app/static/styles.css b/app/static/styles.css index 7df1746e..48f48047 100644 --- a/app/static/styles.css +++ b/app/static/styles.css @@ -355,6 +355,16 @@ nav.navbar { background-color: #0071CE; } +#outer_container { + margin-top: -4px; +} +@media (min-width: 992px) { + #outer_container { + margin-top: 1rem; + padding-top: 0.25rem; + } +} + .card-title { font-size: 140%; } @@ -387,20 +397,36 @@ nav.navbar { padding-top: 8px; padding-bottom: 12px; } +.main_pane .url_thumbnail { + width: 120px; + height: auto; +} +.main_pane .url_thumbnail img { + width: 100%; +} .community_icon { - width: 30px; - height: auto; + width: 20vw; + height: 20vw; + max-width: 30px; + max-height: 30px; + min-width: 20px; + min-height: 20px; } .community_icon_big { - width: 120px; - height: auto; + width: 20vw; + height: 20vw; + max-width: 120px; + max-height: 120px; + min-width: 80px; + min-height: 80px; + object-fit: cover; } .bump_up { position: absolute; - top: 104px; + top: 115px; left: 26px; } diff --git a/app/static/styles.scss b/app/static/styles.scss index f12cfa89..7f47dee6 100644 --- a/app/static/styles.scss +++ b/app/static/styles.scss @@ -54,6 +54,14 @@ nav.navbar { } } +#outer_container { + margin-top: -4px; + @include breakpoint(tablet) { + margin-top: 1rem; + padding-top: 0.25rem; + } +} + .card-title { font-size: 140%; } @@ -85,21 +93,39 @@ nav.navbar { background-color: white; padding-top: 8px; padding-bottom: 12px; + + .url_thumbnail { + width: 120px; + height: auto; + + img { + width: 100%; + } + } } .community_icon { - width: 30px; - height: auto; + width: 20vw; + height: 20vw; + max-width: 30px; + max-height: 30px; + min-width: 20px; + min-height: 20px; } .community_icon_big { - width: 120px; - height: auto; + width: 20vw; + height: 20vw; + max-width: 120px; + max-height: 120px; + min-width: 80px; + min-height: 80px; + object-fit: cover; } .bump_up { position: absolute; - top: 104px; + top: 115px; left: 26px; } diff --git a/app/templates/base.html b/app/templates/base.html index 93a54928..6f1a3084 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -90,7 +90,7 @@ {% endblock %} {% block content %} -
+
{% with messages = get_flashed_messages(with_categories=True) %} {% if messages %} {% for category, message in messages %} diff --git a/app/templates/community/community.html b/app/templates/community/community.html index 7ca0e1ad..289239a3 100644 --- a/app/templates/community/community.html +++ b/app/templates/community/community.html @@ -5,7 +5,7 @@
{% if community.header_image() != '' %} -
+
- {% if is_moderator or current_user.is_admin() %} + {% if is_moderator or is_admin %}

{{ _('Community Settings') }}

diff --git a/app/templates/list_communities.html b/app/templates/list_communities.html index 3eb4e15d..3fbc1fe5 100644 --- a/app/templates/list_communities.html +++ b/app/templates/list_communities.html @@ -2,7 +2,7 @@ {% from 'bootstrap5/form.html' import render_form %} {% block app_content %} -
+
diff --git a/app/templates/post/_post_full.html b/app/templates/post/_post_full.html index 856fae1f..5d72c614 100644 --- a/app/templates/post/_post_full.html +++ b/app/templates/post/_post_full.html @@ -39,13 +39,13 @@
{% include "post/_post_voting_buttons.html" %}
+

{{ post.title }}

{% if post.type == POST_TYPE_LINK and post.image_id and not (post.url and 'youtube.com' in post.url) %}
{% endif %} -

{{ post.title }}

submitted {{ moment(post.posted_at).fromNow() }} by {{ render_username(post.author) }} {% if post.edited_at %} edited {{ moment(post.edited_at).fromNow() }}{% endif %} @@ -68,17 +68,8 @@ width="{{ post.image.thumbnail_width }}" height="{{ post.image.thumbnail_height }}" /> {% endif %} {% endif %} - - + {{ post.body_html|safe if post.body_html else '' }}

{% endif %}
- -{% if post.body_html %} -
-
- {{ post.body_html|safe }} -
-
-{% endif %} \ No newline at end of file diff --git a/app/templates/post/_post_teaser.html b/app/templates/post/_post_teaser.html index 2198c6ad..6e7ccf98 100644 --- a/app/templates/post/_post_teaser.html +++ b/app/templates/post/_post_teaser.html @@ -1,9 +1,19 @@