Merge branch 'master' into organizer

* master:
  Preventing brentozar.com from being subscribed to because their lawyer sent me a Cease and Desist nastygram.
  Adding lisp syntax highlighting. Also upgrading syntax highlighter.
  Per @IFTTT's request, changing ifttt key to meta.
  Make full mark-read action lifecycle recount stories, not just first mark.
  Improve chaining of updated cursor data from Reading activity down through to adapter and fragments.
This commit is contained in:
Samuel Clay 2014-10-14 14:36:55 -07:00
commit 4c4c48f195
9 changed files with 147 additions and 152 deletions

View file

@ -455,14 +455,14 @@ def api_unread_story(request, trigger_slug=None):
"Site": feed and feed['title'],
"SiteURL": feed and feed['website'],
"SiteRSS": feed and feed['address'],
"ifttt": {
"meta": {
"id": story['story_hash'],
"timestamp": int(story['story_date'].strftime("%s"))
},
})
if after:
entries = sorted(entries, key=lambda s: s['ifttt']['timestamp'])
entries = sorted(entries, key=lambda s: s['meta']['timestamp'])
logging.user(request, "~FYChecking unread%s stories with ~SB~FCIFTTT~SN~FY: ~SB%s~SN - ~SB%s~SN stories" % (" ~SBfocus~SN" if trigger_slug == "new-unread-focus-story" else "", feed_or_folder, len(entries)))
@ -511,14 +511,14 @@ def api_saved_story(request):
"Site": feed and feed['title'],
"SiteURL": feed and feed['website'],
"SiteRSS": feed and feed['address'],
"ifttt": {
"meta": {
"id": story['story_hash'],
"timestamp": int(story['starred_date'].strftime("%s"))
},
})
if after:
entries = sorted(entries, key=lambda s: s['ifttt']['timestamp'])
entries = sorted(entries, key=lambda s: s['meta']['timestamp'])
logging.user(request, "~FCChecking saved stories from ~SBIFTTT~SB: ~SB%s~SN - ~SB%s~SN stories" % (story_tag if story_tag else "[All stories]", len(entries)))
@ -597,14 +597,14 @@ def api_shared_story(request):
"Site": feed and feed['title'],
"SiteURL": feed and feed['website'],
"SiteRSS": feed and feed['address'],
"ifttt": {
"meta": {
"id": story['story_hash'],
"timestamp": int(story['shared_date'].strftime("%s"))
},
})
if after:
entries = sorted(entries, key=lambda s: s['ifttt']['timestamp'])
entries = sorted(entries, key=lambda s: s['meta']['timestamp'])
logging.user(request, "~FMChecking shared stories from ~SB~FCIFTTT~SN~FM: ~SB~FM%s~FM~SN - ~SB%s~SN stories" % (blurblog_user, len(entries)))

View file

@ -59,6 +59,10 @@ from utils.ratelimit import ratelimit
from vendor.timezones.utilities import localtime_for_timezone
BANNED_URLS = [
"brentozar.com",
]
@never_cache
@render_to('reader/dashboard.xhtml')
def index(request, **kwargs):
@ -1574,6 +1578,9 @@ def add_url(request):
if not url:
code = -1
message = 'Enter in the website address or the feed URL.'
elif any([(banned_url in url) for banned_url in BANNED_URLS]):
code = -1
message = "The publisher of this website has banned NewsBlur."
else:
if new_folder:
usf, _ = UserSubscriptionFolders.objects.get_or_create(user=request.user)

View file

@ -194,41 +194,34 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
synchronized (STORIES_MUTEX) {
if (cursor != null) {
readingAdapter.swapCursor(cursor);
stories = cursor;
}
if (cursor == null) return;
readingAdapter.swapCursor(cursor);
stories = cursor;
// if this is the first time we've found a cursor, we know the onCreate chain is done
if (this.pager == null) {
int currentUnreadCount = getUnreadCount();
if (currentUnreadCount > this.startingUnreadCount ) {
this.startingUnreadCount = currentUnreadCount;
}
// set up the pager after the unread count, so the first mark-read doesn't happen too quickly
setupPager();
}
try {
readingAdapter.notifyDataSetChanged();
checkStoryCount(pager.getCurrentItem());
if (this.unreadSearchLatch != null) {
this.unreadSearchLatch.countDown();
}
ReadingItemFragment fragment = getReadingFragment();
if (fragment != null ) {
fragment.updateStory(readingAdapter.getStory(pager.getCurrentItem()));
fragment.updateSaveButton();
updateOverlayNav();
updateOverlayText();
}
} catch (IllegalStateException ise) {
// sometimes the pager is already shutting down by the time the callback finishes
finish();
}
checkStoryCount(pager.getCurrentItem());
if (this.unreadSearchLatch != null) {
this.unreadSearchLatch.countDown();
}
updateOverlayNav();
updateOverlayText();
}
}
@ -328,7 +321,6 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
protected void handleUpdate() {
enableMainProgress(NBSyncService.isFeedSetSyncing(this.fs));
updateCursor();
readingAdapter.updateAllFragments();
}
private void updateCursor() {

View file

@ -108,12 +108,18 @@ public abstract class ReadingAdapter extends FragmentStatePagerAdapter {
return frag.get();
}
public void updateAllFragments() {
for (int i=0; i<cachedFragments.size(); i++) {
@Override
public void notifyDataSetChanged() {
super.notifyDataSetChanged();
// go one step further than the default pageradapter and also refresh the
// story object inside each fragment we have active
for (int i=0; i<stories.getCount(); i++) {
WeakReference<ReadingItemFragment> frag = cachedFragments.get(i);
if (frag == null) continue;
ReadingItemFragment rif = frag.get();
if (rif == null) continue;
rif.offerStoryUpdate(getStory(i));
rif.handleUpdate();
}
}

View file

@ -298,17 +298,29 @@ public class BlurDatabaseHelper {
}
public void setStoryReadState(String hash, boolean read) {
ContentValues values = new ContentValues();
values.put(DatabaseConstants.STORY_READ, read);
values.put(DatabaseConstants.STORY_READ_THIS_SESSION, read);
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_HASH + " = ?", new String[]{hash});}
Cursor c = getStory(hash);
if (c.getCount() < 1) {
Log.w(this.getClass().getName(), "story removed before finishing mark-read");
return;
}
Story story = Story.fromCursor(c);
if (story == null) {
Log.w(this.getClass().getName(), "story removed before finishing mark-read");
return;
}
setStoryReadState(story, read);
}
/**
* Marks a story (un)read and also adjusts unread counts for it.
*/
public void setStoryReadState(Story story, boolean read) {
setStoryReadState(story.storyHash, read);
// read flag
ContentValues values = new ContentValues();
values.put(DatabaseConstants.STORY_READ, read);
values.put(DatabaseConstants.STORY_READ_THIS_SESSION, read);
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_HASH + " = ?", new String[]{story.storyHash});}
// non-social feed count
refreshFeedCounts(FeedSet.singleFeed(story.feedId));
// social feed counts

View file

@ -276,22 +276,13 @@ public class ReadingItemFragment extends NbFragment implements ClassifierDialogF
});
}
public void updateSaveButton() {
private void updateSaveButton() {
if (view == null) { return; }
Button saveButton = (Button) view.findViewById(R.id.save_story_button);
if (saveButton == null) { return; }
saveButton.setText(story.starred ? R.string.unsave_this : R.string.save_this);
}
public void updateStory(Story story) {
if (story != null ) {
this.story = story;
if (selectedFeedView == DefaultFeedView.TEXT && originalText == null) {
loadOriginalText();
}
}
}
private void setupShareButton() {
Button shareButton = (Button) view.findViewById(R.id.share_story_button);
@ -450,7 +441,19 @@ public class ReadingItemFragment extends NbFragment implements ClassifierDialogF
((Reading) parent).enableLeftProgressCircle(loading);
}
/**
* Lets the pager offer us an updated version of our story when a new cursor is
* cycled in. This class takes the responsibility of ensureing that the cursor
* index has not shifted, though, by checking story IDs.
*/
public void offerStoryUpdate(Story story) {
if (story == null) return;
if (! TextUtils.equals(story.storyHash, this.story.storyHash)) return;
this.story = story;
}
public void handleUpdate() {
updateSaveButton();
reloadStoryContent();
}

View file

@ -2,161 +2,135 @@
Docco style used in http://jashkenas.github.com/docco/ converted by Simon Madine (@thingsinjars)
*/
pre, code {
background: #F4F6F2;
padding: 0.15em 0.35em;
}
pre {
padding: 0.5em;
}
pre code {
padding: 0;
.hljs {
display: block;
overflow-x: auto;
padding: 0.5em;
color: #000;
background: #F4F6F2;
-webkit-text-size-adjust: none;
}
pre .comment,
pre .template_comment,
pre .diff .header,
pre .javadoc {
.hljs-comment,
.hljs-template_comment,
.diff .hljs-header,
.hljs-javadoc {
color: #408080;
font-style: italic
font-style: italic;
}
pre .keyword,
pre .assignment,
pre .literal,
pre .css .rule .keyword,
pre .winutils,
pre .javascript .title,
pre .lisp .title,
pre .subst {
.hljs-keyword,
.assignment,
.hljs-literal,
.css .rule .hljs-keyword,
.hljs-winutils,
.javascript .hljs-title,
.lisp .hljs-title,
.hljs-subst {
color: #954121;
}
pre .number,
pre .hexcolor {
color: #40a070
.hljs-number,
.hljs-hexcolor {
color: #40a070;
}
pre .string,
pre .tag .value,
pre .phpdoc,
pre .tex .formula {
.hljs-string,
.hljs-tag .hljs-value,
.hljs-phpdoc,
.hljs-dartdoc,
.tex .hljs-formula {
color: #219161;
}
pre .title,
pre .id {
color: #19469D;
.hljs-title,
.hljs-id {
color: #19469d;
}
pre .params {
color: #00F;
.hljs-params {
color: #00f;
}
pre .javascript .title,
pre .lisp .title,
pre .subst {
font-weight: normal
.javascript .hljs-title,
.lisp .hljs-title,
.hljs-subst {
font-weight: normal;
}
pre .class .title,
pre .haskell .label,
pre .tex .command {
.hljs-class .hljs-title,
.haskell .hljs-label,
.tex .hljs-command {
color: #458;
font-weight: bold
}
pre .tag,
pre .tag .title,
pre .rules .property,
pre .django .tag .keyword {
color: #000080;
}
pre .keyword,
pre .id,
pre .title,
pre .built_in,
pre .aggregate,
pre .css .tag,
pre .javadoctag,
pre .phpdoc,
pre .yardoctag,
pre .smalltalk .class,
pre .winutils,
pre .bash .variable,
pre .apache .tag,
pre .go .typename,
pre .tex .command,
pre .asciidoc .strong,
pre .markdown .strong,
pre .request,
pre .status {
font-weight: bold;
}
pre .attribute,
pre .variable,
pre .instancevar,
pre .lisp .body {
color: #008080
.hljs-tag,
.hljs-tag .hljs-title,
.hljs-rules .hljs-property,
.django .hljs-tag .hljs-keyword {
color: #000080;
font-weight: normal;
}
pre .regexp {
color: #B68
.hljs-attribute,
.hljs-variable,
.instancevar,
.lisp .hljs-body {
color: #008080;
}
pre .class {
.hljs-regexp {
color: #b68;
}
.hljs-class {
color: #458;
font-weight: bold
font-weight: bold;
}
pre .symbol,
pre .ruby .symbol .string,
pre .ruby .symbol .keyword,
pre .ruby .symbol .keymethods,
pre .lisp .keyword,
pre .tex .special,
pre .input_number {
color: #990073
.hljs-symbol,
.ruby .hljs-symbol .hljs-string,
.ruby .hljs-symbol .hljs-keyword,
.ruby .hljs-symbol .keymethods,
.lisp .hljs-keyword,
.tex .hljs-special,
.input_number {
color: #990073;
}
pre .builtin,
pre .constructor,
pre .built_in,
pre .lisp .title {
color: #0086b3
.builtin,
.constructor,
.hljs-built_in,
.lisp .hljs-title {
color: #0086b3;
}
pre .preprocessor,
pre .pragma,
pre .pi,
pre .doctype,
pre .shebang,
pre .cdata {
.hljs-preprocessor,
.hljs-pragma,
.hljs-pi,
.hljs-doctype,
.hljs-shebang,
.hljs-cdata {
color: #999;
font-weight: bold
font-weight: bold;
}
pre .deletion {
background: #fdd
.hljs-deletion {
background: #fdd;
}
pre .addition {
background: #dfd
.hljs-addition {
background: #dfd;
}
pre .diff .change {
background: #0086b3
.diff .hljs-change {
background: #0086b3;
}
pre .chunk {
color: #aaa
.hljs-chunk {
color: #aaa;
}
pre .tex .formula {
.tex .hljs-formula {
opacity: 0.5;
}

View file

@ -566,6 +566,7 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
attach_syntax_highlighter_handler: function() {
_.delay(_.bind(function() {
// hljs.configure({useBR: true}); // Don't use
this.$('pre').each(function(i, e) {
hljs.highlightBlock(e);
});

File diff suppressed because one or more lines are too long