import * as ImageManipulator from 'expo-image-manipulator'; /** * Optimize image for avatar upload * - Resize to max 400x400 * - Compress to JPEG with 80% quality * - Returns optimized URI */ export async function optimizeAvatarImage(uri: string): Promise { try { const result = await ImageManipulator.manipulateAsync( uri, [ { resize: { width: 400, height: 400 } }, ], { compress: 0.8, format: ImageManipulator.SaveFormat.JPEG, } ); return result.uri; } catch (error) { // Return original if optimization fails return uri; } } /** * Optimize image for general upload with custom size */ export async function optimizeImage( uri: string, maxSize: number = 800, quality: number = 0.8 ): Promise { try { const result = await ImageManipulator.manipulateAsync( uri, [ { resize: { width: maxSize, height: maxSize } }, ], { compress: quality, format: ImageManipulator.SaveFormat.JPEG, } ); return result.uri; } catch (error) { return uri; } } /** * Add cache-busting query parameter to image URL * Prevents browsers and React Native Image from caching old versions * * @param uri - Image URI (can be http, https, or file://) * @param timestamp - Optional timestamp (defaults to current time) * @returns URI with cache-busting parameter */ export function bustImageCache(uri: string | null | undefined, timestamp?: number): string | null { if (!uri || uri.trim() === '') { return null; } // Don't add cache buster to local file URIs (file://) if (uri.startsWith('file://')) { return uri; } // Don't add cache buster to data URIs if (uri.startsWith('data:')) { return uri; } // Don't add cache buster to placeholder images if (uri.includes('placeholder')) { return uri; } const cacheBuster = timestamp || Date.now(); const separator = uri.includes('?') ? '&' : '?'; return `${uri}${separator}t=${cacheBuster}`; }