Bug 1118061-add "caller unavailable" messages to Hello, r=jaws

This commit is contained in:
Dan Mosedale 2015-01-09 14:51:51 -08:00
Родитель e28a549c00
Коммит f16996b82f
7 изменённых файлов: 134 добавлений и 8 удалений

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

@ -20,6 +20,11 @@ loop.conversationViews = (function(mozL10n) {
// This duplicates a similar function in contacts.jsx that isn't used in the // This duplicates a similar function in contacts.jsx that isn't used in the
// conversation window. If we get too many of these, we might want to consider // conversation window. If we get too many of these, we might want to consider
// finding a logical place for them to be shared. // finding a logical place for them to be shared.
// XXXdmose this code is already out of sync with the code in contacts.jsx
// which, unlike this code, now has unit tests. We should totally do the
// above.
function _getPreferredEmail(contact) { function _getPreferredEmail(contact) {
// A contact may not contain email addresses, but only a phone number. // A contact may not contain email addresses, but only a phone number.
if (!contact.email || contact.email.length === 0) { if (!contact.email || contact.email.length === 0) {
@ -761,6 +766,25 @@ loop.conversationViews = (function(mozL10n) {
return React.createElement("p", {className: "error"}, mozL10n.get("unable_retrieve_url")); return React.createElement("p", {className: "error"}, mozL10n.get("unable_retrieve_url"));
}, },
_getTitleMessage: function() {
var callStateReason =
this.props.store.getStoreState("callStateReason");
if (callStateReason === "reject" || callStateReason === "busy" ||
callStateReason === "setup") {
var contactDisplayName = _getContactDisplayName(this.props.contact);
if (contactDisplayName.length) {
return mozL10n.get(
"contact_unavailable_title",
{"contactName": contactDisplayName});
}
return mozL10n.get("generic_contact_unavailable_title");
} else {
return mozL10n.get("generic_failure_title");
}
},
retryCall: function() { retryCall: function() {
this.props.dispatcher.dispatch(new sharedActions.RetryCall()); this.props.dispatcher.dispatch(new sharedActions.RetryCall());
}, },
@ -784,7 +808,7 @@ loop.conversationViews = (function(mozL10n) {
render: function() { render: function() {
return ( return (
React.createElement("div", {className: "call-window"}, React.createElement("div", {className: "call-window"},
React.createElement("h2", null, mozL10n.get("generic_failure_title")), React.createElement("h2", null, this._getTitleMessage() ),
React.createElement("p", {className: "btn-label"}, mozL10n.get("generic_failure_with_reason2")), React.createElement("p", {className: "btn-label"}, mozL10n.get("generic_failure_with_reason2")),
@ -1048,6 +1072,7 @@ loop.conversationViews = (function(mozL10n) {
CallIdentifierView: CallIdentifierView, CallIdentifierView: CallIdentifierView,
ConversationDetailView: ConversationDetailView, ConversationDetailView: ConversationDetailView,
CallFailedView: CallFailedView, CallFailedView: CallFailedView,
_getContactDisplayName: _getContactDisplayName,
GenericFailureView: GenericFailureView, GenericFailureView: GenericFailureView,
IncomingCallView: IncomingCallView, IncomingCallView: IncomingCallView,
IncomingConversationView: IncomingConversationView, IncomingConversationView: IncomingConversationView,

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

@ -20,6 +20,11 @@ loop.conversationViews = (function(mozL10n) {
// This duplicates a similar function in contacts.jsx that isn't used in the // This duplicates a similar function in contacts.jsx that isn't used in the
// conversation window. If we get too many of these, we might want to consider // conversation window. If we get too many of these, we might want to consider
// finding a logical place for them to be shared. // finding a logical place for them to be shared.
// XXXdmose this code is already out of sync with the code in contacts.jsx
// which, unlike this code, now has unit tests. We should totally do the
// above.
function _getPreferredEmail(contact) { function _getPreferredEmail(contact) {
// A contact may not contain email addresses, but only a phone number. // A contact may not contain email addresses, but only a phone number.
if (!contact.email || contact.email.length === 0) { if (!contact.email || contact.email.length === 0) {
@ -761,6 +766,25 @@ loop.conversationViews = (function(mozL10n) {
return <p className="error">{mozL10n.get("unable_retrieve_url")}</p>; return <p className="error">{mozL10n.get("unable_retrieve_url")}</p>;
}, },
_getTitleMessage: function() {
var callStateReason =
this.props.store.getStoreState("callStateReason");
if (callStateReason === "reject" || callStateReason === "busy" ||
callStateReason === "setup") {
var contactDisplayName = _getContactDisplayName(this.props.contact);
if (contactDisplayName.length) {
return mozL10n.get(
"contact_unavailable_title",
{"contactName": contactDisplayName});
}
return mozL10n.get("generic_contact_unavailable_title");
} else {
return mozL10n.get("generic_failure_title");
}
},
retryCall: function() { retryCall: function() {
this.props.dispatcher.dispatch(new sharedActions.RetryCall()); this.props.dispatcher.dispatch(new sharedActions.RetryCall());
}, },
@ -784,7 +808,7 @@ loop.conversationViews = (function(mozL10n) {
render: function() { render: function() {
return ( return (
<div className="call-window"> <div className="call-window">
<h2>{mozL10n.get("generic_failure_title")}</h2> <h2>{ this._getTitleMessage() }</h2>
<p className="btn-label">{mozL10n.get("generic_failure_with_reason2")}</p> <p className="btn-label">{mozL10n.get("generic_failure_with_reason2")}</p>
@ -1048,6 +1072,7 @@ loop.conversationViews = (function(mozL10n) {
CallIdentifierView: CallIdentifierView, CallIdentifierView: CallIdentifierView,
ConversationDetailView: ConversationDetailView, ConversationDetailView: ConversationDetailView,
CallFailedView: CallFailedView, CallFailedView: CallFailedView,
_getContactDisplayName: _getContactDisplayName,
GenericFailureView: GenericFailureView, GenericFailureView: GenericFailureView,
IncomingCallView: IncomingCallView, IncomingCallView: IncomingCallView,
IncomingConversationView: IncomingConversationView, IncomingConversationView: IncomingConversationView,

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

@ -279,7 +279,7 @@
.call-window h2 { .call-window h2 {
font-size: 1.5em; font-size: 1.5em;
font-weight: normal; font-weight: normal;
text-align: center;
/* compensate for reset.css overriding this; values borrowed from /* compensate for reset.css overriding this; values borrowed from
Firefox Mac html.css */ Firefox Mac html.css */
margin: 0.83em 0; margin: 0.83em 0;

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

@ -412,6 +412,61 @@ describe("loop.conversationViews", function () {
sinon.assert.calledOnce(fakeAudio.play); sinon.assert.calledOnce(fakeAudio.play);
expect(fakeAudio.loop).to.equal(false); expect(fakeAudio.loop).to.equal(false);
}); });
it("should show 'something went wrong' when the reason is 'media-fail'",
function () {
store.setStoreState({callStateReason: "media-fail"});
view = mountTestComponent({contact: contact});
sinon.assert.calledWith(document.mozL10n.get, "generic_failure_title");
});
it("should show 'contact unavailable' when the reason is 'reject'",
function () {
store.setStoreState({callStateReason: "reject"});
view = mountTestComponent({contact: contact});
sinon.assert.calledWithExactly(document.mozL10n.get,
"contact_unavailable_title",
{contactName: loop.conversationViews._getContactDisplayName(contact)});
});
it("should show 'contact unavailable' when the reason is 'busy'",
function () {
store.setStoreState({callStateReason: "busy"});
view = mountTestComponent({contact: contact});
sinon.assert.calledWithExactly(document.mozL10n.get,
"contact_unavailable_title",
{contactName: loop.conversationViews._getContactDisplayName(contact)});
});
it("should show 'contact unavailable' when the reason is 'setup'",
function () {
store.setStoreState({callStateReason: "setup"});
view = mountTestComponent({contact: contact});
sinon.assert.calledWithExactly(document.mozL10n.get,
"contact_unavailable_title",
{contactName: loop.conversationViews._getContactDisplayName(contact)});
});
it("should display a generic contact unavailable msg when the reason is" +
" 'busy' and no display name is available", function() {
store.setStoreState({callStateReason: "busy"});
var phoneOnlyContact = {
tel: [{"pref": true, type: "work", value: ""}]
};
view = mountTestComponent({contact: phoneOnlyContact});
sinon.assert.calledWith(document.mozL10n.get,
"generic_contact_unavailable_title");
});
}); });
describe("OngoingConversationView", function() { describe("OngoingConversationView", function() {
@ -531,7 +586,10 @@ describe("loop.conversationViews", function () {
it("should render the CallFailedView when the call state is 'terminated'", it("should render the CallFailedView when the call state is 'terminated'",
function() { function() {
store.setStoreState({callState: CALL_STATES.TERMINATED}); store.setStoreState({
callState: CALL_STATES.TERMINATED,
contact: contact
});
view = mountTestComponent(); view = mountTestComponent();

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

@ -74,6 +74,11 @@
var feedbackStore = new loop.store.FeedbackStore(dispatcher, { var feedbackStore = new loop.store.FeedbackStore(dispatcher, {
feedbackClient: stageFeedbackApiClient feedbackClient: stageFeedbackApiClient
}); });
var conversationStore = new loop.store.ConversationStore(dispatcher, {
client: {},
mozLoop: navigator.mozLoop,
sdkDriver: {}
});
// Local mocks // Local mocks
@ -376,13 +381,14 @@
React.createElement(Example, {summary: "Call Failed", dashed: "true", React.createElement(Example, {summary: "Call Failed", dashed: "true",
style: {width: "260px", height: "265px"}}, style: {width: "260px", height: "265px"}},
React.createElement("div", {className: "fx-embedded"}, React.createElement("div", {className: "fx-embedded"},
React.createElement(CallFailedView, {dispatcher: dispatcher}) React.createElement(CallFailedView, {dispatcher: dispatcher, store: conversationStore})
) )
), ),
React.createElement(Example, {summary: "Call Failed — with call URL error", dashed: "true", React.createElement(Example, {summary: "Call Failed — with call URL error", dashed: "true",
style: {width: "260px", height: "265px"}}, style: {width: "260px", height: "265px"}},
React.createElement("div", {className: "fx-embedded"}, React.createElement("div", {className: "fx-embedded"},
React.createElement(CallFailedView, {dispatcher: dispatcher, emailLinkError: true}) React.createElement(CallFailedView, {dispatcher: dispatcher, emailLinkError: true,
store: conversationStore})
) )
) )
), ),

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

@ -74,6 +74,11 @@
var feedbackStore = new loop.store.FeedbackStore(dispatcher, { var feedbackStore = new loop.store.FeedbackStore(dispatcher, {
feedbackClient: stageFeedbackApiClient feedbackClient: stageFeedbackApiClient
}); });
var conversationStore = new loop.store.ConversationStore(dispatcher, {
client: {},
mozLoop: navigator.mozLoop,
sdkDriver: {}
});
// Local mocks // Local mocks
@ -376,13 +381,14 @@
<Example summary="Call Failed" dashed="true" <Example summary="Call Failed" dashed="true"
style={{width: "260px", height: "265px"}}> style={{width: "260px", height: "265px"}}>
<div className="fx-embedded"> <div className="fx-embedded">
<CallFailedView dispatcher={dispatcher} /> <CallFailedView dispatcher={dispatcher} store={conversationStore} />
</div> </div>
</Example> </Example>
<Example summary="Call Failed — with call URL error" dashed="true" <Example summary="Call Failed — with call URL error" dashed="true"
style={{width: "260px", height: "265px"}}> style={{width: "260px", height: "265px"}}>
<div className="fx-embedded"> <div className="fx-embedded">
<CallFailedView dispatcher={dispatcher} emailLinkError={true} /> <CallFailedView dispatcher={dispatcher} emailLinkError={true}
store={conversationStore} />
</div> </div>
</Example> </Example>
</Section> </Section>

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

@ -225,6 +225,12 @@ peer_ended_conversation2=The person you were calling has ended the conversation.
conversation_has_ended=Your conversation has ended. conversation_has_ended=Your conversation has ended.
restart_call=Rejoin restart_call=Rejoin
## LOCALIZATION NOTE (contact_unavailable_title): The title displayed
## when a contact is unavailable. Don't translate the part between {{..}}
## because this will be replaced by the contact's name.
contact_unavailable_title={{contactName}} is unavailable.
generic_contact_unavailable_title=This person is unavailable.
generic_failure_title=Something went wrong. generic_failure_title=Something went wrong.
generic_failure_with_reason2=You can try again or email a link to be reached at later. generic_failure_with_reason2=You can try again or email a link to be reached at later.
generic_failure_no_reason2=Would you like to try again? generic_failure_no_reason2=Would you like to try again?