mirror of
https://github.com/reddit-archive/reddit.git
synced 2026-01-14 17:38:04 -05:00
ReadNext: Add "read next" widget to sidebar on comments pages.
This commit is contained in:
@@ -45,6 +45,8 @@ from r2.models import (
|
||||
LINK_FLAIR,
|
||||
LabeledMulti,
|
||||
Link,
|
||||
ReadNextLink,
|
||||
ReadNextListing,
|
||||
Mod,
|
||||
ModSR,
|
||||
MultiReddit,
|
||||
@@ -52,6 +54,7 @@ from r2.models import (
|
||||
Printable,
|
||||
PromoCampaign,
|
||||
PromotionPrices,
|
||||
QueryBuilder,
|
||||
Random,
|
||||
RandomNSFW,
|
||||
RandomSubscription,
|
||||
@@ -1728,6 +1731,27 @@ class LinkInfoPage(Reddit):
|
||||
|
||||
def rightbox(self):
|
||||
rb = Reddit.rightbox(self)
|
||||
|
||||
if (c.site and not c.default_sr and c.render_style == 'html' and
|
||||
feature.is_enabled('read_next')):
|
||||
link = self.link
|
||||
|
||||
def wrapper_fn(thing):
|
||||
w = Wrapped(thing)
|
||||
w.render_class = ReadNextLink
|
||||
return w
|
||||
|
||||
def keep_fn(thing):
|
||||
return thing._fullname != link._fullname
|
||||
|
||||
query_obj = c.site.get_links('hot', 'all').query
|
||||
builder = QueryBuilder(query_obj,
|
||||
wrap=wrapper_fn, keep_fn=keep_fn,
|
||||
skip=True, num=10)
|
||||
listing = ReadNextListing(builder).listing()
|
||||
if len(listing.things):
|
||||
rb.append(ReadNext(c.site, listing.render()))
|
||||
|
||||
if not (self.link.promoted and not c.user_is_sponsor):
|
||||
if c.user_is_admin:
|
||||
from admin_pages import AdminLinkInfoBar
|
||||
@@ -3446,6 +3470,13 @@ class Ads(Templated):
|
||||
self.frame_id = "ad-frame"
|
||||
|
||||
|
||||
class ReadNext(Templated):
|
||||
def __init__(self, sr, links):
|
||||
Templated.__init__(self)
|
||||
self.sr = sr
|
||||
self.links = links
|
||||
|
||||
|
||||
class Embed(Templated):
|
||||
"""wrapper for embedding /help into reddit as if it were not on a separate wiki."""
|
||||
def __init__(self,content = ''):
|
||||
|
||||
@@ -932,6 +932,10 @@ class PromotedLink(Link):
|
||||
Printable.add_props(user, wrapped)
|
||||
|
||||
|
||||
class ReadNextLink(Link):
|
||||
_nodb = True
|
||||
|
||||
|
||||
class Comment(Thing, Printable):
|
||||
_data_int_props = Thing._data_int_props + ('reported', 'gildings')
|
||||
_defaults = dict(reported=0,
|
||||
|
||||
@@ -301,6 +301,10 @@ class SearchListing(LinkListing):
|
||||
return wrapped
|
||||
|
||||
|
||||
class ReadNextListing(Listing):
|
||||
pass
|
||||
|
||||
|
||||
class NestedListing(Listing):
|
||||
def __init__(self, *a, **kw):
|
||||
Listing.__init__(self, *a, **kw)
|
||||
|
||||
@@ -11,3 +11,4 @@
|
||||
@import "modal.less";
|
||||
@import "close.less";
|
||||
@import "toggles.less";
|
||||
@import "read-next.less";
|
||||
|
||||
134
r2/r2/public/static/css/components/read-next.less
Normal file
134
r2/r2/public/static/css/components/read-next.less
Normal file
@@ -0,0 +1,134 @@
|
||||
@read-next-color-background: @color-white;
|
||||
@read-next-color-header: #EFF7FF;
|
||||
@read-next-color-button-background: lighten(@color-ui-blue, 5%);
|
||||
@read-next-color-text: @color-semi-black;
|
||||
@read-next-color-text-light: @color-dark-grey;
|
||||
@read-next-height: 100px;
|
||||
@read-next-width: 300px;
|
||||
@read-next-link-height: 60px;
|
||||
@read-next-thumbnail-size: 50px;
|
||||
@zindex-read-next: @zindex-modal - 1;
|
||||
|
||||
@read-next-font-base: @font-x-small;
|
||||
@read-next-font-small: @font-xx-small;
|
||||
@read-next-line-base: @line-x-small;
|
||||
@read-next-line-large: @line-small;
|
||||
|
||||
.read-next-font-size(@font:@read-next-font-base, @line:@read-next-line-base) {
|
||||
.md-font-size(@read-next-font-base, @font, @line);
|
||||
}
|
||||
|
||||
.read-next-container {
|
||||
font-size: @base-font-keyword;
|
||||
}
|
||||
|
||||
.read-next {
|
||||
.md-base-font-size(@read-next-font-base);
|
||||
background: @read-next-color-background;
|
||||
border: 1px solid darken(@read-next-color-background, 15%);
|
||||
box-sizing: border-box;
|
||||
color: @read-next-color-text;
|
||||
height: @read-next-height;
|
||||
position: relative;
|
||||
-webkit-user-select: none;
|
||||
width: @read-next-width;
|
||||
z-index: @zindex-read-next;
|
||||
|
||||
&.fixed {
|
||||
border-bottom-width: 0;
|
||||
bottom: 0;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.read-next-header {
|
||||
background-color: @read-next-color-header;
|
||||
border-bottom: 1px solid darken(@read-next-color-header, 5%);
|
||||
color: @read-next-color-text-light;
|
||||
padding-left: @margin-small * 1px;
|
||||
padding-right: @margin-small * 1px;
|
||||
padding-top: @margin-x-small * 1px;
|
||||
}
|
||||
|
||||
.read-next-header-title {
|
||||
.read-next-font-size(@read-next-font-base, @read-next-line-large);
|
||||
}
|
||||
|
||||
.read-next-title {
|
||||
.read-next-font-size();
|
||||
display: block;
|
||||
max-height: @read-next-line-base * 3px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.read-next-nav {
|
||||
.read-next-font-size(@read-next-font-small);
|
||||
position: absolute;
|
||||
right: @margin-x-small * 1px;
|
||||
top: @margin-x-small * 1px;
|
||||
}
|
||||
|
||||
.read-next-dismiss,
|
||||
.read-next-button {
|
||||
.transform(scale(1, 1) translateY(0px));
|
||||
.transition(all, 0.2s);
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
height: @read-next-line-base * 1px;
|
||||
margin-left: @margin-x-small * 1px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: @read-next-line-base * 1px;
|
||||
|
||||
&:active {
|
||||
.transform(scale(1.01, 1.01) translateY(1px));
|
||||
}
|
||||
}
|
||||
|
||||
.read-next-button {
|
||||
background-color: @read-next-color-button-background;
|
||||
border-radius: 50%;
|
||||
color: @read-next-color-background;
|
||||
|
||||
&:active {
|
||||
background-color: darken(@read-next-color-button-background, 5%);
|
||||
}
|
||||
}
|
||||
|
||||
.read-next-list {
|
||||
padding: @margin-small * 1px;
|
||||
padding-top: @margin-x-small * 1px;
|
||||
}
|
||||
|
||||
.read-next-link {
|
||||
display: none;
|
||||
float: left;
|
||||
height: @read-next-link-height;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
|
||||
&.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.read-next-thumbnail {
|
||||
display: block;
|
||||
float: left;
|
||||
height: @read-next-thumbnail-size;
|
||||
margin-right: @margin-x-small * 1px;
|
||||
// magic number, but it just looks better
|
||||
margin-top: 3px;
|
||||
width: @read-next-thumbnail-size;
|
||||
|
||||
img {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.read-next-meta {
|
||||
.read-next-font-size(@read-next-font-small);
|
||||
color: @read-next-color-text-light;
|
||||
}
|
||||
}
|
||||
@@ -76,6 +76,7 @@
|
||||
@base-font-keyword: small;
|
||||
@base-font-keyword-size: 13; // small == 13px;
|
||||
|
||||
@font-xx-small: 10;
|
||||
@font-x-small: 12;
|
||||
@font-small: 14;
|
||||
@font-medium: 16;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@import "components/components.less";
|
||||
@import "components/variables.less";
|
||||
@import "components/components.less";
|
||||
@import "markdown.less";
|
||||
|
||||
.no-select {
|
||||
|
||||
@@ -74,6 +74,8 @@ r.ui.init = function() {
|
||||
|
||||
r.ui.initNewCommentHighlighting()
|
||||
|
||||
r.ui.initReadNext();
|
||||
|
||||
r.ui.initTimings()
|
||||
}
|
||||
|
||||
@@ -148,6 +150,95 @@ r.ui.highlightNewComments = function() {
|
||||
});
|
||||
}
|
||||
|
||||
r.ui.initReadNext = function() {
|
||||
var $readNextContainer = $('.read-next-container');
|
||||
|
||||
if ($readNextContainer.length) {
|
||||
this.readNext = new r.ui.ReadNext({
|
||||
el: $readNextContainer,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
r.ui.ReadNext = Backbone.View.extend({
|
||||
events: {
|
||||
'click .read-next-button.next': 'next',
|
||||
'click .read-next-button.prev': 'prev',
|
||||
'click .read-next-dismiss': 'dismiss',
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
this.$readNext = this.$el.find('.read-next');
|
||||
this.$links = this.$readNext.find('.read-next-link');
|
||||
this.numLinks = this.$links.length;
|
||||
|
||||
this.state = new Backbone.Model({
|
||||
fixed: false,
|
||||
index: -1,
|
||||
});
|
||||
|
||||
this.updateScroll = this.updateScroll.bind(this);
|
||||
window.addEventListener('scroll', this.updateScroll);
|
||||
this.state.on('change', this.render.bind(this));
|
||||
|
||||
this.updateScroll();
|
||||
this.state.set({
|
||||
index: 0,
|
||||
});
|
||||
},
|
||||
|
||||
next: function() {
|
||||
var currentIndex = this.state.get('index');
|
||||
var numLinks = this.numLinks;
|
||||
this.state.set({
|
||||
index: currentIndex = (currentIndex + 1) % numLinks,
|
||||
});
|
||||
},
|
||||
|
||||
prev: function() {
|
||||
var currentIndex = this.state.get('index');
|
||||
var numLinks = this.numLinks;
|
||||
this.state.set({
|
||||
index: currentIndex = (currentIndex + numLinks - 1) % numLinks,
|
||||
});
|
||||
},
|
||||
|
||||
dismiss: function() {
|
||||
this.$el.fadeOut();
|
||||
window.removeEventListener('scroll', this.updateScroll);
|
||||
},
|
||||
|
||||
updateScroll: function() {
|
||||
var scrollPosition = window.scrollY;
|
||||
var nodePosition = this.$el.position().top;
|
||||
|
||||
// stick to bottom
|
||||
var scrollOffset = window.innerHeight;
|
||||
var nodeOffset = this.$readNext.height();
|
||||
scrollPosition += scrollOffset;
|
||||
nodePosition += nodeOffset;
|
||||
|
||||
this.state.set({
|
||||
fixed: scrollPosition >= nodePosition,
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var currentIndex = this.state.get('index');
|
||||
var fixedPosition = this.state.get('fixed');
|
||||
|
||||
this.$links.removeClass('active');
|
||||
this.$links.eq(currentIndex).addClass('active');
|
||||
|
||||
if (fixedPosition) {
|
||||
this.$readNext.addClass('fixed');
|
||||
} else {
|
||||
this.$readNext.removeClass('fixed');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
r.ui.initTimings = function() {
|
||||
// return if we're not configured for sending stats
|
||||
if (!r.config.pageInfo.actionName || !r.config.stats_domain) {
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
%>
|
||||
|
||||
<%inherit file="printable.html"/>
|
||||
<%namespace file="utils.html" import="plain_link, thing_timestamp, edited, nsfw_stamp" />
|
||||
<%namespace file="utils.html" import="plain_link, thing_timestamp, edited, nsfw_stamp, thumbnail_img" />
|
||||
<%namespace file="printablebuttons.html" import="toggle_button" />
|
||||
|
||||
<%def name="numcol()">
|
||||
@@ -232,22 +232,7 @@ ${parent.thing_css_class(what)} ${"over18" if thing.over_18 else ""} ${thing.vis
|
||||
<%def name="thumbnail()">
|
||||
%if thing.thumbnail:
|
||||
<%call expr="make_link('thumbnail', 'thumbnail ' + (thing.thumbnail if thing.thumbnail_sprited else ''))">
|
||||
% if not thing.thumbnail_sprited:
|
||||
<%
|
||||
if hasattr(thing, 'thumbnail_size'):
|
||||
scaling_factor = 1
|
||||
if thing.thumbnail_size[0] > g.thumbnail_size[0]:
|
||||
# hidpi scaling, calculate in case hidpi changes definition in the future and
|
||||
# we have multiple sets of image dimensions. Currently should always be 1 or 2.
|
||||
# Width is always the maximum allowed, so we don't need to check height.
|
||||
scaling_factor = thing.thumbnail_size[0] // g.thumbnail_size[0]
|
||||
|
||||
size_str = "width='%d' height='%d'" % (thing.thumbnail_size[0] // scaling_factor, thing.thumbnail_size[1] // scaling_factor)
|
||||
else:
|
||||
size_str = ""
|
||||
%>
|
||||
<img src="${make_url_protocol_relative(thing.thumbnail)}" ${size_str} alt="">
|
||||
% endif
|
||||
${thumbnail_img(thing)}
|
||||
</%call>
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
42
r2/r2/templates/readnext.html
Normal file
42
r2/r2/templates/readnext.html
Normal file
@@ -0,0 +1,42 @@
|
||||
## 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.
|
||||
###############################################################################
|
||||
|
||||
<%namespace file="utils.html" import="plain_link"/>
|
||||
|
||||
<div class="read-next-container">
|
||||
<aside class="read-next">
|
||||
<header class="read-next-header">
|
||||
<div class="read-next-header-title">
|
||||
${_("also in")} 
|
||||
${plain_link(thing.sr.name, thing.sr.path, _sr_path=False)}
|
||||
</div>
|
||||
<nav class="read-next-nav">
|
||||
<span class="read-next-button prev"><</span>
|
||||
<span class="read-next-button next">></span>
|
||||
<span class="read-next-dismiss">X</span>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="read-next-list">
|
||||
${unsafe(thing.links)}
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
47
r2/r2/templates/readnextlink.html
Normal file
47
r2/r2/templates/readnextlink.html
Normal file
@@ -0,0 +1,47 @@
|
||||
## 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.
|
||||
###############################################################################
|
||||
|
||||
<%namespace file="utils.html" import="thumbnail_img" />
|
||||
|
||||
<a class="read-next-link" href="${thing.permalink}">
|
||||
<div class="read-next-meta">
|
||||
<span class="score">${thing.score} points</span> 
|
||||
%if thing.num_comments:
|
||||
· 
|
||||
<span class="comments">${thing.comment_label}</span> 
|
||||
%endif
|
||||
</div>
|
||||
%if thing.thumbnail:
|
||||
${self.thumbnail()}
|
||||
%endif
|
||||
<div class="read-next-title">
|
||||
${thing.title}
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<%def name="thumbnail()">
|
||||
%if thing.thumbnail and not thing.thumbnail_sprited:
|
||||
<div class="read-next-thumbnail">
|
||||
${thumbnail_img(thing)}
|
||||
</div>
|
||||
%endif
|
||||
</%def>
|
||||
29
r2/r2/templates/readnextlisting.html
Normal file
29
r2/r2/templates/readnextlisting.html
Normal file
@@ -0,0 +1,29 @@
|
||||
## 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.
|
||||
###############################################################################
|
||||
|
||||
<div class="listing read-next-listing">
|
||||
<div class="listing-contents">
|
||||
%for a in thing.things:
|
||||
${a}
|
||||
%endfor
|
||||
</div>
|
||||
</div>
|
||||
@@ -637,3 +637,22 @@ ${unsafe(txt)}
|
||||
${_("NSFW")}
|
||||
</acronym>
|
||||
</%def>
|
||||
|
||||
<%def name="thumbnail_img(thing)">
|
||||
%if thing.thumbnail and not thing.thumbnail_sprited:
|
||||
<%
|
||||
if hasattr(thing, 'thumbnail_size'):
|
||||
scaling_factor = 1
|
||||
if thing.thumbnail_size[0] > g.thumbnail_size[0]:
|
||||
# hidpi scaling, calculate in case hidpi changes definition in the future and
|
||||
# we have multiple sets of image dimensions. Currently should always be 1 or 2.
|
||||
# Width is always the maximum allowed, so we don't need to check height.
|
||||
scaling_factor = thing.thumbnail_size[0] // g.thumbnail_size[0]
|
||||
|
||||
size_str = "width='%d' height='%d'" % (thing.thumbnail_size[0] // scaling_factor, thing.thumbnail_size[1] // scaling_factor)
|
||||
else:
|
||||
size_str = ""
|
||||
%>
|
||||
<img src="${make_url_protocol_relative(thing.thumbnail)}" ${size_str} alt="">
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
Reference in New Issue
Block a user