This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import markdownToText from "@helpers/markdownToText";
|
import { markdownToText,readDuration } from "@helpers/markdown";
|
||||||
import { Card } from "./card";
|
import { Card } from "./card";
|
||||||
import { useTranslatedPath, useTranslations } from "@i18n/utils";
|
import { useTranslatedPath, useTranslations } from "@i18n/utils";
|
||||||
import Image from "@components/Image.astro";
|
import Image from "@components/Image.astro";
|
||||||
@@ -30,7 +30,7 @@ const link = translatePath(`/${collection}/${id.split("/")[0]}`);
|
|||||||
<Card
|
<Card
|
||||||
classes={`grid gradient border-1 border-neutral overflow-hidden ${cover ? "grid-rows-[200px_1fr] xs:grid-rows-none xs:grid-cols-[1fr_200px]" : ""}`}>
|
classes={`grid gradient border-1 border-neutral overflow-hidden ${cover ? "grid-rows-[200px_1fr] xs:grid-rows-none xs:grid-cols-[1fr_200px]" : ""}`}>
|
||||||
<Card.Content classes="px-8 py-7 order-last xs:order-first">
|
<Card.Content classes="px-8 py-7 order-last xs:order-first">
|
||||||
{date && (<Card.Metadata date={date} />)}
|
{(date || body)&& <Card.Metadata date={date} readDuration={readDuration(body)} />}
|
||||||
<Card.Title classes="text-4xl flex items-center gap-2">
|
<Card.Title classes="text-4xl flex items-center gap-2">
|
||||||
{
|
{
|
||||||
icon &&
|
icon &&
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
import markdownToText from "@helpers/markdownToText";
|
import { markdownToText } from "@helpers/markdown";
|
||||||
import { useTranslatedPath } from "@i18n/utils";
|
import { useTranslatedPath } from "@i18n/utils";
|
||||||
import type { InferEntrySchema } from "astro:content";
|
import type { InferEntrySchema } from "astro:content";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
export let date: string | Date;
|
export let date: string | Date;
|
||||||
|
export let readDuration: string | undefined;
|
||||||
|
|
||||||
const toDate = (d: string | Date) =>
|
const toDate = (d: string | Date) =>
|
||||||
typeof d === "string" ? new Date(d) : d;
|
typeof d === "string" ? new Date(d) : d;
|
||||||
@@ -22,4 +23,8 @@
|
|||||||
<time class="text-sm text-neutral" datetime={iso(date)}
|
<time class="text-sm text-neutral" datetime={iso(date)}
|
||||||
>{formatDate(date)}</time>
|
>{formatDate(date)}</time>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if readDuration}
|
||||||
|
<div class="text-sm text-neutral">{readDuration} mins read</div>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
25
src/components/resources/Article.astro
Normal file
25
src/components/resources/Article.astro
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
import * as memorium from "@helpers/memorium";
|
||||||
|
import { markdownToHtml, markdownToText } from "@helpers/markdown";
|
||||||
|
import Image from "@components/Image.astro";
|
||||||
|
|
||||||
|
const { resource } = Astro.props;
|
||||||
|
const ingredients = resource?.content?.recipeIngredient || [];
|
||||||
|
const instructions = resource?.content?.recipeInstructions || [];
|
||||||
|
---
|
||||||
|
|
||||||
|
<h1 class="text-4xl">{resource?.content?.headline}</h1>
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
resource?.content?.image && (
|
||||||
|
<Image
|
||||||
|
hash
|
||||||
|
src={{ src: memorium.getImageUrl(resource.content.image) }}
|
||||||
|
alt="Cover for {resource?.content?.name}"
|
||||||
|
class="rounded-2xl overflow-hidden"
|
||||||
|
pictureClass="rounded-2xl"
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div set:html={markdownToHtml(resource?.content?.articleBody)} />
|
||||||
13
src/components/resources/Display.astro
Normal file
13
src/components/resources/Display.astro
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
import Recipe from "./Recipe.astro";
|
||||||
|
import Article from "./Article.astro";
|
||||||
|
const { resource } = Astro.props;
|
||||||
|
const type = resource?.content?._type ?? "unknown";
|
||||||
|
---
|
||||||
|
|
||||||
|
{type === "Recipe" && <Recipe resource={resource} />}
|
||||||
|
{type === "Article" && <Article resource={resource} />}
|
||||||
|
|
||||||
|
{type === "unknown" && <div>
|
||||||
|
<h3>Unknown resource type</h3>
|
||||||
|
</div>}
|
||||||
32
src/components/resources/Recipe.astro
Normal file
32
src/components/resources/Recipe.astro
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
import * as memorium from "@helpers/memorium";
|
||||||
|
import { markdownToHtml, markdownToText } from "@helpers/markdown";
|
||||||
|
import Image from "@components/Image.astro";
|
||||||
|
|
||||||
|
const { resource } = Astro.props
|
||||||
|
const ingredients = resource?.content?.recipeIngredient || [];
|
||||||
|
const instructions = resource?.content?.recipeInstructions || [];
|
||||||
|
---
|
||||||
|
|
||||||
|
<h1 class="text-4xl">{resource?.content?.name}</h1>
|
||||||
|
<div>
|
||||||
|
{resource?.content?.image && <Image hash src={{src: memorium.getImageUrl(resource.content.image)}} alt="Cover for {resource?.content?.name}" class="rounded-2xl overflow-hidden" pictureClass="rounded-2xl" />}
|
||||||
|
</div>
|
||||||
|
<p>{resource?.content?.description}</p>
|
||||||
|
<h2 class="text-2xl">Ingredients</h2>
|
||||||
|
<ul>
|
||||||
|
{
|
||||||
|
ingredients.map((ingredient) => (
|
||||||
|
<li set:html={markdownToHtml(ingredient)}/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 class="text-2xl">Steps</h2>
|
||||||
|
<ol>
|
||||||
|
{
|
||||||
|
instructions.map((ingredient) => (
|
||||||
|
<li set:html={markdownToHtml(ingredient)}/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ol>
|
||||||
@@ -1,8 +1,26 @@
|
|||||||
import MarkdownIt from "markdown-it";
|
import MarkdownIt from "markdown-it";
|
||||||
const parser = new MarkdownIt();
|
const parser = new MarkdownIt();
|
||||||
|
|
||||||
export default function markdownToText(markdown: string): string {
|
export function readDuration(markdown: string): number | undefined {
|
||||||
if(!markdown) return ''
|
const words = markdown.split(" ")?.filter(Boolean)?.length;
|
||||||
|
return words && Math.round(words / 250);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markdownToHtml(markdown: string): string {
|
||||||
|
const md = new MarkdownIt({
|
||||||
|
html: false, // set to true only if you trust the source
|
||||||
|
linkify: true,
|
||||||
|
typographer: true,
|
||||||
|
breaks: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Convert -> sanitize
|
||||||
|
const unsafeHtml = md.render(markdown);
|
||||||
|
return unsafeHtml;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function markdownToText(markdown: string): string {
|
||||||
|
if (!markdown) return "";
|
||||||
return parser
|
return parser
|
||||||
.render(markdown)
|
.render(markdown)
|
||||||
.split("\n")
|
.split("\n")
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
---
|
---
|
||||||
import Layout from "@layouts/Layout.astro";
|
import Layout from "@layouts/Layout.astro";
|
||||||
import { useTranslatedPath } from "@i18n/utils";
|
import { useTranslatedPath } from "@i18n/utils";
|
||||||
|
import ResourceDisplay from "@components/resources/Display.astro";
|
||||||
import * as memorium from "@helpers/memorium";
|
import * as memorium from "@helpers/memorium";
|
||||||
import { resources as resourceTypes } from "../resources.ts";
|
import { resources as resourceTypes } from "../resources.ts";
|
||||||
import markdownToText from "@helpers/markdownToText";
|
import { markdownToText } from "@helpers/markdown";
|
||||||
import Image from "@components/Image.astro";
|
import Image from "@components/Image.astro";
|
||||||
|
|
||||||
const { resourceType, resourceName } = Astro?.params;
|
const { resourceType, resourceName } = Astro?.params;
|
||||||
@@ -35,9 +36,6 @@ export async function getStaticPaths() {
|
|||||||
const resource = await memorium.listResource(
|
const resource = await memorium.listResource(
|
||||||
`${resourceType}/${resourceName}.md`,
|
`${resourceType}/${resourceName}.md`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const ingredients = resource?.content?.recipeIngredient || [];
|
|
||||||
const instructions = resource?.content?.recipeInstructions || [];
|
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title="Max Richter">
|
<Layout title="Max Richter">
|
||||||
@@ -49,7 +47,7 @@ const instructions = resource?.content?.recipeInstructions || [];
|
|||||||
</a>
|
</a>
|
||||||
<div class="date opacity-50">
|
<div class="date opacity-50">
|
||||||
{
|
{
|
||||||
resource?.content.date?.toLocaleString("en-US", {
|
resource?.content.datePublished?.toLocaleString("en-US", {
|
||||||
month: "long",
|
month: "long",
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
year: "numeric",
|
year: "numeric",
|
||||||
@@ -57,28 +55,5 @@ const instructions = resource?.content?.recipeInstructions || [];
|
|||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<ResourceDisplay resource={resource} />
|
||||||
<!-- <pre>{JSON.stringify(resource, null, 2)}</pre> -->
|
|
||||||
<h1 class="text-4xl">{resource?.content?.name}</h1>
|
|
||||||
<div>
|
|
||||||
{resource?.content?.image && <Image hash src={{src: memorium.getImageUrl(resource.content.image)}} alt="Cover for {resource?.content?.name}" class="rounded-2xl overflow-hidden" pictureClass="rounded-2xl" />}
|
|
||||||
</div>
|
|
||||||
<p>{resource?.content?.description}</p>
|
|
||||||
<h2 class="text-2xl">Ingredients</h2>
|
|
||||||
<ul>
|
|
||||||
{
|
|
||||||
ingredients.map((ingredient) => (
|
|
||||||
<li>{markdownToText(ingredient)}</li>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<h2 class="text-2xl">Steps</h2>
|
|
||||||
<ol>
|
|
||||||
{
|
|
||||||
instructions.map((ingredient) => (
|
|
||||||
<li>{markdownToText(ingredient)}</li>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</ol>
|
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Layout from "@layouts/Layout.astro";
|
|||||||
import HeroCard from "@components/HeroCard.astro";
|
import HeroCard from "@components/HeroCard.astro";
|
||||||
import * as memorium from "@helpers/memorium";
|
import * as memorium from "@helpers/memorium";
|
||||||
import { resources as resourceTypes } from "../resources.ts";
|
import { resources as resourceTypes } from "../resources.ts";
|
||||||
import markdownToText from "@helpers/markdownToText";
|
import { markdownToText } from "@helpers/markdown";
|
||||||
|
|
||||||
const { resourceType } = Astro.params;
|
const { resourceType } = Astro.params;
|
||||||
|
|
||||||
@@ -28,19 +28,25 @@ export async function getStaticPaths() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isValidResource(res) {
|
||||||
|
if (res?.content?.name) return true;
|
||||||
|
if (res?.content?.headline) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
<Layout title="Max Richter">
|
<Layout title="Max Richter">
|
||||||
{
|
{
|
||||||
resources.content
|
resources.content
|
||||||
.filter((res) => res && res?.content && res?.content?.name)
|
.filter((res) => res && res?.content)
|
||||||
.map((resource: any) => (
|
.map((resource: any) => (
|
||||||
<HeroCard
|
<HeroCard
|
||||||
post={{
|
post={{
|
||||||
collection: "resources/" + resourceType,
|
collection: "resources/" + resourceType,
|
||||||
id: resource.name.replace(/\.md$/, ""),
|
id: resource.name.replace(/\.md$/, ""),
|
||||||
data: {
|
data: {
|
||||||
title: resource.content.name,
|
title: resource.content.name ?? resource.content.headline,
|
||||||
|
date: resource?.content?.datePublished,
|
||||||
cover: {
|
cover: {
|
||||||
src: memorium.getImageUrl(resource.content.image),
|
src: memorium.getImageUrl(resource.content.image),
|
||||||
},
|
},
|
||||||
@@ -50,3 +56,4 @@ export async function getStaticPaths() {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
</Layout>
|
</Layout>
|
||||||
|
</Layout>
|
||||||
|
|||||||
@@ -19,15 +19,14 @@ type Resource = {
|
|||||||
// },
|
// },
|
||||||
// };
|
// };
|
||||||
|
|
||||||
// const articles = {
|
const articles = {
|
||||||
// id: "Articles",
|
id: "articles",
|
||||||
// collection,
|
collection,
|
||||||
// body: "Articles saved",
|
data: {
|
||||||
// data: {
|
title: "Articles",
|
||||||
// title: "Articles",
|
icon: "📰",
|
||||||
// icon: "📰",
|
},
|
||||||
// },
|
};
|
||||||
// };
|
|
||||||
|
|
||||||
const recipes = {
|
const recipes = {
|
||||||
id: "recipes",
|
id: "recipes",
|
||||||
@@ -58,4 +57,4 @@ const recipes = {
|
|||||||
// },
|
// },
|
||||||
// };
|
// };
|
||||||
|
|
||||||
export const resources: Resource[] = [recipes];
|
export const resources: Resource[] = [recipes, articles];
|
||||||
|
|||||||
Reference in New Issue
Block a user