diff --git a/frontend/components/NewsCard.tsx b/frontend/components/NewsCard.tsx
new file mode 100644
index 00000000..e0b1b392
--- /dev/null
+++ b/frontend/components/NewsCard.tsx
@@ -0,0 +1,37 @@
+// Copyright 2024 the JSR authors. All rights reserved. MIT license.
+export function NewsCard({
+ title,
+ description,
+ image,
+ url,
+}: {
+ title: string;
+ description: string;
+ image: string;
+ url: string;
+}) {
+ return (
+
+
+
+
+
+ {title}
+
+
+ {description}
+
+
+
+
+ );
+}
diff --git a/frontend/routes/index.tsx b/frontend/routes/index.tsx
index d2f95986..9786fa89 100644
--- a/frontend/routes/index.tsx
+++ b/frontend/routes/index.tsx
@@ -9,9 +9,18 @@ import { Head } from "$fresh/runtime.ts";
import { ComponentChildren } from "preact";
import { HomepageHero } from "../components/HomepageHero.tsx";
import { Logo } from "../components/Logo.tsx";
+import { NewsCard } from "../components/NewsCard.tsx";
+
+interface Post {
+ title: string;
+ description: string;
+ image: string;
+ link: string;
+}
interface Data {
stats: Stats;
+ posts: Post[];
}
export default function Home({ data }: PageProps
) {
@@ -32,17 +41,46 @@ export default function Home({ data }: PageProps) {
apiKey={Deno.env.get("ORAMA_PACKAGE_PUBLIC_API_KEY")}
indexId={Deno.env.get("ORAMA_PACKAGE_PUBLIC_INDEX_ID")}
/>
-
-
- {data.stats.featured.map(PackageToPanelEntry)}
-
-
- {data.stats.updated.map(PackageVersionToPanelEntry)}
-
-
- {data.stats.newest.map(PackageToPanelEntry)}
-
-
+ {data.posts.length > 0 && (
+
+ )}
+
+
+
+ Packages
+
+
+
+ {data.stats.featured.map(PackageToPanelEntry)}
+
+
+ {data.stats.updated.map(PackageVersionToPanelEntry)}
+
+
+ {data.stats.newest.map(PackageToPanelEntry)}
+
+
+
= {
const statsResp = await ctx.state.api.get(path`/stats`, undefined, {
anonymous: true,
});
+
+ let posts: Post[] = [];
+ try {
+ const jsrPosts = await fetch("https://deno.com/blog/json?tag=JSR");
+ if (jsrPosts.ok) {
+ posts = await jsrPosts.json() as Post[];
+ console.log(posts);
+ }
+ } catch (e) {
+ // ignore
+ }
+
if (!statsResp.ok) throw statsResp; // gracefully handle this
- return ctx.render({ stats: statsResp.data }, {
+ return ctx.render({ stats: statsResp.data, posts: posts || [] }, {
headers: ctx.state.api.hasToken()
? undefined
: { "Cache-Control": "public, s-maxage=60" },