2013-05-06 02:54:15 +02:00
|
|
|
var stream_list = (function () {
|
|
|
|
|
|
|
|
var exports = {};
|
|
|
|
|
2017-06-14 00:03:00 +02:00
|
|
|
exports.update_count_in_dom = function (unread_count_elem, count) {
|
2017-01-15 17:02:29 +01:00
|
|
|
var count_span = unread_count_elem.find('.count');
|
|
|
|
var value_span = count_span.find('.value');
|
|
|
|
|
|
|
|
if (count === 0) {
|
|
|
|
count_span.hide();
|
|
|
|
if (count_span.parent().hasClass("subscription_block")) {
|
|
|
|
count_span.parent(".subscription_block").removeClass("stream-with-count");
|
|
|
|
}
|
|
|
|
value_span.text('');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
count_span.show();
|
|
|
|
|
|
|
|
if (count_span.parent().hasClass("subscription_block")) {
|
|
|
|
count_span.parent(".subscription_block").addClass("stream-with-count");
|
|
|
|
}
|
|
|
|
value_span.text(count);
|
2017-06-14 00:03:00 +02:00
|
|
|
};
|
|
|
|
|
2017-01-15 17:02:29 +01:00
|
|
|
|
2016-11-11 14:20:19 +01:00
|
|
|
exports.stream_sidebar = (function () {
|
|
|
|
var self = {};
|
|
|
|
|
|
|
|
self.rows = new Dict(); // stream id -> row widget
|
|
|
|
|
|
|
|
self.set_row = function (stream_id, widget) {
|
|
|
|
self.rows.set(stream_id, widget);
|
|
|
|
};
|
|
|
|
|
|
|
|
self.get_row = function (stream_id) {
|
|
|
|
return self.rows.get(stream_id);
|
|
|
|
};
|
|
|
|
|
|
|
|
self.has_row_for = function (stream_id) {
|
|
|
|
return self.rows.has(stream_id);
|
|
|
|
};
|
|
|
|
|
2016-11-11 15:09:08 +01:00
|
|
|
self.remove_row = function (stream_id) {
|
2017-04-18 19:59:35 +02:00
|
|
|
// This only removes the row from our data structure.
|
|
|
|
// Our caller should use build_stream_list() to re-draw
|
|
|
|
// the sidebar, so that we don't have to deal with edge
|
|
|
|
// cases like removing the last pinned stream (and removing
|
|
|
|
// the divider).
|
2017-04-10 22:09:54 +02:00
|
|
|
|
2016-11-11 15:09:08 +01:00
|
|
|
self.rows.del(stream_id);
|
|
|
|
};
|
|
|
|
|
2016-11-11 14:20:19 +01:00
|
|
|
return self;
|
|
|
|
}());
|
|
|
|
|
2017-04-18 17:08:59 +02:00
|
|
|
function get_search_term() {
|
|
|
|
var search_box = $(".stream-list-filter");
|
|
|
|
var search_term = search_box.expectOne().val().trim();
|
|
|
|
return search_term;
|
|
|
|
}
|
|
|
|
|
2017-02-17 18:45:14 +01:00
|
|
|
exports.remove_sidebar_row = function (stream_id) {
|
|
|
|
exports.stream_sidebar.remove_row(stream_id);
|
2017-04-18 19:59:35 +02:00
|
|
|
exports.build_stream_list();
|
2018-04-24 16:59:01 +02:00
|
|
|
exports.stream_cursor.redraw();
|
2017-02-17 18:45:14 +01:00
|
|
|
};
|
|
|
|
|
Clean up startup code for streams.
The startup code in subs.js used to intermingle data
stuff and UI stuff in a loop inside a called function,
which made the code hard to reason about.
Now there is a clear separation of concerns, with these methods
being called in succession:
stream_data.initialize_from_page_params();
stream_list.create_initial_sidebar_rows();
The first method was mostly extracted from subs.js, but I simplified
some things, like not needing to make a copy of the hashes
we were passed in, plus I now garbage collect email_dict. Also,
the code path that initialize_from_page_params() mostly replaces
used to call create_sub(), which fired a trigger, but now it
just does data stuff.
Once the data structure is built up, it's a very simple matter
to build the initial sidebar rows, and that's what the second
method does.
2016-10-17 19:34:58 +02:00
|
|
|
exports.create_initial_sidebar_rows = function () {
|
|
|
|
// This code is slightly opaque, but it ends up building
|
|
|
|
// up list items and attaching them to the "sub" data
|
|
|
|
// structures that are kept in stream_data.js.
|
|
|
|
var subs = stream_data.subscribed_subs();
|
|
|
|
|
|
|
|
_.each(subs, function (sub) {
|
|
|
|
exports.create_sidebar_row(sub);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2013-11-22 20:05:34 +01:00
|
|
|
exports.build_stream_list = function () {
|
Clean up startup code for streams.
The startup code in subs.js used to intermingle data
stuff and UI stuff in a loop inside a called function,
which made the code hard to reason about.
Now there is a clear separation of concerns, with these methods
being called in succession:
stream_data.initialize_from_page_params();
stream_list.create_initial_sidebar_rows();
The first method was mostly extracted from subs.js, but I simplified
some things, like not needing to make a copy of the hashes
we were passed in, plus I now garbage collect email_dict. Also,
the code path that initialize_from_page_params() mostly replaces
used to call create_sub(), which fired a trigger, but now it
just does data stuff.
Once the data structure is built up, it's a very simple matter
to build the initial sidebar rows, and that's what the second
method does.
2016-10-17 19:34:58 +02:00
|
|
|
// This function assumes we have already created the individual
|
|
|
|
// sidebar rows. Our job here is to build the bigger widget,
|
|
|
|
// which largely is a matter of arranging the individual rows in
|
|
|
|
// the right order.
|
2013-08-15 21:11:07 +02:00
|
|
|
var streams = stream_data.subscribed_streams();
|
2013-05-06 23:36:22 +02:00
|
|
|
if (streams.length === 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-04-18 17:08:59 +02:00
|
|
|
// The main logic to build the list is in stream_sort.js, and
|
|
|
|
// we get three lists of streams (pinned/normal/dormant).
|
|
|
|
var stream_groups = stream_sort.sort_groups(get_search_term());
|
|
|
|
|
|
|
|
if (stream_groups.same_as_before) {
|
|
|
|
return;
|
|
|
|
}
|
2016-06-13 22:06:12 +02:00
|
|
|
|
2016-07-01 07:26:09 +02:00
|
|
|
var parent = $('#stream_filters');
|
|
|
|
var elems = [];
|
2013-05-06 23:36:22 +02:00
|
|
|
|
2016-07-01 07:26:09 +02:00
|
|
|
function add_sidebar_li(stream) {
|
2016-11-11 14:20:19 +01:00
|
|
|
var sub = stream_data.get_sub(stream);
|
|
|
|
var sidebar_row = exports.stream_sidebar.get_row(sub.stream_id);
|
2017-04-18 19:59:35 +02:00
|
|
|
sidebar_row.update_whether_active();
|
2017-07-18 13:30:14 +02:00
|
|
|
elems.push(sidebar_row.get_li());
|
2016-07-01 07:26:09 +02:00
|
|
|
}
|
|
|
|
|
2017-04-18 17:08:59 +02:00
|
|
|
parent.empty();
|
2016-07-01 07:26:09 +02:00
|
|
|
|
2017-04-18 17:08:59 +02:00
|
|
|
_.each(stream_groups.pinned_streams, add_sidebar_li);
|
left-sidebar: Sort pinned streams by lowercase stream name.
The pinned streams were sorted in alphabetic order (i.e. Verona appears
before devel). The reason is that after we plucked pinned streams out from
stream_data.subscribed_streams(), we didn't sort them again, so they
remained in the alphabetic order used in stream_data.
However, we did sort unpinned streams explicitly by using custom compare
function in stream_list.js (by default sort by lowercase stream name,
but when there are more than 40 subscribed streams, sort active streams
first). That's why this issue only relates to pinned streams.
Changes were made to sort pinned streams by lowercase stream name, always,
whether they are active or not (different from unpinned streams).
Tests were added to ensure this overall sort order is correct, i.e.
1. pinned streams are always sorted by lowercase stream name.
2. pinned streams are always before unpinned streams.
3. unpinned streams are sorted by lowercase stream name, if there are more
than 40 subscribed streams, sort active streams at the top, among active
and inactive streams, still sorted by lowercase stream name.
Fixes #3701
2017-02-19 15:24:27 +01:00
|
|
|
|
2017-11-13 17:17:34 +01:00
|
|
|
var any_pinned_streams = stream_groups.pinned_streams.length > 0;
|
|
|
|
var any_normal_streams = stream_groups.normal_streams.length > 0;
|
|
|
|
var any_dormant_streams = stream_groups.dormant_streams.length > 0;
|
|
|
|
|
|
|
|
if (any_pinned_streams && (any_normal_streams || any_dormant_streams)) {
|
2017-07-18 13:30:14 +02:00
|
|
|
elems.push('<hr class="stream-split">');
|
2017-04-18 17:08:59 +02:00
|
|
|
}
|
2013-05-06 02:54:15 +02:00
|
|
|
|
2017-04-18 17:08:59 +02:00
|
|
|
_.each(stream_groups.normal_streams, add_sidebar_li);
|
2016-07-01 07:26:09 +02:00
|
|
|
|
2017-11-13 17:17:34 +01:00
|
|
|
if (any_dormant_streams && any_normal_streams) {
|
2017-07-18 13:30:14 +02:00
|
|
|
elems.push('<hr class="stream-split">');
|
2017-04-19 18:18:29 +02:00
|
|
|
}
|
2013-05-06 02:54:15 +02:00
|
|
|
|
2017-04-18 17:08:59 +02:00
|
|
|
_.each(stream_groups.dormant_streams, add_sidebar_li);
|
2016-07-01 07:26:09 +02:00
|
|
|
|
2017-06-02 01:04:14 +02:00
|
|
|
parent.append(elems);
|
2013-05-06 02:54:15 +02:00
|
|
|
};
|
|
|
|
|
2017-05-13 17:41:10 +02:00
|
|
|
exports.get_stream_li = function (stream_id) {
|
2017-06-03 00:13:57 +02:00
|
|
|
var row = exports.stream_sidebar.get_row(stream_id);
|
|
|
|
if (!row) {
|
2017-06-03 14:16:45 +02:00
|
|
|
// Not all streams are in the sidebar, so we don't report
|
|
|
|
// an error here, and it's up for the caller to error if
|
|
|
|
// they expected otherwise.
|
2017-06-03 00:13:57 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var li = row.get_li();
|
|
|
|
if (!li) {
|
|
|
|
blueslip.error('Cannot find li for id ' + stream_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (li.length > 1) {
|
|
|
|
blueslip.error('stream_li has too many elements for ' + stream_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
return li;
|
2017-05-13 16:56:29 +02:00
|
|
|
};
|
|
|
|
|
2017-08-11 01:33:35 +02:00
|
|
|
function zoom_in(options) {
|
Rewrite topic zoom to fix bugs and make cleaner.
In the first cut at topic zoom, I was re-rendering the
streams list, but this created glitches with orphaned
list items. The reproducible bug was that unread counts
on unshown streams weren't updating.
In the new approach, I keep the elements more permanent, and
I just hide and show them as needed, either through jQuery
show/hide or permanent CSS selectors.
I got rid of toggle_zoom(), so that we just explicitly zoom
in and zoom out in all situations. In particular, when we
narrow, it's more clear now that only stay zoomed in when
we're narrowing to the same stream as before (including topic
narrows within that stream).
When you zoom in, the number of topics is no longer limited
to 30, since that was kind of arbitrary anyway. (In practice,
the number of topics is usually well under 30, anyway, due to
the way we track them on the client.)
(imported from commit 5b6c143dee9ba9fe557d8cc36335ff28efb4b0de)
2013-11-26 23:06:39 +01:00
|
|
|
popovers.hide_all();
|
2016-11-10 16:15:12 +01:00
|
|
|
topic_list.zoom_in();
|
2015-12-11 08:24:00 +01:00
|
|
|
$("#streams_list").expectOne().removeClass("zoom-out").addClass("zoom-in");
|
Rewrite topic zoom to fix bugs and make cleaner.
In the first cut at topic zoom, I was re-rendering the
streams list, but this created glitches with orphaned
list items. The reproducible bug was that unread counts
on unshown streams weren't updating.
In the new approach, I keep the elements more permanent, and
I just hide and show them as needed, either through jQuery
show/hide or permanent CSS selectors.
I got rid of toggle_zoom(), so that we just explicitly zoom
in and zoom out in all situations. In particular, when we
narrow, it's more clear now that only stay zoomed in when
we're narrowing to the same stream as before (including topic
narrows within that stream).
When you zoom in, the number of topics is no longer limited
to 30, since that was kind of arbitrary anyway. (In practice,
the number of topics is usually well under 30, anyway, due to
the way we track them on the client.)
(imported from commit 5b6c143dee9ba9fe557d8cc36335ff28efb4b0de)
2013-11-26 23:06:39 +01:00
|
|
|
|
2016-07-01 07:26:09 +02:00
|
|
|
// Hide stream list titles and pinned stream splitter
|
|
|
|
$(".stream-filters-label").each(function () {
|
|
|
|
$(this).hide();
|
|
|
|
});
|
2017-04-18 19:59:35 +02:00
|
|
|
$(".stream-split").each(function () {
|
2016-07-01 07:26:09 +02:00
|
|
|
$(this).hide();
|
|
|
|
});
|
|
|
|
|
Rewrite topic zoom to fix bugs and make cleaner.
In the first cut at topic zoom, I was re-rendering the
streams list, but this created glitches with orphaned
list items. The reproducible bug was that unread counts
on unshown streams weren't updating.
In the new approach, I keep the elements more permanent, and
I just hide and show them as needed, either through jQuery
show/hide or permanent CSS selectors.
I got rid of toggle_zoom(), so that we just explicitly zoom
in and zoom out in all situations. In particular, when we
narrow, it's more clear now that only stay zoomed in when
we're narrowing to the same stream as before (including topic
narrows within that stream).
When you zoom in, the number of topics is no longer limited
to 30, since that was kind of arbitrary anyway. (In practice,
the number of topics is usually well under 30, anyway, due to
the way we track them on the client.)
(imported from commit 5b6c143dee9ba9fe557d8cc36335ff28efb4b0de)
2013-11-26 23:06:39 +01:00
|
|
|
$("#stream_filters li.narrow-filter").each(function () {
|
|
|
|
var elt = $(this);
|
2017-08-11 01:33:35 +02:00
|
|
|
var stream_id = options.stream_id.toString();
|
Rewrite topic zoom to fix bugs and make cleaner.
In the first cut at topic zoom, I was re-rendering the
streams list, but this created glitches with orphaned
list items. The reproducible bug was that unread counts
on unshown streams weren't updating.
In the new approach, I keep the elements more permanent, and
I just hide and show them as needed, either through jQuery
show/hide or permanent CSS selectors.
I got rid of toggle_zoom(), so that we just explicitly zoom
in and zoom out in all situations. In particular, when we
narrow, it's more clear now that only stay zoomed in when
we're narrowing to the same stream as before (including topic
narrows within that stream).
When you zoom in, the number of topics is no longer limited
to 30, since that was kind of arbitrary anyway. (In practice,
the number of topics is usually well under 30, anyway, due to
the way we track them on the client.)
(imported from commit 5b6c143dee9ba9fe557d8cc36335ff28efb4b0de)
2013-11-26 23:06:39 +01:00
|
|
|
|
2017-08-11 01:33:35 +02:00
|
|
|
if (elt.attr('data-stream-id') === stream_id) {
|
Rewrite topic zoom to fix bugs and make cleaner.
In the first cut at topic zoom, I was re-rendering the
streams list, but this created glitches with orphaned
list items. The reproducible bug was that unread counts
on unshown streams weren't updating.
In the new approach, I keep the elements more permanent, and
I just hide and show them as needed, either through jQuery
show/hide or permanent CSS selectors.
I got rid of toggle_zoom(), so that we just explicitly zoom
in and zoom out in all situations. In particular, when we
narrow, it's more clear now that only stay zoomed in when
we're narrowing to the same stream as before (including topic
narrows within that stream).
When you zoom in, the number of topics is no longer limited
to 30, since that was kind of arbitrary anyway. (In practice,
the number of topics is usually well under 30, anyway, due to
the way we track them on the client.)
(imported from commit 5b6c143dee9ba9fe557d8cc36335ff28efb4b0de)
2013-11-26 23:06:39 +01:00
|
|
|
elt.show();
|
|
|
|
} else {
|
|
|
|
elt.hide();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-11-10 16:45:56 +01:00
|
|
|
function zoom_out(options) {
|
Rewrite topic zoom to fix bugs and make cleaner.
In the first cut at topic zoom, I was re-rendering the
streams list, but this created glitches with orphaned
list items. The reproducible bug was that unread counts
on unshown streams weren't updating.
In the new approach, I keep the elements more permanent, and
I just hide and show them as needed, either through jQuery
show/hide or permanent CSS selectors.
I got rid of toggle_zoom(), so that we just explicitly zoom
in and zoom out in all situations. In particular, when we
narrow, it's more clear now that only stay zoomed in when
we're narrowing to the same stream as before (including topic
narrows within that stream).
When you zoom in, the number of topics is no longer limited
to 30, since that was kind of arbitrary anyway. (In practice,
the number of topics is usually well under 30, anyway, due to
the way we track them on the client.)
(imported from commit 5b6c143dee9ba9fe557d8cc36335ff28efb4b0de)
2013-11-26 23:06:39 +01:00
|
|
|
popovers.hide_all();
|
2017-08-11 00:30:23 +02:00
|
|
|
topic_list.zoom_out();
|
2016-11-10 16:15:12 +01:00
|
|
|
|
2017-08-10 12:04:01 +02:00
|
|
|
if (options.stream_li) {
|
2017-08-10 12:32:01 +02:00
|
|
|
exports.scroll_stream_into_view(options.stream_li);
|
2017-08-10 12:04:01 +02:00
|
|
|
}
|
2017-08-11 00:28:16 +02:00
|
|
|
exports.show_all_streams();
|
|
|
|
}
|
2017-08-10 12:04:01 +02:00
|
|
|
|
2017-08-11 00:28:16 +02:00
|
|
|
exports.show_all_streams = function () {
|
2016-07-01 07:26:09 +02:00
|
|
|
// Show stream list titles and pinned stream splitter
|
|
|
|
$(".stream-filters-label").each(function () {
|
|
|
|
$(this).show();
|
|
|
|
});
|
2017-04-18 19:59:35 +02:00
|
|
|
$(".stream-split").each(function () {
|
2016-07-01 07:26:09 +02:00
|
|
|
$(this).show();
|
|
|
|
});
|
|
|
|
|
2015-12-11 08:24:00 +01:00
|
|
|
$("#streams_list").expectOne().removeClass("zoom-in").addClass("zoom-out");
|
Rewrite topic zoom to fix bugs and make cleaner.
In the first cut at topic zoom, I was re-rendering the
streams list, but this created glitches with orphaned
list items. The reproducible bug was that unread counts
on unshown streams weren't updating.
In the new approach, I keep the elements more permanent, and
I just hide and show them as needed, either through jQuery
show/hide or permanent CSS selectors.
I got rid of toggle_zoom(), so that we just explicitly zoom
in and zoom out in all situations. In particular, when we
narrow, it's more clear now that only stay zoomed in when
we're narrowing to the same stream as before (including topic
narrows within that stream).
When you zoom in, the number of topics is no longer limited
to 30, since that was kind of arbitrary anyway. (In practice,
the number of topics is usually well under 30, anyway, due to
the way we track them on the client.)
(imported from commit 5b6c143dee9ba9fe557d8cc36335ff28efb4b0de)
2013-11-26 23:06:39 +01:00
|
|
|
$("#stream_filters li.narrow-filter").show();
|
2017-08-11 00:28:16 +02:00
|
|
|
};
|
Rewrite topic zoom to fix bugs and make cleaner.
In the first cut at topic zoom, I was re-rendering the
streams list, but this created glitches with orphaned
list items. The reproducible bug was that unread counts
on unshown streams weren't updating.
In the new approach, I keep the elements more permanent, and
I just hide and show them as needed, either through jQuery
show/hide or permanent CSS selectors.
I got rid of toggle_zoom(), so that we just explicitly zoom
in and zoom out in all situations. In particular, when we
narrow, it's more clear now that only stay zoomed in when
we're narrowing to the same stream as before (including topic
narrows within that stream).
When you zoom in, the number of topics is no longer limited
to 30, since that was kind of arbitrary anyway. (In practice,
the number of topics is usually well under 30, anyway, due to
the way we track them on the client.)
(imported from commit 5b6c143dee9ba9fe557d8cc36335ff28efb4b0de)
2013-11-26 23:06:39 +01:00
|
|
|
|
2017-05-13 17:41:10 +02:00
|
|
|
exports.set_in_home_view = function (stream_id, in_home) {
|
|
|
|
var li = exports.get_stream_li(stream_id);
|
2017-06-03 00:13:57 +02:00
|
|
|
if (!li) {
|
|
|
|
blueslip.error('passed in bad stream id ' + stream_id);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-06-12 00:07:35 +02:00
|
|
|
if (in_home) {
|
|
|
|
li.removeClass("out_of_home_view");
|
|
|
|
} else {
|
|
|
|
li.addClass("out_of_home_view");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-11-11 02:39:22 +01:00
|
|
|
function build_stream_sidebar_li(sub) {
|
|
|
|
var name = sub.name;
|
2013-05-30 20:50:32 +02:00
|
|
|
var args = {name: name,
|
2014-02-07 16:55:54 +01:00
|
|
|
id: sub.stream_id,
|
2013-05-30 20:50:32 +02:00
|
|
|
uri: narrow.by_stream_uri(name),
|
2017-05-13 20:54:53 +02:00
|
|
|
not_in_home_view: (stream_data.in_home_view(sub.stream_id) === false),
|
2014-02-07 16:55:54 +01:00
|
|
|
invite_only: sub.invite_only,
|
2016-07-01 07:26:09 +02:00
|
|
|
color: stream_data.get_color(name),
|
2017-01-12 00:17:43 +01:00
|
|
|
pin_to_top: sub.pin_to_top,
|
2013-08-27 21:05:32 +02:00
|
|
|
};
|
|
|
|
args.dark_background = stream_color.get_color_class(args.color);
|
2013-06-12 18:03:16 +02:00
|
|
|
var list_item = $(templates.render('stream_sidebar_row', args));
|
2013-05-06 23:36:22 +02:00
|
|
|
return list_item;
|
2013-05-06 22:18:01 +02:00
|
|
|
}
|
2013-05-06 02:54:15 +02:00
|
|
|
|
2016-11-11 02:39:22 +01:00
|
|
|
function build_stream_sidebar_row(sub) {
|
|
|
|
var self = {};
|
|
|
|
var list_item = build_stream_sidebar_li(sub);
|
|
|
|
|
|
|
|
self.update_whether_active = function () {
|
2017-04-28 15:38:02 +02:00
|
|
|
if (stream_data.is_active(sub)) {
|
2016-11-11 02:39:22 +01:00
|
|
|
list_item.removeClass('inactive_stream');
|
|
|
|
} else {
|
|
|
|
list_item.addClass('inactive_stream');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
self.get_li = function () {
|
|
|
|
return list_item;
|
|
|
|
};
|
|
|
|
|
2016-11-11 15:09:08 +01:00
|
|
|
self.remove = function () {
|
|
|
|
list_item.remove();
|
|
|
|
};
|
|
|
|
|
2017-01-15 17:09:16 +01:00
|
|
|
|
|
|
|
self.update_unread_count = function () {
|
2017-05-13 19:26:54 +02:00
|
|
|
var count = unread.num_unread_for_stream(sub.stream_id);
|
2017-06-14 00:03:00 +02:00
|
|
|
exports.update_count_in_dom(list_item, count);
|
2017-01-15 17:09:16 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
self.update_unread_count();
|
|
|
|
|
2016-11-11 14:20:19 +01:00
|
|
|
exports.stream_sidebar.set_row(sub.stream_id, self);
|
2016-11-11 02:39:22 +01:00
|
|
|
}
|
|
|
|
|
2016-10-17 20:02:32 +02:00
|
|
|
exports.create_sidebar_row = function (sub) {
|
2016-11-11 14:20:19 +01:00
|
|
|
if (exports.stream_sidebar.has_row_for(sub.stream_id)) {
|
2013-10-21 22:26:19 +02:00
|
|
|
// already exists
|
2016-11-11 14:20:19 +01:00
|
|
|
blueslip.warn('Dup try to build sidebar row for stream ' + sub.stream_id);
|
2016-10-17 20:02:32 +02:00
|
|
|
return;
|
|
|
|
}
|
2016-11-11 02:39:22 +01:00
|
|
|
build_stream_sidebar_row(sub);
|
2014-01-16 21:38:40 +01:00
|
|
|
};
|
|
|
|
|
2017-06-14 16:33:30 +02:00
|
|
|
exports.redraw_stream_privacy = function (sub) {
|
2017-05-13 17:41:10 +02:00
|
|
|
var li = exports.get_stream_li(sub.stream_id);
|
2017-06-03 00:13:57 +02:00
|
|
|
if (!li) {
|
2018-01-06 12:25:03 +01:00
|
|
|
// We don't want to raise error here, if we can't find stream in subscription
|
|
|
|
// stream list. Cause we allow org admin to update stream privacy
|
|
|
|
// even if they don't subscribe to public stream.
|
2017-06-03 00:13:57 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-05-13 17:41:10 +02:00
|
|
|
var div = li.find('.stream-privacy');
|
2017-06-14 16:33:30 +02:00
|
|
|
var dark_background = stream_color.get_color_class(sub.color);
|
2014-01-17 18:23:39 +01:00
|
|
|
|
|
|
|
var args = {
|
|
|
|
invite_only: sub.invite_only,
|
2017-01-12 00:17:43 +01:00
|
|
|
dark_background: dark_background,
|
2014-01-17 18:23:39 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
var html = templates.render('stream_privacy', args);
|
|
|
|
div.html(html);
|
|
|
|
};
|
|
|
|
|
2017-05-13 19:26:54 +02:00
|
|
|
function set_stream_unread_count(stream_id, count) {
|
2017-05-13 17:41:10 +02:00
|
|
|
var unread_count_elem = exports.get_stream_li(stream_id);
|
2017-06-03 00:13:57 +02:00
|
|
|
if (!unread_count_elem) {
|
2017-06-03 14:16:45 +02:00
|
|
|
// This can happen for legitimate reasons, but we warn
|
|
|
|
// just in case.
|
|
|
|
blueslip.warn('stream id no longer in sidebar: ' + stream_id);
|
2017-06-03 00:13:57 +02:00
|
|
|
return;
|
|
|
|
}
|
2017-06-14 00:03:00 +02:00
|
|
|
exports.update_count_in_dom(unread_count_elem, count);
|
2017-05-13 16:47:45 +02:00
|
|
|
}
|
|
|
|
|
2013-05-06 02:54:15 +02:00
|
|
|
exports.update_streams_sidebar = function () {
|
2013-11-22 20:05:34 +01:00
|
|
|
exports.build_stream_list();
|
2018-04-24 16:59:01 +02:00
|
|
|
exports.stream_cursor.redraw();
|
2018-02-12 22:56:37 +01:00
|
|
|
|
2017-04-25 15:25:31 +02:00
|
|
|
if (! narrow_state.active()) {
|
2013-05-06 02:54:15 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-05-14 20:58:11 +02:00
|
|
|
var filter = narrow_state.filter();
|
|
|
|
|
2017-08-11 00:30:23 +02:00
|
|
|
exports.update_stream_sidebar_for_narrow(filter);
|
2013-05-06 02:54:15 +02:00
|
|
|
};
|
|
|
|
|
2013-05-14 03:58:07 +02:00
|
|
|
exports.update_dom_with_unread_counts = function (counts) {
|
|
|
|
// counts.stream_count maps streams to counts
|
2017-05-13 19:26:54 +02:00
|
|
|
counts.stream_count.each(function (count, stream_id) {
|
|
|
|
set_stream_unread_count(stream_id, count);
|
2013-05-14 03:58:07 +02:00
|
|
|
});
|
|
|
|
|
2017-07-31 14:04:20 +02:00
|
|
|
// counts.topic_count maps streams to hashes of topics to counts
|
|
|
|
counts.topic_count.each(function (subject_hash, stream_id) {
|
2013-08-15 16:16:12 +02:00
|
|
|
subject_hash.each(function (count, subject) {
|
2017-05-13 19:26:54 +02:00
|
|
|
topic_list.set_count(stream_id, subject, count);
|
2013-05-14 03:58:07 +02:00
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2016-12-02 14:06:06 +01:00
|
|
|
exports.rename_stream = function (sub) {
|
|
|
|
// The sub object is expected to already have the updated name
|
2016-11-11 02:39:22 +01:00
|
|
|
build_stream_sidebar_row(sub);
|
2017-05-14 22:48:14 +02:00
|
|
|
exports.update_streams_sidebar(); // big hammer
|
2013-10-21 22:26:19 +02:00
|
|
|
};
|
|
|
|
|
2016-10-26 06:56:10 +02:00
|
|
|
exports.refresh_pinned_or_unpinned_stream = function (sub) {
|
|
|
|
// Pinned/unpinned streams require re-ordering.
|
|
|
|
// We use kind of brute force now, which is probably fine.
|
2016-11-11 02:39:22 +01:00
|
|
|
build_stream_sidebar_row(sub);
|
2016-07-01 07:26:09 +02:00
|
|
|
exports.update_streams_sidebar();
|
2017-05-02 01:22:18 +02:00
|
|
|
|
|
|
|
// Only scroll pinned topics into view. If we're unpinning
|
|
|
|
// a topic, we may be literally trying to get it out of
|
|
|
|
// our sight.
|
|
|
|
if (sub.pin_to_top) {
|
2017-05-13 17:41:10 +02:00
|
|
|
var stream_li = exports.get_stream_li(sub.stream_id);
|
2017-06-03 00:13:57 +02:00
|
|
|
if (!stream_li) {
|
|
|
|
blueslip.error('passed in bad stream id ' + sub.stream_id);
|
|
|
|
return;
|
|
|
|
}
|
2017-08-10 12:32:01 +02:00
|
|
|
exports.scroll_stream_into_view(stream_li);
|
2017-05-02 01:22:18 +02:00
|
|
|
}
|
2016-07-01 07:26:09 +02:00
|
|
|
};
|
|
|
|
|
2017-08-11 00:30:23 +02:00
|
|
|
function clear_topics() {
|
|
|
|
topic_list.close();
|
|
|
|
exports.show_all_streams();
|
|
|
|
}
|
|
|
|
|
2017-08-10 18:31:17 +02:00
|
|
|
exports.get_sidebar_stream_topic_info = function (filter) {
|
|
|
|
var result = {
|
|
|
|
stream_id: undefined,
|
|
|
|
topic_selected: false,
|
|
|
|
};
|
|
|
|
|
2017-05-14 20:30:15 +02:00
|
|
|
var op_stream = filter.operands('stream');
|
2017-08-10 18:31:17 +02:00
|
|
|
if (op_stream.length === 0) {
|
|
|
|
return result;
|
2017-05-14 20:30:15 +02:00
|
|
|
}
|
2017-08-10 18:31:17 +02:00
|
|
|
|
|
|
|
var stream_name = op_stream[0];
|
|
|
|
var stream_id = stream_data.get_stream_id(stream_name);
|
|
|
|
|
|
|
|
if (!stream_id) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!stream_data.id_is_subscribed(stream_id)) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
result.stream_id = stream_id;
|
|
|
|
|
|
|
|
var op_subject = filter.operands('topic');
|
|
|
|
result.topic_selected = (op_subject.length === 1);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
};
|
|
|
|
|
2017-08-12 16:48:34 +02:00
|
|
|
function deselect_stream_items() {
|
|
|
|
$("ul#stream_filters li").removeClass('active-filter active-sub-filter');
|
|
|
|
}
|
|
|
|
|
2017-08-11 00:30:23 +02:00
|
|
|
exports.update_stream_sidebar_for_narrow = function (filter) {
|
2017-08-10 18:31:17 +02:00
|
|
|
var info = exports.get_sidebar_stream_topic_info(filter);
|
|
|
|
|
2017-08-12 16:48:34 +02:00
|
|
|
deselect_stream_items();
|
|
|
|
|
2017-08-10 18:31:17 +02:00
|
|
|
var stream_id = info.stream_id;
|
|
|
|
|
|
|
|
if (!stream_id) {
|
2017-08-11 00:30:23 +02:00
|
|
|
clear_topics();
|
2017-08-10 18:31:17 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var stream_li = exports.get_stream_li(stream_id);
|
|
|
|
|
|
|
|
if (!stream_li) {
|
|
|
|
// It should be the case then when we have a subscribed
|
|
|
|
// stream, there will always be a stream list item
|
|
|
|
// corresponding to that stream in our sidebar. We have
|
|
|
|
// evidence that this assumption breaks down for some users,
|
|
|
|
// but we are not clear why it happens.
|
|
|
|
blueslip.error('No stream_li for subscribed stream ' + stream_id);
|
2017-08-11 00:30:23 +02:00
|
|
|
clear_topics();
|
2017-08-10 18:31:17 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!info.topic_selected) {
|
|
|
|
stream_li.addClass('active-filter');
|
|
|
|
}
|
|
|
|
|
2017-08-11 00:30:23 +02:00
|
|
|
if (stream_id !== topic_list.active_stream_id()) {
|
|
|
|
clear_topics();
|
|
|
|
}
|
|
|
|
|
2017-08-10 18:31:17 +02:00
|
|
|
topic_list.rebuild(stream_li, stream_id);
|
|
|
|
|
|
|
|
return stream_li;
|
2017-05-14 20:30:15 +02:00
|
|
|
};
|
|
|
|
|
2017-08-12 16:49:10 +02:00
|
|
|
exports.handle_narrow_activated = function (filter) {
|
|
|
|
var stream_li = exports.update_stream_sidebar_for_narrow(filter);
|
|
|
|
if (stream_li) {
|
|
|
|
exports.scroll_stream_into_view(stream_li);
|
|
|
|
}
|
|
|
|
// Update scrollbar size.
|
2018-03-15 17:02:29 +01:00
|
|
|
ui.update_scrollbar($("#stream-filters-container"));
|
2017-08-12 16:49:10 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
exports.handle_narrow_deactivated = function () {
|
|
|
|
deselect_stream_items();
|
|
|
|
clear_topics();
|
|
|
|
};
|
|
|
|
|
2018-04-15 12:29:17 +02:00
|
|
|
function focus_stream_filter(e) {
|
2018-04-24 16:59:01 +02:00
|
|
|
exports.stream_cursor.reset();
|
2018-04-15 12:29:17 +02:00
|
|
|
e.stopPropagation();
|
|
|
|
}
|
|
|
|
|
|
|
|
function keydown_enter_key() {
|
2018-04-24 16:59:01 +02:00
|
|
|
var stream_id = exports.stream_cursor.get_key();
|
|
|
|
|
|
|
|
if (stream_id === undefined) {
|
|
|
|
// This can happen for empty searches, no need to warn.
|
|
|
|
return;
|
|
|
|
}
|
2018-04-15 12:29:17 +02:00
|
|
|
|
2018-04-24 16:59:01 +02:00
|
|
|
var sub = stream_data.get_sub_by_id(stream_id);
|
2018-04-15 12:29:17 +02:00
|
|
|
|
2018-04-24 16:59:01 +02:00
|
|
|
if (sub === undefined) {
|
|
|
|
blueslip.error('Unknown stream_id for search/enter: ' + stream_id);
|
|
|
|
return;
|
2018-04-15 12:29:17 +02:00
|
|
|
}
|
|
|
|
|
2018-04-24 16:59:01 +02:00
|
|
|
if (overlays.is_active()) {
|
|
|
|
ui_util.change_tab_to('#home');
|
|
|
|
}
|
|
|
|
exports.clear_and_hide_search();
|
|
|
|
narrow.by('stream', sub.name, {trigger: 'sidebar enter key'});
|
2018-04-15 12:29:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function actually_update_streams_for_search() {
|
|
|
|
exports.update_streams_sidebar();
|
|
|
|
resize.resize_page_components();
|
2018-04-24 16:59:01 +02:00
|
|
|
exports.stream_cursor.reset();
|
2018-04-15 12:29:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
var update_streams_for_search = _.throttle(actually_update_streams_for_search, 50);
|
|
|
|
|
2017-06-02 00:36:28 +02:00
|
|
|
exports.initialize = function () {
|
2016-10-27 03:47:05 +02:00
|
|
|
// TODO, Eventually topic_list won't be a big singleton,
|
|
|
|
// and we can create more component-based click handlers for
|
|
|
|
// each stream.
|
|
|
|
topic_list.set_click_handlers({
|
|
|
|
zoom_in: zoom_in,
|
2017-01-12 00:17:43 +01:00
|
|
|
zoom_out: zoom_out,
|
2016-10-27 03:47:05 +02:00
|
|
|
});
|
|
|
|
|
2013-07-25 22:48:55 +02:00
|
|
|
$(document).on('subscription_add_done.zulip', function (event) {
|
2016-10-17 20:02:32 +02:00
|
|
|
exports.create_sidebar_row(event.sub);
|
2013-11-22 20:05:34 +01:00
|
|
|
exports.build_stream_list();
|
2018-04-24 16:59:01 +02:00
|
|
|
exports.stream_cursor.redraw();
|
2013-05-06 02:54:15 +02:00
|
|
|
});
|
|
|
|
|
2013-07-25 22:48:55 +02:00
|
|
|
$(document).on('subscription_remove_done.zulip', function (event) {
|
2017-02-17 18:45:14 +01:00
|
|
|
exports.remove_sidebar_row(event.sub.stream_id);
|
2013-05-06 02:54:15 +02:00
|
|
|
});
|
2013-11-22 22:25:31 +01:00
|
|
|
|
2017-02-17 18:45:14 +01:00
|
|
|
|
2013-11-22 22:25:31 +01:00
|
|
|
$('#stream_filters').on('click', 'li .subscription_block', function (e) {
|
|
|
|
if (e.metaKey || e.ctrlKey) {
|
|
|
|
return;
|
|
|
|
}
|
2017-05-27 15:40:54 +02:00
|
|
|
if (overlays.is_active()) {
|
2017-03-18 21:35:35 +01:00
|
|
|
ui_util.change_tab_to('#home');
|
2013-11-22 22:25:31 +01:00
|
|
|
}
|
2017-08-17 01:32:39 +02:00
|
|
|
var stream_id = $(e.target).parents('li').attr('data-stream-id');
|
|
|
|
var sub = stream_data.get_sub_by_id(stream_id);
|
2016-12-19 23:38:19 +01:00
|
|
|
popovers.hide_all();
|
2018-04-23 06:02:11 +02:00
|
|
|
narrow.by('stream', sub.name, {trigger: 'sidebar'});
|
2013-11-22 22:25:31 +01:00
|
|
|
|
2018-01-25 20:44:30 +01:00
|
|
|
exports.clear_and_hide_search();
|
|
|
|
|
2013-11-22 22:25:31 +01:00
|
|
|
e.preventDefault();
|
|
|
|
e.stopPropagation();
|
|
|
|
});
|
|
|
|
|
2018-04-15 12:29:17 +02:00
|
|
|
$('#clear_search_stream_button').on('click', exports.clear_search);
|
2016-06-13 22:06:12 +02:00
|
|
|
|
2018-04-15 12:29:17 +02:00
|
|
|
$("#streams_header").expectOne().click(function (e) {
|
|
|
|
exports.toggle_filter_displayed(e);
|
|
|
|
});
|
2018-04-24 16:59:01 +02:00
|
|
|
|
|
|
|
exports.stream_cursor = list_cursor({
|
|
|
|
list: {
|
|
|
|
container: $('#stream-filters-container'),
|
|
|
|
find_li: function (opts) {
|
|
|
|
var stream_id = opts.key;
|
|
|
|
var li = exports.get_stream_li(stream_id);
|
|
|
|
return li;
|
|
|
|
},
|
|
|
|
first_key: stream_sort.first_stream_id,
|
|
|
|
prev_key: stream_sort.prev_stream_id,
|
|
|
|
next_key: stream_sort.next_stream_id,
|
|
|
|
},
|
|
|
|
highlight_class: 'highlighted_stream',
|
|
|
|
});
|
|
|
|
|
|
|
|
var $search_input = $('.stream-list-filter').expectOne();
|
|
|
|
|
|
|
|
keydown_util.handle({
|
|
|
|
elem: $search_input,
|
|
|
|
handlers: {
|
|
|
|
enter_key: function () {
|
|
|
|
keydown_enter_key();
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
up_arrow: function () {
|
|
|
|
exports.stream_cursor.prev();
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
down_arrow: function () {
|
|
|
|
exports.stream_cursor.next();
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
$search_input.on('click', focus_stream_filter);
|
|
|
|
$search_input.on('focusout', exports.stream_cursor.clear);
|
|
|
|
$search_input.on('input', update_streams_for_search);
|
2018-04-15 12:29:17 +02:00
|
|
|
};
|
2016-06-13 22:06:12 +02:00
|
|
|
|
|
|
|
exports.searching = function () {
|
|
|
|
return $('.stream-list-filter').expectOne().is(':focus');
|
|
|
|
};
|
|
|
|
|
|
|
|
exports.escape_search = function () {
|
|
|
|
var filter = $('.stream-list-filter').expectOne();
|
|
|
|
if (filter.val() === '') {
|
|
|
|
exports.clear_and_hide_search();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
filter.val('');
|
|
|
|
update_streams_for_search();
|
|
|
|
};
|
|
|
|
|
2017-04-05 14:18:35 +02:00
|
|
|
exports.clear_search = function () {
|
|
|
|
var filter = $('.stream-list-filter').expectOne();
|
|
|
|
if (filter.val() === '') {
|
|
|
|
exports.clear_and_hide_search();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
filter.val('');
|
|
|
|
filter.blur();
|
|
|
|
update_streams_for_search();
|
|
|
|
};
|
|
|
|
|
2016-06-13 22:06:12 +02:00
|
|
|
exports.initiate_search = function () {
|
|
|
|
var filter = $('.stream-list-filter').expectOne();
|
2017-04-05 14:18:35 +02:00
|
|
|
filter.parent().removeClass('notdisplayed');
|
2017-10-12 05:07:12 +02:00
|
|
|
if (!$(".app-main .column-left").hasClass("expanded")) {
|
|
|
|
popovers.hide_all();
|
|
|
|
stream_popover.show_streamlist_sidebar();
|
|
|
|
}
|
|
|
|
filter.focus();
|
2018-02-12 22:56:37 +01:00
|
|
|
|
2018-04-24 16:59:01 +02:00
|
|
|
exports.stream_cursor.reset();
|
2016-06-13 22:06:12 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
exports.clear_and_hide_search = function () {
|
|
|
|
var filter = $('.stream-list-filter');
|
|
|
|
if (filter.val() !== '') {
|
|
|
|
filter.val('');
|
|
|
|
update_streams_for_search();
|
|
|
|
}
|
2018-04-24 16:59:01 +02:00
|
|
|
exports.stream_cursor.clear();
|
2016-06-13 22:06:12 +02:00
|
|
|
filter.blur();
|
2017-04-05 14:18:35 +02:00
|
|
|
filter.parent().addClass('notdisplayed');
|
2016-06-13 22:06:12 +02:00
|
|
|
};
|
|
|
|
|
2017-09-09 15:00:00 +02:00
|
|
|
exports.toggle_filter_displayed = function (e) {
|
2017-04-05 14:18:35 +02:00
|
|
|
if ($('#stream-filters-container .input-append.notdisplayed').length === 0) {
|
2016-06-13 22:06:12 +02:00
|
|
|
exports.clear_and_hide_search();
|
|
|
|
} else {
|
|
|
|
exports.initiate_search();
|
|
|
|
}
|
|
|
|
e.preventDefault();
|
2017-09-09 15:00:00 +02:00
|
|
|
};
|
2016-06-13 22:06:12 +02:00
|
|
|
|
2017-08-10 12:32:01 +02:00
|
|
|
exports.scroll_stream_into_view = function (stream_li) {
|
2017-05-02 01:08:31 +02:00
|
|
|
var container = $('#stream-filters-container');
|
|
|
|
|
|
|
|
if (stream_li.length !== 1) {
|
|
|
|
blueslip.error('Invalid stream_li was passed in');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-04-24 14:09:16 +02:00
|
|
|
scroll_util.scroll_element_into_container(stream_li, container);
|
2017-05-02 01:08:31 +02:00
|
|
|
};
|
|
|
|
|
2013-05-06 02:54:15 +02:00
|
|
|
return exports;
|
|
|
|
}());
|
2013-11-26 16:39:58 +01:00
|
|
|
if (typeof module !== 'undefined') {
|
|
|
|
module.exports = stream_list;
|
|
|
|
}
|