Changeset View
Changeset View
Standalone View
Standalone View
native/media/encrypted-image.react.js
// @flow | // @flow | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { MediaCacheContext } from 'lib/components/media-cache-provider.react.js'; | |||||
import { decryptMedia } from './encryption-utils.js'; | import { decryptMedia } from './encryption-utils.js'; | ||||
import LoadableImage from './loadable-image.react.js'; | import LoadableImage from './loadable-image.react.js'; | ||||
import { useSelector } from '../redux/redux-utils.js'; | import { useSelector } from '../redux/redux-utils.js'; | ||||
import type { ImageStyle } from '../types/styles.js'; | import type { ImageStyle } from '../types/styles.js'; | ||||
type BaseProps = { | type BaseProps = { | ||||
+holder: string, | +holder: string, | ||||
+encryptionKey: string, | +encryptionKey: string, | ||||
+onLoad: (uri: string) => void, | +onLoad: (uri: string) => void, | ||||
+spinnerColor: string, | +spinnerColor: string, | ||||
+style: ImageStyle, | +style: ImageStyle, | ||||
+invisibleLoad: boolean, | +invisibleLoad: boolean, | ||||
}; | }; | ||||
type Props = { | type Props = { | ||||
...BaseProps, | ...BaseProps, | ||||
}; | }; | ||||
function EncryptedImage(props: Props): React.Node { | function EncryptedImage(props: Props): React.Node { | ||||
const { holder, encryptionKey, onLoad: onLoadProp } = props; | const { holder, encryptionKey, onLoad: onLoadProp } = props; | ||||
const mediaCache = React.useContext(MediaCacheContext); | |||||
const [source, setSource] = React.useState(null); | const [source, setSource] = React.useState(null); | ||||
const connectionStatus = useSelector(state => state.connection.status); | const connectionStatus = useSelector(state => state.connection.status); | ||||
const prevConnectionStatusRef = React.useRef(connectionStatus); | const prevConnectionStatusRef = React.useRef(connectionStatus); | ||||
const [attempt, setAttempt] = React.useState(0); | const [attempt, setAttempt] = React.useState(0); | ||||
if (prevConnectionStatusRef.current !== connectionStatus) { | if (prevConnectionStatusRef.current !== connectionStatus) { | ||||
if (!source && connectionStatus === 'connected') { | if (!source && connectionStatus === 'connected') { | ||||
setAttempt(attempt + 1); | setAttempt(attempt + 1); | ||||
} | } | ||||
prevConnectionStatusRef.current = connectionStatus; | prevConnectionStatusRef.current = connectionStatus; | ||||
} | } | ||||
React.useEffect(() => { | React.useEffect(() => { | ||||
let isMounted = true; | let isMounted = true; | ||||
setSource(null); | setSource(null); | ||||
const loadDecrypted = async () => { | const loadDecrypted = async () => { | ||||
const cached = await mediaCache?.get(holder); | |||||
if (cached && isMounted) { | |||||
setSource({ uri: cached }); | |||||
return; | |||||
} | |||||
const { result } = await decryptMedia(holder, encryptionKey, { | const { result } = await decryptMedia(holder, encryptionKey, { | ||||
destination: 'data_uri', | destination: 'data_uri', | ||||
}); | }); | ||||
// TODO: decide what to do if decryption fails | // TODO: decide what to do if decryption fails | ||||
if (result.success && isMounted) { | if (result.success && isMounted) { | ||||
mediaCache?.set(holder, result.uri); | |||||
setSource({ uri: result.uri }); | setSource({ uri: result.uri }); | ||||
} | } | ||||
}; | }; | ||||
loadDecrypted(); | loadDecrypted(); | ||||
return () => { | return () => { | ||||
isMounted = false; | isMounted = false; | ||||
}; | }; | ||||
}, [attempt, holder, encryptionKey]); | }, [attempt, holder, encryptionKey, mediaCache]); | ||||
const onLoad = React.useCallback(() => { | const onLoad = React.useCallback(() => { | ||||
onLoadProp && onLoadProp(holder); | onLoadProp && onLoadProp(holder); | ||||
}, [holder, onLoadProp]); | }, [holder, onLoadProp]); | ||||
const { style, spinnerColor, invisibleLoad } = props; | const { style, spinnerColor, invisibleLoad } = props; | ||||
return ( | return ( | ||||
Show All 12 Lines |