Extract narrow_state.js.

Despite the length of this commit, it is a very straightforward
moving of code from narrow.js -> narrow_state.js, and then
everything else is just s/narrow.foo()/narrow_state.foo()/
(with a few tiny cleanups to remove some code duplication
in certain callers).

The only new functions are simple setter/getters that
encapsulate the current_filter variable:

    narrow_state.reset_current_filter()
    narrow_state.set_current_filter()
    narrow_state.get_current_filter()

We removed narrow.predicate() as part of this, since it was dead
code.

Also, we removed the shim for narrow_state.set_compose_defaults(),
and since that was the last shim, we removed shim.js from the app.
This commit is contained in:
Steve Howell 2017-04-25 06:25:31 -07:00 committed by Tim Abbott
parent 7326971380
commit 8eb86335b9
30 changed files with 342 additions and 321 deletions

View File

@ -98,6 +98,7 @@
"stream_sort": false, "stream_sort": false,
"stream_list": false, "stream_list": false,
"stream_popover": false, "stream_popover": false,
"narrow_state": false,
"narrow": false, "narrow": false,
"narrow_state": false, "narrow_state": false,
"admin_sections": false, "admin_sections": false,

View File

@ -3,12 +3,16 @@ global.stub_out_jquery();
add_dependencies({ add_dependencies({
hash_util: 'js/hash_util.js', hash_util: 'js/hash_util.js',
hashchange: 'js/hashchange.js', hashchange: 'js/hashchange.js',
narrow_state: 'js/narrow_state.js',
people: 'js/people.js', people: 'js/people.js',
stream_data: 'js/stream_data.js', stream_data: 'js/stream_data.js',
Filter: 'js/filter.js', Filter: 'js/filter.js',
}); });
var narrow = require('js/narrow.js'); var narrow = require('js/narrow.js');
var narrow_state = global.narrow_state;
var Filter = global.Filter; var Filter = global.Filter;
var stream_data = global.stream_data; var stream_data = global.stream_data;
var _ = global._; var _ = global._;
@ -17,68 +21,68 @@ function set_filter(operators) {
operators = _.map(operators, function (op) { operators = _.map(operators, function (op) {
return {operator: op[0], operand: op[1]}; return {operator: op[0], operand: op[1]};
}); });
narrow._set_current_filter(new Filter(operators)); narrow_state.set_current_filter(new Filter(operators));
} }
(function test_stream() { (function test_stream() {
var test_stream = {name: 'Test', stream_id: 15}; var test_stream = {name: 'Test', stream_id: 15};
stream_data.add_sub('Test', test_stream); stream_data.add_sub('Test', test_stream);
assert(!narrow.is_for_stream_id(test_stream.stream_id)); assert(!narrow_state.is_for_stream_id(test_stream.stream_id));
set_filter([['stream', 'Test'], ['topic', 'Bar'], ['search', 'yo']]); set_filter([['stream', 'Test'], ['topic', 'Bar'], ['search', 'yo']]);
assert.equal(narrow.stream(), 'Test'); assert.equal(narrow_state.stream(), 'Test');
assert.equal(narrow.topic(), 'Bar'); assert.equal(narrow_state.topic(), 'Bar');
assert(narrow.is_for_stream_id(test_stream.stream_id)); assert(narrow_state.is_for_stream_id(test_stream.stream_id));
}()); }());
(function test_narrowed() { (function test_narrowed() {
narrow._set_current_filter(undefined); // not narrowed, basically narrow_state.reset_current_filter(); // not narrowed, basically
assert(!narrow.narrowed_to_pms()); assert(!narrow_state.narrowed_to_pms());
assert(!narrow.narrowed_by_reply()); assert(!narrow_state.narrowed_by_reply());
assert(!narrow.narrowed_by_pm_reply()); assert(!narrow_state.narrowed_by_pm_reply());
assert(!narrow.narrowed_by_topic_reply()); assert(!narrow_state.narrowed_by_topic_reply());
assert(!narrow.narrowed_to_search()); assert(!narrow_state.narrowed_to_search());
assert(!narrow.narrowed_to_topic()); assert(!narrow_state.narrowed_to_topic());
set_filter([['stream', 'Foo']]); set_filter([['stream', 'Foo']]);
assert(!narrow.narrowed_to_pms()); assert(!narrow_state.narrowed_to_pms());
assert(!narrow.narrowed_by_reply()); assert(!narrow_state.narrowed_by_reply());
assert(!narrow.narrowed_by_pm_reply()); assert(!narrow_state.narrowed_by_pm_reply());
assert(!narrow.narrowed_by_topic_reply()); assert(!narrow_state.narrowed_by_topic_reply());
assert(!narrow.narrowed_to_search()); assert(!narrow_state.narrowed_to_search());
assert(!narrow.narrowed_to_topic()); assert(!narrow_state.narrowed_to_topic());
set_filter([['pm-with', 'steve@zulip.com']]); set_filter([['pm-with', 'steve@zulip.com']]);
assert(narrow.narrowed_to_pms()); assert(narrow_state.narrowed_to_pms());
assert(narrow.narrowed_by_reply()); assert(narrow_state.narrowed_by_reply());
assert(narrow.narrowed_by_pm_reply()); assert(narrow_state.narrowed_by_pm_reply());
assert(!narrow.narrowed_by_topic_reply()); assert(!narrow_state.narrowed_by_topic_reply());
assert(!narrow.narrowed_to_search()); assert(!narrow_state.narrowed_to_search());
assert(!narrow.narrowed_to_topic()); assert(!narrow_state.narrowed_to_topic());
set_filter([['stream', 'Foo'], ['topic', 'bar']]); set_filter([['stream', 'Foo'], ['topic', 'bar']]);
assert(!narrow.narrowed_to_pms()); assert(!narrow_state.narrowed_to_pms());
assert(narrow.narrowed_by_reply()); assert(narrow_state.narrowed_by_reply());
assert(!narrow.narrowed_by_pm_reply()); assert(!narrow_state.narrowed_by_pm_reply());
assert(narrow.narrowed_by_topic_reply()); assert(narrow_state.narrowed_by_topic_reply());
assert(!narrow.narrowed_to_search()); assert(!narrow_state.narrowed_to_search());
assert(narrow.narrowed_to_topic()); assert(narrow_state.narrowed_to_topic());
set_filter([['search', 'grail']]); set_filter([['search', 'grail']]);
assert(!narrow.narrowed_to_pms()); assert(!narrow_state.narrowed_to_pms());
assert(!narrow.narrowed_by_reply()); assert(!narrow_state.narrowed_by_reply());
assert(!narrow.narrowed_by_pm_reply()); assert(!narrow_state.narrowed_by_pm_reply());
assert(!narrow.narrowed_by_topic_reply()); assert(!narrow_state.narrowed_by_topic_reply());
assert(narrow.narrowed_to_search()); assert(narrow_state.narrowed_to_search());
assert(!narrow.narrowed_to_topic()); assert(!narrow_state.narrowed_to_topic());
}()); }());
(function test_operators() { (function test_operators() {
set_filter([['stream', 'Foo'], ['topic', 'Bar'], ['search', 'Yo']]); set_filter([['stream', 'Foo'], ['topic', 'Bar'], ['search', 'Yo']]);
var result = narrow.operators(); var result = narrow_state.operators();
assert.equal(result.length, 3); assert.equal(result.length, 3);
assert.equal(result[0].operator, 'stream'); assert.equal(result[0].operator, 'stream');
assert.equal(result[0].operand, 'Foo'); assert.equal(result[0].operand, 'Foo');
@ -123,19 +127,19 @@ function set_filter(operators) {
(function test_muting_enabled() { (function test_muting_enabled() {
set_filter([['stream', 'devel']]); set_filter([['stream', 'devel']]);
assert(narrow.muting_enabled()); assert(narrow_state.muting_enabled());
narrow._set_current_filter(undefined); // not narrowed, basically narrow_state.reset_current_filter(); // not narrowed, basically
assert(narrow.muting_enabled()); assert(narrow_state.muting_enabled());
set_filter([['stream', 'devel'], ['topic', 'mac']]); set_filter([['stream', 'devel'], ['topic', 'mac']]);
assert(!narrow.muting_enabled()); assert(!narrow_state.muting_enabled());
set_filter([['search', 'whatever']]); set_filter([['search', 'whatever']]);
assert(!narrow.muting_enabled()); assert(!narrow_state.muting_enabled());
set_filter([['is', 'private']]); set_filter([['is', 'private']]);
assert(!narrow.muting_enabled()); assert(!narrow_state.muting_enabled());
}()); }());
@ -143,7 +147,7 @@ function set_filter(operators) {
set_filter([['stream', 'Foo'], ['topic', 'Bar']]); set_filter([['stream', 'Foo'], ['topic', 'Bar']]);
var opts = {}; var opts = {};
narrow.set_compose_defaults(opts); narrow_state.set_compose_defaults(opts);
assert.equal(opts.stream, 'Foo'); assert.equal(opts.stream, 'Foo');
assert.equal(opts.subject, 'Bar'); assert.equal(opts.subject, 'Bar');
@ -151,7 +155,7 @@ function set_filter(operators) {
set_filter([['stream', 'rome']]); set_filter([['stream', 'rome']]);
opts = {}; opts = {};
narrow.set_compose_defaults(opts); narrow_state.set_compose_defaults(opts);
assert.equal(opts.stream, 'ROME'); assert.equal(opts.stream, 'ROME');
}()); }());
@ -191,7 +195,7 @@ function set_filter(operators) {
return {hide: function () {hide_id = id;}, show: function () {show_id = id;}}; return {hide: function () {hide_id = id;}, show: function () {show_id = id;}};
}; };
narrow._set_current_filter(undefined); narrow_state.reset_current_filter();
narrow.show_empty_narrow_message(); narrow.show_empty_narrow_message();
assert.equal(hide_id,'.empty_feed_notice'); assert.equal(hide_id,'.empty_feed_notice');
assert.equal(show_id, '#empty_narrow_message'); assert.equal(show_id, '#empty_narrow_message');
@ -262,8 +266,8 @@ function set_filter(operators) {
['sender', 'steve@foo.com'], ['sender', 'steve@foo.com'],
['stream', 'steve@foo.com'], // try to be tricky ['stream', 'steve@foo.com'], // try to be tricky
]); ]);
narrow.update_email(steve.user_id, 'showell@foo.com'); narrow_state.update_email(steve.user_id, 'showell@foo.com');
var filter = narrow.filter(); var filter = narrow_state.filter();
assert.deepEqual(filter.operands('pm-with'), ['showell@foo.com']); assert.deepEqual(filter.operands('pm-with'), ['showell@foo.com']);
assert.deepEqual(filter.operands('sender'), ['showell@foo.com']); assert.deepEqual(filter.operands('sender'), ['showell@foo.com']);
assert.deepEqual(filter.operands('stream'), ['steve@foo.com']); assert.deepEqual(filter.operands('stream'), ['steve@foo.com']);

View File

@ -14,7 +14,7 @@ add_dependencies({
typeahead_helper: 'js/typeahead_helper.js', typeahead_helper: 'js/typeahead_helper.js',
people: 'js/people.js', people: 'js/people.js',
stream_data: 'js/stream_data.js', stream_data: 'js/stream_data.js',
narrow: 'js/narrow.js', narrow_state: 'js/narrow_state.js',
}); });
var people = global.people; var people = global.people;
@ -46,7 +46,7 @@ global.stream_data.populate_stream_topics_for_tests({});
return []; return [];
}; };
global.narrow.stream = function () { global.narrow_state.stream = function () {
return 'office'; return 'office';
}; };
@ -65,7 +65,7 @@ global.stream_data.populate_stream_topics_for_tests({});
return []; return [];
}; };
global.narrow.stream = function () { global.narrow_state.stream = function () {
return undefined; return undefined;
}; };
@ -85,7 +85,7 @@ global.stream_data.populate_stream_topics_for_tests({});
return []; return [];
}; };
global.narrow.stream = function () { global.narrow_state.stream = function () {
return undefined; return undefined;
}; };
@ -220,7 +220,7 @@ global.stream_data.populate_stream_topics_for_tests({});
return []; return [];
}; };
global.narrow.stream = function () { global.narrow_state.stream = function () {
return undefined; return undefined;
}; };
@ -332,7 +332,7 @@ init();
return ['devel', 'office']; return ['devel', 'office'];
}; };
global.narrow.stream = function () { global.narrow_state.stream = function () {
return undefined; return undefined;
}; };
@ -369,7 +369,7 @@ init();
return []; return [];
}; };
global.narrow.stream = function () { global.narrow_state.stream = function () {
return undefined; return undefined;
}; };
@ -428,7 +428,7 @@ init();
return ['office']; return ['office'];
}; };
global.narrow.stream = function () { global.narrow_state.stream = function () {
return 'office'; return 'office';
}; };
@ -496,7 +496,7 @@ init();
return ['office']; return ['office'];
}; };
global.narrow.stream = function () { global.narrow_state.stream = function () {
return; return;
}; };
@ -518,7 +518,7 @@ init();
return ['office']; return ['office'];
}; };
global.narrow.stream = function () { global.narrow_state.stream = function () {
return; return;
}; };
@ -541,7 +541,7 @@ init();
return []; return [];
}; };
global.narrow.stream = function () { global.narrow_state.stream = function () {
return; return;
}; };

View File

@ -1,6 +1,6 @@
// Unit test the unread.js module, which depends on these global variables: // Unit test the unread.js module, which depends on these global variables:
// //
// _, narrow, current_msg_list, home_msg_list, subs // _, narrow_state, current_msg_list, home_msg_list, subs
// //
// These tests are framework-free and run sequentially; they are invoked // These tests are framework-free and run sequentially; they are invoked
// immediately after being defined. The contract here is that tests should // immediately after being defined. The contract here is that tests should
@ -29,8 +29,8 @@ var people = global.people;
var unread = require('js/unread.js'); var unread = require('js/unread.js');
var narrow = {}; var narrow_state = {};
global.narrow = narrow; global.narrow_state = narrow_state;
var current_msg_list = {}; var current_msg_list = {};
global.current_msg_list = current_msg_list; global.current_msg_list = current_msg_list;
@ -57,7 +57,7 @@ var zero_counts = {
}; };
(function test_empty_counts_while_narrowed() { (function test_empty_counts_while_narrowed() {
narrow.active = function () { narrow_state.active = function () {
return true; return true;
}; };
current_msg_list.all_messages = function () { current_msg_list.all_messages = function () {
@ -69,7 +69,7 @@ var zero_counts = {
}()); }());
(function test_empty_counts_while_home() { (function test_empty_counts_while_home() {
narrow.active = function () { narrow_state.active = function () {
return false; return false;
}; };
current_msg_list.all_messages = function () { current_msg_list.all_messages = function () {
@ -219,7 +219,7 @@ var zero_counts = {
(function test_home_messages() { (function test_home_messages() {
narrow.active = function () { narrow_state.active = function () {
return false; return false;
}; };
stream_data.is_subscribed = function () { stream_data.is_subscribed = function () {
@ -279,7 +279,7 @@ var zero_counts = {
}()); }());
(function test_private_messages() { (function test_private_messages() {
narrow.active = function () { narrow_state.active = function () {
return false; return false;
}; };
stream_data.is_subscribed = function () { stream_data.is_subscribed = function () {
@ -351,7 +351,7 @@ var zero_counts = {
(function test_mentions() { (function test_mentions() {
narrow.active = function () { narrow_state.active = function () {
return false; return false;
}; };
stream_data.is_subscribed = function () { stream_data.is_subscribed = function () {

View File

@ -304,7 +304,7 @@ exports.on_topic_narrow = function () {
return; return;
} }
if (compose_state.stream_name() !== narrow.stream()) { if (compose_state.stream_name() !== narrow_state.stream()) {
// If we changed streams, then we only leave the // If we changed streams, then we only leave the
// compose box open if there is content. // compose box open if there is content.
if (compose_state.has_message_content()) { if (compose_state.has_message_content()) {
@ -333,12 +333,12 @@ exports.on_topic_narrow = function () {
// stream filled in, and we just need to update the topic. // stream filled in, and we just need to update the topic.
// See #3300 for context--a couple users specifically asked // See #3300 for context--a couple users specifically asked
// for this convenience. // for this convenience.
compose_state.subject(narrow.topic()); compose_state.subject(narrow_state.topic());
$('#new_message_content').focus().select(); $('#new_message_content').focus().select();
}; };
exports.on_narrow = function () { exports.on_narrow = function () {
if (narrow.narrowed_by_topic_reply()) { if (narrow_state.narrowed_by_topic_reply()) {
exports.on_topic_narrow(); exports.on_topic_narrow();
return; return;
} }
@ -348,7 +348,7 @@ exports.on_narrow = function () {
return; return;
} }
if (narrow.narrowed_by_pm_reply()) { if (narrow_state.narrowed_by_pm_reply()) {
exports.start('private'); exports.start('private');
return; return;
} }

View File

@ -211,7 +211,7 @@ exports.try_deliver_locally = function try_deliver_locally(message_request) {
return undefined; return undefined;
} }
if (narrow.active() && !narrow.filter().can_apply_locally()) { if (narrow_state.active() && !narrow_state.filter().can_apply_locally()) {
return undefined; return undefined;
} }

View File

@ -12,7 +12,7 @@ function maybe_add_narrowed_messages(messages, msg_list, messages_are_new, local
url: '/json/messages_in_narrow', url: '/json/messages_in_narrow',
idempotent: true, idempotent: true,
data: {msg_ids: JSON.stringify(ids), data: {msg_ids: JSON.stringify(ids),
narrow: JSON.stringify(narrow.public_operators())}, narrow: JSON.stringify(narrow_state.public_operators())},
timeout: 5000, timeout: 5000,
success: function (data) { success: function (data) {
if (msg_list !== current_msg_list) { if (msg_list !== current_msg_list) {
@ -63,8 +63,8 @@ exports.insert_new_messages = function insert_new_messages(messages, local_id) {
message_util.add_messages(messages, home_msg_list, {messages_are_new: true}); message_util.add_messages(messages, home_msg_list, {messages_are_new: true});
message_util.add_messages(messages, message_list.all, {messages_are_new: true}); message_util.add_messages(messages, message_list.all, {messages_are_new: true});
if (narrow.active()) { if (narrow_state.active()) {
if (narrow.filter().can_apply_locally()) { if (narrow_state.filter().can_apply_locally()) {
message_util.add_messages(messages, message_list.narrowed, {messages_are_new: true}); message_util.add_messages(messages, message_list.narrowed, {messages_are_new: true});
notifications.possibly_notify_new_messages_outside_viewport(messages, local_id); notifications.possibly_notify_new_messages_outside_viewport(messages, local_id);
} else { } else {
@ -78,7 +78,7 @@ exports.insert_new_messages = function insert_new_messages(messages, local_id) {
activity.process_loaded_messages(messages); activity.process_loaded_messages(messages);
message_util.do_unread_count_updates(messages); message_util.do_unread_count_updates(messages);
if (narrow.narrowed_by_reply()) { if (narrow_state.narrowed_by_reply()) {
// If you send a message when narrowed to a recipient, move the // If you send a message when narrowed to a recipient, move the
// pointer to it. // pointer to it.
@ -159,7 +159,7 @@ exports.update_messages = function update_messages(events) {
var selection_changed_topic = _.indexOf(event.message_ids, current_id) >= 0; var selection_changed_topic = _.indexOf(event.message_ids, current_id) >= 0;
if (selection_changed_topic) { if (selection_changed_topic) {
var current_filter = narrow.filter(); var current_filter = narrow_state.filter();
if (current_filter && stream_name) { if (current_filter && stream_name) {
if (current_filter.has_topic(stream_name, event.orig_subject)) { if (current_filter.has_topic(stream_name, event.orig_subject)) {
var new_filter = current_filter.filter_with_new_topic(event.subject); var new_filter = current_filter.filter_with_new_topic(event.subject);

View File

@ -82,8 +82,8 @@ exports.load_old_messages = function load_old_messages(opts) {
num_before: opts.num_before, num_before: opts.num_before,
num_after: opts.num_after}; num_after: opts.num_after};
if (opts.msg_list.narrowed && narrow.active()) { if (opts.msg_list.narrowed && narrow_state.active()) {
var operators = narrow.public_operators(); var operators = narrow_state.public_operators();
if (page_params.narrow !== undefined) { if (page_params.narrow !== undefined) {
operators = operators.concat(page_params.narrow); operators = operators.concat(page_params.narrow);
} }

View File

@ -362,7 +362,7 @@ exports.MessageList.prototype = {
if (!this.narrowed) { if (!this.narrowed) {
return; return;
} }
var stream = narrow.stream(); var stream = narrow_state.stream();
if (stream === undefined) { if (stream === undefined) {
return; return;
} }

View File

@ -577,7 +577,7 @@ MessageListView.prototype = {
_.last(last_message_group.message_containers).msg.historical; _.last(last_message_group.message_containers).msg.historical;
} }
var stream_name = narrow.stream(); var stream_name = narrow_state.stream();
if (stream_name !== undefined) { if (stream_name !== undefined) {
// If user narrows to a stream, doesn't update // If user narrows to a stream, doesn't update
// trailing bookend if user is subscribed. // trailing bookend if user is subscribed.

View File

@ -2,139 +2,8 @@ var narrow = (function () {
var exports = {}; var exports = {};
var current_filter;
var unnarrow_times; var unnarrow_times;
// A small concession to unit testing follows:
exports._set_current_filter = function (filter) {
current_filter = filter;
};
exports.active = function () {
return current_filter !== undefined;
};
exports.filter = function () {
return current_filter;
};
exports.predicate = function () {
if (current_filter === undefined) {
return function () { return true; };
}
return current_filter.predicate();
};
exports.operators = function () {
if (current_filter === undefined) {
return new Filter(page_params.narrow).operators();
}
return current_filter.operators();
};
exports.update_email = function (user_id, new_email) {
if (current_filter !== undefined) {
current_filter.update_email(user_id, new_email);
}
};
/* Operators we should send to the server. */
exports.public_operators = function () {
if (current_filter === undefined) {
return undefined;
}
return current_filter.public_operators();
};
exports.search_string = function () {
return Filter.unparse(exports.operators());
};
// Collect operators which appear only once into an object,
// and discard those which appear more than once.
function collect_single(operators) {
var seen = new Dict();
var result = new Dict();
_.each(operators, function (elem) {
var key = elem.operator;
if (seen.has(key)) {
result.del(key);
} else {
result.set(key, elem.operand);
seen.set(key, true);
}
});
return result;
}
// Modify default compose parameters (stream etc.) based on
// the current narrowed view.
//
// This logic is here and not in the 'compose' module because
// it will get more complicated as we add things to the narrow
// operator language.
exports.set_compose_defaults = function (opts) {
var single = collect_single(exports.operators());
// Set the stream, subject, and/or PM recipient if they are
// uniquely specified in the narrow view.
if (single.has('stream')) {
opts.stream = stream_data.get_name(single.get('stream'));
}
if (single.has('topic')) {
opts.subject = single.get('topic');
}
if (single.has('pm-with')) {
opts.private_message_recipient = single.get('pm-with');
}
};
exports.stream = function () {
if (current_filter === undefined) {
return undefined;
}
var stream_operands = current_filter.operands("stream");
if (stream_operands.length === 1) {
return stream_operands[0];
}
return undefined;
};
exports.is_for_stream_id = function (stream_id) {
// This is not perfect, since we still track narrows by
// name, not id, but at least the interface is good going
// forward.
var sub = stream_data.get_sub_by_id(stream_id);
if (sub === undefined) {
blueslip.error('Bad stream id ' + stream_id);
return false;
}
var narrow_stream_name = exports.stream();
if (narrow_stream_name === undefined) {
return false;
}
return (sub.name === narrow_stream_name);
};
exports.topic = function () {
if (current_filter === undefined) {
return undefined;
}
var operands = current_filter.operands("topic");
if (operands.length === 1) {
return operands[0];
}
return undefined;
};
function report_narrow_time(initial_core_time, initial_free_time, network_time) { function report_narrow_time(initial_core_time, initial_free_time, network_time) {
channel.post({ channel.post({
url: '/json/report_narrow_time', url: '/json/report_narrow_time',
@ -178,7 +47,7 @@ function report_unnarrow_time() {
exports.narrow_title = "home"; exports.narrow_title = "home";
exports.activate = function (raw_operators, opts) { exports.activate = function (raw_operators, opts) {
var start_time = new Date(); var start_time = new Date();
var was_narrowed_already = exports.active(); var was_narrowed_already = narrow_state.active();
// most users aren't going to send a bunch of a out-of-narrow messages // most users aren't going to send a bunch of a out-of-narrow messages
// and expect to visit a list of narrows, so let's get these out of the way. // and expect to visit a list of narrows, so let's get these out of the way.
notifications.clear_compose_notifications(); notifications.clear_compose_notifications();
@ -248,8 +117,8 @@ exports.activate = function (raw_operators, opts) {
// For legacy reasons, we need to set current_filter before calling // For legacy reasons, we need to set current_filter before calling
// muting_enabled. // muting_enabled.
current_filter = filter; narrow_state.set_current_filter(filter);
var muting_enabled = exports.muting_enabled(); var muting_enabled = narrow_state.muting_enabled();
// Save how far from the pointer the top of the message list was. // Save how far from the pointer the top of the message list was.
if (current_msg_list.selected_id() !== -1) { if (current_msg_list.selected_id() !== -1) {
@ -274,10 +143,17 @@ exports.activate = function (raw_operators, opts) {
home_msg_list.pre_narrow_offset = page_params.initial_offset; home_msg_list.pre_narrow_offset = page_params.initial_offset;
} }
var msg_list = new message_list.MessageList('zfilt', current_filter, { var msg_list_opts = {
collapse_messages: ! current_filter.is_search(), collapse_messages: ! narrow_state.get_current_filter().is_search(),
muting_enabled: muting_enabled, muting_enabled: muting_enabled,
}); };
var msg_list = new message_list.MessageList(
'zfilt',
narrow_state.get_current_filter(),
msg_list_opts
);
msg_list.start_time = start_time; msg_list.start_time = start_time;
// Show the new set of messages. It is important to set current_msg_list to // Show the new set of messages. It is important to set current_msg_list to
@ -326,10 +202,12 @@ exports.activate = function (raw_operators, opts) {
// Don't bother populating a message list when it won't contain // Don't bother populating a message list when it won't contain
// the message we want anyway or if the filter can't be applied // the message we want anyway or if the filter can't be applied
// locally. // locally.
if (message_list.all.get(then_select_id) !== undefined && current_filter.can_apply_locally()) { if (message_list.all.get(then_select_id) !== undefined) {
if (narrow_state.get_current_filter().can_apply_locally()) {
message_util.add_messages(message_list.all.all_messages(), message_list.narrowed, message_util.add_messages(message_list.all.all_messages(), message_list.narrowed,
{delay_render: true}); {delay_render: true});
} }
}
var defer_selecting_closest = message_list.narrowed.empty(); var defer_selecting_closest = message_list.narrowed.empty();
message_fetch.load_old_messages({ message_fetch.load_old_messages({
@ -369,6 +247,7 @@ exports.activate = function (raw_operators, opts) {
compose_actions.on_narrow(); compose_actions.on_narrow();
var current_filter = narrow_state.get_current_filter();
$(document).trigger($.Event('narrow_activated.zulip', {msg_list: message_list.narrowed, $(document).trigger($.Event('narrow_activated.zulip', {msg_list: message_list.narrowed,
filter: current_filter, filter: current_filter,
trigger: opts.trigger})); trigger: opts.trigger}));
@ -395,8 +274,8 @@ exports.stream_topic = function () {
// We may be in an empty narrow. In that case we use // We may be in an empty narrow. In that case we use
// our narrow parameters to return the stream/topic. // our narrow parameters to return the stream/topic.
return { return {
stream: exports.stream(), stream: narrow_state.stream(),
topic: exports.topic(), topic: narrow_state.topic(),
}; };
}; };
@ -491,7 +370,7 @@ exports.by_conversation_and_time = function (target_id, opts) {
}; };
exports.deactivate = function () { exports.deactivate = function () {
if (current_filter === undefined) { if (narrow_state.get_current_filter() === undefined) {
return; return;
} }
unnarrow_times = {start_time: new Date()}; unnarrow_times = {start_time: new Date()};
@ -509,7 +388,7 @@ exports.deactivate = function () {
compose_actions.cancel(); compose_actions.cancel();
} }
current_filter = undefined; narrow_state.reset_current_filter();
exports.hide_empty_narrow_message(); exports.hide_empty_narrow_message();
@ -590,6 +469,9 @@ exports.restore_home_state = function () {
function pick_empty_narrow_banner() { function pick_empty_narrow_banner() {
var default_banner = $('#empty_narrow_message'); var default_banner = $('#empty_narrow_message');
var current_filter = narrow_state.get_current_filter();
if (current_filter === undefined) { if (current_filter === undefined) {
return default_banner; return default_banner;
} }
@ -614,7 +496,7 @@ function pick_empty_narrow_banner() {
} }
} else if ((first_operator === "stream") && !stream_data.is_subscribed(first_operand)) { } else if ((first_operator === "stream") && !stream_data.is_subscribed(first_operand)) {
// You are narrowed to a stream to which you aren't subscribed. // You are narrowed to a stream to which you aren't subscribed.
if (!stream_data.get_sub(narrow.stream())) { if (!stream_data.get_sub(narrow_state.stream())) {
return $("#nonsubbed_private_nonexistent_stream_narrow_message"); return $("#nonsubbed_private_nonexistent_stream_narrow_message");
} }
return $("#nonsubbed_stream_narrow_message"); return $("#nonsubbed_stream_narrow_message");
@ -692,58 +574,6 @@ exports.by_conversation_and_time_uri = function (message) {
"/near/" + hash_util.encodeHashComponent(message.id); "/near/" + hash_util.encodeHashComponent(message.id);
}; };
// Are we narrowed to PMs: all PMs or PMs with particular people.
exports.narrowed_to_pms = function () {
if (current_filter === undefined) {
return false;
}
return (current_filter.has_operator("pm-with") ||
current_filter.has_operand("is", "private"));
};
// We auto-reply under certain conditions, namely when you're narrowed
// to a PM (or huddle), and when you're narrowed to some stream/subject pair
exports.narrowed_by_reply = function () {
return (exports.narrowed_by_pm_reply() ||
exports.narrowed_by_topic_reply());
};
exports.narrowed_by_pm_reply = function () {
if (current_filter === undefined) {
return false;
}
var operators = current_filter.operators();
return (operators.length === 1 &&
current_filter.has_operator('pm-with'));
};
exports.narrowed_by_topic_reply = function () {
if (current_filter === undefined) {
return false;
}
var operators = current_filter.operators();
return (operators.length === 2 &&
current_filter.operands("stream").length === 1 &&
current_filter.operands("topic").length === 1);
};
exports.narrowed_to_topic = function () {
if (current_filter === undefined) {
return false;
}
return (current_filter.has_operator("stream") &&
current_filter.has_operator("topic"));
};
exports.narrowed_to_search = function () {
return (current_filter !== undefined) && current_filter.is_search();
};
exports.muting_enabled = function () {
return (!exports.narrowed_to_topic() && !exports.narrowed_to_search() &&
!exports.narrowed_to_pms());
};
return exports; return exports;
}()); }());

194
static/js/narrow_state.js Normal file
View File

@ -0,0 +1,194 @@
var narrow_state = (function () {
var exports = {};
var current_filter;
exports.reset_current_filter = function () {
current_filter = undefined;
};
exports.set_current_filter = function (filter) {
current_filter = filter;
};
exports.get_current_filter = function () {
return current_filter;
};
exports.active = function () {
return current_filter !== undefined;
};
exports.filter = function () {
return current_filter;
};
exports.operators = function () {
if (current_filter === undefined) {
return new Filter(page_params.narrow).operators();
}
return current_filter.operators();
};
exports.update_email = function (user_id, new_email) {
if (current_filter !== undefined) {
current_filter.update_email(user_id, new_email);
}
};
/* Operators we should send to the server. */
exports.public_operators = function () {
if (current_filter === undefined) {
return undefined;
}
return current_filter.public_operators();
};
exports.search_string = function () {
return Filter.unparse(exports.operators());
};
// Collect operators which appear only once into an object,
// and discard those which appear more than once.
function collect_single(operators) {
var seen = new Dict();
var result = new Dict();
_.each(operators, function (elem) {
var key = elem.operator;
if (seen.has(key)) {
result.del(key);
} else {
result.set(key, elem.operand);
seen.set(key, true);
}
});
return result;
}
// Modify default compose parameters (stream etc.) based on
// the current narrowed view.
//
// This logic is here and not in the 'compose' module because
// it will get more complicated as we add things to the narrow
// operator language.
exports.set_compose_defaults = function (opts) {
var single = collect_single(exports.operators());
// Set the stream, subject, and/or PM recipient if they are
// uniquely specified in the narrow view.
if (single.has('stream')) {
opts.stream = stream_data.get_name(single.get('stream'));
}
if (single.has('topic')) {
opts.subject = single.get('topic');
}
if (single.has('pm-with')) {
opts.private_message_recipient = single.get('pm-with');
}
};
exports.stream = function () {
if (current_filter === undefined) {
return undefined;
}
var stream_operands = current_filter.operands("stream");
if (stream_operands.length === 1) {
return stream_operands[0];
}
return undefined;
};
exports.topic = function () {
if (current_filter === undefined) {
return undefined;
}
var operands = current_filter.operands("topic");
if (operands.length === 1) {
return operands[0];
}
return undefined;
};
// Are we narrowed to PMs: all PMs or PMs with particular people.
exports.narrowed_to_pms = function () {
if (current_filter === undefined) {
return false;
}
return (current_filter.has_operator("pm-with") ||
current_filter.has_operand("is", "private"));
};
exports.narrowed_by_pm_reply = function () {
if (current_filter === undefined) {
return false;
}
var operators = current_filter.operators();
return (operators.length === 1 &&
current_filter.has_operator('pm-with'));
};
exports.narrowed_by_topic_reply = function () {
if (current_filter === undefined) {
return false;
}
var operators = current_filter.operators();
return (operators.length === 2 &&
current_filter.operands("stream").length === 1 &&
current_filter.operands("topic").length === 1);
};
// We auto-reply under certain conditions, namely when you're narrowed
// to a PM (or huddle), and when you're narrowed to some stream/subject pair
exports.narrowed_by_reply = function () {
return (exports.narrowed_by_pm_reply() ||
exports.narrowed_by_topic_reply());
};
exports.narrowed_to_topic = function () {
if (current_filter === undefined) {
return false;
}
return (current_filter.has_operator("stream") &&
current_filter.has_operator("topic"));
};
exports.narrowed_to_search = function () {
return (current_filter !== undefined) && current_filter.is_search();
};
exports.muting_enabled = function () {
return (!exports.narrowed_to_topic() && !exports.narrowed_to_search() &&
!exports.narrowed_to_pms());
};
exports.is_for_stream_id = function (stream_id) {
// This is not perfect, since we still track narrows by
// name, not id, but at least the interface is good going
// forward.
var sub = stream_data.get_sub_by_id(stream_id);
if (sub === undefined) {
blueslip.error('Bad stream id ' + stream_id);
return false;
}
var narrow_stream_name = exports.stream();
if (narrow_stream_name === undefined) {
return false;
}
return (sub.name === narrow_stream_name);
};
return exports;
}());
if (typeof module !== 'undefined') {
module.exports = narrow_state;
}

View File

@ -118,12 +118,12 @@ exports.page_down = function () {
exports.cycle_stream = function (direction) { exports.cycle_stream = function (direction) {
var currentStream; var currentStream;
var nextStream; var nextStream;
if (narrow.stream() !== undefined) { if (narrow_state.stream() !== undefined) {
currentStream = stream_list.get_stream_li(narrow.stream()); currentStream = stream_list.get_stream_li(narrow_state.stream());
} }
switch (direction) { switch (direction) {
case 'forward': case 'forward':
if (narrow.stream() === undefined) { if (narrow_state.stream() === undefined) {
nextStream = $("#stream_filters").children('.narrow-filter').first(); nextStream = $("#stream_filters").children('.narrow-filter').first();
} else { } else {
nextStream = currentStream.next('.narrow-filter'); nextStream = currentStream.next('.narrow-filter');
@ -133,7 +133,7 @@ exports.cycle_stream = function (direction) {
} }
break; break;
case 'backward': case 'backward':
if (narrow.stream() === undefined) { if (narrow_state.stream() === undefined) {
nextStream = $("#stream_filters").children('.narrow-filter').last(); nextStream = $("#stream_filters").children('.narrow-filter').last();
} else { } else {
nextStream = currentStream.prev('.narrow-filter'); nextStream = currentStream.prev('.narrow-filter');

View File

@ -152,12 +152,12 @@ exports.rebuild_recent = function (active_conversation) {
exports.update_private_messages = function () { exports.update_private_messages = function () {
exports._build_private_messages_list(); exports._build_private_messages_list();
if (! narrow.active()) { if (! narrow_state.active()) {
return; return;
} }
var is_pm_filter = _.contains(narrow.filter().operands('is'), "private"); var is_pm_filter = _.contains(narrow_state.filter().operands('is'), "private");
var conversation = narrow.filter().operands('pm-with'); var conversation = narrow_state.filter().operands('pm-with');
if (conversation.length === 1) { if (conversation.length === 1) {
exports.rebuild_recent(conversation[0]); exports.rebuild_recent(conversation[0]);
} else if (conversation.length !== 0) { } else if (conversation.length !== 0) {

View File

@ -77,7 +77,7 @@ function show_message_info_popover(element, id) {
user_time: people.get_user_time(message.sender_id), user_time: people.get_user_time(message.sender_id),
pm_with_uri: narrow.pm_with_uri(sender_email), pm_with_uri: narrow.pm_with_uri(sender_email),
sent_by_uri: narrow.by_sender_uri(sender_email), sent_by_uri: narrow.by_sender_uri(sender_email),
narrowed: narrow.active(), narrowed: narrow_state.active(),
historical: message.historical, historical: message.historical,
private_message_class: "respond_personal_button", private_message_class: "respond_personal_button",
}; };
@ -275,7 +275,7 @@ exports.toggle_actions_popover = function (element, id) {
should_display_add_reaction_option: message.sent_by_me, should_display_add_reaction_option: message.sent_by_me,
should_display_edit_history_option: should_display_edit_history_option, should_display_edit_history_option: should_display_edit_history_option,
conversation_time_uri: narrow.by_conversation_and_time_uri(message), conversation_time_uri: narrow.by_conversation_and_time_uri(message),
narrowed: narrow.active(), narrowed: narrow_state.active(),
}; };
var ypos = elt.offset().top; var ypos = elt.offset().top;

View File

@ -46,7 +46,7 @@ function preserve_state(send_after_reload, save_pointer, save_narrow, save_compo
if (save_narrow) { if (save_narrow) {
var row = home_msg_list.selected_row(); var row = home_msg_list.selected_row();
if (!narrow.active()) { if (!narrow_state.active()) {
if (row.length > 0) { if (row.length > 0) {
url += "+offset=" + row.offset().top; url += "+offset=" + row.offset().top;
} }

View File

@ -25,7 +25,7 @@ function update_buttons_with_focus(focused) {
// or we are narrowed. // or we are narrowed.
if (focused if (focused
|| search_query.val() || search_query.val()
|| narrow.active()) { || narrow_state.active()) {
$('.search_button').removeAttr('disabled'); $('.search_button').removeAttr('disabled');
} else { } else {
$('.search_button').attr('disabled', 'disabled'); $('.search_button').attr('disabled', 'disabled');
@ -121,7 +121,7 @@ exports.initialize = function () {
// really it would be OK if they did). // really it would be OK if they did).
setTimeout(function () { setTimeout(function () {
var search_string = narrow.search_string(); var search_string = narrow_state.search_string();
query.val(search_string); query.val(search_string);
exports.update_button_visibility(); exports.update_button_visibility();
}, 100); }, 100);

View File

@ -311,7 +311,7 @@ function get_topic_suggestions(query_operators) {
if (filter.has_operator('stream')) { if (filter.has_operator('stream')) {
stream = filter.operands('stream')[0]; stream = filter.operands('stream')[0];
} else { } else {
stream = narrow.stream(); stream = narrow_state.stream();
query_operators.push({operator: 'stream', operand: stream}); query_operators.push({operator: 'stream', operand: stream});
} }
break; break;

View File

@ -1,11 +0,0 @@
/*
This module has shims to help us break circular dependencies.
We eventually want to to move the actual implementations into
new modules. When we do this, you may need to fix node tests
that still refer to the old name.
*/
var narrow_state = {}; // global, should be made into module
narrow_state.set_compose_defaults = narrow.set_compose_defaults;

View File

@ -402,7 +402,7 @@ $(function () {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
var stream_name = narrow.stream(); var stream_name = narrow_state.stream();
if (stream_name === undefined) { if (stream_name === undefined) {
return; return;
} }

View File

@ -99,7 +99,7 @@ exports.mark_subscribed = function (sub, subscribers, color) {
subs.update_settings_for_subscribed(sub); subs.update_settings_for_subscribed(sub);
if (narrow.is_for_stream_id(sub.stream_id)) { if (narrow_state.is_for_stream_id(sub.stream_id)) {
current_msg_list.update_trailing_bookend(); current_msg_list.update_trailing_bookend();
} }
@ -124,7 +124,7 @@ exports.mark_unsubscribed = function (sub) {
return; return;
} }
if (narrow.is_for_stream_id(sub.stream_id)) { if (narrow_state.is_for_stream_id(sub.stream_id)) {
current_msg_list.update_trailing_bookend(); current_msg_list.update_trailing_bookend();
} }

View File

@ -144,7 +144,7 @@ function zoom_in() {
popovers.hide_all(); popovers.hide_all();
topic_list.zoom_in(); topic_list.zoom_in();
$("#streams_list").expectOne().removeClass("zoom-out").addClass("zoom-in"); $("#streams_list").expectOne().removeClass("zoom-out").addClass("zoom-in");
zoomed_stream = narrow.stream(); zoomed_stream = narrow_state.stream();
// Hide stream list titles and pinned stream splitter // Hide stream list titles and pinned stream splitter
$(".stream-filters-label").each(function () { $(".stream-filters-label").each(function () {
@ -296,11 +296,11 @@ function rebuild_recent_topics(stream) {
exports.update_streams_sidebar = function () { exports.update_streams_sidebar = function () {
exports.build_stream_list(); exports.build_stream_list();
if (! narrow.active()) { if (! narrow_state.active()) {
return; return;
} }
var op_stream = narrow.filter().operands('stream'); var op_stream = narrow_state.filter().operands('stream');
if (op_stream.length !== 0) { if (op_stream.length !== 0) {
if (stream_data.is_subscribed(op_stream[0])) { if (stream_data.is_subscribed(op_stream[0])) {
rebuild_recent_topics(op_stream[0]); rebuild_recent_topics(op_stream[0]);
@ -369,7 +369,7 @@ $(function () {
$(document).on('narrow_activated.zulip', function (event) { $(document).on('narrow_activated.zulip', function (event) {
deselect_top_left_corner_items(); deselect_top_left_corner_items();
reset_to_unnarrowed(narrow.stream() === zoomed_stream); reset_to_unnarrowed(narrow_state.stream() === zoomed_stream);
// TODO: handle confused filters like "in:all stream:foo" // TODO: handle confused filters like "in:all stream:foo"
var op_in = event.filter.operands('in'); var op_in = event.filter.operands('in');

View File

@ -644,7 +644,7 @@ $(function () {
e.preventDefault(); e.preventDefault();
$('#subscription-status').hide(); $('#subscription-status').hide();
var stream_name = narrow.stream(); var stream_name = narrow_state.stream();
if (stream_name === undefined) { if (stream_name === undefined) {
return; return;
} }
@ -709,7 +709,7 @@ $(function () {
}); });
function focus_on_narrowed_stream() { function focus_on_narrowed_stream() {
var stream_name = narrow.stream(); var stream_name = narrow_state.stream();
if (stream_name === undefined) { if (stream_name === undefined) {
return; return;
} }

View File

@ -13,7 +13,7 @@ function make_tab(title, hash, data, extra_class, home) {
function make_tab_data() { function make_tab_data() {
var tabs = []; var tabs = [];
var filter = narrow.filter(); var filter = narrow_state.filter();
// Root breadcrumb item: Either Home or All Messages // Root breadcrumb item: Either Home or All Messages
if (filter !== undefined && if (filter !== undefined &&
@ -34,9 +34,9 @@ function make_tab_data() {
tabs.push(make_tab('Home', "#", "home", "root", true)); tabs.push(make_tab('Home', "#", "home", "root", true));
} }
if (narrow.active() && narrow.operators().length > 0) { if (narrow_state.active() && narrow_state.operators().length > 0) {
var stream; var stream;
var ops = narrow.operators(); var ops = narrow_state.operators();
// Second breadcrumb item // Second breadcrumb item
var hashed = hashchange.operators_to_hash(ops.slice(0, 1)); var hashed = hashchange.operators_to_hash(ops.slice(0, 1));
if (filter.has_operator("stream")) { if (filter.has_operator("stream")) {

View File

@ -157,7 +157,7 @@ exports.build_widget = function (parent_elem, stream, active_topic, max_topics)
exports.rebuild = function (stream_li, stream) { exports.rebuild = function (stream_li, stream) {
var max_topics = 5; var max_topics = 5;
var active_topic = narrow.topic(); var active_topic = narrow_state.topic();
exports.remove_expanded_topics(); exports.remove_expanded_topics();
active_widget = exports.build_widget(stream_li, stream, active_topic, max_topics); active_widget = exports.build_widget(stream_li, stream, active_topic, max_topics);
}; };

View File

@ -15,13 +15,15 @@ var TYPING_STARTED_EXPIRY_PERIOD = 15000; // 15s
// that make typing indicators work. // that make typing indicators work.
function get_users_typing_for_narrow() { function get_users_typing_for_narrow() {
if (!narrow.narrowed_to_pms()) { if (!narrow_state.narrowed_to_pms()) {
// Narrow is neither pm-with nor is: private // Narrow is neither pm-with nor is: private
return []; return [];
} }
if (narrow.operators()[0].operator === 'pm-with') {
var first_term = narrow_state.operators()[0];
if (first_term.operator === 'pm-with') {
// Get list of users typing in this conversation // Get list of users typing in this conversation
var narrow_emails_string = narrow.operators()[0].operand; var narrow_emails_string = first_term.operand;
// TODO: Create people.emails_strings_to_user_ids. // TODO: Create people.emails_strings_to_user_ids.
var narrow_user_ids_string = people.emails_strings_to_user_ids_string(narrow_emails_string); var narrow_user_ids_string = people.emails_strings_to_user_ids_string(narrow_emails_string);
var narrow_user_ids = narrow_user_ids_string.split(',').map(function (user_id_string) { var narrow_user_ids = narrow_user_ids_string.split(',').map(function (user_id_string) {

View File

@ -251,7 +251,7 @@ exports.get_counts = function () {
res.private_message_count = pm_count; res.private_message_count = pm_count;
res.home_unread_messages += pm_count; res.home_unread_messages += pm_count;
if (narrow.active()) { if (narrow_state.active()) {
res.unread_in_current_view = exports.num_unread_current_messages(); res.unread_in_current_view = exports.num_unread_current_messages();
} else { } else {
res.unread_in_current_view = res.home_unread_messages; res.unread_in_current_view = res.home_unread_messages;

View File

@ -20,7 +20,7 @@ exports.update_person = function update(person) {
var user_id = person.user_id; var user_id = person.user_id;
var new_email = person.new_email; var new_email = person.new_email;
narrow.update_email(user_id, new_email); narrow_state.update_email(user_id, new_email);
compose.update_email(user_id, new_email); compose.update_email(user_id, new_email);
if (people.is_my_user_id(person.user_id)) { if (people.is_my_user_id(person.user_id)) {

View File

@ -140,6 +140,7 @@ def find_edges_to_remove(graph, methods):
('settings_notifications', 'stream_edit'), ('settings_notifications', 'stream_edit'),
('compose', 'stream_edit'), ('compose', 'stream_edit'),
('subs', 'stream_edit'), ('subs', 'stream_edit'),
('narrow_state', 'stream_data'),
] # type: List[Edge] ] # type: List[Edge]
def is_exempt(edge): def is_exempt(edge):

View File

@ -854,6 +854,7 @@ JS_SPECS = {
'js/message_list_view.js', 'js/message_list_view.js',
'js/message_list.js', 'js/message_list.js',
'js/message_live_update.js', 'js/message_live_update.js',
'js/narrow_state.js',
'js/narrow.js', 'js/narrow.js',
'js/reload.js', 'js/reload.js',
'js/compose_fade.js', 'js/compose_fade.js',
@ -947,7 +948,6 @@ JS_SPECS = {
'js/typing_data.js', 'js/typing_data.js',
'js/typing_events.js', 'js/typing_events.js',
'js/ui_init.js', 'js/ui_init.js',
'js/shim.js',
# JS bundled by webpack is also included here if PIPELINE_ENABLED setting is true # JS bundled by webpack is also included here if PIPELINE_ENABLED setting is true
], ],
'output_filename': 'min/app.js' 'output_filename': 'min/app.js'