Page MenuHomePhabricator

D20967.id49959.diff
No OneTemporary

D20967.id49959.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -146,7 +146,7 @@
'rsrc/css/phui/phui-comment-form.css' => '68a2d99a',
'rsrc/css/phui/phui-comment-panel.css' => 'ec4e31c0',
'rsrc/css/phui/phui-crumbs-view.css' => '614f43cf',
- 'rsrc/css/phui/phui-curtain-object-ref-view.css' => 'e3331b60',
+ 'rsrc/css/phui/phui-curtain-object-ref-view.css' => '12404744',
'rsrc/css/phui/phui-curtain-view.css' => '68c5efb6',
'rsrc/css/phui/phui-document-pro.css' => 'b9613a10',
'rsrc/css/phui/phui-document-summary.css' => 'b068eed1',
@@ -834,7 +834,7 @@
'phui-comment-form-css' => '68a2d99a',
'phui-comment-panel-css' => 'ec4e31c0',
'phui-crumbs-view-css' => '614f43cf',
- 'phui-curtain-object-ref-view-css' => 'e3331b60',
+ 'phui-curtain-object-ref-view-css' => '12404744',
'phui-curtain-view-css' => '68c5efb6',
'phui-document-summary-view-css' => 'b068eed1',
'phui-document-view-css' => '52b748a5',
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2042,6 +2042,7 @@
'PHUILauncherView' => 'view/phui/PHUILauncherView.php',
'PHUILeftRightExample' => 'applications/uiexample/examples/PHUILeftRightExample.php',
'PHUILeftRightView' => 'view/phui/PHUILeftRightView.php',
+ 'PHUILinkView' => 'view/phui/PHUILinkView.php',
'PHUIListExample' => 'applications/uiexample/examples/PHUIListExample.php',
'PHUIListItemView' => 'view/phui/PHUIListItemView.php',
'PHUIListView' => 'view/phui/PHUIListView.php',
@@ -8242,6 +8243,7 @@
'PHUILauncherView' => 'AphrontTagView',
'PHUILeftRightExample' => 'PhabricatorUIExample',
'PHUILeftRightView' => 'AphrontTagView',
+ 'PHUILinkView' => 'AphrontTagView',
'PHUIListExample' => 'PhabricatorUIExample',
'PHUIListItemView' => 'AphrontTagView',
'PHUIListView' => 'AphrontTagView',
diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php
--- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php
+++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php
@@ -336,6 +336,7 @@
$curtain->addAction($relationship_submenu);
}
+ $viewer_phid = $viewer->getPHID();
$owner_phid = $task->getOwnerPHID();
$author_phid = $task->getAuthorPHID();
$handles = $viewer->loadHandles(array($owner_phid, $author_phid));
@@ -346,7 +347,8 @@
if ($owner_phid) {
$assigned_ref = $assigned_refs->newObjectRefView()
- ->setHandle($handles[$owner_phid]);
+ ->setHandle($handles[$owner_phid])
+ ->setHighlighted($owner_phid === $viewer_phid);
}
$curtain->newPanel()
@@ -358,7 +360,8 @@
$author_ref = $author_refs->newObjectRefView()
->setHandle($handles[$author_phid])
- ->setEpoch($task->getDateCreated());
+ ->setEpoch($task->getDateCreated())
+ ->setHighlighted($author_phid === $viewer_phid);
$curtain->newPanel()
->setHeaderText(pht('Authored By'))
diff --git a/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsCurtainExtension.php b/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsCurtainExtension.php
--- a/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsCurtainExtension.php
+++ b/src/applications/subscriptions/engineextension/PhabricatorSubscriptionsCurtainExtension.php
@@ -15,25 +15,129 @@
public function buildCurtainPanel($object) {
$viewer = $this->getViewer();
+ $viewer_phid = $viewer->getPHID();
$object_phid = $object->getPHID();
+ $max_handles = 100;
+ $max_visible = 8;
+
+ // TODO: We should limit the number of subscriber PHIDs we'll load, so
+ // we degrade gracefully when objects have thousands of subscribers.
+
$subscriber_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID(
$object_phid);
+ $subscriber_count = count($subscriber_phids);
+
+ $subscriber_phids = $this->sortSubscriberPHIDs(
+ $subscriber_phids,
+ null);
+
+ // If we have fewer subscribers than the maximum number of handles we're
+ // willing to load, load all the handles and then sort the list based on
+ // complete handle data.
+
+ // If we have too many PHIDs, we'll skip this step and accept a less
+ // useful ordering.
+ $handles = null;
+ if ($subscriber_count <= $max_handles) {
+ $handles = $viewer->loadHandles($subscriber_phids);
+
+ $subscriber_phids = $this->sortSubscriberPHIDs(
+ $subscriber_phids,
+ $handles);
+ }
- $handles = $viewer->loadHandles($subscriber_phids);
+ // If we have more PHIDs to show than visible slots, slice the list.
+ if ($subscriber_count > $max_visible) {
+ $visible_phids = array_slice($subscriber_phids, 0, $max_visible - 1);
+ $show_all = true;
+ } else {
+ $visible_phids = $subscriber_phids;
+ $show_all = false;
+ }
- // TODO: This class can't accept a HandleList yet.
- $handles = iterator_to_array($handles);
+ // If we didn't load handles earlier because we had too many PHIDs,
+ // load them now.
+ if ($handles === null) {
+ $handles = $viewer->loadHandles($visible_phids);
+ }
- $susbscribers_view = id(new SubscriptionListStringBuilder())
- ->setObjectPHID($object_phid)
- ->setHandles($handles)
- ->buildPropertyString();
+ $ref_list = id(new PHUICurtainObjectRefListView())
+ ->setViewer($viewer)
+ ->setEmptyMessage(pht('None'));
+
+ foreach ($visible_phids as $phid) {
+ $ref = $ref_list->newObjectRefView()
+ ->setHandle($handles[$phid]);
+
+ if ($phid === $viewer_phid) {
+ $ref->setHighlighted(true);
+ }
+ }
+
+ if ($show_all) {
+ $view_all_uri = urisprintf(
+ '/subscriptions/list/%s/',
+ $object_phid);
+
+ $ref_list->newTailLink()
+ ->setURI($view_all_uri)
+ ->setText(pht('View All %d Subscriber(s)', $subscriber_count))
+ ->setWorkflow(true);
+ }
return $this->newPanel()
->setHeaderText(pht('Subscribers'))
->setOrder(20000)
- ->appendChild($susbscribers_view);
+ ->appendChild($ref_list);
+ }
+
+ private function sortSubscriberPHIDs(array $subscriber_phids, $handles) {
+
+ // Sort subscriber PHIDs with or without handle data. If we have handles,
+ // we can sort results more comprehensively.
+
+ $viewer = $this->getViewer();
+
+ $user_type = PhabricatorPeopleUserPHIDType::TYPECONST;
+ $viewer_phid = $viewer->getPHID();
+
+ $type_order_map = array(
+ PhabricatorPeopleUserPHIDType::TYPECONST => 0,
+ PhabricatorProjectProjectPHIDType::TYPECONST => 1,
+ PhabricatorOwnersPackagePHIDType::TYPECONST => 2,
+ );
+ $default_type_order = count($type_order_map);
+
+ $subscriber_map = array();
+ foreach ($subscriber_phids as $subscriber_phid) {
+ $is_viewer = ($viewer_phid === $subscriber_phid);
+
+ $subscriber_type = phid_get_type($subscriber_phid);
+ $type_order = idx($type_order_map, $subscriber_type, $default_type_order);
+
+ $sort_name = '';
+ $is_complete = false;
+ if ($handles) {
+ if (isset($handles[$subscriber_phid])) {
+ $handle = $handles[$subscriber_phid];
+ if ($handle->isComplete()) {
+ $is_complete = true;
+ $sort_name = $handle->getLinkName();
+ }
+ }
+ }
+
+ $subscriber_map[$subscriber_phid] = id(new PhutilSortVector())
+ ->addInt($is_viewer ? 0 : 1)
+ ->addInt($is_complete ? 0 : 1)
+ ->addInt($type_order)
+ ->addString($sort_name);
+ }
+
+ $subscriber_map = msortv($subscriber_map, 'getSelf');
+
+ return array_keys($subscriber_map);
}
}
diff --git a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php
--- a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php
+++ b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php
@@ -1718,6 +1718,11 @@
'then try again.',
),
+ 'View All %d Subscriber(s)' => array(
+ 'View Subscriber',
+ 'View All %d Subscribers',
+ ),
+
);
}
diff --git a/src/view/phui/PHUICurtainObjectRefListView.php b/src/view/phui/PHUICurtainObjectRefListView.php
--- a/src/view/phui/PHUICurtainObjectRefListView.php
+++ b/src/view/phui/PHUICurtainObjectRefListView.php
@@ -5,6 +5,7 @@
private $refs = array();
private $emptyMessage;
+ private $tail = array();
protected function getTagAttributes() {
return array(
@@ -20,18 +21,31 @@
protected function getTagContent() {
$refs = $this->refs;
- if (!$refs) {
- if ($this->emptyMessage) {
- return phutil_tag(
- 'div',
- array(
- 'class' => 'phui-curtain-object-ref-list-view-empty',
- ),
- $this->emptyMessage);
- }
+ if (!$refs && ($this->emptyMessage !== null)) {
+ $view = phutil_tag(
+ 'div',
+ array(
+ 'class' => 'phui-curtain-object-ref-list-view-empty',
+ ),
+ $this->emptyMessage);
+ } else {
+ $view = $refs;
}
- return $refs;
+ $tail = null;
+ if ($this->tail) {
+ $tail = phutil_tag(
+ 'div',
+ array(
+ 'class' => 'phui-curtain-object-ref-list-view-tail',
+ ),
+ $this->tail);
+ }
+
+ return array(
+ $view,
+ $tail,
+ );
}
public function newObjectRefView() {
@@ -43,4 +57,12 @@
return $ref_view;
}
+ public function newTailLink() {
+ $link = new PHUILinkView();
+
+ $this->tail[] = $link;
+
+ return $link;
+ }
+
}
diff --git a/src/view/phui/PHUICurtainObjectRefView.php b/src/view/phui/PHUICurtainObjectRefView.php
--- a/src/view/phui/PHUICurtainObjectRefView.php
+++ b/src/view/phui/PHUICurtainObjectRefView.php
@@ -5,6 +5,7 @@
private $handle;
private $epoch;
+ private $highlighted;
public function setHandle(PhabricatorObjectHandle $handle) {
$this->handle = $handle;
@@ -16,9 +17,22 @@
return $this;
}
+ public function setHighlighted($highlighted) {
+ $this->highlighted = $highlighted;
+ return $this;
+ }
+
protected function getTagAttributes() {
+ $classes = array();
+ $classes[] = 'phui-curtain-object-ref-view';
+
+ if ($this->highlighted) {
+ $classes[] = 'phui-curtain-object-ref-view-highlighted';
+ }
+ $classes = implode(' ', $classes);
+
return array(
- 'class' => 'phui-curtain-object-ref-view',
+ 'class' => $classes,
);
}
@@ -114,6 +128,11 @@
$image_uri = $this->getImageURI();
$target_uri = $this->getTargetURI();
+ $icon_view = null;
+ if ($image_uri == null) {
+ $icon_view = $this->newIconView();
+ }
+
if ($image_uri !== null) {
$image_view = javelin_tag(
'a',
@@ -122,6 +141,15 @@
'href' => $target_uri,
'aural' => false,
));
+ } else if ($icon_view !== null) {
+ $image_view = javelin_tag(
+ 'a',
+ array(
+ 'href' => $target_uri,
+ 'class' => 'phui-curtain-object-ref-view-icon-image',
+ 'aural' => false,
+ ),
+ $icon_view);
} else {
$image_view = null;
}
@@ -151,5 +179,16 @@
return $image_uri;
}
+ private function newIconView() {
+ $handle = $this->handle;
+
+ if ($handle) {
+ $icon_view = id(new PHUIIconView())
+ ->setIcon($handle->getIcon());
+ }
+
+ return $icon_view;
+ }
+
}
diff --git a/src/view/phui/PHUILinkView.php b/src/view/phui/PHUILinkView.php
new file mode 100644
--- /dev/null
+++ b/src/view/phui/PHUILinkView.php
@@ -0,0 +1,50 @@
+<?php
+
+final class PHUILinkView
+ extends AphrontTagView {
+
+ private $uri;
+ private $text;
+ private $workflow;
+
+ public function setURI($uri) {
+ $this->uri = $uri;
+ return $this;
+ }
+
+ public function getURI() {
+ return $this->uri;
+ }
+
+ public function setText($text) {
+ $this->text = $text;
+ return $this;
+ }
+
+ public function setWorkflow($workflow) {
+ $this->workflow = $workflow;
+ return $this;
+ }
+
+ protected function getTagName() {
+ return 'a';
+ }
+
+ protected function getTagAttributes() {
+ $sigil = array();
+
+ if ($this->workflow) {
+ $sigil[] = 'workflow';
+ }
+
+ return array(
+ 'href' => $this->getURI(),
+ 'sigil' => $sigil,
+ );
+ }
+
+ protected function getTagContent() {
+ return $this->text;
+ }
+
+}
diff --git a/webroot/rsrc/css/phui/phui-curtain-object-ref-view.css b/webroot/rsrc/css/phui/phui-curtain-object-ref-view.css
--- a/webroot/rsrc/css/phui/phui-curtain-object-ref-view.css
+++ b/webroot/rsrc/css/phui/phui-curtain-object-ref-view.css
@@ -7,9 +7,14 @@
color: {$greytext};
}
+.phui-curtain-object-ref-view {
+ padding: 4px 6px;
+ border-radius: 3px;
+}
+
.phui-curtain-object-ref-view-image-cell {
min-width: 32px;
- min-height: 32px;
+ padding-bottom: 24px;
}
.phui-curtain-object-ref-view-image-cell > a {
@@ -18,6 +23,24 @@
background-size: 100%;
border-radius: 3px;
display: block;
+ position: absolute;
+}
+
+.phui-curtain-object-ref-view-image-cell .phui-icon-view {
+ font-size: 16px;
+ line-height: 16px;
+ vertical-align: middle;
+ text-align: center;
+ width: 24px;
+ height: 24px;
+ top: 3px;
+ display: block;
+ position: absolute;
+ color: #ffffff;
+}
+
+.phui-curtain-object-ref-view-icon-image {
+ background-color: {$backdrop};
}
.phui-curtain-object-ref-view-title-cell {
@@ -26,7 +49,7 @@
overflow: hidden;
/* This is forcing "text-overflow: ellipsis" to actually work. */
- max-width: 225px;
+ max-width: 210px;
}
.phui-curtain-object-ref-view-without-content >
@@ -46,3 +69,16 @@
.phui-curtain-object-ref-view-epoch-cell {
color: {$greytext};
}
+
+.phui-curtain-object-ref-list-view-tail {
+ text-align: center;
+ margin-top: 8px;
+ padding: 4px;
+ background: {$lightgreybackground};
+ border-top: 1px dashed {$thinblueborder};
+ box-shadow: inset 0 2px 3px rgba(0, 0, 0, 0.04);
+}
+
+.phui-curtain-object-ref-view-highlighted {
+ background: {$bluebackground};
+}

File Metadata

Mime Type
text/plain
Expires
May 16 2024, 2:55 AM (9 w, 3 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6287087
Default Alt Text
D20967.id49959.diff (14 KB)

Event Timeline