THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Browse Source

comment: Support sub-comment for board comment

tags/v1.11.0
Suwon Chae 1 year ago
parent
commit
6c50ebbabb
5 changed files with 119 additions and 86 deletions
  1. +18
    -0
      app/controllers/BoardApp.java
  2. +44
    -39
      app/views/board/partial_comments.scala.html
  3. +1
    -0
      app/views/board/view.scala.html
  4. +1
    -47
      app/views/issue/view.scala.html
  5. +55
    -0
      public/javascripts/common/yona.SubComment.js

+ 18
- 0
app/controllers/BoardApp.java View File

@@ -42,6 +42,7 @@ import java.util.*;
import static com.avaje.ebean.Expr.icontains;
import static controllers.MigrationApp.composePlainCommentsJson;
import static play.libs.Json.toJson;
import static utils.JodaDateUtil.getOptionalShortDate;

public class BoardApp extends AbstractPostingApp {
public static class SearchCondition extends AbstractPostingApp.SearchCondition {
@@ -412,11 +413,28 @@ public class BoardApp extends AbstractPostingApp {
return redirect(routes.BoardApp.post(project.owner, project.name, number));
}

if(StringUtils.isNotEmpty(comment.parentCommentId)){
comment.setParentComment(PostingComment.find.byId(Long.valueOf(comment.parentCommentId)));
}

if(posting.comments.size() == 0) {
User user = User.find.byId(posting.authorId);
comment.previousContents = getPrevious("Original issue", posting.body, posting.updatedDate, user.loginId);
} else {
Comment previousComment = posting.comments.get(posting.comments.size() - 1);
User user = User.find.byId(previousComment.authorId);
comment.previousContents = getPrevious("Previous comment", previousComment.contents, previousComment.createdDate, user.loginId);
}

Comment savedComment = saveComment(project, posting, comment);

return redirect(RouteUtil.getUrl(savedComment));
}

private static String getPrevious(String templateTitle, String contents, Date updatedDate, String authorLoginId) {
return "\n\n<br />\n\n--- " + templateTitle + " from @" + authorLoginId + " " + getOptionalShortDate(updatedDate) + " ---\n\n<br />\n\n" + contents;
}

// Just made for compatibility. No meanings.
public static Result updateComment(String ownerName, String projectName, Long number, Long commentId) throws IOException {
return newComment(ownerName, projectName, number);


+ 44
- 39
app/views/board/partial_comments.scala.html View File

@@ -14,6 +14,7 @@
@import play.libs.Json.toJson
@import utils.Markdown
@import controllers.api.IssueApi
@import models.enumeration.ResourceType

@isAuthorComment(commentId: String) = @{
if(commentId == UserApp.currentUser().loginId) {"author"}
@@ -26,48 +27,52 @@
<ul class="comments">
@for(comment <-post.comments){
@defining(isAllowed(UserApp.currentUser(), comment.asResource(), Operation.UPDATE)) { isAllowedUpdate =>
<li class="comment @isAuthorComment(comment.authorLoginId)" id="comment-@comment.id">
<div class="comment-avatar">
<a href="@routes.UserApp.userInfo(comment.authorLoginId)" class="avatar-wrap" data-toggle="tooltip" data-placement="top" title="@comment.authorName">
<img src="@User.findByLoginId(comment.authorLoginId).avatarUrl(64)" width="32" height="32" alt="@comment.authorLoginId">
</a>
</div>
<div class="media-body">
<div class="meta-info">
<span class="comment_author">
<span class="resp-comment-avatar">
<a href="@userInfo(comment.authorLoginId)" class="avatar-wrap" data-toggle="tooltip" data-placement="top" title="@comment.authorName">
<img src="@User.findByLoginId(comment.authorLoginId).avatarUrl(64)" width="32" height="32" alt="@comment.authorName">
</a>
</span>
<a href="@routes.UserApp.userInfo(comment.authorLoginId)" data-toggle="tooltip" data-placement="top" title="@comment.authorLoginId"><strong>@comment.authorName</strong></a>
</span>
<span class="ago-date">
<a href="#comment-@comment.id" class="ago" title="@JodaDateUtil.getDateString(comment.createdDate)">@utils.TemplateHelper.agoOrDateString(comment.createdDate)</a>
</span>
<span class="act-row pull-right">
@if(StringUtils.isNotBlank(IssueApi.TRANSLATION_API)){
<button type="button" class="icon btn-transparent ml10 comment-translate" data-toggle="tooltip" data-comment-id="@comment.id" title="@Messages("button.translation")"><i class="yobicon-lang"></i></button>
}
@if(comment.getParentComment == null){
<li class="comment @isAuthorComment(comment.authorLoginId)" id="comment-@comment.id">
@common.childCommentsAnchorDiv(post, comment)
<div class="comment-avatar">
<a href="@routes.UserApp.userInfo(comment.authorLoginId)" class="avatar-wrap" data-toggle="tooltip" data-placement="top" title="@comment.authorName">
<img src="@User.findByLoginId(comment.authorLoginId).avatarUrl(64)" width="32" height="32" alt="@comment.authorLoginId">
</a>
</div>
<div class="media-body">
<div class="meta-info">
<span class="comment_author">
<span class="resp-comment-avatar">
<a href="@userInfo(comment.authorLoginId)" class="avatar-wrap" data-toggle="tooltip" data-placement="top" title="@comment.authorName">
<img src="@User.findByLoginId(comment.authorLoginId).avatarUrl(64)" width="32" height="32" alt="@comment.authorName">
</a>
</span>
<a href="@routes.UserApp.userInfo(comment.authorLoginId)" data-toggle="tooltip" data-placement="top" title="@comment.authorLoginId"><strong>@comment.authorName</strong></a>
</span>
<span class="ago-date">
<a href="#comment-@comment.id" class="ago" title="@JodaDateUtil.getDateString(comment.createdDate)">@utils.TemplateHelper.agoOrDateString(comment.createdDate)</a>
</span>
<span class="act-row pull-right">
@if(StringUtils.isNotBlank(IssueApi.TRANSLATION_API)){
<button type="button" class="icon btn-transparent ml10 comment-translate" data-toggle="tooltip" data-comment-id="@comment.id" title="@Messages("button.translation")"><i class="yobicon-lang"></i></button>
}

@if(isAllowed(UserApp.currentUser(), comment.asResource(), Operation.READ)) {
<button type="button" class="btn-transparent ml10" data-toggle="comment-edit" data-comment-id="@comment.id" title="@Messages("common.comment.edit")"><i class="yobicon-edit-2"></i></button>
}
@if(isAllowed(UserApp.currentUser(), comment.asResource(), Operation.DELETE)) {
<button type="button" class="btn-transparent ml6" data-toggle="comment-delete" data-request-uri="@routes.BoardApp.deleteComment(project.owner, project.name, post.getNumber, comment.id)" title="@Messages("common.comment.delete")"><i class="yobicon-trash"></i></button>
}
</span>
</div>
@if(isAllowed(UserApp.currentUser(), comment.asResource(), Operation.READ)) {
<button type="button" class="btn-transparent ml10" data-toggle="comment-edit" data-comment-id="@comment.id" title="@Messages("common.comment.edit")"><i class="yobicon-edit-2"></i></button>
}
@if(isAllowed(UserApp.currentUser(), comment.asResource(), Operation.DELETE)) {
<button type="button" class="btn-transparent ml6" data-toggle="comment-delete" data-request-uri="@routes.BoardApp.deleteComment(project.owner, project.name, post.getNumber, comment.id)" title="@Messages("common.comment.delete")"><i class="yobicon-trash"></i></button>
}
</span>
</div>

@common.commentUpdateForm(comment, routes.BoardApp.newComment(project.owner, project.name, post.getNumber).toString(), comment.contents, isAllowedUpdate)
@common.commentUpdateForm(comment, routes.BoardApp.newComment(project.owner, project.name, post.getNumber).toString(), comment.contents, isAllowedUpdate)

<div id="comment-body-@comment.id">
@common.tasklistBar()
<div class="comment-body markdown-wrap" data-via-email="@OriginalEmail.exists(comment.asResource)" data-allowed-update="@isAllowedUpdate" >@Html(Markdown.render(comment.contents, project))</div>
<div class="attachments pull-right" data-attachments="@toJson(AttachmentApp.getFileList(ResourceType.NONISSUE_COMMENT.toString(), comment.id.toString()))"></div>
</div>
</div>
</li>
<div id="comment-body-@comment.id">
@common.tasklistBar()
<div class="comment-body markdown-wrap" data-via-email="@OriginalEmail.exists(comment.asResource)" data-allowed-update="@isAllowedUpdate" >@Html(Markdown.render(comment.contents, project))</div>
<div class="attachments pull-right" data-attachments="@toJson(AttachmentApp.getFileList(ResourceType.NONISSUE_COMMENT.toString(), comment.id.toString()))"></div>
</div>
</div>
@common.childComments(post, comment, ResourceType.NONISSUE_COMMENT)
</li>
}
}
}
</ul>

+ 1
- 0
app/views/board/view.scala.html View File

@@ -211,6 +211,7 @@
<script type="text/javascript" src="@routes.Assets.at("javascripts/lib/elevator/jquery.elevator.js")"></script>
<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.Sha1.js")"></script>
<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.Tasklist.js")"></script>
<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.SubComment.js")"></script>
<script type="text/javascript">
$(document).ready(function(){
$yobi.loadModule("board.View", {


+ 1
- 47
app/views/issue/view.scala.html View File

@@ -464,6 +464,7 @@
<script type="text/javascript" src="@routes.Assets.at("javascripts/service/yona.issue.Sharer.js")"></script>
<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.Sha1.js")"></script>
<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.Tasklist.js")"></script>
<script type="text/javascript" src="@routes.Assets.at("javascripts/common/yona.SubComment.js")"></script>
<script type="text/javascript">
$(function(){
// yobi.issue.View
@@ -575,53 +576,6 @@
.addClass($yobi.getContrastColor($this.css('background-color')))
});

// Releated with one line sub-comment feature
$(".add-a-comment").on("click", function(e){
var parent = $(this).parents(".comment");

// Show input form
parent.find(".child-comment-input-form").toggle();

parent.find("textarea").on('keypress', function(e) {
// Enter to submit
if ((e.keyCode || e.which) === 13) {
$(this).parents('form').submit();
return false;
}
}).on('keyup', function(e) {
// Cancel input
if ((e.keyCode || e.which) === 27) {
$(".child-comment-input-form").css("display", "none").css("visibility", "hidden");
$(".add-a-comment").show();
}
}).focus();
});

$(".comment").on("mouseenter tab", function () {
$(this).find(".add-a-comment").fadeIn(300);
}).on("mouseleave", function () {
$(this).find(".add-a-comment").fadeOut(300);
});

// Releated with one line sub-comment feature
$(".subcomment-author").each(function addAuthorToLastParagraphOfOnelineComment(index, el){
// append Author and addtionals to mardkown rendered contents
var $el = $(el);
// Remove spaces
var trimmed = $el.html().replace(/\s\s+/g, ' ');
// Find parent element
var $closest = $el.closest('.contents');
var normalTextRenderedParagraph = $closest.find('p').last();

// Remove unused author and addtional text
$el.remove();
if(normalTextRenderedParagraph.length === 0){
$closest.append(trimmed);
} else {
normalTextRenderedParagraph.append(trimmed);
}
});

$.elevator({
shape: 'rounded',
tooltips: true


+ 55
- 0
public/javascripts/common/yona.SubComment.js View File

@@ -0,0 +1,55 @@
$(function(){
// timeline label text color adjusting
$(".event > .label").each(function() {
var $this = $(this);
$this.removeClass("dimgray white")
.addClass($yobi.getContrastColor($this.css('background-color')))
});

// Releated with one line sub-comment feature
$(".add-a-comment").on("click", function(e){
var parent = $(this).parents(".comment");

// Show input form
parent.find(".child-comment-input-form").toggle();

parent.find("textarea").on('keypress', function(e) {
// Enter to submit
if ((e.keyCode || e.which) === 13) {
$(this).parents('form').submit();
return false;
}
}).on('keyup', function(e) {
// Cancel input
if ((e.keyCode || e.which) === 27) {
$(".child-comment-input-form").css("display", "none").css("visibility", "hidden");
$(".add-a-comment").show();
}
}).focus();
});

$(".comment").on("mouseenter tab", function () {
$(this).find(".add-a-comment").fadeIn(300);
}).on("mouseleave", function () {
$(this).find(".add-a-comment").fadeOut(300);
});

// Releated with one line sub-comment feature
$(".subcomment-author").each(function addAuthorToLastParagraphOfOnelineComment(index, el){
// append Author and addtionals to mardkown rendered contents
var $el = $(el);
// Remove spaces
var trimmed = $el.html().replace(/\s\s+/g, ' ');
// Find parent element
var $closest = $el.closest('.contents');
var normalTextRenderedParagraph = $closest.find('p').last();

// Remove unused author and addtional text
$el.remove();
if(normalTextRenderedParagraph.length === 0){
$closest.append(trimmed);
} else {
normalTextRenderedParagraph.append(trimmed);
}
});
});

Loading…
Cancel
Save