Create base for Albums
This commit is contained in:
7685
astro/package-lock.json
generated
7685
astro/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@astrojs/preact": "^5.1.2",
|
"@astrojs/preact": "^5.1.2",
|
||||||
"@directus/sdk": "^21.2.0",
|
"@directus/sdk": "^21.2.0",
|
||||||
|
"@immich/justified-layout-wasm": "^0.4.3",
|
||||||
"@rollup/plugin-graphql": "^2.0.5",
|
"@rollup/plugin-graphql": "^2.0.5",
|
||||||
"@tailwindcss/typography": "^0.5.19",
|
"@tailwindcss/typography": "^0.5.19",
|
||||||
"@tailwindcss/vite": "^4.2.4",
|
"@tailwindcss/vite": "^4.2.4",
|
||||||
@@ -26,7 +27,7 @@
|
|||||||
"reading-time": "^1.5.0",
|
"reading-time": "^1.5.0",
|
||||||
"tailwindcss": "^4.2.4",
|
"tailwindcss": "^4.2.4",
|
||||||
"tslib": "^2.8.1",
|
"tslib": "^2.8.1",
|
||||||
"vite": "^8.0.9 "
|
"vite": "^8.0.9"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/markdown-it": "^14.1.2",
|
"@types/markdown-it": "^14.1.2",
|
||||||
|
|||||||
41
astro/src/components/photos/Album.astro
Normal file
41
astro/src/components/photos/Album.astro
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
import { getAlbumRoute, getPhotoRoute } from '@/lib/routing';
|
||||||
|
import { AlbumPhotos } from './Album.tsx';
|
||||||
|
import { getImageSize, getImageUrl } from '@/lib/images';
|
||||||
|
import { getSettings } from '@/content/settings/settings';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
album: PhotoAlbum;
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = await getSettings();
|
||||||
|
const album = Astro.props.album;
|
||||||
|
|
||||||
|
const remappedPhotos: PhotoAlbumGalleryItem[] = [];
|
||||||
|
|
||||||
|
album.photos.forEach((photo) => {
|
||||||
|
const resizedImage = getImageSize(photo.photo.width, photo.photo.height, 0.67);
|
||||||
|
|
||||||
|
remappedPhotos.push({
|
||||||
|
id: photo.id,
|
||||||
|
url: getPhotoRoute(settings.photo, album, photo),
|
||||||
|
photo: {
|
||||||
|
url: getImageUrl(photo.photo.url),
|
||||||
|
width: resizedImage.width,
|
||||||
|
height: resizedImage.height
|
||||||
|
},
|
||||||
|
text: photo.text
|
||||||
|
});
|
||||||
|
});
|
||||||
|
---
|
||||||
|
|
||||||
|
<div
|
||||||
|
id={`album-${album.id}`}
|
||||||
|
class="flex lg:flex-col flex-col py-12 px-12 lg:container mx-auto gap-y-10 gap-x-18 w-full"
|
||||||
|
>
|
||||||
|
<div class="flex flex-col gap-7">
|
||||||
|
<h1 class="text-5xl font-bold">{album.title}</h1>
|
||||||
|
|
||||||
|
<AlbumPhotos client:only client:load photos={remappedPhotos} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
84
astro/src/components/photos/Album.tsx
Normal file
84
astro/src/components/photos/Album.tsx
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { useEffect, useLayoutEffect, useRef, useState } from "preact/hooks";
|
||||||
|
import { JustifiedLayout } from '@immich/justified-layout-wasm';
|
||||||
|
|
||||||
|
export function AlbumPhotos(props: { photos: PhotoAlbumGalleryItem[] }) {
|
||||||
|
const containerRef = useRef(null);
|
||||||
|
|
||||||
|
const [ hasMounted, setHasMounted ] = useState<boolean>(false);
|
||||||
|
const [ layout, setLayout ] = useState<JustifiedLayout | null>(null);
|
||||||
|
const [ containerWidth, setContainerWidth ] = useState<number | null>(null);
|
||||||
|
const [ containerHeight, setContainerHeight ] = useState<number | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setHasMounted(true);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
if (!hasMounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new ResizeObserver((entries) => {
|
||||||
|
setContainerWidth(entries[0].contentRect.width);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (containerRef.current) {
|
||||||
|
observer.observe(containerRef.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, [ hasMounted ]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (containerWidth === null || !hasMounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const aspectRatios = new Float32Array(props.photos.map((photo => photo.photo.width / photo.photo.height)));
|
||||||
|
|
||||||
|
const justifiedLayout = new JustifiedLayout(aspectRatios, {
|
||||||
|
rowHeight: 265,
|
||||||
|
rowWidth: containerWidth,
|
||||||
|
spacing: 4,
|
||||||
|
heightTolerance: 0.11
|
||||||
|
});
|
||||||
|
|
||||||
|
setContainerHeight(justifiedLayout.containerHeight);
|
||||||
|
setLayout(justifiedLayout);
|
||||||
|
}, [ containerWidth, hasMounted ])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={containerRef} id={`albumgallery`}>
|
||||||
|
{ layout !== null ? (
|
||||||
|
<div class="relative w-full" style={{ height: containerHeight }}>
|
||||||
|
{ props.photos.map((photo, index: number) => {
|
||||||
|
const layoutPosition = layout.getPosition(index);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href={photo.url}
|
||||||
|
key={`photo-${index}`}
|
||||||
|
class="group absolute overflow-hidden bg-neutral-200"
|
||||||
|
style={{
|
||||||
|
top: layoutPosition.top,
|
||||||
|
left: layoutPosition.left,
|
||||||
|
width: layoutPosition.width,
|
||||||
|
height: layoutPosition.height
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={photo.photo.url}
|
||||||
|
alt={photo.text ?? ""}
|
||||||
|
class="group-hover:scale-[101.5%] duration-200 w-full h-full"
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}) }
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div>Loading...</div>
|
||||||
|
) }
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -33,6 +33,7 @@ export async function getAllAlbums(settings: GlobalSettings): Promise<PhotoAlbum
|
|||||||
const album: PhotoAlbum = {
|
const album: PhotoAlbum = {
|
||||||
exists: true,
|
exists: true,
|
||||||
type: "PhotoAlbum",
|
type: "PhotoAlbum",
|
||||||
|
id: albumRecord["id"],
|
||||||
title: albumRecord["title"],
|
title: albumRecord["title"],
|
||||||
description: albumRecord["description"],
|
description: albumRecord["description"],
|
||||||
url: albumRecord["url"],
|
url: albumRecord["url"],
|
||||||
@@ -116,6 +117,7 @@ export async function getAlbum(settings: GlobalSettings, route: string): Promise
|
|||||||
const album: PhotoAlbum = {
|
const album: PhotoAlbum = {
|
||||||
exists: true,
|
exists: true,
|
||||||
type: "PhotoAlbum",
|
type: "PhotoAlbum",
|
||||||
|
id: albumRecord["id"],
|
||||||
title: albumRecord["title"],
|
title: albumRecord["title"],
|
||||||
description: albumRecord["description"],
|
description: albumRecord["description"],
|
||||||
url: albumRecord["url"],
|
url: albumRecord["url"],
|
||||||
@@ -196,6 +198,7 @@ export async function getLastAlbums(amount: number) {
|
|||||||
const album: PhotoAlbum = {
|
const album: PhotoAlbum = {
|
||||||
exists: true,
|
exists: true,
|
||||||
type: "PhotoAlbum",
|
type: "PhotoAlbum",
|
||||||
|
id: albumRecord["id"],
|
||||||
title: albumRecord["title"],
|
title: albumRecord["title"],
|
||||||
description: albumRecord["description"],
|
description: albumRecord["description"],
|
||||||
url: albumRecord["url"],
|
url: albumRecord["url"],
|
||||||
@@ -281,6 +284,7 @@ export async function getCategoryAlbums(settings: GlobalSettings, route: string)
|
|||||||
const album: PhotoAlbum = {
|
const album: PhotoAlbum = {
|
||||||
exists: true,
|
exists: true,
|
||||||
type: "PhotoAlbum",
|
type: "PhotoAlbum",
|
||||||
|
id: albumRecord["id"],
|
||||||
title: albumRecord["title"],
|
title: albumRecord["title"],
|
||||||
description: albumRecord["description"],
|
description: albumRecord["description"],
|
||||||
url: albumRecord["url"],
|
url: albumRecord["url"],
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import BlogPost from "@/components/blogs/BlogPost.astro";
|
|||||||
import ProjectPost from "@/components/projects/ProjectPost.astro";
|
import ProjectPost from "@/components/projects/ProjectPost.astro";
|
||||||
import CategoryIndex from "@/components/photos/CategoryIndex.astro";
|
import CategoryIndex from "@/components/photos/CategoryIndex.astro";
|
||||||
import Category from "@/components/photos/Category.astro";
|
import Category from "@/components/photos/Category.astro";
|
||||||
|
import AlbumPage from "@/components/photos/Album.astro";
|
||||||
import { getImageUrl } from "@/lib/images";
|
import { getImageUrl } from "@/lib/images";
|
||||||
import AlbumPage from "@/components/photos/AlbumPage.astro";
|
|
||||||
|
|
||||||
export async function getStaticPaths() {
|
export async function getStaticPaths() {
|
||||||
const settings = await getSettings();
|
const settings = await getSettings();
|
||||||
@@ -148,7 +148,7 @@ if (page === null || page.page === null || !page.page.exists) {
|
|||||||
{ page.pageType === "PhotoAlbum" && (
|
{ page.pageType === "PhotoAlbum" && (
|
||||||
<WebpageLayout settings={{
|
<WebpageLayout settings={{
|
||||||
searchEngine: {
|
searchEngine: {
|
||||||
title: page.page.category.title,
|
title: page.page.title,
|
||||||
description: `See the photos in the ${page.page.category.title.toLowerCase()} category.`,
|
description: `See the photos in the ${page.page.category.title.toLowerCase()} category.`,
|
||||||
allowCrawlers: true,
|
allowCrawlers: true,
|
||||||
canonical: null,
|
canonical: null,
|
||||||
|
|||||||
2
astro/src/types/photos/album.d.ts
vendored
2
astro/src/types/photos/album.d.ts
vendored
@@ -2,6 +2,8 @@ type PhotoAlbum = {
|
|||||||
type: "PhotoAlbum";
|
type: "PhotoAlbum";
|
||||||
exists: boolean;
|
exists: boolean;
|
||||||
|
|
||||||
|
id: string;
|
||||||
|
|
||||||
title: string;
|
title: string;
|
||||||
url: string;
|
url: string;
|
||||||
description: string | null;
|
description: string | null;
|
||||||
|
|||||||
7
astro/src/types/photos/photo.d.ts
vendored
7
astro/src/types/photos/photo.d.ts
vendored
@@ -15,3 +15,10 @@ type PhotoAlbumItem = {
|
|||||||
photo: PhotoProps;
|
photo: PhotoProps;
|
||||||
url: string;
|
url: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PhotoAlbumGalleryItem = {
|
||||||
|
id: string;
|
||||||
|
photo: PhotoProps;
|
||||||
|
text: string | null;
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user