diff --git a/package-lock.json b/package-lock.json
index 65cb842..bc0c4aa 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -58,6 +58,8 @@
"redis": "^4.7.0",
"sharp": "^0.34.2",
"steamapi": "^3.0.11",
+ "svelte-bricks": "^0.3.2",
+ "svelte-infinite": "^0.5.0",
"svelte-medium-image-zoom": "^0.2.6",
"svelte-portal": "^2.2.1",
"svelte-tiptap": "^2.1.0",
@@ -7809,6 +7811,14 @@
"@types/estree": "^1.0.1"
}
},
+ "node_modules/svelte-bricks": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/svelte-bricks/-/svelte-bricks-0.3.2.tgz",
+ "integrity": "sha512-VKQdeXj0+iRV8U0BWl+5r3W/IzZiA51Z6Zjctj/AYd5PU9MwNraHzj63wzUataKxDXB2AX2vc/bb4LuqywY0/w==",
+ "dependencies": {
+ "svelte": "^5.27.3"
+ }
+ },
"node_modules/svelte-check": {
"version": "3.8.4",
"resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.4.tgz",
@@ -7901,6 +7911,14 @@
"url": "https://opencollective.com/eslint"
}
},
+ "node_modules/svelte-infinite": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/svelte-infinite/-/svelte-infinite-0.5.0.tgz",
+ "integrity": "sha512-3ZomRQcQzg8VWtnqO4MvPC0Jt3hvh1wmC47t64BcI+8UXTl0FJYVfB7ky4d1NJ3mf/KZZa+hcIZJPnV9cOt8gQ==",
+ "peerDependencies": {
+ "svelte": "^5.0.0-0"
+ }
+ },
"node_modules/svelte-medium-image-zoom": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/svelte-medium-image-zoom/-/svelte-medium-image-zoom-0.2.6.tgz",
diff --git a/package.json b/package.json
index bcf5473..85aa070 100644
--- a/package.json
+++ b/package.json
@@ -103,6 +103,8 @@
"redis": "^4.7.0",
"sharp": "^0.34.2",
"steamapi": "^3.0.11",
+ "svelte-bricks": "^0.3.2",
+ "svelte-infinite": "^0.5.0",
"svelte-medium-image-zoom": "^0.2.6",
"svelte-portal": "^2.2.1",
"svelte-tiptap": "^2.1.0",
diff --git a/src/lib/components/MasonryPhotoGrid.svelte b/src/lib/components/MasonryPhotoGrid.svelte
new file mode 100644
index 0000000..44f3278
--- /dev/null
+++ b/src/lib/components/MasonryPhotoGrid.svelte
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+ {#snippet children({ item })}
+
+ {/snippet}
+
+
+
+
\ No newline at end of file
diff --git a/src/routes/api/photos/+server.ts b/src/routes/api/photos/+server.ts
index 5a5b4e1..df68920 100644
--- a/src/routes/api/photos/+server.ts
+++ b/src/routes/api/photos/+server.ts
@@ -35,10 +35,8 @@ export const GET: RequestHandler = async (event) => {
}
}
}
- },
- orderBy: { createdAt: 'desc' },
- skip: offset,
- take: limit
+ }
+ // Remove orderBy to sort everything together later
})
// Fetch individual photos (marked for photography, not in any album)
@@ -63,17 +61,50 @@ export const GET: RequestHandler = async (event) => {
createdAt: true,
photoPublishedAt: true,
exifData: true
- },
- orderBy: { photoPublishedAt: 'desc' },
- skip: offset,
- take: limit
+ }
+ // Remove orderBy to sort everything together later
})
+ // Helper function to extract date from EXIF data
+ const getPhotoDate = (media: any): Date => {
+ // Try to get date from EXIF data
+ if (media.exifData && typeof media.exifData === 'object') {
+ // Check for common EXIF date fields
+ const exif = media.exifData as any
+ const dateTaken = exif.DateTimeOriginal || exif.DateTime || exif.dateTaken
+ if (dateTaken) {
+ // Parse EXIF date format (typically "YYYY:MM:DD HH:MM:SS")
+ const parsedDate = new Date(dateTaken.replace(/^(\d{4}):(\d{2}):(\d{2})/, '$1-$2-$3'))
+ if (!isNaN(parsedDate.getTime())) {
+ return parsedDate
+ }
+ }
+ }
+
+ // Fallback to photoPublishedAt
+ if (media.photoPublishedAt) {
+ return new Date(media.photoPublishedAt)
+ }
+
+ // Final fallback to createdAt
+ return new Date(media.createdAt)
+ }
+
// Transform albums to PhotoAlbum format
const photoAlbums: PhotoAlbum[] = albums
.filter((album) => album.media.length > 0) // Only include albums with media
.map((album) => {
const firstMedia = album.media[0].media
+
+ // Find the most recent EXIF date from all photos in the album
+ let albumDate = new Date(album.createdAt)
+ for (const albumMedia of album.media) {
+ const mediaDate = getPhotoDate(albumMedia.media)
+ if (mediaDate > albumDate) {
+ albumDate = mediaDate
+ }
+ }
+
return {
id: `album-${album.id}`,
slug: album.slug,
@@ -95,24 +126,14 @@ export const GET: RequestHandler = async (event) => {
width: albumMedia.media.width || 400,
height: albumMedia.media.height || 400
})),
- createdAt: album.createdAt.toISOString()
+ createdAt: albumDate.toISOString()
}
})
// Transform individual media to Photo format
const photos: Photo[] = individualMedia.map((media) => {
- // Extract date from EXIF data if available
- let photoDate: string
- if (media.exifData && typeof media.exifData === 'object' && 'dateTaken' in media.exifData) {
- // Use EXIF date if available
- photoDate = media.exifData.dateTaken as string
- } else if (media.photoPublishedAt) {
- // Fall back to published date
- photoDate = media.photoPublishedAt.toISOString()
- } else {
- // Fall back to created date
- photoDate = media.createdAt.toISOString()
- }
+ // Use the same helper function to get the photo date
+ const photoDate = getPhotoDate(media)
return {
id: `media-${media.id}`,
@@ -121,27 +142,32 @@ export const GET: RequestHandler = async (event) => {
caption: media.photoCaption || undefined,
width: media.width || 400,
height: media.height || 400,
- createdAt: photoDate
+ createdAt: photoDate.toISOString()
}
})
// Combine albums and individual photos
- const photoItems: PhotoItem[] = [...photoAlbums, ...photos]
+ let allPhotoItems: PhotoItem[] = [...photoAlbums, ...photos]
// Sort by creation date (both albums and photos now have createdAt)
- photoItems.sort((a, b) => {
+ // Newest first (reverse chronological)
+ allPhotoItems.sort((a, b) => {
const dateA = a.createdAt ? new Date(a.createdAt) : new Date()
const dateB = b.createdAt ? new Date(b.createdAt) : new Date()
return dateB.getTime() - dateA.getTime()
})
+ // Apply pagination after sorting
+ const totalItems = allPhotoItems.length
+ const paginatedItems = allPhotoItems.slice(offset, offset + limit)
+
const response = {
- photoItems,
+ photoItems: paginatedItems,
pagination: {
- total: photoItems.length,
+ total: totalItems,
limit,
offset,
- hasMore: photoItems.length === limit // Simple check, could be more sophisticated
+ hasMore: offset + limit < totalItems
}
}
diff --git a/src/routes/photos/+page.svelte b/src/routes/photos/+page.svelte
index f8425f1..4e918d9 100644
--- a/src/routes/photos/+page.svelte
+++ b/src/routes/photos/+page.svelte
@@ -1,14 +1,76 @@