diff --git a/app/(tabs)/beneficiaries/[id]/index.tsx b/app/(tabs)/beneficiaries/[id]/index.tsx
index 68184af..2cc0179 100644
--- a/app/(tabs)/beneficiaries/[id]/index.tsx
+++ b/app/(tabs)/beneficiaries/[id]/index.tsx
@@ -43,7 +43,6 @@ import {
} from '@/services/BeneficiaryDetailController';
import { BeneficiaryMenu } from '@/components/ui/BeneficiaryMenu';
import { ImageLightbox } from '@/components/ImageLightbox';
-import { bustImageCache } from '@/utils/imageUtils';
import { useDebounce } from '@/hooks/useDebounce';
// WebView Dashboard URL - opens specific deployment directly
@@ -439,7 +438,11 @@ export default function BeneficiaryDetailScreen() {
disabled={!beneficiary.avatar || beneficiary.avatar.trim() === '' || beneficiary.avatar.includes('placeholder')}
>
{beneficiary.avatar && beneficiary.avatar.trim() !== '' && !beneficiary.avatar.includes('placeholder') ? (
-
+
) : (
@@ -575,7 +578,11 @@ export default function BeneficiaryDetailScreen() {
disabled={isSavingEdit}
>
{editForm.avatar ? (
-
+
) : (
@@ -674,7 +681,7 @@ export default function BeneficiaryDetailScreen() {
{/* Avatar Lightbox */}
setLightboxVisible(false)}
/>
diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx
index 40125d9..2305f47 100644
--- a/app/(tabs)/index.tsx
+++ b/app/(tabs)/index.tsx
@@ -27,7 +27,6 @@ import {
} from '@/constants/theme';
import type { Beneficiary } from '@/types';
import { isSubscriptionActive } from '@/services/subscription';
-import { bustImageCache } from '@/utils/imageUtils';
// Beneficiary card with equipment status support
interface BeneficiaryCardProps {
@@ -116,7 +115,11 @@ function BeneficiaryCard({ beneficiary, onPress, onActivate }: BeneficiaryCardPr
{/* Avatar */}
{hasValidAvatar ? (
-
+
) : (
diff --git a/jest.setup.js b/jest.setup.js
index 1247efc..4c3abc3 100644
--- a/jest.setup.js
+++ b/jest.setup.js
@@ -61,6 +61,16 @@ jest.mock('expo-image-picker', () => ({
),
}));
+jest.mock('expo-image-manipulator', () => ({
+ manipulateAsync: jest.fn((uri, actions, options) =>
+ Promise.resolve({ uri: uri })
+ ),
+ SaveFormat: {
+ JPEG: 'jpeg',
+ PNG: 'png',
+ },
+}));
+
// Mock AsyncStorage
jest.mock('@react-native-async-storage/async-storage', () => ({
getItem: jest.fn(),
diff --git a/services/api.ts b/services/api.ts
index 4969017..cc7f789 100644
--- a/services/api.ts
+++ b/services/api.ts
@@ -4,6 +4,7 @@ import * as SecureStore from 'expo-secure-store';
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as wifiPasswordStore from './wifiPasswordStore';
import 'react-native-get-random-values'; // Polyfill for crypto.getRandomValues
+import { bustImageCache } from '@/utils/imageUtils';
// Callback for handling unauthorized responses (401)
let onUnauthorizedCallback: (() => void) | null = null;
@@ -733,7 +734,7 @@ class ApiService {
customName: item.customName || null, // User's custom name for this beneficiary
displayName: item.displayName || item.customName || item.name || item.email, // Server-provided displayName
originalName: item.originalName || item.name, // Original name from beneficiaries table
- avatar: item.avatarUrl || undefined, // Use uploaded avatar from server
+ avatar: bustImageCache(item.avatarUrl) || undefined, // Use uploaded avatar from server with cache-busting
status: 'offline' as const,
email: item.email,
address: typeof item.address === 'string' ? item.address : (item.address?.street ? `${item.address.street}, ${item.address.city}` : undefined),
@@ -779,7 +780,7 @@ class ApiService {
customName: data.customName || null, // User's custom name for this beneficiary
displayName: data.displayName || data.customName || data.name || data.email, // Server-provided displayName
originalName: data.originalName || data.name, // Original name from beneficiaries table
- avatar: data.avatarUrl || undefined,
+ avatar: bustImageCache(data.avatarUrl) || undefined, // Cache-bust avatar URL
status: 'offline' as const,
email: data.email,
address: typeof data.address === 'string' ? data.address : (data.address?.street ? `${data.address.street}, ${data.address.city}` : undefined),