diff --git a/examples/social-feed/astro.config.mjs b/examples/social-feed/astro.config.mjs index 0df803a2e..085a91d81 100644 --- a/examples/social-feed/astro.config.mjs +++ b/examples/social-feed/astro.config.mjs @@ -2,5 +2,5 @@ import { defineConfig } from 'astro/config'; // https://astro.build/config export default defineConfig({ - site: 'https://www.example.com', + site: 'https://www.example.com', }); diff --git a/examples/social-feed/package.json b/examples/social-feed/package.json index c0fee3c0a..343fe9925 100644 --- a/examples/social-feed/package.json +++ b/examples/social-feed/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@astrojs/rss": "^3.0.0", - "astro": "^3.0.1" + "astro": "^3.0.1", + "reading-time": "^1.5.0" } } diff --git a/examples/social-feed/src/assets/mechanical-keyboard.png b/examples/social-feed/src/assets/mechanical-keyboard.png new file mode 100644 index 000000000..0e037471d Binary files /dev/null and b/examples/social-feed/src/assets/mechanical-keyboard.png differ diff --git a/examples/social-feed/src/components/FormattedDate.astro b/examples/social-feed/src/components/FormattedDate.astro new file mode 100644 index 000000000..1bcce73a2 --- /dev/null +++ b/examples/social-feed/src/components/FormattedDate.astro @@ -0,0 +1,17 @@ +--- +interface Props { + date: Date; +} + +const { date } = Astro.props; +--- + + diff --git a/examples/social-feed/src/components/IconPaths.ts b/examples/social-feed/src/components/IconPaths.ts index 796693002..804cdd78c 100644 --- a/examples/social-feed/src/components/IconPaths.ts +++ b/examples/social-feed/src/components/IconPaths.ts @@ -9,25 +9,26 @@ * (or add `stroke="none"` on shapes with no `fill` or `stroke` specified). */ export const iconPaths = { - 'arrow-right': ``, - 'arrow-left': ``, - 'rss-alt': ``, - 'link-h': ``, - 'location-point': ``, - user: ``, - heart: ``, - 'moon-stars': ``, - sun: ``, - 'twitter-logo': ``, - 'codepen-logo': ``, - 'github-logo': ``, - 'twitch-logo': ``, - 'youtube-logo': ``, - 'dribbble-logo': ``, - 'discord-logo': ``, - 'linkedin-logo': ``, - 'instagram-logo': ``, - 'tiktok-logo': ``, - 'devto-logo': ``, - 'mastodon-logo': ``, + 'arrow-right': ``, + 'arrow-left': ``, + 'rss-alt': ``, + 'link-h': ``, + 'location-point': ``, + user: ``, + heart: ``, + 'moon-stars': ``, + sun: ``, + 'twitter-logo': ``, + 'codepen-logo': ``, + 'github-logo': ``, + 'twitch-logo': ``, + 'youtube-logo': ``, + 'dribbble-logo': ``, + 'discord-logo': ``, + 'linkedin-logo': ``, + 'instagram-logo': ``, + 'tiktok-logo': ``, + 'devto-logo': ``, + 'mastodon-logo': ``, + clock: ``, }; diff --git a/examples/social-feed/src/components/Pagination.astro b/examples/social-feed/src/components/Pagination.astro index cfa7e8bcf..b51d2e30c 100644 --- a/examples/social-feed/src/components/Pagination.astro +++ b/examples/social-feed/src/components/Pagination.astro @@ -6,15 +6,15 @@ const { currentPage, lastPage, url } = Astro.props; const firstPage = 1; interface Item { - page: number; - url: string; - current: boolean; + page: number; + url: string; + current: boolean; } const makeItem = (page: number): Item => ({ - page, - url: page === 1 ? '/' : `/${page}`, - current: page === currentPage, + page, + url: page === 1 ? '/' : `/${page}`, + current: page === currentPage, }); const items: (Item | null)[] = []; @@ -23,13 +23,10 @@ const items: (Item | null)[] = []; const beforeAfter = 1; // Get range of pages around current page. const min = Math.max( - Math.min(currentPage - beforeAfter, lastPage - 2 * beforeAfter - 1), - firstPage -); -const max = Math.min( - Math.max(min + 2 * beforeAfter, firstPage + 2 * beforeAfter + 1), - lastPage + Math.min(currentPage - beforeAfter, lastPage - 2 * beforeAfter - 1), + firstPage ); +const max = Math.min(Math.max(min + 2 * beforeAfter, firstPage + 2 * beforeAfter + 1), lastPage); // Always include first page. if (min > firstPage) items.push(makeItem(firstPage)); // Show “…” if the range starts at page 4 or higher. @@ -47,84 +44,84 @@ if (max < lastPage) items.push(makeItem(lastPage)); --- { - lastPage > 1 && ( - - ) + lastPage > 1 && ( + + ) } diff --git a/examples/social-feed/src/components/TagList.astro b/examples/social-feed/src/components/TagList.astro new file mode 100644 index 000000000..dc6cfc5e2 --- /dev/null +++ b/examples/social-feed/src/components/TagList.astro @@ -0,0 +1,31 @@ +--- +interface Props { + tags: string[]; +} + +const { tags } = Astro.props; +--- + +
    + {tags.map((tag) =>
  • {tag}
  • )} +
+ + diff --git a/examples/social-feed/src/content/config.ts b/examples/social-feed/src/content/config.ts index 2bed482e2..7bbf268df 100644 --- a/examples/social-feed/src/content/config.ts +++ b/examples/social-feed/src/content/config.ts @@ -1,26 +1,22 @@ -// import { rssSchema } from '@astrojs/rss'; +import { rssSchema } from '@astrojs/rss'; import { defineCollection, z } from 'astro:content'; const posts = defineCollection({ - // TODO: Extend rssSchema here — was doing that in my standalone project but seems to be broken in the monorepo. - schema: z - .object({ title: z.string(), description: z.string().optional(), pubDate: z.date() }) + schema: ({ image }) => rssSchema .extend({ - tags: z.array(z.string()).default([]), - cover: z - .object({ - src: z.string(), - // TODO: Support experimental assets instead of plain string schema: - // image().refine( - // (img) => img.width >= 885, - // 'Cover image must be at least 885px wide.' - // ) - alt: z.string(), - }) - .optional(), - type: z.enum(['article', 'tweet']).default('tweet'), - }) - .strict(), + tags: z.array(z.string()).default([]), + cover: z + .object({ + src: image().refine( + (img) => img.width >= 885, + 'Cover image must be at least 885px wide.' + ), + alt: z.string(), + }) + .optional(), + type: z.enum(['article', 'note']).default('note'), + }) + .strict(), }); export const collections = { posts }; diff --git a/examples/social-feed/src/content/posts/first-post.md b/examples/social-feed/src/content/posts/first-post.md index 74e3b4f03..19db8a463 100644 --- a/examples/social-feed/src/content/posts/first-post.md +++ b/examples/social-feed/src/content/posts/first-post.md @@ -3,8 +3,26 @@ title: First post on my new site! pubDate: 2023-01-01 tags: [keyboards, thoughts] cover: - src: stock-1.jpg + src: ../../assets/stock-1.jpg alt: A laptop with a code editor open --- -Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. +Dignissim eu sagittis aliquet magna sagittis. Eu etiam faucibus quis non. Laoreet amet aliquam enim sapien. Cras ac enim nulla morbi ultrices elementum metus neque nulla. Quis mi consectetur donec tempor habitant. + +Id at orci nulla nunc. Habitant a amet turpis facilisi purus cursus dui imperdiet. Integer fermentum amet nunc tristique scelerisque feugiat pellentesque phasellus. Feugiat euismod id varius id mattis ac ut. Donec aliquet fusce ut egestas vehicula sagittis. + +Mauris vulputate tristique porttitor sed integer felis. In eget sodales lobortis laoreet molestie aliquet est aliquet tortor. Sodales erat rhoncus tellus mattis etiam nunc ornare. Nisi ut tellus elementum gravida dictum diam porta. Nulla porttitor magna duis pretium egestas nisl ornare. + +## Heading One + +Suscipit id porttitor laoreet purus semper fermentum libero cras magna. Non hendrerit lectus tincidunt condimentum. Sollicitudin pretium at vel nibh tempus proin faucibus donec sed. Habitant et risus auctor platea viverra tellus. Faucibus dapibus neque enim metus porttitor pulvinar pharetra amet sem. Nibh a facilisis pellentesque ut pellentesque. Amet semper ultrices est lacus facilisis pulvinar viverra. Quis velit cursus viverra varius vestibulum. Ultricies interdum id dapibus nunc bibendum vitae varius in. Lorem vel congue in amet et pellentesque sed facilisis. Id amet sed suspendisse tincidunt lacinia sit. Ut libero id ornare cursus porttitor elementum. + +## Heading Number Two + +Sagittis fames arcu tempor morbi sed mauris eu blandit cras. Risus amet nec auctor nunc pretium commodo. Dictum duis nascetur est molestie ullamcorper sit tempor. Vestibulum dictumst magna cursus aenean vitae ornare amet non. + +Dui tortor viverra eu montes. Elit pretium pharetra aliquam pellentesque congue id morbi maecenas. Donec a egestas ipsum sit a ipsum. Quis vestibulum feugiat tincidunt justo tellus turpis. Luctus dignissim porta dictumst ut auctor neque in. + +![Close-up of a mechanical keyboard with LED backlighting](../../assets/mechanical-keyboard.png) + +Pellentesque faucibus faucibus magna tempus proin amet in viverra. Eros non ipsum justo pellentesque vestibulum morbi proin euismod. Sagittis in amet at aliquet facilisis. diff --git a/examples/social-feed/src/content/posts/third-post.md b/examples/social-feed/src/content/posts/third-post.md index ed924287c..44f20052d 100644 --- a/examples/social-feed/src/content/posts/third-post.md +++ b/examples/social-feed/src/content/posts/third-post.md @@ -2,7 +2,7 @@ title: Three is a magic number pubDate: 2023-01-03 cover: - src: stock-2.jpg + src: ../../assets/stock-2.jpg alt: A backlit multicolored mechanical keyboard --- diff --git a/examples/social-feed/src/layouts/Article.astro b/examples/social-feed/src/layouts/Article.astro new file mode 100644 index 000000000..6cd295327 --- /dev/null +++ b/examples/social-feed/src/layouts/Article.astro @@ -0,0 +1,173 @@ +--- +import { Image } from 'astro:assets'; +import type { CollectionEntry } from 'astro:content'; +import getReadingTime from 'reading-time'; +import Layout, { type Props as LayoutProps } from './Base.astro'; +import FormattedDate from '../components/FormattedDate.astro'; +import TagList from '../components/TagList.astro'; +import Icon from '../components/Icon.astro'; + +export interface Props extends LayoutProps { + article: CollectionEntry<'posts'>; + next?: CollectionEntry<'posts'>; + prev?: CollectionEntry<'posts'>; +} + +const { article, next, prev } = Astro.props; + +const { Content, headings } = await article.render(); + +const readingTime = getReadingTime(article.body).text; +--- + + +
+ + + Back to feed + + {article.data.cover && {article.data.cover.alt}} +
+ + +

+ + {readingTime} +

+
+

{article.data.title}

+ {article.data.tags?.length > 0 && ( + + )} +
+
+ +
+ {headings.length > 0 && ( + + )} + {(next || prev) && ( + + )} +
+ + diff --git a/examples/social-feed/src/layouts/Base.astro b/examples/social-feed/src/layouts/Base.astro index fd1449b60..adb228870 100644 --- a/examples/social-feed/src/layouts/Base.astro +++ b/examples/social-feed/src/layouts/Base.astro @@ -7,13 +7,14 @@ import Header from '../components/Header.astro'; import UserProfile from '../components/UserProfile.astro'; import Footer from '../components/Footer.astro'; -interface Props { +export interface Props { title?: string; + wrapperReverse?: boolean; } const canonicalURL = new URL(Astro.url.pathname, Astro.site); -const { title = settings.name } = Astro.props; +const { title = settings.name, wrapperReverse = false } = Astro.props; --- @@ -74,13 +75,10 @@ const { title = settings.name } = Astro.props;
-
- -
- -
-
-
+
+ +
+