Fix blur and sort behavior for autocomplete
Summary: Ref T10163. - If you click a result, we get a blur before your click hits, and deactivate before the click can work. Instead, wait before responding to blur. - Use the standard sort handler which puts unixnames over human names. Also use the standard filter which deals with disabled users not matching unless they're the only match. Test Plan: - Clicked a result, got a replacement. - Named myself "dog dog", typed "@dog", user "@dog" was now first match despite me being "@admin". - Used normal typeaheads to make sure I didn't break sort handler. Reviewers: chad Reviewed By: chad Maniphest Tasks: T10163 Differential Revision: https://secure.phabricator.com/D15031
This commit is contained in:
		@@ -8,7 +8,7 @@
 | 
				
			|||||||
return array(
 | 
					return array(
 | 
				
			||||||
  'names' => array(
 | 
					  'names' => array(
 | 
				
			||||||
    'core.pkg.css' => '3a97c8b9',
 | 
					    'core.pkg.css' => '3a97c8b9',
 | 
				
			||||||
    'core.pkg.js' => '1f5f365a',
 | 
					    'core.pkg.js' => '5813273d',
 | 
				
			||||||
    'darkconsole.pkg.js' => 'e7393ebb',
 | 
					    'darkconsole.pkg.js' => 'e7393ebb',
 | 
				
			||||||
    'differential.pkg.css' => '2de124c9',
 | 
					    'differential.pkg.css' => '2de124c9',
 | 
				
			||||||
    'differential.pkg.js' => 'f83532f8',
 | 
					    'differential.pkg.js' => 'f83532f8',
 | 
				
			||||||
@@ -458,7 +458,7 @@ return array(
 | 
				
			|||||||
    'rsrc/js/core/KeyboardShortcutManager.js' => 'c1700f6f',
 | 
					    'rsrc/js/core/KeyboardShortcutManager.js' => 'c1700f6f',
 | 
				
			||||||
    'rsrc/js/core/MultirowRowManager.js' => 'b5d57730',
 | 
					    'rsrc/js/core/MultirowRowManager.js' => 'b5d57730',
 | 
				
			||||||
    'rsrc/js/core/Notification.js' => 'ccf1cbf8',
 | 
					    'rsrc/js/core/Notification.js' => 'ccf1cbf8',
 | 
				
			||||||
    'rsrc/js/core/Prefab.js' => 'a15cbd65',
 | 
					    'rsrc/js/core/Prefab.js' => 'e67df814',
 | 
				
			||||||
    'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
 | 
					    'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
 | 
				
			||||||
    'rsrc/js/core/TextAreaUtils.js' => '9e54692d',
 | 
					    'rsrc/js/core/TextAreaUtils.js' => '9e54692d',
 | 
				
			||||||
    'rsrc/js/core/Title.js' => 'df5e11d2',
 | 
					    'rsrc/js/core/Title.js' => 'df5e11d2',
 | 
				
			||||||
@@ -507,7 +507,7 @@ return array(
 | 
				
			|||||||
    'rsrc/js/phui/behavior-phui-object-box-tabs.js' => '2bfa2836',
 | 
					    'rsrc/js/phui/behavior-phui-object-box-tabs.js' => '2bfa2836',
 | 
				
			||||||
    'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
 | 
					    'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8',
 | 
				
			||||||
    'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262',
 | 
					    'rsrc/js/phuix/PHUIXActionView.js' => '8cf6d262',
 | 
				
			||||||
    'rsrc/js/phuix/PHUIXAutocomplete.js' => 'c5f5e42f',
 | 
					    'rsrc/js/phuix/PHUIXAutocomplete.js' => '2b735afc',
 | 
				
			||||||
    'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca',
 | 
					    'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca',
 | 
				
			||||||
    'rsrc/js/phuix/PHUIXFormControl.js' => '8fba1997',
 | 
					    'rsrc/js/phuix/PHUIXFormControl.js' => '8fba1997',
 | 
				
			||||||
    'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
 | 
					    'rsrc/js/phuix/PHUIXIconView.js' => 'bff6884b',
 | 
				
			||||||
@@ -760,7 +760,7 @@ return array(
 | 
				
			|||||||
    'phabricator-notification-menu-css' => 'f31c0bde',
 | 
					    'phabricator-notification-menu-css' => 'f31c0bde',
 | 
				
			||||||
    'phabricator-object-selector-css' => '85ee8ce6',
 | 
					    'phabricator-object-selector-css' => '85ee8ce6',
 | 
				
			||||||
    'phabricator-phtize' => 'd254d646',
 | 
					    'phabricator-phtize' => 'd254d646',
 | 
				
			||||||
    'phabricator-prefab' => 'a15cbd65',
 | 
					    'phabricator-prefab' => 'e67df814',
 | 
				
			||||||
    'phabricator-remarkup-css' => 'b748dc17',
 | 
					    'phabricator-remarkup-css' => 'b748dc17',
 | 
				
			||||||
    'phabricator-search-results-css' => '7dea472c',
 | 
					    'phabricator-search-results-css' => '7dea472c',
 | 
				
			||||||
    'phabricator-shaped-request' => '7cbe244b',
 | 
					    'phabricator-shaped-request' => '7cbe244b',
 | 
				
			||||||
@@ -836,7 +836,7 @@ return array(
 | 
				
			|||||||
    'phui-workpanel-view-css' => 'adec7699',
 | 
					    'phui-workpanel-view-css' => 'adec7699',
 | 
				
			||||||
    'phuix-action-list-view' => 'b5c256b8',
 | 
					    'phuix-action-list-view' => 'b5c256b8',
 | 
				
			||||||
    'phuix-action-view' => '8cf6d262',
 | 
					    'phuix-action-view' => '8cf6d262',
 | 
				
			||||||
    'phuix-autocomplete' => 'c5f5e42f',
 | 
					    'phuix-autocomplete' => '2b735afc',
 | 
				
			||||||
    'phuix-dropdown-menu' => 'bd4c8dca',
 | 
					    'phuix-dropdown-menu' => 'bd4c8dca',
 | 
				
			||||||
    'phuix-form-control-view' => '8fba1997',
 | 
					    'phuix-form-control-view' => '8fba1997',
 | 
				
			||||||
    'phuix-icon-view' => 'bff6884b',
 | 
					    'phuix-icon-view' => 'bff6884b',
 | 
				
			||||||
@@ -1023,6 +1023,12 @@ return array(
 | 
				
			|||||||
      'javelin-install',
 | 
					      'javelin-install',
 | 
				
			||||||
      'javelin-util',
 | 
					      'javelin-util',
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
 | 
					    '2b735afc' => array(
 | 
				
			||||||
 | 
					      'javelin-install',
 | 
				
			||||||
 | 
					      'javelin-dom',
 | 
				
			||||||
 | 
					      'phuix-icon-view',
 | 
				
			||||||
 | 
					      'phabricator-prefab',
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
    '2b8de964' => array(
 | 
					    '2b8de964' => array(
 | 
				
			||||||
      'javelin-install',
 | 
					      'javelin-install',
 | 
				
			||||||
      'javelin-util',
 | 
					      'javelin-util',
 | 
				
			||||||
@@ -1589,18 +1595,6 @@ return array(
 | 
				
			|||||||
      'javelin-dom',
 | 
					      'javelin-dom',
 | 
				
			||||||
      'javelin-reactor-dom',
 | 
					      'javelin-reactor-dom',
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    'a15cbd65' => array(
 | 
					 | 
				
			||||||
      'javelin-install',
 | 
					 | 
				
			||||||
      'javelin-util',
 | 
					 | 
				
			||||||
      'javelin-dom',
 | 
					 | 
				
			||||||
      'javelin-typeahead',
 | 
					 | 
				
			||||||
      'javelin-tokenizer',
 | 
					 | 
				
			||||||
      'javelin-typeahead-preloaded-source',
 | 
					 | 
				
			||||||
      'javelin-typeahead-ondemand-source',
 | 
					 | 
				
			||||||
      'javelin-dom',
 | 
					 | 
				
			||||||
      'javelin-stratcom',
 | 
					 | 
				
			||||||
      'javelin-util',
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    'a16ec1c6' => array(
 | 
					    'a16ec1c6' => array(
 | 
				
			||||||
      'javelin-install',
 | 
					      'javelin-install',
 | 
				
			||||||
      'javelin-dom',
 | 
					      'javelin-dom',
 | 
				
			||||||
@@ -1799,12 +1793,6 @@ return array(
 | 
				
			|||||||
      'javelin-dom',
 | 
					      'javelin-dom',
 | 
				
			||||||
      'javelin-vector',
 | 
					      'javelin-vector',
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
    'c5f5e42f' => array(
 | 
					 | 
				
			||||||
      'javelin-install',
 | 
					 | 
				
			||||||
      'javelin-dom',
 | 
					 | 
				
			||||||
      'phuix-icon-view',
 | 
					 | 
				
			||||||
      'phabricator-prefab',
 | 
					 | 
				
			||||||
    ),
 | 
					 | 
				
			||||||
    'c6f720ff' => array(
 | 
					    'c6f720ff' => array(
 | 
				
			||||||
      'javelin-install',
 | 
					      'javelin-install',
 | 
				
			||||||
      'javelin-dom',
 | 
					      'javelin-dom',
 | 
				
			||||||
@@ -1977,6 +1965,18 @@ return array(
 | 
				
			|||||||
      'javelin-workflow',
 | 
					      'javelin-workflow',
 | 
				
			||||||
      'javelin-magical-init',
 | 
					      'javelin-magical-init',
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
 | 
					    'e67df814' => array(
 | 
				
			||||||
 | 
					      'javelin-install',
 | 
				
			||||||
 | 
					      'javelin-util',
 | 
				
			||||||
 | 
					      'javelin-dom',
 | 
				
			||||||
 | 
					      'javelin-typeahead',
 | 
				
			||||||
 | 
					      'javelin-tokenizer',
 | 
				
			||||||
 | 
					      'javelin-typeahead-preloaded-source',
 | 
				
			||||||
 | 
					      'javelin-typeahead-ondemand-source',
 | 
				
			||||||
 | 
					      'javelin-dom',
 | 
				
			||||||
 | 
					      'javelin-stratcom',
 | 
				
			||||||
 | 
					      'javelin-util',
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
    'e6e25838' => array(
 | 
					    'e6e25838' => array(
 | 
				
			||||||
      'javelin-install',
 | 
					      'javelin-install',
 | 
				
			||||||
    ),
 | 
					    ),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -99,81 +99,8 @@ JX.install('Prefab', {
 | 
				
			|||||||
        datasource = new JX.TypeaheadPreloadedSource(config.src);
 | 
					        datasource = new JX.TypeaheadPreloadedSource(config.src);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // Sort results so that the viewing user always comes up first; after
 | 
					      datasource.setSortHandler(
 | 
				
			||||||
      // that, prefer unixname matches to realname matches.
 | 
					        JX.bind(datasource, JX.Prefab.sortHandler, config));
 | 
				
			||||||
 | 
					 | 
				
			||||||
      var sort_handler = function(value, list, cmp) {
 | 
					 | 
				
			||||||
        var priority_hits = {};
 | 
					 | 
				
			||||||
        var self_hits     = {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        var tokens = this.tokenize(value);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for (var ii = 0; ii < list.length; ii++) {
 | 
					 | 
				
			||||||
          var item = list[ii];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          for (var jj = 0; jj < tokens.length; jj++) {
 | 
					 | 
				
			||||||
            if (item.name.indexOf(tokens[jj]) === 0) {
 | 
					 | 
				
			||||||
              priority_hits[item.id] = true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          if (!item.priority) {
 | 
					 | 
				
			||||||
            continue;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          if (config.username && item.priority == config.username) {
 | 
					 | 
				
			||||||
            self_hits[item.id] = true;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          for (var hh = 0; hh < tokens.length; hh++) {
 | 
					 | 
				
			||||||
            if (item.priority.substr(0, tokens[hh].length) == tokens[hh]) {
 | 
					 | 
				
			||||||
              priority_hits[item.id] = true;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        list.sort(function(u, v) {
 | 
					 | 
				
			||||||
          if (self_hits[u.id] != self_hits[v.id]) {
 | 
					 | 
				
			||||||
            return self_hits[v.id] ? 1 : -1;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          // If one result is open and one is closed, show the open result
 | 
					 | 
				
			||||||
          // first. The "!" tricks here are becaused closed values are display
 | 
					 | 
				
			||||||
          // strings, so the value is either `null` or some truthy string. If
 | 
					 | 
				
			||||||
          // we compare the values directly, we'll apply this rule to two
 | 
					 | 
				
			||||||
          // objects which are both closed but for different reasons, like
 | 
					 | 
				
			||||||
          // "Archived" and "Disabled".
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          var u_open = !u.closed;
 | 
					 | 
				
			||||||
          var v_open = !v.closed;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          if (u_open != v_open) {
 | 
					 | 
				
			||||||
            if (u_open) {
 | 
					 | 
				
			||||||
              return -1;
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
              return 1;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          if (priority_hits[u.id] != priority_hits[v.id]) {
 | 
					 | 
				
			||||||
            return priority_hits[v.id] ? 1 : -1;
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          // Sort users ahead of other result types.
 | 
					 | 
				
			||||||
          if (u.priorityType != v.priorityType) {
 | 
					 | 
				
			||||||
            if (u.priorityType == 'user') {
 | 
					 | 
				
			||||||
              return -1;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            if (v.priorityType == 'user') {
 | 
					 | 
				
			||||||
              return 1;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          return cmp(u, v);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      datasource.setSortHandler(JX.bind(datasource, sort_handler));
 | 
					 | 
				
			||||||
      datasource.setFilterHandler(JX.Prefab.filterClosedResults);
 | 
					      datasource.setFilterHandler(JX.Prefab.filterClosedResults);
 | 
				
			||||||
      datasource.setTransformer(JX.Prefab.transformDatasourceResults);
 | 
					      datasource.setTransformer(JX.Prefab.transformDatasourceResults);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -251,6 +178,80 @@ JX.install('Prefab', {
 | 
				
			|||||||
      };
 | 
					      };
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sortHandler: function(config, value, list, cmp) {
 | 
				
			||||||
 | 
					      // Sort results so that the viewing user always comes up first; after
 | 
				
			||||||
 | 
					      // that, prefer unixname matches to realname matches.
 | 
				
			||||||
 | 
					      var priority_hits = {};
 | 
				
			||||||
 | 
					      var self_hits = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      var tokens = this.tokenize(value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (var ii = 0; ii < list.length; ii++) {
 | 
				
			||||||
 | 
					        var item = list[ii];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (var jj = 0; jj < tokens.length; jj++) {
 | 
				
			||||||
 | 
					          if (item.name.indexOf(tokens[jj]) === 0) {
 | 
				
			||||||
 | 
					            priority_hits[item.id] = true;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (!item.priority) {
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (config.username && item.priority == config.username) {
 | 
				
			||||||
 | 
					          self_hits[item.id] = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (var hh = 0; hh < tokens.length; hh++) {
 | 
				
			||||||
 | 
					          if (item.priority.substr(0, tokens[hh].length) == tokens[hh]) {
 | 
				
			||||||
 | 
					            priority_hits[item.id] = true;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      list.sort(function(u, v) {
 | 
				
			||||||
 | 
					        if (self_hits[u.id] != self_hits[v.id]) {
 | 
				
			||||||
 | 
					          return self_hits[v.id] ? 1 : -1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // If one result is open and one is closed, show the open result
 | 
				
			||||||
 | 
					        // first. The "!" tricks here are becaused closed values are display
 | 
				
			||||||
 | 
					        // strings, so the value is either `null` or some truthy string. If
 | 
				
			||||||
 | 
					        // we compare the values directly, we'll apply this rule to two
 | 
				
			||||||
 | 
					        // objects which are both closed but for different reasons, like
 | 
				
			||||||
 | 
					        // "Archived" and "Disabled".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        var u_open = !u.closed;
 | 
				
			||||||
 | 
					        var v_open = !v.closed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (u_open != v_open) {
 | 
				
			||||||
 | 
					          if (u_open) {
 | 
				
			||||||
 | 
					            return -1;
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (priority_hits[u.id] != priority_hits[v.id]) {
 | 
				
			||||||
 | 
					          return priority_hits[v.id] ? 1 : -1;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Sort users ahead of other result types.
 | 
				
			||||||
 | 
					        if (u.priorityType != v.priorityType) {
 | 
				
			||||||
 | 
					          if (u.priorityType == 'user') {
 | 
				
			||||||
 | 
					            return -1;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if (v.priorityType == 'user') {
 | 
				
			||||||
 | 
					            return 1;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return cmp(u, v);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Filter callback for tokenizers and typeaheads which filters out closed
 | 
					     * Filter callback for tokenizers and typeaheads which filters out closed
 | 
				
			||||||
     * or disabled objects unless they are the only options.
 | 
					     * or disabled objects unless they are the only options.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,8 +60,14 @@ JX.install('PHUIXAutocomplete', {
 | 
				
			|||||||
      var device = JX.bind(this, this._ondevice);
 | 
					      var device = JX.bind(this, this._ondevice);
 | 
				
			||||||
      JX.Stratcom.listen('phabricator-device-change', null, device);
 | 
					      JX.Stratcom.listen('phabricator-device-change', null, device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      // When the user clicks away from the textarea, deactivate.
 | 
					      // When the user clicks away from the textarea, deactivate. However, we
 | 
				
			||||||
      var deactivate = JX.bind(this, this._deactivate);
 | 
					      // don't want to deactivate if we're blurring because they clicked an
 | 
				
			||||||
 | 
					      // option in the dropdown, so put a timeout on the deactivation. This
 | 
				
			||||||
 | 
					      // will let the click run first if they did actually click a result.
 | 
				
			||||||
 | 
					      var deactivate = JX.bind(this, function() {
 | 
				
			||||||
 | 
					        setTimeout(JX.bind(this, this._deactivate), 10);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      JX.DOM.listen(area, 'blur', null, deactivate);
 | 
					      JX.DOM.listen(area, 'blur', null, deactivate);
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -134,6 +140,9 @@ JX.install('PHUIXAutocomplete', {
 | 
				
			|||||||
          JX.bind(this, this._onresults, code));
 | 
					          JX.bind(this, this._onresults, code));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        datasource.setTransformer(JX.bind(this, this._transformresult));
 | 
					        datasource.setTransformer(JX.bind(this, this._transformresult));
 | 
				
			||||||
 | 
					        datasource.setSortHandler(
 | 
				
			||||||
 | 
					          JX.bind(datasource, JX.Prefab.sortHandler, {}));
 | 
				
			||||||
 | 
					        datasource.setFilterHandler(JX.Prefab.filterClosedResults);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this._datasources[code] = datasource;
 | 
					        this._datasources[code] = datasource;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user