зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1074694 - Allow rooms to be renamed from the conversation window. r=nperriault
This commit is contained in:
Родитель
8826f41cbe
Коммит
781163fac2
|
@ -376,6 +376,34 @@ let LoopRoomsInternal = {
|
|||
}, callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* Renames a room.
|
||||
*
|
||||
* @param {String} roomToken The room token
|
||||
* @param {String} newRoomName The new name for the room
|
||||
* @param {Function} callback Function that will be invoked once the operation
|
||||
* finished. The first argument passed will be an
|
||||
* `Error` object or `null`.
|
||||
*/
|
||||
rename: function(roomToken, newRoomName, callback) {
|
||||
let room = this.rooms.get(roomToken);
|
||||
let url = "/rooms/" + encodeURIComponent(roomToken);
|
||||
|
||||
let origRoom = this.rooms.get(roomToken);
|
||||
let patchData = {
|
||||
roomName: newRoomName,
|
||||
// XXX We have to supply the max size and room owner due to bug 1099063.
|
||||
maxSize: origRoom.maxSize,
|
||||
roomOwner: origRoom.roomOwner
|
||||
};
|
||||
MozLoopService.hawkRequest(this.sessionType, url, "PATCH", patchData)
|
||||
.then(response => {
|
||||
let data = JSON.parse(response.body);
|
||||
extend(room, data);
|
||||
callback(null, room);
|
||||
}, error => callback(error)).catch(error => callback(error));
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback used to indicate changes to rooms data on the LoopServer.
|
||||
*
|
||||
|
@ -443,6 +471,10 @@ this.LoopRooms = {
|
|||
return LoopRoomsInternal.leave(roomToken, sessionToken, callback);
|
||||
},
|
||||
|
||||
rename: function(roomToken, newRoomName, callback) {
|
||||
return LoopRoomsInternal.rename(roomToken, newRoomName, callback);
|
||||
},
|
||||
|
||||
promise: function(method, ...params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this[method](...params, (error, result) => {
|
||||
|
|
|
@ -59,7 +59,7 @@ loop.roomViews = (function(mozL10n) {
|
|||
* Desktop room invitation view (overlay).
|
||||
*/
|
||||
var DesktopRoomInvitationView = React.createClass({displayName: 'DesktopRoomInvitationView',
|
||||
mixins: [ActiveRoomStoreMixin],
|
||||
mixins: [ActiveRoomStoreMixin, React.addons.LinkedStateMixin],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
||||
|
@ -67,13 +67,23 @@ loop.roomViews = (function(mozL10n) {
|
|||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
copiedUrl: false
|
||||
copiedUrl: false,
|
||||
newRoomName: ""
|
||||
}
|
||||
},
|
||||
|
||||
handleFormSubmit: function(event) {
|
||||
event.preventDefault();
|
||||
// XXX
|
||||
|
||||
var newRoomName = this.state.newRoomName;
|
||||
|
||||
if (newRoomName && this.state.roomName != newRoomName) {
|
||||
this.props.dispatcher.dispatch(
|
||||
new sharedActions.RenameRoom({
|
||||
roomToken: this.state.roomToken,
|
||||
newRoomName: newRoomName
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
handleEmailButtonClick: function(event) {
|
||||
|
@ -96,7 +106,9 @@ loop.roomViews = (function(mozL10n) {
|
|||
return (
|
||||
React.DOM.div({className: "room-invitation-overlay"},
|
||||
React.DOM.form({onSubmit: this.handleFormSubmit},
|
||||
React.DOM.input({type: "text", ref: "roomName",
|
||||
React.DOM.input({type: "text", className: "input-room-name",
|
||||
valueLink: this.linkState("newRoomName"),
|
||||
onBlur: this.handleFormSubmit,
|
||||
placeholder: mozL10n.get("rooms_name_this_room_label")})
|
||||
),
|
||||
React.DOM.p(null, mozL10n.get("invite_header_text")),
|
||||
|
|
|
@ -59,7 +59,7 @@ loop.roomViews = (function(mozL10n) {
|
|||
* Desktop room invitation view (overlay).
|
||||
*/
|
||||
var DesktopRoomInvitationView = React.createClass({
|
||||
mixins: [ActiveRoomStoreMixin],
|
||||
mixins: [ActiveRoomStoreMixin, React.addons.LinkedStateMixin],
|
||||
|
||||
propTypes: {
|
||||
dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
|
||||
|
@ -67,13 +67,23 @@ loop.roomViews = (function(mozL10n) {
|
|||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
copiedUrl: false
|
||||
copiedUrl: false,
|
||||
newRoomName: ""
|
||||
}
|
||||
},
|
||||
|
||||
handleFormSubmit: function(event) {
|
||||
event.preventDefault();
|
||||
// XXX
|
||||
|
||||
var newRoomName = this.state.newRoomName;
|
||||
|
||||
if (newRoomName && this.state.roomName != newRoomName) {
|
||||
this.props.dispatcher.dispatch(
|
||||
new sharedActions.RenameRoom({
|
||||
roomToken: this.state.roomToken,
|
||||
newRoomName: newRoomName
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
handleEmailButtonClick: function(event) {
|
||||
|
@ -96,7 +106,9 @@ loop.roomViews = (function(mozL10n) {
|
|||
return (
|
||||
<div className="room-invitation-overlay">
|
||||
<form onSubmit={this.handleFormSubmit}>
|
||||
<input type="text" ref="roomName"
|
||||
<input type="text" className="input-room-name"
|
||||
valueLink={this.linkState("newRoomName")}
|
||||
onBlur={this.handleFormSubmit}
|
||||
placeholder={mozL10n.get("rooms_name_this_room_label")} />
|
||||
</form>
|
||||
<p>{mozL10n.get("invite_header_text")}</p>
|
||||
|
|
|
@ -242,6 +242,15 @@ loop.shared.actions = (function() {
|
|||
roomToken: String
|
||||
}),
|
||||
|
||||
/**
|
||||
* Renames a room.
|
||||
* XXX: should move to some roomActions module - refs bug 1079284
|
||||
*/
|
||||
RenameRoom: Action.define("renameRoom", {
|
||||
roomToken: String,
|
||||
newRoomName: String
|
||||
}),
|
||||
|
||||
/**
|
||||
* Copy a room url into the user's clipboard.
|
||||
* XXX: should move to some roomActions module - refs bug 1079284
|
||||
|
@ -265,6 +274,19 @@ loop.shared.actions = (function() {
|
|||
error: Object
|
||||
}),
|
||||
|
||||
/**
|
||||
* Sets up the room information when it is received.
|
||||
* XXX: should move to some roomActions module - refs bug 1079284
|
||||
*
|
||||
* @see https://wiki.mozilla.org/Loop/Architecture/Rooms#GET_.2Frooms.2F.7Btoken.7D
|
||||
*/
|
||||
SetupRoomInfo: Action.define("setupRoomInfo", {
|
||||
roomName: String,
|
||||
roomOwner: String,
|
||||
roomToken: String,
|
||||
roomUrl: String
|
||||
}),
|
||||
|
||||
/**
|
||||
* Updates the room information when it is received.
|
||||
* XXX: should move to some roomActions module - refs bug 1079284
|
||||
|
@ -274,7 +296,6 @@ loop.shared.actions = (function() {
|
|||
UpdateRoomInfo: Action.define("updateRoomInfo", {
|
||||
roomName: String,
|
||||
roomOwner: String,
|
||||
roomToken: String,
|
||||
roomUrl: String
|
||||
}),
|
||||
|
||||
|
|
|
@ -150,6 +150,7 @@ loop.store.ActiveRoomStore = (function() {
|
|||
_registerActions: function() {
|
||||
this._dispatcher.register(this, [
|
||||
"roomFailure",
|
||||
"setupRoomInfo",
|
||||
"updateRoomInfo",
|
||||
"joinRoom",
|
||||
"joinedRoom",
|
||||
|
@ -194,7 +195,7 @@ loop.store.ActiveRoomStore = (function() {
|
|||
}
|
||||
|
||||
this._dispatcher.dispatch(
|
||||
new sharedActions.UpdateRoomInfo({
|
||||
new sharedActions.SetupRoomInfo({
|
||||
roomToken: actionData.roomToken,
|
||||
roomName: roomData.roomName,
|
||||
roomOwner: roomData.roomOwner,
|
||||
|
@ -227,15 +228,18 @@ loop.store.ActiveRoomStore = (function() {
|
|||
roomToken: actionData.token,
|
||||
roomState: ROOM_STATES.READY
|
||||
});
|
||||
|
||||
this._mozLoop.rooms.on("update:" + actionData.roomToken,
|
||||
this._handleRoomUpdate.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the updateRoomInfo action. Updates the room data and
|
||||
* Handles the setupRoomInfo action. Sets up the initial room data and
|
||||
* sets the state to `READY`.
|
||||
*
|
||||
* @param {sharedActions.UpdateRoomInfo} actionData
|
||||
* @param {sharedActions.SetupRoomInfo} actionData
|
||||
*/
|
||||
updateRoomInfo: function(actionData) {
|
||||
setupRoomInfo: function(actionData) {
|
||||
this.setStoreState({
|
||||
roomName: actionData.roomName,
|
||||
roomOwner: actionData.roomOwner,
|
||||
|
@ -243,6 +247,36 @@ loop.store.ActiveRoomStore = (function() {
|
|||
roomToken: actionData.roomToken,
|
||||
roomUrl: actionData.roomUrl
|
||||
});
|
||||
|
||||
this._mozLoop.rooms.on("update:" + actionData.roomToken,
|
||||
this._handleRoomUpdate.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the updateRoomInfo action. Updates the room data.
|
||||
*
|
||||
* @param {sharedActions.UpdateRoomInfo} actionData
|
||||
*/
|
||||
updateRoomInfo: function(actionData) {
|
||||
this.setStoreState({
|
||||
roomName: actionData.roomName,
|
||||
roomOwner: actionData.roomOwner,
|
||||
roomUrl: actionData.roomUrl
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles room updates notified by the mozLoop rooms API.
|
||||
*
|
||||
* @param {String} eventName The name of the event
|
||||
* @param {Object} roomData The new roomData.
|
||||
*/
|
||||
_handleRoomUpdate: function(eventName, roomData) {
|
||||
this._dispatcher.dispatch(new sharedActions.UpdateRoomInfo({
|
||||
roomName: roomData.roomName,
|
||||
roomOwner: roomData.roomOwner,
|
||||
roomUrl: roomData.roomUrl
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -351,6 +385,10 @@ loop.store.ActiveRoomStore = (function() {
|
|||
*/
|
||||
windowUnload: function() {
|
||||
this._leaveRoom();
|
||||
|
||||
// If we're closing the window, we can stop listening to updates.
|
||||
this._mozLoop.rooms.off("update:" + this.getStoreState().roomToken,
|
||||
this._handleRoomUpdate.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -87,6 +87,7 @@ loop.store = loop.store || {};
|
|||
"getAllRooms",
|
||||
"getAllRoomsError",
|
||||
"openRoom",
|
||||
"renameRoom",
|
||||
"updateRoomList"
|
||||
]);
|
||||
}
|
||||
|
@ -411,6 +412,21 @@ loop.store = loop.store || {};
|
|||
*/
|
||||
openRoom: function(actionData) {
|
||||
this._mozLoop.rooms.open(actionData.roomToken);
|
||||
},
|
||||
|
||||
/**
|
||||
* Renames a room.
|
||||
*
|
||||
* @param {sharedActions.RenameRoom} actionData
|
||||
*/
|
||||
renameRoom: function(actionData) {
|
||||
this._mozLoop.rooms.rename(actionData.roomToken, actionData.newRoomName,
|
||||
function(err) {
|
||||
if (err) {
|
||||
// XXX Give this a proper UI - bug 1100595.
|
||||
console.error("Failed to rename the room", err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, Backbone.Events);
|
||||
|
||||
|
|
|
@ -170,7 +170,13 @@ loop.StandaloneMozLoop = (function(mozL10n) {
|
|||
action: "leave",
|
||||
sessionToken: sessionToken
|
||||
}, null, callback);
|
||||
}
|
||||
},
|
||||
|
||||
// Dummy functions to reflect those in the desktop mozLoop.rooms that we
|
||||
// don't currently use.
|
||||
on: function() {},
|
||||
once: function() {},
|
||||
off: function() {}
|
||||
};
|
||||
|
||||
var StandaloneMozLoop = function(options) {
|
||||
|
|
|
@ -119,6 +119,48 @@ describe("loop.roomViews", function () {
|
|||
new sharedActions.EmailRoomUrl({roomUrl: "http://invalid"}));
|
||||
});
|
||||
|
||||
describe("Rename Room", function() {
|
||||
var roomNameBox;
|
||||
|
||||
beforeEach(function() {
|
||||
view = mountTestComponent();
|
||||
view.setState({
|
||||
roomToken: "fakeToken",
|
||||
roomName: "fakeName"
|
||||
});
|
||||
|
||||
roomNameBox = view.getDOMNode().querySelector('.input-room-name');
|
||||
|
||||
React.addons.TestUtils.Simulate.change(roomNameBox, { target: {
|
||||
value: "reallyFake"
|
||||
}});
|
||||
});
|
||||
|
||||
it("should dispatch a RenameRoom action when the focus is lost",
|
||||
function() {
|
||||
React.addons.TestUtils.Simulate.blur(roomNameBox);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.RenameRoom({
|
||||
roomToken: "fakeToken",
|
||||
newRoomName: "reallyFake"
|
||||
}));
|
||||
});
|
||||
|
||||
it("should dispatch a RenameRoom action when enter is pressed",
|
||||
function() {
|
||||
React.addons.TestUtils.Simulate.submit(roomNameBox);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.RenameRoom({
|
||||
roomToken: "fakeToken",
|
||||
newRoomName: "reallyFake"
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("Copy Button", function() {
|
||||
beforeEach(function() {
|
||||
view = mountTestComponent();
|
||||
|
|
|
@ -21,10 +21,12 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||
|
||||
fakeMozLoop = {
|
||||
rooms: {
|
||||
get: sandbox.stub(),
|
||||
join: sandbox.stub(),
|
||||
refreshMembership: sandbox.stub(),
|
||||
leave: sandbox.stub()
|
||||
get: sinon.stub(),
|
||||
join: sinon.stub(),
|
||||
refreshMembership: sinon.stub(),
|
||||
leave: sinon.stub(),
|
||||
on: sinon.stub(),
|
||||
off: sinon.stub()
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -161,7 +163,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||
to.have.property('roomState', ROOM_STATES.GATHER);
|
||||
});
|
||||
|
||||
it("should dispatch an UpdateRoomInfo action if the get is successful",
|
||||
it("should dispatch an SetupRoomInfo action if the get is successful",
|
||||
function() {
|
||||
store.setupWindowData(new sharedActions.SetupWindowData({
|
||||
windowId: "42",
|
||||
|
@ -171,7 +173,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||
|
||||
sinon.assert.calledTwice(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.UpdateRoomInfo(_.extend({
|
||||
new sharedActions.SetupRoomInfo(_.extend({
|
||||
roomToken: fakeToken
|
||||
}, fakeRoomData)));
|
||||
});
|
||||
|
@ -233,7 +235,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||
});
|
||||
});
|
||||
|
||||
describe("#updateRoomInfo", function() {
|
||||
describe("#setupRoomInfo", function() {
|
||||
var fakeRoomInfo;
|
||||
|
||||
beforeEach(function() {
|
||||
|
@ -246,18 +248,39 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||
});
|
||||
|
||||
it("should set the state to READY", function() {
|
||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo(fakeRoomInfo));
|
||||
store.setupRoomInfo(new sharedActions.SetupRoomInfo(fakeRoomInfo));
|
||||
|
||||
expect(store._storeState.roomState).eql(ROOM_STATES.READY);
|
||||
});
|
||||
|
||||
it("should save the room information", function() {
|
||||
store.setupRoomInfo(new sharedActions.SetupRoomInfo(fakeRoomInfo));
|
||||
|
||||
var state = store.getStoreState();
|
||||
expect(state.roomName).eql(fakeRoomInfo.roomName);
|
||||
expect(state.roomOwner).eql(fakeRoomInfo.roomOwner);
|
||||
expect(state.roomToken).eql(fakeRoomInfo.roomToken);
|
||||
expect(state.roomUrl).eql(fakeRoomInfo.roomUrl);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#updateRoomInfo", function() {
|
||||
var fakeRoomInfo;
|
||||
|
||||
beforeEach(function() {
|
||||
fakeRoomInfo = {
|
||||
roomName: "Its a room",
|
||||
roomOwner: "Me",
|
||||
roomUrl: "http://invalid"
|
||||
};
|
||||
});
|
||||
|
||||
it("should save the room information", function() {
|
||||
store.updateRoomInfo(new sharedActions.UpdateRoomInfo(fakeRoomInfo));
|
||||
|
||||
var state = store.getStoreState();
|
||||
expect(state.roomName).eql(fakeRoomInfo.roomName);
|
||||
expect(state.roomOwner).eql(fakeRoomInfo.roomOwner);
|
||||
expect(state.roomToken).eql(fakeRoomInfo.roomToken);
|
||||
expect(state.roomUrl).eql(fakeRoomInfo.roomUrl);
|
||||
});
|
||||
});
|
||||
|
@ -596,4 +619,33 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||
expect(store._storeState.roomState).eql(ROOM_STATES.READY);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Events", function() {
|
||||
describe("update:{roomToken}", function() {
|
||||
beforeEach(function() {
|
||||
store.setupRoomInfo(new sharedActions.SetupRoomInfo({
|
||||
roomName: "Its a room",
|
||||
roomOwner: "Me",
|
||||
roomToken: "fakeToken",
|
||||
roomUrl: "http://invalid"
|
||||
}));
|
||||
});
|
||||
|
||||
it("should dispatch an UpdateRoomInfo action", function() {
|
||||
sinon.assert.calledOnce(fakeMozLoop.rooms.on);
|
||||
|
||||
var fakeRoomData = {
|
||||
roomName: "fakeName",
|
||||
roomOwner: "you",
|
||||
roomUrl: "original"
|
||||
};
|
||||
|
||||
fakeMozLoop.rooms.on.callArgWith(1, "update", fakeRoomData);
|
||||
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.UpdateRoomInfo(fakeRoomData));
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -437,4 +437,31 @@ describe("loop.store.RoomStore", function () {
|
|||
sinon.assert.calledWithExactly(fakeMozLoop.rooms.open, "42abc");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#renameRoom", function() {
|
||||
var store, fakeMozLoop;
|
||||
|
||||
beforeEach(function() {
|
||||
fakeMozLoop = {
|
||||
rooms: {
|
||||
rename: sinon.spy()
|
||||
}
|
||||
};
|
||||
store = new loop.store.RoomStore({
|
||||
dispatcher: dispatcher,
|
||||
mozLoop: fakeMozLoop
|
||||
});
|
||||
});
|
||||
|
||||
it("should rename the room via mozLoop", function() {
|
||||
dispatcher.dispatch(new sharedActions.RenameRoom({
|
||||
roomToken: "42abc",
|
||||
newRoomName: "silly name"
|
||||
}));
|
||||
|
||||
sinon.assert.calledOnce(fakeMozLoop.rooms.rename);
|
||||
sinon.assert.calledWith(fakeMozLoop.rooms.rename, "42abc",
|
||||
"silly name");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -209,16 +209,22 @@ add_task(function* setup_server() {
|
|||
res.finish();
|
||||
}
|
||||
|
||||
function getJSONData(body) {
|
||||
return JSON.parse(CommonUtils.readBytesFromInputStream(body));
|
||||
}
|
||||
|
||||
// Add a request handler for each room in the list.
|
||||
[...kRooms.values()].forEach(function(room) {
|
||||
loopServer.registerPathHandler("/rooms/" + encodeURIComponent(room.roomToken), (req, res) => {
|
||||
if (req.method == "POST") {
|
||||
let body = CommonUtils.readBytesFromInputStream(req.bodyInputStream);
|
||||
let data = JSON.parse(body);
|
||||
let data = getJSONData(req.bodyInputStream);
|
||||
res.setStatusLine(null, 200, "OK");
|
||||
res.write(JSON.stringify(data));
|
||||
res.processAsync();
|
||||
res.finish();
|
||||
} else if (req.method == "PATCH") {
|
||||
let data = getJSONData(req.bodyInputStream);
|
||||
returnRoomDetails(res, data.roomName);
|
||||
} else {
|
||||
returnRoomDetails(res, room.roomName);
|
||||
}
|
||||
|
@ -363,6 +369,13 @@ add_task(function* test_leaveRoom() {
|
|||
Assert.equal(leaveData.sessionToken, "fakeLeaveSessionToken");
|
||||
});
|
||||
|
||||
// Test if renaming a room works as expected.
|
||||
add_task(function* test_renameRoom() {
|
||||
let roomToken = "_nxD4V4FflQ";
|
||||
let renameData = yield LoopRooms.promise("rename", roomToken, "fakeName");
|
||||
Assert.equal(renameData.roomName, "fakeName");
|
||||
});
|
||||
|
||||
// Test if the event emitter implementation doesn't leak and is working as expected.
|
||||
add_task(function* () {
|
||||
Assert.strictEqual(gExpectedAdds.length, 0, "No room additions should be expected anymore");
|
||||
|
|
Загрузка…
Ссылка в новой задаче