diff --git a/blog/loaders/extensions/BlogpostList/ratings.ts b/blog/loaders/extensions/BlogpostList/ratings.ts index 72a9a13ca..bfb5a3731 100644 --- a/blog/loaders/extensions/BlogpostList/ratings.ts +++ b/blog/loaders/extensions/BlogpostList/ratings.ts @@ -1,14 +1,25 @@ import { ExtensionOf } from "../../../../website/loaders/extension.ts"; import { AppContext } from "../../../mod.ts"; -import { BlogPost } from "../../../types.ts"; +import { BlogPost, Ignore } from "../../../types.ts"; import { getRatings } from "../../../utils/records.ts"; +interface Props { + /** + * @description Ignore ratings in the aggregateRating calc + */ + ignoreRatings?: Ignore; + /** + * @description Return only aggregate rating object + */ + onlyAggregate?: boolean; +} + /** * @title ExtensionOf BlogPost list: Ratings * @description It can harm performance. Use wisely */ export default function ratingsExt( - _props: unknown, + { ignoreRatings, onlyAggregate }: Props, _req: Request, ctx: AppContext, ): ExtensionOf { @@ -19,7 +30,12 @@ export default function ratingsExt( const postsWithRatings = await Promise.all( posts.map(async (post) => { - const ratings = await getRatings({ post, ctx }); + const ratings = await getRatings({ + post, + ctx, + ignoreRatings, + onlyAggregate, + }); return { ...post, ...ratings }; }), ); diff --git a/blog/loaders/extensions/BlogpostList/reviews.ts b/blog/loaders/extensions/BlogpostList/reviews.ts index bd3fc46ff..83df367fe 100644 --- a/blog/loaders/extensions/BlogpostList/reviews.ts +++ b/blog/loaders/extensions/BlogpostList/reviews.ts @@ -1,14 +1,25 @@ import { ExtensionOf } from "../../../../website/loaders/extension.ts"; import { AppContext } from "../../../mod.ts"; -import { BlogPost } from "../../../types.ts"; +import { BlogPost, Ignore } from "../../../types.ts"; import { getReviews } from "../../../utils/records.ts"; +interface Props { + /** + * @description Ignore specific reviews + */ + ignoreReviews?: Ignore; + /** + * @description Order By + */ + orderBy?: "date_asc" | "date_desc"; +} + /** * @title ExtensionOf BlogPost list: Reviews * @description It can harm performance. Use wisely */ export default function reviewsExt( - _props: unknown, + { ignoreReviews, orderBy }: Props, _req: Request, ctx: AppContext, ): ExtensionOf { @@ -19,7 +30,7 @@ export default function reviewsExt( const postsWithReviews = await Promise.all( posts.map(async (post) => { - const reviews = await getReviews({ post, ctx }); + const reviews = await getReviews({ post, ctx, ignoreReviews, orderBy }); return { ...post, ...reviews }; }), ); diff --git a/blog/loaders/extensions/BlogpostListing/ratings.ts b/blog/loaders/extensions/BlogpostListing/ratings.ts index abe8ddd6d..2d33e7e21 100644 --- a/blog/loaders/extensions/BlogpostListing/ratings.ts +++ b/blog/loaders/extensions/BlogpostListing/ratings.ts @@ -1,14 +1,25 @@ import { ExtensionOf } from "../../../../website/loaders/extension.ts"; import { AppContext } from "../../../mod.ts"; -import { BlogPostListingPage } from "../../../types.ts"; +import { BlogPostListingPage, Ignore } from "../../../types.ts"; import { getRatings } from "../../../utils/records.ts"; +interface Props { + /** + * @description Ignore ratings in the aggregateRating calc + */ + ignoreRatings?: Ignore; + /** + * @description Return only aggregate rating object + */ + onlyAggregate?: boolean; +} + /** - * @title ExtensionOf BlogPostPage: Ratings + * @title ExtensionOf BlogPostListing: Ratings * @description It can harm performance. Use wisely */ export default function ratingsExt( - _props: unknown, + { ignoreRatings, onlyAggregate }: Props, _req: Request, ctx: AppContext, ): ExtensionOf { @@ -19,7 +30,12 @@ export default function ratingsExt( const posts = await Promise.all( blogpostListingPage.posts.map(async (post) => { - const ratings = await getRatings({ post, ctx }); + const ratings = await getRatings({ + post, + ctx, + onlyAggregate, + ignoreRatings, + }); return { ...post, ...ratings }; }), ); diff --git a/blog/loaders/extensions/BlogpostListing/reviews.ts b/blog/loaders/extensions/BlogpostListing/reviews.ts index ee5919f5d..f91a84b4b 100644 --- a/blog/loaders/extensions/BlogpostListing/reviews.ts +++ b/blog/loaders/extensions/BlogpostListing/reviews.ts @@ -1,14 +1,25 @@ import { ExtensionOf } from "../../../../website/loaders/extension.ts"; import { AppContext } from "../../../mod.ts"; -import { BlogPostListingPage } from "../../../types.ts"; +import { BlogPostListingPage, Ignore } from "../../../types.ts"; import { getReviews } from "../../../utils/records.ts"; +interface Props { + /** + * @description Ignore specific reviews + */ + ignoreReviews?: Ignore; + /** + * @description Order By + */ + orderBy?: "date_asc" | "date_desc"; +} + /** - * @title ExtensionOf BlogPostPage: Reviews + * @title ExtensionOf BlogPostListing: Reviews * @description It can harm performance. Use wisely */ export default function reviewsExt( - _props: unknown, + { ignoreReviews, orderBy }: Props, _req: Request, ctx: AppContext, ): ExtensionOf { @@ -19,7 +30,7 @@ export default function reviewsExt( const posts = await Promise.all( blogpostListingPage.posts.map(async (post) => { - const reviews = await getReviews({ post, ctx }); + const reviews = await getReviews({ post, ctx, ignoreReviews, orderBy }); return { ...post, ...reviews }; }), ); diff --git a/blog/loaders/extensions/BlogpostPage/ratings.ts b/blog/loaders/extensions/BlogpostPage/ratings.ts index bbd6b385b..bf4f66083 100644 --- a/blog/loaders/extensions/BlogpostPage/ratings.ts +++ b/blog/loaders/extensions/BlogpostPage/ratings.ts @@ -1,14 +1,21 @@ import { ExtensionOf } from "../../../../website/loaders/extension.ts"; import { AppContext } from "../../../mod.ts"; -import { BlogPostPage } from "../../../types.ts"; +import { BlogPostPage, Ignore } from "../../../types.ts"; import { getRatings } from "../../../utils/records.ts"; +interface Props { + /** + * @description Ignore ratings in the aggregateRating calc + */ + ignoreRatings?: Ignore; +} + /** * @title ExtensionOf BlogPostPage: Ratings * @description It can harm performance. Use wisely */ export default function ratingsExt( - _props: unknown, + { ignoreRatings }: Props, _req: Request, ctx: AppContext, ): ExtensionOf { @@ -16,7 +23,11 @@ export default function ratingsExt( if (!blogpostPage) { return null; } - const post = await getRatings({ post: blogpostPage.post, ctx }); + const post = await getRatings({ + post: blogpostPage.post, + ctx, + ignoreRatings, + }); return { ...blogpostPage, post }; }; } diff --git a/blog/loaders/extensions/BlogpostPage/reviews.ts b/blog/loaders/extensions/BlogpostPage/reviews.ts index ca5481142..6b8f08c20 100644 --- a/blog/loaders/extensions/BlogpostPage/reviews.ts +++ b/blog/loaders/extensions/BlogpostPage/reviews.ts @@ -1,14 +1,25 @@ import { ExtensionOf } from "../../../../website/loaders/extension.ts"; import { AppContext } from "../../../mod.ts"; -import { BlogPostPage } from "../../../types.ts"; +import { BlogPostPage, Ignore } from "../../../types.ts"; import { getReviews } from "../../../utils/records.ts"; +interface Props { + /** + * @description Ignore specific reviews + */ + ignoreReviews?: Ignore; + /** + * @description Order By + */ + orderBy?: "date_asc" | "date_desc"; +} + /** * @title ExtensionOf BlogPostPage: Reviews * @description It can harm performance. Use wisely */ export default function reviewsExt( - _props: unknown, + { ignoreReviews, orderBy }: Props, _req: Request, ctx: AppContext, ): ExtensionOf { @@ -16,7 +27,12 @@ export default function reviewsExt( if (!blogpostPage) { return null; } - const post = await getReviews({ post: blogpostPage.post, ctx }); + const post = await getReviews({ + post: blogpostPage.post, + ctx, + ignoreReviews, + orderBy, + }); return { ...blogpostPage, post }; }; } diff --git a/blog/sections/Template.tsx b/blog/sections/Template.tsx index 8dcb278fe..4ee094d3b 100644 --- a/blog/sections/Template.tsx +++ b/blog/sections/Template.tsx @@ -14,7 +14,7 @@ export default function Template({ post }: Props) { excerpt = "Excerpt", date, image, - alt + alt, } = post; return ( @@ -33,7 +33,11 @@ export default function Template({ post }: Props) { : ""}

{image && ( - {alt + {alt )}
diff --git a/blog/types.ts b/blog/types.ts index 491c0d036..97730a1e1 100644 --- a/blog/types.ts +++ b/blog/types.ts @@ -148,3 +148,14 @@ export interface AggregateRating { /** The lowest value allowed in this rating system. */ worstRating?: number; } + +export interface Ignore { + /** + * @title Active + */ + active?: boolean; + /** + * @title When additionalType is marked with: + */ + markedAs?: string[]; +} diff --git a/blog/utils/records.ts b/blog/utils/records.ts index 4a3b1340c..d22bbab79 100644 --- a/blog/utils/records.ts +++ b/blog/utils/records.ts @@ -1,8 +1,14 @@ import { rating, review } from "../db/schema.ts"; import { AppContext } from "../mod.ts"; import { type Resolvable } from "@deco/deco"; -import { eq } from "https://esm.sh/drizzle-orm@0.30.10"; -import { BlogPost, Rating, Review } from "../types.ts"; +import { + and, + asc, + desc, + eq, + notInArray, +} from "https://esm.sh/drizzle-orm@0.30.10"; +import { BlogPost, Ignore, Rating, Review } from "../types.ts"; import { logger } from "@deco/deco/o11y"; export async function getRecordsByPath( @@ -49,28 +55,44 @@ export async function getRatingsBySlug( } export async function getRatings( - { ctx, post }: { ctx: AppContext; post: BlogPost }, + { ctx, post, ignoreRatings, onlyAggregate }: { + ctx: AppContext; + post: BlogPost; + ignoreRatings?: Ignore; + onlyAggregate?: boolean; + }, ): Promise { const contentRating = await getRatingsBySlug({ ctx, slug: post.slug, }); - const ratingValue = contentRating.length === 0 ? 0 : contentRating.reduce( - (acc, rating) => acc = acc + rating!.ratingValue!, - 0, - ) / contentRating.length; + const { ratingCount, ratingTotal } = contentRating.length === 0 + ? { ratingCount: 0, ratingTotal: 0 } + : contentRating.reduce( + (acc, { ratingValue, additionalType }) => + ignoreRatings?.active && additionalType && + ignoreRatings.markedAs?.includes(additionalType) + ? acc + : { + ratingCount: acc.ratingCount + 1, + ratingTotal: acc.ratingTotal + (ratingValue ?? 0), + }, + { ratingCount: 0, ratingTotal: 0 }, + ); + + const ratingValue = ratingTotal / ratingCount; return { ...post, - contentRating, + contentRating: onlyAggregate ? undefined : contentRating, aggregateRating: { ...post.aggregateRating, "@type": "AggregateRating", - ratingCount: contentRating.length, + ratingCount, + ratingValue: isNaN(ratingValue) ? 0 : ratingValue, bestRating: 5, worstRating: 1, - ratingValue, }, }; } @@ -103,9 +125,26 @@ export const getReviewById = async ( }; export async function getReviewsBySlug( - { ctx, slug }: { ctx: AppContext; slug: string }, + { ctx, slug, ignoreReviews, orderBy = "date_desc" }: { + ctx: AppContext; + slug: string; + ignoreReviews?: Ignore; + orderBy?: "date_asc" | "date_desc"; + }, ): Promise { const records = await ctx.invoke.records.loaders.drizzle(); + + const whereClause = ignoreReviews?.active && ignoreReviews?.markedAs && + ignoreReviews?.markedAs?.length > 0 + ? and( + eq(review.itemReviewed, slug), + notInArray(review.additionalType, ignoreReviews.markedAs), + ) + : eq(review.itemReviewed, slug); + const orderClause = orderBy.endsWith("desc") + ? desc(review.datePublished) + : asc(review.datePublished); + try { const currentComments = await records.select({ itemReviewed: review.itemReviewed, @@ -118,7 +157,7 @@ export async function getReviewsBySlug( additionalType: review.additionalType, id: review.id, }) - .from(review).where(eq(review.itemReviewed, slug)) as + .from(review).where(whereClause).orderBy(orderClause) as | Review[] | undefined; @@ -130,11 +169,17 @@ export async function getReviewsBySlug( } export async function getReviews( - { ctx, post }: { ctx: AppContext; post: BlogPost }, + { ctx, post, ...rest }: { + ctx: AppContext; + post: BlogPost; + ignoreReviews?: Ignore; + orderBy?: "date_asc" | "date_desc"; + }, ): Promise { const review = await getReviewsBySlug({ ctx, slug: post.slug, + ...rest, }); return {