From f16996b82f298fb93322ace23982c1c04f69c3f8 Mon Sep 17 00:00:00 2001 From: Dan Mosedale Date: Fri, 9 Jan 2015 14:51:51 -0800 Subject: [PATCH] Bug 1118061-add "caller unavailable" messages to Hello, r=jaws --- .../loop/content/js/conversationViews.js | 27 ++++++++- .../loop/content/js/conversationViews.jsx | 27 ++++++++- .../loop/content/shared/css/conversation.css | 2 +- .../desktop-local/conversationViews_test.js | 60 ++++++++++++++++++- browser/components/loop/ui/ui-showcase.js | 10 +++- browser/components/loop/ui/ui-showcase.jsx | 10 +++- .../en-US/chrome/browser/loop/loop.properties | 6 ++ 7 files changed, 134 insertions(+), 8 deletions(-) diff --git a/browser/components/loop/content/js/conversationViews.js b/browser/components/loop/content/js/conversationViews.js index 0e359a4f79d4..a7302c386021 100644 --- a/browser/components/loop/content/js/conversationViews.js +++ b/browser/components/loop/content/js/conversationViews.js @@ -20,6 +20,11 @@ loop.conversationViews = (function(mozL10n) { // 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 // 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) { // A contact may not contain email addresses, but only a phone number. 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")); }, + _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() { this.props.dispatcher.dispatch(new sharedActions.RetryCall()); }, @@ -784,7 +808,7 @@ loop.conversationViews = (function(mozL10n) { render: function() { return ( 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")), @@ -1048,6 +1072,7 @@ loop.conversationViews = (function(mozL10n) { CallIdentifierView: CallIdentifierView, ConversationDetailView: ConversationDetailView, CallFailedView: CallFailedView, + _getContactDisplayName: _getContactDisplayName, GenericFailureView: GenericFailureView, IncomingCallView: IncomingCallView, IncomingConversationView: IncomingConversationView, diff --git a/browser/components/loop/content/js/conversationViews.jsx b/browser/components/loop/content/js/conversationViews.jsx index 5ca8bc30a41d..50a6590a783b 100644 --- a/browser/components/loop/content/js/conversationViews.jsx +++ b/browser/components/loop/content/js/conversationViews.jsx @@ -20,6 +20,11 @@ loop.conversationViews = (function(mozL10n) { // 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 // 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) { // A contact may not contain email addresses, but only a phone number. if (!contact.email || contact.email.length === 0) { @@ -761,6 +766,25 @@ loop.conversationViews = (function(mozL10n) { return

{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() { this.props.dispatcher.dispatch(new sharedActions.RetryCall()); }, @@ -784,7 +808,7 @@ loop.conversationViews = (function(mozL10n) { render: function() { return (
-

{mozL10n.get("generic_failure_title")}

+

{ this._getTitleMessage() }

{mozL10n.get("generic_failure_with_reason2")}

@@ -1048,6 +1072,7 @@ loop.conversationViews = (function(mozL10n) { CallIdentifierView: CallIdentifierView, ConversationDetailView: ConversationDetailView, CallFailedView: CallFailedView, + _getContactDisplayName: _getContactDisplayName, GenericFailureView: GenericFailureView, IncomingCallView: IncomingCallView, IncomingConversationView: IncomingConversationView, diff --git a/browser/components/loop/content/shared/css/conversation.css b/browser/components/loop/content/shared/css/conversation.css index 8ecc74b371ce..661a680b6669 100644 --- a/browser/components/loop/content/shared/css/conversation.css +++ b/browser/components/loop/content/shared/css/conversation.css @@ -279,7 +279,7 @@ .call-window h2 { font-size: 1.5em; font-weight: normal; - + text-align: center; /* compensate for reset.css overriding this; values borrowed from Firefox Mac html.css */ margin: 0.83em 0; diff --git a/browser/components/loop/test/desktop-local/conversationViews_test.js b/browser/components/loop/test/desktop-local/conversationViews_test.js index 3667facee1d0..dd5ac32b7100 100644 --- a/browser/components/loop/test/desktop-local/conversationViews_test.js +++ b/browser/components/loop/test/desktop-local/conversationViews_test.js @@ -412,6 +412,61 @@ describe("loop.conversationViews", function () { sinon.assert.calledOnce(fakeAudio.play); 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() { @@ -531,7 +586,10 @@ describe("loop.conversationViews", function () { it("should render the CallFailedView when the call state is 'terminated'", function() { - store.setStoreState({callState: CALL_STATES.TERMINATED}); + store.setStoreState({ + callState: CALL_STATES.TERMINATED, + contact: contact + }); view = mountTestComponent(); diff --git a/browser/components/loop/ui/ui-showcase.js b/browser/components/loop/ui/ui-showcase.js index a23281076f2d..ee9b8f73edbb 100644 --- a/browser/components/loop/ui/ui-showcase.js +++ b/browser/components/loop/ui/ui-showcase.js @@ -74,6 +74,11 @@ var feedbackStore = new loop.store.FeedbackStore(dispatcher, { feedbackClient: stageFeedbackApiClient }); + var conversationStore = new loop.store.ConversationStore(dispatcher, { + client: {}, + mozLoop: navigator.mozLoop, + sdkDriver: {} + }); // Local mocks @@ -376,13 +381,14 @@ React.createElement(Example, {summary: "Call Failed", dashed: "true", style: {width: "260px", height: "265px"}}, 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", style: {width: "260px", height: "265px"}}, React.createElement("div", {className: "fx-embedded"}, - React.createElement(CallFailedView, {dispatcher: dispatcher, emailLinkError: true}) + React.createElement(CallFailedView, {dispatcher: dispatcher, emailLinkError: true, + store: conversationStore}) ) ) ), diff --git a/browser/components/loop/ui/ui-showcase.jsx b/browser/components/loop/ui/ui-showcase.jsx index 66b12c56e48c..c5c786973ad0 100644 --- a/browser/components/loop/ui/ui-showcase.jsx +++ b/browser/components/loop/ui/ui-showcase.jsx @@ -74,6 +74,11 @@ var feedbackStore = new loop.store.FeedbackStore(dispatcher, { feedbackClient: stageFeedbackApiClient }); + var conversationStore = new loop.store.ConversationStore(dispatcher, { + client: {}, + mozLoop: navigator.mozLoop, + sdkDriver: {} + }); // Local mocks @@ -376,13 +381,14 @@
- +
- +
diff --git a/browser/locales/en-US/chrome/browser/loop/loop.properties b/browser/locales/en-US/chrome/browser/loop/loop.properties index ab5448e9a3bb..a84d46fa9440 100644 --- a/browser/locales/en-US/chrome/browser/loop/loop.properties +++ b/browser/locales/en-US/chrome/browser/loop/loop.properties @@ -225,6 +225,12 @@ peer_ended_conversation2=The person you were calling has ended the conversation. conversation_has_ended=Your conversation has ended. 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_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?