mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1188771
- Inform users when they can't use the Hello service due to ToS compliance. r=mikedeboer
This commit is contained in:
parent
670f9899d9
commit
41c6ed66ad
@ -401,6 +401,9 @@ loop.conversationViews = (function(mozL10n) {
|
||||
case FAILURE_DETAILS.NO_MEDIA:
|
||||
case FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA:
|
||||
return mozL10n.get("no_media_failure_message");
|
||||
case FAILURE_DETAILS.TOS_FAILURE:
|
||||
return mozL10n.get("tos_failure_message",
|
||||
{ clientShortname: mozL10n.get("clientShortname2") });
|
||||
default:
|
||||
return mozL10n.get("generic_failure_message");
|
||||
}
|
||||
|
@ -401,6 +401,9 @@ loop.conversationViews = (function(mozL10n) {
|
||||
case FAILURE_DETAILS.NO_MEDIA:
|
||||
case FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA:
|
||||
return mozL10n.get("no_media_failure_message");
|
||||
case FAILURE_DETAILS.TOS_FAILURE:
|
||||
return mozL10n.get("tos_failure_message",
|
||||
{ clientShortname: mozL10n.get("clientShortname2") });
|
||||
default:
|
||||
return mozL10n.get("generic_failure_message");
|
||||
}
|
||||
|
@ -917,25 +917,38 @@ loop.OTSdkDriver = (function() {
|
||||
},
|
||||
|
||||
_onOTException: function(event) {
|
||||
if (event.code === OT.ExceptionCodes.UNABLE_TO_PUBLISH &&
|
||||
event.message === "GetUserMedia") {
|
||||
// We free up the publisher here in case the store wants to try
|
||||
// grabbing the media again.
|
||||
if (this.publisher) {
|
||||
this.publisher.off("accessAllowed accessDenied accessDialogOpened streamCreated");
|
||||
this.publisher.destroy();
|
||||
delete this.publisher;
|
||||
delete this._mockPublisherEl;
|
||||
}
|
||||
this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
|
||||
reason: FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA
|
||||
}));
|
||||
} else if (event.code === OT.ExceptionCodes.UNABLE_TO_PUBLISH) {
|
||||
// We need to log the message so that we can understand where the exception
|
||||
// is coming from. Potentially a temporary addition.
|
||||
this._notifyMetricsEvent("sdk.exception." + event.code + "." + event.message);
|
||||
} else {
|
||||
this._notifyMetricsEvent("sdk.exception." + event.code);
|
||||
switch (event.code) {
|
||||
case OT.ExceptionCodes.UNABLE_TO_PUBLISH:
|
||||
if (event.message === "GetUserMedia") {
|
||||
// We free up the publisher here in case the store wants to try
|
||||
// grabbing the media again.
|
||||
if (this.publisher) {
|
||||
this.publisher.off("accessAllowed accessDenied accessDialogOpened streamCreated");
|
||||
this.publisher.destroy();
|
||||
delete this.publisher;
|
||||
delete this._mockPublisherEl;
|
||||
}
|
||||
this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
|
||||
reason: FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA
|
||||
}));
|
||||
// No exception logging as this is a handled event.
|
||||
} else {
|
||||
// We need to log the message so that we can understand where the exception
|
||||
// is coming from. Potentially a temporary addition.
|
||||
this._notifyMetricsEvent("sdk.exception." + event.code + "." + event.message);
|
||||
}
|
||||
break;
|
||||
case OT.ExceptionCodes.TERMS_OF_SERVICE_FAILURE:
|
||||
this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
|
||||
reason: FAILURE_DETAILS.TOS_FAILURE
|
||||
}));
|
||||
// We still need to log the exception so that the server knows why this
|
||||
// attempt failed.
|
||||
this._notifyMetricsEvent("sdk.exception." + event.code);
|
||||
break;
|
||||
default:
|
||||
this._notifyMetricsEvent("sdk.exception." + event.code);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -80,6 +80,9 @@ var inChrome = typeof Components != "undefined" && "utils" in Components;
|
||||
COULD_NOT_CONNECT: "reason-could-not-connect",
|
||||
NETWORK_DISCONNECTED: "reason-network-disconnected",
|
||||
EXPIRED_OR_INVALID: "reason-expired-or-invalid",
|
||||
// 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"
|
||||
};
|
||||
|
||||
|
@ -43,6 +43,9 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
return mozL10n.get("rooms_media_denied_message");
|
||||
case FAILURE_DETAILS.EXPIRED_OR_INVALID:
|
||||
return mozL10n.get("rooms_unavailable_notification_message");
|
||||
case FAILURE_DETAILS.TOS_FAILURE:
|
||||
return mozL10n.get("tos_failure_message",
|
||||
{ clientShortname: mozL10n.get("clientShortname2") });
|
||||
default:
|
||||
return mozL10n.get("status_error");
|
||||
}
|
||||
@ -52,7 +55,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
* This renders a retry button if one is necessary.
|
||||
*/
|
||||
renderRetryButton: function() {
|
||||
if (this.props.failureReason === FAILURE_DETAILS.EXPIRED_OR_INVALID) {
|
||||
if (this.props.failureReason === FAILURE_DETAILS.EXPIRED_OR_INVALID ||
|
||||
this.props.failureReason === FAILURE_DETAILS.TOS_FAILURE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -593,6 +597,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
});
|
||||
|
||||
return {
|
||||
StandaloneRoomFailureView: StandaloneRoomFailureView,
|
||||
StandaloneRoomFooter: StandaloneRoomFooter,
|
||||
StandaloneRoomHeader: StandaloneRoomHeader,
|
||||
StandaloneRoomInfoArea: StandaloneRoomInfoArea,
|
||||
|
@ -43,6 +43,9 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
return mozL10n.get("rooms_media_denied_message");
|
||||
case FAILURE_DETAILS.EXPIRED_OR_INVALID:
|
||||
return mozL10n.get("rooms_unavailable_notification_message");
|
||||
case FAILURE_DETAILS.TOS_FAILURE:
|
||||
return mozL10n.get("tos_failure_message",
|
||||
{ clientShortname: mozL10n.get("clientShortname2") });
|
||||
default:
|
||||
return mozL10n.get("status_error");
|
||||
}
|
||||
@ -52,7 +55,8 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
* This renders a retry button if one is necessary.
|
||||
*/
|
||||
renderRetryButton: function() {
|
||||
if (this.props.failureReason === FAILURE_DETAILS.EXPIRED_OR_INVALID) {
|
||||
if (this.props.failureReason === FAILURE_DETAILS.EXPIRED_OR_INVALID ||
|
||||
this.props.failureReason === FAILURE_DETAILS.TOS_FAILURE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -593,6 +597,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
});
|
||||
|
||||
return {
|
||||
StandaloneRoomFailureView: StandaloneRoomFailureView,
|
||||
StandaloneRoomFooter: StandaloneRoomFooter,
|
||||
StandaloneRoomHeader: StandaloneRoomHeader,
|
||||
StandaloneRoomInfoArea: StandaloneRoomInfoArea,
|
||||
|
@ -3,6 +3,10 @@ conversation_has_ended=Your conversation has ended.
|
||||
generic_failure_message=We're having technical difficulties…
|
||||
generic_failure_with_reason2=You can try again or email a link to be reached at later.
|
||||
generic_failure_no_reason2=Would you like to try again?
|
||||
## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname2}}
|
||||
## as this will be replaced by the shortname.
|
||||
tos_failure_message={{clientShortname}} is not available in your country.
|
||||
|
||||
retry_call_button=Retry
|
||||
unable_retrieve_call_info=Unable to retrieve conversation information.
|
||||
hangup_button_title=Hang up
|
||||
|
@ -307,6 +307,16 @@ describe("loop.conversationViews", function () {
|
||||
expect(message.textContent).eql("contact_unavailable_title");
|
||||
});
|
||||
|
||||
it("should display a ToS failure message for the ToS failure reason", function() {
|
||||
view = mountTestComponent({
|
||||
failureReason: FAILURE_DETAILS.TOS_FAILURE
|
||||
});
|
||||
|
||||
var message = view.getDOMNode().querySelector(".failure-info-message");
|
||||
|
||||
expect(message.textContent).eql("tos_failure_message");
|
||||
});
|
||||
|
||||
it("should display a generic unavailable message if the contact doesn't have a display name", function() {
|
||||
view = mountTestComponent({
|
||||
contact: {
|
||||
|
@ -68,6 +68,7 @@ describe("loop.OTSdkDriver", function () {
|
||||
window.OT = {
|
||||
ExceptionCodes: {
|
||||
CONNECT_FAILED: 1006,
|
||||
TERMS_OF_SERVICE_FAILURE: 1026,
|
||||
UNABLE_TO_PUBLISH: 1500
|
||||
}
|
||||
};
|
||||
@ -1599,6 +1600,38 @@ describe("loop.OTSdkDriver", function () {
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("ToS Failure", function() {
|
||||
it("should dispatch a ConnectionFailure action", function() {
|
||||
sdk.trigger("exception", {
|
||||
code: OT.ExceptionCodes.TERMS_OF_SERVICE_FAILURE,
|
||||
message: "Fake"
|
||||
});
|
||||
|
||||
sinon.assert.calledTwice(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionFailure({
|
||||
reason: FAILURE_DETAILS.TOS_FAILURE
|
||||
}));
|
||||
});
|
||||
|
||||
it("should notify metrics", function() {
|
||||
sdk.trigger("exception", {
|
||||
code: OT.ExceptionCodes.TERMS_OF_SERVICE_FAILURE,
|
||||
message: "Fake"
|
||||
});
|
||||
|
||||
sinon.assert.calledTwice(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.ConnectionStatus({
|
||||
event: "sdk.exception." + OT.ExceptionCodes.TERMS_OF_SERVICE_FAILURE,
|
||||
state: "starting",
|
||||
connections: 0,
|
||||
sendStreams: 0,
|
||||
recvStreams: 0
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -17,7 +17,7 @@ describe("loop.standaloneRoomViews", function() {
|
||||
var fixtures = document.querySelector("#fixtures");
|
||||
|
||||
var sandbox, dispatcher, activeRoomStore, dispatch;
|
||||
var clock, fakeWindow;
|
||||
var clock, fakeWindow, view;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@ -63,6 +63,7 @@ describe("loop.standaloneRoomViews", function() {
|
||||
sandbox.restore();
|
||||
clock.restore();
|
||||
React.unmountComponentAtNode(fixtures);
|
||||
view = null;
|
||||
});
|
||||
|
||||
describe("StandaloneRoomHeader", function() {
|
||||
@ -75,7 +76,7 @@ describe("loop.standaloneRoomViews", function() {
|
||||
}
|
||||
|
||||
it("should dispatch a RecordClick action when the support link is clicked", function() {
|
||||
var view = mountTestComponent();
|
||||
view = mountTestComponent();
|
||||
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector("a"));
|
||||
|
||||
@ -87,13 +88,93 @@ describe("loop.standaloneRoomViews", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("StandaloneRoomFailureView", function() {
|
||||
function mountTestComponent(extraProps) {
|
||||
var props = _.extend({
|
||||
dispatcher: dispatcher
|
||||
}, extraProps);
|
||||
return TestUtils.renderIntoDocument(
|
||||
React.createElement(
|
||||
loop.standaloneRoomViews.StandaloneRoomFailureView, props));
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
activeRoomStore.setStoreState({ roomState: ROOM_STATES.FAILED });
|
||||
});
|
||||
|
||||
it("should display a status error message if not reason is supplied", function() {
|
||||
view = mountTestComponent();
|
||||
|
||||
expect(view.getDOMNode().querySelector(".failed-room-message").textContent)
|
||||
.eql("status_error");
|
||||
});
|
||||
|
||||
it("should display a denied message on MEDIA_DENIED", function() {
|
||||
view = mountTestComponent({ failureReason: FAILURE_DETAILS.MEDIA_DENIED });
|
||||
|
||||
expect(view.getDOMNode().querySelector(".failed-room-message").textContent)
|
||||
.eql("rooms_media_denied_message");
|
||||
});
|
||||
|
||||
it("should display a denied message on NO_MEDIA", function() {
|
||||
view = mountTestComponent({ failureReason: FAILURE_DETAILS.NO_MEDIA });
|
||||
|
||||
expect(view.getDOMNode().querySelector(".failed-room-message").textContent)
|
||||
.eql("rooms_media_denied_message");
|
||||
});
|
||||
|
||||
it("should display an unavailable message on EXPIRED_OR_INVALID", function() {
|
||||
view = mountTestComponent({ failureReason: FAILURE_DETAILS.EXPIRED_OR_INVALID });
|
||||
|
||||
expect(view.getDOMNode().querySelector(".failed-room-message").textContent)
|
||||
.eql("rooms_unavailable_notification_message");
|
||||
});
|
||||
|
||||
it("should display an tos failure message on TOS_FAILURE", function() {
|
||||
view = mountTestComponent({ failureReason: FAILURE_DETAILS.TOS_FAILURE });
|
||||
|
||||
expect(view.getDOMNode().querySelector(".failed-room-message").textContent)
|
||||
.eql("tos_failure_message");
|
||||
});
|
||||
|
||||
it("should not display a retry button when the failure reason is expired or invalid", function() {
|
||||
view = mountTestComponent({ failureReason: FAILURE_DETAILS.EXPIRED_OR_INVALID });
|
||||
|
||||
expect(view.getDOMNode().querySelector(".btn-info")).eql(null);
|
||||
});
|
||||
|
||||
it("should not display a retry button when the failure reason is tos failure", function() {
|
||||
view = mountTestComponent({ failureReason: FAILURE_DETAILS.TOS_FAILURE });
|
||||
|
||||
expect(view.getDOMNode().querySelector(".btn-info")).eql(null);
|
||||
});
|
||||
|
||||
it("should display a retry button for any other reason", function() {
|
||||
view = mountTestComponent({ failureReason: FAILURE_DETAILS.NO_MEDIA });
|
||||
|
||||
expect(view.getDOMNode().querySelector(".btn-info")).not.eql(null);
|
||||
});
|
||||
|
||||
it("should dispatch a RetryAfterRoomFailure action when the retry button is pressed", function() {
|
||||
view = mountTestComponent({ failureReason: FAILURE_DETAILS.NO_MEDIA });
|
||||
|
||||
var button = view.getDOMNode().querySelector(".btn-info");
|
||||
|
||||
TestUtils.Simulate.click(button);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.RetryAfterRoomFailure());
|
||||
});
|
||||
});
|
||||
|
||||
describe("StandaloneRoomInfoArea in fixture", function() {
|
||||
it("should dispatch a RecordClick action when the tile is clicked", function(done) {
|
||||
// Point the iframe to a page that will auto-"click"
|
||||
loop.config.tilesIframeUrl = "data:text/html,<script>parent.postMessage('tile-click', '*');</script>";
|
||||
|
||||
// Render the iframe into the fixture to cause it to load
|
||||
var view = React.render(
|
||||
view = React.render(
|
||||
React.createElement(
|
||||
loop.standaloneRoomViews.StandaloneRoomInfoArea, {
|
||||
activeRoomStore: activeRoomStore,
|
||||
@ -135,7 +216,7 @@ describe("loop.standaloneRoomViews", function() {
|
||||
}));
|
||||
}
|
||||
|
||||
function expectActionDispatched(view) {
|
||||
function expectActionDispatched() {
|
||||
sinon.assert.calledOnce(dispatch);
|
||||
sinon.assert.calledWithExactly(dispatch,
|
||||
sinon.match.instanceOf(sharedActions.SetupStreamElements));
|
||||
@ -144,7 +225,7 @@ describe("loop.standaloneRoomViews", function() {
|
||||
describe("#componentWillUpdate", function() {
|
||||
it("should set document.title to roomName and brand name when the READY state is dispatched", function() {
|
||||
activeRoomStore.setStoreState({roomName: "fakeName", roomState: ROOM_STATES.INIT});
|
||||
var view = mountTestComponent();
|
||||
view = mountTestComponent();
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
|
||||
|
||||
expect(fakeWindow.document.title).to.equal("fakeName — clientShortname2");
|
||||
@ -153,7 +234,7 @@ describe("loop.standaloneRoomViews", function() {
|
||||
it("should dispatch a `SetupStreamElements` action when the MEDIA_WAIT state " +
|
||||
"is entered", function() {
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
|
||||
var view = mountTestComponent();
|
||||
view = mountTestComponent();
|
||||
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
|
||||
|
||||
@ -163,7 +244,7 @@ describe("loop.standaloneRoomViews", function() {
|
||||
it("should dispatch a `SetupStreamElements` action on MEDIA_WAIT state is " +
|
||||
"re-entered", function() {
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED});
|
||||
var view = mountTestComponent();
|
||||
view = mountTestComponent();
|
||||
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
|
||||
|
||||
@ -172,8 +253,6 @@ describe("loop.standaloneRoomViews", function() {
|
||||
});
|
||||
|
||||
describe("#componentDidUpdate", function() {
|
||||
var view;
|
||||
|
||||
beforeEach(function() {
|
||||
view = mountTestComponent();
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINING});
|
||||
@ -223,8 +302,6 @@ describe("loop.standaloneRoomViews", function() {
|
||||
});
|
||||
|
||||
describe("#componentWillReceiveProps", function() {
|
||||
var view;
|
||||
|
||||
beforeEach(function() {
|
||||
view = mountTestComponent();
|
||||
|
||||
@ -280,8 +357,6 @@ describe("loop.standaloneRoomViews", function() {
|
||||
});
|
||||
|
||||
describe("#publishStream", function() {
|
||||
var view;
|
||||
|
||||
beforeEach(function() {
|
||||
view = mountTestComponent();
|
||||
view.setState({
|
||||
@ -314,8 +389,6 @@ describe("loop.standaloneRoomViews", function() {
|
||||
});
|
||||
|
||||
describe("#render", function() {
|
||||
var view;
|
||||
|
||||
beforeEach(function() {
|
||||
view = mountTestComponent();
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINING});
|
||||
@ -424,35 +497,11 @@ describe("loop.standaloneRoomViews", function() {
|
||||
});
|
||||
|
||||
describe("Failed room message", function() {
|
||||
beforeEach(function() {
|
||||
it("should display the StandaloneRoomFailureView", function() {
|
||||
activeRoomStore.setStoreState({ roomState: ROOM_STATES.FAILED });
|
||||
});
|
||||
|
||||
it("should display a failed room message on FAILED", function() {
|
||||
expect(view.getDOMNode().querySelector(".failed-room-message"))
|
||||
.not.eql(null);
|
||||
});
|
||||
|
||||
it("should display a retry button", function() {
|
||||
expect(view.getDOMNode().querySelector(".btn-info")).not.eql(null);
|
||||
});
|
||||
|
||||
it("should not display a retry button when the failure reason is expired or invalid", function() {
|
||||
activeRoomStore.setStoreState({
|
||||
failureReason: FAILURE_DETAILS.EXPIRED_OR_INVALID
|
||||
});
|
||||
|
||||
expect(view.getDOMNode().querySelector(".btn-info")).eql(null);
|
||||
});
|
||||
|
||||
it("should dispatch a RetryAfterRoomFailure action when the retry button is pressed", function() {
|
||||
var button = view.getDOMNode().querySelector(".btn-info");
|
||||
|
||||
TestUtils.Simulate.click(button);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.RetryAfterRoomFailure());
|
||||
TestUtils.findRenderedComponentWithType(view,
|
||||
loop.standaloneRoomViews.StandaloneRoomFailureView);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -280,6 +280,9 @@ generic_contact_unavailable_title=This person is unavailable.
|
||||
generic_failure_message=We're having technical difficulties…
|
||||
generic_failure_with_reason2=You can try again or email a link to be reached at later.
|
||||
generic_failure_no_reason2=Would you like to try again?
|
||||
## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname2}}
|
||||
## as this will be replaced by the shortname.
|
||||
tos_failure_message={{clientShortname}} is not available in your country.
|
||||
|
||||
## LOCALIZATION NOTE (contact_offline_title): Title which is displayed when the
|
||||
## contact is offline.
|
||||
|
Loading…
Reference in New Issue
Block a user