@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

[Rough Sketch] Differential ObjectItemView Smexyness

Summary:
Tried out `PhabricatorObjectItemView` for Differential. It looks smexy and smooth.

Refs T2014

- Title and Date as Maniphest
- Author in the handle icon
- Bar color reflects revision status (Needs Review, Accepted, Abandoned etc.) @chad looking for non-blue is faster than keeping watch for everything that's not "Closed" in old table form
- Some status information are in footer icons; currently only stale/old status display as well as saved drafts, maybe more in future; these come into my mind:
- No reviewer warning
- Push Blocking Priority (T2730)
- Trivial, fast review guaranteed
- Sketch / Just looking for advice/help
- Arcanist Project (T2614)
- Denote "Public Send-in" (T1476)

{F37662}
{F37663}
{F37664}
{F37665}

Some flaws:

- Date and reviewers on every entry the same?
- No respect for Differential fields (for some reason, every entry appeared the same, so broke it to parts)
- Plenty of (potential) increase in height - advise reducing paging length from 100 to 50 - or just ignore me

Suggestions for the future:

- Expand the meta information regarding revisions; e.g. the various status displays above
- Uh... T2543, T1279, T793, T731 and what else I want for Differential, because they are awesome!
- T793 should be in particular easy appearance-wise, just copy-paste from Maniphest

Test Plan: By looking at it, of course. Verified there are no errors or crashed

Reviewers: epriestley, chad, btrahan, liguobig

Reviewed By: chad

CC: aran, Korvin, edward, nh

Maniphest Tasks: T2014

Differential Revision: https://secure.phabricator.com/D5451

Conflicts:
src/__celerity_resource_map__.php

authored by

Anh Nhan Nguyen and committed by
epriestley
d9f01d6f 23e18b1c

+159 -142
+46 -46
src/__celerity_resource_map__.php
··· 805 805 ), 806 806 'aphront-error-view-css' => 807 807 array( 808 - 'uri' => '/res/a574aa01/rsrc/css/aphront/error-view.css', 808 + 'uri' => '/res/e2bb50c4/rsrc/css/aphront/error-view.css', 809 809 'type' => 'css', 810 810 'requires' => 811 811 array( ··· 3249 3249 ), 3250 3250 'phabricator-object-item-list-view-css' => 3251 3251 array( 3252 - 'uri' => '/res/8d18c133/rsrc/css/layout/phabricator-object-item-list-view.css', 3252 + 'uri' => '/res/d58ecb3c/rsrc/css/layout/phabricator-object-item-list-view.css', 3253 3253 'type' => 'css', 3254 3254 'requires' => 3255 3255 array( ··· 4073 4073 ), array( 4074 4074 'packages' => 4075 4075 array( 4076 - 'a1d34b1a' => 4076 + '0ad4a08f' => 4077 4077 array( 4078 4078 'name' => 'core.pkg.css', 4079 4079 'symbols' => ··· 4121 4121 40 => 'phabricator-property-list-view-css', 4122 4122 41 => 'phabricator-tag-view-css', 4123 4123 ), 4124 - 'uri' => '/res/pkg/a1d34b1a/core.pkg.css', 4124 + 'uri' => '/res/pkg/0ad4a08f/core.pkg.css', 4125 4125 'type' => 'css', 4126 4126 ), 4127 4127 'f2ad0683' => ··· 4315 4315 'reverse' => 4316 4316 array( 4317 4317 'aphront-attached-file-view-css' => 'adc3c36d', 4318 - 'aphront-dialog-view-css' => 'a1d34b1a', 4319 - 'aphront-error-view-css' => 'a1d34b1a', 4320 - 'aphront-form-view-css' => 'a1d34b1a', 4321 - 'aphront-list-filter-view-css' => 'a1d34b1a', 4322 - 'aphront-pager-view-css' => 'a1d34b1a', 4323 - 'aphront-panel-view-css' => 'a1d34b1a', 4324 - 'aphront-table-view-css' => 'a1d34b1a', 4325 - 'aphront-tokenizer-control-css' => 'a1d34b1a', 4326 - 'aphront-tooltip-css' => 'a1d34b1a', 4327 - 'aphront-typeahead-control-css' => 'a1d34b1a', 4318 + 'aphront-dialog-view-css' => '0ad4a08f', 4319 + 'aphront-error-view-css' => '0ad4a08f', 4320 + 'aphront-form-view-css' => '0ad4a08f', 4321 + 'aphront-list-filter-view-css' => '0ad4a08f', 4322 + 'aphront-pager-view-css' => '0ad4a08f', 4323 + 'aphront-panel-view-css' => '0ad4a08f', 4324 + 'aphront-table-view-css' => '0ad4a08f', 4325 + 'aphront-tokenizer-control-css' => '0ad4a08f', 4326 + 'aphront-tooltip-css' => '0ad4a08f', 4327 + 'aphront-typeahead-control-css' => '0ad4a08f', 4328 4328 'differential-changeset-view-css' => 'dd27a69b', 4329 4329 'differential-core-view-css' => 'dd27a69b', 4330 4330 'differential-inline-comment-editor' => '9488bb69', ··· 4338 4338 'differential-table-of-contents-css' => 'dd27a69b', 4339 4339 'diffusion-commit-view-css' => 'c8ce2d88', 4340 4340 'diffusion-icons-css' => 'c8ce2d88', 4341 - 'global-drag-and-drop-css' => 'a1d34b1a', 4341 + 'global-drag-and-drop-css' => '0ad4a08f', 4342 4342 'inline-comment-summary-css' => 'dd27a69b', 4343 4343 'javelin-aphlict' => 'f2ad0683', 4344 4344 'javelin-behavior' => 'a9f14d76', ··· 4412 4412 'javelin-util' => 'a9f14d76', 4413 4413 'javelin-vector' => 'a9f14d76', 4414 4414 'javelin-workflow' => 'a9f14d76', 4415 - 'lightbox-attachment-css' => 'a1d34b1a', 4415 + 'lightbox-attachment-css' => '0ad4a08f', 4416 4416 'maniphest-task-summary-css' => 'adc3c36d', 4417 4417 'maniphest-transaction-detail-css' => 'adc3c36d', 4418 - 'phabricator-action-list-view-css' => 'a1d34b1a', 4419 - 'phabricator-application-launch-view-css' => 'a1d34b1a', 4418 + 'phabricator-action-list-view-css' => '0ad4a08f', 4419 + 'phabricator-application-launch-view-css' => '0ad4a08f', 4420 4420 'phabricator-busy' => 'f2ad0683', 4421 4421 'phabricator-content-source-view-css' => 'dd27a69b', 4422 - 'phabricator-core-css' => 'a1d34b1a', 4423 - 'phabricator-crumbs-view-css' => 'a1d34b1a', 4422 + 'phabricator-core-css' => '0ad4a08f', 4423 + 'phabricator-crumbs-view-css' => '0ad4a08f', 4424 4424 'phabricator-drag-and-drop-file-upload' => '9488bb69', 4425 4425 'phabricator-dropdown-menu' => 'f2ad0683', 4426 4426 'phabricator-file-upload' => 'f2ad0683', 4427 - 'phabricator-filetree-view-css' => 'a1d34b1a', 4428 - 'phabricator-flag-css' => 'a1d34b1a', 4429 - 'phabricator-form-view-css' => 'a1d34b1a', 4430 - 'phabricator-header-view-css' => 'a1d34b1a', 4427 + 'phabricator-filetree-view-css' => '0ad4a08f', 4428 + 'phabricator-flag-css' => '0ad4a08f', 4429 + 'phabricator-form-view-css' => '0ad4a08f', 4430 + 'phabricator-header-view-css' => '0ad4a08f', 4431 4431 'phabricator-hovercard' => 'f2ad0683', 4432 - 'phabricator-jump-nav' => 'a1d34b1a', 4432 + 'phabricator-jump-nav' => '0ad4a08f', 4433 4433 'phabricator-keyboard-shortcut' => 'f2ad0683', 4434 4434 'phabricator-keyboard-shortcut-manager' => 'f2ad0683', 4435 - 'phabricator-main-menu-view' => 'a1d34b1a', 4435 + 'phabricator-main-menu-view' => '0ad4a08f', 4436 4436 'phabricator-menu-item' => 'f2ad0683', 4437 - 'phabricator-nav-view-css' => 'a1d34b1a', 4437 + 'phabricator-nav-view-css' => '0ad4a08f', 4438 4438 'phabricator-notification' => 'f2ad0683', 4439 - 'phabricator-notification-css' => 'a1d34b1a', 4440 - 'phabricator-notification-menu-css' => 'a1d34b1a', 4441 - 'phabricator-object-item-list-view-css' => 'a1d34b1a', 4439 + 'phabricator-notification-css' => '0ad4a08f', 4440 + 'phabricator-notification-menu-css' => '0ad4a08f', 4441 + 'phabricator-object-item-list-view-css' => '0ad4a08f', 4442 4442 'phabricator-object-selector-css' => 'dd27a69b', 4443 4443 'phabricator-phtize' => 'f2ad0683', 4444 4444 'phabricator-prefab' => 'f2ad0683', 4445 4445 'phabricator-project-tag-css' => 'adc3c36d', 4446 - 'phabricator-property-list-view-css' => 'a1d34b1a', 4447 - 'phabricator-remarkup-css' => 'a1d34b1a', 4446 + 'phabricator-property-list-view-css' => '0ad4a08f', 4447 + 'phabricator-remarkup-css' => '0ad4a08f', 4448 4448 'phabricator-shaped-request' => '9488bb69', 4449 - 'phabricator-side-menu-view-css' => 'a1d34b1a', 4450 - 'phabricator-standard-page-view' => 'a1d34b1a', 4451 - 'phabricator-tag-view-css' => 'a1d34b1a', 4449 + 'phabricator-side-menu-view-css' => '0ad4a08f', 4450 + 'phabricator-standard-page-view' => '0ad4a08f', 4451 + 'phabricator-tag-view-css' => '0ad4a08f', 4452 4452 'phabricator-textareautils' => 'f2ad0683', 4453 4453 'phabricator-tooltip' => 'f2ad0683', 4454 - 'phabricator-transaction-view-css' => 'a1d34b1a', 4455 - 'phabricator-zindex-css' => 'a1d34b1a', 4456 - 'phui-button-css' => 'a1d34b1a', 4457 - 'phui-form-css' => 'a1d34b1a', 4458 - 'phui-icon-view-css' => 'a1d34b1a', 4459 - 'phui-spacing-css' => 'a1d34b1a', 4460 - 'sprite-apps-large-css' => 'a1d34b1a', 4461 - 'sprite-gradient-css' => 'a1d34b1a', 4462 - 'sprite-icons-css' => 'a1d34b1a', 4463 - 'sprite-menu-css' => 'a1d34b1a', 4464 - 'syntax-highlighting-css' => 'a1d34b1a', 4454 + 'phabricator-transaction-view-css' => '0ad4a08f', 4455 + 'phabricator-zindex-css' => '0ad4a08f', 4456 + 'phui-button-css' => '0ad4a08f', 4457 + 'phui-form-css' => '0ad4a08f', 4458 + 'phui-icon-view-css' => '0ad4a08f', 4459 + 'phui-spacing-css' => '0ad4a08f', 4460 + 'sprite-apps-large-css' => '0ad4a08f', 4461 + 'sprite-gradient-css' => '0ad4a08f', 4462 + 'sprite-icons-css' => '0ad4a08f', 4463 + 'sprite-menu-css' => '0ad4a08f', 4464 + 'syntax-highlighting-css' => '0ad4a08f', 4465 4465 ), 4466 4466 ));
+1
src/__phutil_library_map__.php
··· 2319 2319 'DifferentialRevisionListController' => 'DifferentialController', 2320 2320 'DifferentialRevisionListView' => 'AphrontView', 2321 2321 'DifferentialRevisionMailReceiver' => 'PhabricatorObjectMailReceiver', 2322 + 'DifferentialRevisionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 2322 2323 'DifferentialRevisionStatsView' => 'AphrontView', 2323 2324 'DifferentialRevisionStatusFieldSpecification' => 'DifferentialFieldSpecification', 2324 2325 'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
+6 -23
src/applications/differential/field/specification/DifferentialReviewersFieldSpecification.php
··· 138 138 public function renderValueForRevisionList(DifferentialRevision $revision) { 139 139 $primary_reviewer = $revision->getPrimaryReviewer(); 140 140 if ($primary_reviewer) { 141 - $other_reviewers = array_flip($revision->getReviewers()); 142 - unset($other_reviewers[$primary_reviewer]); 143 - if ($other_reviewers) { 144 - $names = array(); 145 - foreach ($other_reviewers as $reviewer => $_) { 146 - $names[] = $this->getHandle($reviewer)->getLinkName(); 147 - } 148 - $suffix = javelin_tag( 149 - 'abbr', 150 - array( 151 - 'sigil' => 'has-tooltip', 152 - 'meta' => array( 153 - 'tip' => implode(', ', $names), 154 - 'align' => 'E', 155 - ), 156 - ), 157 - '(+'.(count($names)).')'); 158 - } else { 159 - $suffix = null; 141 + $names = array(); 142 + 143 + foreach ($revision->getReviewers() as $reviewer) { 144 + $names[] = $this->getHandle($reviewer)->renderLink(); 160 145 } 161 - return hsprintf( 162 - '%s %s', 163 - $this->getHandle($primary_reviewer)->renderLink(), 164 - $suffix); 146 + 147 + return phutil_implode_html(', ', $names); 165 148 } else { 166 149 return phutil_tag('em', array(), 'None'); 167 150 }
+89 -73
src/applications/differential/view/DifferentialRevisionListView.php
··· 76 76 throw new Exception("Call setUser() before render()!"); 77 77 } 78 78 79 - $fresh = null; 80 - $stale = null; 79 + $fresh = PhabricatorEnv::getEnvConfig('differential.days-fresh'); 80 + if ($fresh) { 81 + $fresh = PhabricatorCalendarHoliday::getNthBusinessDay( 82 + time(), 83 + -$fresh); 84 + } 81 85 82 - if ($this->highlightAge) { 83 - $fresh = PhabricatorEnv::getEnvConfig('differential.days-fresh'); 84 - if ($fresh) { 85 - $fresh = PhabricatorCalendarHoliday::getNthBusinessDay( 86 - time(), 87 - -$fresh); 88 - } 89 - 90 - $stale = PhabricatorEnv::getEnvConfig('differential.days-stale'); 91 - if ($stale) { 92 - $stale = PhabricatorCalendarHoliday::getNthBusinessDay( 93 - time(), 94 - -$stale); 95 - } 86 + $stale = PhabricatorEnv::getEnvConfig('differential.days-stale'); 87 + if ($stale) { 88 + $stale = PhabricatorCalendarHoliday::getNthBusinessDay( 89 + time(), 90 + -$stale); 96 91 } 97 92 98 93 Javelin::initBehavior('phabricator-tooltips', array()); ··· 101 96 $flagged = mpull($this->flags, null, 'getObjectPHID'); 102 97 103 98 foreach ($this->fields as $field) { 104 - $field->setUser($this->user); 105 99 $field->setHandles($this->handles); 106 100 } 107 101 108 - $cell_classes = array(); 109 - $rows = array(); 102 + $list = new PhabricatorObjectItemListView(); 103 + $list->setCards(true); 104 + $list->setFlush(true); 105 + 110 106 foreach ($this->revisions as $revision) { 107 + $item = new PhabricatorObjectItemView(); 108 + $rev_fields = array(); 109 + $icons = array(); 110 + 111 111 $phid = $revision->getPHID(); 112 - $flag = ''; 113 112 if (isset($flagged[$phid])) { 114 - $class = PhabricatorFlagColor::getCSSClass($flagged[$phid]->getColor()); 115 - $note = $flagged[$phid]->getNote(); 116 - $flag = javelin_tag( 117 - 'div', 118 - $note ? array( 119 - 'class' => 'phabricator-flag-icon '.$class, 120 - 'sigil' => 'has-tooltip', 121 - 'meta' => array( 122 - 'tip' => $note, 123 - 'align' => 'N', 124 - 'size' => 240, 125 - ), 126 - ) : array( 127 - 'class' => 'phabricator-flag-icon '.$class, 128 - ), 129 - ''); 130 - 131 - } else if (array_key_exists($revision->getID(), $this->drafts)) { 132 - $src = '/rsrc/image/icon/fatcow/page_white_edit.png'; 133 - $flag = hsprintf( 134 - '<a href="%s">%s</a>', 135 - '/D'.$revision->getID().'#comment-preview', 136 - phutil_tag( 137 - 'img', 138 - array( 139 - 'src' => celerity_get_resource_uri($src), 140 - 'width' => 16, 141 - 'height' => 16, 142 - 'alt' => 'Draft', 143 - 'title' => pht('Draft Comment'), 144 - ))); 113 + $icons['flag'] = array( 114 + 'icon' => 'flag-'.$flagged[$phid]->getColor(), 115 + ); 145 116 } 146 - 147 - $row = array($flag); 117 + if (array_key_exists($revision->getID(), $this->drafts)) { 118 + $icons['draft'] = array( 119 + 'icon' => 'file-white', 120 + 'href' => '/D'.$revision->getID().'#comment-preview', 121 + ); 122 + } 148 123 149 124 $modified = $revision->getDateModified(); 150 125 ··· 152 127 if (($fresh || $stale) && 153 128 $field instanceof DifferentialDateModifiedFieldSpecification) { 154 129 if ($stale && $modified < $stale) { 155 - $class = 'revision-age-old'; 130 + $days = floor((time() - $modified) / 60 / 60 / 24); 131 + $icons['age'] = array( 132 + 'icon' => 'warning-white', 133 + 'label' => pht('Old (%d days)', $days), 134 + ); 156 135 } else if ($fresh && $modified < $fresh) { 157 - $class = 'revision-age-stale'; 136 + $days = floor((time() - $modified) / 60 / 60 / 24); 137 + $icons['age'] = array( 138 + 'icon' => 'perflab-white', 139 + 'label' => pht('Stale (%d days)', $days), 140 + ); 158 141 } else { 159 - $class = 'revision-age-fresh'; 142 + // Fresh, noOp(); 160 143 } 161 - $cell_classes[count($rows)][count($row)] = $class; 162 144 } 163 145 164 - $row[] = $field->renderValueForRevisionList($revision); 146 + $rev_header = $field->renderHeaderForRevisionList(); 147 + $rev_fields[$rev_header] = $field 148 + ->renderValueForRevisionList($revision); 149 + } 150 + 151 + $status = $revision->getStatus(); 152 + $status_name = 153 + ArcanistDifferentialRevisionStatus::getNameForRevisionStatus($status); 154 + 155 + $flag_icon = null; 156 + if (isset($icons['flag'])) { 157 + $flag_icon = $icons['flag']['icon']; 165 158 } 166 159 167 - $rows[] = $row; 168 - } 160 + $item->setObjectName('D'.$revision->getID()); 161 + $item->setHeader(phutil_tag('a', 162 + array('href' => '/D'.$revision->getID()), 163 + $revision->getTitle())); 164 + $item->addAttribute($status_name); 165 + 166 + // Author 167 + $author_handle = $this->handles[$revision->getAuthorPHID()]; 168 + $item->addByline(pht('Author: %s', $author_handle->renderLink())); 169 + 170 + // Reviewers 171 + $item->addAttribute(pht('Reviewers: %s', $rev_fields['Reviewers'])); 172 + 173 + $do_not_display_age = array( 174 + ArcanistDifferentialRevisionStatus::CLOSED => true, 175 + ArcanistDifferentialRevisionStatus::ABANDONED => true, 176 + ); 177 + if (isset($icons['age']) && !isset($do_not_display_age[$status])) { 178 + $item->addFootIcon($icons['age']['icon'], $icons['age']['label']); 179 + } 180 + if (isset($icons['draft'])) { 181 + $item->addFootIcon($icons['draft']['icon'], pht('Saved Draft'), 182 + $icons['draft']['href']); 183 + } 169 184 170 - $headers = array(''); 171 - $classes = array(''); 172 - foreach ($this->fields as $field) { 173 - $headers[] = $field->renderHeaderForRevisionList(); 174 - $classes[] = $field->getColumnClassForRevisionList(); 175 - } 185 + // Updated on 186 + $item->addIcon($flag_icon ? $flag_icon : 'none', 187 + hsprintf('%s', $rev_fields['Updated'])); 176 188 177 - $table = new AphrontTableView($rows); 178 - $table->setHeaders($headers); 179 - $table->setColumnClasses($classes); 180 - $table->setCellClasses($cell_classes); 189 + // First remove the fields we already have 190 + $count = 7; 191 + $rev_fields = array_slice($rev_fields, $count); 181 192 182 - $table->setNoDataString(pht('No revisions found.')); 193 + // Then add each one of them 194 + // TODO: Add render-to-foot-icon support 195 + foreach ($rev_fields as $header => $field) { 196 + $item->addAttribute(pht('%s: %s', $header, $field)); 197 + } 183 198 184 - require_celerity_resource('differential-revision-history-css'); 199 + $list->addItem($item); 200 + } 185 201 186 - return $table->render(); 202 + return $list; 187 203 } 188 204 189 205 public static function getDefaultFields(PhabricatorUser $user) {
+5
webroot/rsrc/css/aphront/error-view.css
··· 74 74 background: #f3f3f3; 75 75 color: #666; 76 76 } 77 + 78 + .make-me-sneeze .aphront-error-severity-nodata { 79 + background: #fff; 80 + border-color: #e6e6e6; 81 + }
+12
webroot/rsrc/css/layout/phabricator-object-item-list-view.css
··· 14 14 padding: 0; 15 15 } 16 16 17 + .phabricator-object-list-flush .aphront-error-view { 18 + margin: 0; 19 + } 20 + 17 21 .phabricator-object-item { 18 22 background: #ffffff; 19 23 border-style: solid; ··· 306 310 307 311 min-height: 18px; 308 312 line-height: 18px; 313 + } 314 + 315 + /* 316 + * Items with icon 'none' still have on mobile, thus creating a weird vertical 317 + * margin for elements which follow 318 + */ 319 + .device-phone .phabricator-object-item-icon-none { 320 + display: none; 309 321 } 310 322 311 323 .device-desktop .phabricator-object-item-icon {