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

Update Ponder Answer layout

Summary: Ref T9099, A step forward for the main Ponder UI. Mostly moving stuff into View classes and reducing clutter. Took a pass at keeping comments and helpfuls, but unclear what the 'final' UI will be (I'm just designing as I use the product).

Test Plan:
Review a number of questions and answers.

{F702495}

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin

Maniphest Tasks: T9099

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

+332 -175
+2 -2
resources/celerity/map.php
··· 93 93 'rsrc/css/application/policy/policy-edit.css' => '815c66f7', 94 94 'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43', 95 95 'rsrc/css/application/policy/policy.css' => '957ea14c', 96 - 'rsrc/css/application/ponder/ponder-view.css' => '4e557c89', 96 + 'rsrc/css/application/ponder/ponder-view.css' => '6a399881', 97 97 'rsrc/css/application/projects/project-icon.css' => '4e3eaa5a', 98 98 'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733', 99 99 'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5', ··· 811 811 'policy-css' => '957ea14c', 812 812 'policy-edit-css' => '815c66f7', 813 813 'policy-transaction-detail-css' => '82100a43', 814 - 'ponder-view-css' => '4e557c89', 814 + 'ponder-view-css' => '6a399881', 815 815 'project-icon-css' => '4e3eaa5a', 816 816 'raphael-core' => '51ee6b43', 817 817 'raphael-g' => '40dde778',
+4 -1
src/__phutil_library_map__.php
··· 3398 3398 'PonderAnswerTransaction' => 'applications/ponder/storage/PonderAnswerTransaction.php', 3399 3399 'PonderAnswerTransactionComment' => 'applications/ponder/storage/PonderAnswerTransactionComment.php', 3400 3400 'PonderAnswerTransactionQuery' => 'applications/ponder/query/PonderAnswerTransactionQuery.php', 3401 + 'PonderAnswerView' => 'applications/ponder/view/PonderAnswerView.php', 3401 3402 'PonderConstants' => 'applications/ponder/constants/PonderConstants.php', 3402 3403 'PonderController' => 'applications/ponder/controller/PonderController.php', 3403 3404 'PonderDAO' => 'applications/ponder/storage/PonderDAO.php', 3404 3405 'PonderDefaultViewCapability' => 'applications/ponder/capability/PonderDefaultViewCapability.php', 3405 3406 'PonderEditor' => 'applications/ponder/editor/PonderEditor.php', 3407 + 'PonderFooterView' => 'applications/ponder/view/PonderFooterView.php', 3406 3408 'PonderHelpfulSaveController' => 'applications/ponder/controller/PonderHelpfulSaveController.php', 3407 3409 'PonderModerateCapability' => 'applications/ponder/capability/PonderModerateCapability.php', 3408 3410 'PonderQuestion' => 'applications/ponder/storage/PonderQuestion.php', ··· 7569 7571 'PhabricatorPolicyInterface', 7570 7572 'PhabricatorFlaggableInterface', 7571 7573 'PhabricatorSubscribableInterface', 7572 - 'PhabricatorTokenReceiverInterface', 7573 7574 'PhabricatorDestructibleInterface', 7574 7575 ), 7575 7576 'PonderAnswerCommentController' => 'PonderController', ··· 7583 7584 'PonderAnswerTransaction' => 'PhabricatorApplicationTransaction', 7584 7585 'PonderAnswerTransactionComment' => 'PhabricatorApplicationTransactionComment', 7585 7586 'PonderAnswerTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 7587 + 'PonderAnswerView' => 'AphrontTagView', 7586 7588 'PonderConstants' => 'Phobject', 7587 7589 'PonderController' => 'PhabricatorController', 7588 7590 'PonderDAO' => 'PhabricatorLiskDAO', 7589 7591 'PonderDefaultViewCapability' => 'PhabricatorPolicyCapability', 7590 7592 'PonderEditor' => 'PhabricatorApplicationTransactionEditor', 7593 + 'PonderFooterView' => 'AphrontTagView', 7591 7594 'PonderHelpfulSaveController' => 'PonderController', 7592 7595 'PonderModerateCapability' => 'PhabricatorPolicyCapability', 7593 7596 'PonderQuestion' => array(
+36 -158
src/applications/ponder/controller/PonderQuestionViewController.php
··· 16 16 return new Aphront404Response(); 17 17 } 18 18 19 - $question_xactions = $this->buildQuestionTransactions($question); 20 19 $answers = $this->buildAnswers($question->getAnswers()); 21 20 22 21 $authors = mpull($question->getAnswers(), null, 'getAuthorPHID'); ··· 54 53 $properties = $this->buildPropertyListView($question, $actions); 55 54 $sidebar = $this->buildSidebar($question); 56 55 56 + $content_id = celerity_generate_unique_node_id(); 57 + $timeline = $this->buildTransactionTimeline( 58 + $question, 59 + id(new PonderQuestionTransactionQuery()) 60 + ->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT))); 61 + $xactions = $timeline->getTransactions(); 62 + 63 + $add_comment = id(new PhabricatorApplicationTransactionCommentView()) 64 + ->setUser($viewer) 65 + ->setObjectPHID($question->getPHID()) 66 + ->setShowPreview(false) 67 + ->setHeaderText(pht('Question Comment')) 68 + ->setAction($this->getApplicationURI("/question/comment/{$id}/")) 69 + ->setSubmitButtonName(pht('Comment')); 70 + 71 + $comment_view = phutil_tag( 72 + 'div', 73 + array( 74 + 'id' => $content_id, 75 + 'style' => 'display: none;', 76 + ), 77 + array( 78 + $timeline, 79 + $add_comment, 80 + )); 81 + 82 + $footer = id(new PonderFooterView()) 83 + ->setContentID($content_id) 84 + ->setCount(count($xactions)); 85 + 57 86 $object_box = id(new PHUIObjectBoxView()) 58 87 ->setHeader($header) 59 - ->addPropertyList($properties); 88 + ->addPropertyList($properties) 89 + ->appendChild($footer); 60 90 61 91 $crumbs = $this->buildApplicationCrumbs($this->buildSideNavView()); 62 92 $crumbs->addTextCrumb('Q'.$id, '/Q'.$id); ··· 64 94 $ponder_view = id(new PHUITwoColumnView()) 65 95 ->setMainColumn(array( 66 96 $object_box, 67 - $question_xactions, 97 + $comment_view, 68 98 $answers, 69 99 $answer_add_panel, 70 100 )) ··· 170 200 return $view; 171 201 } 172 202 173 - private function buildQuestionTransactions(PonderQuestion $question) { 174 - $viewer = $this->getViewer(); 175 - $id = $question->getID(); 176 - 177 - $timeline = $this->buildTransactionTimeline( 178 - $question, 179 - id(new PonderQuestionTransactionQuery()) 180 - ->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT))); 181 - $xactions = $timeline->getTransactions(); 182 - 183 - $add_comment = id(new PhabricatorApplicationTransactionCommentView()) 184 - ->setUser($viewer) 185 - ->setObjectPHID($question->getPHID()) 186 - ->setShowPreview(false) 187 - ->setHeaderText(pht('Question Comment')) 188 - ->setAction($this->getApplicationURI("/question/comment/{$id}/")) 189 - ->setSubmitButtonName(pht('Comment')); 190 - 191 - return $this->wrapComments( 192 - count($xactions), 193 - array( 194 - $timeline, 195 - $add_comment, 196 - )); 197 - } 198 - 199 203 /** 200 204 * This is fairly non-standard; building N timelines at once (N = number of 201 205 * answers) is tricky business. ··· 205 209 */ 206 210 private function buildAnswers(array $answers) { 207 211 $viewer = $this->getViewer(); 208 - 209 - $out = array(); 210 212 211 213 $xactions = id(new PonderAnswerTransactionQuery()) 212 214 ->setViewer($viewer) ··· 227 229 228 230 $xaction_groups = mgroup($xactions, 'getObjectPHID'); 229 231 232 + $view = array(); 230 233 foreach ($answers as $answer) { 231 - $author_phid = $answer->getAuthorPHID(); 232 234 $xactions = idx($xaction_groups, $answer->getPHID(), array()); 233 235 $id = $answer->getID(); 234 236 235 - $out[] = phutil_tag('br'); 236 - $out[] = phutil_tag('br'); 237 - $out[] = id(new PhabricatorAnchorView()) 238 - ->setAnchorName("A$id"); 239 - $header = id(new PHUIHeaderView()) 240 - ->setHeader($viewer->renderHandle($author_phid)); 241 - 242 - $actions = $this->buildAnswerActions($answer); 243 - $properties = $this->buildAnswerProperties($answer, $actions); 244 - 245 - $object_box = id(new PHUIObjectBoxView()) 246 - ->setHeader($header) 247 - ->addPropertyList($properties); 248 - 249 - $out[] = $object_box; 250 - $details = array(); 251 - 252 - $details[] = id(new PhabricatorApplicationTransactionView()) 237 + $view[] = id(new PonderAnswerView()) 253 238 ->setUser($viewer) 254 - ->setObjectPHID($answer->getPHID()) 239 + ->setAnswer($answer) 255 240 ->setTransactions($xactions) 256 241 ->setMarkupEngine($engine); 257 242 258 - $form = id(new PhabricatorApplicationTransactionCommentView()) 259 - ->setUser($viewer) 260 - ->setObjectPHID($answer->getPHID()) 261 - ->setShowPreview(false) 262 - ->setHeaderText(pht('Answer Comment')) 263 - ->setAction($this->getApplicationURI("/answer/comment/{$id}/")) 264 - ->setSubmitButtonName(pht('Comment')); 265 - 266 - $details[] = $form; 267 - 268 - $out[] = $this->wrapComments( 269 - count($xactions), 270 - $details); 271 243 } 272 - 273 - $out[] = phutil_tag('br'); 274 - $out[] = phutil_tag('br'); 275 - 276 - return $out; 277 - } 278 - 279 - private function buildAnswerActions(PonderAnswer $answer) { 280 - $viewer = $this->getViewer(); 281 - $request = $this->getRequest(); 282 - $id = $answer->getID(); 283 - 284 - $can_edit = PhabricatorPolicyFilter::hasCapability( 285 - $viewer, 286 - $answer, 287 - PhabricatorPolicyCapability::CAN_EDIT); 288 - 289 - $view = id(new PhabricatorActionListView()) 290 - ->setUser($viewer) 291 - ->setObject($answer) 292 - ->setObjectURI($request->getRequestURI()); 293 - 294 - $user_marked = $answer->getUserVote(); 295 - $can_vote = $viewer->isLoggedIn(); 296 - 297 - if ($user_marked) { 298 - $helpful_uri = "/answer/helpful/remove/{$id}/"; 299 - $helpful_icon = 'fa-times'; 300 - $helpful_text = pht('Remove Helpful'); 301 - } else { 302 - $helpful_uri = "/answer/helpful/add/{$id}/"; 303 - $helpful_icon = 'fa-thumbs-up'; 304 - $helpful_text = pht('Mark as Helpful'); 305 - } 306 - 307 - $view->addAction( 308 - id(new PhabricatorActionView()) 309 - ->setIcon($helpful_icon) 310 - ->setName($helpful_text) 311 - ->setHref($this->getApplicationURI($helpful_uri)) 312 - ->setRenderAsForm(true) 313 - ->setDisabled(!$can_vote) 314 - ->setWorkflow($can_vote)); 315 - 316 - $view->addAction( 317 - id(new PhabricatorActionView()) 318 - ->setIcon('fa-pencil') 319 - ->setName(pht('Edit Answer')) 320 - ->setHref($this->getApplicationURI("/answer/edit/{$id}/")) 321 - ->setDisabled(!$can_edit) 322 - ->setWorkflow(!$can_edit)); 323 - 324 - $view->addAction( 325 - id(new PhabricatorActionView()) 326 - ->setIcon('fa-list') 327 - ->setName(pht('View History')) 328 - ->setHref($this->getApplicationURI("/answer/history/{$id}/"))); 329 - 330 - return $view; 331 - } 332 - 333 - private function buildAnswerProperties( 334 - PonderAnswer $answer, 335 - PhabricatorActionListView $actions) { 336 - 337 - $viewer = $this->getViewer(); 338 - $view = id(new PHUIPropertyListView()) 339 - ->setUser($viewer) 340 - ->setObject($answer) 341 - ->setActionList($actions); 342 - 343 - $view->addProperty( 344 - pht('Created'), 345 - phabricator_datetime($answer->getDateCreated(), $viewer)); 346 - 347 - $view->addProperty( 348 - pht('Helpfuls'), 349 - $answer->getVoteCount()); 350 - 351 - $view->invokeWillRenderEvent(); 352 - 353 - $view->addSectionHeader(pht('Answer')); 354 - $view->addTextContent( 355 - array( 356 - phutil_tag( 357 - 'div', 358 - array( 359 - 'class' => 'phabricator-remarkup', 360 - ), 361 - PhabricatorMarkupEngine::renderOneObject( 362 - $answer, 363 - $answer->getMarkupField(), 364 - $viewer)), 365 - )); 366 244 367 245 return $view; 368 246 }
-11
src/applications/ponder/storage/PonderAnswer.php
··· 8 8 PhabricatorPolicyInterface, 9 9 PhabricatorFlaggableInterface, 10 10 PhabricatorSubscribableInterface, 11 - PhabricatorTokenReceiverInterface, 12 11 PhabricatorDestructibleInterface { 13 12 14 13 const MARKUP_FIELD_CONTENT = 'markup:content'; ··· 231 230 break; 232 231 } 233 232 return $out; 234 - } 235 - 236 - 237 - /* -( PhabricatorTokenReceiverInterface )---------------------------------- */ 238 - 239 - 240 - public function getUsersToNotifyOfTokenGiven() { 241 - return array( 242 - $this->getAuthorPHID(), 243 - ); 244 233 } 245 234 246 235
+172
src/applications/ponder/view/PonderAnswerView.php
··· 1 + <?php 2 + 3 + final class PonderAnswerView extends AphrontTagView { 4 + 5 + private $answer; 6 + private $transactions; 7 + private $engine; 8 + 9 + public function setAnswer($answer) { 10 + $this->answer = $answer; 11 + return $this; 12 + } 13 + 14 + public function setTransactions($transactions) { 15 + $this->transactions = $transactions; 16 + return $this; 17 + } 18 + 19 + public function setMarkupEngine(PhabricatorMarkupEngine $engine) { 20 + $this->engine = $engine; 21 + return $this; 22 + } 23 + 24 + protected function getTagAttributes() { 25 + return array( 26 + 'class' => 'ponder-answer-view', 27 + ); 28 + } 29 + 30 + protected function getTagContent() { 31 + require_celerity_resource('ponder-view-css'); 32 + $answer = $this->answer; 33 + $viewer = $this->getUser(); 34 + $author_phid = $answer->getAuthorPHID(); 35 + $actions = $this->buildAnswerActions(); 36 + 37 + $action_button = id(new PHUIButtonView()) 38 + ->setTag('a') 39 + ->setText(pht('Actions')) 40 + ->setHref('#') 41 + ->setIconFont('fa-bars') 42 + ->setDropdownMenu($actions); 43 + 44 + $header = id(new PHUIHeaderView()) 45 + ->setUser($viewer) 46 + ->setEpoch($answer->getDateCreated()) 47 + ->setHeader($viewer->renderHandle($author_phid)) 48 + ->addActionLink($action_button); 49 + 50 + $content = phutil_tag( 51 + 'div', 52 + array( 53 + 'class' => 'phabricator-remarkup mlt mlb msr msl', 54 + ), 55 + PhabricatorMarkupEngine::renderOneObject( 56 + $answer, 57 + $answer->getMarkupField(), 58 + $viewer)); 59 + 60 + $id = $answer->getID(); 61 + $anchor = id(new PhabricatorAnchorView()) 62 + ->setAnchorName("A$id"); 63 + 64 + $content_id = celerity_generate_unique_node_id(); 65 + $footer = id(new PonderFooterView()) 66 + ->setContentID($content_id) 67 + ->setCount(count($this->transactions)); 68 + 69 + $votes = $answer->getVoteCount(); 70 + if ($votes) { 71 + $icon = id(new PHUIIconView()) 72 + ->setIconFont('fa-thumbs-up'); 73 + $helpful = phutil_tag( 74 + 'span', 75 + array( 76 + 'class' => 'ponder-footer-action', 77 + ), 78 + array($votes, $icon)); 79 + $footer->addAction($helpful); 80 + } 81 + 82 + $answer_view = id(new PHUIObjectBoxView()) 83 + ->setHeader($header) 84 + ->appendChild($anchor) 85 + ->appendChild($content) 86 + ->appendChild($footer); 87 + 88 + $transaction_view = id(new PhabricatorApplicationTransactionView()) 89 + ->setUser($viewer) 90 + ->setObjectPHID($answer->getPHID()) 91 + ->setTransactions($this->transactions) 92 + ->setMarkupEngine($this->engine); 93 + 94 + $comment_view = id(new PhabricatorApplicationTransactionCommentView()) 95 + ->setUser($viewer) 96 + ->setObjectPHID($answer->getPHID()) 97 + ->setShowPreview(false) 98 + ->setHeaderText(pht('Answer Comment')) 99 + ->setAction("/ponder/answer/comment/{$id}/") 100 + ->setSubmitButtonName(pht('Comment')); 101 + 102 + $hidden_view = phutil_tag( 103 + 'div', 104 + array( 105 + 'id' => $content_id, 106 + 'style' => 'display: none;', 107 + ), 108 + array( 109 + $transaction_view, 110 + $comment_view, 111 + )); 112 + 113 + return array( 114 + $answer_view, 115 + $hidden_view, 116 + ); 117 + } 118 + 119 + private function buildAnswerActions() { 120 + $viewer = $this->getUser(); 121 + $answer = $this->answer; 122 + $id = $answer->getID(); 123 + 124 + $can_edit = PhabricatorPolicyFilter::hasCapability( 125 + $viewer, 126 + $answer, 127 + PhabricatorPolicyCapability::CAN_EDIT); 128 + 129 + $view = id(new PhabricatorActionListView()) 130 + ->setUser($viewer) 131 + ->setObject($answer) 132 + ->setObjectURI('Q'.$answer->getQuestionID()); 133 + 134 + $user_marked = $answer->getUserVote(); 135 + $can_vote = $viewer->isLoggedIn(); 136 + 137 + if ($user_marked) { 138 + $helpful_uri = "/ponder/answer/helpful/remove/{$id}/"; 139 + $helpful_icon = 'fa-times'; 140 + $helpful_text = pht('Remove Helpful'); 141 + } else { 142 + $helpful_uri = "/ponder/answer/helpful/add/{$id}/"; 143 + $helpful_icon = 'fa-thumbs-up'; 144 + $helpful_text = pht('Mark as Helpful'); 145 + } 146 + 147 + $view->addAction( 148 + id(new PhabricatorActionView()) 149 + ->setIcon($helpful_icon) 150 + ->setName($helpful_text) 151 + ->setHref($helpful_uri) 152 + ->setRenderAsForm(true) 153 + ->setDisabled(!$can_vote) 154 + ->setWorkflow($can_vote)); 155 + 156 + $view->addAction( 157 + id(new PhabricatorActionView()) 158 + ->setIcon('fa-pencil') 159 + ->setName(pht('Edit Answer')) 160 + ->setHref("/ponder/answer/edit/{$id}/") 161 + ->setDisabled(!$can_edit) 162 + ->setWorkflow(!$can_edit)); 163 + 164 + $view->addAction( 165 + id(new PhabricatorActionView()) 166 + ->setIcon('fa-list') 167 + ->setName(pht('View History')) 168 + ->setHref("/ponder/answer/history/{$id}/")); 169 + 170 + return $view; 171 + } 172 + }
+79
src/applications/ponder/view/PonderFooterView.php
··· 1 + <?php 2 + 3 + final class PonderFooterView extends AphrontTagView { 4 + 5 + private $contentID; 6 + private $count; 7 + private $actions = array(); 8 + 9 + public function setContentID($content_id) { 10 + $this->contentID = $content_id; 11 + return $this; 12 + } 13 + 14 + public function setCount($count) { 15 + $this->count = $count; 16 + return $this; 17 + } 18 + 19 + public function addAction($action) { 20 + $this->actions[] = $action; 21 + return $this; 22 + } 23 + 24 + protected function getTagAttributes() { 25 + return array( 26 + 'class' => 'ponder-footer-view', 27 + ); 28 + } 29 + 30 + protected function getTagContent() { 31 + Javelin::initBehavior('phabricator-reveal-content'); 32 + 33 + $hide_action_id = celerity_generate_unique_node_id(); 34 + $show_action_id = celerity_generate_unique_node_id(); 35 + $content_id = $this->contentID; 36 + 37 + if ($this->count == 0) { 38 + $text = pht('Add a Comment'); 39 + } else { 40 + $text = pht('Show %s Comments', new PhutilNumber($this->count)); 41 + } 42 + 43 + $actions = array(); 44 + $hide_action = javelin_tag( 45 + 'a', 46 + array( 47 + 'sigil' => 'reveal-content', 48 + 'class' => 'ponder-footer-action', 49 + 'id' => $hide_action_id, 50 + 'href' => '#', 51 + 'meta' => array( 52 + 'showIDs' => array($content_id, $show_action_id), 53 + 'hideIDs' => array($hide_action_id), 54 + ), 55 + ), 56 + $text); 57 + 58 + $show_action = javelin_tag( 59 + 'a', 60 + array( 61 + 'sigil' => 'reveal-content', 62 + 'style' => 'display: none;', 63 + 'class' => 'ponder-footer-action', 64 + 'id' => $show_action_id, 65 + 'href' => '#', 66 + 'meta' => array( 67 + 'showIDs' => array($hide_action_id), 68 + 'hideIDs' => array($content_id, $show_action_id), 69 + ), 70 + ), 71 + pht('Hide Comments')); 72 + 73 + $actions[] = $hide_action; 74 + $actions[] = $show_action; 75 + 76 + return array($actions, $this->actions); 77 + } 78 + 79 + }
+39 -3
webroot/rsrc/css/application/ponder/ponder-view.css
··· 14 14 border-right: 1px solid {$lightblueborder}; 15 15 } 16 16 17 - .device-desktop .ponder-comments-view { 18 - width: 90%; 19 - margin: 0 auto; 17 + .ponder-question-view .phui-property-list-properties-wrap { 18 + width: 66%; 19 + } 20 + 21 + .ponder-question-view .phui-property-list-actions { 22 + width: 30%; 23 + } 24 + 25 + .ponder-answer-view { 26 + margin-top: 16px; 27 + } 28 + 29 + .ponder-answer-view .phui-header-subheader { 30 + display: inline; 31 + margin-left: 12px; 32 + } 33 + 34 + .ponder-footer-view { 35 + margin: 0 4px -4px; 36 + } 37 + 38 + .ponder-footer-view .ponder-footer-action { 39 + padding: 4px 8px; 40 + margin-right: 8px; 41 + color: {$bluetext}; 42 + display: inline-block; 43 + background-color: rgba(71, 87, 120, 0.06); 44 + font-size: {$smallerfontsize}; 45 + } 46 + 47 + .ponder-footer-view .ponder-footer-action .phui-icon-view { 48 + color: {$bluetext}; 49 + margin-left: 4px; 50 + } 51 + 52 + .ponder-footer-view a:hover { 53 + text-decoration: none; 54 + color: {$darkbluetext}; 55 + background-color: rgba(71, 87, 120, 0.10); 20 56 }