~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/bugs/javascript/subscription.js

[bug=728370] [r=benji] adds all of the direct subscription actions
        for the unsubscribe in anger story.

Show diffs side-by-side

added added

removed removed

Lines of Context:
697
697
    unsubscribe: 'remove-direct-subscription',
698
698
    unsubscribe_with_warning: 'remove-direct-subscription-with-warning'
699
699
};
 
700
namespace._action_ids = action_ids;
700
701
 
701
702
/**
702
703
 * Get direct subscription information.
860
861
}
861
862
namespace._safely_render_description = safely_render_description;
862
863
 
 
864
/**
 
865
 * This is a simple helper function for the *_action functions below.  It
 
866
 * takes an id and returns a Y.Node div with that id.
 
867
 */
863
868
function make_action(id) {
864
869
    return Y.Node.create('<div/>')
865
870
        .addClass('hidden')
866
871
        .set('id', id);
867
872
}
868
873
 
 
874
/**
 
875
 * Return a node for muting the bug.
 
876
 */
869
877
function mute_action() {
870
878
    return make_action(action_ids.mute)
871
 
        .set('text', 'MUTE'); // TODO fill in actual contents
 
879
        .append(
 
880
            make_action_link(
 
881
                'mute all emails from this bug',
 
882
                 'no', 'mute', {}));
872
883
}
 
884
namespace._mute_action = mute_action;
873
885
 
 
886
/**
 
887
 * Return a node for unmuting the bug.
 
888
 */
874
889
function unmute_action() {
875
890
    return make_action(action_ids.unmute)
876
 
        .set('text', 'UNMUTE'); // TODO fill in actual contents
 
891
        .append(
 
892
            make_action_link(
 
893
                'receive emails about this bug from other subscriptions',
 
894
                 'yes', 'unmute', {}));
877
895
}
 
896
namespace._unmute_action = unmute_action;
878
897
 
 
898
/**
 
899
 * Return a node for subscribing to all emails from the bug.
 
900
 */
879
901
function subscribe_all_action() {
880
902
    return make_action(action_ids.subscribe_all)
881
 
        .set('text', 'SUBSCRIBE ALL'); // TODO fill in actual contents
 
903
        .append(make_subscribe_link(
 
904
            'receive all emails about this bug', 'Discussion'));
882
905
}
 
906
namespace._subscribe_all_action = subscribe_all_action;
883
907
 
 
908
/**
 
909
 * Return a node for subscribing to emails from this bug other than comments.
 
910
 */
884
911
function subscribe_metadata_action() {
885
912
    return make_action(action_ids.subscribe_metadata)
886
 
        .set('text', 'SUBSCRIBE METADATA'); // TODO fill in actual contents
 
913
        .append(make_subscribe_link(
 
914
            'receive all emails about this bug except comments', 'Details'));
887
915
}
 
916
namespace._subscribe_metadata_action = subscribe_metadata_action;
888
917
 
 
918
/**
 
919
 * Return a node for subscribing to emails about this bug closing.
 
920
 */
889
921
function subscribe_closed_action() {
890
922
    return make_action(action_ids.subscribe_closed)
891
 
        .set('text', 'SUBSCRIBE CLOSED'); // TODO fill in actual contents
 
923
        .append(make_subscribe_link(
 
924
            'only receive email when this bug is closed', 'Lifecycle'));
892
925
}
 
926
namespace._subscribe_closed_action = subscribe_closed_action;
893
927
 
 
928
/**
 
929
 * Return a node for reducing emails received from this bug to eliminate
 
930
 * comments.  This is functionally identical to subscribe_metadata_action,
 
931
 * but has different text and is presented as a reduction, not an increase.
 
932
 */
894
933
function subscribe_only_metadata_action() {
895
934
    return make_action(action_ids.subscribe_only_metadata)
896
 
        .set('text', 'SUBSCRIBE ONLY METADATA'); // TODO fill in
 
935
        .append(make_subscribe_link(
 
936
            'stop receiving comments from this bug', 'Details'));
897
937
}
 
938
namespace._subscribe_only_metadata_action = subscribe_only_metadata_action;
898
939
 
 
940
/**
 
941
 * Return a node for reducing emails received from this bug to eliminate
 
942
 * everything but closing notifications.  This is functionally identical to
 
943
 * subscribe_closed_action, but has different text and is presented as a
 
944
 * reduction, not an increase.
 
945
 */
899
946
function subscribe_only_closed_action() {
900
947
    return make_action(action_ids.subscribe_only_closed)
901
 
        .set('text', 'SUBSCRIBE ONLY CLOSED'); // TODO fill in actual contents
 
948
        .append(make_subscribe_link(
 
949
            'only receive email when this bug is closed', 'Lifecycle'));
902
950
}
 
951
namespace._subscribe_only_closed_action = subscribe_only_closed_action;
903
952
 
 
953
/**
 
954
 * Return a node for unsubscribing to emails about this bug.
 
955
 */
904
956
function unsubscribe_action() {
905
957
    return make_action(action_ids.unsubscribe)
906
 
        .set('text', 'UNSUBSCRIBE'); // TODO fill in actual contents
 
958
        .append(
 
959
            make_action_link(
 
960
                'unsubscribe from this bug',
 
961
                 'remove', 'unsubscribe', {}));
907
962
}
 
963
namespace._unsubscribe_action = unsubscribe_action;
908
964
 
 
965
/**
 
966
 * Return a node for unsubscribing to emails about this bug.  This is
 
967
 * functionally identical to unsubscribe_action, but has different text and
 
968
 * includes a warning that unsubscribing may not stop all emails.  This node
 
969
 * is intended to be used if there are other, non-direct non-personal
 
970
 * subscriptions that will cause the person to receive emails.
 
971
 */
909
972
function unsubscribe_with_warning_action() {
910
973
    return make_action(action_ids.unsubscribe_with_warning)
911
 
        .set('text', 'UNSUBSCRIBE WITH WARNING'); // TODO fill in
912
 
}
 
974
        .append(
 
975
            Y.Node.create('<span/>')
 
976
                .set('text', 'You can also '))
 
977
        .append(
 
978
            make_action_link(
 
979
                'unsubscribe from this bug',
 
980
                 'remove', 'unsubscribe', {}))
 
981
        .append(
 
982
            Y.Node.create('<span/>')
 
983
                .set('text',
 
984
                     '.  However, you also have other subscriptions to '+
 
985
                     'this bug that may send you email once you have '+
 
986
                     'unsubscribed.'));
 
987
}
 
988
namespace._unsubscribe_with_warning_action = unsubscribe_with_warning_action;
 
989
 
 
990
/**
 
991
 * Makes links for subscribing actions.
 
992
 */
 
993
function make_subscribe_link(text, level) {
 
994
    return make_action_link(
 
995
        text,
 
996
        'edit',
 
997
        'subscribe',
 
998
        {person: LP.links.me, level: level}
 
999
    );
 
1000
}
 
1001
 
 
1002
/**
 
1003
 * Makes links for all kinds of actions.
 
1004
 *
 
1005
 * The link will be constructed to have an icon followed by text with
 
1006
 * it all part of the <a> link.
 
1007
 *
 
1008
 * @param {String} text Text of the link to be created.
 
1009
 * @param {String} sprite_class Name of the sprite to use for an icon.
 
1010
 * @param {String} method_name API method to call on the bug_link when the
 
1011
 *            link is clicked.
 
1012
 * @param {Object} parameters Dict of parameters to be passed to method_name.
 
1013
 */
 
1014
function make_action_link(text, sprite_class, method_name, parameters) {
 
1015
    var node = Y.Node.create('<a/>')
 
1016
        .set('text', text)
 
1017
        .set('href', '#') // Makes the mouse arrow change into a hand.
 
1018
        .addClass('sprite')
 
1019
        .addClass('modify')
 
1020
        .addClass(sprite_class)
 
1021
        .addClass('js-action');
 
1022
    var client = lp_client();
 
1023
    var handler = new Y.lp.client.ErrorHandler();
 
1024
 
 
1025
    handler.showError = function(error_msg) {
 
1026
        Y.lp.app.errors.display_error(node, error_msg);
 
1027
    };
 
1028
    handler.clearProgressUI = function () {
 
1029
        node.replaceClass('spinner', sprite_class);
 
1030
    };
 
1031
    node.on(
 
1032
        'click',
 
1033
        function (e) {
 
1034
            e.halt();
 
1035
            var config = {
 
1036
                on: {success:
 
1037
                        function (maybe_sub) {
 
1038
                            node.replaceClass('spinner', sprite_class);
 
1039
                            var info = LP.cache.bug_subscription_info;
 
1040
                            var old = info.direct.personal[0];
 
1041
                            if (Y.Lang.isValue(maybe_sub)) {
 
1042
                                // Set the subscription in info and in cache.
 
1043
                                var sub = maybe_sub.getAttrs();
 
1044
                                if (Y.Lang.isValue(old)) {
 
1045
                                    info.direct.personal[0].subscription =
 
1046
                                        sub;
 
1047
                                } else {
 
1048
                                    // We don't have enough information to
 
1049
                                    // calculate everything on the fly.
 
1050
                                    // Luckily, we don't need most of it,
 
1051
                                    // and we think it is alright to not
 
1052
                                    // include the extra information about
 
1053
                                    // principal_is_reporter,
 
1054
                                    // security_contact_pillars, and
 
1055
                                    // bug_supervisor_pillars.
 
1056
                                    info.direct.personal.push(
 
1057
                                        {principal: {},
 
1058
                                         bug: {},
 
1059
                                         subscription: sub,
 
1060
                                         principal_is_reporter: false,
 
1061
                                         security_contact_pillars: [],
 
1062
                                         bug_supervisor_pillars: []
 
1063
                                        });
 
1064
                                    info.direct.count += 1;
 
1065
                                    info.count += 1;
 
1066
                                }
 
1067
                                info.muted = (
 
1068
                                    sub.bug_notification_level ===
 
1069
                                    'Nothing');
 
1070
                            } else {
 
1071
                                if (Y.Lang.isValue(old)) {
 
1072
                                    info.direct.personal.pop();
 
1073
                                    info.direct.count -= 1;
 
1074
                                    info.count -= 1;
 
1075
                                }
 
1076
                                info.muted = false;
 
1077
                            }
 
1078
                            reveal_direct_description_actions(
 
1079
                                Y.one('#direct-subscription'),
 
1080
                                get_direct_subscription_information(info));
 
1081
                         },
 
1082
                     failure: handler.getFailureHandler()
 
1083
                    },
 
1084
                parameters: parameters
 
1085
            };
 
1086
            node.replaceClass(sprite_class, 'spinner');
 
1087
            client.named_post(
 
1088
                LP.cache.context.bug_link,
 
1089
                method_name,
 
1090
                config);
 
1091
        }
 
1092
    );
 
1093
    return node;
 
1094
}
 
1095
namespace._make_action_link = make_action_link;
913
1096
 
914
1097
function border_box(title, content_div) {
915
1098
    return Y.Node.create('<div/>')
916
1099
        .addClass('hidden')
917
1100
        .setStyle('border', '1px solid #ddd')
918
1101
        .setStyle('padding', '0 1em 1em 1em')
 
1102
        .setStyle('marginTop', '1em')
919
1103
        .append(Y.Node.create('<span/>')
920
1104
            .setStyle('backgroundColor', '#fff')
921
1105
            .setStyle('float', 'left')
922
 
            .setStyle('marginTop', '-0.6em')
923
 
            .setStyle('padding', '0 1ex')
 
1106
            .setStyle('marginTop', '-0.8em')
 
1107
            .setStyle('padding', '0 0.5em')
924
1108
            .set('text', title))
925
1109
        .append(content_div
926
1110
            .setStyle('clear', 'both')
930
1114
/**
931
1115
 * Creates a node to store the direct subscription information.
932
1116
 *
933
 
 * @param {Object} info LP.cache.bug_subscription_info object.
934
 
 * @returns {Object} Y.Node with the ID of 'direct-description' and
935
 
 *     text set to the actual textual description of the direct
936
 
 *     personal subscriptions (if any).
 
1117
 * @returns {Object} Y.Node with the ID of 'direct-description' with the
 
1118
 *     expected structure for reason, reducing actions and increasing actions.
937
1119
 */
938
 
function get_direct_description_node(info) {
939
 
    var i;
940
 
    var direct_info = get_direct_subscription_information(info);
 
1120
function get_direct_description_node() {
941
1121
    var less_email_title = 'If you don\'t want to receive emails about '+
942
1122
        'this bug you can';
943
1123
    var more_email_title = 'If you want to receive more emails about '+
944
1124
        'this bug you can';
945
1125
    var direct_node = Y.Node.create('<div/>')
946
1126
        .set('id', 'direct-subscription')
947
 
        .set('text', direct_info.reason)
 
1127
        // Make element for reason's text.
 
1128
        .append(Y.Node.create('<div/>')
 
1129
            .setStyle('paddingBottom', '1.0em')
 
1130
            .addClass('reason'))
948
1131
        // Make border box for actions that reduce email.
949
1132
        .append(border_box(less_email_title, Y.Node.create('<div/>')
950
1133
                .append(mute_action())
961
1144
            .addClass('increases'))
962
1145
        // Add the unsubscribe action for when they have other subscriptions.
963
1146
        .append(unsubscribe_with_warning_action());
 
1147
    return direct_node;
 
1148
}
 
1149
namespace._get_direct_description_node = get_direct_description_node;
964
1150
 
 
1151
/**
 
1152
 * Mutates direct subscription node to render the appropriate information.
 
1153
 *
 
1154
 * @param {Object} direct_node The Y.Node as generated by
 
1155
 *     get_direct_description_node.
 
1156
 * @param {Object} direct_info An object as returned from
 
1157
 *     get_direct_subscription_information.  Should have a text `reason`,
 
1158
 *     a `reductions` list of reduction action ids to reveal, and a
 
1159
 *     `increases` list of increasing action ids to reveal.
 
1160
 */
 
1161
function reveal_direct_description_actions(direct_node, direct_info) {
 
1162
    var i;
 
1163
    var key;
 
1164
    direct_node.one('.reason').set('text', direct_info.reason);
 
1165
    // Hide all actions.  This is particularly important when we redraw
 
1166
    // actions after a successful direct subscription action.  After we hide
 
1167
    // these, we will reveal the ones we want, immediately below.
 
1168
    for (key in action_ids) {
 
1169
        if (action_ids.hasOwnProperty(key)) {
 
1170
            direct_node.one('#'+action_ids[key]).addClass('hidden');
 
1171
        }
 
1172
    }
965
1173
    if (direct_info.reductions.length !== 0) {
966
1174
        // If there are any actions the user can take, unhide the actions box
967
1175
        // and then unhide the specific actions that they should see.
970
1178
            direct_node.one('#'+direct_info.reductions[i])
971
1179
                .removeClass('hidden');
972
1180
        }
 
1181
    } else {
 
1182
        direct_node.one('.reductions').addClass('hidden');
973
1183
    }
974
1184
 
975
1185
    if (direct_info.increases.length !== 0) {
980
1190
            direct_node.one('#'+direct_info.increases[i])
981
1191
                .removeClass('hidden');
982
1192
        }
 
1193
    } else {
 
1194
        direct_node.one('.increases').addClass('hidden');
983
1195
    }
984
 
    return direct_node;
985
1196
}
986
 
namespace._get_direct_description_node = get_direct_description_node;
 
1197
namespace._reveal_direct_description_actions =
 
1198
    reveal_direct_description_actions;
987
1199
 
988
1200
/**
989
1201
 * Creates a node to store single subscription description.
1112
1324
 
1113
1325
    var content_node = Y.one(config.description_box);
1114
1326
 
1115
 
    var direct_node = get_direct_description_node(info);
 
1327
    var direct_node = get_direct_description_node();
 
1328
    reveal_direct_description_actions(
 
1329
        direct_node,
 
1330
        get_direct_subscription_information(info));
1116
1331
    content_node.appendChild(direct_node);
1117
1332
 
1118
1333
    var other_node = get_other_descriptions_node(info, extra_data);