var stream_data = (function () { var exports = {}; // The stream_info variable maps stream names to stream properties objects // Call clear_subscriptions() to initialize it. var stream_info; var subs_by_stream_id; exports.clear_subscriptions = function () { stream_info = new Dict({fold_case: true}); subs_by_stream_id = new Dict(); }; exports.recent_subjects = new Dict({fold_case: true}); exports.clear_subscriptions(); exports.add_sub = function (stream_name, sub) { stream_info.set(stream_name, sub); subs_by_stream_id.set(sub.stream_id, sub); }; exports.get_sub = function (stream_name) { return stream_info.get(stream_name); }; exports.get_sub_by_id = function (stream_id) { return subs_by_stream_id.get(stream_id); }; exports.delete_sub = function (stream_name) { stream_info.del(stream_name); }; exports.subscribed_subs = function () { return _.where(stream_info.values(), {subscribed: true}); }; exports.subscribed_streams = function () { return _.pluck(exports.subscribed_subs(), 'name'); }; exports.get_colors = function () { return _.pluck(exports.subscribed_subs(), 'color'); }; exports.all_subscribed_streams_are_in_home_view = function () { return _.every(exports.subscribed_subs(), function (sub) { return sub.in_home_view; } ); }; exports.home_view_stream_names = function () { var home_view_subs = _.filter(exports.subscribed_subs(), function (sub) { return sub.in_home_view; } ); return _.map(home_view_subs, function (sub) { return sub.name; }); }; exports.canonicalized_name = function (stream_name) { return stream_name.toString().toLowerCase(); }; exports.get_color = function (stream_name) { var sub = exports.get_sub(stream_name); if (sub === undefined) { return stream_color.default_color; } return sub.color; }; exports.in_home_view = function (stream_name) { var sub = exports.get_sub(stream_name); return sub !== undefined && sub.in_home_view; }; exports.is_subscribed = function (stream_name) { var sub = exports.get_sub(stream_name); return sub !== undefined && sub.subscribed; }; exports.get_invite_only = function (stream_name) { var sub = exports.get_sub(stream_name); if (sub === undefined) { return false; } return sub.invite_only; }; exports.get_name = function (stream_name) { // This returns the actual name of a stream if we are subscribed to // it (i.e "Denmark" vs. "denmark"), while falling thru to // stream_name if we don't have a subscription. (Stream names // are case-insensitive, but we try to display the actual name // when we know it.) var sub = exports.get_sub(stream_name); if (sub === undefined) { return stream_name; } return sub.name; }; exports.set_subscribers = function (sub, emails) { sub.subscribers = Dict.from_array(emails || [], {fold_case: true}); }; exports.add_subscriber = function (stream_name, user_email) { var sub = exports.get_sub(stream_name); if (typeof sub === 'undefined') { blueslip.warn("We got an add_subscriber call for a non-existent stream."); return; } sub.subscribers.set(user_email, true); }; exports.remove_subscriber = function (stream_name, user_email) { var sub = exports.get_sub(stream_name); if (typeof sub === 'undefined' || !sub.subscribed) { // If we're not subscribed, we don't track this, and shouldn't // get these events. Likewise, if we don't know about the stream, // we don't want to track this. blueslip.warn("We got a remove_subscriber call for a non-existent or unsubscribed stream."); return; } sub.subscribers.del(user_email); }; exports.user_is_subscribed = function (stream_name, user_email) { var sub = exports.get_sub(stream_name); if (typeof sub === 'undefined' || !sub.subscribed) { // If we don't know about the stream, or we ourselves are not // subscribed, we can't keep track of the subscriber list in general, // so we return undefined (treated as falsy if not explicitly handled). blueslip.warn("We got a user_is_subscribed call for a non-existent or unsubscribed stream."); return undefined; } return sub.subscribers.has(user_email); }; exports.create_streams = function (streams) { _.each(streams, function (stream) { var attrs = _.defaults(stream, { subscribed: false }); exports.create_sub_from_server_data(stream.name, attrs); }); }; exports.create_sub_from_server_data = function (stream_name, attrs) { var sub = exports.get_sub(stream_name); if (sub !== undefined) { // We've already created this subscription, no need to continue. return sub; } if (!attrs.stream_id) { // fail fast (blueslip.fatal will throw an error on our behalf) blueslip.fatal("We cannot create a sub without a stream_id"); return; // this line is never actually reached } // Our internal data structure for subscriptions is mostly plain dictionaries, // so we just reuse the attrs that are passed in to us, but we encapsulate how // we handle subscribers. var subscriber_emails = attrs.subscribers; var raw_attrs = _.omit(attrs, 'subscribers'); sub = _.defaults(raw_attrs, { name: stream_name, render_subscribers: !page_params.is_zephyr_mirror_realm || attrs.invite_only === true, subscribed: true, in_home_view: true, invite_only: false, desktop_notifications: page_params.stream_desktop_notifications_enabled, audible_notifications: page_params.stream_sounds_enabled, description: '' }); exports.set_subscribers(sub, subscriber_emails); if (!sub.color) { var used_colors = exports.get_colors(); sub.color = stream_color.pick_color(used_colors); } exports.add_sub(stream_name, sub); return sub; }; exports.receives_desktop_notifications = function (stream_name) { var sub = exports.get_sub(stream_name); if (sub === undefined) { return false; } return sub.desktop_notifications; }; exports.receives_audible_notifications = function (stream_name) { var sub = exports.get_sub(stream_name); if (sub === undefined) { return false; } return sub.audible_notifications; }; exports.add_admin_options = function (sub) { return _.extend(sub, { 'is_admin': page_params.is_admin, 'can_make_public': page_params.is_admin && sub.invite_only && sub.subscribed, 'can_make_private': page_params.is_admin && !sub.invite_only }); }; exports.get_streams_for_settings_page = function (public_streams) { // Build up our list of subscribed streams from the data we already have. var subscribed_rows = exports.subscribed_subs(); // To avoid dups, build a set of names we already subscribed to. var subscribed_set = new Dict({fold_case: true}); _.each(subscribed_rows, function (sub) { subscribed_set.set(sub.name, true); }); // Right now the back end gives us all public streams; we really only // need to add the ones we haven't already subscribed to. var unsubscribed_streams = _.reject(public_streams.streams, function (stream) { return subscribed_set.has(stream.name); }); // Build up our list of unsubscribed rows. var unsubscribed_rows = []; _.each(unsubscribed_streams, function (stream) { var sub = exports.get_sub(stream.name); if (!sub) { sub = exports.create_sub_from_server_data( stream.name, _.extend({subscribed: false}, stream)); } unsubscribed_rows.push(sub); }); // Sort and combine all our streams. function by_name(a,b) { return util.strcmp(a.name, b.name); } subscribed_rows.sort(by_name); unsubscribed_rows.sort(by_name); var all_subs = subscribed_rows.concat(unsubscribed_rows); // Add in admin options. var sub_rows = []; _.each(all_subs, function (sub) { sub = exports.add_admin_options(sub); sub_rows.push(sub); }); return sub_rows; }; exports.initialize_from_page_params = function () { function populate_subscriptions(subs, subscribed) { subs.forEach(function (sub) { var stream_name = sub.name; sub.subscribed = subscribed; // When we get subscriber lists from the back end, // they are sent as user ids to save bandwidth, // but the legacy JS code wants emails. if (sub.subscribers) { sub.subscribers = _.map(sub.subscribers, function (subscription) { return page_params.email_dict[subscription]; }); } exports.create_sub_from_server_data(stream_name, sub); }); } populate_subscriptions(page_params.subbed_info, true); populate_subscriptions(page_params.unsubbed_info, false); populate_subscriptions(page_params.neversubbed_info, false); // Garbage collect data structures that were only used for initialization. delete page_params.subbed_info; delete page_params.unsubbed_info; delete page_params.neversubbed_info; delete page_params.email_dict; }; return exports; }()); if (typeof module !== 'undefined') { module.exports = stream_data; }