diff --git a/web/media/multimedia-modal.react.js b/web/media/multimedia-modal.react.js index 90526241f..336389fd3 100644 --- a/web/media/multimedia-modal.react.js +++ b/web/media/multimedia-modal.react.js @@ -1,145 +1,145 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import { fetchableMediaURI } from 'lib/media/media-utils.js'; import type { EncryptedMediaType, MediaType, Dimensions, } from 'lib/types/media-types.js'; import EncryptedMultimedia from './encrypted-multimedia.react.js'; import LoadableVideo from './loadable-video.react.js'; import { usePlaceholder } from './media-utils.js'; import FullScreenViewModal from '../modals/full-screen-view-modal.react.js'; type MediaInfo = | { +type: MediaType, +uri: string, +dimensions: ?Dimensions, +thumbHash: ?string, +thumbnailURI: ?string, } | { +type: EncryptedMediaType, +blobURI: string, +encryptionKey: string, +dimensions: ?Dimensions, +thumbHash: ?string, +thumbnailBlobURI: ?string, +thumbnailEncryptionKey: ?string, }; type Props = { +media: MediaInfo, }; function MultimediaModal(props: Props): React.Node { const { media } = props; const thumbHashEncryptionKey = media.thumbnailEncryptionKey ?? media.encryptionKey; const placeholderImage = usePlaceholder( media.thumbHash, thumbHashEncryptionKey, ); const [dimensions, setDimensions] = React.useState(null); const photo = React.useMemo(() => { if (media.type !== 'photo') { return null; } const uri = fetchableMediaURI(media.uri); const style = { backgroundImage: placeholderImage ? `url(${placeholderImage})` : undefined, }; return ; }, [media.type, media.uri, placeholderImage]); const video = React.useMemo(() => { if (media.type !== 'video') { return null; } const uri = fetchableMediaURI(media.uri); const { thumbnailURI } = media; invariant(thumbnailURI, 'video missing thumbnail'); return ( ); }, [media, placeholderImage]); const encryptedMultimedia = React.useMemo(() => { if (media.type !== 'encrypted_photo' && media.type !== 'encrypted_video') { return null; } const { type, blobURI, encryptionKey, thumbnailBlobURI, thumbnailEncryptionKey, } = media; const contentDimensions = dimensions ?? media.dimensions; const elementStyle = contentDimensions ? { width: `${contentDimensions.width}px`, height: `${contentDimensions.height}px`, } : undefined; return ( ); }, [dimensions, media, placeholderImage]); const mediaModalItem = React.useMemo(() => { if (media.type === 'photo') { return photo; } else if (media.type === 'video') { return video; } else { return encryptedMultimedia; } }, [encryptedMultimedia, media.type, photo, video]); const multimediaModal = React.useMemo( () => ( {mediaModalItem} ), [dimensions, mediaModalItem], ); return multimediaModal; } export default MultimediaModal; diff --git a/web/modals/full-screen-view-modal.react.js b/web/modals/full-screen-view-modal.react.js index 0cf361dcf..024596f8a 100644 --- a/web/modals/full-screen-view-modal.react.js +++ b/web/modals/full-screen-view-modal.react.js @@ -1,116 +1,128 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import { XCircle as XCircleIcon } from 'react-feather'; import { useModalContext } from 'lib/components/modal-provider.react.js'; import type { SetState } from 'lib/types/hook-types.js'; import type { Dimensions } from 'lib/types/media-types.js'; import css from './full-screen-view-modal.css'; type BaseProps = { +children: React.Node, - +contentDimensions: ?Dimensions, - +setContentDimensions: SetState, + +dynamicContentDimensions?: ?Dimensions, + +setDynamicContentDimensions?: SetState, }; type Props = { ...BaseProps, +popModal: (modal: ?React.Node) => void, }; class FullScreenViewModal extends React.PureComponent { overlay: ?HTMLDivElement; componentDidMount() { invariant(this.overlay, 'overlay ref unset'); this.overlay.focus(); - this.calculateMediaDimensions(); - window.addEventListener('resize', this.calculateMediaDimensions); + this.calculateDynamicContentDimensions(); + window.addEventListener('resize', this.calculateDynamicContentDimensions); } componentWillUnmount() { - window.removeEventListener('resize', this.calculateMediaDimensions); + window.removeEventListener( + 'resize', + this.calculateDynamicContentDimensions, + ); } render(): React.Node { return (
{this.props.children}
); } overlayRef: (overlay: ?HTMLDivElement) => void = overlay => { this.overlay = overlay; }; onBackgroundClick: (event: SyntheticEvent) => void = event => { if (event.target === this.overlay) { this.props.popModal(); } }; onKeyDown: (event: SyntheticKeyboardEvent) => void = event => { if (event.key === 'Escape') { this.props.popModal(); } }; - calculateMediaDimensions: () => void = () => { - if (!this.overlay || !this.props.contentDimensions) { + calculateDynamicContentDimensions: () => mixed = () => { + const { dynamicContentDimensions, setDynamicContentDimensions } = + this.props; + + if ( + !this.overlay || + !dynamicContentDimensions || + !setDynamicContentDimensions + ) { return; } + const containerWidth = this.overlay.clientWidth; const containerHeight = this.overlay.clientHeight; const containerAspectRatio = containerWidth / containerHeight; - const { width: mediaWidth, height: mediaHeight } = - this.props.contentDimensions; - const mediaAspectRatio = mediaWidth / mediaHeight; + const { width: contentWidth, height: contentHeight } = + dynamicContentDimensions; + const contentAspectRatio = contentWidth / contentHeight; let newWidth, newHeight; - if (containerAspectRatio > mediaAspectRatio) { - newWidth = Math.min(mediaWidth, containerHeight * mediaAspectRatio); - newHeight = newWidth / mediaAspectRatio; + if (containerAspectRatio > contentAspectRatio) { + newWidth = Math.min(contentWidth, containerHeight * contentAspectRatio); + newHeight = newWidth / contentAspectRatio; } else { - newHeight = Math.min(mediaHeight, containerWidth / mediaAspectRatio); - newWidth = newHeight * mediaAspectRatio; + newHeight = Math.min(contentHeight, containerWidth / contentAspectRatio); + newWidth = newHeight * contentAspectRatio; } - this.props.setContentDimensions({ + + setDynamicContentDimensions({ width: newWidth, height: newHeight, }); }; } function ConnectedFullScreenViewModal(props: BaseProps): React.Node { const modalContext = useModalContext(); const fullScreenViewModal = React.useMemo( () => , [modalContext.popModal, props], ); return fullScreenViewModal; } export default ConnectedFullScreenViewModal;