diff --git a/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentEditController.php b/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentEditController.php index 0dd5a95ed1..b67c833b4c 100644 --- a/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentEditController.php +++ b/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentEditController.php @@ -4,11 +4,9 @@ final class PhabricatorApplicationTransactionCommentEditController extends PhabricatorApplicationTransactionController { private $phid; - private $anchor; public function willProcessRequest(array $data) { $this->phid = $data['phid']; - $this->anchor = idx($data, 'anchor'); } public function processRequest() { @@ -59,7 +57,26 @@ final class PhabricatorApplicationTransactionCommentEditController ))) ->applyEdit($xaction, $comment); - return id(new AphrontReloadResponse())->setURI($obj_handle->getURI()); + if ($request->isAjax()) { + $view = id(new PhabricatorApplicationTransactionView()) + ->setViewer($user) + ->setTransactions(array($xaction)); + + $anchor = $request->getStr('anchor'); + if ($anchor) { + $view->setAnchorOffset($anchor); + } + + return id(new AphrontAjaxResponse())->setContent( + array( + 'xactions' => mpull( + $view->buildEvents(), + 'render', + 'getTransactionPHID'), + )); + } else { + return id(new AphrontReloadResponse())->setURI($obj_handle->getURI()); + } } $dialog = id(new AphrontDialogView()) @@ -67,6 +84,7 @@ final class PhabricatorApplicationTransactionCommentEditController ->setTitle(pht('Edit Comment')); $dialog + ->addHiddenInput('anchor', $request->getStr('anchor')) ->appendChild( id(new PhabricatorRemarkupControl()) ->setName('text') diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php index 8ffe238684..b58aad2b64 100644 --- a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php +++ b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php @@ -8,7 +8,7 @@ class PhabricatorApplicationTransactionView extends AphrontView { private $viewer; private $transactions; private $engine; - private $anchorOffset = 0; + private $anchorOffset = 1; private $showEditActions = true; public function setShowEditActions($show_edit_actions) { @@ -41,33 +41,19 @@ class PhabricatorApplicationTransactionView extends AphrontView { return $this; } - public function render() { + public function buildEvents() { $field = PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT; + $engine = $this->getOrBuildEngine(); - if (!$this->engine) { - $engine = id(new PhabricatorMarkupEngine()) - ->setViewer($this->viewer); - foreach ($this->transactions as $xaction) { - if (!$xaction->hasComment()) { - continue; - } - $engine->addObject($xaction->getComment(), $field); - } - $engine->process(); - - $this->engine = $engine; - } - - $view = new PhabricatorTimelineView(); $viewer = $this->viewer; $anchor = $this->anchorOffset; + $events = array(); foreach ($this->transactions as $xaction) { if ($xaction->shouldHide()) { continue; } - $anchor++; $event = id(new PhabricatorTimelineEventView()) ->setViewer($viewer) ->setTransactionPHID($xaction->getPHID()) @@ -79,6 +65,9 @@ class PhabricatorApplicationTransactionView extends AphrontView { ->setContentSource($xaction->getContentSource()) ->setAnchor($anchor); + $anchor++; + + $has_deleted_comment = $xaction->getComment() && $xaction->getComment()->getIsDeleted(); @@ -102,16 +91,59 @@ class PhabricatorApplicationTransactionView extends AphrontView { if ($xaction->hasComment()) { $event->appendChild( - $this->engine->getOutput($xaction->getComment(), $field)); + $engine->getOutput($xaction->getComment(), $field)); } else if ($has_deleted_comment) { $event->appendChild( ''.pht('This comment has been deleted.').''); } + $events[] = $event; + } + + return $events; + } + + public function render() { + $view = new PhabricatorTimelineView(); + foreach ($this->buildEvents() as $event) { $view->addEvent($event); } + if ($this->getShowEditActions()) { + $list_id = celerity_generate_unique_node_id(); + + $view->setID($list_id); + + Javelin::initBehavior( + 'phabricator-transaction-list', + array( + 'listID' => $list_id, + )); + } + return $view->render(); } + + + private function getOrBuildEngine() { + if ($this->engine) { + return $this->engine; + } + + $field = PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT; + + $engine = id(new PhabricatorMarkupEngine()) + ->setViewer($this->viewer); + foreach ($this->transactions as $xaction) { + if (!$xaction->hasComment()) { + continue; + } + $engine->addObject($xaction->getComment(), $field); + } + $engine->process(); + + return $engine; + } + } diff --git a/src/view/layout/PhabricatorTimelineEventView.php b/src/view/layout/PhabricatorTimelineEventView.php index 6a5db0ff72..cfd4b4af9a 100644 --- a/src/view/layout/PhabricatorTimelineEventView.php +++ b/src/view/layout/PhabricatorTimelineEventView.php @@ -126,7 +126,7 @@ final class PhabricatorTimelineEventView extends AphrontView { 'a', array( 'href' => '/transactions/edit/'.$xaction_phid.'/', - 'sigil' => 'workflow', + 'sigil' => 'workflow transaction-edit', ), pht('Edit')); } @@ -258,11 +258,23 @@ final class PhabricatorTimelineEventView extends AphrontView { $outer_classes[] = 'phabricator-timeline-'.$this->color; } - return phutil_render_tag( + $sigil = null; + $meta = null; + if ($this->getTransactionPHID()) { + $sigil = 'transaction'; + $meta = array( + 'phid' => $this->getTransactionPHID(), + 'anchor' => $this->anchor, + ); + } + + return javelin_render_tag( 'div', array( 'class' => implode(' ', $outer_classes), 'id' => $this->anchor ? 'anchor-'.$this->anchor : null, + 'sigil' => $sigil, + 'meta' => $meta, ), phutil_render_tag( 'div', diff --git a/src/view/layout/PhabricatorTimelineView.php b/src/view/layout/PhabricatorTimelineView.php index f48b7144b2..eae7f527d6 100644 --- a/src/view/layout/PhabricatorTimelineView.php +++ b/src/view/layout/PhabricatorTimelineView.php @@ -3,6 +3,12 @@ final class PhabricatorTimelineView extends AphrontView { private $events = array(); + private $id; + + public function setID($id) { + $this->id = $id; + return $this; + } public function addEvent(PhabricatorTimelineEventView $event) { $this->events[] = $event; @@ -31,6 +37,7 @@ final class PhabricatorTimelineView extends AphrontView { 'div', array( 'class' => 'phabricator-timeline-view', + 'id' => $this->id, ), implode('', $events)); } diff --git a/webroot/rsrc/js/application/transactions/behavior-transaction-list.js b/webroot/rsrc/js/application/transactions/behavior-transaction-list.js new file mode 100644 index 0000000000..b683a5213e --- /dev/null +++ b/webroot/rsrc/js/application/transactions/behavior-transaction-list.js @@ -0,0 +1,51 @@ +/** + * @provides javelin-behavior-phabricator-transaction-list + * @requires javelin-behavior + * javelin-stratcom + * javelin-workflow + * javelin-dom + */ + +JX.behavior('phabricator-transaction-list', function(config) { + + var list = JX.$(config.listID); + var xaction_nodes = null; + + function get_xaction_nodes() { + if (xaction_nodes === null) { + xaction_nodes = {}; + var xactions = JX.DOM.scry(list, 'div', 'transaction'); + for (var ii = 0; ii < xactions.length; ii++) { + xaction_nodes[JX.Stratcom.getData(xactions[ii]).phid] = xactions[ii]; + } + } + return xaction_nodes; + } + + function ontransactions(response) { + var nodes = get_xaction_nodes(); + for (var phid in response.xactions) { + var new_node = JX.$H(response.xactions[phid]).getFragment().firstChild; + if (nodes[phid]) { + JX.DOM.replace(nodes[phid], new_node); + } else { + list.appendChild(new_node); + } + nodes[phid] = new_node; + } + } + + JX.DOM.listen(list, 'click', 'transaction-edit', function(e) { + if (!e.isNormalClick()) { + return; + } + + JX.Workflow.newFromLink(e.getTarget()) + .setData({anchor: e.getNodeData('transaction').anchor}) + .setHandler(ontransactions) + .start(); + + e.kill(); + }); + +});