فهرست منبع

Fix #2231 add sort by query (#2234)

Use of lowercase letters and Infinity to avoid calculation errors when the index is -1
bernard ng 4 سال پیش
والد
کامیت
efd4e50378
3فایلهای تغییر یافته به همراه61 افزوده شده و 2 حذف شده
  1. 1 0
      CHANGES.md
  2. 47 0
      spec/autocomplete.js
  3. 13 2
      src/converse-autocomplete.js

+ 1 - 0
CHANGES.md

@@ -6,6 +6,7 @@
 configuration settings should now be accessed via `_converse.api.settings.get` and not directly on the `_converse` object.
 Soon we'll deprecate the latter, so prepare now.
 
+- #2231: add sort_by_query and remove sort_by_length
 - #1313: Stylistic improvements to the send button
 - #1481: MUC OMEMO: Error No record for device
 - #1490: Busy-loop when fetching registration form fails

+ 47 - 0
spec/autocomplete.js

@@ -60,6 +60,53 @@ describe("The nickname autocomplete feature", function () {
         done();
     }));
 
+    it("should order by query index position and length", mock.initConverse(
+        ['rosterGroupsFetched', 'chatBoxesFetched'], {}, async function (done, _converse) {
+            await mock.openAndEnterChatRoom(_converse, 'lounge@montague.lit', 'tom');
+            const view = _converse.chatboxviews.get('lounge@montague.lit');
+
+            // Nicknames from presences
+            ['bernard', 'naber', 'helberlo', 'john', 'jones'].forEach((nick) => {
+                _converse.connection._dataRecv(mock.createRequest(
+                    $pres({
+                        'to': 'tom@montague.lit/resource',
+                        'from': `lounge@montague.lit/${nick}`
+                    })
+                        .c('x', { xmlns: Strophe.NS.MUC_USER })
+                        .c('item', {
+                            'affiliation': 'none',
+                            'jid': `${nick}@montague.lit/resource`,
+                            'role': 'participant'
+                        })));
+            });
+
+            const textarea = view.el.querySelector('textarea.chat-textarea');
+            const at_event = {
+                'target': textarea,
+                'preventDefault': function preventDefault() { },
+                'stopPropagation': function stopPropagation() { },
+                'keyCode': 50,
+                'key': '@'
+            };
+
+            // Test that results are sorted by query index
+            view.onKeyDown(at_event);
+            textarea.value = '@ber';
+            view.onKeyUp(at_event);
+            await u.waitUntil(() => view.el.querySelectorAll('.suggestion-box__results li').length === 3);
+            expect(view.el.querySelector('.suggestion-box__results li:first-child').textContent).toBe('bernard');
+            expect(view.el.querySelector('.suggestion-box__results li:nth-child(2)').textContent).toBe('naber');
+            expect(view.el.querySelector('.suggestion-box__results li:nth-child(3)').textContent).toBe('helberlo');
+
+            // Test that when the query index is equal, results should be sorted by length
+            textarea.value = '@jo';
+            view.onKeyUp(at_event);
+            await u.waitUntil(() => view.el.querySelectorAll('.suggestion-box__results li').length === 2);
+            expect(view.el.querySelector('.suggestion-box__results li:first-child').textContent).toBe('john');
+            expect(view.el.querySelector('.suggestion-box__results li:nth-child(2)').textContent).toBe('jones');
+            done();
+    }));
+
     it("autocompletes when the user presses tab",
         mock.initConverse(
             ['rosterGroupsFetched', 'chatBoxesFetched'], {},

+ 13 - 2
src/converse-autocomplete.js

@@ -22,13 +22,24 @@ export const FILTER_STARTSWITH = function (text, input) {
 };
 
 
-const SORT_BYLENGTH = function (a, b) {
+const SORT_BY_LENGTH = function (a, b) {
     if (a.length !== b.length) {
         return a.length - b.length;
     }
     return a < b? -1 : 1;
 };
 
+const SORT_BY_QUERY_POSITION = function (a, b) {
+    const query = a.query.toLowerCase();
+    const x = a.label.toLowerCase().indexOf(query);
+    const y = b.label.toLowerCase().indexOf(query);
+
+    if (x === y) {
+        return SORT_BY_LENGTH(a, b);
+    }
+    return (x === -1 ? Infinity : x) < (y === -1 ? Infinity : y) ? -1 : 1
+}
+
 
 const ITEM = (text, input) => {
     input = input.trim();
@@ -147,7 +158,7 @@ export class AutoComplete {
             'auto_first': false, // Should the first element be automatically selected?
             'data': a => a,
             'filter': FILTER_CONTAINS,
-            'sort': config.sort === false ? false : SORT_BYLENGTH,
+            'sort': config.sort === false ? false : SORT_BY_QUERY_POSITION,
             'item': ITEM
         }, config);