From 45d6c7a5a8a3a642475e2d3bcfeaa4a5b9603010 Mon Sep 17 00:00:00 2001 From: Daniel Gasienica Date: Wed, 25 Apr 2018 18:15:57 -0400 Subject: [PATCH] Implement video support in lightbox --- js/views/attachment_view.js | 3 +- js/views/conversation_view.js | 15 +++--- ts/components/Lightbox.md | 3 +- ts/components/Lightbox.tsx | 53 +++++++++++++++---- .../media-gallery/types/Message.tsx | 11 ++-- 5 files changed, 61 insertions(+), 24 deletions(-) diff --git a/js/views/attachment_view.js b/js/views/attachment_view.js index c19e4ebf44..84d7fab35e 100644 --- a/js/views/attachment_view.js +++ b/js/views/attachment_view.js @@ -109,7 +109,8 @@ } const props = { - imageURL: this.objectUrl, + objectURL: this.objectUrl, + contentType: this.model.contentType, onSave: () => this.saveFile(), // implicit: `close` }; diff --git a/js/views/conversation_view.js b/js/views/conversation_view.js index 956a9ede16..aa5c738ec5 100644 --- a/js/views/conversation_view.js +++ b/js/views/conversation_view.js @@ -600,25 +600,28 @@ const media = await loadMessages(rawMedia); const saveAttachment = async ({ message } = {}) => { - const loadedMessage = await Signal.Migrations.loadMessage(message); - const attachment = loadedMessage.attachments[0]; - const timestamp = loadedMessage.received_at; + const attachment = message.attachments[0]; + const timestamp = message.received_at; Signal.Types.AttachmentTS.save({ attachment, timestamp }); }; const onItemClick = async ({ message, type }) => { + const loadedMessage = Signal.Components.Types.Message + .withObjectURL(await Signal.Migrations.loadMessage(message)); switch (type) { case 'documents': { - saveAttachment({ message }); + saveAttachment({ message: loadedMessage }); break; } case 'media': { + const attachment = loadedMessage.attachments[0]; this.lightboxView = new Whisper.ReactWrapperView({ Component: Signal.Components.Lightbox, props: { - imageURL: message.objectURL, - onSave: () => saveAttachment({ message }), + objectURL: loadedMessage.objectURL, + contentType: attachment.contentType, + onSave: () => saveAttachment({ message: loadedMessage }), }, onClose: () => Signal.Backbone.Views.Lightbox.hide(), }); diff --git a/ts/components/Lightbox.md b/ts/components/Lightbox.md index a5fbd148e6..4bc11546d2 100644 --- a/ts/components/Lightbox.md +++ b/ts/components/Lightbox.md @@ -3,7 +3,8 @@ const noop = () => {};
void; - imageURL?: string; + objectURL: string; + contentType: MIME.MIMEType | undefined; onNext?: () => void; onPrevious?: () => void; - onSave: () => void; + onSave?: () => void; } const styles = { @@ -67,7 +72,7 @@ export class Lightbox extends React.Component { } public render() { - const { imageURL } = this.props; + const { contentType, objectURL } = this.props; return (
{ ref={this.setContainerRef} >
- + {!is.undefined(contentType) + ? this.renderObject({ objectURL, contentType }) + : null}
@@ -97,6 +100,38 @@ export class Lightbox extends React.Component { ); } + private renderObject = ({ + objectURL, + contentType, + }: { + objectURL: string; + contentType: MIME.MIMEType; + }) => { + const isImage = GoogleChrome.isImageTypeSupported(contentType); + if (isImage) { + return ( + + ); + } + + const isVideo = GoogleChrome.isVideoTypeSupported(contentType); + if (isVideo) { + return ( + + ); + } + + // tslint:disable-next-line no-console + console.log('Lightbox: Unexpected content type', { contentType }); + return null; + }; + private setContainerRef = (value: HTMLDivElement) => { this.containerRef = value; }; @@ -125,7 +160,7 @@ export class Lightbox extends React.Component { this.onClose(); }; - private onImageClick = (event: React.MouseEvent) => { + private onObjectClick = (event: React.MouseEvent) => { event.stopPropagation(); this.onClose(); }; diff --git a/ts/components/conversation/media-gallery/types/Message.tsx b/ts/components/conversation/media-gallery/types/Message.tsx index 73431ec59a..4b47504bd7 100644 --- a/ts/components/conversation/media-gallery/types/Message.tsx +++ b/ts/components/conversation/media-gallery/types/Message.tsx @@ -8,7 +8,6 @@ import * as MIME from '../../../../types/MIME'; import { arrayBufferToObjectURL } from '../../../../util/arrayBufferToObjectURL'; import { Attachment } from '../../../../types/Attachment'; import { MapAsync } from '../../../../types/MapAsync'; -import { MIMEType } from '../../../../types/MIME'; export type Message = { id: string; @@ -16,8 +15,6 @@ export type Message = { received_at: number; } & { objectURL?: string }; -const DEFAULT_CONTENT_TYPE: MIMEType = 'application/octet-stream' as MIMEType; - export const loadWithObjectURL = (loadMessage: MapAsync) => async ( messages: Array ): Promise> => { @@ -51,17 +48,17 @@ const hasVideoAttachment = (message: Message): boolean => MIME.isVideo(attachment.contentType) ); -const withObjectURL = (message: Message): Message => { +export const withObjectURL = (message: Message): Message => { if (message.attachments.length === 0) { throw new TypeError('`message.attachments` cannot be empty'); } const attachment = message.attachments[0]; - if (typeof attachment.contentType === 'undefined') { + if (is.undefined(attachment.contentType)) { throw new TypeError('`attachment.contentType` is required'); } - if (MIME.isVideo(attachment.contentType)) { + if (is.undefined(attachment.data) && MIME.isVideo(attachment.contentType)) { return { ...message, objectURL: 'images/video.svg', @@ -70,7 +67,7 @@ const withObjectURL = (message: Message): Message => { const objectURL = arrayBufferToObjectURL({ data: attachment.data, - type: attachment.contentType || DEFAULT_CONTENT_TYPE, + type: attachment.contentType, }); return { ...message,