fix: some image loading
Some checks failed
Deploy to SFTP Server / build (push) Failing after 1m55s

This commit is contained in:
Max Richter
2025-10-24 13:43:34 +02:00
parent 231bdb09a3
commit 6230126d38
11 changed files with 120 additions and 92 deletions

View File

@@ -15,7 +15,7 @@ interface Props {
} }
const { const {
data: { title, cover, icon, date, reviewRating }, data: { title, cover, icon, date, rating },
collection, collection,
body, body,
id, id,
@@ -28,11 +28,11 @@ 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={`max-h-250px 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 || body || reviewRating !== undefined) && ( (date || body || rating !== undefined) && (
<Card.Metadata date={date} readDuration={readDuration(body)} rating={reviewRating} /> <Card.Metadata date={date} readDuration={readDuration(body)} rating={rating} />
) )
} }
<Card.Title classes="text-4xl flex items-center gap-2"> <Card.Title classes="text-4xl flex items-center gap-2">

View File

@@ -15,12 +15,12 @@ interface Props {
} }
async function checkImage(image: ImageMetadata) { async function checkImage(image: ImageMetadata) {
const src = image.src; const src = typeof image === "string" ? image : image.src;
if (!src) return false; if (!src) return false;
try { try {
if (src.startsWith("/@fs") || src.startsWith("/_astro")) return true; if (src.startsWith("/@fs") || src.startsWith("/_astro")) return true;
const res = await inferRemoteSize(src); const res = await inferRemoteSize(src);
if (res.format) { console.log(res)
image.format = res.format; image.format = res.format;
return true; return true;
} else { } else {

View File

@@ -1,6 +1,7 @@
<script lang="ts"> <script lang="ts">
export let date: string | Date; export let date: string | Date;
export let readDuration: string | undefined; export let readDuration: number | undefined;
export let rating: string | number | 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;
@@ -10,6 +11,13 @@
return isNaN(v.getTime()) ? "" : v.toISOString(); return isNaN(v.getTime()) ? "" : v.toISOString();
}; };
function formatRating(rating: string | number) {
if (typeof rating === "number") {
return "⭐".repeat(rating);
}
return rating;
}
const formatDate = (d: string | Date) => { const formatDate = (d: string | Date) => {
try { try {
return new Intl.DateTimeFormat("de-DE", { return new Intl.DateTimeFormat("de-DE", {
@@ -29,7 +37,13 @@
>{formatDate(date)}</time> >{formatDate(date)}</time>
{/if} {/if}
{#if readDuration} {#if typeof readDuration === "number"}
{#if readDuration > 1}
<div class="text-sm text-neutral">{readDuration} mins read</div> <div class="text-sm text-neutral">{readDuration} mins read</div>
{/if} {/if}
{/if}
{#if rating}
<div class="text-sm text-neutral">{formatRating(rating)}</div>
{/if}
</div> </div>

View File

@@ -2,7 +2,6 @@
import { Code } from 'astro:components'; import { Code } from 'astro:components';
import Recipe from "./Recipe.astro"; import Recipe from "./Recipe.astro";
import Article from "./Article.astro"; import Article from "./Article.astro";
import IconCode from "~icons/tabler/Code";
import Review from "./Review.astro"; import Review from "./Review.astro";
const { resource } = Astro.props; const { resource } = Astro.props;
const type = resource?.content?._type ?? "unknown"; const type = resource?.content?._type ?? "unknown";
@@ -20,15 +19,7 @@ const type = resource?.content?._type ?? "unknown";
) )
} }
<details> <details >
<summary><IconCode class="inline"/></summary> <summary class="flex"><span class="i-tabler-code w-6 h-6 inline"/></summary>
<Code code={JSON.stringify(resource, null, 2)} lang="json" theme="dark-plus" /> <Code code={JSON.stringify(resource, null, 2)} lang="json" theme="dark-plus" />
</details> </details>
<style>
summary::marker {
display: none;
}
</style>

View File

@@ -21,7 +21,7 @@ const { resource } = Astro.props;
/> />
) )
} }
<h1 class="text-4xl">{resource?.content?.itemReviewed?.name || "Unknown Name"}</h1> <h1 class="text-4xl mb-4">{resource?.content?.itemReviewed?.name || "Unknown Name"}</h1>
{ resource?.content?.reviewRating?.ratingValue !== undefined && <div>{resource?.content?.reviewRating?.ratingValue}</div>} { resource?.content?.reviewRating?.ratingValue !== undefined && <div>{resource?.content?.reviewRating?.ratingValue}</div>}
<div set:html={markdownToHtml(resource?.content?.reviewBody)} /> <div set:html={markdownToHtml(resource?.content?.reviewBody)} />

View File

@@ -1,11 +1,13 @@
import { glob } from 'astro/loaders'; import { glob } from "astro/loaders";
import { defineCollection, z, type ImageFunction } from 'astro:content'; import { defineCollection, type ImageFunction, z } from "astro:content";
const defaultSchema = ({ image }: { image: ImageFunction }) => z.object({ const defaultSchema = ({ image }: { image: ImageFunction }) =>
z.object({
title: z.string(), title: z.string(),
date: z.date(), date: z.date(),
cover: image().optional(), cover: image().optional(),
links: z.array(z.array(z.string())).optional(), links: z.array(z.array(z.string())).optional(),
rating: z.union([z.string(), z.number()]).optional(),
coverAlt: z.string().optional(), coverAlt: z.string().optional(),
toc: z.boolean().optional(), toc: z.boolean().optional(),
description: z.string().optional(), description: z.string().optional(),
@@ -13,21 +15,26 @@ const defaultSchema = ({ image }: { image: ImageFunction }) => z.object({
draft: z.boolean().optional(), draft: z.boolean().optional(),
featured: z.boolean().optional(), featured: z.boolean().optional(),
tags: z.array(z.string()).optional(), tags: z.array(z.string()).optional(),
_layout: z.enum(['normal', 'transparent']).optional(), _layout: z.enum(["normal", "transparent"]).optional(),
}) });
export const collections = { export const collections = {
'blog': defineCollection({ "blog": defineCollection({
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/blog" }), loader: glob({ pattern: "**/[^_]*.{md,mdx}", base: "./src/content/blog" }),
schema: defaultSchema, schema: defaultSchema,
}), }),
"projects": defineCollection({ "projects": defineCollection({
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/projects" }), loader: glob({
pattern: "**/[^_]*.{md,mdx}",
base: "./src/content/projects",
}),
schema: defaultSchema, schema: defaultSchema,
}), }),
"photos": defineCollection({ "photos": defineCollection({
loader: glob({ pattern: '**/[^_]*.{md,mdx}', base: "./src/content/photos" }), loader: glob({
pattern: "**/[^_]*.{md,mdx}",
base: "./src/content/photos",
}),
schema: defaultSchema, schema: defaultSchema,
}) }),
}; };

View File

@@ -4,6 +4,7 @@ const parser = new MarkdownIt();
export function readDuration(markdown: string): number | undefined { export function readDuration(markdown: string): number | undefined {
if (!markdown) return; if (!markdown) return;
const words = markdown.split(" ")?.filter(Boolean)?.length; const words = markdown.split(" ")?.filter(Boolean)?.length;
if (!words) return;
return words && Math.round(words / 250); return words && Math.round(words / 250);
} }

View File

@@ -1,10 +1,9 @@
export const languages = { export const languages = {
en: 'English', en: "English",
de: 'Deutsch', de: "Deutsch",
}; };
export const defaultLang = 'de'; export const defaultLang = "de";
export const ui = { export const ui = {
en: { en: {
@@ -15,15 +14,24 @@ export const ui = {
"read-more": "Read More", "read-more": "Read More",
"latest-posts": "Latest Posts", "latest-posts": "Latest Posts",
"latest-projects": "Latest Projects", "latest-projects": "Latest Projects",
'home.title': 'Hi, Im Max :)', "home.title": "Hi, Im Max :)",
'website-source': 'source', "website-source": "source",
'home.subtitle': 'Trained Media Designer, Blender Nerd, Developer and Hardware Tinkerer.', "home.subtitle":
'nav.blog': 'Blog', "Trained Media Designer, Blender Nerd, Developer and Hardware Tinkerer.",
'nav.projects': 'Projects', "nav.blog": "Blog",
'nav.resources': 'Resources', "nav.projects": "Projects",
'nav.photos': 'Photos', "nav.resources": "Resources",
'toc.title': 'Table of Contents', "nav.photos": "Photos",
"toc.title": "Table of Contents",
"resume": "Resume", "resume": "Resume",
"articles": "📰 Articles",
"articles.description": "Random articles that impressed me while reading.",
"movies": "🎥 Movies",
"movies.description": "Watched, not watched, found good, found bad.",
"recipes": "🍲 Recipes",
"recipes.description": "Things I have cooked or want to cook.",
"series": "📺 Series",
"series.description": "Series I have watched or plan to watch.",
}, },
de: { de: {
"en": "English", "en": "English",
@@ -33,15 +41,26 @@ export const ui = {
"latest-posts": "Neueste Posts", "latest-posts": "Neueste Posts",
"latest-projects": "Neueste Projekte", "latest-projects": "Neueste Projekte",
"read-more": "weiterlesen", "read-more": "weiterlesen",
'home.title': 'Hi, ich bin Max :)', "home.title": "Hi, ich bin Max :)",
'website-source': 'Sourcecode', "website-source": "Sourcecode",
'home.subtitle': 'Ausgebildeter Mediengestalter, Blender Nerd, Entwickler und Hardware Bastler.', "home.subtitle":
'nav.blog': 'Blog', "Ausgebildeter Mediengestalter, Blender Nerd, Entwickler und Hardware Bastler.",
'nav.projects': 'Projekte', "nav.blog": "Blog",
'nav.resources': 'Resources', "nav.projects": "Projekte",
'nav.photos': 'Fotos', "nav.resources": "Resourcen",
"nav.photos": "Fotos",
"resume": "Lebenslauf", "resume": "Lebenslauf",
'toc.title': 'Inhaltsverzeichnis', "toc.title": "Inhaltsverzeichnis",
"articles": "📰 Artikel",
"articles.description":
"Random Artikel die mich beim lesen beindruckt haben",
"movies": "🎥 Filme",
"movies.description":
"Gesehen, nicht gesehen, für gut befunden, für schlecht befunden.",
"recipes": "🍲 Rezepte",
"recipes.description": "Sachen die ich gekocht habe oder kochen will",
"series": "📺 Serien",
"series.description": "Serien die ich angeguckt habe",
}, },
} as const; } as const;

View File

@@ -3,9 +3,12 @@ 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 {useTranslations} from "@i18n/utils";
const { resourceType } = Astro.params; const { resourceType } = Astro.params;
const t = useTranslations(Astro.url);
async function safeGetResource() { async function safeGetResource() {
try { try {
return await memorium.listResource(resourceType); return await memorium.listResource(resourceType);
@@ -21,7 +24,6 @@ export async function getStaticPaths() {
return { return {
params: { params: {
resourceType: type.id, resourceType: type.id,
resourceName: type.data.title,
}, },
}; };
}); });
@@ -32,7 +34,10 @@ function isValidResource(res) {
} }
--- ---
<Layout title="Max Richter"> <Layout title="Max Richter">
<h1 class="text-4xl mb-4">{t(resourceType)}</h1>
<p>{t(`${resourceType as "articles"}.description`)}</p>
{ {
resources.content resources.content
.filter((res) => isValidResource(res)) .filter((res) => isValidResource(res))
@@ -44,7 +49,7 @@ function isValidResource(res) {
data: { data: {
title: resource?.content?.name ?? resource?.content?.headline ?? resource.content?.itemReviewed?.name, title: resource?.content?.name ?? resource?.content?.headline ?? resource.content?.itemReviewed?.name,
date: resource?.content?.datePublished, date: resource?.content?.datePublished,
rating: resource?.content?.reviewRating, rating: resource?.content?.reviewRating?.ratingValue,
cover: { cover: {
src: memorium.getImageUrl(resource.content.image), src: memorium.getImageUrl(resource.content.image),
}, },

View File

@@ -2,8 +2,11 @@
import Layout from "@layouts/Layout.astro"; import Layout from "@layouts/Layout.astro";
import HeroCard from "@components/HeroCard.astro"; import HeroCard from "@components/HeroCard.astro";
import { resources } from "./resources.ts"; import { resources } from "./resources.ts";
import {useTranslations} from "@i18n/utils";
const t = useTranslations(Astro.url);
--- ---
<Layout title="Max Richter"> <Layout title="Max Richter">
{resources.map((resource) => <HeroCard post={resource} />)} {resources.map((resource) => <HeroCard post={{...resource, body: t(`${resource.id}.description`), data: {cover:{src:resource.cover}, title: t(resource.id)}}} />)}
</Layout> </Layout>

View File

@@ -1,13 +1,9 @@
const collection = "resources"; const collection = "resources";
export type ResourceType = { export type ResourceType = {
id: string; id: "articles" | "recipes" | "movies" | "series";
collection: string; collection: string;
body?: string; cover: string;
data: {
title: string;
icon: string;
};
}; };
// const wiki = { // const wiki = {
@@ -23,37 +19,29 @@ export type ResourceType = {
const articles = { const articles = {
id: "articles", id: "articles",
collection, collection,
data: { cover:
title: "Articles", "https://i0.wp.com/old-dressaday-images.s3-website-us-west-2.amazonaws.com/6a0133ed1b1479970b0134809d9f8b970c.jpg",
icon: "📰", } as const;
},
};
const recipes = { const recipes = {
id: "recipes", id: "recipes",
collection, collection,
data: { cover:
title: "Recipes", "https://marka.max-richter.dev/resources/recipes/images/auberginen_reispfanne.webp",
icon: "🍲", } as const;
},
};
const movies = { const movies = {
id: "movies", id: "movies",
collection, collection,
data: { cover:
title: "Movies", "https://marka.max-richter.dev/resources/movies/images/2001_a_space_odyssey_cover.jpg",
icon: "🎥", } as const;
},
};
const series = { const series = {
id: "series", id: "series",
collection, collection,
data: { cover:
title: "Series", "https://marka.max-richter.dev/resources/series/images/arcane_cover.jpg",
icon: "📺", } as const;
},
};
export const resources: ResourceType[] = [recipes, articles, movies, series]; export const resources: ResourceType[] = [recipes, articles, movies, series];