mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 08:42:41 +01:00
Save drafts for inline comments currently being edited
Summary: Ref T13513. As users type text into inline comments, save the comment state as a draft on the server. This has some rough edges, particularly around previews, but mostly works. See T13513 for notes. Test Plan: Started an inline, typed some text, waited a second, reloaded the page, saw an editing inline with the saved text. Maniphest Tasks: T13513 Differential Revision: https://secure.phabricator.com/D21216
This commit is contained in:
parent
27b7ba814a
commit
fe501bd7f7
15 changed files with 283 additions and 51 deletions
|
@ -13,7 +13,7 @@ return array(
|
||||||
'core.pkg.js' => '632fb8f5',
|
'core.pkg.js' => '632fb8f5',
|
||||||
'dark-console.pkg.js' => '187792c2',
|
'dark-console.pkg.js' => '187792c2',
|
||||||
'differential.pkg.css' => '2d70b7b9',
|
'differential.pkg.css' => '2d70b7b9',
|
||||||
'differential.pkg.js' => '4287e51f',
|
'differential.pkg.js' => '4d375e61',
|
||||||
'diffusion.pkg.css' => '42c75c37',
|
'diffusion.pkg.css' => '42c75c37',
|
||||||
'diffusion.pkg.js' => 'a98c0bf7',
|
'diffusion.pkg.js' => 'a98c0bf7',
|
||||||
'maniphest.pkg.css' => '35995d6d',
|
'maniphest.pkg.css' => '35995d6d',
|
||||||
|
@ -380,8 +380,8 @@ return array(
|
||||||
'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '1e413dc9',
|
'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '1e413dc9',
|
||||||
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => '0116d3e8',
|
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => '0116d3e8',
|
||||||
'rsrc/js/application/diff/DiffChangeset.js' => 'a49dc31e',
|
'rsrc/js/application/diff/DiffChangeset.js' => 'a49dc31e',
|
||||||
'rsrc/js/application/diff/DiffChangesetList.js' => '10726e6a',
|
'rsrc/js/application/diff/DiffChangesetList.js' => '6992b85c',
|
||||||
'rsrc/js/application/diff/DiffInline.js' => '7f804f2b',
|
'rsrc/js/application/diff/DiffInline.js' => 'a39fd98e',
|
||||||
'rsrc/js/application/diff/DiffPathView.js' => '8207abf9',
|
'rsrc/js/application/diff/DiffPathView.js' => '8207abf9',
|
||||||
'rsrc/js/application/diff/DiffTreeView.js' => '5d83623b',
|
'rsrc/js/application/diff/DiffTreeView.js' => '5d83623b',
|
||||||
'rsrc/js/application/diff/behavior-preview-link.js' => 'f51e9c17',
|
'rsrc/js/application/diff/behavior-preview-link.js' => 'f51e9c17',
|
||||||
|
@ -462,7 +462,7 @@ return array(
|
||||||
'rsrc/js/core/MultirowRowManager.js' => '5b54c823',
|
'rsrc/js/core/MultirowRowManager.js' => '5b54c823',
|
||||||
'rsrc/js/core/Notification.js' => 'a9b91e3f',
|
'rsrc/js/core/Notification.js' => 'a9b91e3f',
|
||||||
'rsrc/js/core/Prefab.js' => '5793d835',
|
'rsrc/js/core/Prefab.js' => '5793d835',
|
||||||
'rsrc/js/core/ShapedRequest.js' => 'abf88db8',
|
'rsrc/js/core/ShapedRequest.js' => '995f5102',
|
||||||
'rsrc/js/core/TextAreaUtils.js' => 'f340a484',
|
'rsrc/js/core/TextAreaUtils.js' => 'f340a484',
|
||||||
'rsrc/js/core/Title.js' => '43bc9360',
|
'rsrc/js/core/Title.js' => '43bc9360',
|
||||||
'rsrc/js/core/ToolTip.js' => '83754533',
|
'rsrc/js/core/ToolTip.js' => '83754533',
|
||||||
|
@ -777,8 +777,8 @@ return array(
|
||||||
'phabricator-darkmessage' => '26cd4b73',
|
'phabricator-darkmessage' => '26cd4b73',
|
||||||
'phabricator-dashboard-css' => '5a205b9d',
|
'phabricator-dashboard-css' => '5a205b9d',
|
||||||
'phabricator-diff-changeset' => 'a49dc31e',
|
'phabricator-diff-changeset' => 'a49dc31e',
|
||||||
'phabricator-diff-changeset-list' => '10726e6a',
|
'phabricator-diff-changeset-list' => '6992b85c',
|
||||||
'phabricator-diff-inline' => '7f804f2b',
|
'phabricator-diff-inline' => 'a39fd98e',
|
||||||
'phabricator-diff-path-view' => '8207abf9',
|
'phabricator-diff-path-view' => '8207abf9',
|
||||||
'phabricator-diff-tree-view' => '5d83623b',
|
'phabricator-diff-tree-view' => '5d83623b',
|
||||||
'phabricator-drag-and-drop-file-upload' => '4370900d',
|
'phabricator-drag-and-drop-file-upload' => '4370900d',
|
||||||
|
@ -800,7 +800,7 @@ return array(
|
||||||
'phabricator-prefab' => '5793d835',
|
'phabricator-prefab' => '5793d835',
|
||||||
'phabricator-remarkup-css' => 'c286eaef',
|
'phabricator-remarkup-css' => 'c286eaef',
|
||||||
'phabricator-search-results-css' => '9ea70ace',
|
'phabricator-search-results-css' => '9ea70ace',
|
||||||
'phabricator-shaped-request' => 'abf88db8',
|
'phabricator-shaped-request' => '995f5102',
|
||||||
'phabricator-slowvote-css' => '1694baed',
|
'phabricator-slowvote-css' => '1694baed',
|
||||||
'phabricator-source-code-view-css' => '03d7ac28',
|
'phabricator-source-code-view-css' => '03d7ac28',
|
||||||
'phabricator-standard-page-view' => 'a374f94c',
|
'phabricator-standard-page-view' => 'a374f94c',
|
||||||
|
@ -1022,11 +1022,6 @@ return array(
|
||||||
'javelin-workflow',
|
'javelin-workflow',
|
||||||
'phuix-icon-view',
|
'phuix-icon-view',
|
||||||
),
|
),
|
||||||
'10726e6a' => array(
|
|
||||||
'javelin-install',
|
|
||||||
'phuix-button-view',
|
|
||||||
'phabricator-diff-tree-view',
|
|
||||||
),
|
|
||||||
'111bfd2d' => array(
|
'111bfd2d' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
),
|
),
|
||||||
|
@ -1519,6 +1514,11 @@ return array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
),
|
),
|
||||||
|
'6992b85c' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'phuix-button-view',
|
||||||
|
'phabricator-diff-tree-view',
|
||||||
|
),
|
||||||
'6a1583a8' => array(
|
'6a1583a8' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-history',
|
'javelin-history',
|
||||||
|
@ -1626,9 +1626,6 @@ return array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
),
|
),
|
||||||
'7f804f2b' => array(
|
|
||||||
'javelin-dom',
|
|
||||||
),
|
|
||||||
'80bff3af' => array(
|
'80bff3af' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-typeahead-source',
|
'javelin-typeahead-source',
|
||||||
|
@ -1797,6 +1794,12 @@ return array(
|
||||||
'javelin-request',
|
'javelin-request',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
),
|
),
|
||||||
|
'995f5102' => array(
|
||||||
|
'javelin-install',
|
||||||
|
'javelin-util',
|
||||||
|
'javelin-request',
|
||||||
|
'javelin-router',
|
||||||
|
),
|
||||||
'9aae2b66' => array(
|
'9aae2b66' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-util',
|
'javelin-util',
|
||||||
|
@ -1838,6 +1841,9 @@ return array(
|
||||||
'javelin-workflow',
|
'javelin-workflow',
|
||||||
'phabricator-draggable-list',
|
'phabricator-draggable-list',
|
||||||
),
|
),
|
||||||
|
'a39fd98e' => array(
|
||||||
|
'javelin-dom',
|
||||||
|
),
|
||||||
'a4356cde' => array(
|
'a4356cde' => array(
|
||||||
'javelin-install',
|
'javelin-install',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
@ -1916,12 +1922,6 @@ return array(
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
'phabricator-notification',
|
'phabricator-notification',
|
||||||
),
|
),
|
||||||
'abf88db8' => array(
|
|
||||||
'javelin-install',
|
|
||||||
'javelin-util',
|
|
||||||
'javelin-request',
|
|
||||||
'javelin-router',
|
|
||||||
),
|
|
||||||
'ad258e28' => array(
|
'ad258e28' => array(
|
||||||
'javelin-behavior',
|
'javelin-behavior',
|
||||||
'javelin-dom',
|
'javelin-dom',
|
||||||
|
|
|
@ -105,7 +105,13 @@ final class PhabricatorAuditEditor
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
case PhabricatorAuditActionConstants::INLINE:
|
case PhabricatorAuditActionConstants::INLINE:
|
||||||
$xaction->getComment()->setAttribute('editing', false);
|
$comment = $xaction->getComment();
|
||||||
|
|
||||||
|
$comment->setAttribute('editing', false);
|
||||||
|
|
||||||
|
PhabricatorVersionedDraft::purgeDrafts(
|
||||||
|
$comment->getPHID(),
|
||||||
|
$this->getActingAsPHID());
|
||||||
return;
|
return;
|
||||||
case PhabricatorAuditTransaction::TYPE_COMMIT:
|
case PhabricatorAuditTransaction::TYPE_COMMIT:
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -111,13 +111,6 @@ final class PhabricatorAuditInlineComment
|
||||||
$viewer->getPHID());
|
$viewer->getPHID());
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($inlines as $key => $inline) {
|
|
||||||
$is_draft = !$inline->getTransactionPHID();
|
|
||||||
if ($is_draft && $inline->isEmptyInlineComment()) {
|
|
||||||
unset($inlines[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::buildProxies($inlines);
|
return self::buildProxies($inlines);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -197,7 +197,6 @@ final class DifferentialChangesetViewController extends DifferentialController {
|
||||||
$query = id(new DifferentialInlineCommentQuery())
|
$query = id(new DifferentialInlineCommentQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->needHidden(true)
|
->needHidden(true)
|
||||||
->withEmptyInlineComments(false)
|
|
||||||
->withRevisionPHIDs(array($revision->getPHID()));
|
->withRevisionPHIDs(array($revision->getPHID()));
|
||||||
$inlines = $query->execute();
|
$inlines = $query->execute();
|
||||||
$inlines = $query->adjustInlinesForChangesets(
|
$inlines = $query->adjustInlinesForChangesets(
|
||||||
|
|
|
@ -112,7 +112,13 @@ final class DifferentialTransactionEditor
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
case DifferentialTransaction::TYPE_INLINE:
|
case DifferentialTransaction::TYPE_INLINE:
|
||||||
$xaction->getComment()->setAttribute('editing', false);
|
$comment = $xaction->getComment();
|
||||||
|
|
||||||
|
$comment->setAttribute('editing', false);
|
||||||
|
|
||||||
|
PhabricatorVersionedDraft::purgeDrafts(
|
||||||
|
$comment->getPHID(),
|
||||||
|
$this->getActingAsPHID());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -752,6 +752,8 @@ final class DifferentialChangesetParser extends Phobject {
|
||||||
$range_len = null,
|
$range_len = null,
|
||||||
$mask_force = array()) {
|
$mask_force = array()) {
|
||||||
|
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$renderer = $this->getRenderer();
|
$renderer = $this->getRenderer();
|
||||||
if (!$renderer) {
|
if (!$renderer) {
|
||||||
$renderer = $this->newRenderer();
|
$renderer = $this->newRenderer();
|
||||||
|
@ -853,6 +855,16 @@ final class DifferentialChangesetParser extends Phobject {
|
||||||
|
|
||||||
$has_document_engine = ($engine_blocks !== null);
|
$has_document_engine = ($engine_blocks !== null);
|
||||||
|
|
||||||
|
// Remove empty comments that don't have any unsaved draft data.
|
||||||
|
PhabricatorInlineComment::loadAndAttachVersionedDrafts(
|
||||||
|
$viewer,
|
||||||
|
$this->comments);
|
||||||
|
foreach ($this->comments as $key => $comment) {
|
||||||
|
if ($comment->isVoidComment($viewer)) {
|
||||||
|
unset($this->comments[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// See T13515. Sometimes, we collapse file content by default: for
|
// See T13515. Sometimes, we collapse file content by default: for
|
||||||
// example, if the file is marked as containing generated code.
|
// example, if the file is marked as containing generated code.
|
||||||
|
|
||||||
|
@ -1050,6 +1062,7 @@ final class DifferentialChangesetParser extends Phobject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$renderer
|
$renderer
|
||||||
->setOldComments($old_comments)
|
->setOldComments($old_comments)
|
||||||
->setNewComments($new_comments);
|
->setNewComments($new_comments);
|
||||||
|
|
|
@ -16,7 +16,6 @@ final class DifferentialInlineCommentQuery
|
||||||
private $revisionPHIDs;
|
private $revisionPHIDs;
|
||||||
private $deletedDrafts;
|
private $deletedDrafts;
|
||||||
private $needHidden;
|
private $needHidden;
|
||||||
private $withEmpty;
|
|
||||||
|
|
||||||
public function setViewer(PhabricatorUser $viewer) {
|
public function setViewer(PhabricatorUser $viewer) {
|
||||||
$this->viewer = $viewer;
|
$this->viewer = $viewer;
|
||||||
|
@ -62,11 +61,6 @@ final class DifferentialInlineCommentQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function withEmptyInlineComments($empty) {
|
|
||||||
$this->withEmpty = $empty;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function execute() {
|
public function execute() {
|
||||||
$table = new DifferentialTransactionComment();
|
$table = new DifferentialTransactionComment();
|
||||||
$conn_r = $table->establishConnection('r');
|
$conn_r = $table->establishConnection('r');
|
||||||
|
@ -80,15 +74,6 @@ final class DifferentialInlineCommentQuery
|
||||||
|
|
||||||
$comments = $table->loadAllFromArray($data);
|
$comments = $table->loadAllFromArray($data);
|
||||||
|
|
||||||
if ($this->withEmpty !== null) {
|
|
||||||
$want_empty = (bool)$this->withEmpty;
|
|
||||||
foreach ($comments as $key => $value) {
|
|
||||||
if ($value->isEmptyInlineComment() !== $want_empty) {
|
|
||||||
unset($comments[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->needHidden) {
|
if ($this->needHidden) {
|
||||||
$viewer_phid = $this->getViewer()->getPHID();
|
$viewer_phid = $this->getViewer()->getPHID();
|
||||||
if ($viewer_phid && $comments) {
|
if ($viewer_phid && $comments) {
|
||||||
|
|
|
@ -20,13 +20,24 @@ final class DifferentialTransactionQuery
|
||||||
->needReplyToComments(true)
|
->needReplyToComments(true)
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
// Don't count empty inlines when considering draft state.
|
|
||||||
foreach ($inlines as $key => $inline) {
|
foreach ($inlines as $key => $inline) {
|
||||||
if ($inline->isEmptyInlineComment()) {
|
$inlines[$key] = DifferentialInlineComment::newFromModernComment(
|
||||||
|
$inline);
|
||||||
|
}
|
||||||
|
|
||||||
|
PhabricatorInlineComment::loadAndAttachVersionedDrafts(
|
||||||
|
$viewer,
|
||||||
|
$inlines);
|
||||||
|
|
||||||
|
// Don't count void inlines when considering draft state.
|
||||||
|
foreach ($inlines as $key => $inline) {
|
||||||
|
if ($inline->isVoidComment($viewer)) {
|
||||||
unset($inlines[$key]);
|
unset($inlines[$key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$inlines = mpull($inlines, 'getStorageObject');
|
||||||
|
|
||||||
return $inlines;
|
return $inlines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,23 @@ final class PhabricatorVersionedDraft extends PhabricatorDraftDAO {
|
||||||
return idx($this->properties, $key, $default);
|
return idx($this->properties, $key, $default);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function loadDrafts(
|
||||||
|
array $object_phids,
|
||||||
|
$viewer_phid) {
|
||||||
|
|
||||||
|
$rows = id(new self())->loadAllWhere(
|
||||||
|
'objectPHID IN (%Ls) AND authorPHID = %s ORDER BY version ASC',
|
||||||
|
$object_phids,
|
||||||
|
$viewer_phid);
|
||||||
|
|
||||||
|
$map = array();
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$map[$row->getObjectPHID()] = $row;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $map;
|
||||||
|
}
|
||||||
|
|
||||||
public static function loadDraft(
|
public static function loadDraft(
|
||||||
$object_phid,
|
$object_phid,
|
||||||
$viewer_phid) {
|
$viewer_phid) {
|
||||||
|
|
|
@ -192,11 +192,15 @@ abstract class PhabricatorInlineCommentController
|
||||||
->setIsEditing(false);
|
->setIsEditing(false);
|
||||||
|
|
||||||
$this->saveComment($inline);
|
$this->saveComment($inline);
|
||||||
|
$this->purgeVersionedDrafts($inline);
|
||||||
|
|
||||||
return $this->buildRenderedCommentResponse(
|
return $this->buildRenderedCommentResponse(
|
||||||
$inline,
|
$inline,
|
||||||
$this->getIsOnRight());
|
$this->getIsOnRight());
|
||||||
} else {
|
} else {
|
||||||
$this->deleteComment($inline);
|
$this->deleteComment($inline);
|
||||||
|
$this->purgeVersionedDrafts($inline);
|
||||||
|
|
||||||
return $this->buildEmptyResponse();
|
return $this->buildEmptyResponse();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -235,6 +239,23 @@ abstract class PhabricatorInlineCommentController
|
||||||
$this->saveComment($inline);
|
$this->saveComment($inline);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->purgeVersionedDrafts($inline);
|
||||||
|
|
||||||
|
return $this->buildEmptyResponse();
|
||||||
|
case 'draft':
|
||||||
|
$inline = $this->loadCommentForEdit($this->getCommentID());
|
||||||
|
|
||||||
|
$versioned_draft = PhabricatorVersionedDraft::loadOrCreateDraft(
|
||||||
|
$inline->getPHID(),
|
||||||
|
$viewer->getPHID(),
|
||||||
|
$inline->getID());
|
||||||
|
|
||||||
|
$text = $this->getCommentText();
|
||||||
|
|
||||||
|
$versioned_draft
|
||||||
|
->setProperty('inline.text', $text)
|
||||||
|
->save();
|
||||||
|
|
||||||
return $this->buildEmptyResponse();
|
return $this->buildEmptyResponse();
|
||||||
case 'new':
|
case 'new':
|
||||||
case 'reply':
|
case 'reply':
|
||||||
|
@ -405,4 +426,13 @@ abstract class PhabricatorInlineCommentController
|
||||||
->setContent($response);
|
->setContent($response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function purgeVersionedDrafts(
|
||||||
|
PhabricatorInlineComment $inline) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
PhabricatorVersionedDraft::purgeDrafts(
|
||||||
|
$inline->getPHID(),
|
||||||
|
$viewer->getPHID());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,11 +15,51 @@ abstract class PhabricatorInlineComment
|
||||||
private $storageObject;
|
private $storageObject;
|
||||||
private $syntheticAuthor;
|
private $syntheticAuthor;
|
||||||
private $isGhost;
|
private $isGhost;
|
||||||
|
private $versionedDrafts = array();
|
||||||
|
|
||||||
public function __clone() {
|
public function __clone() {
|
||||||
$this->storageObject = clone $this->storageObject;
|
$this->storageObject = clone $this->storageObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final public static function loadAndAttachVersionedDrafts(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
array $inlines) {
|
||||||
|
|
||||||
|
$viewer_phid = $viewer->getPHID();
|
||||||
|
if (!$viewer_phid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$inlines = mpull($inlines, null, 'getPHID');
|
||||||
|
|
||||||
|
$load = array();
|
||||||
|
foreach ($inlines as $key => $inline) {
|
||||||
|
if (!$inline->getIsEditing()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($inline->getAuthorPHID() !== $viewer_phid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$load[$key] = $inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$load) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$drafts = PhabricatorVersionedDraft::loadDrafts(
|
||||||
|
array_keys($load),
|
||||||
|
$viewer_phid);
|
||||||
|
|
||||||
|
$drafts = mpull($drafts, null, 'getObjectPHID');
|
||||||
|
foreach ($inlines as $inline) {
|
||||||
|
$draft = idx($drafts, $inline->getPHID());
|
||||||
|
$inline->attachVersionedDraftForViewer($viewer, $draft);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function setSyntheticAuthor($synthetic_author) {
|
public function setSyntheticAuthor($synthetic_author) {
|
||||||
$this->syntheticAuthor = $synthetic_author;
|
$this->syntheticAuthor = $synthetic_author;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -204,6 +244,57 @@ abstract class PhabricatorInlineComment
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function attachVersionedDraftForViewer(
|
||||||
|
PhabricatorUser $viewer,
|
||||||
|
PhabricatorVersionedDraft $draft = null) {
|
||||||
|
|
||||||
|
$key = $viewer->getCacheFragment();
|
||||||
|
$this->versionedDrafts[$key] = $draft;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasVersionedDraftForViewer(PhabricatorUser $viewer) {
|
||||||
|
$key = $viewer->getCacheFragment();
|
||||||
|
return array_key_exists($key, $this->versionedDrafts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getVersionedDraftForViewer(PhabricatorUser $viewer) {
|
||||||
|
$key = $viewer->getCacheFragment();
|
||||||
|
if (!array_key_exists($key, $this->versionedDrafts)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Versioned draft is not attached for user with fragment "%s".',
|
||||||
|
$key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->versionedDrafts[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isVoidComment(PhabricatorUser $viewer) {
|
||||||
|
return !strlen($this->getContentForEdit($viewer));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContentForEdit(PhabricatorUser $viewer) {
|
||||||
|
$content = $this->getContent();
|
||||||
|
|
||||||
|
if (!$this->hasVersionedDraftForViewer($viewer)) {
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
$versioned_draft = $this->getVersionedDraftForViewer($viewer);
|
||||||
|
if (!$versioned_draft) {
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
$draft_text = $versioned_draft->getProperty('inline.text');
|
||||||
|
if ($draft_text === null) {
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $draft_text;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorMarkupInterface Implementation )-------------------------- */
|
/* -( PhabricatorMarkupInterface Implementation )-------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,7 @@ abstract class PHUIDiffInlineCommentView extends AphrontView {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getInlineCommentMetadata() {
|
protected function getInlineCommentMetadata() {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
$inline = $this->getInlineComment();
|
$inline = $this->getInlineComment();
|
||||||
|
|
||||||
$is_synthetic = (bool)$inline->getSyntheticAuthor();
|
$is_synthetic = (bool)$inline->getSyntheticAuthor();
|
||||||
|
@ -74,6 +75,8 @@ abstract class PHUIDiffInlineCommentView extends AphrontView {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$original_text = $inline->getContentForEdit($viewer);
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
'id' => $inline->getID(),
|
'id' => $inline->getID(),
|
||||||
'phid' => $inline->getPHID(),
|
'phid' => $inline->getPHID(),
|
||||||
|
@ -81,7 +84,7 @@ abstract class PHUIDiffInlineCommentView extends AphrontView {
|
||||||
'number' => $inline->getLineNumber(),
|
'number' => $inline->getLineNumber(),
|
||||||
'length' => $inline->getLineLength(),
|
'length' => $inline->getLineLength(),
|
||||||
'isNewFile' => (bool)$inline->getIsNewFile(),
|
'isNewFile' => (bool)$inline->getIsNewFile(),
|
||||||
'original' => $inline->getContent(),
|
'original' => $original_text,
|
||||||
'replyToCommentPHID' => $inline->getReplyToCommentPHID(),
|
'replyToCommentPHID' => $inline->getReplyToCommentPHID(),
|
||||||
'isDraft' => $inline->isDraft(),
|
'isDraft' => $inline->isDraft(),
|
||||||
'isFixed' => $is_fixed,
|
'isFixed' => $is_fixed,
|
||||||
|
|
|
@ -2110,6 +2110,13 @@ JX.install('DiffChangesetList', {
|
||||||
'click',
|
'click',
|
||||||
['differential-inline-comment', 'differential-inline-reply'],
|
['differential-inline-comment', 'differential-inline-reply'],
|
||||||
onreply);
|
onreply);
|
||||||
|
|
||||||
|
var ondraft = JX.bind(this, this._onInlineEvent, 'draft');
|
||||||
|
JX.Stratcom.listen(
|
||||||
|
'keydown',
|
||||||
|
['differential-inline-comment', 'tag:textarea'],
|
||||||
|
ondraft);
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_onInlineEvent: function(action, e) {
|
_onInlineEvent: function(action, e) {
|
||||||
|
@ -2117,7 +2124,9 @@ JX.install('DiffChangesetList', {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
e.kill();
|
if (action !== 'draft') {
|
||||||
|
e.kill();
|
||||||
|
}
|
||||||
|
|
||||||
var inline = this._getInlineForEvent(e);
|
var inline = this._getInlineForEvent(e);
|
||||||
var is_ref = false;
|
var is_ref = false;
|
||||||
|
@ -2172,6 +2181,9 @@ JX.install('DiffChangesetList', {
|
||||||
case 'reply':
|
case 'reply':
|
||||||
inline.reply();
|
inline.reply();
|
||||||
break;
|
break;
|
||||||
|
case 'draft':
|
||||||
|
inline.triggerDraft();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,8 @@ JX.install('DiffInline', {
|
||||||
_undoType: null,
|
_undoType: null,
|
||||||
_undoText: null,
|
_undoText: null,
|
||||||
|
|
||||||
|
_draftRequest: null,
|
||||||
|
|
||||||
bindToRow: function(row) {
|
bindToRow: function(row) {
|
||||||
this._row = row;
|
this._row = row;
|
||||||
|
|
||||||
|
@ -89,11 +91,17 @@ JX.install('DiffInline', {
|
||||||
this._isEditing = data.isEditing;
|
this._isEditing = data.isEditing;
|
||||||
|
|
||||||
if (this._isEditing) {
|
if (this._isEditing) {
|
||||||
this.edit();
|
// NOTE: The "original" shipped down in the DOM may reflect a draft
|
||||||
|
// which we're currently editing. This flow is a little clumsy, but
|
||||||
|
// reasonable until some future change moves away from "send down
|
||||||
|
// the inline, then immediately click edit".
|
||||||
|
this.edit(this._originalText);
|
||||||
} else {
|
} else {
|
||||||
this.setInvisible(false);
|
this.setInvisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._startDrafts();
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -153,6 +161,7 @@ JX.install('DiffInline', {
|
||||||
parent_row.parentNode.insertBefore(row, target_row);
|
parent_row.parentNode.insertBefore(row, target_row);
|
||||||
|
|
||||||
this.setInvisible(true);
|
this.setInvisible(true);
|
||||||
|
this._startDrafts();
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
@ -213,6 +222,7 @@ JX.install('DiffInline', {
|
||||||
parent_row.parentNode.insertBefore(row, target_row);
|
parent_row.parentNode.insertBefore(row, target_row);
|
||||||
|
|
||||||
this.setInvisible(true);
|
this.setInvisible(true);
|
||||||
|
this._startDrafts();
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
@ -795,7 +805,59 @@ JX.install('DiffInline', {
|
||||||
var changeset = this.getChangeset();
|
var changeset = this.getChangeset();
|
||||||
var list = changeset.getChangesetList();
|
var list = changeset.getChangesetList();
|
||||||
return list.getInlineURI();
|
return list.getInlineURI();
|
||||||
|
},
|
||||||
|
|
||||||
|
_startDrafts: function() {
|
||||||
|
if (this._draftRequest) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var onresponse = JX.bind(this, this._onDraftResponse);
|
||||||
|
var draft = JX.bind(this, this._getDraftState);
|
||||||
|
|
||||||
|
var uri = this._getInlineURI();
|
||||||
|
var request = new JX.PhabricatorShapedRequest(uri, onresponse, draft);
|
||||||
|
|
||||||
|
// The main transaction code uses a 500ms delay on desktop and a
|
||||||
|
// 10s delay on mobile. Perhaps this should be standardized.
|
||||||
|
request.setRateLimit(2000);
|
||||||
|
|
||||||
|
this._draftRequest = request;
|
||||||
|
|
||||||
|
request.start();
|
||||||
|
},
|
||||||
|
|
||||||
|
_onDraftResponse: function() {
|
||||||
|
// For now, do nothing.
|
||||||
|
},
|
||||||
|
|
||||||
|
_getDraftState: function() {
|
||||||
|
if (this.isDeleted()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.isEditing()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var text = this._readText(this._editRow);
|
||||||
|
if (text === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
op: 'draft',
|
||||||
|
id: this.getID(),
|
||||||
|
text: text
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
triggerDraft: function() {
|
||||||
|
if (this._draftRequest) {
|
||||||
|
this._draftRequest.trigger();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -81,6 +81,10 @@ JX.install('PhabricatorShapedRequest', {
|
||||||
},
|
},
|
||||||
|
|
||||||
shouldSendRequest : function(last, data) {
|
shouldSendRequest : function(last, data) {
|
||||||
|
if (data === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (last === null) {
|
if (last === null) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue