mirror of
https://codeberg.org/rimu/pyfedi
synced 2025-01-23 11:26:56 -08:00
inline commenting
This commit is contained in:
parent
468a083dfa
commit
fada29b70e
7 changed files with 209 additions and 7 deletions
|
@ -25,6 +25,7 @@ from app.models import Post, PostReply, \
|
||||||
Topic, User, Instance, NotificationSubscription, UserFollower, Poll, PollChoice, PollChoiceVote, PostBookmark, \
|
Topic, User, Instance, NotificationSubscription, UserFollower, Poll, PollChoice, PollChoiceVote, PostBookmark, \
|
||||||
PostReplyBookmark, CommunityBlock, File
|
PostReplyBookmark, CommunityBlock, File
|
||||||
from app.post import bp
|
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, \
|
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, \
|
shorten_string, markdown_to_text, gibberish, ap_datetime, return_304, \
|
||||||
request_etag_matches, ip_address, user_ip_banned, instance_banned, \
|
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)
|
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'])
|
@bp.route('/post/<int:post_id>/options_menu', methods=['GET'])
|
||||||
def post_options(post_id: int):
|
def post_options(post_id: int):
|
||||||
post = Post.query.get_or_404(post_id)
|
post = Post.query.get_or_404(post_id)
|
||||||
|
|
|
@ -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 '' }}"
|
<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>
|
width="{{ post.image.thumbnail_width }}" height="{{ post.image.thumbnail_height }}" loading="lazy" /></a>
|
||||||
</div>
|
</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 -%}
|
{% endif -%}
|
||||||
<div>{% if post.reports > 0 and current_user.is_authenticated and post.community.is_moderator(current_user) -%}
|
<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>
|
<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>
|
{% 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>
|
</div>
|
||||||
{% if post.type == POST_TYPE_IMAGE -%}
|
{% 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">
|
<div class="post_image">
|
||||||
{% if post.image_id -%}
|
{% if post.image_id -%}
|
||||||
{% if low_bandwidth -%}
|
{% if low_bandwidth -%}
|
||||||
|
@ -40,7 +39,7 @@
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
</div>
|
</div>
|
||||||
{% elif post.type == POST_TYPE_LINK -%}
|
{% 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>
|
<span class="fe fe-external"></span></a>
|
||||||
{% if post.domain.post_warning -%}
|
{% if post.domain.post_warning -%}
|
||||||
<span class="fe fe-warning red" title="{{ post.domain.post_warning }}"></span>
|
<span class="fe fe-warning red" title="{{ post.domain.post_warning }}"></span>
|
||||||
|
|
|
@ -68,7 +68,15 @@
|
||||||
<div class="post_replies_link">
|
<div class="post_replies_link">
|
||||||
{% if post_reply.post.comments_enabled -%}
|
{% if post_reply.post.comments_enabled -%}
|
||||||
{% if not post_reply.post.deleted and not post_reply.deleted -%}
|
{% 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 -%}
|
{% else -%}
|
||||||
<span class="fe fe-reply"></span> reply
|
<span class="fe fe-reply"></span> reply
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
|
@ -114,6 +122,7 @@
|
||||||
{% endif -%}
|
{% endif -%}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="reply_to_{{ post_reply.id }}" class="hidable"></div>
|
||||||
{% if not post_reply.author.indexable -%}<!--googleon all-->{% endif -%}
|
{% if not post_reply.author.indexable -%}<!--googleon all-->{% endif -%}
|
||||||
|
|
||||||
{% if children -%}
|
{% if children -%}
|
||||||
|
|
24
app/templates/post/add_reply_inline.html
Normal file
24
app/templates/post/add_reply_inline.html
Normal 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>
|
132
app/templates/post/add_reply_inline_result.html
Normal file
132
app/templates/post/add_reply_inline_result.html
Normal 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>
|
||||||
|
|
|
@ -1143,7 +1143,7 @@ def remove_tracking_from_link(url):
|
||||||
|
|
||||||
|
|
||||||
def show_ban_message():
|
def show_ban_message():
|
||||||
flash('You have been banned.', 'error')
|
flash(_('You have been banned.'), 'error')
|
||||||
logout_user()
|
logout_user()
|
||||||
resp = make_response(redirect(url_for('main.index')))
|
resp = make_response(redirect(url_for('main.index')))
|
||||||
resp.set_cookie('sesion', '17489047567495', expires=datetime(year=2099, month=12, day=30))
|
resp.set_cookie('sesion', '17489047567495', expires=datetime(year=2099, month=12, day=30))
|
||||||
|
|
|
@ -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).
|
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)
|
[Cross-posting](https://piefed.social/post/411644)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue