Middle of refactor

This commit is contained in:
Jerko Steiner 2017-06-08 21:35:24 -04:00
parent b50eb79463
commit 80c8be2f56
38 changed files with 326 additions and 740 deletions

View File

@ -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,

View File

@ -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",

View File

@ -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'
}
}]])
})
})
})

View File

@ -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 }

View 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
}
})

View File

@ -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 }

View File

@ -1,2 +0,0 @@
'use strict'
module.exports = object => window.URL.createObjectURL(object)

View File

@ -1,2 +0,0 @@
'use strict'
module.exports = window.navigator

View File

@ -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
View File

@ -0,0 +1 @@
export default window.document.getElementById('callId').value

View 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>
)
}
}

View 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>
)
}
}

View 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>
)
}
}

View File

@ -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>&nbsp;</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

View File

@ -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

View File

@ -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
View 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'

View File

@ -1,5 +0,0 @@
const Dispatcher = require('flux').Dispatcher
const dispatcher = new Dispatcher()
module.exports = dispatcher

View File

@ -1,3 +1,3 @@
module.exports = JSON.parse(
export default JSON.parse(
window.document.getElementById('iceServers').value
)

View 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
}
}

View 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
}

View 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
}
}

View 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
}
}

View File

@ -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
View 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)
)

View File

@ -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)
})
})
})

View File

@ -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()
})
})
})

View File

@ -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 ])
})
})
})

View File

@ -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)
})
})
})

View File

@ -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
}

View File

@ -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
}

View File

@ -1,5 +0,0 @@
function getCallId () {
return window.document.getElementById('callId').value
}
module.exports = { getCallId }

View File

@ -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
}

View File

@ -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
}

View File

@ -0,0 +1 @@
export default object => window.URL.createObjectURL(object)

View File

@ -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

View File

@ -0,0 +1 @@
export default window.navigator

View File

@ -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 }