Bug 1172662 - ICE failures occuring in Loop conversations should be reported to the user. r=Standard8

This commit is contained in:
Manuel Casas 2015-10-05 14:40:15 +01:00
parent 366a15220e
commit d63afeb685
14 changed files with 116 additions and 4 deletions

View File

@ -404,6 +404,8 @@ loop.conversationViews = (function(mozL10n) {
case FAILURE_DETAILS.TOS_FAILURE:
return mozL10n.get("tos_failure_message",
{ clientShortname: mozL10n.get("clientShortname2") });
case FAILURE_DETAILS.ICE_FAILED:
return mozL10n.get("ice_failure_message");
default:
return mozL10n.get("generic_failure_message");
}

View File

@ -404,6 +404,8 @@ loop.conversationViews = (function(mozL10n) {
case FAILURE_DETAILS.TOS_FAILURE:
return mozL10n.get("tos_failure_message",
{ clientShortname: mozL10n.get("clientShortname2") });
case FAILURE_DETAILS.ICE_FAILED:
return mozL10n.get("ice_failure_message");
default:
return mozL10n.get("generic_failure_message");
}

View File

@ -8,6 +8,7 @@ loop.roomViews = (function(mozL10n) {
var ROOM_STATES = loop.store.ROOM_STATES;
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
var sharedActions = loop.shared.actions;
var sharedMixins = loop.shared.mixins;
var sharedUtils = loop.shared.utils;
@ -99,6 +100,14 @@ loop.roomViews = (function(mozL10n) {
{ id: "feedback" },
{ id: "help" }
];
var btnTitle;
if (this.props.failureReason === FAILURE_DETAILS.ICE_FAILED) {
btnTitle = mozL10n.get("retry_call_button");
} else {
btnTitle = mozL10n.get("rejoin_button");
}
return (
React.createElement("div", {className: "room-failure"},
React.createElement(loop.conversationViews.FailureInfoView, {
@ -106,7 +115,7 @@ loop.roomViews = (function(mozL10n) {
React.createElement("div", {className: "btn-group call-action-group"},
React.createElement("button", {className: "btn btn-info btn-rejoin",
onClick: this.handleRejoinCall},
mozL10n.get("rejoin_button")
btnTitle
)
),
React.createElement(loop.shared.views.SettingsControlButton, {

View File

@ -8,6 +8,7 @@ loop.roomViews = (function(mozL10n) {
var ROOM_STATES = loop.store.ROOM_STATES;
var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
var sharedActions = loop.shared.actions;
var sharedMixins = loop.shared.mixins;
var sharedUtils = loop.shared.utils;
@ -99,6 +100,14 @@ loop.roomViews = (function(mozL10n) {
{ id: "feedback" },
{ id: "help" }
];
var btnTitle;
if (this.props.failureReason === FAILURE_DETAILS.ICE_FAILED) {
btnTitle = mozL10n.get("retry_call_button");
} else {
btnTitle = mozL10n.get("rejoin_button");
}
return (
<div className="room-failure">
<loop.conversationViews.FailureInfoView
@ -106,7 +115,7 @@ loop.roomViews = (function(mozL10n) {
<div className="btn-group call-action-group">
<button className="btn btn-info btn-rejoin"
onClick={this.handleRejoinCall}>
{mozL10n.get("rejoin_button")}
{btnTitle}
</button>
</div>
<loop.shared.views.SettingsControlButton

View File

@ -928,6 +928,13 @@ loop.OTSdkDriver = (function() {
_onOTException: function(event) {
switch (event.code) {
case OT.ExceptionCodes.PUBLISHER_ICE_WORKFLOW_FAILED:
case OT.ExceptionCodes.SUBSCRIBER_ICE_WORKFLOW_FAILED:
this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
reason: FAILURE_DETAILS.ICE_FAILED
}));
this._notifyMetricsEvent("sdk.exception." + event.code);
break;
case OT.ExceptionCodes.UNABLE_TO_PUBLISH:
if (event.message === "GetUserMedia") {
// We free up the publisher here in case the store wants to try

View File

@ -84,7 +84,8 @@ var inChrome = typeof Components != "undefined" && "utils" in Components;
// TOS_FAILURE reflects the sdk error code 1026:
// https://tokbox.com/developer/sdks/js/reference/ExceptionEvent.html
TOS_FAILURE: "reason-tos-failure",
UNKNOWN: "reason-unknown"
UNKNOWN: "reason-unknown",
ICE_FAILED: "reason-ice-failed"
};
var ROOM_INFO_FAILURES = {

View File

@ -158,6 +158,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
case FAILURE_DETAILS.TOS_FAILURE:
return mozL10n.get("tos_failure_message",
{ clientShortname: mozL10n.get("clientShortname2") });
case FAILURE_DETAILS.ICE_FAILED:
return mozL10n.get("rooms_ice_failure_message");
default:
return mozL10n.get("status_error");
}

View File

@ -158,6 +158,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
case FAILURE_DETAILS.TOS_FAILURE:
return mozL10n.get("tos_failure_message",
{ clientShortname: mozL10n.get("clientShortname2") });
case FAILURE_DETAILS.ICE_FAILED:
return mozL10n.get("rooms_ice_failure_message");
default:
return mozL10n.get("status_error");
}

View File

@ -72,6 +72,7 @@ rooms_unavailable_notification_message=Sorry, you cannot join this conversation.
rooms_media_denied_message=We could not get access to your microphone or camera. Please reload the page to try again.
room_information_failure_not_available=No information about this conversation is available. Please request a new link from the person who sent it to you.
room_information_failure_unsupported_browser=Your browser cannot access any information about this conversation. Please make sure you're using the latest version.
rooms_ice_failure_message=Connection failed. Your firewall may be blocking calls.
## LOCALIZATION_NOTE(rooms_read_while_wait_offer): This string is followed by a
# tile/offer image and title that are provided by a separate service that has

View File

@ -351,6 +351,16 @@ describe("loop.conversationViews", function () {
expect(extraFailureMessage.textContent).eql("Fake failure message");
});
it("should display an ICE failure message", function() {
view = mountTestComponent({
failureReason: FAILURE_DETAILS.ICE_FAILED
});
var message = view.getDOMNode().querySelector(".failure-info-message");
expect(message.textContent).eql("ice_failure_message");
});
});
describe("DirectCallFailureView", function() {

View File

@ -142,7 +142,7 @@ describe("loop.roomViews", function () {
function mountTestComponent(props) {
props = _.extend({
dispatcher: dispatcher,
failureReason: FAILURE_DETAILS.UNKNOWN,
failureReason: props && props.failureReason || FAILURE_DETAILS.UNKNOWN,
mozLoop: fakeMozLoop
});
return TestUtils.renderIntoDocument(
@ -177,6 +177,16 @@ describe("loop.roomViews", function () {
new sharedActions.JoinRoom());
});
it("should render retry button when an ice failure is dispatched", function() {
view = mountTestComponent({
failureReason: FAILURE_DETAILS.ICE_FAILED
});
var retryBtn = view.getDOMNode().querySelector(".btn-rejoin");
expect(retryBtn.textContent).eql("retry_call_button");
});
it("should play a failure sound, once", function() {
view = mountTestComponent();

View File

@ -1661,6 +1661,51 @@ describe("loop.OTSdkDriver", function () {
}));
});
});
describe("ICE failed", function() {
it("should dispatch a ConnectionFailure action (Publisher)", function() {
sdk.trigger("exception", {
code: OT.ExceptionCodes.PUBLISHER_ICE_WORKFLOW_FAILED,
message: "ICE failed"
});
sinon.assert.calledTwice(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.ConnectionFailure({
reason: FAILURE_DETAILS.ICE_FAILED
}));
});
it("should dispatch a ConnectionFailure action (Subscriber)", function() {
sdk.trigger("exception", {
code: OT.ExceptionCodes.SUBSCRIBER_ICE_WORKFLOW_FAILED,
message: "ICE failed"
});
sinon.assert.calledTwice(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.ConnectionFailure({
reason: FAILURE_DETAILS.ICE_FAILED
}));
});
it("should notify metrics", function() {
sdk.trigger("exception", {
code: OT.ExceptionCodes.PUBLISHER_ICE_WORKFLOW_FAILED,
message: "ICE failed"
});
sinon.assert.calledTwice(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.ConnectionStatus({
event: "sdk.exception." + OT.ExceptionCodes.PUBLISHER_ICE_WORKFLOW_FAILED,
state: "starting",
connections: 0,
sendStreams: 0,
recvStreams: 0
}));
});
});
});
});

View File

@ -652,6 +652,17 @@ describe("loop.standaloneRoomViews", function() {
TestUtils.findRenderedComponentWithType(view,
loop.standaloneRoomViews.StandaloneRoomFailureView);
});
it("should display ICE failure message", function() {
activeRoomStore.setStoreState({
roomState: ROOM_STATES.FAILED,
failureReason: FAILURE_DETAILS.ICE_FAILED
});
var ice_failed_message = view.getDOMNode().querySelector(".failed-room-message").textContent;
expect(ice_failed_message).eql("rooms_ice_failure_message");
expect(view.getDOMNode().querySelector(".btn-info")).not.eql(null);
});
});
describe("Join button", function() {

View File

@ -298,6 +298,7 @@ cannot_start_call_session_not_ready=Can't start call, session is not ready.
network_disconnected=The network connection terminated abruptly.
connection_error_see_console_notification=Call failed; see console for details.
no_media_failure_message=No camera or microphone found.
ice_failure_message=Connection failed. Your firewall may be blocking calls.
## LOCALIZATION NOTE (legal_text_and_links3): In this item, don't translate the
## parts between {{..}} because these will be replaced with links with the labels