Page MenuHomePhorge

D7947.1768416624.diff
No OneTemporary

Size
7 KB
Referenced Files
None
Subscribers
None

D7947.1768416624.diff

diff --git a/web/media/media.css b/web/media/media.css
--- a/web/media/media.css
+++ b/web/media/media.css
@@ -95,13 +95,24 @@
display: flex;
justify-content: center;
}
-div.multimediaModalOverlay > img,
-div.multimediaModalOverlay > video {
- object-fit: scale-down;
+div.multimediaModalOverlay > .mediaContainer {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ width: 100%;
+ height: 100%;
+}
+div.mediaContainer > img,
+div.mediaContainer > video {
width: auto;
height: auto;
max-width: 100%;
max-height: 100%;
+ display: block;
+ margin: auto;
+ background-position: center;
+ background-size: cover;
+ background-repeat: no-repeat;
}
svg.closeMultimediaModal {
position: absolute;
diff --git a/web/media/multimedia-modal.react.js b/web/media/multimedia-modal.react.js
--- a/web/media/multimedia-modal.react.js
+++ b/web/media/multimedia-modal.react.js
@@ -6,20 +6,33 @@
import { useModalContext } from 'lib/components/modal-provider.react.js';
import { fetchableMediaURI } from 'lib/media/media-utils.js';
-import type { EncryptedMediaType, MediaType } from 'lib/types/media-types.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 css from './media.css';
type MediaInfo =
| {
+type: MediaType,
+uri: string,
+ +dimensions: ?Dimensions,
+ +thumbHash: ?string,
+ +thumbnailURI: ?string,
}
| {
+type: EncryptedMediaType,
+holder: string,
+encryptionKey: string,
+ +dimensions: ?Dimensions,
+ +thumbHash: ?string,
+ +thumbnailHolder: ?string,
+ +thumbnailEncryptionKey: ?string,
};
type BaseProps = {
@@ -29,28 +42,53 @@
type Props = {
...BaseProps,
+popModal: (modal: ?React.Node) => void,
+ +placeholderImage: ?string,
};
-class MultimediaModal extends React.PureComponent<Props> {
+type State = {
+ +dimensions: ?Dimensions,
+};
+
+class MultimediaModal extends React.PureComponent<Props, State> {
overlay: ?HTMLDivElement;
+ constructor(props: Props) {
+ super(props);
+ this.state = { dimensions: null };
+ }
+
componentDidMount() {
invariant(this.overlay, 'overlay ref unset');
this.overlay.focus();
+ this.calculateMediaDimensions();
+ window.addEventListener('resize', this.calculateMediaDimensions);
+ }
+
+ componentWillUnmount() {
+ window.removeEventListener('resize', this.calculateMediaDimensions);
}
render(): React.Node {
let mediaModalItem;
- const { media } = this.props;
+ const { media, placeholderImage } = this.props;
+ const style = {
+ backgroundImage: placeholderImage
+ ? `url(${placeholderImage})`
+ : undefined,
+ };
if (media.type === 'photo') {
const uri = fetchableMediaURI(media.uri);
- mediaModalItem = <img src={uri} />;
+ mediaModalItem = <img src={uri} style={style} />;
} else if (media.type === 'video') {
const uri = fetchableMediaURI(media.uri);
+ const { thumbnailURI } = media;
+ invariant(thumbnailURI, 'video missing thumbnail');
mediaModalItem = (
- <video controls>
- <source src={uri} />
- </video>
+ <LoadableVideo
+ uri={uri}
+ thumbnailSource={{ thumbnailURI }}
+ thumbHashDataURL={placeholderImage}
+ />
);
} else {
invariant(
@@ -75,7 +113,7 @@
tabIndex={0}
onKeyDown={this.onKeyDown}
>
- {mediaModalItem}
+ <div className={css.mediaContainer}>{mediaModalItem}</div>
<XCircleIcon
onClick={this.props.popModal}
className={css.closeMultimediaModal}
@@ -101,12 +139,49 @@
this.props.popModal();
}
};
+
+ calculateMediaDimensions: () => void = () => {
+ if (!this.overlay || !this.props.media.dimensions) {
+ return;
+ }
+ const containerWidth = this.overlay.clientWidth;
+ const containerHeight = this.overlay.clientHeight;
+ const containerAspectRatio = containerWidth / containerHeight;
+
+ const { width: mediaWidth, height: mediaHeight } =
+ this.props.media.dimensions;
+ const mediaAspectRatio = mediaWidth / mediaHeight;
+
+ let newWidth, newHeight;
+ if (containerAspectRatio > mediaAspectRatio) {
+ newWidth = Math.min(mediaWidth, containerHeight * mediaAspectRatio);
+ newHeight = newWidth / mediaAspectRatio;
+ } else {
+ newHeight = Math.min(mediaHeight, containerWidth / mediaAspectRatio);
+ newWidth = newHeight * mediaAspectRatio;
+ }
+ this.setState({
+ dimensions: {
+ width: newWidth,
+ height: newHeight,
+ },
+ });
+ };
}
function ConnectedMultiMediaModal(props: BaseProps): React.Node {
const modalContext = useModalContext();
-
- return <MultimediaModal {...props} popModal={modalContext.popModal} />;
+ const { thumbHash, encryptionKey, thumbnailEncryptionKey } = props.media;
+ const thumbHashEncryptionKey = thumbnailEncryptionKey ?? encryptionKey;
+ const placeholderImage = usePlaceholder(thumbHash, thumbHashEncryptionKey);
+
+ return (
+ <MultimediaModal
+ {...props}
+ popModal={modalContext.popModal}
+ placeholderImage={placeholderImage}
+ />
+ );
}
export default ConnectedMultiMediaModal;
diff --git a/web/media/multimedia.react.js b/web/media/multimedia.react.js
--- a/web/media/multimedia.react.js
+++ b/web/media/multimedia.react.js
@@ -98,19 +98,7 @@
const { pushModal } = useModalContext();
const handleClick = React.useCallback(() => {
- let media;
- if (
- mediaSource.type === 'encrypted_photo' ||
- mediaSource.type === 'encrypted_video'
- ) {
- const { type, holder, encryptionKey } = mediaSource;
- media = { type, holder, encryptionKey };
- } else {
- const { type, uri } = mediaSource;
- invariant(uri, 'uri is missing for media modal');
- media = { type, uri };
- }
- pushModal(<MultimediaModal media={media} />);
+ pushModal(<MultimediaModal media={mediaSource} />);
}, [pushModal, mediaSource]);
let progressIndicator, errorIndicator, removeButton;
diff --git a/web/modals/threads/gallery/thread-settings-media-gallery.react.js b/web/modals/threads/gallery/thread-settings-media-gallery.react.js
--- a/web/modals/threads/gallery/thread-settings-media-gallery.react.js
+++ b/web/modals/threads/gallery/thread-settings-media-gallery.react.js
@@ -48,18 +48,33 @@
const onClick = React.useCallback(
(media: Media) => {
- // This branching is needed for Flow.
- let mediaInfo;
+ const thumbHash = media.thumbnailThumbHash ?? media.thumbHash;
+ let mediaInfo = {
+ thumbHash,
+ dimensions: media.dimensions,
+ };
if (media.type === 'photo' || media.type === 'video') {
+ const { uri, thumbnailURI } = media;
mediaInfo = {
+ ...mediaInfo,
type: media.type,
- uri: media.uri,
+ uri,
+ thumbnailURI,
};
} else {
+ const {
+ holder,
+ encryptionKey,
+ thumbnailHolder,
+ thumbnailEncryptionKey,
+ } = media;
mediaInfo = {
+ ...mediaInfo,
type: media.type,
- holder: media.holder,
- encryptionKey: media.encryptionKey,
+ holder,
+ encryptionKey,
+ thumbnailHolder,
+ thumbnailEncryptionKey,
};
}
pushModal(<MultimediaModal media={mediaInfo} />);

File Metadata

Mime Type
text/plain
Expires
Wed, Jan 14, 6:50 PM (3 h, 47 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5933457
Default Alt Text
D7947.1768416624.diff (7 KB)

Event Timeline