Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add subreddit interest discovery bar.

  • Loading branch information...
commit 613b27de9eec1f6e982bbcd5e1cdfbb252600531 1 parent df628d4
Max Goodman authored
View
2  r2/example.ini
@@ -498,3 +498,5 @@ beaker.session_secret = somesecret
frontpage_dart = false
# spotlight links for subreddit discovery
sr_discovery_links =
+spotlight_interest_sub_p = .05
+spotlight_interest_nosub_p = .1
View
35 r2/r2/controllers/api.py
@@ -59,6 +59,7 @@
from r2.lib.filters import safemarkdown
from r2.lib.scraper import str_to_image
from r2.controllers.api_docs import api_doc, api_section
+from r2.lib.cloudsearch import basic_query
import csv
from collections import defaultdict
@@ -2783,3 +2784,37 @@ def POST_disable_otp(self, form, jquery):
c.user.otp_secret = ""
c.user._commit()
form.redirect("/prefs/otp")
+
+ @json_validate(query=VPrintable("query", max_length=50))
+ @api_doc(api_section.subreddits, extensions=["json"])
+ def GET_subreddits_by_topic(self, responder, query):
+ if not g.CLOUDSEARCH_SEARCH_API:
+ return []
+
+ if not query or not query.strip():
+ return []
+
+ exclude = Subreddit.default_subreddits()
+
+ q = basic_query(query,
+ facets={"reddit":{"sort":"-sum(text_relevance)", "count":20}},
+ record_stats=True)
+ if not q["facets"]:
+ return []
+
+ sr_facets = [f["value"] for f in q["facets"]["reddit"]["constraints"]]
+ srs = Subreddit._by_name(sr_facets)
+
+ results = []
+ for sr_name in sr_facets:
+ sr = srs.get(sr_name)
+ if (sr._id in exclude or (sr.over_18 and not c.over18)
+ or not sr.can_view(c.user)
+ or sr.type == "archived"):
+ continue
+
+ results.append({
+ "name": sr_name,
+ })
+
+ return results
View
11 r2/r2/controllers/listingcontroller.py
@@ -313,6 +313,17 @@ def spotlight(self):
max_num = self.listing_obj.max_num,
max_score = self.listing_obj.max_score).listing()
+ has_subscribed = c.user.has_subscribed
+ promo_visible = promote.is_promo(s.lookup[vislink])
+ if not promo_visible:
+ prob = g.live_config['spotlight_interest_sub_p'
+ if has_subscribed else
+ 'spotlight_interest_nosub_p']
+ if random.random() < prob:
+ bar = InterestBar(has_subscribed)
+ s.spotlight_items.insert(pos, bar)
+ s.visible_item = bar
+
if len(s.things) > 0:
# only pass through a listing if the links made it
# through our builder
View
4 r2/r2/lib/app_globals.py
@@ -180,6 +180,10 @@ class Globals(object):
ConfigValue.bool: [
'frontpage_dart',
],
+ ConfigValue.float: [
+ 'spotlight_interest_sub_p',
+ 'spotlight_interest_nosub_p',
+ ],
ConfigValue.tuple: [
'sr_discovery_links',
],
View
1  r2/r2/lib/js.py
@@ -275,6 +275,7 @@ def use(self):
"login.js",
"analytics.js",
"flair.js",
+ "interestbar.js",
"reddit.js",
)
View
5 r2/r2/lib/pages/pages.py
@@ -3619,3 +3619,8 @@ def __init__(self, id, title, interval, columns, rows,
self.classes = " ".join(classes)
Templated.__init__(self)
+
+class InterestBar(Templated):
+ def __init__(self, has_subscribed):
+ self.has_subscribed = has_subscribed
+ Templated.__init__(self)
View
1  r2/r2/lib/strings.py
@@ -189,6 +189,7 @@
view_subreddit_traffic = _("view subreddit traffic"),
an_error_occurred = _("an error occurred"),
+ an_error_occurred_friendly = _("an error occurred. please try again later!"),
)
class StringHandler(object):
View
115 r2/r2/public/static/css/reddit.css
@@ -5709,3 +5709,118 @@ tr.gold-accent + tr > td {
.users-online .word, .users-online .number:after {
cursor: help;
}
+
+.sr-interest-bar {
+ position: relative;
+ background: #cee3f8 url(../snoo-upside-down.png) 15px top no-repeat;
+ padding: 5px;
+ overflow: hidden;
+ border: 1px solid #336699;
+ margin-bottom: 10px;
+}
+
+.organic-listing .sr-interest-bar {
+ border: none;
+ margin: 0;
+}
+
+.sr-interest-bar .bubble {
+ position: relative;
+ margin-left: 85px;
+ margin-right: 68px;
+ max-width: 700px;
+ font-size: 13px;
+ background: white;
+ padding: 6px;
+ border-radius: 8px;
+}
+
+.sr-interest-bar .bubble:after {
+ position: absolute;
+ display: block;
+ content: '';
+ border: 10px solid;
+ border-style: solid solid outset;
+ border-color: transparent;
+ border-right-color: white;
+ left: -20px;
+ top: 15px;
+}
+
+.sr-interest-bar .bubble p {
+ margin: 6px 3px;
+ margin-top: 0;
+}
+
+.sr-interest-bar .subscribe {
+ background-image: url(../bg-button-add.png); /* SPRITE stretch-x */
+ border: 1px solid #444;
+ border-radius: 3px;
+ padding: 0 6px;
+ color: white;
+ font-weight: bold;
+}
+
+.sr-interest-bar .query-box {
+ position: relative;
+ padding: 2px 4px;
+ border: 2px solid #979797;
+ border-radius: 5px;
+}
+
+.sr-interest-bar.focus .query-box {
+ border-color: #5f99cf;
+}
+
+.sr-interest-bar.error .query-box {
+ border-color: #cf5e5e;
+}
+
+.sr-interest-bar .error-caption, .sr-interest-bar.error .caption {
+ display: none;
+}
+
+.sr-interest-bar.error .error-caption {
+ display: block;
+}
+
+.sr-interest-bar .query {
+ width: 100%;
+ font-size: 20px;
+ margin: 0;
+ padding: 0;
+ border: none;
+ outline: none;
+}
+
+.sr-interest-bar .throbber {
+ position: absolute;
+ right: 3px;
+ top: 5px;
+}
+
+.sr-interest-bar ul.results {
+ margin: 0;
+ margin-top: 6px;
+ padding-top: 2px;
+ border-top: 1px dotted #bbb;
+ display: none;
+}
+
+.sr-interest-bar li {
+ display: inline-block;
+ margin: 6px 3px;
+}
+
+.sr-interest-bar a {
+ padding: 1px 2px;
+}
+
+.sr-interest-bar a:hover {
+ text-decoration: underline;
+}
+
+.sr-interest-bar .results .random {
+ color: gray;
+ font-weight: bold;
+}
View
2  r2/r2/public/static/js/analytics.js
@@ -113,7 +113,7 @@ r.analytics = {
}
r.analytics.breadcrumbs = {
- selector: '.thing, .side, .sr-list, .srdrop, .tagline, .md, .organic-listing, .gadget, a, button, input',
+ selector: '.thing, .side, .sr-list, .srdrop, .tagline, .md, .organic-listing, .gadget, .sr-interest-bar, a, button, input',
init: function() {
this.hasSessionStorage = this._checkSessionStorage()
View
1  r2/r2/public/static/js/base.js
@@ -13,4 +13,5 @@ $(function() {
r.login.ui.init()
r.analytics.init()
r.ui.HelpBubble.init()
+ r.interestbar.init()
})
View
81 r2/r2/public/static/js/interestbar.js
@@ -0,0 +1,81 @@
+r.interestbar = {
+ init: function() {
+ new r.ui.InterestBar($('.sr-interest-bar'))
+ }
+}
+
+r.ui.InterestBar = function() {
+ r.ui.Base.apply(this, arguments)
+ this.$query = this.$el.find('.query')
+ this.queryChangedDebounced = _.debounce($.proxy(this, 'queryChanged'), 500)
+ this.$query.on('keyup', $.proxy(this, 'keyPressed'))
+
+ this.$query
+ .on('focus', $.proxy(function() {
+ this.$el.addClass('focus')
+ }, this))
+ .on('blur', $.proxy(function() {
+ this.$el.removeClass('focus')
+ }, this))
+}
+r.ui.InterestBar.prototype = {
+ keyPressed: function() {
+ var query = this.$query.val()
+ query = $.trim(query)
+ if (query != this._lastQuery) {
+ this._lastQuery = query
+ this.$el.addClass('working')
+ this.queryChangedDebounced(query)
+ }
+
+ if (!query) {
+ this.$el.removeClass('working error')
+ this.hideResults()
+ }
+ },
+
+ queryChanged: function(query) {
+ if (query) {
+ $.ajax({
+ url: '/api/subreddits_by_topic.json',
+ data: {'query': query},
+ success: $.proxy(this, 'displayResults'),
+ error: $.proxy(this, 'displayError')
+ })
+ }
+ },
+
+ displayResults: function(results) {
+ this.$el.removeClass('working error')
+
+ var first = this.$el.find('.results li:first'),
+ last = this.$el.find('.results li:last')
+
+ var item = _.template(
+ '<li><a href="/r/<%= name %>" target="_blank">'
+ +'/r/<%= name %>'
+ +'</a></li>'
+ )
+
+ this.$el.find('.results')
+ .empty()
+ .append(first)
+ .append(_.map(results, item).join(''))
+ .append(last)
+ .slideDown(150)
+ },
+
+ hideResults: function() {
+ this.$el.find('.results').slideUp(150)
+ },
+
+ displayError: function(xhr) {
+ this.$el
+ .removeClass('working')
+ .addClass('error')
+ .find('.error-caption')
+ .text(r.strings.an_error_occurred_friendly + ' (' + xhr.status + ')')
+
+ this.hideResults()
+ }
+}
View
2  r2/r2/public/static/js/reddit.js
@@ -334,6 +334,8 @@ function organic_help(listing, thing) {
help.find('.help-section').hide()
if (thing.hasClass('promoted')) {
help.find('.help-promoted').show()
+ } else if (thing.hasClass('interestbar')) {
+ help.find('.help-interestbar').show()
} else {
help.find('.help-organic').show()
}
View
BIN  r2/r2/public/static/snoo-upside-down.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
45 r2/r2/templates/interestbar.html
@@ -0,0 +1,45 @@
+## 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-2012
+## reddit Inc. All Rights Reserved.
+###############################################################################
+
+<div class="sr-interest-bar">
+ <div class="bubble">
+ <%
+ if thing.has_subscribed:
+ msg = _("ready for something new? %s subscribe %s to some new subreddits.")
+ else:
+ msg = _("it looks like you haven't %s subscribed %s to any subreddits yet. want some ideas?")
+
+ pre, sub, post = msg.split("%s")
+ %>
+ <p class="caption">${pre}&#32;<span class="subscribe">${sub}</span>&#32;${post}</p>
+ <p class="error-caption"></p>
+ <div class="query-box"><input class="query" placeholder="${_('what are you interested in?')}"></input><div class="throbber"></div></div>
+ <ul class="results">
+ <li>${_('try these:')}</li>
+ <li>
+ <a href="/r/random" class="random" target="_blank">
+ <span class="name">${_("serendipity")}</span>
+ </a>
+ </li>
+ </ul>
+ </div>
+</div>
View
18 r2/r2/templates/spotlightlisting.html
@@ -21,9 +21,10 @@
###############################################################################
<%namespace file="printablebuttons.html" import="ynbutton"/>
-<%namespace file="utils.html" import="text_with_links"/>
+<%namespace file="utils.html" import="tags, text_with_links"/>
<%
from r2.lib.template_helpers import static
+ from r2.lib.wrapped import Templated
%>
<div id="siteTable_organic" class="organic-listing">
@@ -31,7 +32,11 @@
seen = set([])
%>
%for name in thing.spotlight_items:
- %if name in seen:
+ %if isinstance(name, Templated):
+ <div class="thing ${type(name).__name__.lower()}" ${tags(style="display:none" if thing.visible_item != name else None)}>
+ ${unsafe(name.render())}
+ </div>
+ %elif name in seen:
<% pass %>
%elif name in thing.lookup:
<% seen.add(name) %>
@@ -77,6 +82,15 @@
hidden_data = dict(id="organic"))}
%endif
</div>
+ <div class="help-section help-interestbar">
+ <p>${_("Enter a keyword or topic to discover new subreddits around your interests. Be specific!")}</p>
+ <p>
+ ${text_with_links(
+ _("You can access this tool at any time on the %(reddits)s page."),
+ reddits=dict(link_text="/reddits/", path="/reddits/")
+ )}
+ </p>
+ </div>
</div>
</div>
</div>
Please sign in to comment.
Something went wrong with that request. Please try again.