-
-
Notifications
You must be signed in to change notification settings - Fork 790
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
feat: User-specific Recipe Ratings #3345
feat: User-specific Recipe Ratings #3345
Conversation
Wow, what seemed like a small suggestion looks like it was quite a bit of work. Thanks! For the sorting by the rating I have a few ideas you could implement (if it's not too difficult)
|
Thanks for the feedback!
At least for now I want to avoid this, I think it will add clutter to the sort menu (and I don't think it would be super easy to implement). Maybe something down the line.
Yeah I think this is the best solution. I'll see if it's feasible, right now I'm not sure if it's possible to sort by a value on another table, but there might be some workaround we can do. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🚀
Didn't know this was an option, but I will try it out now that I have multiple Users using Mealie! |
What type of PR is this?
(REQUIRED)
What this PR does / why we need it:
(REQUIRED)
This PR migrates recipe ratings to a new user rating. Recipe ratings are now user-specific, and the recipe's "rating" property has been repurposed to display the average user rating.
More technically, a new secondary table
UserToRecipe
has been created, which stores the relationship between a user and a recipe. Existing favorites are also migrated to this secondary table, so we don't have two user/recipe tables. TheUserToRecipe
table has two data properties (aside from keys):rating
: a float representation of the user's ratingis_favorite
: a boolean indicating if the recipe is favorited or notThis has a ton of consequences, which I'll go over:
Migration
The migration for this change converts favorites into
UserToRecipe
rows. This was pretty straightforward (before we delete the old table, migrate the favorites).The ratings are a little more complicated; since we don't know who rated the recipe originally, ratings are created for all users. The migration also accounts for inserting new rows vs updating existing rows (from the favorites migration).
Previously, recipe ratings were ints, not floats. Since we're averaging user ratings, I thought it would give us an opportunity to display decimal ratings.
I ended out not actually implementing this on the frontendnow I did, see below.Database Schema
I redid the recipe's
favorited_by
and the user'sfavorite_recipes
to use the same data as the rating, with a filter onis_favorite == True
. More specifically, it's defined like this:It's view-only, so it can't be set (you have to set it using the
UserToRecipe
table instead), but it can be filtered. I used this on the frontend to efficiently load the user's favorited recipes (more on why I had to do this below):Since the recipe rating is now the average user rating, it can't be set directly. This is done with an event trigger:
Creating/modifying/deleting a
UserToRecipe
row triggers this by setting the recipe's rating to -1 (thus causing it to recalculate):API
I converted the existing
UserFavorites
controller and APIs into a newUserRatings
controller. The oldPOST
andDELETE
methods for recipe favorites still exist, which are basically just adapters to the newratings
API.I updated recipe rating sorting to sort by a combined value (as suggested by @Desecrate2337 below). That is the "sorting value" for rating is calculated such that:
This also allows public users to sort by average recipe rating (since user ratings don't exist).
There are two breaking changes to the API:
favoritedBy.id = "${userId}"
Frontend
Several changes are made to the frontend to accommodate this. I won't go into details on how it works since it's reasonably straightforward (you can check the diff), but I will go into what changes the user sees. For favorites, the experience is the same, the only difference is how the data is populated/updated/refreshed. See below for the rating changes:
If a user has not rated a recipe, but another user has, the recipe's rating (i.e. the average user rating) is shown in grey:
If a user has rated a recipe, it's red (i.e. the secondary color), same as before:
If the user is logged-out, or visiting from another group (i.e. they're an anonymous user) the grey color logic is ignored and the rating is always the secondary color (i.e. how it used to be).
Since averages are floats, when viewing an average rating, half-stars are enabled (note the color is red since I'm viewing as a non-logged-in user):
However you can still only select whole stars as a user (because selecting a half-star is kinda weird and adds unnecessary complexity, IMO, though we can change this if users would rather have that granularity).
If a user unsets a recipe, it shows as empty until the next page refresh. This is for two reasons:
When hovering over a grey rating, the color changes from grey to secondary (since you're setting the user rating, not the average rating). Here's a video demonstrating both this and the unset logic:
Obviously we can change this behavior, but I thought this made the most sense for now. Happy to gather feedback!
Since sorting uses a combined value (preferring user ratings over global, see API changes above), this is communicated to the user through the different star colors:
Which issue(s) this PR fixes:
(REQUIRED)
Closes #889
Special notes for your reviewer:
(fill-in or delete this section)
While this PR obviously does a ton, it also opens the door for some interesting average recipe vs user recipe components. I implemented the simple one with grey vs red stars, but there's definitely a lot more room there.
One small issue is that sorting by rating only sorts by average ratingfixed, see above.Testing
(fill-in or delete this section)
Lots of frontend clicking and pytest