Mute any topic named "muted" on zulip.com.

This change will allow us to test the muting feature on
staging.  Any topic named "muted" will automatically be
muted.  You can also mute any other topic on the console:

    muting.mute_topic('devel', 'ios');
    current_msg_list.rerender();

More UI around this experiment will be coming soon, as well
as support for muting entire streams.

The muting module keeps track of which topics are muted, but a
user can expand muted messages, and once that happens, the
messages are marked with the "force_expand" flag that gets
persisted to the back end.

Muted messages are rendered in similar fashion to the summarized
rows, and as part of unifying some of that code, we have
made it so that expanding a summarized section doesn't remove
individual flags related to summaries; instead, the messages
get the force_expand flag set.

(imported from commit acee4190e63813d46850415c41ff8ebfae4a6953)
This commit is contained in:
Steve Howell 2013-08-20 16:05:56 -04:00
parent 7edf9b095e
commit 795248a1d0
8 changed files with 72 additions and 18 deletions

View File

@ -156,7 +156,17 @@ MessageListView.prototype = {
finish_summary(); finish_summary();
} }
if (list.is_summarized_message(message)) { var summary_adjective;
if (!_.contains(message.flags, 'force_expand')) {
if (muting.is_topic_muted(message.stream, message.subject)) {
summary_adjective = 'muted';
} else if (list.is_summarized_message(message)) {
summary_adjective = 'read';
}
}
if (summary_adjective) {
if (prev) { if (prev) {
prev.include_footer = true; prev.include_footer = true;
} }
@ -176,6 +186,7 @@ MessageListView.prototype = {
include_footer: false, include_footer: false,
include_bookend: false, include_bookend: false,
first_message_id: message.id, first_message_id: message.id,
summary_adjective: summary_adjective,
messages: [message] messages: [message]
}); });
} else { } else {
@ -248,14 +259,15 @@ MessageListView.prototype = {
if (prev) { if (prev) {
prev.include_footer = true; prev.include_footer = true;
} }
if (messages_to_render.length === 0) {
return;
}
if (has_summary) { if (has_summary) {
finish_summary(); finish_summary();
} }
if (messages_to_render.length === 0) {
return;
}
if (current_group.length > 0) { if (current_group.length > 0) {
new_message_groups.push(current_group); new_message_groups.push(current_group);
} }

28
static/js/muting.js Normal file
View File

@ -0,0 +1,28 @@
var muting = (function () {
var exports = {};
var muted_topics = new Dict();
exports.mute_topic = function (stream, topic) {
var sub_dict = muted_topics.get(stream);
if (!sub_dict) {
sub_dict = new Dict();
muted_topics.set(stream, sub_dict);
}
sub_dict.set(topic, true);
};
exports.is_topic_muted = function (stream, topic) {
if (page_params.domain === 'zulip.com' && topic === 'muted') {
return true;
}
var sub_dict = muted_topics.get(stream);
return sub_dict && sub_dict.get(topic);
};
return exports;
}());
if (typeof module !== 'undefined') {
module.exports = muting;
}

View File

@ -718,17 +718,11 @@ exports.expand_summary_row = function (row) {
return all_msg_list.get(id); return all_msg_list.get(id);
}); });
function remove_flag(flag) {
_.each(messages, function (msg){ _.each(messages, function (msg){
msg.flags = _.without(msg.flags, flag); msg.flags.push('force_expand');
}); });
update_message_flag(messages, flag, false); update_message_flag(messages, 'force_expand', true);
}
remove_flag('summarize_in_stream');
if (!narrow.active()) {
remove_flag('summarize_in_home');
}
//TODO: Avoid a full re-render //TODO: Avoid a full re-render
home_msg_list.rerender(); home_msg_list.rerender();

View File

@ -37,9 +37,9 @@
<td class="message_header message_header_stream right_part"> <td class="message_header message_header_stream right_part">
<span class="message_label_clickable"><i class="icon-vector-expand-alt"></i> <span class="message_label_clickable"><i class="icon-vector-expand-alt"></i>
{{#if is_stream}} {{#if is_stream}}
{{display_recipient}} &nbsp;<i class="icon-vector-narrow icon-vector-small"></i><span class="copy-paste-text">&gt;</span> &nbsp;{{subject}} ({{count}} read) {{display_recipient}} &nbsp;<i class="icon-vector-narrow icon-vector-small"></i><span class="copy-paste-text">&gt;</span> &nbsp;{{subject}} ({{count}} {{summary_adjective}})
{{else}} {{else}}
You and {{display_reply_to}} ({{count}} read) You and {{display_reply_to}} ({{count}} {{summary_adjective}})
{{/if}} {{/if}}
</span> </span>
</td> </td>

View File

@ -26,7 +26,7 @@ var globals =
+ ' invite ui util activity timerender MessageList MessageListView blueslip unread stream_list' + ' invite ui util activity timerender MessageList MessageListView blueslip unread stream_list'
+ ' onboarding message_edit tab_bar emoji popovers navigate message_tour' + ' onboarding message_edit tab_bar emoji popovers navigate message_tour'
+ ' avatar feature_flags search_suggestion referral stream_color Dict' + ' avatar feature_flags search_suggestion referral stream_color Dict'
+ ' Filter summary admin stream_data' + ' Filter summary admin stream_data muting'
// colorspace.js // colorspace.js
+ ' colorspace' + ' colorspace'

View File

@ -508,7 +508,7 @@ class UserMessage(models.Model):
# on later # on later
archived = models.BooleanField() archived = models.BooleanField()
ALL_FLAGS = ['read', 'starred', 'collapsed', 'mentioned', 'wildcard_mentioned', ALL_FLAGS = ['read', 'starred', 'collapsed', 'mentioned', 'wildcard_mentioned',
'summarize_in_home', 'summarize_in_stream'] 'summarize_in_home', 'summarize_in_stream', 'force_expand']
flags = BitField(flags=ALL_FLAGS, default=0) flags = BitField(flags=ALL_FLAGS, default=0)
class Meta: class Meta:

View File

@ -0,0 +1,19 @@
var assert = require('assert');
add_dependencies({
_: 'third/underscore/underscore.js',
Dict: 'js/dict.js'
});
set_global('page_params', {
domain: 'zulip.com'
});
var muting = require('js/muting.js');
(function test_basics() {
assert(!muting.is_topic_muted('devel', 'java'));
muting.mute_topic('devel', 'java');
assert(muting.is_topic_muted('devel', 'java'));
assert(muting.is_topic_muted('whatever', 'muted'));
}());

View File

@ -365,6 +365,7 @@ JS_SPECS = {
'js/summary.js', 'js/summary.js',
'js/util.js', 'js/util.js',
'js/dict.js', 'js/dict.js',
'js/muting.js',
'js/setup.js', 'js/setup.js',
'js/viewport.js', 'js/viewport.js',
'js/rows.js', 'js/rows.js',