Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14840931
D20967.id49959.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
14 KB
Referenced Files
None
Subscribers
None
D20967.id49959.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Sun, Feb 2, 10:53 AM (7 h, 57 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7084264
Default Alt Text
D20967.id49959.diff (14 KB)
Attached To
Mode
D20967: Use the new "CurtainObjectRefList" UI element for subscribers
Attached
Detach File
Event Timeline
Log In to Comment