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 { MediaCacheContext } from 'lib/components/media-cache-provider.react.js'; | ||||
import { decryptMedia } from './encryption-utils.js'; | import { decryptBase64, 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, | ||||
+thumbHash?: ?string, | |||||
}; | }; | ||||
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, | |||||
thumbHash: encryptedThumbHash, | |||||
} = props; | |||||
const mediaCache = React.useContext(MediaCacheContext); | 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; | ||||
} | } | ||||
const placeholder = React.useMemo(() => { | |||||
if (!encryptedThumbHash) { | |||||
return null; | |||||
} | |||||
try { | |||||
const decryptedThumbHash = decryptBase64( | |||||
encryptedThumbHash, | |||||
encryptionKey, | |||||
); | |||||
return { thumbhash: decryptedThumbHash }; | |||||
} catch (e) { | |||||
return null; | |||||
} | |||||
}, [encryptedThumbHash, encryptionKey]); | |||||
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); | const cached = await mediaCache?.get(holder); | ||||
if (cached && isMounted) { | if (cached && isMounted) { | ||||
setSource({ uri: cached }); | setSource({ uri: cached }); | ||||
Show All 20 Lines | function EncryptedImage(props: Props): React.Node { | ||||
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 ( | ||||
<LoadableImage | <LoadableImage | ||||
placeholder={placeholder} | |||||
source={source} | source={source} | ||||
onLoad={onLoad} | onLoad={onLoad} | ||||
spinnerColor={spinnerColor} | spinnerColor={spinnerColor} | ||||
style={style} | style={style} | ||||
invisibleLoad={invisibleLoad} | invisibleLoad={invisibleLoad} | ||||
key={attempt} | key={attempt} | ||||
/> | /> | ||||
); | ); | ||||
} | } | ||||
export default EncryptedImage; | export default EncryptedImage; |