[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.*
|
Iceshrimp.Frontend/.pnp.*
|
||||||
|
|
||||||
*.DotSettings.user
|
*.DotSettings.user
|
||||||
|
|
||||||
|
TestResults/
|
||||||
|
|
|
@ -23,13 +23,12 @@ public class MastodonTimelineController(DatabaseContext db, NoteRenderer noteRen
|
||||||
[HttpGet("home")]
|
[HttpGet("home")]
|
||||||
[Produces("application/json")]
|
[Produces("application/json")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<Status>))]
|
[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 user = HttpContext.GetOauthUser() ?? throw new GracefulException("Failed to get user from HttpContext");
|
||||||
var res = await db.Notes
|
var res = await db.Notes
|
||||||
.WithIncludes()
|
.WithIncludes()
|
||||||
.FilterByFollowingAndOwn(user)
|
.FilterByFollowingAndOwn(user)
|
||||||
.OrderByIdDesc()
|
.Paginate(query, 20, 40)
|
||||||
.Take(40)
|
|
||||||
.RenderAllForMastodonAsync(noteRenderer);
|
.RenderAllForMastodonAsync(noteRenderer);
|
||||||
|
|
||||||
return Ok(res);
|
return Ok(res);
|
||||||
|
@ -39,13 +38,12 @@ public class MastodonTimelineController(DatabaseContext db, NoteRenderer noteRen
|
||||||
[HttpGet("public")]
|
[HttpGet("public")]
|
||||||
[Produces("application/json")]
|
[Produces("application/json")]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<Status>))]
|
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<Status>))]
|
||||||
public async Task<IActionResult> GetPublicTimeline() {
|
public async Task<IActionResult> GetPublicTimeline(PaginationQuery query) {
|
||||||
var res = await db.Notes
|
var res = await db.Notes
|
||||||
.WithIncludes()
|
.WithIncludes()
|
||||||
.HasVisibility(Note.NoteVisibility.Public)
|
.HasVisibility(Note.NoteVisibility.Public)
|
||||||
.OrderByIdDesc()
|
.Paginate(query, 20, 40)
|
||||||
.Take(40)
|
.RenderAllForMastodonAsync(noteRenderer);
|
||||||
.RenderAllForMastodonAsync(noteRenderer);
|
|
||||||
|
|
||||||
return Ok(res);
|
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.Renderers;
|
||||||
|
using Iceshrimp.Backend.Controllers.Mastodon.Schemas;
|
||||||
using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
|
using Iceshrimp.Backend.Controllers.Mastodon.Schemas.Entities;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
|
using Iceshrimp.Backend.Core.Middleware;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Core.Extensions;
|
namespace Iceshrimp.Backend.Core.Extensions;
|
||||||
|
@ -14,6 +16,30 @@ public static class NoteQueryableExtensions {
|
||||||
.ThenInclude(p => p != null ? p.User : null);
|
.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) {
|
public static IQueryable<Note> HasVisibility(this IQueryable<Note> query, Note.NoteVisibility visibility) {
|
||||||
return query.Where(note => note.Visibility == visibility);
|
return query.Where(note => note.Visibility == visibility);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
|
using EntityFrameworkCore.Projectables;
|
||||||
|
|
||||||
namespace Iceshrimp.Backend.Core.Extensions;
|
namespace Iceshrimp.Backend.Core.Extensions;
|
||||||
|
|
||||||
|
@ -15,3 +17,26 @@ public static class StringExtensions {
|
||||||
return new IdnMapping().GetUnicode(target);
|
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