Skip to content

Commit

Permalink
Audio file upload with redux-saga
Browse files Browse the repository at this point in the history
see: #17
  • Loading branch information
sujinleeme committed Jul 24, 2018
1 parent 83a922c commit d5bb571
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 3 deletions.
Empty file removed src/player/sagas.js
Empty file.
5 changes: 3 additions & 2 deletions src/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import modal from "../modal/reducer"
import client from "../client/reducer"
import snackbar from "../snackbar/reducer"
import player from "../player/reducer"

import upload from "../upload/reducer"
export default combineReducers({
routing: routerReducer,
client,
modal,
signup,
snackbar,
player
player,
upload
})

3 changes: 2 additions & 1 deletion src/sagas/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { all } from "redux-saga/effects"
import ModalSaga from "../modal/sagas"
import SignupSaga from "../signup/sagas"
import SnackbarSaga from "../snackbar/sagas"
import UploaderSaga from "../upload/sagas"

export default function* rootSagas() {

yield all([
ModalSaga(),
SignupSaga(),
SnackbarSaga(),
UploaderSaga()
])
}
14 changes: 14 additions & 0 deletions src/upload/actions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { UPLOAD_REQUEST, UPLOAD_RESET } from "./constants"

export const uploadRequest = ({file}) => {
return {
type: UPLOAD_REQUEST,
file
}
}

export const uploadReset = () => {
return {
type: UPLOAD_RESET,
}
}
5 changes: 5 additions & 0 deletions src/upload/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const UPLOAD_REQUEST = "UPLOAD_REQUEST"
export const UPLOAD_PROGRESS = "UPLOAD_PROGRESS"
export const UPLOAD_SUCCESS = "UPLOAD_SUCCESS"
export const UPLOAD_ERROR = "UPLOAD_ERROR"
export const UPLOAD_RESET = "UPLOAD_RESET"
47 changes: 47 additions & 0 deletions src/upload/createFileUploadChannel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* refs
https://decembersoft.com/posts/file-upload-progress-with-redux-saga/
*/

import { buffers, eventChannel, END } from "redux-saga"

const createUploadFileChannel = (endpoint, file) => {
return eventChannel(emitter => {
const xhr = new XMLHttpRequest()
const onProgress = (e) => {
if (e.lengthComputable) {
const progress = e.loaded / e.total
emitter({progress})
}
}
const onFailure = (e) => {
emitter({err: new Error("Upload failed")})
emitter(END)
}
xhr.upload.addEventListener("progress", onProgress)
xhr.upload.addEventListener("error", onFailure)
xhr.upload.addEventListener("abort", onFailure)
xhr.onreadystatechange = () => {
const {readyState, status} = xhr
if (readyState === 4) {
if (status === 200) {
emitter({success: true})
emitter(END)
}
else {
onFailure(null)
}
}
}
xhr.open("POST", endpoint, true)
xhr.send(file)
return () => {
xhr.upload.removeEventListener("progress", onProgress)
xhr.upload.removeEventListener("error", onFailure)
xhr.upload.removeEventListener("abort", onFailure)
xhr.onreadystatechange = null
xhr.abort()
}
}, buffers.sliding(2))
}

export default createUploadFileChannel
File renamed without changes.
73 changes: 73 additions & 0 deletions src/upload/reducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { UPLOAD_RESET, UPLOAD_REQUEST, UPLOAD_SUCCESS, UPLOAD_ERROR, UPLOAD_PROGRESS } from "./constants"

const initialState = {
file: null,
requesting: false,
successful: false,
progress: 0,
messages: [],
errors: []
}

const reducer = (state = initialState, action) => {
switch (action.type) {
case UPLOAD_RESET:
return {
file: null,
requesting: false,
successful: false,
progress: 0,
messages: [],
errors: []
}
case UPLOAD_REQUEST:
return {
file: null,
requesting: true,
successful: false,
progress: 0,
messages: [{body: "Uploading...", time: new Date()}],
errors: []
}

case UPLOAD_SUCCESS:
return {
file: action.file,
requesting: false,
successful: true,
progress: 100,
errors: [],
messages: [{
body: `Successfully save file.`,
time: new Date()
}]
}

case UPLOAD_ERROR:
return {
file: state.file,
requesting: false,
successful: false,
progress: null,
errors: state.errors.concat([{
body: action.error.toString(),
time: new Date()
}]),
messages: []
}

case UPLOAD_PROGRESS:
return {
file: action.file,
requesting: false,
successful: false,
progress: action.progress,
errors: [],
messages: []
}
default:
return state
}
}

export default reducer
37 changes: 37 additions & 0 deletions src/upload/sagas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { call, put, take, takeEvery } from "redux-saga/effects"
import { UPLOAD_REQUEST, UPLOAD_RESET, UPLOAD_PROGRESS, UPLOAD_SUCCESS, UPLOAD_ERROR} from "./constants"
import {snackbarRequest } from "../snackbar/actions"
import { PREVIEW_SUCCESS} from "../snackbar/constants"
import createFileUploadChannel from "./createFileUploadChannel"

// Upload the specified file
function* uploadFileFlow(file) {
const channel = yield call(createFileUploadChannel, "/some/path", file)
while (true) {
const {progress = 0, error, success} = yield take(channel)
if (error) {
yield put({type: UPLOAD_ERROR, error})
return
}
if (success) {
yield put({type: UPLOAD_SUCCESS, file})
return
}

// show snackbar notification
yield put(snackbarRequest({snackbarType: PREVIEW_SUCCESS, snackbarOpen: true}))
yield put({type: UPLOAD_PROGRESS, file, progress})
}
}

// Watch for an upload request and then
// defer to another saga to perform the actual upload

function* uploadFileWatcher() {
yield takeEvery(UPLOAD_REQUEST, function* (action) {
const file = action.file
yield call(uploadFileFlow, file)
})
}

export default uploadFileWatcher

0 comments on commit d5bb571

Please sign in to comment.