UNPKG

expo-cached-image

Version:

Super fast cached image component for react-native applications powered by expo

146 lines 5.42 kB
import { Directory, File } from 'expo-file-system'; import React, { useEffect, useRef, useState } from 'react'; import { Image } from 'react-native'; import * as CONST from './consts'; const CachedImage = (props) => { const { source, cacheKey, placeholderContent, ...rest } = props; const { uri, headers, expiresIn } = source; const sanitizedKey = CONST.sanitizeCacheKey(cacheKey); const file = new File(CONST.IMAGE_CACHE_FOLDER, `${sanitizedKey}.png`); const fileURI = file.uri; const [imgUri, setImgUri] = useState(fileURI); const componentIsMounted = useRef(false); const requestOption = (headers != null) ? { headers } : undefined; useEffect(() => { componentIsMounted.current = true; void loadImageAsync(); return () => { componentIsMounted.current = false; }; }, []); const calculateFileAge = (modificationTime) => { return (Date.now() - modificationTime) / 1000; }; const hasExpiryConfig = () => { return expiresIn != null && expiresIn > 0; }; const isFileExpired = (metadata) => { var _a; if (!metadata.exists || !hasExpiryConfig()) { return false; } const fileAge = calculateFileAge((_a = metadata.modificationTime) !== null && _a !== void 0 ? _a : 0); return fileAge > (expiresIn !== null && expiresIn !== void 0 ? expiresIn : 0); }; const isFileInvalid = (metadata) => { var _a; return !metadata.exists || ((_a = metadata.size) !== null && _a !== void 0 ? _a : 0) === 0; }; const shouldRedownload = (metadata, expired) => { return isFileInvalid(metadata) || expired; }; const downloadAndMoveFile = async () => { const downloaded = await File.downloadFileAsync(uri, new Directory(CONST.IMAGE_CACHE_FOLDER), requestOption); // Move/rename if the server selected a different filename if (downloaded.uri !== fileURI) { new File(downloaded.uri).move(file); } }; const deleteExpiredFile = () => { file.delete(); }; const updateImageUri = (newUri) => { if (componentIsMounted.current) { setImgUri(newUri); } }; const handleCachedImageLoad = async () => { await setImgUri(null); if (!componentIsMounted.current) return; await downloadAndMoveFile(); updateImageUri(`${fileURI}`); }; const handleExpiredImageLoad = async () => { await setImgUri(null); if (!componentIsMounted.current) return; deleteExpiredFile(); await downloadAndMoveFile(); updateImageUri(`${fileURI}`); }; const handleImageLoadError = (err) => { console.error('Error loading image:', err); setImgUri(uri); }; const loadImageAsync = async () => { try { const metadata = file.info(); const expired = isFileExpired(metadata); if (!shouldRedownload(metadata, expired)) { return; } if (expired) { await handleExpiredImageLoad(); } else { await handleCachedImageLoad(); } } catch (err) { handleImageLoadError(err); } }; if (imgUri !== null && imgUri !== '') { return (<Image {...rest} source={{ ...source, uri: imgUri }}/>); } return <>{placeholderContent !== null && placeholderContent !== void 0 ? placeholderContent : null}</>; }; export const CacheManager = { addToCache: async ({ file, key }) => { const sanitizedKey = CONST.sanitizeCacheKey(key); new File(file).copy(new File(CONST.IMAGE_CACHE_FOLDER, `${sanitizedKey}.png`)); return await CacheManager.getCachedUri({ key }); }, getCachedUri: async ({ key }) => { const sanitizedKey = CONST.sanitizeCacheKey(key); return new File(CONST.IMAGE_CACHE_FOLDER, `${sanitizedKey}.png`).uri; }, downloadAsync: async ({ uri, key, options }) => { const sanitizedKey = CONST.sanitizeCacheKey(key); const result = await File.downloadFileAsync(uri, new Directory(CONST.IMAGE_CACHE_FOLDER), options); const target = new File(CONST.IMAGE_CACHE_FOLDER, `${sanitizedKey}.png`); if (result.uri !== target.uri) { new File(result.uri).move(target); } return result; }, getMetadata: async ({ key }) => { var _a, _b; const sanitizedKey = CONST.sanitizeCacheKey(key); const fileRef = new File(CONST.IMAGE_CACHE_FOLDER, `${sanitizedKey}.png`); const fileURI = fileRef.uri; try { const metadata = fileRef.info(); if (!metadata.exists) { return null; } return { exists: metadata.exists, size: (_a = metadata.size) !== null && _a !== void 0 ? _a : 0, modificationTime: new Date((_b = metadata.modificationTime) !== null && _b !== void 0 ? _b : 0), uri: fileURI, isDirectory: false }; } catch (err) { console.error('Error getting cache metadata:', err); return null; } } }; export default CachedImage; //# sourceMappingURL=index.js.map