diff --git a/native/media/encrypted-image.react.js b/native/media/encrypted-image.react.js new file mode 100644 --- /dev/null +++ b/native/media/encrypted-image.react.js @@ -0,0 +1,120 @@ +// @flow + +import { Image } from 'expo-image'; +import * as React from 'react'; +import { View, StyleSheet, ActivityIndicator } from 'react-native'; + +import { decryptMedia } from './encryption-utils.js'; +import { useSelector } from '../redux/redux-utils.js'; +import type { ImageStyle } from '../types/styles.js'; + +type BaseProps = { + +holder: string, + +encryptionKey: string, + +onLoad: (uri: string) => void, + +spinnerColor: string, + +style: ImageStyle, + +invisibleLoad: boolean, +}; +type Props = { + ...BaseProps, +}; + +function EncryptedImage(props: Props): React.Node { + const { holder, encryptionKey, onLoad: onLoadProp } = props; + + const connectionStatus = useSelector(state => state.connection.status); + const [prevConnectionStatus, setPrevConnectionStatus] = + React.useState(connectionStatus); + const [attempt, setAttempt] = React.useState(0); + + const [source, setSource] = React.useState(null); + + if (connectionStatus !== prevConnectionStatus) { + // attempt reload after connection is restored + if (!source && connectionStatus === 'connected') { + setAttempt(it => it + 1); + } + setPrevConnectionStatus(connectionStatus); + } + + React.useEffect(() => { + let isMounted = true; + setSource(null); + + const loadDecrypted = async () => { + const { result } = await decryptMedia(holder, encryptionKey, { + destination: 'data_uri', + }); + // TODO: decide what to do if decryption fails + if (result.success && isMounted) { + setSource({ uri: result.uri }); + } + }; + + loadDecrypted(); + + return () => { + isMounted = false; + }; + }, [attempt, holder, encryptionKey]); + + const onLoad = React.useCallback(() => { + onLoadProp && onLoadProp(holder); + }, [holder, onLoadProp]); + + if (source) { + return ( + + ); + } + if (props.invisibleLoad) { + return ( + + ); + } + + return ( + + + + + + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + invisible: { + opacity: 0, + }, + spinnerContainer: { + alignItems: 'center', + bottom: 0, + justifyContent: 'center', + left: 0, + position: 'absolute', + right: 0, + top: 0, + }, +}); + +export default EncryptedImage;