diff options
Diffstat (limited to 'app/assets/javascripts/gfm_auto_complete.js')
-rw-r--r-- | app/assets/javascripts/gfm_auto_complete.js | 78 |
1 files changed, 74 insertions, 4 deletions
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index d209a971c39..c5ea4cc92fd 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -3,11 +3,12 @@ import '~/lib/utils/jquery_at_who'; import { escape, template } from 'lodash'; import * as Emoji from '~/emoji'; import axios from '~/lib/utils/axios_utils'; -import { s__ } from '~/locale'; +import { s__, __, sprintf } from '~/locale'; import { isUserBusy } from '~/set_status_modal/utils'; import SidebarMediator from '~/sidebar/sidebar_mediator'; import AjaxCache from './lib/utils/ajax_cache'; import { spriteIcon } from './lib/utils/common_utils'; +import { parsePikadayDate } from './lib/utils/datetime_utility'; import glRegexp from './lib/utils/regexp'; function sanitize(str) { @@ -266,6 +267,7 @@ class GfmAutoComplete { }, // eslint-disable-next-line no-template-curly-in-string insertTpl: '${atwho-at}${username}', + limit: 10, searchKey: 'search', alwaysHighlightFirst: true, skipSpecialCharacterTest: true, @@ -311,6 +313,38 @@ class GfmAutoComplete { return data; }, + sorter(query, items) { + // Disable auto-selecting the loading icon + this.setting.highlightFirst = this.setting.alwaysHighlightFirst; + if (GfmAutoComplete.isLoading(items)) { + this.setting.highlightFirst = false; + return items; + } + + if (!query) { + return items; + } + + const lowercaseQuery = query.toLowerCase(); + const members = items.slice(); + const { nameOrUsernameStartsWith, nameOrUsernameIncludes } = GfmAutoComplete.Members; + + return members.sort((a, b) => { + if (nameOrUsernameStartsWith(a, lowercaseQuery)) { + return -1; + } + if (nameOrUsernameStartsWith(b, lowercaseQuery)) { + return 1; + } + if (nameOrUsernameIncludes(a, lowercaseQuery)) { + return -1; + } + if (nameOrUsernameIncludes(b, lowercaseQuery)) { + return 1; + } + return 0; + }); + }, }, }); } @@ -359,7 +393,7 @@ class GfmAutoComplete { displayTpl(value) { let tmpl = GfmAutoComplete.Loading.template; if (value.title != null) { - tmpl = GfmAutoComplete.Milestones.templateFunction(value.title); + tmpl = GfmAutoComplete.Milestones.templateFunction(value.title, value.expired); } return tmpl; }, @@ -367,16 +401,39 @@ class GfmAutoComplete { callbacks: { ...this.getDefaultCallbacks(), beforeSave(milestones) { - return $.map(milestones, (m) => { + const parsedMilestones = $.map(milestones, (m) => { if (m.title == null) { return m; } + + const dueDate = m.due_date ? parsePikadayDate(m.due_date) : null; + const expired = dueDate ? Date.now() > dueDate.getTime() : false; + return { id: m.iid, title: sanitize(m.title), search: m.title, + expired, + dueDate, }; }); + + // Sort milestones by due date when present. + if (typeof parsedMilestones[0] === 'object') { + return parsedMilestones.sort((mA, mB) => { + // Move all expired milestones to the bottom. + if (mA.expired) return 1; + if (mB.expired) return -1; + + // Move milestones without due dates just above expired milestones. + if (!mA.dueDate) return 1; + if (!mB.dueDate) return -1; + + // Sort by due date in ascending order. + return mA.dueDate - mB.dueDate; + }); + } + return parsedMilestones; }, }, }); @@ -772,6 +829,14 @@ GfmAutoComplete.Members = { title, )}${availabilityStatus}</small> ${icon}</li>`; }, + nameOrUsernameStartsWith(member, query) { + // `member.search` is a name:username string like `MargeSimpson msimpson` + return member.search.split(' ').some((name) => name.toLowerCase().startsWith(query)); + }, + nameOrUsernameIncludes(member, query) { + // `member.search` is a name:username string like `MargeSimpson msimpson` + return member.search.toLowerCase().includes(query); + }, }; GfmAutoComplete.Labels = { templateFunction(color, title) { @@ -792,7 +857,12 @@ GfmAutoComplete.Issues = { }; // Milestones GfmAutoComplete.Milestones = { - templateFunction(title) { + templateFunction(title, expired) { + if (expired) { + return `<li>${sprintf(__('%{milestone} (expired)'), { + milestone: escape(title), + })}</li>`; + } return `<li>${escape(title)}</li>`; }, }; |