Skip to content
This repository has been archived by the owner on Oct 3, 2023. It is now read-only.

Commit

Permalink
Add audio input for attaching files
Browse files Browse the repository at this point in the history
  • Loading branch information
vegeta897 committed Feb 10, 2018
1 parent 2887f91 commit 904055f
Show file tree
Hide file tree
Showing 12 changed files with 102 additions and 43 deletions.
17 changes: 17 additions & 0 deletions src/components/atoms/AudioInput/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';

const AudioInput = ({ getInput, id }) => {
return (
<div>
<input id={id} type='file' accept='audio/*' capture onChange={getInput} />
</div>
);
};

AudioInput.propTypes = {
id: PropTypes.string.isRequired,
getInput: PropTypes.func.isRequired
};

export default AudioInput;
8 changes: 8 additions & 0 deletions src/components/atoms/AudioInput/index.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { AudioInput } from 'components';

storiesOf('Audio', module)
.add('default', () => (
<AudioInput id='recorder' />
));
10 changes: 10 additions & 0 deletions src/components/atoms/AudioInput/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import React from 'react';
import { shallow } from 'enzyme';
import AudioInput from '.';

const wrap = (props = {}) => shallow(<AudioInput id='recorder' getInput={() => {}} {...props} />);

it('renders props when passed in', () => {
const wrapper = wrap({ id: 'foo' });
expect(wrapper.find({ id: 'foo' })).toHaveLength(1);
});
2 changes: 1 addition & 1 deletion src/components/organisms/ClipList/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ClipListItem } from 'components';
const ClipList = ({ list, loading, failed, ...props }) => {
return (
<div {...props}>
<h1>Clips!</h1>
<h2>Clips!</h2>
{!list.length && loading && <p>Loading</p>}
{failed && <p>Failed to load clip list, sorry!</p>}
<ul>
Expand Down
2 changes: 1 addition & 1 deletion src/components/organisms/ClipList/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ it('renders list', () => {

it('renders clip list items', () => {
const wrapper = wrap();
expect(wrapper.find('ul').children().find('ClipListItem')).toHaveLength(list.length);
expect(wrapper.find('ClipListItem')).toHaveLength(list.length);
});
10 changes: 2 additions & 8 deletions src/components/organisms/Player/index.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import { shallow } from 'enzyme';
import Player from '.';
import { Audio } from 'components';

const wrap = (props = {}) => shallow(<Player {...props} />);

Expand All @@ -22,15 +21,10 @@ it('renders failed when passed in', () => {

it('renders title when passed in', () => {
const wrapper = wrap({ detail: { title: 'Title' } });
expect(wrapper.find('div').children().find('h2').contains('Title')).toBe(true);
expect(wrapper.find('h2').contains('Title')).toBe(true);
});

it('renders audio when detail passed in', () => {
const wrapper = wrap({ detail: { title: 'Title', url: 'http://' } });
expect(wrapper.find('div').children().find('Audio')).toHaveLength(1);
});

it('renders link to clips route', () => {
const wrapper = wrap({ detail: { title: 'Title' } });
expect(wrapper.find('div').children().find({ to: '/clips' })).toHaveLength(1);
expect(wrapper.find('Audio')).toHaveLength(1);
});
24 changes: 14 additions & 10 deletions src/components/organisms/Studio/index.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Audio } from 'components';
import { Audio, AudioInput } from 'components';

const Studio = ({ startRecording, stopRecording, info, recording, started, startFailed, stopped, stopFailed, ...props }) => {
const Studio = ({ startRecording, stopRecording, info, recording, startFailed, stopFailed, clip, getInput, ...props }) => {
return (
<div {...props}>
In the stu-jo
{stopped || <button onClick={started ? stopRecording : startRecording}>
{started ? 'Stop' : 'Start'}
</button>}
{!stopped || <Audio src={recording.fileUrl} autoPlay />}
<h2>In the stu-jo</h2>
{!clip && <div>
<button onClick={recording ? stopRecording : startRecording}>
{recording ? 'Stop' : 'Start'}
</button>
<AudioInput id='recorder' getInput={getInput} />
</div>}
{clip && <Audio src={clip.fileUrl} autoPlay />}
</div>
);
};

Studio.propTypes = {
info: PropTypes.object,
recording: PropTypes.object,
clip: PropTypes.object,
startFailed: PropTypes.bool,
stopFailed: PropTypes.bool,
started: PropTypes.bool,
stopped: PropTypes.bool
recording: PropTypes.bool,
startRecording: PropTypes.func.isRequired,
stopRecording: PropTypes.func.isRequired
};

export default Studio;
14 changes: 8 additions & 6 deletions src/components/organisms/Studio/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,21 @@ import React from 'react';
import { shallow } from 'enzyme';
import Studio from '.';

const wrap = (props = {}) => shallow(<Studio {...props} />);
const wrap = (props = {}) => shallow(<Studio {...props} startRecording={() => {}} stopRecording={() => {}} />);

it('renders props when passed in', () => {
const wrapper = wrap({ id: 'foo' });
expect(wrapper.find({ id: 'foo' })).toHaveLength(1);
});

it('renders start button when not started', () => {
const wrapper = wrap({ started: false });
expect(wrapper.find('button').contains('Start')).toBe(true);
it('renders audio input when no clip', () => {
const wrapper = wrap({ clip: null });
expect(wrapper.find('AudioInput')).toHaveLength(1);
});

it('renders stop button when started', () => {
const wrapper = wrap({ started: true });
it('changes record button text when recording', () => {
const wrapper = wrap({ recording: false });
expect(wrapper.find('button').contains('Start')).toBe(true);
wrapper.setProps({ recording: true });
expect(wrapper.find('button').contains('Stop')).toBe(true);
});
19 changes: 10 additions & 9 deletions src/containers/Studio.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,39 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { hasFailed, isDone } from 'redux-saga-thunk';
import { fromStudio } from 'store/selectors';
import { recorderStartRequest, recorderStopRequest } from 'store/actions';
import { recorderStartRequest, recorderStopRequest, recorderGetInput } from 'store/actions';

import { Studio } from 'components';

class StudioContainer extends Component {
static propTypes = {
info: PropTypes.object,
recording: PropTypes.object,
clip: PropTypes.object,
startFailed: PropTypes.bool,
stopFailed: PropTypes.bool,
started: PropTypes.bool,
stopped: PropTypes.bool,
recording: PropTypes.bool,
getInput: PropTypes.func.isRequired,
startRecording: PropTypes.func.isRequired,
stopRecording: PropTypes.func.isRequired
};
render() {
return <Studio {...this.props} />
let { ...props } = this.props;
return <Studio {...props} />
}
}

const mapStateToProps = state => ({
info: fromStudio.getInfo(state, 'main'),
recording: fromStudio.getRecording(state, 'main'),
clip: fromStudio.getClip(state, 'main'),
startFailed: hasFailed(state, 'mainRecorderStart'),
stopFailed: hasFailed(state, 'mainRecorderStop'),
started: isDone(state, 'mainRecorderStart'),
stopped: isDone(state, 'mainRecorderStop')
recording: isDone(state, 'mainRecorderStart')
});

const mapDispatchToProps = (dispatch) => ({
startRecording: () => dispatch(recorderStartRequest('main')),
stopRecording: () => dispatch(recorderStopRequest('main'))
stopRecording: () => dispatch(recorderStopRequest('main')),
getInput: e => dispatch(recorderGetInput('main', e.target))
});

export default connect(mapStateToProps, mapDispatchToProps)(StudioContainer);
10 changes: 10 additions & 0 deletions src/store/studio/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,14 @@ export const recorderStopFailure = (studio, error, thunk) => ({
}
});

export const RECORDER_GET_INPUT = 'RECORDER_GET_INPUT';

export const recorderGetInput = (studio, input) => ({
type: RECORDER_GET_INPUT,
payload: input,
meta: {
studio
}
});

// TODO: Pause and resume actions, error action (any error while recording)
21 changes: 17 additions & 4 deletions src/store/studio/reducer.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import get from 'lodash/get';
import { initialState, getRecorderState, getInfo, getRecording } from './selectors';
import { initialState, getRecorderState, getInfo, getClip } from './selectors';
import {
RECORDER_START_REQUEST,
RECORDER_START_SUCCESS,
RECORDER_STOP_REQUEST,
RECORDER_STOP_SUCCESS
RECORDER_STOP_SUCCESS,
RECORDER_GET_INPUT
} from './actions';

export default (state = initialState, { type, payload, meta }) => {
Expand Down Expand Up @@ -36,15 +37,27 @@ export default (state = initialState, { type, payload, meta }) => {
...state,
[studio]: {
...getRecorderState(state, studio),
recording: getRecording(initialState, studio)
clip: getClip(initialState, studio)
}
};
case RECORDER_STOP_SUCCESS:
return {
...state,
[studio]: {
...getRecorderState(state, studio),
recording: payload
clip: payload
}
};
case RECORDER_GET_INPUT:
let file = payload.files[0];
return {
...state,
[studio]: {
...getRecorderState(state, studio),
clip: {
file,
fileUrl: URL.createObjectURL(file)
}
}
};
default:
Expand Down
8 changes: 4 additions & 4 deletions src/store/studio/selectors.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
export const initialState = {};

export const initialStudioState = {
info: null,
recording: null
clip: null,
info: null
};

export const getRecorderState = (state = initialState, studio) => state[studio] || initialStudioState;

export const getRecording = (state = initialState, studio) =>
getRecorderState(state, studio).recording;
export const getClip = (state = initialState, studio) =>
getRecorderState(state, studio).clip;

export const getInfo = (state = initialState, studio) =>
getRecorderState(state, studio).info;

0 comments on commit 904055f

Please sign in to comment.