зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1102170 - Share a room url by email when Loop direct call fails. r=Standard8
This commit is contained in:
Родитель
437abd21ee
Коммит
db9bde32fb
|
@ -27,6 +27,13 @@ loop.conversationViews = (function(mozL10n) {
|
||||||
return contact.email.find(e => e.pref) || contact.email[0];
|
return contact.email.find(e => e.pref) || contact.email[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _getContactDisplayName(contact) {
|
||||||
|
if (contact.name && contact.name[0]) {
|
||||||
|
return contact.name[0];
|
||||||
|
}
|
||||||
|
return _getPreferredEmail(contact).value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays information about the call
|
* Displays information about the call
|
||||||
* Caller avatar, name & conversation creation date
|
* Caller avatar, name & conversation creation date
|
||||||
|
@ -107,14 +114,7 @@ loop.conversationViews = (function(mozL10n) {
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var contactName;
|
var contactName = _getContactDisplayName(this.props.contact);
|
||||||
|
|
||||||
if (this.props.contact.name &&
|
|
||||||
this.props.contact.name[0]) {
|
|
||||||
contactName = this.props.contact.name[0];
|
|
||||||
} else {
|
|
||||||
contactName = _getPreferredEmail(this.props.contact).value;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.title = contactName;
|
document.title = contactName;
|
||||||
|
|
||||||
|
@ -262,7 +262,10 @@ loop.conversationViews = (function(mozL10n) {
|
||||||
emailLinkButtonDisabled: true
|
emailLinkButtonDisabled: true
|
||||||
});
|
});
|
||||||
|
|
||||||
this.props.dispatcher.dispatch(new sharedActions.FetchEmailLink());
|
this.props.dispatcher.dispatch(new sharedActions.FetchRoomEmailLink({
|
||||||
|
roomOwner: navigator.mozLoop.userProfile.email,
|
||||||
|
roomName: _getContactDisplayName(this.props.contact)
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
|
|
@ -27,6 +27,13 @@ loop.conversationViews = (function(mozL10n) {
|
||||||
return contact.email.find(e => e.pref) || contact.email[0];
|
return contact.email.find(e => e.pref) || contact.email[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _getContactDisplayName(contact) {
|
||||||
|
if (contact.name && contact.name[0]) {
|
||||||
|
return contact.name[0];
|
||||||
|
}
|
||||||
|
return _getPreferredEmail(contact).value;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Displays information about the call
|
* Displays information about the call
|
||||||
* Caller avatar, name & conversation creation date
|
* Caller avatar, name & conversation creation date
|
||||||
|
@ -107,14 +114,7 @@ loop.conversationViews = (function(mozL10n) {
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
var contactName;
|
var contactName = _getContactDisplayName(this.props.contact);
|
||||||
|
|
||||||
if (this.props.contact.name &&
|
|
||||||
this.props.contact.name[0]) {
|
|
||||||
contactName = this.props.contact.name[0];
|
|
||||||
} else {
|
|
||||||
contactName = _getPreferredEmail(this.props.contact).value;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.title = contactName;
|
document.title = contactName;
|
||||||
|
|
||||||
|
@ -262,7 +262,10 @@ loop.conversationViews = (function(mozL10n) {
|
||||||
emailLinkButtonDisabled: true
|
emailLinkButtonDisabled: true
|
||||||
});
|
});
|
||||||
|
|
||||||
this.props.dispatcher.dispatch(new sharedActions.FetchEmailLink());
|
this.props.dispatcher.dispatch(new sharedActions.FetchRoomEmailLink({
|
||||||
|
roomOwner: navigator.mozLoop.userProfile.email,
|
||||||
|
roomName: _getContactDisplayName(this.props.contact)
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
|
|
||||||
render: function() {
|
render: function() {
|
||||||
|
|
|
@ -76,10 +76,12 @@ loop.shared.actions = (function() {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch a new call url from the server, intended to be sent over email when
|
* Fetch a new room url from the server, intended to be sent over email when
|
||||||
* a contact can't be reached.
|
* a contact can't be reached.
|
||||||
*/
|
*/
|
||||||
FetchEmailLink: Action.define("fetchEmailLink", {
|
FetchRoomEmailLink: Action.define("fetchRoomEmailLink", {
|
||||||
|
roomOwner: String,
|
||||||
|
roomName: String
|
||||||
}),
|
}),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -210,7 +210,7 @@ loop.store = loop.store || {};
|
||||||
"retryCall",
|
"retryCall",
|
||||||
"mediaConnected",
|
"mediaConnected",
|
||||||
"setMute",
|
"setMute",
|
||||||
"fetchEmailLink"
|
"fetchRoomEmailLink"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
this.setStoreState({
|
this.setStoreState({
|
||||||
|
@ -323,18 +323,21 @@ loop.store = loop.store || {};
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches a new call URL intended to be sent over email when a contact
|
* Fetches a new room URL intended to be sent over email when a contact
|
||||||
* can't be reached.
|
* can't be reached.
|
||||||
*/
|
*/
|
||||||
fetchEmailLink: function() {
|
fetchRoomEmailLink: function(actionData) {
|
||||||
// XXX This is an empty string as a conversation identifier. Bug 1015938 implements
|
this.mozLoop.rooms.create({
|
||||||
// a user-set string.
|
roomName: actionData.roomName,
|
||||||
this.client.requestCallUrl("", function(err, callUrlData) {
|
roomOwner: actionData.roomOwner,
|
||||||
|
maxSize: loop.store.MAX_ROOM_CREATION_SIZE,
|
||||||
|
expiresIn: loop.store.DEFAULT_EXPIRES_IN
|
||||||
|
}, function(err, createdRoomData) {
|
||||||
if (err) {
|
if (err) {
|
||||||
this.trigger("error:emailLink");
|
this.trigger("error:emailLink");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.setStoreState({"emailLink": callUrlData.callUrl});
|
this.setStoreState({"emailLink": createdRoomData.roomUrl});
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,20 @@ loop.store = loop.store || {};
|
||||||
*/
|
*/
|
||||||
var sharedActions = loop.shared.actions;
|
var sharedActions = loop.shared.actions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum size given to createRoom; only 2 is supported (and is
|
||||||
|
* always passed) because that's what the user-experience is currently
|
||||||
|
* designed and tested to handle.
|
||||||
|
* @type {Number}
|
||||||
|
*/
|
||||||
|
var MAX_ROOM_CREATION_SIZE = loop.store.MAX_ROOM_CREATION_SIZE = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of hours for which the room will exist - default 8 weeks
|
||||||
|
* @type {Number}
|
||||||
|
*/
|
||||||
|
var DEFAULT_EXPIRES_IN = loop.store.DEFAULT_EXPIRES_IN = 24 * 7 * 8;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Room validation schema. See validate.js.
|
* Room validation schema. See validate.js.
|
||||||
* @type {Object}
|
* @type {Object}
|
||||||
|
@ -61,13 +75,13 @@ loop.store = loop.store || {};
|
||||||
* designed and tested to handle.
|
* designed and tested to handle.
|
||||||
* @type {Number}
|
* @type {Number}
|
||||||
*/
|
*/
|
||||||
maxRoomCreationSize: 2,
|
maxRoomCreationSize: MAX_ROOM_CREATION_SIZE,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of hours for which the room will exist - default 8 weeks
|
* The number of hours for which the room will exist - default 8 weeks
|
||||||
* @type {Number}
|
* @type {Number}
|
||||||
*/
|
*/
|
||||||
defaultExpiresIn: 24 * 7 * 8,
|
defaultExpiresIn: DEFAULT_EXPIRES_IN,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registered actions.
|
* Registered actions.
|
||||||
|
|
|
@ -56,7 +56,10 @@ describe("loop.conversationViews", function () {
|
||||||
},
|
},
|
||||||
getAudioBlob: sinon.spy(function(name, callback) {
|
getAudioBlob: sinon.spy(function(name, callback) {
|
||||||
callback(null, new Blob([new ArrayBuffer(10)], {type: "audio/ogg"}));
|
callback(null, new Blob([new ArrayBuffer(10)], {type: "audio/ogg"}));
|
||||||
})
|
}),
|
||||||
|
userProfile: {
|
||||||
|
email: "bob@invalid.tld"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fakeWindow = {
|
fakeWindow = {
|
||||||
|
@ -241,12 +244,15 @@ describe("loop.conversationViews", function () {
|
||||||
describe("CallFailedView", function() {
|
describe("CallFailedView", function() {
|
||||||
var store, fakeAudio;
|
var store, fakeAudio;
|
||||||
|
|
||||||
function mountTestComponent(props) {
|
var contact = {email: [{value: "test@test.tld"}]};
|
||||||
|
|
||||||
|
function mountTestComponent(options) {
|
||||||
|
options = options || {};
|
||||||
return TestUtils.renderIntoDocument(
|
return TestUtils.renderIntoDocument(
|
||||||
loop.conversationViews.CallFailedView({
|
loop.conversationViews.CallFailedView({
|
||||||
dispatcher: dispatcher,
|
dispatcher: dispatcher,
|
||||||
store: store,
|
store: store,
|
||||||
contact: {email: [{value: "test@test.tld"}]}
|
contact: options.contact
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +272,7 @@ describe("loop.conversationViews", function () {
|
||||||
|
|
||||||
it("should dispatch a retryCall action when the retry button is pressed",
|
it("should dispatch a retryCall action when the retry button is pressed",
|
||||||
function() {
|
function() {
|
||||||
view = mountTestComponent();
|
view = mountTestComponent({contact: contact});
|
||||||
|
|
||||||
var retryBtn = view.getDOMNode().querySelector('.btn-retry');
|
var retryBtn = view.getDOMNode().querySelector('.btn-retry');
|
||||||
|
|
||||||
|
@ -279,7 +285,7 @@ describe("loop.conversationViews", function () {
|
||||||
|
|
||||||
it("should dispatch a cancelCall action when the cancel button is pressed",
|
it("should dispatch a cancelCall action when the cancel button is pressed",
|
||||||
function() {
|
function() {
|
||||||
view = mountTestComponent();
|
view = mountTestComponent({contact: contact});
|
||||||
|
|
||||||
var cancelBtn = view.getDOMNode().querySelector('.btn-cancel');
|
var cancelBtn = view.getDOMNode().querySelector('.btn-cancel');
|
||||||
|
|
||||||
|
@ -290,9 +296,9 @@ describe("loop.conversationViews", function () {
|
||||||
sinon.match.hasOwn("name", "cancelCall"));
|
sinon.match.hasOwn("name", "cancelCall"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should dispatch a fetchEmailLink action when the cancel button is pressed",
|
it("should dispatch a fetchRoomEmailLink action when the email button is pressed",
|
||||||
function() {
|
function() {
|
||||||
view = mountTestComponent();
|
view = mountTestComponent({contact: contact});
|
||||||
|
|
||||||
var emailLinkBtn = view.getDOMNode().querySelector('.btn-email');
|
var emailLinkBtn = view.getDOMNode().querySelector('.btn-email');
|
||||||
|
|
||||||
|
@ -300,12 +306,32 @@ describe("loop.conversationViews", function () {
|
||||||
|
|
||||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||||
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||||
sinon.match.hasOwn("name", "fetchEmailLink"));
|
sinon.match.hasOwn("name", "fetchRoomEmailLink"));
|
||||||
|
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||||
|
sinon.match.hasOwn("roomOwner", fakeMozLoop.userProfile.email));
|
||||||
|
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||||
|
sinon.match.hasOwn("roomName", "test@test.tld"));
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should name the created room using the contact name when available",
|
||||||
|
function() {
|
||||||
|
view = mountTestComponent({contact: {
|
||||||
|
email: [{value: "test@test.tld"}],
|
||||||
|
name: ["Mr Fake ContactName"]
|
||||||
|
}});
|
||||||
|
|
||||||
|
var emailLinkBtn = view.getDOMNode().querySelector('.btn-email');
|
||||||
|
|
||||||
|
React.addons.TestUtils.Simulate.click(emailLinkBtn);
|
||||||
|
|
||||||
|
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||||
|
sinon.assert.calledWithMatch(dispatcher.dispatch,
|
||||||
|
sinon.match.hasOwn("roomName", "Mr Fake ContactName"));
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should disable the email link button once the action is dispatched",
|
it("should disable the email link button once the action is dispatched",
|
||||||
function() {
|
function() {
|
||||||
view = mountTestComponent();
|
view = mountTestComponent({contact: contact});
|
||||||
var emailLinkBtn = view.getDOMNode().querySelector('.btn-email');
|
var emailLinkBtn = view.getDOMNode().querySelector('.btn-email');
|
||||||
React.addons.TestUtils.Simulate.click(emailLinkBtn);
|
React.addons.TestUtils.Simulate.click(emailLinkBtn);
|
||||||
|
|
||||||
|
@ -314,7 +340,7 @@ describe("loop.conversationViews", function () {
|
||||||
|
|
||||||
it("should compose an email once the email link is received", function() {
|
it("should compose an email once the email link is received", function() {
|
||||||
var composeCallUrlEmail = sandbox.stub(sharedUtils, "composeCallUrlEmail");
|
var composeCallUrlEmail = sandbox.stub(sharedUtils, "composeCallUrlEmail");
|
||||||
view = mountTestComponent();
|
view = mountTestComponent({contact: contact});
|
||||||
store.setStoreState({emailLink: "http://fake.invalid/"});
|
store.setStoreState({emailLink: "http://fake.invalid/"});
|
||||||
|
|
||||||
sinon.assert.calledOnce(composeCallUrlEmail);
|
sinon.assert.calledOnce(composeCallUrlEmail);
|
||||||
|
@ -324,7 +350,7 @@ describe("loop.conversationViews", function () {
|
||||||
|
|
||||||
it("should close the conversation window once the email link is received",
|
it("should close the conversation window once the email link is received",
|
||||||
function() {
|
function() {
|
||||||
view = mountTestComponent();
|
view = mountTestComponent({contact: contact});
|
||||||
|
|
||||||
store.setStoreState({emailLink: "http://fake.invalid/"});
|
store.setStoreState({emailLink: "http://fake.invalid/"});
|
||||||
|
|
||||||
|
@ -333,7 +359,7 @@ describe("loop.conversationViews", function () {
|
||||||
|
|
||||||
it("should display an error message in case email link retrieval failed",
|
it("should display an error message in case email link retrieval failed",
|
||||||
function() {
|
function() {
|
||||||
view = mountTestComponent();
|
view = mountTestComponent({contact: contact});
|
||||||
|
|
||||||
store.trigger("error:emailLink");
|
store.trigger("error:emailLink");
|
||||||
|
|
||||||
|
@ -342,7 +368,7 @@ describe("loop.conversationViews", function () {
|
||||||
|
|
||||||
it("should allow retrying to get a call url if it failed previously",
|
it("should allow retrying to get a call url if it failed previously",
|
||||||
function() {
|
function() {
|
||||||
view = mountTestComponent();
|
view = mountTestComponent({contact: contact});
|
||||||
|
|
||||||
store.trigger("error:emailLink");
|
store.trigger("error:emailLink");
|
||||||
|
|
||||||
|
@ -350,7 +376,7 @@ describe("loop.conversationViews", function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should play a failure sound, once", function() {
|
it("should play a failure sound, once", function() {
|
||||||
view = mountTestComponent();
|
view = mountTestComponent({contact: contact});
|
||||||
|
|
||||||
sinon.assert.calledOnce(navigator.mozLoop.getAudioBlob);
|
sinon.assert.calledOnce(navigator.mozLoop.getAudioBlob);
|
||||||
sinon.assert.calledWithExactly(navigator.mozLoop.getAudioBlob,
|
sinon.assert.calledWithExactly(navigator.mozLoop.getAudioBlob,
|
||||||
|
|
|
@ -42,6 +42,9 @@ describe("loop.store.ConversationStore", function () {
|
||||||
calls: {
|
calls: {
|
||||||
setCallInProgress: sandbox.stub(),
|
setCallInProgress: sandbox.stub(),
|
||||||
clearCallInProgress: sandbox.stub()
|
clearCallInProgress: sandbox.stub()
|
||||||
|
},
|
||||||
|
rooms: {
|
||||||
|
create: sandbox.stub()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -701,20 +704,29 @@ describe("loop.store.ConversationStore", function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#fetchEmailLink", function() {
|
describe("#fetchRoomEmailLink", function() {
|
||||||
it("should request a new call url to the server", function() {
|
it("should request a new call url to the server", function() {
|
||||||
store.fetchEmailLink(new sharedActions.FetchEmailLink());
|
store.fetchRoomEmailLink(new sharedActions.FetchRoomEmailLink({
|
||||||
|
roomOwner: "bob@invalid.tld",
|
||||||
|
roomName: "FakeRoomName"
|
||||||
|
}));
|
||||||
|
|
||||||
sinon.assert.calledOnce(client.requestCallUrl);
|
sinon.assert.calledOnce(fakeMozLoop.rooms.create);
|
||||||
sinon.assert.calledWith(client.requestCallUrl, "");
|
sinon.assert.calledWithMatch(fakeMozLoop.rooms.create, {
|
||||||
|
roomOwner: "bob@invalid.tld",
|
||||||
|
roomName: "FakeRoomName"
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should update the emailLink attribute when the new call url is received",
|
it("should update the emailLink attribute when the new room url is received",
|
||||||
function() {
|
function() {
|
||||||
client.requestCallUrl = function(callId, cb) {
|
fakeMozLoop.rooms.create = function(roomData, cb) {
|
||||||
cb(null, {callUrl: "http://fake.invalid/"});
|
cb(null, {roomUrl: "http://fake.invalid/"});
|
||||||
};
|
};
|
||||||
store.fetchEmailLink(new sharedActions.FetchEmailLink());
|
store.fetchRoomEmailLink(new sharedActions.FetchRoomEmailLink({
|
||||||
|
roomOwner: "bob@invalid.tld",
|
||||||
|
roomName: "FakeRoomName"
|
||||||
|
}));
|
||||||
|
|
||||||
expect(store.getStoreState("emailLink")).eql("http://fake.invalid/");
|
expect(store.getStoreState("emailLink")).eql("http://fake.invalid/");
|
||||||
});
|
});
|
||||||
|
@ -722,10 +734,13 @@ describe("loop.store.ConversationStore", function () {
|
||||||
it("should trigger an error:emailLink event in case of failure",
|
it("should trigger an error:emailLink event in case of failure",
|
||||||
function() {
|
function() {
|
||||||
var trigger = sandbox.stub(store, "trigger");
|
var trigger = sandbox.stub(store, "trigger");
|
||||||
client.requestCallUrl = function(callId, cb) {
|
fakeMozLoop.rooms.create = function(roomData, cb) {
|
||||||
cb("error");
|
cb(new Error("error"));
|
||||||
};
|
};
|
||||||
store.fetchEmailLink(new sharedActions.FetchEmailLink());
|
store.fetchRoomEmailLink(new sharedActions.FetchRoomEmailLink({
|
||||||
|
roomOwner: "bob@invalid.tld",
|
||||||
|
roomName: "FakeRoomName"
|
||||||
|
}));
|
||||||
|
|
||||||
sinon.assert.calledOnce(trigger);
|
sinon.assert.calledOnce(trigger);
|
||||||
sinon.assert.calledWithExactly(trigger, "error:emailLink");
|
sinon.assert.calledWithExactly(trigger, "error:emailLink");
|
||||||
|
|
Загрузка…
Ссылка в новой задаче