inline commenting

This commit is contained in:
rimu 2025-01-17 15:50:37 +13:00
parent 468a083dfa
commit fada29b70e
7 changed files with 209 additions and 7 deletions

View file

@ -25,6 +25,7 @@ from app.models import Post, PostReply, \
Topic, User, Instance, NotificationSubscription, UserFollower, Poll, PollChoice, PollChoiceVote, PostBookmark, \
PostReplyBookmark, CommunityBlock, File
from app.post import bp
from app.shared.tasks import task_selector
from app.utils import get_setting, render_template, allowlist_html, markdown_to_html, validation_required, \
shorten_string, markdown_to_text, gibberish, ap_datetime, return_304, \
request_etag_matches, ip_address, user_ip_banned, instance_banned, \
@ -473,6 +474,43 @@ def add_reply(post_id: int, comment_id: int):
inoculation=inoculation[randint(0, len(inoculation) - 1)] if g.site.show_inoculation_block else None)
@bp.route('/post/<int:post_id>/comment/<int:comment_id>/reply_inline', methods=['GET', 'POST'])
@login_required
def add_reply_inline(post_id: int, comment_id: int):
if current_user.banned or current_user.ban_comments:
return _('You have been banned.')
post = Post.query.get_or_404(post_id)
if not post.comments_enabled:
return _('Comments have been disabled.')
in_reply_to = PostReply.query.get_or_404(comment_id)
if in_reply_to.author.has_blocked_user(current_user.id):
return _('You cannot reply to %(name)s', name=in_reply_to.author.display_name())
if request.method == 'GET':
return render_template('post/add_reply_inline.html', post_id=post_id, comment_id=comment_id, languages=languages_for_form())
else:
content = request.form.get('body', '').strip()
language_id = int(request.form.get('language_id'))
if content == '':
abort(406) # stop htmx from replacing the form with anything
reply = PostReply.new(current_user, post, in_reply_to=in_reply_to, body=piefed_markdown_to_lemmy_markdown(content),
body_html=markdown_to_html(content), notify_author=True,
language_id=language_id)
current_user.language_id = language_id
reply.ap_id = reply.profile_id()
db.session.commit()
# Federate the reply
task_selector('make_reply', user_id=current_user.id, reply_id=reply.id, parent_id=in_reply_to.id)
return render_template('post/add_reply_inline_result.html', post_reply=reply)
@bp.route('/post/<int:post_id>/options_menu', methods=['GET'])
def post_options(post_id: int):
post = Post.query.get_or_404(post_id)

View file

@ -10,9 +10,6 @@
<a href="{{ post.url }}" target="_blank" rel="nofollow ugc" class="post_link"><img src="{{ post.image.thumbnail_url() }}" alt="{{ post.image.alt_text if post.image.alt_text else '' }}"
width="{{ post.image.thumbnail_width }}" height="{{ post.image.thumbnail_height }}" loading="lazy" /></a>
</div>
{% elif post.type == POST_TYPE_IMAGE and post.url -%}
<p><a href="{{ post.url }}" rel="nofollow ugc" target="_blank" aria-label="Go to image">{{ post.url|shorten_url }}
<span class="fe fe-external"></span></a></p>
{% endif -%}
<div>{% if post.reports > 0 and current_user.is_authenticated and post.community.is_moderator(current_user) -%}
<span class="red fe fe-report" title="{{ _('Reported. Check post for issues.') }}"></span>
@ -21,6 +18,8 @@
{% if post.edited_at -%} edited <time datetime="{{ arrow.get(post.posted_at).format('YYYY-MM-DD HH:mm:ss ZZ') }}" title="{{ arrow.get(post.posted_at).format('YYYY-MM-DD HH:mm:ss ZZ') }}">{{ arrow.get(post.edited_at).humanize(locale=locale) }}</time>{% endif -%}</small>
</div>
{% if post.type == POST_TYPE_IMAGE -%}
<p class="mt-2"><a href="{{ post.url }}" rel="nofollow ugc" target="_blank" aria-label="Go to image">{{ post.url|shorten_url }}
<span class="fe fe-external"></span></a></p>
<div class="post_image">
{% if post.image_id -%}
{% if low_bandwidth -%}
@ -40,7 +39,7 @@
{% endif -%}
</div>
{% elif post.type == POST_TYPE_LINK -%}
<p><a href="{{ post.url }}" rel="nofollow ugc" target="_blank" class="post_link" aria-label="Go to post url">{{ post.url|shorten_url }}
<p class="mt-2"><a href="{{ post.url }}" rel="nofollow ugc" target="_blank" class="post_link" aria-label="Go to post url">{{ post.url|shorten_url }}
<span class="fe fe-external"></span></a>
{% if post.domain.post_warning -%}
<span class="fe fe-warning red" title="{{ post.domain.post_warning }}"></span>

View file

@ -68,7 +68,15 @@
<div class="post_replies_link">
{% if post_reply.post.comments_enabled -%}
{% if not post_reply.post.deleted and not post_reply.deleted -%}
<a href="{{ url_for('post.add_reply', post_id=post_reply.post.id, comment_id=post_reply.id) }}" rel="nofollow noindex"><span class="fe fe-reply"></span> reply</a>
{% if current_user.is_authenticated -%}
<a href="{{ url_for('post.add_reply', post_id=post_reply.post.id, comment_id=post_reply.id) }}"
hx-get="{{ url_for('post.add_reply_inline', post_id=post_reply.post.id, comment_id=post_reply.id) }}"
hx-target="#reply_to_{{ post_reply.id }}"
hx-swap="innerHTML"
rel="nofollow noindex"><span class="fe fe-reply"></span> reply</a>
{% else -%}
<a href="{{ url_for('auth.login', next='/post/' + str(post_reply.post.id)) }}"><span class="fe fe-reply"></span> reply</a>
{% endif -%}
{% else -%}
<span class="fe fe-reply"></span> reply
{% endif -%}
@ -114,6 +122,7 @@
{% endif -%}
</div>
</div>
<div id="reply_to_{{ post_reply.id }}" class="hidable"></div>
{% if not post_reply.author.indexable -%}<!--googleon all-->{% endif -%}
{% if children -%}

View file

@ -0,0 +1,24 @@
<form>
<div class="row">
<div class="col">
<textarea name="body" class="form-control" rows="5" autofocus="autofocus" required="required" aria-required="true"></textarea>
</div>
</div>
<div class="row mb-3">
<div class="col-8">
<a href="#"
class="btn btn-primary mt-2"
hx-post="{{ url_for('post.add_reply_inline', post_id=post_id, comment_id=comment_id) }}"
hx-target="#reply_to_{{ comment_id }}"
hx-swap="outerHTML"
>{{ _('Comment') }}</a>
</div>
<div class="col-4 text-right">
<select name="language_id" class="form-select mt-2">
{% for language in languages %}
<option value="{{ language[0] }}">{{ language[1] }}</option>
{% endfor %}
</select>
</div>
</div>
</form>

View file

@ -0,0 +1,132 @@
{% macro render_username(user, add_domain=True, htmx_redirect_back_to=None) -%}
<span class="render_username">
{% if user.deleted -%}
{% if current_user.is_authenticated and current_user.is_admin() -%}
<a href="/u/{{ user.link() }}" title="{{ user.ap_id if user.ap_id != none else user.user_name }}" aria-label="{{ _('Author') }}">[deleted]</a>
{% else -%}
[deleted]
{% endif -%}
{% else -%}
<a href="/u/{{ user.link() }}" aria-label="{{ _('Author') }}" class="author_link" title="">
{% if user.avatar_id and not low_bandwidth and not collapsed -%}
<img src="{{ user.avatar_thumbnail() }}" alt="" loading="lazy" />
{% endif -%}
{{ user.display_name() }}{% if add_domain and not user.is_local() %}<span class="text-muted">@{{ user.ap_domain }}</span>{% endif %}
</a>
<div class="d-none user_preview" id="preview_{{ user.id }}"
{% if htmx_redirect_back_to -%}
hx-get="{{ url_for('user.user_preview', user_id=user.id, return_to=htmx_redirect_back_to) }}"
{% else %}
hx-get="{{ url_for('user.user_preview', user_id=user.id) }}"
{% endif %}
hx-trigger="intersect once"
hx-target="this"
hx-swap="innerHTML"
></div>
{% if user.created_recently() -%}
<span class="fe fe-new-account" title="New account"> </span>
{% endif -%}
{% if user.bot -%}
<span class="fe fe-bot-account" title="Bot account"> </span>
{% endif -%}
{% if user.id != current_user.id -%}
{% if user.reputation < -10 -%}
<span class="fe fe-warning red" title="Very low reputation. Beware."> </span>
<span class="fe fe-warning red" title="Very low reputation. Beware!"> </span>
{% elif user.reputation < 0 -%}
<span class="fe fe-warning orangered" title="Low reputation."> </span>
{% endif -%}
{% endif -%}
{% if current_user.is_authenticated -%}
{% set user_note = user.get_note(current_user) %}
{% if user_note -%}
<span class="user_note" title="{{ _('User note: %(note)s', note=user_note) }}">[{{ user_note | truncate(12, True) }}]</span>
{% endif -%}
{% endif -%}
{% endif -%}
</span>
{% endmacro -%}
{% set collapsed = false -%}
<div class="replies hidable" role="group" aria-live="polite">
<div class="container comment{% if post_reply.score and post_reply.score <= -10 %} low_score{% endif %}{% if post_reply.author.id == post_reply.post.author.id %} original_poster{% endif %}" id="comment_{{ post_reply.id }}"{% if post_reply.language_id and post_reply.language.code != 'en' %} lang="{{ post_reply.language.code }}"{% endif %} aria-level="{{ post_reply.depth+1 }}" role="treeitem">
<div class="row">
<div class="col-auto comment_author">
<span class="visually-hidden">by</span>
{{ render_username(post_reply.author, htmx_redirect_back_to=request.path + '#comment_' + str(post_reply.id)) }}
{% if post_reply.author.id == post_reply.post.author.id -%}
<span title="Submitter of original post" aria-label="{{ _('Post creator') }}" class="small"> [OP]</span>
{% endif -%}
</div>
<div class="col-auto text-muted small pt-05">
<time datetime="{{ arrow.get(post_reply.posted_at).format('YYYY-MM-DD HH:mm:ss ZZ') }}" title="{{ arrow.get(post_reply.posted_at).format('YYYY-MM-DD HH:mm:ss ZZ') }}">{{ arrow.get(post_reply.posted_at).humanize(locale=locale) }}</time>{% if post_reply.edited_at -%}, edited <time datetime="{{ arrow.get(post_reply.posted_at) }}" title="{{ arrow.get(post_reply.posted_at) }}">{{ arrow.get(post_reply.edited_at).humanize(locale=locale) }}</time>{% endif -%}
</div>
<div class="col-auto">
{% if post_reply.reports and current_user.is_authenticated and post_reply.post.community.is_moderator(current_user) -%}
<span class="red fe fe-report" title="{{ _('Reported. Check comment for issues.') }}"></span>
{% endif -%}
</div>
<div class="col-auto">
{% if post_reply.deleted -%}
<span class="red fe fe-delete" title="{{ _('Comment deleted') }}"></span>
{% endif -%}
</div>
<div class="col-auto">
<a class="unhide" href="#"><span class="fe fe-expand"></span></a>
</div>
</div>
<div class="row comment_body hidable{% if post_reply.reports and current_user.is_authenticated and post_reply.post.community.is_moderator(current_user) %} reported{% endif %}">
<div class="col-12">
{{ post_reply.body_html | community_links | person_links | safe }}
</div>
</div>
<div class="comment_actions hidable">
<div class="post_replies_link">
<a href="{{ url_for('post.add_reply', post_id=post_reply.post.id, comment_id=post_reply.id) }}"
hx-get="{{ url_for('post.add_reply_inline', post_id=post_reply.post.id, comment_id=post_reply.id) }}"
hx-target="#reply_to_{{ post_reply.id }}"
hx-swap="innerHTML"
rel="nofollow noindex"><span class="fe fe-reply"></span> reply</a>
</div>
<div class="voting_buttons_new">
{% with comment=post_reply, community=post_reply.post.community -%}
{% include "post/_comment_voting_buttons.html" -%}
{% endwith -%}
</div>
<div class="hide_button">
{% if collapsed -%}
<a href='#' class=""><span class="fe fe-expand"></span></a>
{% else -%}
<a href='#' class=""><span class="fe fe-collapse"></span></a>
{% endif -%}
</div>
<div class="notify_toggle pull-right">
{% if current_user.is_authenticated and current_user.verified -%}
{% with comment=dict(comment=post_reply) -%}
{% include "post/_reply_notification_toggle.html" -%}
{% endwith -%}
{% endif -%}
</div>
<div class="comment_actions_link">
<div class="dropdown">
<a
href="{{ url_for('post.post_reply_options', post_id=post_reply.post.id, comment_id=post_reply.id) if low_bandwidth else '#' }}"
data-bs-toggle="dropdown" rel="nofollow noindex"
aria-label="{{ _('Comment options') }}">
<span class="fe fe-options" title="Options"> </span>
</a>
<ul class="dropdown-menu" style="width: 320px">
<div
hx-get="{{ url_for('post.post_reply_options', post_id=post_reply.post.id, comment_id=post_reply.id) }}"
hx-trigger="intersect once"
hx-target="this"
hx-swap="outerHTML"
></div>
</ul>
</div>
</div>
</div>
<div id="reply_to_{{ post_reply.id }}" class="hidable"></div>
</div>
</div>

View file

@ -1143,7 +1143,7 @@ def remove_tracking_from_link(url):
def show_ban_message():
flash('You have been banned.', 'error')
flash(_('You have been banned.'), 'error')
logout_user()
resp = make_response(redirect(url_for('main.index')))
resp.set_cookie('sesion', '17489047567495', expires=datetime(year=2099, month=12, day=30))

View file

@ -2,7 +2,7 @@
The following are our goals for 2025. We brainstormed ideas then [ranked them by voting](https://piefed.social/c/piefed_2025?sort=top).
[Inline commenting](https://piefed.social/post/411646)
[Inline commenting](https://piefed.social/post/411646)
[Cross-posting](https://piefed.social/post/411644)