Files
thrilltrack-explorer/src-old/lib/photoHelpers.ts

116 lines
3.0 KiB
TypeScript

/**
* Photo Helpers
* Utilities for normalizing and validating photo data from different sources
*/
import type { PhotoItem, NormalizedPhoto, PhotoDataSource } from '@/types/photos';
import type { PhotoSubmissionItem } from '@/types/photo-submissions';
/**
* Type guard: Check if data is a photo submission item
*/
export function isPhotoSubmissionItem(data: any): data is PhotoSubmissionItem {
return (
data &&
typeof data === 'object' &&
'cloudflare_image_id' in data &&
'cloudflare_image_url' in data &&
'order_index' in data
);
}
/**
* Type guard: Check if content is a review with photos
*/
export function isReviewWithPhotos(content: any): boolean {
return (
content &&
typeof content === 'object' &&
Array.isArray(content.photos) &&
content.photos.length > 0 &&
content.photos[0]?.url
);
}
/**
* Normalize photo data from any source to PhotoItem[]
*/
export function normalizePhotoData(source: PhotoDataSource): PhotoItem[] {
switch (source.type) {
case 'review':
return source.photos.map((photo, index) => ({
id: `review-${index}`,
url: photo.url,
filename: photo.filename || `Review photo ${index + 1}`,
caption: photo.caption,
size: photo.size,
type: photo.type,
}));
case 'submission_jsonb':
return source.photos.map((photo, index) => ({
id: `jsonb-${index}`,
url: photo.url,
filename: photo.filename || `Photo ${index + 1}`,
caption: photo.caption,
title: photo.title,
size: photo.size,
type: photo.type,
}));
case 'submission_items':
return source.items.map((item) => ({
id: item.id,
url: item.cloudflare_image_url,
filename: item.filename || `Photo ${item.order_index + 1}`,
caption: item.caption,
title: item.title,
date_taken: item.date_taken,
}));
default:
return [];
}
}
/**
* Convert PhotoSubmissionItem[] to NormalizedPhoto[]
*/
export function normalizePhotoSubmissionItems(
items: PhotoSubmissionItem[]
): NormalizedPhoto[] {
return items.map((item) => ({
id: item.id,
url: item.cloudflare_image_url,
filename: item.filename || `Photo ${item.order_index + 1}`,
caption: item.caption || undefined,
title: item.title || undefined,
date_taken: item.date_taken || undefined,
order_index: item.order_index,
}));
}
/**
* Validate photo URL is from Cloudflare Images
* Supports both old imagedelivery.net and new CDN URLs
*/
export function isValidCloudflareUrl(url: string): boolean {
try {
const urlObj = new URL(url);
return urlObj.hostname.includes('imagedelivery.net') ||
urlObj.hostname === 'cdn.thrillwiki.com';
} catch {
return false;
}
}
/**
* Generate photo alt text from available metadata
*/
export function generatePhotoAlt(photo: PhotoItem | NormalizedPhoto): string {
if (photo.title) return photo.title;
if (photo.caption) return photo.caption;
return photo.filename || 'Photo';
}