From 306af6fb2892b2841f7e26f9c6b76e78a4df8686 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 11 Aug 2015 10:42:40 -0700 Subject: [PATCH] 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 --- resources/celerity/map.php | 4 +- src/__phutil_library_map__.php | 5 +- .../PonderQuestionViewController.php | 194 ++++-------------- .../ponder/storage/PonderAnswer.php | 11 - .../ponder/view/PonderAnswerView.php | 172 ++++++++++++++++ .../ponder/view/PonderFooterView.php | 79 +++++++ .../css/application/ponder/ponder-view.css | 42 +++- 7 files changed, 332 insertions(+), 175 deletions(-) create mode 100644 src/applications/ponder/view/PonderAnswerView.php create mode 100644 src/applications/ponder/view/PonderFooterView.php diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 5c3fcbbeeb..64d3ff99f3 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -93,7 +93,7 @@ return array( 'rsrc/css/application/policy/policy-edit.css' => '815c66f7', 'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43', 'rsrc/css/application/policy/policy.css' => '957ea14c', - 'rsrc/css/application/ponder/ponder-view.css' => '4e557c89', + 'rsrc/css/application/ponder/ponder-view.css' => '6a399881', 'rsrc/css/application/projects/project-icon.css' => '4e3eaa5a', 'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733', 'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5', @@ -811,7 +811,7 @@ return array( 'policy-css' => '957ea14c', 'policy-edit-css' => '815c66f7', 'policy-transaction-detail-css' => '82100a43', - 'ponder-view-css' => '4e557c89', + 'ponder-view-css' => '6a399881', 'project-icon-css' => '4e3eaa5a', 'raphael-core' => '51ee6b43', 'raphael-g' => '40dde778', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 9e76723b92..268e934669 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3398,11 +3398,13 @@ phutil_register_library_map(array( 'PonderAnswerTransaction' => 'applications/ponder/storage/PonderAnswerTransaction.php', 'PonderAnswerTransactionComment' => 'applications/ponder/storage/PonderAnswerTransactionComment.php', 'PonderAnswerTransactionQuery' => 'applications/ponder/query/PonderAnswerTransactionQuery.php', + 'PonderAnswerView' => 'applications/ponder/view/PonderAnswerView.php', 'PonderConstants' => 'applications/ponder/constants/PonderConstants.php', 'PonderController' => 'applications/ponder/controller/PonderController.php', 'PonderDAO' => 'applications/ponder/storage/PonderDAO.php', 'PonderDefaultViewCapability' => 'applications/ponder/capability/PonderDefaultViewCapability.php', 'PonderEditor' => 'applications/ponder/editor/PonderEditor.php', + 'PonderFooterView' => 'applications/ponder/view/PonderFooterView.php', 'PonderHelpfulSaveController' => 'applications/ponder/controller/PonderHelpfulSaveController.php', 'PonderModerateCapability' => 'applications/ponder/capability/PonderModerateCapability.php', 'PonderQuestion' => 'applications/ponder/storage/PonderQuestion.php', @@ -7569,7 +7571,6 @@ phutil_register_library_map(array( 'PhabricatorPolicyInterface', 'PhabricatorFlaggableInterface', 'PhabricatorSubscribableInterface', - 'PhabricatorTokenReceiverInterface', 'PhabricatorDestructibleInterface', ), 'PonderAnswerCommentController' => 'PonderController', @@ -7583,11 +7584,13 @@ phutil_register_library_map(array( 'PonderAnswerTransaction' => 'PhabricatorApplicationTransaction', 'PonderAnswerTransactionComment' => 'PhabricatorApplicationTransactionComment', 'PonderAnswerTransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'PonderAnswerView' => 'AphrontTagView', 'PonderConstants' => 'Phobject', 'PonderController' => 'PhabricatorController', 'PonderDAO' => 'PhabricatorLiskDAO', 'PonderDefaultViewCapability' => 'PhabricatorPolicyCapability', 'PonderEditor' => 'PhabricatorApplicationTransactionEditor', + 'PonderFooterView' => 'AphrontTagView', 'PonderHelpfulSaveController' => 'PonderController', 'PonderModerateCapability' => 'PhabricatorPolicyCapability', 'PonderQuestion' => array( diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index 10236d775e..a974716540 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -16,7 +16,6 @@ final class PonderQuestionViewController extends PonderController { return new Aphront404Response(); } - $question_xactions = $this->buildQuestionTransactions($question); $answers = $this->buildAnswers($question->getAnswers()); $authors = mpull($question->getAnswers(), null, 'getAuthorPHID'); @@ -54,9 +53,40 @@ final class PonderQuestionViewController extends PonderController { $properties = $this->buildPropertyListView($question, $actions); $sidebar = $this->buildSidebar($question); + $content_id = celerity_generate_unique_node_id(); + $timeline = $this->buildTransactionTimeline( + $question, + id(new PonderQuestionTransactionQuery()) + ->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT))); + $xactions = $timeline->getTransactions(); + + $add_comment = id(new PhabricatorApplicationTransactionCommentView()) + ->setUser($viewer) + ->setObjectPHID($question->getPHID()) + ->setShowPreview(false) + ->setHeaderText(pht('Question Comment')) + ->setAction($this->getApplicationURI("/question/comment/{$id}/")) + ->setSubmitButtonName(pht('Comment')); + + $comment_view = phutil_tag( + 'div', + array( + 'id' => $content_id, + 'style' => 'display: none;', + ), + array( + $timeline, + $add_comment, + )); + + $footer = id(new PonderFooterView()) + ->setContentID($content_id) + ->setCount(count($xactions)); + $object_box = id(new PHUIObjectBoxView()) ->setHeader($header) - ->addPropertyList($properties); + ->addPropertyList($properties) + ->appendChild($footer); $crumbs = $this->buildApplicationCrumbs($this->buildSideNavView()); $crumbs->addTextCrumb('Q'.$id, '/Q'.$id); @@ -64,7 +94,7 @@ final class PonderQuestionViewController extends PonderController { $ponder_view = id(new PHUITwoColumnView()) ->setMainColumn(array( $object_box, - $question_xactions, + $comment_view, $answers, $answer_add_panel, )) @@ -170,32 +200,6 @@ final class PonderQuestionViewController extends PonderController { return $view; } - private function buildQuestionTransactions(PonderQuestion $question) { - $viewer = $this->getViewer(); - $id = $question->getID(); - - $timeline = $this->buildTransactionTimeline( - $question, - id(new PonderQuestionTransactionQuery()) - ->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT))); - $xactions = $timeline->getTransactions(); - - $add_comment = id(new PhabricatorApplicationTransactionCommentView()) - ->setUser($viewer) - ->setObjectPHID($question->getPHID()) - ->setShowPreview(false) - ->setHeaderText(pht('Question Comment')) - ->setAction($this->getApplicationURI("/question/comment/{$id}/")) - ->setSubmitButtonName(pht('Comment')); - - return $this->wrapComments( - count($xactions), - array( - $timeline, - $add_comment, - )); - } - /** * This is fairly non-standard; building N timelines at once (N = number of * answers) is tricky business. @@ -206,8 +210,6 @@ final class PonderQuestionViewController extends PonderController { private function buildAnswers(array $answers) { $viewer = $this->getViewer(); - $out = array(); - $xactions = id(new PonderAnswerTransactionQuery()) ->setViewer($viewer) ->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT)) @@ -227,143 +229,19 @@ final class PonderQuestionViewController extends PonderController { $xaction_groups = mgroup($xactions, 'getObjectPHID'); + $view = array(); foreach ($answers as $answer) { - $author_phid = $answer->getAuthorPHID(); $xactions = idx($xaction_groups, $answer->getPHID(), array()); $id = $answer->getID(); - $out[] = phutil_tag('br'); - $out[] = phutil_tag('br'); - $out[] = id(new PhabricatorAnchorView()) - ->setAnchorName("A$id"); - $header = id(new PHUIHeaderView()) - ->setHeader($viewer->renderHandle($author_phid)); - - $actions = $this->buildAnswerActions($answer); - $properties = $this->buildAnswerProperties($answer, $actions); - - $object_box = id(new PHUIObjectBoxView()) - ->setHeader($header) - ->addPropertyList($properties); - - $out[] = $object_box; - $details = array(); - - $details[] = id(new PhabricatorApplicationTransactionView()) + $view[] = id(new PonderAnswerView()) ->setUser($viewer) - ->setObjectPHID($answer->getPHID()) + ->setAnswer($answer) ->setTransactions($xactions) ->setMarkupEngine($engine); - $form = id(new PhabricatorApplicationTransactionCommentView()) - ->setUser($viewer) - ->setObjectPHID($answer->getPHID()) - ->setShowPreview(false) - ->setHeaderText(pht('Answer Comment')) - ->setAction($this->getApplicationURI("/answer/comment/{$id}/")) - ->setSubmitButtonName(pht('Comment')); - - $details[] = $form; - - $out[] = $this->wrapComments( - count($xactions), - $details); } - $out[] = phutil_tag('br'); - $out[] = phutil_tag('br'); - - return $out; - } - - private function buildAnswerActions(PonderAnswer $answer) { - $viewer = $this->getViewer(); - $request = $this->getRequest(); - $id = $answer->getID(); - - $can_edit = PhabricatorPolicyFilter::hasCapability( - $viewer, - $answer, - PhabricatorPolicyCapability::CAN_EDIT); - - $view = id(new PhabricatorActionListView()) - ->setUser($viewer) - ->setObject($answer) - ->setObjectURI($request->getRequestURI()); - - $user_marked = $answer->getUserVote(); - $can_vote = $viewer->isLoggedIn(); - - if ($user_marked) { - $helpful_uri = "/answer/helpful/remove/{$id}/"; - $helpful_icon = 'fa-times'; - $helpful_text = pht('Remove Helpful'); - } else { - $helpful_uri = "/answer/helpful/add/{$id}/"; - $helpful_icon = 'fa-thumbs-up'; - $helpful_text = pht('Mark as Helpful'); - } - - $view->addAction( - id(new PhabricatorActionView()) - ->setIcon($helpful_icon) - ->setName($helpful_text) - ->setHref($this->getApplicationURI($helpful_uri)) - ->setRenderAsForm(true) - ->setDisabled(!$can_vote) - ->setWorkflow($can_vote)); - - $view->addAction( - id(new PhabricatorActionView()) - ->setIcon('fa-pencil') - ->setName(pht('Edit Answer')) - ->setHref($this->getApplicationURI("/answer/edit/{$id}/")) - ->setDisabled(!$can_edit) - ->setWorkflow(!$can_edit)); - - $view->addAction( - id(new PhabricatorActionView()) - ->setIcon('fa-list') - ->setName(pht('View History')) - ->setHref($this->getApplicationURI("/answer/history/{$id}/"))); - - return $view; - } - - private function buildAnswerProperties( - PonderAnswer $answer, - PhabricatorActionListView $actions) { - - $viewer = $this->getViewer(); - $view = id(new PHUIPropertyListView()) - ->setUser($viewer) - ->setObject($answer) - ->setActionList($actions); - - $view->addProperty( - pht('Created'), - phabricator_datetime($answer->getDateCreated(), $viewer)); - - $view->addProperty( - pht('Helpfuls'), - $answer->getVoteCount()); - - $view->invokeWillRenderEvent(); - - $view->addSectionHeader(pht('Answer')); - $view->addTextContent( - array( - phutil_tag( - 'div', - array( - 'class' => 'phabricator-remarkup', - ), - PhabricatorMarkupEngine::renderOneObject( - $answer, - $answer->getMarkupField(), - $viewer)), - )); - return $view; } diff --git a/src/applications/ponder/storage/PonderAnswer.php b/src/applications/ponder/storage/PonderAnswer.php index 937ae75411..42ba5f3816 100644 --- a/src/applications/ponder/storage/PonderAnswer.php +++ b/src/applications/ponder/storage/PonderAnswer.php @@ -8,7 +8,6 @@ final class PonderAnswer extends PonderDAO PhabricatorPolicyInterface, PhabricatorFlaggableInterface, PhabricatorSubscribableInterface, - PhabricatorTokenReceiverInterface, PhabricatorDestructibleInterface { const MARKUP_FIELD_CONTENT = 'markup:content'; @@ -234,16 +233,6 @@ final class PonderAnswer extends PonderDAO } -/* -( PhabricatorTokenReceiverInterface )---------------------------------- */ - - - public function getUsersToNotifyOfTokenGiven() { - return array( - $this->getAuthorPHID(), - ); - } - - /* -( PhabricatorSubscribableInterface )----------------------------------- */ diff --git a/src/applications/ponder/view/PonderAnswerView.php b/src/applications/ponder/view/PonderAnswerView.php new file mode 100644 index 0000000000..435a613828 --- /dev/null +++ b/src/applications/ponder/view/PonderAnswerView.php @@ -0,0 +1,172 @@ +answer = $answer; + return $this; + } + + public function setTransactions($transactions) { + $this->transactions = $transactions; + return $this; + } + + public function setMarkupEngine(PhabricatorMarkupEngine $engine) { + $this->engine = $engine; + return $this; + } + + protected function getTagAttributes() { + return array( + 'class' => 'ponder-answer-view', + ); + } + + protected function getTagContent() { + require_celerity_resource('ponder-view-css'); + $answer = $this->answer; + $viewer = $this->getUser(); + $author_phid = $answer->getAuthorPHID(); + $actions = $this->buildAnswerActions(); + + $action_button = id(new PHUIButtonView()) + ->setTag('a') + ->setText(pht('Actions')) + ->setHref('#') + ->setIconFont('fa-bars') + ->setDropdownMenu($actions); + + $header = id(new PHUIHeaderView()) + ->setUser($viewer) + ->setEpoch($answer->getDateCreated()) + ->setHeader($viewer->renderHandle($author_phid)) + ->addActionLink($action_button); + + $content = phutil_tag( + 'div', + array( + 'class' => 'phabricator-remarkup mlt mlb msr msl', + ), + PhabricatorMarkupEngine::renderOneObject( + $answer, + $answer->getMarkupField(), + $viewer)); + + $id = $answer->getID(); + $anchor = id(new PhabricatorAnchorView()) + ->setAnchorName("A$id"); + + $content_id = celerity_generate_unique_node_id(); + $footer = id(new PonderFooterView()) + ->setContentID($content_id) + ->setCount(count($this->transactions)); + + $votes = $answer->getVoteCount(); + if ($votes) { + $icon = id(new PHUIIconView()) + ->setIconFont('fa-thumbs-up'); + $helpful = phutil_tag( + 'span', + array( + 'class' => 'ponder-footer-action', + ), + array($votes, $icon)); + $footer->addAction($helpful); + } + + $answer_view = id(new PHUIObjectBoxView()) + ->setHeader($header) + ->appendChild($anchor) + ->appendChild($content) + ->appendChild($footer); + + $transaction_view = id(new PhabricatorApplicationTransactionView()) + ->setUser($viewer) + ->setObjectPHID($answer->getPHID()) + ->setTransactions($this->transactions) + ->setMarkupEngine($this->engine); + + $comment_view = id(new PhabricatorApplicationTransactionCommentView()) + ->setUser($viewer) + ->setObjectPHID($answer->getPHID()) + ->setShowPreview(false) + ->setHeaderText(pht('Answer Comment')) + ->setAction("/ponder/answer/comment/{$id}/") + ->setSubmitButtonName(pht('Comment')); + + $hidden_view = phutil_tag( + 'div', + array( + 'id' => $content_id, + 'style' => 'display: none;', + ), + array( + $transaction_view, + $comment_view, + )); + + return array( + $answer_view, + $hidden_view, + ); + } + + private function buildAnswerActions() { + $viewer = $this->getUser(); + $answer = $this->answer; + $id = $answer->getID(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $answer, + PhabricatorPolicyCapability::CAN_EDIT); + + $view = id(new PhabricatorActionListView()) + ->setUser($viewer) + ->setObject($answer) + ->setObjectURI('Q'.$answer->getQuestionID()); + + $user_marked = $answer->getUserVote(); + $can_vote = $viewer->isLoggedIn(); + + if ($user_marked) { + $helpful_uri = "/ponder/answer/helpful/remove/{$id}/"; + $helpful_icon = 'fa-times'; + $helpful_text = pht('Remove Helpful'); + } else { + $helpful_uri = "/ponder/answer/helpful/add/{$id}/"; + $helpful_icon = 'fa-thumbs-up'; + $helpful_text = pht('Mark as Helpful'); + } + + $view->addAction( + id(new PhabricatorActionView()) + ->setIcon($helpful_icon) + ->setName($helpful_text) + ->setHref($helpful_uri) + ->setRenderAsForm(true) + ->setDisabled(!$can_vote) + ->setWorkflow($can_vote)); + + $view->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-pencil') + ->setName(pht('Edit Answer')) + ->setHref("/ponder/answer/edit/{$id}/") + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); + + $view->addAction( + id(new PhabricatorActionView()) + ->setIcon('fa-list') + ->setName(pht('View History')) + ->setHref("/ponder/answer/history/{$id}/")); + + return $view; + } +} diff --git a/src/applications/ponder/view/PonderFooterView.php b/src/applications/ponder/view/PonderFooterView.php new file mode 100644 index 0000000000..7e553b3f4b --- /dev/null +++ b/src/applications/ponder/view/PonderFooterView.php @@ -0,0 +1,79 @@ +contentID = $content_id; + return $this; + } + + public function setCount($count) { + $this->count = $count; + return $this; + } + + public function addAction($action) { + $this->actions[] = $action; + return $this; + } + + protected function getTagAttributes() { + return array( + 'class' => 'ponder-footer-view', + ); + } + + protected function getTagContent() { + Javelin::initBehavior('phabricator-reveal-content'); + + $hide_action_id = celerity_generate_unique_node_id(); + $show_action_id = celerity_generate_unique_node_id(); + $content_id = $this->contentID; + + if ($this->count == 0) { + $text = pht('Add a Comment'); + } else { + $text = pht('Show %s Comments', new PhutilNumber($this->count)); + } + + $actions = array(); + $hide_action = javelin_tag( + 'a', + array( + 'sigil' => 'reveal-content', + 'class' => 'ponder-footer-action', + 'id' => $hide_action_id, + 'href' => '#', + 'meta' => array( + 'showIDs' => array($content_id, $show_action_id), + 'hideIDs' => array($hide_action_id), + ), + ), + $text); + + $show_action = javelin_tag( + 'a', + array( + 'sigil' => 'reveal-content', + 'style' => 'display: none;', + 'class' => 'ponder-footer-action', + 'id' => $show_action_id, + 'href' => '#', + 'meta' => array( + 'showIDs' => array($hide_action_id), + 'hideIDs' => array($content_id, $show_action_id), + ), + ), + pht('Hide Comments')); + + $actions[] = $hide_action; + $actions[] = $show_action; + + return array($actions, $this->actions); + } + +} diff --git a/webroot/rsrc/css/application/ponder/ponder-view.css b/webroot/rsrc/css/application/ponder/ponder-view.css index 4cf9509281..867009c4bb 100644 --- a/webroot/rsrc/css/application/ponder/ponder-view.css +++ b/webroot/rsrc/css/application/ponder/ponder-view.css @@ -14,7 +14,43 @@ border-right: 1px solid {$lightblueborder}; } -.device-desktop .ponder-comments-view { - width: 90%; - margin: 0 auto; +.ponder-question-view .phui-property-list-properties-wrap { + width: 66%; +} + +.ponder-question-view .phui-property-list-actions { + width: 30%; +} + +.ponder-answer-view { + margin-top: 16px; +} + +.ponder-answer-view .phui-header-subheader { + display: inline; + margin-left: 12px; +} + +.ponder-footer-view { + margin: 0 4px -4px; +} + +.ponder-footer-view .ponder-footer-action { + padding: 4px 8px; + margin-right: 8px; + color: {$bluetext}; + display: inline-block; + background-color: rgba(71, 87, 120, 0.06); + font-size: {$smallerfontsize}; +} + +.ponder-footer-view .ponder-footer-action .phui-icon-view { + color: {$bluetext}; + margin-left: 4px; +} + +.ponder-footer-view a:hover { + text-decoration: none; + color: {$darkbluetext}; + background-color: rgba(71, 87, 120, 0.10); }