diff --git a/.eslintrc.json b/.eslintrc.json index 563180173d..d671a56eba 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -36,6 +36,7 @@ "resize": false, "loading": false, "typing": false, + "typing_data": false, "compose": false, "compose_actions": false, "compose_state": false, diff --git a/frontend_tests/node_tests/typing_data.js b/frontend_tests/node_tests/typing_data.js new file mode 100644 index 0000000000..bee4541b7c --- /dev/null +++ b/frontend_tests/node_tests/typing_data.js @@ -0,0 +1,47 @@ +var typing_data = require("js/typing_data.js"); + + +(function test_basics() { + // The typing_data needs to be robust with lists of + // user ids being in arbitrary sorting order and + // possibly in string form instead of integer. So all + // the apparent randomness in these tests has a purpose. + typing_data.add_typist([5, 10, 15], 15); + assert.deepEqual(typing_data.get_group_typists([15, 10, 5]), [15]); + + // test that you can add twice + typing_data.add_typist([5, 10, 15], 15); + + // add another id to our first group + typing_data.add_typist([5, 10, 15], "10"); + assert.deepEqual(typing_data.get_group_typists([10, 15, 5]), [10, 15]); + + // start adding to a new group + typing_data.add_typist([7, 15], 7); + typing_data.add_typist([7, "15"], 15); + + // test get_all_typists + assert.deepEqual(typing_data.get_all_typists(), [7, 10, 15]); + + // test basic removal + assert(typing_data.remove_typist([15, 7], "7")); + assert.deepEqual(typing_data.get_group_typists([7, 15]), [15]); + + // test removing an id that is not there + assert(!typing_data.remove_typist([15, 7], 7)); + assert.deepEqual(typing_data.get_group_typists([7, 15]), [15]); + assert.deepEqual(typing_data.get_all_typists(), [10, 15]); + + // remove user from one group, but "15" will still be among + // "all typists" + assert(typing_data.remove_typist(["15", 7], "15")); + assert.deepEqual(typing_data.get_all_typists(), [10, 15]); + + // now remove from the other group + assert(typing_data.remove_typist([5, 15, 10], 15)); + assert.deepEqual(typing_data.get_all_typists(), [10]); + + // test duplicate ids in a groups + typing_data.add_typist([20, 40, 20], 20); + assert.deepEqual(typing_data.get_group_typists([20, 40]), [20]); +}()); diff --git a/static/js/typing_data.js b/static/js/typing_data.js new file mode 100644 index 0000000000..d02cb1f216 --- /dev/null +++ b/static/js/typing_data.js @@ -0,0 +1,71 @@ +var typing_data = (function () { +var exports = {}; + +var typist_dct = new Dict(); + +function to_int(s) { + return parseInt(s, 10); +} + +function sorted(user_ids) { + // This mapping makes sure we are using ints, and + // it also makes sure we don't mutate the list. + var id_list = _.map(user_ids, to_int); + id_list.sort(function (a, b) { + return a - b; + }); + id_list = _.uniq(id_list, true); + + return id_list; +} + +function get_key(group) { + var ids = sorted(group); + return ids.join(','); +} + +exports.add_typist = function (group, typist) { + var key = get_key(group); + var current = typist_dct.get(key) || []; + typist = to_int(typist); + if (!_.contains(current, typist)) { + current.push(typist); + } + typist_dct.set(key, sorted(current)); +}; + +exports.remove_typist = function (group, typist) { + var key = get_key(group); + var current = typist_dct.get(key) || []; + + typist = to_int(typist); + if (!_.contains(current, typist)) { + return false; + } + + current = _.reject(current, function (user_id) { + return to_int(user_id) === to_int(typist); + }); + + typist_dct.set(key, current); + return true; +}; + +exports.get_group_typists = function (group) { + var key = get_key(group); + return typist_dct.get(key) || []; +}; + +exports.get_all_typists = function () { + var typists = _.flatten(typist_dct.values(), true); + typists = sorted(typists); + typists = _.uniq(typists, true); + return typists; +}; + +return exports; +}()); + +if (typeof module !== 'undefined') { + module.exports = typing_data; +} diff --git a/zproject/settings.py b/zproject/settings.py index 6e40406da9..70f3e64624 100644 --- a/zproject/settings.py +++ b/zproject/settings.py @@ -902,6 +902,7 @@ JS_SPECS = { 'js/bot_data.js', 'js/reactions.js', 'js/typing.js', + 'js/typing_data.js', 'js/ui_init.js', 'js/shim.js', # JS bundled by webpack is also included here if PIPELINE_ENABLED setting is true