Refactor CommentBox for drafts and published comments

Add PublishedBox and DraftBox that extend CommentBox.

Change-Id: Ibab8b198499d9f7920ef914b3c2ed88e5daa60d4
This commit is contained in:
Michael Zhou
2013-06-21 15:50:55 -07:00
parent ba149cefc0
commit 41d9278253
11 changed files with 423 additions and 125 deletions

View File

@@ -268,8 +268,11 @@ public class CodeMirrorDemo extends Screen {
Side mySide = info.side(); Side mySide = info.side();
CodeMirror cm = mySide == Side.PARENT ? cmA : cmB; CodeMirror cm = mySide == Side.PARENT ? cmA : cmB;
CodeMirror other = otherCM(cm); CodeMirror other = otherCM(cm);
final CommentBox box = new CommentBox(info.author(), info.updated(), final CommentBox box = isDraft ?
info.message(), commentLinkProcessor, isDraft); new DraftBox(info.author(), info.updated(), info.message(),
commentLinkProcessor) :
new PublishedBox(info.author(), info.updated(), info.message(),
commentLinkProcessor);
int line = info.line() - 1; // CommentInfo is 1-based, but CM is 0-based int line = info.line() - 1; // CommentInfo is 1-based, but CM is 0-based
diffTable.add(box); diffTable.add(box);
cm.addLineWidget(line, box.getElement(), config); cm.addLineWidget(line, box.getElement(), config);

View File

@@ -14,12 +14,8 @@
package com.google.gerrit.client.diff; package com.google.gerrit.client.diff;
import com.google.gerrit.client.AvatarImage;
import com.google.gerrit.client.FormatUtil;
import com.google.gerrit.client.account.AccountInfo; import com.google.gerrit.client.account.AccountInfo;
import com.google.gerrit.client.ui.CommentLinkProcessor; import com.google.gerrit.client.ui.CommentLinkProcessor;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
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.event.shared.HandlerRegistration; import com.google.gwt.event.shared.HandlerRegistration;
@@ -28,18 +24,14 @@ 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.user.client.ui.Composite; import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.Widget;
import com.google.gwtexpui.safehtml.client.SafeHtml; import com.google.gwtexpui.safehtml.client.SafeHtml;
import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder; import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder;
import java.sql.Timestamp; import java.sql.Timestamp;
/** An HtmlPanel holding the DialogBox to display a comment */ /** An HtmlPanel for displaying a comment */
class CommentBox extends Composite { abstract class CommentBox extends Composite {
interface Binder extends UiBinder<HTMLPanel, CommentBox> {}
private static Binder uiBinder = GWT.create(Binder.class);
interface CommentBoxStyle extends CssResource { interface CommentBoxStyle extends CssResource {
String open(); String open();
String close(); String close();
@@ -49,38 +41,21 @@ class CommentBox extends Composite {
private HandlerRegistration headerClick; private HandlerRegistration headerClick;
private Runnable clickCallback; private Runnable clickCallback;
@UiField @UiField(provided=true)
Widget header; CommentBoxHeader header;
@UiField
AvatarImage avatar;
@UiField
Element name;
@UiField
Element summary;
@UiField
Element date;
@UiField
Element contentPanel;
@UiField @UiField
HTML contentPanelMessage; HTML contentPanelMessage;
@UiField @UiField
CommentBoxStyle style; CommentBoxResources res;
CommentBox(AccountInfo author, Timestamp when, String message, protected CommentBox(UiBinder<? extends Widget, CommentBox> binder,
AccountInfo author, Timestamp when, String message,
CommentLinkProcessor linkProcessor, boolean isDraft) { CommentLinkProcessor linkProcessor, boolean isDraft) {
initWidget(uiBinder.createAndBindUi(this));
// TODO: Format the comment box differently based on whether isDraft
// is true.
commentLinkProcessor = linkProcessor; commentLinkProcessor = linkProcessor;
setAuthorNameText(author); header = new CommentBoxHeader(author, when, isDraft);
date.setInnerText(FormatUtil.shortFormatDayTime(when)); initWidget(binder.createAndBindUi(this));
setMessageText(message); setMessageText(message);
setOpen(false); setOpen(false);
} }
@@ -98,12 +73,19 @@ class CommentBox extends Composite {
} }
} }
}, ClickEvent.getType()); }, ClickEvent.getType());
res.style().ensureInjected();
} }
void setOpenCloseHandler(final Runnable callback) { void setOpenCloseHandler(final Runnable callback) {
clickCallback = callback; clickCallback = callback;
} }
protected void runClickCallback() {
if (clickCallback != null) {
clickCallback.run();
}
}
@Override @Override
protected void onUnload() { protected void onUnload() {
super.onUnload(); super.onUnload();
@@ -114,19 +96,13 @@ class CommentBox extends Composite {
} }
} }
private void setAuthorNameText(AccountInfo author) {
// TODO: Set avatar's display to none if we get a 404.
avatar = new AvatarImage(author, 26);
name.setInnerText(FormatUtil.name(author));
}
private void setMessageText(String message) { private void setMessageText(String message) {
if (message == null) { if (message == null) {
message = ""; message = "";
} else { } else {
message = message.trim(); message = message.trim();
} }
summary.setInnerText(message); header.setSummaryText(message);
SafeHtml buf = new SafeHtmlBuilder().append(message).wikify(); SafeHtml buf = new SafeHtmlBuilder().append(message).wikify();
buf = commentLinkProcessor.apply(buf); buf = commentLinkProcessor.apply(buf);
SafeHtml.set(contentPanelMessage, buf); SafeHtml.set(contentPanelMessage, buf);
@@ -134,15 +110,15 @@ class CommentBox extends Composite {
private void setOpen(boolean open) { private void setOpen(boolean open) {
if (open) { if (open) {
removeStyleName(style.close()); removeStyleName(res.style().close());
addStyleName(style.open()); addStyleName(res.style().open());
} else { } else {
removeStyleName(style.open()); removeStyleName(res.style().open());
addStyleName(style.close()); addStyleName(res.style().close());
} }
} }
private boolean isOpen() { private boolean isOpen() {
return getStyleName().contains(style.open()); return getStyleName().contains(res.style().open());
} }
} }

View File

@@ -1,77 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2013 The Android Open Source Project
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.
-->
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:c='urn:import:com.google.gerrit.client'>
<ui:style type='com.google.gerrit.client.diff.CommentBox.CommentBoxStyle'>
.commentBox {
background-color: #e5ecf9;
border: 1px solid black;
}
.table {
width: 100%;
cursor: pointer;
table-layout: fixed;
}
.summary {
width: 60%;
}
.summaryText {
color: #777;
height: 1em;
max-width: 80%;
overflow: hidden;
padding-bottom: 1px;
text-overflow: ellipsis;
white-space: nowrap;
}
.open .summaryText {
display: none;
}
.date {
width: 25%;
text-align: right;
}
.close .contentPanel {
display: none;
}
.message {
margin: 5px;
}
</ui:style>
<g:HTMLPanel styleName='{style.commentBox}'>
<g:HTMLPanel ui:field='header'>
<table class='{style.table}'>
<tr>
<td><c:AvatarImage ui:field='avatar' /></td>
<td ui:field='name'></td>
<td class='{style.summary}'>
<div ui:field='summary' class='{style.summaryText}'></div>
</td>
<td ui:field='date' class='{style.date}'></td>
</tr>
</table>
</g:HTMLPanel>
<div ui:field='contentPanel' class='{style.contentPanel}'>
<g:HTML ui:field='contentPanelMessage' styleName='{style.message}'></g:HTML>
<div>
<button ui:field='reply'><ui:msg>Reply ...</ui:msg></button>
<button ui:field='replyDone'><ui:msg>Reply 'Done'</ui:msg></button>
</div>
</div>
</g:HTMLPanel>
</ui:UiBinder>

View File

@@ -0,0 +1,67 @@
//Copyright (C) 2013 The Android Open Source Project
//
// 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.
package com.google.gerrit.client.diff;
import com.google.gerrit.client.AvatarImage;
import com.google.gerrit.client.FormatUtil;
import com.google.gerrit.client.account.AccountInfo;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTMLPanel;
import java.sql.Timestamp;
/**
* An HtmlPanel representing the header of a CommentBox, displaying
* the author's avatar (if applicable), the author's name, the summary,
* and the date.
*/
class CommentBoxHeader extends Composite {
interface Binder extends UiBinder<HTMLPanel, CommentBoxHeader> {}
private static Binder uiBinder = GWT.create(Binder.class);
@UiField(provided=true)
AvatarImage avatar;
@UiField
Element name;
@UiField
Element summary;
@UiField
Element date;
CommentBoxHeader(AccountInfo author, Timestamp when, boolean isDraft) {
// TODO: Set avatar's display to none if we get a 404.
avatar = new AvatarImage(author, 26);
initWidget(uiBinder.createAndBindUi(this));
String dateText = FormatUtil.shortFormatDayTime(when);
if (isDraft) {
name.setInnerText("(Draft)");
date.setInnerText("Draft saved at " + dateText);
} else {
name.setInnerText(FormatUtil.name(author));
date.setInnerText(dateText);
}
}
void setSummaryText(String message) {
summary.setInnerText(message);
}
}

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2013 The Android Open Source Project
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.
-->
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:c='urn:import:com.google.gerrit.client'>
<ui:with field='res' type='com.google.gerrit.client.diff.CommentBoxResources' />
<g:HTMLPanel>
<table class='{res.style.table}'>
<tr>
<td><c:AvatarImage ui:field='avatar' /></td>
<td ui:field='name'></td>
<td class='{res.style.summary}'>
<div ui:field='summary' class='{res.style.summaryText}'></div>
</td>
<td ui:field='date' class='{res.style.date}'></td>
</tr>
</table>
</g:HTMLPanel>
</ui:UiBinder>

View File

@@ -0,0 +1,38 @@
// Copyright (C) 2013 The Android Open Source Project
//
// 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.
package com.google.gerrit.client.diff;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
/**
* Resources used by diff.
*/
interface CommentBoxResources extends ClientBundle {
@Source("CommentBoxUi.css")
Style style();
interface Style extends CssResource {
String open();
String close();
String commentBox();
String table();
String summary();
String summaryText();
String date();
String contentPanel();
String message();
}
}

View File

@@ -0,0 +1,41 @@
.commentBox {
background-color: #e5ecf9;
border: 1px solid black;
}
.table {
width: 100%;
cursor: pointer;
table-layout: fixed;
}
.summary {
width: 60%;
}
.summaryText {
color: #777;
height: 1em;
max-width: 80%;
overflow: hidden;
padding-bottom: 1px;
text-overflow: ellipsis;
white-space: nowrap;
}
.open .summaryText {
display: none;
}
.date {
width: 25%;
text-align: right;
}
.close .contentPanel {
display: none;
}
.message {
margin: 5px;
}

View File

@@ -0,0 +1,96 @@
//Copyright (C) 2013 The Android Open Source Project
//
// 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.
package com.google.gerrit.client.diff;
import com.google.gerrit.client.account.AccountInfo;
import com.google.gerrit.client.ui.CommentLinkProcessor;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.DoubleClickEvent;
import com.google.gwt.event.dom.client.DoubleClickHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwtexpui.globalkey.client.NpTextArea;
import java.sql.Timestamp;
/** An HtmlPanel for displaying and editing a draft */
//TODO: Make the buttons functional.
class DraftBox extends CommentBox {
interface Binder extends UiBinder<HTMLPanel, DraftBox> {}
private static UiBinder<HTMLPanel, CommentBox> uiBinder =
GWT.create(Binder.class);
interface DraftBoxStyle extends CssResource {
String edit();
String view();
}
@UiField
NpTextArea editArea;
@UiField
DraftBoxStyle draftStyle;
private HandlerRegistration messageClick;
DraftBox(AccountInfo author, Timestamp when, String message,
CommentLinkProcessor linkProcessor) {
super(uiBinder, author, when, message, linkProcessor, true);
setEdit(false);
// TODO: Need a resize handler on editArea.
}
@Override
protected void onLoad() {
super.onLoad();
messageClick = contentPanelMessage.addDomHandler(new DoubleClickHandler() {
@Override
public void onDoubleClick(DoubleClickEvent arg0) {
editArea.setText(contentPanelMessage.getText());
setEdit(!isEdit());
runClickCallback();
}
}, DoubleClickEvent.getType());
}
private void setEdit(boolean edit) {
if (edit) {
removeStyleName(draftStyle.view());
addStyleName(draftStyle.edit());
} else {
removeStyleName(draftStyle.edit());
addStyleName(draftStyle.view());
}
}
private boolean isEdit() {
return getStyleName().contains(draftStyle.edit());
}
@Override
protected void onUnload() {
super.onUnload();
if (messageClick != null) {
messageClick.removeHandler();
messageClick = null;
}
}
}

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2013 The Android Open Source Project
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.
-->
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:d='urn:import:com.google.gerrit.client.diff'
xmlns:c='urn:import:com.google.gwtexpui.globalkey.client'>
<ui:with field='res' type='com.google.gerrit.client.diff.CommentBoxResources' />
<ui:style field='draftStyle' type='com.google.gerrit.client.diff.DraftBox.DraftBoxStyle'>
.edit .messagePanel {
display: none;
}
.view .editArea {
display: none;
}
</ui:style>
<g:HTMLPanel styleName='{res.style.commentBox}'>
<d:CommentBoxHeader ui:field='header' />
<div class='{res.style.contentPanel}'>
<c:NpTextArea ui:field='editArea' styleName='{draftStyle.editArea}'/>
<div>
<button ui:field='save' class='{draftStyle.editArea}'>
<ui:msg>Save</ui:msg>
</button>
<button ui:field='cancel' class='{draftStyle.editArea}'>
<ui:msg>Cancel</ui:msg>
</button>
<button ui:field='discard' class='{draftStyle.editArea}'>
<ui:msg>Discard</ui:msg>
</button>
</div>
</div>
<div class='{res.style.contentPanel}'>
<g:HTML ui:field='contentPanelMessage'
styleName='{res.style.message} {draftStyle.messagePanel}'></g:HTML>
<button ui:field='edit' class='{draftStyle.messagePanel}'>
<ui:msg>Edit</ui:msg>
</button>
</div>
</g:HTMLPanel>
</ui:UiBinder>

View File

@@ -0,0 +1,36 @@
//Copyright (C) 2013 The Android Open Source Project
//
// 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.
package com.google.gerrit.client.diff;
import com.google.gerrit.client.account.AccountInfo;
import com.google.gerrit.client.ui.CommentLinkProcessor;
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.user.client.ui.HTMLPanel;
import java.sql.Timestamp;
/** An HtmlPanel for displaying a published comment */
// TODO: Make the buttons functional.
class PublishedBox extends CommentBox {
interface Binder extends UiBinder<HTMLPanel, PublishedBox> {}
private static UiBinder<HTMLPanel, CommentBox> uiBinder =
GWT.create(Binder.class);
PublishedBox(AccountInfo author, Timestamp when, String message,
CommentLinkProcessor linkProcessor) {
super(uiBinder, author, when, message, linkProcessor, false);
}
}

View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (C) 2013 The Android Open Source Project
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.
-->
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:d='urn:import:com.google.gerrit.client.diff'>
<ui:with field='res' type='com.google.gerrit.client.diff.CommentBoxResources' />
<g:HTMLPanel styleName='{res.style.commentBox}'>
<d:CommentBoxHeader ui:field='header' />
<g:HTMLPanel styleName='{res.style.contentPanel}'>
<g:HTML ui:field='contentPanelMessage' styleName='{res.style.message}'></g:HTML>
<div>
<button ui:field='reply'><ui:msg>Reply ...</ui:msg></button>
<button ui:field='replyDone'><ui:msg>Reply 'Done'</ui:msg></button>
</div>
</g:HTMLPanel>
</g:HTMLPanel>
</ui:UiBinder>