Bug 1209078 - Part 2. If a user attempts to open their own room within Firefox when the room is already open, provide a message to inform the user. r=mikedeboer

This commit is contained in:
Mark Banner 2015-09-29 14:50:15 +01:00
Родитель 218567ce2a
Коммит 85a8eff79d
10 изменённых файлов: 169 добавлений и 26 удалений

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

@ -623,12 +623,18 @@ loop.store.ActiveRoomStore = (function() {
// previously. We should add better user feedback here.
console.error("Firefox didn't handle room it said it could.");
} else {
this.dispatcher.dispatch(new sharedActions.JoinedRoom({
apiKey: "",
sessionToken: "",
sessionId: "",
expires: 0
}));
if (e.detail.message.alreadyOpen) {
this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
reason: FAILURE_DETAILS.ROOM_ALREADY_OPEN
}));
} else {
this.dispatcher.dispatch(new sharedActions.JoinedRoom({
apiKey: "",
sessionToken: "",
sessionId: "",
expires: 0
}));
}
}
}
@ -1064,6 +1070,15 @@ loop.store.ActiveRoomStore = (function() {
* will skip the leave message.
*/
_leaveRoom: function(nextState, failedJoinRequest) {
if (this._storeState.standalone && this._storeState.userAgentHandlesRoom) {
// If the user agent is handling the room, all we need to do is advance
// to the next state.
this.setStoreState({
roomState: nextState
});
return;
}
if (loop.standaloneMedia) {
loop.standaloneMedia.multiplexGum.reset();
}

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

@ -75,6 +75,7 @@ var inChrome = typeof Components != "undefined" && "utils" in Components;
var FAILURE_DETAILS = {
MEDIA_DENIED: "reason-media-denied",
NO_MEDIA: "reason-no-media",
ROOM_ALREADY_OPEN: "reason-room-already-open",
UNABLE_TO_PUBLISH_MEDIA: "unable-to-publish-media",
USER_UNAVAILABLE: "reason-user-unavailable",
COULD_NOT_CONNECT: "reason-could-not-connect",

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

@ -184,6 +184,14 @@ html[dir="rtl"] .rooms-footer .footer-logo {
margin: 2rem auto;
}
.handle-user-agent-view > .info-panel > .failure {
color: red;
font-weight: bold;
/* Add padding to match the height of the button. */
padding: 1.15rem 0;
margin: 0;
}
.handle-user-agent-view > .info-panel > button {
width: 80%;
height: 4rem;

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

@ -125,12 +125,19 @@ loop.store.StandaloneMetricsStore = (function() {
* @param {sharedActions.ConnectionFailure} actionData
*/
connectionFailure: function(actionData) {
if (actionData.reason === FAILURE_DETAILS.MEDIA_DENIED) {
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.failed,
"Media denied");
} else if (actionData.reason === FAILURE_DETAILS.NO_MEDIA) {
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.failed,
"No media");
switch(actionData.reason) {
case FAILURE_DETAILS.MEDIA_DENIED:
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.failed,
"Media denied");
break;
case FAILURE_DETAILS.NO_MEDIA:
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.failed,
"No media");
break;
case FAILURE_DETAILS.ROOM_ALREADY_OPEN:
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.failed,
"Room already open");
break;
}
},

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

@ -75,7 +75,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
this.props.dispatcher.dispatch(new sharedActions.JoinRoom());
},
render: function() {
_renderJoinButton: function() {
var buttonMessage = this.state.roomState === ROOM_STATES.JOINED ?
mozL10n.get("rooms_room_joined_own_conversation_label") :
mozL10n.get("rooms_room_join_label");
@ -86,6 +86,22 @@ loop.standaloneRoomViews = (function(mozL10n) {
disabled: this.state.roomState === ROOM_STATES.JOINED
});
return (
React.createElement("button", {
className: buttonClasses,
onClick: this.handleJoinButton},
buttonMessage
)
);
},
_renderFailureText: function() {
return (
React.createElement("p", {className: "failure"}, mozL10n.get("rooms_already_joined") )
);
},
render: function() {
// The extra scroller div here is for providing a scroll view for shorter
// screens, as the common.css specifies overflow:hidden for the body which
// we need in some places.
@ -96,11 +112,11 @@ loop.standaloneRoomViews = (function(mozL10n) {
React.createElement("p", {className: "loop-logo-text", title: mozL10n.get("clientShortname2") }),
React.createElement("p", {className: "roomName"}, this.state.roomName),
React.createElement("p", {className: "loop-logo"}),
React.createElement("button", {
className: buttonClasses,
onClick: this.handleJoinButton},
buttonMessage
)
this.state.failureReason ?
this._renderFailureText() :
this._renderJoinButton()
),
React.createElement(ToSView, {
dispatcher: this.props.dispatcher}),

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

@ -75,7 +75,7 @@ loop.standaloneRoomViews = (function(mozL10n) {
this.props.dispatcher.dispatch(new sharedActions.JoinRoom());
},
render: function() {
_renderJoinButton: function() {
var buttonMessage = this.state.roomState === ROOM_STATES.JOINED ?
mozL10n.get("rooms_room_joined_own_conversation_label") :
mozL10n.get("rooms_room_join_label");
@ -86,6 +86,22 @@ loop.standaloneRoomViews = (function(mozL10n) {
disabled: this.state.roomState === ROOM_STATES.JOINED
});
return (
<button
className={buttonClasses}
onClick={this.handleJoinButton}>
{buttonMessage}
</button>
);
},
_renderFailureText: function() {
return (
<p className="failure">{ mozL10n.get("rooms_already_joined") }</p>
);
},
render: function() {
// The extra scroller div here is for providing a scroll view for shorter
// screens, as the common.css specifies overflow:hidden for the body which
// we need in some places.
@ -96,11 +112,11 @@ loop.standaloneRoomViews = (function(mozL10n) {
<p className="loop-logo-text" title={ mozL10n.get("clientShortname2") }></p>
<p className="roomName">{ this.state.roomName }</p>
<p className="loop-logo" />
<button
className={buttonClasses}
onClick={this.handleJoinButton}>
{buttonMessage}
</button>
{
this.state.failureReason ?
this._renderFailureText() :
this._renderJoinButton()
}
</div>
<ToSView
dispatcher={this.props.dispatcher} />

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

@ -69,6 +69,7 @@ rooms_room_full_call_to_action_label=Learn more about {{clientShortname}} »
rooms_room_joined_label=Someone has joined the conversation!
rooms_room_join_label=Join the conversation
rooms_room_joined_own_conversation_label=Enjoy your conversation
rooms_already_joined=You're already in this conversation.
rooms_display_name_guest=Guest
rooms_unavailable_notification_message=Sorry, you cannot join this conversation. The link may be expired or invalid.
rooms_media_denied_message=We could not get access to your microphone or camera. Please reload the page to try again.

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

@ -873,7 +873,7 @@ describe("loop.store.ActiveRoomStore", function () {
});
});
describe("Firefox Handles Room", function() {
describe("User Agent Handles Room", function() {
var channelListener;
beforeEach(function() {
@ -951,7 +951,8 @@ describe("loop.store.ActiveRoomStore", function () {
detail: {
id: "loop-link-clicker",
message: {
response: true
response: true,
alreadyOpen: false
}
}
});
@ -965,6 +966,28 @@ describe("loop.store.ActiveRoomStore", function () {
expires: 0
}));
});
it("should dispatch a ConnectionFailure action if the room was already opened", function() {
// Start the join.
store.joinRoom();
// Pretend Firefox calls back.
channelListener({
detail: {
id: "loop-link-clicker",
message: {
response: true,
alreadyOpen: true
}
}
});
sinon.assert.called(dispatcher.dispatch);
sinon.assert.calledWithExactly(dispatcher.dispatch,
new sharedActions.ConnectionFailure({
reason: FAILURE_DETAILS.ROOM_ALREADY_OPEN
}));
});
});
});
@ -1257,6 +1280,29 @@ describe("loop.store.ActiveRoomStore", function () {
expect(store.getStoreState().roomState).eql(ROOM_STATES.FAILED);
});
it("should set the state to `FAILED` if the user agent is handling the room", function() {
store.setStoreState({
standalone: true,
userAgentHandlesRoom: true
});
store.connectionFailure(connectionFailureAction);
expect(store.getStoreState().roomState).eql(ROOM_STATES.FAILED);
});
it("should not do any other cleanup if the user agent is handling the room", function() {
store.setStoreState({
standalone: true,
userAgentHandlesRoom: true
});
store.connectionFailure(connectionFailureAction);
sinon.assert.notCalled(fakeMultiplexGum.reset);
sinon.assert.notCalled(fakeSdkDriver.disconnectSession);
});
});
describe("#setMute", function() {

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

@ -77,6 +77,17 @@ describe("loop.store.StandaloneMetricsStore", function() {
"No media");
});
it("should log an event on connection failure if the room was already open", function() {
store.connectionFailure(new sharedActions.ConnectionFailure({
reason: FAILURE_DETAILS.ROOM_ALREADY_OPEN
}));
sinon.assert.calledOnce(window.ga);
sinon.assert.calledWithExactly(window.ga,
"send", "event", METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.failed,
"Room already open");
});
it("should log an event on GotMediaPermission", function() {
store.gotMediaPermission();

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

@ -183,6 +183,28 @@ describe("loop.standaloneRoomViews", function() {
expect(button.classList.contains("disabled")).eql(true);
});
it("should not display a join button if there is a failure reason", function() {
activeRoomStore.setStoreState({
failureReason: FAILURE_DETAILS.ROOM_ALREADY_OPEN
});
view = mountTestComponent();
var button = view.getDOMNode().querySelector(".info-panel > button");
expect(button).eql(null);
});
it("should display a room already joined message if opening failed", function() {
activeRoomStore.setStoreState({
failureReason: FAILURE_DETAILS.ROOM_ALREADY_OPEN
});
view = mountTestComponent();
var text = view.getDOMNode().querySelector(".failure");
expect(text.textContent).eql("rooms_already_joined");
});
});
describe("StandaloneRoomHeader", function() {