({
imageCache = {},
onErrorMsg,
src = "",
isPublic = false,
shareUid,
shareType = "experiment",
...props
})
| 20 | * It caches signed and local URLs to avoid repeated API calls. |
| 21 | * */ |
| 22 | const AuthorizedImage: FC<AuthorizedImageProps> = ({ |
| 23 | imageCache = {}, |
| 24 | onErrorMsg, |
| 25 | src = "", |
| 26 | isPublic = false, |
| 27 | shareUid, |
| 28 | shareType = "experiment", |
| 29 | ...props |
| 30 | }) => { |
| 31 | const [imageSrc, setImageSrc] = useState<string | null>(null); |
| 32 | const [errorMsg, setErrorMsg] = useState<string | null>(null); |
| 33 | |
| 34 | const { apiCall } = useAuth(); |
| 35 | |
| 36 | useEffect(() => { |
| 37 | const fetchData = async (src) => { |
| 38 | try { |
| 39 | const imageData: Blob = await apiCall(new URL(src).pathname); |
| 40 | const imageUrl = URL.createObjectURL(imageData); |
| 41 | imageCache[src] = { url: imageUrl, expiresAt: "never" }; // Local files never expire |
| 42 | setImageSrc(imageUrl); |
| 43 | } catch (e) { |
| 44 | setErrorMsg(e.message); |
| 45 | } |
| 46 | }; |
| 47 | |
| 48 | const fetchSignedUrl = async (originalSrc: string, path: string) => { |
| 49 | try { |
| 50 | let endpoint = isPublic |
| 51 | ? `/upload/public-signed-url/${path}` |
| 52 | : `/upload/signed-url/${path}`; |
| 53 | |
| 54 | // Add shareUid and shareType as query parameters for public endpoints |
| 55 | if (isPublic && shareUid) { |
| 56 | endpoint += `?shareUid=${encodeURIComponent(shareUid)}&shareType=${encodeURIComponent(shareType)}`; |
| 57 | } |
| 58 | |
| 59 | let response: SignedImageUrlResponse; |
| 60 | |
| 61 | if (isPublic) { |
| 62 | // For public endpoints, use fetch without credentials to avoid CORS issues |
| 63 | const res = await fetch(getApiHost() + endpoint); |
| 64 | if (!res.ok) { |
| 65 | const errorData = await res |
| 66 | .json() |
| 67 | .catch(() => ({ message: res.statusText })); |
| 68 | throw new Error( |
| 69 | errorData.message || |
| 70 | `Failed to fetch signed URL: ${res.statusText}`, |
| 71 | ); |
| 72 | } |
| 73 | response = await res.json(); |
| 74 | } else { |
| 75 | // For authenticated endpoints, use apiCall which includes credentials |
| 76 | response = await apiCall<SignedImageUrlResponse>(endpoint); |
| 77 | } |
| 78 | |
| 79 | const { signedUrl, expiresAt } = response; |
nothing calls this directly
no test coverage detected
searching dependent graphs…