add authors page, facebook and twitter autofil from ghost if not set by user, and added an about page that will auto populate if users have a Page on their ghost server called about like in the default configuration.

This commit is contained in:
Adam Matthiesen 2024-02-21 16:22:58 -08:00
parent 271ad759a8
commit e99553f413
10 changed files with 280 additions and 8 deletions

View File

@ -2,12 +2,16 @@ import type { StarlightPlugin, StarlightUserConfig } from '@astrojs/starlight/ty
import type { AstroIntegrationLogger } from 'astro'
import { type StarlightGhostConfig, validateConfig } from './src/schemas/config'
import { vitePluginStarlightGhostConfig } from './src/integrations/vite'
import { facebook, getSettings, invariant, twitter } from './src/utils/api'
const settings = await getSettings()
export type { StarlightGhostConfig }
export default function starlightGhostCMS(userConfig?: StarlightGhostConfig): StarlightPlugin {
const config: StarlightGhostConfig = validateConfig(userConfig)
invariant(settings, "Settings not available... check your api key/url")
return {
name: '@matthiesenxyz/starlight-ghostcms-plugin',
hooks: {
@ -15,7 +19,9 @@ export default function starlightGhostCMS(userConfig?: StarlightGhostConfig): St
updateStarlightConfig({
social: {
...starlightConfig.social,
rss: `${astroConfig.site}/rss.xml`
rss: `${astroConfig.site}/rss.xml`,
twitter: twitter(settings.twitter?settings.twitter:""),
facebook: facebook(settings.facebook?settings.facebook:""),
},
components: {
...starlightConfig.components,
@ -39,6 +45,15 @@ export default function starlightGhostCMS(userConfig?: StarlightGhostConfig): St
entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/[slug].astro',
prerender: true,
})
injectRoute({
pattern: '/blog/about',
entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/about.astro',
prerender: true,
})
injectRoute({
pattern: '/blog/authors',
entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/authors.astro',
})
injectRoute({
pattern: '/rss.xml',
entrypoint: '@matthiesenxyz/starlight-ghostcms/routes/rss.xml.ts'

View File

@ -46,6 +46,8 @@
"./overrides/Sidebar.astro": "./src/overrides/Sidebar.astro",
"./overrides/SiteTitle.astro": "./src/overrides/SiteTitle.astro",
"./routes/index.astro": "./src/routes/index.astro",
"./routes/about.astro": "./src/routes/about.astro",
"./routes/authors.astro": "./src/routes/authors.astro",
"./routes/[slug].astro": "./src/routes/[slug].astro",
"./routes/rss.xml.ts": "./src/routes/rss.xml.ts",
"./schema": "./src/schemas/config.ts"

File diff suppressed because one or more lines are too long

View File

@ -7,11 +7,11 @@ interface Props {
const { author } = Astro.props
const isLink = author.website !== undefined
const isLink = author.slug !== undefined
const Element = isLink ? 'a' : 'div'
---
<Element href={isLink ? author.website : undefined} class="author">
<Element href={isLink ? '/blog/authors' : undefined} class="author">
{author.profile_image && <img alt={author.name} src={author.profile_image} />}
<div class="text">
<div class="name">{author.name}</div>

View File

@ -3,23 +3,63 @@ import StarlightSidebar from '@astrojs/starlight/components/Sidebar.astro'
import type { Props } from '@astrojs/starlight/props'
import config from 'virtual:starlight-ghost-config'
import { isBlogPostPage, isBlogRoot } from '../utils/page'
import { getAllPosts } from '../utils/api'
import { getAllPages, getAllPosts, getSluggedPage } from '../utils/api/api-functions.js'
import type { SidebarEntry } from './sidebartypes'
export async function getRecentBlogEntries(){
const entries = await getAllPosts()
return entries.slice(0, config.recentPostCount)
}
export async function getBlogPageEntries(){
const entries = await getAllPages()
return entries;
}
export function checkpath(path: string){
if ( path.slice(0, 5) === "/blog" ){
return true
} else { return false }
}
export function isAbout(path: string){
if ( path === "/blog/about" ){
return true
} else { return false }
}
export function isAuthors(path: string){
if ( path === "/blog/authors" ){
return true
} else { return false }
}
const isBlog = checkpath(Astro.url.pathname)
const recentEntries = isBlog ? await getRecentBlogEntries() : []
const aboutPage = await getSluggedPage("about");
const AboutEntry:SidebarEntry = {
attrs: {}, badge: undefined,
href: '/blog/about',
isCurrent: isAbout(Astro.url.pathname),
type: 'link',
label: aboutPage?.post?.title
}
const emptyEntry:SidebarEntry = { attrs: {}, badge: undefined,
href: '#', isCurrent: false, type: 'link', label: '', }
const about = aboutPage?AboutEntry:emptyEntry
const blogSidebar: Props['sidebar'] = isBlog
? [
about,
{
attrs: {},
badge: undefined,
href: '/blog/authors',
isCurrent: isAuthors(Astro.url.pathname),
label: 'Our Authors',
type: 'link',
},
{
attrs: {},
badge: undefined,
@ -33,7 +73,7 @@ const blogSidebar: Props['sidebar'] = isBlog
collapsed: false,
entries: recentEntries.map((blogEntry) => ({
attrs: {},
badge: undefined,
badge: blogEntry.featured?({text: "⭐", variant: "note"}):undefined,
href: `/blog/${blogEntry.slug}`,
isCurrent: isBlogPostPage(Astro.props.slug, `blog/${blogEntry.slug}`),
label: blogEntry.title,

View File

@ -1,12 +1,11 @@
---
import type { Props } from "@astrojs/starlight/props";
import AstrolightSiteTitle from "@astrojs/starlight/components/SiteTitle.astro";
import config from 'virtual:starlight-ghost-config'
---
<AstrolightSiteTitle {...Astro.props} />
<div>
<a href="/blog">{config.title}</a>
<a href="/blog">Blog</a>
</div>
<style>

View File

@ -0,0 +1,46 @@
import { z } from 'astro/zod';
import type { AstroBuiltinAttributes } from 'astro';
import type { HTMLAttributes } from 'astro/types';
const linkHTMLAttributesSchema = z.record(
z.union([z.string(), z.number(), z.boolean(), z.undefined()])
) as z.Schema<Omit<HTMLAttributes<'a'>, keyof AstroBuiltinAttributes | 'children'>>;
export type LinkHTMLAttributes = z.infer<typeof linkHTMLAttributesSchema>;
const badgeSchema = () =>
z.object({
variant: z.enum(['note', 'danger', 'success', 'caution', 'tip', 'default']).default('default'),
text: z.string(),
});
export const BadgeConfigSchema = () =>
z
.union([z.string(), badgeSchema()])
.transform((badge) => {
if (typeof badge === 'string') {
return { variant: 'default' as const, text: badge };
}
return badge;
})
.optional();
export type Badge = z.output<ReturnType<typeof badgeSchema>>;
export interface Link {
type: 'link';
label: string;
href: string;
isCurrent: boolean;
badge: Badge | undefined;
attrs: LinkHTMLAttributes;
}
interface Group {
type: 'group';
label: string;
entries: (Link | Group)[];
collapsed: boolean;
badge: Badge | undefined;
}
export type SidebarEntry = Link | Group;

View File

@ -0,0 +1,36 @@
---
import config from 'virtual:starlight-ghost-config'
import Metadata from '../components/Metadata.astro'
import Page from '../components/Page.astro'
//import PrevNextLinks from '../components/PrevNextLinks.astro'
import { getPageProps } from '../utils/page'
import { getSluggedPage } from '../utils/api'
const aboutPage = await getSluggedPage("about");
//const { entries, nextLink, prevLink } = Astro.props
const pageProps = getPageProps(aboutPage?.post?.title)
---
<Page {...pageProps}>
{config.supportGhost && (
<div id="pghost">Powered by <a href="https://ghost.org">Ghost</a></div>
)}
<Metadata entry={aboutPage.post} />
<Fragment set:html={aboutPage.post.html} />
<footer class="not-content">
<!--PrevNextLinks next={nextLink} prev={prevLink} /-->
</footer>
</Page>
<style>
#pghost {
color: gray;
position: absolute;
top: 4rem;
}
#pghost a {
color: gray;
}
</style>

View File

@ -0,0 +1,48 @@
---
import config from 'virtual:starlight-ghost-config'
import Page from '../components/Page.astro'
//import PrevNextLinks from '../components/PrevNextLinks.astro'
import { getAllAuthors } from '../utils/api/api-functions'
import { getPageProps } from '../utils/page'
import AdvancedAuthorCard from '../components/AdvancedAuthorCard.astro';
//const { entries, nextLink, prevLink } = Astro.props
const { authors } = await getAllAuthors();
const pageProps = getPageProps("Our Authors")
---
<Page {...pageProps}>
{config.supportGhost && (
<div id="pghost">Powered by <a href="https://ghost.org">Ghost</a></div>
)}
<div class="authors">
<ul>
{authors.map((author: any) => (
<li>
<AdvancedAuthorCard {author} />
</li>
))}
</ul>
<footer class="not-content">
<!--PrevNextLinks next={nextLink} prev={prevLink} /-->
</footer>
</Page>
<style>
#pghost {
color: gray;
position: absolute;
top: 4rem;
}
#pghost a {
color: gray;
}
ul {
list-style: none;
}
</style>

View File

@ -106,6 +106,22 @@ export const getAllPages = async () => {
return pages;
};
export const getSluggedPage = async (slug:string) => {
const results = await api.pages
.read({slug: slug})
.include({
authors: true,
tags: true,
}).fetch()
if (!results.success) {
throw new Error(results.errors.map((e) => e.message).join(", "));
}
return {
post: results.data,
};
};
export const getSettings = async () => {
const res = await api.settings.fetch();
if (res.success) {