Skip to content

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
...
  • 3 commits
  • 14 files changed
  • 0 commit comments
  • 2 contributors
Commits on Oct 24, 2013
@chromakode chromakode Add tab index to listing links.
This makes it slightly easier to browse listings by keyword.
a54d977
@ketralnis ketralnis Add backend for new gold feature: "remember my visits".
Store and mark what links users have already read, even between
computers.
ff002a4
@chromakode chromakode Add frontend for new gold feature: "remember my visits". 703336a
View
2 r2/example.ini
@@ -248,7 +248,6 @@ db_table_link = thing
db_table_account = thing
db_table_message = thing
db_table_savehide = relation, account, link
-db_table_click = relation, account, link
db_table_comment = thing
db_table_subreddit = thing
db_table_srmember = relation, subreddit, account
@@ -277,7 +276,6 @@ db_servers_link = main, main
db_servers_account = main
db_servers_message = main
db_servers_savehide = main
-db_servers_click = main
db_servers_comment = comment
db_servers_subreddit = comment
db_servers_srmember = comment
View
9 r2/r2/controllers/api.py
@@ -3632,3 +3632,12 @@ def GET_subreddit_recommendations(self, srs, to_omit):
def POST_server_seconds_visibility(self, form, jquery, seconds_visibility):
c.user.pref_public_server_seconds = seconds_visibility == "public"
c.user._commit()
+
+ @noresponse(VGold(),
+ links = VByName('links', thing_cls=Link, multiple=True,
+ limit=100))
+ def POST_store_visits(self, links):
+ if not c.user.pref_store_visits or not links:
+ return
+
+ LinkVisitsByAccount._visit(c.user, links)
View
1 r2/r2/controllers/post.py
@@ -99,6 +99,7 @@ def POST_unlogged_options(self, all_langs, pref_lang):
pref_collapse_read_messages = VBoolean("collapse_read_messages"),
pref_private_feeds = VBoolean("private_feeds"),
pref_local_js = VBoolean('local_js'),
+ pref_store_visits = VBoolean('store_visits'),
pref_show_adbox = VBoolean("show_adbox"),
pref_show_sponsors = VBoolean("show_sponsors"),
pref_show_sponsorships = VBoolean("show_sponsorships"),
View
1 r2/r2/lib/js.py
@@ -457,6 +457,7 @@ def use(self):
"login.js",
"flair.js",
"interestbar.js",
+ "visited.js",
"wiki.js",
"apps.js",
"gold.js",
View
2 r2/r2/lib/jsontemplates.py
@@ -391,7 +391,7 @@ class LinkJsonTemplate(ThingJsonTemplate):
author_flair_css_class="author_flair_css_class",
author_flair_text="author_flair_text",
banned_by="banned_by",
- clicked="clicked",
+ visited="visited",
distinguished="distinguished",
domain="domain",
downs="downvotes",
View
10 r2/r2/lib/template_helpers.py
@@ -116,9 +116,12 @@ def media_https_if_secure(url):
def js_config(extra_config=None):
+ logged = c.user_is_loggedin and c.user.name
+ gold = bool(logged and c.user.gold)
+
config = {
# is the user logged in?
- "logged": c.user_is_loggedin and c.user.name,
+ "logged": logged,
# the subreddit's name (for posts)
"post_site": c.site.name if not c.default_sr else "",
# are we in an iframe?
@@ -127,6 +130,11 @@ def js_config(extra_config=None):
"modhash": c.modhash or False,
# the current rendering style
"renderstyle": c.render_style,
+
+ # they're welcome to try to override this in the DOM because we just
+ # disable the features server-side if applicable
+ 'store_visits': gold and c.user.pref_store_visits,
+
# current domain
"cur_domain": get_domain(cname=c.frameless_cname, subreddit=False, no_www=True),
# where do ajax requests go?
View
1 r2/r2/models/account.py
@@ -63,6 +63,7 @@ class Account(Thing):
pref_frame_commentspanel = False,
pref_newwindow = False,
pref_clickgadget = 5,
+ pref_store_visits = False,
pref_public_votes = False,
pref_hide_from_robots = False,
pref_research = False,
View
51 r2/r2/models/link.py
@@ -207,13 +207,6 @@ def _unsave(self, user):
return self._unsomething(user, self._saved, 'save')
@classmethod
- def _clicked(cls, user, link):
- return cls._somethinged(Click, user, link, 'click')
-
- def _click(self, user):
- return self._something(Click, user, self._clicked, 'click')
-
- @classmethod
def _hidden(cls, user, link):
return cls._somethinged(SaveHide, user, link, 'hide')
@@ -374,6 +367,9 @@ def add_props(cls, user, wrapped):
pref_newwindow = user.pref_newwindow
cname = c.cname
site = c.site
+ now = datetime.now(g.tz)
+
+ saved = hidden = visited = {}
if user_is_admin:
# Checking if a domain's banned isn't even cheap
@@ -388,13 +384,14 @@ def add_props(cls, user, wrapped):
try:
saved = LinkSavesByAccount.fast_query(user, wrapped)
hidden = LinkHidesByAccount.fast_query(user, wrapped)
- except tdb_cassandra.TRANSIENT_EXCEPTIONS as e:
- g.log.warning("Cassandra save/hide lookup failed: %r", e)
- saved = hidden = {}
- clicked = {}
- else:
- saved = hidden = clicked = {}
+ if user.gold and user.pref_store_visits:
+ visited = LinkVisitsByAccount.fast_query(user, wrapped)
+
+ except tdb_cassandra.TRANSIENT_EXCEPTIONS as e:
+ # saved or hidden or may have been done properly, so go ahead
+ # with what we do have
+ g.log.warning("Cassandra save/hide/visited lookup failed: %r", e)
for item in wrapped:
show_media = False
@@ -460,10 +457,10 @@ def add_props(cls, user, wrapped):
if user_is_loggedin:
item.saved = (user, item) in saved
item.hidden = (user, item) in hidden
+ item.visited = (user, item) in visited
- item.clicked = bool(clicked.get((user, item, 'click')))
else:
- item.saved = item.hidden = item.clicked = False
+ item.saved = item.hidden = item.visited = False
item.num = None
item.permalink = item.make_permalink(item.subreddit)
@@ -569,7 +566,7 @@ def add_props(cls, user, wrapped):
item.fresh = not any((item.likes != None,
item.saved,
- item.clicked,
+ item.visited,
item.hidden,
item._deleted,
item._spam))
@@ -586,7 +583,8 @@ def add_props(cls, user, wrapped):
item.author = DeletedUser()
item.as_deleted = True
- item_age = datetime.now(g.tz) - item._date
+ item_age = now - item._date
+
if item_age.days > g.VOTE_AGE_LIMIT and item.promoted is None:
item.votable = False
else:
@@ -977,6 +975,7 @@ def add_props(cls, user, wrapped):
focal_comment = c.focal_comment
cname = c.cname
site = c.site
+ now = datetime.now(g.tz)
if user_is_loggedin:
gilded = [comment for comment in wrapped if comment.gildings > 0]
@@ -1031,7 +1030,7 @@ def add_props(cls, user, wrapped):
item.can_reply = False
if c.can_reply or (item.sr_id in can_reply_srs):
- age = datetime.now(g.tz) - item._date
+ age = now - item._date
if item.link.promoted or age.days < g.REPLY_AGE_LIMIT:
item.can_reply = True
@@ -1534,8 +1533,6 @@ def keep_item(self, wrapped):
return True
class SaveHide(Relation(Account, Link)): pass
-class Click(Relation(Account, Link)): pass
-
class GildedCommentsByAccount(tdb_cassandra.DenormalizedRelation):
_use_db = True
@@ -1709,6 +1706,20 @@ def _cached_queries(cls, user, thing):
from r2.lib.db import queries
return [queries.get_hidden_links(user)]
+class LinkVisitsByAccount(_SaveHideByAccount):
+ _use_db = True
+ _last_modified_name = 'Visit'
+ _views = []
+ _ttl = timedelta(days=7)
+ _write_consistency_level = tdb_cassandra.CL.ONE
+
+ @classmethod
+ def _visit(cls, user, things):
+ cls._savehide(user, things)
+
+ @classmethod
+ def _unvisit(cls, user, things):
+ cls._unsavehide(user, things)
class _ThingSavesBySubreddit(tdb_cassandra.View):
@classmethod
View
20 r2/r2/public/static/css/reddit.less
@@ -943,23 +943,9 @@ a.author { margin-right: 0.5em; }
overflow: hidden;
}
-.thing .title:visited { color: #551a8b }
-.thing .title.click { color: #551a8b }
-
-.thing .title.loggedin { color: blue }
-.thing .title.loggedin:visited { color: #551a8b }
-.thing .title.loggedin.click { color: #551a8b }
-.thing .title.loggedin.click:visited { color: #551a8b }
-
-.stickied-link { font-weight: bold; color: @moderator-color; }
-.thing.stickied {
- a.title { .stickied-link }
- a.title:visited { .stickied-link }
- a.title.loggedin { .stickied-link }
- a.title.loggedin:visited { .stickied-link }
- a.title.loggedin.click { .stickied-link }
- a.title.loggedin.click:visited { .stickied-link }
-}
+.thing .title:visited, .thing.visited .title { color: #551a8b }
+
+.thing.stickied a.title, .thing.stickied a.title:visited, .thing.stickied a.title.visited { font-weight: bold; color: @moderator-color; }
.sitetable { list-style-type: none; }
.ajaxhook { position: absolute; top: -1000px; left: 0px; }
View
1 r2/r2/public/static/js/base.js
@@ -86,6 +86,7 @@ $(function() {
r.analytics.init()
r.ui.init()
r.interestbar.init()
+ r.visited.init()
r.apps.init()
r.wiki.init()
r.gold.init()
View
2 r2/r2/public/static/js/reddit.js
@@ -522,8 +522,6 @@ function updateEventHandlers(thing) {
/* click on a title.. */
$(thing).filter(".link")
.find("a.title, a.comments").mousedown(function() {
- /* mark as clicked */
- $(this).addClass("click");
/* set the click cookie. */
add_thing_to_cookie(this, "recentclicks2");
});
View
51 r2/r2/public/static/js/visited.js
@@ -0,0 +1,51 @@
+r.visited = {
+ key: 'visited',
+
+ init: function() {
+ this.sendVisits = _.throttle(this._sendVisits, 100)
+ if (r.config.logged && r.config.store_visits) {
+ $('.content').on('mousedown keydown', '.link:not(.visited) a.title, .link:not(.visited) a.thumbnail', _.bind(this.onVisit, this))
+
+ // listen for custom "visit" event for third-party extensions to trigger in a non-UI specific way
+ $('.content').on('visit', '.link:not(.visited)', _.bind(this.onVisit, this))
+
+ // send any pending visits
+ this.sendVisits()
+ }
+ },
+
+ onVisit: function(ev) {
+ if (ev.type == 'keydown' && ev.which != 13) {
+ // only handle enter key presses
+ return
+ }
+ this.storeVisit($(ev.target).closest('.thing').data('fullname'))
+ this.sendVisits()
+ },
+
+ storeVisit: function(fullname) {
+ var fullnames = store.safeGet(this.key) || []
+ fullnames.push(fullname)
+ store.safeSet(this.key, fullnames)
+ },
+
+ _sendVisits: function() {
+ var fullnames = store.safeGet(this.key) || []
+ if (!fullnames.length) {
+ return
+ }
+
+ fullnames = _.last(_.uniq(fullnames), 100)
+
+ r.ajax({
+ type: 'POST',
+ url: '/api/store_visits',
+ data: {
+ 'links': fullnames.join(',')
+ }
+ })
+
+ store.safeSet(this.key, [])
+ $.things.apply($, fullnames).addClass("visited");
+ }
+}
View
11 r2/r2/templates/link.html
@@ -41,9 +41,12 @@
</span>
</%def>
-<%def name="make_link(name, css_class)">
- <a class="${css_class} ${ c.user_is_loggedin and 'loggedin' or ''} ${thing.clicked and 'click' or ''}"
+<%def name="make_link(name, css_class, tabindex=0)">
+ <a class="${css_class} ${ c.user_is_loggedin and 'loggedin' or ''}"
href="${thing.href_url}"
+ %if tabindex:
+ tabindex="${tabindex}"
+ %endif
%if thing.nofollow:
rel="nofollow"
%endif
@@ -88,7 +91,7 @@
%if c.site.link_flair_position == 'left':
<%call expr="flair()" />
%endif
- <%call expr="make_link('title', 'title')">
+ <%call expr="make_link('title', 'title', tabindex=1)">
${thing.title}
</%call>
%if c.site.link_flair_position == 'right':
@@ -157,7 +160,7 @@
</%def>
<%def name="thing_css_class(what)" buffered="True">
-${parent.thing_css_class(what)} ${"over18" if thing.over_18 else ""}
+${parent.thing_css_class(what)} ${"over18" if thing.over_18 else ""} ${thing.visited and 'visited' or ''}
%if c.user.pref_show_link_flair and (thing.flair_text or thing.flair_css_class):
<% css = thing.flair_css_class or '' %>
linkflair ${' '.join('linkflair-' + c for c in css.split())}
View
3 r2/r2/templates/prefoptions.html
@@ -316,6 +316,9 @@
${checkbox(_("show sponsorships"), "show_sponsorships")}
&#32;<span class="little gray">(${_("the 300x100 'sponsored by...' images that sometimes appear in sidebars")})</span>
<br/>
+ ${checkbox(_("remember what links I've visited"), "store_visits")}
+ &#32;<span class="little gray">(${_("we'll remember and mark what links you've already read, even between computers")})</span>
+ <br/>
<br/>
${checkbox(_("highlight new comments"), "highlight_new_comments")}
&#32;<span class="little gray">

No commit comments for this range

Something went wrong with that request. Please try again.