xMerge branch 'ur-sci-3135-comments-section-changes' of https://github.com/urbanrotnik/scinote-web into urbanrotnik-ur-sci-3135-comments-section-changes

This commit is contained in:
Anton Ignatov 2019-04-24 13:40:41 +02:00
commit e52b985e7b
8 changed files with 481 additions and 14 deletions

View file

@ -28,8 +28,17 @@ var Comments = (function() {
});
}
initCommentForm(that);
initCommentFormNew(that);
initCommentsLink(that);
scrollBottom(that.find('.content-comments'));
initCommentsLinkNew(that);
initDeleteCommentNew(that);
initEditCommentNew(that);
var scrollItem = that.find('.content-comments'); // Check for new version of comments
if (that.hasClass('content-comments')) {
scrollItem = that
}
scrollBottom(scrollItem);
});
}
}
@ -84,6 +93,29 @@ var Comments = (function() {
});
}
function initCommentsLinkNew($el) {
$el.find('.btn-more-comments-new').off()
.on('ajax:success', function (e, data) {
if (data.html) {
var stepId = $(this).data('step-id')
var list = $('#comments-list-' + stepId);
var moreBtn = $(this);
list.prepend(data.html);
if (data.resultsNumber < data.perPage) {
moreBtn.remove();
} else {
moreBtn.attr('href', data.moreUrl);
moreBtn.trigger('blur');
}
} else {
$('.btn-more-comments').remove();
}
});
}
// Initialize comment form.
function initCommentForm($el) {
@ -163,6 +195,55 @@ var Comments = (function() {
});
}
function initCommentFormNew($el) {
var stepId = $el.data('step-id');
var $form = $('#new-message-' + stepId).find('form');
var $list = $el.find('.comments-list');
var $submitBtn = $form.find('.new-comment-button')
$submitBtn.on('click', function() {
$form.submit();
});
$('.help-block', $form).addClass('hide');
$form.off().on('ajax:send', function () {
$('#comment_message', $form).attr('readonly', true);
$submitBtn.off('click');
})
.on('ajax:success', function (e, data) {
if (data.html) {
$list.append(data.html).scrollTop(0);
scrollBottom($el);
$('#comment_message', $form).val('');
$('.form-group', $form).removeClass('has-error');
$('.help-block', $form).html('').addClass('hide');
$submitBtn.removeClass('has-error');
var currnetCount = $('#counter-' + stepId).html()
$('#counter-' + stepId).html(parseInt(currnetCount) + 1)
}
})
.on('ajax:error', function (ev, xhr) {
if (xhr.status === 400) {
var messageError = xhr.responseJSON.errors.message;
if (messageError) {
$('.form-group', $form).addClass('has-error');
$('.help-block', $form).html(messageError[0]).removeClass('hide');
$submitBtn.addClass('has-error');
}
}
})
.on('ajax:complete', function () {
scrollBottom($('#comment_message', $form));
$('#comment_message', $form).attr('readonly', false).focus();
$submitBtn.on('click', function() {
$form.submit();
});
});
}
function initCommentOptions(scrollableContainer, useParentOffset) {
if ( ! _.isUndefined(useParentOffset) ) {
useParentOffset = useParentOffset;
@ -260,6 +341,35 @@ var Comments = (function() {
});
}
function initDeleteCommentNew($el) {
var parents = $el.find('.comments-list')
$(parents).unbind('click').on('click', '[data-action=delete-comment-new]', function(e) {
e.preventDefault();
var $this = $(this)
if (confirm($this.attr('data-confirm-message'))) {
$.ajax({
url: $this.attr('data-url'),
type: 'DELETE',
dataType: 'json',
success: function(data) {
var commentEl = $this.closest('.comment-row').hide()
commentEl.remove();
var stepID = $this.attr('data-step-id')
var currnetCount = $('#counter-' + stepID).html()
$('#counter-' + stepID).html(parseInt(currnetCount) - 1)
},
error: function(data) {
// Display alert
alert(data.responseJSON.message);
}
});
}
});
}
function initEditComments(parent) {
$(parent).unbind('click').on('click', '[data-action=edit-comment]', function() {
@ -346,10 +456,110 @@ var Comments = (function() {
});
}
function initEditCommentNew($el) {
var editBtnSelector = '[data-action=edit-comment-new]';
var cancelBtnSelector = '[data-action=cancel-comment-new]';
var saveBtnSelector = '[data-action=save-comment-new]';
var commentMessageSelector = '.comment-message';
var textareaSelector = '.comment-textarea'
var parents = $el.find('.comments-list')
$(parents).on('click', commentMessageSelector, function(e) {
var $this = $(this);
startEditingMode($this.data('comment-id'))
})
$(parents).on('click', editBtnSelector, function(e) {
e.preventDefault();
var $this = $(this);
startEditingMode($this.closest('.comment-row').data('comment-id'))
})
$(parents).on('click', cancelBtnSelector, function(e) {
e.preventDefault();
var $this = $(this);
var commentId = $this.closest('.comment-row').data('comment-id')
var $commentTextArea = $('#comment-textarea-' + commentId);
$commentTextArea.val($commentTextArea.data('message'));
resetAllEditingModes();
})
$(parents).on('focusout', textareaSelector, function(e) {
var $this = $(this);
var $comment = $this.closest('.comment-row')
var commentId = $comment.data('comment-id');
if($this[0].dataset.editing == 0) return;
updateComment(commentId);
})
$(parents).on('click', saveBtnSelector, function(e) {
e.preventDefault();
var commentId = $(this).closest('.comment-row').data('comment-id');
updateComment(commentId);
});
}
function updateComment(commentId){
var $comment = $('#comment-' + commentId);
var $form = $comment.find('form');
var $textarea = $form.find('textarea');
var $commentMessage = $('#comment-message-' + commentId);
var $saveBtn = $comment.find('.save-comment-new');
$form
.off('ajax:send').on('ajax:send', function() {
$textarea.attr('readonly', true);
$saveBtn.addClass('hidden');
$textarea[0].dataset.editing = 0;
})
.off('ajax:success').on('ajax:success', function(ev, data) {
$commentMessage.html(data.comment);
$textarea.data('message', $textarea.val());
resetAllEditingModes();
})
.off('ajax:error').on('ajax:error', function(ev, xhr) {
if (xhr.status === 422) {
alert(xhr.responseJSON.errors.message)
}
else{
alert('Error. Cannot update comment!')
}
})
.off('ajax:complete').on('ajax:complete', function() {
$textarea.attr('readonly', false).focus();
$saveBtn.removeClass('hidden');
});
$form.submit();
}
function startEditingMode(commentId){
resetAllEditingModes();
var $commentTextArea = $('#comment-textarea-' + commentId);
var tempContent = $commentTextArea.val();
$commentTextArea[0].dataset.editing = 1;
$('#comment-'+commentId + ' > .comment-container').addClass('edit');
$commentTextArea.focus().val('').val(tempContent);
}
function resetAllEditingModes() {
$('.comment-container').removeClass('edit');
}
return {
initialize: initializeComments,
scrollBottom: scrollBottom,
moreComments: initCommentsLink,
initCommentsLinkNew: initCommentsLinkNew,
initCommentFormNew: initCommentFormNew,
initDeleteCommentNew: initDeleteCommentNew,
initEditCommentNew: initEditCommentNew,
form: initCommentForm,
initCommentOptions: initCommentOptions,
initDeleteComments: initDeleteComments,

View file

@ -184,5 +184,150 @@
}
}
.step .textarea-sm {
border-radius: 0;
}
.comments-title {
color: $color-emperor;
}
.comment-container {
display: flex;
padding: 5px 0 5px 5px;
&:hover {
background-color: $brand-primary-light;
.comment-actions {
display: inline;
a {
color: $color-silver-chalice;
&:hover {
text-decoration: none;
}
}
a:active {
color: $color-dove-gray;
}
}
}
&.edit {
.comment-textarea {
display: block;
}
.comment-message {
display: none;
}
.edit-actions {
display: none;
}
.editing-actions {
display: inline-block;
}
}
.comment-textarea {
display: none;
}
.comment-message {
display: block;
}
.edit-actions {
display: inline-block;
}
.editing-actions {
display: none;
}
}
.avatar-placehodler {
height: 30px;
width: 30px;
img {
border-radius: 30px;
position: relative;
top: 7px;
}
}
.content-placeholder {
flex-grow: 1;
.row-content {
margin-left: -25px;
}
}
.comment-right {
line-height: 16px;
}
.comment-datetime {
font-size: 12px;
}
.comment-name {
color: $color-silver-chalice;
font-size: 16px;
p {
margin-bottom: 3px;
}
}
.comment-message {
font-size: 14px;
line-height: 17px;
margin-top: -4px;
padding-left: 10px;
}
.comment-actions {
display: none;
font-size: 16px;
margin-right: 20px;
.action-icon-delete {
margin-left: 20px;
}
.edit-label {
font-size: 14px;
margin-left: 3px;
}
}
.step-comments {
overflow: auto;
}
.new-comment-button {
cursor: pointer;
font-size: 18px;
left: calc(100% - 29px);
position: relative;
top: -41px;
&.has-error {
top: -64px;
}
}
.new-message-continer {
float: left;
margin-top: 20px;
width: 100%;
}

View file

@ -19,7 +19,7 @@ class StepCommentsController < ApplicationController
# messages. 'list' partial is used for showing more
# comments.
partial = 'index.html.erb'
partial = 'list.html.erb' if @last_comment_id.positive?
partial = 'steps/comments/list.html.erb' if @last_comment_id.positive?
more_url = ''
if @comments.size.positive?
more_url = url_for(step_step_comments_path(@step,
@ -57,7 +57,7 @@ class StepCommentsController < ApplicationController
format.json {
render json: {
html: render_to_string(
partial: "comment.html.erb",
partial: 'steps/comments/item.html.erb',
locals: {
comment: @comment
}

View file

@ -0,0 +1,42 @@
<% per_page = Constants::COMMENTS_SEARCH_LIMIT %>
<% if can_read_protocol_in_module?(@protocol) %>
<div class="col-xs-12 comments-title">
<h4>
<%=t('protocols.steps.comments') %>
(<span id="counter-<%= step.id %>"><%= comments_count %></span>)
</h4>
</div>
<div class="col-xs-12 step-comment content-comments"
id="new-step-comments-<%= step.id %>"
data-href="<%= url_for step_step_comments_path(step_id: step.id, format: :json) %>"
data-step-id="<%= step.id %>">
<% if comments.size == per_page %>
<div class="comment-more text-center">
<a class="btn btn-default btn-more-comments-new" id="more-btn-<%=step.id %>"
href="<%= url_for(step_step_comments_path(step, format: :json, from: comments.first.id)) %>"
data-remote="true",
data-step-id="<%= step.id %>">
<%=t "general.more_comments" %>
</a>
</div>
<% end %>
<div class="comments-list" id="comments-list-<%= step.id %>">
<%= render partial: 'steps/comments/list.html.erb', locals: { comments: comments} %>
</div>
</div>
<div class="new-message-continer" id="new-message-<%= step.id %>">
<% if can_create_comments_in_module?(@protocol.my_module) %>
<%= bootstrap_form_for( :comment, url: step_step_comments_path(step, format: :json), html: { method: :post }, remote: true) do |f| %>
<%= f.smart_text_area :message,
single_line: true,
hide_label: true,
placeholder: t('general.comment_placeholder_new'),
help: '.',
data: { 'atwho-edit' => '' } %>
<i class="fas fa-paper-plane new-comment-button"></i>
<% end %>
<% end %>
</div>
<% end %>

View file

@ -143,17 +143,11 @@
</div>
<% end %>
</div>
<% if can_read_protocol_in_module?(@protocol) %>
<div class="row">
<div class="step-comment"
id="step-comments-<%= step.id %>"
data-href="<%= url_for step_step_comments_path(step_id: step.id, format: :json) %>">
<%= render partial: "step_comments/index.html.erb",
locals: { step: step, comments: step.last_comments, per_page: Constants::COMMENTS_SEARCH_LIMIT } %>
</div>
</div>
<% end %>
<hr>
<br>
<%= render partial: 'steps/comments.html.erb', locals: { comments: step.last_comments,
comments_count: step.step_comments.count,
step: step } %>
</div>
</div>
</div>

View file

@ -0,0 +1,70 @@
<div class="row comment-row" id="comment-<%= comment.id %>" data-comment-id="<%= comment.id %>">
<div class="comment-container">
<div class="avatar-placehodler">
<%= image_tag avatar_path(comment.user, :icon_small), class: 'avatar' %>
</div>
<div class="content-placeholder">
<div class="row row-content">
<div class="col-xs-4 comment-name"><p><%= comment.user.full_name %></p></div>
<div class="col-xs-8">
<div class="comment-right pull-right">
<div class="comment-datetime pull-right">
<p><%= l(comment.created_at, format: :full) %></p>
</div>
<% if comment.user == current_user %>
<div class="comment-actions pull-right">
<a href="#"
data-action="save-comment-new"
data-url="<%= step_step_comment_path(comment.step, comment, format: :json) %>"
data-turbolinks="false"
class="editing-actions">
<span class="fas fa-save action-icon"></span><span class="edit-label"><%= t('general.save') %></span>
</a>
<a href="#"
data-action="cancel-comment-new"
data-turbolinks="false"
class="editing-actions">
<span class="fas fa-times action-icon-delete"></span>
</a>
<a href="#"
class="edit-actions"
data-action="edit-comment-new"
data-turbolinks="false">
<span class="fas fa-pen action-icon"></span><span class="edit-label"><%= t('general.edit') %></span>
</a>
<a href="#"
class="edit-actions"
data-action="delete-comment-new"
data-url="<%= step_step_comment_path(comment.step, comment, format: :json) %>"
data-confirm-message="<%= t('comments.delete_confirm') %>"
data-step-id="<%= comment.step.id %>"
data-turbolinks="false">
<span class="fas fa-trash action-icon-delete"></span>
</a>
</div>
<% end %>
</div>
</div>
<div class="row">
<div class="col-xs-12 comment-message" id="comment-message-<%= comment.id %>" data-comment-id="<%= comment.id %>">
<%= custom_auto_link(comment.message, team: current_team) %>
</div>
<%= bootstrap_form_for( :comment, url: step_step_comment_path(comment.step, comment, format: :json),
html: { method: :put }, remote: true) do |f| %>
<%= f.smart_text_area :message,
single_line: true,
hide_label: true,
value: comment.message,
data: { 'atwho-edit': '', message: comment.message },
class: 'comment-textarea',
id: 'comment-textarea-' + comment.id.to_s %>
<% end %>
</div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,4 @@
<% comments.each do |comment| %>
<%= render partial: 'steps/comments/item.html.erb',
locals: { comment: comment } %>
<% end %>

View file

@ -1739,6 +1739,7 @@ en:
no_description: "This step has no description."
tables: "Tables"
files: "Attachments (%{count})"
comments: "Comments"
empty_checklist: "No items"
comment_title: "%{user} at %{time}:"
options:
@ -1959,6 +1960,7 @@ en:
no_comments: "No comments!"
more_comments: "More comments"
comment_placeholder: "Your Message"
comment_placeholder_new: "Add new comment…"
module:
one: "task"
other: "tasks"