Browse Source

#417: Updated notifications UI

Rafał Pitoń 10 years ago
parent
commit
e756c73ed7

+ 1 - 0
misago/notifications/api.py

@@ -38,6 +38,7 @@ def notify_user(user, message, url, trigger, formats=None, sender=None,
     user.new_notifications = F('new_notifications') + 1
     user.new_notifications = F('new_notifications') + 1
     if update_user:
     if update_user:
         user.save(update_fields=['new_notifications'])
         user.save(update_fields=['new_notifications'])
+    return new_notification
 
 
 
 
 def read_user_notification(user, trigger, atomic=True):
 def read_user_notification(user, trigger, atomic=True):

+ 65 - 20
misago/notifications/tests/test_views.py

@@ -13,15 +13,16 @@ class NotificationViewsTests(AuthenticatedUserTestCase):
         super(NotificationViewsTests, self).setUp()
         super(NotificationViewsTests, self).setUp()
 
 
     def notify_user(self):
     def notify_user(self):
-        notify_user(self.user,
-                    "Test notify %(token)s",
-                    "/users/",
-                    "test",
-                    {'token': 'Bob'},
-                    self.user)
+        notification = notify_user(self.user,
+                                   "Test notify %(token)s",
+                                   "/users/",
+                                   "test",
+                                   {'token': 'Bob'},
+                                   self.user)
         self.user = get_user_model().objects.get(id=self.user.id)
         self.user = get_user_model().objects.get(id=self.user.id)
+        return notification
 
 
-    def test_get(self):
+    def test_list(self):
         """get request to list renders list"""
         """get request to list renders list"""
         response = self.client.get(self.view_link)
         response = self.client.get(self.view_link)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
@@ -33,36 +34,80 @@ class NotificationViewsTests(AuthenticatedUserTestCase):
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn("Test notify <strong>Bob</strong>", response.content)
         self.assertIn("Test notify <strong>Bob</strong>", response.content)
 
 
-    def test_post(self):
-        """post request to list sets all notifications as read"""
+    def test_list_ajax(self):
+        """get ajax to list renders list"""
+        response = self.client.get(self.view_link, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+        self.assertIn("have any new notifications", response.content)
+
         self.notify_user()
         self.notify_user()
 
 
-        response = self.client.post(self.view_link)
+        response = self.client.get(self.view_link, **self.ajax_header)
+        self.assertEqual(response.status_code, 200)
+        self.assertIn("Test notify <strong>Bob</strong>", response.content)
+
+    def test_read_one(self):
+        """read_notification POST reads one notification"""
+        self.notify_user()
+        notification = self.notify_user()
+        self.notify_user()
+
+        response = self.client.post(self.view_link, data={
+            'notification': notification.pk,
+        })
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
 
 
         response = self.client.get(self.view_link)
         response = self.client.get(self.view_link)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
 
 
         self.reload_user()
         self.reload_user()
-        self.assertEqual(self.user.new_notifications, 0)
+        self.assertEqual(self.user.new_notifications, 2)
 
 
-    def test_get_ajax(self):
-        """get ajax to list renders list"""
-        response = self.client.get(self.view_link, **self.ajax_header)
+        notification = self.user.notifications.get(id=notification.pk)
+        self.assertFalse(notification.is_new)
+
+    def test_read_one_ajax(self):
+        """read_notification ajax POST reads one notification"""
+        self.notify_user()
+        notification = self.notify_user()
+        self.notify_user()
+
+        response = self.client.post(self.view_link, data={
+            'notification': notification.pk,
+        },**self.ajax_header)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
-        self.assertIn("have any new notifications", response.content)
 
 
+        response = self.client.get(self.view_link)
+        self.assertEqual(response.status_code, 200)
+
+        self.reload_user()
+        self.assertEqual(self.user.new_notifications, 2)
+
+        notification = self.user.notifications.get(id=notification.pk)
+        self.assertFalse(notification.is_new)
+
+    def test_read_all(self):
+        """read_all POST request to list sets all notifications as read"""
         self.notify_user()
         self.notify_user()
 
 
-        response = self.client.get(self.view_link, **self.ajax_header)
+        response = self.client.post(self.view_link, data={
+            'read-all': True,
+        })
+        self.assertEqual(response.status_code, 302)
+
+        response = self.client.get(self.view_link)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
-        self.assertIn("Test notify <strong>Bob</strong>", response.content)
 
 
-    def test_post_ajax(self):
-        """post ajax to list sets all notifications as read"""
+        self.reload_user()
+        self.assertEqual(self.user.new_notifications, 0)
+
+    def test_read_all_ajax(self):
+        """real_all POST ajax to list sets all notifications as read"""
         self.notify_user()
         self.notify_user()
 
 
-        response = self.client.post(self.view_link, **self.ajax_header)
+        response = self.client.post(self.view_link, data={
+            'read-all': True,
+        },**self.ajax_header)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
 
 
         response = self.client.get(self.view_link)
         response = self.client.get(self.view_link)

+ 41 - 3
misago/notifications/views.py

@@ -9,14 +9,18 @@ from misago.users.decorators import deny_guests
 
 
 from misago.notifications import (read_all_user_alerts,
 from misago.notifications import (read_all_user_alerts,
                                   assert_real_new_notifications_count)
                                   assert_real_new_notifications_count)
+from misago.notifications.models import Notification
 
 
 
 
 @deny_guests
 @deny_guests
 def notifications(request):
 def notifications(request):
     if request.method == 'POST':
     if request.method == 'POST':
-        read_all(request)
-        if not request.is_ajax():
-            return redirect('misago:notifications')
+        if 'read-all' in request.POST:
+            read_all(request)
+            if not request.is_ajax():
+                return redirect('misago:notifications')
+        if request.POST.get('notification'):
+            return read_notification(request)
     else:
     else:
         assert_real_new_notifications_count(request.user)
         assert_real_new_notifications_count(request.user)
 
 
@@ -53,6 +57,40 @@ def read_all(request):
     read_all_user_alerts(request.user)
     read_all_user_alerts(request.user)
 
 
 
 
+def read_notification(request):
+    try:
+        queryset = request.user.notifications
+        notification = queryset.get(id=request.POST['notification'])
+
+        if notification.is_new:
+            is_changed = True
+            with atomic():
+                notification.is_new = False
+                notification.save(update_fields=['is_new'])
+                assert_real_new_notifications_count(request.user)
+        else:
+            is_changed = False
+
+        if request.is_ajax():
+            return JsonResponse({
+                'is_error': False,
+                'is_changed': is_changed
+            })
+        else:
+            messages.success(request, _("Notification was marked as read."))
+            return redirect('misago:notifications')
+    except Notification.DoesNotExist:
+        message = _("Specified notification could not be found.")
+        if request.is_ajax():
+            return JsonResponse({
+                'is_error': True,
+                'message': message,
+            })
+        else:
+            messages.error(request, message)
+            return redirect('misago:notifications')
+
+
 @uiview('misago_notifications')
 @uiview('misago_notifications')
 @deny_guests
 @deny_guests
 def event_sender(request, resolver_match):
 def event_sender(request, resolver_match):

+ 9 - 1
misago/static/misago/css/misago/notifications.less

@@ -107,6 +107,7 @@
 
 
     .state-icon {
     .state-icon {
       float: left;
       float: left;
+      margin-top: 2px;
       margin-right: @line-height-computed / 2;
       margin-right: @line-height-computed / 2;
 
 
       color: @state-default;
       color: @state-default;
@@ -115,6 +116,8 @@
 
 
     .message {
     .message {
       float: left;
       float: left;
+      margin-top: 2px;
+
       font-size: @font-size-large;
       font-size: @font-size-large;
 
 
       &:link, &:active, &:visited {
       &:link, &:active, &:visited {
@@ -135,12 +138,17 @@
     }
     }
 
 
     footer {
     footer {
-      margin-top: 3px;
+      margin-top: 5px;
       float: right;
       float: right;
 
 
       a {
       a {
         margin-right: @line-height-computed / 3;
         margin-right: @line-height-computed / 3;
       }
       }
     }
     }
+
+    .read-form {
+      float: right;
+      margin-left: @line-height-computed;
+    }
   }
   }
 }
 }

+ 1 - 0
misago/static/misago/js/misago-notifications.js

@@ -20,6 +20,7 @@ $(function() {
         $badge.fadeOut();
         $badge.fadeOut();
     }
     }
     $link.attr("title", data.message);
     $link.attr("title", data.message);
+    $.misago_dom().changed();
 
 
     if (ajax_cache != null && data.count != ajax_cache.count) {
     if (ajax_cache != null && data.count != ajax_cache.count) {
       ajax_cache = null;
       ajax_cache = null;

+ 3 - 0
misago/static/misago/js/misago-tooltips.js

@@ -5,6 +5,9 @@ $(function() {
     $('.tooltip-bottom').tooltip({container: 'body', placement: 'bottom'});
     $('.tooltip-bottom').tooltip({container: 'body', placement: 'bottom'});
     $('.tooltip-left').tooltip({container: 'body', placement: 'left'});
     $('.tooltip-left').tooltip({container: 'body', placement: 'left'});
     $('.tooltip-right').tooltip({container: 'body', placement: 'right'});
     $('.tooltip-right').tooltip({container: 'body', placement: 'right'});
+    $('.tooltip-top, .tooltip-bottom, .tooltip-left, .tooltip-right').each(function() {
+      $(this).tooltip('fixTitle');
+    });
   });
   });
 });
 });
 
 

+ 1 - 1
misago/templates/misago/notifications/dropdown.html

@@ -62,7 +62,7 @@
     <button type="button" class="btn btn-default btn-refresh btn-sm" style="display: none;">
     <button type="button" class="btn btn-default btn-refresh btn-sm" style="display: none;">
       {% trans "Refresh" %}
       {% trans "Refresh" %}
     </button>
     </button>
-    <button type="submit" class="btn btn-default btn-read-all btn-sm pull-right" {% if not user.new_notifications %}style="display: none;"{% endif %}>
+    <button type="submit" name="read-all" class="btn btn-default btn-read-all btn-sm pull-right" {% if not user.new_notifications %}style="display: none;"{% endif %}>
       {% trans "Mark all as read" %}
       {% trans "Mark all as read" %}
     </button>
     </button>
   </form>
   </form>

+ 32 - 1
misago/templates/misago/notifications/full.html

@@ -32,7 +32,7 @@ You have {{ notifications }} notifications
     <div class="page-actions">
     <div class="page-actions">
       <form method="post">
       <form method="post">
         {% csrf_token %}
         {% csrf_token %}
-        <button type="submit" class="btn btn-default">
+        <button type="submit" name="read-all" class="btn btn-default">
           {% trans "Mark all read" %}
           {% trans "Mark all read" %}
         </button>
         </button>
       </form>
       </form>
@@ -56,6 +56,15 @@ You have {{ notifications }} notifications
 
 
       <a href="{{ item.url }}" class="message">{{ item.message|safe }}</a>
       <a href="{{ item.url }}" class="message">{{ item.message|safe }}</a>
 
 
+      <form method="POST" class="read-form">
+        {% csrf_token %}
+        <input type="hidden" name="notification" value="{{ item.pk }}">
+        <button type="submit" class="btn btn-default btn-sm"{% if not item.is_new %} disabled="disabled"{% endif %}>
+          <span class="fa fa-check"></span>
+          {% trans "Read" %}
+        </button>
+      </form>
+
       <footer class="text-muted">
       <footer class="text-muted">
         {% if item.sender_username %}
         {% if item.sender_username %}
           {% if item.sender_id %}
           {% if item.sender_id %}
@@ -79,3 +88,25 @@ You have {{ notifications }} notifications
   {% endif %}
   {% endif %}
 </div>
 </div>
 {% endblock content %}
 {% endblock content %}
+
+
+{% block javascripts %}
+<script lang="JavaScript">
+  $(function() {
+    $('.read-form').submit(function() {
+      var $form = $(this);
+
+      $.post($form.attr('action'), $form.serialize(), function(data) {
+        $row = $form.parents('li');
+        $row.removeClass("new");
+        $icon = $row.find('.state-icon .fa');
+        $row.find('.btn').attr("disabled", "disabled");
+        $icon.removeClass("fa-circle").addClass('fa-circle-o');
+        $icon.attr("title", "{% trans "Old notification" %}");
+        $.misago_dom().changed();
+      });
+      return false;
+    });
+  });
+</script>
+{% endblock javascripts %}