Bug 1097742 - Part 2 Standalone Rooms shouldn't join the room until after user media has been accepted. r=abr

This commit is contained in:
Mark Banner 2014-11-25 23:46:42 +00:00
Родитель 140f453285
Коммит 1d978cc95f
8 изменённых файлов: 173 добавлений и 19 удалений

Просмотреть файл

@ -162,6 +162,12 @@ loop.shared.actions = (function() {
getRemoteElementFunc: Function
}),
/**
* Used for notifying that local media has been obtained.
*/
GotMediaPermission: Action.define("gotMediaPermission", {
}),
/**
* Used for notifying that the media is now up for the call.
*/

Просмотреть файл

@ -28,6 +28,8 @@ loop.store.ActiveRoomStore = (function() {
GATHER: "room-gather",
// The store has got the room data
READY: "room-ready",
// Obtaining media from the user
MEDIA_WAIT: "room-media-wait",
// The room is known to be joined on the loop-server
JOINED: "room-joined",
// The room is connected to the sdk server.
@ -127,6 +129,7 @@ loop.store.ActiveRoomStore = (function() {
"roomFailure",
"setupRoomInfo",
"updateRoomInfo",
"gotMediaPermission",
"joinRoom",
"joinedRoom",
"connectedToSdkServers",
@ -260,6 +263,14 @@ loop.store.ActiveRoomStore = (function() {
this.setStoreState({failureReason: undefined});
}
this.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
},
/**
* Handles the action that signifies when media permission has been
* granted and starts joining the room.
*/
gotMediaPermission: function() {
this._mozLoop.rooms.join(this._storeState.roomToken,
function(error, responseData) {
if (error) {

Просмотреть файл

@ -207,6 +207,9 @@ loop.OTSdkDriver = (function() {
_onPublishComplete: function(event) {
event.preventDefault();
this._publisherReady = true;
this.dispatcher.dispatch(new sharedActions.GotMediaPermission());
this._maybePublishLocalStream();
},

Просмотреть файл

@ -68,6 +68,16 @@ loop.standaloneRoomViews = (function(mozL10n) {
)
);
}
case ROOM_STATES.MEDIA_WAIT: {
var msg = mozL10n.get("call_progress_getting_media_description",
{clientShortname: mozL10n.get("clientShortname2")});
// XXX Bug 1047040 will add images to help prompt the user.
return (
React.DOM.p({className: "prompt-media-message"},
msg
)
);
}
case ROOM_STATES.JOINED:
case ROOM_STATES.SESSION_CONNECTED: {
return (
@ -211,7 +221,31 @@ loop.standaloneRoomViews = (function(mozL10n) {
};
},
/**
* Used to update the video container whenever the orientation or size of the
* display area changes.
*/
updateVideoContainer: function() {
var localStreamParent = this._getElement('.local .OT_publisher');
var remoteStreamParent = this._getElement('.remote .OT_subscriber');
if (localStreamParent) {
localStreamParent.style.width = "100%";
}
if (remoteStreamParent) {
remoteStreamParent.style.height = "100%";
}
},
componentDidMount: function() {
/**
* OT inserts inline styles into the markup. Using a listener for
* resize events helps us trigger a full width/height on the element
* so that they update to the correct dimensions.
* XXX: this should be factored as a mixin, bug 1104930
*/
window.addEventListener('orientationchange', this.updateVideoContainer);
window.addEventListener('resize', this.updateVideoContainer);
// Adding a class to the document body element from here to ease styling it.
document.body.classList.add("is-standalone-room");
},
@ -228,14 +262,22 @@ loop.standaloneRoomViews = (function(mozL10n) {
* @param {Object} nextState Next state object.
*/
componentWillUpdate: function(nextProps, nextState) {
if (this.state.roomState !== ROOM_STATES.JOINED &&
nextState.roomState === ROOM_STATES.JOINED) {
if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({
publisherConfig: this._getPublisherConfig(),
getLocalElementFunc: this._getElement.bind(this, ".local"),
getRemoteElementFunc: this._getElement.bind(this, ".remote")
}));
}
if (this.state.roomState !== ROOM_STATES.JOINED &&
nextState.roomState === ROOM_STATES.JOINED) {
// This forces the video size to update - creating the publisher
// first, and then connecting to the session doesn't seem to set the
// initial size correctly.
this.updateVideoContainer();
}
},
joinRoom: function() {

Просмотреть файл

@ -68,6 +68,16 @@ loop.standaloneRoomViews = (function(mozL10n) {
</button>
);
}
case ROOM_STATES.MEDIA_WAIT: {
var msg = mozL10n.get("call_progress_getting_media_description",
{clientShortname: mozL10n.get("clientShortname2")});
// XXX Bug 1047040 will add images to help prompt the user.
return (
<p className="prompt-media-message">
{msg}
</p>
);
}
case ROOM_STATES.JOINED:
case ROOM_STATES.SESSION_CONNECTED: {
return (
@ -211,7 +221,31 @@ loop.standaloneRoomViews = (function(mozL10n) {
};
},
/**
* Used to update the video container whenever the orientation or size of the
* display area changes.
*/
updateVideoContainer: function() {
var localStreamParent = this._getElement('.local .OT_publisher');
var remoteStreamParent = this._getElement('.remote .OT_subscriber');
if (localStreamParent) {
localStreamParent.style.width = "100%";
}
if (remoteStreamParent) {
remoteStreamParent.style.height = "100%";
}
},
componentDidMount: function() {
/**
* OT inserts inline styles into the markup. Using a listener for
* resize events helps us trigger a full width/height on the element
* so that they update to the correct dimensions.
* XXX: this should be factored as a mixin, bug 1104930
*/
window.addEventListener('orientationchange', this.updateVideoContainer);
window.addEventListener('resize', this.updateVideoContainer);
// Adding a class to the document body element from here to ease styling it.
document.body.classList.add("is-standalone-room");
},
@ -228,14 +262,22 @@ loop.standaloneRoomViews = (function(mozL10n) {
* @param {Object} nextState Next state object.
*/
componentWillUpdate: function(nextProps, nextState) {
if (this.state.roomState !== ROOM_STATES.JOINED &&
nextState.roomState === ROOM_STATES.JOINED) {
if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({
publisherConfig: this._getPublisherConfig(),
getLocalElementFunc: this._getElement.bind(this, ".local"),
getRemoteElementFunc: this._getElement.bind(this, ".remote")
}));
}
if (this.state.roomState !== ROOM_STATES.JOINED &&
nextState.roomState === ROOM_STATES.JOINED) {
// This forces the video size to update - creating the publisher
// first, and then connecting to the session doesn't seem to set the
// initial size correctly.
this.updateVideoContainer();
}
},
joinRoom: function() {

Просмотреть файл

@ -284,10 +284,6 @@ describe("loop.store.ActiveRoomStore", function () {
});
describe("#joinRoom", function() {
beforeEach(function() {
store.setStoreState({roomToken: "tokenFake"});
});
it("should reset failureReason", function() {
store.setStoreState({failureReason: "Test"});
@ -296,9 +292,23 @@ describe("loop.store.ActiveRoomStore", function () {
expect(store.getStoreState().failureReason).eql(undefined);
});
it("should call rooms.join on mozLoop", function() {
it("should set the state to MEDIA_WAIT", function() {
store.setStoreState({roomState: ROOM_STATES.READY});
store.joinRoom();
expect(store.getStoreState().roomState).eql(ROOM_STATES.MEDIA_WAIT);
});
});
describe("#gotMediaPermission", function() {
beforeEach(function() {
store.setStoreState({roomToken: "tokenFake"});
});
it("should call rooms.join on mozLoop", function() {
store.gotMediaPermission();
sinon.assert.calledOnce(fakeMozLoop.rooms.join);
sinon.assert.calledWith(fakeMozLoop.rooms.join, "tokenFake");
});
@ -313,7 +323,7 @@ describe("loop.store.ActiveRoomStore", function () {
fakeMozLoop.rooms.join.callsArgWith(1, null, responseData);
store.joinRoom();
store.gotMediaPermission();
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWith(dispatcher.dispatch,
@ -325,7 +335,7 @@ describe("loop.store.ActiveRoomStore", function () {
fakeMozLoop.rooms.join.callsArgWith(1, fakeError);
store.joinRoom();
store.gotMediaPermission();
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWith(dispatcher.dispatch,

Просмотреть файл

@ -295,6 +295,14 @@ describe("loop.OTSdkDriver", function () {
sinon.assert.calledOnce(session.publish);
});
it("should dispatch a GotMediaPermission action", function() {
publisher.trigger("accessAllowed", fakeEvent);
sinon.assert.calledOnce(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.GotMediaPermission());
});
});
describe("accessDenied", function() {

Просмотреть файл

@ -53,27 +53,49 @@ describe("loop.standaloneRoomViews", function() {
}
describe("#componentWillUpdate", function() {
it("should dispatch a `SetupStreamElements` action on room joined",
function() {
it("should dispatch a `SetupStreamElements` action when the MEDIA_WAIT state " +
"is entered", function() {
activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
var view = mountTestComponent();
sinon.assert.notCalled(dispatch);
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
expectActionDispatched(view);
});
it("should dispatch a `SetupStreamElements` action on room rejoined",
function() {
it("should dispatch a `SetupStreamElements` action on MEDIA_WAIT state is " +
"re-entered", function() {
activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED});
var view = mountTestComponent();
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
expectActionDispatched(view);
});
it("should updateVideoContainer when the JOINED state is entered", function() {
activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
var view = mountTestComponent();
sandbox.stub(view, "updateVideoContainer");
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
sinon.assert.calledOnce(view.updateVideoContainer);
});
it("should updateVideoContainer when the JOINED state is re-entered", function() {
activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED});
var view = mountTestComponent();
sandbox.stub(view, "updateVideoContainer");
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
sinon.assert.calledOnce(view.updateVideoContainer);
});
});
describe("#publishStream", function() {
@ -143,6 +165,16 @@ describe("loop.standaloneRoomViews", function() {
});
});
describe("Prompt media message", function() {
it("should display a prompt for user media on MEDIA_WAIT",
function() {
activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
expect(view.getDOMNode().querySelector(".prompt-media-message"))
.not.eql(null);
});
});
describe("Full room message", function() {
it("should display a full room message on FULL",
function() {