using Elternbeirat.Contracts; namespace Elternbeirat.Web.Features.Posts; /// /// The posts next to a given post in time: the one published before it and the /// one published after it. /// /// /// The older post, or if the post is the oldest. /// /// /// The newer post, or if the post is the newest. /// /// /// "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. /// /// public sealed record PostNeighbors(Post? Previous, Post? Next) { /// /// Finds the neighbours of the post with the given slug. /// /// /// All public posts, newest first, as PocketBaseClient.GetPostsAsync /// returns them. /// /// The slug of the post being shown. /// /// The older and the newer post. Both are if the slug is /// not in the list, or if it is the only post. /// /// /// The slug is compared case-insensitively, like the route lookup in /// , so a page reached as /posts/New-Board still /// gets its neighbours. /// /// /// /// // posts = [March, February, January] (newest first) /// var neighbors = PostNeighbors.Of(posts, "february"); /// // neighbors.Previous is January, neighbors.Next is March /// /// public static PostNeighbors Of(IReadOnlyList 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); /// /// The position of the post with , or -1. /// private static int IndexOf(IReadOnlyList posts, string slug) => posts .Select((post, index) => (post, index)) .FirstOrDefault( entry => string.Equals(entry.post.Slug, slug, StringComparison.OrdinalIgnoreCase), (null!, -1)) .index; }