Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Video Editor: Add VideoPress poster picker #11653

Merged
merged 30 commits into from
Jun 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c0ccd87
Add "Edit Thumbnail" button to media modal for videos
donnapep Feb 28, 2017
f6146d6
Render video editor when "Edit Thumbnail" button is clicked
donnapep Feb 28, 2017
027e585
Switch modal views when video editor "Cancel" button clicked
donnapep Feb 28, 2017
bb78872
Handle error if VideoPress script failed to load
donnapep Feb 28, 2017
73d475a
Add ability to pause VideoPress player
donnapep Feb 28, 2017
b98fc84
Switch to detail view when poster updated
donnapep Feb 28, 2017
0addb80
Update Redux state when video has loaded
donnapep Feb 28, 2017
ba1b245
Enable video editor for sites with VideoPress
donnapep Feb 28, 2017
ad760c6
Refresh poster upon returning to the Media Library modal
donnapep Feb 28, 2017
e20870a
Enable video editor for development, wpcalypso and horizon
donnapep Feb 28, 2017
c96f346
Make it work for Media Library
donnapep Mar 6, 2017
6042dee
Add ability to play VideoPress player
donnapep Mar 8, 2017
9f43e9d
Use component props instead of connected props for script and video l…
donnapep Mar 16, 2017
f873e17
Alter conditions under which video is considered loaded
donnapep Mar 17, 2017
80be35e
Refactor check for deciding which buttons to render in modal
donnapep May 15, 2017
8d5ea50
Safely invoke VideoPress player methods
donnapep May 15, 2017
54e9be3
Use lodash.get and sane defaults for nested properties
donnapep May 15, 2017
c50f5dc
VideoPress component does not need to be connected to Redux store
donnapep May 15, 2017
25e7964
Define new constant for storing poster URL without query string
donnapep May 15, 2017
5891978
Use selectors for Jetpack site VideoPress enabled check
donnapep Jun 7, 2017
c8c5382
Simplify variable assignment for button text
donnapep Jun 7, 2017
8c97ca3
Simplify destructuring assignment
donnapep Jun 7, 2017
a192970
sites is no longer passed as a prop, use selectedSite instead
donnapep Jun 9, 2017
73a3db2
Fix logic for deciding if "Edit Thumbnail" button should be shown
donnapep Jun 14, 2017
6a6d66b
Fix failing test
donnapep Jun 14, 2017
98481ef
Reinstate code that should not have been removed
donnapep Jun 15, 2017
6bfb549
Move classes declaration inside of respective functions
donnapep Jun 15, 2017
a452195
Tighten up the use of destructuring
donnapep Jun 15, 2017
500c73d
Simplify conditionals by using lodash get
donnapep Jun 15, 2017
0924df1
PostEditor: Add QueryJetpackModules to determine if VideoPress is active
ockham Jun 15, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 56 additions & 18 deletions client/my-sites/media/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import SidebarNavigation from 'my-sites/sidebar-navigation';
import Dialog from 'components/dialog';
import { EditorMediaModalDetail } from 'post-editor/media-modal/detail';
import ImageEditor from 'blocks/image-editor';
import VideoEditor from 'blocks/video-editor';
import MediaActions from 'lib/media/actions';
import MediaUtils from 'lib/media/utils';
import MediaLibrarySelectedData from 'components/data/media-library-selected-data';
Expand All @@ -28,9 +29,10 @@ class Media extends Component {
};

state = {
editedItem: null,
currentDetail: null,
selectedImages: [],
editedImageItem: null,
editedVideoItem: null,
selectedItems: [],
};

componentDidMount() {
Expand All @@ -56,7 +58,7 @@ class Media extends Component {
openDetailsModalForASingleImage = ( image ) => {
this.setState( {
currentDetail: 0,
selectedImages: [ image ],
selectedItems: [ image ],
} );
};

Expand All @@ -66,21 +68,25 @@ class Media extends Component {

this.setState( {
currentDetail: 0,
selectedImages: selected
selectedItems: selected
} );
};

closeDetailsModal = () => {
this.setState( { editedItem: null, currentDetail: null, selectedImages: [] } );
this.setState( { editedImageItem: null, editedVideoItem: null, currentDetail: null, selectedItems: [] } );
};

editImage = () => {
this.setState( { currentDetail: null, editedItem: this.state.currentDetail } );
this.setState( { currentDetail: null, editedImageItem: this.state.currentDetail } );
};

editVideo = () => {
this.setState( { currentDetail: null, editedVideoItem: this.state.currentDetail } );
};

onImageEditorCancel = ( imageEditorProps ) => {
const { resetAllImageEditorState } = imageEditorProps;
this.setState( { currentDetail: this.state.editedItem, editedItem: null } );
this.setState( { currentDetail: this.state.editedImageItem, editedImageItem: null } );

resetAllImageEditorState();
};
Expand Down Expand Up @@ -112,12 +118,12 @@ class Media extends Component {

MediaActions.update( site.ID, item, true );
resetAllImageEditorState();
this.setState( { currentDetail: null, editedItem: null, selectedImages: [] } );
this.setState( { currentDetail: null, editedImageItem: null, selectedItems: [] } );
};

getModalButtons() {
// do not render buttons if the media image editor is opened
if ( this.state.editedItem !== null ) {
// do not render buttons if the media image or video editor is opened
if ( ( this.state.editedImageItem !== null ) || ( this.state.editedVideoItem !== null ) ) {
return null;
}

Expand All @@ -142,12 +148,36 @@ class Media extends Component {
];
}

onVideoEditorCancel = () => {
this.setState( { currentDetail: this.state.editedVideoItem, editedVideoItem: null } );
};

onVideoEditorUpdatePoster = ( { ID, posterUrl } ) => {
const site = this.props.selectedSite;

// Photon does not support URLs with a querystring component.
const urlBeforeQuery = ( posterUrl || '' ).split( '?' )[ 0 ];

if ( site ) {
MediaActions.edit( site.ID, {
ID,
thumbnails: {
fmt_hd: urlBeforeQuery,
fmt_dvd: urlBeforeQuery,
fmt_std: urlBeforeQuery,
}
} );
}

this.setState( { currentDetail: null, editedVideoItem: null, selectedItems: [] } );
};

restoreOriginalMedia = ( siteId, item ) => {
if ( ! siteId || ! item ) {
return;
}
MediaActions.update( siteId, { ID: item.ID, media_url: item.guid }, true );
this.setState( { currentDetail: null, editedItem: null, selectedImages: [] } );
this.setState( { currentDetail: null, editedImageItem: null, selectedItems: [] } );
};

setDetailSelectedIndex = ( index ) => {
Expand Down Expand Up @@ -198,8 +228,8 @@ class Media extends Component {
return;
}

const selected = this.state.selectedImages && this.state.selectedImages.length
? this.state.selectedImages
const selected = this.state.selectedItems && this.state.selectedItems.length
? this.state.selectedItems
: MediaLibrarySelectedStore.getAll( site.ID );

MediaActions.delete( site.ID, selected );
Expand All @@ -210,7 +240,7 @@ class Media extends Component {
return (
<div ref="container" className="main main-column media" role="main">
<SidebarNavigation />
{ ( this.state.editedItem !== null || this.state.currentDetail !== null ) &&
{ ( this.state.editedImageItem !== null || this.state.editedVideoItem !== null || this.state.currentDetail !== null ) &&
<Dialog
isVisible={ true }
additionalClassNames="editor-media-modal media__item-dialog"
Expand All @@ -220,22 +250,30 @@ class Media extends Component {
{ this.state.currentDetail !== null &&
<EditorMediaModalDetail
site={ site }
items={ this.state.selectedImages }
items={ this.state.selectedItems }
selectedIndex={ this.state.currentDetail }
onReturnToList={ this.closeDetailsModal }
onEditItem={ this.editImage }
onEditImageItem={ this.editImage }
onEditVideoItem={ this.editVideo }
onRestoreItem={ this.restoreOriginalMedia }
onSelectedIndexChange={ this.setDetailSelectedIndex }
/>
}
{ this.state.editedItem !== null &&
{ this.state.editedImageItem !== null &&
<ImageEditor
siteId={ site && site.ID }
media={ this.state.selectedImages[ this.state.editedItem ] }
media={ this.state.selectedItems[ this.state.editedImageItem ] }
onDone={ this.onImageEditorDone }
onCancel={ this.onImageEditorCancel }
/>
}
{ this.state.editedVideoItem !== null &&
<VideoEditor
media={ this.state.selectedItems[ this.state.editedVideoItem ] }
onCancel={ this.onVideoEditorCancel }
onUpdatePoster={ this.onVideoEditorUpdatePoster }
/>
}
</Dialog>
}
{ site && site.ID && (
Expand Down
106 changes: 96 additions & 10 deletions client/post-editor/media-modal/detail/detail-item.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
* External dependencies
*/
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import classNames from 'classnames';
import { noop } from 'lodash';
import { flowRight, includes, noop } from 'lodash';
import { localize } from 'i18n-calypso';
import url from 'url';
import Gridicon from 'gridicons';
Expand All @@ -19,18 +20,21 @@ import EditorMediaModalDetailPreviewVideo from './detail-preview-video';
import EditorMediaModalDetailPreviewAudio from './detail-preview-audio';
import EditorMediaModalDetailPreviewDocument from './detail-preview-document';
import Button from 'components/button';
import QueryJetpackModules from 'components/data/query-jetpack-modules';
import { userCan } from 'lib/site/utils';
import versionCompare from 'lib/version-compare';
import MediaUtils, { isItemBeingUploaded } from 'lib/media/utils';
import config from 'config';
import { getSelectedSiteId } from 'state/ui/selectors';
import { getSiteOption, isJetpackModuleActive, isJetpackSite } from 'state/sites/selectors';

/**
* This function return true if the image editor can be
* enabled/shown
*
* @param {object} item - media item
* @param {object} site - current site
* @return {boolean} `true` is the image-editor can be enabled.
* @return {boolean} `true` if the image-editor can be enabled.
*/
const enableImageEditing = ( item, site ) => {
// do not allow if, for some reason, there isn't a valid item yet
Expand Down Expand Up @@ -80,6 +84,41 @@ class EditorMediaModalDetailItem extends Component {
onRestore: noop,
};

/**
* This function returns true if the video editor can be enabled/shown.
*
* @param {Object} item Media item
* @return {Boolean} Whether the video editor can be enabled
*/
enableVideoEditing( item ) {
if ( ! config.isEnabled( 'post-editor/video-editor' ) ) {
return false;
}

const {
isJetpack,
isVideoPressEnabled,
isVideoPressModuleActive,
} = this.props;

// Not a VideoPress video
if ( ! MediaUtils.isVideoPressItem( item ) ) {
return false;
}

// Jetpack and VideoPress disabled
if ( isJetpack ) {
if ( ! isVideoPressModuleActive ) {
return false;
}
// WP.com and VideoPress disabled
} else if ( ! isVideoPressEnabled ) {
return false;
}

return true;
}

renderEditButton() {
const {
item,
Expand All @@ -94,17 +133,21 @@ class EditorMediaModalDetailItem extends Component {

const mimePrefix = MediaUtils.getMimePrefix( item );

if ( 'image' !== mimePrefix ) {
if ( ! includes( [ 'image', 'video' ], mimePrefix ) ) {
return null;
}

const editText = 'video' === mimePrefix
? translate( 'Edit Thumbnail' )
: translate( 'Edit Image' );

return (
<Button
className="editor-media-modal-detail__edit"
onClick={ onEdit }
disabled={ isItemBeingUploaded( item ) }
>
<Gridicon icon="pencil" size={ 36 } /> { translate( 'Edit Image' ) }
<Gridicon icon="pencil" size={ 36 } /> { editText }
</Button>
);
}
Expand Down Expand Up @@ -144,10 +187,22 @@ class EditorMediaModalDetailItem extends Component {
);
}

renderImageEditorButtons( item, classname = 'is-desktop' ) {
renderMediaEditorButtons( item, classname = 'is-desktop' ) {
if ( ! item ) {
return null;
}

const mimePrefix = MediaUtils.getMimePrefix( item );

return 'video' === mimePrefix
? this.renderVideoEditorButtons( item, classname )
: this.renderImageEditorButtons( classname );
}

renderImageEditorButtons( classname ) {
const { site } = this.props;

if ( ! enableImageEditing( item, site ) ) {
if ( ! enableImageEditing( site ) ) {
return null;
}

Expand All @@ -161,6 +216,20 @@ class EditorMediaModalDetailItem extends Component {
);
}

renderVideoEditorButtons( item, classname ) {
if ( ! this.enableVideoEditing( item ) ) {
return null;
}

const classes = classNames( 'editor-media-modal-detail__edition-bar', classname );

return (
<div className={ classes }>
{ this.renderEditButton() }
</div>
);
}

renderFields() {
const { site, item } = this.props;

Expand Down Expand Up @@ -250,7 +319,7 @@ class EditorMediaModalDetailItem extends Component {
}

render() {
const { item } = this.props;
const { isJetpack, item, siteId } = this.props;

const classes = classNames( 'editor-media-modal-detail__item', {
'is-loading': ! item
Expand All @@ -262,13 +331,14 @@ class EditorMediaModalDetailItem extends Component {

<div className="editor-media-modal-detail__preview-wrapper">
{ this.renderItem() }
{ this.renderImageEditorButtons( item ) }
{ this.renderMediaEditorButtons( item ) }
{ this.renderPreviousItemButton() }
{ this.renderNextItemButton() }
</div>

<div className="editor-media-modal-detail__sidebar">
{ this.renderImageEditorButtons( item, 'is-mobile' ) }
{ isJetpack && <QueryJetpackModules siteId={ siteId } /> /* Is the VideoPress module active? */ }
{ this.renderMediaEditorButtons( item, 'is-mobile' ) }
{ this.renderFields() }
<EditorMediaModalDetailFileInfo
item={ item } />
Expand All @@ -280,4 +350,20 @@ class EditorMediaModalDetailItem extends Component {
}
}

export default localize( EditorMediaModalDetailItem );
const connectComponent = connect(
( state ) => {
const siteId = getSelectedSiteId( state );

return {
isJetpack: isJetpackSite( state, siteId ),
isVideoPressEnabled: getSiteOption( state, siteId, 'videopress_enabled' ),
isVideoPressModuleActive: isJetpackModuleActive( state, siteId, 'videopress' ),
siteId,
};
}
);

export default flowRight(
connectComponent,
localize,
)( EditorMediaModalDetailItem );
Loading