diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index 3b03e33b30..6f40f55c33 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -607,6 +607,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionEditor' => 'applications/transactions/editor/PhabricatorApplicationTransactionEditor.php',
'PhabricatorApplicationTransactionFeedStory' => 'applications/transactions/feed/PhabricatorApplicationTransactionFeedStory.php',
'PhabricatorApplicationTransactionQuery' => 'applications/transactions/query/PhabricatorApplicationTransactionQuery.php',
+ 'PhabricatorApplicationTransactionResponse' => 'applications/transactions/response/PhabricatorApplicationTransactionResponse.php',
'PhabricatorApplicationTransactionView' => 'applications/transactions/view/PhabricatorApplicationTransactionView.php',
'PhabricatorApplicationTransactions' => 'applications/transactions/application/PhabricatorApplicationTransactions.php',
'PhabricatorApplicationUIExamples' => 'applications/uiexample/application/PhabricatorApplicationUIExamples.php',
@@ -1884,6 +1885,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionEditor' => 'PhabricatorEditor',
'PhabricatorApplicationTransactionFeedStory' => 'PhabricatorFeedStory',
'PhabricatorApplicationTransactionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
+ 'PhabricatorApplicationTransactionResponse' => 'AphrontProxyResponse',
'PhabricatorApplicationTransactionView' => 'AphrontView',
'PhabricatorApplicationTransactions' => 'PhabricatorApplication',
'PhabricatorApplicationUIExamples' => 'PhabricatorApplication',
diff --git a/src/applications/macro/controller/PhabricatorMacroCommentController.php b/src/applications/macro/controller/PhabricatorMacroCommentController.php
index 241348fdec..a52e720fb7 100644
--- a/src/applications/macro/controller/PhabricatorMacroCommentController.php
+++ b/src/applications/macro/controller/PhabricatorMacroCommentController.php
@@ -42,8 +42,15 @@ final class PhabricatorMacroCommentController
)))
->applyTransactions($macro, $xactions);
- return id(new AphrontRedirectResponse())
- ->setURI($view_uri);
+ if ($request->isAjax()) {
+ return id(new PhabricatorApplicationTransactionResponse())
+ ->setViewer($user)
+ ->setTransactions($xactions)
+ ->setAnchorOffset($request->getStr('anchor'));
+ } else {
+ return id(new AphrontRedirectResponse())
+ ->setURI($view_uri);
+ }
}
}
diff --git a/src/applications/macro/controller/PhabricatorMacroViewController.php b/src/applications/macro/controller/PhabricatorMacroViewController.php
index 82e54df157..30b417101a 100644
--- a/src/applications/macro/controller/PhabricatorMacroViewController.php
+++ b/src/applications/macro/controller/PhabricatorMacroViewController.php
@@ -82,6 +82,7 @@ final class PhabricatorMacroViewController
$add_comment_form = id(new AphrontFormView())
->setWorkflow(true)
->setFlexible(true)
+ ->addSigil('transaction-append')
->setAction($this->getApplicationURI('/comment/'.$macro->getID().'/'))
->setUser($user)
->appendChild(
diff --git a/src/applications/pholio/controller/PholioMockCommentController.php b/src/applications/pholio/controller/PholioMockCommentController.php
index 62a81906b1..950332af70 100644
--- a/src/applications/pholio/controller/PholioMockCommentController.php
+++ b/src/applications/pholio/controller/PholioMockCommentController.php
@@ -43,7 +43,8 @@ final class PholioMockCommentController extends PholioController {
'ip' => $request->getRemoteAddr(),
));
- $xaction = id(new PholioTransaction())
+ $xactions = array();
+ $xactions[] = id(new PholioTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->attachComment(
id(new PholioTransactionComment())
@@ -52,9 +53,16 @@ final class PholioMockCommentController extends PholioController {
id(new PholioMockEditor())
->setActor($user)
->setContentSource($content_source)
- ->applyTransactions($mock, array($xaction));
+ ->applyTransactions($mock, $xactions);
- return id(new AphrontRedirectResponse())->setURI($mock_uri);
+ if ($request->isAjax()) {
+ return id(new PhabricatorApplicationTransactionResponse())
+ ->setViewer($user)
+ ->setTransactions($xactions)
+ ->setAnchorOffset($request->getStr('anchor'));
+ } else {
+ return id(new AphrontRedirectResponse())->setURI($mock_uri);
+ }
}
}
diff --git a/src/applications/pholio/controller/PholioMockViewController.php b/src/applications/pholio/controller/PholioMockViewController.php
index 5a1bd4bfd6..07b870b55c 100644
--- a/src/applications/pholio/controller/PholioMockViewController.php
+++ b/src/applications/pholio/controller/PholioMockViewController.php
@@ -47,7 +47,7 @@ final class PholioMockViewController extends PholioController {
if ($xaction->getComment()) {
$engine->addObject(
$xaction->getComment(),
- PholioTransaction::MARKUP_FIELD_COMMENT);
+ PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
}
}
$engine->process();
@@ -64,7 +64,10 @@ final class PholioMockViewController extends PholioController {
'
'.
'Carousel Goes Here
';
- $xaction_view = $this->buildTransactionView($xactions, $engine);
+ $xaction_view = id(new PhabricatorApplicationTransactionView())
+ ->setViewer($this->getRequest()->getUser())
+ ->setTransactions($xactions)
+ ->setMarkupEngine($engine);
$add_comment = $this->buildAddCommentView($mock);
@@ -171,6 +174,7 @@ final class PholioMockViewController extends PholioController {
$form = id(new AphrontFormView())
->setUser($user)
+ ->addSigil('transaction-append')
->setAction($this->getApplicationURI('/comment/'.$mock->getID().'/'))
->setWorkflow(true)
->setFlexible(true)
@@ -189,42 +193,4 @@ final class PholioMockViewController extends PholioController {
);
}
- private function buildTransactionView(
- array $xactions,
- PhabricatorMarkupEngine $engine) {
- assert_instances_of($xactions, 'PholioTransaction');
-
- $view = new PhabricatorTimelineView();
-
- $anchor_name = 0;
- foreach ($xactions as $xaction) {
- if ($xaction->shouldHide()) {
- continue;
- }
-
- $anchor_name++;
-
- $event = id(new PhabricatorTimelineEventView())
- ->setViewer($this->getRequest()->getUser())
- ->setUserHandle($xaction->getHandle($xaction->getAuthorPHID()))
- ->setIcon($xaction->getIcon())
- ->setColor($xaction->getColor())
- ->setTitle($xaction->getTitle())
- ->setDateCreated($xaction->getDateCreated())
- ->setContentSource($xaction->getContentSource())
- ->setAnchor($anchor_name);
-
- if ($xaction->getComment()) {
- $event->appendChild(
- $engine->getOutput(
- $xaction->getComment(),
- PholioTransaction::MARKUP_FIELD_COMMENT));
- }
-
- $view->addEvent($event);
- }
-
- return $view;
- }
-
}
diff --git a/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentEditController.php b/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentEditController.php
index b67c833b4c..f89e274b5c 100644
--- a/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentEditController.php
+++ b/src/applications/transactions/controller/PhabricatorApplicationTransactionCommentEditController.php
@@ -58,22 +58,10 @@ final class PhabricatorApplicationTransactionCommentEditController
->applyEdit($xaction, $comment);
if ($request->isAjax()) {
- $view = id(new PhabricatorApplicationTransactionView())
+ return id(new PhabricatorApplicationTransactionResponse())
->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'),
- ));
+ ->setTransactions(array($xaction))
+ ->setAnchorOffset($request->getStr('anchor'));
} else {
return id(new AphrontReloadResponse())->setURI($obj_handle->getURI());
}
diff --git a/src/applications/transactions/response/PhabricatorApplicationTransactionResponse.php b/src/applications/transactions/response/PhabricatorApplicationTransactionResponse.php
new file mode 100644
index 0000000000..a0ff24345f
--- /dev/null
+++ b/src/applications/transactions/response/PhabricatorApplicationTransactionResponse.php
@@ -0,0 +1,65 @@
+anchorOffset = $anchor_offset;
+ return $this;
+ }
+
+ public function getAnchorOffset() {
+ return $this->anchorOffset;
+ }
+
+ public function setTransactions($transactions) {
+ assert_instances_of($transactions, 'PhabricatorApplicationTransaction');
+
+ $this->transactions = $transactions;
+ return $this;
+ }
+
+ public function getTransactions() {
+ return $this->transactions;
+ }
+
+ public function setViewer(PhabricatorUser $viewer) {
+ $this->viewer = $viewer;
+ return $this;
+ }
+
+ public function getViewer() {
+ return $this->viewer;
+ }
+
+ public function buildResponseString() {
+ $view = id(new PhabricatorApplicationTransactionView())
+ ->setViewer($this->getViewer())
+ ->setTransactions($this->getTransactions());
+
+ if ($this->getAnchorOffset()) {
+ $view->setAnchorOffset($this->getAnchorOffset());
+ }
+
+ $xactions = mpull($view->buildEvents(), 'render', 'getTransactionPHID');
+
+ $content = array(
+ 'xactions' => $xactions,
+ 'spacer' => PhabricatorTimelineView::renderSpacer(),
+ );
+
+ return $this
+ ->getProxy()
+ ->setContent($content)
+ ->buildResponseString();
+ }
+
+}
diff --git a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php
index b58aad2b64..df4bfda588 100644
--- a/src/applications/transactions/view/PhabricatorApplicationTransactionView.php
+++ b/src/applications/transactions/view/PhabricatorApplicationTransactionView.php
@@ -67,7 +67,6 @@ class PhabricatorApplicationTransactionView extends AphrontView {
$anchor++;
-
$has_deleted_comment = $xaction->getComment() &&
$xaction->getComment()->getIsDeleted();
@@ -105,7 +104,8 @@ class PhabricatorApplicationTransactionView extends AphrontView {
public function render() {
$view = new PhabricatorTimelineView();
- foreach ($this->buildEvents() as $event) {
+ $events = $this->buildEvents();
+ foreach ($events as $event) {
$view->addEvent($event);
}
@@ -117,7 +117,8 @@ class PhabricatorApplicationTransactionView extends AphrontView {
Javelin::initBehavior(
'phabricator-transaction-list',
array(
- 'listID' => $list_id,
+ 'listID' => $list_id,
+ 'nextAnchor' => $this->anchorOffset + count($events),
));
}
diff --git a/src/view/form/AphrontFormView.php b/src/view/form/AphrontFormView.php
index 0196b9c6ed..01a5e2d4e0 100644
--- a/src/view/form/AphrontFormView.php
+++ b/src/view/form/AphrontFormView.php
@@ -11,6 +11,7 @@ final class AphrontFormView extends AphrontView {
private $workflow;
private $id;
private $flexible;
+ private $sigils = array();
public function setFlexible($flexible) {
$this->flexible = $flexible;
@@ -52,6 +53,11 @@ final class AphrontFormView extends AphrontView {
return $this;
}
+ public function addSigil($sigil) {
+ $this->sigils[] = $sigil;
+ return $this;
+ }
+
public function render() {
if ($this->flexible) {
require_celerity_resource('phabricator-form-view-css');
@@ -76,6 +82,11 @@ final class AphrontFormView extends AphrontView {
throw new Exception('You must pass the user to AphrontFormView.');
}
+ $sigils = $this->sigils;
+ if ($this->workflow) {
+ $sigils[] = 'workflow';
+ }
+
return phabricator_render_form(
$this->user,
array(
@@ -83,7 +94,7 @@ final class AphrontFormView extends AphrontView {
'action' => $this->action,
'method' => $this->method,
'enctype' => $this->encType,
- 'sigil' => $this->workflow ? 'workflow' : null,
+ 'sigil' => $sigils ? implode(' ', $sigils) : null,
'id' => $this->id,
),
$layout->render());
diff --git a/src/view/layout/PhabricatorTimelineView.php b/src/view/layout/PhabricatorTimelineView.php
index eae7f527d6..4def7593ea 100644
--- a/src/view/layout/PhabricatorTimelineView.php
+++ b/src/view/layout/PhabricatorTimelineView.php
@@ -18,13 +18,7 @@ final class PhabricatorTimelineView extends AphrontView {
public function render() {
require_celerity_resource('phabricator-timeline-view-css');
- $spacer = phutil_render_tag(
- 'div',
- array(
- 'class' => 'phabricator-timeline-event-view '.
- 'phabricator-timeline-spacer',
- ),
- '');
+ $spacer = self::renderSpacer();
$events = array();
foreach ($this->events as $event) {
@@ -42,4 +36,13 @@ final class PhabricatorTimelineView extends AphrontView {
implode('', $events));
}
+ public static function renderSpacer() {
+ return phutil_render_tag(
+ 'div',
+ array(
+ 'class' => 'phabricator-timeline-event-view '.
+ 'phabricator-timeline-spacer',
+ ),
+ '');
+ }
}
diff --git a/webroot/rsrc/js/application/transactions/behavior-transaction-list.js b/webroot/rsrc/js/application/transactions/behavior-transaction-list.js
index b683a5213e..481b39b8b5 100644
--- a/webroot/rsrc/js/application/transactions/behavior-transaction-list.js
+++ b/webroot/rsrc/js/application/transactions/behavior-transaction-list.js
@@ -4,12 +4,14 @@
* javelin-stratcom
* javelin-workflow
* javelin-dom
+ * javelin-fx
*/
JX.behavior('phabricator-transaction-list', function(config) {
var list = JX.$(config.listID);
var xaction_nodes = null;
+ var next_anchor = config.nextAnchor;
function get_xaction_nodes() {
if (xaction_nodes === null) {
@@ -23,16 +25,41 @@ JX.behavior('phabricator-transaction-list', function(config) {
}
function ontransactions(response) {
+ var fade_in = [];
+ var first_new = null;
+
var nodes = get_xaction_nodes();
for (var phid in response.xactions) {
var new_node = JX.$H(response.xactions[phid]).getFragment().firstChild;
+ fade_in.push(new_node);
+
if (nodes[phid]) {
JX.DOM.replace(nodes[phid], new_node);
} else {
+ if (first_new === null) {
+ first_new = new_node;
+ }
list.appendChild(new_node);
+
+ // Add a spacer after new transactions.
+ var spacer = JX.$H(response.spacer).getFragment().firstChild;
+ list.appendChild(spacer);
+ fade_in.push(spacer);
+
+ next_anchor++;
}
nodes[phid] = new_node;
}
+
+ // Scroll to the first new transaction, if transactions were added.
+ if (first_new) {
+ JX.DOM.scrollTo(first_new);
+ }
+
+ // Make any new or updated transactions fade in.
+ for (var ii = 0; ii < fade_in.length; ii++) {
+ new JX.FX(fade_in[ii]).setDuration(500).start({opacity: [0, 1]});
+ }
}
JX.DOM.listen(list, 'click', 'transaction-edit', function(e) {
@@ -48,4 +75,17 @@ JX.behavior('phabricator-transaction-list', function(config) {
e.kill();
});
+ JX.Stratcom.listen('submit', 'transaction-append', function(e) {
+ var form = e.getTarget();
+
+ JX.Workflow.newFromForm(form, {anchor: next_anchor})
+ .setHandler(function(response) {
+ ontransactions(response);
+ form.reset();
+ })
+ .start();
+
+ e.kill();
+ });
+
});