Bug 1000131 (Part 2): Loop expired call url view styling, r=dmose.

This commit is contained in:
Nicolas Perriault 2014-07-28 12:51:42 -07:00
parent 8081742abe
commit c30531cbde
8 changed files with 273 additions and 55 deletions

View File

@ -15,19 +15,27 @@
body {
margin: 0;
padding: 0;
font-family: "Helvetica Neue", Helvetica, Arial, sans;
font-size: 14px;
font-family: "Lucida Grande", sans-serif;
font-size: 12px;
background: #fbfbfb;
}
button {
font-size: .9em; /* for some reason, text is larger within <button> */
/* Resetting default <button> font properties; eg. strangely enough, FF mac
wants to use Helvetica/12px whatever we define for parent elements */
font-family: "Lucida Grande", sans-serif;
font-size: 1em;
}
img {
border: none;
}
h1, h2, h3 {
font-family: "Open Sans", sans-serif;
color: #666;
}
/* Helpers */
/**
@ -73,14 +81,24 @@ img {
border: none;
color: #fff;
text-decoration: none;
height: 26px;
padding: 0 0.5em;
padding: .3em .6em;
border: 1px solid transparent;
border-radius: 2px;
cursor: pointer;
font-size: .9em;
text-align: center;
}
button.btn {
/* for some reason, buttons respond differently to setting padding than
regular links */
padding: .3em .3em .3em .5em;
}
.btn-large {
padding: .4em 1.6em;
}
.btn-info {
background-color: #0096dd;
border: 1px solid #0095dd;
@ -310,3 +328,39 @@ img {
.linux h1 {
font-family: 'Ubuntu Bold';
}
/* Web panel */
.info-panel {
border-radius: 4px;
background: #fff;
padding: 20px 0;
border: 1px solid #e7e7e7;
box-shadow: 0 2px 0 rgba(0, 0, 0, .03);
margin-bottom: 25px;
}
.info-panel h1 {
font-size: 1.2em;
font-weight: 700;
padding: 20px 0;
text-align: center;
margin: 0;
}
.info-panel h4 {
color: #aaa;
text-align: center;
font-weight: 300;
margin: 0;
}
/* Logos */
.firefox-logo {
background: transparent url(../img/firefox-logo.png) no-repeat center center;
background-size: contain;
width: 100px;
height: 100px;
margin: 0px auto; /* horizontal block centering */
}

View File

@ -194,6 +194,24 @@
font-weight: normal;
}
/* Expired call url page */
.expired-url-info {
width: 400px;
margin: 0 auto;
}
.promote-firefox {
text-align: center;
font-size: 18px;
line-height: 24px;
margin: 2em 0;
}
.promote-firefox h3 {
font-weight: 300;
}
/* Block incoming call */
.native-dropdown-menu {

View File

@ -3,10 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Panel styles */
body {
font-size: 12px;
}
.panel {
/* XXX the Social API panel behaves weirdly on inner element size changes,
adding unwanted scrollbars; quickfix is to hide these for now. */

View File

@ -17,7 +17,10 @@
width: 600px;
margin: 1em auto;
background: #fff;
font-family: Helvetica, Arial, sans;
font-size: 12px;
}
h2 {
margin-top: 3em;
}
</style>
</head>
@ -102,8 +105,8 @@
<div class="conversation">
<ul class="controls">
<li><button class="btn btn-hangup" title="Hangup"></button></li>
<li><button class="btn media-control btn-mute-video streaming" title="Mute video"></button></li>
<li><button class="btn media-control btn-mute-audio" title="Mute audio"></button></li>
<li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
<li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
</ul>
<div class="media nested">
<div class="remote_wrapper">
@ -119,8 +122,8 @@
<div class="conversation">
<ul class="controls">
<li><button class="btn btn-hangup" title="Hangup"></button></li>
<li><button class="btn media-control btn-mute-video streaming" title="Mute video"></button></li>
<li><button class="btn media-control btn-mute-audio" title="Mute audio"></button></li>
<li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
<li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
</ul>
<div class="media nested">
<div class="remote_wrapper">
@ -150,8 +153,8 @@
<div class="conversation">
<ul class="controls">
<li><button class="btn btn-hangup" title="Hangup"></button></li>
<li><button class="btn media-control btn-mute-video" title="Mute video"></button></li>
<li><button class="btn media-control btn-mute-audio" title="Mute audio"></button></li>
<li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
<li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
</ul>
</div>
</div>
@ -162,8 +165,8 @@
<div class="conversation">
<ul class="controls">
<li><button class="btn btn-hangup" title="Hangup"></button></li>
<li><button class="btn media-control btn-mute-video" title="Mute video"></button></li>
<li><button class="btn media-control btn-mute-audio muted" title="Mute audio"></button></li>
<li><button class="btn media-control local-media btn-mute-video" title="Mute video"></button></li>
<li><button class="btn media-control local-media btn-mute-audio muted" title="Mute audio"></button></li>
</ul>
</div>
</div>
@ -174,33 +177,25 @@
<div class="conversation">
<ul class="controls">
<li><button class="btn btn-hangup" title="Hangup"></button></li>
<li><button class="btn media-control btn-mute-video muted" title="Mute video"></button></li>
<li><button class="btn media-control btn-mute-audio" title="Mute audio"></button></li>
<li><button class="btn media-control local-media btn-mute-video muted" title="Mute video"></button></li>
<li><button class="btn media-control local-media btn-mute-audio" title="Mute audio"></button></li>
</ul>
</div>
</div>
<h3>Local audio streaming</h3>
<h2>Expired call url view</h2>
<div style="width: 204px; min-height: 26px">
<div class="conversation">
<ul class="controls">
<li><button class="btn btn-hangup" title="Hangup"></button></li>
<li><button class="btn media-control btn-mute-video" title="Mute video"></button></li>
<li><button class="btn media-control btn-mute-audio streaming" title="Mute audio"></button></li>
</ul>
<div class="expired-url-info">
<div class="info-panel">
<div class="firefox-logo"></div>
<h1 >Oops!</h1>
<h4 >This URL is unavailable.</h4>
</div>
</div>
<h3>Local video streaming</h3>
<div style="width: 204px; min-height: 26px">
<div class="conversation">
<ul class="controls">
<li><button class="btn btn-hangup" title="Hangup"></button></li>
<li><button class="btn media-control btn-mute-video streaming" title="Mute video"></button></li>
<li><button class="btn media-control btn-mute-audio" title="Mute audio"></button></li>
</ul>
<div class="promote-firefox">
<h3>Download Firefox to make free audio and video calls!</h3>
<p>
<a class="btn btn-large btn-success" href="https://www.mozilla.org/firefox/" data-reactid=".0.1.1.0">Get Firefox</a>
</p>
</div>
</div>
@ -230,6 +225,16 @@
<button class="btn btn-error">error</button>
</p>
<h3>Large buttons</h3>
<p>
<a class="btn btn-large">default</a>
<a class="btn btn-large btn-info">info</a>
<a class="btn btn-large btn-success">success</a>
<a class="btn btn-large btn-warning">warning</a>
<a class="btn btn-large btn-error">error</a>
</p>
<h2>Alerts</h2>
<div class="alert alert-error">
@ -242,6 +247,12 @@
<p class="message">Oops! This is a warning.</p>
</div>
<h2>Logos</h2>
<h3>Centered Firefox logo</h3>
<div class="firefox-logo"></div>
<h2>Incoming call</h2>
<div class="incoming-call">

View File

@ -32,15 +32,51 @@ loop.webapp = (function($, _, OT, webL10n) {
template: _.template('<p data-l10n-id="welcome"></p>')
});
/**
* Firefox promotion interstitial. Will display only to non-Firefox users.
*/
var PromoteFirefoxView = React.createClass({displayName: 'PromoteFirefoxView',
propTypes: {
helper: React.PropTypes.object.isRequired
},
render: function() {
if (this.props.helper.isFirefox(navigator.userAgent)) {
return React.DOM.div(null );
}
return (
React.DOM.div( {className:"promote-firefox"},
React.DOM.h3(null, __("promote_firefox_hello_heading")),
React.DOM.p(null,
React.DOM.a( {className:"btn btn-large btn-success",
href:"https://www.mozilla.org/firefox/"},
__("get_firefox_button")
)
)
)
);
}
});
/**
* Expired call URL view.
*/
var CallUrlExpiredView = React.createClass({displayName: 'CallUrlExpiredView',
propTypes: {
helper: React.PropTypes.object.isRequired
},
render: function() {
/* jshint ignore:start */
return (
// XXX proper UX/design should be implemented here (see bug 1000131)
React.DOM.div(null, __("call_url_unavailable_notification"))
React.DOM.div( {className:"expired-url-info"},
React.DOM.div( {className:"info-panel"},
React.DOM.div( {className:"firefox-logo"} ),
React.DOM.h1(null, __("call_url_unavailable_notification_heading")),
React.DOM.h4(null, __("call_url_unavailable_notification_message"))
),
PromoteFirefoxView( {helper:this.props.helper} )
)
);
/* jshint ignore:end */
}
@ -135,7 +171,12 @@ loop.webapp = (function($, _, OT, webL10n) {
"call/:token": "initiate"
},
initialize: function() {
initialize: function(options) {
this.helper = options.helper;
if (!this.helper) {
throw new Error("WebappRouter requires an helper object");
}
// Load default view
this.loadView(new HomeView());
@ -193,7 +234,7 @@ loop.webapp = (function($, _, OT, webL10n) {
},
expired: function() {
this.loadReactComponent(CallUrlExpiredView());
this.loadReactComponent(CallUrlExpiredView({helper: this.helper}));
},
/**
@ -238,8 +279,14 @@ loop.webapp = (function($, _, OT, webL10n) {
this._iOSRegex = /^(iPad|iPhone|iPod)/;
}
WebappHelper.prototype.isIOS = function isIOS(platform) {
return this._iOSRegex.test(platform);
WebappHelper.prototype = {
isFirefox: function(platform) {
return platform.indexOf("Firefox") !== -1;
},
isIOS: function(platform) {
return this._iOSRegex.test(platform);
}
};
/**
@ -248,6 +295,7 @@ loop.webapp = (function($, _, OT, webL10n) {
function init() {
var helper = new WebappHelper();
router = new WebappRouter({
helper: helper,
notifier: new sharedViews.NotificationListView({el: "#messages"}),
conversation: new sharedModels.ConversationModel({}, {
sdk: OT,
@ -267,8 +315,9 @@ loop.webapp = (function($, _, OT, webL10n) {
CallUrlExpiredView: CallUrlExpiredView,
ConversationFormView: ConversationFormView,
HomeView: HomeView,
WebappHelper: WebappHelper,
init: init,
PromoteFirefoxView: PromoteFirefoxView,
WebappHelper: WebappHelper,
WebappRouter: WebappRouter
};
})(jQuery, _, window.OT, document.webL10n);

View File

@ -32,15 +32,51 @@ loop.webapp = (function($, _, OT, webL10n) {
template: _.template('<p data-l10n-id="welcome"></p>')
});
/**
* Firefox promotion interstitial. Will display only to non-Firefox users.
*/
var PromoteFirefoxView = React.createClass({
propTypes: {
helper: React.PropTypes.object.isRequired
},
render: function() {
if (this.props.helper.isFirefox(navigator.userAgent)) {
return <div />;
}
return (
<div className="promote-firefox">
<h3>{__("promote_firefox_hello_heading")}</h3>
<p>
<a className="btn btn-large btn-success"
href="https://www.mozilla.org/firefox/">
{__("get_firefox_button")}
</a>
</p>
</div>
);
}
});
/**
* Expired call URL view.
*/
var CallUrlExpiredView = React.createClass({
propTypes: {
helper: React.PropTypes.object.isRequired
},
render: function() {
/* jshint ignore:start */
return (
// XXX proper UX/design should be implemented here (see bug 1000131)
<div>{__("call_url_unavailable_notification")}</div>
<div className="expired-url-info">
<div className="info-panel">
<div className="firefox-logo" />
<h1>{__("call_url_unavailable_notification_heading")}</h1>
<h4>{__("call_url_unavailable_notification_message")}</h4>
</div>
<PromoteFirefoxView helper={this.props.helper} />
</div>
);
/* jshint ignore:end */
}
@ -135,7 +171,12 @@ loop.webapp = (function($, _, OT, webL10n) {
"call/:token": "initiate"
},
initialize: function() {
initialize: function(options) {
this.helper = options.helper;
if (!this.helper) {
throw new Error("WebappRouter requires an helper object");
}
// Load default view
this.loadView(new HomeView());
@ -193,7 +234,7 @@ loop.webapp = (function($, _, OT, webL10n) {
},
expired: function() {
this.loadReactComponent(CallUrlExpiredView());
this.loadReactComponent(CallUrlExpiredView({helper: this.helper}));
},
/**
@ -238,8 +279,14 @@ loop.webapp = (function($, _, OT, webL10n) {
this._iOSRegex = /^(iPad|iPhone|iPod)/;
}
WebappHelper.prototype.isIOS = function isIOS(platform) {
return this._iOSRegex.test(platform);
WebappHelper.prototype = {
isFirefox: function(platform) {
return platform.indexOf("Firefox") !== -1;
},
isIOS: function(platform) {
return this._iOSRegex.test(platform);
}
};
/**
@ -248,6 +295,7 @@ loop.webapp = (function($, _, OT, webL10n) {
function init() {
var helper = new WebappHelper();
router = new WebappRouter({
helper: helper,
notifier: new sharedViews.NotificationListView({el: "#messages"}),
conversation: new sharedModels.ConversationModel({}, {
sdk: OT,
@ -267,8 +315,9 @@ loop.webapp = (function($, _, OT, webL10n) {
CallUrlExpiredView: CallUrlExpiredView,
ConversationFormView: ConversationFormView,
HomeView: HomeView,
WebappHelper: WebappHelper,
init: init,
PromoteFirefoxView: PromoteFirefoxView,
WebappHelper: WebappHelper,
WebappRouter: WebappRouter
};
})(jQuery, _, window.OT, document.webL10n);

View File

@ -19,7 +19,10 @@ incompatible_device=Incompatible device
sorry_device_unsupported=Sorry, Loop does not currently support your device.
use_firefox_windows_mac_linux=Please open this page using the latest Firefox on Windows, Android, Mac or Linux.
connection_error_see_console_notification=Call failed; see console for details.
call_url_unavailable_notification=This URL is unavailable.
call_url_unavailable_notification_heading=Oops!
call_url_unavailable_notification_message=This URL is unavailable.
promote_firefox_hello_heading=Download Firefox to make free audio and video calls!
get_firefox_button=Get Firefox
[fr]
call_has_ended=L'appel est terminé.
@ -41,4 +44,7 @@ use_latest_firefox.innerHTML=Veuillez essayer ce lien dans un navigateur accepta
incompatible_device=Plateforme non supportée
sorry_device_unsupported=Désolé, Loop ne fonctionne actuellement pas sur votre appareil.
use_firefox_windows_mac_linux=Merci d'ouvrir cette page avec une version récente de Firefox pour Windows, Android, Mac ou Linux.
call_url_unavailable_notification=Cette URL n'est pas disponible.
call_url_unavailable_notification_heading=Oups !
call_url_unavailable_notification_message=Cette URL n'est pas disponible.
promote_firefox_hello_heading=Téléchargez Firefox pour passer des appels audio et vidéo gratuitement !
get_firefox_button=Téléchargez Firefox

View File

@ -5,6 +5,7 @@
/* global loop, sinon */
var expect = chai.expect;
var TestUtils = React.addons.TestUtils;
describe("loop.webapp", function() {
"use strict";
@ -76,6 +77,7 @@ describe("loop.webapp", function() {
pendingCallTimeout: 1000
});
router = new loop.webapp.WebappRouter({
helper: {},
conversation: conversation,
notifier: notifier
});
@ -359,6 +361,26 @@ describe("loop.webapp", function() {
});
});
describe("PromoteFirefoxView", function() {
describe("#render", function() {
it("should not render when using Firefox", function() {
var comp = TestUtils.renderIntoDocument(loop.webapp.PromoteFirefoxView({
helper: {isFirefox: function() { return true; }}
}));
expect(comp.getDOMNode().querySelectorAll("h3").length).eql(0);
});
it("should render when not using Firefox", function() {
var comp = TestUtils.renderIntoDocument(loop.webapp.PromoteFirefoxView({
helper: {isFirefox: function() { return false; }}
}));
expect(comp.getDOMNode().querySelectorAll("h3").length).eql(1);
});
});
});
describe("WebappHelper", function() {
var helper;
@ -378,5 +400,18 @@ describe("loop.webapp", function() {
expect(helper.isIOS("MacIntel")).eql(false);
});
});
describe("#isFirefox", function() {
it("should detect Firefox", function() {
expect(helper.isFirefox("Firefox")).eql(true);
expect(helper.isFirefox("Gecko/Firefox")).eql(true);
expect(helper.isFirefox("Firefox/Gecko")).eql(true);
expect(helper.isFirefox("Gecko/Firefox/Chuck Norris")).eql(true);
});
it("shouldn't detect Firefox with other platforms", function() {
expect(helper.isFirefox("Opera")).eql(false);
});
});
});
});