Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix unread comments status #574

Closed
wants to merge 4 commits into from

Conversation

fosterfarrell9
Copy link
Collaborator

This fixes #573. I think that the behaviour in all the edge cases considered in #515 should remain unchanged.

@fosterfarrell9 fosterfarrell9 changed the base branch from main to mampf-next December 19, 2023 16:19
Copy link
Member

@Splines Splines left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I don't understand what is really going on since I don't see why we need the unread_media_comments method in the first place and what it's relation is to the unseen_comments of the comments_controller. See my comments below.

Edit: Start with the comment containing this symbol (⏺) as some things have clarified in the meantime .

Comment on lines +779 to +780
(Reader.find_by(user: self, thread: mc[:thread])
&.updated_at || 1000.years.ago) < mc[:latest_comment].created_at &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract expression to variable, then use it to do the comparison.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also note that a very similar logic is used in the update method of the ReadersController.

@@ -774,6 +774,14 @@ def last_sign_in_ip=(_ip)
def current_sign_in_ip=(_ip)
end

def unread_media_comments
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In _navbar.html.erb we use current_user.unread_comments to assign the "new-comment" CSS class accordingly. I don't understand why we need a separate unread_media_comments and what its purpose is.

Furthermore, I'm not too sure if the user model is the place where this methods belongs to as it is related to the comments and not the user itself. But this is a problem generally with the user.rb file: it's so long because it contains lots of stuff that is semantically related to a user but actually deals with something else at its heart, e.g. media comments management. Of course, MaMpf is a user-centric platform, so everything is somehow related to a user. We should still split the functionality. As restructuring this would probably entail bigger refactoring efforts, I'm fine with leaving this method here for now.

Copy link
Collaborator Author

@fosterfarrell9 fosterfarrell9 Dec 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the gneral sentiment towards user.rb: it needs a lot of refactorization. But the unread_media_comments method I would at least for the moment leave here. Comments management is currently only done in the Commontator gem models, I would not want to place a method that is central for us in a gem's model.

return if current_user.unread_media_comments.any?

current_user.update(unread_comments: false)
@no_unread_comments = true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see how this achieves the goal of this PR. Right now, update_unread_status is only called from the create method of the comments_controller, so why would we want to set the unread_comments to false in the create method? Shouldn't we set the unread_comments to false whenever a user clicks on the "X" button next to the comment to mark it as read? How is this reflected in this PR?

Copy link
Member

@Splines Splines Dec 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⏺ Update

Maybe I get it now. unseen_comments is actually rather a unseen_new_comments, right? That is, we check whether any other user posted a comment during the time a user drafted and saved a comment. It's not actually if there are any unseen comments, just new ones in that specific timespan.

That's why we need another method to find out if the user generally has any unseen comments and this is the unread_media_comments method which is also used to identify all the unread comments of a user for the @media_array shown in the frontend (on the comments overview page after clicking on the yellow bubble).

We don't want to execute this rather costly operation of searching all unread comments on every pageload to determine which color the bubble should have, right? That's why we have the unread_comments boolean for the user but we didn't set it to false correctly.


If what I said is correct so far I'm still not sure why update_unread_status is only called in the create method and why the logic for setting unread_comments to false is in there. Shouldn't this logic be executed only when a user clicks on the "X" next to a comment (on the comments overview page) to dismiss it? Then, we could check whether any unread comment is left and if not, set the flag unread_comments to false.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't want to execute this rather costly operation of searching all unread comments on every pageload to determine which color the bubble should have, right? That's why we have the unread_comments boolean for the user but we didn't set it to false correctly.

That's it exactly.

If what I said is correct so far I'm still not sure why update_unread_status is only called in the create method and why the logic for setting unread_comments to false is in there. Shouldn't this logic be executed only when a user clicks on the "X" next to a comment (on the comments overview page) to dismiss it? Then, we could check whether any unread comment is left and if not, set the flag unread_comments to false.

That is precisely the logic we had before we introduced #515 (and which we did not change in #515), see the raeders_controller which does exactly what you describe and resets the unread_comments flag to false. However, with the introduction of #515, the case described in #573 (which we missed in #515) occurs: In this situation the coresponding comment will not be listed in the comments index page, so there will be no "X", and no action related to the readers_controller will ever be executed and the unread_comments flag is not set to false. The only other place we can do it is in the create action for the comment.

Comment on lines 32 to 38
<% if @update_icon %>
$('#commentsIcon').addClass('new-comment');
$("#commentsIcon").addClass("new-comment");
<% end %>

<% if @no_unread_comments %>
$("#commentsIcon").removeClass("new-comment");
<% end %>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the flow inside the update_unread_status method of the comments_controller, we will never encounter the case where both variables (@update_icon and @no_unread_comments are true), so we won't have the case that we add a CSS class just to then remove it immediately. This would result in a short visual flickering which is unwanted. So, this is not a problem.

But still, maybe we can simplify the logic in the controller such that the frontend only has to deal with one variable determining whether to add the CSS class or do nothing? Or do we really have to remove the CSS class because it is set in the _navbar.html.erb and we need to get rid of it? If so, why not make the changes such that the navbar doesn't add the class in the first place?

Copy link
Collaborator Author

@fosterfarrell9 fosterfarrell9 Dec 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The navbar is loaded way before. In the situation described in #573, the teacher finds a yellow new_comments icon in the navbar (correctly so), posts their reply, and then the icon has to turn white. During that process, the navbar does not get reloaded. The alternative to what is done in the PR would be to reload the navbar. But the view we have here is a view of the commontator gem which we modify (currently only by the two simple simple statements). Reloading the navbar would be a bigger operation which I suggest we keep out of this "external" view.

@@ -216,6 +216,10 @@ def update_unread_status
return
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed this in the last PR. Why do we need a global variable here (@reader)? Instead, pass the reader to unseen_comments as parameter. This way it's even easier to understand what is going on. A smiliar scenario can be found in the update method in readers_controller.

@fosterfarrell9
Copy link
Collaborator Author

fosterfarrell9 commented Dec 20, 2023

I want to point out that the behavior introduced in #515 is pretty inconsistent. Consider the following to scenarios:

  • First scenario: Consider a medium M without comments. We have users A and B. By assumption, M has an empty commontator_thread T and there does not exist a reader where the thread is equal to T. Now suppose A makes a comment at time t1. B sees the comment and posts a reply at time t2. What happens in the comments_controller for B's reply? Since there is no reader with thread T, in
@reader = Reader.find_or_create_by(user: current_user, thread: @commontator_thread)

a new reader R is created for user B and thread T at around time t2, lets say for simplictiy at exactly time t2. The updated_at column of R is then filled with t2. The unseen_comments method of #515

def unseen_comments?
   @commontator_thread.comments.any? do |c|
       c.creator != current_user && c.created_at > @reader.updated_at
   end
end

will return false because the only comment in T not created by B was created at time t1<t2.

  • Second scenario: Consider a medium M with existing comments. We have users A and B. B has seen the existing comments at some time t0. Hence, M has a nonempty commontator_thread T and there exists a reader R for thread T and user B, whose updated_column is given by t0. Now suppose A makes a comment at time t1. B sees the comment and posts a reply at time t2. What happens in the comments_controller for B's reply? Since there is already reader R for thread T and user B,
@reader = Reader.find_or_create_by(user: current_user, thread: @commontator_thread)

reader ist set to R. Now, the unseen_comments? method will return true because t1>t0.

TLDR: In both scenarios user B has no unread comments for the given medium before, but depending on whether there
are older comments, unseen_comments returns either true or false.

#515 has introduced inconsistencies such as the one above, an issue like #573, the business logic has become more complex and there now some edge cases discussed in #515. I would at least put it up for discussion that we revert #515. Business logic was very simple before and the Reader model was tailored to just adress this simple setting. If we decide not tp revert we should make the code a lot more robust here.

@Splines Splines mentioned this pull request Jan 3, 2024
1 task
Splines added a commit that referenced this pull request Jan 4, 2024
See the discussion on #574 for the reasons behind this.
@Splines
Copy link
Member

Splines commented Jan 4, 2024

Let COP $\coloneqq$ "comments overview page" (/main/comments/).

@fosterfarrell9 thanks a lot for your detailed description of the scenarios, this actually helped while debugging this issue. Note I've also used pgadmin and it worked like a charm and allowed me to easily see what happens exactly in the Readers table.
After digging around for quite some time, here is my analysis. The respective fix is provided in #576. But I'm sure there will be still some things I've not taken into account 🙈


The root cause for our problem is that we mix granularities. The Reader instance and all the checks figuring out whether comments have been read operate on the granularity Thread (which is attached to one medium) and not on the granularity Comment of a thread. Yet still, starting of #515, we create a Reader entry also when we posted any comment. The updated_at column is updated accordingly and this messes everything up later when we try to check on a higher granularity whether there are any unread comments with a simple time < comparison.

# make sure that the thread associated to this comment is marked as read
# by the comment creator (unless some other user posted a comment in it
# that has not yet been read)
@reader = Reader.find_or_create_by(user: current_user,
thread: @commontator_thread)

The create logic was even introduced in the very first commit of #515 and then later only refined to read find_or_create_by to fix a bug, but still: it has "create" in it and that seems to be the problem.


Maybe a fix: The comment you originally wrote above this line states what we want to achieve originally with the update_unread_status method:

make sure that the thread associated to this comment is marked as read by the comment creator (unless some other user posted a comment in it that has not yet been read)

You achieved this by creating a new Reader entry or updated the updated_at timestamp accordingly such that it isn't displayed anymore in the COP (we don't want comments posted by yourself to come up there as unread comment). Instead: why not do exactly this in the controller that is responsible for the comments. Unintuitively, it is called main_controller.rb and here is the crucial part:

def comments
@media_comments = current_user.media_latest_comments
@media_comments.select! do |m|
(Reader.find_by(user: current_user, thread: m[:thread])
&.updated_at || 1000.years.ago) < m[:latest_comment].created_at &&
m[:medium].visible_for_user?(current_user)
end
@media_array = Kaminari.paginate_array(@media_comments)
.page(params[:page]).per(10)
end

Here

(Reader.find_by(user: current_user, thread: m[:thread])
&.updated_at || 1000.years.ago) < m[:latest_comment].created_at &&

we use a timestamp and compare it to the latest comment created for that media. The timestamp used is either the updated_at entry of the Reader or 1000.years.ago (which is $-\infty$ for practical reasons). If no reader exists, we cannot just randomly take any other value. That's why we instead have to modify what media we are considering here in the first place.

We get the media via this method:

@media_comments = current_user.media_latest_comments

So, let's modify it in the user model:

mampf/app/models/user.rb

Lines 552 to 567 in 9583efb

def media_latest_comments
relevant_media = []
subscribed_commentable_media_with_comments.each do |m|
latest_comment = m.commontator_thread.comments
.reject { |c| c.creator == self }
.max_by(&:created_at)
next unless latest_comment
relevant_media << { medium: m,
thread: m.commontator_thread,
latest_comment: latest_comment }
end
relevant_media.sort_by { |x| x[:latest_comment].created_at }.reverse
end

For any media, we will now disregard any comments we posted by ourselves. Thus, the latest_comment variable can never refer to any comment/reply we posted. And this is what we want: now the comment method in the MainController is not even considering these kind of comments and therefore they cannot appear in the COP.

Finally, this allows us to not bother creating a Reader entry whenever we post something. Instead we can simply use a find_by:

@reader = Reader.find_by(user: current_user, thread: @commontator_thread)
return unless @reader


From my preliminary tests, this seems to work even for your scenarios. I'm not sure if we should get rid of the reader stuff alltogether here:

# make sure that the thread associated to this comment is marked as read
# by the comment creator (unless some other user posted a comment in it
# that has not yet been read)
@reader = Reader.find_by(user: current_user, thread: @commontator_thread)
return unless @reader
if unseen_comments?
@update_icon = true
return
end
@reader.touch
end

  • At least, the @reader.touch shouldn't be there anymore, right? The reader updated_at field should be only changed whenever I - as a user - click on the dismiss button or the "Clear all" button in the COP. And this is already done in the ReadersController alongside setting the unread_comments to false accordingly. In which scenario, do we have to update the unread comments icon directly after creating a new comment? Is there any?
  • The reason for the lines after the find_by is this comment of yours. But I have to dig deeper there again.
  • Some logic is copy and pasted!, e.g. see the call to the Reader in ReaderController.update and MainController.comments.
  • Check that the bug I encountered in #515 is not present here.
  • Currently in Fix unread comments status (new trial) #576, when we post a reply, the comment is still marked as unread in the COP. However, this behavior was the same beforehand (based on a short test, but this is true, right?) -> I think that one can assume someone has seen the comment when that person even replies to it, so we could mark the respective comment we are replying to as "read" using the same logic executed when the person clicks on the "dismiss" button in the COP to mark a comment as read. But this behavioral change is something for another PR I guess.

Please don't comment over at #576 and instead here. If #576 actually works out, we can still incorporate it into this PR such that we still have all the discussion and comments in one place.

@Splines Splines added the bug label Jan 4, 2024
@fosterfarrell9
Copy link
Collaborator Author

At least, the @reader.touch shouldn't be there anymore, right? The reader updated_at field should be only changed whenever I - as a user - click on the dismiss button or the "Clear all" button in the COP. And this is already done in the ReadersController alongside setting the unread_comments to false accordingly. In which scenario, do we have to update the unread comments icon directly after creating a new comment? Is there any?

I agree that (probably) the @reader.touch can be omitted now. As for the update of the commentsIcon: The code change in comments_controller.rb to

   # make sure that the thread associated to this comment is marked as read 
   # by the comment creator (unless some other user posted a comment in it 
   # that has not yet been read) 
   @reader = Reader.find_by(user: current_user, thread: @commontator_thread) 
   return unless @reader 
  
   if unseen_comments? 
     @update_icon = true 
     return 
   end 
   @reader.touch 
 end 

leads to the following minor inconsistency:
Assume you have a medium without any coment yet, your commentsIcon is white and you are writing a comment. During the time you are writing, some other user posts a comment on the same medium. After you post your comment, your commentsIcon remains white. After the next page reload, it turns yellow. Now repeat the same procedure with a medium that already has a read comment (e.g. the medium just used). Then the commentsIcon will turn yellow after you post your comment. The reason is that in the first case, no reader is found and the method returns immediately, while in the second case the update_icon flag is set to true.

I think we should decide on what the wanted behaviour should be in this situation.

Currently in #576, when we post a reply, the comment is still marked as unread in the COP. However, this behavior was the same beforehand (based on a short test, but this is true, right?)

This is part of the issue with #576. It is as you describe with a medium that already has comments, but with a medium without comments the scenario you describe (assume you have no toher unread comments) results in a yellow commentsIcon, but an empty list in the COP page.

Another minor inconsistency (that I could live with) that results from the rewriting of the media_latest_comments method is the following: In the list of comments on the COP, the "Latest post" column now ignores posts by the user themself so it should actually be "Latest post by other users".

@Splines
Copy link
Member

Splines commented Jan 5, 2024

  • With regards to the early return. You're right, we should not have that early return and still check whether the icon should be updated in that method. I can fix that.
  • For the line starting with "It is as you describe with..." I need a bit more clarification on what is not working right now. I cannot reproduce your original scenario anymore with my changes on Fix unread comments status (new trial) #576.
  • With regards to the "latest post" column: A simple fix would be to create a separate method and not filter the media array there such that this column is still accurate and might also show your own posts. This solution could be constructed such that we don't have to query twice.

@fosterfarrell9
Copy link
Collaborator Author

With regards to the early return. You're right, we should not have that early return and still check whether the icon should be updated in that method. I can fix that.

Since @reader.touch seems no longer necessary, the lines after the comment should probably simplify to

return unless unseen_comments?

@update_icon = true 

or something like that.

For the line starting with "It is as you describe with..." I need a bit more clarification on what is not working right now. I cannot reproduce #573 (comment) anymore with my changes on #576.

We probably misunderstood each other. I understood your question

However, this behavior was the same beforehand (based on a short test, but this is true, right?)

as a question on the behaviour in #515. So, in my answer

This is part of the issue with #576. It is as you describe with a medium that already has comments, but with a medium without comments the scenario you describe (assume you have no toher unread comments) results in a yellow commentsIcon, but an empty list in the COP page.

I refer to #515, but mistakenly wrote #576.
Does that clear it up?

With regards to the "latest post" column: A simple fix would be to create a separate method and not filter the media array there such that this column is still accurate and might also show your own posts. This solution could be constructed such that we don't have to query twice.

That is fine with me.

@Splines
Copy link
Member

Splines commented Jan 5, 2024

However, this behavior was the same beforehand (based on a short test, but this is true, right?)

This was indeed a question referring to the issue #515. What I'm refering to with "this behavior" is the one denoted the line before (though I agree my sentence was really not the best and easy to misunderstand)

when we post a reply, the comment is still marked as unread in the COP.

As far as I know, this was already the case before #515 and might be a behavior we don't want. It's different from the issue we are dealing with here. It's just something that I wanted to bring up to the table: since I would expect that whenever I post a reply to a comment that I clicked on in the COP, then this comment should be marked as "read" for me and not appear in the COP anymore. However, this requires a finer granularity; namely, this requires to store for each comment -- and not just whole threads -- whether this comment was seen by a user. As this is probably something for another PR, I asked if that behavior was the same even before #515, and I'm sure it was (based on a short test by checking out a commit before the one in #515). Just wanted to make sure that I didn't accidentally break something that was working beforehand.


With regards to the issue we're actually dealing with here: did I understand it correctly that #515 is not reproducible anymore in #576? If so, I could go ahead and refine #576. As to how to proceed: if #576 is the way to go, I've changed my mind and think it's better to continue working over there as reverting the commits here might be too tedious. And we also have all the open conversations here that clutter the space and might make a review for #576 (if incorporated into this PR) a bit of an annoying process.

@fosterfarrell9
Copy link
Collaborator Author

With regards to the issue we're actually dealing with here: did I understand it correctly that #515 is not reproducible anymore in #576?

Yes.

If so, I could go ahead and refine #576. As to how to proceed: if #576 is the way to go

I think #576 is the way to go.

since I would expect that whenever I post a reply to a comment that I clicked on in the COP, then this comment should be marked as "read" for me and not appear in the COP anymore. However, this requires a finer granularity; namely, this requires to store for each comment -- and not just whole threads -- whether this comment was seen by a user. As this is probably something for another PR, I asked if that behavior was the same even before #515, and I'm sure it was (based on a short test by checking out a commit before the one in #515). Just wanted to make sure that I didn't accidentally break something that was working beforehand.

I agree that this would be something for a different PR. Personally, I am honestly rather indifferent towards the current behaviour (recall that I also did not mind the behaviour before #515), so if introducing this finer granularity requires some work, we should (at a later point) at least discuss if the additional work would be worth it.

Splines added a commit that referenced this pull request Jan 8, 2024
See discussion on #574 for further details.
Previous PR for this was #576, closed in favor of this one
as this directly branches off the new "dev" branch.
@Splines Splines mentioned this pull request Jan 8, 2024
3 tasks
@Splines
Copy link
Member

Splines commented Jan 8, 2024

Closed in favor of #585, will delete this branch once #585 is merged into dev.

@Splines Splines closed this Jan 8, 2024
@Splines Splines deleted the fix/unread-comments-status branch January 16, 2024 18:15
Splines added a commit that referenced this pull request Jan 16, 2024
* Reapply first fix for Reader/Media

See discussion on #574 for further details.
Previous PR for this was #576, closed in favor of this one
as this directly branches off the new "dev" branch.

* Correctly show latest post (might be current_user's comment)

* Fix update of unread comments logic in comments controller

* Fix update icon logic and latest post comment

* Simplify latest comment logic

* Improve code comments

* Further improve comments

* Fix wording in comment

* Fix construction of media array & use `.blank?` instead of `.empty?`
Splines added a commit that referenced this pull request Feb 14, 2024
* Reapply first fix for Reader/Media

See discussion on #574 for further details.
Previous PR for this was #576, closed in favor of this one
as this directly branches off the new "dev" branch.

* Correctly show latest post (might be current_user's comment)

* Fix update of unread comments logic in comments controller

* Fix update icon logic and latest post comment

* Simplify latest comment logic

* Improve code comments

* Further improve comments

* Fix wording in comment

* Fix construction of media array & use `.blank?` instead of `.empty?`
fosterfarrell9 added a commit that referenced this pull request Feb 16, 2024
* Warn about too long GitHub commit messages (#586)

* Fix comment status (#585)

* Reapply first fix for Reader/Media

See discussion on #574 for further details.
Previous PR for this was #576, closed in favor of this one
as this directly branches off the new "dev" branch.

* Correctly show latest post (might be current_user's comment)

* Fix update of unread comments logic in comments controller

* Fix update icon logic and latest post comment

* Simplify latest comment logic

* Improve code comments

* Further improve comments

* Fix wording in comment

* Fix construction of media array & use `.blank?` instead of `.empty?`

* Migrate `unread_comments` flag (fix inconsistencies) (#587)

* Add dummy migration

* Implement migration for unread comment flag

* Remove unnecessary comment

* Declare migration as not idempotent

* Use array.length instead of counting

* Throw error to prevent revert of migration

* Fix severe flaws in unread comments migration

* Simplify Reader retrieval

* Use the more explicit `.nil?` method

* Update migration date

* Fix annoying bug: don't use `.select!` but `.select`

* Polish migration

e.g. update comment, more suitable name for the method etc.

* Rename method according to #585

* Use `warn` log level for migration (#588)

* Rename get_statistics.coffee file to statistics.coffee (#591)

This is to reflect the corresponding renaming of the route
due to rubocop.

* fix behaviour of media search

* Disable OR/AND buttons if "all" tags is enabled

(inside the media search, right now only frontend change)

* Use ids autogenerated by Rails

This is such that we can still click on the label to select
the respective radio button, instead of only being able
to click on the radio button itself.

* Disable AND/OR initially (until user deselects "all tags")

* Don't restrict media search if "all" tags is enabled.

If the "all tags" option is enabled in the search, we should allow the
results to include *any* tag. This is automatically the case if we just
don't add any restriction regarding the tag ids in the SOLR search.

This change accompanies the frontend change to disable the AND/OR
buttons if the "all" button is selected meaning the user wants to search
for all tags. See #593 for more details.

---------

Co-authored-by: Splines <[email protected]>
Co-authored-by: Splines <[email protected]>
Splines added a commit that referenced this pull request Mar 20, 2024
Due to a forced push of the dev branch, the following list might
unfortunately include items from other branches as well.

* Init Feedback model

* Add Feedback modal view and corresponding controller

First working version, of course still a lot to improve from here.

* Migrate feedback form to Bootstrap v5

* Add basic styling to Feedback form

* Add "allow contact via mail" checkbox

A new column was added to the Feedbacks schema.
Note that we did not create a new migration as this is a PR which should
only contain one migration, namely the one for the creation of the whol
Feedback table.

* Toggle "allow email contact" by default

* Improve submit button handler (outsource to function)

* Init feedback mailer

Right now just for ourselves, so that we get a plaintext mail with
the feedback of a user.

Env variables were adjusted accordingly, but need to be set manually
in the production environment!

* Adjust feedback mail in views

* Implement success/error flow with toast messages

* Add missing database field "can_contact"

* Add internationalization to feedback error/success

* Lint some files

* Set feedback text field as required with min 10 chars

* Add "optional" to title in email

* Adjust spacing around feedback button

* Internationalize tooltip

* Delete console log

* Add comment describing hidden submit button handler

* Delete default test specs

* Add proper validation for Feedback body

Alongside this, also made sure that we use a custom client-side
validation message when input is too short (under 10 chars long).
This allows us to use the language the user has selected in MaMpf
instead of the browser language.

* Default `can_contact` to false in backend

* Update bootstrap to v5.3.1

command used: bundle update bootstrap
bundle update bootstrap --conservative did not work, as docker
containers did not start again due to dependency errors

* Revert "Update bootstrap to v5.3.1" in favor of PR #537

This reverts commit 5cd1af2.

* Submit form via Ctrl + Enter when modal is opened

* Remove default nil value from ENV.fetch()

* Revert "Remove default nil value from ENV.fetch()"

This reverts commit 696a395.

* Rename button to 'Send' (not 'Save')

* Check if should register feedback event handlers

* Make feedback button ID more specific

* Fix line wrapping (code style)

* Use delete on cascade to be able to delete a user

even if he/she has sent some feedback

* Move Send button before Cancel button

* Replace "on delete cascade" with "dependent destroy"

* Add cypress rules to ESLint & ignore some patterns

* Allow usage of tempusDominus global variable

* Ignore JS files with Sprocket syntax

* Further improve rules, e.g. allow common globals

* Ignore sprocket syntax in cable.js

* Autofix all `.js` and `.js.erb` files

Command used:
`yarn run eslint --fix .`

Still 47 problems (27 errors, 20 warnings) after this.

* Fix variables in turbolink fix

* Prepend unused variables with "_"

* Get rid of unused widget variable

* Fix specs comment tab alignment

* Warn about too long GitHub commit messages (#586)

* Fix comment status (#585)

* Reapply first fix for Reader/Media

See discussion on #574 for further details.
Previous PR for this was #576, closed in favor of this one
as this directly branches off the new "dev" branch.

* Correctly show latest post (might be current_user's comment)

* Fix update of unread comments logic in comments controller

* Fix update icon logic and latest post comment

* Simplify latest comment logic

* Improve code comments

* Further improve comments

* Fix wording in comment

* Fix construction of media array & use `.blank?` instead of `.empty?`

* Migrate `unread_comments` flag (fix inconsistencies) (#587)

* Add dummy migration

* Implement migration for unread comment flag

* Remove unnecessary comment

* Declare migration as not idempotent

* Use array.length instead of counting

* Throw error to prevent revert of migration

* Fix severe flaws in unread comments migration

* Simplify Reader retrieval

* Use the more explicit `.nil?` method

* Update migration date

* Fix annoying bug: don't use `.select!` but `.select`

* Polish migration

e.g. update comment, more suitable name for the method etc.

* Rename method according to #585

* Use `warn` log level for migration (#588)

* Fix linting in feedback.js

* Fix RuboCop errors

* Fix remaining ESLint errors

* Update timestamp of feedback migration

* Add missing Feedback email to prod docker.env

* Remove unnecessary Feedback env variables

* Add validation message for empty body

* Change `const` to `var` to avoid "redefined" errors

* Update timestamp of feedback migration (again)
Splines added a commit that referenced this pull request Mar 31, 2024
* Outsource resize and fullscreen functions for thyme players.

* Outsource chapter functionality of the (normal) thyme player.

* Fix some minor errors and clean up.

* Outsource emergency button to separate file.

* Outsource heatmap functionality to separate JavaScript file.

* Make heatmap a variable of thyme feedback.

* Made some auxiliary functions private.

* Outsource functionaly of the annotation toggle and improve behaviour.

* Outsource some parts of the functionality of thyme which checks if the native HTML player is used or not.

* Improve display manager.

* Outscource video reference into thyme_attributes.js.

* Put element field back into the constructors of buttons such that the ids from the HTML files can be different in different players.

* Remove speed selector from thyme feedback player and adjust some CSS attributes in normal and feedback player.

* Outsource speed selector functionality in separate file.

* Rename update_markers into update_annotations (now also in non-JS-files).

* Remove reference on the video control bar in the java script files as it is not used.

* Outsource global annotation function into a new class called "AnnotationManager".

* Restructure annotation area.

* Fix some properties of the annotation-tool.

* Bring toggle-visible-for-teacher-annotations function (fully) to frontend (Java Script).

* Fix controller method to make the last commit work.

* Rename thyme.js into thyme_player.js.

* Clean up.

* Remove annotationSort() from utility.js as this function is now part of annotation_manager.js.

* Remove function annotationIndex() from utility.js as this function is unused by now.

* Outsource the parts of the thyme player scripts which set up the max time label.

* Code clean up and fix error message for editing annotations.

* Remove comment and made the function sortAnnotations() static.

* Next and previous annotation buttons now consider only valid annotations.

* Move attribute activeAnnotationId to the class AnnotationArea (now save the full annotation instead of just the id).

* Optical and structural improvements of code and fix of updating annotations after submitting the annotation form.

* Rename class Button as Component.

* Outsource hide functionality of the control bar.

* Rename class ControlBar as ControlBarHider and restyle some parts of the code.

* Outsource ia button.

* Outsource ia-close button.

* Replace expressions of the form "$('#' + component.id)" by "$(component)".

* Renamed chapters.js into chapter_manager.js and wrap the functionality into a class.

* Remove function iconClass() as it seems to be used nowhere.

* Made max height of the heatmap a static attribute.

* Outsource chapter functionality into chapter manager class. Remove back button (it'll be added again in a few commits when the chapter manager is well-structured).

* Changed fixed chapters id into an attribute of the chapter manager constructor.

* Remove parameter from nextChapterStart() and previousChapterStart() (current video time can be taken from thymeAttributes).

* Remove chapter class as modelling chapters as objects seems to make the situation more complicated than it is (at least for the moment).

* Remove unused variable from chapter manager.

* Outsource metadata functionality.

* Made metadataBefore() and metadataAfter() private.

* Clean up.

* Add back button again.

* Remove unnecessary variables from thyme editor and fix use of thymeUtility function secondsToTime().

* Change var to let or const in the thyme editor script.

* Add missing ; in for-loop and fix variable declaration.

* Make time in the plus/minus (ten) button variable.

* Replace comment.

* Use play, mute and plus/minus x seconds buttons from the new classes.

* Use seek bar from the new class and fix an event listener.

* Use volume bar from the new class.

* Use setUpMaxTime() from thymeUtility and remove unnecessary time update listener.

* Move dataURLtoBlob() to thymeUtility.

* Outsource add item/reference/screenshot button.

* Rename near_mistake_annotations by num_nearby_mistake_annotations and clean up code.

* Replace find_by_id by find_by.

* Clean up medium model.

* Get rid of unused comment.

* Rename id.

* Change id name.

* Capitalized the German words Du, Deine, etc.

* Fix emergency link feature and made "You need further help? ..." invisible if no link was entered by the teacher.

* Split up thyme CSS.

* Remove global CSS which is not needed.

* Remove duplication of the color map.

* Update annotation area when annotations are updated.

* Fix bad behaviour of ia-button, interactive area and annotation area.

* Replace CoffeeScript by JavaScript in app/views/annotations and clean up. Now, all subcategory radios show the emergency link (before it was only definition).

* Add tooltips for the category radio buttons and for the "post as comment"-field.

* Clean up code.

* Add comment.

* Put thyme player scripts in the thyme folder, separate load of thyme related scripts in application.js and add warning comment.

* Fix missing Latex preview in the annotation modal.

* Lower the opacity of previous/next button in the annotation area if the given annotation ist the first/last of the array.

* Correct language.

* Select necessary annotation attributes in backend, not in frontend.

* Improve interaction between annotation and associated comment.

* Set default value of visible_for_teacher to false, whenever the annotation_status doesn't equal 1 (before it was only set to false if the annotation_status equalled -1).

* Fix wrong/missing annotation subtext in the annotation area.

* Remove warning locales from the form html file and put it into the locales html file.

* Make the annotations in the thyme player appear (uniformly) in the same language as the user language.

* Code clean up.

* Replace text "subtext" by enum "subcategory" in the annotation table in the db.

* Add (sub)category class (+ superclass) to remove hardcoded categories in the (non-view) JavaScript.

* Comment update implies annotation update.

* Change label "Enable emergency button?" and add helpdesk.

* Add case "Thymestamp not found" to update_comments.

* Code clean up.

* Code clean up.

* Fix resize.

* link Commontator::Comment and Annotation models

* Delete unwanted .ruby-version file

* Update pdfcomprezzor to version on mampf-next

* Fix resize and IA bugs.

* Remove "video is not null" check from thyme players.

* Remove test for Internet Explorer

* Set default values for annotations_status

* Update annotations without delay

* Replace plus/minus button by time button.

* Add shortcuts for prev/next annotation.

* Merge together multiple annotation-related migrations

* Improve code comment

* Remove renaming of "this" in JS class

* Redesign "post as comment" checkbox

according to Bootstrap here:
https://getbootstrap.com/docs/5.3/forms/checks-radios/#checks

* Restructure cancel/close buttons

* Improve annotation commontator::comment association

* Code clean up

* Code clean-up

* Replace name "emergency button" by "annotation button"

* Add color check

* Fix comment is nil bug

* Code clean-up

* Add non-null constraint for category

* CSS improvement and bugfix

* Correct locales

* Add null constraint to category in original migration

* Rearrange save button

* Quick fix thyme feedback resize.

* Add non-nil constraint

* Add constraint to AnnotationsController

* Improve code style

* Rename variables to proper camelCase

* Improve code style

* Revert time_stamp.rb

* Remove ==/!= null statements

* Add newline after/before function

* Add comment

* Clean up code/fix bug

* Check timestamp in backend

* Add annotations status check + update

* Fix user authorization

* Reduce complexity of small/large display checks

Also removed deprecated device-width, see:
https://stackoverflow.com/a/18500871/

* Implement locking to prevent unnecessary DB calls

The AnnotationManager should handle its internal state on its own.
Therefore, updateMarkers() now checks if annotations are null
and if they are, it calls updateAnnotations() accordingly,
but only if no other method has already called updateMarkers
recently, e.g. when multiple resize events are fired in a small
period of time. Resource is freed in updateAnnotations.

* Rewrite handling of annotation update

* Use gender-neutral pronoun

* Replace alert() by Bootstrap alert

Used for "Post as comment" warnings.

* Use JQuery syntax and lint file

Note we also use .trigger("change") to trigger event listeners
for the checkbox, otherwise they won't be fired.

* Add tooltip (helpdesk) for category

* Make color picker elements round

* Use paddings instead of line breaks

* Don't show warnings if annotation was posted

* Show further help link in new tab

Also shortened current distinction into different categories
as right now it is not needed.

* Fix unwanted line indentation

* Use bootstrap switch for preview toggle

* Use bootstrap switch for annotation toggle

Also removed unwanted string concatenation artifacts

* Fix alignment of preview column & make more responsive

Note that we did not optimize for very small devices as the annotation tool
cannot be opened there. However, for tablets it should work fine.

* Fix case-sensitive color lookup bug

Due to this bug, one could not create annotations
with the last color (almost white).

* Pause video also when editing an annotation

* Update modal background according to annotation color

When we create a new annotation, a random color is chosen.
TODO: Maybe do this random choice already in the backend,
not just in the frontend.

* Fix another non-capitalized color bug

* Add transition to modal header background color

* Add more restrictive color check

* Don't show toggle if it doesn't do anything

* Put radius to backend

* Add cypress rules to ESLint & ignore some patterns

* Allow usage of tempusDominus global variable

* Ignore JS files with Sprocket syntax

* Further improve rules, e.g. allow common globals

* Ignore sprocket syntax in cable.js

* Autofix all `.js` and `.js.erb` files

Command used:
`yarn run eslint --fix .`

Still 47 problems (27 errors, 20 warnings) after this.

* Fix variables in turbolink fix

* Prepend unused variables with "_"

* Get rid of unused widget variable

* Fix specs comment tab alignment

* Bump active record schema version

This was automatically done when running the pending migrations.

* Run ESLint autofixes

* Hack: Add Thyme & Annotation tool globals to ESLint globals

See the comment in .eslintrc.js for more details for why we do this.

* Rename resize to Resizer to avoid name conflicts

* Show correct modal title.

* Fix duplicate comment bug

* Execute Rubocop safe autocorrect.

* Warn about too long GitHub commit messages (#586)

* Fix safe rubocops (manually)

* Fix unsafe autocorrections

* Fix comment status (#585)

* Reapply first fix for Reader/Media

See discussion on #574 for further details.
Previous PR for this was #576, closed in favor of this one
as this directly branches off the new "dev" branch.

* Correctly show latest post (might be current_user's comment)

* Fix update of unread comments logic in comments controller

* Fix update icon logic and latest post comment

* Simplify latest comment logic

* Improve code comments

* Further improve comments

* Fix wording in comment

* Fix construction of media array & use `.blank?` instead of `.empty?`

* Migrate `unread_comments` flag (fix inconsistencies) (#587)

* Add dummy migration

* Implement migration for unread comment flag

* Remove unnecessary comment

* Declare migration as not idempotent

* Use array.length instead of counting

* Throw error to prevent revert of migration

* Fix severe flaws in unread comments migration

* Simplify Reader retrieval

* Use the more explicit `.nil?` method

* Update migration date

* Fix annoying bug: don't use `.select!` but `.select`

* Polish migration

e.g. update comment, more suitable name for the method etc.

* Rename method according to #585

* Use `.instance_of` (fix unwanted merge artifact)

* Clean up annotation migrations

* Fix wrong table changing in migration & update schema

* Improve annotation area css

* Fix scrollable video bug

* Improve icons in annotation area

* Increase button click area

* Change annotation button icon

* Fix spacing between annotation area buttons

* Add title directly on <a> tags & replace close button

* Fix alignment of video control bar

* Redesign annotation markers (use map pin icon)

* Highlight currently active annotation marker

* Reregister hotkeys also after *edit* modal closes

* Improve styling of annotation button

* Add subtle gradient to video control bar

* Show active annotation marker in front

* Fix ESLint warnings

* Delete duplicate line

* Remove unused variables resp. add underscore

* Delete unnecessary `annotations.coffee` file

* Show new annotation directly in AnnotationArea

* Add shortcut to jump to current annotation

* Make annotation button more prominent

- place it closer to the timeline
- color it with more distinct colors to showcase it

* Show preview by default in annotation modal

* Make delete button id more specific

* Add icons to save/delete in annotation modal

* Improve feedback player (CSS, category toggles etc.)

- CSS: extend from main thyme player instead of copying all CSS styles over
- Register correct shortcut to switch between annotations
- Use bootstrap switches instead of custom-made toggles
- Redesign colors for feedback player category toggles
- Simplify AnnotationCategoryToggle component

* Remove syntax error (duplicated line)

* Remove unnecessary console log

* Fix broken key listeners in feedback player

* Improve positioning of heatmap & toggle spikes as well

* Also show heatmap spikes for "mistake" annotations

* Avoid heatmap being cut off at the sides

* Fix video size in feedback player

* Fix small gap in thyme player resize issue

Instead of jQuery width() and height(), we use
window.innerWidth and window.innerHeight.

* Fix wrong resizing when exiting full screen mode

* Disable annotation key listeners when area closed

* Increase width of timeline in feedback player

Also fixed positioning of other elements on the bottom bar.

* Fix keyboard shortcut abbreviation hint

* Update schema & timestamps for annotation DB migrations

* Ignore other lib paths in autoloading

* Do not load Commontator extension in precompiling

* Get rid of "@extend" in SCSS, use CSS classes instead

* Fix amplitude of heatmap if no annotations are present

* Use string interpolation to construct heatmap string

* Don't rescue `NoDatabaseError`

* Fix nulldb error

* Add "sure to delete" warning in annotation modal

* Change annotation status numbering & activate by default

This is the new labeling:
-1: inherit from lecture
0: disabled
1: enabled

Beforehand it was:
-1: disabled
0: inherit from lecture
1: enabled

However, I think it makes more sense to have 0/1 for disabled/enabled
and leave the -1 for the "inherit" use case.

Also, the "share annotation with lecturer" feature is now enabled by
default for all lectures.

* Rename "Thymestamp" to "Timestamp"

* Update annotation db migration timestamps (one last time)

* Use enum for status in emergency link update

* Prepend emergency link with "https://" if necessary

* Remove unwanted change

---------

Co-authored-by: fosterfarrell9 <[email protected]>
Co-authored-by: Splines <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Comments icon colored with no new comments
2 participants