Bug 1213851 - Display only active room when user enters room [r=Mardak]

This commit is contained in:
David Critchley 2015-10-28 09:35:56 -07:00
parent 2325f694ea
commit b8555d522b
5 changed files with 153 additions and 92 deletions

View File

@ -254,15 +254,13 @@ body {
/* See .room-entry-context-item for the margin/size reductions.
* An extra 40px to make space for the call button and chevron. */
width: calc(100% - 1rem - 56px);
}
.room-list > .room-entry.room-active > h2 {
.room-list > .room-entry.room-active:not(.room-opened) > h2 {
font-weight: bold;
color: #000;
}
.room-list > .room-entry:hover {
.room-list > .room-entry:not(.room-opened):hover {
background: #dbf7ff;
}
@ -417,7 +415,7 @@ html[dir="rtl"] .room-entry-context-actions > .dropdown-menu {
height: 16px;
}
.room-entry:hover .room-entry-context-item {
.room-entry:not(.room-opened):hover .room-entry-context-item {
display: none;
}

View File

@ -362,10 +362,14 @@ loop.panel = (function(_, mozL10n) {
/**
* Room list entry.
*
* Active Room means there are participants in the room.
* Opened Room means the user is in the room.
*/
var RoomEntry = React.createClass({displayName: "RoomEntry",
propTypes: {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
isOpenedRoom: React.PropTypes.bool.isRequired,
mozLoop: React.PropTypes.object.isRequired,
room: React.PropTypes.instanceOf(loop.store.Room).isRequired
},
@ -418,7 +422,8 @@ loop.panel = (function(_, mozL10n) {
render: function() {
var roomClasses = React.addons.classSet({
"room-entry": true,
"room-active": this._isActive()
"room-active": this._isActive(),
"room-opened": this.props.isOpenedRoom
});
var roomTitle = this.props.room.decryptedContext.roomName ||
@ -427,8 +432,8 @@ loop.panel = (function(_, mozL10n) {
return (
React.createElement("div", {className: roomClasses,
onClick: this.handleClickEntry,
onMouseLeave: this._handleMouseOut,
onClick: this.props.isOpenedRoom ? null : this.handleClickEntry,
onMouseLeave: this.props.isOpenedRoom ? null : this._handleMouseOut,
ref: "roomEntry"},
React.createElement("h2", null,
roomTitle
@ -436,15 +441,17 @@ loop.panel = (function(_, mozL10n) {
React.createElement(RoomEntryContextItem, {
mozLoop: this.props.mozLoop,
roomUrls: this.props.room.decryptedContext.urls}),
React.createElement(RoomEntryContextButtons, {
dispatcher: this.props.dispatcher,
eventPosY: this.state.eventPosY,
handleClickEntry: this.handleClickEntry,
handleContextChevronClick: this.handleContextChevronClick,
ref: "contextActions",
room: this.props.room,
showMenu: this.state.showMenu,
toggleDropdownMenu: this.toggleDropdownMenu})
this.props.isOpenedRoom ? null :
React.createElement(RoomEntryContextButtons, {
dispatcher: this.props.dispatcher,
eventPosY: this.state.eventPosY,
handleClickEntry: this.handleClickEntry,
handleContextChevronClick: this.handleContextChevronClick,
ref: "contextActions",
room: this.props.room,
showMenu: this.state.showMenu,
toggleDropdownMenu: this.toggleDropdownMenu})
)
);
}
@ -719,12 +726,20 @@ loop.panel = (function(_, mozL10n) {
return (
React.createElement("div", {className: "rooms"},
this._renderNewRoomButton(),
React.createElement("h1", null, mozL10n.get("rooms_list_recent_conversations")),
React.createElement("h1", null, mozL10n.get(this.state.openedRoom === null ?
"rooms_list_recently_browsed" :
"rooms_list_currently_browsing")),
React.createElement("div", {className: "room-list"},
this.state.rooms.map(function(room, i) {
if (this.state.openedRoom !== null &&
room.roomToken !== this.state.openedRoom) {
return null;
}
return (
React.createElement(RoomEntry, {
dispatcher: this.props.dispatcher,
isOpenedRoom: room.roomToken === this.state.openedRoom,
key: room.roomToken,
mozLoop: this.props.mozLoop,
room: room})
@ -927,8 +942,8 @@ loop.panel = (function(_, mozL10n) {
clearOnDocumentHidden: true,
notifications: this.props.notifications}),
React.createElement(RoomList, {dispatcher: this.props.dispatcher,
mozLoop: this.props.mozLoop,
store: this.props.roomStore}),
mozLoop: this.props.mozLoop,
store: this.props.roomStore}),
React.createElement("div", {className: "footer"},
React.createElement("div", {className: "user-details"},
React.createElement(AccountLink, {fxAEnabled: this.props.mozLoop.fxAEnabled,

View File

@ -362,10 +362,14 @@ loop.panel = (function(_, mozL10n) {
/**
* Room list entry.
*
* Active Room means there are participants in the room.
* Opened Room means the user is in the room.
*/
var RoomEntry = React.createClass({
propTypes: {
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
isOpenedRoom: React.PropTypes.bool.isRequired,
mozLoop: React.PropTypes.object.isRequired,
room: React.PropTypes.instanceOf(loop.store.Room).isRequired
},
@ -418,7 +422,8 @@ loop.panel = (function(_, mozL10n) {
render: function() {
var roomClasses = React.addons.classSet({
"room-entry": true,
"room-active": this._isActive()
"room-active": this._isActive(),
"room-opened": this.props.isOpenedRoom
});
var roomTitle = this.props.room.decryptedContext.roomName ||
@ -427,8 +432,8 @@ loop.panel = (function(_, mozL10n) {
return (
<div className={roomClasses}
onClick={this.handleClickEntry}
onMouseLeave={this._handleMouseOut}
onClick={this.props.isOpenedRoom ? null : this.handleClickEntry}
onMouseLeave={this.props.isOpenedRoom ? null : this._handleMouseOut}
ref="roomEntry">
<h2>
{roomTitle}
@ -436,15 +441,17 @@ loop.panel = (function(_, mozL10n) {
<RoomEntryContextItem
mozLoop={this.props.mozLoop}
roomUrls={this.props.room.decryptedContext.urls} />
<RoomEntryContextButtons
dispatcher={this.props.dispatcher}
eventPosY={this.state.eventPosY}
handleClickEntry={this.handleClickEntry}
handleContextChevronClick={this.handleContextChevronClick}
ref="contextActions"
room={this.props.room}
showMenu={this.state.showMenu}
toggleDropdownMenu={this.toggleDropdownMenu} />
{this.props.isOpenedRoom ? null :
<RoomEntryContextButtons
dispatcher={this.props.dispatcher}
eventPosY={this.state.eventPosY}
handleClickEntry={this.handleClickEntry}
handleContextChevronClick={this.handleContextChevronClick}
ref="contextActions"
room={this.props.room}
showMenu={this.state.showMenu}
toggleDropdownMenu={this.toggleDropdownMenu} />
}
</div>
);
}
@ -719,12 +726,20 @@ loop.panel = (function(_, mozL10n) {
return (
<div className="rooms">
{this._renderNewRoomButton()}
<h1>{mozL10n.get("rooms_list_recent_conversations")}</h1>
<h1>{mozL10n.get(this.state.openedRoom === null ?
"rooms_list_recently_browsed" :
"rooms_list_currently_browsing")}</h1>
<div className="room-list">{
this.state.rooms.map(function(room, i) {
if (this.state.openedRoom !== null &&
room.roomToken !== this.state.openedRoom) {
return null;
}
return (
<RoomEntry
dispatcher={this.props.dispatcher}
isOpenedRoom={room.roomToken === this.state.openedRoom}
key={room.roomToken}
mozLoop={this.props.mozLoop}
room={room} />
@ -927,8 +942,8 @@ loop.panel = (function(_, mozL10n) {
clearOnDocumentHidden={true}
notifications={this.props.notifications} />
<RoomList dispatcher={this.props.dispatcher}
mozLoop={this.props.mozLoop}
store={this.props.roomStore} />
mozLoop={this.props.mozLoop}
store={this.props.roomStore} />
<div className="footer">
<div className="user-details">
<AccountLink fxAEnabled={this.props.mozLoop.fxAEnabled}

View File

@ -13,6 +13,7 @@ describe("loop.panel", function() {
var sandbox, notifications;
var fakeXHR, fakeWindow, fakeMozLoop, fakeEvent;
var requests = [];
var roomData, roomData2, roomList, roomName;
var mozL10nGetSpy;
beforeEach(function() {
@ -73,6 +74,45 @@ describe("loop.panel", function() {
userProfile: null
};
roomName = "First Room Name";
roomData = {
roomToken: "QzBbvGmIZWU",
roomUrl: "http://sample/QzBbvGmIZWU",
decryptedContext: {
roomName: roomName
},
maxSize: 2,
participants: [{
displayName: "Alexis",
account: "alexis@example.com",
roomConnectionId: "2a1787a6-4a73-43b5-ae3e-906ec1e763cb"
}, {
displayName: "Adam",
roomConnectionId: "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7"
}],
ctime: 1405517418
};
roomData2 = {
roomToken: "QzBbvlmIZWU",
roomUrl: "http://sample/QzBbvlmIZWU",
decryptedContext: {
roomName: "Second Room Name"
},
maxSize: 2,
participants: [{
displayName: "Bill",
account: "bill@example.com",
roomConnectionId: "2a1737a6-4a73-43b5-ae3e-906ec1e763cb"
}, {
displayName: "Bob",
roomConnectionId: "781f212b-f1ea-4ce1-9105-7cfc36fb4ec7"
}],
ctime: 1405517417
};
roomList = [new loop.store.Room(roomData), new loop.store.Room(roomData2)];
document.mozL10n.initialize(navigator.mozLoop);
sandbox.stub(document.mozL10n, "get").returns("Fake title");
});
@ -538,25 +578,10 @@ describe("loop.panel", function() {
});
describe("loop.panel.RoomEntry", function() {
var dispatcher, roomData;
var dispatcher;
beforeEach(function() {
dispatcher = new loop.Dispatcher();
roomData = {
roomToken: "QzBbvGmIZWU",
roomUrl: "http://sample/QzBbvGmIZWU",
decryptedContext: {
roomName: "Second Room Name"
},
maxSize: 2,
participants: [
{ displayName: "Alexis", account: "alexis@example.com",
roomConnectionId: "2a1787a6-4a73-43b5-ae3e-906ec1e763cb" },
{ displayName: "Adam",
roomConnectionId: "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7" }
],
ctime: 1405517418
};
});
function mountRoomEntry(props) {
@ -576,7 +601,10 @@ describe("loop.panel", function() {
// the actions we are triggering.
sandbox.stub(dispatcher, "dispatch");
view = mountRoomEntry({ room: new loop.store.Room(roomData) });
view = mountRoomEntry({
isOpenedRoom: false,
room: new loop.store.Room(roomData)
});
});
// XXX Current version of React cannot use TestUtils.Simulate, please
@ -618,6 +646,7 @@ describe("loop.panel", function() {
roomEntry = mountRoomEntry({
deleteRoom: sandbox.stub(),
isOpenedRoom: false,
room: new loop.store.Room(roomData)
});
});
@ -648,6 +677,18 @@ describe("loop.panel", function() {
sinon.assert.calledOnce(fakeWindow.close);
});
it("should not dispatch an OpenRoom action when button is clicked if room is already opened", function() {
roomEntry = mountRoomEntry({
deleteRoom: sandbox.stub(),
isOpenedRoom: true,
room: new loop.store.Room(roomData)
});
TestUtils.Simulate.click(roomEntry.refs.roomEntry.getDOMNode());
sinon.assert.notCalled(dispatcher.dispatch);
});
});
});
@ -656,6 +697,7 @@ describe("loop.panel", function() {
function mountEntryForContext() {
return mountRoomEntry({
isOpenedRoom: false,
room: new loop.store.Room(roomData)
});
}
@ -716,6 +758,7 @@ describe("loop.panel", function() {
roomEntry = mountRoomEntry({
dispatcher: dispatcher,
isOpenedRoom: false,
room: new loop.store.Room(roomData)
});
roomEntryNode = roomEntry.getDOMNode();
@ -727,6 +770,7 @@ describe("loop.panel", function() {
it("should update room name", function() {
var roomEntry = mountRoomEntry({
dispatcher: dispatcher,
isOpenedRoom: false,
room: new loop.store.Room(roomData)
});
var updatedRoom = new loop.store.Room(_.extend({}, roomData, {
@ -808,7 +852,7 @@ describe("loop.panel", function() {
});
describe("loop.panel.RoomList", function() {
var roomStore, dispatcher, fakeEmail, dispatch, roomData;
var roomStore, dispatcher, fakeEmail, dispatch;
beforeEach(function() {
fakeEmail = "fakeEmail@example.com";
@ -817,6 +861,7 @@ describe("loop.panel", function() {
mozLoop: navigator.mozLoop
});
roomStore.setStoreState({
openedRoom: null,
pendingCreation: false,
pendingInitialRetrieval: false,
rooms: [],
@ -824,24 +869,6 @@ describe("loop.panel", function() {
});
dispatch = sandbox.stub(dispatcher, "dispatch");
roomData = {
roomToken: "QzBbvGmIZWU",
roomUrl: "http://sample/QzBbvGmIZWU",
decryptedContext: {
roomName: "Second Room Name"
},
maxSize: 2,
participants: [{
displayName: "Alexis",
account: "alexis@example.com",
roomConnectionId: "2a1787a6-4a73-43b5-ae3e-906ec1e763cb"
}, {
displayName: "Adam",
roomConnectionId: "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7"
}],
ctime: 1405517418
};
});
function createTestComponent() {
@ -896,6 +923,27 @@ describe("loop.panel", function() {
expect(view.getDOMNode().querySelectorAll(".room-list-loading").length).to.eql(1);
});
it("should show multiple rooms in list with no opened room", function() {
roomStore.setStoreState({ rooms: roomList });
var view = createTestComponent();
var node = view.getDOMNode();
expect(node.querySelectorAll(".room-opened").length).to.eql(0);
expect(node.querySelectorAll(".room-entry").length).to.eql(2);
});
it("should only show the opened room you're in when you're in a room", function() {
roomStore.setStoreState({ rooms: roomList, openedRoom: roomList[0].roomToken });
var view = createTestComponent();
var node = view.getDOMNode();
expect(node.querySelectorAll(".room-opened").length).to.eql(1);
expect(node.querySelectorAll(".room-entry").length).to.eql(1);
expect(node.querySelectorAll(".room-opened h2")[0].textContent).to.equal(roomName);
});
});
describe("loop.panel.NewRoomView", function() {
@ -1074,7 +1122,7 @@ describe("loop.panel", function() {
});
describe("RoomEntryContextButtons", function() {
var view, dispatcher, roomData;
var view, dispatcher;
function createTestComponent(extraProps) {
var props = _.extend({
@ -1091,24 +1139,6 @@ describe("loop.panel", function() {
}
beforeEach(function() {
roomData = {
roomToken: "QzBbvGmIZWU",
roomUrl: "http://sample/QzBbvGmIZWU",
decryptedContext: {
roomName: "Second Room Name"
},
maxSize: 2,
participants: [{
displayName: "Alexis",
account: "alexis@example.com",
roomConnectionId: "2a1787a6-4a73-43b5-ae3e-906ec1e763cb"
}, {
displayName: "Adam",
roomConnectionId: "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7"
}],
ctime: 1405517418
};
dispatcher = new loop.Dispatcher();
sandbox.stub(dispatcher, "dispatch");

View File

@ -183,9 +183,12 @@ tour_label=Tour
## will be replaced by a number. For example "Conversation 1" or "Conversation 12".
rooms_default_room_name_template=Conversation {{conversationLabel}}
rooms_leave_button_label=Leave
## LOCALIZATION NOTE (rooms_list_recent_conversations): String is in all caps
## LOCALIZATION NOTE (rooms_list_recently_browsed): String is in all caps
## for emphasis reasons, it is a heading. Proceed as appropriate for locale.
rooms_list_recent_conversations=RECENT CONVERSATIONS
rooms_list_recently_browsed=RECENTLY BROWSED
## LOCALIZATION NOTE (rooms_list_currently_browsing): String is in all caps
## for emphasis reasons, it is a heading. Proceed as appropriate for locale.
rooms_list_currently_browsing=CURRENTLY BROWSING
rooms_change_failed_label=Conversation cannot be updated
rooms_panel_title=Choose a conversation or start a new one
rooms_room_full_label=There are already two people in this conversation.