diff --git a/ProjectLighthouse/Controllers/ReviewController.cs b/ProjectLighthouse/Controllers/ReviewController.cs index 35948771..c6145e3e 100644 --- a/ProjectLighthouse/Controllers/ReviewController.cs +++ b/ProjectLighthouse/Controllers/ReviewController.cs @@ -76,6 +76,12 @@ namespace LBPUnion.ProjectLighthouse.Controllers ratedLevel.Rating = Math.Max(Math.Min(1, rating), -1); + Review? review = await this.database.Reviews.FirstOrDefaultAsync(r => r.SlotId == slotId && r.ReviewerId == user.UserId); + if (review != null) + { + review.Thumb = ratedLevel.Rating; + } + await this.database.SaveChangesAsync(); return this.Ok(); @@ -95,15 +101,18 @@ namespace LBPUnion.ProjectLighthouse.Controllers review = new(); review.SlotId = slotId; review.ReviewerId = user.UserId; - review.DeletedBy = "none"; - + review.DeletedBy = DeletedBy.None; + review.ThumbsUp = 0; + review.ThumbsDown = 0; + this.database.Reviews.Add(review); } + review.Thumb = newReview.Thumb; review.LabelCollection = newReview.LabelCollection; review.Text = newReview.Text; review.Deleted = false; review.Timestamp = TimeHelper.UnixTimeMilliseconds(); - // sometimes the game posts a review without also calling dpadrate/user/etc (why??) + // sometimes the game posts/updates a review rating without also calling dpadrate/user/etc (why??) RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.SlotId == slotId && r.UserId == user.UserId); if (ratedLevel == null) { @@ -116,7 +125,6 @@ namespace LBPUnion.ProjectLighthouse.Controllers ratedLevel.Rating = newReview.Thumb; - await this.database.SaveChangesAsync(); return this.Ok(); @@ -137,21 +145,51 @@ namespace LBPUnion.ProjectLighthouse.Controllers Random rand = new(); - IEnumerable reviews = this.database.Reviews.Where(r => r.SlotId == slotId && r.Slot.GameVersion <= gameVersion) + Review? yourReview = await this.database.Reviews.FirstOrDefaultAsync(r => r.ReviewerId == user.UserId && r.SlotId == slotId && r.Slot.GameVersion <= gameVersion); + VisitedLevel? visitedLevel = await this.database.VisitedLevels.FirstOrDefaultAsync(v => v.UserId == user.UserId && v.SlotId == slotId && v.Slot.GameVersion <= gameVersion); + Slot? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == slotId); + if (slot == null) return this.BadRequest(); + Boolean canNowReviewLevel = visitedLevel != null && yourReview == null; + if (canNowReviewLevel) + { + RatedLevel? ratedLevel = await this.database.RatedLevels.FirstOrDefaultAsync(r => r.UserId == user.UserId && r.SlotId == slotId && r.Slot.GameVersion <= gameVersion); + + yourReview = new(); + yourReview.ReviewerId = user.UserId; + yourReview.Reviewer = user; + yourReview.Thumb = ratedLevel?.Rating == null ? 0 : ratedLevel.Rating; + yourReview.Slot = slot; + yourReview.SlotId = slotId; + yourReview.DeletedBy = DeletedBy.None; + yourReview.Text = "You haven't reviewed this level yet. Edit this blank review to upload it!"; + yourReview.LabelCollection = ""; + yourReview.Deleted = false; + yourReview.Timestamp = TimeHelper.UnixTimeMilliseconds(); + } + + IQueryable reviews = this.database.Reviews.Where(r => r.SlotId == slotId && r.Slot.GameVersion <= gameVersion) .Include(r => r.Reviewer) .Include(r => r.Slot) - .AsEnumerable() // performance? Needed for next line (ThumbsUp is not in DB) .OrderByDescending(r => r.ThumbsUp) - .ThenByDescending(_ => rand.Next()) + .ThenByDescending(_ => EF.Functions.Random()) .Skip(pageStart - 1) .Take(pageSize); - string inner = Enumerable.Aggregate(reviews, string.Empty, (current, review) => + IEnumerable prependedReviews; + if (canNowReviewLevel) // this can only be true if you have not posted a review but have visited the level { - RatedLevel? ratedLevel = this.database.RatedLevels.FirstOrDefault(r => r.SlotId == slotId && r.UserId == review.ReviewerId); - RatedReview? ratedReview = this.database.RatedReviews.FirstOrDefault(r => r.ReviewId == review.ReviewId && r.UserId == user.UserId); + // prepend the fake review to the top of the list to be easily edited + prependedReviews = reviews.ToList().Prepend(yourReview); + } + else + { + prependedReviews = reviews.ToList(); + } - return current + review.Serialize(ratedLevel, ratedReview); + string inner = Enumerable.Aggregate(prependedReviews, string.Empty, (current, review) => + { + if (review == null) return current; + return current + review.Serialize(); }); string response = LbpSerializer.TaggedStringElement("reviews", inner, new Dictionary diff --git a/ProjectLighthouse/Types/DeletedBy.cs b/ProjectLighthouse/Types/DeletedBy.cs index 02d61ec0..666e4437 100644 --- a/ProjectLighthouse/Types/DeletedBy.cs +++ b/ProjectLighthouse/Types/DeletedBy.cs @@ -1,9 +1,15 @@ +using System.Xml.Serialization; + namespace LBPUnion.ProjectLighthouse.Types { - public static class DeletedBy + public enum DeletedBy { - public static string Moderator { get => "moderator"; } - public static string LevelAuthor { get => "level_author"; } + [XmlEnum(Name = "none")] + None, + [XmlEnum(Name = "moderator")] + Moderator, + [XmlEnum(Name = "level_author")] + LevelAuthor // TODO: deletion types for comments (profile etc) } } \ No newline at end of file diff --git a/ProjectLighthouse/Types/Reviews/Review.cs b/ProjectLighthouse/Types/Reviews/Review.cs index a2ee79d0..3a25bd06 100644 --- a/ProjectLighthouse/Types/Reviews/Review.cs +++ b/ProjectLighthouse/Types/Reviews/Review.cs @@ -37,7 +37,8 @@ namespace LBPUnion.ProjectLighthouse.Types.Reviews [NotMapped] [XmlIgnore] - public string[] Labels { + public string[] Labels + { get => this.LabelCollection.Split(","); set => this.LabelCollection = string.Join(',', value); } @@ -46,44 +47,29 @@ namespace LBPUnion.ProjectLighthouse.Types.Reviews public Boolean Deleted { get; set; } [XmlElement("deleted_by")] - public string DeletedBy { get; set; } // enum ? Needs testing e.g. Moderated/Author/Level Author? etc. + public DeletedBy DeletedBy { get; set; } [XmlElement("text")] public string Text { get; set; } - [NotMapped] [XmlElement("thumb")] - public int Thumb { get; set; } // (unused) -- temp value for getting thumb from review upload body for updating level rating - - [NotMapped] + public int Thumb { get; set; } + [XmlElement("thumbsup")] - public int ThumbsUp { - get { - using Database database = new(); - - return database.RatedReviews.Count(r => r.ReviewId == this.ReviewId && r.Thumb == 1); - } - } - [NotMapped] + public int ThumbsUp { get; set; } [XmlElement("thumbsdown")] - public int ThumbsDown { - get { - using Database database = new(); + public int ThumbsDown { get; set; } - return database.RatedReviews.Count(r => r.ReviewId == this.ReviewId && r.Thumb == -1); - } - } - - public string Serialize(RatedLevel? yourLevelRating = null, RatedReview? yourRatingStats = null) { + public string Serialize(RatedLevel? yourLevelRating = null, RatedReview? yourRatingStats = null) + { return this.Serialize("review", yourLevelRating, yourRatingStats); } public string Serialize(string elementOverride, RatedLevel? yourLevelRating = null, RatedReview? yourRatingStats = null) { - string reviewData = LbpSerializer.TaggedStringElement("slot_id", this.SlotId, "type", this.Slot.Type) + LbpSerializer.StringElement("reviewer", this.Reviewer.Username) + - LbpSerializer.StringElement("thumb", yourLevelRating?.Rating) + + LbpSerializer.StringElement("thumb", this.Thumb) + LbpSerializer.StringElement("timestamp", this.Timestamp) + LbpSerializer.StringElement("labels", this.LabelCollection) + LbpSerializer.StringElement("deleted", this.Deleted) + @@ -97,5 +83,5 @@ namespace LBPUnion.ProjectLighthouse.Types.Reviews } } - + } \ No newline at end of file