Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 33 additions & 4 deletions app/components/expo_image/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import React, {forwardRef, useMemo} from 'react';
import Animated from 'react-native-reanimated';

import {useServerUrl} from '@context/server';
import NetworkManager from '@managers/network_manager';
import {urlSafeBase64Encode} from '@utils/security';

type ExpoImagePropsWithId = ImageProps & {id: string};
Expand All @@ -16,8 +17,24 @@ type ExpoImageBackgroundPropsWithId = ImageBackgroundProps & {id: string};
type ExpoImageBackgroundPropsMemoryOnly = ImageBackgroundProps & {cachePolicy: 'memory'; id?: string};
type ExpoImageBackgroundProps = ExpoImageBackgroundPropsWithId | ExpoImageBackgroundPropsMemoryOnly;

function shouldAttachServerAuthHeaders(uri: string | undefined, serverUrl: string) {
if (!uri) {
return false;
}

return uri.startsWith(serverUrl) && uri.includes('/api/v4/');
}

const ExpoImage = forwardRef<Image, ExpoImageProps>(({id, ...props}, ref) => {
const serverUrl = useServerUrl();
const requestHeaders = useMemo(() => {
try {
const client = NetworkManager.getClient(serverUrl);
return client.getRequestHeaders('GET');
} catch {
return undefined;
}
}, [serverUrl]);

/**
* SECURITY NOTE: cachePath uses base64 encoding for URL safety, NOT encryption.
Expand All @@ -30,35 +47,47 @@ const ExpoImage = forwardRef<Image, ExpoImageProps>(({id, ...props}, ref) => {
return props.source;
}

const sourceHeaders = shouldAttachServerAuthHeaders(props.source?.uri, serverUrl) && requestHeaders ? {...requestHeaders, ...props.source?.headers} : props.source?.headers;

// Only add cacheKey and cachePath if id is provided (i.e., not memory-only caching)
if (id) {
return {
...props.source,
headers: sourceHeaders,
cacheKey: id,
cachePath,
};
}

return props.source;
}, [id, props.source, cachePath]);
return {
...props.source,
headers: sourceHeaders,
};
}, [id, props.source, cachePath, requestHeaders, serverUrl]);

// Process placeholder to add cachePath and cacheKey if it has a uri
const placeholder: ImageSource | undefined = useMemo(() => {
if (!props.placeholder || typeof props.placeholder === 'number' || typeof props.placeholder === 'string') {
return props.placeholder;
}

const placeholderHeaders = shouldAttachServerAuthHeaders(props.placeholder?.uri, serverUrl) && requestHeaders ? {...requestHeaders, ...props.placeholder?.headers} : props.placeholder?.headers;

// If placeholder has a uri and id is provided, add cachePath and cacheKey
if (props.placeholder.uri && id) {
return {
...props.placeholder,
headers: placeholderHeaders,
cacheKey: `${id}-thumb`,
cachePath,
};
}

return props.placeholder;
}, [props.placeholder, id, cachePath]);
return {
...props.placeholder,
headers: placeholderHeaders,
};
}, [props.placeholder, id, cachePath, requestHeaders, serverUrl]);

return (
<Image
Expand Down
Loading