@krowdy-ui/views
Version:
React components that implement Google's Material Design.
338 lines (313 loc) • 9.49 kB
JavaScript
import _extends from "@babel/runtime/helpers/esm/extends";
import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import MicRecorder from 'mic-recorder-to-mp3';
import { IconButton } from '@krowdy-ui/core';
import { withStyles } from '@krowdy-ui/styles';
import Timer from 'react-compound-timer';
const Mp3Recorder = new MicRecorder({
bitRate: 128
});
const SPECTRO_PASSIVE_COLOR = 'grey';
const SPECTRO_ACTIVE_COLOR = 'white';
const TIME_RECORD = 120;
const INCREMENT_STYLE = 6.92;
const PADDING_LEFT = 15;
const PADDING_RIGHT = 15; // position
const X_POSITION = PADDING_LEFT;
const Y_SIZE = 0;
export const styles = theme => ({
audio: {
height: 0,
visibility: 'hidden',
width: 0
},
barContainer: {
alignItems: 'center',
display: 'flex',
flexDirection: 'row',
position: 'relative',
width: 'calc(100% - 230px)'
},
button: {
color: theme.palette.primary['contrastText'],
fontWeight: 'bold',
left: 10,
position: 'absolute'
},
canvas: {
background: theme.palette.primary[500],
borderRadius: 60,
height: 50,
padding: 5,
width: '100%'
},
container: {
display: 'flex',
flexDirection: 'row'
},
counter: {
color: theme.palette.primary['contrastText'],
fontWeight: 'bold',
position: 'absolute',
right: 26
}
});
const withTimer = timerProps => WrappedComponent => wrappedComponentProps => /*#__PURE__*/React.createElement(Timer, timerProps, timerRenderProps => /*#__PURE__*/React.createElement(WrappedComponent, _extends({}, wrappedComponentProps, {
timer: timerRenderProps
})));
let interval = null;
const getLimitDate = secs => {
let rawDate = new Date(null);
rawDate.setSeconds(secs);
return rawDate.toISOString().substr(14, 5);
};
var _ref = /*#__PURE__*/React.createElement(Timer.Minutes, null);
var _ref2 = /*#__PURE__*/React.createElement("span", null, ":");
var _ref3 = /*#__PURE__*/React.createElement(Timer.Seconds, null);
var _ref4 = /*#__PURE__*/React.createElement("span", null, "/");
const AudioRecorder = props => {
const {
classes,
pause: PauseIcon,
play: PlayIcon,
record: RecordIcon,
stop: StopIcon,
'delete': DeleteIcon,
timer: {
start,
pause,
stop,
reset,
// timerState,
getTime
},
onBlobUrl = () => {},
spectroActiveColor = SPECTRO_ACTIVE_COLOR,
spectroPassiveColor = SPECTRO_PASSIVE_COLOR,
onStatus = () => {},
classNameCanvas
} = props;
const [blobURL, setBlobURL] = React.useState('');
const [blocked, setBlocked] = React.useState(false);
const [ctx, setCtx] = React.useState(null);
const [time, setTime] = React.useState(0);
const [type, setType] = React.useState('record');
const [currentTime, setCurrentTime] = React.useState({
i: X_POSITION,
x: Y_SIZE
});
const canvas = React.useRef();
const audio = React.useRef();
const drawStick = (ctx, i, x, color) => {
const cut = canvas.current.height * Math.abs(Math.sin(x));
ctx.fillStyle = color;
ctx.fillRect(i * 2, (canvas.current.height - cut) / 2, 2, cut);
};
const drawSticks = React.useCallback((ctx, limit, color, lazy) => {
if (!lazy) ctx.clearRect(0, 0, canvas.current.width, canvas.current.height);
let i = X_POSITION;
let x = Y_SIZE;
while (limit - (PADDING_LEFT + PADDING_RIGHT) > i) {
x += INCREMENT_STYLE;
drawStick(ctx, i, x, color);
i += 2;
}
}, []);
const loopFrames = (lapse, i = X_POSITION, x = Y_SIZE) => {
interval = setInterval(() => {
if (getTime() >= lapse * 1000) {
_handleStop();
clearInterval(interval);
setCurrentTime({
i: X_POSITION,
x: Y_SIZE
});
} else {
if (canvas && canvas.current && canvas.current.height) {
if (canvas.current.height - (PADDING_LEFT + PADDING_RIGHT) > i) {
x += INCREMENT_STYLE;
drawStick(ctx, i, x, spectroActiveColor);
i += 2;
setCurrentTime({
i,
x
});
} else {
_handleStop();
clearInterval(interval);
setCurrentTime({
i: X_POSITION,
x: Y_SIZE
});
}
} else {
_handleStop();
clearInterval(interval);
setCurrentTime({
i: X_POSITION,
x: Y_SIZE
});
}
}
}, lapse * 1000 / ((canvas.current.height - (PADDING_LEFT + PADDING_RIGHT + PADDING_RIGHT)) / 2));
};
const changeType = type => {
setType(type);
onStatus(type);
};
const updateCanvas = React.useCallback(() => {
const ctx = canvas.current.getContext('2d');
setCtx(ctx);
drawSticks(ctx, canvas.current.height, spectroPassiveColor);
}, [drawSticks, spectroPassiveColor]);
const _handleRecord = async () => {
if (!blocked) try {
setBlobURL('');
setTime(0);
drawSticks(ctx, canvas.current.height, spectroPassiveColor);
loopFrames(TIME_RECORD);
await Mp3Recorder.start();
changeType('stop');
start();
} catch (error) {
console.error(error);
} else console.error('Permission Denied');
};
const _handlePause = () => {
audio.current.pause();
pause();
clearInterval(interval);
changeType('play');
};
const _handleStop = async () => {
try {
const [buffer, blob] = await Mp3Recorder.stop().getMp3();
const blobURL = URL.createObjectURL(blob);
onBlobUrl({
blob,
blobURL,
buffer
});
changeType('play');
drawSticks(ctx, canvas.current.height, spectroPassiveColor);
stop();
reset();
clearInterval(interval);
setCurrentTime({
i: X_POSITION,
x: Y_SIZE
});
setBlobURL(blobURL);
} catch (error) {
console.error(error);
}
};
const _handlePlay = () => {
const {
i,
x
} = currentTime;
if (i === X_POSITION || x === 0) drawSticks(ctx, canvas.current.height, spectroPassiveColor);
audio.current.play();
changeType('pause');
loopFrames(time, i, x);
setCurrentTime({
i: X_POSITION,
x: Y_SIZE
});
start();
};
const _handleDelete = () => {
changeType('record');
setBlobURL('');
setTime(0);
clearInterval(interval);
setCurrentTime({
i: X_POSITION,
x: Y_SIZE
});
stop();
reset();
drawSticks(ctx, canvas.current.height, spectroPassiveColor);
};
React.useEffect(() => {
updateCanvas();
}, [updateCanvas]);
React.useEffect(() => {
navigator.getUserMedia({
audio: true
}, () => {
setBlocked(false);
}, () => {
setBlocked(true);
});
}, []);
React.useEffect(() => {
audio.current.onloadedmetadata = () => {
setTime(audio.current.duration);
};
}, [blobURL]);
return /*#__PURE__*/React.createElement("div", {
className: classes.container
}, /*#__PURE__*/React.createElement("div", {
className: classes.barContainer
}, /*#__PURE__*/React.createElement("canvas", {
className: clsx(classes.canvas, classNameCanvas),
ref: canvas
}), /*#__PURE__*/React.createElement("audio", {
className: classes.audio,
controls: "controls",
ref: audio,
src: blobURL
}), /*#__PURE__*/React.createElement("div", {
className: classes.button
}, type === 'record' ? /*#__PURE__*/React.createElement(IconButton, {
color: "inherit",
onClick: _handleRecord,
variant: "contained"
}, /*#__PURE__*/React.createElement(RecordIcon, null)) : type === 'play' ? /*#__PURE__*/React.createElement(IconButton, {
color: "inherit",
onClick: _handlePlay,
variant: "contained"
}, /*#__PURE__*/React.createElement(PlayIcon, null)) : type === 'pause' ? /*#__PURE__*/React.createElement(IconButton, {
color: "inherit",
onClick: _handlePause,
variant: "contained"
}, /*#__PURE__*/React.createElement(PauseIcon, null)) : type === 'stop' ? /*#__PURE__*/React.createElement(IconButton, {
color: "inherit",
onClick: _handleStop,
variant: "contained"
}, /*#__PURE__*/React.createElement(StopIcon, null)) : /*#__PURE__*/React.createElement(IconButton, {
color: "inherit",
onClick: _handleRecord,
variant: "contained"
}, /*#__PURE__*/React.createElement(RecordIcon, null))), /*#__PURE__*/React.createElement("div", {
className: classes.counter
}, _ref, _ref2, _ref3, _ref4, /*#__PURE__*/React.createElement("span", null, getLimitDate(time ? time : TIME_RECORD)))), blobURL && /*#__PURE__*/React.createElement(IconButton, {
color: "error",
onClick: _handleDelete,
variant: "contained"
}, /*#__PURE__*/React.createElement(DeleteIcon, null)));
};
process.env.NODE_ENV !== "production" ? AudioRecorder.propTypes = {
classNameCanvas: PropTypes.string,
classes: PropTypes.object,
'delete': PropTypes.func.isRequired,
onBlobUrl: PropTypes.func,
onStatus: PropTypes.func,
pause: PropTypes.func.isRequired,
play: PropTypes.func.isRequired,
record: PropTypes.func.isRequired,
spectroActiveColor: PropTypes.string,
spectroPassiveColor: PropTypes.string,
stop: PropTypes.func.isRequired
} : void 0;
AudioRecorder.muiName = 'AudioRecorder';
export default withStyles(styles, {
name: 'AudioRecorder'
})(withTimer({
formatValue: value => `${value < 10 ? `0${value}` : value}`,
startImmediately: false
})(AudioRecorder));