From 955581d7c749de9add0d5460af43d5af64a9307a Mon Sep 17 00:00:00 2001 From: Splines Date: Wed, 20 Mar 2024 16:33:09 +0100 Subject: [PATCH 1/2] Fix discard behavior in quiz editor -> edit answer --- app/abilities/answer_ability.rb | 4 +- app/assets/javascripts/answers.coffee | 69 ---------------- app/assets/javascripts/answers.coffee.erb | 95 ++++++++++++++++++++++ app/controllers/answers_controller.rb | 7 +- app/views/answers/cancel_edit.js.erb | 39 +++++++++ app/views/answers/update_answer_box.coffee | 3 - config/routes.rb | 6 +- 7 files changed, 141 insertions(+), 82 deletions(-) delete mode 100644 app/assets/javascripts/answers.coffee create mode 100644 app/assets/javascripts/answers.coffee.erb create mode 100644 app/views/answers/cancel_edit.js.erb delete mode 100644 app/views/answers/update_answer_box.coffee diff --git a/app/abilities/answer_ability.rb b/app/abilities/answer_ability.rb index f4b41b3c1..a88cc96ee 100644 --- a/app/abilities/answer_ability.rb +++ b/app/abilities/answer_ability.rb @@ -4,10 +4,8 @@ class AnswerAbility def initialize(user) clear_aliased_actions - can [:new, :create, :update, :destroy], Answer do |answer| + can [:new, :create, :update, :destroy, :cancel_edit], Answer do |answer| answer.question.present? && user.can_edit?(answer.question) end - - can :update_answer_box, Answer end end diff --git a/app/assets/javascripts/answers.coffee b/app/assets/javascripts/answers.coffee deleted file mode 100644 index c7ea9d8c4..000000000 --- a/app/assets/javascripts/answers.coffee +++ /dev/null @@ -1,69 +0,0 @@ -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://coffeescript.org/ - -# change button 'Bearbeiten' to 'verwerfen' after answer body is revealed - -$(document).on 'turbolinks:load', -> - - $(document).on 'shown.bs.collapse', '[id^="collapse-answer-"]', -> - $target = $('#targets-answer-' + $(this).data('id')) - $target.empty().append($target.data('discard')) - .removeClass('btn-primary').addClass('btn-secondary') - return - - # submit form to database after answer body is hidden and restore - # buttons after after answer body is hidden - - $(document).on 'hidden.bs.collapse', '[id^="collapse-answer-"]', -> - answerId = $(this).data('id') - text = $('#tex-area-answer-' + answerId).val() - value = $('#answer-true-' + answerId).is(':checked') - explanation = $('#tex-area-explanation-' + answerId).val() - $target = $('#targets-answer-' + answerId) - $target.empty() - .append($target.data('edit')) - .removeClass('btn-secondary').addClass 'btn-primary' - $.ajax Routes.answer_path(answerId), - type: 'PATCH' - dataType: 'script' - data: { - answer: { - text: text - value: value - explanation: explanation - } - } - return - - # change correctness box for answer if radio button is clicked - - $(document).on 'change', '[id^="answer-value-"]', -> - $.ajax Routes.update_answer_box_path(), - type: 'GET' - dataType: 'script' - data: { - answer_id: $(this).data('id') - value: $('#answer-true-' + $(this).data('id')).is(':checked') - } - error: (jqXHR, textStatus, errorThrown) -> - console.log("AJAX Error: #{textStatus}") - return - - # remove card for new answer if creation of new answer is cancelled - - $(document).on 'click', '#new-answer-cancel', -> - $('#new-answer').show() - $('#new-answer-field').empty() - return - - return - -# clean up everything before turbolinks caches -$(document).on 'turbolinks:before-cache', -> - $(document).off 'shown.bs.collapse', '[id^="collapse-answer-"]' - $(document).off 'hidden.bs.collapse', '[id^="collapse-answer-"]' - $(document).off 'change', '[id^="answer-value-"]' - $(document).off 'click', '#new-answer-cancel' - return - diff --git a/app/assets/javascripts/answers.coffee.erb b/app/assets/javascripts/answers.coffee.erb new file mode 100644 index 000000000..aadce1473 --- /dev/null +++ b/app/assets/javascripts/answers.coffee.erb @@ -0,0 +1,95 @@ +# https://stackoverflow.com/a/8133832/ +<% environment.context_class.instance_eval { include ApplicationHelper } %> + +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ + +# change button 'Bearbeiten' to 'verwerfen' after answer body is revealed + + # the "target button" is either the "discard" button or the "edit" button +# what its purpose is is stored in this object with a mapping +# id -> boolean +targetButtonIsDiscardButton = {} +window.registeredDiscardListeners = new Set(); # set of answer ids + +$(document).on 'turbolinks:load', -> + + $(document).on 'shown.bs.collapse', '[id^="collapse-answer-"]', -> + # Answer is now shown to the user and can be edited + answerId = $(this).data('id'); + registerDiscardListeners(); + targetButtonIsDiscardButton[answerId] = true; + $target = $('#targets-answer-' + answerId) + $target.empty().append($target.data('discard')) + .removeClass('btn-primary').addClass('btn-secondary') + + $(document).on 'hidden.bs.collapse', '[id^="collapse-answer-"]', -> + # Answer is now hidden from the user + answerId = $(this).data('id') + targetButtonIsDiscardButton[answerId] = false; + $target = $('#targets-answer-' + answerId) + $target.empty().append($target.data('edit')) + .removeClass('btn-secondary').addClass('btn-primary') + + # change color of box depending on whether answer is marked + # as correct or incorrect + $(document).on 'change', '[id^="answer-value-"]', -> + id = $(this).data('id') + isCorrectAnswer = $('#answer-true-' + id).is(':checked') + + # Set background color + if isCorrectAnswer + newClass = "<%= bgcolor(true) %>"; + else + newClass = "<%= bgcolor(false) %>"; + $('#answer-header-' + id) + .removeClass('bg-correct') + .removeClass('bg-incorrect') + .addClass(newClass) + + # Set ballot box + answerBox = $('#answer-box-' + id) + answerBox.empty() + if isCorrectAnswer + answerBox.append '<%= ballot_box(true) %>' + else + answerBox.append '<%= ballot_box(false) %>' + + # Cancel new answer creation + $(document).on 'click', '#new-answer-cancel', -> + $('#new-answer').show() + $('#new-answer-field').empty() + +# clean up everything before turbolinks caches +$(document).on 'turbolinks:before-cache', -> + $(document).off 'shown.bs.collapse', '[id^="collapse-answer-"]' + $(document).off 'hidden.bs.collapse', '[id^="collapse-answer-"]' + $(document).off 'change', '[id^="answer-value-"]' + $(document).off 'click', '#new-answer-cancel' + + +registerDiscardListeners = () -> + buttons = $('[id^=targets-answer-]'); + $.each(buttons, (i,btn) -> + btn = $(btn); + answerId = btn.attr('id').split('-')[2]; + + # Don't register listeners multiple times + if answerId in window.registeredDiscardListeners + return; + + window.registeredDiscardListeners.add(answerId); + $(this).on('click', (evt) => + isDiscardButton = targetButtonIsDiscardButton[answerId]; + if not isDiscardButton + return; + + # On discard + $.ajax Routes.cancel_edit_answer_path(answerId), + type: 'GET' + dataType: 'script' + error: (jqXHR, textStatus, errorThrown) -> + console.log("AJAX Error: #{textStatus}") + ); + ); diff --git a/app/controllers/answers_controller.rb b/app/controllers/answers_controller.rb index 937273189..88d24404c 100644 --- a/app/controllers/answers_controller.rb +++ b/app/controllers/answers_controller.rb @@ -1,6 +1,6 @@ # AnswersController class AnswersController < ApplicationController - before_action :set_answer, except: [:new, :create, :update_answer_box] + before_action :set_answer, except: [:new, :create] authorize_resource except: [:new, :create] def current_ability @@ -34,9 +34,8 @@ def destroy @success = true end - def update_answer_box - @answer_id = params[:answer_id].to_i - @value = params[:value] == "true" + def cancel_edit + I18n.locale = @answer.question&.locale_with_inheritance end private diff --git a/app/views/answers/cancel_edit.js.erb b/app/views/answers/cancel_edit.js.erb new file mode 100644 index 000000000..7fafd4631 --- /dev/null +++ b/app/views/answers/cancel_edit.js.erb @@ -0,0 +1,39 @@ +var answerId = <%= @answer.id %>; +var answerCard = $(`#answers-accordion > #answer-card-${answerId}`); +window.registeredDiscardListeners.delete(answerId); + +// eslint-disable-next-line @stylistic/quotes +var newAnswerCardElements = $(`<%= j render partial: 'answers/card', locals: { answer: @answer } %>`).children(); + +// re-render possible MathJax content +// eslint-disable-next-line no-undef +renderMathInElement(newAnswerCardElements.get(0), { + delimiters: [ + { + left: "$$", + right: "$$", + display: true, + }, + { + left: "$", + right: "$", + display: false, + }, + { + left: "\\(", + right: "\\)", + display: false, + }, + { + left: "\\[", + right: "\\]", + display: true, + }, + ], + throwOnError: false, +}, +); + +setTimeout(() => { + answerCard.empty().append(newAnswerCardElements); +}, 100); diff --git a/app/views/answers/update_answer_box.coffee b/app/views/answers/update_answer_box.coffee deleted file mode 100644 index 4d1a0971f..000000000 --- a/app/views/answers/update_answer_box.coffee +++ /dev/null @@ -1,3 +0,0 @@ -$('#answer-box-<%= @answer_id %>').empty().append '<%= ballot_box(@value) %>' -$('#answer-header-<%= @answer_id %>').removeClass('bg-correct') - .removeClass('bg-incorrect').addClass '<%= bgcolor(@value) %>' diff --git a/config/routes.rb b/config/routes.rb index 719a40ff8..3f7a095aa 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -51,9 +51,9 @@ resources :announcements, only: [:index, :new, :create] # answers routes - - get "answers/update_answer_box", - as: "update_answer_box" + get "answers/:id/cancel_edit", + to: "answers#cancel_edit", + as: "cancel_edit_answer" resources :answers, except: [:index, :show, :edit] From 4a37abdeb7d204cef2062a68b9e0422339d35e9a Mon Sep 17 00:00:00 2001 From: Splines Date: Fri, 22 Mar 2024 16:20:10 +0100 Subject: [PATCH 2/2] Clean up code comments --- app/assets/javascripts/answers.coffee.erb | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/app/assets/javascripts/answers.coffee.erb b/app/assets/javascripts/answers.coffee.erb index aadce1473..90e037a20 100644 --- a/app/assets/javascripts/answers.coffee.erb +++ b/app/assets/javascripts/answers.coffee.erb @@ -1,17 +1,13 @@ -# https://stackoverflow.com/a/8133832/ <% environment.context_class.instance_eval { include ApplicationHelper } %> -# Place all the behaviors and hooks related to the matching controller here. -# All this logic will automatically be available in application.js. -# You can use CoffeeScript in this file: http://coffeescript.org/ - -# change button 'Bearbeiten' to 'verwerfen' after answer body is revealed - - # the "target button" is either the "discard" button or the "edit" button -# what its purpose is is stored in this object with a mapping -# id -> boolean +# The "target button" is either the "discard" button or the "edit" button +# what its purpose is is stored in this object with a mapping: +# answerId -> boolean targetButtonIsDiscardButton = {} -window.registeredDiscardListeners = new Set(); # set of answer ids + +# Set of answer ids that have a discard listener registered +# This is to avoid registering the same listener multiple times. +window.registeredDiscardListeners = new Set(); $(document).on 'turbolinks:load', -> @@ -32,8 +28,7 @@ $(document).on 'turbolinks:load', -> $target.empty().append($target.data('edit')) .removeClass('btn-secondary').addClass('btn-primary') - # change color of box depending on whether answer is marked - # as correct or incorrect + # Appearance of box $(document).on 'change', '[id^="answer-value-"]', -> id = $(this).data('id') isCorrectAnswer = $('#answer-true-' + id).is(':checked')