Add feature-flagged newsletter bar to the logged-out homepage.

This commit is contained in:
umbrae
2015-02-28 21:40:09 -08:00
parent 3d8f234b68
commit 031e20ed36
12 changed files with 286 additions and 4 deletions

View File

@@ -296,6 +296,23 @@ class ApiController(RedditController):
# Pylons does not handle 204s correctly.
return {}
@json_validate(
VModhashIfLoggedIn(),
VRatelimit(rate_ip=True, prefix="rate_newsletter_"),
email=ValidEmail("email"),
)
def POST_newsletter(self, responder, email):
"""Add an email to our newsletter."""
VRatelimit.ratelimit(rate_ip=True,
prefix="rate_newsletter_")
try:
newsletter.add_subscriber(email, source="newsletterbar")
except newsletter.NewsletterError as e:
g.log.warning("Failed to subscribe: %r" % e)
abort(500)
@allow_oauth2_access
@json_validate()
@api_doc(api_section.captcha)

View File

@@ -496,6 +496,7 @@ module["reddit"] = LocalizedModule("reddit.js",
"ui.js",
"popup.js",
"login.js",
"newsletter.js",
"flair.js",
"interestbar.js",
"visited.js",

View File

@@ -262,6 +262,7 @@ class Reddit(Templated):
#add the infobar
self.welcomebar = None
self.newsletterbar = None
self.locationbar = None
self.infobar = None
# generate a canonical link for google
@@ -304,6 +305,8 @@ class Reddit(Templated):
if not c.user_is_loggedin:
self.welcomebar = WelcomeBar()
if feature.is_enabled('newsletter') and getattr(self, "show_newsletterbar", True):
self.newsletterbar = NewsletterBar()
show_locationbar &= not c.user.pref_hide_locationbar
if (show_locationbar and c.used_localized_defaults and
@@ -824,9 +827,17 @@ class Reddit(Templated):
def content(self):
"""returns a Wrapped (or renderable) item for the main content div."""
if self.newsletterbar:
self.welcomebar = None
return self.content_stack((
self.welcomebar, self.infobar, self.locationbar, self.nav_menu,
self._content))
self.welcomebar,
self.newsletterbar,
self.infobar,
self.locationbar,
self.nav_menu,
self._content,
))
def is_gold_page(self):
return "gold-page-ga-tracking" in self.supplied_page_classes
@@ -2294,6 +2305,9 @@ class WelcomeBar(InfoBar):
_("where your votes shape what the world is talking about."))
InfoBar.__init__(self, message=message)
class NewsletterBar(InfoBar):
pass
class ClientInfoBar(InfoBar):
"""Draws the message the top of a login page before OAuth2 authorization"""
def __init__(self, client, *args, **kwargs):

View File

@@ -39,3 +39,7 @@
.c-btn-primary {
.button-variant(@text-color: #fff; @bg-color: #4f86b5; @bevel-color: #4270a2);
}
.c-btn-highlight {
.button-variant(@text-color: #fff; @bg-color: #DC6431; @bevel-color: #C9532B);
}

View File

@@ -1,3 +1,7 @@
.c-hidden {
display: none;
}
.c-clearfix {
.clearfix();
}

View File

@@ -126,6 +126,9 @@ h3 { font-size:110%; /*text-transform:uppercase;*/ }
a img { border: 0 none; }
a { text-decoration: none; color: #369; }
/* Polyfill for HTML5 hidden attribute: http://caniuse.com/#feat=hidden */
[hidden] { display: none; }
/*
a:active { border: 0 none;}
a:focus { -moz-outline-style: none; }
@@ -1847,6 +1850,104 @@ body.with-listing-chooser.explore-page #header .pagename {
border-bottom: 1px solid #a73a11;
}
.infobar.newsletterbar {
.box-sizing(border-box);
position: relative;
overflow: hidden;
min-height: 80px;
padding: 15px 20px 20px;
border: none;
border-radius: 2px;
background-color: #30659B;
header {
float: left;
height: 45px;
width: 325px;
}
a.newsletter-close {
position: absolute;
right: 3px;
top: 0;
font-size: 11px;
color: #CCC;
}
form {
margin-left: 340px;
margin-right: 150px;
max-width: 400px;
min-width: 150px;
line-height: 45px;
white-space: nowrap;
}
&.success {
header {
padding-left: 65px;
&:before {
content: "✓";
color: #80d654;
font-weight: bold;
font-size: 60px;
position: absolute;
top: 0;
left: 15px;
}
}
}
h1 {
margin: 0;
a:hover {
border-bottom: 1px dotted #999;
}
}
h2 {
color: white;
font-weight: normal;
font-size: 14px;
}
.c-form-group {
display: inline-block;
width: 100%;
}
/* Display error-feedback indicator inside the input */
.c-form-control-feedback-wrapper {
margin-left: -30px;
top: 5px;
}
input[type="email"] {
display: inline-block;
}
button {
.button-size(@padding-base-vertical; @padding-base-horizontal; 12px; 20px; 3px);
margin-left: 10px;
}
@media screen and (max-width: @screen-md-min) {
header {
float: none;
}
form {
margin: 10px 0 0;
}
.c-form-group {
max-width: 50%;
}
}
}
.locationbar {
margin: 5px;

View File

@@ -124,6 +124,7 @@ $(function() {
r.saved.init()
r.messages.init()
r.filter.init()
r.newsletter.ui.init()
} catch (err) {
r.sendError('Error during base.js init', err)
}

View File

@@ -0,0 +1,84 @@
r.newsletter = {
post: function(form) {
var email = $('input[name="email"]', form.$el).val();
var apiTarget = form.$el.attr('action');
var params = form.serialize();
params.push({name:'api_type', value:'json'});
return r.ajax({
url: apiTarget,
type: 'POST',
dataType: 'json',
data: params,
xhrFields: {
withCredentials: true
}
});
}
};
r.newsletter.ui = {
init: function() {
var newsletterBarSeen = !!store.get('newsletterbar.seen');
if (newsletterBarSeen || $('.newsletterbar').length === 0) {
return;
}
$('.newsletterbar').show();
$('.newsletter-signup').each(function(i, el) {
new r.newsletter.ui.NewsletterForm(el)
})
$('.newsletter-close').on('click', function() {
$('.newsletterbar').addClass('c-hidden');
});
store.set('newsletterbar.seen', true);
},
};
r.newsletter.ui.NewsletterForm = function() {
r.ui.Form.apply(this, arguments)
};
r.newsletter.ui.NewsletterForm.prototype = $.extend(new r.ui.Form(), {
showStatus: function() {
this.$el.find('.error').css('opacity', 1)
r.ui.Form.prototype.showStatus.apply(this, arguments)
},
_submit: function() {
r.analytics.fireGAEvent('newsletter-form', 'submit');
return r.newsletter.post(this);
},
_showSuccess: function() {
var parentEl = this.$el.parents('.newsletterbar');
parentEl.find('.result-message').text(r._('you\'ll get your first newsletter soon'));
parentEl.addClass('success');
parentEl.find('header').fadeTo(250, 1);
},
_handleResult: function(result) {
if (result.json.errors.length) {
r.ui.Form.prototype._handleResult.call(this, result);
}
var parentEl = this.$el.parents('.newsletterbar');
var calloutImg = parentEl.find('.subscribe-callout img');
var thanksImg = $('<img />').attr('src', calloutImg.data('thanks-src'))
.attr('alt', r._('thanks for subscribing'));
parentEl.find('header, form').fadeTo(250, 0, function() {
calloutImg.hide().after(thanksImg);
if (thanksImg.get(0).complete) {
this._showSuccess();
} else {
thanksImg.one("load", this._showSuccess);
}
}.bind(this));
}
})

View File

@@ -255,9 +255,13 @@ r.ui.Form = function(el) {
$(this).stateify('set', 'success');
})
.on('invalid.validator', function(e, resp) {
var error = r.utils.parseError(resp.errors[0]);
// resp may not always be set if client side validation triggered, like
// from input type=email
if (resp) {
var error = r.utils.parseError(resp.errors[0]);
$(this).stateify('set', 'error', error.message);
$(this).stateify('set', 'error', error.message);
}
})
.on('loading.validator', function(e) {
$(this).stateify('set', 'loading');

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.5 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.9 KiB

View File

@@ -0,0 +1,50 @@
## The contents of this file are subject to the Common Public Attribution
## License Version 1.0. (the "License"); you may not use this file except in
## compliance with the License. You may obtain a copy of the License at
## http://code.reddit.com/LICENSE. The License is based on the Mozilla Public
## License Version 1.1, but Sections 14 and 15 have been added to cover use of
## software over a computer network and provide for limited attribution for the
## Original Developer. In addition, Exhibit A has been modified to be
## consistent with Exhibit B.
##
## Software distributed under the License is distributed on an "AS IS" basis,
## WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
## the specific language governing rights and limitations under the License.
##
## The Original Code is reddit.
##
## The Original Developer is the Initial Developer. The Initial Developer of
## the Original Code is reddit Inc.
##
## All portions of the code written by reddit are Copyright (c) 2006-2015
## reddit Inc. All Rights Reserved.
###############################################################################
<%!
from r2.lib.template_helpers import static
%>
<%namespace file="utils.html" import="form_group" />
<section hidden class="infobar newsletterbar">
<header>
<h1 class="subscribe-callout">
<a href="/newsletter"><img src="${static('subscribe-header.svg')}" data-thanks-src="${static('subscribe-header-thanks.svg')}" alt="${_('subscribe to our newsletter')}"></a>
</h1>
<h2 class="result-message">${_('get the best of reddit, delivered once a week')}</h2>
</header>
<form class="newsletter-signup form-v2" method="post" action="/api/newsletter.json">
<%call expr="form_group('email', 'BAD_EMAIL', show_errors=True)">
<label for="email" class="screenreader-only">${_('email')}:</label>
<input value=""
name="email"
class="c-form-control"
type="email"
placeholder="${_('enter your email')}"
data-validate-url="/api/check_email.json"
data-validate-on="change blur">
</%call>
<button type="submit" class="c-btn c-btn-highlight">${_('subscribe')}</button>
</form>
<a href="#" class="newsletter-close" title="close">&times;</a>
</section>