Implement video support in lightbox

This commit is contained in:
Daniel Gasienica 2018-04-25 18:15:57 -04:00
parent ac04f0648a
commit 45d6c7a5a8
5 changed files with 61 additions and 24 deletions

View file

@ -109,7 +109,8 @@
} }
const props = { const props = {
imageURL: this.objectUrl, objectURL: this.objectUrl,
contentType: this.model.contentType,
onSave: () => this.saveFile(), onSave: () => this.saveFile(),
// implicit: `close` // implicit: `close`
}; };

View file

@ -600,25 +600,28 @@
const media = await loadMessages(rawMedia); const media = await loadMessages(rawMedia);
const saveAttachment = async ({ message } = {}) => { const saveAttachment = async ({ message } = {}) => {
const loadedMessage = await Signal.Migrations.loadMessage(message); const attachment = message.attachments[0];
const attachment = loadedMessage.attachments[0]; const timestamp = message.received_at;
const timestamp = loadedMessage.received_at;
Signal.Types.AttachmentTS.save({ attachment, timestamp }); Signal.Types.AttachmentTS.save({ attachment, timestamp });
}; };
const onItemClick = async ({ message, type }) => { const onItemClick = async ({ message, type }) => {
const loadedMessage = Signal.Components.Types.Message
.withObjectURL(await Signal.Migrations.loadMessage(message));
switch (type) { switch (type) {
case 'documents': { case 'documents': {
saveAttachment({ message }); saveAttachment({ message: loadedMessage });
break; break;
} }
case 'media': { case 'media': {
const attachment = loadedMessage.attachments[0];
this.lightboxView = new Whisper.ReactWrapperView({ this.lightboxView = new Whisper.ReactWrapperView({
Component: Signal.Components.Lightbox, Component: Signal.Components.Lightbox,
props: { props: {
imageURL: message.objectURL, objectURL: loadedMessage.objectURL,
onSave: () => saveAttachment({ message }), contentType: attachment.contentType,
onSave: () => saveAttachment({ message: loadedMessage }),
}, },
onClose: () => Signal.Backbone.Views.Lightbox.hide(), onClose: () => Signal.Backbone.Views.Lightbox.hide(),
}); });

View file

@ -3,7 +3,8 @@ const noop = () => {};
<div style={{position: 'relative', width: '100%', height: 500}}> <div style={{position: 'relative', width: '100%', height: 500}}>
<Lightbox <Lightbox
imageURL="https://placekitten.com/800/600" objectURL="https://placekitten.com/800/600"
contentType="image/jpeg"
onNext={noop} onNext={noop}
onPrevious={noop} onPrevious={noop}
onSave={noop} onSave={noop}

View file

@ -4,13 +4,18 @@
import React from 'react'; import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import is from '@sindresorhus/is';
import * as GoogleChrome from '../util/GoogleChrome';
import * as MIME from '../types/MIME';
interface Props { interface Props {
close: () => void; close: () => void;
imageURL?: string; objectURL: string;
contentType: MIME.MIMEType | undefined;
onNext?: () => void; onNext?: () => void;
onPrevious?: () => void; onPrevious?: () => void;
onSave: () => void; onSave?: () => void;
} }
const styles = { const styles = {
@ -67,7 +72,7 @@ export class Lightbox extends React.Component<Props, {}> {
} }
public render() { public render() {
const { imageURL } = this.props; const { contentType, objectURL } = this.props;
return ( return (
<div <div
style={styles.container} style={styles.container}
@ -75,11 +80,9 @@ export class Lightbox extends React.Component<Props, {}> {
ref={this.setContainerRef} ref={this.setContainerRef}
> >
<div style={styles.objectContainer}> <div style={styles.objectContainer}>
<img {!is.undefined(contentType)
style={styles.image} ? this.renderObject({ objectURL, contentType })
src={imageURL} : null}
onClick={this.onImageClick}
/>
</div> </div>
<div style={styles.controls}> <div style={styles.controls}>
<IconButton type="close" onClick={this.onClose} /> <IconButton type="close" onClick={this.onClose} />
@ -97,6 +100,38 @@ export class Lightbox extends React.Component<Props, {}> {
); );
} }
private renderObject = ({
objectURL,
contentType,
}: {
objectURL: string;
contentType: MIME.MIMEType;
}) => {
const isImage = GoogleChrome.isImageTypeSupported(contentType);
if (isImage) {
return (
<img
style={styles.image}
src={objectURL}
onClick={this.onObjectClick}
/>
);
}
const isVideo = GoogleChrome.isVideoTypeSupported(contentType);
if (isVideo) {
return (
<video controls>
<source src={objectURL} />
</video>
);
}
// tslint:disable-next-line no-console
console.log('Lightbox: Unexpected content type', { contentType });
return null;
};
private setContainerRef = (value: HTMLDivElement) => { private setContainerRef = (value: HTMLDivElement) => {
this.containerRef = value; this.containerRef = value;
}; };
@ -125,7 +160,7 @@ export class Lightbox extends React.Component<Props, {}> {
this.onClose(); this.onClose();
}; };
private onImageClick = (event: React.MouseEvent<HTMLImageElement>) => { private onObjectClick = (event: React.MouseEvent<HTMLImageElement>) => {
event.stopPropagation(); event.stopPropagation();
this.onClose(); this.onClose();
}; };

View file

@ -8,7 +8,6 @@ import * as MIME from '../../../../types/MIME';
import { arrayBufferToObjectURL } from '../../../../util/arrayBufferToObjectURL'; import { arrayBufferToObjectURL } from '../../../../util/arrayBufferToObjectURL';
import { Attachment } from '../../../../types/Attachment'; import { Attachment } from '../../../../types/Attachment';
import { MapAsync } from '../../../../types/MapAsync'; import { MapAsync } from '../../../../types/MapAsync';
import { MIMEType } from '../../../../types/MIME';
export type Message = { export type Message = {
id: string; id: string;
@ -16,8 +15,6 @@ export type Message = {
received_at: number; received_at: number;
} & { objectURL?: string }; } & { objectURL?: string };
const DEFAULT_CONTENT_TYPE: MIMEType = 'application/octet-stream' as MIMEType;
export const loadWithObjectURL = (loadMessage: MapAsync<Message>) => async ( export const loadWithObjectURL = (loadMessage: MapAsync<Message>) => async (
messages: Array<Message> messages: Array<Message>
): Promise<Array<Message>> => { ): Promise<Array<Message>> => {
@ -51,17 +48,17 @@ const hasVideoAttachment = (message: Message): boolean =>
MIME.isVideo(attachment.contentType) MIME.isVideo(attachment.contentType)
); );
const withObjectURL = (message: Message): Message => { export const withObjectURL = (message: Message): Message => {
if (message.attachments.length === 0) { if (message.attachments.length === 0) {
throw new TypeError('`message.attachments` cannot be empty'); throw new TypeError('`message.attachments` cannot be empty');
} }
const attachment = message.attachments[0]; const attachment = message.attachments[0];
if (typeof attachment.contentType === 'undefined') { if (is.undefined(attachment.contentType)) {
throw new TypeError('`attachment.contentType` is required'); throw new TypeError('`attachment.contentType` is required');
} }
if (MIME.isVideo(attachment.contentType)) { if (is.undefined(attachment.data) && MIME.isVideo(attachment.contentType)) {
return { return {
...message, ...message,
objectURL: 'images/video.svg', objectURL: 'images/video.svg',
@ -70,7 +67,7 @@ const withObjectURL = (message: Message): Message => {
const objectURL = arrayBufferToObjectURL({ const objectURL = arrayBufferToObjectURL({
data: attachment.data, data: attachment.data,
type: attachment.contentType || DEFAULT_CONTENT_TYPE, type: attachment.contentType,
}); });
return { return {
...message, ...message,