Middle of refactor
This commit is contained in:
parent
b50eb79463
commit
80c8be2f56
@ -1,7 +1,9 @@
|
||||
{
|
||||
"parser": "babel-eslint",
|
||||
"extends": ["standard", "standard-react"],
|
||||
"rules": {
|
||||
"max-len": [2, 80, 4]
|
||||
"max-len": [2, 80, 4],
|
||||
"jsx-quotes": ["error", "prefer-double"]
|
||||
},
|
||||
"globals": {
|
||||
"expect": true,
|
||||
|
||||
12
package.json
12
package.json
@ -11,14 +11,21 @@
|
||||
"lint": "eslint ./index.js ./src/js"
|
||||
},
|
||||
"babel": {
|
||||
"presets": ["es2016", "react", "transform-object-rest-spread"]
|
||||
"presets": [
|
||||
"es2016",
|
||||
"react",
|
||||
"transform-object-rest-spread",
|
||||
"transform-class-properties"
|
||||
]
|
||||
},
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bluebird": "^3.3.4",
|
||||
"classnames": "^2.2.5",
|
||||
"config": "^1.26.1",
|
||||
"express": "^4.13.3",
|
||||
"prop-types": "^15.5.10",
|
||||
"pug": "^2.0.0-rc.2",
|
||||
"react": "^15.5.4",
|
||||
"react-addons-transition-group": "^15.5.2",
|
||||
@ -26,6 +33,7 @@
|
||||
"react-redux": "^5.0.5",
|
||||
"redux": "^3.6.0",
|
||||
"redux-thunk": "^2.2.0",
|
||||
"seamless-immutable": "^7.1.2",
|
||||
"simple-peer": "^8.1.0",
|
||||
"socket.io": "1.6.0",
|
||||
"socket.io-client": "1.6.0",
|
||||
@ -33,7 +41,9 @@
|
||||
"uuid": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^7.2.3",
|
||||
"babel-jest": "^20.0.3",
|
||||
"babel-plugin-transform-class-properties": "^6.24.1",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.23.0",
|
||||
"babel-preset-es2016": "^6.24.1",
|
||||
"babel-preset-react": "^6.24.1",
|
||||
|
||||
@ -1,66 +0,0 @@
|
||||
jest.unmock('../notify.js')
|
||||
|
||||
const dispatcher = require('../../dispatcher/dispatcher.js')
|
||||
const notify = require('../notify.js')
|
||||
|
||||
describe('notify', () => {
|
||||
beforeEach(() => dispatcher.dispatch.mockClear())
|
||||
|
||||
describe('info', () => {
|
||||
it('should dispatch info notification', () => {
|
||||
notify.info('test: {0} {1}', 'arg1', 'arg2')
|
||||
|
||||
expect(dispatcher.dispatch.mock.calls).toEqual([[{
|
||||
type: 'notify',
|
||||
notification: {
|
||||
message: 'test: arg1 arg2',
|
||||
type: 'info'
|
||||
}
|
||||
}]])
|
||||
})
|
||||
})
|
||||
|
||||
describe('warn', () => {
|
||||
it('should dispatch warning notification', () => {
|
||||
notify.warn('test: {0} {1}', 'arg1', 'arg2')
|
||||
|
||||
expect(dispatcher.dispatch.mock.calls).toEqual([[{
|
||||
type: 'notify',
|
||||
notification: {
|
||||
message: 'test: arg1 arg2',
|
||||
type: 'warning'
|
||||
}
|
||||
}]])
|
||||
})
|
||||
})
|
||||
|
||||
describe('error', () => {
|
||||
it('should dispatch error notification', () => {
|
||||
notify.error('test: {0} {1}', 'arg1', 'arg2')
|
||||
|
||||
expect(dispatcher.dispatch.mock.calls).toEqual([[{
|
||||
type: 'notify',
|
||||
notification: {
|
||||
message: 'test: arg1 arg2',
|
||||
type: 'error'
|
||||
}
|
||||
}]])
|
||||
})
|
||||
})
|
||||
|
||||
describe('alert', () => {
|
||||
it('should dispatch an alert', () => {
|
||||
notify.alert('alert!', true)
|
||||
|
||||
expect(dispatcher.dispatch.mock.calls).toEqual([[{
|
||||
type: 'alert',
|
||||
alert: {
|
||||
action: 'Dismiss',
|
||||
dismissable: true,
|
||||
message: 'alert!',
|
||||
type: 'warning'
|
||||
}
|
||||
}]])
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,42 +0,0 @@
|
||||
const dispatcher = require('../dispatcher/dispatcher.js')
|
||||
|
||||
function format (string, args) {
|
||||
string = args
|
||||
.reduce((string, arg, i) => string.replace('{' + i + '}', arg), string)
|
||||
return string
|
||||
}
|
||||
|
||||
function _notify (type, args) {
|
||||
let string = args[0] || ''
|
||||
let message = format(string, Array.prototype.slice.call(args, 1))
|
||||
dispatcher.dispatch({
|
||||
type: 'notify',
|
||||
notification: { type, message }
|
||||
})
|
||||
}
|
||||
|
||||
function info () {
|
||||
_notify('info', arguments)
|
||||
}
|
||||
|
||||
function warn () {
|
||||
_notify('warning', arguments)
|
||||
}
|
||||
|
||||
function error () {
|
||||
_notify('error', arguments)
|
||||
}
|
||||
|
||||
function alert (message, dismissable) {
|
||||
dispatcher.dispatch({
|
||||
type: 'alert',
|
||||
alert: {
|
||||
action: dismissable ? 'Dismiss' : '',
|
||||
dismissable: !!dismissable,
|
||||
message,
|
||||
type: 'warning'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = { alert, info, warn, error }
|
||||
49
src/client/actions/CallActions.js
Normal file
49
src/client/actions/CallActions.js
Normal file
@ -0,0 +1,49 @@
|
||||
import * as NotifyActions from './NotifyActions.js'
|
||||
import * as constants from '../constants.js'
|
||||
import Promise from 'bluebird'
|
||||
import callId from '../callId.js'
|
||||
import getUserMedia from './browser/getUserMedia.js'
|
||||
import handshake from './peer/handshake.js'
|
||||
import socket from './socket.js'
|
||||
|
||||
export const init = () => dispatch => {
|
||||
return Promise.all([
|
||||
connect()(dispatch),
|
||||
getCameraStream()(dispatch)
|
||||
])
|
||||
.spread((socket, stream) => {
|
||||
handshake.init({ socket, callId, stream })
|
||||
})
|
||||
}
|
||||
|
||||
export const connect = () => dispatch => {
|
||||
return new Promise(resolve => {
|
||||
socket.once('connect', () => {
|
||||
dispatch(NotifyActions.warn('Connected to server socket'))
|
||||
resolve(socket)
|
||||
})
|
||||
socket.on('disconnect', () => {
|
||||
dispatch(NotifyActions.error('Server socket disconnected'))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export const getCameraStream = () => dispatch => {
|
||||
return getUserMedia({ video: true, audio: true })
|
||||
.then(stream => {
|
||||
dispatch(addStream(stream))
|
||||
return stream
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch(NotifyActions.alert('Could not get access to microphone & camera'))
|
||||
return null
|
||||
})
|
||||
}
|
||||
|
||||
const addStream = stream => ({
|
||||
type: constants.STREAM_ADD,
|
||||
payload: {
|
||||
userId: '_me_',
|
||||
stream
|
||||
}
|
||||
})
|
||||
@ -7,34 +7,32 @@ function format (string, args) {
|
||||
function _notify (type, args) {
|
||||
let string = args[0] || ''
|
||||
let message = format(string, Array.prototype.slice.call(args, 1))
|
||||
dispatcher.dispatch({
|
||||
return {
|
||||
type: 'notify',
|
||||
notification: { type, message }
|
||||
})
|
||||
payload: { type, message }
|
||||
}
|
||||
}
|
||||
|
||||
function info () {
|
||||
_notify('info', arguments)
|
||||
export function info () {
|
||||
return _notify('info', arguments)
|
||||
}
|
||||
|
||||
function warn () {
|
||||
_notify('warning', arguments)
|
||||
export function warn () {
|
||||
return _notify('warning', arguments)
|
||||
}
|
||||
|
||||
function error () {
|
||||
_notify('error', arguments)
|
||||
export function error () {
|
||||
return _notify('error', arguments)
|
||||
}
|
||||
|
||||
function alert (message, dismissable) {
|
||||
dispatcher.dispatch({
|
||||
export function alert (message, dismissable) {
|
||||
return {
|
||||
type: 'alert',
|
||||
alert: {
|
||||
payload: {
|
||||
action: dismissable ? 'Dismiss' : '',
|
||||
dismissable: !!dismissable,
|
||||
message,
|
||||
type: 'warning'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { alert, info, warn, error }
|
||||
|
||||
@ -1,2 +0,0 @@
|
||||
'use strict'
|
||||
module.exports = object => window.URL.createObjectURL(object)
|
||||
@ -1,2 +0,0 @@
|
||||
'use strict'
|
||||
module.exports = window.navigator
|
||||
@ -1,53 +0,0 @@
|
||||
const Promise = require('bluebird')
|
||||
const debug = require('debug')('peer-calls:call')
|
||||
const dispatcher = require('./dispatcher/dispatcher.js')
|
||||
const getUserMedia = require('./browser/getUserMedia.js')
|
||||
const play = require('./browser/video.js').play
|
||||
const notify = require('./action/notify.js')
|
||||
const handshake = require('./peer/handshake.js')
|
||||
const socket = require('./socket.js')
|
||||
|
||||
dispatcher.register(action => {
|
||||
if (action.type === 'play') play()
|
||||
})
|
||||
|
||||
function init () {
|
||||
const callId = window.document.getElementById('callId').value
|
||||
|
||||
Promise.all([connect(), getCameraStream()])
|
||||
.spread((_socket, stream) => {
|
||||
debug('initializing peer connection')
|
||||
handshake.init(_socket, callId, stream)
|
||||
})
|
||||
}
|
||||
|
||||
function connect () {
|
||||
return new Promise(resolve => {
|
||||
socket.once('connect', () => {
|
||||
notify.warn('Connected to server socket')
|
||||
debug('socket connected')
|
||||
resolve(socket)
|
||||
})
|
||||
socket.on('disconnect', () => {
|
||||
notify.error('Server socket disconnected')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function getCameraStream () {
|
||||
return getUserMedia({ video: true, audio: true })
|
||||
.then(stream => {
|
||||
debug('got our media stream:', stream)
|
||||
dispatcher.dispatch({
|
||||
type: 'add-stream',
|
||||
userId: '_me_',
|
||||
stream
|
||||
})
|
||||
return stream
|
||||
})
|
||||
.catch(() => {
|
||||
notify.alert('Could not get access to microphone & camera')
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = { init }
|
||||
1
src/client/callId.js
Normal file
1
src/client/callId.js
Normal file
@ -0,0 +1 @@
|
||||
export default window.document.getElementById('callId').value
|
||||
50
src/client/components/Alert.js
Normal file
50
src/client/components/Alert.js
Normal file
@ -0,0 +1,50 @@
|
||||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
const React = require('react')
|
||||
|
||||
const AlertPropType = PropTypes.shape({
|
||||
dismissable: PropTypes.bool,
|
||||
action: PropTypes.string.isRequired,
|
||||
message: PropTypes.string.isRequired
|
||||
})
|
||||
|
||||
export class Alert extends React.PureComponent {
|
||||
static propTypes = {
|
||||
alert: AlertPropType,
|
||||
dismiss: PropTypes.func.isRequired
|
||||
}
|
||||
dismiss = () => {
|
||||
const { alert, dismiss } = this.props
|
||||
dismiss(alert)
|
||||
}
|
||||
render () {
|
||||
const { alert, dismiss } = this.props
|
||||
|
||||
return (
|
||||
<div className={classnames('alert', alert.type)}>
|
||||
<span>{alert.message}</span>
|
||||
{alert.dismissable && (
|
||||
<button onClick={dismiss}>{alert.action}</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default class Alerts extends React.PureComponent {
|
||||
static propTypes = {
|
||||
alerts: PropTypes.arrayOf(AlertPropType).isRequired,
|
||||
dismiss: PropTypes.func.isRequired
|
||||
}
|
||||
render () {
|
||||
const { alerts, dismiss } = this.props
|
||||
return (
|
||||
<div className="alerts">
|
||||
{alerts.map((alert, i) => (
|
||||
<Alert alert={alert} dismiss={dismiss} key={i} />
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
45
src/client/components/Input.js
Normal file
45
src/client/components/Input.js
Normal file
@ -0,0 +1,45 @@
|
||||
import React from 'react'
|
||||
import notify from '../action/notify.js'
|
||||
import peers from '../peer/peers.js'
|
||||
|
||||
export default class Input extends React.PureComponent {
|
||||
constructor () {
|
||||
super()
|
||||
this.state = {
|
||||
message: ''
|
||||
}
|
||||
}
|
||||
handleChange = e => {
|
||||
this.setState({
|
||||
message: e.target.value
|
||||
})
|
||||
}
|
||||
handleSubmit = e => {
|
||||
e.preventDefault()
|
||||
this.submit()
|
||||
}
|
||||
handleKeyPress = e => {
|
||||
e.preventDefault()
|
||||
e.key === 'Enter' && this.submit()
|
||||
}
|
||||
submit = () => {
|
||||
const { message } = this.state
|
||||
peers.message(message)
|
||||
notify.info('You: ' + message)
|
||||
this.setState({ message: '' })
|
||||
}
|
||||
render () {
|
||||
const { message } = this.state
|
||||
return (
|
||||
<form className="input" onSubmit={this.handleSubmit}>
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
onKeyPress={this.onKeyPress}
|
||||
placeholder="Enter your message..."
|
||||
type="text"
|
||||
value={message}
|
||||
/>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
}
|
||||
39
src/client/components/Notifications.js
Normal file
39
src/client/components/Notifications.js
Normal file
@ -0,0 +1,39 @@
|
||||
import CSSTransitionGroup from 'react-transition-group'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
export default class Notifications extends React.PureComponent {
|
||||
static propTypes = {
|
||||
notifications: PropTypes.arrayOf(PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
type: PropTypes.string.isRequired,
|
||||
message: PropTypes.string.isRequired
|
||||
})),
|
||||
max: PropTypes.number.isRequired
|
||||
}
|
||||
static defaultProps = {
|
||||
max: 10
|
||||
}
|
||||
render () {
|
||||
const { notifications, max } = this.props
|
||||
return (
|
||||
<div className="notifications">
|
||||
<CSSTransitionGroup
|
||||
transitionEnterTimeout={200}
|
||||
transitionLeaveTimeout={100}
|
||||
transitionName="fade"
|
||||
>
|
||||
{notifications.slice(max).map(notification => (
|
||||
<div
|
||||
className={classnames(notification.type, 'notification')}
|
||||
key={notification.id}
|
||||
>
|
||||
{notification.message}
|
||||
</div>
|
||||
))}
|
||||
</CSSTransitionGroup>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
const React = require('react')
|
||||
const alertStore = require('../store/alertStore.js')
|
||||
const dispatcher = require('../dispatcher/dispatcher.js')
|
||||
|
||||
function alert () {
|
||||
let alert = alertStore.getAlert()
|
||||
if (!alert) return <div className='alert hidden'><span> </span></div>
|
||||
let button
|
||||
|
||||
function dismiss () {
|
||||
dispatcher.dispatch({
|
||||
type: 'alert-dismiss',
|
||||
alert
|
||||
})
|
||||
}
|
||||
|
||||
if (alert.dismissable) {
|
||||
button = <button onClick={dismiss}>{alert.action}</button>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={alert.type + ' alert'}>
|
||||
<span>{alert.message}</span>
|
||||
{button}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = alert
|
||||
@ -1,39 +0,0 @@
|
||||
const React = require('react')
|
||||
const peers = require('../peer/peers.js')
|
||||
const notify = require('../action/notify.js')
|
||||
|
||||
const Input = React.createClass({
|
||||
getInitialState () {
|
||||
return {
|
||||
visible: false,
|
||||
message: ''
|
||||
}
|
||||
},
|
||||
handleChange (e) {
|
||||
this.setState({
|
||||
message: e.target.value
|
||||
})
|
||||
},
|
||||
handleSubmit (e) {
|
||||
e.preventDefault()
|
||||
const { message } = this.state
|
||||
peers.message(message)
|
||||
notify.info('You: ' + message)
|
||||
this.setState({ message: '' })
|
||||
},
|
||||
render () {
|
||||
const { message } = this.state
|
||||
return (
|
||||
<form className='input' onSubmit={this.handleSubmit}>
|
||||
<input
|
||||
onChange={this.handleChange}
|
||||
placeholder='Enter your message...'
|
||||
type='text'
|
||||
value={message}
|
||||
/>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = Input
|
||||
@ -1,29 +0,0 @@
|
||||
const React = require('react')
|
||||
const Transition = require('react-addons-css-transition-group')
|
||||
const notificationsStore = require('../store/notificationsStore.js')
|
||||
|
||||
function notifications (props) {
|
||||
let notifs = notificationsStore.getNotifications(props.max || 10)
|
||||
|
||||
let notificationElements = notifs.map(notif => {
|
||||
return (
|
||||
<div className={notif.type + ' notification'} key={notif._id}>
|
||||
{notif.message}
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
return (
|
||||
<div className='notifications'>
|
||||
<Transition
|
||||
transitionEnterTimeout={200}
|
||||
transitionLeaveTimeout={100}
|
||||
transitionName='fade'
|
||||
>
|
||||
{notificationElements}
|
||||
</Transition>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
module.exports = notifications
|
||||
11
src/client/constants.js
Normal file
11
src/client/constants.js
Normal file
@ -0,0 +1,11 @@
|
||||
export const ALERT = 'ALERT'
|
||||
export const ALERT_DISMISS = 'ALERT_DISMISS'
|
||||
export const ALERT_CLEAR = 'ALERT_CLEAR'
|
||||
|
||||
export const NOTIFY = 'NOTIFY'
|
||||
export const NOTIFY_DISMISS = 'NOTIFY_DISMISS'
|
||||
export const NOTIFY_CLEAR = 'NOTIFY_CLEAR'
|
||||
|
||||
export const STREAM_ADD = 'STREAM_ADD'
|
||||
export const STREAM_ACTIVATE = 'STREAM_ACTIVATE'
|
||||
export const STREAM_REMOVE = 'STREAM_REMOVE'
|
||||
@ -1,5 +0,0 @@
|
||||
const Dispatcher = require('flux').Dispatcher
|
||||
|
||||
const dispatcher = new Dispatcher()
|
||||
|
||||
module.exports = dispatcher
|
||||
@ -1,3 +1,3 @@
|
||||
module.exports = JSON.parse(
|
||||
export default JSON.parse(
|
||||
window.document.getElementById('iceServers').value
|
||||
)
|
||||
|
||||
17
src/client/reducers/alerts.js
Normal file
17
src/client/reducers/alerts.js
Normal file
@ -0,0 +1,17 @@
|
||||
import * as constants from '../constants.js'
|
||||
import Immutable from 'seamless-immutable'
|
||||
|
||||
const defaultState = Immutable([])
|
||||
|
||||
export default function alert (state = defaultState, action) {
|
||||
switch (action && action.type) {
|
||||
case constants.ALERT:
|
||||
return Immutable(state.asMutable().push(action.payload))
|
||||
case constants.ALERT_DISMISS:
|
||||
return state.filter(a => a !== action.payload)
|
||||
case constants.ALERT_CLEAR:
|
||||
return defaultState
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
9
src/client/reducers/index.js
Normal file
9
src/client/reducers/index.js
Normal file
@ -0,0 +1,9 @@
|
||||
import alerts from './alerts.js'
|
||||
import notifications from './notifications.js'
|
||||
import streams from './streams.js'
|
||||
|
||||
export default {
|
||||
alerts,
|
||||
notifications,
|
||||
streams
|
||||
}
|
||||
25
src/client/reducers/notifications.js
Normal file
25
src/client/reducers/notifications.js
Normal file
@ -0,0 +1,25 @@
|
||||
import * as constants from '../constants.js'
|
||||
import Immutable from 'seamless-immutable'
|
||||
|
||||
const defaultState = Immutable({
|
||||
notifications: []
|
||||
})
|
||||
|
||||
export default function notify (state = defaultState, action) {
|
||||
switch (action && action.type) {
|
||||
case constants.NOTIFY:
|
||||
const notifications = state.notifications.asMutable()
|
||||
notifications.push(action.payload)
|
||||
return state.merge({
|
||||
notifications
|
||||
})
|
||||
case constants.NOTIFY_DISMISS:
|
||||
return state.merge({
|
||||
notifications: state.notifications.filter(n => n !== action.payload)
|
||||
})
|
||||
case constants.NOTIFY_CLEAR:
|
||||
return state.merge({ notifications: [] })
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
36
src/client/reducers/stream.js
Normal file
36
src/client/reducers/stream.js
Normal file
@ -0,0 +1,36 @@
|
||||
import * as constants from '../constants.js'
|
||||
import createObjectURL from '../browser/createObjectURL'
|
||||
import Immutable from 'seamless-imutable'
|
||||
|
||||
const defaultState = Immutable({
|
||||
active: null,
|
||||
streams: {}
|
||||
})
|
||||
|
||||
function addStream (state, action) {
|
||||
const { userId, stream } = action.payload
|
||||
const streams = state.streams.merge({
|
||||
[userId]: {
|
||||
userId,
|
||||
stream,
|
||||
url: createObjectURL(stream)
|
||||
}
|
||||
})
|
||||
return { active: userId, streams }
|
||||
}
|
||||
|
||||
function removeStream (state, action) {
|
||||
const streams = state.streams.without(action.payload.userId)
|
||||
return state.merge({ streams })
|
||||
}
|
||||
|
||||
export default function stream (state = defaultState, action) {
|
||||
switch (action && action.type) {
|
||||
case constants.STREAM_ADD:
|
||||
return addStream(state, action)
|
||||
case constants.STREAM_REMOVE:
|
||||
return removeStream(state, action)
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,2 @@
|
||||
let SocketIOClient = require('socket.io-client')
|
||||
|
||||
let socket = new SocketIOClient()
|
||||
|
||||
module.exports = socket
|
||||
import SocketIOClient from 'socket.io-client'
|
||||
export default new SocketIOClient()
|
||||
|
||||
8
src/client/store.js
Normal file
8
src/client/store.js
Normal file
@ -0,0 +1,8 @@
|
||||
import { applyMiddleware, createStore } from 'redux'
|
||||
import thunk from 'redux-thunk'
|
||||
import reducer from './reducers'
|
||||
|
||||
export default createStore(
|
||||
reducer,
|
||||
applyMiddleware(thunk)
|
||||
)
|
||||
@ -1,30 +0,0 @@
|
||||
jest.unmock('../activeStore.js')
|
||||
|
||||
const dispatcher = require('../../dispatcher/dispatcher.js')
|
||||
const activeStore = require('../activeStore.js')
|
||||
|
||||
describe('activeStore', () => {
|
||||
let handleAction = dispatcher.register.mock.calls[0][0]
|
||||
let onChange = jest.genMockFunction()
|
||||
|
||||
beforeEach(() => {
|
||||
onChange.mockClear()
|
||||
activeStore.addListener(onChange)
|
||||
})
|
||||
afterEach(() => activeStore.removeListener(onChange))
|
||||
|
||||
describe('mark-active', () => {
|
||||
it('should mark id as active', () => {
|
||||
expect(activeStore.getActive()).not.toBeDefined()
|
||||
expect(activeStore.isActive('user1')).toBe(false)
|
||||
expect(onChange.mock.calls.length).toBe(0)
|
||||
|
||||
handleAction({ type: 'mark-active', userId: 'user1' })
|
||||
|
||||
expect(activeStore.getActive()).toBe('user1')
|
||||
expect(activeStore.isActive('user1')).toBe(true)
|
||||
|
||||
expect(onChange.mock.calls.length).toBe(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,62 +0,0 @@
|
||||
jest.unmock('../alertStore.js')
|
||||
|
||||
const dispatcher = require('../../dispatcher/dispatcher.js')
|
||||
const alertStore = require('../alertStore.js')
|
||||
|
||||
describe('alertStore', () => {
|
||||
let handleAction, onChange
|
||||
beforeEach(() => {
|
||||
handleAction = dispatcher.register.mock.calls[0][0]
|
||||
handleAction({ type: 'alert-clear' })
|
||||
|
||||
onChange = jest.genMockFunction()
|
||||
alertStore.addListener(onChange)
|
||||
})
|
||||
afterEach(() => {
|
||||
alertStore.removeListener(onChange)
|
||||
})
|
||||
|
||||
describe('alert', () => {
|
||||
it('should add alerts to end of queue and dispatch change', () => {
|
||||
let alert1 = { message: 'example alert 1' }
|
||||
let alert2 = { message: 'example alert 2' }
|
||||
|
||||
handleAction({ type: 'alert', alert: alert1 })
|
||||
handleAction({ type: 'alert', alert: alert2 })
|
||||
|
||||
expect(onChange.mock.calls.length).toBe(2)
|
||||
expect(alertStore.getAlerts()).toEqual([ alert1, alert2 ])
|
||||
expect(alertStore.getAlert()).toBe(alert1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('alert-dismiss', () => {
|
||||
it('should remove alert and dispatch change', () => {
|
||||
let alert1 = { message: 'example alert 1' }
|
||||
let alert2 = { message: 'example alert 2' }
|
||||
|
||||
handleAction({ type: 'alert', alert: alert1 })
|
||||
handleAction({ type: 'alert', alert: alert2 })
|
||||
handleAction({ type: 'alert-dismiss', alert: alert1 })
|
||||
|
||||
expect(onChange.mock.calls.length).toBe(3)
|
||||
expect(alertStore.getAlerts()).toEqual([ alert2 ])
|
||||
expect(alertStore.getAlert()).toBe(alert2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('alert-clear', () => {
|
||||
it('should remove all alerts', () => {
|
||||
let alert1 = { message: 'example alert 1' }
|
||||
let alert2 = { message: 'example alert 2' }
|
||||
|
||||
handleAction({ type: 'alert', alert: alert1 })
|
||||
handleAction({ type: 'alert', alert: alert2 })
|
||||
handleAction({ type: 'alert-clear' })
|
||||
|
||||
expect(onChange.mock.calls.length).toBe(3)
|
||||
expect(alertStore.getAlerts()).toEqual([])
|
||||
expect(alertStore.getAlert()).not.toBeDefined()
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,61 +0,0 @@
|
||||
jest.unmock('../notificationsStore.js')
|
||||
|
||||
const dispatcher = require('../../dispatcher/dispatcher.js')
|
||||
const store = require('../notificationsStore.js')
|
||||
|
||||
describe('store', () => {
|
||||
let handleAction, onChange
|
||||
beforeEach(() => {
|
||||
dispatcher.dispatch.mockClear()
|
||||
handleAction = dispatcher.register.mock.calls[0][0]
|
||||
|
||||
handleAction({ type: 'notify-clear' })
|
||||
|
||||
onChange = jest.genMockFunction()
|
||||
store.addListener(onChange)
|
||||
})
|
||||
|
||||
describe('notify', () => {
|
||||
it('should add notification and dispatch change', () => {
|
||||
let notif1 = { message: 'example notif 1' }
|
||||
let notif2 = { message: 'example notif 2' }
|
||||
|
||||
handleAction({ type: 'notify', notification: notif1 })
|
||||
handleAction({ type: 'notify', notification: notif2 })
|
||||
|
||||
expect(onChange.mock.calls.length).toBe(2)
|
||||
expect(store.getNotifications()).toEqual([ notif1, notif2 ])
|
||||
expect(store.getNotifications(1)).toEqual([ notif2 ])
|
||||
expect(store.getNotifications(3)).toEqual([ notif1, notif2 ])
|
||||
})
|
||||
|
||||
it('should add timeout for autoremoval', () => {
|
||||
let notif1 = { message: 'example notif 1' }
|
||||
|
||||
handleAction({ type: 'notify', notification: notif1 })
|
||||
|
||||
expect(onChange.mock.calls.length).toBe(1)
|
||||
expect(store.getNotifications()).toEqual([ notif1 ])
|
||||
|
||||
jest.runAllTimers()
|
||||
|
||||
expect(onChange.mock.calls.length).toBe(2)
|
||||
expect(store.getNotifications()).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('notify-dismiss', () => {
|
||||
it('should remove notif and dispatch change', () => {
|
||||
let notif1 = { message: 'example notif 1' }
|
||||
let notif2 = { message: 'example notif 2' }
|
||||
|
||||
handleAction({ type: 'notify', notification: notif1 })
|
||||
handleAction({ type: 'notify', notification: notif2 })
|
||||
handleAction({ type: 'notify-dismiss', notification: notif1 })
|
||||
|
||||
expect(onChange.mock.calls.length).toBe(3)
|
||||
expect(store.getNotifications()).toEqual([ notif2 ])
|
||||
expect(store.getNotifications(2)).toEqual([ notif2 ])
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,77 +0,0 @@
|
||||
jest.unmock('../streamStore.js')
|
||||
|
||||
const createObjectUrl = require('../../browser/createObjectURL.js')
|
||||
const dispatcher = require('../../dispatcher/dispatcher.js')
|
||||
const streamStore = require('../streamStore.js')
|
||||
|
||||
describe('streamStore', () => {
|
||||
let handleAction = dispatcher.register.mock.calls[0][0]
|
||||
let onChange = jest.genMockFunction()
|
||||
|
||||
beforeEach(() => {
|
||||
onChange.mockClear()
|
||||
streamStore.addListener(onChange)
|
||||
})
|
||||
afterEach(() => streamStore.removeListener(onChange))
|
||||
|
||||
describe('add-stream and remove-stream', () => {
|
||||
it('should add a stream', () => {
|
||||
let stream = {}
|
||||
|
||||
createObjectUrl.mockImplementation(str => {
|
||||
if (str === stream) return 'url1'
|
||||
})
|
||||
|
||||
handleAction({ type: 'add-stream', userId: 'user1', stream })
|
||||
|
||||
expect(streamStore.getStream('user1')).toEqual({
|
||||
stream,
|
||||
url: 'url1'
|
||||
})
|
||||
expect(onChange.mock.calls.length).toEqual(1)
|
||||
})
|
||||
|
||||
it('should add a stream multiple times', () => {
|
||||
let stream1 = {}
|
||||
let stream2 = {}
|
||||
|
||||
createObjectUrl.mockImplementation(stream => {
|
||||
if (stream === stream1) return 'url1'
|
||||
if (stream === stream2) return 'url2'
|
||||
})
|
||||
|
||||
handleAction({ type: 'add-stream', userId: 'user1', stream: stream1 })
|
||||
handleAction({ type: 'add-stream', userId: 'user2', stream: stream2 })
|
||||
|
||||
expect(streamStore.getStream('user1')).toEqual({
|
||||
stream: stream1,
|
||||
url: 'url1'
|
||||
})
|
||||
expect(streamStore.getStream('user2')).toEqual({
|
||||
stream: stream2,
|
||||
url: 'url2'
|
||||
})
|
||||
expect(streamStore.getStreams()).toEqual({
|
||||
user1: {
|
||||
stream: stream1,
|
||||
url: 'url1'
|
||||
},
|
||||
user2: {
|
||||
stream: stream2,
|
||||
url: 'url2'
|
||||
}
|
||||
})
|
||||
expect(onChange.mock.calls.length).toEqual(2)
|
||||
})
|
||||
|
||||
it('should remove a stream', () => {
|
||||
let stream = {}
|
||||
|
||||
handleAction({ type: 'add-stream', userId: 'user1', stream })
|
||||
handleAction({ type: 'remove-stream', userId: 'user1' })
|
||||
|
||||
expect(streamStore.getStream('user1')).not.toBeDefined()
|
||||
expect(onChange.mock.calls.length).toEqual(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,43 +0,0 @@
|
||||
'use strict'
|
||||
const EventEmitter = require('events')
|
||||
const debug = require('debug')('peer-calls:activeStore')
|
||||
const dispatcher = require('../dispatcher/dispatcher.js')
|
||||
|
||||
const emitter = new EventEmitter()
|
||||
const addListener = cb => emitter.on('change', cb)
|
||||
const removeListener = cb => emitter.removeListener('change', cb)
|
||||
|
||||
let active
|
||||
|
||||
const handlers = {
|
||||
'add-stream': ({ userId }) => {
|
||||
active = userId
|
||||
},
|
||||
'mark-active': ({ userId }) => {
|
||||
debug('mark-active, userId: %s', userId)
|
||||
active = userId
|
||||
}
|
||||
}
|
||||
|
||||
const dispatcherIndex = dispatcher.register(action => {
|
||||
let handle = handlers[action.type]
|
||||
if (!handle) return
|
||||
handle(action)
|
||||
emitter.emit('change')
|
||||
})
|
||||
|
||||
function getActive () {
|
||||
return active
|
||||
}
|
||||
|
||||
function isActive (test) {
|
||||
return active === test
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
dispatcherIndex,
|
||||
addListener,
|
||||
removeListener,
|
||||
getActive,
|
||||
isActive
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
const EventEmitter = require('events')
|
||||
const debug = require('debug')('peer-calls:alertStore')
|
||||
const dispatcher = require('../dispatcher/dispatcher.js')
|
||||
|
||||
const emitter = new EventEmitter()
|
||||
const addListener = cb => emitter.on('change', cb)
|
||||
const removeListener = cb => emitter.removeListener('change', cb)
|
||||
|
||||
let alerts = []
|
||||
|
||||
const handlers = {
|
||||
alert: ({ alert }) => {
|
||||
debug('alert: %s', alert.message)
|
||||
alerts.push(alert)
|
||||
},
|
||||
'alert-dismiss': ({ alert }) => {
|
||||
debug('alert-dismiss: %s', alert.message)
|
||||
let index = alerts.indexOf(alert)
|
||||
debug('index: %s', index)
|
||||
if (index < 0) return
|
||||
alerts.splice(index, 1)
|
||||
},
|
||||
'alert-clear': () => {
|
||||
debug('alert-clear')
|
||||
alerts = []
|
||||
}
|
||||
}
|
||||
|
||||
const dispatcherIndex = dispatcher.register(action => {
|
||||
let handle = handlers[action.type]
|
||||
if (!handle) return
|
||||
handle(action)
|
||||
emitter.emit('change')
|
||||
})
|
||||
|
||||
function getAlert () {
|
||||
return alerts[0]
|
||||
}
|
||||
|
||||
function getAlerts () {
|
||||
return alerts
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
dispatcherIndex,
|
||||
addListener,
|
||||
removeListener,
|
||||
getAlert,
|
||||
getAlerts
|
||||
}
|
||||
@ -1,5 +0,0 @@
|
||||
function getCallId () {
|
||||
return window.document.getElementById('callId').value
|
||||
}
|
||||
|
||||
module.exports = { getCallId }
|
||||
@ -1,65 +0,0 @@
|
||||
const EventEmitter = require('events')
|
||||
const debug = require('debug')('peer-calls:alertStore')
|
||||
const dispatcher = require('../dispatcher/dispatcher.js')
|
||||
|
||||
const emitter = new EventEmitter()
|
||||
const addListener = cb => emitter.on('change', cb)
|
||||
const removeListener = cb => emitter.removeListener('change', cb)
|
||||
|
||||
let index = 0
|
||||
let notifications = []
|
||||
|
||||
function dismiss (notification) {
|
||||
let index = notifications.indexOf(notification)
|
||||
if (index < 0) return
|
||||
notifications.splice(index, 1)
|
||||
clearTimeout(notification._timeout)
|
||||
delete notification._timeout
|
||||
}
|
||||
|
||||
function emitChange () {
|
||||
emitter.emit('change')
|
||||
}
|
||||
|
||||
const handlers = {
|
||||
notify: ({ notification }) => {
|
||||
index++
|
||||
debug('notify', notification.message)
|
||||
notification._id = index
|
||||
notifications.push(notification)
|
||||
notification._timeout = setTimeout(() => {
|
||||
debug('notify-dismiss timeout: %s', notification.message)
|
||||
dismiss(notification)
|
||||
emitChange()
|
||||
}, 10000)
|
||||
},
|
||||
'notify-dismiss': ({ notification }) => {
|
||||
debug('notify-dismiss: %s', notification.message)
|
||||
dismiss(notification)
|
||||
},
|
||||
'notify-clear': () => {
|
||||
debug('notify-clear')
|
||||
notifications = []
|
||||
}
|
||||
}
|
||||
|
||||
const dispatcherIndex = dispatcher.register(action => {
|
||||
let handle = handlers[action.type]
|
||||
if (!handle) return
|
||||
handle(action)
|
||||
emitChange()
|
||||
})
|
||||
|
||||
function getNotifications (max) {
|
||||
if (!max) max = notifications.length
|
||||
let start = notifications.length - max
|
||||
if (start < 0) start = 0
|
||||
return notifications.slice(start, notifications.length)
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
dispatcherIndex,
|
||||
addListener,
|
||||
removeListener,
|
||||
getNotifications
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
'use strict'
|
||||
const EventEmitter = require('events')
|
||||
const createObjectURL = require('../browser/createObjectURL')
|
||||
const debug = require('debug')('peer-calls:streamStore')
|
||||
const dispatcher = require('../dispatcher/dispatcher.js')
|
||||
|
||||
const emitter = new EventEmitter()
|
||||
const addListener = cb => emitter.on('change', cb)
|
||||
const removeListener = cb => emitter.removeListener('change', cb)
|
||||
|
||||
const streams = {}
|
||||
|
||||
const handlers = {
|
||||
'add-stream': ({ userId, stream }) => {
|
||||
debug('add-stream, user: %s', userId)
|
||||
streams[userId] = {
|
||||
stream,
|
||||
url: createObjectURL(stream)
|
||||
}
|
||||
},
|
||||
'remove-stream': ({ userId }) => {
|
||||
debug('remove-stream, user: %s', userId)
|
||||
delete streams[userId]
|
||||
}
|
||||
}
|
||||
|
||||
const dispatcherIndex = dispatcher.register(action => {
|
||||
let handle = handlers[action.type]
|
||||
if (!handle) return
|
||||
handle(action)
|
||||
emitter.emit('change')
|
||||
})
|
||||
|
||||
function getStream (userId) {
|
||||
return streams[userId]
|
||||
}
|
||||
|
||||
function getStreams () {
|
||||
return streams
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
dispatcherIndex,
|
||||
addListener,
|
||||
removeListener,
|
||||
getStream,
|
||||
getStreams
|
||||
}
|
||||
1
src/client/window/createObjectURL.js
Normal file
1
src/client/window/createObjectURL.js
Normal file
@ -0,0 +1 @@
|
||||
export default object => window.URL.createObjectURL(object)
|
||||
@ -1,8 +1,7 @@
|
||||
'use strict'
|
||||
const navigator = require('../browser/navigator.js')
|
||||
const Promise = require('bluebird')
|
||||
import Promise from 'bluebird'
|
||||
import navigator from './navigator.js'
|
||||
|
||||
function getUserMedia (constraints) {
|
||||
export default function getUserMedia (constraints) {
|
||||
if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
|
||||
return navigator.mediaDevices.getUserMedia(constraints)
|
||||
}
|
||||
@ -13,5 +12,3 @@ function getUserMedia (constraints) {
|
||||
getMedia.call(navigator, constraints, resolve, reject)
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = getUserMedia
|
||||
1
src/client/window/navigator.js
Normal file
1
src/client/window/navigator.js
Normal file
@ -0,0 +1 @@
|
||||
export default window.navigator
|
||||
@ -1,6 +1,6 @@
|
||||
const debug = require('debug')('peer-calls:video')
|
||||
|
||||
function play () {
|
||||
export function play () {
|
||||
let videos = window.document.querySelectorAll('video')
|
||||
Array.prototype.forEach.call(videos, (video, index) => {
|
||||
debug('playing video: %s', index)
|
||||
@ -11,5 +11,3 @@ function play () {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = { play }
|
||||
Loading…
x
Reference in New Issue
Block a user