Create base for Albums
This commit is contained in:
7677
astro/package-lock.json
generated
7677
astro/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@
|
||||
"dependencies": {
|
||||
"@astrojs/preact": "^5.1.2",
|
||||
"@directus/sdk": "^21.2.0",
|
||||
"@immich/justified-layout-wasm": "^0.4.3",
|
||||
"@rollup/plugin-graphql": "^2.0.5",
|
||||
"@tailwindcss/typography": "^0.5.19",
|
||||
"@tailwindcss/vite": "^4.2.4",
|
||||
|
||||
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 = {
|
||||
exists: true,
|
||||
type: "PhotoAlbum",
|
||||
id: albumRecord["id"],
|
||||
title: albumRecord["title"],
|
||||
description: albumRecord["description"],
|
||||
url: albumRecord["url"],
|
||||
@@ -116,6 +117,7 @@ export async function getAlbum(settings: GlobalSettings, route: string): Promise
|
||||
const album: PhotoAlbum = {
|
||||
exists: true,
|
||||
type: "PhotoAlbum",
|
||||
id: albumRecord["id"],
|
||||
title: albumRecord["title"],
|
||||
description: albumRecord["description"],
|
||||
url: albumRecord["url"],
|
||||
@@ -196,6 +198,7 @@ export async function getLastAlbums(amount: number) {
|
||||
const album: PhotoAlbum = {
|
||||
exists: true,
|
||||
type: "PhotoAlbum",
|
||||
id: albumRecord["id"],
|
||||
title: albumRecord["title"],
|
||||
description: albumRecord["description"],
|
||||
url: albumRecord["url"],
|
||||
@@ -281,6 +284,7 @@ export async function getCategoryAlbums(settings: GlobalSettings, route: string)
|
||||
const album: PhotoAlbum = {
|
||||
exists: true,
|
||||
type: "PhotoAlbum",
|
||||
id: albumRecord["id"],
|
||||
title: albumRecord["title"],
|
||||
description: albumRecord["description"],
|
||||
url: albumRecord["url"],
|
||||
|
||||
@@ -13,8 +13,8 @@ import BlogPost from "@/components/blogs/BlogPost.astro";
|
||||
import ProjectPost from "@/components/projects/ProjectPost.astro";
|
||||
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 AlbumPage from "@/components/photos/AlbumPage.astro";
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const settings = await getSettings();
|
||||
@@ -148,7 +148,7 @@ if (page === null || page.page === null || !page.page.exists) {
|
||||
{ page.pageType === "PhotoAlbum" && (
|
||||
<WebpageLayout settings={{
|
||||
searchEngine: {
|
||||
title: page.page.category.title,
|
||||
title: page.page.title,
|
||||
description: `See the photos in the ${page.page.category.title.toLowerCase()} category.`,
|
||||
allowCrawlers: true,
|
||||
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";
|
||||
exists: boolean;
|
||||
|
||||
id: string;
|
||||
|
||||
title: string;
|
||||
url: string;
|
||||
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;
|
||||
url: string;
|
||||
}
|
||||
|
||||
type PhotoAlbumGalleryItem = {
|
||||
id: string;
|
||||
photo: PhotoProps;
|
||||
text: string | null;
|
||||
url: string;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user