mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 06:42:42 +01:00
Use the new "CurtainObjectRefList" UI element for subscribers
Summary: Depends on D20966. Ref T13486. Curtains currently render subscribers in a plain text list, but the new ref list element is a good fit for this. Also, improve the sorting and ordering behavior. This makes the subscriber list take up a bit more space, but it should make it a lot easier to read at a glance. Test Plan: Viewed object subscriber lists at varying limits and subscriber counts, saw sensible subscriber lists. Maniphest Tasks: T13486 Differential Revision: https://secure.phabricator.com/D20967
This commit is contained in:
parent
2a92fef879
commit
0e82bd024a
9 changed files with 286 additions and 25 deletions
|
@ -146,7 +146,7 @@ return array(
|
|||
'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 @@ return array(
|
|||
'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',
|
||||
|
|
|
@ -2042,6 +2042,7 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'PHUILauncherView' => 'AphrontTagView',
|
||||
'PHUILeftRightExample' => 'PhabricatorUIExample',
|
||||
'PHUILeftRightView' => 'AphrontTagView',
|
||||
'PHUILinkView' => 'AphrontTagView',
|
||||
'PHUIListExample' => 'PhabricatorUIExample',
|
||||
'PHUIListItemView' => 'AphrontTagView',
|
||||
'PHUIListView' => 'AphrontTagView',
|
||||
|
|
|
@ -336,6 +336,7 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
|||
$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 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
|||
|
||||
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 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
|||
|
||||
$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'))
|
||||
|
|
|
@ -15,25 +15,129 @@ final class PhabricatorSubscriptionsCurtainExtension
|
|||
|
||||
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);
|
||||
|
||||
$handles = $viewer->loadHandles($subscriber_phids);
|
||||
$subscriber_phids = $this->sortSubscriberPHIDs(
|
||||
$subscriber_phids,
|
||||
null);
|
||||
|
||||
// TODO: This class can't accept a HandleList yet.
|
||||
$handles = iterator_to_array($handles);
|
||||
// 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.
|
||||
|
||||
$susbscribers_view = id(new SubscriptionListStringBuilder())
|
||||
->setObjectPHID($object_phid)
|
||||
->setHandles($handles)
|
||||
->buildPropertyString();
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// If we didn't load handles earlier because we had too many PHIDs,
|
||||
// load them now.
|
||||
if ($handles === null) {
|
||||
$handles = $viewer->loadHandles($visible_phids);
|
||||
}
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1718,6 +1718,11 @@ final class PhabricatorUSEnglishTranslation
|
|||
'then try again.',
|
||||
),
|
||||
|
||||
'View All %d Subscriber(s)' => array(
|
||||
'View Subscriber',
|
||||
'View All %d Subscribers',
|
||||
),
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ final class PHUICurtainObjectRefListView
|
|||
|
||||
private $refs = array();
|
||||
private $emptyMessage;
|
||||
private $tail = array();
|
||||
|
||||
protected function getTagAttributes() {
|
||||
return array(
|
||||
|
@ -20,18 +21,31 @@ final class PHUICurtainObjectRefListView
|
|||
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 @@ final class PHUICurtainObjectRefListView
|
|||
return $ref_view;
|
||||
}
|
||||
|
||||
public function newTailLink() {
|
||||
$link = new PHUILinkView();
|
||||
|
||||
$this->tail[] = $link;
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ final class PHUICurtainObjectRefView
|
|||
|
||||
private $handle;
|
||||
private $epoch;
|
||||
private $highlighted;
|
||||
|
||||
public function setHandle(PhabricatorObjectHandle $handle) {
|
||||
$this->handle = $handle;
|
||||
|
@ -16,9 +17,22 @@ final class PHUICurtainObjectRefView
|
|||
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 @@ final class PHUICurtainObjectRefView
|
|||
$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 @@ final class PHUICurtainObjectRefView
|
|||
'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 @@ final class PHUICurtainObjectRefView
|
|||
return $image_uri;
|
||||
}
|
||||
|
||||
private function newIconView() {
|
||||
$handle = $this->handle;
|
||||
|
||||
if ($handle) {
|
||||
$icon_view = id(new PHUIIconView())
|
||||
->setIcon($handle->getIcon());
|
||||
}
|
||||
|
||||
return $icon_view;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
50
src/view/phui/PHUILinkView.php
Normal file
50
src/view/phui/PHUILinkView.php
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue