From 1f4c4f8afaeedafda756bb0cac25df6fce2afe8d Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov Date: Tue, 30 Jun 2020 00:02:29 -0400 Subject: [PATCH 1/3] In the mentions dropdown, display users from the API AFTER posts in the discussion --- js/src/forum/addComposerAutocomplete.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/js/src/forum/addComposerAutocomplete.js b/js/src/forum/addComposerAutocomplete.js index 25ecdde..63e6643 100644 --- a/js/src/forum/addComposerAutocomplete.js +++ b/js/src/forum/addComposerAutocomplete.js @@ -109,18 +109,6 @@ export default function addComposerAutocomplete() { const buildSuggestions = () => { const suggestions = []; - // If the user has started to type a username, then suggest users - // matching that username. - if (typed) { - app.store.all('users').forEach(user => { - if (!userMatches(user)) return; - - suggestions.push( - makeSuggestion(user, '@' + user.username(), '', 'MentionsDropdown-user') - ); - }); - } - // If the user is replying to a discussion, or if they are editing a // post, then we can suggest other posts in the discussion to mention. // We will add the 5 most recent comments in the discussion which @@ -147,6 +135,18 @@ export default function addComposerAutocomplete() { }); } + // If the user has started to type a username, then suggest users + // matching that username. + if (typed) { + app.store.all('users').forEach(user => { + if (!userMatches(user)) return; + + suggestions.push( + makeSuggestion(user, '@' + user.username(), '', 'MentionsDropdown-user') + ); + }); + } + if (suggestions.length) { dropdown.props.items = suggestions; m.render($container[0], dropdown.render()); From e4020192c2b0e4f203191923097429f3161d5aec Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov Date: Tue, 30 Jun 2020 21:13:12 -0400 Subject: [PATCH 2/3] Only start sending API calls for users once more than 2 characters have been typed --- js/src/forum/addComposerAutocomplete.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/js/src/forum/addComposerAutocomplete.js b/js/src/forum/addComposerAutocomplete.js index 63e6643..fc249c7 100644 --- a/js/src/forum/addComposerAutocomplete.js +++ b/js/src/forum/addComposerAutocomplete.js @@ -179,7 +179,9 @@ export default function addComposerAutocomplete() { dropdown.$().scrollTop(0); clearTimeout(searchTimeout); - if (typed) { + // Don't send API calls searching for users until at least 2 characters have been typed. + // This focuses the mention results on users and posts in the discussion. + if (typed.length > 1) { searchTimeout = setTimeout(function() { const typedLower = typed.toLowerCase(); if (searched.indexOf(typedLower) === -1) { From 5ab467d43146008b8a33d3f2c2ff0d653f7b59d7 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov Date: Tue, 30 Jun 2020 21:37:01 -0400 Subject: [PATCH 3/3] Serve user list in the order that it was provided, revert back to posts after users --- js/src/forum/addComposerAutocomplete.js | 40 ++++++++++++++++--------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/js/src/forum/addComposerAutocomplete.js b/js/src/forum/addComposerAutocomplete.js index fc249c7..f34362e 100644 --- a/js/src/forum/addComposerAutocomplete.js +++ b/js/src/forum/addComposerAutocomplete.js @@ -25,6 +25,12 @@ export default function addComposerAutocomplete() { let typed; let searchTimeout; + // We store users returned from an API here to preserve order in which they are returned + // This prevents the user list jumping around while users are returned. + // We also use a hashset for user IDs to provide O(1) lookup for the users already in the list. + const returnedUsers = Array.from(app.store.all('users')); + const returnedUserIds = new Set(returnedUsers.map(u => u.id())); + const applySuggestion = function(replacement) { const insert = replacement + ' '; @@ -109,6 +115,18 @@ export default function addComposerAutocomplete() { const buildSuggestions = () => { const suggestions = []; + // If the user has started to type a username, then suggest users + // matching that username. + if (typed) { + returnedUsers.forEach(user => { + if (!userMatches(user)) return; + + suggestions.push( + makeSuggestion(user, '@' + user.username(), '', 'MentionsDropdown-user') + ); + }); + } + // If the user is replying to a discussion, or if they are editing a // post, then we can suggest other posts in the discussion to mention. // We will add the 5 most recent comments in the discussion which @@ -128,25 +146,13 @@ export default function addComposerAutocomplete() { const user = post.user(); suggestions.push( makeSuggestion(user, '@' + user.username() + '#' + post.id(), [ - app.translator.trans('flarum-mentions.forum.composer.reply_to_post_text', {number: post.number()}), ' — ', + app.translator.trans('flarum-mentions.forum.composer.reply_to_post_text', { number: post.number() }), ' — ', truncate(post.contentPlain(), 200) ], 'MentionsDropdown-post') ); }); } - // If the user has started to type a username, then suggest users - // matching that username. - if (typed) { - app.store.all('users').forEach(user => { - if (!userMatches(user)) return; - - suggestions.push( - makeSuggestion(user, '@' + user.username(), '', 'MentionsDropdown-user') - ); - }); - } - if (suggestions.length) { dropdown.props.items = suggestions; m.render($container[0], dropdown.render()); @@ -185,7 +191,13 @@ export default function addComposerAutocomplete() { searchTimeout = setTimeout(function() { const typedLower = typed.toLowerCase(); if (searched.indexOf(typedLower) === -1) { - app.store.find('users', {filter: {q: typed}, page: {limit: 5}}).then(() => { + app.store.find('users', { filter: { q: typed }, page: { limit: 5 } }).then(results => { + results.forEach(u => { + if (!returnedUserIds.has(u.id())) { + returnedUserIds.add(u.id()); + returnedUsers.push(u); + } + }) if (dropdown.active) buildSuggestions(); }); searched.push(typedLower);