diff --git a/tildes/tildes/routes.py b/tildes/tildes/routes.py index 38b025e..ae2b7a2 100644 --- a/tildes/tildes/routes.py +++ b/tildes/tildes/routes.py @@ -183,6 +183,11 @@ def add_intercooler_routes(config: Configurator) -> None: '/user/{username}/default_listing_options', factory=user_by_username, ) + add_ic_route( + 'mark_notifications_read_before', + '/notifications/{last_notification_seen}/mark_read_before', + factory=LoggedInFactory, + ) class LoggedInFactory: diff --git a/tildes/tildes/templates/notifications_unread.jinja2 b/tildes/tildes/templates/notifications_unread.jinja2 index b580f1f..a29f248 100644 --- a/tildes/tildes/templates/notifications_unread.jinja2 +++ b/tildes/tildes/templates/notifications_unread.jinja2 @@ -5,7 +5,19 @@ {% block title %}Unread notifications{% endblock %} -{% block main_heading %}Unread notifications{% endblock %} +{% block main_heading %}Unread notifications + {% if notifications and not request.user.auto_mark_notifications_read %} + + {% endif %} + +{% endblock %} {% block content %} {% if notifications %} diff --git a/tildes/tildes/views/api/web/notifications.py b/tildes/tildes/views/api/web/notifications.py new file mode 100644 index 0000000..28c611d --- /dev/null +++ b/tildes/tildes/views/api/web/notifications.py @@ -0,0 +1,73 @@ +"""Web API endpoints related to comment notifications.""" + +from datetime import datetime + +from marshmallow.fields import DateTime +from pyramid.request import Request +from pyramid.response import Response +from sqlalchemy.dialects.postgresql import insert +from webargs.pyramidparser import use_kwargs +from zope.sqlalchemy import mark_changed + +from tildes.lib.datetime import utc_now +from tildes.models.comment import CommentNotification +from tildes.models.topic import TopicVisit +from tildes.views.decorators import ic_view_config + + +@ic_view_config( + route_name='mark_notifications_read_before', request_method='PUT', +) +@use_kwargs( + {'last_notification_seen': DateTime(required=True)}, + locations=('matchdict',), +) +def put_notifications_read_before( + request: Request, + last_notification_seen: datetime, +) -> Response: + """Clear all user comment notifications before the latest displayed.""" + notifications = ( + request.query(CommentNotification) + .join_all_relationships() + .filter( + CommentNotification.user == request.user, + CommentNotification.is_unread == True, # noqa + CommentNotification.created_time <= last_notification_seen, + ) + .all() + ) + + # If the user has the "track comment visits" feature enabled, we want to + # increment the number of comments they've seen in the thread that the + # comment came from, so that they don't *both* get a notification as well + # as have the thread highlight with "(1 new)". This should only happen if + # their last visit was before the comment was posted, however. + # Below, this is implemented as a INSERT ... ON CONFLICT DO UPDATE so that + # it will insert a new topic visit with 1 comment if they didn't previously + # have one at all. + if request.user.track_comment_visits: + for notification in notifications: + statement = ( + insert(TopicVisit.__table__) + .values( + user_id=request.user.user_id, + topic_id=notification.comment.topic_id, + visit_time=utc_now(), + num_comments=1, + ) + .on_conflict_do_update( + constraint=TopicVisit.__table__.primary_key, + set_={'num_comments': TopicVisit.num_comments + 1}, + where=TopicVisit.visit_time < + notification.comment.created_time, + ) + ) + + request.db_session.execute(statement) + mark_changed(request.db_session) + + for notification in notifications: + notification.is_unread = False + + return Response('Your comment notifications have been cleared.')