Refactor typeahead for user names (PM and @reply).

Get data directly from the main user list, rather than maintaining a
separate list just for autocomplete.

Fixes trac #1362 -- Does not depend on historical messages, so
will do the correct autocomplete after a single reload.

(imported from commit 6b35a709dba3384530082e8cfacf0151f9e0eb26)
This commit is contained in:
Kevin Mehall 2013-06-18 15:09:29 -04:00
parent fbed3e23a8
commit 9322e63d32
5 changed files with 46 additions and 56 deletions

View File

@ -34,14 +34,16 @@ function get_last_recipient_in_pm(query_string) {
}
function composebox_typeahead_highlighter(item) {
var query = this.query;
if ($(this.$element).attr('id') === 'private_message_recipient') {
// There could be multiple recipients in a private message,
// we want to decide what to highlight based only on the most
// recent one we're entering.
query = get_last_recipient_in_pm(this.query);
}
return typeahead_helper.highlight_with_escaping(query, item);
return typeahead_helper.highlight_with_escaping(this.query, item);
}
function query_matches_person (query, person) {
// Case-insensitive.
query = query.toLowerCase();
return ( person.email .toLowerCase().indexOf(query) !== -1
|| person.full_name.toLowerCase().indexOf(query) !== -1);
}
// nextFocus is set on a keydown event to indicate where we should focus on keyup.
@ -206,10 +208,14 @@ exports.initialize = function () {
});
$( "#private_message_recipient" ).typeahead({
source: typeahead_helper.private_message_typeahead_list,
source: page_params.people_list,
items: 5,
dropup: true,
highlighter: composebox_typeahead_highlighter,
highlighter: function (item) {
var query = get_last_recipient_in_pm(this.query);
var item_formatted = typeahead_helper.render_person(item);
return typeahead_helper.highlight_with_escaping(query, item_formatted);
},
matcher: function (item) {
var current_recipient = get_last_recipient_in_pm(this.query);
// If the name is only whitespace (does not contain any non-whitespace),
@ -218,8 +224,7 @@ exports.initialize = function () {
return false;
}
// Case-insensitive.
return (item.toLowerCase().indexOf(current_recipient.toLowerCase()) !== -1);
return query_matches_person(current_recipient, item);
},
sorter: typeahead_helper.sort_recipientbox_typeahead,
updater: function (item) {
@ -229,15 +234,18 @@ exports.initialize = function () {
if (previous_recipients.length !== 0) {
previous_recipients += ", ";
}
return previous_recipients + typeahead_helper.private_message_mapped[item].email + ", ";
return previous_recipients + item.email + ", ";
},
stopAdvance: true // Do not advance to the next field on a tab or enter
});
$( "#new_message_content" ).typeahead({
source: typeahead_helper.private_message_typeahead_list,
source: page_params.people_list,
items: 5,
highlighter: composebox_typeahead_highlighter,
highlighter: function (item) {
var item_formatted = typeahead_helper.render_person(item);
return typeahead_helper.highlight_with_escaping(this.query, item_formatted);
},
dropup: true,
matcher: function (item) {
var query = exports.split_at_cursor(this.query)[0];
@ -250,10 +258,8 @@ exports.initialize = function () {
if (current_recipient.length < 2 || current_recipient.charAt(0) !== "@") {
return false;
}
current_recipient = current_recipient.substring(1);
// Case-insensitive.
return (item.toLowerCase().indexOf(current_recipient.toLowerCase()) !== -1);
return query_matches_person(current_recipient.substring(1), item);
},
sorter: typeahead_helper.sort_textbox_typeahead,
updater: function (item) {
@ -261,7 +267,7 @@ exports.initialize = function () {
var beginning = pieces[0];
var rest = pieces[1];
beginning = beginning.replace(/@\S+$/, "") + "@**" + typeahead_helper.private_message_mapped[item].full_name + "**";
beginning = beginning.replace(/@\S+$/, "") + "@**" + item.full_name + "**";
// Keep the cursor after the newly inserted name, as Bootstrap will call textbox.change() to overwrite the text
// in the textbox.
setTimeout(function () {

View File

@ -159,7 +159,7 @@ function searchbox_sorter(items) {
// Get the first object in sorted order.
if (action === 'private_message' || action === 'sender') {
objs.sort(function (x, y) {
return typeahead_helper.compare_by_pms(get_person(x), get_person(y));
return typeahead_helper.compare_by_pms(get_query(x), get_query(y));
});
} else if (action !== 'stream') {
// streams are already sorted

View File

@ -145,24 +145,16 @@ exports.sorter = function (query, objs, get_item) {
return results.matches.concat(results.rest);
};
exports.compare_by_pms = function(user_a, user_b) {
var x_count = 0, y_count = 0;
if (typeahead_helper.private_message_mapped[user_a]) {
x_count = typeahead_helper.private_message_mapped[user_a].count;
}
if (typeahead_helper.private_message_mapped[user_b]) {
y_count = typeahead_helper.private_message_mapped[user_b].count;
}
if (x_count > y_count) {
exports.compare_by_pms = function (user_a, user_b) {
if (user_a.pm_recipient_count > user_b.pm_recipient_count) {
return -1;
} else if (x_count < y_count) {
} else if (user_a.pm_recipient_count < user_b.pm_recipient_count) {
return 1;
}
// We use alpha sort as a tiebreaker, which might be helpful for
// new users.
if (user_a < user_b)
if (user_a.full_name < user_b.full_name)
return -1;
else if (user_a === user_b)
return 0;
@ -171,10 +163,7 @@ exports.compare_by_pms = function(user_a, user_b) {
};
exports.sort_by_pms = function(objs) {
objs.sort(function (x, y) {
return exports.compare_by_pms(x, y);
});
objs.sort(exports.compare_by_pms);
return objs;
};
@ -182,17 +171,13 @@ function identity(item) {
return item;
}
function email_from_identity(identity) {
return exports.private_message_mapped[identity].email;
}
exports.sort_subjects = function (items) {
return exports.sorter(this.query, items, identity);
};
exports.sort_recipients = function (matches, query) {
var name_results = prefix_sort(query, matches, identity);
var email_results = prefix_sort(query, name_results.rest, email_from_identity);
var name_results = prefix_sort(query, matches, function (x) { return x.full_name; });
var email_results = prefix_sort(query, name_results.rest, function (x) { return x.email; });
var matches_sorted_by_pms = exports.sort_by_pms(name_results.matches.concat(email_results.matches));
var rest_sorted_by_pms = exports.sort_by_pms(email_results.rest);
return matches_sorted_by_pms.concat(rest_sorted_by_pms);

View File

@ -1571,7 +1571,6 @@ $(function () {
});
// initialize other stuff
typeahead_helper.update_all_recipients(page_params.people_list);
composebox_typeahead.initialize();
search.initialize();
notifications.initialize();

View File

@ -43,6 +43,7 @@ var pointer_update_in_flight = false;
function add_person(person) {
page_params.people_list.push(person);
people_dict[person.email] = person;
person.pm_recipient_count = 0;
}
function remove_person(person) {
@ -59,11 +60,15 @@ function remove_person(person) {
$(function () {
$.each(page_params.people_list, function (idx, person) {
people_dict[person.email] = person;
person.pm_recipient_count = 0;
});
// The special account feedback@humbughq.com is used for in-app
// feedback and should always show up as an autocomplete option.
typeahead_helper.update_your_recipients([{"email": "feedback@humbughq.com",
"full_name": "Humbug Feedback Bot"}]);
if (people_dict['feedback@humbughq.com'] === undefined){
add_person({"email": "feedback@humbughq.com",
"full_name": "Humbug Feedback Bot"});
}
$.each(page_params.initial_presences, function (email, presence) {
activity.set_user_status(email, presence, page_params.initial_servertime);
@ -577,12 +582,6 @@ function add_message_metadata(message, dummy) {
message.display_reply_to = get_private_message_recipient(message, 'full_name');
involved_people = message.display_recipient;
if (message.sent_by_me) {
typeahead_helper.update_your_recipients(involved_people);
} else {
typeahead_helper.update_all_recipients(involved_people);
}
break;
}
@ -592,9 +591,12 @@ function add_message_metadata(message, dummy) {
// with keys like "hasOwnProperty"
if (people_dict[person.email] === undefined) {
add_person(person);
if (!typeahead_helper.known_to_typeahead(person)) {
typeahead_helper.autocomplete_needs_update(true);
}
if (message.type === 'private' && message.sent_by_me) {
// Track the number of PMs we've sent to this person to improve autocomplete
people_dict[person.email].pm_recipient_count += 1;
}
});
@ -849,10 +851,8 @@ function get_updates(options) {
case 'realm_user':
if (event.op === 'add') {
add_person(event.person);
typeahead_helper.update_all_recipients([event.person]);
} else if (event.op === 'remove') {
remove_person(event.person);
typeahead_helper.remove_recipient([event.person]);
}
typeahead_helper.autocomplete_needs_update(true);
break;