21 Commits

Author SHA1 Message Date
itsfinniii
b13935c7c5 Prepare a get files function for later development 2026-04-27 22:24:20 +02:00
itsfinniii
0ef04d5f3e Add minify of website to Astro config 2026-04-27 22:15:19 +02:00
itsfinniii
d9430335d5 Clean up the project a bit
Fix imports, remove unnecessary imports, and replace all single apostrophe to double apostrophes
2026-04-27 21:15:32 +02:00
itsfinniii
9a92b1939c Fix the Photo Layout thumbnail 2026-04-27 20:59:43 +02:00
itsfinniii
b304cc400c Fix thumbnail size for the album 2026-04-27 17:24:08 +02:00
itsfinniii
79e343dbdc Add some comments 2026-04-27 17:11:11 +02:00
itsfinniii
7116aa2348 Fix category index thumbnail 2026-04-27 16:55:43 +02:00
itsfinniii
c112a69f0e Fix category index thumbnail 2026-04-27 16:40:53 +02:00
itsfinniii
4a9d0fb273 Fix thumbnail for Project Posts 2026-04-27 16:29:41 +02:00
itsfinniii
29c47ab72d Fix keywords for BlogLayout 2026-04-27 16:11:37 +02:00
itsfinniii
27b8dc4118 Fix thumbnail for blog posts 2026-04-27 16:08:14 +02:00
itsfinniii
45a2627ec6 Fix thumbnail for web pages 2026-04-27 15:42:38 +02:00
itsfinniii
eba518ccc2 Fix problem with build 2026-04-27 15:34:12 +02:00
itsfinniii
3cfe6697a9 Hopefully fix some problems 2026-04-27 15:28:08 +02:00
itsfinniii
07716dae17 Revert back to Vite 6 2026-04-27 14:52:26 +02:00
itsfinniii
db65ac52a3 Finish the photo page 2026-04-27 14:37:13 +02:00
itsfinniii
fdc8a0aae6 Make first test for photos 2026-04-26 22:26:51 +02:00
itsfinniii
092d2a4458 Add loading spinner to gallery 2026-04-26 16:25:21 +02:00
itsfinniii
760281f7a4 Start with Changelogs.md file 2026-04-26 16:16:43 +02:00
itsfinniii
157cb9389c Increase spacing for justified layout 2026-04-26 16:13:20 +02:00
itsfinniii
be02d749dd Add pagination to album page 2026-04-26 16:12:57 +02:00
59 changed files with 3271 additions and 4913 deletions

View File

@@ -1,22 +1,45 @@
// @ts-check
import { defineConfig } from 'astro/config';
import preact from '@astrojs/preact';
import tailwindcss from '@tailwindcss/vite';
import graphql from '@rollup/plugin-graphql';
import { defineConfig } from "astro/config";
import preact from "@astrojs/preact";
import tailwindcss from "@tailwindcss/vite";
import graphql from "@rollup/plugin-graphql";
import playformCompress from "@playform/compress";
import { getFiles } from "./src/build/files.ts";
// https://astro.build/config
export default defineConfig({
integrations: [preact()],
integrations: [preact(), playformCompress({
CSS: true,
Image: false,
JSON: true,
JavaScript: false,
HTML: true
}), {
name: "download-files",
hooks: {
"astro:build:start": async ({ }) => {
await getFiles();
}
}
}],
output: "static",
prefetch: true,
build: {
assets: "assets"
},
image: {
domains: ['development.directus.itsfinniii.com']
domains: ["development.directus.itsfinniii.com"]
},
vite: {
plugins: [tailwindcss(), graphql()],
plugins: [graphql(), tailwindcss()],
resolve: {
alias: {
react: "preact/compat",
"react-dom": "preact/compat",
},
},
optimizeDeps: {
exclude: ["@immich/justified-layout-wasm"]
}
}
});

20
astro/changelogs.md Normal file
View File

@@ -0,0 +1,20 @@
# 1.0.0.0 - Release
**Release date: **
- Add web pages with the following components:
- Contact
- Equipment Table
- Frequently Asked Questions
- Hero
- Last Albums
- Last Blogs
- Last Projects
- Reviews
- Text with Side Image
- Upcoming Events
- Wall of Text
- Add blogs
- Add projects
- Add photo categories, photo albums and photos
- Add sitemaps
- Add robots.txt

7350
astro/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -12,6 +12,7 @@
"@astrojs/preact": "^5.1.2",
"@directus/sdk": "^21.2.0",
"@immich/justified-layout-wasm": "^0.4.3",
"@playform/compress": "^0.2.3",
"@rollup/plugin-graphql": "^2.0.5",
"@tailwindcss/typography": "^0.5.19",
"@tailwindcss/vite": "^4.2.4",
@@ -26,8 +27,10 @@
"react-responsive-masonry": "^2.7.2",
"reading-time": "^1.5.0",
"tailwindcss": "^4.2.4",
"tslib": "^2.8.1",
"vite": "^8.0.9"
"tslib": "^2.8.1"
},
"overrides": {
"vite": "^7.0.0"
},
"devDependencies": {
"@types/markdown-it": "^14.1.2",

3
astro/src/build/files.ts Normal file
View File

@@ -0,0 +1,3 @@
// This file gets files, and puts them in the public folder before starting the build.
export async function getFiles() {
}

View File

@@ -1,11 +1,11 @@
---
import { getAllPaginatedBlogs } from '@/content/blogs/blogs';
import { getSettings } from '@/content/settings/settings';
import CalendarIcon from '@/icons/CalendarIcon.astro';
import { getImageSize, getImageUrl } from '@/lib/images';
import { markdownToHtml } from '@/lib/markdown';
import { getBlogRoute } from '@/lib/routing';
import { Image } from 'astro:assets';
import { getAllPaginatedBlogs } from "@/content/blogs/blogs";
import { getSettings } from "@/content/settings/settings";
import CalendarIcon from "@/icons/CalendarIcon.astro";
import { getImageSize, getImageUrl } from "@/lib/images";
import { markdownToHtml } from "@/lib/markdown";
import { getBlogRoute } from "@/lib/routing";
import { Image } from "astro:assets";
interface Props {
page: BlogIndex;

View File

@@ -1,17 +1,14 @@
---
import CalendarIcon from '@/icons/CalendarIcon.astro';
import { getImageSize, getImageUrl } from '@/lib/images';
import { markdownToHtml } from '@/lib/markdown';
import { getTypographyClasses } from '@/styles/markdownClasses';
import { Image } from 'astro:assets';
import CalendarIcon from "@/icons/CalendarIcon.astro";
import { markdownToHtml } from "@/lib/markdown";
import { getTypographyClasses } from "@/styles/markdownClasses";
import { Image } from "astro:assets";
interface Props {
blog: BlogPost;
}
const { blog } = Astro.props;
const imageSize = getImageSize(blog.searchEngine.thumbnail.width, blog.searchEngine.thumbnail.height, 1);
---
<div
@@ -30,9 +27,9 @@ const imageSize = getImageSize(blog.searchEngine.thumbnail.width, blog.searchEng
<div class="aspect-1200/630 w-full max-w-full overflow-hidden">
<div class="w-full h-full rounded-2xl shadow-md object-cover">
<Image
src={getImageUrl(blog.searchEngine.thumbnail.url)}
width={imageSize.width}
height={imageSize.height}
src={blog.searchEngine.thumbnail.url}
width={blog.searchEngine.thumbnail.width}
height={blog.searchEngine.thumbnail.height}
alt={blog.title}
class="rounded-2xl"
/>

View File

@@ -0,0 +1,24 @@
---
interface Props {
page: number;
totalPages: number;
urlTemplate: string;
}
const { page, totalPages, urlTemplate } = Astro.props;
---
<div class="flex flex-row gap-2">
{ totalPages < 7 && (
<>
{ [...Array(totalPages)].map((_: number, i: number) => (
<a
href={`${i + 1 === 1 ? urlTemplate : `${urlTemplate}/${i + 1}`}`}
class={`flex select-none hover:cursor-pointer text-lg justify-center items-center
${(i + 1 === page) ? "bg-(--ptc) text-(--ptt)" : "bg-neutral-200"} hover:bg-(--stc) hover:text-(--stt) duration-300 shadow-md rounded-full w-12 h-12`.trim()}>
<span>{i + 1}</span>
</a>
)) }
</>
) }
</div>

View File

@@ -1,8 +1,9 @@
---
import { getPhotoRoute } from '@/lib/routing';
import { AlbumPhotos } from './Album.tsx';
import { getImageSize, getImageUrl } from '@/lib/images';
import { getSettings } from '@/content/settings/settings';
import { getAlbumRoute, getPhotoRoute } from "@/lib/routing";
import { AlbumPhotos } from "./Album.tsx";
import { getImageSize, getImageUrl } from "@/lib/images";
import { getSettings } from "@/content/settings/settings";
import Pagination from "@/components/common/Pagination.astro";
interface Props {
page: PhotoAlbumPage;
@@ -12,13 +13,14 @@ const settings = await getSettings();
const album = Astro.props.page;
const pageNumber = Astro.props.page.pageNumber;
const startNumber = (pageNumber - 1) * settings.photo.album.perPage;
const endNumber = pageNumber * settings.photo.album.perPage;
const totalAlbumPages = Math.ceil(album.photos.length / settings.photo.album.perPage);
const sliceStartNumber = (pageNumber - 1) * settings.photo.album.perPage;
const sliceEndNumber = pageNumber * settings.photo.album.perPage;
const remappedPhotos: PhotoAlbumGalleryItem[] = [];
album.photos.slice(startNumber, endNumber).forEach((photo) => {
const resizedImage = getImageSize(photo.photo.width, photo.photo.height, 0.67);
album.photos.slice(sliceStartNumber, sliceEndNumber).forEach((photo) => {
const resizedImage = getImageSize(photo.photo.width, photo.photo.height, 0.756);
remappedPhotos.push({
id: photo.id,
@@ -40,6 +42,14 @@ album.photos.slice(startNumber, endNumber).forEach((photo) => {
<div class="flex flex-col gap-7">
<h1 class="text-5xl font-bold">{album.title}</h1>
<AlbumPhotos client:only client:load photos={remappedPhotos} />
<AlbumPhotos client:only photos={remappedPhotos} />
{ totalAlbumPages > 1 && (
<Pagination
page={pageNumber}
totalPages={totalAlbumPages}
urlTemplate={getAlbumRoute(settings.photo, album)}
/>
) }
</div>
</div>

View File

@@ -1,5 +1,6 @@
import { useEffect, useLayoutEffect, useRef, useState } from "preact/hooks";
import { JustifiedLayout } from '@immich/justified-layout-wasm';
import { JustifiedLayout } from "@immich/justified-layout-wasm";
import { LoadingSpinner } from "@/icons/jsx/loadingSpinner";
export function AlbumPhotos(props: { photos: PhotoAlbumGalleryItem[] }) {
const containerRef = useRef(null);
@@ -39,7 +40,7 @@ export function AlbumPhotos(props: { photos: PhotoAlbumGalleryItem[] }) {
const justifiedLayout = new JustifiedLayout(aspectRatios, {
rowHeight: 265,
rowWidth: containerWidth,
spacing: 4,
spacing: 10,
heightTolerance: 0.11
});
@@ -77,7 +78,9 @@ export function AlbumPhotos(props: { photos: PhotoAlbumGalleryItem[] }) {
}) }
</div>
) : (
<div>Loading...</div>
<div class="flex ">
<LoadingSpinner width={50} height={50} />
</div>
) }
</div>
)

View File

@@ -1,7 +1,6 @@
---
import { getAllCategories } from "@/content/photos/categories";
import { getSettings } from "@/content/settings/settings"
import { getImageUrl } from "@/lib/images";
import { getCategoryRoute } from "@/lib/routing";
import { Image } from "astro:assets";
@@ -23,7 +22,7 @@ const categories = await getAllCategories(settings);
<a href={getCategoryRoute(settings.photo, category)} class="group relative block w-[70%] overflow-hidden rounded-2xl shadow-md">
<div>
<Image
src={getImageUrl(category.thumbnail.url)}
src={category.thumbnail.url}
alt={category.title}
width={category.thumbnail.width}
height={category.thumbnail.height}

View File

@@ -0,0 +1,100 @@
---
import { getAlbum } from "@/content/photos/albums";
import { getSettings } from "@/content/settings/settings";
import ChevronUp from "@/icons/ChevronUp.astro";
import Download from "@/icons/Download.astro";
import Close from "@/icons/Close.astro";
import { getImageSize, getImageUrl } from "@/lib/images";
import { getAlbumRoute, getPhotoRoute } from "@/lib/routing";
import { getImage } from "astro:assets";
import { Image } from "astro:assets";
import { getPhotoHash } from "@/lib/hash";
interface Props {
page: PhotoPage;
}
const photo = Astro.props.page;
const settings = await getSettings();
const album = await getAlbum(settings, photo.album.url);
const photoIndex = album.photos.findIndex(p => p.id === photo.id);
let previousUrl: string | null = null;
let nextUrl: string | null = null;
// Check for previous photo
if (photoIndex > 0) {
previousUrl = getPhotoRoute(settings.photo, album, album.photos[photoIndex - 1]);
}
// Check for next photo
if (photoIndex + 1 < album.photos.length) {
nextUrl = getPhotoRoute(settings.photo, album, album.photos[photoIndex + 1]);
}
const albumPageNumber = Math.ceil((photoIndex + 1) / settings.photo.album.perPage);
const returnUrl = albumPageNumber > 1
? `${getAlbumRoute(settings.photo, album)}/${albumPageNumber}`
: getAlbumRoute(settings.photo, album);
const resizedImageSize = getImageSize(photo.photo.width, photo.photo.height, 1);
const downloadImageSize = getImageSize(photo.photo.width, photo.photo.height, 5);
const downloadUrl = await getImage({
src: getImageUrl(photo.photo.url),
width: downloadImageSize.width,
height: downloadImageSize.height,
format: "jpeg",
quality: 100
});
const downloadFileName = `${album.url.replaceAll("/", "")}-${getPhotoHash(photo)}.jpeg`;
---
<div class="h-screen flex flex-col justify-center items-center">
<div class="flex flex-col justify-center items-center h-full">
<Image
src={getImageUrl(photo.photo.url)}
width={resizedImageSize.width}
height={resizedImageSize.height}
alt={photo.text ?? ""}
class="h-full w-full object-contain"
/>
</div>
<div class="flex flex-row gap-6 absolute top-8 right-8 text-white py-2.5 px-5 bg-[#000000aa] rounded-full z-10">
<a data-astro-prefetch href={downloadUrl.src} download={downloadFileName}>
<Download width={36} height={36} />
</a>
<a data-astro-prefetch href={returnUrl}>
<Close width={36} height={36} />
</a>
</div>
{ photo.text !== null && (
<div class="absolute bottom-0 text-white text-xl bg-[#000000aa] w-full px-20 py-8">{photo.text.trim()}</div>
) }
{ previousUrl !== null && (
<a
data-astro-prefetch
href={previousUrl}
class="absolute left-8 text-white p-3 bg-[#000000aa] rounded-full z-10 rotate-270"
>
<ChevronUp width={28} height={28} />
</a>
) }
{ nextUrl !== null && (
<a
data-astro-prefetch
href={nextUrl}
class="absolute right-8 text-white p-3 bg-[#000000aa] rounded-full z-10 rotate-90"
>
<ChevronUp width={28} height={28} />
</a>
) }
</div>

View File

@@ -1,12 +1,11 @@
---
import { getSettings } from '@/content/settings/settings';
import { getAllPaginatedProjects } from '@/content/projects/projects';
import { markdownToHtml } from '@/lib/markdown';
import { Image } from 'astro:assets';
import { getProjectRoute } from '@/lib/routing';
import CalendarIcon from '@/icons/CalendarIcon.astro';
import { getImageSize, getImageUrl } from '@/lib/images';
import { promise } from 'astro:schema';
import { getSettings } from "@/content/settings/settings";
import { getAllPaginatedProjects } from "@/content/projects/projects";
import { markdownToHtml } from "@/lib/markdown";
import { Image } from "astro:assets";
import { getProjectRoute } from "@/lib/routing";
import CalendarIcon from "@/icons/CalendarIcon.astro";
import { getImageSize, getImageUrl } from "@/lib/images";
interface Props {
page: ProjectIndex;

View File

@@ -1,9 +1,9 @@
---
import CalendarIcon from '@/icons/CalendarIcon.astro';
import { getImageSize, getImageUrl } from '@/lib/images';
import { markdownToHtml } from '@/lib/markdown';
import { getTypographyClasses } from '@/styles/markdownClasses';
import { Image } from 'astro:assets';
import CalendarIcon from "@/icons/CalendarIcon.astro";
import { getImageSize } from "@/lib/images";
import { markdownToHtml } from "@/lib/markdown";
import { getTypographyClasses } from "@/styles/markdownClasses";
import { Image } from "astro:assets";
interface Props {
project: ProjectPost;
@@ -30,7 +30,7 @@ const imageSize = getImageSize(project.searchEngine.thumbnail.width, project.sea
<div class="aspect-1200/630 w-full max-w-full overflow-hidden">
<div class="w-full h-full rounded-2xl shadow-md object-cover">
<Image
src={getImageUrl(project.searchEngine.thumbnail.url)}
src={project.searchEngine.thumbnail.url}
width={imageSize.width}
height={imageSize.height}
alt={project.title}

View File

@@ -1,6 +1,6 @@
---
import { markdownToHtml } from '@/lib/markdown';
import { Image } from 'astro:assets';
import { markdownToHtml } from "@/lib/markdown";
import { Image } from "astro:assets";
interface Props {
contact: ContactComponent;

View File

@@ -1,6 +1,6 @@
---
import { markdownToHtml } from '@/lib/markdown';
import { Image } from 'astro:assets';
import { markdownToHtml } from "@/lib/markdown";
import { Image } from "astro:assets";
interface Props {
equipment: EquipmentTableComponent;

View File

@@ -1,6 +1,6 @@
---
import { markdownToHtml } from '@/lib/markdown';
import { QuestionList } from '@/components/web/subcomponents/QuestionList.tsx';
import { markdownToHtml } from "@/lib/markdown";
import { QuestionList } from "@/components/web/subcomponents/QuestionList.tsx";
interface Props {
faq: FrequentlyAskedQuestionsComponent;

View File

@@ -1,5 +1,5 @@
---
import { Image } from 'astro:assets';
import { Image } from "astro:assets";
interface Props {
hero: HeroComponent;

View File

@@ -1,10 +1,10 @@
---
import { getLastBlogs } from '@/content/blogs/blogs';
import { getSettings } from '@/content/settings/settings';
import CalendarIcon from '@/icons/CalendarIcon.astro';
import { getImageSize, getImageUrl } from '@/lib/images';
import { getBlogRoute } from '@/lib/routing';
import { Image } from 'astro:assets';
import { getLastBlogs } from "@/content/blogs/blogs";
import { getSettings } from "@/content/settings/settings";
import CalendarIcon from "@/icons/CalendarIcon.astro";
import { getImageSize, getImageUrl } from "@/lib/images";
import { getBlogRoute } from "@/lib/routing";
import { Image } from "astro:assets";
interface Props {
blogs: LastBlogsComponent;

View File

@@ -1,10 +1,10 @@
---
import { getLastProjects } from '@/content/projects/projects';
import { getSettings } from '@/content/settings/settings';
import CalendarIcon from '@/icons/CalendarIcon.astro';
import { getImageSize, getImageUrl } from '@/lib/images';
import { getProjectRoute } from '@/lib/routing';
import { Image } from 'astro:assets';
import { getLastProjects } from "@/content/projects/projects";
import { getSettings } from "@/content/settings/settings";
import CalendarIcon from "@/icons/CalendarIcon.astro";
import { getImageSize, getImageUrl } from "@/lib/images";
import { getProjectRoute } from "@/lib/routing";
import { Image } from "astro:assets";
interface Props {
projects: LastProjectsComponent;

View File

@@ -1,6 +1,6 @@
---
import { markdownToHtml } from '@/lib/markdown';
import StarRating from './subcomponents/StarRating.astro';
import { markdownToHtml } from "@/lib/markdown";
import StarRating from "./subcomponents/StarRating.astro";
interface Props {
reviews: ReviewListComponent;

View File

@@ -1,6 +1,6 @@
---
import { markdownToHtml } from '@/lib/markdown';
import { Image } from 'astro:assets';
import { markdownToHtml } from "@/lib/markdown";
import { Image } from "astro:assets";
interface Props {
textWithImage: TextWithImageComponent;

View File

@@ -1,8 +1,8 @@
---
import CalendarIcon from '@/icons/CalendarIcon.astro';
import { Image } from 'astro:assets';
import { upcomingEvent as UpcomingEvent } from './subcomponents/UpcomingEvent';
import { markdownToHtml } from '@/lib/markdown';
import CalendarIcon from "@/icons/CalendarIcon.astro";
import { Image } from "astro:assets";
import { upcomingEvent as UpcomingEvent } from "./subcomponents/UpcomingEvent";
import { markdownToHtml } from "@/lib/markdown";
interface Props {
upcomingEvents: UpcomingEventsComponent;

View File

@@ -1,15 +1,15 @@
---
import FrequentlyAskedQuestions from '../web/FrequentlyAskedQuestions.astro';
import Hero from '../web/Hero.astro';
import TextWithImage from '../web/TextWithImage.astro';
import UpcomingEvents from '../web/UpcomingEvents.astro';
import WallOfText from '../web/WallOfText.astro';
import EquipmentTable from '../web/EquipmentTable.astro';
import Reviews from '../web/Reviews.astro';
import LastBlogs from '../web/LastBlogs.astro';
import LastProjects from '../web/LastProjects.astro';
import LastAlbums from '../web/LastAlbums.astro';
import Contact from '../web/Contact.astro';
import FrequentlyAskedQuestions from '@/components/web/FrequentlyAskedQuestions.astro';
import Hero from '@/components/web/Hero.astro';
import TextWithImage from '@/components/web/TextWithImage.astro';
import UpcomingEvents from '@/components/web/UpcomingEvents.astro';
import WallOfText from '@/components/web/WallOfText.astro';
import EquipmentTable from '@/components/web/EquipmentTable.astro';
import Reviews from '@/components/web/Reviews.astro';
import LastBlogs from '@/components/web/LastBlogs.astro';
import LastProjects from '@/components/web/LastProjects.astro';
import LastAlbums from '@/components/web/LastAlbums.astro';
import Contact from '@/components/web/Contact.astro';
interface Props {
webpage: WebpageComponent[];

View File

@@ -1,11 +1,12 @@
import { createDirectusConnection } from "@/lib/directus";
import { print } from 'graphql';
import getBlogs from '@/graphql/blogs/getBlogs.graphql';
import getBlogPost from '@/graphql/blogs/getBlog.graphql';
import getLastBlogPosts from '@/graphql/blogs/getLastBlogPosts.graphql';
import getPaginatedBlogs from '@/graphql/blogs/getPaginatedBlogs.graphql';
import { print } from "graphql";
import getBlogs from "@/graphql/blogs/getBlogs.graphql";
import getBlogPost from "@/graphql/blogs/getBlog.graphql";
import getLastBlogPosts from "@/graphql/blogs/getLastBlogPosts.graphql";
import getPaginatedBlogs from "@/graphql/blogs/getPaginatedBlogs.graphql";
import { formatDate } from "@/lib/dates";
import { getImageSize } from "@/lib/images";
import { getImageSize, getImageUrl } from "@/lib/images";
import { getImage } from "astro:assets";
export async function getAllBlogs(settings: GlobalSettings): Promise<BlogPost[]> {
const client = await createDirectusConnection();
@@ -38,6 +39,11 @@ export async function getAllBlogs(settings: GlobalSettings): Promise<BlogPost[]>
content: blogRecord["content"],
date: blogRecord["date"],
url: blogRecord["url"],
thumbnail: {
url: blogRecord["search_engine"][0]["thumbnail"]["filename_disk"],
width: blogRecord["search_engine"][0]["thumbnail"]["width"],
height: blogRecord["search_engine"][0]["thumbnail"]["height"]
},
searchEngine: {
title: blogRecord["search_engine"][0]["title"],
description: blogRecord["search_engine"][0]["description"],
@@ -99,8 +105,15 @@ export async function getBlog(settings: GlobalSettings, route: string): Promise<
blogRecord["search_engine"][0]["thumbnail"]["created_on"]
];
const blogThumbnailImage =
getImageSize(blogRecord["search_engine"][0]["thumbnail"]["width"], blogRecord["search_engine"][0]["thumbnail"]["height"], 0.756);
const blogThumbnailImage = getImageSize(blogRecord["search_engine"][0]["thumbnail"]["width"],
blogRecord["search_engine"][0]["thumbnail"]["height"], 0.756);
const thumbnail = await getImage({
src: getImageUrl(blogRecord["search_engine"][0]["thumbnail"]["filename_disk"]),
width: blogThumbnailImage.width,
height: blogThumbnailImage.height,
format: "jpeg"
});
const blog: BlogPost = {
exists: true,
@@ -111,6 +124,11 @@ export async function getBlog(settings: GlobalSettings, route: string): Promise<
content: blogRecord["content"],
date: blogRecord["date"],
url: blogRecord["url"],
thumbnail: {
url: blogRecord["search_engine"][0]["thumbnail"]["filename_disk"],
width: blogRecord["search_engine"][0]["thumbnail"]["width"],
height: blogRecord["search_engine"][0]["thumbnail"]["height"]
},
searchEngine: {
title: blogRecord["search_engine"][0]["title"],
description: blogRecord["search_engine"][0]["description"],
@@ -118,7 +136,7 @@ export async function getBlog(settings: GlobalSettings, route: string): Promise<
canonical: blogRecord["search_engine"][0]["canonical"],
priority: blogRecord["search_engine"][0]["priority"],
thumbnail: {
url: blogRecord["search_engine"][0]["thumbnail"]["filename_disk"],
url: `${settings.website.domainName}${thumbnail.src}`,
width: blogThumbnailImage.width,
height: blogThumbnailImage.height
}
@@ -181,6 +199,11 @@ export async function getLastBlogs(amount: number): Promise<BlogPost[]> {
content: blogRecord["content"],
date: blogRecord["date"],
url: blogRecord["url"],
thumbnail: {
url: blogRecord["search_engine"][0]["thumbnail"]["filename_disk"],
width: blogRecord["search_engine"][0]["thumbnail"]["width"],
height: blogRecord["search_engine"][0]["thumbnail"]["height"]
},
searchEngine: {
title: blogRecord["search_engine"][0]["title"],
description: blogRecord["search_engine"][0]["description"],
@@ -257,6 +280,11 @@ export async function getAllPaginatedBlogs(settings: GlobalSettings, page: numbe
content: blogRecord["content"],
date: blogRecord["date"],
url: blogRecord["url"],
thumbnail: {
url: blogRecord["search_engine"][0]["thumbnail"]["filename_disk"],
width: blogRecord["search_engine"][0]["thumbnail"]["width"],
height: blogRecord["search_engine"][0]["thumbnail"]["height"]
},
searchEngine: {
title: blogRecord["search_engine"][0]["title"],
description: blogRecord["search_engine"][0]["description"],

View File

@@ -1,95 +1,95 @@
import { createDirectusConnection } from "@/lib/directus";
import { print } from 'graphql';
import { print } from "graphql";
import type { Footer, FooterColumn, FooterSecondaryLink, FooterSocial } from "@/types/footers/footer";
import getFooterQuery from '@/graphql/footer/getFooter.graphql';
import getFooterQuery from "@/graphql/footer/getFooter.graphql";
import { getImageUrl } from "@/lib/images";
export async function getFooter(): Promise<Footer> {
const client = await createDirectusConnection();
const result = await client.query(print(getFooterQuery));
const footerRecord = result['Footer'];
const footerRecord = result["Footer"];
let dates: string[] = [
footerRecord['date_created'],
footerRecord['date_updated']
footerRecord["date_created"],
footerRecord["date_updated"]
];
let footer: Footer = {
id: footerRecord['id'],
title: footerRecord['title'],
id: footerRecord["id"],
title: footerRecord["title"],
logo: {
url: getImageUrl(footerRecord['logo']['filename_disk']),
width: footerRecord['logo']['width'],
height: footerRecord['logo']['height']
url: getImageUrl(footerRecord["logo"]["filename_disk"]),
width: footerRecord["logo"]["width"],
height: footerRecord["logo"]["height"]
},
copyright: footerRecord['copyright'],
copyright: footerRecord["copyright"],
columns: [],
socials: null,
secondaryLinks: null,
lastModified: new Date()
};
if (footerRecord['columns'] !== null) {
footerRecord['columns'].forEach((footerColumn: any) => {
if (footerRecord["columns"] !== null) {
footerRecord["columns"].forEach((footerColumn: any) => {
const column: FooterColumn = {
id: footerColumn['id'],
title: footerColumn['title'],
id: footerColumn["id"],
title: footerColumn["title"],
links: []
};
footerColumn['links'].forEach((columnLink: any) => {
footerColumn["links"].forEach((columnLink: any) => {
column.links.push({
id: columnLink['id'],
text: columnLink['text'],
url: columnLink['url']
id: columnLink["id"],
text: columnLink["text"],
url: columnLink["url"]
});
dates.push(columnLink['date_created']);
dates.push(columnLink['date_updated']);
dates.push(columnLink["date_created"]);
dates.push(columnLink["date_updated"]);
});
footer.columns.push(column);
dates.push(footerColumn['date_created']);
dates.push(footerColumn['date_updated']);
dates.push(footerColumn["date_created"]);
dates.push(footerColumn["date_updated"]);
});
}
if (footerRecord['socials'] !== null) {
if (footerRecord["socials"] !== null) {
let socials: FooterSocial[] = [];
footerRecord['socials'].forEach((footerSocial: any) => {
footerRecord["socials"].forEach((footerSocial: any) => {
socials.push({
id: footerSocial['id'],
name: footerSocial['name'],
url: footerSocial['url'],
id: footerSocial["id"],
name: footerSocial["name"],
url: footerSocial["url"],
icon: {
url: getImageUrl(footerSocial['icon']['filename_disk']),
width: footerSocial['icon']['width'],
height: footerSocial['icon']['height']
url: getImageUrl(footerSocial["icon"]["filename_disk"]),
width: footerSocial["icon"]["width"],
height: footerSocial["icon"]["height"]
}
});
dates.push(footerSocial['date_created']);
dates.push(footerSocial['date_updated']);
dates.push(footerSocial["date_created"]);
dates.push(footerSocial["date_updated"]);
});
footer.socials = socials;
}
if (footerRecord['secondary_links'] !== null) {
if (footerRecord["secondary_links"] !== null) {
let secondaryLinks: FooterSecondaryLink[] = [];
footerRecord['secondary_links'].forEach((footerSecondaryLink: any) => {
footerRecord["secondary_links"].forEach((footerSecondaryLink: any) => {
secondaryLinks.push({
id: footerSecondaryLink['id'],
text: footerSecondaryLink['text'],
url: footerSecondaryLink['url']
id: footerSecondaryLink["id"],
text: footerSecondaryLink["text"],
url: footerSecondaryLink["url"]
});
dates.push(footerSecondaryLink['date_created']);
dates.push(footerSecondaryLink['date_updated']);
dates.push(footerSecondaryLink["date_created"]);
dates.push(footerSecondaryLink["date_updated"]);
});
footer.secondaryLinks = secondaryLinks;

View File

@@ -6,28 +6,28 @@ export async function getMenu(): Promise<Menu> {
const client = await createDirectusConnection();
const result = await client.query(print(getMenuQuery));
const menuRecord = result['Menu'];
const menuRecord = result["Menu"];
let menu: Menu = {
id: menuRecord['id'],
id: menuRecord["id"],
items: []
};
menuRecord['items'].forEach((menuItem: any) => {
if (menuItem['collection'] === "Menu_Column") {
menuRecord["items"].forEach((menuItem: any) => {
if (menuItem["collection"] === "Menu_Column") {
let menuColumnItem: MenuColumn = {
id: menuItem['item']['id'],
id: menuItem["item"]["id"],
type: "Column",
title: menuItem['item']['title'],
title: menuItem["item"]["title"],
links: []
};
menuItem['item']['links'].forEach((menuItemLink: any) => {
menuItem["item"]["links"].forEach((menuItemLink: any) => {
menuColumnItem.links.push({
id: menuItemLink['id'],
id: menuItemLink["id"],
type: "Link",
text: menuItemLink['text'],
url: menuItemLink['url']
text: menuItemLink["text"],
url: menuItemLink["url"]
});
});

View File

@@ -1,5 +1,5 @@
import { createDirectusConnection } from "@/lib/directus";
import { print } from 'graphql';
import { print } from "graphql";
import { formatDate } from "@/lib/dates";
import getAllPages from "@/graphql/pages/getAllPages.graphql";
import getPage from "@/graphql/pages/getPage.graphql";

View File

@@ -1,9 +1,9 @@
import { createDirectusConnection } from "@/lib/directus";
import { print } from "graphql";
import getAlbums from '@/graphql/photos/getAlbums.graphql';
import getAlbumItem from '@/graphql/photos/getAlbum.graphql';
import getLastAlbumsQuery from '@/graphql/photos/getLastAlbums.graphql';
import getCategoryAlbumQuery from '@/graphql/photos/getCategoryAlbum.graphql';
import getAlbums from "@/graphql/photos/getAlbums.graphql";
import getAlbumItem from "@/graphql/photos/getAlbum.graphql";
import getLastAlbumsQuery from "@/graphql/photos/getLastAlbums.graphql";
import getCategoryAlbumQuery from "@/graphql/photos/getCategoryAlbum.graphql";
import { formatDate } from "@/lib/dates";
import { getImageSize } from "@/lib/images";
@@ -135,7 +135,7 @@ export async function getAlbum(settings: GlobalSettings, route: string): Promise
}
},
thumbnail: {
url: albumRecord["thumbnail"]["filename_download"],
url: albumRecord["thumbnail"]["filename_disk"],
width: thumbnailImage.width,
height: thumbnailImage.height
},

View File

@@ -1,8 +1,8 @@
import { createDirectusConnection } from "@/lib/directus";
import { print } from "graphql";
import getCategories from '@/graphql/photos/getCategories.graphql';
import getCategory from '@/graphql/photos/getCategory.graphql';
import { getImageSize } from "@/lib/images";
import getCategories from "@/graphql/photos/getCategories.graphql";
import getCategory from "@/graphql/photos/getCategory.graphql";
import { getImageSize, getImageUrl } from "@/lib/images";
export async function getAllCategories(settings: GlobalSettings): Promise<PhotoAlbumCategory[]> {
const client = await createDirectusConnection();
@@ -11,15 +11,15 @@ export async function getAllCategories(settings: GlobalSettings): Promise<PhotoA
let categories: PhotoAlbumCategory[] = [];
result["Photo_Categories"].forEach((photoCategoryRecord: any) => {
const imageSize =
getImageSize(photoCategoryRecord["thumbnail"]["width"], photoCategoryRecord["thumbnail"]["height"], 1.5);
const imageSize = getImageSize(photoCategoryRecord["thumbnail"]["width"],
photoCategoryRecord["thumbnail"]["height"], 1.5);
categories.push({
id: photoCategoryRecord["id"],
title: photoCategoryRecord["title"],
url: photoCategoryRecord["url"],
thumbnail: {
url: photoCategoryRecord["thumbnail"]["filename_disk"],
url: getImageUrl(photoCategoryRecord["thumbnail"]["filename_disk"]),
width: imageSize.width,
height: imageSize.height
}
@@ -37,15 +37,15 @@ export async function getPhotoCategory(url: string): Promise<PhotoAlbumCategory>
const item = result["Photo_Categories"][0];
const imageSize =
getImageSize(item["thumbnail"]["width"], item["thumbnail"]["height"], 1.5);
const imageSize = getImageSize(item["thumbnail"]["width"],
item["thumbnail"]["height"], 1.5);
let categories: PhotoAlbumCategory = {
id: item["id"],
title: item["title"],
url: item["url"],
thumbnail: {
url: item["thumbnail"]["filename_disk"],
url: getImageUrl(item["thumbnail"]["filename_disk"]),
width: imageSize.width,
height: imageSize.height
}

View File

@@ -1,6 +1,6 @@
import { createDirectusConnection } from "@/lib/directus";
import { print } from "graphql";
import getPhotos from '@/graphql/photos/getPhotos.graphql';
import getPhotos from "@/graphql/photos/getPhotos.graphql";
import md5 from "md5";
export async function getPhotoFromHash(albumUrl: string, hash: string): Promise<PhotoAlbumItem | null> {
@@ -14,7 +14,7 @@ export async function getPhotoFromHash(albumUrl: string, hash: string): Promise<
result["Photo_Albums"][0]["photos"].forEach((photo: any) => {
/*
* I have decided to not put the getImageSize here, it can mess up the
* hashing, or anything else. It seems smarter to do this in the photo's and galleries.
* hashing, or anything else. It seems smarter to do this in the photo"s and galleries.
*/
const hashObject = md5(JSON.stringify({

View File

@@ -1,10 +1,11 @@
import { formatDate } from "@/lib/dates";
import { createDirectusConnection } from "@/lib/directus";
import { print } from "graphql";
import getProjects from '@/graphql/projects/getProjects.graphql';
import getProjectPost from '@/graphql/projects/getProject.graphql';
import getLastProjectsQuery from '@/graphql/projects/getLastProjects.graphql';
import { getImageSize } from "@/lib/images";
import getProjects from "@/graphql/projects/getProjects.graphql";
import getProjectPost from "@/graphql/projects/getProject.graphql";
import getLastProjectsQuery from "@/graphql/projects/getLastProjects.graphql";
import { getImageSize, getImageUrl } from "@/lib/images";
import { getImage } from "astro:assets";
export async function getAllProjects(settings: GlobalSettings): Promise<ProjectPost[]> {
const client = await createDirectusConnection();
@@ -25,8 +26,8 @@ export async function getAllProjects(settings: GlobalSettings): Promise<ProjectP
projectRecord["search_engine"][0]["thumbnail"]["created_on"]
];
const projectThumbnailImage =
getImageSize(projectRecord["search_engine"][0]["thumbnail"]["width"], projectRecord["search_engine"][0]["thumbnail"]["height"], 0.756)
const projectThumbnailImage = getImageSize(projectRecord["search_engine"][0]["thumbnail"]["width"],
projectRecord["search_engine"][0]["thumbnail"]["height"], 0.756);
const project: ProjectPost = {
exists: true,
@@ -37,6 +38,11 @@ export async function getAllProjects(settings: GlobalSettings): Promise<ProjectP
content: projectRecord["content"],
date: projectRecord["date"],
url: projectRecord["url"],
thumbnail: {
url: getImageUrl(projectRecord["search_engine"][0]["thumbnail"]["filename_disk"]),
width: projectRecord["search_engine"][0]["thumbnail"]["width"],
height: projectRecord["search_engine"][0]["thumbnail"]["height"]
},
searchEngine: {
title: projectRecord["search_engine"][0]["title"],
description: projectRecord["search_engine"][0]["description"],
@@ -44,7 +50,7 @@ export async function getAllProjects(settings: GlobalSettings): Promise<ProjectP
canonical: projectRecord["search_engine"][0]["canonical"],
priority: projectRecord["search_engine"][0]["priority"],
thumbnail: {
url: projectRecord["search_engine"][0]["thumbnail"]["filename_disk"],
url: getImageUrl(projectRecord["search_engine"][0]["thumbnail"]["filename_disk"]),
width: projectThumbnailImage.width,
height: projectThumbnailImage.height
}
@@ -98,8 +104,15 @@ export async function getProject(settings: GlobalSettings, route: string): Promi
projectRecord["search_engine"][0]["thumbnail"]["created_on"]
];
const projectThumbnailImage =
getImageSize(projectRecord["search_engine"][0]["thumbnail"]["width"], projectRecord["search_engine"][0]["thumbnail"]["height"], 0.756)
const projectThumbnailImage = getImageSize(projectRecord["search_engine"][0]["thumbnail"]["width"],
projectRecord["search_engine"][0]["thumbnail"]["height"], 0.756);
const thumbnail = await getImage({
src: getImageUrl(projectRecord["search_engine"][0]["thumbnail"]["filename_disk"]),
width: projectThumbnailImage.width,
height: projectThumbnailImage.height,
format: "jpeg"
});
const project: ProjectPost = {
type: "ProjectPost",
@@ -111,6 +124,11 @@ export async function getProject(settings: GlobalSettings, route: string): Promi
content: projectRecord["content"],
date: projectRecord["date"],
url: projectRecord["url"],
thumbnail: {
url: projectRecord["search_engine"][0]["thumbnail"]["filename_disk"],
width: projectRecord["search_engine"][0]["thumbnail"]["width"],
height: projectRecord["search_engine"][0]["thumbnail"]["height"]
},
searchEngine: {
title: projectRecord["search_engine"][0]["title"],
description: projectRecord["search_engine"][0]["description"],
@@ -118,7 +136,7 @@ export async function getProject(settings: GlobalSettings, route: string): Promi
canonical: projectRecord["search_engine"][0]["canonical"],
priority: projectRecord["search_engine"][0]["priority"],
thumbnail: {
url: projectRecord["search_engine"][0]["thumbnail"]["filename_disk"],
url: `${settings.website.domainName}${thumbnail.src}`,
width: projectThumbnailImage.width,
height: projectThumbnailImage.height
}
@@ -181,6 +199,11 @@ export async function getLastProjects(amount: number): Promise<ProjectPost[]> {
content: projectRecord["content"],
date: projectRecord["date"],
url: projectRecord["url"],
thumbnail: {
url: projectRecord["search_engine"][0]["thumbnail"]["filename_disk"],
width: projectRecord["search_engine"][0]["thumbnail"]["width"],
height: projectRecord["search_engine"][0]["thumbnail"]["height"]
},
searchEngine: {
title: projectRecord["search_engine"][0]["title"],
description: projectRecord["search_engine"][0]["description"],
@@ -257,6 +280,11 @@ export async function getAllPaginatedProjects(settings: GlobalSettings, page: nu
content: projectRecord["content"],
date: projectRecord["date"],
url: projectRecord["url"],
thumbnail: {
url: projectRecord["search_engine"][0]["thumbnail"]["filename_disk"],
width: projectRecord["search_engine"][0]["thumbnail"]["width"],
height: projectRecord["search_engine"][0]["thumbnail"]["height"]
},
searchEngine: {
title: projectRecord["search_engine"][0]["title"],
description: projectRecord["search_engine"][0]["description"],

View File

@@ -1,6 +1,6 @@
import { createDirectusConnection } from "@/lib/directus";
import { print } from 'graphql';
import getRobotsQuery from '@/graphql/settings/robots.graphql';
import { print } from "graphql";
import getRobotsQuery from "@/graphql/settings/robots.graphql";
export async function getRobotsSettings(): Promise<RobotsSettings> {
const client = await createDirectusConnection();

View File

@@ -1,6 +1,6 @@
import { print } from 'graphql';
import { print } from "graphql";
import { createDirectusConnection } from "@/lib/directus";
import getSettingsQuery from '@/graphql/settings/settings.graphql';
import getSettingsQuery from "@/graphql/settings/settings.graphql";
export async function getSettings(): Promise<GlobalSettings> {
const client = await createDirectusConnection();

View File

@@ -0,0 +1,10 @@
---
interface Props {
width?: number;
height?: number;
}
---
<svg xmlns="http://www.w3.org/2000/svg" width={Astro.props.width ?? "24"} height={Astro.props.height ?? "24"} viewBox="0 0 24 24">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m4 15l8-8l8 8" />
</svg>

View File

@@ -0,0 +1,10 @@
---
interface Props {
width?: number;
height?: number;
}
---
<svg xmlns="http://www.w3.org/2000/svg" width={Astro.props.width ?? "24"} height={Astro.props.height ?? "24"} viewBox="0 0 24 24">
<path fill="currentColor" d="M6.4 19L5 17.6l5.6-5.6L5 6.4L6.4 5l5.6 5.6L17.6 5L19 6.4L13.4 12l5.6 5.6l-1.4 1.4l-5.6-5.6z" />
</svg>

View File

@@ -0,0 +1,10 @@
---
interface Props {
width?: number;
height?: number;
}
---
<svg xmlns="http://www.w3.org/2000/svg" width={Astro.props.width ?? "24"} height={Astro.props.height ?? "24"} viewBox="0 0 24 24">
<path fill="currentColor" d="m12 16l-5-5l1.4-1.45l2.6 2.6V4h2v8.15l2.6-2.6L17 11zm-6 4q-.825 0-1.412-.587T4 18v-3h2v3h12v-3h2v3q0 .825-.587 1.413T18 20z" />
</svg>

View File

@@ -0,0 +1,5 @@
export function LoadingSpinner(props: { width?: number, height?: number }) {
return (
<svg xmlns="http://www.w3.org/2000/svg" width={props.width ?? "24"} height={props.height ?? "24"} viewBox="0 0 24 24"><circle cx="12" cy="2" r="0" fill="currentColor"><animate attributeName="r" begin="0" calcMode="spline" dur="1s" keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8" repeatCount="indefinite" values="0;2;0;0"/></circle><circle cx="12" cy="2" r="0" fill="currentColor" transform="rotate(45 12 12)"><animate attributeName="r" begin="0.125s" calcMode="spline" dur="1s" keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8" repeatCount="indefinite" values="0;2;0;0"/></circle><circle cx="12" cy="2" r="0" fill="currentColor" transform="rotate(90 12 12)"><animate attributeName="r" begin="0.25s" calcMode="spline" dur="1s" keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8" repeatCount="indefinite" values="0;2;0;0"/></circle><circle cx="12" cy="2" r="0" fill="currentColor" transform="rotate(135 12 12)"><animate attributeName="r" begin="0.375s" calcMode="spline" dur="1s" keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8" repeatCount="indefinite" values="0;2;0;0"/></circle><circle cx="12" cy="2" r="0" fill="currentColor" transform="rotate(180 12 12)"><animate attributeName="r" begin="0.5s" calcMode="spline" dur="1s" keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8" repeatCount="indefinite" values="0;2;0;0"/></circle><circle cx="12" cy="2" r="0" fill="currentColor" transform="rotate(225 12 12)"><animate attributeName="r" begin="0.625s" calcMode="spline" dur="1s" keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8" repeatCount="indefinite" values="0;2;0;0"/></circle><circle cx="12" cy="2" r="0" fill="currentColor" transform="rotate(270 12 12)"><animate attributeName="r" begin="0.75s" calcMode="spline" dur="1s" keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8" repeatCount="indefinite" values="0;2;0;0"/></circle><circle cx="12" cy="2" r="0" fill="currentColor" transform="rotate(315 12 12)"><animate attributeName="r" begin="0.875s" calcMode="spline" dur="1s" keySplines="0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8;0.2 0.2 0.4 0.8" repeatCount="indefinite" values="0;2;0;0"/></circle></svg>
)
}

View File

@@ -1,13 +1,14 @@
---
import '@/styles/global.css';
import "@/styles/global.css";
import { getSettings } from "@/content/settings/settings";
import { getTextColor } from '@/lib/colors';
import Footer from '@/components/footer/Footer.astro';
import { getTextColor } from "@/lib/colors";
import Footer from "@/components/footer/Footer.astro";
interface Props {
settings: BlogLayoutProps;
}
const tags = Astro.props.settings.tags ?? [];
const pageSettings = Astro.props.settings.searchEngine;
const settings = await getSettings();
@@ -42,7 +43,7 @@ const css = {
<link rel="alternate" type="application/rss+xml" href="/rss.xml" title="RSS" />
<link rel="canonical" href={`${settings.website.domainName}/`} />
<meta name="robots" content="index,follow" />
<meta name="keywords" content={[].join(',')} />
<meta name="keywords" content={tags.join(",")} />
<!-- Low Priority Page Metadata -->
<meta name="description" content={pageSettings.description} />
@@ -55,7 +56,7 @@ const css = {
<meta property="og:url" content={`${settings.website.domainName}${Astro.url.pathname}`} />
<meta property="og:site_name" content={settings.website.applicationName} />
<meta property="article:author" content={settings.website.author.name} />
<meta property="article:tags" content={[].join(',')} />
<meta property="article:tags" content={[].join(",")} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={settings.website.titleTemplate.replaceAll("%T", pageSettings.title)} />

View File

@@ -2,7 +2,8 @@
import '@/styles/global.css';
import { getSettings } from "@/content/settings/settings";
import { getTextColor } from '@/lib/colors';
import Footer from '@/components/footer/Footer.astro';
import { getImageSize } from '@/lib/images';
import { getImage } from 'astro:assets';
interface Props {
settings: WebpageLayoutProps;
@@ -19,6 +20,21 @@ const css = {
? getTextColor(settings.website.colors.secondary)
: getTextColor(settings.website.colors.primary)
};
const searchengine = Astro.props.settings.searchEngine;
// Changing the thumbnail here is okay, as we now have two images: a new thumbnail, and the original image.
// Might have to make it prettier down the line?
// TODO: See comment above.
const resizedThumbnail = getImageSize(searchengine.thumbnail.width,
searchengine.thumbnail.height, 0.756);
const thumbnail = await getImage({
src: searchengine.thumbnail.url,
width: resizedThumbnail.width,
height: resizedThumbnail.height,
format: "jpeg"
});
---
<!DOCTYPE html>
@@ -50,14 +66,14 @@ const css = {
<meta property="og:locale" content="en-GB" />
<meta property="og:title" content={settings.website.titleTemplate.replaceAll("%T", pageSettings.title)} />
<meta property="og:description" content={pageSettings.description} />
<meta property="og:image:url" content={pageSettings.thumbnail.url} />
<meta property="og:image:url" content={`${settings.website.domainName}${thumbnail.src}`} />
<meta property="og:url" content={`${settings.website.domainName}${Astro.url.pathname}`} />
<meta property="og:site_name" content={settings.website.applicationName} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={settings.website.titleTemplate.replaceAll("%T", pageSettings.title)} />
<meta name="twitter:description" content={pageSettings.description} />
<meta name="twitter:image" content={pageSettings.thumbnail.url} />
<meta name="twitter:image" content={`${settings.website.domainName}${thumbnail.src}`} />
<meta name="twitter:url" content={`${settings.website.domainName}${Astro.url.pathname}`} />
<meta name="twitter:site" content={settings.website.twitter.handle} />
<meta name="twitter:creator" content={settings.website.twitter.handle} />
@@ -76,9 +92,7 @@ const css = {
<!-- Scripts and Style -->
</head>
<body style={ css } class="bg-[#fcfcfc] flex flex-col min-h-screen">
<body style={ css } class="bg-neutral-950 flex flex-col min-h-screen">
<slot class="grow" name="content" />
<Footer />
</body>
</html>

View File

@@ -1,8 +1,8 @@
---
import '@/styles/global.css';
import "@/styles/global.css";
import { getSettings } from "@/content/settings/settings";
import { getTextColor } from '@/lib/colors';
import Footer from '@/components/footer/Footer.astro';
import { getTextColor } from "@/lib/colors";
import Footer from "@/components/footer/Footer.astro";
interface Props {
settings: BlogLayoutProps;
@@ -42,7 +42,7 @@ const css = {
<link rel="alternate" type="application/rss+xml" href="/rss.xml" title="RSS" />
<link rel="canonical" href={`${settings.website.domainName}/`} />
<meta name="robots" content="index,follow" />
<meta name="keywords" content={[].join(',')} />
<meta name="keywords" content={[].join(",")} />
<!-- Low Priority Page Metadata -->
<meta name="description" content={pageSettings.description} />
@@ -55,7 +55,7 @@ const css = {
<meta property="og:url" content={`${settings.website.domainName}${Astro.url.pathname}`} />
<meta property="og:site_name" content={settings.website.applicationName} />
<meta property="article:author" content={settings.website.author.name} />
<meta property="article:tags" content={[].join(',')} />
<meta property="article:tags" content={[].join(",")} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={settings.website.titleTemplate.replaceAll("%T", pageSettings.title)} />

View File

@@ -1,8 +1,8 @@
---
import '@/styles/global.css';
import "@/styles/global.css";
import { getSettings } from "@/content/settings/settings";
import { getTextColor } from '@/lib/colors';
import Footer from '@/components/footer/Footer.astro';
import { getTextColor } from "@/lib/colors";
import Footer from "@/components/footer/Footer.astro";
interface Props {
settings: WebpageLayoutProps;

View File

@@ -1,6 +1,6 @@
export function getTextColor(bgColor: string) {
// Remove # if present
const hex = bgColor.replace('#', '');
const hex = bgColor.replace("#", "");
// Convert hex to RGB
const r = parseInt(hex.substring(0, 2), 16) / 255;
@@ -19,5 +19,5 @@ export function getTextColor(bgColor: string) {
const luminance = 0.2126 * R + 0.7152 * G + 0.0722 * B;
// Return white for dark backgrounds, black for light backgrounds
return luminance > 0.179 ? '#000000' : '#fcfcfc';
return luminance > 0.179 ? "#000000" : "#fcfcfc";
}

View File

@@ -1,6 +1,6 @@
export function formatDate(date: Date, format: string) {
return format
.replaceAll("%Y", date.getFullYear().toString())
.replaceAll("%M", (date.getMonth() + 1).toString().padStart(2, '0'))
.replaceAll("%D", date.getDate().toString().padStart(2, '0'));
.replaceAll("%M", (date.getMonth() + 1).toString().padStart(2, "0"))
.replaceAll("%D", date.getDate().toString().padStart(2, "0"));
}

View File

@@ -4,6 +4,8 @@ import { getAlbum } from "@/content/photos/albums";
import { getAllCategories, getPhotoCategory } from "@/content/photos/categories";
import { getPhotoFromHash } from "@/content/photos/photos";
import { getProject } from "@/content/projects/projects";
import { getImageSize, getImageUrl } from "./images";
import { getImage } from "astro:assets";
export async function getPage(settings: GlobalSettings, route: string): Promise<PageType | null> {
// Blog Index
@@ -95,6 +97,24 @@ export async function getPage(settings: GlobalSettings, route: string): Promise<
const allCategories = await getAllCategories(settings);
const lastCategory = allCategories[0];
// Changing the thumbnail here is okay, as it gets everything again in the CategoryIndex.astro file.
// Might have to make it prettier down the line?
// TODO: See comment above.
const resizedThumbnail = getImageSize(lastCategory.thumbnail.width, lastCategory.thumbnail.height, 0.756);
const thumbnail = await getImage({
src: lastCategory.thumbnail.url,
width: resizedThumbnail.width,
height: resizedThumbnail.height,
format: "jpeg"
});
lastCategory.thumbnail = {
url: `${settings.website.domainName}${thumbnail.src}`,
width: resizedThumbnail.width,
height: resizedThumbnail.height
}
return {
route: route,
pageType: "PhotoCategoryIndex",
@@ -119,6 +139,24 @@ export async function getPage(settings: GlobalSettings, route: string): Promise<
const category = await getPhotoCategory(`/${params["C"]}`);
// Changing the thumbnail here is okay, as it gets everything again in the Category.astro file.
// Might have to make it prettier down the line?
// TODO: See comment above.
const resizedThumbnail = getImageSize(category.thumbnail.width, category.thumbnail.height, 0.756);
const thumbnail = await getImage({
src: category.thumbnail.url,
width: resizedThumbnail.width,
height: resizedThumbnail.height,
format: "jpeg"
});
category.thumbnail = {
url: `${settings.website.domainName}${thumbnail.src}`,
width: resizedThumbnail.width,
height: resizedThumbnail.height
};
return {
route: route,
pageType: "PhotoCategory",
@@ -143,13 +181,30 @@ export async function getPage(settings: GlobalSettings, route: string): Promise<
});
const photoAlbum = await getAlbum(settings, `/${params["R"]}`);
// Changing the thumbnail here is okay, as the thumbnail is unused in the Album.astro file.
// Might have to make it prettier down the line?
// TODO: See comment above.
const resizedThumbnail = getImageSize(photoAlbum.thumbnail.width, photoAlbum.thumbnail.height, 0.756);
const thumbnail = await getImage({
src: getImageUrl(photoAlbum.thumbnail.url),
width: resizedThumbnail.width,
height: resizedThumbnail.height,
format: "jpeg"
});
return {
route: route,
pageType: "PhotoAlbum",
page: {
...photoAlbum,
pageNumber: params["page"] !== undefined ? Number(params['page']) : 1
thumbnail: {
url: `${settings.website.domainName}${thumbnail.src}`,
width: resizedThumbnail.width,
height: resizedThumbnail.height
},
pageNumber: params["page"] !== undefined ? Number(params["page"]) : 1
}
};
}
@@ -198,7 +253,17 @@ export async function getPage(settings: GlobalSettings, route: string): Promise<
}
}
}
const resizedImage = getImageSize(webpageContent.searchEngine.thumbnail.width,
webpageContent.searchEngine.thumbnail.height, 0.756);
const thumbnail = await getImage({
src: webpageContent.searchEngine.thumbnail.url,
width: resizedImage.width,
height: resizedImage.height,
format: "jpeg"
});
return {
route: route,
pageType: "Webpage",
@@ -208,7 +273,14 @@ export async function getPage(settings: GlobalSettings, route: string): Promise<
id: webpageContent.id,
lastModified: webpageContent.lastModified,
url: webpageContent.url,
searchEngine: webpageContent.searchEngine,
searchEngine: {
...webpageContent.searchEngine,
thumbnail: {
url: `${settings.website.domainName}${thumbnail.src}`,
width: resizedImage.width,
height: resizedImage.height
}
},
components: webpageContent.components
}
};

View File

@@ -2,7 +2,7 @@ import { getAllBlogs } from "@/content/blogs/blogs";
import { getAllWebpages } from "@/content/pages/pages";
import { getAllAlbums } from "@/content/photos/albums";
import { getAllProjects } from "@/content/projects/projects";
import { getPhotoHash } from "./hash";
import { getPhotoHash } from "@/lib/hash";
import { getAllCategories } from "@/content/photos/categories";
export async function getAllRoutesList(settings: GlobalSettings): Promise<string[]> {
@@ -99,10 +99,10 @@ export function getBlogRoute(blogSettings: BlogSettings, blog: BlogPost) {
return blogSettings.blogRouteTemplate
.replaceAll("%Y", date.getFullYear().toString())
.replaceAll("%M", (date.getMonth() + 1).toString().padStart(2, '0'))
.replaceAll("%D", date.getDate().toString().padStart(2, '0'))
.replaceAll("%M", (date.getMonth() + 1).toString().padStart(2, "0"))
.replaceAll("%D", date.getDate().toString().padStart(2, "0"))
.replaceAll("%R", blog.url)
.replace(/\/+/g, '/');
.replace(/\/+/g, "/");
}
export function getProjectRoute(projectSettings: ProjectSettings, project: ProjectPost) {
@@ -110,16 +110,16 @@ export function getProjectRoute(projectSettings: ProjectSettings, project: Proje
return projectSettings.projectRouteTemplate
.replaceAll("%Y", date.getFullYear().toString())
.replaceAll("%M", (date.getMonth() + 1).toString().padStart(2, '0'))
.replaceAll("%D", date.getDate().toString().padStart(2, '0'))
.replaceAll("%M", (date.getMonth() + 1).toString().padStart(2, "0"))
.replaceAll("%D", date.getDate().toString().padStart(2, "0"))
.replaceAll("%R", project.url)
.replace(/\/+/g, '/');
.replace(/\/+/g, "/");
}
export function getCategoryRoute(photoSettings: WebsitePhotoSettings, category: PhotoAlbumCategory) {
return photoSettings.category.routeTemplate
.replaceAll("%C", category.url)
.replace(/\/+/g, '/');
.replace(/\/+/g, "/");
}
export function getAlbumRoute(photoSettings: WebsitePhotoSettings, album: PhotoAlbum) {
@@ -127,11 +127,11 @@ export function getAlbumRoute(photoSettings: WebsitePhotoSettings, album: PhotoA
return photoSettings.album.routeTemplate
.replaceAll("%Y", date.getFullYear().toString())
.replaceAll("%M", (date.getMonth() + 1).toString().padStart(2, '0'))
.replaceAll("%D", date.getDate().toString().padStart(2, '0'))
.replaceAll("%M", (date.getMonth() + 1).toString().padStart(2, "0"))
.replaceAll("%D", date.getDate().toString().padStart(2, "0"))
.replaceAll("%C", album.category.url)
.replaceAll("%R", album.url)
.replace(/\/+/g, '/');
.replace(/\/+/g, "/");
}
export function getPhotoRoute(photoSettings: WebsitePhotoSettings, album: PhotoAlbum, photo: PhotoAlbumPhoto) {
@@ -139,10 +139,10 @@ export function getPhotoRoute(photoSettings: WebsitePhotoSettings, album: PhotoA
return photoSettings.photo.routeTemplate
.replaceAll("%Y", date.getFullYear().toString())
.replaceAll("%M", (date.getMonth() + 1).toString().padStart(2, '0'))
.replaceAll("%D", date.getDate().toString().padStart(2, '0'))
.replaceAll("%M", (date.getMonth() + 1).toString().padStart(2, "0"))
.replaceAll("%D", date.getDate().toString().padStart(2, "0"))
.replaceAll("%C", album.category.url)
.replaceAll("%R", album.url)
.replaceAll("%H", getPhotoHash(photo))
.replace(/\/+/g, '/');
.replace(/\/+/g, "/");
}

View File

@@ -5,7 +5,7 @@ import { getSettings } from "@/content/settings/settings"
import WebpageLayout from "@/layouts/WebpageLayout.astro";
import BlogLayout from "@/layouts/BlogLayout.astro";
import ProjectLayout from "@/layouts/ProjectLayout.astro";
import PhotoLayout from '@/layouts/PhotoLayout.astro';
import PhotoLayout from "@/layouts/PhotoLayout.astro";
import BlogIndex from "@/components/blogs/BlogIndex.astro";
import ProjectIndex from "@/components/projects/ProjectIndex.astro";
import Webpage from "@/components/webpage/Webpage.astro";
@@ -15,6 +15,7 @@ import CategoryIndex from "@/components/photos/CategoryIndex.astro";
import Category from "@/components/photos/Category.astro";
import AlbumPage from "@/components/photos/Album.astro";
import { getImageUrl } from "@/lib/images";
import Photo from "@/components/photos/Photo.astro";
export async function getStaticPaths() {
const settings = await getSettings();
@@ -153,7 +154,7 @@ if (page === null || page.page === null || !page.page.exists) {
allowCrawlers: true,
canonical: null,
priority: 65,
thumbnail: page.page.category.thumbnail
thumbnail: page.page.thumbnail
}}}>
<Fragment slot="content">
<AlbumPage page={page.page} />
@@ -176,7 +177,7 @@ if (page === null || page.page === null || !page.page.exists) {
}
}}}>
<Fragment slot="content">
<div>{JSON.stringify(page.page)}</div>
<Photo page={page.page} />
</Fragment>
</PhotoLayout>
) }

View File

@@ -7,8 +7,8 @@ export const GET = (async () => {
const robots = await getRobotsSettings();
let crawlers = [
{ id: 'google', name: 'Googlebot' },
{ id: 'bing', name: "Bingbot" },
{ id: "google", name: "Googlebot" },
{ id: "bing", name: "Bingbot" },
{ id: "slurp", name: "Slurp" },
{ id: "duckduckgo", name: "DuckDuckBot" },
{ id: "baidu", name: "Baiduspider" },

View File

@@ -39,7 +39,7 @@ export const GET = (async ({ params }) => {
<loc>${settings.website.domainName}${page.url}</loc>
<lastmod>${page.lastModified.toISOString()}</lastmod>
</sitemap>
`).join('')}
`).join("")}
</sitemapindex>
`;

View File

@@ -54,7 +54,7 @@ export const GET = (async () => {
<loc>${settings.website.domainName}${item.url}</loc>
<lastmod>${item.lastModified.toISOString()}</lastmod>
</sitemap>
`).join('')}
`).join("")}
</sitemapindex>
`;

View File

@@ -39,7 +39,7 @@ export const GET = (async ({ params }) => {
<loc>${settings.website.domainName}${page.url}</loc>
<lastmod>${page.lastModified.toISOString()}</lastmod>
</sitemap>
`).join('')}
`).join("")}
</sitemapindex>
`;

View File

@@ -54,7 +54,7 @@ export const GET = (async () => {
<loc>${settings.website.domainName}${item.url}</loc>
<lastmod>${item.lastModified.toISOString()}</lastmod>
</sitemap>
`).join('')}
`).join("")}
</sitemapindex>
`;

View File

@@ -90,7 +90,7 @@ export const GET = (async () => {
<loc>${settings.website.domainName}${item.url}</loc>
<lastmod>${item.lastModified.toISOString()}</lastmod>
</sitemap>
`).join('')}
`).join("")}
</sitemapindex>
`;

View File

@@ -33,7 +33,7 @@ export const GET = (async ({ params }) => {
<loc>${settings.website.domainName}${page.url}</loc>
<lastmod>${page.lastModified.toISOString()}</lastmod>
</sitemap>
`).join('')}
`).join("")}
</sitemapindex>
`;

View File

@@ -28,7 +28,7 @@ export const GET = (async () => {
<loc>${settings.website.domainName}${item.url}</loc>
<lastmod>${item.lastModified.toISOString()}</lastmod>
</sitemap>
`).join('')}
`).join("")}
</sitemapindex>
`;

View File

@@ -39,7 +39,7 @@ export const GET = (async ({ params }) => {
<loc>${settings.website.domainName}${page.url}</loc>
<lastmod>${page.lastModified.toISOString()}</lastmod>
</sitemap>
`).join('')}
`).join("")}
</sitemapindex>
`;

View File

@@ -54,7 +54,7 @@ export const GET = (async () => {
<loc>${settings.website.domainName}${item.url}</loc>
<lastmod>${item.lastModified.toISOString()}</lastmod>
</sitemap>
`).join('')}
`).join("")}
</sitemapindex>
`;

View File

@@ -8,6 +8,8 @@ type BlogPost = {
date: string;
content: string;
thumbnail: PhotoProps;
tags: Tag[];
searchEngine: SearchEngine;

View File

@@ -8,6 +8,8 @@ type ProjectPost = {
date: string;
content: string;
thumbnail: PhotoProps;
tags: Tag[];
searchEngine: SearchEngine;