diff --git a/app/assets/javascripts/initializePage.js.erb b/app/assets/javascripts/initializePage.js.erb index 9c513224a..0eba87429 100644 --- a/app/assets/javascripts/initializePage.js.erb +++ b/app/assets/javascripts/initializePage.js.erb @@ -37,6 +37,7 @@ function callInitalizers(){ initializeAnalytics(); initializeCommentDropdown(); initializeSettings(); + initializeCommentPreview(); initializeAdditionalContentBoxes(); if (!initializeLiveArticle.called){ initializeLiveArticle(); diff --git a/app/assets/javascripts/initializers/initializeCommentPreview.js b/app/assets/javascripts/initializers/initializeCommentPreview.js new file mode 100644 index 000000000..a9d3cffe9 --- /dev/null +++ b/app/assets/javascripts/initializers/initializeCommentPreview.js @@ -0,0 +1,50 @@ +function initializeCommentPreview() { + if (document.getElementById('preview-button')) { + document.getElementById('preview-button').onclick = handleCommentPreview; + } +} + +function handleCommentPreview(e) { + e.preventDefault(); + var form = e.target.parentElement.parentElement; + var textArea = form.querySelector('textarea'); + if (textArea.value !== '') { + var previewDiv = form.querySelector('.comment-preview-div'); + var previewButton = form.querySelector('.comment-action-preview'); + if (previewButton.innerHTML.indexOf('PREVIEW') > -1) { + textArea.classList.toggle('preview-loading'); + getAndShowPreview(previewDiv, textArea); + previewButton.innerHTML = 'MARKDOWN'; + } else { + previewDiv.classList.toggle('preview-toggle'); + textArea.classList.toggle('preview-toggle'); + previewButton.innerHTML = 'PREVIEW'; + } + } +} + +function getAndShowPreview(previewDiv, textArea) { + var commentMarkdown = textArea.value; + var payload = JSON.stringify({ + comment: { + body_markdown: commentMarkdown, + }, + }); + getCsrfToken() + .then(sendFetch('comment-preview', payload)) + .then(function(response) { + return response.json(); + }) + .then(successCb) + .catch(function(err) { + console.log('error!'); + console.log(err); + }); + + function successCb(body) { + previewDiv.classList.toggle('preview-toggle'); + textArea.classList.toggle('preview-loading'); + textArea.classList.toggle('preview-toggle'); + previewDiv.innerHTML = body.processed_html; + } +} diff --git a/app/assets/javascripts/initializers/initializeCommentsPage.js.erb b/app/assets/javascripts/initializers/initializeCommentsPage.js.erb index 51e6d796d..9128e4365 100644 --- a/app/assets/javascripts/initializers/initializeCommentsPage.js.erb +++ b/app/assets/javascripts/initializers/initializeCommentsPage.js.erb @@ -228,6 +228,9 @@ function handleCommentSubmit(event) { mainCommentsForm.classList.remove("submitting"); textArea.value = ""; textArea.classList = ""; + var preview = document.getElementById("preview-div"); + preview.classList.add("preview-toggle"); + preview.innerHTML = ""; var container = document.getElementById("comment-trees-container"); container.insertBefore(newNode, container.firstChild); } diff --git a/app/assets/javascripts/utilities/buildCommentFormHTML.js.erb b/app/assets/javascripts/utilities/buildCommentFormHTML.js.erb index c216f177f..2b79323fc 100644 --- a/app/assets/javascripts/utilities/buildCommentFormHTML.js.erb +++ b/app/assets/javascripts/utilities/buildCommentFormHTML.js.erb @@ -9,6 +9,10 @@ function buildCommentFormHTML(commentableId, commentableType, parentId) { ' } var randomIdNumber = Math.floor(Math.random() * 1991) + + var previewDiv = '
' + var previewButton = '' + return ''; } @@ -38,4 +46,4 @@ function cancel(event, el) { event.preventDefault(); replaceActionButts(el.parentNode.parentNode.parentNode.parentNode); initializeCommentsPage(); -} \ No newline at end of file +} diff --git a/app/assets/javascripts/utilities/sendFetch.js b/app/assets/javascripts/utilities/sendFetch.js index 40a8522e9..9e0ba27cd 100644 --- a/app/assets/javascripts/utilities/sendFetch.js +++ b/app/assets/javascripts/utilities/sendFetch.js @@ -61,6 +61,18 @@ function sendFetch(switchStatement, body) { credentials: 'same-origin', }); }; + case 'comment-preview': + return function(csrfToken) { + return window.fetch('/comments/preview', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRF-Token': csrfToken, + }, + body: body, + credentials: 'same-origin', + }); + }; default: console.log('A wrong switchStatement was used.'); break; diff --git a/app/assets/stylesheets/comments.scss b/app/assets/stylesheets/comments.scss index 4b1c7dd67..4647c777f 100644 --- a/app/assets/stylesheets/comments.scss +++ b/app/assets/stylesheets/comments.scss @@ -125,7 +125,7 @@ a.header-link{ position: absolute; left:calc(0.2vw + 43px); bottom:0px; - width:calc(100% - 145px); + width: initial; text-align:left; .uploaded-image { vertical-align:11px; @@ -248,9 +248,57 @@ a.header-link{ &.embiggened-max{ height:calc(30vw + 18px); } + &.preview-loading{ + background: white url(image_path('loading-ellipsis.svg')) no-repeat center center; + background-size: 50px; + } + } + .preview-toggle { + display: none; + } + .comment-preview-div { + padding: 10px 20px 2px; + min-height:calc(13vw + 6px); + text-align: left; + font-size: 19px; + font-family: $helvetica; + background: lighten($yellow, 19%); + box-shadow: $shadow; + width: calc(100% - 80px); + margin: 19px auto 8px; + border: 2px solid $yellow; + p.loading-message{ + opacity:0.6; + } + pre { + overflow-x: auto; + } } .actions{ text-align:right; + + .comment-action-button { + font-family: $helvetica-condensed; + font-stretch:condensed; + color:white; + border:0px; + font-size:13px; + font-weight:500; + margin-top:3px; + padding:5px 12px; + border-radius:3px; + cursor:pointer; + appearance: none; + } + + .comment-action-preview { + background: $dark-gray; + z-index: 10; + } + } + .reply-actions { + margin-bottom: 0; + padding-bottom: 0; } .code-of-conduct { display: block; @@ -272,17 +320,6 @@ a.header-link{ input[type="submit"]{ margin-right:19px; background: $bold-blue; - font-family: $helvetica-condensed; - font-stretch:condensed; - color:white; - border:0px; - font-size:13px; - font-weight:500; - margin-top:3px; - padding:5px 12px; - border-radius:3px; - cursor:pointer; - appearance: none; } &.submitting{ input[type="submit"]{ @@ -677,7 +714,7 @@ a.header-link{ height:20px; } .editor-image-upload{ - width:calc(100% - 150px); + width: initial; text-align:left; .uploaded-image { width: calc( 93% - 54px); @@ -747,6 +784,7 @@ a.header-link{ input[type="submit"]{ padding:5px 8px; margin-bottom:5px; + margin-right:7px; } .cancel{ diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index d9dc1d4ab..a8b0219a5 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -1,7 +1,8 @@ class CommentsController < ApplicationController - before_action :set_comment, only: [ :update, :destroy] + before_action :set_comment, only: %i[update destroy] before_action :set_cache_control_headers, only: [:index] - before_action :raise_banned, only: [:create,:update] + before_action :raise_banned, only: %i[create update] + before_action :authenticate_user!, only: %i[preview create] # GET /comments # GET /comments.json @@ -16,7 +17,7 @@ class CommentsController < ApplicationController if @podcast @user = @podcast - @commentable = @user.podcast_episodes.find_by_slug(params[:slug]) or not_found + (@commentable = @user.podcast_episodes.find_by_slug(params[:slug])) || not_found else @user = User.find_by_username(params[:username]) || Organization.find_by_slug(params[:username]) || @@ -51,10 +52,6 @@ class CommentsController < ApplicationController # POST /comments # POST /comments.json def create - unless current_user - redirect_to "/" - return - end raise if RateLimitChecker.new(current_user).limit_by_situation("comment_creation") @comment = Comment.new(comment_params) @comment.user_id = current_user.id @@ -101,7 +98,7 @@ class CommentsController < ApplicationController # PATCH/PUT /comments/1.json def update raise unless @comment.user_id == current_user.id - if @comment.update(comment_update_params.merge({ edited_at: DateTime.now })) + if @comment.update(comment_update_params.merge(edited_at: DateTime.now)) Mention.create_all(@comment) redirect_to "#{@comment.commentable.path}/comments/#{@comment.id_code_generated}", notice: "Comment was successfully updated." else @@ -125,29 +122,43 @@ class CommentsController < ApplicationController end def delete_confirm - unless current_user && Comment.where(id: params[:id_code].to_i(26), user_id: current_user.id ).first + unless current_user && Comment.where(id: params[:id_code].to_i(26), user_id: current_user.id).first @comment = Comment.find(params[:id_code].to_i(26)) redirect_to @comment.path return end - @comment = Comment.where(id: params[:id_code].to_i(26), user_id: current_user.id ).first + @comment = Comment.where(id: params[:id_code].to_i(26), user_id: current_user.id).first + end + + def preview + begin + fixed_body_markdown = MarkdownFixer.fix_for_preview(comment_params[:body_markdown]) + parsed_markdown = MarkdownParser.new(fixed_body_markdown) + processed_html = parsed_markdown.finalize + rescue StandardError => e + processed_html = "😔 There was a error in your markdown
#{e}
" + end + respond_to do |format| + format.json { render json: { processed_html: processed_html }, status: 200 } + end end private - # Use callbacks to share common setup or constraints between actions. - def set_comment - @comment = Comment.find(params[:id]) - end - # Never trust parameters from the scary internet, only allow the white list through. - def comment_params - params.require(:comment).permit(:body_markdown, - :commentable_id, - :commentable_type, - :parent_id) - end + # Use callbacks to share common setup or constraints between actions. + def set_comment + @comment = Comment.find(params[:id]) + end - def comment_update_params - params.require(:comment).permit(:body_markdown) - end + # Never trust parameters from the scary internet, only allow the white list through. + def comment_params + params.require(:comment).permit(:body_markdown, + :commentable_id, + :commentable_type, + :parent_id) + end + + def comment_update_params + params.require(:comment).permit(:body_markdown) + end end diff --git a/app/labor/hex_comparer.rb b/app/labor/hex_comparer.rb index 3bd9a09a2..0ebc982e3 100644 --- a/app/labor/hex_comparer.rb +++ b/app/labor/hex_comparer.rb @@ -1,14 +1,14 @@ class HexComparer attr_accessor :hexes, :amount - def initialize(hexes, amount=1) + def initialize(hexes, amount = 1) @hexes = hexes @amount = amount end def order hexes.sort - end + end def smallest order.first diff --git a/app/views/comments/_form.html.erb b/app/views/comments/_form.html.erb index 6bca72a94..58bfc5e82 100644 --- a/app/views/comments/_form.html.erb +++ b/app/views/comments/_form.html.erb @@ -26,7 +26,9 @@ id: "text-area", readonly: !user_signed_in?, autofocus: @comment.persisted?, - required: true %> + required: true + %> +