[backend/masto-client] Add pagination
This commit is contained in:
parent
af7a776337
commit
8e2a116ad9
5 changed files with 71 additions and 9 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -98,3 +98,5 @@ Iceshrimp.Frontend/.yarn
|
|||
Iceshrimp.Frontend/.pnp.*
|
||||
|
||||
*.DotSettings.user
|
||||
|
||||
TestResults/
|
||||
|
|
|
@ -23,13 +23,12 @@ public class MastodonTimelineController(DatabaseContext db, NoteRenderer noteRen
|
|||
[HttpGet("home")]
|
||||
[Produces("application/json")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<Status>))]
|
||||
public async Task<IActionResult> GetHomeTimeline() {
|
||||
public async Task<IActionResult> GetHomeTimeline(PaginationQuery query) {
|
||||
var user = HttpContext.GetOauthUser() ?? throw new GracefulException("Failed to get user from HttpContext");
|
||||
var res = await db.Notes
|
||||
.WithIncludes()
|
||||
.FilterByFollowingAndOwn(user)
|
||||
.OrderByIdDesc()
|
||||
.Take(40)
|
||||
.Paginate(query, 20, 40)
|
||||
.RenderAllForMastodonAsync(noteRenderer);
|
||||
|
||||
return Ok(res);
|
||||
|
@ -39,13 +38,12 @@ public class MastodonTimelineController(DatabaseContext db, NoteRenderer noteRen
|
|||
[HttpGet("public")]
|
||||
[Produces("application/json")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<Status>))]
|
||||
public async Task<IActionResult> GetPublicTimeline() {
|
||||
public async Task<IActionResult> GetPublicTimeline(PaginationQuery query) {
|
||||
var res = await db.Notes
|
||||
.WithIncludes()
|
||||
.HasVisibility(Note.NoteVisibility.Public)
|
||||
.OrderByIdDesc()
|
||||
.Take(40)
|
||||
.RenderAllForMastodonAsync(noteRenderer);
|
||||
.WithIncludes()
|
||||
.HasVisibility(Note.NoteVisibility.Public)
|
||||
.Paginate(query, 20, 40)
|
||||
.RenderAllForMastodonAsync(noteRenderer);
|
||||
|
||||
return Ok(res);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using B = Microsoft.AspNetCore.Mvc.BindPropertyAttribute;
|
||||
|
||||
namespace Iceshrimp.Backend.Controllers.Mastodon.Schemas;
|
||||
|
||||
public class PaginationQuery {
|
||||
[FromQuery] [B(Name = "max_id")] public string? MaxId { get; set; }
|
||||
[FromQuery] [B(Name = "since_id")] public string? SinceId { get; set; }
|
||||
[FromQuery] [B(Name = "min_id")] public string? MinId { get; set; }
|
||||
[FromQuery] [B(Name = "limit")] public int? Limit { get; set; }
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
using Iceshrimp.Backend.Controllers.Mastodon.Renderers;
|
||||
using Iceshrimp.Backend.Controllers.Mastodon.Schemas;
|
||||
using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
|
||||
using Iceshrimp.Backend.Core.Database.Tables;
|
||||
using Iceshrimp.Backend.Core.Middleware;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Iceshrimp.Backend.Core.Extensions;
|
||||
|
@ -14,6 +16,30 @@ public static class NoteQueryableExtensions {
|
|||
.ThenInclude(p => p != null ? p.User : null);
|
||||
}
|
||||
|
||||
public static IQueryable<Note> Paginate(this IQueryable<Note> query, PaginationQuery p, int defaultLimit,
|
||||
int maxLimit) {
|
||||
if (p is { SinceId: not null, MinId: not null })
|
||||
throw GracefulException.BadRequest("Can't use sinceId and minId params simultaneously");
|
||||
|
||||
query = p switch {
|
||||
{ SinceId: not null, MaxId: not null } => query
|
||||
.Where(note => note.Id.IsGreaterThan(p.SinceId) &&
|
||||
note.Id.IsLessThan(p.MaxId))
|
||||
.OrderByDescending(note => note.Id),
|
||||
{ MinId: not null, MaxId: not null } => query
|
||||
.Where(note => note.Id.IsGreaterThan(p.MinId) &&
|
||||
note.Id.IsLessThan(p.MaxId))
|
||||
.OrderBy(note => note.Id),
|
||||
{ SinceId: not null } => query.Where(note => note.Id.IsGreaterThan(p.SinceId))
|
||||
.OrderByDescending(note => note.Id),
|
||||
{ MinId: not null } => query.Where(note => note.Id.IsGreaterThan(p.MinId)).OrderBy(note => note.Id),
|
||||
{ MaxId: not null } => query.Where(note => note.Id.IsLessThan(p.MaxId)).OrderByDescending(note => note.Id),
|
||||
_ => query
|
||||
};
|
||||
|
||||
return query.Take(Math.Min(p.Limit ?? defaultLimit, maxLimit));
|
||||
}
|
||||
|
||||
public static IQueryable<Note> HasVisibility(this IQueryable<Note> query, Note.NoteVisibility visibility) {
|
||||
return query.Where(note => note.Visibility == visibility);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using EntityFrameworkCore.Projectables;
|
||||
|
||||
namespace Iceshrimp.Backend.Core.Extensions;
|
||||
|
||||
|
@ -15,3 +17,26 @@ public static class StringExtensions {
|
|||
return new IdnMapping().GetUnicode(target);
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "StringCompareToIsCultureSpecific")]
|
||||
public static class ProjectableStringExtensions {
|
||||
[Projectable]
|
||||
public static bool IsLessThan(this string a, string b) {
|
||||
return a.CompareTo(b) < 0;
|
||||
}
|
||||
|
||||
[Projectable]
|
||||
public static bool IsLessOrEqualTo(this string a, string b) {
|
||||
return a.CompareTo(b) <= 0;
|
||||
}
|
||||
|
||||
[Projectable]
|
||||
public static bool IsGreaterThan(this string a, string b) {
|
||||
return a.CompareTo(b) > 0;
|
||||
}
|
||||
|
||||
[Projectable]
|
||||
public static bool IsGreaterOrEqualTo(this string a, string b) {
|
||||
return a.CompareTo(b) >= 0;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue