This commit is contained in:
Iñaki Baz Castillo
2017-11-02 16:38:52 +01:00
parent 625f20f547
commit 52acf81eff
100 changed files with 26907 additions and 10234 deletions
+99
View File
@@ -0,0 +1,99 @@
# APP STATE
```js
{
room :
{
url : 'https://example.io/?&roomId=d0el8y34',
state : 'connected', // new/connecting/connected/closed
activeSpeakerName : 'alice'
},
me :
{
name : 'bob',
displayName : 'Bob McFLower',
displayNameSet : false, // true if got from cookie or manually set.
device : { flag: 'firefox', name: 'Firefox', version: '61' },
canSendMic : true,
canSendWebcam : true,
canChangeWebcam : false,
webcamInProgress : false,
audioOnly : false,
audioOnlyInProgress : false,
restartIceInProgress : false
},
producers :
{
1111 :
{
id : 1111,
source : 'mic', // mic/webcam,
locallyPaused : true,
remotelyPaused : false,
track : MediaStreamTrack,
codec : 'opus'
},
1112 :
{
id : 1112,
source : 'webcam', // mic/webcam
deviceLabel : 'Macbook Webcam',
type : 'front', // front/back
locallyPaused : false,
remotelyPaused : false,
track : MediaStreamTrack,
codec : 'vp8',
}
},
peers :
{
'alice' :
{
name : 'alice',
displayName : 'Alice Thomsom',
device : { flag: 'chrome', name: 'Chrome', version: '58' },
consumers : [ 5551, 5552 ]
}
},
consumers :
{
5551 :
{
id : 5551,
peerName : 'alice',
source : 'mic', // mic/webcam
supported : true,
locallyPaused : false,
remotelyPaused : false,
profile : 'default',
track : MediaStreamTrack,
codec : 'opus'
},
5552 :
{
id : 5552,
peerName : 'alice',
source : 'webcam',
supported : false,
locallyPaused : false,
remotelyPaused : true,
profile : 'medium',
track : null,
codec : 'h264'
}
},
notifications :
[
{
id : 'qweasdw43we',
type : 'info' // info/error
text : 'You joined the room'
},
{
id : 'j7sdhkjjkcc',
type : 'error'
text : 'Could not add webcam'
}
]
}
```
+75
View File
@@ -0,0 +1,75 @@
const initialState = {};
const consumers = (state = initialState, action) =>
{
switch (action.type)
{
case 'ADD_CONSUMER':
{
const { consumer } = action.payload;
return { ...state, [consumer.id]: consumer };
}
case 'REMOVE_CONSUMER':
{
const { consumerId } = action.payload;
const newState = { ...state };
delete newState[consumerId];
return newState;
}
case 'SET_CONSUMER_PAUSED':
{
const { consumerId, originator } = action.payload;
const consumer = state[consumerId];
let newConsumer;
if (originator === 'local')
newConsumer = { ...consumer, locallyPaused: true };
else
newConsumer = { ...consumer, remotelyPaused: true };
return { ...state, [consumerId]: newConsumer };
}
case 'SET_CONSUMER_RESUMED':
{
const { consumerId, originator } = action.payload;
const consumer = state[consumerId];
let newConsumer;
if (originator === 'local')
newConsumer = { ...consumer, locallyPaused: false };
else
newConsumer = { ...consumer, remotelyPaused: false };
return { ...state, [consumerId]: newConsumer };
}
case 'SET_CONSUMER_EFFECTIVE_PROFILE':
{
const { consumerId, profile } = action.payload;
const consumer = state[consumerId];
const newConsumer = { ...consumer, profile };
return { ...state, [consumerId]: newConsumer };
}
case 'SET_CONSUMER_TRACK':
{
const { consumerId, track } = action.payload;
const consumer = state[consumerId];
const newConsumer = { ...consumer, track };
return { ...state, [consumerId]: newConsumer };
}
default:
return state;
}
};
export default consumers;
+19
View File
@@ -0,0 +1,19 @@
import { combineReducers } from 'redux';
import room from './room';
import me from './me';
import producers from './producers';
import peers from './peers';
import consumers from './consumers';
import notifications from './notifications';
const reducers = combineReducers(
{
room,
me,
producers,
peers,
consumers,
notifications
});
export default reducers;
+85
View File
@@ -0,0 +1,85 @@
const initialState =
{
name : null,
displayName : null,
displayNameSet : false,
device : null,
canSendMic : false,
canSendWebcam : false,
canChangeWebcam : false,
webcamInProgress : false,
audioOnly : false,
audioOnlyInProgress : false,
restartIceInProgress : false
};
const me = (state = initialState, action) =>
{
switch (action.type)
{
case 'SET_ME':
{
const { peerName, displayName, displayNameSet, device } = action.payload;
return { ...state, name: peerName, displayName, displayNameSet, device };
}
case 'SET_MEDIA_CAPABILITIES':
{
const { canSendMic, canSendWebcam } = action.payload;
return { ...state, canSendMic, canSendWebcam };
}
case 'SET_CAN_CHANGE_WEBCAM':
{
const canChangeWebcam = action.payload;
return { ...state, canChangeWebcam };
}
case 'SET_WEBCAM_IN_PROGRESS':
{
const { flag } = action.payload;
return { ...state, webcamInProgress: flag };
}
case 'SET_DISPLAY_NAME':
{
let { displayName } = action.payload;
// Be ready for undefined displayName (so keep previous one).
if (!displayName)
displayName = state.displayName;
return { ...state, displayName, displayNameSet: true };
}
case 'SET_AUDIO_ONLY_STATE':
{
const { enabled } = action.payload;
return { ...state, audioOnly: enabled };
}
case 'SET_AUDIO_ONLY_IN_PROGRESS':
{
const { flag } = action.payload;
return { ...state, audioOnlyInProgress: flag };
}
case 'SET_RESTART_ICE_IN_PROGRESS':
{
const { flag } = action.payload;
return { ...state, restartIceInProgress: flag };
}
default:
return state;
}
};
export default me;
+31
View File
@@ -0,0 +1,31 @@
const initialState = [];
const notifications = (state = initialState, action) =>
{
switch (action.type)
{
case 'ADD_NOTIFICATION':
{
const { notification } = action.payload;
return [ ...state, notification ];
}
case 'REMOVE_NOTIFICATION':
{
const { notificationId } = action.payload;
return state.filter((notification) => notification.id !== notificationId);
}
case 'REMOVE_ALL_NOTIFICATIONS':
{
return [];
}
default:
return state;
}
};
export default notifications;
+79
View File
@@ -0,0 +1,79 @@
const initialState = {};
const peers = (state = initialState, action) =>
{
switch (action.type)
{
case 'ADD_PEER':
{
const { peer } = action.payload;
return { ...state, [peer.name]: peer };
}
case 'REMOVE_PEER':
{
const { peerName } = action.payload;
const newState = { ...state };
delete newState[peerName];
return newState;
}
case 'SET_PEER_DISPLAY_NAME':
{
const { displayName, peerName } = action.payload;
const peer = state[peerName];
if (!peer)
throw new Error('no Peer found');
const newPeer = { ...peer, displayName };
return { ...state, [newPeer.name]: newPeer };
}
case 'ADD_CONSUMER':
{
const { consumer, peerName } = action.payload;
const peer = state[peerName];
if (!peer)
throw new Error('no Peer found for new Consumer');
const newConsumers = [ ...peer.consumers, consumer.id ];
const newPeer = { ...peer, consumers: newConsumers };
return { ...state, [newPeer.name]: newPeer };
}
case 'REMOVE_CONSUMER':
{
const { consumerId, peerName } = action.payload;
const peer = state[peerName];
// NOTE: This means that the Peer was closed before, so it's ok.
if (!peer)
return state;
const idx = peer.consumers.indexOf(consumerId);
if (idx === -1)
throw new Error('Consumer not found');
const newConsumers = peer.consumers.slice();
newConsumers.splice(idx, 1);
const newPeer = { ...peer, consumers: newConsumers };
return { ...state, [newPeer.name]: newPeer };
}
default:
return state;
}
};
export default peers;
+66
View File
@@ -0,0 +1,66 @@
const initialState = {};
const producers = (state = initialState, action) =>
{
switch (action.type)
{
case 'ADD_PRODUCER':
{
const { producer } = action.payload;
return { ...state, [producer.id]: producer };
}
case 'REMOVE_PRODUCER':
{
const { producerId } = action.payload;
const newState = { ...state };
delete newState[producerId];
return newState;
}
case 'SET_PRODUCER_PAUSED':
{
const { producerId, originator } = action.payload;
const producer = state[producerId];
let newProducer;
if (originator === 'local')
newProducer = { ...producer, locallyPaused: true };
else
newProducer = { ...producer, remotelyPaused: true };
return { ...state, [producerId]: newProducer };
}
case 'SET_PRODUCER_RESUMED':
{
const { producerId, originator } = action.payload;
const producer = state[producerId];
let newProducer;
if (originator === 'local')
newProducer = { ...producer, locallyPaused: false };
else
newProducer = { ...producer, remotelyPaused: false };
return { ...state, [producerId]: newProducer };
}
case 'SET_PRODUCER_TRACK':
{
const { producerId, track } = action.payload;
const producer = state[producerId];
const newProducer = { ...producer, track };
return { ...state, [producerId]: newProducer };
}
default:
return state;
}
};
export default producers;
+41
View File
@@ -0,0 +1,41 @@
const initialState =
{
url : null,
state : 'new', // new/connecting/connected/disconnected/closed,
activeSpeakerName : null
};
const room = (state = initialState, action) =>
{
switch (action.type)
{
case 'SET_ROOM_URL':
{
const { url } = action.payload;
return { ...state, url };
}
case 'SET_ROOM_STATE':
{
const roomState = action.payload.state;
if (roomState == 'connected')
return { ...state, state: roomState };
else
return { ...state, state: roomState, activeSpeakerName: null };
}
case 'SET_ROOM_ACTIVE_SPEAKER':
{
const { peerName } = action.payload;
return { ...state, activeSpeakerName: peerName };
}
default:
return state;
}
};
export default room;
+117
View File
@@ -0,0 +1,117 @@
import randomString from 'random-string';
import * as stateActions from './stateActions';
export const joinRoom = (
{ roomId, peerName, displayName, device, useSimulcast, produce }) =>
{
return {
type : 'JOIN_ROOM',
payload : { roomId, peerName, displayName, device, useSimulcast, produce }
};
};
export const leaveRoom = () =>
{
return {
type : 'LEAVE_ROOM'
};
};
export const changeDisplayName = (displayName) =>
{
return {
type : 'CHANGE_DISPLAY_NAME',
payload : { displayName }
};
};
export const muteMic = () =>
{
return {
type : 'MUTE_MIC'
};
};
export const unmuteMic = () =>
{
return {
type : 'UNMUTE_MIC'
};
};
export const enableWebcam = () =>
{
return {
type : 'ENABLE_WEBCAM'
};
};
export const disableWebcam = () =>
{
return {
type : 'DISABLE_WEBCAM'
};
};
export const changeWebcam = () =>
{
return {
type : 'CHANGE_WEBCAM'
};
};
export const enableAudioOnly = () =>
{
return {
type : 'ENABLE_AUDIO_ONLY'
};
};
export const disableAudioOnly = () =>
{
return {
type : 'DISABLE_AUDIO_ONLY'
};
};
export const restartIce = () =>
{
return {
type : 'RESTART_ICE'
};
};
// This returns a redux-thunk action (a function).
export const notify = ({ type = 'info', text, timeout }) =>
{
if (!timeout)
{
switch (type)
{
case 'info':
timeout = 3000;
break;
case 'error':
timeout = 5000;
break;
}
}
const notification =
{
id : randomString({ length: 6 }).toLowerCase(),
type : type,
text : text,
timeout : timeout
};
return (dispatch) =>
{
dispatch(stateActions.addNotification(notification));
setTimeout(() =>
{
dispatch(stateActions.removeNotification(notification.id));
}, timeout);
};
};
+115
View File
@@ -0,0 +1,115 @@
import RoomClient from '../RoomClient';
export default ({ dispatch, getState }) => (next) =>
{
let client;
return (action) =>
{
switch (action.type)
{
case 'JOIN_ROOM':
{
const {
roomId,
peerName,
displayName,
device,
useSimulcast,
produce
} = action.payload;
client = new RoomClient(
{
roomId,
peerName,
displayName,
device,
useSimulcast,
produce,
dispatch,
getState
});
// TODO: TMP
global.CLIENT = client;
break;
}
case 'LEAVE_ROOM':
{
client.close();
break;
}
case 'CHANGE_DISPLAY_NAME':
{
const { displayName } = action.payload;
client.changeDisplayName(displayName);
break;
}
case 'MUTE_MIC':
{
client.muteMic();
break;
}
case 'UNMUTE_MIC':
{
client.unmuteMic();
break;
}
case 'ENABLE_WEBCAM':
{
client.enableWebcam();
break;
}
case 'DISABLE_WEBCAM':
{
client.disableWebcam();
break;
}
case 'CHANGE_WEBCAM':
{
client.changeWebcam();
break;
}
case 'ENABLE_AUDIO_ONLY':
{
client.enableAudioOnly();
break;
}
case 'DISABLE_AUDIO_ONLY':
{
client.disableAudioOnly();
break;
}
case 'RESTART_ICE':
{
client.restartIce();
break;
}
}
return next(action);
};
};
+222
View File
@@ -0,0 +1,222 @@
export const setRoomUrl = (url) =>
{
return {
type : 'SET_ROOM_URL',
payload : { url }
};
};
export const setRoomState = (state) =>
{
return {
type : 'SET_ROOM_STATE',
payload : { state }
};
};
export const setRoomActiveSpeaker = (peerName) =>
{
return {
type : 'SET_ROOM_ACTIVE_SPEAKER',
payload : { peerName }
};
};
export const setMe = ({ peerName, displayName, displayNameSet, device }) =>
{
return {
type : 'SET_ME',
payload : { peerName, displayName, displayNameSet, device }
};
};
export const setMediaCapabilities = ({ canSendMic, canSendWebcam }) =>
{
return {
type : 'SET_MEDIA_CAPABILITIES',
payload : { canSendMic, canSendWebcam }
};
};
export const setCanChangeWebcam = (flag) =>
{
return {
type : 'SET_CAN_CHANGE_WEBCAM',
payload : flag
};
};
export const setDisplayName = (displayName) =>
{
return {
type : 'SET_DISPLAY_NAME',
payload : { displayName }
};
};
export const setAudioOnlyState = (enabled) =>
{
return {
type : 'SET_AUDIO_ONLY_STATE',
payload : { enabled }
};
};
export const setAudioOnlyInProgress = (flag) =>
{
return {
type : 'SET_AUDIO_ONLY_IN_PROGRESS',
payload : { flag }
};
};
export const setRestartIceInProgress = (flag) =>
{
return {
type : 'SET_RESTART_ICE_IN_PROGRESS',
payload : { flag }
};
};
export const addProducer = (producer) =>
{
return {
type : 'ADD_PRODUCER',
payload : { producer }
};
};
export const removeProducer = (producerId) =>
{
return {
type : 'REMOVE_PRODUCER',
payload : { producerId }
};
};
export const setProducerPaused = (producerId, originator) =>
{
return {
type : 'SET_PRODUCER_PAUSED',
payload : { producerId, originator }
};
};
export const setProducerResumed = (producerId, originator) =>
{
return {
type : 'SET_PRODUCER_RESUMED',
payload : { producerId, originator }
};
};
export const setProducerTrack = (producerId, track) =>
{
return {
type : 'SET_PRODUCER_TRACK',
payload : { producerId, track }
};
};
export const setWebcamInProgress = (flag) =>
{
return {
type : 'SET_WEBCAM_IN_PROGRESS',
payload : { flag }
};
};
export const addPeer = (peer) =>
{
return {
type : 'ADD_PEER',
payload : { peer }
};
};
export const removePeer = (peerName) =>
{
return {
type : 'REMOVE_PEER',
payload : { peerName }
};
};
export const setPeerDisplayName = (displayName, peerName) =>
{
return {
type : 'SET_PEER_DISPLAY_NAME',
payload : { displayName, peerName }
};
};
export const addConsumer = (consumer, peerName) =>
{
return {
type : 'ADD_CONSUMER',
payload : { consumer, peerName }
};
};
export const removeConsumer = (consumerId, peerName) =>
{
return {
type : 'REMOVE_CONSUMER',
payload : { consumerId, peerName }
};
};
export const setConsumerPaused = (consumerId, originator) =>
{
return {
type : 'SET_CONSUMER_PAUSED',
payload : { consumerId, originator }
};
};
export const setConsumerResumed = (consumerId, originator) =>
{
return {
type : 'SET_CONSUMER_RESUMED',
payload : { consumerId, originator }
};
};
export const setConsumerEffectiveProfile = (consumerId, profile) =>
{
return {
type : 'SET_CONSUMER_EFFECTIVE_PROFILE',
payload : { consumerId, profile }
};
};
export const setConsumerTrack = (consumerId, track) =>
{
return {
type : 'SET_CONSUMER_TRACK',
payload : { consumerId, track }
};
};
export const addNotification = (notification) =>
{
return {
type : 'ADD_NOTIFICATION',
payload : { notification }
};
};
export const removeNotification = (notificationId) =>
{
return {
type : 'REMOVE_NOTIFICATION',
payload : { notificationId }
};
};
export const removeAllNotifications = () =>
{
return {
type : 'REMOVE_ALL_NOTIFICATIONS'
};
};