WellNuo/utils/imageUtils.ts
Sergei 74a4c9e8f4 Fix avatar caching after upload with cache-busting
Implemented cache-busting mechanism to prevent stale avatar images
after upload. React Native Image component caches images by URI,
causing old avatars to persist even after successful upload.

Changes:
- Added bustImageCache() utility function in utils/imageUtils.ts
- Appends timestamp query parameter (?t=timestamp) to avatar URLs
- Skips cache-busting for local file://, data: URIs and placeholders
- Applied bustImageCache() to all avatar Image components:
  - Beneficiary detail screen (header, edit modal, lightbox)
  - Beneficiary list cards on dashboard
- Ensured loadBeneficiary() is called after avatar upload completes
- Added comprehensive unit tests for cache-busting logic

Backend already generates unique URLs with timestamps when uploading
to MinIO, but this ensures frontend always requests fresh images.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-29 11:22:49 -08:00

87 lines
2.0 KiB
TypeScript

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<string> {
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<string> {
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}`;
}