Browse Source

Add a delay to comment back-and-forths

merge-requests/106/head
Deimos 4 years ago
parent
commit
15ced1a750
  1. 1
      tildes/scss/modules/_btn.scss
  2. 58
      tildes/tildes/views/api/web/comment.py

1
tildes/scss/modules/_btn.scss

@ -101,6 +101,7 @@
// Status message (errors) should be on their own line as well
> .text-status-message {
min-width: 100%;
margin: 0.4rem;
}
}

58
tildes/tildes/views/api/web/comment.py

@ -3,6 +3,8 @@
"""Web API endpoints related to comments."""
from typing import Optional
from marshmallow.fields import Boolean
from pyramid.httpexceptions import HTTPUnprocessableEntity
from pyramid.request import Request
@ -20,6 +22,7 @@ from tildes.models.comment import (
CommentVote,
)
from tildes.models.log import LogComment
from tildes.models.user import User
from tildes.schemas.comment import CommentLabelSchema, CommentSchema
from tildes.views import IC_NOOP
from tildes.views.decorators import ic_view_config, rate_limit_view
@ -88,6 +91,13 @@ def post_toplevel_comment(request: Request, markdown: str) -> dict:
def post_comment_reply(request: Request, markdown: str) -> dict:
"""Post a reply to a comment with Intercooler."""
parent_comment = request.context
wait_mins = _reply_wait_minutes(request, request.user, parent_comment.user)
if wait_mins:
raise HTTPUnprocessableEntity(
f"You can't reply to this user yet. Please wait {wait_mins} minutes."
)
new_comment = Comment(
topic=parent_comment.topic,
author=request.user,
@ -138,6 +148,12 @@ def get_comment_contents(request: Request) -> dict:
)
def get_comment_reply(request: Request) -> dict:
"""Get the reply form for a comment with Intercooler."""
wait_mins = _reply_wait_minutes(request, request.user, request.context.user)
if wait_mins:
raise HTTPUnprocessableEntity(
f"You can't reply to this user yet. Please wait {wait_mins} minutes."
)
return {"parent_comment": request.context}
@ -451,3 +467,45 @@ def delete_comment_bookmark(request: Request) -> dict:
def get_comment_markdown_source(request: Request) -> dict:
"""Get the Markdown source for a comment with Intercooler."""
return {"post": request.context}
def _reply_wait_minutes(
request: Request, replying_user: User, replying_to_user: User
) -> Optional[int]:
"""Return how many mins replying_user needs to wait to respond to replying_to_user.
`replying_user` is the one that wants to be able to post a new reply. They can't do
so if there exists a back-and-forth between them and `replying_to_user` where the
most recent comment is within the time threshold.
That is, call the user trying to reply "A", and the user they're replying to "B".
If a sequence of comments exists that are written by users (B, A, B) and the last
comment is "too recent", this will return how many minutes they need to wait. If
there isn't any recent sequence like that, it will return None.
"""
threshold_minutes = 30
# if a "too recent" exchange exists, this will return a single value for how many
# minutes ago the final comment in the back-and-forth was posted
result = request.db_session.execute(
"""
select ceil(extract(epoch from now() - main.created_time) / 60)
from comments as main
inner join comments as parent on parent.comment_id = main.parent_comment_id
inner join comments as grandparent
on grandparent.comment_id = parent.parent_comment_id
where main.user_id = :replying_to_user_id
and parent.user_id = :replying_user_id
and grandparent.user_id = :replying_to_user_id
and main.created_time > now() - interval ':threshold_minutes minutes'""",
{
"replying_to_user_id": replying_to_user.user_id,
"replying_user_id": replying_user.user_id,
"threshold_minutes": threshold_minutes,
},
).fetchone()
if not result:
return None
return threshold_minutes - int(result[0])
Loading…
Cancel
Save