import { useEffect, useRef, useState } from 'react'; import { MatrixClient } from 'matrix-js-sdk'; import { IEncryptedFile } from '../../types/matrix/common'; import { decryptFile, downloadEncryptedMedia, mxcUrlToHttp } from '../utils/matrix'; type DecryptState = { status: 'loading' } | { status: 'ok'; url: string } | { status: 'error' }; export function useDecryptedMediaUrl( mx: MatrixClient, mxcUrl: string | undefined, encInfo: IEncryptedFile | undefined, useAuthentication: boolean, mimeType?: string, enabled = true, ): DecryptState { const [state, setState] = useState({ status: 'loading' }); const prevBlobUrl = useRef(null); useEffect(() => { if (!enabled) return undefined; if (!mxcUrl) { setState({ status: 'error' }); return; } let cancelled = false; setState({ status: 'loading' }); const run = async () => { const httpUrl = mxcUrlToHttp(mx, mxcUrl, useAuthentication); if (!httpUrl) throw new Error('bad url'); if (encInfo) { const blob = await downloadEncryptedMedia(httpUrl, (buf) => decryptFile(buf, mimeType ?? 'application/octet-stream', encInfo), ); const blobUrl = URL.createObjectURL(blob); if (cancelled) { URL.revokeObjectURL(blobUrl); return; } if (prevBlobUrl.current) URL.revokeObjectURL(prevBlobUrl.current); prevBlobUrl.current = blobUrl; setState({ status: 'ok', url: blobUrl }); } else { setState({ status: 'ok', url: httpUrl }); } }; run().catch(() => { if (!cancelled) setState({ status: 'error' }); }); return () => { cancelled = true; }; }, [mx, mxcUrl, encInfo, useAuthentication, mimeType, enabled]); useEffect( () => () => { if (prevBlobUrl.current) URL.revokeObjectURL(prevBlobUrl.current); }, [], ); return state; }