@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Lift inline comment draft behaviors to "InlineController"

Summary:
Ref T13513. Currently, if you:

- click a line to create an inline;
- type some text;
- wait a moment; and
- close the page.

...you don't get an "Unsubmitted Draft" marker in the revision list.

Lift all the draft behavior to "InlineController" and make saving a draft dirty the overall container draft state.

Test Plan:
- Took the steps described above, got a draft state marker.
- Created, edited, submitted, etc., inlines in Diffusion and Differential.

Maniphest Tasks: T13513

Differential Revision: https://secure.phabricator.com/D21235

+103 -77
+4 -32
src/applications/differential/controller/DifferentialInlineCommentEditController.php
··· 7 7 return new DifferentialDiffInlineCommentQuery(); 8 8 } 9 9 10 + protected function newContainerObject() { 11 + return $this->loadRevision(); 12 + } 13 + 10 14 private function getRevisionID() { 11 15 return $this->getRequest()->getURIData('id'); 12 16 } ··· 137 141 return true; 138 142 } 139 143 140 - protected function deleteComment(PhabricatorInlineComment $inline) { 141 - $inline->openTransaction(); 142 - $inline->setIsDeleted(1)->save(); 143 - $this->syncDraft(); 144 - $inline->saveTransaction(); 145 - } 146 - 147 - protected function undeleteComment( 148 - PhabricatorInlineComment $inline) { 149 - $inline->openTransaction(); 150 - $inline->setIsDeleted(0)->save(); 151 - $this->syncDraft(); 152 - $inline->saveTransaction(); 153 - } 154 - 155 - protected function saveComment(PhabricatorInlineComment $inline) { 156 - $inline->openTransaction(); 157 - $inline->save(); 158 - $this->syncDraft(); 159 - $inline->saveTransaction(); 160 - } 161 - 162 144 protected function loadObjectOwnerPHID( 163 145 PhabricatorInlineComment $inline) { 164 146 return $this->loadRevision()->getAuthorPHID(); ··· 196 178 $table->getTableName(), 197 179 $viewer->getPHID(), 198 180 $ids); 199 - } 200 - 201 - private function syncDraft() { 202 - $viewer = $this->getViewer(); 203 - $revision = $this->loadRevision(); 204 - 205 - $revision->newDraftEngine() 206 - ->setObject($revision) 207 - ->setViewer($viewer) 208 - ->synchronize(); 209 181 } 210 182 211 183 }
+1
src/applications/differential/engine/DifferentialRevisionDraftEngine.php
··· 11 11 ->setViewer($viewer) 12 12 ->withRevisionPHIDs(array($revision->getPHID())) 13 13 ->withPublishableComments(true) 14 + ->setLimit(1) 14 15 ->execute(); 15 16 16 17 return (bool)$inlines;
+4 -13
src/applications/diffusion/controller/DiffusionInlineCommentController.php
··· 7 7 return new DiffusionDiffInlineCommentQuery(); 8 8 } 9 9 10 + protected function newContainerObject() { 11 + return $this->loadCommit(); 12 + } 13 + 10 14 private function getCommitPHID() { 11 15 return $this->getRequest()->getURIData('phid'); 12 16 } ··· 101 105 } 102 106 103 107 return true; 104 - } 105 - 106 - protected function deleteComment(PhabricatorInlineComment $inline) { 107 - $inline->setIsDeleted(1)->save(); 108 - } 109 - 110 - protected function undeleteComment( 111 - PhabricatorInlineComment $inline) { 112 - $inline->setIsDeleted(0)->save(); 113 - } 114 - 115 - protected function saveComment(PhabricatorInlineComment $inline) { 116 - return $inline->save(); 117 108 } 118 109 119 110 protected function loadObjectOwnerPHID(
+1
src/applications/diffusion/engine/DiffusionCommitDraftEngine.php
··· 11 11 ->setViewer($viewer) 12 12 ->withCommitPHIDs(array($commit->getPHID())) 13 13 ->withPublishableComments(true) 14 + ->setLimit(1) 14 15 ->execute(); 15 16 16 17 return (bool)$inlines;
+93 -32
src/infrastructure/diff/PhabricatorInlineCommentController.php
··· 3 3 abstract class PhabricatorInlineCommentController 4 4 extends PhabricatorController { 5 5 6 + private $containerObject; 7 + 6 8 abstract protected function createComment(); 7 9 abstract protected function newInlineCommentQuery(); 8 10 abstract protected function loadCommentForDone($id); 9 11 abstract protected function loadObjectOwnerPHID( 10 12 PhabricatorInlineComment $inline); 11 - abstract protected function deleteComment( 12 - PhabricatorInlineComment $inline); 13 - abstract protected function undeleteComment( 14 - PhabricatorInlineComment $inline); 15 - abstract protected function saveComment( 16 - PhabricatorInlineComment $inline); 13 + abstract protected function newContainerObject(); 14 + 15 + final protected function getContainerObject() { 16 + if ($this->containerObject === null) { 17 + $object = $this->newContainerObject(); 18 + if (!$object) { 19 + throw new Exception( 20 + pht( 21 + 'Failed to load container object for inline comment.')); 22 + } 23 + $this->containerObject = $object; 24 + } 25 + 26 + return $this->containerObject; 27 + } 17 28 18 29 protected function hideComments(array $ids) { 19 30 throw new PhutilMethodNotImplementedException(); ··· 173 184 $inline = $this->loadCommentByIDForEdit($this->getCommentID()); 174 185 175 186 if ($is_delete) { 176 - $this->deleteComment($inline); 187 + $inline->setIsDeleted(1); 177 188 } else { 178 - $this->undeleteComment($inline); 189 + $inline->setIsDeleted(0); 179 190 } 191 + 192 + $this->saveComment($inline); 180 193 181 194 return $this->buildEmptyResponse(); 182 195 case 'edit': ··· 190 203 ->setIsEditing(false); 191 204 192 205 $this->saveComment($inline); 193 - $this->purgeVersionedDrafts($inline); 194 206 195 207 return $this->buildRenderedCommentResponse( 196 208 $inline, 197 209 $this->getIsOnRight()); 198 210 } else { 199 - $this->deleteComment($inline); 200 - $this->purgeVersionedDrafts($inline); 211 + $inline->setIsDeleted(1); 212 + 213 + $this->saveComment($inline); 201 214 202 215 return $this->buildEmptyResponse(); 203 216 } 204 217 } else { 205 - $inline->setIsEditing(true); 218 + // NOTE: At time of writing, the "editing" state of inlines is 219 + // preserved by simluating a click on "Edit" when the inline loads. 220 + 221 + // In this case, we don't want to "saveComment()", because it 222 + // recalculates object drafts and purges versioned drafts. 223 + 224 + // The recalculation is merely unnecessary (state doesn't change) 225 + // but purging drafts means that loading a page and then closing it 226 + // discards your drafts. 227 + 228 + // To avoid the purge, only invoke "saveComment()" if we actually 229 + // have changes to apply. 230 + 231 + $is_dirty = false; 232 + if (!$inline->getIsEditing()) { 233 + $inline->setIsEditing(true); 234 + $is_dirty = true; 235 + } 206 236 207 237 if (strlen($text)) { 208 238 $inline->setContent($text); 239 + $is_dirty = true; 240 + } else { 241 + PhabricatorInlineComment::loadAndAttachVersionedDrafts( 242 + $viewer, 243 + array($inline)); 209 244 } 210 245 211 - $this->saveComment($inline); 212 - 213 - if (strlen($text)) { 214 - $this->purgeVersionedDrafts($inline); 246 + if ($is_dirty) { 247 + $this->saveComment($inline); 215 248 } 216 - 217 - PhabricatorInlineComment::loadAndAttachVersionedDrafts( 218 - $viewer, 219 - array($inline)); 220 249 } 221 250 222 251 $edit_dialog = $this->buildEditDialog($inline) ··· 240 269 241 270 $content = $inline->getContent(); 242 271 if (!strlen($content)) { 243 - $this->deleteComment($inline); 244 - } else { 245 - $this->saveComment($inline); 272 + $inline->setIsDeleted(1); 246 273 } 247 274 248 - $this->purgeVersionedDrafts($inline); 275 + $this->saveComment($inline); 249 276 250 277 return $this->buildEmptyResponse(); 251 278 case 'draft': ··· 261 288 $versioned_draft 262 289 ->setProperty('inline.text', $text) 263 290 ->save(); 291 + 292 + // We have to synchronize the draft engine after saving a versioned 293 + // draft, because taking an inline comment from "no text, no draft" 294 + // to "no text, text in a draft" marks the container object as having 295 + // a draft. 296 + $draft_engine = $this->newDraftEngine(); 297 + if ($draft_engine) { 298 + $draft_engine->synchronize(); 299 + } else { 300 + phlog('no draft engine'); 301 + } 264 302 265 303 return $this->buildEmptyResponse(); 266 304 case 'new': ··· 432 470 ->setContent($response); 433 471 } 434 472 435 - private function purgeVersionedDrafts( 436 - PhabricatorInlineComment $inline) { 437 - $viewer = $this->getViewer(); 438 - PhabricatorVersionedDraft::purgeDrafts( 439 - $inline->getPHID(), 440 - $viewer->getPHID()); 441 - } 442 - 443 473 final protected function loadCommentByID($id) { 444 474 $query = $this->newInlineCommentQuery() 445 475 ->withIDs(array($id)); ··· 492 522 } 493 523 494 524 return $inline; 525 + } 526 + 527 + private function saveComment(PhabricatorInlineComment $inline) { 528 + $viewer = $this->getViewer(); 529 + $draft_engine = $this->newDraftEngine(); 530 + 531 + $inline->openTransaction(); 532 + $inline->save(); 533 + 534 + PhabricatorVersionedDraft::purgeDrafts( 535 + $inline->getPHID(), 536 + $viewer->getPHID()); 537 + 538 + if ($draft_engine) { 539 + $draft_engine->synchronize(); 540 + } 541 + 542 + $inline->saveTransaction(); 543 + } 544 + 545 + private function newDraftEngine() { 546 + $viewer = $this->getViewer(); 547 + $object = $this->getContainerObject(); 548 + 549 + if (!($object instanceof PhabricatorDraftInterface)) { 550 + return null; 551 + } 552 + 553 + return $object->newDraftEngine() 554 + ->setObject($object) 555 + ->setViewer($viewer); 495 556 } 496 557 497 558 }