diff --git a/react/channels/publish.js b/react/channels/publish.js new file mode 100644 index 00000000..b617d682 --- /dev/null +++ b/react/channels/publish.js @@ -0,0 +1,52 @@ +import {buffers, END, eventChannel} from 'redux-saga'; + +export const makePublishRequestChannel = (fd) => { + console.log('making publish request'); + return eventChannel(emitter => { + const uri = '/api/claim/publish'; + const xhr = new XMLHttpRequest(); + // add event listeners + const onLoadStart = () => { + console.log('load started'); + emitter({loadStart: true}); + }; + const onProgress = (event) => { + if (event.lengthComputable) { + const percentage = Math.round((event.loaded * 100) / event.total); + console.log('progress:', percentage); + emitter({progress: percentage}); + } + }; + const onLoad = () => { + console.log('load completed'); + emitter({load: true}); + }; + xhr.upload.addEventListener('loadstart', onLoadStart); + xhr.upload.addEventListener('progress', onProgress); + xhr.upload.addEventListener('load', onLoad); + // set state change handler + xhr.onreadystatechange = () => { + if (xhr.readyState === 4) { + const response = JSON.parse(xhr.response); + if ((xhr.status === 200) && response.success) { + emitter({success: response}); + emitter(END); + } else { + emitter({error: new Error(response.message)}); + emitter(END); + } + } + }; + // open and send + xhr.open('POST', uri, true); + xhr.send(fd); + // clean up + return () => { + xhr.upload.removeEventListener('loadstart', onLoadStart); + xhr.upload.removeEventListener('progress', onProgress); + xhr.upload.removeEventListener('load', onLoad); + xhr.onreadystatechange = null; + xhr.abort(); + }; + }, buffers.sliding(2)); +}; diff --git a/react/sagas/publish.js b/react/sagas/publish.js index ca189576..31998a7e 100644 --- a/react/sagas/publish.js +++ b/react/sagas/publish.js @@ -1,4 +1,4 @@ -import { call, put, select, takeLatest } from 'redux-saga/effects'; +import { call, put, select, take, takeLatest } from 'redux-saga/effects'; import * as actions from 'constants/publish_action_types'; import * as publishStates from 'constants/publish_claim_states'; import { updateError, updatePublishStatus, clearFile } from 'actions/publish'; @@ -6,42 +6,7 @@ import { selectPublishState } from 'selectors/publish'; import { selectChannelState } from 'selectors/channel'; import { validateChannelSelection, validatePublishParams } from 'utils/validate'; import { createPublishMetadata, createPublishFormData } from 'utils/publish'; - -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', () => { - put(updatePublishStatus(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); - put(updatePublishStatus(publishStates.LOADING, `${percentage}%`)); - } - }, false); - xhr.upload.addEventListener('load', () => { - console.log('loaded 100%'); - put(updatePublishStatus(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) { - resolve(response); - } else { - reject(new Error(response.message)); - } - } - }; - // Initiate a multipart/form-data upload - xhr.send(fd); - }); -}; +import { makePublishRequestChannel } from 'channels/publish'; function * publishFile (action) { const { history } = action.data; @@ -65,13 +30,26 @@ function * publishFile (action) { // create form data const publishFormData = createPublishFormData(file, publishMetadata); // make the publish request - let response; - try { - response = yield call(makePublishRequest, publishFormData); - yield put(clearFile()); - history.push(`/${response.data.claimId}/${response.data.name}`); - } catch (error) { - return yield put(updatePublishStatus(publishStates.FAILED, error.message)); + const channel = yield call(makePublishRequestChannel, publishFormData); + while (true) { + const {loadStart, progress, load, success, error} = yield take(channel); + console.log('emitted:', loadStart, progress, load, success, error); + if (error) { + return yield put(updatePublishStatus(publishStates.FAILED, error.message)); + } + if (success) { + yield put(clearFile()); + return history.push(`/${success.data.claimId}/${success.data.name}`); + } + if (loadStart) { + yield put(updatePublishStatus(publishStates.LOAD_START, 'upload started')); + } + if (progress) { + yield put(updatePublishStatus(publishStates.LOADING, `${progress}%`)); + } + if (load) { + yield put(updatePublishStatus(publishStates.PUBLISHING, null)); + } } };