From 4483819bd7c4ef158e6a89766e1c702bebb24167 Mon Sep 17 00:00:00 2001 From: Slendy Date: Fri, 23 Sep 2022 00:03:54 -0500 Subject: [PATCH] Implement hearting playlists --- .../Controllers/Slots/ListController.cs | 63 ++++++++++++++ ProjectLighthouse/Database.cs | 24 +++++ ProjectLighthouse/Levels/Playlist.cs | 10 +-- ProjectLighthouse/PlayerData/Profiles/User.cs | 5 ++ ProjectLighthouse/ProjectLighthouse.csproj | 4 +- .../Migrations/DatabaseModelSnapshot.cs | 87 ++++++++++++++++++- 6 files changed, 185 insertions(+), 8 deletions(-) diff --git a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs index 3e1d051b..6abe73d1 100644 --- a/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs +++ b/ProjectLighthouse.Servers.GameServer/Controllers/Slots/ListController.cs @@ -197,6 +197,69 @@ public class ListController : ControllerBase #endregion + #region Hearted Playlists + + [HttpGet("favouritePlaylists/{username}")] + public async Task GetFavouritePlaylists(string username, [FromQuery] int pageStart, [FromQuery] int pageSize) + { + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); + + if (pageSize <= 0) return this.BadRequest(); + + User? targetUser = await this.database.Users.FirstOrDefaultAsync(u => u.Username == username); + if (targetUser == null) return this.StatusCode(403, ""); + + IEnumerable heartedPlaylists = this.database.Playlists.Where(p => p.CreatorId == targetUser.UserId) + .Skip(Math.Max(0, pageStart - 1)) + .Take(Math.Min(pageSize, 30)) + .AsEnumerable(); + + string response = heartedPlaylists.Aggregate(string.Empty, (current, p) => current + p.Serialize()); + + return this.Ok + ( + LbpSerializer.TaggedStringElement("favouritePlaylists", response, new Dictionary + { + { "total", this.database.HeartedPlaylists.Count(p => p.UserId == targetUser.UserId) }, + { "hint_start", pageStart + Math.Min(pageSize, 30) }, + }) + ); + } + + [HttpPost("favourite/playlist/{playlistId:int}")] + public async Task AddFavouritePlaylist(int playlistId) + { + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); + + User? user = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == token.UserId); + if (user == null) return this.BadRequest(); + + Playlist? playlist = await this.database.Playlists.FirstOrDefaultAsync(s => s.PlaylistId == playlistId); + if (playlist == null) return this.NotFound(); + + await this.database.HeartPlaylist(token.UserId, playlist); + + return await this.GetFavouritePlaylists(user.Username, 1, 30); + } + + [HttpPost("unfavourite/slot/{playlistId:int}")] + public async Task RemoveFavouritePlaylist(int playlistId) + { + GameToken? token = await this.database.GameTokenFromRequest(this.Request); + if (token == null) return this.StatusCode(403, ""); + + Playlist? playlist = await this.database.Playlists.FirstOrDefaultAsync(s => s.PlaylistId == playlistId); + if (playlist == null) return this.NotFound(); + + await this.database.UnheartPlaylist(token.UserId, playlist); + + return this.Ok(); + } + + #endregion + #endregion Levels #region Users diff --git a/ProjectLighthouse/Database.cs b/ProjectLighthouse/Database.cs index 86d1a38a..c87263fe 100644 --- a/ProjectLighthouse/Database.cs +++ b/ProjectLighthouse/Database.cs @@ -30,6 +30,7 @@ public class Database : DbContext public DbSet QueuedLevels { get; set; } public DbSet HeartedLevels { get; set; } public DbSet HeartedProfiles { get; set; } + public DbSet HeartedPlaylists { get; set; } public DbSet Comments { get; set; } public DbSet GameTokens { get; set; } public DbSet WebTokens { get; set; } @@ -51,6 +52,7 @@ public class Database : DbContext public DbSet PasswordResetTokens { get; set; } public DbSet RegistrationTokens { get; set; } public DbSet APIKeys { get; set; } + public DbSet Playlists { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder options) => options.UseMySql(ServerConfiguration.Instance.DbConnectionString, MySqlServerVersion.LatestSupportedServerVersion); @@ -231,6 +233,28 @@ public class Database : DbContext await this.SaveChangesAsync(); } + public async Task HeartPlaylist(int userId, Playlist heartedPlaylist) + { + HeartedPlaylist? heartedList = await this.HeartedPlaylists.FirstOrDefaultAsync(p => p.UserId == userId && p.PlaylistId == heartedPlaylist.PlaylistId); + if (heartedList != null) return; + + this.HeartedPlaylists.Add(new HeartedPlaylist() + { + PlaylistId = heartedPlaylist.PlaylistId, + UserId = userId, + }); + + await this.SaveChangesAsync(); + } + + public async Task UnheartPlaylist(int userId, Playlist heartedPlaylist) + { + HeartedPlaylist? heartedList = await this.HeartedPlaylists.FirstOrDefaultAsync(p => p.UserId == userId && p.PlaylistId == heartedPlaylist.PlaylistId); + if (heartedList != null) this.HeartedPlaylists.Remove(heartedList); + + await this.SaveChangesAsync(); + } + public async Task HeartLevel(int userId, Slot heartedSlot) { HeartedLevel? heartedLevel = await this.HeartedLevels.FirstOrDefaultAsync(q => q.UserId == userId && q.SlotId == heartedSlot.SlotId); diff --git a/ProjectLighthouse/Levels/Playlist.cs b/ProjectLighthouse/Levels/Playlist.cs index 4355afc2..deb2a108 100644 --- a/ProjectLighthouse/Levels/Playlist.cs +++ b/ProjectLighthouse/Levels/Playlist.cs @@ -27,6 +27,8 @@ public class Playlist [JsonIgnore] public User? Creator { get; set; } + public int Hearts(Database database) => database.HeartedPlaylists.Count(p => p.HeartedPlaylistId == this.PlaylistId); + public string SlotCollection { get; set; } = ""; [JsonIgnore] @@ -46,13 +48,11 @@ public class Playlist using Database database = new(); string playlist = LbpSerializer.StringElement("id", this.PlaylistId) + LbpSerializer.StringElement("author", - LbpSerializer.StringElement("npHandle", this.Creator?.Username)) + + LbpSerializer.StringElement("npHandle", this.Creator?.Username)) + LbpSerializer.StringElement("name", this.Name) + LbpSerializer.StringElement("description", this.Description) + LbpSerializer.StringElement("levels", this.SlotIds.Length) + - LbpSerializer.StringElement("thumbs", 0) + - LbpSerializer.StringElement("plays", 0) + - LbpSerializer.StringElement("unique_plays", 0) + + LbpSerializer.StringElement("hearts", this.Hearts(database)) + LbpSerializer.StringElement("levels_quota", ServerConfiguration.Instance.UserGeneratedContentLimits.ListsQuota) + this.SerializeIcons(database); @@ -62,7 +62,7 @@ public class Playlist private string SerializeIcons(Database database) { string iconList = this.SlotIds.Select(id => database.Slots.FirstOrDefault(s => s.SlotId == id)) - .Where(slot => slot != null) + .Where(slot => slot != null && slot.IconHash.Length > 0) .Aggregate(string.Empty, (current, slot) => current + LbpSerializer.StringElement("icon", slot?.IconHash)); return LbpSerializer.StringElement("icons", iconList); } diff --git a/ProjectLighthouse/PlayerData/Profiles/User.cs b/ProjectLighthouse/PlayerData/Profiles/User.cs index 534fc9bd..46983b08 100644 --- a/ProjectLighthouse/PlayerData/Profiles/User.cs +++ b/ProjectLighthouse/PlayerData/Profiles/User.cs @@ -116,6 +116,10 @@ public class User [JsonIgnore] public int HeartedUsers => this.database.HeartedProfiles.Count(p => p.UserId == this.UserId); + [NotMapped] + [JsonIgnore] + public int HeartedPlaylists => this.database.HeartedPlaylists.Count(p => p.UserId == this.UserId); + [NotMapped] [JsonIgnore] public int QueuedLevels => this.database.QueuedLevels.Count(p => p.UserId == this.UserId); @@ -218,6 +222,7 @@ public class User LbpSerializer.StringElement("location", this.Location.Serialize()) + LbpSerializer.StringElement("favouriteSlotCount", this.HeartedLevels, true) + LbpSerializer.StringElement("favouriteUserCount", this.HeartedUsers, true) + + LbpSerializer.StringElement("favouritePlaylistCount", this.HeartedPlaylists, true) + LbpSerializer.StringElement("lolcatftwCount", this.QueuedLevels, true) + LbpSerializer.StringElement("pins", this.Pins, true); diff --git a/ProjectLighthouse/ProjectLighthouse.csproj b/ProjectLighthouse/ProjectLighthouse.csproj index fd7a2b22..8f670e49 100644 --- a/ProjectLighthouse/ProjectLighthouse.csproj +++ b/ProjectLighthouse/ProjectLighthouse.csproj @@ -10,8 +10,8 @@ - - + + diff --git a/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs b/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs index 1ce63adf..e4084263 100644 --- a/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs +++ b/ProjectLighthouse/ProjectLighthouse/Migrations/DatabaseModelSnapshot.cs @@ -16,7 +16,7 @@ namespace ProjectLighthouse.Migrations { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "6.0.8") + .HasAnnotation("ProductVersion", "6.0.9") .HasAnnotation("Relational:MaxIdentifierLength", 64); modelBuilder.Entity("LBPUnion.ProjectLighthouse.Administration.CompletedMigration", b => @@ -172,6 +172,55 @@ namespace ProjectLighthouse.Migrations b.ToTable("HeartedLevels"); }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.HeartedPlaylist", b => + { + b.Property("HeartedPlaylistId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("PlaylistId") + .HasColumnType("int"); + + b.Property("UserId") + .HasColumnType("int"); + + b.HasKey("HeartedPlaylistId"); + + b.HasIndex("PlaylistId"); + + b.HasIndex("UserId"); + + b.ToTable("HeartedPlaylists"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.Playlist", b => + { + b.Property("PlaylistId") + .ValueGeneratedOnAdd() + .HasColumnType("int"); + + b.Property("CreatorId") + .HasColumnType("int"); + + b.Property("Description") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("SlotCollection") + .IsRequired() + .HasColumnType("longtext"); + + b.HasKey("PlaylistId"); + + b.HasIndex("CreatorId"); + + b.ToTable("Playlists"); + }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.QueuedLevel", b => { b.Property("QueuedLevelId") @@ -273,6 +322,9 @@ namespace ProjectLighthouse.Migrations b.Property("InternalSlotId") .HasColumnType("int"); + b.Property("IsAdventurePlanet") + .HasColumnType("tinyint(1)"); + b.Property("LastUpdated") .HasColumnType("bigint"); @@ -903,6 +955,9 @@ namespace ProjectLighthouse.Migrations .ValueGeneratedOnAdd() .HasColumnType("int"); + b.Property("ChildSlotId") + .HasColumnType("int"); + b.Property("PlayerIdCollection") .HasColumnType("longtext"); @@ -989,6 +1044,36 @@ namespace ProjectLighthouse.Migrations b.Navigation("User"); }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.HeartedPlaylist", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.Levels.Playlist", "Playlist") + .WithMany() + .HasForeignKey("PlaylistId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "User") + .WithMany() + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Playlist"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.Playlist", b => + { + b.HasOne("LBPUnion.ProjectLighthouse.PlayerData.Profiles.User", "Creator") + .WithMany() + .HasForeignKey("CreatorId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Creator"); + }); + modelBuilder.Entity("LBPUnion.ProjectLighthouse.Levels.QueuedLevel", b => { b.HasOne("LBPUnion.ProjectLighthouse.Levels.Slot", "Slot")