Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions apps/strapi/src/api/blog/content-types/blog/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
"type": "component",
"component": "forms.newsletter",
"repeatable": false
},
"featuredBlogPost": {
"type": "relation",
"relation": "oneToOne",
"target": "api::blog-post.blog-post"
}
}
}
4 changes: 4 additions & 0 deletions apps/strapi/types/generated/contentTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -650,6 +650,10 @@ export interface ApiBlogBlog extends Struct.SingleTypeSchema {
createdAt: Schema.Attribute.DateTime
createdBy: Schema.Attribute.Relation<"oneToOne", "admin::user"> &
Schema.Attribute.Private
featuredBlogPost: Schema.Attribute.Relation<
"oneToOne",
"api::blog-post.blog-post"
>
locale: Schema.Attribute.String
localizations: Schema.Attribute.Relation<"oneToMany", "api::blog.blog">
navigation: Schema.Attribute.Component<"blog.navigation", false>
Expand Down
9 changes: 6 additions & 3 deletions apps/ui/src/app/[locale]/blog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import { NewsletterSignup } from "@/components/newsletter/NewsletterSignup"
import { StrapiSeoStructuredDataByFullPath } from "@/components/page-builder/components/seo-utilities/StrapiSeoStructuredData"
import {
BLOG_INDEX_EXCLUDED_CATEGORY_SLUGS,
getBlogIndexPosts,
getBlogNewsletterHubspot,
type BlogPost,
} from "@/lib/blog-utils"
import { getBlogIndexMetadata } from "@/lib/metadata"
import { fetchBlog, fetchBlogPostsPage } from "@/lib/strapi-api/content/server"
Expand Down Expand Up @@ -49,8 +49,10 @@ export default function BlogIndexPage(props: PageProps<"/[locale]/blog">) {
)

const hubspotForm = getBlogNewsletterHubspot(blog)
const featuredPost: BlogPost | null = allPosts.posts[0] ?? null
const remainingPosts: BlogPost[] = allPosts.posts.slice(1)
const { featuredPost, remainingPosts, excludeSlugs } = getBlogIndexPosts(
blog?.data,
allPosts.posts
)

return (
<>
Expand All @@ -68,6 +70,7 @@ export default function BlogIndexPage(props: PageProps<"/[locale]/blog">) {
total={allPosts.total}
loadMoreLabel={t("loadMore")}
excludeCategorySlugs={BLOG_INDEX_EXCLUDED_CATEGORY_SLUGS}
excludeSlugs={excludeSlugs}
/>
</HeroContainerContent>

Expand Down
5 changes: 4 additions & 1 deletion apps/ui/src/components/blog/BlogPostsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface BlogPostsListProps {
readonly tagSlug?: string
readonly authorSlug?: string
readonly excludeCategorySlugs?: readonly string[]
readonly excludeSlugs?: readonly string[]
}

export function BlogPostsList({
Expand All @@ -37,6 +38,7 @@ export function BlogPostsList({
tagSlug,
authorSlug,
excludeCategorySlugs,
excludeSlugs,
}: BlogPostsListProps) {
const [posts, setPosts] = useState<readonly BlogPost[]>(initialPosts)
const [offset, setOffset] = useState(initialOffset)
Expand All @@ -53,10 +55,11 @@ export function BlogPostsList({
tagSlug,
authorSlug,
excludeCategorySlugs,
excludeSlugs,
})

setPosts((prev) => [...prev, ...res.posts])
setOffset((prev) => prev + res.posts.length)
setOffset((prev) => prev + res.fetchedCount)
setHasMore(res.hasMore)
})
}
Expand Down
12 changes: 11 additions & 1 deletion apps/ui/src/components/blog/blog-posts-load-more.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ export interface LoadMoreBlogPostsArgs {
readonly tagSlug?: string
readonly authorSlug?: string
readonly excludeCategorySlugs?: readonly string[]
readonly excludeSlugs?: readonly string[]
}

export interface LoadMoreBlogPostsResult {
readonly posts: BlogPost[]
readonly fetchedCount: number
readonly hasMore: boolean
}

Expand All @@ -28,6 +30,7 @@ export async function loadMoreBlogPosts({
tagSlug,
authorSlug,
excludeCategorySlugs,
excludeSlugs,
}: LoadMoreBlogPostsArgs): Promise<LoadMoreBlogPostsResult> {
const { posts, total } = await fetchBlogPostsPage(locale, {
offset,
Expand All @@ -38,8 +41,15 @@ export async function loadMoreBlogPosts({
excludeCategorySlugs,
})

const excluded = new Set(excludeSlugs)
const filteredPosts =
excluded.size === 0
? posts
: posts.filter((post) => !excluded.has(post.slug as string))

return {
posts,
posts: filteredPosts,
fetchedCount: posts.length,
hasMore: offset + posts.length < total,
}
}
27 changes: 27 additions & 0 deletions apps/ui/src/lib/blog-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,33 @@ export function getBlogNewsletterHubspot(
return null
}

export function getBlogIndexPosts(
blog: BlogData | null | undefined,
posts: readonly BlogPost[]
): {
featuredPost: BlogPost | null
remainingPosts: BlogPost[]
excludeSlugs: string[] | undefined
} {
const hasFeaturedPost = !!blog?.featuredBlogPost

const featuredPost = hasFeaturedPost
? (blog?.featuredBlogPost as BlogPost)
: (posts[0] ?? null)

const remainingPosts = featuredPost
? posts.filter((post) => post.documentId !== featuredPost.documentId)
: [...posts]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I'm not sure you need to spread the posts into an array, if they are already an array.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its just a typescript thing - posts are a readonly type (returned from the api) but remainingPosts is returned as a mutable array

it would probably be ok if i suppressed this type error but i think its safer to leave it this way

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's the right call then, thank you 🙏


return {
featuredPost,
remainingPosts,
excludeSlugs: hasFeaturedPost
? [blog?.featuredBlogPost?.slug as string]
: undefined,
}
}

export function combineAuthors(
author: AuthorAvatarData | null | undefined,
coauthors?: readonly AuthorAvatarData[]
Expand Down
3 changes: 3 additions & 0 deletions apps/ui/src/lib/strapi-api/content/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -750,6 +750,9 @@ export async function fetchBlog(locale: Locale) {
newsletter: {
populate: { hubspotForm: true },
},
featuredBlogPost: {
populate: blogListPopulate,
},
},
} satisfies FindFirst<"api::blog.blog">

Expand Down
Loading