mirror of
https://github.com/meshtastic/meshtastic.git
synced 2024-11-09 23:24:10 -08:00
Enable json blog feed
This commit is contained in:
parent
9391a5397a
commit
becc8f1722
|
@ -134,6 +134,34 @@ const config = {
|
|||
blogTitle: "Meshtastic Blog",
|
||||
blogDescription:
|
||||
"Discover in-depth insights from developers and maintainers, including project updates and changes. Hear from the community about their projects and ideas.",
|
||||
feedOptions: {
|
||||
type: "json",
|
||||
title: "My Feed",
|
||||
description: "",
|
||||
copyright: "",
|
||||
language: undefined,
|
||||
createFeedItems: async (params) => {
|
||||
const { blogPosts, defaultCreateFeedItems, ...rest } = params;
|
||||
const feedItems = await defaultCreateFeedItems({
|
||||
blogPosts: blogPosts.map((post) => {
|
||||
return {
|
||||
metadataTitle: post.metadata.title,
|
||||
title: post.metadata.title,
|
||||
id: post.id,
|
||||
metadata: post.metadata,
|
||||
permalink: post.metadata.permalink,
|
||||
description: post.metadata.description,
|
||||
date: post.metadata.date,
|
||||
content: post.content,
|
||||
authors: post.metadata.authors,
|
||||
tags: post.metadata.tags,
|
||||
};
|
||||
}),
|
||||
...rest,
|
||||
});
|
||||
return feedItems;
|
||||
},
|
||||
},
|
||||
},
|
||||
theme: {
|
||||
customCss: require.resolve("./src/css/custom.css"),
|
||||
|
|
12
package.json
12
package.json
|
@ -14,11 +14,11 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@algolia/client-search": "^4.22.1",
|
||||
"@docusaurus/core": "3.1.1",
|
||||
"@docusaurus/plugin-content-docs": "3.1.1",
|
||||
"@docusaurus/preset-classic": "3.1.1",
|
||||
"@docusaurus/theme-common": "3.1.1",
|
||||
"@docusaurus/theme-mermaid": "3.1.1",
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/plugin-content-docs": "3.5.2",
|
||||
"@docusaurus/preset-classic": "3.5.2",
|
||||
"@docusaurus/theme-common": "3.5.2",
|
||||
"@docusaurus/theme-mermaid": "3.5.2",
|
||||
"@giscus/react": "^3.0.0",
|
||||
"@heroicons/react": "^2.1.1",
|
||||
"@mdx-js/react": "^3.0.1",
|
||||
|
@ -40,7 +40,7 @@
|
|||
"devDependencies": {
|
||||
"@biomejs/biome": "^1.5.3",
|
||||
"@buf/meshtastic_protobufs.bufbuild_es": "1.7.2-20240216123215-6b07c41c68c9.1",
|
||||
"@docusaurus/module-type-aliases": "3.1.1",
|
||||
"@docusaurus/module-type-aliases": "3.5.2",
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
"@tsconfig/docusaurus": "^2.0.2",
|
||||
"@types/node": "^20.11.19",
|
||||
|
|
4566
pnpm-lock.yaml
4566
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,37 +0,0 @@
|
|||
import { PageMetadata } from "@docusaurus/theme-common";
|
||||
import { useBlogPost } from "@docusaurus/theme-common/internal";
|
||||
import React from "react";
|
||||
|
||||
export default function BlogPostPageMetadata() {
|
||||
const { assets, metadata } = useBlogPost();
|
||||
const { title, description, date, tags, authors, frontMatter } = metadata;
|
||||
const { keywords } = frontMatter;
|
||||
const image = assets.image ?? frontMatter.image;
|
||||
return (
|
||||
<PageMetadata
|
||||
title={title}
|
||||
description={description}
|
||||
keywords={keywords}
|
||||
image={image}
|
||||
>
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="article:published_time" content={date} />
|
||||
{/* TODO double check those article meta array syntaxes, see https://ogp.me/#array */}
|
||||
{authors.some((author) => author.url) && (
|
||||
<meta
|
||||
property="article:author"
|
||||
content={authors
|
||||
.map((author) => author.url)
|
||||
.filter(Boolean)
|
||||
.join(",")}
|
||||
/>
|
||||
)}
|
||||
{tags.length > 0 && (
|
||||
<meta
|
||||
property="article:tag"
|
||||
content={tags.map((tag) => tag.label).join(",")}
|
||||
/>
|
||||
)}
|
||||
</PageMetadata>
|
||||
);
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
import {
|
||||
HtmlClassNameProvider,
|
||||
ThemeClassNames,
|
||||
} from "@docusaurus/theme-common";
|
||||
import {
|
||||
BlogPostProvider,
|
||||
useBlogPost,
|
||||
} from "@docusaurus/theme-common/internal";
|
||||
import GiscusComponent from "@site/src/components/GiscusComponent";
|
||||
import BlogLayout from "@theme/BlogLayout";
|
||||
import BlogPostItem from "@theme/BlogPostItem";
|
||||
import BlogPostPageMetadata from "@theme/BlogPostPage/Metadata";
|
||||
import BlogPostPaginator from "@theme/BlogPostPaginator";
|
||||
import TOC from "@theme/TOC";
|
||||
import Unlisted from "@theme/Unlisted";
|
||||
import clsx from "clsx";
|
||||
import React from "react";
|
||||
function BlogPostPageContent({ sidebar, children }) {
|
||||
const { metadata, toc } = useBlogPost();
|
||||
const { nextItem, prevItem, frontMatter, unlisted } = metadata;
|
||||
const {
|
||||
hide_table_of_contents: hideTableOfContents,
|
||||
toc_min_heading_level: tocMinHeadingLevel,
|
||||
toc_max_heading_level: tocMaxHeadingLevel,
|
||||
} = frontMatter;
|
||||
return (
|
||||
<BlogLayout
|
||||
sidebar={sidebar}
|
||||
toc={
|
||||
!hideTableOfContents && toc.length > 0 ? (
|
||||
<TOC
|
||||
toc={toc}
|
||||
minHeadingLevel={tocMinHeadingLevel}
|
||||
maxHeadingLevel={tocMaxHeadingLevel}
|
||||
/>
|
||||
) : undefined
|
||||
}
|
||||
>
|
||||
{unlisted && <Unlisted />}
|
||||
<BlogPostItem>{children}</BlogPostItem>
|
||||
<GiscusComponent />
|
||||
|
||||
{(nextItem || prevItem) && (
|
||||
<BlogPostPaginator nextItem={nextItem} prevItem={prevItem} />
|
||||
)}
|
||||
</BlogLayout>
|
||||
);
|
||||
}
|
||||
|
||||
export default function BlogPostPage(props) {
|
||||
const BlogPostContent = props.content;
|
||||
return (
|
||||
<BlogPostProvider content={props.content} isBlogPostPage>
|
||||
<HtmlClassNameProvider
|
||||
className={clsx(
|
||||
ThemeClassNames.wrapper.blogPages,
|
||||
ThemeClassNames.page.blogPostPage,
|
||||
)}
|
||||
>
|
||||
<BlogPostPageMetadata />
|
||||
<BlogPostPageContent sidebar={props.sidebar}>
|
||||
<BlogPostContent />
|
||||
</BlogPostPageContent>
|
||||
</HtmlClassNameProvider>
|
||||
</BlogPostProvider>
|
||||
);
|
||||
}
|
11
src/theme/Footer/Copyright/index.js
Normal file
11
src/theme/Footer/Copyright/index.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import React from 'react';
|
||||
export default function FooterCopyright({copyright}) {
|
||||
return (
|
||||
<div
|
||||
className="footer__copyright"
|
||||
// Developer provided the HTML, so assume it's safe.
|
||||
// eslint-disable-next-line react/no-danger
|
||||
dangerouslySetInnerHTML={{__html: copyright}}
|
||||
/>
|
||||
);
|
||||
}
|
20
src/theme/Footer/Layout/index.js
Normal file
20
src/theme/Footer/Layout/index.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
export default function FooterLayout({style, links, logo, copyright}) {
|
||||
return (
|
||||
<footer
|
||||
className={clsx('footer', {
|
||||
'footer--dark': style === 'dark',
|
||||
})}>
|
||||
<div className="container container-fluid">
|
||||
{links}
|
||||
{(logo || copyright) && (
|
||||
<div className="footer__bottom text--center">
|
||||
{logo && <div className="margin-bottom--sm">{logo}</div>}
|
||||
{copyright}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
25
src/theme/Footer/LinkItem/index.js
Normal file
25
src/theme/Footer/LinkItem/index.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
import React from 'react';
|
||||
import Link from '@docusaurus/Link';
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||
import isInternalUrl from '@docusaurus/isInternalUrl';
|
||||
import IconExternalLink from '@theme/Icon/ExternalLink';
|
||||
export default function FooterLinkItem({item}) {
|
||||
const {to, href, label, prependBaseUrlToHref, ...props} = item;
|
||||
const toUrl = useBaseUrl(to);
|
||||
const normalizedHref = useBaseUrl(href, {forcePrependBaseUrl: true});
|
||||
return (
|
||||
<Link
|
||||
className="footer__link-item"
|
||||
{...(href
|
||||
? {
|
||||
href: prependBaseUrlToHref ? normalizedHref : href,
|
||||
}
|
||||
: {
|
||||
to: toUrl,
|
||||
})}
|
||||
{...props}>
|
||||
{label}
|
||||
{href && !isInternalUrl(href) && <IconExternalLink />}
|
||||
</Link>
|
||||
);
|
||||
}
|
37
src/theme/Footer/Links/MultiColumn/index.js
Normal file
37
src/theme/Footer/Links/MultiColumn/index.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
import React from 'react';
|
||||
import LinkItem from '@theme/Footer/LinkItem';
|
||||
function ColumnLinkItem({item}) {
|
||||
return item.html ? (
|
||||
<li
|
||||
className="footer__item"
|
||||
// Developer provided the HTML, so assume it's safe.
|
||||
// eslint-disable-next-line react/no-danger
|
||||
dangerouslySetInnerHTML={{__html: item.html}}
|
||||
/>
|
||||
) : (
|
||||
<li key={item.href ?? item.to} className="footer__item">
|
||||
<LinkItem item={item} />
|
||||
</li>
|
||||
);
|
||||
}
|
||||
function Column({column}) {
|
||||
return (
|
||||
<div className="col footer__col">
|
||||
<div className="footer__title">{column.title}</div>
|
||||
<ul className="footer__items clean-list">
|
||||
{column.items.map((item, i) => (
|
||||
<ColumnLinkItem key={i} item={item} />
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default function FooterLinksMultiColumn({columns}) {
|
||||
return (
|
||||
<div className="row footer__links">
|
||||
{columns.map((column, i) => (
|
||||
<Column key={i} column={column} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
31
src/theme/Footer/Links/Simple/index.js
Normal file
31
src/theme/Footer/Links/Simple/index.js
Normal file
|
@ -0,0 +1,31 @@
|
|||
import React from 'react';
|
||||
import LinkItem from '@theme/Footer/LinkItem';
|
||||
function Separator() {
|
||||
return <span className="footer__link-separator">·</span>;
|
||||
}
|
||||
function SimpleLinkItem({item}) {
|
||||
return item.html ? (
|
||||
<span
|
||||
className="footer__link-item"
|
||||
// Developer provided the HTML, so assume it's safe.
|
||||
// eslint-disable-next-line react/no-danger
|
||||
dangerouslySetInnerHTML={{__html: item.html}}
|
||||
/>
|
||||
) : (
|
||||
<LinkItem item={item} />
|
||||
);
|
||||
}
|
||||
export default function FooterLinksSimple({links}) {
|
||||
return (
|
||||
<div className="footer__links text--center">
|
||||
<div className="footer__links">
|
||||
{links.map((item, i) => (
|
||||
<React.Fragment key={i}>
|
||||
<SimpleLinkItem item={item} />
|
||||
{links.length !== i + 1 && <Separator />}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
11
src/theme/Footer/Links/index.js
Normal file
11
src/theme/Footer/Links/index.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
import React from 'react';
|
||||
import {isMultiColumnFooterLinks} from '@docusaurus/theme-common';
|
||||
import FooterLinksMultiColumn from '@theme/Footer/Links/MultiColumn';
|
||||
import FooterLinksSimple from '@theme/Footer/Links/Simple';
|
||||
export default function FooterLinks({links}) {
|
||||
return isMultiColumnFooterLinks(links) ? (
|
||||
<FooterLinksMultiColumn columns={links} />
|
||||
) : (
|
||||
<FooterLinksSimple links={links} />
|
||||
);
|
||||
}
|
35
src/theme/Footer/Logo/index.js
Normal file
35
src/theme/Footer/Logo/index.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import Link from '@docusaurus/Link';
|
||||
import {useBaseUrlUtils} from '@docusaurus/useBaseUrl';
|
||||
import ThemedImage from '@theme/ThemedImage';
|
||||
import styles from './styles.module.css';
|
||||
function LogoImage({logo}) {
|
||||
const {withBaseUrl} = useBaseUrlUtils();
|
||||
const sources = {
|
||||
light: withBaseUrl(logo.src),
|
||||
dark: withBaseUrl(logo.srcDark ?? logo.src),
|
||||
};
|
||||
return (
|
||||
<ThemedImage
|
||||
className={clsx('footer__logo', logo.className)}
|
||||
alt={logo.alt}
|
||||
sources={sources}
|
||||
width={logo.width}
|
||||
height={logo.height}
|
||||
style={logo.style}
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default function FooterLogo({logo}) {
|
||||
return logo.href ? (
|
||||
<Link
|
||||
href={logo.href}
|
||||
className={styles.footerLogoLink}
|
||||
target={logo.target}>
|
||||
<LogoImage logo={logo} />
|
||||
</Link>
|
||||
) : (
|
||||
<LogoImage logo={logo} />
|
||||
);
|
||||
}
|
9
src/theme/Footer/Logo/styles.module.css
Normal file
9
src/theme/Footer/Logo/styles.module.css
Normal file
|
@ -0,0 +1,9 @@
|
|||
.footerLogoLink {
|
||||
opacity: 0.5;
|
||||
transition: opacity var(--ifm-transition-fast)
|
||||
var(--ifm-transition-timing-default);
|
||||
}
|
||||
|
||||
.footerLogoLink:hover {
|
||||
opacity: 1;
|
||||
}
|
40
src/theme/Footer/index.js
Normal file
40
src/theme/Footer/index.js
Normal file
|
@ -0,0 +1,40 @@
|
|||
import React from "react";
|
||||
import { useThemeConfig } from "@docusaurus/theme-common";
|
||||
import FooterLinks from "@theme/Footer/Links";
|
||||
import FooterLogo from "@theme/Footer/Logo";
|
||||
import FooterCopyright from "@theme/Footer/Copyright";
|
||||
import FooterLayout from "@theme/Footer/Layout";
|
||||
import { useLocation } from "@docusaurus/router";
|
||||
import GiscusComponent from "../../components/GiscusComponent";
|
||||
function Footer() {
|
||||
const { footer } = useThemeConfig();
|
||||
if (!footer) {
|
||||
return null;
|
||||
}
|
||||
const { copyright, links, logo, style } = footer;
|
||||
const location = useLocation();
|
||||
const isBlogSubPage =
|
||||
location.pathname.startsWith("/blog/") && location.pathname !== "/blog/";
|
||||
|
||||
return (
|
||||
<>
|
||||
{isBlogSubPage && (
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col col--7 col--offset-3">
|
||||
<GiscusComponent />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<FooterLayout
|
||||
style={style}
|
||||
links={links && links.length > 0 && <FooterLinks links={links} />}
|
||||
logo={logo && <FooterLogo logo={logo} />}
|
||||
copyright={copyright && <FooterCopyright copyright={copyright} />}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
export default React.memo(Footer);
|
Loading…
Reference in a new issue