From f29537adb7a581158d90948d498fbac53edd35eb Mon Sep 17 00:00:00 2001 From: Chad Birch Date: Mon, 1 Apr 2019 08:13:50 -0600 Subject: [PATCH] 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. --- ...6_user_add_interact_mark_notifications_.py | 32 +++++++++++ tildes/tildes/models/user/user.py | 3 + .../templates/notifications_unread.jinja2 | 4 ++ tildes/tildes/templates/settings.jinja2 | 23 ++++++++ tildes/tildes/views/api/web/comment.py | 56 +++++++++++++++++++ tildes/tildes/views/api/web/user.py | 16 ++++++ 6 files changed, 134 insertions(+) create mode 100644 tildes/alembic/versions/fef2c9c9a186_user_add_interact_mark_notifications_.py diff --git a/tildes/alembic/versions/fef2c9c9a186_user_add_interact_mark_notifications_.py b/tildes/alembic/versions/fef2c9c9a186_user_add_interact_mark_notifications_.py new file mode 100644 index 0000000..8cbf1b7 --- /dev/null +++ b/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") diff --git a/tildes/tildes/models/user/user.py b/tildes/tildes/models/user/user.py index 7308e61..f3acfd3 100644 --- a/tildes/tildes/models/user/user.py +++ b/tildes/tildes/models/user/user.py @@ -96,6 +96,9 @@ class User(DatabaseModel): auto_mark_notifications_read: bool = Column( Boolean, nullable=False, server_default="false" ) + interact_mark_notifications_read: bool = Column( + Boolean, nullable=False, server_default="true" + ) open_new_tab_external: bool = Column( Boolean, nullable=False, server_default="false" ) diff --git a/tildes/tildes/templates/notifications_unread.jinja2 b/tildes/tildes/templates/notifications_unread.jinja2 index 012914c..428a13f 100644 --- a/tildes/tildes/templates/notifications_unread.jinja2 +++ b/tildes/tildes/templates/notifications_unread.jinja2 @@ -68,6 +68,10 @@ {% endif %} {% endif %} + + {% if request.user.interact_mark_notifications_read %} +

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 the Settings page.

+ {% endif %} {% else %}

No unread notifications.

Go to previously read notifications

diff --git a/tildes/tildes/templates/settings.jinja2 b/tildes/tildes/templates/settings.jinja2 index 4107bcb..5f5f8f5 100644 --- a/tildes/tildes/templates/settings.jinja2 +++ b/tildes/tildes/templates/settings.jinja2 @@ -36,6 +36,28 @@ + +
  • +
    +
    + +
    +
    +
  • +
  • +
  • Open links in new tabs

    dict: ) 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 request.tm.commit() @@ -205,6 +213,14 @@ def put_vote_comment(request: Request) -> dict: new_vote = CommentVote(request.user, comment) 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: # manually flush before attempting to commit, to avoid having all objects # 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 ).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 request.tm.commit() @@ -279,6 +303,14 @@ def put_label_comment( label = CommentLabel(comment, request.user, name, weight, reason) 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: # manually flush before attempting to commit, to avoid having all objects # 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, ).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 request.tm.commit() @@ -410,6 +450,14 @@ def put_comment_bookmark(request: Request) -> dict: bookmark = CommentBookmark(request.user, comment) 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: # manually flush before attempting to commit, to avoid having all # 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 ).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 request.tm.commit() diff --git a/tildes/tildes/views/api/web/user.py b/tildes/tildes/views/api/web/user.py index 4686c96..b4df52a 100644 --- a/tildes/tildes/views/api/web/user.py +++ b/tildes/tildes/views/api/web/user.py @@ -160,6 +160,22 @@ def patch_change_auto_mark_notifications(request: Request) -> Response: 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( route_name="user", request_method="PATCH",