mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-28 12:45:27 +00:00
Bug 1126733 - Brief message appears when entering a standalone room that the user is the only person in the room [r=Standard8]
This commit is contained in:
parent
8ab64f8f73
commit
a2a29ccf86
@ -199,6 +199,12 @@ loop.shared.actions = (function() {
|
||||
publisherConfig: Object
|
||||
}),
|
||||
|
||||
/**
|
||||
* Used for notifying that a waiting tile was shown.
|
||||
*/
|
||||
TileShown: Action.define("tileShown", {
|
||||
}),
|
||||
|
||||
/**
|
||||
* Used for notifying that local media has been obtained.
|
||||
*/
|
||||
|
@ -51,6 +51,7 @@ loop.store.StandaloneMetricsStore = (function() {
|
||||
"recordClick",
|
||||
"remotePeerConnected",
|
||||
"retryAfterRoomFailure",
|
||||
"tileShown",
|
||||
"windowUnload"
|
||||
],
|
||||
|
||||
@ -201,6 +202,14 @@ loop.store.StandaloneMetricsStore = (function() {
|
||||
"Retry failed room");
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles when a tile was finally shown (potentially after a delay)
|
||||
*/
|
||||
tileShown: function() {
|
||||
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.pageLoad,
|
||||
"Tile shown");
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles notifications that the activeRoomStore has changed, updating
|
||||
* the metrics for room state and mute state as necessary.
|
||||
|
@ -77,6 +77,10 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
});
|
||||
|
||||
var StandaloneRoomInfoArea = React.createClass({displayName: "StandaloneRoomInfoArea",
|
||||
statics: {
|
||||
RENDER_WAITING_DELAY: 2000
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
activeRoomStore: React.PropTypes.oneOfType([
|
||||
React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
|
||||
@ -90,11 +94,60 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
roomUsed: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return { waitToRenderWaiting: true };
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
// Watch for messages from the waiting-tile iframe
|
||||
window.addEventListener("message", this.recordTileClick);
|
||||
},
|
||||
|
||||
/**
|
||||
* Change state to allow for the waiting message to be shown and send an
|
||||
* event to record that fact.
|
||||
*/
|
||||
_allowRenderWaiting: function() {
|
||||
delete this._waitTimer;
|
||||
|
||||
// Only update state if we're still showing a waiting message.
|
||||
switch (this.props.roomState) {
|
||||
case ROOM_STATES.JOINING:
|
||||
case ROOM_STATES.JOINED:
|
||||
case ROOM_STATES.SESSION_CONNECTED:
|
||||
this.setState({ waitToRenderWaiting: false });
|
||||
this.props.dispatcher.dispatch(new sharedActions.TileShown());
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
componentDidUpdate: function() {
|
||||
// Start a timer once from the earliest waiting state if we need to wait
|
||||
// before showing a message.
|
||||
if (this.props.roomState === ROOM_STATES.JOINING &&
|
||||
this.state.waitToRenderWaiting &&
|
||||
this._waitTimer === undefined) {
|
||||
this._waitTimer = setTimeout(this._allowRenderWaiting,
|
||||
this.constructor.RENDER_WAITING_DELAY);
|
||||
}
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
switch (nextProps.roomState) {
|
||||
// Reset waiting for the next time the user joins.
|
||||
case ROOM_STATES.ENDED:
|
||||
case ROOM_STATES.READY:
|
||||
if (!this.state.waitToRenderWaiting) {
|
||||
this.setState({ waitToRenderWaiting: true });
|
||||
}
|
||||
if (this._waitTimer !== undefined) {
|
||||
clearTimeout(this._waitTimer);
|
||||
delete this._waitTimer;
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
window.removeEventListener("message", this.recordTileClick);
|
||||
},
|
||||
@ -170,6 +223,12 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
case ROOM_STATES.JOINING:
|
||||
case ROOM_STATES.JOINED:
|
||||
case ROOM_STATES.SESSION_CONNECTED: {
|
||||
// Don't show the waiting display until after a brief wait in case
|
||||
// there's another participant that will momentarily appear.
|
||||
if (this.state.waitToRenderWaiting) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
React.createElement("div", {className: "room-inner-info-area"},
|
||||
React.createElement("p", {className: "empty-room-message"},
|
||||
|
@ -77,6 +77,10 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
});
|
||||
|
||||
var StandaloneRoomInfoArea = React.createClass({
|
||||
statics: {
|
||||
RENDER_WAITING_DELAY: 2000
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
activeRoomStore: React.PropTypes.oneOfType([
|
||||
React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
|
||||
@ -90,11 +94,60 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
roomUsed: React.PropTypes.bool.isRequired
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return { waitToRenderWaiting: true };
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
// Watch for messages from the waiting-tile iframe
|
||||
window.addEventListener("message", this.recordTileClick);
|
||||
},
|
||||
|
||||
/**
|
||||
* Change state to allow for the waiting message to be shown and send an
|
||||
* event to record that fact.
|
||||
*/
|
||||
_allowRenderWaiting: function() {
|
||||
delete this._waitTimer;
|
||||
|
||||
// Only update state if we're still showing a waiting message.
|
||||
switch (this.props.roomState) {
|
||||
case ROOM_STATES.JOINING:
|
||||
case ROOM_STATES.JOINED:
|
||||
case ROOM_STATES.SESSION_CONNECTED:
|
||||
this.setState({ waitToRenderWaiting: false });
|
||||
this.props.dispatcher.dispatch(new sharedActions.TileShown());
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
componentDidUpdate: function() {
|
||||
// Start a timer once from the earliest waiting state if we need to wait
|
||||
// before showing a message.
|
||||
if (this.props.roomState === ROOM_STATES.JOINING &&
|
||||
this.state.waitToRenderWaiting &&
|
||||
this._waitTimer === undefined) {
|
||||
this._waitTimer = setTimeout(this._allowRenderWaiting,
|
||||
this.constructor.RENDER_WAITING_DELAY);
|
||||
}
|
||||
},
|
||||
|
||||
componentWillReceiveProps: function(nextProps) {
|
||||
switch (nextProps.roomState) {
|
||||
// Reset waiting for the next time the user joins.
|
||||
case ROOM_STATES.ENDED:
|
||||
case ROOM_STATES.READY:
|
||||
if (!this.state.waitToRenderWaiting) {
|
||||
this.setState({ waitToRenderWaiting: true });
|
||||
}
|
||||
if (this._waitTimer !== undefined) {
|
||||
clearTimeout(this._waitTimer);
|
||||
delete this._waitTimer;
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
window.removeEventListener("message", this.recordTileClick);
|
||||
},
|
||||
@ -170,6 +223,12 @@ loop.standaloneRoomViews = (function(mozL10n) {
|
||||
case ROOM_STATES.JOINING:
|
||||
case ROOM_STATES.JOINED:
|
||||
case ROOM_STATES.SESSION_CONNECTED: {
|
||||
// Don't show the waiting display until after a brief wait in case
|
||||
// there's another participant that will momentarily appear.
|
||||
if (this.state.waitToRenderWaiting) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="room-inner-info-area">
|
||||
<p className="empty-room-message">
|
||||
|
@ -17,7 +17,7 @@ describe("loop.standaloneRoomViews", function() {
|
||||
var fixtures = document.querySelector("#fixtures");
|
||||
|
||||
var sandbox, dispatcher, activeRoomStore, dispatch;
|
||||
var fakeWindow;
|
||||
var clock, fakeWindow;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
@ -35,7 +35,7 @@ describe("loop.standaloneRoomViews", function() {
|
||||
textChatStore: textChatStore
|
||||
});
|
||||
|
||||
sandbox.useFakeTimers();
|
||||
clock = sandbox.useFakeTimers();
|
||||
fakeWindow = {
|
||||
close: sandbox.stub(),
|
||||
addEventListener: function() {},
|
||||
@ -61,6 +61,7 @@ describe("loop.standaloneRoomViews", function() {
|
||||
afterEach(function() {
|
||||
loop.shared.mixins.setRootObject(window);
|
||||
sandbox.restore();
|
||||
clock.restore();
|
||||
React.unmountComponentAtNode(fixtures);
|
||||
});
|
||||
|
||||
@ -92,23 +93,27 @@ describe("loop.standaloneRoomViews", function() {
|
||||
loop.config.tilesIframeUrl = "data:text/html,<script>parent.postMessage('tile-click', '*');</script>";
|
||||
|
||||
// Render the iframe into the fixture to cause it to load
|
||||
React.render(
|
||||
var view = React.render(
|
||||
React.createElement(
|
||||
loop.standaloneRoomViews.StandaloneRoomInfoArea, {
|
||||
activeRoomStore: activeRoomStore,
|
||||
dispatcher: dispatcher,
|
||||
isFirefox: true,
|
||||
joinRoom: sandbox.stub(),
|
||||
roomState: ROOM_STATES.JOINED,
|
||||
roomState: ROOM_STATES.INIT,
|
||||
roomUsed: false
|
||||
}), fixtures);
|
||||
|
||||
// Change states and move time to get the iframe to load
|
||||
view.setProps({ roomState: ROOM_STATES.JOINING });
|
||||
clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
|
||||
|
||||
// Wait for the iframe to load and trigger a message that should also
|
||||
// cause the RecordClick action
|
||||
window.addEventListener("message", function onMessage() {
|
||||
window.removeEventListener("message", onMessage);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledTwice(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.RecordClick({
|
||||
linkInfo: "Tiles iframe click"
|
||||
@ -166,6 +171,103 @@ describe("loop.standaloneRoomViews", function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("#componentDidUpdate", function() {
|
||||
var view;
|
||||
|
||||
beforeEach(function() {
|
||||
view = mountTestComponent();
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINING});
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
|
||||
});
|
||||
|
||||
it("should not dispatch a `TileShown` action immediately in the JOINED state",
|
||||
function() {
|
||||
sinon.assert.notCalled(dispatch);
|
||||
});
|
||||
|
||||
it("should dispatch a `TileShown` action after a wait when in the JOINED state",
|
||||
function() {
|
||||
clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
|
||||
|
||||
sinon.assert.calledOnce(dispatch);
|
||||
sinon.assert.calledWithExactly(dispatch, new sharedActions.TileShown());
|
||||
});
|
||||
|
||||
it("should dispatch a single `TileShown` action after a wait when going through multiple waiting states",
|
||||
function() {
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.SESSION_CONNECTED});
|
||||
clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
|
||||
|
||||
sinon.assert.calledOnce(dispatch);
|
||||
sinon.assert.calledWithExactly(dispatch, new sharedActions.TileShown());
|
||||
});
|
||||
|
||||
it("should not dispatch a `TileShown` action after a wait when in the HAS_PARTICIPANTS state",
|
||||
function() {
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.HAS_PARTICIPANTS});
|
||||
clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
|
||||
|
||||
sinon.assert.notCalled(dispatch);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#componentWillReceiveProps", function() {
|
||||
var view;
|
||||
|
||||
beforeEach(function() {
|
||||
view = mountTestComponent();
|
||||
|
||||
// Pretend the user waited a little bit
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINING});
|
||||
clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY - 1);
|
||||
});
|
||||
|
||||
describe("Support multiple joins", function() {
|
||||
it("should send the first `TileShown` after waiting in JOINING state",
|
||||
function() {
|
||||
clock.tick(1);
|
||||
|
||||
sinon.assert.calledOnce(dispatch);
|
||||
sinon.assert.calledWithExactly(dispatch, new sharedActions.TileShown());
|
||||
});
|
||||
|
||||
it("should send the second `TileShown` after ending and rejoining",
|
||||
function() {
|
||||
// Trigger the first message then rejoin and wait
|
||||
clock.tick(1);
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED});
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINING});
|
||||
clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
|
||||
|
||||
sinon.assert.calledTwice(dispatch);
|
||||
sinon.assert.calledWithExactly(dispatch, new sharedActions.TileShown());
|
||||
});
|
||||
});
|
||||
|
||||
describe("Handle leaving quickly", function() {
|
||||
beforeEach(function() {
|
||||
// The user left and rejoined
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.ENDED});
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINING});
|
||||
});
|
||||
|
||||
it("should not dispatch an old `TileShown` action after leaving and rejoining",
|
||||
function() {
|
||||
clock.tick(1);
|
||||
|
||||
sinon.assert.notCalled(dispatch);
|
||||
});
|
||||
|
||||
it("should dispatch a new `TileShown` action after leaving and rejoining and waiting",
|
||||
function() {
|
||||
clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
|
||||
|
||||
sinon.assert.calledOnce(dispatch);
|
||||
sinon.assert.calledWithExactly(dispatch, new sharedActions.TileShown());
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("#publishStream", function() {
|
||||
var view;
|
||||
|
||||
@ -205,29 +307,57 @@ describe("loop.standaloneRoomViews", function() {
|
||||
|
||||
beforeEach(function() {
|
||||
view = mountTestComponent();
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINING});
|
||||
});
|
||||
|
||||
describe("Empty room message", function() {
|
||||
it("should display an empty room message on JOINED",
|
||||
it("should not display an message immediately in the JOINED state",
|
||||
function() {
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
|
||||
|
||||
expect(view.getDOMNode().querySelector(".empty-room-message"))
|
||||
.not.eql(null);
|
||||
.eql(null);
|
||||
});
|
||||
|
||||
it("should display an empty room message on SESSION_CONNECTED",
|
||||
it("should display an empty room message after a wait when in the JOINED state",
|
||||
function() {
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.SESSION_CONNECTED});
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
|
||||
clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
|
||||
|
||||
expect(view.getDOMNode().querySelector(".empty-room-message"))
|
||||
.not.eql(null);
|
||||
});
|
||||
|
||||
it("shouldn't display an empty room message on HAS_PARTICIPANTS",
|
||||
it("should not display an message immediately in the SESSION_CONNECTED state",
|
||||
function() {
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.SESSION_CONNECTED});
|
||||
|
||||
expect(view.getDOMNode().querySelector(".empty-room-message"))
|
||||
.eql(null);
|
||||
});
|
||||
|
||||
it("should display an empty room message after a wait when in the SESSION_CONNECTED state",
|
||||
function() {
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.SESSION_CONNECTED});
|
||||
clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
|
||||
|
||||
expect(view.getDOMNode().querySelector(".empty-room-message"))
|
||||
.not.eql(null);
|
||||
});
|
||||
|
||||
it("should not display an message immediately in the HAS_PARTICIPANTS state",
|
||||
function() {
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.HAS_PARTICIPANTS});
|
||||
|
||||
expect(view.getDOMNode().querySelector(".empty-room-message"))
|
||||
.eql(null);
|
||||
});
|
||||
|
||||
it("should not display an empty room message even after a wait when in the HAS_PARTICIPANTS state",
|
||||
function() {
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.HAS_PARTICIPANTS});
|
||||
clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
|
||||
|
||||
expect(view.getDOMNode().querySelector(".empty-room-message"))
|
||||
.eql(null);
|
||||
});
|
||||
@ -238,6 +368,7 @@ describe("loop.standaloneRoomViews", function() {
|
||||
var DUMMY_TILE_URL = "http://tile/";
|
||||
loop.config.tilesIframeUrl = DUMMY_TILE_URL;
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
|
||||
clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
|
||||
|
||||
expect(view.getDOMNode().querySelector(".room-waiting-area")).not.eql(null);
|
||||
|
||||
@ -248,10 +379,11 @@ describe("loop.standaloneRoomViews", function() {
|
||||
|
||||
it("should dispatch a RecordClick action when the tile support link is clicked", function() {
|
||||
activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
|
||||
clock.tick(loop.standaloneRoomViews.StandaloneRoomInfoArea.RENDER_WAITING_DELAY);
|
||||
|
||||
TestUtils.Simulate.click(view.getDOMNode().querySelector(".room-waiting-area a"));
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledTwice(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.RecordClick({
|
||||
linkInfo: "Tiles support link click"
|
||||
|
Loading…
Reference in New Issue
Block a user