diff --git a/.eslintrc b/.eslintrc index c93e21c..5058461 100644 --- a/.eslintrc +++ b/.eslintrc @@ -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, diff --git a/package.json b/package.json index af79789..5dce48a 100644 --- a/package.json +++ b/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", diff --git a/src/client/action/__tests__/notify-test.js b/src/client/action/__tests__/notify-test.js deleted file mode 100644 index 850b3cd..0000000 --- a/src/client/action/__tests__/notify-test.js +++ /dev/null @@ -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' - } - }]]) - }) - }) -}) diff --git a/src/client/action/notify.js b/src/client/action/notify.js deleted file mode 100644 index 62e3c80..0000000 --- a/src/client/action/notify.js +++ /dev/null @@ -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 } diff --git a/src/client/actions/CallActions.js b/src/client/actions/CallActions.js new file mode 100644 index 0000000..2ee9c4f --- /dev/null +++ b/src/client/actions/CallActions.js @@ -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 + } +}) diff --git a/src/client/actions/NotifyActions.js b/src/client/actions/NotifyActions.js index b480ef3..7f6b895 100644 --- a/src/client/actions/NotifyActions.js +++ b/src/client/actions/NotifyActions.js @@ -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 } diff --git a/src/client/browser/createObjectURL.js b/src/client/browser/createObjectURL.js deleted file mode 100644 index e79b657..0000000 --- a/src/client/browser/createObjectURL.js +++ /dev/null @@ -1,2 +0,0 @@ -'use strict' -module.exports = object => window.URL.createObjectURL(object) diff --git a/src/client/browser/navigator.js b/src/client/browser/navigator.js deleted file mode 100644 index e7206fe..0000000 --- a/src/client/browser/navigator.js +++ /dev/null @@ -1,2 +0,0 @@ -'use strict' -module.exports = window.navigator diff --git a/src/client/call.js b/src/client/call.js deleted file mode 100644 index c8810e2..0000000 --- a/src/client/call.js +++ /dev/null @@ -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 } diff --git a/src/client/callId.js b/src/client/callId.js new file mode 100644 index 0000000..ffbb994 --- /dev/null +++ b/src/client/callId.js @@ -0,0 +1 @@ +export default window.document.getElementById('callId').value diff --git a/src/client/components/Alert.js b/src/client/components/Alert.js new file mode 100644 index 0000000..2550005 --- /dev/null +++ b/src/client/components/Alert.js @@ -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 ( +
+ {alert.message} + {alert.dismissable && ( + + )} +
+ ) + } +} + +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 ( +
+ {alerts.map((alert, i) => ( + + ))} +
+ ) + } +} diff --git a/src/client/components/Input.js b/src/client/components/Input.js new file mode 100644 index 0000000..31de1d7 --- /dev/null +++ b/src/client/components/Input.js @@ -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 ( +
+ +
+ ) + } +} diff --git a/src/client/components/Notifications.js b/src/client/components/Notifications.js new file mode 100644 index 0000000..e81b5be --- /dev/null +++ b/src/client/components/Notifications.js @@ -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 ( +
+ + {notifications.slice(max).map(notification => ( +
+ {notification.message} +
+ ))} +
+
+ ) + } +} diff --git a/src/client/components/alert.js b/src/client/components/alert.js deleted file mode 100644 index 77bc66b..0000000 --- a/src/client/components/alert.js +++ /dev/null @@ -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
 
- let button - - function dismiss () { - dispatcher.dispatch({ - type: 'alert-dismiss', - alert - }) - } - - if (alert.dismissable) { - button = - } - - return ( -
- {alert.message} - {button} -
- ) -} - -module.exports = alert diff --git a/src/client/components/input.js b/src/client/components/input.js deleted file mode 100644 index 781a776..0000000 --- a/src/client/components/input.js +++ /dev/null @@ -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 ( -
- -
- ) - } -}) - -module.exports = Input diff --git a/src/client/components/notifications.js b/src/client/components/notifications.js deleted file mode 100644 index 84112d9..0000000 --- a/src/client/components/notifications.js +++ /dev/null @@ -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 ( -
- {notif.message} -
- ) - }) - - return ( -
- - {notificationElements} - -
- ) -} - -module.exports = notifications diff --git a/src/client/constants.js b/src/client/constants.js new file mode 100644 index 0000000..22d9f4a --- /dev/null +++ b/src/client/constants.js @@ -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' diff --git a/src/client/dispatcher/dispatcher.js b/src/client/dispatcher/dispatcher.js deleted file mode 100644 index eddbb60..0000000 --- a/src/client/dispatcher/dispatcher.js +++ /dev/null @@ -1,5 +0,0 @@ -const Dispatcher = require('flux').Dispatcher - -const dispatcher = new Dispatcher() - -module.exports = dispatcher diff --git a/src/client/iceServers.js b/src/client/iceServers.js index 972198c..d780061 100644 --- a/src/client/iceServers.js +++ b/src/client/iceServers.js @@ -1,3 +1,3 @@ -module.exports = JSON.parse( +export default JSON.parse( window.document.getElementById('iceServers').value ) diff --git a/src/client/reducers/alerts.js b/src/client/reducers/alerts.js new file mode 100644 index 0000000..7c7ebb2 --- /dev/null +++ b/src/client/reducers/alerts.js @@ -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 + } +} diff --git a/src/client/reducers/index.js b/src/client/reducers/index.js new file mode 100644 index 0000000..c0cc0d5 --- /dev/null +++ b/src/client/reducers/index.js @@ -0,0 +1,9 @@ +import alerts from './alerts.js' +import notifications from './notifications.js' +import streams from './streams.js' + +export default { + alerts, + notifications, + streams +} diff --git a/src/client/reducers/notifications.js b/src/client/reducers/notifications.js new file mode 100644 index 0000000..c74c6a3 --- /dev/null +++ b/src/client/reducers/notifications.js @@ -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 + } +} diff --git a/src/client/reducers/stream.js b/src/client/reducers/stream.js new file mode 100644 index 0000000..fadf08d --- /dev/null +++ b/src/client/reducers/stream.js @@ -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 + } +} diff --git a/src/client/socket.js b/src/client/socket.js index 0dbb2b9..4821823 100644 --- a/src/client/socket.js +++ b/src/client/socket.js @@ -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() diff --git a/src/client/store.js b/src/client/store.js new file mode 100644 index 0000000..6b64bbb --- /dev/null +++ b/src/client/store.js @@ -0,0 +1,8 @@ +import { applyMiddleware, createStore } from 'redux' +import thunk from 'redux-thunk' +import reducer from './reducers' + +export default createStore( + reducer, + applyMiddleware(thunk) +) diff --git a/src/client/store/__tests__/activeStore-test.js b/src/client/store/__tests__/activeStore-test.js deleted file mode 100644 index 47c6b3e..0000000 --- a/src/client/store/__tests__/activeStore-test.js +++ /dev/null @@ -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) - }) - }) -}) diff --git a/src/client/store/__tests__/alertStore-test.js b/src/client/store/__tests__/alertStore-test.js deleted file mode 100644 index e3fc1e0..0000000 --- a/src/client/store/__tests__/alertStore-test.js +++ /dev/null @@ -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() - }) - }) -}) diff --git a/src/client/store/__tests__/notificationsStore-test.js b/src/client/store/__tests__/notificationsStore-test.js deleted file mode 100644 index c84354f..0000000 --- a/src/client/store/__tests__/notificationsStore-test.js +++ /dev/null @@ -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 ]) - }) - }) -}) diff --git a/src/client/store/__tests__/streamStore-test.js b/src/client/store/__tests__/streamStore-test.js deleted file mode 100644 index df0739a..0000000 --- a/src/client/store/__tests__/streamStore-test.js +++ /dev/null @@ -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) - }) - }) -}) diff --git a/src/client/store/activeStore.js b/src/client/store/activeStore.js deleted file mode 100644 index 201eb71..0000000 --- a/src/client/store/activeStore.js +++ /dev/null @@ -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 -} diff --git a/src/client/store/alertStore.js b/src/client/store/alertStore.js deleted file mode 100644 index a85a317..0000000 --- a/src/client/store/alertStore.js +++ /dev/null @@ -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 -} diff --git a/src/client/store/callIdStore.js b/src/client/store/callIdStore.js deleted file mode 100644 index 921ed1b..0000000 --- a/src/client/store/callIdStore.js +++ /dev/null @@ -1,5 +0,0 @@ -function getCallId () { - return window.document.getElementById('callId').value -} - -module.exports = { getCallId } diff --git a/src/client/store/notificationsStore.js b/src/client/store/notificationsStore.js deleted file mode 100644 index 4a08bb5..0000000 --- a/src/client/store/notificationsStore.js +++ /dev/null @@ -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 -} diff --git a/src/client/store/streamStore.js b/src/client/store/streamStore.js deleted file mode 100644 index c2b0c5e..0000000 --- a/src/client/store/streamStore.js +++ /dev/null @@ -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 -} diff --git a/src/client/window/createObjectURL.js b/src/client/window/createObjectURL.js new file mode 100644 index 0000000..026adf9 --- /dev/null +++ b/src/client/window/createObjectURL.js @@ -0,0 +1 @@ +export default object => window.URL.createObjectURL(object) diff --git a/src/client/browser/getUserMedia.js b/src/client/window/getUserMedia.js similarity index 69% rename from src/client/browser/getUserMedia.js rename to src/client/window/getUserMedia.js index 9e93c5e..2bd4f23 100644 --- a/src/client/browser/getUserMedia.js +++ b/src/client/window/getUserMedia.js @@ -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 diff --git a/src/client/window/navigator.js b/src/client/window/navigator.js new file mode 100644 index 0000000..46f7f47 --- /dev/null +++ b/src/client/window/navigator.js @@ -0,0 +1 @@ +export default window.navigator diff --git a/src/client/browser/video.js b/src/client/window/video.js similarity index 87% rename from src/client/browser/video.js rename to src/client/window/video.js index b5a8c76..2ff499c 100644 --- a/src/client/browser/video.js +++ b/src/client/window/video.js @@ -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 }