From 93ff395c0a36cfaf85a6b4eb0c756a467510ad7d Mon Sep 17 00:00:00 2001 From: Yury Shevchenko Date: Tue, 17 Aug 2021 13:19:44 -0700 Subject: [PATCH] Initial commit for support of video-type attachments --- src/components/post-attachment-video.jsx | 74 ++++++ src/components/post-attachments.jsx | 37 ++- styles/shared/attachments-edit.scss | 1 + styles/shared/attachments.scss | 31 +++ styles/shared/media-viewer.scss | 9 +- test/jest-setup.js | 5 + .../post-attachments.test.js.snap | 211 ++++++++++++++++++ test/jest/post-attachments.test.js | 82 +++++++ 8 files changed, 443 insertions(+), 7 deletions(-) create mode 100644 src/components/post-attachment-video.jsx create mode 100644 test/jest/__snapshots__/post-attachments.test.js.snap create mode 100644 test/jest/post-attachments.test.js diff --git a/src/components/post-attachment-video.jsx b/src/components/post-attachment-video.jsx new file mode 100644 index 000000000..7ef3d98b3 --- /dev/null +++ b/src/components/post-attachment-video.jsx @@ -0,0 +1,74 @@ +import { PureComponent } from 'react'; +import { faFileVideo, faPlayCircle } from '@fortawesome/free-regular-svg-icons'; +import { faTimes } from '@fortawesome/free-solid-svg-icons'; + +import { formatFileSize } from '../utils'; +import { ButtonLink } from './button-link'; +import { Icon } from './fontawesome-icons'; + +class VideoAttachment extends PureComponent { + state = { + isOpen: false, + }; + + handleClickOnRemoveAttachment = () => { + this.props.removeAttachment(this.props.id); + }; + + toggleOpen = () => { + this.setState({ isOpen: true }); + }; + + render() { + const { props } = this; + const { isOpen } = this.state; + const formattedFileSize = formatFileSize(props.fileSize); + + const title = `${props.fileName} (${formattedFileSize})`; + + const thumbnailStyle = { + width: props.imageSizes.t?.w, + height: props.imageSizes.t?.h, + backgroundImage: `url(${props.imageSizes.t?.url})`, + }; + + return ( +
+ {isOpen ? ( +
+ +
+ ) : ( + + + + )} +
+ + + {title} + + + {props.isEditing && ( + + )} +
+
+ ); + } +} + +export default VideoAttachment; diff --git a/src/components/post-attachments.jsx b/src/components/post-attachments.jsx index a99fa8638..b7e6888f8 100644 --- a/src/components/post-attachments.jsx +++ b/src/components/post-attachments.jsx @@ -1,12 +1,29 @@ import ImageAttachmentsContainer from './post-attachment-image-container'; import AudioAttachment from './post-attachment-audio'; import GeneralAttachment from './post-attachment-general'; +import VideoAttachment from './post-attachment-video'; import ErrorBoundary from './error-boundary'; export default (props) => { const attachments = props.attachments || []; - const imageAttachments = attachments.filter((attachment) => attachment.mediaType === 'image'); + const imageAttachments = []; + const audioAttachments = []; + const videoAttachments = []; + const generalAttachments = []; + + attachments.forEach((attachment) => { + if (attachment.mediaType === 'image') { + imageAttachments.push(attachment); + } else if (attachment.mediaType === 'audio') { + audioAttachments.push(attachment); + } else if (attachment.mediaType === 'video') { + videoAttachments.push(attachment); + } else { + generalAttachments.push(attachment); + } + }); + const imageAttachmentsContainer = imageAttachments.length > 0 ? ( { false ); - const audioAttachments = attachments.filter((attachment) => attachment.mediaType === 'audio'); const audioAttachmentsNodes = audioAttachments.map((attachment) => ( { false ); - const generalAttachments = attachments.filter((attachment) => attachment.mediaType === 'general'); + const videoAttachmentsNodes = videoAttachments.map((attachment) => ( + + )); + const videoAttachmentsContainer = + videoAttachments.length > 0 ? ( +
{videoAttachmentsNodes}
+ ) : ( + false + ); + const generalAttachmentsNodes = generalAttachments.map((attachment) => ( { {imageAttachmentsContainer} {audioAttachmentsContainer} + {videoAttachmentsContainer} {generalAttachmentsContainer} diff --git a/styles/shared/attachments-edit.scss b/styles/shared/attachments-edit.scss index 928f213eb..5585706d9 100644 --- a/styles/shared/attachments-edit.scss +++ b/styles/shared/attachments-edit.scss @@ -74,6 +74,7 @@ } .audio-attachments, +.video-attachments, .general-attachments { .attachment:hover .remove-attachment { display: block; diff --git a/styles/shared/attachments.scss b/styles/shared/attachments.scss index 0020ea60f..f990b9145 100644 --- a/styles/shared/attachments.scss +++ b/styles/shared/attachments.scss @@ -132,6 +132,7 @@ } .audio-attachments, +.video-attachments, .general-attachments { .attachment { position: relative; @@ -149,3 +150,33 @@ } } } + +.video-attachments { + .video-attachment-thumb { + display: flex; + width: 80px; + height: 80px; + justify-content: center; + align-items: center; + border-radius: 2px; + background-color: #000; + background-position: center; + background-repeat: no-repeat; + font-size: 2em; + + &:hover { + font-size: 2.5em; + } + } + + .video-attachment-click-to-play { + filter: drop-shadow(0px 0px 3px #000); + color: #fff; + } + + video { + max-width: 100%; + max-height: 400px; + background-color: #eee; + } +} diff --git a/styles/shared/media-viewer.scss b/styles/shared/media-viewer.scss index b189f7ad8..fecdfca1d 100644 --- a/styles/shared/media-viewer.scss +++ b/styles/shared/media-viewer.scss @@ -34,10 +34,11 @@ width: 100%; height: 100%; } -} -video { - width: 100% !important; - height: auto !important; + + video { + width: 100% !important; + height: auto !important; + } } .media-link { .icon-bond { diff --git a/test/jest-setup.js b/test/jest-setup.js index c85715e93..59daf3b48 100644 --- a/test/jest-setup.js +++ b/test/jest-setup.js @@ -1 +1,6 @@ require('../config/lib/loader-node'); + +// https://github.com/testing-library/react-testing-library/issues/470 +Object.defineProperty(HTMLMediaElement.prototype, 'muted', { + set: () => {}, +}); diff --git a/test/jest/__snapshots__/post-attachments.test.js.snap b/test/jest/__snapshots__/post-attachments.test.js.snap new file mode 100644 index 000000000..474d3b68e --- /dev/null +++ b/test/jest/__snapshots__/post-attachments.test.js.snap @@ -0,0 +1,211 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`PostAttachments Displays all post attachment types 1`] = ` + + + +`; + +exports[`PostAttachments Renders an empty attachments container 1`] = ``; diff --git a/test/jest/post-attachments.test.js b/test/jest/post-attachments.test.js new file mode 100644 index 000000000..a829b8dca --- /dev/null +++ b/test/jest/post-attachments.test.js @@ -0,0 +1,82 @@ +/* global describe, it, expect */ +import { render } from '@testing-library/react'; + +import PostAttachments from '../../src/components/post-attachments'; + +const renderPostAttachments = (props = {}) => { + return render(); +}; + +describe('PostAttachments', () => { + it('Renders an empty attachments container', () => { + const { asFragment } = renderPostAttachments(); + expect(asFragment()).toMatchSnapshot(); + }); + + it('Displays all post attachment types', () => { + const image1 = { + id: 'im1', + mediaType: 'image', + fileName: 'CAT.JPG', + fileSize: 200000, + thumbnailUrl: 'https://thumbnail/CAT.JPG', + url: 'https://media/CAT.JPG', + imageSizes: { + t: { + w: 400, + h: 300, + }, + o: { + w: 2000, + h: 1500, + }, + }, + }; + + const image2 = { + id: 'im2', + mediaType: 'image', + fileName: 'food.jpg', + fileSize: 2000, + thumbnailUrl: 'https://thumbnail/food.jpg', + url: 'https://media/food.jpg', + imageSizes: { + o: { + w: 2000, + h: 1500, + }, + }, + }; + + const video1 = { + id: 'vi1', + mediaType: 'general', + fileName: 'sunrise.mp4', + fileSize: 123456789, + url: 'https://media/sunrise.mp4', + }; + + const audio1 = { + id: 'au1', + mediaType: 'audio', + fileName: 'wonderwall.mp3', + artist: 'Oasis', + title: 'Wonderwall', + fileSize: 1234567, + url: 'https://media/wonderwall.mp3', + }; + + const general1 = { + id: 'ge1', + mediaType: 'general', + fileName: 'rfc.pdf', + fileSize: 50000, + url: 'https://media/rfc.pdf', + }; + + const { asFragment } = renderPostAttachments({ + attachments: [video1, image1, general1, image2, audio1], + }); + expect(asFragment()).toMatchSnapshot(); + }); +});