feat(blog): show random fallback articles in post footer navigation instead of blank spaces
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 14s
Build & Deploy / 🧪 QA (push) Successful in 6m40s
Build & Deploy / 🏗️ Build (push) Successful in 8m28s
Build & Deploy / 🚀 Deploy (push) Successful in 29s
Build & Deploy / 🧪 Smoke Test (push) Successful in 55s
Build & Deploy / ⚡ Lighthouse (push) Successful in 7m8s
Build & Deploy / 🔔 Notify (push) Successful in 2s

This commit is contained in:
2026-02-21 18:53:10 +01:00
parent 21288a4a45
commit 678c803408
5 changed files with 95 additions and 45 deletions

View File

@@ -109,7 +109,7 @@ export async function getAllPostsMetadata(locale: string): Promise<Partial<PostM
export async function getAdjacentPosts(
slug: string,
locale: string,
): Promise<{ prev: PostMdx | null; next: PostMdx | null }> {
): Promise<{ prev: PostMdx | null; next: PostMdx | null; isPrevRandom?: boolean; isNextRandom?: boolean }> {
const posts = await getAllPosts(locale);
const currentIndex = posts.findIndex((post) => post.slug === slug);
@@ -120,10 +120,31 @@ export async function getAdjacentPosts(
// Posts are sorted by date descending (newest first)
// So "next" post (newer) is at index - 1
// And "previous" post (older) is at index + 1
const next = currentIndex > 0 ? posts[currentIndex - 1] : null;
const prev = currentIndex < posts.length - 1 ? posts[currentIndex + 1] : null;
let next = currentIndex > 0 ? posts[currentIndex - 1] : null;
let prev = currentIndex < posts.length - 1 ? posts[currentIndex + 1] : null;
return { prev, next };
let isNextRandom = false;
let isPrevRandom = false;
const getRandomPost = (excludeSlugs: string[]) => {
const available = posts.filter(p => !excludeSlugs.includes(p.slug));
if (available.length === 0) return null;
return available[Math.floor(Math.random() * available.length)];
};
// If there's no next post (we are at the newest post), show a random post instead
if (!next && posts.length > 2) {
next = getRandomPost([slug, prev?.slug].filter(Boolean) as string[]);
isNextRandom = true;
}
// If there's no previous post (we are at the oldest post), show a random post instead
if (!prev && posts.length > 2) {
prev = getRandomPost([slug, next?.slug].filter(Boolean) as string[]);
isPrevRandom = true;
}
return { prev, next, isPrevRandom, isNextRandom };
}
export function getReadingTime(content: string): number {

View File

@@ -23,11 +23,28 @@ export default function imgproxyLoader({
return src;
}
// Check if src contains custom gravity query parameter
let gravity = 'sm'; // Use smart gravity (content-aware) by default
let cleanSrc = src;
try {
// Dummy base needed for relative URLs
const url = new URL(src, 'http://localhost');
const customGravity = url.searchParams.get('gravity');
if (customGravity) {
gravity = customGravity;
url.searchParams.delete('gravity');
cleanSrc = src.startsWith('http') ? url.href : url.pathname + url.search;
}
} catch (e) {
// Fallback if parsing fails
}
// We use the width provided by Next.js for responsive images
// Height is set to 0 to maintain aspect ratio
return getImgproxyUrl(src, {
return getImgproxyUrl(cleanSrc, {
width,
resizing_type: 'fit',
gravity: 'sm', // Use smart gravity (content-aware) instead of face detection (requires ML/Pro)
gravity,
});
}