mirror of https://github.com/zulip/zulip.git
Add feature to mark all in stream/topic as read with mouse.
Fixes #736.
This commit is contained in:
parent
34fb276b7b
commit
635828069f
|
@ -3,6 +3,7 @@
|
|||
All notable changes to the Zulip server are documented in this file.
|
||||
|
||||
### Unreleased
|
||||
- Added UI for marking all messages in a stream or topic as read.
|
||||
- Added new Attachment model to keep track of uploaded files.
|
||||
- Added caching of virtualenvs in development.
|
||||
- Fixed missing helper scripts for RabbitMQ Nagios plugins.
|
||||
|
|
|
@ -383,6 +383,13 @@ exports.register_click_handlers = function () {
|
|||
e.preventDefault();
|
||||
});
|
||||
|
||||
$('body').on('click', '.sidebar-popover-mark-topic-read', function (e) {
|
||||
var topic = $(e.currentTarget).attr('data-topic-name');
|
||||
var stream = $(e.currentTarget).attr('data-stream-name');
|
||||
popovers.hide_topic_sidebar_popover();
|
||||
unread.mark_topic_as_read(stream,topic);
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
$('#stream_filters').on('click', '.stream-sidebar-arrow', function (e) {
|
||||
var elt = e.target;
|
||||
|
@ -547,6 +554,13 @@ exports.register_click_handlers = function () {
|
|||
e.stopPropagation();
|
||||
});
|
||||
|
||||
$('body').on('click', '.mark_stream_as_read', function (e) {
|
||||
var stream = $(e.currentTarget).parents('ul').attr('data-name');
|
||||
popovers.hide_stream_sidebar_popover();
|
||||
unread.mark_stream_as_read(stream);
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
$('body').on('click', '.open_stream_settings', function (e) {
|
||||
var stream = $(e.currentTarget).parents('ul').attr('data-name');
|
||||
popovers.hide_stream_sidebar_popover();
|
||||
|
|
|
@ -272,6 +272,33 @@ exports.mark_current_list_as_read = function mark_current_list_as_read(options)
|
|||
exports.mark_messages_as_read(current_msg_list.all_messages(), options);
|
||||
};
|
||||
|
||||
exports.mark_stream_as_read = function mark_stream_as_read(stream, cont) {
|
||||
channel.post({
|
||||
url: '/json/messages/flags',
|
||||
idempotent: true,
|
||||
data: {messages: JSON.stringify([]),
|
||||
all: false,
|
||||
op: 'add',
|
||||
flag: 'read',
|
||||
stream_name: stream
|
||||
},
|
||||
success: cont});
|
||||
};
|
||||
|
||||
exports.mark_topic_as_read = function mark_topic_as_read(stream, topic, cont) {
|
||||
channel.post({
|
||||
url: '/json/messages/flags',
|
||||
idempotent: true,
|
||||
data: {messages: JSON.stringify([]),
|
||||
all: false,
|
||||
op: 'add',
|
||||
flag: 'read',
|
||||
topic_name: topic,
|
||||
stream_name: stream
|
||||
},
|
||||
success: cont});
|
||||
};
|
||||
|
||||
return exports;
|
||||
}());
|
||||
if (typeof module !== 'undefined') {
|
||||
|
|
|
@ -23,6 +23,12 @@
|
|||
Compose a message to stream <b>{{stream.name}}</b>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="mark_stream_as_read">
|
||||
<i class="icon-vector-book"></i>
|
||||
Mark all messages in <b>{{stream.name}}</b> as read
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="open_stream_settings">
|
||||
<i class="icon-vector-cog"></i>
|
||||
|
|
|
@ -24,5 +24,12 @@
|
|||
</a>
|
||||
</li>
|
||||
{{/if}}
|
||||
<li>
|
||||
<a class="sidebar-popover-mark-topic-read" data-stream-name="{{ stream_name }}" data-topic-name="{{ topic_name }}">
|
||||
<i class="icon-vector-book"></i>
|
||||
Mark all messages in <b>{{topic_name}}</b> as read
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
|
|
|
@ -2050,12 +2050,20 @@ def do_update_pointer(user_profile, pointer, update_flags=False):
|
|||
event = dict(type='pointer', pointer=pointer)
|
||||
send_event(event, [user_profile.id])
|
||||
|
||||
def do_update_message_flags(user_profile, operation, flag, messages, all):
|
||||
def do_update_message_flags(user_profile, operation, flag, messages, all, stream_obj, topic_name):
|
||||
flagattr = getattr(UserMessage.flags, flag)
|
||||
|
||||
if all:
|
||||
log_statsd_event('bankruptcy')
|
||||
msgs = UserMessage.objects.filter(user_profile=user_profile)
|
||||
elif stream_obj is not None:
|
||||
recipient = get_recipient(Recipient.STREAM, stream_obj.id)
|
||||
if topic_name:
|
||||
msgs = UserMessage.objects.filter(message__recipient=recipient,
|
||||
user_profile=user_profile,
|
||||
message__subject__iexact=topic_name)
|
||||
else:
|
||||
msgs = UserMessage.objects.filter(message__recipient=recipient, user_profile=user_profile)
|
||||
else:
|
||||
msgs = UserMessage.objects.filter(user_profile=user_profile,
|
||||
message__id__in=messages)
|
||||
|
@ -2092,9 +2100,13 @@ def do_update_message_flags(user_profile, operation, flag, messages, all):
|
|||
# are kind of magical; they are actually just testing the one bit.
|
||||
if operation == 'add':
|
||||
msgs = msgs.filter(flags=~flagattr)
|
||||
if stream_obj:
|
||||
messages = list(msgs.values_list('message__id', flat=True))
|
||||
count = msgs.update(flags=F('flags').bitor(flagattr))
|
||||
elif operation == 'remove':
|
||||
msgs = msgs.filter(flags=flagattr)
|
||||
if stream_obj:
|
||||
messages = list(msgs.values_list('message__id', flat=True))
|
||||
count = msgs.update(flags=F('flags').bitand(~flagattr))
|
||||
|
||||
event = {'type': 'update_message_flags',
|
||||
|
|
|
@ -21,7 +21,7 @@ class Command(BaseCommand):
|
|||
print("e-mail %s doesn't exist in the system, skipping" % (email,))
|
||||
continue
|
||||
|
||||
do_update_message_flags(user_profile, "add", "read", None, True)
|
||||
do_update_message_flags(user_profile, "add", "read", None, True, None, None)
|
||||
|
||||
messages = Message.objects.filter(
|
||||
usermessage__user_profile=user_profile).order_by('-id')[:1]
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# -*- coding: utf-8 -*-AA
|
||||
from __future__ import absolute_import
|
||||
|
||||
from zerver.models import (
|
||||
get_user_profile_by_email, Recipient, UserMessage,
|
||||
get_user_profile_by_email, Recipient, UserMessage
|
||||
)
|
||||
|
||||
from zerver.lib.test_helpers import AuthedTestCase
|
||||
from zerver.lib.test_helpers import AuthedTestCase, tornado_redirected_to_list
|
||||
import ujson
|
||||
|
||||
class PointerTest(AuthedTestCase):
|
||||
|
@ -137,3 +137,102 @@ class UnreadCountTests(AuthedTestCase):
|
|||
for msg in self.get_old_messages():
|
||||
self.assertEqual(msg['flags'], [])
|
||||
|
||||
def test_mark_all_in_stream_read(self):
|
||||
self.login("hamlet@zulip.com")
|
||||
user_profile = get_user_profile_by_email("hamlet@zulip.com")
|
||||
self.subscribe_to_stream(user_profile.email, "test_stream", user_profile.realm)
|
||||
|
||||
message_id = self.send_message("hamlet@zulip.com", "test_stream", Recipient.STREAM, "hello")
|
||||
unrelated_message_id = self.send_message("hamlet@zulip.com", "Denmark", Recipient.STREAM, "hello")
|
||||
|
||||
events = []
|
||||
with tornado_redirected_to_list(events):
|
||||
result = self.client.post("/json/messages/flags", {"messages": ujson.dumps([]),
|
||||
"op": "add",
|
||||
"flag": "read",
|
||||
"stream_name": "test_stream"})
|
||||
|
||||
self.assert_json_success(result)
|
||||
self.assertTrue(len(events) == 1)
|
||||
|
||||
event = events[0]['event']
|
||||
expected = dict(operation='add',
|
||||
messages=[message_id],
|
||||
flag='read',
|
||||
type='update_message_flags',
|
||||
all=False)
|
||||
|
||||
differences = [key for key in expected if expected[key] != event[key]]
|
||||
self.assertTrue(len(differences) == 0)
|
||||
|
||||
um = list(UserMessage.objects.filter(message=message_id))
|
||||
for msg in um:
|
||||
if msg.user_profile.email == "hamlet@zulip.com":
|
||||
self.assertTrue(msg.flags.read)
|
||||
else:
|
||||
self.assertFalse(msg.flags.read)
|
||||
|
||||
unrelated_messages = list(UserMessage.objects.filter(message=unrelated_message_id))
|
||||
for msg in unrelated_messages:
|
||||
if msg.user_profile.email == "hamlet@zulip.com":
|
||||
self.assertFalse(msg.flags.read)
|
||||
|
||||
|
||||
def test_mark_all_in_invalid_stream_read(self):
|
||||
self.login("hamlet@zulip.com")
|
||||
invalid_stream_name = ""
|
||||
result = self.client.post("/json/messages/flags", {"messages": ujson.dumps([]),
|
||||
"op": "add",
|
||||
"flag": "read",
|
||||
"stream_name": invalid_stream_name})
|
||||
self.assert_json_error(result, 'No such stream \'\'')
|
||||
|
||||
def test_mark_all_in_stream_topic_read(self):
|
||||
self.login("hamlet@zulip.com")
|
||||
user_profile = get_user_profile_by_email("hamlet@zulip.com")
|
||||
self.subscribe_to_stream(user_profile.email, "test_stream", user_profile.realm)
|
||||
|
||||
message_id = self.send_message("hamlet@zulip.com", "test_stream", Recipient.STREAM, "hello", "test_topic")
|
||||
unrelated_message_id = self.send_message("hamlet@zulip.com", "Denmark", Recipient.STREAM, "hello", "Denmark2")
|
||||
events = []
|
||||
with tornado_redirected_to_list(events):
|
||||
result = self.client.post("/json/messages/flags", {"messages": ujson.dumps([]),
|
||||
"op": "add",
|
||||
"flag": "read",
|
||||
"topic_name": "test_topic",
|
||||
"stream_name": "test_stream"})
|
||||
|
||||
self.assert_json_success(result)
|
||||
self.assertTrue(len(events) == 1)
|
||||
|
||||
event = events[0]['event']
|
||||
expected = dict(operation='add',
|
||||
messages=[message_id],
|
||||
flag='read',
|
||||
type='update_message_flags',
|
||||
all=False)
|
||||
|
||||
differences = [key for key in expected if expected[key] != event[key]]
|
||||
self.assertTrue(len(differences) == 0)
|
||||
|
||||
um = list(UserMessage.objects.filter(message=message_id))
|
||||
for msg in um:
|
||||
if msg.user_profile.email == "hamlet@zulip.com":
|
||||
self.assertTrue(msg.flags.read)
|
||||
|
||||
unrelated_messages = list(UserMessage.objects.filter(message=unrelated_message_id))
|
||||
for msg in unrelated_messages:
|
||||
if msg.user_profile.email == "hamlet@zulip.com":
|
||||
self.assertFalse(msg.flags.read)
|
||||
|
||||
|
||||
def test_mark_all_in_invalid_topic_read(self):
|
||||
self.login("hamlet@zulip.com")
|
||||
invalid_topic_name = "abc"
|
||||
result = self.client.post("/json/messages/flags", {"messages": ujson.dumps([]),
|
||||
"op": "add",
|
||||
"flag": "read",
|
||||
"topic_name": invalid_topic_name,
|
||||
"stream_name": "Denmark"})
|
||||
self.assert_json_error(result, 'No such topic \'abc\'')
|
||||
|
||||
|
|
|
@ -603,11 +603,26 @@ def get_old_messages_backend(request, user_profile,
|
|||
|
||||
@has_request_variables
|
||||
def update_message_flags(request, user_profile,
|
||||
messages=REQ('messages', validator=check_list(check_int)),
|
||||
operation=REQ('op'), flag=REQ('flag'),
|
||||
all=REQ('all', validator=check_bool, default=False)):
|
||||
messages=REQ('messages', validator=check_list(check_int)),
|
||||
operation=REQ('op'), flag=REQ('flag'),
|
||||
all=REQ('all', validator=check_bool, default=False),
|
||||
stream_name=REQ('stream_name', default=None),
|
||||
topic_name=REQ('topic_name', default=None)):
|
||||
|
||||
request._log_data["extra"] = "[%s %s]" % (operation, flag)
|
||||
do_update_message_flags(user_profile, operation, flag, messages, all)
|
||||
stream = None
|
||||
if stream_name is not None:
|
||||
stream = get_stream(stream_name, user_profile.realm)
|
||||
if not stream:
|
||||
raise JsonableError('No such stream \'%s\'' % (stream_name,))
|
||||
if topic_name:
|
||||
topic_exists = UserMessage.objects.filter(user_profile=user_profile,
|
||||
message__recipient__type_id=stream.id,
|
||||
message__recipient__type=Recipient.STREAM,
|
||||
message__subject__iexact=topic_name).exists()
|
||||
if not topic_exists:
|
||||
raise JsonableError('No such topic \'%s\'' % (topic_name,))
|
||||
do_update_message_flags(user_profile, operation, flag, messages, all, stream, topic_name)
|
||||
return json_success({'result': 'success',
|
||||
'messages': messages,
|
||||
'msg': ''})
|
||||
|
|
Loading…
Reference in New Issue