From 8b36f1aecfe803b67980dc01b9504ef71fc63a2d Mon Sep 17 00:00:00 2001 From: Laura Hausmann Date: Sat, 15 Jun 2024 15:41:05 +0200 Subject: [PATCH] [backend/masto-client] Paginate likes & bookmarks based on their identifier (instead of the note identifier) --- .../Controllers/Mastodon/AccountController.cs | 35 ++++++++++++------- .../Core/Database/Tables/Note.cs | 12 +++++-- .../Core/Extensions/QueryableExtensions.cs | 16 +++++++++ 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/Iceshrimp.Backend/Controllers/Mastodon/AccountController.cs b/Iceshrimp.Backend/Controllers/Mastodon/AccountController.cs index 9d10b5ed..812462d1 100644 --- a/Iceshrimp.Backend/Controllers/Mastodon/AccountController.cs +++ b/Iceshrimp.Backend/Controllers/Mastodon/AccountController.cs @@ -504,13 +504,18 @@ public class AccountController( public async Task GetLikedNotes(MastodonPaginationQuery query) { var user = HttpContext.GetUserOrFail(); - var res = await db.Notes - .Where(p => db.Users.First(u => u == user).HasLiked(p)) - .IncludeCommonProperties() - .Paginate(query, ControllerContext) - .PrecomputeVisibilities(user) - .RenderAllForMastodonAsync(noteRenderer, user); + var likes = await db.NoteLikes + .Where(p => p.User == user) + .IncludeCommonProperties() + .Select(p => new EntityWrapper + { + Id = p.Id, Entity = p.Note.WithPrecomputedVisibilities(user) + }) + .Paginate(query, ControllerContext) + .ToListAsync(); + HttpContext.SetPaginationData(likes); + var res = await noteRenderer.RenderManyAsync(likes.Select(p => p.Entity).EnforceRenoteReplyVisibility(), user); return Ok(res); } @@ -522,13 +527,19 @@ public class AccountController( public async Task GetBookmarkedNotes(MastodonPaginationQuery query) { var user = HttpContext.GetUserOrFail(); - var res = await db.Notes - .Where(p => db.Users.First(u => u == user).HasBookmarked(p)) - .IncludeCommonProperties() - .Paginate(query, ControllerContext) - .PrecomputeVisibilities(user) - .RenderAllForMastodonAsync(noteRenderer, user); + var bookmarks = await db.NoteBookmarks + .Where(p => p.User == user) + .IncludeCommonProperties() + .Select(p => new EntityWrapper + { + Id = p.Id, Entity = p.Note.WithPrecomputedVisibilities(user) + }) + .Paginate(query, ControllerContext) + .ToListAsync(); + HttpContext.SetPaginationData(bookmarks); + var res = + await noteRenderer.RenderManyAsync(bookmarks.Select(p => p.Entity).EnforceRenoteReplyVisibility(), user); return Ok(res); } diff --git a/Iceshrimp.Backend/Core/Database/Tables/Note.cs b/Iceshrimp.Backend/Core/Database/Tables/Note.cs index f7f7b65e..fe8937e3 100644 --- a/Iceshrimp.Backend/Core/Database/Tables/Note.cs +++ b/Iceshrimp.Backend/Core/Database/Tables/Note.cs @@ -61,14 +61,14 @@ public class Note : IEntity [Column("renoteId")] [StringLength(32)] public string? RenoteId { get; set; } - + /// /// The URI of the reply target, if it couldn't be resolved at time of ingestion. /// [Column("replyUri")] [StringLength(512)] public string? ReplyUri { get; set; } - + /// /// The URI of the renote target, if it couldn't be resolved at time of ingestion. /// @@ -329,6 +329,14 @@ public class Note : IEntity return this; } + [Projectable] + [SuppressMessage("ReSharper", "MergeIntoPattern", Justification = "Projectables")] + [SuppressMessage("ReSharper", "MergeSequentialChecks", Justification = "Projectables")] + public Note WithPrecomputedVisibilities(User user) + => WithPrecomputedVisibilities(Reply != null && Reply.IsVisibleFor(user), + Renote != null && Renote.IsVisibleFor(user), + Renote != null && Renote.Renote != null && Renote.Renote.IsVisibleFor(user)); + public string GetPublicUri(Config.InstanceSection config) => UserHost == null ? $"https://{config.WebDomain}/notes/{Id}" : throw new Exception("Cannot access PublicUri for remote note"); diff --git a/Iceshrimp.Backend/Core/Extensions/QueryableExtensions.cs b/Iceshrimp.Backend/Core/Extensions/QueryableExtensions.cs index f6564ab6..ac125f2e 100644 --- a/Iceshrimp.Backend/Core/Extensions/QueryableExtensions.cs +++ b/Iceshrimp.Backend/Core/Extensions/QueryableExtensions.cs @@ -643,6 +643,22 @@ public static class QueryableExtensions .Include(p => p.Followee.UserProfile); } + public static IQueryable IncludeCommonProperties(this IQueryable query) + { + return query.Include(p => p.Note.User.UserProfile) + .Include(p => p.Note.Renote.User.UserProfile) + .Include(p => p.Note.Renote.Renote.User.UserProfile) + .Include(p => p.Note.Reply.User.UserProfile); + } + + public static IQueryable IncludeCommonProperties(this IQueryable query) + { + return query.Include(p => p.Note.User.UserProfile) + .Include(p => p.Note.Renote.User.UserProfile) + .Include(p => p.Note.Renote.Renote.User.UserProfile) + .Include(p => p.Note.Reply.User.UserProfile); + } + public static IQueryable IncludeCommonProperties(this IQueryable query) { return query.Include(p => p.Notifiee.UserProfile)