mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-22 13:30:55 +01:00
Formally track "initial", "committed", and "active" states for inline comments
Summary: Ref T13559. Various client decisions depend on the "initial" or "committed" states of inline comments. Previously, these were informally constructed from "mostly similar" available values, or glossed over in some cases. On the server, save the initial state when creating a comment. Save the committed state when applying a "save" operation. Send all three states to the client. On the client, load and track all three states explicitly. Test Plan: Created inlines, etc. See followups. Maniphest Tasks: T13559 Differential Revision: https://secure.phabricator.com/D21653
This commit is contained in:
parent
b75517918d
commit
6fd55d692f
5 changed files with 163 additions and 76 deletions
|
@ -13,7 +13,7 @@ return array(
|
|||
'core.pkg.js' => '68f29322',
|
||||
'dark-console.pkg.js' => '187792c2',
|
||||
'differential.pkg.css' => 'ffb69e3d',
|
||||
'differential.pkg.js' => 'fbde899f',
|
||||
'differential.pkg.js' => '59453886',
|
||||
'diffusion.pkg.css' => '42c75c37',
|
||||
'diffusion.pkg.js' => '78c9885d',
|
||||
'maniphest.pkg.css' => '35995d6d',
|
||||
|
@ -385,7 +385,7 @@ return array(
|
|||
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => '0116d3e8',
|
||||
'rsrc/js/application/diff/DiffChangeset.js' => 'd7d3ba75',
|
||||
'rsrc/js/application/diff/DiffChangesetList.js' => 'cc2c5de5',
|
||||
'rsrc/js/application/diff/DiffInline.js' => '62fff8eb',
|
||||
'rsrc/js/application/diff/DiffInline.js' => '26664c24',
|
||||
'rsrc/js/application/diff/DiffInlineContentState.js' => '68e6339d',
|
||||
'rsrc/js/application/diff/DiffPathView.js' => '8207abf9',
|
||||
'rsrc/js/application/diff/DiffTreeView.js' => '5d83623b',
|
||||
|
@ -788,7 +788,7 @@ return array(
|
|||
'phabricator-dashboard-css' => '5a205b9d',
|
||||
'phabricator-diff-changeset' => 'd7d3ba75',
|
||||
'phabricator-diff-changeset-list' => 'cc2c5de5',
|
||||
'phabricator-diff-inline' => '62fff8eb',
|
||||
'phabricator-diff-inline' => '26664c24',
|
||||
'phabricator-diff-inline-content-state' => '68e6339d',
|
||||
'phabricator-diff-path-view' => '8207abf9',
|
||||
'phabricator-diff-tree-view' => '5d83623b',
|
||||
|
@ -1162,6 +1162,10 @@ return array(
|
|||
'javelin-json',
|
||||
'phabricator-prefab',
|
||||
),
|
||||
'26664c24' => array(
|
||||
'javelin-dom',
|
||||
'phabricator-diff-inline-content-state',
|
||||
),
|
||||
'289bf236' => array(
|
||||
'javelin-install',
|
||||
'javelin-util',
|
||||
|
@ -1532,10 +1536,6 @@ return array(
|
|||
'javelin-request',
|
||||
'javelin-uri',
|
||||
),
|
||||
'62fff8eb' => array(
|
||||
'javelin-dom',
|
||||
'phabricator-diff-inline-content-state',
|
||||
),
|
||||
'65bb0011' => array(
|
||||
'javelin-behavior',
|
||||
'javelin-dom',
|
||||
|
|
|
@ -180,56 +180,60 @@ abstract class PhabricatorInlineCommentController
|
|||
$this->saveComment($inline);
|
||||
|
||||
return $this->buildEmptyResponse();
|
||||
case 'edit':
|
||||
case 'save':
|
||||
$inline = $this->loadCommentByIDForEdit($this->getCommentID());
|
||||
if ($op === 'save') {
|
||||
$this->updateCommentContentState($inline);
|
||||
|
||||
$this->updateCommentContentState($inline);
|
||||
|
||||
$inline
|
||||
->setIsEditing(false)
|
||||
->setIsDeleted(0);
|
||||
|
||||
// Since we're saving the comment, update the committed state.
|
||||
$active_state = $inline->getContentState();
|
||||
$inline->setCommittedContentState($active_state);
|
||||
|
||||
$this->saveComment($inline);
|
||||
|
||||
return $this->buildRenderedCommentResponse(
|
||||
$inline,
|
||||
$this->getIsOnRight());
|
||||
case 'edit':
|
||||
$inline = $this->loadCommentByIDForEdit($this->getCommentID());
|
||||
|
||||
// NOTE: At time of writing, the "editing" state of inlines is
|
||||
// preserved by simulating a click on "Edit" when the inline loads.
|
||||
|
||||
// In this case, we don't want to "saveComment()", because it
|
||||
// recalculates object drafts and purges versioned drafts.
|
||||
|
||||
// The recalculation is merely unnecessary (state doesn't change)
|
||||
// but purging drafts means that loading a page and then closing it
|
||||
// discards your drafts.
|
||||
|
||||
// To avoid the purge, only invoke "saveComment()" if we actually
|
||||
// have changes to apply.
|
||||
|
||||
$is_dirty = false;
|
||||
if (!$inline->getIsEditing()) {
|
||||
$inline
|
||||
->setIsEditing(false)
|
||||
->setIsDeleted(0);
|
||||
->setIsDeleted(0)
|
||||
->setIsEditing(true);
|
||||
|
||||
$this->saveComment($inline);
|
||||
$is_dirty = true;
|
||||
}
|
||||
|
||||
return $this->buildRenderedCommentResponse(
|
||||
$inline,
|
||||
$this->getIsOnRight());
|
||||
if ($this->hasContentState()) {
|
||||
$this->updateCommentContentState($inline);
|
||||
$is_dirty = true;
|
||||
} else {
|
||||
// NOTE: At time of writing, the "editing" state of inlines is
|
||||
// preserved by simulating a click on "Edit" when the inline loads.
|
||||
PhabricatorInlineComment::loadAndAttachVersionedDrafts(
|
||||
$viewer,
|
||||
array($inline));
|
||||
}
|
||||
|
||||
// In this case, we don't want to "saveComment()", because it
|
||||
// recalculates object drafts and purges versioned drafts.
|
||||
|
||||
// The recalculation is merely unnecessary (state doesn't change)
|
||||
// but purging drafts means that loading a page and then closing it
|
||||
// discards your drafts.
|
||||
|
||||
// To avoid the purge, only invoke "saveComment()" if we actually
|
||||
// have changes to apply.
|
||||
|
||||
$is_dirty = false;
|
||||
if (!$inline->getIsEditing()) {
|
||||
$inline
|
||||
->setIsDeleted(0)
|
||||
->setIsEditing(true);
|
||||
|
||||
$is_dirty = true;
|
||||
}
|
||||
|
||||
if ($this->hasContentState()) {
|
||||
$this->updateCommentContentState($inline);
|
||||
$is_dirty = true;
|
||||
} else {
|
||||
PhabricatorInlineComment::loadAndAttachVersionedDrafts(
|
||||
$viewer,
|
||||
array($inline));
|
||||
}
|
||||
|
||||
if ($is_dirty) {
|
||||
$this->saveComment($inline);
|
||||
}
|
||||
if ($is_dirty) {
|
||||
$this->saveComment($inline);
|
||||
}
|
||||
|
||||
$edit_dialog = $this->buildEditDialog($inline)
|
||||
|
@ -333,7 +337,10 @@ abstract class PhabricatorInlineCommentController
|
|||
$state = $inline->getContentState();
|
||||
$default_suggestion = $inline->getDefaultSuggestionText();
|
||||
$state->setContentSuggestionText($default_suggestion);
|
||||
|
||||
$inline->setInitialContentState($state);
|
||||
$inline->setContentState($state);
|
||||
|
||||
$inline->setIsDeleted(0);
|
||||
|
||||
$this->saveComment($inline);
|
||||
|
@ -461,6 +468,7 @@ abstract class PhabricatorInlineCommentController
|
|||
PhabricatorInlineComment $inline,
|
||||
$view,
|
||||
$is_edit) {
|
||||
$viewer = $this->getViewer();
|
||||
|
||||
if ($inline->getReplyToCommentPHID()) {
|
||||
$can_suggest = false;
|
||||
|
@ -469,18 +477,15 @@ abstract class PhabricatorInlineCommentController
|
|||
}
|
||||
|
||||
if ($is_edit) {
|
||||
$viewer = $this->getViewer();
|
||||
$content_state = $inline->getContentStateForEdit($viewer);
|
||||
$state = $inline->getContentStateMapForEdit($viewer);
|
||||
} else {
|
||||
$content_state = $inline->getContentState();
|
||||
$state = $inline->getContentStateMap();
|
||||
}
|
||||
|
||||
$state_map = $content_state->newStorageMap();
|
||||
|
||||
$response = array(
|
||||
'inline' => array(
|
||||
'id' => $inline->getID(),
|
||||
'contentState' => $state_map,
|
||||
'state' => $state,
|
||||
'canSuggestEdit' => $can_suggest,
|
||||
),
|
||||
'view' => hsprintf('%s', $view),
|
||||
|
|
|
@ -336,13 +336,29 @@ abstract class PhabricatorInlineComment
|
|||
return $this->newContentState()->readFromRequest($request);
|
||||
}
|
||||
|
||||
public function getContentState() {
|
||||
$state = $this->newContentState();
|
||||
public function getInitialContentState() {
|
||||
return $this->getNamedContentState('inline.state.initial');
|
||||
}
|
||||
|
||||
$storage = $this->getStorageObject();
|
||||
$storage_map = $storage->getAttribute('inline.state');
|
||||
if (is_array($storage_map)) {
|
||||
$state->readStorageMap($storage_map);
|
||||
public function setInitialContentState(
|
||||
PhabricatorInlineCommentContentState $state) {
|
||||
return $this->setNamedContentState('inline.state.initial', $state);
|
||||
}
|
||||
|
||||
public function getCommittedContentState() {
|
||||
return $this->getNamedContentState('inline.state.committed');
|
||||
}
|
||||
|
||||
public function setCommittedContentState(
|
||||
PhabricatorInlineCommentContentState $state) {
|
||||
return $this->setNamedContentState('inline.state.committed', $state);
|
||||
}
|
||||
|
||||
public function getContentState() {
|
||||
$state = $this->getNamedContentState('inline.state');
|
||||
|
||||
if (!$state) {
|
||||
$state = $this->newContentState();
|
||||
}
|
||||
|
||||
$state->setContentText($this->getContent());
|
||||
|
@ -351,11 +367,31 @@ abstract class PhabricatorInlineComment
|
|||
}
|
||||
|
||||
public function setContentState(PhabricatorInlineCommentContentState $state) {
|
||||
$this->setContent($state->getContentText());
|
||||
|
||||
return $this->setNamedContentState('inline.state', $state);
|
||||
}
|
||||
|
||||
private function getNamedContentState($key) {
|
||||
$storage = $this->getStorageObject();
|
||||
|
||||
$storage_map = $storage->getAttribute($key);
|
||||
if (!is_array($storage_map)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$state = $this->newContentState();
|
||||
$state->readStorageMap($storage_map);
|
||||
return $state;
|
||||
}
|
||||
|
||||
private function setNamedContentState(
|
||||
$key,
|
||||
PhabricatorInlineCommentContentState $state) {
|
||||
|
||||
$storage = $this->getStorageObject();
|
||||
$storage_map = $state->newStorageMap();
|
||||
$storage->setAttribute('inline.state', $storage_map);
|
||||
|
||||
$this->setContent($state->getContentText());
|
||||
$storage->setAttribute($key, $storage_map);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
@ -364,6 +400,42 @@ abstract class PhabricatorInlineComment
|
|||
return $this->getStorageObject()->getInlineContext();
|
||||
}
|
||||
|
||||
public function getContentStateMapForEdit(PhabricatorUser $viewer) {
|
||||
return $this->getWireContentStateMap(true, $viewer);
|
||||
}
|
||||
|
||||
public function getContentStateMap() {
|
||||
return $this->getWireContentStateMap(false, null);
|
||||
}
|
||||
|
||||
private function getWireContentStateMap(
|
||||
$is_edit,
|
||||
PhabricatorUser $viewer = null) {
|
||||
|
||||
$initial_state = $this->getInitialContentState();
|
||||
$committed_state = $this->getCommittedContentState();
|
||||
|
||||
if ($is_edit) {
|
||||
$active_state = $this->getContentStateForEdit($viewer);
|
||||
} else {
|
||||
$active_state = $this->getContentState();
|
||||
}
|
||||
|
||||
return array(
|
||||
'initial' => $this->getWireContentState($initial_state),
|
||||
'committed' => $this->getWireContentState($committed_state),
|
||||
'active' => $this->getWireContentState($active_state),
|
||||
);
|
||||
}
|
||||
|
||||
private function getWireContentState($content_state) {
|
||||
if ($content_state === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $content_state->newStorageMap();
|
||||
}
|
||||
|
||||
public function getDefaultSuggestionText() {
|
||||
$context = $this->getInlineContext();
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ abstract class PHUIDiffInlineCommentView extends AphrontView {
|
|||
'startOffset' => $inline->getStartOffset(),
|
||||
'endOffset' => $inline->getEndOffset(),
|
||||
'on_right' => $this->getIsOnRight(),
|
||||
'contentState' => $inline->getContentState()->newStorageMap(),
|
||||
'state' => $inline->getContentStateMap(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
JX.install('DiffInline', {
|
||||
|
||||
construct : function() {
|
||||
this._activeContentState = new JX.DiffInlineContentState();
|
||||
this._committedContentState = new JX.DiffInlineContentState();
|
||||
this._state = {};
|
||||
},
|
||||
|
||||
members: {
|
||||
|
@ -56,8 +55,7 @@ JX.install('DiffInline', {
|
|||
_isSelected: false,
|
||||
_canSuggestEdit: false,
|
||||
|
||||
_committedContentState: null,
|
||||
_activeContentState: null,
|
||||
_state: null,
|
||||
|
||||
bindToRow: function(row) {
|
||||
this._row = row;
|
||||
|
@ -602,15 +600,23 @@ JX.install('DiffInline', {
|
|||
_readInlineState: function(state) {
|
||||
this._id = state.id;
|
||||
|
||||
// TODO: This is not the correct content state after a reload: it is
|
||||
// the draft state.
|
||||
this._getCommittedContentState().readWireFormat(state.contentState);
|
||||
|
||||
this._getActiveContentState().readWireFormat(state.contentState);
|
||||
this._state = {
|
||||
initial: this._newContentStateFromWireFormat(state.state.initial),
|
||||
committed: this._newContentStateFromWireFormat(state.state.committed),
|
||||
active: this._newContentStateFromWireFormat(state.state.active)
|
||||
};
|
||||
|
||||
this._canSuggestEdit = state.canSuggestEdit;
|
||||
},
|
||||
|
||||
_newContentStateFromWireFormat: function(map) {
|
||||
if (map === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new JX.DiffInlineContentState().readWireFormat(map);
|
||||
},
|
||||
|
||||
_ondeleteresponse: function() {
|
||||
// If there's an existing "unedit" undo element, remove it.
|
||||
if (this._undoRow) {
|
||||
|
@ -787,7 +793,7 @@ JX.install('DiffInline', {
|
|||
},
|
||||
|
||||
_getActiveContentState: function() {
|
||||
var state = this._activeContentState;
|
||||
var state = this._state.active;
|
||||
|
||||
if (this._editRow) {
|
||||
state.readForm(this._editRow);
|
||||
|
@ -797,7 +803,11 @@ JX.install('DiffInline', {
|
|||
},
|
||||
|
||||
_getCommittedContentState: function() {
|
||||
return this._committedContentState;
|
||||
return this._state.committed;
|
||||
},
|
||||
|
||||
_getInitialContentState: function() {
|
||||
return this._state.initial;
|
||||
},
|
||||
|
||||
setHasSuggestion: function(has_suggestion) {
|
||||
|
|
Loading…
Reference in a new issue