mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	This removes an unnecessary layer of indirection and allows webpack to catch filename mistakes. Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
		
			
				
	
	
		
			346 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			346 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
var render_more_topics = require('../templates/more_topics.hbs');
 | 
						|
var render_topic_list_item = require('../templates/topic_list_item.hbs');
 | 
						|
var Dict = require('./dict').Dict;
 | 
						|
 | 
						|
var topic_list = (function () {
 | 
						|
 | 
						|
var exports = {};
 | 
						|
 | 
						|
/*
 | 
						|
    Track all active widgets with a Dict.
 | 
						|
 | 
						|
    (We have at max one for now, but we may
 | 
						|
    eventually allow multiple streams to be
 | 
						|
    expanded.)
 | 
						|
*/
 | 
						|
 | 
						|
var active_widgets = new Dict();
 | 
						|
 | 
						|
// We know whether we're zoomed or not.
 | 
						|
var zoomed = false;
 | 
						|
 | 
						|
exports.remove_expanded_topics = function () {
 | 
						|
    stream_popover.hide_topic_popover();
 | 
						|
 | 
						|
    _.each(active_widgets.values(), function (widget) {
 | 
						|
        widget.remove();
 | 
						|
    });
 | 
						|
 | 
						|
    active_widgets.clear();
 | 
						|
};
 | 
						|
 | 
						|
exports.close = function () {
 | 
						|
    zoomed = false;
 | 
						|
    exports.remove_expanded_topics();
 | 
						|
};
 | 
						|
 | 
						|
exports.zoom_out = function () {
 | 
						|
    zoomed = false;
 | 
						|
 | 
						|
    var stream_ids = active_widgets.keys();
 | 
						|
 | 
						|
    if (stream_ids.length !== 1) {
 | 
						|
        blueslip.error('Unexpected number of topic lists to zoom out.');
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    var stream_id = stream_ids[0];
 | 
						|
    var widget = active_widgets.get(stream_id);
 | 
						|
    var parent_widget = widget.get_parent();
 | 
						|
 | 
						|
    exports.rebuild(parent_widget, stream_id);
 | 
						|
};
 | 
						|
 | 
						|
function update_unread_count(unread_count_elem, count) {
 | 
						|
    // unread_count_elem is a jquery element...we expect DOM
 | 
						|
    // to look like this:
 | 
						|
    //   <div class="topic-unread-count {{#if is_zero}}zero_count{{/if}}">
 | 
						|
    //        <div class="value">{{unread}}</div>
 | 
						|
    //   </div>
 | 
						|
    var value_span = unread_count_elem.find('.value');
 | 
						|
 | 
						|
    if (value_span.length === 0) {
 | 
						|
        blueslip.error('malformed dom for unread count');
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (count === 0) {
 | 
						|
        unread_count_elem.hide();
 | 
						|
        value_span.text('');
 | 
						|
    } else {
 | 
						|
        unread_count_elem.removeClass("zero_count");
 | 
						|
        unread_count_elem.show();
 | 
						|
        value_span.text(count);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
exports.set_count = function (stream_id, topic, count) {
 | 
						|
    var widget = active_widgets.get(stream_id);
 | 
						|
 | 
						|
    if (widget === undefined) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    widget.set_count(topic, count);
 | 
						|
};
 | 
						|
 | 
						|
exports.widget = function (parent_elem, my_stream_id) {
 | 
						|
    var self = {};
 | 
						|
 | 
						|
    self.build_list = function () {
 | 
						|
        self.topic_items = new Dict({fold_case: true});
 | 
						|
 | 
						|
        var max_topics = 5;
 | 
						|
        var topic_names = topic_data.get_recent_names(my_stream_id);
 | 
						|
 | 
						|
        var ul = $('<ul class="topic-list">');
 | 
						|
 | 
						|
        _.each(topic_names, function (topic_name, idx) {
 | 
						|
            var num_unread = unread.num_unread_for_topic(my_stream_id, topic_name);
 | 
						|
 | 
						|
            if (!zoomed) {
 | 
						|
                // Show the most recent topics, as well as any with unread messages
 | 
						|
                var show_topic = idx < max_topics || num_unread > 0 ||
 | 
						|
                                 self.active_topic === topic_name.toLowerCase();
 | 
						|
 | 
						|
                if (!show_topic) {
 | 
						|
                    return;
 | 
						|
                }
 | 
						|
            }
 | 
						|
 | 
						|
            var topic_info = {
 | 
						|
                topic_name: topic_name,
 | 
						|
                unread: num_unread,
 | 
						|
                is_zero: num_unread === 0,
 | 
						|
                is_muted: muting.is_topic_muted(my_stream_id, topic_name),
 | 
						|
                url: hash_util.by_stream_topic_uri(my_stream_id, topic_name),
 | 
						|
            };
 | 
						|
            var li = $(render_topic_list_item(topic_info));
 | 
						|
            self.topic_items.set(topic_name, li);
 | 
						|
            ul.append(li);
 | 
						|
        });
 | 
						|
 | 
						|
        // Now, we decide whether we need to show the "more topics"
 | 
						|
        // widget.  We need it if there are at least 5 topics in the
 | 
						|
        // frontend's cache, or if we (possibly) don't have all
 | 
						|
        // historical topics in the browser's cache.
 | 
						|
        var show_more = self.build_more_topics_section();
 | 
						|
        var sub = stream_data.get_sub_by_id(my_stream_id);
 | 
						|
 | 
						|
        if (topic_names.length > max_topics || !stream_data.all_topics_in_cache(sub)) {
 | 
						|
            ul.append(show_more);
 | 
						|
        }
 | 
						|
        return ul;
 | 
						|
    };
 | 
						|
 | 
						|
    self.build_more_topics_section = function () {
 | 
						|
        var show_more_html = render_more_topics();
 | 
						|
        return $(show_more_html);
 | 
						|
    };
 | 
						|
 | 
						|
    self.get_parent = function () {
 | 
						|
        return parent_elem;
 | 
						|
    };
 | 
						|
 | 
						|
    self.get_stream_id = function () {
 | 
						|
        return my_stream_id;
 | 
						|
    };
 | 
						|
 | 
						|
    self.get_dom = function () {
 | 
						|
        return self.dom;
 | 
						|
    };
 | 
						|
 | 
						|
    self.remove = function () {
 | 
						|
        self.dom.remove();
 | 
						|
    };
 | 
						|
 | 
						|
    self.num_items = function () {
 | 
						|
        return self.topic_items.num_items();
 | 
						|
    };
 | 
						|
 | 
						|
    self.set_count = function (topic, count) {
 | 
						|
        if (!self.topic_items.has(topic)) {
 | 
						|
            // This can happen for truncated topic lists.  No need
 | 
						|
            // to warn about it.
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        var topic_li = self.topic_items.get(topic);
 | 
						|
        var unread_count_elem = topic_li.find('.topic-unread-count').expectOne();
 | 
						|
 | 
						|
        update_unread_count(unread_count_elem, count);
 | 
						|
    };
 | 
						|
 | 
						|
    self.activate_topic = function () {
 | 
						|
        var li = self.topic_items.get(self.active_topic);
 | 
						|
        if (li) {
 | 
						|
            li.addClass('active-sub-filter');
 | 
						|
        }
 | 
						|
    };
 | 
						|
 | 
						|
    self.show_spinner = function () {
 | 
						|
        // The spinner will go away once we get results and redraw
 | 
						|
        // the whole list.
 | 
						|
        var spinner = self.dom.find('.searching-for-more-topics');
 | 
						|
        spinner.show();
 | 
						|
    };
 | 
						|
 | 
						|
    self.show_no_more_topics = function () {
 | 
						|
        var elem = self.dom.find('.no-more-topics-found');
 | 
						|
        elem.show();
 | 
						|
        self.no_more_topics = true;
 | 
						|
    };
 | 
						|
 | 
						|
    self.build = function (active_topic, no_more_topics) {
 | 
						|
        self.no_more_topics = false; // for now
 | 
						|
 | 
						|
        if (active_topic) {
 | 
						|
            active_topic = active_topic.toLowerCase();
 | 
						|
        }
 | 
						|
        self.active_topic = active_topic;
 | 
						|
 | 
						|
        self.dom = self.build_list();
 | 
						|
 | 
						|
        parent_elem.append(self.dom);
 | 
						|
 | 
						|
        // We often rebuild an entire topic list, and the
 | 
						|
        // caller will pass us in no_more_topics as true
 | 
						|
        // if we were showing "No more topics found" from
 | 
						|
        // the initial zooming.
 | 
						|
        if (no_more_topics) {
 | 
						|
            self.show_no_more_topics();
 | 
						|
        }
 | 
						|
 | 
						|
        if (active_topic) {
 | 
						|
            self.activate_topic();
 | 
						|
        }
 | 
						|
    };
 | 
						|
 | 
						|
    return self;
 | 
						|
};
 | 
						|
 | 
						|
exports.active_stream_id = function () {
 | 
						|
    var stream_ids = active_widgets.keys();
 | 
						|
 | 
						|
    if (stream_ids.length !== 1) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    return stream_ids[0];
 | 
						|
};
 | 
						|
 | 
						|
exports.get_stream_li = function () {
 | 
						|
    var widgets = active_widgets.values();
 | 
						|
 | 
						|
    if (widgets.length !== 1) {
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    var stream_li = widgets[0].get_parent();
 | 
						|
    return stream_li;
 | 
						|
};
 | 
						|
 | 
						|
exports.need_to_show_no_more_topics = function (stream_id) {
 | 
						|
    // This function is important, and the use case here is kind of
 | 
						|
    // subtle.  We do complete redraws of the topic list when new
 | 
						|
    // messages come in, and we don't want to overwrite the
 | 
						|
    // "no more topics" error message.
 | 
						|
    if (!zoomed) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!active_widgets.has(stream_id)) {
 | 
						|
        return false;
 | 
						|
    }
 | 
						|
 | 
						|
    var widget = active_widgets.get(stream_id);
 | 
						|
 | 
						|
    return widget.no_more_topics;
 | 
						|
};
 | 
						|
 | 
						|
exports.rebuild = function (stream_li, stream_id) {
 | 
						|
    var active_topic = narrow_state.topic();
 | 
						|
    var no_more_topics = exports.need_to_show_no_more_topics(stream_id);
 | 
						|
 | 
						|
    exports.remove_expanded_topics();
 | 
						|
    var widget = exports.widget(stream_li, stream_id);
 | 
						|
    widget.build(active_topic, no_more_topics);
 | 
						|
 | 
						|
    active_widgets.set(stream_id, widget);
 | 
						|
};
 | 
						|
 | 
						|
// For zooming, we only do topic-list stuff here...let stream_list
 | 
						|
// handle hiding/showing the non-narrowed streams
 | 
						|
exports.zoom_in = function () {
 | 
						|
    zoomed = true;
 | 
						|
 | 
						|
    var stream_id = exports.active_stream_id();
 | 
						|
    if (!stream_id) {
 | 
						|
        blueslip.error('Cannot find widget for topic history zooming.');
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    var active_widget = active_widgets.get(stream_id);
 | 
						|
 | 
						|
    var before_count = active_widget.num_items();
 | 
						|
 | 
						|
    function on_success() {
 | 
						|
        if (!active_widgets.has(stream_id)) {
 | 
						|
            blueslip.warn('User re-narrowed before topic history was returned.');
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        if (!zoomed) {
 | 
						|
            blueslip.warn('User zoomed out before topic history was returned.');
 | 
						|
            // Note that we could attempt to re-draw the zoomed out topic list
 | 
						|
            // here, given that we have more history, but that might be more
 | 
						|
            // confusing than helpful to a user who is likely trying to browse
 | 
						|
            // other streams.
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        var widget = active_widgets.get(stream_id);
 | 
						|
 | 
						|
        exports.rebuild(widget.get_parent(), stream_id);
 | 
						|
 | 
						|
        var after_count = widget.num_items();
 | 
						|
 | 
						|
        if (after_count === before_count) {
 | 
						|
            widget.show_no_more_topics();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    ui.get_scroll_element($('#stream-filters-container')).scrollTop(0);
 | 
						|
    active_widget.show_spinner();
 | 
						|
    topic_data.get_server_history(stream_id, on_success);
 | 
						|
};
 | 
						|
 | 
						|
exports.initialize = function () {
 | 
						|
    $('#stream_filters').on('click', '.topic-box', function (e) {
 | 
						|
        if (e.metaKey || e.ctrlKey) {
 | 
						|
            return;
 | 
						|
        }
 | 
						|
 | 
						|
        // In a more componentized world, we would delegate some
 | 
						|
        // of this stuff back up to our parents.
 | 
						|
 | 
						|
        var stream_id = $(e.target).parents('.narrow-filter').attr('data-stream-id');
 | 
						|
        var sub = stream_data.get_sub_by_id(stream_id);
 | 
						|
        var topic = $(e.target).parents('li').attr('data-topic-name');
 | 
						|
 | 
						|
        narrow.activate([
 | 
						|
            {operator: 'stream', operand: sub.name},
 | 
						|
            {operator: 'topic', operand: topic}],
 | 
						|
                        {trigger: 'sidebar'});
 | 
						|
 | 
						|
        e.preventDefault();
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
return exports;
 | 
						|
}());
 | 
						|
if (typeof module !== 'undefined') {
 | 
						|
    module.exports = topic_list;
 | 
						|
}
 | 
						|
window.topic_list = topic_list;
 |