Page Menu
Home
WMGMC Issues
搜索
Configure Global Search
登录
Files
F16294
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
订阅
标记用于日后
授予令牌
Size
19 KB
Referenced Files
None
订阅者
None
View Options
diff --git a/src/applications/differential/view/inlinecomment/DifferentialInlineCommentView.php b/src/applications/differential/view/inlinecomment/DifferentialInlineCommentView.php
index 604004ac5a..930abcaa83 100644
--- a/src/applications/differential/view/inlinecomment/DifferentialInlineCommentView.php
+++ b/src/applications/differential/view/inlinecomment/DifferentialInlineCommentView.php
@@ -1,250 +1,264 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class DifferentialInlineCommentView extends AphrontView {
private $inlineComment;
private $onRight;
private $buildScaffolding;
private $handles;
private $markupEngine;
private $editable;
private $preview;
public function setInlineComment(PhabricatorInlineCommentInterface $comment) {
$this->inlineComment = $comment;
return $this;
}
public function setOnRight($on_right) {
$this->onRight = $on_right;
return $this;
}
public function setBuildScaffolding($scaffold) {
$this->buildScaffolding = $scaffold;
return $this;
}
public function setHandles(array $handles) {
$this->handles = $handles;
return $this;
}
public function setMarkupEngine(PhutilMarkupEngine $engine) {
$this->markupEngine = $engine;
return $this;
}
public function setEditable($editable) {
$this->editable = $editable;
return $this;
}
public function setPreview($preview) {
$this->preview = $preview;
}
public function render() {
$inline = $this->inlineComment;
$start = $inline->getLineNumber();
$length = $inline->getLineLength();
if ($length) {
$end = $start + $length;
$line = 'Lines '.number_format($start).'-'.number_format($end);
} else {
$line = 'Line '.number_format($start);
}
$metadata = array(
'id' => $inline->getID(),
'number' => $inline->getLineNumber(),
'length' => $inline->getLineLength(),
'on_right' => $this->onRight,
'original' => $inline->getContent(),
);
$sigil = 'differential-inline-comment';
$content = $inline->getContent();
$handles = $this->handles;
$links = array();
$is_synthetic = false;
if ($inline->getSyntheticAuthor()) {
$is_synthetic = true;
}
$is_draft = false;
if ($inline->isDraft() && !$is_synthetic) {
$links[] = 'Not Submitted Yet';
$is_draft = true;
}
if (!$this->preview) {
$links[] = javelin_render_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'differential-inline-prev',
),
'Previous');
$links[] = javelin_render_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'differential-inline-next',
),
'Next');
if (!$is_synthetic) {
// NOTE: No product reason why you can't reply to these, but the reply
// mechanism currently sends the inline comment ID to the server, not
// file/line information, and synthetic comments don't have an inline
// comment ID.
$links[] = javelin_render_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'differential-inline-reply',
),
'Reply');
}
}
+ $anchor_name = 'inline-'.$inline->getID();
+
if ($this->editable && !$this->preview) {
$links[] = javelin_render_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'differential-inline-edit',
),
'Edit');
$links[] = javelin_render_tag(
'a',
array(
'href' => '#',
'mustcapture' => true,
'sigil' => 'differential-inline-delete',
),
'Delete');
+ } else if ($this->preview) {
+ $links[] = javelin_render_tag(
+ 'a',
+ array(
+ 'meta' => array(
+ 'anchor' => $anchor_name,
+ ),
+ 'sigil' => 'differential-inline-preview-jump',
+ ),
+ 'Not Visible');
}
if ($links) {
$links =
'<span class="differential-inline-comment-links">'.
implode(' · ', $links).
'</span>';
} else {
$links = null;
}
$cache = $inline->getCache();
if (strlen($cache)) {
$content = $cache;
} else {
$content = $this->markupEngine->markupText($content);
if ($inline->getID()) {
$inline->setCache($content);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$inline->save();
unset($unguarded);
}
}
- $anchor_name = 'inline-'.$inline->getID();
-
- $anchor = phutil_render_tag(
- 'a',
- array(
- 'name' => $anchor_name,
- 'id' => $anchor_name,
- 'class' => 'differential-inline-comment-anchor',
- ),
- '');
+ if ($this->preview) {
+ $anchor = null;
+ } else {
+ $anchor = phutil_render_tag(
+ 'a',
+ array(
+ 'name' => $anchor_name,
+ 'id' => $anchor_name,
+ 'class' => 'differential-inline-comment-anchor',
+ ),
+ '');
+ }
$classes = array(
'differential-inline-comment',
);
if ($is_draft) {
$classes[] = 'differential-inline-comment-unsaved-draft';
}
if ($is_synthetic) {
$classes[] = 'differential-inline-comment-synthetic';
}
$classes = implode(' ', $classes);
if ($is_synthetic) {
$author = $inline->getSyntheticAuthor();
} else {
$author = $handles[$inline->getAuthorPHID()]->getName();
}
$markup = javelin_render_tag(
'div',
array(
'class' => $classes,
'sigil' => $sigil,
'meta' => $metadata,
),
'<div class="differential-inline-comment-head">'.
$anchor.
$links.
' <span class="differential-inline-comment-line">'.$line.'</span> '.
phutil_escape_html($author).
'</div>'.
'<div class="differential-inline-comment-content">'.
'<div class="phabricator-remarkup">'.
$content.
'</div>'.
'</div>');
return $this->scaffoldMarkup($markup);
}
private function scaffoldMarkup($markup) {
if (!$this->buildScaffolding) {
return $markup;
}
$left_markup = !$this->onRight ? $markup : '';
$right_markup = $this->onRight ? $markup : '';
return
'<table>'.
'<tr class="inline">'.
'<th></th>'.
'<td>'.$left_markup.'</td>'.
'<th></th>'.
'<td>'.$right_markup.'</td>'.
'</tr>'.
'</table>';
}
}
diff --git a/webroot/rsrc/css/application/differential/revision-comment.css b/webroot/rsrc/css/application/differential/revision-comment.css
index 99f802df03..6fb291101b 100644
--- a/webroot/rsrc/css/application/differential/revision-comment.css
+++ b/webroot/rsrc/css/application/differential/revision-comment.css
@@ -1,95 +1,106 @@
/**
* @provides differential-revision-comment-css
*/
.differential-comment-list {
max-width: 1162px;
}
.differential-add-comment-panel {
max-width: 1162px;
}
-/* Spooky haunted panel which floats on the bottom of the screen. */
-.differential-haunted-panel .differential-add-comment-panel {
+/* Spooky haunted panel which floats on the bottom of the screen.
+ Haunt modes are:
+
+ - Mode 1: Just the comment box.
+ - Mode 2: Comment box, comment preview, and inline comment previews.
+*/
+.differential-haunt-mode-1 .differential-add-comment-panel,
+.differential-haunt-mode-2 .differential-add-comment-panel {
position: fixed;
width: 100%;
bottom: 0;
right: 0;
left: 0;
z-index: 5;
overflow: auto;
max-height: 375px;
max-width: none;
box-shadow: 0 0 4px #000;
-webkit-box-shadow: 0 0 4px #000;
-moz-box-shadow: 0 0 4px #000;
}
-.differential-haunted-panel .differential-add-comment-panel h1 {
+.differential-haunt-mode-2 .differential-add-comment-panel {
+ max-height: 75%;
+}
+
+.differential-haunt-mode-1 .differential-add-comment-panel h1,
+.differential-haunt-mode-2 .differential-add-comment-panel h1 {
display: none;
}
-.differential-haunted-panel .aphront-panel-preview {
+.differential-haunt-mode-1 .aphront-panel-preview {
display: none;
}
-.differential-haunted-panel {
+.differential-haunt-mode-1 {
padding-bottom: 250px;
}
.differential-comment-list .anchor-target {
background-color: #ffffdd;
border-color: #ffff00;
}
.differential-inline-comment-content {
overflow: auto;
}
.differential-comment-core .phabricator-remarkup .remarkup-code-block {
width: 88ex;
width: 81ch;
}
.phabricator-transaction-view .differential-comment-action-testplan {
border-color: #660099;
}
.phabricator-transaction-view .differential-comment-action-abandon {
border-color: #222222;
}
.phabricator-transaction-view .differential-comment-action-accept {
border-color: #009966;
}
.phabricator-transaction-view .differential-comment-action-reject {
border-color: #aa0000;
}
.phabricator-transaction-view .differential-comment-action-rethink {
border-color: #aa0000;
}
.phabricator-transaction-view .differential-comment-action-commit {
border-color: #006699;
}
.phabricator-transaction-view .differential-comment-action-reclaim {
border-color: #0099aa;
}
.phabricator-transaction-view .differential-comment-action-update {
border-color: #6699cc;
}
.phabricator-transaction-view .differential-comment-action-add_reviewers {
border-color: #aa99cc;
}
.phabricator-transaction-view .differential-comment-action-request_review {
border-color: #cc9966;
}
diff --git a/webroot/rsrc/js/application/differential/behavior-comment-preview.js b/webroot/rsrc/js/application/differential/behavior-comment-preview.js
index 48ffaaa681..104c6e072d 100644
--- a/webroot/rsrc/js/application/differential/behavior-comment-preview.js
+++ b/webroot/rsrc/js/application/differential/behavior-comment-preview.js
@@ -1,62 +1,82 @@
/**
* @provides javelin-behavior-differential-feedback-preview
* @requires javelin-behavior
* javelin-stratcom
* javelin-dom
* javelin-request
* javelin-util
* phabricator-shaped-request
*/
JX.behavior('differential-feedback-preview', function(config) {
var action = JX.$(config.action);
var content = JX.$(config.content);
var previewTokenizers = {};
for (var field in config.previewTokenizers) {
var tokenizer = JX.$(config.previewTokenizers[field]);
previewTokenizers[field] = JX.Stratcom.getData(tokenizer).tokenizer;
}
var callback = function(r) {
JX.DOM.setContent(JX.$(config.preview), JX.$H(r));
};
var getdata = function() {
var data = {
content : content.value,
action : action.value
};
for (var field in previewTokenizers) {
data[field] = JX.keys(previewTokenizers[field].getTokens()).join(',');
}
return data;
};
var request = new JX.PhabricatorShapedRequest(config.uri, callback, getdata);
var trigger = JX.bind(request, request.trigger);
JX.DOM.listen(content, 'keydown', null, trigger);
JX.DOM.listen(action, 'change', null, trigger);
for (var field in previewTokenizers) {
previewTokenizers[field].listen('change', trigger);
}
request.start();
function refreshInlinePreview() {
new JX.Request(config.inlineuri, function(r) {
- JX.DOM.setContent(JX.$(config.inline), JX.$H(r));
+ var inline = JX.$(config.inline);
+
+ JX.DOM.setContent(inline, JX.$H(r));
+
+ // Go through the previews and activate any "View" links where the
+ // actual comment appears in the document.
+
+ var links = JX.DOM.scry(
+ inline,
+ 'a',
+ 'differential-inline-preview-jump');
+ for (var ii = 0; ii < links.length; ii++) {
+ var data = JX.Stratcom.getData(links[ii]);
+ try {
+ JX.$(data.anchor);
+ links[ii].href = '#' + data.anchor;
+ JX.DOM.setContent(links[ii], 'View');
+ } catch (ignored) {
+ // This inline comment isn't visible, e.g. on some other diff.
+ }
+ }
})
.setTimeout(5000)
.send();
}
JX.Stratcom.listen(
'differential-inline-comment-update',
null,
refreshInlinePreview);
refreshInlinePreview();
});
diff --git a/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js b/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js
index 2944095d47..b465efc5f3 100644
--- a/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js
+++ b/webroot/rsrc/js/application/differential/behavior-keyboard-nav.js
@@ -1,230 +1,233 @@
/**
* @provides javelin-behavior-differential-keyboard-navigation
* @requires javelin-behavior
* javelin-dom
* javelin-stratcom
* phabricator-keyboard-shortcut
*/
JX.behavior('differential-keyboard-navigation', function(config) {
var cursor = -1;
var changesets;
var selection_begin = null;
var selection_end = null;
function init() {
if (changesets) {
return;
}
changesets = JX.DOM.scry(document.body, 'div', 'differential-changeset');
}
function getBlocks(cursor) {
// TODO: This might not be terribly fast; we can't currently memoize it
// because it can change as ajax requests come in (e.g., content loads).
var rows = JX.DOM.scry(changesets[cursor], 'tr');
var blocks = [[changesets[cursor], changesets[cursor]]];
var start = null;
var type;
var ii;
function push() {
if (start) {
blocks.push([start, rows[ii - 1]]);
}
start = null;
}
for (ii = 0; ii < rows.length; ii++) {
type = getRowType(rows[ii]);
if (type == 'comment') {
// If we see these types of rows, make a block for each one.
push();
}
if (!type) {
push();
} else if (type && !start) {
start = rows[ii];
}
}
push();
return blocks;
}
function getRowType(row) {
// NOTE: Being somewhat over-general here to allow other types of objects
// to be easily focused in the future (inline comments, 'show more..').
if (row.className.indexOf('inline') !== -1) {
return 'comment';
}
if (row.className.indexOf('differential-changeset') !== -1) {
return 'file';
}
var cells = JX.DOM.scry(row, 'td');
for (var ii = 0; ii < cells.length; ii++) {
// NOTE: The semantic use of classnames here is for performance; don't
// emulate this elsewhere since it's super terrible.
if (cells[ii].className.indexOf('old') !== -1 ||
cells[ii].className.indexOf('new') !== -1) {
return 'change';
}
}
return null;
}
function jump(manager, delta, jump_to_type) {
init();
if (cursor < 0) {
if (delta < 0) {
// If the user goes "back" without a selection, just reject the action.
return;
} else {
cursor = 0;
}
}
while (true) {
var blocks = getBlocks(cursor);
var focus;
if (delta < 0) {
focus = blocks.length;
} else {
focus = -1;
}
for (var ii = 0; ii < blocks.length; ii++) {
if (blocks[ii][0] == selection_begin) {
focus = ii;
break;
}
}
while (true) {
focus += delta;
if (blocks[focus]) {
var row_type = getRowType(blocks[focus][0]);
if (jump_to_type && row_type != jump_to_type) {
continue;
}
selection_begin = blocks[focus][0];
selection_end = blocks[focus][1];
manager.scrollTo(selection_begin);
manager.focusOn(selection_begin, selection_end);
return;
} else {
var adjusted = (cursor + delta);
if (adjusted < 0 || adjusted >= changesets.length) {
// Stop cursor movement when the user reaches either end.
return;
}
cursor = adjusted;
// Break the inner loop and go to the next file.
break;
}
}
}
}
// When inline comments are updated, wipe out our cache of blocks since
// comments may have been added or deleted.
JX.Stratcom.listen(
null,
'differential-inline-comment-update',
function() {
changesets = null;
});
- var is_haunted = false;
+ var haunt_mode = 0;
function haunt() {
- is_haunted = !is_haunted;
- var haunt = JX.$(config.haunt)
- JX.DOM.alterClass(haunt, 'differential-haunted-panel', is_haunted);
+ haunt_mode = (haunt_mode + 1) % 3;
+
+ var el = JX.$(config.haunt);
+ for (var ii = 1; ii <= 2; ii++) {
+ JX.DOM.alterClass(el, 'differential-haunt-mode-'+ii, (haunt_mode == ii));
+ }
}
new JX.KeyboardShortcut('j', 'Jump to next change.')
.setHandler(function(manager) {
jump(manager, 1);
})
.register();
new JX.KeyboardShortcut('k', 'Jump to previous change.')
.setHandler(function(manager) {
jump(manager, -1);
})
.register();
new JX.KeyboardShortcut('J', 'Jump to next file.')
.setHandler(function(manager) {
jump(manager, 1, 'file');
})
.register();
new JX.KeyboardShortcut('K', 'Jump to previous file.')
.setHandler(function(manager) {
jump(manager, -1, 'file');
})
.register();
new JX.KeyboardShortcut('n', 'Jump to next inline comment.')
.setHandler(function(manager) {
jump(manager, 1, 'comment');
})
.register();
new JX.KeyboardShortcut('p', 'Jump to previous inline comment.')
.setHandler(function(manager) {
jump(manager, -1, 'comment');
})
.register();
function inline_op(node, op) {
if (!JX.DOM.scry(node, 'a', 'differential-inline-' + op)) {
// No link for this operation, e.g. editing a comment you can't edit.
return;
}
var data = {
node: JX.DOM.find(node, 'div', 'differential-inline-comment'),
op: op
};
JX.Stratcom.invoke('differential-inline-action', null, data);
}
new JX.KeyboardShortcut('r', 'Reply to selected inline comment.')
.setHandler(function(manager) {
inline_op(selection_begin, 'reply');
})
.register();
new JX.KeyboardShortcut('e', 'Edit selected inline comment.')
.setHandler(function(manager) {
inline_op(selection_begin, 'edit');
})
.register();
if (config.haunt) {
- new JX.KeyboardShortcut('z', 'Haunt / unhaunt comment panel.')
+ new JX.KeyboardShortcut('z', 'Cycle comment panel haunting modes.')
.setHandler(haunt)
.register();
}
});
File Metadata
详情
附加的
Mime Type
text/x-diff
Expires
9月 12 Fri, 3:02 AM (1 d, 2 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5747
默认替代文本
(19 KB)
Attached To
Mode
rP phorge
附加的
Detach File
Event Timeline
Log In to Comment