@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.

Make the client authoritative for "Save" actions

Summary:
Ref T13559. When you click "Save" on an inline comment and it's empty, we may actually delete the comment.

Currently, the client and server both make decisions about whether the comment should be deleted. These decisions may not agree, causing the client state to fall out of sync.

Make the client authoritative about whether it wants to handle the user clicking the "Save" button as an intent to save or an intent to delete.

Test Plan: Saved empty and nonempty inlines. See followup changes.

Maniphest Tasks: T13559

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

+72 -54
+7 -7
resources/celerity/map.php
··· 13 13 'core.pkg.js' => 'ab3502fe', 14 14 'dark-console.pkg.js' => '187792c2', 15 15 'differential.pkg.css' => 'ffb69e3d', 16 - 'differential.pkg.js' => '442567d7', 16 + 'differential.pkg.js' => '7747755e', 17 17 'diffusion.pkg.css' => '42c75c37', 18 18 'diffusion.pkg.js' => '78c9885d', 19 19 'maniphest.pkg.css' => '35995d6d', ··· 385 385 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => '0116d3e8', 386 386 'rsrc/js/application/diff/DiffChangeset.js' => 'd7d3ba75', 387 387 'rsrc/js/application/diff/DiffChangesetList.js' => 'cc2c5de5', 388 - 'rsrc/js/application/diff/DiffInline.js' => 'fdebbba6', 388 + 'rsrc/js/application/diff/DiffInline.js' => '34ccdeda', 389 389 'rsrc/js/application/diff/DiffInlineContentState.js' => '68e6339d', 390 390 'rsrc/js/application/diff/DiffPathView.js' => '8207abf9', 391 391 'rsrc/js/application/diff/DiffTreeView.js' => '5d83623b', ··· 788 788 'phabricator-dashboard-css' => '5a205b9d', 789 789 'phabricator-diff-changeset' => 'd7d3ba75', 790 790 'phabricator-diff-changeset-list' => 'cc2c5de5', 791 - 'phabricator-diff-inline' => 'fdebbba6', 791 + 'phabricator-diff-inline' => '34ccdeda', 792 792 'phabricator-diff-inline-content-state' => '68e6339d', 793 793 'phabricator-diff-path-view' => '8207abf9', 794 794 'phabricator-diff-tree-view' => '5d83623b', ··· 1220 1220 'javelin-stratcom', 1221 1221 'javelin-workflow', 1222 1222 ), 1223 + '34ccdeda' => array( 1224 + 'javelin-dom', 1225 + 'phabricator-diff-inline-content-state', 1226 + ), 1223 1227 '34e2a838' => array( 1224 1228 'aphront-typeahead-control-css', 1225 1229 'phui-tag-view-css', ··· 2230 2234 ), 2231 2235 'fdc13e4e' => array( 2232 2236 'javelin-install', 2233 - ), 2234 - 'fdebbba6' => array( 2235 - 'javelin-dom', 2236 - 'phabricator-diff-inline-content-state', 2237 2237 ), 2238 2238 'ff688a7a' => array( 2239 2239 'owners-path-editor',
+7 -15
src/infrastructure/diff/PhabricatorInlineCommentController.php
··· 186 186 if ($op === 'save') { 187 187 $this->updateCommentContentState($inline); 188 188 189 - $inline->setIsEditing(false); 190 - 191 - if (!$inline->isVoidComment($viewer)) { 192 - $inline->setIsDeleted(0); 193 - 194 - $this->saveComment($inline); 195 - 196 - return $this->buildRenderedCommentResponse( 197 - $inline, 198 - $this->getIsOnRight()); 199 - } else { 200 - $inline->setIsDeleted(1); 189 + $inline 190 + ->setIsEditing(false) 191 + ->setIsDeleted(0); 201 192 202 - $this->saveComment($inline); 193 + $this->saveComment($inline); 203 194 204 - return $this->buildEmptyResponse(); 205 - } 195 + return $this->buildRenderedCommentResponse( 196 + $inline, 197 + $this->getIsOnRight()); 206 198 } else { 207 199 // NOTE: At time of writing, the "editing" state of inlines is 208 200 // preserved by simulating a click on "Edit" when the inline loads.
+58 -32
webroot/rsrc/js/application/diff/DiffInline.js
··· 45 45 _undoRow: null, 46 46 _undoType: null, 47 47 _undoState: null, 48 + _preventUndo: false, 48 49 49 50 _draftRequest: null, 50 51 _skipFocus: false, ··· 159 160 160 161 getEndOffset: function() { 161 162 return this._endOffset; 163 + }, 164 + 165 + _setPreventUndo: function(prevent_undo) { 166 + this._preventUndo = prevent_undo; 167 + }, 168 + 169 + _getPreventUndo: function() { 170 + return this._preventUndo; 162 171 }, 163 172 164 173 setIsSelected: function(is_selected) { ··· 617 626 this._editRow = null; 618 627 } 619 628 620 - this._drawUndeleteRows(state); 629 + if (!this._getPreventUndo()) { 630 + this._drawUndeleteRows(state); 631 + } 621 632 622 633 this.setLoading(false); 623 634 this.setDeleted(true); ··· 815 826 }, 816 827 817 828 save: function() { 829 + if (this._shouldDeleteOnSave()) { 830 + this._setPreventUndo(true); 831 + this._applyDelete(); 832 + } else { 833 + this._applySave(); 834 + } 835 + }, 836 + 837 + _shouldDeleteOnSave: function() { 818 838 var state = this._getActiveContentState(); 819 - var handler = JX.bind(this, this._onsubmitresponse); 820 839 821 - this.setLoading(true); 840 + // TODO: This is greatly simplified because we don't track all the 841 + // state we need yet. 822 842 823 - var uri = this._getInlineURI(); 843 + return !state.getText().length; 844 + }, 845 + 846 + _applySave: function() { 847 + var handler = JX.bind(this, this._onsaveresponse); 848 + 849 + var state = this._getActiveContentState(); 824 850 var data = this._newRequestData('save', state.getWireFormat()); 825 851 826 - new JX.Request(uri, handler) 827 - .setData(data) 828 - .send(); 852 + this._applyCall(handler, data); 853 + }, 854 + 855 + _applyDelete: function() { 856 + var handler = JX.bind(this, this._ondeleteresponse); 857 + 858 + var data = this._newRequestData('delete'); 859 + 860 + this._applyCall(handler, data); 861 + }, 862 + 863 + _applyCall: function(handler, data) { 864 + var uri = this._getInlineURI(); 865 + 866 + var callback = JX.bind(this, function() { 867 + this.setLoading(false); 868 + handler.apply(null, arguments); 869 + }); 870 + 871 + this.setLoading(true); 872 + 873 + new JX.Workflow(uri, data) 874 + .setHandler(callback) 875 + .start(); 829 876 }, 830 877 831 878 undo: function() { ··· 917 964 } 918 965 }, 919 966 920 - _onsubmitresponse: function(response) { 967 + _onsaveresponse: function(response) { 921 968 if (this._editRow) { 922 969 JX.DOM.remove(this._editRow); 923 970 this._editRow = null; ··· 927 974 this.setInvisible(false); 928 975 this.setEditing(false); 929 976 930 - this._onupdate(response); 931 - }, 932 - 933 - _onupdate: function(response) { 934 - var new_row; 935 - if (response.view) { 936 - new_row = this._drawContentRows(JX.$H(response.view).getNode()); 937 - } 938 - 939 - // TODO: Save the old row so the action it's undo-able if it was a 940 - // delete. 941 - var remove_old = true; 942 - if (remove_old) { 943 - JX.DOM.remove(this._row); 944 - } 945 - 946 - // If you delete the content on a comment and save it, it acts like a 947 - // delete: the server does not return a new row. 948 - if (new_row) { 949 - this.bindToRow(new_row); 950 - } else { 951 - this.setDeleted(true); 952 - this._row = null; 953 - } 977 + var new_row = this._drawContentRows(JX.$H(response.view).getNode()); 978 + JX.DOM.remove(this._row); 979 + this.bindToRow(new_row); 954 980 955 981 this._didUpdate(); 956 982 },