added slider to select thumb

This commit is contained in:
bill bittner 2018-02-28 21:25:51 -08:00
parent d3dc86fd1f
commit 41b5a05a55
7 changed files with 112 additions and 164 deletions

View file

@ -283,11 +283,12 @@ a, a:visited {
/* ERROR MESSAGES */
.info-message--success, .info-message--failure {
.info-message, .info-message--success, .info-message--failure {
font-size: medium;
margin: 0px;
padding: 0.3em;
color: #9b9b9b;
}
.info-message--success {
@ -603,33 +604,6 @@ table {
text-align: center;
}
/* ---- grid items ---- */
.grid-item {
width: calc(33% - 2rem);
padding: 0px;
margin: 1rem;
float: left;
border: 0.5px solid white;
}
.grid-item-image {
width: 100%;
}
.grid-item-details {
position: absolute;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
cursor: pointer;
}
.grid-item-details-text {
font-size: medium;
margin: 0px;
text-align: center;
padding: 1em 0px 1em 0px;
width: 100%;
.slider {
width: 100%
}

View file

@ -75,23 +75,13 @@ export function toggleMetadataInputs (showMetadataInputs) {
export function updateThumbnailClaim (claim, url) {
return {
type: actions.THUMBNAIL_CLAIM_UPDATE,
claim,
url,
};
};
export function updateThumbnailFileOptions (fileOne, fileTwo, fileThree) {
return {
type: actions.THUMBNAIL_FILES_UPDATE,
fileOne,
fileTwo,
fileThree,
data: { claim, url },
};
};
export function updateThumbnailSelectedFile (file) {
return {
type: actions.THUMBNAIL_FILE_SELECT,
file,
data: file,
};
};

View file

@ -34,10 +34,10 @@ class Preview extends React.Component {
render () {
return (
<img
id="dropzone-preview"
id='dropzone-preview'
src={this.state.imgSource}
className={this.props.dimPreview ? 'dim' : ''}
alt="publish preview"
alt='publish preview'
/>
);
}

View file

@ -8,5 +8,4 @@ export const ERROR_UPDATE = 'ERROR_UPDATE';
export const SELECTED_CHANNEL_UPDATE = 'SELECTED_CHANNEL_UPDATE';
export const TOGGLE_METADATA_INPUTS = 'TOGGLE_METADATA_INPUTS';
export const THUMBNAIL_CLAIM_UPDATE = 'THUMBNAIL_CLAIM_UPDATE';
export const THUMBNAIL_FILES_UPDATE = 'THUMBNAIL_FILES_UPDATE';
export const THUMBNAIL_FILE_SELECT = 'THUMBNAIL_FILE_SELECT';

View file

@ -1,30 +1,25 @@
import { connect } from 'react-redux';
import { updateThumbnailClaim, updateThumbnailFileOptions, updateThumbnailSelectedFile } from 'actions/publish';
import { updateThumbnailClaim, updateThumbnailSelectedFile } from 'actions/publish';
import View from './view';
const mapStateToProps = ({ publish, site }) => {
return {
host : site.host,
host : site.host,
// file props
publishFile : publish.file,
publishClaim : publish.claim,
file : publish.file,
claim : publish.claim,
// channel props
channel : publish.thumbnail.channel,
claim : publish.thumbnail.claim,
url : publish.thumbnail.url,
potentialFiles: publish.thumbnail.potentialFiles,
selectedFile : publish.thumbnail.selectedFile,
thumbnailChannel: publish.thumbnail.channel,
thumbnailClaim : publish.thumbnail.claim,
thumbnailFile : publish.thumbnail.selectedFile,
};
};
const mapDispatchToProps = dispatch => {
return {
onThumbnailClaimChange: (claim, url) => {
onThumbnailChange: (claim, url) => {
dispatch(updateThumbnailClaim(claim, url));
},
onThumbnailFileOptionsChange: (fileOne, fileTwo, fileThree) => {
dispatch(updateThumbnailFileOptions(fileOne, fileTwo, fileThree));
},
onThumbnailFileSelect: (file) => {
dispatch(updateThumbnailSelectedFile(file));
},

View file

@ -1,117 +1,113 @@
import React from 'react';
const ThumbnailPreview = ({dataUrl}) => {
const divStyle = {
width : '30%',
margin : '1%',
display: 'inline-block',
border : 'solid 1px black',
};
const imageStyle = {
width : '100%',
display: 'block',
};
return (
<div style={divStyle}>
{ dataUrl ? (
<img style={imageStyle} src={dataUrl} alt='image preview here' />
) : (
<p>loading...</p>
)}
</div>
);
};
class PublishThumbnailInput extends React.Component {
constructor (props) {
super(props);
this.state = {
videoPreviewSrc: null,
thumbnailError : null,
thumbnailInput : '',
videoSource : null,
error : null,
sliderMinRange: 1,
sliderMaxRange: null,
sliderValue : null,
};
this.handleSliderChange = this.handleSliderChange.bind(this);
this.handleVideoLoadedData = this.handleVideoLoadedData.bind(this);
this.setThumbnailWithSnapshot = this.setThumbnailWithSnapshot.bind(this);
}
componentDidMount () {
this.setClaimAndThumbailUrl(this.props.publishClaim);
this.previewThumbnails(this.props.publishFile);
this.setThumbnailClaimAndUrl();
this.setVideoSource();
}
componentWillReceiveProps (nextProps) {
if (nextProps.publishClaim !== this.props.publishClaim) {
console.log(nextProps.publishClaim, this.props.publishClaim);
this.setClaimAndThumbailUrl(nextProps.publishClaim);
}
setThumbnailClaimAndUrl () {
const { claim, host, thumbnailChannel } = this.props;
const url = `${host}/${thumbnailChannel}/${claim}-thumb.png`;
this.props.onThumbnailChange(`${claim}-thumb`, url);
}
previewThumbnails (videoFile) {
console.log('video file:', videoFile);
this.loadFileAndReturnThumbnails(videoFile)
.then((thumbnail) => {
this.selectVideoThumb(thumbnail);
this.setPossibleThumbnailFiles(thumbnail, thumbnail, thumbnail);
})
.catch(error => {
console.log('error:', error);
this.setState({error: error.message});
});
setVideoSource () {
const { file } = this.props;
const previewReader = new FileReader();
previewReader.readAsDataURL(file);
previewReader.onloadend = () => {
this.setState({videoSource: previewReader.result});
};
}
loadFileAndReturnThumbnails (file) {
return new Promise((resolve, reject) => {
var fileReader = new FileReader();
fileReader.onload = () => {
console.log('file loaded');
const blob = new Blob([fileReader.result], {type: file.type});
const url = URL.createObjectURL(blob);
let video = document.createElement('video');
const snapShot = () => {
let canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
const imageDataUrl = canvas.toDataURL();
const success = imageDataUrl.length > 1000;
if (success) {
return imageDataUrl;
}
return success;
};
const loadedata = () => {
console.log('loadeddata');
console.log('readyState', video.readyState);
const duration = video.duration;
console.log('readyState', duration);
const thumb = snapShot();
resolve(thumb);
};
video.addEventListener('loadeddata', loadedata);
video.preload = 'metadata';
video.src = url;
// Load video in Safari / IE11
video.muted = true;
video.playsInline = true;
video.play();
};
fileReader.readAsArrayBuffer(file);
handleVideoLoadedData (event) {
const duration = event.target.duration;
// set the slider
this.setState({
sliderMaxRange: duration * 100,
sliderValue : duration * 100 / 2,
});
// update the current time of the video
let video = document.getElementById('video-thumb-player');
video.currentTime = duration / 2;
}
selectVideoThumb (dataUrl) {
// update this.props.selectedFile
this.props.onThumbnailFileSelect(dataUrl);
handleSliderChange (event) {
const value = parseInt(event.target.value);
// update the slider value
this.setState({
sliderValue: value,
});
// update the current time of the video
let video = document.getElementById('video-thumb-player');
video.currentTime = value / 100;
}
setPossibleThumbnailFiles (fileOne, fileTwo, fileThree) {
console.log('updating thumbnail file options');
this.props.onThumbnailFileOptionsChange(fileOne, fileTwo, fileThree);
setThumbnailWithSnapshot () {
// take a snapshot
const snapshot = this.takeSnapShot();
// set the thumbnail in redux store
if (snapshot) this.props.onThumbnailFileSelect(snapshot);
}
setClaimAndThumbailUrl (claim) {
// set thumbnail claim based on publish claim name
const url = `${this.props.host}/${this.props.channel}/${claim}.jpeg`;
this.props.onThumbnailClaimChange(claim, url);
takeSnapShot () {
let video = document.getElementById('video-thumb-player');
let canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height);
const imageDataUrl = canvas.toDataURL();
const success = imageDataUrl.length > 1000;
if (!success) {
this.setState({error: 'error taking snapshot'});
return false;
}
return imageDataUrl;
}
render () {
const { error, videoSource, sliderMinRange, sliderMaxRange, sliderValue } = this.state;
return (
<div>
<label className='label'>Thumbnail:</label>
<div>
<p className='info-message-placeholder info-message--failure'>{this.state.error}</p>
{this.props.potentialFiles.map((file, index) => <ThumbnailPreview dataUrl={file} key={index} />)}
{ error ? (
<p className='info-message--failure'>{error}</p>
) : (
<p className='info-message'>Use slider to set thumbnail:</p>
)}
<video
id='video-thumb-player'
preload='metadata'
muted
style={{display: 'none'}}
playsInline
onLoadedData={this.handleVideoLoadedData}
src={videoSource}
onTimeUpdate={this.setThumbnailWithSnapshot}
/>
{
sliderValue ? (
<div className='slide-container'>
<input
type='range'
min={sliderMinRange}
max={sliderMaxRange}
value={sliderValue}
className='slider'
onChange={this.handleSliderChange}
/>
</div>
) : (
<p>loading slider... </p>
)
}
</div>
</div>
);

View file

@ -26,11 +26,9 @@ const initialState = {
nsfw : false,
},
thumbnail: {
channel : publish.thumbnailChannel,
claim : null,
url : null,
potentialFiles: [], // should be named 'thumbnailFiles' or something
selectedFile : null,
channel : publish.thumbnailChannel,
claim : null,
selectedFile: null,
},
};
@ -76,21 +74,17 @@ export default function (state = initialState, action) {
});
case actions.THUMBNAIL_CLAIM_UPDATE:
return Object.assign({}, state, {
thumbnail: Object.assign({}, state.thumbnail, {
claim: action.claim,
url : action.url,
metadata: Object.assign({}, state.metadata, {
thumbnail: action.data.url,
}),
});
case actions.THUMBNAIL_FILES_UPDATE:
return Object.assign({}, state, {
thumbnail: Object.assign({}, state.thumbnail, {
potentialFiles: [action.fileOne, action.fileTwo, action.fileThree],
claim: action.data.claim,
}),
});
case actions.THUMBNAIL_FILE_SELECT:
return Object.assign({}, state, {
thumbnail: Object.assign({}, state.thumbnail, {
selectedFile: action.file,
selectedFile: action.data,
}),
});
default: