@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.

Allow inline comments to be individually hidden

Summary:
Ref T7447. Implements per-viewer comment hiding. Once a comment is obsolete or uninteresting, you can hide it completely.

This is sticky per-user.

My hope is that this will strike a better balance between concerns than some of the other approaches (conservative porting, summarization, hide-all).

Specifically, this adds a new action here:

{F435621}

Clicking it completely collapses the comment into a small icon on the previous line, and saves the comment state as hidden for you:

{F435626}

You can click the icon to reveal all hidden comments below the line.

Test Plan:
- Hid comments.
- Showed comments.
- Created, edited, deleted and submitted comments.
- Used Diffusion comments (hiding is not implemented there yet, but I'd plan to bring it there eventually if it works out in Differential).

Reviewers: btrahan, chad

Reviewed By: btrahan

Subscribers: jparise, yelirekim, epriestley

Maniphest Tasks: T7447

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

+446 -57
+23 -23
resources/celerity/map.php
··· 10 10 'core.pkg.css' => '439658b5', 11 11 'core.pkg.js' => '328799d0', 12 12 'darkconsole.pkg.js' => 'e7393ebb', 13 - 'differential.pkg.css' => 'bb338e4b', 14 - 'differential.pkg.js' => '63a77807', 13 + 'differential.pkg.css' => '30602b8c', 14 + 'differential.pkg.js' => '8c98ce21', 15 15 'diffusion.pkg.css' => '591664fa', 16 16 'diffusion.pkg.js' => '0115b37c', 17 17 'maniphest.pkg.css' => '68d4dd3d', ··· 60 60 'rsrc/css/application/differential/add-comment.css' => 'c47f8c40', 61 61 'rsrc/css/application/differential/changeset-view.css' => 'e19cfd6e', 62 62 'rsrc/css/application/differential/core.css' => '7ac3cabc', 63 - 'rsrc/css/application/differential/phui-inline-comment.css' => '2174771a', 63 + 'rsrc/css/application/differential/phui-inline-comment.css' => 'aa16f165', 64 64 'rsrc/css/application/differential/results-table.css' => '181aa9d9', 65 65 'rsrc/css/application/differential/revision-comment.css' => '14b8565a', 66 66 'rsrc/css/application/differential/revision-history.css' => '0e8eb855', ··· 353 353 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 354 354 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', 355 355 'rsrc/js/application/differential/behavior-dropdown-menus.js' => '2035b9cb', 356 - 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => 'e723c323', 356 + 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '037b59eb', 357 357 'rsrc/js/application/differential/behavior-keyboard-nav.js' => '2c426492', 358 358 'rsrc/js/application/differential/behavior-populate.js' => '8694b1df', 359 359 'rsrc/js/application/differential/behavior-show-field-details.js' => 'bba9eedf', ··· 443 443 'rsrc/js/core/behavior-device.js' => 'a205cf28', 444 444 'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '6d49590e', 445 445 'rsrc/js/core/behavior-error-log.js' => '6882e80a', 446 - 'rsrc/js/core/behavior-fancy-datepicker.js' => '2d4029a8', 446 + 'rsrc/js/core/behavior-fancy-datepicker.js' => '5c0f680f', 447 447 'rsrc/js/core/behavior-file-tree.js' => '88236f00', 448 448 'rsrc/js/core/behavior-form.js' => '5c54cbf3', 449 449 'rsrc/js/core/behavior-gesture.js' => '3ab51e2c', ··· 560 560 'javelin-behavior-differential-comment-jump' => '4fdb476d', 561 561 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', 562 562 'javelin-behavior-differential-dropdown-menus' => '2035b9cb', 563 - 'javelin-behavior-differential-edit-inline-comments' => 'e723c323', 563 + 'javelin-behavior-differential-edit-inline-comments' => '037b59eb', 564 564 'javelin-behavior-differential-feedback-preview' => 'b064af76', 565 565 'javelin-behavior-differential-keyboard-navigation' => '2c426492', 566 566 'javelin-behavior-differential-populate' => '8694b1df', ··· 576 576 'javelin-behavior-durable-column' => '16c695bf', 577 577 'javelin-behavior-error-log' => '6882e80a', 578 578 'javelin-behavior-event-all-day' => '38dcf3c8', 579 - 'javelin-behavior-fancy-datepicker' => '2d4029a8', 579 + 'javelin-behavior-fancy-datepicker' => '5c0f680f', 580 580 'javelin-behavior-global-drag-and-drop' => 'c8e57404', 581 581 'javelin-behavior-herald-rule-editor' => '7ebaeed3', 582 582 'javelin-behavior-high-security-warning' => 'a464fe03', ··· 782 782 'phui-image-mask-css' => '5a8b09c8', 783 783 'phui-info-panel-css' => '27ea50a1', 784 784 'phui-info-view-css' => 'c6f0aef8', 785 - 'phui-inline-comment-view-css' => '2174771a', 785 + 'phui-inline-comment-view-css' => 'aa16f165', 786 786 'phui-list-view-css' => '2e25ebfb', 787 787 'phui-object-box-css' => '7d160002', 788 788 'phui-object-item-list-view-css' => 'f3a22696', ··· 830 830 '029a133d' => array( 831 831 'aphront-dialog-view-css', 832 832 ), 833 + '037b59eb' => array( 834 + 'javelin-behavior', 835 + 'javelin-stratcom', 836 + 'javelin-dom', 837 + 'javelin-util', 838 + 'javelin-vector', 839 + 'differential-inline-comment-editor', 840 + ), 833 841 '048330fa' => array( 834 842 'javelin-behavior', 835 843 'javelin-typeahead-ondemand-source', ··· 1042 1050 'javelin-install', 1043 1051 'javelin-event', 1044 1052 ), 1045 - '2d4029a8' => array( 1046 - 'javelin-behavior', 1047 - 'javelin-util', 1048 - 'javelin-dom', 1049 - 'javelin-stratcom', 1050 - 'javelin-vector', 1051 - ), 1052 1053 '331b1611' => array( 1053 1054 'javelin-install', 1054 1055 ), ··· 1240 1241 'javelin-mask', 1241 1242 'javelin-uri', 1242 1243 'javelin-routable', 1244 + ), 1245 + '5c0f680f' => array( 1246 + 'javelin-behavior', 1247 + 'javelin-util', 1248 + 'javelin-dom', 1249 + 'javelin-stratcom', 1250 + 'javelin-vector', 1243 1251 ), 1244 1252 '5c54cbf3' => array( 1245 1253 'javelin-behavior', ··· 1924 1932 ), 1925 1933 'e6e25838' => array( 1926 1934 'javelin-install', 1927 - ), 1928 - 'e723c323' => array( 1929 - 'javelin-behavior', 1930 - 'javelin-stratcom', 1931 - 'javelin-dom', 1932 - 'javelin-util', 1933 - 'javelin-vector', 1934 - 'differential-inline-comment-editor', 1935 1935 ), 1936 1936 'e9581f08' => array( 1937 1937 'javelin-behavior',
+7
resources/sql/autopatches/20150525.diff.hidden.1.sql
··· 1 + CREATE TABLE {$NAMESPACE}_differential.differential_hiddencomment ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + userPHID VARBINARY(64) NOT NULL, 4 + commentID INT UNSIGNED NOT NULL, 5 + UNIQUE KEY `key_user` (userPHID, commentID), 6 + KEY `key_comment` (commentID) 7 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+4
src/__phutil_library_map__.php
··· 374 374 'DifferentialGetWorkingCopy' => 'applications/differential/DifferentialGetWorkingCopy.php', 375 375 'DifferentialGitHubLandingStrategy' => 'applications/differential/landing/DifferentialGitHubLandingStrategy.php', 376 376 'DifferentialGitSVNIDField' => 'applications/differential/customfield/DifferentialGitSVNIDField.php', 377 + 'DifferentialHiddenComment' => 'applications/differential/storage/DifferentialHiddenComment.php', 377 378 'DifferentialHostField' => 'applications/differential/customfield/DifferentialHostField.php', 378 379 'DifferentialHostedGitLandingStrategy' => 'applications/differential/landing/DifferentialHostedGitLandingStrategy.php', 379 380 'DifferentialHostedMercurialLandingStrategy' => 'applications/differential/landing/DifferentialHostedMercurialLandingStrategy.php', ··· 1178 1179 'PHUIDiffInlineCommentUndoView' => 'infrastructure/diff/view/PHUIDiffInlineCommentUndoView.php', 1179 1180 'PHUIDiffInlineCommentView' => 'infrastructure/diff/view/PHUIDiffInlineCommentView.php', 1180 1181 'PHUIDiffOneUpInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffOneUpInlineCommentRowScaffold.php', 1182 + 'PHUIDiffRevealIconView' => 'infrastructure/diff/view/PHUIDiffRevealIconView.php', 1181 1183 'PHUIDiffTwoUpInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffTwoUpInlineCommentRowScaffold.php', 1182 1184 'PHUIDocumentExample' => 'applications/uiexample/examples/PHUIDocumentExample.php', 1183 1185 'PHUIDocumentView' => 'view/phui/PHUIDocumentView.php', ··· 3616 3618 'DifferentialGetRevisionConduitAPIMethod' => 'DifferentialConduitAPIMethod', 3617 3619 'DifferentialGitHubLandingStrategy' => 'DifferentialLandingStrategy', 3618 3620 'DifferentialGitSVNIDField' => 'DifferentialCustomField', 3621 + 'DifferentialHiddenComment' => 'DifferentialDAO', 3619 3622 'DifferentialHostField' => 'DifferentialCustomField', 3620 3623 'DifferentialHostedGitLandingStrategy' => 'DifferentialLandingStrategy', 3621 3624 'DifferentialHostedMercurialLandingStrategy' => 'DifferentialLandingStrategy', ··· 4506 4509 'PHUIDiffInlineCommentUndoView' => 'PHUIDiffInlineCommentView', 4507 4510 'PHUIDiffInlineCommentView' => 'AphrontView', 4508 4511 'PHUIDiffOneUpInlineCommentRowScaffold' => 'PHUIDiffInlineCommentRowScaffold', 4512 + 'PHUIDiffRevealIconView' => 'AphrontView', 4509 4513 'PHUIDiffTwoUpInlineCommentRowScaffold' => 'PHUIDiffInlineCommentRowScaffold', 4510 4514 'PHUIDocumentExample' => 'PhabricatorUIExample', 4511 4515 'PHUIDocumentView' => 'AphrontTagView',
+8
src/applications/audit/storage/PhabricatorAuditInlineComment.php
··· 23 23 return $this->proxy; 24 24 } 25 25 26 + public function supportsHiding() { 27 + return false; 28 + } 29 + 30 + public function isHidden() { 31 + return false; 32 + } 33 + 26 34 public function getTransactionCommentForSave() { 27 35 $content_source = PhabricatorContentSource::newForSource( 28 36 PhabricatorContentSource::SOURCE_LEGACY,
+1
src/applications/differential/controller/DifferentialChangesetViewController.php
··· 191 191 if ($revision) { 192 192 $query = id(new DifferentialInlineCommentQuery()) 193 193 ->setViewer($viewer) 194 + ->needHidden(true) 194 195 ->withRevisionPHIDs(array($revision->getPHID())); 195 196 $inlines = $query->execute(); 196 197 $inlines = $query->adjustInlinesForChangesets(
+36
src/applications/differential/controller/DifferentialInlineCommentEditController.php
··· 42 42 ->setViewer($this->getViewer()) 43 43 ->withIDs(array($id)) 44 44 ->withDeletedDrafts(true) 45 + ->needHidden(true) 45 46 ->executeOne(); 46 47 } 47 48 ··· 50 51 ->setViewer($this->getViewer()) 51 52 ->withPHIDs(array($phid)) 52 53 ->withDeletedDrafts(true) 54 + ->needHidden(true) 53 55 ->executeOne(); 54 56 } 55 57 ··· 150 152 protected function loadObjectOwnerPHID( 151 153 PhabricatorInlineCommentInterface $inline) { 152 154 return $this->loadRevision()->getAuthorPHID(); 155 + } 156 + 157 + protected function hideComments(array $ids) { 158 + $viewer = $this->getViewer(); 159 + $table = new DifferentialHiddenComment(); 160 + $conn_w = $table->establishConnection('w'); 161 + 162 + $sql = array(); 163 + foreach ($ids as $id) { 164 + $sql[] = qsprintf( 165 + $conn_w, 166 + '(%s, %d)', 167 + $viewer->getPHID(), 168 + $id); 169 + } 170 + 171 + queryfx( 172 + $conn_w, 173 + 'INSERT IGNORE INTO %T (userPHID, commentID) VALUES %Q', 174 + $table->getTableName(), 175 + implode(', ', $sql)); 176 + } 177 + 178 + protected function showComments(array $ids) { 179 + $viewer = $this->getViewer(); 180 + $table = new DifferentialHiddenComment(); 181 + $conn_w = $table->establishConnection('w'); 182 + 183 + queryfx( 184 + $conn_w, 185 + 'DELETE FROM %T WHERE userPHID = %s AND commentID IN (%Ld)', 186 + $table->getTableName(), 187 + $viewer->getPHID(), 188 + $ids); 153 189 } 154 190 155 191 }
+1
src/applications/differential/controller/DifferentialInlineCommentPreviewController.php
··· 19 19 ->withDrafts(true) 20 20 ->withAuthorPHIDs(array($viewer->getPHID())) 21 21 ->withRevisionPHIDs(array($revision->getPHID())) 22 + ->needHidden(true) 22 23 ->execute(); 23 24 } 24 25
+1
src/applications/differential/controller/DifferentialRevisionViewController.php
··· 175 175 176 176 $query = id(new DifferentialInlineCommentQuery()) 177 177 ->setViewer($user) 178 + ->needHidden(true) 178 179 ->withRevisionPHIDs(array($revision->getPHID())); 179 180 $inlines = $query->execute(); 180 181 $inlines = $query->adjustInlinesForChangesets(
+26
src/applications/differential/query/DifferentialInlineCommentQuery.php
··· 15 15 private $authorPHIDs; 16 16 private $revisionPHIDs; 17 17 private $deletedDrafts; 18 + private $needHidden; 18 19 19 20 public function setViewer(PhabricatorUser $viewer) { 20 21 $this->viewer = $viewer; ··· 55 56 return $this; 56 57 } 57 58 59 + public function needHidden($need) { 60 + $this->needHidden = $need; 61 + return $this; 62 + } 63 + 58 64 public function execute() { 59 65 $table = new DifferentialTransactionComment(); 60 66 $conn_r = $table->establishConnection('r'); ··· 67 73 $this->buildLimitClause($conn_r)); 68 74 69 75 $comments = $table->loadAllFromArray($data); 76 + 77 + if ($this->needHidden) { 78 + $viewer_phid = $this->getViewer()->getPHID(); 79 + if ($viewer_phid && $comments) { 80 + $hidden = queryfx_all( 81 + $conn_r, 82 + 'SELECT commentID FROM %T WHERE userPHID = %s 83 + AND commentID IN (%Ls)', 84 + id(new DifferentialHiddenComment())->getTableName(), 85 + $viewer_phid, 86 + mpull($comments, 'getID')); 87 + $hidden = array_fuse(ipull($hidden, 'commentID')); 88 + } else { 89 + $hidden = array(); 90 + } 91 + 92 + foreach ($comments as $inline) { 93 + $inline->attachIsHidden(isset($hidden[$inline->getID()])); 94 + } 95 + } 70 96 71 97 foreach ($comments as $key => $value) { 72 98 $comments[$key] = DifferentialInlineComment::newFromModernComment(
+44 -4
src/applications/differential/render/DifferentialChangesetOneUpRenderer.php
··· 41 41 42 42 $column_width = 4; 43 43 44 + $hidden = new PHUIDiffRevealIconView(); 45 + 44 46 $out = array(); 45 - foreach ($primitives as $p) { 47 + foreach ($primitives as $k => $p) { 46 48 $type = $p['type']; 47 49 switch ($type) { 48 50 case 'old': ··· 51 53 case 'new-file': 52 54 $is_old = ($type == 'old' || $type == 'old-file'); 53 55 56 + $o_hidden = array(); 57 + $n_hidden = array(); 58 + 59 + for ($look = $k + 1; isset($primitives[$look]); $look++) { 60 + $next = $primitives[$look]; 61 + switch ($next['type']) { 62 + case 'inline': 63 + $comment = $next['comment']; 64 + if ($comment->isHidden()) { 65 + if ($next['right']) { 66 + $n_hidden[] = $comment; 67 + } else { 68 + $o_hidden[] = $comment; 69 + } 70 + } 71 + break; 72 + default: 73 + break 2; 74 + } 75 + } 76 + 54 77 $cells = array(); 55 78 if ($is_old) { 56 79 if ($p['htype']) { ··· 68 91 } else { 69 92 $left_id = null; 70 93 } 71 - $cells[] = phutil_tag('th', array('id' => $left_id), $p['line']); 94 + 95 + $line = $p['line']; 96 + if ($o_hidden) { 97 + $line = array($hidden, $line); 98 + } 99 + 100 + $cells[] = phutil_tag('th', array('id' => $left_id), $line); 72 101 73 102 $cells[] = phutil_tag('th', array()); 74 103 $cells[] = $no_copy; ··· 85 114 } else { 86 115 $left_id = null; 87 116 } 88 - $cells[] = phutil_tag('th', array('id' => $left_id), $p['oline']); 117 + 118 + $oline = $p['oline']; 119 + if ($o_hidden) { 120 + $oline = array($hidden, $oline); 121 + } 122 + 123 + $cells[] = phutil_tag('th', array('id' => $left_id), $oline); 89 124 } 90 125 91 126 if ($type == 'new-file') { ··· 97 132 } else { 98 133 $right_id = null; 99 134 } 100 - $cells[] = phutil_tag('th', array('id' => $right_id), $p['line']); 101 135 136 + $line = $p['line']; 137 + if ($n_hidden) { 138 + $line = array($hidden, $line); 139 + } 140 + 141 + $cells[] = phutil_tag('th', array('id' => $right_id), $line); 102 142 103 143 $cells[] = $no_copy; 104 144 $cells[] = phutil_tag('td', array('class' => $class), $p['render']);
+59 -26
src/applications/differential/render/DifferentialChangesetTwoUpRenderer.php
··· 69 69 $depths = $this->getDepths(); 70 70 $mask = $this->getMask(); 71 71 72 + $hidden = new PHUIDiffRevealIconView(); 73 + 72 74 for ($ii = $range_start; $ii < $range_start + $range_len; $ii++) { 73 75 if (empty($mask[$ii])) { 74 76 // If we aren't going to show this line, we've just entered a gap. ··· 235 237 $n_id = null; 236 238 } 237 239 238 - // NOTE: This is a unicode zero-width space, which we use as a hint when 239 - // intercepting 'copy' events to make sure sensible text ends up on the 240 - // clipboard. See the 'phabricator-oncopy' behavior. 241 - $zero_space = "\xE2\x80\x8B"; 242 - 243 - $html[] = phutil_tag('tr', array(), array( 244 - phutil_tag('th', array('id' => $o_id), $o_num), 245 - phutil_tag('td', array('class' => $o_classes), $o_text), 246 - phutil_tag('th', array('id' => $n_id), $n_num), 247 - $n_copy, 248 - phutil_tag( 249 - 'td', 250 - array('class' => $n_classes, 'colspan' => $n_colspan), 251 - array( 252 - phutil_tag('span', array('class' => 'zwsp'), $zero_space), 253 - $n_text, 254 - )), 255 - $n_cov, 256 - )); 257 - 258 - if ($context_not_available && ($ii == $rows - 1)) { 259 - $html[] = $context_not_available; 260 - } 261 - 262 240 $old_comments = $this->getOldComments(); 263 241 $new_comments = $this->getNewComments(); 242 + $scaffolds = array(); 243 + 244 + $o_hidden = array(); 245 + $n_hidden = array(); 264 246 265 247 if ($o_num && isset($old_comments[$o_num])) { 266 248 foreach ($old_comments[$o_num] as $comment) { ··· 268 250 $comment, 269 251 $on_right = false); 270 252 $scaffold = $this->getRowScaffoldForInline($inline); 253 + 254 + if ($comment->isHidden()) { 255 + $o_hidden[] = $comment; 256 + } 271 257 272 258 if ($n_num && isset($new_comments[$n_num])) { 273 259 foreach ($new_comments[$n_num] as $key => $new_comment) { ··· 276 262 $new_comment, 277 263 $on_right = true); 278 264 265 + if ($new_comment->isHidden()) { 266 + $n_hidden = $new_comment; 267 + } 268 + 279 269 $scaffold->addInlineView($companion); 280 270 unset($new_comments[$n_num][$key]); 281 271 break; ··· 283 273 } 284 274 } 285 275 286 - $html[] = $scaffold; 276 + 277 + $scaffolds[] = $scaffold; 287 278 } 288 279 } 280 + 289 281 if ($n_num && isset($new_comments[$n_num])) { 290 282 foreach ($new_comments[$n_num] as $comment) { 291 283 $inline = $this->buildInlineComment( 292 284 $comment, 293 285 $on_right = true); 294 - $html[] = $this->getRowScaffoldForInline($inline); 286 + 287 + if ($comment->isHidden()) { 288 + $n_hidden[] = $comment; 289 + } 290 + 291 + $scaffolds[] = $this->getRowScaffoldForInline($inline); 295 292 } 293 + } 294 + 295 + if ($o_hidden) { 296 + $o_num = array($hidden, $o_num); 297 + } 298 + 299 + if ($n_hidden) { 300 + $n_num = array($hidden, $n_num); 301 + } 302 + 303 + // NOTE: This is a unicode zero-width space, which we use as a hint when 304 + // intercepting 'copy' events to make sure sensible text ends up on the 305 + // clipboard. See the 'phabricator-oncopy' behavior. 306 + $zero_space = "\xE2\x80\x8B"; 307 + 308 + $html[] = phutil_tag('tr', array(), array( 309 + phutil_tag('th', array('id' => $o_id), $o_num), 310 + phutil_tag('td', array('class' => $o_classes), $o_text), 311 + phutil_tag('th', array('id' => $n_id), $n_num), 312 + $n_copy, 313 + phutil_tag( 314 + 'td', 315 + array('class' => $n_classes, 'colspan' => $n_colspan), 316 + array( 317 + phutil_tag('span', array('class' => 'zwsp'), $zero_space), 318 + $n_text, 319 + )), 320 + $n_cov, 321 + )); 322 + 323 + if ($context_not_available && ($ii == $rows - 1)) { 324 + $html[] = $context_not_available; 325 + } 326 + 327 + foreach ($scaffolds as $scaffold) { 328 + $html[] = $scaffold; 296 329 } 297 330 } 298 331
+24
src/applications/differential/storage/DifferentialHiddenComment.php
··· 1 + <?php 2 + 3 + final class DifferentialHiddenComment 4 + extends DifferentialDAO { 5 + 6 + protected $userPHID; 7 + protected $commentID; 8 + 9 + protected function getConfiguration() { 10 + return array( 11 + self::CONFIG_TIMESTAMPS => false, 12 + self::CONFIG_KEY_SCHEMA => array( 13 + 'key_user' => array( 14 + 'columns' => array('userPHID', 'commentID'), 15 + 'unique' => true, 16 + ), 17 + 'key_comment' => array( 18 + 'columns' => array('commentID'), 19 + ), 20 + ), 21 + ) + parent::getConfiguration(); 22 + } 23 + 24 + }
+9
src/applications/differential/storage/DifferentialInlineComment.php
··· 24 24 ->setViewPolicy('public') 25 25 ->setEditPolicy($this->getAuthorPHID()) 26 26 ->setContentSource($content_source) 27 + ->attachIsHidden(false) 27 28 ->setCommentVersion(1); 28 29 29 30 return $this->proxy; ··· 47 48 $this->proxy->delete(); 48 49 49 50 return $this; 51 + } 52 + 53 + public function supportsHiding() { 54 + return true; 55 + } 56 + 57 + public function isHidden() { 58 + return $this->proxy->getIsHidden(); 50 59 } 51 60 52 61 public function getID() {
+1
src/applications/differential/storage/DifferentialRevision.php
··· 522 522 } 523 523 524 524 $query = id(new DifferentialInlineCommentQuery()) 525 + ->needHidden(true) 525 526 ->setViewer($viewer); 526 527 527 528 // NOTE: This is a bit sketchy: this method adjusts the inlines as a
+10
src/applications/differential/storage/DifferentialTransactionComment.php
··· 13 13 protected $replyToCommentPHID; 14 14 15 15 private $replyToComment = self::ATTACHABLE; 16 + private $isHidden = self::ATTACHABLE; 16 17 17 18 public function getApplicationTransactionObject() { 18 19 return new DifferentialTransaction(); ··· 97 98 } 98 99 99 100 return $inline_groups; 101 + } 102 + 103 + public function getIsHidden() { 104 + return $this->assertAttached($this->isHidden); 105 + } 106 + 107 + public function attachIsHidden($hidden) { 108 + $this->isHidden = $hidden; 109 + return $this; 100 110 } 101 111 102 112 }
+3 -2
src/applications/differential/view/DifferentialChangesetListView.php
··· 232 232 233 233 if ($this->inlineURI) { 234 234 Javelin::initBehavior('differential-edit-inline-comments', array( 235 - 'uri' => $this->inlineURI, 236 - 'stage' => 'differential-review-stage', 235 + 'uri' => $this->inlineURI, 236 + 'stage' => 'differential-review-stage', 237 + 'revealIcon' => hsprintf('%s', new PHUIDiffRevealIconView()), 237 238 )); 238 239 } 239 240
+24
src/infrastructure/diff/PhabricatorInlineCommentController.php
··· 15 15 abstract protected function saveComment( 16 16 PhabricatorInlineCommentInterface $inline); 17 17 18 + protected function hideComments(array $ids) { 19 + throw new PhutilMethodNotImplementedException(); 20 + } 21 + 22 + protected function showComments(array $ids) { 23 + throw new PhutilMethodNotImplementedException(); 24 + } 25 + 18 26 private $changesetID; 19 27 private $isNewFile; 20 28 private $isOnRight; ··· 84 92 85 93 $op = $this->getOperation(); 86 94 switch ($op) { 95 + case 'hide': 96 + case 'show': 97 + if (!$request->validateCSRF()) { 98 + return new Aphront404Response(); 99 + } 100 + 101 + $ids = $request->getStrList('ids'); 102 + if ($ids) { 103 + if ($op == 'hide') { 104 + $this->hideComments($ids); 105 + } else { 106 + $this->showComments($ids); 107 + } 108 + } 109 + 110 + return id(new AphrontAjaxResponse())->setContent(array()); 87 111 case 'done': 88 112 if (!$request->validateCSRF()) { 89 113 return new Aphront404Response();
+3
src/infrastructure/diff/interface/PhabricatorInlineCommentInterface.php
··· 57 57 public function setIsGhost($is_ghost); 58 58 public function getIsGhost(); 59 59 60 + public function supportsHiding(); 61 + public function isHidden(); 62 + 60 63 }
+18
src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php
··· 18 18 return $this; 19 19 } 20 20 21 + public function isHidden() { 22 + return $this->inlineComment->isHidden(); 23 + } 24 + 21 25 public function setHandles(array $handles) { 22 26 assert_instances_of($handles, 'PhabricatorObjectHandle'); 23 27 $this->handles = $handles; ··· 192 196 if (!$this->preview) { 193 197 $nextprev = new PHUIButtonBarView(); 194 198 $nextprev->addClass('mml'); 199 + 200 + 195 201 $up = id(new PHUIButtonView()) 196 202 ->setTag('a') 197 203 ->setColor(PHUIButtonView::SIMPLE) ··· 207 213 ->setIconFont('fa-chevron-down') 208 214 ->addSigil('differential-inline-next') 209 215 ->setMustCapture(true); 216 + 217 + $hide = id(new PHUIButtonView()) 218 + ->setTag('a') 219 + ->setColor(PHUIButtonView::SIMPLE) 220 + ->setTooltip(pht('Hide Comment')) 221 + ->setIconFont('fa-times') 222 + ->addSigil('hide-inline') 223 + ->setMustCapture(true); 224 + 225 + if ($viewer_phid && $inline->getID() && $inline->supportsHiding()) { 226 + $nextprev->addButton($hide); 227 + } 210 228 211 229 $nextprev->addButton($up); 212 230 $nextprev->addButton($down);
+10
src/infrastructure/diff/view/PHUIDiffInlineCommentRowScaffold.php
··· 23 23 protected function getRowAttributes() { 24 24 // TODO: This is semantic information used by the JS when placing comments 25 25 // and using keyboard navigation; we should move it out of class names. 26 + 27 + $style = null; 28 + foreach ($this->getInlineViews() as $view) { 29 + if ($view->isHidden()) { 30 + $style = 'display: none'; 31 + } 32 + } 33 + 26 34 return array( 27 35 'class' => 'inline', 36 + 'sigil' => 'inline-row', 37 + 'style' => $style, 28 38 ); 29 39 } 30 40
+4
src/infrastructure/diff/view/PHUIDiffInlineCommentView.php
··· 17 17 return null; 18 18 } 19 19 20 + public function isHidden() { 21 + return false; 22 + } 23 + 20 24 }
+1 -1
src/infrastructure/diff/view/PHUIDiffOneUpInlineCommentRowScaffold.php
··· 28 28 phutil_tag('td', $attrs, $inline), 29 29 ); 30 30 31 - return phutil_tag('tr', $this->getRowAttributes(), $cells); 31 + return javelin_tag('tr', $this->getRowAttributes(), $cells); 32 32 } 33 33 34 34 }
+27
src/infrastructure/diff/view/PHUIDiffRevealIconView.php
··· 1 + <?php 2 + 3 + final class PHUIDiffRevealIconView extends AphrontView { 4 + 5 + public function render() { 6 + $icon = id(new PHUIIconView()) 7 + ->setIconFont('fa-comment') 8 + ->addSigil('has-tooltip') 9 + ->setMetadata( 10 + array( 11 + 'tip' => pht('Show Hidden Comments'), 12 + 'align' => 'E', 13 + 'size' => 275, 14 + )); 15 + 16 + return javelin_tag( 17 + 'a', 18 + array( 19 + 'href' => '#', 20 + 'class' => 'reveal-inlines', 21 + 'sigil' => 'reveal-inlines', 22 + 'mustcapture' => true, 23 + ), 24 + $icon); 25 + } 26 + 27 + }
+1 -1
src/infrastructure/diff/view/PHUIDiffTwoUpInlineCommentRowScaffold.php
··· 68 68 phutil_tag('td', $right_attrs, $right_side), 69 69 ); 70 70 71 - return phutil_tag('tr', $this->getRowAttributes(), $cells); 71 + return javelin_tag('tr', $this->getRowAttributes(), $cells); 72 72 } 73 73 74 74 }
+18
webroot/rsrc/css/application/differential/phui-inline-comment.css
··· 434 434 border-color: {$lightgreyborder}; 435 435 color: {$lightgreytext}; 436 436 } 437 + 438 + 439 + /* - Hiding Inlines ------------------------------------------------------------ 440 + */ 441 + 442 + .reveal-inlines { 443 + float: left; 444 + margin-left: 4px; 445 + color: {$lightbluetext}; 446 + } 447 + 448 + .reveal-inlines span.phui-icon-view { 449 + color: {$lightbluetext}; 450 + } 451 + 452 + .reveal-inlines:hover span.phui-icon-view { 453 + color: {$darkbluetext}; 454 + }
+83
webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js
··· 395 395 handle_inline_action(data.node, data.op); 396 396 }); 397 397 398 + // Respond to the user clicking the "Hide Inline" button on an inline 399 + // comment. 400 + JX.Stratcom.listen('click', 'hide-inline', function(e) { 401 + e.kill(); 402 + 403 + var row = e.getNode('inline-row'); 404 + JX.DOM.hide(row); 405 + 406 + var prev = row.previousSibling; 407 + while (prev && JX.Stratcom.hasSigil(prev, 'inline-row')) { 408 + prev = prev.previousSibling; 409 + } 410 + 411 + if (!prev) { 412 + return; 413 + } 414 + 415 + var comment = e.getNodeData('differential-inline-comment'); 416 + 417 + var slots = []; 418 + for (var ii = 0; ii < prev.childNodes.length; ii++) { 419 + if (JX.DOM.isType(prev.childNodes[ii], 'th')) { 420 + slots.push(prev.childNodes[ii]); 421 + } 422 + } 423 + 424 + // Select the right-hand side if the comment is on the right. 425 + var slot = (comment.on_right && slots[1]) || slots[0]; 426 + 427 + var reveal = JX.DOM.scry(slot, 'a', 'reveal-inlines')[0]; 428 + if (!reveal) { 429 + reveal = JX.$N( 430 + 'a', 431 + { 432 + className: 'reveal-inlines', 433 + sigil: 'reveal-inlines' 434 + }, 435 + JX.$H(config.revealIcon)); 436 + 437 + JX.DOM.prependContent(slot, reveal); 438 + } 439 + 440 + new JX.Workflow(config.uri, {op: 'hide', ids: comment.id}) 441 + .setHandler(JX.bag) 442 + .start(); 443 + }); 444 + 445 + JX.Stratcom.listen('click', 'reveal-inlines', function(e) { 446 + e.kill(); 447 + 448 + var row = e.getNode('tag:tr'); 449 + var next = row.nextSibling; 450 + 451 + var ids = []; 452 + var ii; 453 + 454 + // Show any hidden inline comment rows directly below this one. 455 + while (next && JX.Stratcom.hasSigil(next, 'inline-row')) { 456 + JX.DOM.show(next); 457 + 458 + var comments = JX.DOM.scry(next, 'div', 'differential-inline-comment'); 459 + for (ii = 0; ii < comments.length; ii++) { 460 + var id = JX.Stratcom.getData(comments[ii]).id; 461 + if (id) { 462 + ids.push(id); 463 + } 464 + } 465 + 466 + next = next.nextSibling; 467 + } 468 + 469 + // Remove any "reveal" icons on the row. 470 + var reveals = JX.DOM.scry(row, 'a', 'reveal-inlines'); 471 + for (ii = 0; ii < reveals.length; ii++) { 472 + JX.DOM.remove(reveals[ii]); 473 + } 474 + 475 + new JX.Workflow(config.uri, {op: 'show', ids: ids.join(',')}) 476 + .setHandler(JX.bag) 477 + .start(); 478 + }); 479 + 480 + 398 481 });