From 8e2e406404184d67a3e0421de0e180b86be4b333 Mon Sep 17 00:00:00 2001 From: Steve Howell Date: Sun, 5 Mar 2017 08:28:40 -0800 Subject: [PATCH] refactor: Extract stream_popover.js. This module handles the popovers in the stream list--one for stream actions and another for topic-specific actions. The extraction was mostly straightforward, but I did move some of the code related to the color picker to be more consistent with how I organized the other click handlers. --- .eslintrc.json | 1 + static/js/click_handlers.js | 1 + static/js/pm_list.js | 2 +- static/js/popovers.js | 283 +------------------------------ static/js/stream_popover.js | 322 ++++++++++++++++++++++++++++++++++++ static/js/topic_list.js | 2 +- zproject/settings.py | 1 + 7 files changed, 333 insertions(+), 279 deletions(-) create mode 100644 static/js/stream_popover.js diff --git a/.eslintrc.json b/.eslintrc.json index d844701080..5bb22722e9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -66,6 +66,7 @@ "message_flags": false, "bot_data": false, "stream_list": false, + "stream_popover": false, "narrow": false, "admin": false, "stream_data": false, diff --git a/static/js/click_handlers.js b/static/js/click_handlers.js index 49bdafeed4..6cc745f0d4 100644 --- a/static/js/click_handlers.js +++ b/static/js/click_handlers.js @@ -314,6 +314,7 @@ $(function () { }()); popovers.register_click_handlers(); + stream_popover.register_click_handlers(); notifications.register_click_handlers(); $('body').on('click', '.logout_button', function () { diff --git a/static/js/pm_list.js b/static/js/pm_list.js index 58372c1f08..08c733f505 100644 --- a/static/js/pm_list.js +++ b/static/js/pm_list.js @@ -62,7 +62,7 @@ function set_pm_conversation_count(conversation, count) { } function remove_expanded_private_messages() { - popovers.hide_topic_sidebar_popover(); + stream_popover.hide_topic_popover(); $("ul.expanded_private_messages").remove(); resize.resize_stream_filters_container(); } diff --git a/static/js/popovers.js b/static/js/popovers.js index 5b1a247e15..c86cf11f5e 100644 --- a/static/js/popovers.js +++ b/static/js/popovers.js @@ -37,17 +37,6 @@ var list_of_popovers = []; }($.fn.popover)); -function stream_popover_sub(e) { - // TODO: use data-stream-id in stream list - var stream_name = $(e.currentTarget).parents('ul').attr('data-name'); - var sub = stream_data.get_sub(stream_name); - if (!sub) { - blueslip.error('Unknown stream: ' + stream_name); - return; - } - return sub; -} - function show_message_info_popover(element, id) { var last_popover_elem = current_message_info_popover_elem; popovers.hide_all(); @@ -280,25 +269,6 @@ exports.hide_actions_popover = function () { } }; -exports.topic_ops = { - mute: function (stream, topic) { - popovers.hide_topic_sidebar_popover(); - muting_ui.mute_topic(stream, topic); - muting_ui.persist_and_rerender(); - muting_ui.notify_with_undo_option(stream, topic); - muting_ui.set_up_muted_topics_ui(muting.get_muted_topics()); - }, - // we don't run a unmute_notif function because it isn't an issue as much - // if someone accidentally unmutes a stream rather than if they mute it - // and miss out on info. - unmute: function (stream, topic) { - popovers.hide_topic_sidebar_popover(); - muting_ui.unmute_topic(stream, topic); - muting_ui.persist_and_rerender(); - muting_ui.set_up_muted_topics_ui(muting.get_muted_topics()); - }, -}; - function message_info_popped() { return current_message_info_popover_elem !== undefined; } @@ -325,10 +295,6 @@ exports.hide_userlist_sidebar = function () { $(".app-main .column-right").removeClass("expanded"); }; -exports.hide_streamlist_sidebar = function () { - $(".app-main .column-left").removeClass("expanded"); -}; - exports.hide_pm_list_sidebar = function () { $(".app-main .column-left").removeClass("expanded"); }; @@ -338,34 +304,18 @@ exports.show_userlist_sidebar = function () { resize.resize_page_components(); }; -exports.show_streamlist_sidebar = function () { - $(".app-main .column-left").addClass("expanded"); - resize.resize_page_components(); -}; - exports.show_pm_list_sidebar = function () { $(".app-main .column-left").addClass("expanded"); resize.resize_page_components(); }; -var current_stream_sidebar_elem; -var current_topic_sidebar_elem; var current_user_sidebar_user_id; var current_user_sidebar_popover; - function user_sidebar_popped() { return current_user_sidebar_popover !== undefined; } -function stream_sidebar_popped() { - return current_stream_sidebar_elem !== undefined; -} - -function topic_sidebar_popped() { - return current_topic_sidebar_elem !== undefined; -} - exports.hide_emoji_map_popover = function () { if (emoji_map_is_open) { $('.emoji_popover').css('display', 'none'); @@ -374,20 +324,6 @@ exports.hide_emoji_map_popover = function () { } }; -exports.hide_stream_sidebar_popover = function () { - if (stream_sidebar_popped()) { - $(current_stream_sidebar_elem).popover("destroy"); - current_stream_sidebar_elem = undefined; - } -}; - -exports.hide_topic_sidebar_popover = function () { - if (topic_sidebar_popped()) { - $(current_topic_sidebar_elem).popover("destroy"); - current_topic_sidebar_elem = undefined; - } -}; - exports.hide_user_sidebar_popover = function () { if (user_sidebar_popped()) { // this hide_* method looks different from all the others since @@ -626,155 +562,6 @@ exports.register_click_handlers = function () { }); - $('#stream_filters').on('click', '.topic-sidebar-arrow', function (e) { - var elt = e.target; - - if (topic_sidebar_popped() - && current_topic_sidebar_elem === elt) { - // If the popover is already shown, clicking again should toggle it. - popovers.hide_topic_sidebar_popover(); - e.stopPropagation(); - return; - } - - popovers.hide_all(); - popovers.show_streamlist_sidebar(); - - var stream_name = $(elt).closest('.topic-list').expectOne().attr('data-stream'); - var topic_name = $(elt).closest('li').expectOne().attr('data-name'); - - var is_muted = muting.is_topic_muted(stream_name, topic_name); - var can_mute_topic = !is_muted; - var can_unmute_topic = is_muted; - - var content = templates.render('topic_sidebar_actions', { - stream_name: stream_name, - topic_name: topic_name, - can_mute_topic: can_mute_topic, - can_unmute_topic: can_unmute_topic, - }); - - $(elt).popover({ - content: content, - trigger: "manual", - fixed: true, - }); - - $(elt).popover("show"); - - current_topic_sidebar_elem = elt; - e.stopPropagation(); - }); - - $('body').on('click', '.narrow_to_topic', function (e) { - popovers.hide_topic_sidebar_popover(); - - var row = $(e.currentTarget).closest('.narrow_to_topic').expectOne(); - var stream_name = row.attr('data-stream-name'); - var topic_name = row.attr('data-topic-name'); - - var operators = [ - {operator: 'stream', operand: stream_name}, - {operator: 'topic', operand: topic_name}, - ]; - var opts = {select_first_unread: true, trigger: 'sidebar'}; - narrow.activate(operators, opts); - - e.stopPropagation(); - }); - - $('body').on('click', '.sidebar-popover-mute-topic', function (e) { - var stream = $(e.currentTarget).attr('data-stream-name'); - var topic = $(e.currentTarget).attr('data-topic-name'); - exports.topic_ops.mute(stream, topic); - e.stopPropagation(); - e.preventDefault(); - }); - - $('body').on('click', '.sidebar-popover-unmute-topic', function (e) { - var stream = $(e.currentTarget).attr('data-stream-name'); - var topic = $(e.currentTarget).attr('data-topic-name'); - exports.topic_ops.unmute(stream, topic); - e.stopPropagation(); - e.preventDefault(); - }); - - $('body').on('click', '.sidebar-popover-mark-topic-read', function (e) { - var topic = $(e.currentTarget).attr('data-topic-name'); - var stream = $(e.currentTarget).attr('data-stream-name'); - popovers.hide_topic_sidebar_popover(); - unread_ui.mark_topic_as_read(stream, topic); - e.stopPropagation(); - }); - - $('#stream_filters').on('click', '.stream-sidebar-arrow', function (e) { - var elt = e.target; - if (stream_sidebar_popped() - && current_stream_sidebar_elem === elt) { - // If the popover is already shown, clicking again should toggle it. - popovers.hide_stream_sidebar_popover(); - e.stopPropagation(); - return; - } - - popovers.hide_all(); - popovers.show_streamlist_sidebar(); - - var stream = $(elt).parents('li').attr('data-name'); - - $(elt).popover({ - content: templates.render('stream_sidebar_actions', {stream: stream_data.get_sub(stream)}), - trigger: "manual", - fixed: true, - }); - - // This little function is a workaround for the fact that - // Bootstrap popovers don't properly handle being resized -- - // so after resizing our popover to add in the spectrum color - // picker, we need to adjust its height accordingly. - function update_spectrum(popover, update_func) { - var initial_height = popover[0].offsetHeight; - - var colorpicker = popover.find('.colorpicker-container').find('.colorpicker'); - update_func(colorpicker); - var after_height = popover[0].offsetHeight; - - var popover_root = popover.closest(".popover"); - var current_top_px = parseFloat(popover_root.css('top').replace('px', '')); - var height_delta = - (after_height - initial_height) * 0.5; - - popover_root.css('top', (current_top_px + height_delta) + "px"); - } - - $(elt).popover("show"); - var data_id = stream_data.get_sub(stream).stream_id; - var popover = $('.streams_popover[data-id=' + data_id + ']'); - update_spectrum(popover, function (colorpicker) { - colorpicker.spectrum(stream_color.sidebar_popover_colorpicker_options); - }); - - $('.streams_popover').on('click', '.custom_color', function (e) { - update_spectrum($(e.target).closest('.streams_popover'), function (colorpicker) { - colorpicker.spectrum("destroy"); - colorpicker.spectrum(stream_color.sidebar_popover_colorpicker_options_full); - // In theory this should clean up the old color picker, - // but this seems a bit flaky -- the new colorpicker - // doesn't fire until you click a button, but the buttons - // have been hidden. We work around this by just manually - // fixing it up here. - colorpicker.parent().find('.sp-container').removeClass('sp-buttons-disabled'); - $(e.target).hide(); - }); - - $('.streams_popover').on('click', 'a.sp-cancel', function () { - popovers.hide_stream_sidebar_popover(); - }); - }); - - current_stream_sidebar_elem = elt; - e.stopPropagation(); - }); - $('body').on('click', '.respond_button', function (e) { var textarea = $("#new_message_content"); var msgid = $(e.currentTarget).data("message-id"); @@ -872,65 +659,6 @@ exports.register_click_handlers = function () { e.preventDefault(); }); - $('body').on('click', '.toggle_home', function (e) { - var sub = stream_popover_sub(e); - popovers.hide_stream_sidebar_popover(); - subs.toggle_home(sub); - e.stopPropagation(); - }); - - $('body').on('click', '.narrow_to_stream', function (e) { - var sub = stream_popover_sub(e); - popovers.hide_stream_sidebar_popover(); - narrow.by('stream', sub.name, - {select_first_unread: true, trigger: 'sidebar popover'} - ); - e.stopPropagation(); - }); - - $('body').on('click', '.compose_to_stream', function (e) { - var sub = stream_popover_sub(e); - popovers.hide_stream_sidebar_popover(); - compose.start('stream', {stream: sub.name, trigger: 'sidebar stream actions'}); - e.stopPropagation(); - }); - - $('body').on('click', '.mark_stream_as_read', function (e) { - var sub = stream_popover_sub(e); - popovers.hide_stream_sidebar_popover(); - unread_ui.mark_stream_as_read(sub.name); - e.stopPropagation(); - }); - - $('body').on('click', '.pin_to_top', function (e) { - var sub = stream_popover_sub(e); - popovers.hide_stream_sidebar_popover(); - subs.toggle_pin_to_top_stream(sub); - e.stopPropagation(); - }); - - $('body').on('click', '.open_stream_settings', function (e) { - var sub = stream_popover_sub(e); - popovers.hide_stream_sidebar_popover(); - - window.location.hash = "#subscriptions"; - // the template for subs needs to render. - - subs.onlaunch("narrow_to_row", function () { - $(".stream-row[data-stream-name='" + sub.name + "']").click(); - }, true); - }); - - $("body").on("click", ".popover_sub_unsub_button", function (e) { - $(this).toggleClass("unsub"); - $(this).closest(".popover").fadeOut(500).delay(500).remove(); - - var sub = stream_popover_sub(e); - subs.sub_or_unsub(sub); - e.preventDefault(); - e.stopPropagation(); - }); - (function () { var last_scroll = 0; @@ -953,8 +681,9 @@ exports.register_click_handlers = function () { exports.any_active = function () { // True if any popover (that this module manages) is currently shown. - return popovers.actions_popped() || user_sidebar_popped() || stream_sidebar_popped() || - topic_sidebar_popped() || message_info_popped() || emoji_map_is_open || + return popovers.actions_popped() || user_sidebar_popped() || + stream_popover.stream_popped() || stream_popover.topic_popped() || + message_info_popped() || emoji_map_is_open || reaction_popped(); }; @@ -963,11 +692,11 @@ exports.hide_all = function () { popovers.hide_actions_popover(); popovers.hide_message_info_popover(); popovers.hide_reactions_popover(); - popovers.hide_stream_sidebar_popover(); - popovers.hide_topic_sidebar_popover(); + stream_popover.hide_stream_popover(); + stream_popover.hide_topic_popover(); popovers.hide_user_sidebar_popover(); popovers.hide_userlist_sidebar(); - popovers.hide_streamlist_sidebar(); + stream_popover.restore_stream_list_size(); popovers.hide_emoji_map_popover(); // look through all the popovers that have been added and removed. diff --git a/static/js/stream_popover.js b/static/js/stream_popover.js new file mode 100644 index 0000000000..b567e2e11c --- /dev/null +++ b/static/js/stream_popover.js @@ -0,0 +1,322 @@ +var stream_popover = (function () { + +var exports = {}; + +// We handle stream popovers and topic popovers in this +// module. Both are popped up from the left sidebar. +var current_stream_sidebar_elem; +var current_topic_sidebar_elem; + +exports.stream_popped = function () { + return current_stream_sidebar_elem !== undefined; +}; + +exports.topic_popped = function () { + return current_topic_sidebar_elem !== undefined; +}; + +exports.hide_stream_popover = function () { + if (exports.stream_popped()) { + $(current_stream_sidebar_elem).popover("destroy"); + current_stream_sidebar_elem = undefined; + } +}; + +exports.hide_topic_popover = function () { + if (exports.topic_popped()) { + $(current_topic_sidebar_elem).popover("destroy"); + current_topic_sidebar_elem = undefined; + } +}; + +// These are the only two functions that is really shared by the +// two popovers, so we could split out topic stuff to +// another module pretty easily. +exports.resize_stream_list = function () { + $(".app-main .column-left").addClass("expanded"); + resize.resize_page_components(); +}; + +exports.restore_stream_list_size = function () { + $(".app-main .column-left").removeClass("expanded"); +}; + + +function stream_popover_sub(e) { + // TODO: use data-stream-id in stream list + var stream_name = $(e.currentTarget).parents('ul').attr('data-name'); + var sub = stream_data.get_sub(stream_name); + if (!sub) { + blueslip.error('Unknown stream: ' + stream_name); + return; + } + return sub; +} + +// This little function is a workaround for the fact that +// Bootstrap popovers don't properly handle being resized -- +// so after resizing our popover to add in the spectrum color +// picker, we need to adjust its height accordingly. +function update_spectrum(popover, update_func) { + var initial_height = popover[0].offsetHeight; + + var colorpicker = popover.find('.colorpicker-container').find('.colorpicker'); + update_func(colorpicker); + var after_height = popover[0].offsetHeight; + + var popover_root = popover.closest(".popover"); + var current_top_px = parseFloat(popover_root.css('top').replace('px', '')); + var height_delta = - (after_height - initial_height) * 0.5; + + popover_root.css('top', (current_top_px + height_delta) + "px"); +} + +function build_stream_popover(e) { + var elt = e.target; + if (exports.stream_popped() + && current_stream_sidebar_elem === elt) { + // If the popover is already shown, clicking again should toggle it. + exports.hide_stream_popover(); + e.stopPropagation(); + return; + } + + popovers.hide_all(); + exports.resize_stream_list(); + + var stream = $(elt).parents('li').attr('data-name'); + + var content = templates.render( + 'stream_sidebar_actions', + {stream: stream_data.get_sub(stream)} + ); + + $(elt).popover({ + content: content, + trigger: "manual", + fixed: true, + }); + + $(elt).popover("show"); + var data_id = stream_data.get_sub(stream).stream_id; + var popover = $('.streams_popover[data-id=' + data_id + ']'); + + update_spectrum(popover, function (colorpicker) { + colorpicker.spectrum(stream_color.sidebar_popover_colorpicker_options); + }); + + current_stream_sidebar_elem = elt; + e.stopPropagation(); +} + +function build_topic_popover(e) { + var elt = e.target; + + if (exports.topic_popped() + && current_topic_sidebar_elem === elt) { + // If the popover is already shown, clicking again should toggle it. + popovers.hide_topic_popover(); + e.stopPropagation(); + return; + } + + popovers.hide_all(); + exports.resize_stream_list(); + + var stream_name = $(elt).closest('.topic-list').expectOne().attr('data-stream'); + var topic_name = $(elt).closest('li').expectOne().attr('data-name'); + + var is_muted = muting.is_topic_muted(stream_name, topic_name); + var can_mute_topic = !is_muted; + var can_unmute_topic = is_muted; + + var content = templates.render('topic_sidebar_actions', { + stream_name: stream_name, + topic_name: topic_name, + can_mute_topic: can_mute_topic, + can_unmute_topic: can_unmute_topic, + }); + + $(elt).popover({ + content: content, + trigger: "manual", + fixed: true, + }); + + $(elt).popover("show"); + + current_topic_sidebar_elem = elt; + e.stopPropagation(); +} + +exports.register_click_handlers = function () { + $('#stream_filters').on('click', + '.stream-sidebar-arrow', build_stream_popover); + + $('#stream_filters').on('click', + '.topic-sidebar-arrow', build_topic_popover); + + exports.register_stream_handlers(); + exports.register_topic_handlers(); +}; + +exports.register_stream_handlers = function () { + // Stream settings + $('body').on('click', '.open_stream_settings', function (e) { + var sub = stream_popover_sub(e); + exports.hide_stream_popover(); + + window.location.hash = "#subscriptions"; + // the template for subs needs to render. + + subs.onlaunch("narrow_to_row", function () { + $(".stream-row[data-stream-name='" + sub.name + "']").click(); + }, true); + }); + + // Narrow to stream + $('body').on('click', '.narrow_to_stream', function (e) { + var sub = stream_popover_sub(e); + exports.hide_stream_popover(); + narrow.by('stream', sub.name, + {select_first_unread: true, trigger: 'sidebar popover'} + ); + e.stopPropagation(); + }); + + // Pin/unpin + $('body').on('click', '.pin_to_top', function (e) { + var sub = stream_popover_sub(e); + exports.hide_stream_popover(); + subs.toggle_pin_to_top_stream(sub); + e.stopPropagation(); + }); + + // Compose a message to stream + $('body').on('click', '.compose_to_stream', function (e) { + var sub = stream_popover_sub(e); + exports.hide_stream_popover(); + compose.start('stream', {stream: sub.name, trigger: 'sidebar stream actions'}); + e.stopPropagation(); + }); + + // Mark all messages as read + $('body').on('click', '.mark_stream_as_read', function (e) { + var sub = stream_popover_sub(e); + exports.hide_stream_popover(); + unread_ui.mark_stream_as_read(sub.name); + e.stopPropagation(); + }); + + // Mute/unmute + $('body').on('click', '.toggle_home', function (e) { + var sub = stream_popover_sub(e); + exports.hide_stream_popover(); + subs.toggle_home(sub); + e.stopPropagation(); + }); + + // Unsubscribe + $('body').on("click", ".popover_sub_unsub_button", function (e) { + $(this).toggleClass("unsub"); + $(this).closest(".popover").fadeOut(500).delay(500).remove(); + + var sub = stream_popover_sub(e); + subs.sub_or_unsub(sub); + e.preventDefault(); + e.stopPropagation(); + }); + + // Choose custom color + $('body').on('click', '.custom_color', function (e) { + update_spectrum($(e.target).closest('.streams_popover'), function (colorpicker) { + colorpicker.spectrum("destroy"); + colorpicker.spectrum(stream_color.sidebar_popover_colorpicker_options_full); + // In theory this should clean up the old color picker, + // but this seems a bit flaky -- the new colorpicker + // doesn't fire until you click a button, but the buttons + // have been hidden. We work around this by just manually + // fixing it up here. + colorpicker.parent().find('.sp-container').removeClass('sp-buttons-disabled'); + $(e.target).hide(); + }); + + $('.streams_popover').on('click', 'a.sp-cancel', function () { + exports.hide_stream_popover(); + }); + }); + +}; + +exports.topic_ops = { + mute: function (stream, topic) { + exports.hide_topic_popover(); + muting_ui.mute_topic(stream, topic); + muting_ui.persist_and_rerender(); + muting_ui.notify_with_undo_option(stream, topic); + muting_ui.set_up_muted_topics_ui(muting.get_muted_topics()); + }, + // we don't run a unmute_notify function because it isn't an issue as much + // if someone accidentally unmutes a stream rather than if they mute it + // and miss out on info. + unmute: function (stream, topic) { + exports.hide_topic_popover(); + muting_ui.unmute_topic(stream, topic); + muting_ui.persist_and_rerender(); + muting_ui.set_up_muted_topics_ui(muting.get_muted_topics()); + }, +}; + +exports.register_topic_handlers = function () { + // Narrow to topic + $('body').on('click', '.narrow_to_topic', function (e) { + exports.hide_topic_popover(); + + var row = $(e.currentTarget).closest('.narrow_to_topic').expectOne(); + var stream_name = row.attr('data-stream-name'); + var topic_name = row.attr('data-topic-name'); + + var operators = [ + {operator: 'stream', operand: stream_name}, + {operator: 'topic', operand: topic_name}, + ]; + var opts = {select_first_unread: true, trigger: 'sidebar'}; + narrow.activate(operators, opts); + + e.stopPropagation(); + }); + + // Mute the topic + $('body').on('click', '.sidebar-popover-mute-topic', function (e) { + var stream = $(e.currentTarget).attr('data-stream-name'); + var topic = $(e.currentTarget).attr('data-topic-name'); + exports.topic_ops.mute(stream, topic); + e.stopPropagation(); + e.preventDefault(); + }); + + // Unmute the topic + $('body').on('click', '.sidebar-popover-unmute-topic', function (e) { + var stream = $(e.currentTarget).attr('data-stream-name'); + var topic = $(e.currentTarget).attr('data-topic-name'); + exports.topic_ops.unmute(stream, topic); + e.stopPropagation(); + e.preventDefault(); + }); + + // Mark all messages as read + $('body').on('click', '.sidebar-popover-mark-topic-read', function (e) { + var topic = $(e.currentTarget).attr('data-topic-name'); + var stream = $(e.currentTarget).attr('data-stream-name'); + exports.hide_topic_popover(); + unread_ui.mark_topic_as_read(stream, topic); + e.stopPropagation(); + }); +}; + +return exports; +}()); + +if (typeof module !== 'undefined') { + module.exports = stream_popover; +} diff --git a/static/js/topic_list.js b/static/js/topic_list.js index fb33cfb958..500da19524 100644 --- a/static/js/topic_list.js +++ b/static/js/topic_list.js @@ -9,7 +9,7 @@ var active_widget; var zoomed = false; exports.remove_expanded_topics = function () { - popovers.hide_topic_sidebar_popover(); + stream_popover.hide_topic_popover(); if (active_widget) { active_widget.remove(); diff --git a/zproject/settings.py b/zproject/settings.py index 8a3a857a35..3a446ba35e 100644 --- a/zproject/settings.py +++ b/zproject/settings.py @@ -838,6 +838,7 @@ JS_SPECS = { 'js/scroll_bar.js', 'js/gear_menu.js', 'js/copy_and_paste.js', + 'js/stream_popover.js', 'js/popovers.js', 'js/typeahead_helper.js', 'js/search_suggestion.js',