Add reply button to each cover message comment
Reply button in cover message comment would allow to reply onto a specific comment. When pressed a new cover message comment would be created with the master cover message copied and '>' prefixes inserted. Feature: issue 592 Change-Id: I7ae221d8de09f2b643e3564ffddb03a9a2ceadf6
This commit is contained in:

committed by
Shawn Pearce

parent
08c3cd446d
commit
57d0f41ef9
@@ -417,7 +417,7 @@ public class ChangeScreen2 extends Screen {
|
|||||||
|
|
||||||
private void onReply() {
|
private void onReply() {
|
||||||
if (Gerrit.isSignedIn()) {
|
if (Gerrit.isSignedIn()) {
|
||||||
replyAction.onReply();
|
replyAction.onReply(null);
|
||||||
} else {
|
} else {
|
||||||
Gerrit.doSignIn(getToken());
|
Gerrit.doSignIn(getToken());
|
||||||
}
|
}
|
||||||
@@ -673,7 +673,6 @@ public class ChangeScreen2 extends Screen {
|
|||||||
related.set(info, revision);
|
related.set(info, revision);
|
||||||
reviewers.set(info);
|
reviewers.set(info);
|
||||||
quickApprove.set(info, revision);
|
quickApprove.set(info, revision);
|
||||||
history.set(commentLinkProcessor, changeId, info);
|
|
||||||
|
|
||||||
if (Gerrit.isSignedIn()) {
|
if (Gerrit.isSignedIn()) {
|
||||||
initEditMessageAction(info, revision);
|
initEditMessageAction(info, revision);
|
||||||
@@ -687,6 +686,8 @@ public class ChangeScreen2 extends Screen {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
history.set(commentLinkProcessor, replyAction, changeId, info);
|
||||||
|
|
||||||
if (current) {
|
if (current) {
|
||||||
loadMergeable(info.status(), canSubmit);
|
loadMergeable(info.status(), canSubmit);
|
||||||
}
|
}
|
||||||
|
@@ -40,14 +40,17 @@ import java.util.Set;
|
|||||||
|
|
||||||
class History extends FlowPanel {
|
class History extends FlowPanel {
|
||||||
private CommentLinkProcessor clp;
|
private CommentLinkProcessor clp;
|
||||||
|
private ReplyAction replyAction;
|
||||||
private Change.Id changeId;
|
private Change.Id changeId;
|
||||||
|
|
||||||
private final Set<Integer> loaded = new HashSet<Integer>();
|
private final Set<Integer> loaded = new HashSet<Integer>();
|
||||||
private final Map<AuthorRevision, List<CommentInfo>> byAuthor =
|
private final Map<AuthorRevision, List<CommentInfo>> byAuthor =
|
||||||
new HashMap<AuthorRevision, List<CommentInfo>>();
|
new HashMap<AuthorRevision, List<CommentInfo>>();
|
||||||
|
|
||||||
void set(CommentLinkProcessor clp, Change.Id id, ChangeInfo info) {
|
void set(CommentLinkProcessor clp, ReplyAction ra,
|
||||||
|
Change.Id id, ChangeInfo info) {
|
||||||
this.clp = clp;
|
this.clp = clp;
|
||||||
|
this.replyAction = ra;
|
||||||
this.changeId = id;
|
this.changeId = id;
|
||||||
|
|
||||||
JsArray<MessageInfo> messages = info.messages();
|
JsArray<MessageInfo> messages = info.messages();
|
||||||
@@ -70,6 +73,10 @@ class History extends FlowPanel {
|
|||||||
return changeId;
|
return changeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void replyTo(MessageInfo info) {
|
||||||
|
replyAction.onReply(info);
|
||||||
|
}
|
||||||
|
|
||||||
void addComments(int id, NativeMap<JsArray<CommentInfo>> map) {
|
void addComments(int id, NativeMap<JsArray<CommentInfo>> map) {
|
||||||
loaded.add(id);
|
loaded.add(id);
|
||||||
|
|
||||||
|
@@ -25,11 +25,14 @@ import com.google.gerrit.reviewdb.client.Patch;
|
|||||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
import com.google.gwt.core.client.GWT;
|
import com.google.gwt.core.client.GWT;
|
||||||
import com.google.gwt.dom.client.Element;
|
import com.google.gwt.dom.client.Element;
|
||||||
|
import com.google.gwt.dom.client.Style.Visibility;
|
||||||
import com.google.gwt.event.dom.client.ClickEvent;
|
import com.google.gwt.event.dom.client.ClickEvent;
|
||||||
import com.google.gwt.event.dom.client.ClickHandler;
|
import com.google.gwt.event.dom.client.ClickHandler;
|
||||||
import com.google.gwt.resources.client.CssResource;
|
import com.google.gwt.resources.client.CssResource;
|
||||||
import com.google.gwt.uibinder.client.UiBinder;
|
import com.google.gwt.uibinder.client.UiBinder;
|
||||||
import com.google.gwt.uibinder.client.UiField;
|
import com.google.gwt.uibinder.client.UiField;
|
||||||
|
import com.google.gwt.uibinder.client.UiHandler;
|
||||||
|
import com.google.gwt.user.client.ui.Button;
|
||||||
import com.google.gwt.user.client.ui.Composite;
|
import com.google.gwt.user.client.ui.Composite;
|
||||||
import com.google.gwt.user.client.ui.FlowPanel;
|
import com.google.gwt.user.client.ui.FlowPanel;
|
||||||
import com.google.gwt.user.client.ui.HTMLPanel;
|
import com.google.gwt.user.client.ui.HTMLPanel;
|
||||||
@@ -55,6 +58,7 @@ class Message extends Composite {
|
|||||||
@UiField Element name;
|
@UiField Element name;
|
||||||
@UiField Element summary;
|
@UiField Element summary;
|
||||||
@UiField Element date;
|
@UiField Element date;
|
||||||
|
@UiField Button reply;
|
||||||
@UiField Element message;
|
@UiField Element message;
|
||||||
@UiField FlowPanel comments;
|
@UiField FlowPanel comments;
|
||||||
|
|
||||||
@@ -91,6 +95,19 @@ class Message extends Composite {
|
|||||||
summary.setInnerText(msg);
|
summary.setInnerText(msg);
|
||||||
message.setInnerSafeHtml(history.getCommentLinkProcessor()
|
message.setInnerSafeHtml(history.getCommentLinkProcessor()
|
||||||
.apply(new SafeHtmlBuilder().append(msg).wikify()));
|
.apply(new SafeHtmlBuilder().append(msg).wikify()));
|
||||||
|
} else {
|
||||||
|
reply.getElement().getStyle().setVisibility(Visibility.HIDDEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiHandler("reply")
|
||||||
|
void onReply(ClickEvent e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
if (Gerrit.isSignedIn()) {
|
||||||
|
history.replyTo(info);
|
||||||
|
} else {
|
||||||
|
Gerrit.doSignIn(com.google.gwt.user.client.History.getToken());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -66,7 +66,7 @@ limitations under the License.
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 120px;
|
left: 120px;
|
||||||
width: 915px;
|
width: 880px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -76,7 +76,25 @@ limitations under the License.
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
right: 5px;
|
right: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.reply {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 1px;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
border: none;
|
||||||
|
background: transparent;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 15px;
|
||||||
|
font-family: Arial Unicode MS, sans-serif;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
.closed .reply {
|
||||||
|
visibility: HIDDEN;
|
||||||
}
|
}
|
||||||
</ui:style>
|
</ui:style>
|
||||||
|
|
||||||
@@ -89,6 +107,12 @@ limitations under the License.
|
|||||||
<div class='{style.name}' ui:field='name'/>
|
<div class='{style.name}' ui:field='name'/>
|
||||||
<div ui:field='summary' class='{style.summary}'/>
|
<div ui:field='summary' class='{style.summary}'/>
|
||||||
<div class='{style.date}' ui:field='date'/>
|
<div class='{style.date}' ui:field='date'/>
|
||||||
|
<g:Button styleName='{style.reply}'
|
||||||
|
ui:field='reply'
|
||||||
|
title='Reply to this message'>
|
||||||
|
<ui:attribute name='title'/>
|
||||||
|
<div>↩</div>
|
||||||
|
</g:Button>
|
||||||
</g:HTMLPanel>
|
</g:HTMLPanel>
|
||||||
<div ui:field='message'
|
<div ui:field='message'
|
||||||
aria-hidden='true'
|
aria-hidden='true'
|
||||||
|
@@ -16,6 +16,7 @@ package com.google.gerrit.client.change;
|
|||||||
|
|
||||||
import com.google.gerrit.client.changes.ChangeInfo;
|
import com.google.gerrit.client.changes.ChangeInfo;
|
||||||
import com.google.gerrit.client.changes.ChangeInfo.LabelInfo;
|
import com.google.gerrit.client.changes.ChangeInfo.LabelInfo;
|
||||||
|
import com.google.gerrit.client.changes.ChangeInfo.MessageInfo;
|
||||||
import com.google.gerrit.client.rpc.NativeMap;
|
import com.google.gerrit.client.rpc.NativeMap;
|
||||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
import com.google.gwt.core.client.JsArrayString;
|
import com.google.gwt.core.client.JsArrayString;
|
||||||
@@ -57,7 +58,7 @@ class ReplyAction {
|
|||||||
: NativeMap.<JsArrayString> create();
|
: NativeMap.<JsArrayString> create();
|
||||||
}
|
}
|
||||||
|
|
||||||
void onReply() {
|
void onReply(MessageInfo msg) {
|
||||||
if (popup != null) {
|
if (popup != null) {
|
||||||
popup.hide();
|
popup.hide();
|
||||||
return;
|
return;
|
||||||
@@ -72,6 +73,9 @@ class ReplyAction {
|
|||||||
allLabels = null;
|
allLabels = null;
|
||||||
permittedLabels = null;
|
permittedLabels = null;
|
||||||
}
|
}
|
||||||
|
if (msg != null) {
|
||||||
|
replyBox.replyTo(msg);
|
||||||
|
}
|
||||||
|
|
||||||
final PluginSafePopupPanel p = new PluginSafePopupPanel(true);
|
final PluginSafePopupPanel p = new PluginSafePopupPanel(true);
|
||||||
p.setStyleName(style.replyBox());
|
p.setStyleName(style.replyBox());
|
||||||
|
@@ -18,6 +18,7 @@ import com.google.gerrit.client.Gerrit;
|
|||||||
import com.google.gerrit.client.changes.ChangeApi;
|
import com.google.gerrit.client.changes.ChangeApi;
|
||||||
import com.google.gerrit.client.changes.ChangeInfo.ApprovalInfo;
|
import com.google.gerrit.client.changes.ChangeInfo.ApprovalInfo;
|
||||||
import com.google.gerrit.client.changes.ChangeInfo.LabelInfo;
|
import com.google.gerrit.client.changes.ChangeInfo.LabelInfo;
|
||||||
|
import com.google.gerrit.client.changes.ChangeInfo.MessageInfo;
|
||||||
import com.google.gerrit.client.changes.ReviewInput;
|
import com.google.gerrit.client.changes.ReviewInput;
|
||||||
import com.google.gerrit.client.rpc.GerritCallback;
|
import com.google.gerrit.client.rpc.GerritCallback;
|
||||||
import com.google.gerrit.client.rpc.NativeMap;
|
import com.google.gerrit.client.rpc.NativeMap;
|
||||||
@@ -27,6 +28,7 @@ import com.google.gerrit.reviewdb.client.PatchSet;
|
|||||||
import com.google.gwt.core.client.GWT;
|
import com.google.gwt.core.client.GWT;
|
||||||
import com.google.gwt.core.client.JsArrayString;
|
import com.google.gwt.core.client.JsArrayString;
|
||||||
import com.google.gwt.core.client.Scheduler;
|
import com.google.gwt.core.client.Scheduler;
|
||||||
|
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
|
||||||
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
|
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
|
||||||
import com.google.gwt.dom.client.Element;
|
import com.google.gwt.dom.client.Element;
|
||||||
import com.google.gwt.event.dom.client.ClickEvent;
|
import com.google.gwt.event.dom.client.ClickEvent;
|
||||||
@@ -38,6 +40,7 @@ import com.google.gwt.resources.client.CssResource;
|
|||||||
import com.google.gwt.uibinder.client.UiBinder;
|
import com.google.gwt.uibinder.client.UiBinder;
|
||||||
import com.google.gwt.uibinder.client.UiField;
|
import com.google.gwt.uibinder.client.UiField;
|
||||||
import com.google.gwt.uibinder.client.UiHandler;
|
import com.google.gwt.uibinder.client.UiHandler;
|
||||||
|
import com.google.gwt.user.client.Window;
|
||||||
import com.google.gwt.user.client.ui.Button;
|
import com.google.gwt.user.client.ui.Button;
|
||||||
import com.google.gwt.user.client.ui.CheckBox;
|
import com.google.gwt.user.client.ui.CheckBox;
|
||||||
import com.google.gwt.user.client.ui.Composite;
|
import com.google.gwt.user.client.ui.Composite;
|
||||||
@@ -102,8 +105,18 @@ class ReplyBox extends Composite {
|
|||||||
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
|
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
|
||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
|
Window.scrollTo(0, 0);
|
||||||
message.setFocus(true);
|
message.setFocus(true);
|
||||||
}});
|
}});
|
||||||
|
Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
|
||||||
|
@Override
|
||||||
|
public boolean execute() {
|
||||||
|
String t = message.getText();
|
||||||
|
if (t != null) {
|
||||||
|
message.setCursorPos(t.length());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}}, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiHandler("message")
|
@UiHandler("message")
|
||||||
@@ -155,6 +168,34 @@ class ReplyBox extends Composite {
|
|||||||
hide();
|
hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void replyTo(MessageInfo msg) {
|
||||||
|
if (msg.message() != null) {
|
||||||
|
String t = message.getText();
|
||||||
|
String m = quote(msg);
|
||||||
|
if (t == null || t.isEmpty()) {
|
||||||
|
t = m;
|
||||||
|
} else if (t.endsWith("\n\n")) {
|
||||||
|
t += m;
|
||||||
|
} else if (t.endsWith("\n")) {
|
||||||
|
t += "\n" + m;
|
||||||
|
} else {
|
||||||
|
t += "\n\n" + m;
|
||||||
|
}
|
||||||
|
message.setText(t + "\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String quote(MessageInfo msg) {
|
||||||
|
String m = msg.message().trim();
|
||||||
|
if (m.startsWith("Patch Set ")) {
|
||||||
|
int i = m.indexOf('\n');
|
||||||
|
if (i > 0) {
|
||||||
|
m = m.substring(i + 1).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "> " + m.replaceAll("\\n", "\\\n> ");
|
||||||
|
}
|
||||||
|
|
||||||
private void hide() {
|
private void hide() {
|
||||||
for (Widget w = getParent(); w != null; w = w.getParent()) {
|
for (Widget w = getParent(); w != null; w = w.getParent()) {
|
||||||
if (w instanceof PopupPanel) {
|
if (w instanceof PopupPanel) {
|
||||||
|
Reference in New Issue
Block a user