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

In Differential, allow "r" to create comments and "R" to quote

Summary:
Ref T11401. Fixes T5232. Ref T12616.

Partly, this moves more code over to the new stuff.

This also allows "r" to work if you have code selected (not just comments). If you "reply" to code, you start a new comment.

You can "R" a comment to quote it. This just starts a new comment normally if you "R" a block of code. This is sort of a power-user version of "quote" since it seems like it probably doesn't really make sense to put it in the UI ever (maybe).

With the new click-to-select, you can click + "R" to reply-with-quote.

Test Plan: Used "r" and "R" to reply to comments and code.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T12616, T11401, T5232

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

+170 -68
+33 -33
resources/celerity/map.php
··· 13 13 'core.pkg.js' => '8c5f913d', 14 14 'darkconsole.pkg.js' => '1f9a31bc', 15 15 'differential.pkg.css' => 'ea471cb0', 16 - 'differential.pkg.js' => '7f24021f', 16 + 'differential.pkg.js' => 'ae6f5198', 17 17 'diffusion.pkg.css' => 'b93d9b8c', 18 18 'diffusion.pkg.js' => '84c8f8fd', 19 19 'favicon.ico' => '30672e08', ··· 390 390 'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => '408bf173', 391 391 'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '453c5375', 392 392 'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => 'd4eecc63', 393 - 'rsrc/js/application/diff/DiffChangeset.js' => '34e513e2', 394 - 'rsrc/js/application/diff/DiffChangesetList.js' => 'fcbf80e9', 395 - 'rsrc/js/application/diff/DiffInline.js' => 'c2e9ff4c', 393 + 'rsrc/js/application/diff/DiffChangeset.js' => '68758d99', 394 + 'rsrc/js/application/diff/DiffChangesetList.js' => '57c491b5', 395 + 'rsrc/js/application/diff/DiffInline.js' => '1afe9760', 396 396 'rsrc/js/application/diff/behavior-preview-link.js' => '051c7832', 397 397 'rsrc/js/application/differential/behavior-comment-preview.js' => 'b064af76', 398 398 'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1', 399 - 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => 'd59300f5', 399 + 'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '1c6bc8cf', 400 400 'rsrc/js/application/differential/behavior-populate.js' => '5e41c819', 401 401 'rsrc/js/application/differential/behavior-toggle-files.js' => 'ca3f91eb', 402 402 'rsrc/js/application/differential/behavior-user-select.js' => 'a8d8459d', ··· 619 619 'javelin-behavior-device' => 'bb1dd507', 620 620 'javelin-behavior-diff-preview-link' => '051c7832', 621 621 'javelin-behavior-differential-diff-radios' => 'e1ff79b1', 622 - 'javelin-behavior-differential-edit-inline-comments' => 'd59300f5', 622 + 'javelin-behavior-differential-edit-inline-comments' => '1c6bc8cf', 623 623 'javelin-behavior-differential-feedback-preview' => 'b064af76', 624 624 'javelin-behavior-differential-populate' => '5e41c819', 625 625 'javelin-behavior-differential-toggle-files' => 'ca3f91eb', ··· 779 779 'phabricator-darklog' => 'c8e1ffe3', 780 780 'phabricator-darkmessage' => 'c48cccdd', 781 781 'phabricator-dashboard-css' => 'fe5b1869', 782 - 'phabricator-diff-changeset' => '34e513e2', 783 - 'phabricator-diff-changeset-list' => 'fcbf80e9', 784 - 'phabricator-diff-inline' => 'c2e9ff4c', 782 + 'phabricator-diff-changeset' => '68758d99', 783 + 'phabricator-diff-changeset-list' => '57c491b5', 784 + 'phabricator-diff-inline' => '1afe9760', 785 785 'phabricator-drag-and-drop-file-upload' => '58dea2fa', 786 786 'phabricator-draggable-list' => 'bea6e7f4', 787 787 'phabricator-fatal-config-template-css' => '8f18fa41', ··· 1030 1030 'javelin-util', 1031 1031 'phabricator-keyboard-shortcut-manager', 1032 1032 ), 1033 + '1afe9760' => array( 1034 + 'javelin-dom', 1035 + ), 1033 1036 '1bd28176' => array( 1034 1037 'javelin-install', 1035 1038 'javelin-dom', 1036 1039 'javelin-vector', 1037 1040 'javelin-request', 1038 1041 'javelin-uri', 1042 + ), 1043 + '1c6bc8cf' => array( 1044 + 'javelin-behavior', 1045 + 'javelin-stratcom', 1046 + 'javelin-dom', 1047 + 'javelin-util', 1048 + 'javelin-vector', 1039 1049 ), 1040 1050 '1def2711' => array( 1041 1051 'javelin-install', ··· 1129 1139 'javelin-dom', 1130 1140 'javelin-workflow', 1131 1141 ), 1132 - '34e513e2' => array( 1133 - 'javelin-dom', 1134 - 'javelin-util', 1135 - 'javelin-stratcom', 1136 - 'javelin-install', 1137 - 'javelin-workflow', 1138 - 'javelin-router', 1139 - 'javelin-behavior-device', 1140 - 'javelin-vector', 1141 - 'phabricator-diff-inline', 1142 - ), 1143 1142 '3ab51e2c' => array( 1144 1143 'javelin-behavior', 1145 1144 'javelin-behavior-device', ··· 1332 1331 'javelin-vector', 1333 1332 'javelin-dom', 1334 1333 ), 1334 + '57c491b5' => array( 1335 + 'javelin-install', 1336 + ), 1335 1337 '58dea2fa' => array( 1336 1338 'javelin-install', 1337 1339 'javelin-util', ··· 1406 1408 'javelin-dom', 1407 1409 'phabricator-notification', 1408 1410 ), 1411 + '68758d99' => array( 1412 + 'javelin-dom', 1413 + 'javelin-util', 1414 + 'javelin-stratcom', 1415 + 'javelin-install', 1416 + 'javelin-workflow', 1417 + 'javelin-router', 1418 + 'javelin-behavior-device', 1419 + 'javelin-vector', 1420 + 'phabricator-diff-inline', 1421 + ), 1409 1422 '6882e80a' => array( 1410 1423 'javelin-dom', 1411 1424 ), ··· 1910 1923 'javelin-dom', 1911 1924 'javelin-vector', 1912 1925 ), 1913 - 'c2e9ff4c' => array( 1914 - 'javelin-dom', 1915 - ), 1916 1926 'c420b0b9' => array( 1917 1927 'javelin-behavior', 1918 1928 'javelin-behavior-device', ··· 2035 2045 'javelin-behavior', 2036 2046 'javelin-dom', 2037 2047 'javelin-stratcom', 2038 - ), 2039 - 'd59300f5' => array( 2040 - 'javelin-behavior', 2041 - 'javelin-stratcom', 2042 - 'javelin-dom', 2043 - 'javelin-util', 2044 - 'javelin-vector', 2045 2048 ), 2046 2049 'd5a2d665' => array( 2047 2050 'javelin-behavior', ··· 2220 2223 'javelin-behavior', 2221 2224 'javelin-dom', 2222 2225 'phortune-credit-card-form', 2223 - ), 2224 - 'fcbf80e9' => array( 2225 - 'javelin-install', 2226 2226 ), 2227 2227 'fe287620' => array( 2228 2228 'javelin-install',
+8 -4
src/applications/differential/view/DifferentialChangesetListView.php
··· 244 244 pht('Jump to previous inline comment.'), 245 245 'Jump to the table of contents.' => 246 246 pht('Jump to the table of contents.'), 247 - 'Reply to selected inline comment.' => 248 - pht('Reply to selected inline comment.'), 247 + 249 248 'Edit selected inline comment.' => 250 249 pht('Edit selected inline comment.'), 251 - 'You must select a comment to reply to.' => 252 - pht('You must select a comment to reply to.'), 253 250 'You must select a comment to edit.' => 254 251 pht('You must select a comment to edit.'), 252 + 253 + 'Reply to selected inline comment or change.' => 254 + pht('Reply to selected inline comment or change.'), 255 + 'You must select a comment or change to reply to.' => 256 + pht('You must select a comment or change to reply to.'), 257 + 'Reply and quote selected inline comment.' => 258 + pht('Reply and quote selected inline comment.'), 255 259 256 260 'Mark or unmark selected inline comment as done.' => 257 261 pht('Mark or unmark selected inline comment as done.'),
+33 -3
webroot/rsrc/js/application/diff/DiffChangeset.js
··· 526 526 return data.inline; 527 527 }, 528 528 529 - newInlineForRange: function(data) { 529 + newInlineForRange: function(origin, target) { 530 + var list = this.getChangesetList(); 531 + 532 + var src = list.getLineNumberFromHeader(origin); 533 + var dst = list.getLineNumberFromHeader(target); 534 + 535 + var changeset_id = null; 536 + var side = list.getDisplaySideFromHeader(origin); 537 + if (side == 'right') { 538 + changeset_id = this.getRightChangesetID(); 539 + } else { 540 + changeset_id = this.getLeftChangesetID(); 541 + } 542 + 543 + var is_new = false; 544 + if (side == 'right') { 545 + is_new = true; 546 + } else if (this.getRightChangesetID() != this.getLeftChangesetID()) { 547 + is_new = true; 548 + } 549 + 550 + var data = { 551 + origin: origin, 552 + target: target, 553 + number: src, 554 + length: dst - src, 555 + changesetID: changeset_id, 556 + displaySide: side, 557 + isNewFile: is_new 558 + }; 559 + 530 560 var inline = new JX.DiffInline() 531 561 .setChangeset(this) 532 562 .bindToRange(data); ··· 538 568 return inline; 539 569 }, 540 570 541 - newInlineReply: function(original) { 571 + newInlineReply: function(original, text) { 542 572 var inline = new JX.DiffInline() 543 573 .setChangeset(this) 544 574 .bindToReply(original); 545 575 546 576 this._inlines.push(inline); 547 577 548 - inline.create(); 578 + inline.create(text); 549 579 550 580 return inline; 551 581 },
+84 -7
webroot/rsrc/js/application/diff/DiffChangesetList.js
··· 136 136 label = pht('Jump to the table of contents.'); 137 137 this._installKey('t', label, this._ontoc); 138 138 139 - label = pht('Reply to selected inline comment.'); 140 - this._installKey('r', label, this._onkeyreply); 139 + label = pht('Reply to selected inline comment or change.'); 140 + this._installKey('r', label, JX.bind(this, this._onkeyreply, false)); 141 + 142 + label = pht('Reply and quote selected inline comment.'); 143 + this._installKey('R', label, JX.bind(this, this._onkeyreply, true)); 141 144 142 145 label = pht('Edit selected inline comment.'); 143 146 this._installKey('e', label, this._onkeyedit); ··· 234 237 manager.scrollTo(toc); 235 238 }, 236 239 237 - _onkeyreply: function() { 240 + _onkeyreply: function(is_quote) { 238 241 var cursor = this._cursorItem; 239 242 240 243 if (cursor) { ··· 243 246 if (inline.canReply()) { 244 247 this.setFocus(null); 245 248 246 - inline.reply(); 249 + var text; 250 + if (is_quote) { 251 + text = inline.getRawText(); 252 + text = '> ' + text.replace(/\n/g, '\n> ') + '\n\n'; 253 + } else { 254 + text = ''; 255 + } 256 + 257 + inline.reply(text); 247 258 return; 248 259 } 249 260 } 261 + 262 + // If the keyboard cursor is selecting a range of lines, we may have 263 + // a mixture of old and new changes on the selected rows. It is not 264 + // entirely unambiguous what the user means when they say they want 265 + // to reply to this, but we use this logic: reply on the new file if 266 + // there are any new lines. Otherwise (if there are only removed 267 + // lines) reply on the old file. 268 + 269 + if (cursor.type == 'change') { 270 + var origin = cursor.nodes.begin; 271 + var target = cursor.nodes.end; 272 + 273 + // The "origin" and "target" are entire rows, but we need to find 274 + // a range of "<th />" nodes to actually create an inline, so go 275 + // fishing. 276 + 277 + var old_list = []; 278 + var new_list = []; 279 + 280 + var row = origin; 281 + while (row) { 282 + var header = row.firstChild; 283 + while (header) { 284 + if (JX.DOM.isType(header, 'th')) { 285 + if (header.className.indexOf('old') !== -1) { 286 + old_list.push(header); 287 + } else if (header.className.indexOf('new') !== -1) { 288 + new_list.push(header); 289 + } 290 + } 291 + header = header.nextSibling; 292 + } 293 + 294 + if (row == target) { 295 + break; 296 + } 297 + 298 + row = row.nextSibling; 299 + } 300 + 301 + var use_list; 302 + if (new_list.length) { 303 + use_list = new_list; 304 + } else { 305 + use_list = old_list; 306 + } 307 + 308 + var src = use_list[0]; 309 + var dst = use_list[use_list.length - 1]; 310 + 311 + cursor.changeset.newInlineForRange(src, dst); 312 + 313 + this.setFocus(null); 314 + return; 315 + } 250 316 } 251 317 252 318 var pht = this.getTranslations(); 253 - this._warnUser(pht('You must select a comment to reply to.')); 319 + this._warnUser(pht('You must select a comment or change to reply to.')); 254 320 }, 255 321 256 322 _onkeyedit: function() { ··· 804 870 // Clear the mouse hover reticle after a substantive edit: we don't get 805 871 // a "mouseout" event if the row vanished because of row being removed 806 872 // after an edit. 807 - this.reseHover(); 873 + this.resetHover(); 808 874 }, 809 875 810 876 setFocus: function(node, extended_node) { ··· 978 1044 979 1045 var inline_row = e.getNode('inline-row'); 980 1046 return changeset.getInlineForRow(inline_row); 981 - } 1047 + }, 1048 + 1049 + getLineNumberFromHeader: function(th) { 1050 + try { 1051 + return parseInt(th.id.match(/^C\d+[ON]L(\d+)$/)[1], 10); 1052 + } catch (x) { 1053 + return null; 1054 + } 1055 + }, 982 1056 1057 + getDisplaySideFromHeader: function(th) { 1058 + return (th.parentNode.firstChild != th) ? 'right' : 'left'; 1059 + } 983 1060 } 984 1061 985 1062 });
+11 -7
webroot/rsrc/js/application/diff/DiffInline.js
··· 148 148 return true; 149 149 }, 150 150 151 + getRawText: function() { 152 + return this._originalText; 153 + }, 154 + 151 155 _hasAction: function(action) { 152 156 var nodes = JX.DOM.scry(this._row, 'a', 'differential-inline-' + action); 153 157 return (nodes.length > 0); ··· 247 251 .send(); 248 252 }, 249 253 250 - reply: function() { 254 + reply: function(text) { 251 255 var changeset = this.getChangeset(); 252 - return changeset.newInlineReply(this); 256 + return changeset.newInlineReply(this, text); 253 257 }, 254 258 255 259 edit: function(text) { ··· 365 369 }, 366 370 367 371 _oncreateresponse: function(response) { 368 - try { 369 372 var rows = JX.$H(response).getNode(); 370 373 371 374 this._drawEditRows(rows); 372 - } catch (e) { 373 - JX.log(e); 374 - } 375 375 }, 376 376 377 377 _ondeleteresponse: function() { ··· 471 471 'textarea', 472 472 'differential-inline-comment-edit-textarea'); 473 473 if (textareas.length) { 474 - textareas[0].focus(); 474 + var area = textareas[0]; 475 + area.focus(); 476 + 477 + var length = area.value.length; 478 + JX.TextAreaUtils.setSelectionRange(area, length, length); 475 479 } 476 480 477 481 row = next_row;
+1 -14
webroot/rsrc/js/application/differential/behavior-edit-inline-comments.js
··· 72 72 return node.parentNode.firstChild != node; 73 73 } 74 74 75 - function isNewFile(node) { 76 - var data = JX.Stratcom.getData(root); 77 - return isOnRight(node) || (data.left != data.right); 78 - } 79 - 80 75 function getRowNumber(th_node) { 81 76 try { 82 77 return parseInt(th_node.id.match(/^C\d+[ON]L(\d+)$/)[1], 10); ··· 192 187 193 188 var view = JX.DiffChangeset.getForNode(root); 194 189 195 - view.newInlineForRange({ 196 - origin: origin, 197 - target: target, 198 - number: o, 199 - length: len, 200 - changesetID: changeset, 201 - isNewFile: isNewFile(target), 202 - displaySide: isOnRight(target) ? 'right' : 'left' 203 - }); 190 + view.newInlineForRange(origin, target); 204 191 205 192 selecting = false; 206 193 origin = null;