WIP: refactoring the types to distinguish between articles and notes
This commit is contained in:
parent
ae23274489
commit
01bef5d14b
28 changed files with 307 additions and 201 deletions
|
@ -48,7 +48,7 @@ const { text: readingTime } = await getReadingTime(article.body);
|
|||
|
||||
<h1>{article.data.title}</h1>
|
||||
|
||||
{article.data.tags?.length > 0 && <TagList tags={article.data.tags} />}
|
||||
{article.data.categories?.length > 0 && <TagList tags={article.data.categories} />}
|
||||
</header>
|
||||
|
||||
<style>
|
||||
|
|
99
examples/social-feed/src/components/Card.astro
Normal file
99
examples/social-feed/src/components/Card.astro
Normal file
|
@ -0,0 +1,99 @@
|
|||
---
|
||||
import type { HTMLAttributes } from 'astro/types';
|
||||
import { Image } from 'astro:assets';
|
||||
import FormattedDate from './FormattedDate.astro';
|
||||
import Icon from './Icon.astro';
|
||||
import TagList from './TagList.astro';
|
||||
import settings from '../settings.js';
|
||||
|
||||
export interface Props extends HTMLAttributes<'article'> {
|
||||
post: CollectionEntry<'articles'> | CollectionEntry<'notes'>;
|
||||
}
|
||||
|
||||
const { post } = Astro.props;
|
||||
|
||||
const { pubDate, categories = [], image, title, href } = post.data;
|
||||
|
||||
const { Content } = await post.render();
|
||||
|
||||
console.log(post.data)
|
||||
---
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<Image {...settings.avatar} width={120} class="u-image" />
|
||||
<strong class="u-name">{settings.name}</strong>
|
||||
<div class="u-nickname">
|
||||
{settings.username}
|
||||
•
|
||||
<FormattedDate date={pubDate} />
|
||||
</div>
|
||||
{categories.length > 0 && <TagList tags={categories} class="tags" />}
|
||||
</header>
|
||||
{image && <Image {...image} width={1060} />}
|
||||
{title && <h3>{title}</h3>}
|
||||
{
|
||||
post.data.type === 'article' ? (
|
||||
<p>{post.data.description}</p>
|
||||
) : (
|
||||
post.render().then(({ Content }) => <Content />)
|
||||
)
|
||||
}
|
||||
<footer>
|
||||
<a href={`/post/${post.slug}/`}>Full {post.data.type === 'article' ? 'article' : 'note'}</a>
|
||||
<a href={`javascript: navigator.clipboard.writeText(window.location.href + "post/${post.slug}/");`}>
|
||||
<Icon icon="share" size="1.5rem" />
|
||||
<span class="sr-only">Share this post</span>
|
||||
</a>
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
<style>
|
||||
article {
|
||||
padding: 1.25rem 1.25rem 1.5rem;
|
||||
background-color: var(--theme-bg-accent);
|
||||
border-radius: var(--theme-radius-xl);
|
||||
box-shadow: var(--theme-shadow-md);
|
||||
}
|
||||
article > :global(* + *) {
|
||||
margin-block-start: 1.25rem;
|
||||
}
|
||||
header {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto 1fr;
|
||||
grid-template-areas: 'avatar name nickname' 'avatar tags tags';
|
||||
align-items: center;
|
||||
row-gap: 0.5rem;
|
||||
column-gap: 1rem;
|
||||
}
|
||||
.u-image {
|
||||
grid-area: avatar;
|
||||
width: 3.75rem;
|
||||
height: 3.75rem;
|
||||
border-radius: var(--theme-radius-full);
|
||||
background-color: var(--theme-shade-subtle);
|
||||
}
|
||||
.u-name {
|
||||
grid-area: name;
|
||||
font-family: var(--font-brand);
|
||||
font-weight: bold;
|
||||
}
|
||||
.u-nickname {
|
||||
grid-area: nickname;
|
||||
font-size: var(--theme-text-sm);
|
||||
color: var(--theme-gray-300);
|
||||
}
|
||||
.tags {
|
||||
grid-area: tags;
|
||||
}
|
||||
article > img {
|
||||
aspect-ratio: 21/9;
|
||||
object-fit: cover;
|
||||
border-radius: var(--theme-radius-lg);
|
||||
}
|
||||
footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
|
@ -31,4 +31,5 @@ export const iconPaths = {
|
|||
'devto-logo': `<path fill-rule="evenodd" d="M37 33c-2.2.5-4 2-4.7 4.2l-.3.8v181.8l.3.8c.7 2 2 3.3 4 4l.8.3H219l.7-.3c2-.7 3.3-2 4-4l.3-.8V38l-.3-.8c-.7-2-2-3.3-4-4l-.8-.3H37Zm43.6 58.3A20.7 20.7 0 0 1 98 109.6c.3 1.9.3 36.7 0 38.6a20.5 20.5 0 0 1-16 17.9c-2.6.5-1.8.5-14.9.6h-12V91h12.2c11 0 12.4 0 13.2.2Zm63.6 6.5v6.8H120v17.6h14.8v13.4H120v17.5h24.2v13.6h-30l-.8-.3a9.3 9.3 0 0 1-6.7-6.7l-.2-.9v-60l.2-.8c1-3.4 3.6-6 7-6.7.8-.2 1.8-.2 15.7-.2h14.8v6.7Zm27 8.6a268210 268210 0 0 1 9.5 36.4 5274.6 5274.6 0 0 0 13.5-51.6H209l-8.8 33a527.2 527.2 0 0 1-9.3 34.2 15 15 0 0 1-6.5 7.5c-1.3.6-2 .7-3.7.7-1.2 0-1.6 0-2.2-.2-3.2-.8-5.8-3.3-8-7.7l-.7-1.5-8.7-32.9-8.7-33c0-.2.3-.2 7.4-.2h7.4l4 15.3ZM68.7 128.8v24.3h9.5l.8-.3a9 9 0 0 0 4.7-3.4 7 7 0 0 0 1-2.4v-35.7l-.1-.8c-.6-2.3-1.9-3.8-4.3-5-1.8-.9-1.9-.9-7-1h-4.6v24.3Z" clip-rule="evenodd"/>`,
|
||||
'mastodon-logo': `<path d="M219.3 74.8a49.7 49.7 0 0 0-43-40.4c-3.7-.6-17.6-2.5-49.8-2.5h-.3c-32.2 0-39.1 2-42.8 2.5-21.1 3-40.5 17.8-45.2 39a132.2 132.2 0 0 0-2 32.4c.6 15.1.7 30.3 2 45.3 1 10 2.7 20 5 29.8 4.5 18 22.6 33.1 40.3 39.2a108.8 108.8 0 0 0 65.1 1.4c4.7-1.5 10.3-3.2 14.4-6a.5.5 0 0 0 .2-.4v-14.7a.4.4 0 0 0-.4-.4h-.2a158 158 0 0 1-38.1 4.4c-22.1 0-28-10.3-29.8-14.7a45.1 45.1 0 0 1-2.4-12 .4.4 0 0 1 .4 0c12.3 3 24.9 4.4 37.5 4.4h9.1c12.7-.4 26.1-1 38.6-3.5.3 0 .6 0 .9-.2 19.7-3.7 38.5-15.5 40.4-45.3l.3-13.5c0-4 1.3-29.3-.2-44.8Zm-30.4 74.3h-20.7V99c0-10.6-4.5-16-13.6-16-10 0-15 6.4-15 19v27.5h-20.5V102c0-12.6-5-19-15-19-9 0-13.5 5.4-13.5 16v50H69.9V97.4A37 37 0 0 1 78 72c5.6-6.2 13-9.4 22.1-9.4 10.7 0 18.7 4 24 12.1l5.2 8.6 5.2-8.6c5.3-8 13.3-12 24-12 9 0 16.5 3.1 22.1 9.3a36.9 36.9 0 0 1 8.2 25.3V149Z"/>`,
|
||||
clock: `<path d="M158 118H138V78C138 75.3478 136.946 72.8043 135.071 70.929C133.196 69.0536 130.652 68 128 68C125.348 68 122.804 69.0536 120.929 70.929C119.054 72.8043 118 75.3478 118 78V128C118 130.652 119.054 133.196 120.929 135.071C122.804 136.946 125.348 138 128 138H158C160.653 138 163.195 136.946 165.07 135.071C166.947 133.196 168 130.652 168 128C168 125.348 166.947 122.804 165.07 120.929C163.195 119.054 160.653 118 158 118ZM128 28C108.222 28 88.8878 33.865 72.443 44.853C55.9981 55.8411 43.1808 71.459 35.612 89.7317C28.0434 108.004 26.063 128.111 29.9214 147.509C33.78 166.907 43.3042 184.725 57.2893 198.71C71.2746 212.696 89.093 222.221 108.491 226.078C127.889 229.938 147.996 227.957 166.269 220.387C184.541 212.819 200.158 200.002 211.147 183.557C222.134 167.112 228 147.778 228 128C228 114.868 225.413 101.864 220.387 89.7317C215.363 77.599 207.997 66.5752 198.71 57.2893C189.426 48.0035 178.402 40.6374 166.269 35.612C154.136 30.5866 141.132 28 128 28ZM128 208C112.177 208 96.7104 203.309 83.5544 194.517C70.3984 185.726 60.1446 173.232 54.0896 158.615C48.0347 143.997 46.4504 127.911 49.5371 112.393C52.624 96.8742 60.2432 82.6197 71.4315 71.4315C82.6197 60.2432 96.8742 52.624 112.393 49.5371C127.911 46.4504 143.997 48.0346 158.615 54.0896C173.232 60.1446 185.726 70.3984 194.517 83.5544C203.309 96.7102 208 112.177 208 128C208 149.217 199.571 169.566 184.568 184.568C169.566 199.571 149.217 208 128 208Z" fill="currentColor"/>`,
|
||||
share: `<path d="M192 149.333C185.698 149.375 179.483 150.812 173.803 153.541C168.123 156.271 163.117 160.225 159.147 165.12L104.747 140.053C107.305 132.221 107.305 123.778 104.747 115.947L159.147 90.8799C165.564 98.6235 174.51 103.851 184.406 105.641C194.304 107.43 204.514 105.666 213.236 100.66C221.96 95.654 228.634 87.7276 232.08 78.2799C235.528 68.832 235.526 58.47 232.078 49.0229C228.629 39.5756 221.953 31.6505 213.23 26.6459C204.507 21.6415 194.296 19.8793 184.399 21.6706C174.504 23.4618 165.557 28.6913 159.141 36.4361C152.725 44.1809 149.252 53.9431 149.333 63.9999C149.365 66.541 149.615 69.0747 150.08 71.5732L93.76 97.4932C87.7556 91.622 80.1519 87.6534 71.9019 86.0844C63.6519 84.5155 55.1224 85.4161 47.3819 88.6732C39.6414 91.9305 33.0343 97.3995 28.3883 104.395C23.7423 111.391 21.264 119.602 21.264 128C21.264 136.398 23.7423 144.609 28.3883 151.604C33.0343 158.6 39.6414 164.069 47.3819 167.327C55.1224 170.583 63.6519 171.485 71.9019 169.916C80.1519 168.347 87.7556 164.378 93.76 158.507L150.08 184.427C149.615 186.925 149.365 189.459 149.333 192C149.333 200.438 151.836 208.688 156.524 215.704C161.212 222.721 167.876 228.19 175.673 231.419C183.469 234.648 192.047 235.493 200.324 233.846C208.601 232.2 216.203 228.136 222.17 222.17C228.137 216.203 232.201 208.6 233.846 200.324C235.493 192.047 234.649 183.469 231.419 175.672C228.19 167.876 222.721 161.212 215.705 156.524C208.688 151.836 200.438 149.333 192 149.333ZM192 42.6665C196.22 42.6665 200.344 43.9177 203.852 46.2618C207.36 48.606 210.095 51.9378 211.71 55.836C213.324 59.7341 213.746 64.0235 212.924 68.1618C212.1 72.3 210.068 76.1013 207.085 79.0849C204.101 82.0683 200.3 84.1001 196.162 84.9233C192.024 85.7464 187.734 85.324 183.836 83.7093C179.938 82.0947 176.606 79.3603 174.262 75.852C171.918 72.3437 170.667 68.2193 170.667 63.9999C170.667 58.3419 172.914 52.9157 176.915 48.9149C180.916 44.9142 186.342 42.6665 192 42.6665ZM64 149.333C59.7806 149.333 55.6561 148.082 52.1479 145.738C48.6396 143.394 45.9052 140.062 44.2906 136.164C42.6759 132.266 42.2535 127.976 43.0766 123.838C43.8998 119.699 45.9316 115.899 48.915 112.915C51.8986 109.932 55.6998 107.9 59.8381 107.076C63.9764 106.253 68.2657 106.676 72.1639 108.29C76.0621 109.905 79.3939 112.64 81.7381 116.148C84.0822 119.656 85.3334 123.78 85.3334 128C85.3334 133.658 83.0857 139.084 79.0849 143.085C75.0842 147.086 69.658 149.333 64 149.333ZM192 213.333C187.78 213.333 183.657 212.082 180.148 209.738C176.64 207.394 173.905 204.062 172.29 200.164C170.676 196.266 170.254 191.977 171.076 187.838C171.9 183.699 173.932 179.899 176.915 176.915C179.899 173.932 183.7 171.9 187.838 171.076C191.977 170.254 196.266 170.676 200.164 172.29C204.062 173.905 207.394 176.64 209.738 180.148C212.082 183.655 213.333 187.78 213.333 192C213.333 197.658 211.086 203.084 207.085 207.085C203.084 211.086 197.658 213.333 192 213.333Z" fill="currentColor"/>`
|
||||
};
|
||||
|
|
|
@ -1,23 +1,25 @@
|
|||
---
|
||||
interface Props {
|
||||
import type { HTMLAttributes } from 'astro/types';
|
||||
|
||||
interface Props extends HTMLAttributes<'ul'> {
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
const { tags } = Astro.props;
|
||||
const { tags, ...attrs } = Astro.props;
|
||||
---
|
||||
|
||||
<ul class="tags">
|
||||
{tags.map((tag) => <li class="tag">{tag}</li>)}
|
||||
<ul {...attrs}>
|
||||
{tags.map((tag) => <li>{tag}</li>)}
|
||||
</ul>
|
||||
|
||||
<style>
|
||||
.tags {
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
gap: 0.3125rem;
|
||||
}
|
||||
.tag {
|
||||
li {
|
||||
padding: 0.25rem 0.5625rem;
|
||||
border-radius: var(--theme-radius-full);
|
||||
background-color: var(--theme-accent-dark);
|
||||
|
@ -25,7 +27,7 @@ const { tags } = Astro.props;
|
|||
font-size: var(--theme-text-sm);
|
||||
font-weight: 500;
|
||||
}
|
||||
.tag::before {
|
||||
li::before {
|
||||
content: '#';
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
---
|
||||
title: First post on my new site!
|
||||
description: 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.
|
||||
pubDate: 2023-01-01
|
||||
tags: [keyboards, thoughts]
|
||||
categories: [keyboards, thoughts]
|
||||
cover:
|
||||
src: ../../assets/stock-1.jpg
|
||||
alt: A laptop with a code editor open
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Second time lucky
|
||||
description: Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
|
||||
pubDate: 2023-01-02
|
||||
---
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
title: Three is a magic number
|
||||
description: Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
|
||||
pubDate: 2023-01-03
|
||||
cover:
|
||||
src: ../../assets/stock-2.jpg
|
|
@ -1,10 +1,9 @@
|
|||
import { rssSchema } from '@astrojs/rss';
|
||||
import { defineCollection, z } from 'astro:content';
|
||||
|
||||
const posts = defineCollection({
|
||||
const articles = defineCollection({
|
||||
schema: ({ image }) => rssSchema
|
||||
.extend({
|
||||
tags: z.array(z.string()).default([]),
|
||||
cover: z
|
||||
.object({
|
||||
src: image().refine(
|
||||
|
@ -14,9 +13,26 @@ const posts = defineCollection({
|
|||
alt: z.string(),
|
||||
})
|
||||
.optional(),
|
||||
type: z.enum(['article', 'note']).default('note'),
|
||||
type: z.literal('article').default('article')
|
||||
})
|
||||
.required({
|
||||
// requiring the description for articles, this will be shown as the short preview text on cards
|
||||
description: true
|
||||
})
|
||||
.strict(),
|
||||
});
|
||||
})
|
||||
|
||||
export const collections = { posts };
|
||||
const notes = defineCollection({
|
||||
schema: rssSchema
|
||||
.extend({
|
||||
type: z.literal('note').default('note')
|
||||
})
|
||||
.omit({
|
||||
// notes are short, self-contained content without unique titles or descriptions
|
||||
description: true,
|
||||
title: true
|
||||
})
|
||||
.strict()
|
||||
})
|
||||
|
||||
export const collections = { articles, notes };
|
||||
|
|
7
examples/social-feed/src/content/notes/post-10.md
Normal file
7
examples/social-feed/src/content/notes/post-10.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
pubDate: 2023-01-10
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Quis commodo odio aenean sed. Enim nulla aliquet porttitor lacus luctus accumsan tortor posuere ac.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Aenean vel elit scelerisque mauris. Sapien eget mi proin sed libero enim sed.
|
5
examples/social-feed/src/content/notes/post-4.md
Normal file
5
examples/social-feed/src/content/notes/post-4.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
pubDate: 2023-01-04
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Adipiscing elit ut aliquam purus sit amet luctus. Rhoncus aenean vel elit scelerisque mauris. Nulla pellentesque dignissim enim sit amet venenatis urna cursus.
|
7
examples/social-feed/src/content/notes/post-5.md
Normal file
7
examples/social-feed/src/content/notes/post-5.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
pubDate: 2023-01-05
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Pellentesque habitant morbi tristique senectus et netus et.
|
||||
|
||||
![../../assets/mechanical-keyboard.png](Mechanical keyboard with LED backlighting)
|
5
examples/social-feed/src/content/notes/post-6.md
Normal file
5
examples/social-feed/src/content/notes/post-6.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
pubDate: 2023-01-06
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Donec adipiscing tristique risus nec feugiat in fermentum posuere. Mauris pellentesque pulvinar pellentesque habitant morbi tristique senectus et netus. Cursus sit amet dictum sit amet justo donec.
|
7
examples/social-feed/src/content/notes/post-7.md
Normal file
7
examples/social-feed/src/content/notes/post-7.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
pubDate: 2023-01-07
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Volutpat maecenas volutpat blandit aliquam etiam erat velit.
|
||||
|
||||
Read more in [the docs](https://docs.astro.build)
|
7
examples/social-feed/src/content/notes/post-8.md
Normal file
7
examples/social-feed/src/content/notes/post-8.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
pubDate: 2023-01-08
|
||||
---
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Adipiscing bibendum est ultricies integer quis auctor.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Semper viverra nam libero justo laoreet sit amet cursus sit.
|
5
examples/social-feed/src/content/notes/post-9.md
Normal file
5
examples/social-feed/src/content/notes/post-9.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
pubDate: 2023-01-09
|
||||
---
|
||||
|
||||
ultrices neque ornare aenean euismod elementum
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Ten for Ten
|
||||
pubDate: 2023-01-10
|
||||
---
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Four is more
|
||||
pubDate: 2023-01-04
|
||||
---
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Take Five!
|
||||
pubDate: 2023-01-05
|
||||
---
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Then We Were Six
|
||||
pubDate: 2023-01-06
|
||||
---
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: 7th Heaven
|
||||
pubDate: 2023-01-07
|
||||
---
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Eight is Great!
|
||||
pubDate: 2023-01-08
|
||||
---
|
|
@ -1,4 +0,0 @@
|
|||
---
|
||||
title: Nine Lives
|
||||
pubDate: 2023-01-09
|
||||
---
|
|
@ -2,8 +2,12 @@ import { getCollection } from 'astro:content';
|
|||
|
||||
/** Get everything in your posts collection, sorted by date. */
|
||||
export async function getSortedPosts(order: 'asc' | 'desc' = 'desc') {
|
||||
const posts = await getCollection('posts');
|
||||
posts.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime());
|
||||
if (order === 'asc') posts.reverse();
|
||||
return posts;
|
||||
const posts = await Promise.all([
|
||||
getCollection('articles'),
|
||||
getCollection('notes'),
|
||||
])
|
||||
.then((collections) => collections.flat())
|
||||
posts.sort((a, b) => b.data.pubDate.getTime() - a.data.pubDate.getTime());
|
||||
if (order === 'asc') posts.reverse();
|
||||
return posts;
|
||||
}
|
||||
|
|
|
@ -4,11 +4,10 @@ import '../style/theme.css';
|
|||
import '../style/global.css';
|
||||
import '../style/utilities.css';
|
||||
import Header from '../components/Header.astro';
|
||||
import UserProfile from '../components/UserProfile.astro';
|
||||
import Footer from '../components/Footer.astro';
|
||||
|
||||
export interface Props {
|
||||
title?: string;
|
||||
title?: string;
|
||||
wrapperReverse?: boolean;
|
||||
}
|
||||
|
||||
|
@ -18,103 +17,89 @@ const { title = settings.name, wrapperReverse = false } = Astro.props;
|
|||
---
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{title}</title>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta name="generator" content={Astro.generator} />
|
||||
<title>{title}</title>
|
||||
|
||||
<!-- Canonical URL -->
|
||||
<link rel="canonical" href={canonicalURL} />
|
||||
<!-- Canonical URL -->
|
||||
<link rel="canonical" href={canonicalURL} />
|
||||
|
||||
<!-- RSS Feed Discovery -->
|
||||
<link
|
||||
rel="alternate"
|
||||
type="application/rss+xml"
|
||||
title="RSS"
|
||||
href="/rss.xml"
|
||||
/>
|
||||
<!-- RSS Feed Discovery -->
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS" href="/rss.xml" />
|
||||
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@500;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@500;700&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
|
||||
<script is:inline>
|
||||
// This code is inlined in the head to make dark mode instant & blocking.
|
||||
const getThemePreference = () => {
|
||||
if (
|
||||
typeof localStorage !== 'undefined' &&
|
||||
localStorage.getItem('theme')
|
||||
) {
|
||||
return localStorage.getItem('theme');
|
||||
}
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? 'dark'
|
||||
: 'light';
|
||||
};
|
||||
const isDark = getThemePreference() === 'dark';
|
||||
document.documentElement.classList[isDark ? 'add' : 'remove'](
|
||||
'theme-dark'
|
||||
);
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
// Watch the document element and persist user preference when it changes.
|
||||
const observer = new MutationObserver(() => {
|
||||
const isDark =
|
||||
document.documentElement.classList.contains('theme-dark');
|
||||
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
||||
});
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class'],
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="flex-col">
|
||||
<div class="flex-col pad">
|
||||
<Header />
|
||||
<div class="flex-col wrapper" class:list={["flex-col wrapper", { reverse: wrapperReverse }]}>
|
||||
<script is:inline>
|
||||
// This code is inlined in the head to make dark mode instant & blocking.
|
||||
const getThemePreference = () => {
|
||||
if (typeof localStorage !== 'undefined' && localStorage.getItem('theme')) {
|
||||
return localStorage.getItem('theme');
|
||||
}
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
};
|
||||
const isDark = getThemePreference() === 'dark';
|
||||
document.documentElement.classList[isDark ? 'add' : 'remove']('theme-dark');
|
||||
if (typeof localStorage !== 'undefined') {
|
||||
// Watch the document element and persist user preference when it changes.
|
||||
const observer = new MutationObserver(() => {
|
||||
const isDark = document.documentElement.classList.contains('theme-dark');
|
||||
localStorage.setItem('theme', isDark ? 'dark' : 'light');
|
||||
});
|
||||
observer.observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ['class'],
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body class="flex-col">
|
||||
<div class="flex-col pad">
|
||||
<Header />
|
||||
<div class="flex-col wrapper" class:list={['flex-col wrapper', { reverse: wrapperReverse }]}>
|
||||
<slot />
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</body>
|
||||
<Footer />
|
||||
</body>
|
||||
|
||||
<style>
|
||||
.flex-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
<style>
|
||||
.flex-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.pad {
|
||||
gap: 2.5rem;
|
||||
padding: 1.875rem 1.25rem;
|
||||
}
|
||||
.pad {
|
||||
gap: 2.5rem;
|
||||
padding: 1.875rem 1.25rem;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
gap: 2.5rem;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
max-width: 55rem;
|
||||
}
|
||||
|
||||
@media (min-width: 50em) {
|
||||
.wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 12rem 1fr;
|
||||
gap: 2.5rem;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
max-width: 55rem;
|
||||
}
|
||||
|
||||
.wrapper.reverse {
|
||||
grid-template-columns: 1fr 12rem;
|
||||
}
|
||||
}
|
||||
@media (min-width: 50em) {
|
||||
.wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 12rem 1fr;
|
||||
}
|
||||
|
||||
</style>
|
||||
.wrapper.reverse {
|
||||
grid-template-columns: 1fr 12rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</html>
|
||||
|
|
25
examples/social-feed/src/layouts/Note.astro
Normal file
25
examples/social-feed/src/layouts/Note.astro
Normal file
|
@ -0,0 +1,25 @@
|
|||
---
|
||||
import type { CollectionEntry } from 'astro:content';
|
||||
import Layout, { type Props as LayoutProps } from './Base.astro';
|
||||
import Card from '../components/Card.astro';
|
||||
|
||||
export interface Props extends LayoutProps {
|
||||
note: CollectionEntry<'notes'>;
|
||||
}
|
||||
|
||||
const { note, ...layout } = Astro.props;
|
||||
---
|
||||
|
||||
<Layout {...layout}>
|
||||
<div>
|
||||
<Card post={note} />
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
div {
|
||||
grid-column: 1 / -1;
|
||||
max-width: 50ch;
|
||||
margin-inline: auto;
|
||||
}
|
||||
</style>
|
|
@ -2,6 +2,7 @@
|
|||
import type { GetStaticPathsOptions, Page } from 'astro';
|
||||
import { Image } from 'astro:assets';
|
||||
import type { CollectionEntry } from 'astro:content';
|
||||
import Card from '../components/Card.astro';
|
||||
import Pagination from '../components/Pagination.astro';
|
||||
import TagList from '../components/TagList.astro';
|
||||
import { getSortedPosts } from '../helpers/getSortedPosts';
|
||||
|
@ -13,7 +14,7 @@ export async function getStaticPaths({ paginate }: GetStaticPathsOptions) {
|
|||
}
|
||||
|
||||
interface Props {
|
||||
page: Page<CollectionEntry<'posts'>>;
|
||||
page: Page<CollectionEntry<'articles'> | CollectionEntry<'notes'>>;
|
||||
}
|
||||
|
||||
const { page } = Astro.props;
|
||||
|
@ -21,45 +22,11 @@ const { page } = Astro.props;
|
|||
|
||||
<Layout>
|
||||
<div class="stack">
|
||||
<!-- <h1>Page {page.currentPage}</h1> -->
|
||||
<ol>
|
||||
{
|
||||
page.data.map((post) => (
|
||||
<li>
|
||||
<article class="card">
|
||||
<header>
|
||||
<div style="display: flex;gap:0.875rem;align-items:center;">
|
||||
<Image
|
||||
src={settings.avatar.src}
|
||||
width={120}
|
||||
class="u-avatar"
|
||||
alt={settings.avatar.alt}
|
||||
/>
|
||||
<div>
|
||||
<p style="font-family:var(--theme-font-brand);font-weight:700;font-size:var(--theme-text-lg)">
|
||||
{settings.name}
|
||||
</p>
|
||||
<p style="color:var(--theme-gray-200);font-size:var(--theme-text-sm);">
|
||||
{settings.username} •{' '}
|
||||
<time datetime={post.data.pubDate.toISOString()}>
|
||||
{post.data.pubDate.toLocaleDateString('en', {
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
year: 'numeric',
|
||||
})}
|
||||
</time>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{post.data.tags.length > 0 && <TagList tags={post.data.tags} />}
|
||||
<h2>
|
||||
<a href={'/post/' + post.slug}>{post.data.title}</a>
|
||||
</h2>
|
||||
</header>
|
||||
{post.render().then(({ Content }) => (
|
||||
<Content />
|
||||
))}
|
||||
</article>
|
||||
<Card {post} />
|
||||
</li>
|
||||
))
|
||||
}
|
||||
|
@ -81,19 +48,4 @@ const { page } = Astro.props;
|
|||
flex-direction: column;
|
||||
gap: 1.25rem;
|
||||
}
|
||||
.card {
|
||||
padding: 1.25rem 1.25rem 1.5rem;
|
||||
background-color: var(--theme-bg-accent);
|
||||
border-radius: var(--theme-radius-xl);
|
||||
box-shadow: var(--theme-shadow-md);
|
||||
}
|
||||
header > * + * {
|
||||
margin-top: 0.625rem;
|
||||
}
|
||||
.u-avatar {
|
||||
width: 3.75rem;
|
||||
height: 3.75rem;
|
||||
border-radius: var(--theme-radius-full);
|
||||
background-color: var(--theme-shade-subtle);
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
---
|
||||
import type { CollectionEntry } from 'astro:content';
|
||||
import { getSortedPosts } from '../../helpers/getSortedPosts';
|
||||
import Layout from '../../layouts/Article.astro';
|
||||
import ArticleLayout from '../../layouts/Article.astro';
|
||||
import NoteLayout from '../../layouts/Note.astro';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await getSortedPosts();
|
||||
|
@ -18,9 +19,12 @@ interface Props {
|
|||
}
|
||||
|
||||
const { post, prev, next } = Astro.props;
|
||||
const { Content } = await post.render();
|
||||
---
|
||||
|
||||
<Layout title={post.data.title} article={post} {next} {prev}>
|
||||
<Content />
|
||||
</Layout>
|
||||
{
|
||||
post.data.type === 'article' ? (
|
||||
<ArticleLayout title={post.data.title} article={post} {next} {prev} />
|
||||
) : (
|
||||
<NoteLayout note={post} {next} {prev} />
|
||||
)
|
||||
}
|
||||
|
|
|
@ -6,18 +6,18 @@ import settings from '../settings';
|
|||
const { title, description } = settings.rss;
|
||||
|
||||
export async function get(context: APIContext) {
|
||||
const posts = await getSortedPosts();
|
||||
return rss({
|
||||
// `<title>` field in output xml
|
||||
title,
|
||||
// `<description>` field in output xml
|
||||
description,
|
||||
// Pull in your project "site" from the endpoint context
|
||||
// https://docs.astro.build/en/reference/api-reference/#contextsite
|
||||
site: context.site!.href,
|
||||
// Array of `<item>`s in output xml
|
||||
// See "Generating items" section for examples using content collections and glob imports
|
||||
items: posts.map(({ data, slug }) => ({ ...data, link: `/post/${slug}` })),
|
||||
stylesheet: '/rss/styles.xsl',
|
||||
});
|
||||
const posts = await getSortedPosts();
|
||||
return rss({
|
||||
// `<title>` field in output xml
|
||||
title,
|
||||
// `<description>` field in output xml
|
||||
description,
|
||||
// Pull in your project "site" from the endpoint context
|
||||
// https://docs.astro.build/en/reference/api-reference/#contextsite
|
||||
site: context.site!.href,
|
||||
// Array of `<item>`s in output xml
|
||||
// See "Generating items" section for examples using content collections and glob imports
|
||||
items: posts.map(({ data, slug }) => ({ ...data, link: `/post/${slug}` })),
|
||||
stylesheet: '/rss/styles.xsl',
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue