fix(photos): fix SQL type mismatch, pagination panic, missing icons, and add day/month/year grouping

- Fix photos endpoint 500: remove ::uuid cast on user_id WHERE clause (VARCHAR column)
- Fix pagination underflow panic when total_pages is 0
- Add missing 'images' and 'play' icons to SVG icon registry
- Add day/month/year grouping toggle with localStorage persistence
- Improve grid spacing and group header styling per mode
- Add i18n translations for grouping labels (all 9 locales)
This commit is contained in:
Jared Wolff
2026-03-05 14:46:30 -05:00
parent 53e4f5afe6
commit 6a84a5c44e
22 changed files with 220 additions and 64 deletions
+15 -5
View File
@@ -61,13 +61,23 @@ impl ExifService {
// ── Camera info ──
if let Some(field) = exif.get_field(Tag::Make, In::PRIMARY) {
let val = field.display_value().to_string().trim_matches('"').trim().to_string();
let val = field
.display_value()
.to_string()
.trim_matches('"')
.trim()
.to_string();
if !val.is_empty() {
meta.camera_make = Some(val);
}
}
if let Some(field) = exif.get_field(Tag::Model, In::PRIMARY) {
let val = field.display_value().to_string().trim_matches('"').trim().to_string();
let val = field
.display_value()
.to_string()
.trim_matches('"')
.trim()
.to_string();
if !val.is_empty() {
meta.camera_model = Some(val);
}
@@ -159,7 +169,7 @@ fn parse_u32_value(value: &exif::Value) -> Option<u32> {
/// to match the intended orientation.
pub fn apply_orientation(img: image::DynamicImage, orientation: u16) -> image::DynamicImage {
match orientation {
1 => img, // Normal
1 => img, // Normal
2 => image::DynamicImage::from(image::imageops::flip_horizontal(&img)), // Mirror horizontal
3 => image::DynamicImage::from(image::imageops::rotate180(&img)), // Rotate 180°
4 => image::DynamicImage::from(image::imageops::flip_vertical(&img)), // Mirror vertical
@@ -168,13 +178,13 @@ pub fn apply_orientation(img: image::DynamicImage, orientation: u16) -> image::D
let flipped = image::imageops::flip_horizontal(&img);
image::DynamicImage::from(image::imageops::rotate270(&flipped))
}
6 => image::DynamicImage::from(image::imageops::rotate90(&img)), // Rotate 90° CW
6 => image::DynamicImage::from(image::imageops::rotate90(&img)), // Rotate 90° CW
7 => {
// Transverse: flip horizontal then rotate 90°
let flipped = image::imageops::flip_horizontal(&img);
image::DynamicImage::from(image::imageops::rotate90(&flipped))
}
8 => image::DynamicImage::from(image::imageops::rotate270(&img)), // Rotate 270° CW
8 => image::DynamicImage::from(image::imageops::rotate270(&img)), // Rotate 270° CW
_ => img,
}
}
@@ -271,7 +271,9 @@ impl ThumbnailService {
// Apply EXIF orientation so thumbnails display correctly
let img = {
use crate::infrastructure::services::exif_service::{ExifService, apply_orientation};
use crate::infrastructure::services::exif_service::{
ExifService, apply_orientation,
};
let orientation = ExifService::extract(&data)
.and_then(|m| m.orientation)
.unwrap_or(1);
@@ -360,7 +362,9 @@ impl ThumbnailService {
// Apply EXIF orientation so thumbnails display correctly
let img = {
use crate::infrastructure::services::exif_service::{ExifService, apply_orientation};
use crate::infrastructure::services::exif_service::{
ExifService, apply_orientation,
};
let orientation = ExifService::extract(&data)
.and_then(|m| m.orientation)
.unwrap_or(1);