diff --git a/react/actions/publish.js b/react/actions/publish.js index ba7e2714..9c90e2b0 100644 --- a/react/actions/publish.js +++ b/react/actions/publish.js @@ -85,3 +85,9 @@ export function updateThumbnailSelectedFile (file) { data: file, }; }; + +export function startPublish () { + return { + type: actions.PUBLISH_START, + }; +} diff --git a/react/api/publishApi.js b/react/api/publishApi.js new file mode 100644 index 00000000..e3d9939f --- /dev/null +++ b/react/api/publishApi.js @@ -0,0 +1,7 @@ +import Request from 'utils/request'; +const { site: { host } } = require('../../config/speechConfig.js'); + +export function publish () { + // const url = `${host}/api/file/availability/${name}/${claimId}`; + +} diff --git a/react/constants/publish_action_types.js b/react/constants/publish_action_types.js index f3c70947..42487c3b 100644 --- a/react/constants/publish_action_types.js +++ b/react/constants/publish_action_types.js @@ -9,3 +9,4 @@ 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_FILE_SELECT = 'THUMBNAIL_FILE_SELECT'; +export const PUBLISH_START = 'PUBLISH_START'; diff --git a/react/containers/PublishForm/index.js b/react/containers/PublishForm/index.js index e0bc4473..1eb77195 100644 --- a/react/containers/PublishForm/index.js +++ b/react/containers/PublishForm/index.js @@ -1,5 +1,5 @@ import {connect} from 'react-redux'; -import {clearFile, updateError, updatePublishStatus} from 'actions/publish'; +import {clearFile, updateError, updatePublishStatus, startPublish} from 'actions/publish'; import View from './view'; const mapStateToProps = ({ channel, publish }) => { @@ -28,12 +28,10 @@ const mapDispatchToProps = dispatch => { onPublishStatusChange: (status, message) => { dispatch(updatePublishStatus(status, message)); }, - onChannelSelectionError: (value) => { - dispatch(updateError('channel', value)); - }, onPublishSubmitError: (value) => { dispatch(updateError('publishSubmit', value)); }, + startPublish, }; }; diff --git a/react/containers/PublishForm/view.jsx b/react/containers/PublishForm/view.jsx index 90e0ce51..b8e40476 100644 --- a/react/containers/PublishForm/view.jsx +++ b/react/containers/PublishForm/view.jsx @@ -9,120 +9,6 @@ import ChannelSelect from 'containers/ChannelSelect'; import * as publishStates from 'constants/publish_claim_states'; class PublishForm extends React.Component { - constructor (props) { - super(props); - // this.makePublishRequest = this.makePublishRequest.bind(this); - this.publish = this.publish.bind(this); - } - validateChannelSelection () { - console.log('validating channel selection'); - // make sure all required data is provided - return new Promise((resolve, reject) => { - // if publishInChannel is true, is a channel selected & logged in? - if (this.props.publishInChannel && (this.props.selectedChannel !== this.props.loggedInChannel.name)) { - // update state with error - this.props.onChannelSelectionError('Log in to a channel or select Anonymous"'); - // reject this promise - return reject(new Error('Fix the channel')); - } - resolve(); - }); - } - validatePublishParams () { - console.log('validating publish params'); - // make sure all required data is provided - return new Promise((resolve, reject) => { - // is there a file? - if (!this.props.file) { - return reject(new Error('Please choose a file')); - } - // is there a claim chosen? - if (!this.props.claim) { - return reject(new Error('Please enter a URL')); - } - if (this.props.urlError) { - return reject(new Error('Fix the url')); - } - resolve(); - }); - } - makePublishRequest (file, metadata) { - console.log('making publish request'); - const uri = '/api/claim/publish'; - const xhr = new XMLHttpRequest(); - const fd = this.appendDataToFormData(file, metadata); - xhr.upload.addEventListener('loadstart', () => { - this.props.onPublishStatusChange(publishStates.LOAD_START, 'upload started'); - }); - xhr.upload.addEventListener('progress', (e) => { - if (e.lengthComputable) { - const percentage = Math.round((e.loaded * 100) / e.total); - console.log('progress:', percentage); - this.props.onPublishStatusChange(publishStates.LOADING, `${percentage}%`); - } - }, false); - xhr.upload.addEventListener('load', () => { - console.log('loaded 100%'); - this.props.onPublishStatusChange(publishStates.PUBLISHING, null); - }, false); - xhr.open('POST', uri, true); - xhr.onreadystatechange = () => { - if (xhr.readyState === 4) { - const response = JSON.parse(xhr.response); - console.log('publish response:', response); - if ((xhr.status === 200) && response.success) { - this.props.history.push(`/${response.data.claimId}/${response.data.name}`); - this.props.onFileClear(); - } else { - this.props.onPublishStatusChange(publishStates.FAILED, response.message); - } - } - }; - // Initiate a multipart/form-data upload - xhr.send(fd); - } - createMetadata () { - console.log('creating metadata'); - let metadata = { - name : this.props.claim, - title : this.props.title, - description: this.props.description, - license : this.props.license, - nsfw : this.props.nsfw, - type : this.props.file.type, - thumbnail : this.props.thumbnail, - }; - if (this.props.publishInChannel) { - metadata['channelName'] = this.props.selectedChannel; - } - return metadata; - } - appendDataToFormData (file, metadata) { - var fd = new FormData(); - fd.append('file', file); - for (var key in metadata) { - if (metadata.hasOwnProperty(key)) { - fd.append(key, metadata[key]); - } - } - return fd; - } - publish () { - console.log('publishing file'); - // publish the asset - this.validateChannelSelection() - .then(() => { - return this.validatePublishParams(); - }) - .then(() => { - const metadata = this.createMetadata(); - // publish the claim - return this.makePublishRequest(this.props.file, metadata); - }) - .catch((error) => { - this.props.onPublishSubmitError(error.message); - }); - } render () { return (
@@ -153,7 +39,7 @@ class PublishForm extends React.Component {
- +
diff --git a/react/containers/PublishThumbnailInput/view.jsx b/react/containers/PublishThumbnailInput/view.jsx index ba34b8c4..b964248b 100644 --- a/react/containers/PublishThumbnailInput/view.jsx +++ b/react/containers/PublishThumbnailInput/view.jsx @@ -64,8 +64,7 @@ class PublishThumbnailInput extends React.Component { canvas.width = video.videoWidth; canvas.height = video.videoHeight; canvas.getContext('2d').drawImage(video, 0, 0, canvas.width, canvas.height); - const imageDataUrl = canvas.toDataURL(); - return imageDataUrl; + return canvas.toDataURL(); } render () { const { error, videoSource, sliderMinRange, sliderMaxRange, sliderValue } = this.state; diff --git a/react/sagas/index.js b/react/sagas/index.js index 83936994..ac34adeb 100644 --- a/react/sagas/index.js +++ b/react/sagas/index.js @@ -3,6 +3,7 @@ import { watchHandleShowPageUri } from './show_uri'; import { watchNewAssetRequest } from './show_asset'; import { watchNewChannelRequest, watchUpdateChannelClaims } from './show_channel'; import { watchFileIsRequested } from './file'; +import { watchPublishStart } from './publish'; export default function * rootSaga () { yield all([ @@ -11,5 +12,6 @@ export default function * rootSaga () { watchNewChannelRequest(), watchUpdateChannelClaims(), watchFileIsRequested(), + watchPublishStart(), ]); } diff --git a/react/sagas/publish.js b/react/sagas/publish.js new file mode 100644 index 00000000..b582ab20 --- /dev/null +++ b/react/sagas/publish.js @@ -0,0 +1,136 @@ +import { call, put, select, takeLatest } from 'redux-saga/effects'; +import * as actions from 'constants/publish_action_types'; +import { updateError } from 'actions/publish'; +// import { publish } from 'api/fileApi'; +import { selectPublishState } from '../selectors/publish'; +import { selectChannelState } from '../selectors/channel'; +import * as publishStates from '../constants/publish_claim_states'; + +const validateChannelSelection = (publishInChannel, selectedChannel, loggedInChannel) => { + console.log('validating channel selection'); + // make sure all required data is provided + return new Promise((resolve, reject) => { + // if publishInChannel is true, is a channel selected & logged in? + if (publishInChannel && (selectedChannel !== loggedInChannel.name)) { + return reject('Log in to a channel or select Anonymous'); + } + resolve(); + }); +} + +const validatePublishParams = (file, claim, urlError) => { + console.log('validating publish params'); + // make sure all required data is provided + return new Promise((resolve, reject) => { + // is there a file? + if (!file) { + return reject('Please choose a file'); + } + // is there a claim chosen? + if (!claim) { + return reject('Please enter a URL'); + } + if (urlError) { + return reject('Fix the url'); + } + resolve(); + }); +} + +const createPublishMetadata = (claim, { type }, { title, thumbnail, description, license, nsfw }, publishInChannel, selectedChannel) => { + let metadata = { + name: claim, + title, + thumbnail, + description, + license, + nsfw, + type, + }; + if (publishInChannel) { + metadata['channelName'] = selectedChannel; + } + return metadata; +} + +const createPublishFormData = (file, metadata) => { + var fd = new FormData(); + // append file + fd.append('file', file); + // append metadata + for (var key in metadata) { + if (metadata.hasOwnProperty(key)) { + fd.append(key, metadata[key]); + } + } + return fd; +} + +const makePublishRequest = (fd) => { + console.log('making publish request'); + return new Promise((resolve, reject) => { + const uri = '/api/claim/publish'; + const xhr = new XMLHttpRequest(); + xhr.upload.addEventListener('loadstart', () => { + this.props.onPublishStatusChange(publishStates.LOAD_START, 'upload started'); + }); + xhr.upload.addEventListener('progress', (e) => { + if (e.lengthComputable) { + const percentage = Math.round((e.loaded * 100) / e.total); + console.log('progress:', percentage); + this.props.onPublishStatusChange(publishStates.LOADING, `${percentage}%`); + } + }, false); + xhr.upload.addEventListener('load', () => { + console.log('loaded 100%'); + this.props.onPublishStatusChange(publishStates.PUBLISHING, null); + }, false); + xhr.open('POST', uri, true); + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + const response = JSON.parse(xhr.response); + console.log('publish response:', response); + if ((xhr.status === 200) && response.success) { + this.props.history.push(`/${response.data.claimId}/${response.data.name}`); + this.props.onFileClear(); + } else { + this.props.onPublishStatusChange(publishStates.FAILED, response.message); + } + } + }; + // Initiate a multipart/form-data upload + xhr.send(fd); + }); +} + +function * publishFile () { + console.log('publishing file'); + const { publishInChannel, selectedChannel, file, claim, metadata, error: { url: urlError } } = yield select(selectPublishState); + const { loggedInChannel } = yield select(selectChannelState); + // validate the channel selection + try { + yield call(validateChannelSelection, publishInChannel, selectedChannel, loggedInChannel); + } catch (error) { + return yield put(updateError('channel', error)); + }; + // validate publish parameters + try { + yield call(validatePublishParams, file, claim, urlError); + } catch (error) { + return yield put(updateError('publishSubmit', error)); + } + // create metadata + const publishMetadata = createPublishMetadata(claim, file, metadata, publishInChannel, selectedChannel); + // create form data + const publishFormData = createPublishFormData(file, publishMetadata); + // make the publish request + try { + yield call(makePublishRequest, publishFormData); + } catch (error) { + return yield put(updateError('publishSubmit', error)); + } +}; + +export function * watchPublishStart () { + yield takeLatest(actions.PUBLISH_START, publishFile); +}; diff --git a/react/selectors/channel.js b/react/selectors/channel.js new file mode 100644 index 00000000..53e1c9f7 --- /dev/null +++ b/react/selectors/channel.js @@ -0,0 +1,3 @@ +export const selectChannelState = (state) => { + return state.channel; +}; diff --git a/react/selectors/publish.js b/react/selectors/publish.js new file mode 100644 index 00000000..7cfbc525 --- /dev/null +++ b/react/selectors/publish.js @@ -0,0 +1,3 @@ +export const selectPublishState = (state) => { + return state.publish; +};