From 9271fdb3c359b7f9ce94bf3f553655f5c3b87106 Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Fri, 10 Feb 2023 16:09:44 +0100 Subject: [PATCH 01/49] Created migrations for annotations. --- .../20221015114100_create_annotations.rb | 15 +++++++++++++ ...124508_add_annotations_status_to_medium.rb | 5 +++++ ...24528_add_annotations_status_to_lecture.rb | 5 +++++ db/schema.rb | 21 +++++++++++++++++-- 4 files changed, 44 insertions(+), 2 deletions(-) create mode 100755 db/migrate/20221015114100_create_annotations.rb create mode 100755 db/migrate/20230117124508_add_annotations_status_to_medium.rb create mode 100755 db/migrate/20230117124528_add_annotations_status_to_lecture.rb diff --git a/db/migrate/20221015114100_create_annotations.rb b/db/migrate/20221015114100_create_annotations.rb new file mode 100755 index 000000000..355b2ece7 --- /dev/null +++ b/db/migrate/20221015114100_create_annotations.rb @@ -0,0 +1,15 @@ +class CreateAnnotations < ActiveRecord::Migration[7.0] + def change + create_table :annotations do |t| + t.references :medium, null: false, foreign_key: true + t.references :user, null: false, foreign_key: true + t.string :timestamp + t.text :comment + t.integer :category + t.boolean :visible_for_teacher + t.string :color + + t.timestamps + end + end +end diff --git a/db/migrate/20230117124508_add_annotations_status_to_medium.rb b/db/migrate/20230117124508_add_annotations_status_to_medium.rb new file mode 100755 index 000000000..6de139be6 --- /dev/null +++ b/db/migrate/20230117124508_add_annotations_status_to_medium.rb @@ -0,0 +1,5 @@ +class AddAnnotationsStatusToMedium < ActiveRecord::Migration[7.0] + def change + add_column :media, :annotations_status, :integer + end +end diff --git a/db/migrate/20230117124528_add_annotations_status_to_lecture.rb b/db/migrate/20230117124528_add_annotations_status_to_lecture.rb new file mode 100755 index 000000000..3039866d7 --- /dev/null +++ b/db/migrate/20230117124528_add_annotations_status_to_lecture.rb @@ -0,0 +1,5 @@ +class AddAnnotationsStatusToLecture < ActiveRecord::Migration[7.0] + def change + add_column :lectures, :annotations_status, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index a0d5c9e3c..c14c66685 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,12 +10,25 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2022_01_25_162730) do - +ActiveRecord::Schema[7.0].define(version: 2023_01_17_124528) do # These are extensions that must be enabled in order to support this database enable_extension "pgcrypto" enable_extension "plpgsql" + create_table "annotations", force: :cascade do |t| + t.bigint "medium_id", null: false + t.bigint "user_id", null: false + t.string "timestamp" + t.text "comment" + t.integer "category" + t.boolean "visible_for_teacher" + t.string "color" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["medium_id"], name: "index_annotations_on_medium_id" + t.index ["user_id"], name: "index_annotations_on_user_id" + end + create_table "announcements", force: :cascade do |t| t.bigint "lecture_id" t.bigint "announcer_id" @@ -273,6 +286,7 @@ t.integer "submission_max_team_size" t.integer "submission_grace_period", default: 15 t.boolean "legacy_seminar", default: false + t.integer "annotations_status" t.index ["teacher_id"], name: "index_lectures_on_teacher_id" t.index ["term_id"], name: "index_lectures_on_term_id" end @@ -351,6 +365,7 @@ t.datetime "released_at", precision: nil t.text "publisher" t.datetime "file_last_edited", precision: nil + t.integer "annotations_status" t.index ["quizzable_type", "quizzable_id"], name: "index_media_on_quizzable_type_and_quizzable_id" t.index ["teachable_type", "teachable_id"], name: "index_media_on_teachable_type_and_teachable_id" end @@ -898,6 +913,8 @@ t.index ["watchlist_entry_id"], name: "index_watchlists_on_watchlist_entry_id" end + add_foreign_key "annotations", "media" + add_foreign_key "annotations", "users" add_foreign_key "announcements", "lectures" add_foreign_key "announcements", "users", column: "announcer_id" add_foreign_key "assignments", "lectures" From a5530ff2c2398061715c13028bf40ce4a6a4a4d0 Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Fri, 10 Feb 2023 16:12:00 +0100 Subject: [PATCH 02/49] Added/changed abilities for annotations. Changed permission of migrations to 100644. --- app/abilities/annotation_ability.rb | 8 ++++++++ app/abilities/medium_ability.rb | 5 +++-- db/migrate/20221015114100_create_annotations.rb | 0 .../20230117124508_add_annotations_status_to_medium.rb | 0 .../20230117124528_add_annotations_status_to_lecture.rb | 0 5 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 app/abilities/annotation_ability.rb mode change 100755 => 100644 db/migrate/20221015114100_create_annotations.rb mode change 100755 => 100644 db/migrate/20230117124508_add_annotations_status_to_medium.rb mode change 100755 => 100644 db/migrate/20230117124528_add_annotations_status_to_lecture.rb diff --git a/app/abilities/annotation_ability.rb b/app/abilities/annotation_ability.rb new file mode 100644 index 000000000..4028eba8d --- /dev/null +++ b/app/abilities/annotation_ability.rb @@ -0,0 +1,8 @@ +class AnnotationAbility + include CanCan::Ability + + def initialize(user) + can :create, Annotation + end + +end diff --git a/app/abilities/medium_ability.rb b/app/abilities/medium_ability.rb index 1a39a4d36..95ca643dd 100644 --- a/app/abilities/medium_ability.rb +++ b/app/abilities/medium_ability.rb @@ -5,7 +5,7 @@ def initialize(user) user ||= User.new clear_aliased_actions - can [:index, :new, :search], Medium + can [:index, :new, :check_annotation_visibility], Medium can [:show, :show_comments], Medium do |medium| medium.visible_for_user?(user) && @@ -50,4 +50,5 @@ def initialize(user) !user.new_record? end end -end \ No newline at end of file +end + diff --git a/db/migrate/20221015114100_create_annotations.rb b/db/migrate/20221015114100_create_annotations.rb old mode 100755 new mode 100644 diff --git a/db/migrate/20230117124508_add_annotations_status_to_medium.rb b/db/migrate/20230117124508_add_annotations_status_to_medium.rb old mode 100755 new mode 100644 diff --git a/db/migrate/20230117124528_add_annotations_status_to_lecture.rb b/db/migrate/20230117124528_add_annotations_status_to_lecture.rb old mode 100755 new mode 100644 From 03777ce9807abb0b1d39cc357552bf34579f0768 Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Fri, 10 Feb 2023 16:15:18 +0100 Subject: [PATCH 03/49] Added/changed asset files for the annotation-tool. --- app/assets/javascripts/annotations.coffee | 3 + app/assets/javascripts/thyme.coffee | 327 ++++++++++++++++++++-- app/assets/stylesheets/annotations.scss | 0 app/assets/stylesheets/media.scss | 17 +- app/assets/stylesheets/thyme.scss | 116 +++++++- 5 files changed, 432 insertions(+), 31 deletions(-) create mode 100644 app/assets/javascripts/annotations.coffee create mode 100644 app/assets/stylesheets/annotations.scss diff --git a/app/assets/javascripts/annotations.coffee b/app/assets/javascripts/annotations.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/annotations.coffee @@ -0,0 +1,3 @@ +# 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/ diff --git a/app/assets/javascripts/thyme.coffee b/app/assets/javascripts/thyme.coffee index 65f8a7a8d..7a461824c 100644 --- a/app/assets/javascripts/thyme.coffee +++ b/app/assets/javascripts/thyme.coffee @@ -1,9 +1,49 @@ +# a boolean that helps to deactivate all key listeners +# for the time the annotation modal opens and the user +# has to write text into the command box +lockKeyListeners = false + +# when callig the updateMarkers() method this will be used to save an +# array containing all annotations +annotations = null + +# helps to find the annotation that is currently shown in the +# annotation area +activeAnnotationId = 0 + +# if the window width (in px) gets over this threshold value, hide the control bar +# (default value) +hideControlBarThreshold = + x: 850 + y: 500 + # convert time in seconds to string of the form H:MM:SS secondsToTime = (seconds) -> date = new Date(null) date.setSeconds seconds return date.toISOString().substr(12, 7) +# convert time in H:MM:SS to seconds +timeToSeconds = (time) -> + array = time.split(':') + return (+array[0]) * 3600 + (+array[1]) * 60 + (+array[2]) + +# lightens up a given color (given in a string in hexadecimal +# representation "#xxyyzz") such that e.g. black becomes dark grey. +# The higher the value of "factor" the brighter the colors become. +lightenUp = (color, factor) -> + red = Math.floor(((factor - 1) * 255 + Number("0x" + color.substr(5, 2))) / factor) + green = Math.floor(((factor - 1) * 255 + Number("0x" + color.substr(3, 2))) / factor) + blue = Math.floor(((factor - 1) * 255 + Number("0x" + color.substr(1, 2))) / factor) + return "#" + blue.toString(16) + green.toString(16) + red.toString(16) + +sortAnnotations = -> + if annotations == null + return + annotations.sort (ann1, ann2) -> + timeToSeconds(ann1.timestamp) - timeToSeconds(ann2.timestamp) + return + # return the start time of the next chapter relative to a given time in seconds nextChapterStart = (seconds) -> chapters = document.getElementById('chapters') @@ -390,6 +430,8 @@ $(document).on 'turbolinks:load', -> nextChapterButton = document.getElementById('next-chapter') previousChapterButton = document.getElementById('previous-chapter') backButton = document.getElementById('back-button') + emergencyButton = document.getElementById('emergency-button') + annotationsToggle = document.getElementById('annotations-toggle-check') # Sliders seekBar = document.getElementById('seek-bar') volumeBar = document.getElementById('volume-bar') @@ -401,6 +443,21 @@ $(document).on 'turbolinks:load', -> # ControlBar videoControlBar = document.getElementById('video-controlBar') + # User is teacher/editor for the given medium? + #-> show toggle annotations button + mediumId = thyme.dataset.medium + $.ajax Routes.check_annotation_visibility_path(mediumId), + type: 'GET' + dataType: 'json' + success: (isPermitted) -> + if isPermitted + $('#volume-controls').css('left', '66%') + $('#speed-control').css('left', '77%') + $('#emergency-button').css('left', '86%') + hideControlBarThreshold.x = 960 + updateControlBarType() + return + # resizes the thyme container to the window dimensions, taking into account # whether the interactive area is displayed or hidden resizeContainer = -> @@ -418,6 +475,10 @@ $(document).on 'turbolinks:load', -> $('#thyme-container').css('width', width + 'px') $('#thyme-container').css('top', top + 'px') $('#thyme-container').css('left', left + 'px') + #iaHeight = $('#annotation-caption').css('height') + #commentHeight = Number(iaHeight.substr(0, iaHeight.length - 2)) - 110 + #$('#annotation-comment').css('height', commentHeight + 'px') + updateMarkers() return # detect IE/edge and inform user that they are not suppported if necessary, @@ -425,6 +486,7 @@ $(document).on 'turbolinks:load', -> if document.documentMode || /Edge/.test(navigator.userAgent) alert($('body').data('badbrowser')) $('#caption').hide() + $('#annotation-caption').hide() $('#video-controlBar').hide() video.style.width = '100%' video.controls = true @@ -438,6 +500,7 @@ $(document).on 'turbolinks:load', -> # on small mobile display, fall back to standard browser player mobileDisplay = -> $('#caption').hide() + $('#annotation-caption').hide() $('#video-controlBar').hide() video.controls = true video.style.width = '100%' @@ -447,50 +510,81 @@ $(document).on 'turbolinks:load', -> largeDisplay = -> video.controls = false $('#caption').show() + $('#annotation-caption').show() $('#video-controlBar').show() video.style.width = '82%' - # directly closes the IA again, if the IA-button status is "-" if iaButton.dataset.status == 'false' iaButton.innerHTML = 'remove_from_queue' $('#caption').hide() + $('#annotation-caption').hide() video.style.width = '100%' $('#video-controlBar').css('width', '100%') $(window).trigger('resize') return - # display native control bar if screen is very small - if window.matchMedia("screen and (max-width: 767px)").matches - mobileDisplay() - - if window.matchMedia("screen and (max-device-width: 767px)").matches - mobileDisplay() - - # mediaQuery listener for very small screens - match_verysmall = window.matchMedia("screen and (max-width: 767px)") - match_verysmall.addListener (result) -> - if result.matches + updateControlBarType = -> + # display native control bar if screen is very small + if window.matchMedia("screen and (max-width: " + hideControlBarThreshold.x + "px)").matches or + window.matchMedia("screen and (max-height: " + hideControlBarThreshold.y + "px)").matches mobileDisplay() - return - match_verysmalldevice = window.matchMedia("screen and (max-device-width: 767px)") - match_verysmalldevice.addListener (result) -> - if result.matches + if window.matchMedia("screen and (max-device-width: " + hideControlBarThreshold.x + "px)").matches or + window.matchMedia("screen and (max-device-height: " + hideControlBarThreshold.y + "px)").matches mobileDisplay() - return - # mediaQuery listener for normal screens - match_normal = window.matchMedia("screen and (min-width: 768px)") - match_normal.addListener (result) -> - if result.matches - largeDisplay() - return + # mediaQuery listener for very small screens + match_verysmall_x = window.matchMedia("screen and (max-width: " + hideControlBarThreshold.x + "px)") + match_verysmall_x.addListener (result) -> + if result.matches + mobileDisplay() + return + match_verysmall_y = window.matchMedia("screen and (max-height: " + hideControlBarThreshold.y + "px)") + match_verysmall_y.addListener (result) -> + if result.matches + mobileDisplay() + return - match_normal = window.matchMedia("screen and (min-device-width: 768px)") - match_normal.addListener (result) -> - if result.matches - largeDisplay() + match_verysmalldevice_x = window.matchMedia("screen and (max-device-width: " + hideControlBarThreshold.x + "px)") + match_verysmalldevice_x.addListener (result) -> + if result.matches + mobileDisplay() + return + match_verysmalldevice_y = window.matchMedia("screen and (max-device-height: " + hideControlBarThreshold.y + "px)") + match_verysmalldevice_y.addListener (result) -> + if result.matches + mobileDisplay() + return + + # mediaQuery listener for normal screens + match_normal_x = window.matchMedia("screen and (min-width: " + (hideControlBarThreshold.x + 1) + "px)") + match_normal_x.addListener (result) -> + match_normal_y = window.matchMedia("screen and (min-height: " + (hideControlBarThreshold.y + 1) + "px)") + if result.matches && match_normal_y.matches + largeDisplay() + return + match_normal_y = window.matchMedia("screen and (min-height: " + (hideControlBarThreshold.y + 1) + "px)") + match_normal_y.addListener (result) -> + match_normal_x = window.matchMedia("screen and (min-width: " + (hideControlBarThreshold.x + 1) + "px)") + if result.matches && match_normal_x.matches + largeDisplay() + return + + match_normaldevice_x = window.matchMedia("screen and (min-device-width: " + (hideControlBarThreshold.x + 1) + "px)") + match_normaldevice_x.addListener (result) -> + match_normaldevice_y = window.matchMedia("screen and (min-device-height: " + (hideControlBarThreshold.y + 1) + "px)") + if result.matches && match_normal_y.matches + largeDisplay() + return + match_normaldevice_y = window.matchMedia("screen and (min-device-height: " + (hideControlBarThreshold.y + 1) + "px)") + match_normaldevice_y.addListener (result) -> + match_normaldevice_x = window.matchMedia("screen and (min-device-width: " + (hideControlBarThreshold.x + 1) + "px)") + if result.matches && match_normal_x.matches + largeDisplay() + return return + updateControlBarType() + window.onresize = resizeContainer video.onloadedmetadata = resizeContainer @@ -547,6 +641,57 @@ $(document).on 'turbolinks:load', -> video.currentTime = previousChapterStart(video.currentTime) if previous? return + # Event handler for the emergency button + emergencyButton.addEventListener 'click', -> + video.pause() + # round time to full seconds + time = video.currentTime + intTime = Math.floor(time) + roundTime = intTime + mediumId = thyme.dataset.medium + $.ajax Routes.new_annotation_path(), + type: 'GET' + dataType: 'script' + data: { + timestamp: secondsToTime(roundTime) + mediumId: mediumId + } + # When the modal opens, all key listeners must be + # deactivated until the modal gets closed again + lockKeyListeners = true + $('#annotation-modal').on('hidden.bs.modal', -> + lockKeyListeners = false + ) + return + + if annotationsToggle != null + annotationsToggle.addEventListener 'click', -> + updateMarkers() + + # Update annotations after submitting the annotations form + $(document).on 'click', '#submit-button', -> + # NOTE: + # Updating might take some time on the backend, + # so I added a slight delay. + # I couldn't think of an easy way to let the script + # wait for the update to complete (as with the delete button), + # but it might be possible! + setTimeout(updateMarkers, 500) + + # Update annotations after deleting an annotation + $(document).on 'click', '#delete-button', -> + annotationId = Number(document.getElementById('annotation_id').textContent) + $.ajax Routes.annotation_path(annotationId), + type: 'DELETE' + dataType: 'json' + data: { + annotationId: annotationId + } + success: -> + updateMarkers() + $('#annotation-close-button').click() + return + # Event handler for speed speed selector speedSelector.addEventListener 'change', -> if video.preservesPitch? @@ -564,6 +709,7 @@ $(document).on 'turbolinks:load', -> iaButton.innerHTML = 'remove_from_queue' iaButton.dataset.status = 'false' $('#caption').hide() + $('#annotation-caption').hide() video.style.width = '100%' $('#video-controlBar').css('width', '100%') $(window).trigger('resize') @@ -573,6 +719,7 @@ $(document).on 'turbolinks:load', -> video.style.width = '82%' $('#video-controlBar').css('width', '82%') $('#caption').show() + $('#annotation-caption').show() $(window).trigger('resize') return @@ -729,6 +876,8 @@ $(document).on 'turbolinks:load', -> # m - mute # i - toggle interactive area window.addEventListener 'keydown', (evt) -> + if lockKeyListeners == true + return key = evt.key if key == ' ' if video.paused == true @@ -754,4 +903,126 @@ $(document).on 'turbolinks:load', -> else if key == 'i' $(iaButton).trigger('click') return - return + + # updates the annotation markers + updateMarkers = -> + mediumId = thyme.dataset.medium + toggled = $('#annotations-toggle-check').is(":checked") + $.ajax Routes.update_markers_path(), + type: 'GET' + dataType: 'json' + data: { + mediumId: mediumId + toggled: toggled + } + success: (annots) -> + annotations = annots + sortAnnotations() + $('#markers').empty() + if annotations == null + return + flag = false + for annotation in annotations + createMarker(annotation) + if annotation.id == activeAnnotationId + updateAnnotationArea(annotation) + flag = true + if flag == false && $('#annotation-caption').is(":visible") == true + $('#annotation-caption').hide() + $('#caption').show() + return + return + + # an auxiliary method for "updateMarkers()" creating a single marker + createMarker = (annotation) -> + # create marker + markerStr = ' + + + ' + $('#markers').append(markerStr) + # set the correct position for the marker + marker = $('#marker-' + annotation.id) + size = seekBar.clientWidth - 13 + ratio = timeToSeconds(annotation.timestamp) / video.duration + offset = marker.parent().offset().left + ratio * size + 3 + marker.offset({ left: offset }) + marker.on 'click', -> + updateAnnotationArea(annotation) + $('#caption').hide() + $('#annotation-caption').show() + return + + updateAnnotationArea = (annotation) -> + activeAnnotationId = annotation.id + comment = annotation.comment.replaceAll('\n', '
') + headColor = lightenUp(annotation.color, 2) + backgroundColor = lightenUp(annotation.color, 3) + $('#annotation-infobar').empty().append(annotation.category) + $('#annotation-infobar').css('background-color', headColor) + $('#annotation-infobar').css('text-align', 'center') + $('#annotation-comment').empty().append(comment) + $('#annotation-caption').css('background-color', backgroundColor) + # remove old listeners + $('#annotation-previous-button').off 'click' + $('#annotation-next-button').off 'click' + $('#annotation-goto-button').off 'click' + $('#annotation-edit-button').off 'click' + $('#annotation-close-button').off 'click' + # previous annotation listener + $('#annotation-previous-button').on 'click', -> + for i in [0 .. annotations.length - 1] + if i != 0 && annotations[i] == annotation + updateAnnotationArea(annotations[i - 1]) + # next annotation Listener + $('#annotation-next-button').on 'click', -> + for i in [0 .. annotations.length - 1] + if i != annotations.length - 1 && annotations[i] == annotation + updateAnnotationArea(annotations[i + 1]) + # goto listener + $('#annotation-goto-button').on 'click', -> + video.currentTime = timeToSeconds(annotation.timestamp) + # edit listener + $('#annotation-edit-button').on 'click', -> + lockKeyListeners = true + $.ajax Routes.edit_annotation_path(annotation.id), + type: 'GET' + dataType: 'script' + data: { + annotationId: annotation.id + } + # close listener + $('#annotation-close-button').on 'click', -> + activeAnnotationId = 0 + $('#annotation-caption').hide() + $('#caption').show() + # LaTex + renderMathInElement document.getElementById('annotation-comment'), + delimiters: [ + { + left: '$$' + right: '$$' + display: true + } + { + left: '$' + right: '$' + display: false + } + { + left: '\\(' + right: '\\)' + display: false + } + { + left: '\\[' + right: '\\]' + display: true + } + ] + throwOnError: false + return + + return \ No newline at end of file diff --git a/app/assets/stylesheets/annotations.scss b/app/assets/stylesheets/annotations.scss new file mode 100644 index 000000000..e69de29bb diff --git a/app/assets/stylesheets/media.scss b/app/assets/stylesheets/media.scss index ca56d63ec..9ea3b2dfe 100644 --- a/app/assets/stylesheets/media.scss +++ b/app/assets/stylesheets/media.scss @@ -328,4 +328,19 @@ .tab-content > .active { visibility: visible; -} \ No newline at end of file +} + +/* annotation modal */ +#annotation-modal { + #annotation_comment { + height: 150px; + resize: none; + } + #annotation_category_text { + width: 200px; + } + #annotation_visible_for_teacher { + width: 18px; + height: 18px; + } +} diff --git a/app/assets/stylesheets/thyme.scss b/app/assets/stylesheets/thyme.scss index 6fbed3b35..a1f5d4967 100644 --- a/app/assets/stylesheets/thyme.scss +++ b/app/assets/stylesheets/thyme.scss @@ -125,7 +125,7 @@ position: absolute; padding: 3px; font-size: 12px; - left: 81%; + left: 79%; } #volume-bar { @@ -135,6 +135,84 @@ margin-left: 4px; } +#emergency-button { + position: absolute; + display: flex; + left: 89%; +} + +#annotations-toggle { + position: absolute; + display: flex; + left: 89%; + top: 50%; + -ms-transform: translateY(-50%); + transform: translateY(-50%); +} + +/* The switch - the box around the slider */ +.switch { + position: relative; + display: inline-block; + width: 30px; + height: 17px; +} + +/* Hide default HTML checkbox */ +.switch input { + opacity: 0; + width: 0; + height: 0; +} + +/* The slider */ +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; +} + +.slider:before { + position: absolute; + content: ""; + height: 13px; + width: 13px; + left: 2px; + bottom: 2px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; +} + +input:checked + .slider { + background-color: #2196F3; +} + +input:focus + .slider { + box-shadow: 0 0 1px #2196F3; +} + +input:checked + .slider:before { + -webkit-transform: translateX(13px); + -ms-transform: translateX(13px); + transform: translateX(13px); +} + +/* Rounded sliders */ +.slider.round { + border-radius: 17px; +} + +.slider.round:before { + border-radius: 50%; +} + #size-buttons { position: absolute; right: 0%; @@ -325,7 +403,7 @@ figure { .replay { position: absolute; top: 0; - right:0; + right: 0; padding: 3px; cursor: pointer; -webkit-user-select: none; @@ -341,3 +419,37 @@ figure { { background-color: black; } + + +#markers { + position: relative; + top: -2px; + width: 0; + height: 0; + display: flex; + cursor: pointer; +} + +#annotation-infobar { + height: 2em; + color: black; + overflow-y: scroll; + padding: 3px; + border-left: 1px solid darkgray; + border-right: 1px solid darkgray; + border-top: 1px solid darkgray; +} + +#annotation-comment { + margin-left: 3px; + margin-right: 3px; + overflow-y: scroll; + height: 85%; +} + +#annotation-buttons { + display: flex; + justify-content: space-between; + margin-left: 10%; + margin-right: 10%; +} \ No newline at end of file From f9d0bb88b6b2cb0ddc9dc443f88383d3ad72d522 Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Fri, 10 Feb 2023 16:19:03 +0100 Subject: [PATCH 04/49] Added/changed controllers for annotation-tool. --- app/controllers/annotations_controller.rb | 79 +++++++++++++++++++++++ app/controllers/lectures_controller.rb | 7 ++ app/controllers/media_controller.rb | 7 ++ 3 files changed, 93 insertions(+) create mode 100644 app/controllers/annotations_controller.rb diff --git a/app/controllers/annotations_controller.rb b/app/controllers/annotations_controller.rb new file mode 100644 index 000000000..7d1993ef7 --- /dev/null +++ b/app/controllers/annotations_controller.rb @@ -0,0 +1,79 @@ +class AnnotationsController < ApplicationController + + def create + @annotation = Annotation.new(annotation_params) + @annotation.user_id = current_user.id + @annotation.category = category_translation + @annotation.save + end + + def edit + @annotation = Annotation.find(params[:annotationId]) + @timestamp = @annotation.timestamp + @medium_id = @annotation.medium_id + end + + def new + @annotation = Annotation.new + @timestamp = params[:timestamp] + @medium_id = params[:mediumId] + end + + def show + @annotation = Annotation.find(params[:id]) + end + + def update + @annotation = Annotation.find(params[:id]) + @annotation.update(annotation_params) + end + + def destroy + Annotation.find(params[:annotationId]).destroy + render json: [] + end + + def update_markers + medium = Medium.find_by_id(params[:mediumId]) + toggled = params[:toggled] + + if medium.annotations_visible?(current_user) && toggled == "true" + annots = Annotation.where(medium: medium, + visible_for_teacher: true).or( + Annotation.where(medium: medium, + user: current_user)) + else + annots = Annotation.where(medium: medium, + user: current_user) + end + + render json: annots + end + + + + private + + def annotation_params + params.require(:annotation).permit( + :color, :comment, :medium_id, + :timestamp, :visible_for_teacher) + end + + def category_translation + category = params[:annotation][:category_text] + case category + when 'Need help!' + return Annotation.categories[:help] + when 'Found a mistake' + return Annotation.categories[:mistake] + when 'Give a comment' + return Annotation.categories[:comment] + when 'Note' + return Annotation.categories[:note] + when 'Other' + return Annotation.categories[:other] + end + end + +end diff --git a/app/controllers/lectures_controller.rb b/app/controllers/lectures_controller.rb index bf7a63ffb..88ba2bf2c 100644 --- a/app/controllers/lectures_controller.rb +++ b/app/controllers/lectures_controller.rb @@ -228,6 +228,12 @@ def search_examples def close_comments @lecture.close_comments!(current_user) + # disable emergency button + @lecture.update(annotations_status: -1) + @lecture.media.update(annotations_status: 0) + @lecture.lessons.each do |lesson| + lesson.media.update(annotations_status: 0) + end redirect_to edit_lecture_path(@lecture) end @@ -310,6 +316,7 @@ def lecture_params :comments_disabled, :submission_max_team_size, :submission_grace_period, + :annotation_status, editor_ids: []) end diff --git a/app/controllers/media_controller.rb b/app/controllers/media_controller.rb index 3f4c03d8a..6477bad9e 100644 --- a/app/controllers/media_controller.rb +++ b/app/controllers/media_controller.rb @@ -496,6 +496,12 @@ def fill_reassign_modal @no_rights = params[:rights] == 'none' end + def check_annotation_visibility + medium = Medium.find_by_id(params[:id]) + isPermitted = medium.annotations_visible?(current_user) + render json: isPermitted + end + private def medium_params @@ -505,6 +511,7 @@ def medium_params :teachable_type, :teachable_id, :released, :text, :locale, :content, :boost, + :annotations_status, editor_ids: [], tag_ids: [], linked_medium_ids: []) From 7bab1a1dcf6ca65b1badb575452789f1a126f86b Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Fri, 10 Feb 2023 16:24:58 +0100 Subject: [PATCH 05/49] Added/changed models for the annotation-tool. Added (empty) annotation helper. --- app/helpers/annotations_helper.rb | 2 ++ app/models/annotation.rb | 5 +++++ app/models/medium.rb | 29 ++++++++++++++++++++++++++++- 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 app/helpers/annotations_helper.rb create mode 100644 app/models/annotation.rb diff --git a/app/helpers/annotations_helper.rb b/app/helpers/annotations_helper.rb new file mode 100644 index 000000000..442c10bc0 --- /dev/null +++ b/app/helpers/annotations_helper.rb @@ -0,0 +1,2 @@ +module AnnotationsHelper +end diff --git a/app/models/annotation.rb b/app/models/annotation.rb new file mode 100644 index 000000000..720643d29 --- /dev/null +++ b/app/models/annotation.rb @@ -0,0 +1,5 @@ +class Annotation < ApplicationRecord + belongs_to :medium + belongs_to :user + enum category: { other: 0, note: 1, comment: 2, mistake: 3, help: 4 } +end diff --git a/app/models/medium.rb b/app/models/medium.rb index 687e24a37..a4c31de54 100644 --- a/app/models/medium.rb +++ b/app/models/medium.rb @@ -1044,7 +1044,34 @@ def subscribed_users Lecture.find_by(id: teachable.lecture_id).user_ids end - + + def get_annotations_status + if annotations_status != 0 + return annotations_status + else + return lecture.annotations_status + end + end + + def annotations_visible?(user) + # find lecture to which this medium is associated (might be nil) + if lesson != nil + lecture = lesson.lecture + end + + # If the medium is associated to a lecture/course + # and the user is a teacher/editor of this lecture, + # return true + if lecture&.teacher == user || + lecture&.editors&.include?(user) || + editors&.include?(user) || + course&.editors&.include?(user) + return true + else + return false + end + end + private # media of type kaviar associated to a lesson and script do not require From 306a7b9781b90fdf59cf1ef64a9c642f32b77b8a Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Fri, 10 Feb 2023 16:30:38 +0100 Subject: [PATCH 06/49] Added/edited view files for the annotation-tool. --- .../annotations/_annotation_modal.html.erb | 21 ++++++ app/views/annotations/_form.html.erb | 71 +++++++++++++++++++ app/views/annotations/create.coffee | 1 + app/views/annotations/edit.coffee | 3 + app/views/annotations/get_marker.coffee | 1 + app/views/annotations/new.coffee | 3 + app/views/annotations/update.coffee | 1 + app/views/lectures/edit/_comments.html.erb | 27 ++++++- app/views/media/_basics.html.erb | 36 ++++++++++ app/views/media/play.html.erb | 61 +++++++++++++++- 10 files changed, 223 insertions(+), 2 deletions(-) create mode 100644 app/views/annotations/_annotation_modal.html.erb create mode 100644 app/views/annotations/_form.html.erb create mode 100644 app/views/annotations/create.coffee create mode 100644 app/views/annotations/edit.coffee create mode 100644 app/views/annotations/get_marker.coffee create mode 100644 app/views/annotations/new.coffee create mode 100644 app/views/annotations/update.coffee diff --git a/app/views/annotations/_annotation_modal.html.erb b/app/views/annotations/_annotation_modal.html.erb new file mode 100644 index 000000000..b9e0eefeb --- /dev/null +++ b/app/views/annotations/_annotation_modal.html.erb @@ -0,0 +1,21 @@ + + +
+ Enable Emergency Button? +
+
+ <%= f.radio_button :annotations_status, + 1, + class: 'custom-control-input' %> + <%= f.label :annotations_status, + 'yes', + value: 1, + class: 'custom-control-label' %> +
+
+ <%= f.radio_button :annotations_status, + -1, + class: 'custom-control-input' %> + <%= f.label :annotations_status, + 'no', + value: -1, + class: 'custom-control-label' %> +
+
+
+
@@ -58,4 +83,4 @@ <% end %>
-
\ No newline at end of file + diff --git a/app/views/media/_basics.html.erb b/app/views/media/_basics.html.erb index 848a80ccb..ce8ddba99 100644 --- a/app/views/media/_basics.html.erb +++ b/app/views/media/_basics.html.erb @@ -182,6 +182,42 @@ class: 'form-control' %> <% end %> + + <% if medium.video.present? %> +
+ Enable Emergency Button? +
+
+ <%= f.radio_button :annotations_status, + '0', + class: 'custom-control-input' %> + <%= f.label :annotations_status, + 'inherit from lecture', + value: '0', + class: 'custom-control-label' %> +
+
+ <%= f.radio_button :annotations_status, + '1', + class: 'custom-control-input' %> + <%= f.label :annotations_status, + 'yes', + value: '1', + class: 'custom-control-label' %> +
+
+ <%= f.radio_button :annotations_status, + '-1', + class: 'custom-control-input' %> + <%= f.label :annotations_status, + 'no', + value: '-1', + class: 'custom-control-label' %> +
+
+
+ <% end %> + <%= f.hidden_field :teachable_id, value: medium.teachable_id %> <%= f.hidden_field :teachable_type, value: medium.teachable_type %> diff --git a/app/views/media/play.html.erb b/app/views/media/play.html.erb index 382e1fb44..e6862bd77 100644 --- a/app/views/media/play.html.erb +++ b/app/views/media/play.html.erb @@ -1,8 +1,10 @@ <% content_for :title, "THymE - \"#{@medium.caption}\"" %>
-
+
+
+
+ +
+ + bookmark_add + +
+ + <% if @medium.annotations_visible?(current_user) %> +
+ +
+ <% end %>
@@ -71,6 +91,42 @@
+
+
+ +
+
+ +
+ +
+ + + + + + + + + + +
+
+ +<%= render partial: "annotations/annotation_modal" %> +<%#= javascript_include_tag('markers.js') %> From 40ca2fbb4fcb80c43e82937a9681aed585db3c81 Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Fri, 10 Feb 2023 16:32:42 +0100 Subject: [PATCH 07/49] Added routes for the annotation-tool. --- config/routes.rb | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index 8aee5a21f..e16fdc2e0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -38,7 +38,15 @@ get '/administration/classification', to: 'administration#classification', as: 'classification' + + # annotation routes + get 'annotations/update_markers', + to: 'annotations#update_markers', + as: 'update_markers' + + resources :annotations, only: [:new, :create, :edit, :update, :destroy] + # announcements routes post 'announcements/:id/propagate', @@ -390,7 +398,11 @@ get 'media/:id/fill_reassign_modal', to: 'media#fill_reassign_modal', as: 'fill_reassign_modal' - + + get 'media/:id/check_annotation_visibility', + to: 'media#check_annotation_visibility', + as: 'check_annotation_visibility' + resources :media # notifications controller From 89e0384dfd351ebe4931de377e9aabf96751062a Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Sun, 12 Feb 2023 16:29:50 +0100 Subject: [PATCH 08/49] Improved the CSS of the annotation buttons in the interactive area. --- app/assets/stylesheets/thyme.scss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/assets/stylesheets/thyme.scss b/app/assets/stylesheets/thyme.scss index a1f5d4967..0233a1817 100644 --- a/app/assets/stylesheets/thyme.scss +++ b/app/assets/stylesheets/thyme.scss @@ -430,6 +430,12 @@ figure { cursor: pointer; } +#annotation-caption { + display: flex; + flex-direction: column; + justify-content: space-between; +} + #annotation-infobar { height: 2em; color: black; @@ -452,4 +458,6 @@ figure { justify-content: space-between; margin-left: 10%; margin-right: 10%; + margin-bottom: 15px; + margin-top: auto; } \ No newline at end of file From d043fd2ffd647617520b4a2e7c1309005fdf4414 Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Sun, 12 Feb 2023 17:16:56 +0100 Subject: [PATCH 09/49] Fixed a bug which caused the annotation's category not to be updated when editing the annotation. --- app/controllers/annotations_controller.rb | 24 ++++++------------- app/helpers/annotations_helper.rb | 29 +++++++++++++++++++++++ app/views/annotations/_form.html.erb | 2 +- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/app/controllers/annotations_controller.rb b/app/controllers/annotations_controller.rb index 7d1993ef7..5ccb39171 100644 --- a/app/controllers/annotations_controller.rb +++ b/app/controllers/annotations_controller.rb @@ -3,7 +3,8 @@ class AnnotationsController < ApplicationController def create @annotation = Annotation.new(annotation_params) @annotation.user_id = current_user.id - @annotation.category = category_translation + @annotation.category = helpers.category_text_to_int( + params[:annotation][:category_text]) @annotation.save end @@ -11,6 +12,9 @@ def edit @annotation = Annotation.find(params[:annotationId]) @timestamp = @annotation.timestamp @medium_id = @annotation.medium_id + # A variable that helps to assign the correct text to + # the given category, e.g. "Need help!" for the category 'help'. + @category_text = helpers.category_token_to_text(@annotation.category) end def new @@ -25,6 +29,8 @@ def show def update @annotation = Annotation.find(params[:id]) + @annotation.category = helpers.category_text_to_int( + params[:annotation][:category_text]) @annotation.update(annotation_params) end @@ -60,20 +66,4 @@ def annotation_params :timestamp, :visible_for_teacher) end - def category_translation - category = params[:annotation][:category_text] - case category - when 'Need help!' - return Annotation.categories[:help] - when 'Found a mistake' - return Annotation.categories[:mistake] - when 'Give a comment' - return Annotation.categories[:comment] - when 'Note' - return Annotation.categories[:note] - when 'Other' - return Annotation.categories[:other] - end - end - end diff --git a/app/helpers/annotations_helper.rb b/app/helpers/annotations_helper.rb index 442c10bc0..f7f3babb0 100644 --- a/app/helpers/annotations_helper.rb +++ b/app/helpers/annotations_helper.rb @@ -1,2 +1,31 @@ module AnnotationsHelper + def category_text_to_int(text) + case text + when 'Need help!' + return Annotation.categories[:help] + when 'Found a mistake' + return Annotation.categories[:mistake] + when 'Give a comment' + return Annotation.categories[:comment] + when 'Note' + return Annotation.categories[:note] + when 'Other' + return Annotation.categories[:other] + end + end + + def category_token_to_text(token) + case token + when 'help' + return 'Need help!' + when 'mistake' + return 'Found a mistake' + when 'comment' + return 'Give a comment' + when 'note' + return "Note" + when 'other' + return 'Other' + end + end end diff --git a/app/views/annotations/_form.html.erb b/app/views/annotations/_form.html.erb index c891820db..51b0d992e 100644 --- a/app/views/annotations/_form.html.erb +++ b/app/views/annotations/_form.html.erb @@ -29,7 +29,7 @@ <%= f.select :category_text, ['Need help!', 'Found a mistake', 'Give a comment', 'Note', 'Other'], - {}, + { selected: @category_text }, { class: 'form-control' } %>

From 618b0114be0a34dc06ca33bafb71170a996167df Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Mon, 13 Feb 2023 20:53:47 +0100 Subject: [PATCH 10/49] Added ":search" in line 8 of the file medium_ability.rb which was accidently overwritten. --- app/abilities/medium_ability.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/abilities/medium_ability.rb b/app/abilities/medium_ability.rb index 95ca643dd..b28694ef0 100644 --- a/app/abilities/medium_ability.rb +++ b/app/abilities/medium_ability.rb @@ -5,7 +5,7 @@ def initialize(user) user ||= User.new clear_aliased_actions - can [:index, :new, :check_annotation_visibility], Medium + can [:index, :new, :search, :check_annotation_visibility], Medium can [:show, :show_comments], Medium do |medium| medium.visible_for_user?(user) && From 84567fbe65e91b60fef8af9704913b0fe2e23b1d Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Sat, 18 Feb 2023 15:18:58 +0100 Subject: [PATCH 11/49] Changed marker offset such that it fits better for larger videos. --- app/assets/javascripts/thyme.coffee | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/assets/javascripts/thyme.coffee b/app/assets/javascripts/thyme.coffee index 7a461824c..a00e5f7c3 100644 --- a/app/assets/javascripts/thyme.coffee +++ b/app/assets/javascripts/thyme.coffee @@ -646,8 +646,7 @@ $(document).on 'turbolinks:load', -> video.pause() # round time to full seconds time = video.currentTime - intTime = Math.floor(time) - roundTime = intTime + roundTime = Math.floor(time) mediumId = thyme.dataset.medium $.ajax Routes.new_annotation_path(), type: 'GET' @@ -945,7 +944,7 @@ $(document).on 'turbolinks:load', -> $('#markers').append(markerStr) # set the correct position for the marker marker = $('#marker-' + annotation.id) - size = seekBar.clientWidth - 13 + size = seekBar.clientWidth - 15 ratio = timeToSeconds(annotation.timestamp) / video.duration offset = marker.parent().offset().left + ratio * size + 3 marker.offset({ left: offset }) From e7dbf2dd5d4cb49c1d4ed85996bac781b1fb2c5b Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Mon, 6 Mar 2023 20:34:21 +0100 Subject: [PATCH 12/49] Changed 'annotation_status' in the lectures controller to the correct term 'annotations_status' and added default value 0. --- app/controllers/lectures_controller.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/lectures_controller.rb b/app/controllers/lectures_controller.rb index 88ba2bf2c..b7aa87699 100644 --- a/app/controllers/lectures_controller.rb +++ b/app/controllers/lectures_controller.rb @@ -91,6 +91,7 @@ def new # info to the lecture @lecture.course = Course.find_by_id(params[:course]) I18n.locale = @lecture.course.locale + @lecture.annotations_status = 0 end def create @@ -316,7 +317,7 @@ def lecture_params :comments_disabled, :submission_max_team_size, :submission_grace_period, - :annotation_status, + :annotations_status, editor_ids: []) end From e2b3fdf8089e9f97b7d6432b03ae5b30b9956d67 Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Mon, 13 Mar 2023 20:38:56 +0100 Subject: [PATCH 13/49] Replaced the standard color picker of the annotation form by a selection of 15 colors. --- app/assets/stylesheets/thyme.scss | 68 +++++++++++++++++++++++++++- app/helpers/annotations_helper.rb | 36 +++++++++++++++ app/views/annotations/_form.html.erb | 16 ++++++- 3 files changed, 118 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/thyme.scss b/app/assets/stylesheets/thyme.scss index 0233a1817..a7162c60d 100644 --- a/app/assets/stylesheets/thyme.scss +++ b/app/assets/stylesheets/thyme.scss @@ -460,4 +460,70 @@ figure { margin-right: 10%; margin-bottom: 15px; margin-top: auto; -} \ No newline at end of file +} + + +/* A collection of all colors. +Be aware that color changes must also be written in the +AnnotationHelper! */ + +$annotation_colors: ( + annotation_color1: #DB2828, + annotation_color2: #F2711C, + annotation_color3: #FBBD08, + annotation_color4: #B5CC18, + annotation_color5: #21BA45, + annotation_color6: #00B5AD, + annotation_color7: #2185D0, + annotation_color8: #6435C9, + annotation_color9: #A333C8, + annotation_color10: #E03997, + annotation_color11: #d05d41, + annotation_color12: #924129, + annotation_color13: #444444, + annotation_color14: #999999, + annotation_color15: #eeeeee, +); + +* { + box-sizing: border-box; +} + +input[type="radio"] { + display: none; + &:checked + label { + span { transform: scale(1.25); } + @each $name, $value in $annotation_colors { + .#{$name} { + border: 2px solid black; + } + } + } +} + +#annotation-color-picker { + text-align: center; + label { + display: inline-block; + width: 25px; + height: 25px; + margin-right: 2px; + cursor: pointer; + &:hover { + span { + transform: scale(1.25); + } + } + span { + display: block; + width: 100%; + height: 100%; + transition: transform .2s ease-in-out; + @each $name, $value in $annotation_colors { + &.#{$name} { + background: $value; + } + } + } + } +} diff --git a/app/helpers/annotations_helper.rb b/app/helpers/annotations_helper.rb index f7f3babb0..4e830f8b4 100644 --- a/app/helpers/annotations_helper.rb +++ b/app/helpers/annotations_helper.rb @@ -28,4 +28,40 @@ def category_token_to_text(token) return 'Other' end end + + def annotation_color(int) + case int + when 1 + return '#DB2828' + when 2 + return '#F2711C' + when 3 + return '#FBBD08' + when 4 + return '#B5CC18' + when 5 + return '#21BA45' + when 6 + return '#00B5AD' + when 7 + return '#2185D0' + when 8 + return '#6435C9' + when 9 + return '#A333C8' + when 10 + return '#E03997' + when 11 + return '#d05d41' + when 12 + return '#924129' + when 13 + return '#444444' + when 14 + return '#999999' + when 15 + return '#eeeeee' + end + end + end diff --git a/app/views/annotations/_form.html.erb b/app/views/annotations/_form.html.erb index 51b0d992e..3f598ccf9 100644 --- a/app/views/annotations/_form.html.erb +++ b/app/views/annotations/_form.html.erb @@ -19,8 +19,22 @@

+ Color: +
+ <% for i in 1..15 do %> + <%= f.radio_button :color, + annotation_color(i), + id: "annotation_color#{i}" %> + <%= f.label :color, + "annotation_color#{i}", + for: "annotation_color#{i}" do %> + + <% end %> + <% end %> +
+

From ff4f51ad3ae60c889fc6cf49ba2521c979a5e652 Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Wed, 29 Mar 2023 19:15:45 +0200 Subject: [PATCH 14/49] Replaced cases with hash in the AnnotationsHelper. --- app/helpers/annotations_helper.rb | 50 +++++++++++-------------------- 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/app/helpers/annotations_helper.rb b/app/helpers/annotations_helper.rb index 4e830f8b4..62a0f31c7 100644 --- a/app/helpers/annotations_helper.rb +++ b/app/helpers/annotations_helper.rb @@ -30,38 +30,24 @@ def category_token_to_text(token) end def annotation_color(int) - case int - when 1 - return '#DB2828' - when 2 - return '#F2711C' - when 3 - return '#FBBD08' - when 4 - return '#B5CC18' - when 5 - return '#21BA45' - when 6 - return '#00B5AD' - when 7 - return '#2185D0' - when 8 - return '#6435C9' - when 9 - return '#A333C8' - when 10 - return '#E03997' - when 11 - return '#d05d41' - when 12 - return '#924129' - when 13 - return '#444444' - when 14 - return '#999999' - when 15 - return '#eeeeee' - end + color_map = { + 1 => '#DB2828', + 2 => '#F2711C', + 3 => '#FBBD08', + 4 => '#B5CC18', + 5 => '#21BA45', + 6 => '#00B5AD', + 7 => '#2185D0', + 8 => '#6435C9', + 9 => '#A333C8', + 10 => '#E03997', + 11 => '#d05d41', + 12 => '#924129', + 13 => '#444444', + 14 => '#999999', + 15 => '#eeeeee' + } + color_map[int] || '#000000' end end From fd61649bfe6383565654c07d1c86f2016d52101e Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Tue, 11 Apr 2023 11:02:48 +0200 Subject: [PATCH 15/49] The timestamp of an annotation is now properly saved as a TimeStamp object and the annotation categories have been replaced. --- app/assets/javascripts/thyme.coffee | 20 +++--- app/controllers/annotations_controller.rb | 12 ++-- app/helpers/annotations_helper.rb | 32 ++++----- app/models/annotation.rb | 6 +- app/models/time_stamp.rb | 2 +- app/views/annotations/_form.html.erb | 83 ++++++++++++++++++++--- 6 files changed, 108 insertions(+), 47 deletions(-) diff --git a/app/assets/javascripts/thyme.coffee b/app/assets/javascripts/thyme.coffee index a00e5f7c3..31872044d 100644 --- a/app/assets/javascripts/thyme.coffee +++ b/app/assets/javascripts/thyme.coffee @@ -26,7 +26,11 @@ secondsToTime = (seconds) -> # convert time in H:MM:SS to seconds timeToSeconds = (time) -> array = time.split(':') - return (+array[0]) * 3600 + (+array[1]) * 60 + (+array[2]) + return (+array[0]) * 3600 + (+array[1]) * 60 + (+array[2]) + (+array[3]) * .0001 + +# converts a json timestamp to a double containing the absolute count of millitseconds +timestampToMillis = (timestamp) -> + return 3600 * timestamp.hours + 60 * timestamp.minutes + timestamp.seconds + 0.001 * timestamp.milliseconds # lightens up a given color (given in a string in hexadecimal # representation "#xxyyzz") such that e.g. black becomes dark grey. @@ -41,7 +45,7 @@ sortAnnotations = -> if annotations == null return annotations.sort (ann1, ann2) -> - timeToSeconds(ann1.timestamp) - timeToSeconds(ann2.timestamp) + timestampToMillis(ann1.timestamp) - timestampToMillis(ann2.timestamp) return # return the start time of the next chapter relative to a given time in seconds @@ -644,16 +648,12 @@ $(document).on 'turbolinks:load', -> # Event handler for the emergency button emergencyButton.addEventListener 'click', -> video.pause() - # round time to full seconds - time = video.currentTime - roundTime = Math.floor(time) - mediumId = thyme.dataset.medium $.ajax Routes.new_annotation_path(), type: 'GET' dataType: 'script' data: { - timestamp: secondsToTime(roundTime) - mediumId: mediumId + total_seconds: video.currentTime + mediumId: thyme.dataset.medium } # When the modal opens, all key listeners must be # deactivated until the modal gets closed again @@ -945,7 +945,7 @@ $(document).on 'turbolinks:load', -> # set the correct position for the marker marker = $('#marker-' + annotation.id) size = seekBar.clientWidth - 15 - ratio = timeToSeconds(annotation.timestamp) / video.duration + ratio = timestampToMillis(annotation.timestamp) / video.duration offset = marker.parent().offset().left + ratio * size + 3 marker.offset({ left: offset }) marker.on 'click', -> @@ -982,7 +982,7 @@ $(document).on 'turbolinks:load', -> updateAnnotationArea(annotations[i + 1]) # goto listener $('#annotation-goto-button').on 'click', -> - video.currentTime = timeToSeconds(annotation.timestamp) + video.currentTime = timestampToMillis(annotation.timestamp) # edit listener $('#annotation-edit-button').on 'click', -> lockKeyListeners = true diff --git a/app/controllers/annotations_controller.rb b/app/controllers/annotations_controller.rb index 5ccb39171..3ce458870 100644 --- a/app/controllers/annotations_controller.rb +++ b/app/controllers/annotations_controller.rb @@ -2,6 +2,7 @@ class AnnotationsController < ApplicationController def create @annotation = Annotation.new(annotation_params) + @annotation.timestamp = TimeStamp.new(total_seconds: params[:annotation][:total_seconds]) @annotation.user_id = current_user.id @annotation.category = helpers.category_text_to_int( params[:annotation][:category_text]) @@ -10,7 +11,7 @@ def create def edit @annotation = Annotation.find(params[:annotationId]) - @timestamp = @annotation.timestamp + @total_seconds = @annotation.timestamp.total_seconds @medium_id = @annotation.medium_id # A variable that helps to assign the correct text to # the given category, e.g. "Need help!" for the category 'help'. @@ -19,7 +20,7 @@ def edit def new @annotation = Annotation.new - @timestamp = params[:timestamp] + @total_seconds = params[:total_seconds] @medium_id = params[:mediumId] end @@ -50,9 +51,9 @@ def update_markers user: current_user)) else annots = Annotation.where(medium: medium, - user: current_user) + user: current_user) end - + render json: annots end @@ -62,8 +63,7 @@ def update_markers def annotation_params params.require(:annotation).permit( - :color, :comment, :medium_id, - :timestamp, :visible_for_teacher) + :color, :comment, :medium_id, :visible_for_teacher) end end diff --git a/app/helpers/annotations_helper.rb b/app/helpers/annotations_helper.rb index 62a0f31c7..dd7fa80b9 100644 --- a/app/helpers/annotations_helper.rb +++ b/app/helpers/annotations_helper.rb @@ -1,31 +1,27 @@ module AnnotationsHelper def category_text_to_int(text) case text - when 'Need help!' - return Annotation.categories[:help] - when 'Found a mistake' - return Annotation.categories[:mistake] - when 'Give a comment' - return Annotation.categories[:comment] - when 'Note' + when 'I want to create a personal note.' return Annotation.categories[:note] - when 'Other' - return Annotation.categories[:other] + when 'I have a problem with understanding the content.' + return Annotation.categories[:content] + when 'I found a mistake.' + return Annotation.categories[:mistake] + when 'I can\'t read everything in this part.' + return Annotation.categories[:presentation] end end def category_token_to_text(token) case token - when 'help' - return 'Need help!' - when 'mistake' - return 'Found a mistake' - when 'comment' - return 'Give a comment' when 'note' - return "Note" - when 'other' - return 'Other' + return 'I want to create a personal note.' + when 'content' + return 'I have a problem with understanding the content.' + when 'mistake' + return 'I found a mistake.' + when 'presentation' + return 'I can\'t read everything in this part.' end end diff --git a/app/models/annotation.rb b/app/models/annotation.rb index 720643d29..d899bae12 100644 --- a/app/models/annotation.rb +++ b/app/models/annotation.rb @@ -1,5 +1,9 @@ class Annotation < ApplicationRecord belongs_to :medium belongs_to :user - enum category: { other: 0, note: 1, comment: 2, mistake: 3, help: 4 } + + # the timestamp for the annotation position is serialized as text in the db + serialize :timestamp, TimeStamp + + enum category: { note: 0, content: 1, mistake: 2, presentation: 3 } end diff --git a/app/models/time_stamp.rb b/app/models/time_stamp.rb index d5232735c..61d1b05d7 100644 --- a/app/models/time_stamp.rb +++ b/app/models/time_stamp.rb @@ -5,7 +5,7 @@ class TimeStamp validates :milliseconds, presence: true attr_reader :hours, :minutes, :seconds, :milliseconds - + # extract from YAML def self.load(text) YAML.safe_load(text, permitted_classes: [TimeStamp, diff --git a/app/views/annotations/_form.html.erb b/app/views/annotations/_form.html.erb index 3f598ccf9..79f070e76 100644 --- a/app/views/annotations/_form.html.erb +++ b/app/views/annotations/_form.html.erb @@ -1,11 +1,11 @@ <%= form_with model: @annotation do |f| %> - Time:   <%= @timestamp %> + Time:   <%= TimeStamp.new(total_seconds: @total_seconds).hms_colon_string %>

<%= f.hidden_field :medium_id, value: @medium_id %> - <%= f.hidden_field :timestamp, value: @timestamp %> + <%= f.hidden_field :total_seconds, value: @total_seconds %>
- Enable Emergency Button? + <%= t('admin.lecture.enable_emergency_button') %>
<%= f.radio_button :annotations_status, 1, class: 'custom-control-input' %> <%= f.label :annotations_status, - 'yes', + t('basics.yes_lc'), value: 1, class: 'custom-control-label' %>
@@ -60,7 +60,7 @@ -1, class: 'custom-control-input' %> <%= f.label :annotations_status, - 'no', + t('basics.no_lc'), value: -1, class: 'custom-control-label' %>
diff --git a/app/views/lectures/edit/_preferences.html.erb b/app/views/lectures/edit/_preferences.html.erb index 891613b9a..91a6c440f 100644 --- a/app/views/lectures/edit/_preferences.html.erb +++ b/app/views/lectures/edit/_preferences.html.erb @@ -152,14 +152,14 @@ <% if lecture.annotations_status == 1 %>
- Link for emergency button + <%= t('admin.lecture.emergency_link') %> @@ -177,7 +177,7 @@ :direct_link, class: 'custom-control-input' %> <%= f.label :emergency_link_status, - 'direct link', + t('admin.lecture.emergency_link_direct_link'), value: :direct_link, class: 'custom-control-label' %>
@@ -185,14 +185,14 @@
<% end %> - \ No newline at end of file + + +<%= render partial: "annotations/annotation_locales" %> \ No newline at end of file diff --git a/app/views/media/play.html.erb b/app/views/media/play.html.erb index 610fa2189..0be25a50f 100644 --- a/app/views/media/play.html.erb +++ b/app/views/media/play.html.erb @@ -159,3 +159,4 @@ <%= render partial: "annotations/annotation_modal" %> +<%= render partial: "annotations/annotation_locales" %> \ No newline at end of file diff --git a/config/locales/de.yml b/config/locales/de.yml index 84703afb0..b7a3a75fa 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -322,6 +322,45 @@ de: title: 'Titel' teacher: 'DozentIn' editors: 'EditorInnen' + annotation: + inherit_from_lecture: 'von Vorlesung erben' + annotations: 'Annotationen' + time: 'Zeit:' + comment: 'Kommentar' + color: 'Farbe' + category: 'Kategorie' + note: 'Notiz' + content: 'Inhalt' + mistake: 'Fehler' + presentation: 'Darstellung' + where_problem: > + Wo liegt das Problem? + definition: 'Definition' + argument: 'Argument' + strategy: 'Beweisstrategie' + further_help: 'Du brauchst weitere Hilfe? Dann klicke auf:' + warning_message: + publishing: > + Jeder, der diese Vorlesung abonniert hat, wird deinen Kommentar + lesen können, sobald du auf "Speichern" klickst. + Falls du das nicht möchtest, entferne bitte wieder den Haken + dieser Checkbox. + mistake: > + Bevor du diesen Kommentar abschickst, überprüfe außerdem bitte, + ob bereits ein Kommentar zu dem von dir gefundenen Fehler existiert. + one_close_annotation: > + In der Nähe deiner Annotation gibt es bereits eine veröffentlichte + Fehler-Annotation. + multiple_close_annotations_1: "In der Nähe deiner Annotation gibt es bereits " + multiple_close_annotations_2: " veröffentliche Fehler-Annotationen." + permission: "Dir fehlen die Rechte, um diese Annotation zu bearbeiten." + visible_for_teacher: 'Für DozentIn sichtbar?' + post_as_comment: 'Als Kommentar veröffentlichen?' + toggle: + note: "Notiz-Annotationen ein-/ausblenden" + content: "Inhalt-Annotationen ein-/ausblenden" + mistake: "Fehler-Annotationen ein-/ausblenden" + presentation: "Darstellung-Annotationen ein-/ausblenden" announcement: help: > Hier kannst Du den Text eingeben. Du kannst LaTeX benutzen @@ -371,6 +410,7 @@ de: no_imported_media: 'Es sind keine importierten Medien vorhanden.' comments_disabled: > Kommentare für neu veröffentlichte Medien sind standardmäßig deaktiviert + enable_emergency_button: 'Emergency-Button aktivieren?' new_notifications: 'neue Benachrichtigungen' new_media: 'neue Medien' new_posts: 'neue Forenbeiträge' @@ -620,6 +660,12 @@ de: script_based: > unter Verwendung eines Veranstaltungsskriptes, das mit dem MaMpf LaTeX-Paket erstellt wurde + emergency_link: 'Link für den Emergency-Button' + emergency_link_no_link: 'kein Link' + emergency_link_lecture_link: 'Vorlesungslink' + emergency_link_direct_link: 'direkter Link' + enter_emergency_link: 'Emergency-Link eingeben:' + select_helpdesk: 'Helpdesk auswählen:' no_chapters: 'Es sind noch keine Kapitel vorhanden.' no_talks: Es sind noch keine Vorträge vorhanden. orphaned_lessons: 'Verwaiste Sitzungen' diff --git a/config/locales/en.yml b/config/locales/en.yml index 037f67f4b..7c3d5680d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -323,6 +323,44 @@ en: title: 'Title' teacher: 'Teacher' editors: 'Editors' + annotation: + inherit_from_lecture: 'inherit from lecture' + annotations: 'Annotations' + time: 'Time:' + comment: 'Comment' + color: 'Color' + category: 'Category' + note: 'note' + content: 'content' + mistake: 'mistake' + presentation: 'presentation' + where_problem: > + Where's the problem? + definition: 'definition' + argument: 'argument' + strategy: 'proof strategy' + further_help: 'You need further help? Then click on:' + warning_message: + publishing: > + Everyone who subscribed this lecture will be able to read + your comment once you click "Save". + If this is not what you intended to do, + please unselect this checkbox. + mistake: > + Before submitting, please also check, if someone already posted + a comment concerning this mistake. + one_close_annotation: > + There already is a public mistake annotation that is close to yours. + multiple_close_annotations_1: "There are already " + multiple_close_annotations_2: " mistake annotations that are close to yours." + permission: "You don't have the permission to edit this annotation." + visible_for_teacher: 'Visible for teacher?' + post_as_comment: 'Post as comment?' + toggle: + note: "toggle note annotations" + content: "toggle note content" + mistake: "toggle note mistake" + presentation: "toggle note presentation" announcement: help: > Here you can enter a text. You may use LaTeX (by putting the @@ -372,6 +410,7 @@ en: close_comments: 'Close all threads for related media' comments_disabled: > Comments for newly published media are disabled by default + enable_emergency_button: 'Enable emergency button?' new_notifications: 'new notifications' new_media: 'new media' new_posts: 'new forum posts' @@ -590,6 +629,12 @@ en: content_mode: 'Content determination' video_based: 'media based' script_based: 'using a manuscript generated by the MaMpf LaTeX package' + emergency_link: 'Link for the emergency button' + emergency_link_no_link: 'no link' + emergency_link_lecture_link: 'lecture link' + emergency_link_direct_link: 'direct link' + enter_emergency_link: 'Enter emergency link:' + select_helpdesk: 'Select helpdesk:' no_chapters: 'There are no chapters yet.' no_talks: 'There are no talks yet.' orphaned_lessons: 'Orphaned Sessions' From 885a731115e1cae014b9e534cb7ee19f74983cbb Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Tue, 27 Jun 2023 12:15:37 +0200 Subject: [PATCH 32/49] Combined new and edit scripts for the annotations controller (as they basically share the same code). --- app/controllers/annotations_controller.rb | 1 + app/views/annotations/edit.coffee | 19 ++-- app/views/annotations/new.coffee | 108 ---------------------- 3 files changed, 11 insertions(+), 117 deletions(-) delete mode 100644 app/views/annotations/new.coffee diff --git a/app/controllers/annotations_controller.rb b/app/controllers/annotations_controller.rb index 66b971c0f..f2a34312e 100644 --- a/app/controllers/annotations_controller.rb +++ b/app/controllers/annotations_controller.rb @@ -46,6 +46,7 @@ def new @annotation = Annotation.new(category: :note, color: helpers.annotation_color(1)) @total_seconds = params[:total_seconds] @medium_id = params[:mediumId] + render :edit end def show diff --git a/app/views/annotations/edit.coffee b/app/views/annotations/edit.coffee index 97ea2448b..0cf37c9cc 100644 --- a/app/views/annotations/edit.coffee +++ b/app/views/annotations/edit.coffee @@ -6,14 +6,11 @@ submitButton = document.getElementById('submit-button') postAsComment = document.getElementById('post-as-comment') postAsComment.addEventListener 'change', (evt) -> - message = "Everyone who subscribed this lecture will be able to read your comment once " + - "you click \"Create Annotation\". " + - "If this is not what you intended to do, please unselect this checkbox." + message = document.getElementById('warning').dataset.publishing if this.checked mistakeRadio = document.getElementById('annotation_category_mistake') if mistakeRadio.checked - message += "\n\nBefore submitting, please also check, if someone already posted " + - " a comment concerning this mistake. " + message += "\n" + document.getElementById('warning').dataset.mistake medId = thyme.dataset.medium rad = 60 # annotations that are inside this radius are considered as "near". $.ajax Routes.near_mistake_annotations_path(), @@ -27,9 +24,11 @@ postAsComment.addEventListener 'change', (evt) -> success: (c) -> if c != undefined && c != 0 if c == 1 - message += "There already is a (public) mistake annotation that is close to yours." + message += document.getElementById('warning').dataset.oneCloseAnnotation else - message += "There are already " + c + " (public) mistake annotations that are close to yours." + message += document.getElementById('warning').dataset.multipleCloseAnnotations1 + + "" + c + + document.getElementById('warning').dataset.multipleCloseAnnotations2 alert message else alert message @@ -110,7 +109,8 @@ postComment = (boolean) -> -# select correct subcategory (this is not automatically done by the rails form +# If this script is rendered by the edit method of the annotation controller: +# Select correct subcategory (this is not automatically done by the rails form # as the content is dynamically rendered). contentRadio = document.getElementById('annotation_category_content') if contentRadio.checked @@ -120,4 +120,5 @@ if contentRadio.checked switch subtext when "definition" then document.getElementById('content-category-definition').checked = true when "argument" then document.getElementById('content-category-argument').checked = true - when "strategy" then document.getElementById('content-category-strategy').checked = true \ No newline at end of file + when "strategy" then document.getElementById('content-category-strategy').checked = true + return \ No newline at end of file diff --git a/app/views/annotations/new.coffee b/app/views/annotations/new.coffee deleted file mode 100644 index 73b6962d4..000000000 --- a/app/views/annotations/new.coffee +++ /dev/null @@ -1,108 +0,0 @@ -$('#annotation-modal-content').empty() - .append('<%= j render partial: "annotations/form"%>') -$('#annotation-modal').modal('show') - -submitButton = document.getElementById('submit-button') -postAsComment = document.getElementById('post-as-comment') - -postAsComment.addEventListener 'change', (evt) -> - message = document.getElementById('warning').dataset.publishing - if this.checked - mistakeRadio = document.getElementById('annotation_category_mistake') - if mistakeRadio.checked - message += "\n" + document.getElementById('warning').dataset.mistake - medId = thyme.dataset.medium - rad = 60 # annotations that are inside this radius are considered as "near". - $.ajax Routes.near_mistake_annotations_path(), - type: 'GET' - dataType: 'json' - data: { - mediumId: medId - timestamp: video.currentTime - radius: rad - } - success: (c) -> - if c != undefined && c != 0 - if c == 1 - message += document.getElementById('warning').dataset.oneCloseAnnotation - else - message += document.getElementById('warning').dataset.multipleCloseAnnotations1 + - "" + c + - document.getElementById('warning').dataset.multipleCloseAnnotations2 - alert message - else - alert message - return - - - -# CATEGORY - -categoryRadios = document.getElementById('category-radios') - -categoryRadios.addEventListener 'click', (evt) -> - if evt.target && event.target.matches("input[type='radio']") - switch evt.target.value - when "note" then note() - when "content" then content() - when "mistake" then mistake() - when "presentation" then presentation() - return - -note = -> - $('#specific').empty() - submitButton.disabled = false - visibleForTeacher(false) - postComment(false) - return - -content = -> - $('#specific').empty().append('<%= j render partial: "annotations/form_content"%>') - submitButton.disabled = true # disable submit button until the content category is selected - visibleForTeacher(true) - postComment(false) - contentCategoryRadios = document.getElementById('content-category-radios') - contentCategoryRadios.addEventListener 'click', (evt) -> - if evt.target && event.target.matches("input[type='radio']") - submitButton.disabled = false - switch evt.target.value - when "definition" then definition() - when "argument" then argument() - when "strategy" then strategy() - return - definition = -> - $('#content-specific').empty().append('<%= j render partial: "annotations/form_content_definition"%>') - return - argument = -> - $('#content-specific').empty() - return - strategy = -> - $('#content-specific').empty() - return - return - -mistake = -> - $('#specific').empty() - submitButton.disabled = false - visibleForTeacher(true) - postComment(true) - return - -presentation = -> - $('#specific').empty() - submitButton.disabled = false - visibleForTeacher(true) - postComment(false) - return - - - -# Auxiliary methods - -visibleForTeacher = (boolean) -> - $('#annotation_visible_for_teacher').prop("checked", boolean) - return - -postComment = (boolean) -> - $('#annotation_post_as_comment').prop("checked", boolean) - return \ No newline at end of file From 1011f97a2ee08c2fd5c8ac815235bc60e27eda99 Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Tue, 27 Jun 2023 13:26:47 +0200 Subject: [PATCH 33/49] Fixed bug which caused the annotation area to overlap with the video player, if one clicks the iaButton and then click on an annotation. --- app/assets/javascripts/thyme.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/assets/javascripts/thyme.coffee b/app/assets/javascripts/thyme.coffee index f51794832..1b406f327 100644 --- a/app/assets/javascripts/thyme.coffee +++ b/app/assets/javascripts/thyme.coffee @@ -952,8 +952,10 @@ $(document).on 'turbolinks:load', -> offset = marker.parent().offset().left + ratio * size + 3 marker.offset({ left: offset }) marker.on 'click', -> - updateAnnotationArea(annotation) + if iaButton.dataset.status == "false" + $(iaButton).trigger('click') $('#caption').hide() + updateAnnotationArea(annotation) $('#annotation-caption').show() return From 34d28bdb033d16217d62bab27943f17897a02288 Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Sat, 1 Jul 2023 09:27:15 +0200 Subject: [PATCH 34/49] Manually set the ranges of the thyme feedback player to a default value on reload (time to 0, volume to 1). This still doesn't solve the bug which causes the ranges to be white (instead of blue) until the user manually changes their values. --- app/assets/javascripts/thyme_feedback.coffee | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/assets/javascripts/thyme_feedback.coffee b/app/assets/javascripts/thyme_feedback.coffee index 60d062bca..1c5d4f811 100644 --- a/app/assets/javascripts/thyme_feedback.coffee +++ b/app/assets/javascripts/thyme_feedback.coffee @@ -113,6 +113,9 @@ $(document).on 'turbolinks:load', -> toggleContentAnnotations = document.getElementById('toggle-content-annotations-check') toggleMistakeAnnotations = document.getElementById('toggle-mistake-annotations-check') togglePresentationAnnotations = document.getElementById('toggle-presentation-annotations-check') + # set seek bar to 0 and volume bar to 1 + seekBar.value = 0 + volumeBar.value = 1 # resizes the thyme container to the window dimensions resizeContainer = -> From 088050741ba2c9a305531ad4d7200b4c2b94c893 Mon Sep 17 00:00:00 2001 From: Frodo161 Date: Sat, 8 Jul 2023 14:16:27 +0200 Subject: [PATCH 35/49] Updated all emergency button forms to bootstrap 5 standards. Changed a CSS paragraph which caused all radio buttons to disappear. Fixed a bug in the annotation form which caused unwanted behavior of the publish-button. --- app/assets/stylesheets/thyme.scss | 19 +++---- .../annotations/_annotation_modal.html.erb | 11 ++-- app/views/annotations/_form.html.erb | 48 ++++++++-------- app/views/annotations/_form_content.html.erb | 20 +++---- app/views/annotations/edit.coffee | 57 ++++++++++--------- app/views/lectures/edit/_comments.html.erb | 14 ++--- app/views/lectures/edit/_preferences.html.erb | 20 +++---- app/views/media/_basics.html.erb | 22 +++---- app/views/media/feedback.html.erb | 12 ++-- app/views/media/play.html.erb | 14 ++--- config/locales/de.yml | 3 +- config/locales/en.yml | 3 +- 12 files changed, 124 insertions(+), 119 deletions(-) diff --git a/app/assets/stylesheets/thyme.scss b/app/assets/stylesheets/thyme.scss index 1474a4254..9b9e43f8f 100644 --- a/app/assets/stylesheets/thyme.scss +++ b/app/assets/stylesheets/thyme.scss @@ -507,19 +507,18 @@ $annotation_colors: ( box-sizing: border-box; } -input[type="radio"] { - display: none; - &:checked + label { - span { transform: scale(1.25); } - @each $name, $value in $annotation_colors { - .#{$name} { - border: 2px solid black; +#annotation-color-picker { + input[type="radio"] { + display: none; + &:checked + label { + span { transform: scale(1.25); } + @each $name, $value in $annotation_colors { + .#{$name} { + border: 2px solid black; + } } } } -} - -#annotation-color-picker { text-align: center; label { display: inline-block; diff --git a/app/views/annotations/_annotation_modal.html.erb b/app/views/annotations/_annotation_modal.html.erb index b9e0eefeb..79c119c53 100644 --- a/app/views/annotations/_annotation_modal.html.erb +++ b/app/views/annotations/_annotation_modal.html.erb @@ -2,20 +2,19 @@ \ No newline at end of file + diff --git a/app/views/annotations/_form.html.erb b/app/views/annotations/_form.html.erb index a17224abc..bef853d08 100644 --- a/app/views/annotations/_form.html.erb +++ b/app/views/annotations/_form.html.erb @@ -4,26 +4,26 @@

- + <%= f.hidden_field :medium_id, value: @medium_id %> <%= f.hidden_field :total_seconds, value: @total_seconds %> - + <%= t('admin.annotation.comment') %> <%= f.text_area :comment, class: 'form-control' %>
- + <%= t('admin.annotation.color') %>
<% for i in 1..15 do %> @@ -37,62 +37,62 @@ <% end %> <% end %>
-
- + <%= t('admin.annotation.category') %>
-
+
<%= f.radio_button :category, :note, - class: 'custom-control-input' %> + class: 'form-check-input' %> <%= f.label :category, t('admin.annotation.note'), value: :note, - class: 'custom-control-label' %> + class: 'form-check-label' %>
-
+
<%= f.radio_button :category, :content, - class: 'custom-control-input' %> + class: 'form-check-input' %> <%= f.label :category, t('admin.annotation.content'), value: :content, - class: 'custom-control-label' %> + class: 'form-check-label' %>
-
+
<%= f.radio_button :category, :mistake, - class: 'custom-control-input' %> + class: 'form-check-input' %> <%= f.label :category, t('admin.annotation.mistake'), value: :mistake, - class: 'custom-control-label' %> + class: 'form-check-label' %>
-
+
<%= f.radio_button :category, :presentation, - class: 'custom-control-input' %> + class: 'form-check-input' %> <%= f.label :category, t('admin.annotation.presentation'), value: :presentation, - class: 'custom-control-label' %> + class: 'form-check-label' %>

- +

- + <% annotations_status = Medium.find_by(id: @medium_id).get_annotations_status %> <% if annotations_status == 1 %> <%= f.label :visible_for_teacher, class: "checkbox inline" do %> @@ -105,7 +105,7 @@ <% end %> <%= t('admin.annotation.post_as_comment') %>   - <%= f.check_box :post_as_comment, id: "post-as-comment" %> + <%= f.check_box :post_as_comment %>
@@ -115,12 +115,12 @@
<% end %> @@ -132,4 +132,4 @@ data-one-close-annotation="<%= t('admin.annotation.warning_message.one_close_annotation') %>" data-multiple-close-annotations1="<%= t('admin.annotation.warning_message.multiple_close_annotations_1') %>" data-multiple-close-annotations2="<%= t('admin.annotation.warning_message.multiple_close_annotations_2') %>"> -
\ No newline at end of file +
diff --git a/app/views/annotations/_form_content.html.erb b/app/views/annotations/_form_content.html.erb index 49cf18bbe..bac4a39b2 100644 --- a/app/views/annotations/_form_content.html.erb +++ b/app/views/annotations/_form_content.html.erb @@ -3,35 +3,35 @@
-
+
-
-
+
-
-
+
- @@ -41,5 +41,5 @@
- +
diff --git a/app/views/annotations/edit.coffee b/app/views/annotations/edit.coffee index 0cf37c9cc..b250d3a56 100644 --- a/app/views/annotations/edit.coffee +++ b/app/views/annotations/edit.coffee @@ -3,35 +3,39 @@ $('#annotation-modal-content').empty() $('#annotation-modal').modal('show') submitButton = document.getElementById('submit-button') -postAsComment = document.getElementById('post-as-comment') +postAsComment = document.getElementById('annotation_post_as_comment') postAsComment.addEventListener 'change', (evt) -> - message = document.getElementById('warning').dataset.publishing if this.checked - mistakeRadio = document.getElementById('annotation_category_mistake') - if mistakeRadio.checked - message += "\n" + document.getElementById('warning').dataset.mistake - medId = thyme.dataset.medium - rad = 60 # annotations that are inside this radius are considered as "near". - $.ajax Routes.near_mistake_annotations_path(), - type: 'GET' - dataType: 'json' - data: { - mediumId: medId - timestamp: video.currentTime - radius: rad - } - success: (c) -> - if c != undefined && c != 0 - if c == 1 - message += document.getElementById('warning').dataset.oneCloseAnnotation - else - message += document.getElementById('warning').dataset.multipleCloseAnnotations1 + - "" + c + - document.getElementById('warning').dataset.multipleCloseAnnotations2 - alert message - else - alert message + warningMessage() + return + +warningMessage = () -> + message = document.getElementById('warning').dataset.publishing + mistakeRadio = document.getElementById('annotation_category_mistake') + if mistakeRadio.checked + message += "\n" + document.getElementById('warning').dataset.mistake + medId = thyme.dataset.medium + rad = 60 # annotations that are inside this radius (in seconds) are considered as "near". + $.ajax Routes.near_mistake_annotations_path(), + type: 'GET' + dataType: 'json' + data: { + mediumId: medId + timestamp: video.currentTime + radius: rad + } + success: (c) -> + if c != undefined && c != 0 + if c == 1 + message += document.getElementById('warning').dataset.oneCloseAnnotation + else + message += document.getElementById('warning').dataset.multipleCloseAnnotations1 + + "" + c + + document.getElementById('warning').dataset.multipleCloseAnnotations2 + alert message + else + alert message return @@ -86,6 +90,7 @@ mistake = -> submitButton.disabled = false visibleForTeacher(true) postComment(true) + warningMessage() return presentation = -> diff --git a/app/views/lectures/edit/_comments.html.erb b/app/views/lectures/edit/_comments.html.erb index 1fe0bd2c0..ac3c59510 100644 --- a/app/views/lectures/edit/_comments.html.erb +++ b/app/views/lectures/edit/_comments.html.erb @@ -57,27 +57,27 @@
- +
<%= t('admin.lecture.enable_emergency_button') %>
-
+
<%= f.radio_button :annotations_status, 1, - class: 'custom-control-input' %> + class: 'form-check-input' %> <%= f.label :annotations_status, t('basics.yes_lc'), value: 1, - class: 'custom-control-label' %> + class: 'form-check-label' %>
-
+
<%= f.radio_button :annotations_status, -1, - class: 'custom-control-input' %> + class: 'form-check-input' %> <%= f.label :annotations_status, t('basics.no_lc'), value: -1, - class: 'custom-control-label' %> + class: 'form-check-label' %>
diff --git a/app/views/lectures/edit/_preferences.html.erb b/app/views/lectures/edit/_preferences.html.erb index 609c3215e..1d79ba00a 100644 --- a/app/views/lectures/edit/_preferences.html.erb +++ b/app/views/lectures/edit/_preferences.html.erb @@ -158,37 +158,37 @@
<% end %> - + <% if lecture.annotations_status == 1 %>
<%= t('admin.lecture.emergency_link') %>