66 lines
2.7 KiB
C#
66 lines
2.7 KiB
C#
using Elternbeirat.Contracts;
|
|
|
|
namespace Elternbeirat.Web.Features.Posts;
|
|
|
|
/// <summary>
|
|
/// The posts next to a given post in time: the one published before it and the
|
|
/// one published after it.
|
|
/// </summary>
|
|
/// <param name="Previous">
|
|
/// The older post, or <see langword="null"/> if the post is the oldest.
|
|
/// </param>
|
|
/// <param name="Next">
|
|
/// The newer post, or <see langword="null"/> if the post is the newest.
|
|
/// </param>
|
|
/// <remarks>
|
|
/// "Previous" and "next" follow time, as a reader moving through the news would:
|
|
/// "Vorheriger Beitrag" is the older one, "Nächster Beitrag" the newer one. The
|
|
/// list order of the posts page (newest first) is the other way round, which is
|
|
/// why the direction is pinned down here instead of being left to the markup.
|
|
/// </remarks>
|
|
/// <seealso cref="PostDetail"/>
|
|
public sealed record PostNeighbors(Post? Previous, Post? Next)
|
|
{
|
|
/// <summary>
|
|
/// Finds the neighbours of the post with the given slug.
|
|
/// </summary>
|
|
/// <param name="posts">
|
|
/// All public posts, newest first, as <c>PocketBaseClient.GetPostsAsync</c>
|
|
/// returns them.
|
|
/// </param>
|
|
/// <param name="slug">The slug of the post being shown.</param>
|
|
/// <returns>
|
|
/// The older and the newer post. Both are <see langword="null"/> if the slug is
|
|
/// not in the list, or if it is the only post.
|
|
/// </returns>
|
|
/// <remarks>
|
|
/// The slug is compared case-insensitively, like the route lookup in
|
|
/// <see cref="PostDetail"/>, so a page reached as <c>/posts/New-Board</c> still
|
|
/// gets its neighbours.
|
|
/// </remarks>
|
|
/// <example>
|
|
/// <code>
|
|
/// // posts = [March, February, January] (newest first)
|
|
/// var neighbors = PostNeighbors.Of(posts, "february");
|
|
/// // neighbors.Previous is January, neighbors.Next is March
|
|
/// </code>
|
|
/// </example>
|
|
public static PostNeighbors Of(IReadOnlyList<Post> posts, string slug) =>
|
|
IndexOf(posts, slug) is var index and >= 0
|
|
? new PostNeighbors(
|
|
Previous: index + 1 < posts.Count ? posts[index + 1] : null,
|
|
Next: index > 0 ? posts[index - 1] : null)
|
|
: new PostNeighbors(null, null);
|
|
|
|
/// <summary>
|
|
/// The position of the post with <paramref name="slug"/>, or <c>-1</c>.
|
|
/// </summary>
|
|
private static int IndexOf(IReadOnlyList<Post> posts, string slug) =>
|
|
posts
|
|
.Select((post, index) => (post, index))
|
|
.FirstOrDefault(
|
|
entry => string.Equals(entry.post.Slug, slug, StringComparison.OrdinalIgnoreCase),
|
|
(null!, -1))
|
|
.index;
|
|
}
|