Browse Source

Mark notifications read on interact (can disable)

When a user interacts with a comment (by voting, replying, labeling, or
bookmarking), any unread notifications they have from that comment will
now be marked as read.

This behavior is on by default, but can be disabled in Settings if the
user would rather mark all notifications read manually.
merge-requests/68/head
Chad Birch 6 years ago
parent
commit
f29537adb7
  1. 32
      tildes/alembic/versions/fef2c9c9a186_user_add_interact_mark_notifications_.py
  2. 3
      tildes/tildes/models/user/user.py
  3. 4
      tildes/tildes/templates/notifications_unread.jinja2
  4. 23
      tildes/tildes/templates/settings.jinja2
  5. 56
      tildes/tildes/views/api/web/comment.py
  6. 16
      tildes/tildes/views/api/web/user.py

32
tildes/alembic/versions/fef2c9c9a186_user_add_interact_mark_notifications_.py

@ -0,0 +1,32 @@
"""User: add interact_mark_notifications_read
Revision ID: fef2c9c9a186
Revises: beaa57144e49
Create Date: 2019-04-01 13:21:38.441021
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = "fef2c9c9a186"
down_revision = "beaa57144e49"
branch_labels = None
depends_on = None
def upgrade():
op.add_column(
"users",
sa.Column(
"interact_mark_notifications_read",
sa.Boolean(),
server_default="true",
nullable=False,
),
)
def downgrade():
op.drop_column("users", "interact_mark_notifications_read")

3
tildes/tildes/models/user/user.py

@ -96,6 +96,9 @@ class User(DatabaseModel):
auto_mark_notifications_read: bool = Column( auto_mark_notifications_read: bool = Column(
Boolean, nullable=False, server_default="false" Boolean, nullable=False, server_default="false"
) )
interact_mark_notifications_read: bool = Column(
Boolean, nullable=False, server_default="true"
)
open_new_tab_external: bool = Column( open_new_tab_external: bool = Column(
Boolean, nullable=False, server_default="false" Boolean, nullable=False, server_default="false"
) )

4
tildes/tildes/templates/notifications_unread.jinja2

@ -68,6 +68,10 @@
{% endif %} {% endif %}
</div> </div>
{% endif %} {% endif %}
{% if request.user.interact_mark_notifications_read %}
<p class="text-secondary text-small">Comments will be automatically marked read when you take an action on them (such as voting or replying). They will not disappear from the unread page until reloading. If you prefer to always mark them manually, you can disable this behavior on <a href="/settings">the Settings page</a>.</p>
{% endif %}
{% else %} {% else %}
<p>No unread notifications.</p> <p>No unread notifications.</p>
<p><a href="/notifications">Go to previously read notifications</a></p> <p><a href="/notifications">Go to previously read notifications</a></p>

23
tildes/tildes/templates/settings.jinja2

@ -36,6 +36,28 @@
</button> </button>
</form> </form>
</li> </li>
<li>
<form
name="interact-mark-notifications-read"
autocomplete="off"
data-ic-patch-to="{{ request.route_url('ic_user', username=request.user.username) }}"
>
<div class="form-group">
<label class="form-checkbox">
<input
type="checkbox"
id="interact_mark_notifications_read"
name="interact_mark_notifications_read"
data-js-autosubmit-on-change
{% if request.user.interact_mark_notifications_read %}checked{% endif %}
>
<i class="form-icon"></i> Automatically mark notifications read when you interact with the source comment (by voting, labeling, replying, etc.)
</label>
</div>
</form>
</li>
<li> <li>
<form <form
name="auto-mark-notifications-read" name="auto-mark-notifications-read"
@ -56,6 +78,7 @@
</div> </div>
</form> </form>
</li> </li>
<li> <li>
<h4>Open links in new tabs</h4> <h4>Open links in new tabs</h4>
<form <form

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

@ -124,6 +124,14 @@ def post_comment_reply(request: Request, markdown: str) -> dict:
) )
request.db_session.add(notification) request.db_session.add(notification)
# mark any notifications from the parent comment read if interaction-marking enabled
if request.user.interact_mark_notifications_read:
request.query(CommentNotification).filter(
CommentNotification.user == request.user,
CommentNotification.comment == parent_comment,
CommentNotification.is_unread == True, # noqa
).update({"is_unread": False}, synchronize_session=False)
# commit and then re-query the new comment to get complete data # commit and then re-query the new comment to get complete data
request.tm.commit() request.tm.commit()
@ -205,6 +213,14 @@ def put_vote_comment(request: Request) -> dict:
new_vote = CommentVote(request.user, comment) new_vote = CommentVote(request.user, comment)
request.db_session.add(new_vote) request.db_session.add(new_vote)
# mark any notifications from the comment read if interaction-marking enabled
if request.user.interact_mark_notifications_read:
request.query(CommentNotification).filter(
CommentNotification.user == request.user,
CommentNotification.comment == comment,
CommentNotification.is_unread == True, # noqa
).update({"is_unread": False}, synchronize_session=False)
try: try:
# manually flush before attempting to commit, to avoid having all objects # manually flush before attempting to commit, to avoid having all objects
# detached from the session in case of an error # detached from the session in case of an error
@ -239,6 +255,14 @@ def delete_vote_comment(request: Request) -> dict:
CommentVote.comment == comment, CommentVote.user == request.user CommentVote.comment == comment, CommentVote.user == request.user
).delete(synchronize_session=False) ).delete(synchronize_session=False)
# mark any notifications from the comment read if interaction-marking enabled
if request.user.interact_mark_notifications_read:
request.query(CommentNotification).filter(
CommentNotification.user == request.user,
CommentNotification.comment == comment,
CommentNotification.is_unread == True, # noqa
).update({"is_unread": False}, synchronize_session=False)
# manually commit the transaction so triggers will execute # manually commit the transaction so triggers will execute
request.tm.commit() request.tm.commit()
@ -279,6 +303,14 @@ def put_label_comment(
label = CommentLabel(comment, request.user, name, weight, reason) label = CommentLabel(comment, request.user, name, weight, reason)
request.db_session.add(label) request.db_session.add(label)
# mark any notifications from the comment read if interaction-marking enabled
if request.user.interact_mark_notifications_read:
request.query(CommentNotification).filter(
CommentNotification.user == request.user,
CommentNotification.comment == comment,
CommentNotification.is_unread == True, # noqa
).update({"is_unread": False}, synchronize_session=False)
try: try:
# manually flush before attempting to commit, to avoid having all objects # manually flush before attempting to commit, to avoid having all objects
# detached from the session in case of an error # detached from the session in case of an error
@ -315,6 +347,14 @@ def delete_label_comment(request: Request, name: CommentLabelOption) -> Response
CommentLabel.label == name, CommentLabel.label == name,
).delete(synchronize_session=False) ).delete(synchronize_session=False)
# mark any notifications from the comment read if interaction-marking enabled
if request.user.interact_mark_notifications_read:
request.query(CommentNotification).filter(
CommentNotification.user == request.user,
CommentNotification.comment == comment,
CommentNotification.is_unread == True, # noqa
).update({"is_unread": False}, synchronize_session=False)
# commit and then re-query the comment to get complete data # commit and then re-query the comment to get complete data
request.tm.commit() request.tm.commit()
@ -410,6 +450,14 @@ def put_comment_bookmark(request: Request) -> dict:
bookmark = CommentBookmark(request.user, comment) bookmark = CommentBookmark(request.user, comment)
request.db_session.add(bookmark) request.db_session.add(bookmark)
# mark any notifications from the comment read if interaction-marking enabled
if request.user.interact_mark_notifications_read:
request.query(CommentNotification).filter(
CommentNotification.user == request.user,
CommentNotification.comment == comment,
CommentNotification.is_unread == True, # noqa
).update({"is_unread": False}, synchronize_session=False)
try: try:
# manually flush before attempting to commit, to avoid having all # manually flush before attempting to commit, to avoid having all
# objects detached from the session in case of an error # objects detached from the session in case of an error
@ -444,6 +492,14 @@ def delete_comment_bookmark(request: Request) -> dict:
CommentBookmark.user == request.user, CommentBookmark.comment == comment CommentBookmark.user == request.user, CommentBookmark.comment == comment
).delete(synchronize_session=False) ).delete(synchronize_session=False)
# mark any notifications from the comment read if interaction-marking enabled
if request.user.interact_mark_notifications_read:
request.query(CommentNotification).filter(
CommentNotification.user == request.user,
CommentNotification.comment == comment,
CommentNotification.is_unread == True, # noqa
).update({"is_unread": False}, synchronize_session=False)
# commit and then re-query the comment to get complete data # commit and then re-query the comment to get complete data
request.tm.commit() request.tm.commit()

16
tildes/tildes/views/api/web/user.py

@ -160,6 +160,22 @@ def patch_change_auto_mark_notifications(request: Request) -> Response:
return IC_NOOP return IC_NOOP
@ic_view_config(
route_name="user",
request_method="PATCH",
request_param="ic-trigger-name=interact-mark-notifications-read",
permission="change_interact_mark_notifications_read_setting",
)
def patch_change_interact_mark_notifications(request: Request) -> Response:
"""Change the user's "automatically mark notifications read on interact" setting."""
user = request.context
new_value = bool(request.params.get("interact_mark_notifications_read"))
user.interact_mark_notifications_read = new_value
return IC_NOOP
@ic_view_config( @ic_view_config(
route_name="user", route_name="user",
request_method="PATCH", request_method="PATCH",

Loading…
Cancel
Save