Page MenuHomePhabricator

D12425.id29874.diff
No OneTemporary

D12425.id29874.diff

diff --git a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php
--- a/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php
+++ b/src/applications/typeahead/controller/PhabricatorTypeaheadModularDatasourceController.php
@@ -37,9 +37,18 @@
->setQuery($query)
->setRawQuery($raw_query);
+ $hard_limit = 1000;
+
if ($is_browse) {
$limit = 10;
$offset = $request->getInt('offset');
+
+ if (($offset + $limit) >= $hard_limit) {
+ // Offset-based paging is intrinsically slow; hard-cap how far we're
+ // willing to go with it.
+ return new Aphront404Response();
+ }
+
$composite
->setLimit($limit + 1)
->setOffset($offset);
@@ -49,15 +58,32 @@
if ($is_browse) {
$next_link = null;
+
if (count($results) > $limit) {
$results = array_slice($results, 0, $limit, $preserve_keys = true);
- $next_link = phutil_tag(
- 'a',
- array(
- 'href' => id(new PhutilURI($request->getRequestURI()))
- ->setQueryParam('offset', $offset + $limit),
- ),
- pht('Next Page'));
+ if (($offset + (2 * $limit)) < $hard_limit) {
+ $next_uri = id(new PhutilURI($request->getRequestURI()))
+ ->setQueryParam('offset', $offset + $limit);
+
+ $next_link = javelin_tag(
+ 'a',
+ array(
+ 'href' => $next_uri,
+ 'class' => 'typeahead-browse-more',
+ 'sigil' => 'typeahead-browse-more',
+ 'mustcapture' => true,
+ ),
+ pht('More Results'));
+ } else {
+ // If the user has paged through more than 1K results, don't
+ // offer to page any further.
+ $next_link = javelin_tag(
+ 'div',
+ array(
+ 'class' => 'typeahead-browse-hard-limit',
+ ),
+ pht('You reach the edge of the abyss.'));
+ }
}
$items = array();
@@ -72,11 +98,32 @@
$token);
}
+ $markup = array(
+ $items,
+ $next_link,
+ );
+
+ if ($request->isAjax()) {
+ $content = array(
+ 'markup' => hsprintf('%s', $markup),
+ );
+ return id(new AphrontAjaxResponse())->setContent($content);
+ }
+
+ $this->requireResource('typeahead-browse-css');
+ $this->initBehavior('typeahead-browse');
+
+ $markup = phutil_tag(
+ 'div',
+ array(
+ 'class' => 'typeahead-browse-frame',
+ ),
+ $markup);
+
return $this->newDialog()
->setWidth(AphrontDialogView::WIDTH_FORM)
->setTitle(get_class($source)) // TODO: Provide nice names.
- ->appendChild($items)
- ->appendChild($next_link)
+ ->appendChild($markup)
->addCancelButton('/', pht('Close'));
}
diff --git a/webroot/rsrc/css/aphront/typeahead-browse.css b/webroot/rsrc/css/aphront/typeahead-browse.css
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/css/aphront/typeahead-browse.css
@@ -0,0 +1,34 @@
+/**
+ * @provides typeahead-browse-css
+ */
+
+.typeahead-browse-more,
+.typeahead-browse-hard-limit {
+ display: block;
+ padding: 8px;
+ margin: 8px 0 0;
+ text-align: center;
+}
+
+.typeahead-browse-more {
+ background: {$lightblue};
+ border: 1px solid {$lightblueborder};
+}
+
+.typeahead-browse-more.loading {
+ opacity: 0.8;
+}
+
+.typeahead-browse-hard-limit {
+ background: {$lightgreybackground};
+ border: 1px solid {$lightgreyborder};
+ color: {$lightgreytext};
+}
+
+.typeahead-browse-frame {
+ overflow-x: hidden;
+ overflow-y: auto;
+ padding: 4px;
+ height: 260px;
+ border: 1px solid {$lightgreyborder};
+}
diff --git a/webroot/rsrc/js/application/typeahead/behavior-typeahead-browse.js b/webroot/rsrc/js/application/typeahead/behavior-typeahead-browse.js
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/js/application/typeahead/behavior-typeahead-browse.js
@@ -0,0 +1,31 @@
+/**
+ * @provides javelin-behavior-typeahead-browse
+ * @requires javelin-behavior
+ * javelin-stratcom
+ * javelin-workflow
+ * javelin-dom
+ */
+
+JX.behavior('typeahead-browse', function() {
+ var loading = false;
+
+ JX.Stratcom.listen('click', 'typeahead-browse-more', function(e) {
+ e.kill();
+
+ if (loading) {
+ return;
+ }
+ var link = e.getTarget();
+
+ loading = true;
+ JX.DOM.alterClass(link, 'loading', true);
+
+ JX.Workflow.newFromLink(link)
+ .setHandler(function(r) {
+ loading = false;
+ JX.DOM.replace(link, JX.$H(r.markup));
+ })
+ .start();
+ });
+
+});

File Metadata

Mime Type
text/plain
Expires
Tue, Nov 5, 4:19 PM (1 w, 6 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6732154
Default Alt Text
D12425.id29874.diff (4 KB)

Event Timeline