Skip to content

Commit 0296dbb

Browse files
committed
Show latest blog announcement on home page
1 parent a69e805 commit 0296dbb

4 files changed

Lines changed: 111 additions & 11 deletions

File tree

docusaurus.config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type * as Preset from '@docusaurus/preset-classic';
22
import type { Config } from '@docusaurus/types';
33

44
import disableFullySpecified from './src/plugins/disable-fully-specified.ts';
5+
import latestAnnouncement from './src/plugins/latest-announcement.ts';
56
import llmsTxt from './src/plugins/llms-txt.ts';
67
import ogImage from './src/plugins/og-image.ts';
78
import reactNavigationVersions from './src/plugins/react-navigation-versions.ts';
@@ -137,6 +138,7 @@ const config: Config = {
137138
disableFullySpecified,
138139
reactNavigationVersions,
139140
[llmsTxt, { latestVersion }],
141+
latestAnnouncement,
140142
ogImage,
141143
[
142144
'@docusaurus/plugin-client-redirects',

src/pages/home/Splash/index.tsx

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
11
import Link from '@docusaurus/Link';
22
import useBaseUrl from '@docusaurus/useBaseUrl';
3+
import { usePluginData } from '@docusaurus/useGlobalData';
34

5+
import type { LatestAnnouncement } from '../../../plugins/latest-announcement.ts';
46
import SplashLeftIllustration from './SplashLeftIllustration';
57
import SplashRightIllustration from './SplashRightIllustration';
68
import styles from './styles.module.css';
79

10+
function isLatestAnnouncement(value: unknown): value is LatestAnnouncement {
11+
return (
12+
typeof value === 'object' &&
13+
value !== null &&
14+
'title' in value &&
15+
typeof value.title === 'string' &&
16+
'permalink' in value &&
17+
typeof value.permalink === 'string'
18+
);
19+
}
20+
821
export default function Splash() {
22+
const data = usePluginData('latest-announcement');
23+
const announcement = isLatestAnnouncement(data) ? data : null;
24+
25+
console.log('Announcement:', data);
26+
927
return (
1028
<section className={styles.wrapper}>
1129
<div className={styles.container}>
@@ -36,16 +54,17 @@ export default function Splash() {
3654
</div>
3755
<SplashRightIllustration />
3856
</div>
39-
<div className={styles.migrationText}>
40-
✨ React Navigation 8 is coming. Check out the{' '}
41-
<Link
42-
to={useBaseUrl('/blog/2025/12/19/react-navigation-8.0-alpha')}
43-
className={styles.linkText}
44-
>
45-
announcement
46-
</Link>
47-
.
48-
</div>
57+
{announcement && (
58+
<div className={styles.migrationText}>
59+
{' '}
60+
<Link
61+
to={useBaseUrl(announcement.permalink)}
62+
className={styles.linkText}
63+
>
64+
{announcement.title}
65+
</Link>
66+
</div>
67+
)}
4968
</section>
5069
);
5170
}

src/pages/home/Splash/styles.module.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@
113113

114114
.linkText {
115115
color: var(--ifm-home-color-text);
116-
font-weight: bold;
116+
font-weight: 500;
117117
}
118118

119119
.linkText:hover {

src/plugins/latest-announcement.ts

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import type { AllContent, Plugin } from '@docusaurus/types';
2+
3+
export type LatestAnnouncement = {
4+
title: string;
5+
permalink: string;
6+
date: string;
7+
};
8+
9+
type BlogPost = {
10+
metadata: {
11+
title: string;
12+
permalink: string;
13+
date: Date;
14+
tags: { label: string }[];
15+
};
16+
};
17+
18+
function isBlogPost(value: unknown): value is BlogPost {
19+
if (typeof value !== 'object' || value === null || !('metadata' in value)) {
20+
return false;
21+
}
22+
23+
const metadata = value.metadata;
24+
25+
return (
26+
typeof metadata === 'object' &&
27+
metadata !== null &&
28+
'title' in metadata &&
29+
typeof metadata.title === 'string' &&
30+
'permalink' in metadata &&
31+
typeof metadata.permalink === 'string' &&
32+
'date' in metadata &&
33+
metadata.date instanceof Date &&
34+
'tags' in metadata &&
35+
Array.isArray(metadata.tags)
36+
);
37+
}
38+
39+
function findLatestAnnouncement(
40+
allContent: AllContent
41+
): LatestAnnouncement | null {
42+
const blogContent = allContent['docusaurus-plugin-content-blog']?.default;
43+
44+
if (
45+
typeof blogContent !== 'object' ||
46+
blogContent === null ||
47+
!('blogPosts' in blogContent) ||
48+
!Array.isArray(blogContent.blogPosts)
49+
) {
50+
return null;
51+
}
52+
53+
const latest = blogContent.blogPosts
54+
.filter(isBlogPost)
55+
.filter((post) =>
56+
post.metadata.tags.some((tag) => tag.label === 'announcement')
57+
)
58+
.sort((a, b) => b.metadata.date.getTime() - a.metadata.date.getTime())[0];
59+
60+
if (!latest) {
61+
return null;
62+
}
63+
64+
return {
65+
title: latest.metadata.title,
66+
permalink: latest.metadata.permalink,
67+
date: latest.metadata.date.toISOString(),
68+
};
69+
}
70+
71+
export default function latestAnnouncementPlugin(): Plugin<void> {
72+
return {
73+
name: 'latest-announcement',
74+
75+
async allContentLoaded({ allContent, actions }) {
76+
actions.setGlobalData(findLatestAnnouncement(allContent));
77+
},
78+
};
79+
}

0 commit comments

Comments
 (0)