diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 79d53f0d7a..e0e56f29de 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -2269,6 +2269,19 @@ celerity_register_resource_map(array( ), 'disk' => '/rsrc/js/application/repository/repository-crossreference.js', ), + 'javelin-behavior-slowvote-embed' => + array( + 'uri' => '/res/1315b118/rsrc/js/application/slowvote/behavior-slowvote-embed.js', + 'type' => 'js', + 'requires' => + array( + 0 => 'javelin-behavior', + 1 => 'javelin-util', + 2 => 'javelin-stratcom', + 3 => 'javelin-dom', + ), + 'disk' => '/rsrc/js/application/slowvote/behavior-slowvote-embed.js', + ), 'javelin-behavior-stripe-payment-form' => array( 'uri' => '/res/c1a12d77/rsrc/js/application/phortune/behavior-stripe-payment-form.js', @@ -3332,7 +3345,7 @@ celerity_register_resource_map(array( ), 'phabricator-slowvote-css' => array( - 'uri' => '/res/357ccc42/rsrc/css/application/slowvote/slowvote.css', + 'uri' => '/res/d1c2e05a/rsrc/css/application/slowvote/slowvote.css', 'type' => 'css', 'requires' => array( diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 1dc265f7ac..7b124be28a 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1388,6 +1388,7 @@ phutil_register_library_map(array( 'PhabricatorSlowvoteOption' => 'applications/slowvote/storage/PhabricatorSlowvoteOption.php', 'PhabricatorSlowvotePoll' => 'applications/slowvote/storage/PhabricatorSlowvotePoll.php', 'PhabricatorSlowvotePollController' => 'applications/slowvote/controller/PhabricatorSlowvotePollController.php', + 'PhabricatorSlowvoteVoteController' => 'applications/slowvote/controller/PhabricatorSlowvoteVoteController.php', 'PhabricatorSlug' => 'infrastructure/util/PhabricatorSlug.php', 'PhabricatorSlugTestCase' => 'infrastructure/util/__tests__/PhabricatorSlugTestCase.php', 'PhabricatorSortTableExample' => 'applications/uiexample/examples/PhabricatorSortTableExample.php', @@ -3088,6 +3089,7 @@ phutil_register_library_map(array( 'PhabricatorSlowvoteOption' => 'PhabricatorSlowvoteDAO', 'PhabricatorSlowvotePoll' => 'PhabricatorSlowvoteDAO', 'PhabricatorSlowvotePollController' => 'PhabricatorSlowvoteController', + 'PhabricatorSlowvoteVoteController' => 'PhabricatorSlowvoteController', 'PhabricatorSlugTestCase' => 'PhabricatorTestCase', 'PhabricatorSortTableExample' => 'PhabricatorUIExample', 'PhabricatorSourceCodeView' => 'AphrontView', diff --git a/src/applications/slowvote/application/PhabricatorApplicationSlowvote.php b/src/applications/slowvote/application/PhabricatorApplicationSlowvote.php index 8970fe15b5..4802e1470d 100644 --- a/src/applications/slowvote/application/PhabricatorApplicationSlowvote.php +++ b/src/applications/slowvote/application/PhabricatorApplicationSlowvote.php @@ -42,6 +42,7 @@ final class PhabricatorApplicationSlowvote extends PhabricatorApplication { '/vote/' => array( '(?:view/(?P\w+)/)?' => 'PhabricatorSlowvoteListController', 'create/' => 'PhabricatorSlowvoteCreateController', + '(?P[1-9]\d*)/' => 'PhabricatorSlowvoteVoteController', ), ); } diff --git a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php index 6db949faa6..7c070eb4c3 100644 --- a/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php +++ b/src/applications/slowvote/controller/PhabricatorSlowvotePollController.php @@ -44,48 +44,16 @@ final class PhabricatorSlowvotePollController $comment_text = $viewer_comment->getCommentText(); } - if ($request->isFormPost()) { - $comment = idx($comments_by_user, $viewer_phid, null); - if ($comment) { - $comment->delete(); - } + if ($request->isAjax()) { + $embed = id(new SlowvoteEmbedView()) + ->setPoll($poll) + ->setOptions($options) + ->setViewerChoices($viewer_choices); - $comment_text = $request->getStr('comments'); - if (strlen($comment_text)) { - id(new PhabricatorSlowvoteComment()) - ->setAuthorPHID($viewer_phid) - ->setPollID($poll->getID()) - ->setCommentText($comment_text) - ->save(); - } - - $votes = $request->getArr('vote'); - - switch ($poll->getMethod()) { - case PhabricatorSlowvotePoll::METHOD_PLURALITY: - // Enforce only one vote. - $votes = array_slice($votes, 0, 1); - break; - case PhabricatorSlowvotePoll::METHOD_APPROVAL: - // No filtering. - break; - default: - throw new Exception("Unknown poll method!"); - } - - foreach ($viewer_choices as $viewer_choice) { - $viewer_choice->delete(); - } - - foreach ($votes as $vote) { - id(new PhabricatorSlowvoteChoice()) - ->setAuthorPHID($viewer_phid) - ->setPollID($poll->getID()) - ->setOptionID($vote) - ->save(); - } - - return id(new AphrontRedirectResponse())->setURI('/V'.$poll->getID()); + return id(new AphrontAjaxResponse()) + ->setContent(array( + 'pollID' => $poll->getID(), + 'contentHTML' => $embed->render())); } require_celerity_resource('phabricator-slowvote-css'); @@ -159,6 +127,7 @@ final class PhabricatorSlowvotePollController $form = id(new AphrontFormView()) ->setUser($user) + ->setAction(sprintf('/vote/%d/', $poll->getID())) ->appendChild(hsprintf( '

%s

', $instructions)) diff --git a/src/applications/slowvote/controller/PhabricatorSlowvoteVoteController.php b/src/applications/slowvote/controller/PhabricatorSlowvoteVoteController.php new file mode 100644 index 0000000000..4f0193d9b5 --- /dev/null +++ b/src/applications/slowvote/controller/PhabricatorSlowvoteVoteController.php @@ -0,0 +1,135 @@ +id = $data['id']; + } + + public function processRequest() { + $request = $this->getRequest(); + $user = $request->getUser(); + + $poll = id(new PhabricatorSlowvotePoll())->load($this->id); + $options = id(new PhabricatorSlowvoteOption())->loadAllWhere( + 'pollID = %d', + $poll->getID()); + $user_choices = id(new PhabricatorSlowvoteChoice())->loadAllWhere( + 'pollID = %d AND authorPHID = %s', + $poll->getID(), + $user->getPHID()); + + $comment_text = $request->getStr('comments'); + $old_comment = id(new PhabricatorSlowvoteComment())->loadOneWhere( + 'pollID = %d AND authorPHID = %s', + $poll->getID(), + $user->getPHID()); + + $update_comment = false; + if ($old_comment && $comment_text && + $old_comment->getCommentText() !== $comment_text) { + + $update_comment = true; + } else if (!$old_comment && $comment_text) { + $update_comment = true; + } + + if ($update_comment) { + if ($old_comment) { + $old_comment->delete(); + } + + id(new PhabricatorSlowvoteComment()) + ->setAuthorPHID($user->getPHID()) + ->setPollID($poll->getID()) + ->setCommentText($comment_text) + ->save(); + } + + $old_votes = mpull($user_choices, null, 'getOptionID'); + + if ($request->isAjax()) { + $vote = $request->getInt('vote'); + $votes = array_keys($old_votes); + $votes = array_fuse($votes, $votes); + + if ($poll->getMethod() == PhabricatorSlowvotePoll::METHOD_PLURALITY) { + if (idx($votes, $vote, false)) { + $votes = array(); + } else { + $votes = array($vote); + } + } else { + if (idx($votes, $vote, false)) { + unset($votes[$vote]); + } else { + $votes[$vote] = $vote; + } + } + + $this->updateVotes($user, $poll, $old_votes, $votes); + + $updated_choices = id(new PhabricatorSlowvoteChoice())->loadAllWhere( + 'pollID = %d AND authorPHID = %s', + $poll->getID(), + $user->getPHID()); + + $embed = id(new SlowvoteEmbedView()) + ->setPoll($poll) + ->setOptions($options) + ->setViewerChoices($updated_choices); + + return id(new AphrontAjaxResponse()) + ->setContent(array( + 'pollID' => $poll->getID(), + 'contentHTML' => $embed->render())); + } + + if (!$request->isFormPost()) { + return id(new Aphront404Response()); + } + + $votes = $request->getArr('vote'); + $votes = array_fuse($votes, $votes); + + $this->updateVotes($user, $poll, $old_votes, $votes); + + return id(new AphrontRedirectResponse())->setURI('/V'.$poll->getID()); + + } + + private function updateVotes($user, $poll, $old_votes, $votes) { + + if (!empty($votes) && count($votes) > 1 && + $poll->getMethod() == PhabricatorSlowvotePoll::METHOD_PLURALITY) { + return id(new Aphront400Response()); + } + + foreach ($old_votes as $old_vote) { + if (!idx($votes, $old_vote->getOptionID(), false)) { + $old_vote->delete(); + } + } + + foreach ($votes as $vote) { + if (idx($old_votes, $vote, false)) { + continue; + } + + id(new PhabricatorSlowvoteChoice()) + ->setAuthorPHID($user->getPHID()) + ->setPollID($poll->getID()) + ->setOptionID($vote) + ->save(); + } + + } + + +} diff --git a/src/applications/slowvote/view/SlowvoteEmbedView.php b/src/applications/slowvote/view/SlowvoteEmbedView.php index 543ad24248..626026de70 100644 --- a/src/applications/slowvote/view/SlowvoteEmbedView.php +++ b/src/applications/slowvote/view/SlowvoteEmbedView.php @@ -39,6 +39,11 @@ final class SlowvoteEmbedView extends AphrontView { } require_celerity_resource('phabricator-slowvote-css'); + require_celerity_resource('javelin-behavior-slowvote-embed'); + + $config = array( + 'pollID' => $this->poll->getID()); + Javelin::initBehavior('slowvote-embed', $config); $user_choices = array(); if (!empty($this->viewerChoices)) { @@ -56,17 +61,30 @@ final class SlowvoteEmbedView extends AphrontView { $selected = ''; + if (idx($user_choices, $option->getID(), false)) { $classes .= ' phabricator-slowvote-embed-option-selected'; $selected = '@'; } - $option_text = phutil_tag( + $is_selected = javelin_tag( 'div', array( - 'class' => $classes + 'class' => 'phabricator-slowvote-embed-option-vote', + 'sigil' => 'slowvote-embed-vote' ), - $selected.$option->getName()); + $selected); + + $option_text = javelin_tag( + 'div', + array( + 'class' => $classes, + 'sigil' => 'slowvote-option', + 'meta' => array( + 'optionID' => $option->getID() + ) + ), + array($is_selected, $option->getName())); $options[] = phutil_tag( 'div', @@ -95,11 +113,16 @@ final class SlowvoteEmbedView extends AphrontView { array(), $options); - return phutil_tag( + return javelin_tag( 'div', array( - 'class' => 'phabricator-slowvote-embed' + 'class' => 'phabricator-slowvote-embed', + 'sigil' => 'slowvote-embed', + 'meta' => array( + 'pollID' => $this->poll->getID() + ) ), array($header, $body)); } + } diff --git a/webroot/rsrc/css/application/slowvote/slowvote.css b/webroot/rsrc/css/application/slowvote/slowvote.css index 45433c1ac7..84470e973f 100644 --- a/webroot/rsrc/css/application/slowvote/slowvote.css +++ b/webroot/rsrc/css/application/slowvote/slowvote.css @@ -80,12 +80,12 @@ background: #F0FFFF; } -.phabricator-slowvote-embed-option-selected { - padding-left: 0px !important; +.phabricator-slowvote-embed-option-vote { + display: inline-block; + width: 1em; } .phabricator-slowvote-embed-option-text { border: 1px solid #dbdbdb; border-left: 0px; - padding-left: 1em; } diff --git a/webroot/rsrc/js/application/slowvote/behavior-slowvote-embed.js b/webroot/rsrc/js/application/slowvote/behavior-slowvote-embed.js new file mode 100644 index 0000000000..b35cdadd09 --- /dev/null +++ b/webroot/rsrc/js/application/slowvote/behavior-slowvote-embed.js @@ -0,0 +1,43 @@ +/** + * @provides javelin-behavior-slowvote-embed + * @requires javelin-behavior + * javelin-util + * javelin-stratcom + * javelin-dom + */ +JX.behavior('slowvote-embed', function(config) { + JX.Stratcom.listen( + ['click'], + 'slowvote-option', + function(e) { + if (!e.isNormalMouseEvent()) { + return; + } + e.kill(); + + var pollID = e.getNodeData('slowvote-embed').pollID; + var voteURI = '/vote/' + pollID + '/'; + + var request = new JX.Request(voteURI, function(r) { + var updated_poll = JX.$H(r.contentHTML); + var root = JX.$('base-page'); + + var polls = JX.DOM.scry(root, 'div', 'slowvote-embed'); + + for(var i = 0; i < polls.length; i++) { + var data = JX.Stratcom.getData(polls[i]); + + if (data.pollID == pollID) { + JX.DOM.replace(polls[i], updated_poll); + } + + } + + }); + + request.addData({vote: e.getNodeData('slowvote-option').optionID}); + request.send(); + + }); + +});