Merge branch 'master' into feed_settings

* master:
  Adding iphone icon as a logo that I can submit to sites.
  Brief cleanup.
  Finished suggestion to add unread counts to window title. Now a preference.
  Adding color transform to read stories in Feed view.
  As per a suggestion, making the max-width of the Feed view set to 700px. Great idea.
  Fixing bug in send_story_email -- newlines in the subject line. Doh. Also adding story_Date to read_stories, in the hopes it could fix another bug for bad unread counts.
  Fixing issues around broken existing stories on secondary vs primary.
  Fixing mark_feed_stories_as_read, and fixing a bad MStory query.
  Finishing the revamping mark as read button. Now confirms, as well as allows choice between entire feed/folder or just visible stories.
  Adding bullshit user agent string because some sites are sniffing for browsers in order to serve the correct site.
  Swapping out Instapaper and Email.
  Separating out mark read button in iOS app to be for either visible stories or all stories. Needs hookups for visible stories and folders.
This commit is contained in:
Samuel Clay 2011-11-12 15:37:30 -08:00
commit 422242a06f
17 changed files with 274 additions and 77 deletions

View file

@ -167,7 +167,9 @@ class UserSubscription(models.Model):
continue
now = datetime.datetime.utcnow()
date = now if now > story.story_date else story.story_date # For handling future stories
m = MUserStory(story=story, user_id=self.user.pk, feed_id=self.feed.pk, read_date=date, story_id=story_id)
m = MUserStory(story=story, user_id=self.user.pk,
feed_id=self.feed.pk, read_date=date,
story_id=story_id, story_date=story.story_date)
try:
m.save()
except OperationError, e:
@ -180,9 +182,10 @@ class UserSubscription(models.Model):
logging.user(request, "~BRRead now date: %s, original read: %s, story_date: %s." % (m.read_date, original_m.read_date, story.story_date))
original_m.story_id = story_id
original_m.read_date = date
original_m.story_date = story.story_date
original_m.save()
except OperationError, e:
logging.user(request, "~BRCan't even save: %s" % (origin_m.story_id))
logging.user(request, "~BRCan't even save: %s" % (original_m.story_id))
pass
return data
@ -284,9 +287,10 @@ class UserSubscription(models.Model):
self.save()
# if (self.unread_count_positive == 0 and
# self.unread_count_neutral == 0):
# self.mark_feed_read()
if (self.unread_count_positive == 0 and
self.unread_count_neutral == 0 and
self.unread_count_negative == 0):
self.mark_feed_read()
cache.delete('usersub:%s' % self.user.id)
@ -308,6 +312,7 @@ class MUserStory(mongo.Document):
feed_id = mongo.IntField()
read_date = mongo.DateTimeField()
story_id = mongo.StringField()
story_date = mongo.DateTimeField()
story = mongo.ReferenceField(MStory, unique_with=('user_id', 'feed_id'))
meta = {

View file

@ -672,9 +672,10 @@ def mark_story_as_read(request):
@ajax_login_required
@json.json_view
def mark_feed_stories_as_read(request):
feeds_stories = request.REQUEST.get('feeds_stories', {})
feeds_stories = request.REQUEST.get('feeds_stories', "{}")
feeds_stories = json.decode(feeds_stories)
for feed_id, story_ids in feeds_stories.items():
feed_id = int(feed_id)
try:
usersub = UserSubscription.objects.select_related('feed').get(user=request.user, feed=feed_id)
except (UserSubscription.DoesNotExist, Feed.DoesNotExist):
@ -687,7 +688,6 @@ def mark_feed_stories_as_read(request):
continue
else:
continue
usersub.mark_story_ids_as_read(story_ids)
return dict(code=1)
@ -711,7 +711,7 @@ def mark_story_as_unread(request):
if story.story_date < usersub.mark_read_date:
# Story is outside the mark as read range, so invert all stories before.
newer_stories = MStory.objects(story_feed_id=story.story_feed_id,
newer_stories = MStory.objects(story_feed_id=story.story_feed_id,
story_date__gte=story.story_date,
story_date__lte=usersub.mark_read_date
).only('story_guid')
@ -1094,6 +1094,7 @@ def send_story_email(request):
text = render_to_string('mail/email_story_text.xhtml', locals())
html = render_to_string('mail/email_story_html.xhtml', locals())
subject = "%s is sharing a story with you: \"%s\"" % (from_name, story['story_title'])
subject = subject.replace('\n', ' ')
msg = EmailMultiAlternatives(subject, text,
from_email='NewsBlur <%s>' % from_address,
to=[to_address],

View file

@ -237,6 +237,7 @@ class Feed(models.Model):
# %s - %s - %s
# """ % (feed_address, self.__dict__, pprint(self.__dict__))
# mail_admins('Wierdo alert', message, fail_silently=True)
logging.debug(" ---> Feed points to 'Wierdo', ignoring.")
return False
try:
self.feed_address = feed_address
@ -625,7 +626,14 @@ class Feed(models.Model):
# logging.debug('- Updated story in feed (%s - %s): %s / %s' % (self.feed_title, story.get('title'), len(existing_story.story_content), len(story_content)))
story_guid = story.get('guid') or story.get('id') or story.get('link')
original_content = None
existing_story = MStory.objects.get(story_feed_id=existing_story.story_feed_id, story_guid=existing_story.story_guid)
try:
if existing_story and existing_story.id:
existing_story = MStory.objects.get(id=existing_story.id)
else:
existing_story = MStory.objects.get(story_feed_id=existing_story.story_feed_id, story_guid=existing_story.story_guid)
except MStory.DoesNotExist:
ret_values[ENTRY_ERR] += 1
continue
if existing_story.story_original_content_z:
original_content = zlib.decompress(existing_story.story_original_content_z)
elif existing_story.story_content_z:

View file

@ -1726,6 +1726,10 @@ background: transparent;
text-shadow: 1px 1px 0 #e8e8e8;
}
#story_pane .read a.NB-feed-story-title {
color: #3C415F;
}
#story_pane a.NB-feed-story-title:hover,
#story_pane a.NB-feed-story-title:hover .NB-score-1,
#story_pane a.NB-feed-story-title:hover .NB-score--1 {
@ -1741,6 +1745,9 @@ background: transparent;
text-shadow: 1px 1px 0 #F0F0F0;
cursor: pointer;
}
#story_pane .read .NB-feed-story-author {
color: #A0A0A0;
}
#story_pane .NB-feed-story-author.NB-score-1 {
color: #34912E;
}
@ -1794,6 +1801,10 @@ background: transparent;
-webkit-border-radius: 4px;
cursor: pointer;
}
#story_pane .read .NB-feed-story-tag {
color: #B1ADA3;
}
#story_pane .NB-feed-story-tag.NB-score-1 {
/* Green */
background-color: #34912E;
@ -1889,6 +1900,7 @@ background: transparent;
#story_pane .NB-feed-story-content {
margin: 0 140px 32px 28px;
padding: 12px 0px;
max-width: 700px;
}
#story_pane .NB-feed-story-endbar {
@ -4267,7 +4279,7 @@ background: transparent;
background: transparent url('../img/icons/silk/color_swatch.png') no-repeat 0 2px;
}
.NB-menu-manage .NB-menu-manage-story-thirdparty .NB-menu-manage-image {
background: transparent url('../img/reader/instapaper.png') no-repeat 0 1px;
background: transparent url('../img/icons/silk/email.png') no-repeat 0 1px;
}
.NB-menu-manage .NB-menu-manage-story-thirdparty .NB-menu-manage-thirdparty-icon {
width: 16px;
@ -4336,11 +4348,11 @@ background: transparent;
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-googleplus .NB-menu-manage-thirdparty-googleplus {
opacity: 1;
}
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-email .NB-menu-manage-image,
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-email .NB-menu-manage-thirdparty-icon {
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-instapaper .NB-menu-manage-image,
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-instapaper .NB-menu-manage-thirdparty-icon {
opacity: .2;
}
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-email .NB-menu-manage-thirdparty-email {
.NB-menu-manage .NB-menu-manage-story-thirdparty.NB-menu-manage-highlight-instapaper .NB-menu-manage-thirdparty-instapaper {
opacity: 1;
}
@ -5400,6 +5412,12 @@ background: transparent;
clear: both;
float: left;
}
.NB-modal-preferences .NB-preference .NB-preference-options input[type=radio] {
margin-top: 4px;
}
.NB-modal-preferences .NB-preference .NB-preference-options input[type=checkbox] {
margin-top: 5px;
}
.NB-modal-preferences .NB-preference .NB-preference-options label {
padding-left: 4px;
margin: 0 0 4px 0;
@ -5511,7 +5529,7 @@ background: transparent;
background: transparent url('../img/reader/googleplus.png') no-repeat 0 0;
}
.NB-modal-preferences .NB-preference-story-share label[for=NB-preference-story-share-email] {
background: transparent url('../img/reader/email.png') no-repeat 0 0;
background: transparent url('../img/icons/silk/email.png') no-repeat 0 0;
}
.NB-modal-preferences .NB-preference-story-share label[for=NB-preference-story-share-readability] {
background: transparent url('../img/reader/readability.png') no-repeat 0 0;

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

View file

@ -41,7 +41,6 @@
- (void)renderStories:(NSArray *)newStories;
- (void)scrollViewDidScroll:(UIScrollView *)scroll;
- (IBAction)markAllRead;
- (IBAction)selectIntelligence;
- (NSDictionary *)getStoryAtRow:(NSInteger)indexPathRow;
- (void)checkScroll;

View file

@ -596,10 +596,24 @@
#pragma mark Feed Actions
- (IBAction)markAllRead {
if (appDelegate.isRiverView) {
return [self doOpenMarkReadActionSheet:nil];
} else {
- (void)markFeedsReadWithAllStories:(BOOL)includeHidden {
if (appDelegate.isRiverView && includeHidden) {
// Mark folder as read
NSString *urlString = [NSString stringWithFormat:@"http://%@/reader/mark_folder_as_read",
NEWSBLUR_URL];
NSURL *url = [NSURL URLWithString:urlString];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:appDelegate.activeFolder forKey:@"folder_name"];
[request setDelegate:nil];
[request startAsynchronous];
[appDelegate markActiveFolderAllRead];
[appDelegate.navigationController
popToViewController:[appDelegate.navigationController.viewControllers
objectAtIndex:0]
animated:YES];
} else if (!appDelegate.isRiverView && includeHidden) {
// Mark feed as read
NSString *urlString = [NSString stringWithFormat:@"http://%@/reader/mark_feed_as_read",
NEWSBLUR_URL];
NSURL *url = [NSURL URLWithString:urlString];
@ -607,7 +621,23 @@
[request setPostValue:[appDelegate.activeFeed objectForKey:@"id"] forKey:@"feed_id"];
[request setDelegate:nil];
[request startAsynchronous];
[appDelegate markActiveFeedAllRead];
[appDelegate.navigationController
popToViewController:[appDelegate.navigationController.viewControllers
objectAtIndex:0]
animated:YES];
} else {
// Mark visible stories as read
NSDictionary *feedsStories = [appDelegate markVisibleStoriesRead];
NSString *urlString = [NSString stringWithFormat:@"http://%@/reader/mark_feed_stories_as_read",
NEWSBLUR_URL];
NSURL *url = [NSURL URLWithString:urlString];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:[feedsStories JSONRepresentation] forKey:@"feeds_stories"];
[request setDelegate:nil];
[request startAsynchronous];
[appDelegate.navigationController
popToViewController:[appDelegate.navigationController.viewControllers
objectAtIndex:0]
@ -615,24 +645,41 @@
}
}
- (void)markFeedsReadWithAllStories:(BOOL)includeHidden {
}
- (IBAction)doOpenMarkReadActionSheet:(id)sender {
NSString *title = appDelegate.isRiverView ?
appDelegate.activeFolder :
[appDelegate.activeFeed objectForKey:@"feed_title"];
UIActionSheet *options = [[UIActionSheet alloc]
initWithTitle:appDelegate.activeFolder
initWithTitle:title
delegate:self
cancelButtonTitle:nil
destructiveButtonTitle:nil
otherButtonTitles:nil];
int unreadCount = [[appDelegate activeFeedStoryLocations] count];
NSString *visibleText = [NSString stringWithFormat:@"Mark %@ read",
unreadCount == 1 ?
@"this story as" :
[NSString stringWithFormat:@"these %d stories", unreadCount]];
NSArray *buttonTitles = [NSArray arrayWithObjects:visibleText, @"Mark entire folder read", nil];
int visibleUnreadCount = appDelegate.visibleUnreadCount;
int totalUnreadCount = [appDelegate unreadCount];
NSArray *buttonTitles = nil;
if (visibleUnreadCount >= totalUnreadCount || visibleUnreadCount <= 0) {
NSString *visibleText = [NSString stringWithFormat:@"Mark %@ read",
appDelegate.isRiverView ?
@"entire folder" :
@"this site"];
buttonTitles = [NSArray arrayWithObjects:visibleText, nil];
options.destructiveButtonIndex = 0;
} else {
NSString *visibleText = [NSString stringWithFormat:@"Mark %@ read",
visibleUnreadCount == 1 ?
@"this story as" :
[NSString stringWithFormat:@"these %d stories",
visibleUnreadCount]];
NSString *entireText = [NSString stringWithFormat:@"Mark %@ read",
appDelegate.isRiverView ?
@"entire folder" :
@"this site"];
buttonTitles = [NSArray arrayWithObjects:visibleText, entireText, nil];
options.destructiveButtonIndex = 1;
}
for (id title in buttonTitles) {
[options addButtonWithTitle:title];
}
@ -656,19 +703,28 @@
[options addButtonWithTitle:title];
}
options.cancelButtonIndex = [options addButtonWithTitle:@"Cancel"];
options.tag = kSettingsActionSheet;
[options showInView:self.view];
[options release];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
NSLog(@"Action option #%d", buttonIndex);
if (actionSheet.tag == 1) {
if (buttonIndex == 0) {
[self markFeedsReadWithAllStories:NO];
} else if (buttonIndex == 1) {
[self markFeedsReadWithAllStories:YES];
}
int visibleUnreadCount = appDelegate.visibleUnreadCount;
int totalUnreadCount = [appDelegate unreadCount];
if (visibleUnreadCount >= totalUnreadCount || visibleUnreadCount <= 0) {
if (buttonIndex == 0) {
[self markFeedsReadWithAllStories:YES];
}
} else {
if (buttonIndex == 0) {
[self markFeedsReadWithAllStories:NO];
} else if (buttonIndex == 1) {
[self markFeedsReadWithAllStories:YES];
}
}
} else if (actionSheet.tag == 2) {
if (buttonIndex == 0) {
[self confirmDeleteSite];

View file

@ -76,7 +76,7 @@
<object class="IBUISegmentedControl" id="122945842">
<reference key="NSNextResponder" ref="929039419"/>
<int key="NSvFlags">292</int>
<string key="NSFrame">{{93, 8}, {161, 30}}</string>
<string key="NSFrame">{{79, 8}, {161, 30}}</string>
<reference key="NSSuperview" ref="929039419"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView"/>
@ -134,8 +134,9 @@
<object class="NSMutableArray" key="IBUIItems">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBUIBarButtonItem" id="625055884">
<string key="IBUITitle">All Read</string>
<string key="IBUITitle">Read</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<float key="IBUIWidth">48</float>
<int key="IBUIStyle">1</int>
<reference key="IBUIToolbar" ref="929039419"/>
</object>
@ -160,7 +161,7 @@
<string key="NSResourceName">settings_icon.png</string>
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<float key="IBUIWidth">42</float>
<float key="IBUIWidth">48</float>
<int key="IBUIStyle">1</int>
<reference key="IBUIToolbar" ref="929039419"/>
</object>
@ -241,11 +242,11 @@
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">markAllRead</string>
<string key="label">doOpenMarkReadActionSheet:</string>
<reference key="source" ref="625055884"/>
<reference key="destination" ref="372490531"/>
</object>
<int key="connectionID">51</int>
<int key="connectionID">62</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
@ -400,7 +401,7 @@
<reference key="dict.values" ref="0"/>
</object>
<nil key="sourceID"/>
<int key="maxID">61</int>
<int key="maxID">62</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
@ -646,8 +647,8 @@
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>doOpenMarkReadActionSheet:</string>
<string>doOpenSettingsActionSheet</string>
<string>markAllRead</string>
<string>selectIntelligence</string>
</object>
<object class="NSMutableArray" key="dict.values">
@ -661,18 +662,18 @@
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>doOpenMarkReadActionSheet:</string>
<string>doOpenSettingsActionSheet</string>
<string>markAllRead</string>
<string>selectIntelligence</string>
</object>
<object class="NSMutableArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBActionInfo">
<string key="name">doOpenSettingsActionSheet</string>
<string key="name">doOpenMarkReadActionSheet:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">markAllRead</string>
<string key="name">doOpenSettingsActionSheet</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
@ -1269,6 +1270,7 @@
<string>appDelegate</string>
<string>buttonNext</string>
<string>buttonPrevious</string>
<string>feedTitleGradient</string>
<string>progressView</string>
<string>toolbar</string>
<string>webView</string>
@ -1279,6 +1281,7 @@
<string>NewsBlurAppDelegate</string>
<string>UIBarButtonItem</string>
<string>UIBarButtonItem</string>
<string>UIView</string>
<string>UIProgressView</string>
<string>UIToolbar</string>
<string>UIWebView</string>
@ -1292,6 +1295,7 @@
<string>appDelegate</string>
<string>buttonNext</string>
<string>buttonPrevious</string>
<string>feedTitleGradient</string>
<string>progressView</string>
<string>toolbar</string>
<string>webView</string>
@ -1314,6 +1318,10 @@
<string key="name">buttonPrevious</string>
<string key="candidateClassName">UIBarButtonItem</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">feedTitleGradient</string>
<string key="candidateClassName">UIView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">progressView</string>
<string key="candidateClassName">UIProgressView</string>

View file

@ -40,6 +40,7 @@
int storyCount;
int originalStoryCount;
NSInteger selectedIntelligence;
int visibleUnreadCount;
NSMutableArray * recentlyReadStories;
NSMutableSet * recentlyReadFeeds;
NSMutableArray * readStories;
@ -70,6 +71,7 @@
@property (readwrite, retain) NSURL * activeOriginalStoryURL;
@property (readwrite) int storyCount;
@property (readwrite) int originalStoryCount;
@property (readwrite) int visibleUnreadCount;
@property (readwrite) NSInteger selectedIntelligence;
@property (readwrite, retain) NSMutableArray * recentlyReadStories;
@property (readwrite, retain) NSMutableSet * recentlyReadFeeds;
@ -105,7 +107,12 @@
- (int)unreadCountForFeed:(NSString *)feedId;
- (int)unreadCountForFolder:(NSString *)folderName;
- (void)markActiveStoryRead;
- (NSDictionary *)markVisibleStoriesRead;
- (void)markStoryRead:(NSString *)storyId feedId:(id)feedId;
- (void)markStoryRead:(NSDictionary *)story feed:(NSDictionary *)feed;
- (void)markActiveFeedAllRead;
- (void)markActiveFolderAllRead;
- (void)markFeedAllRead:(id)feedId;
- (void)calculateStoryLocations;
+ (int)computeStoryScore:(NSDictionary *)intelligence;
+ (UIView *)makeGradientView:(CGRect)rect startColor:(NSString *)start endColor:(NSString *)end;

View file

@ -37,6 +37,7 @@
@synthesize activeFeedStoryLocationIds;
@synthesize activeStory;
@synthesize storyCount;
@synthesize visibleUnreadCount;
@synthesize originalStoryCount;
@synthesize selectedIntelligence;
@synthesize activeOriginalStoryURL;
@ -64,6 +65,7 @@
- (void)viewDidLoad {
self.selectedIntelligence = 1;
self.visibleUnreadCount = 0;
[self setRecentlyReadStories:[NSMutableArray array]];
}
@ -367,8 +369,47 @@
NSDictionary *feed = [self.dictFeeds objectForKey:feedIdStr];
NSDictionary *story = [activeFeedStories objectAtIndex:activeIndex];
[story setValue:[NSNumber numberWithInt:1] forKey:@"read_status"];
[self.recentlyReadStories addObject:[NSNumber numberWithInt:activeLocation]];
[self markStoryRead:story feed:feed];
// NSLog(@"Marked read %d-%d: %@: %d", activeIndex, activeLocation, self.recentlyReadStories, score);
}
- (NSDictionary *)markVisibleStoriesRead {
NSMutableDictionary *feedsStories = [NSMutableDictionary dictionary];
for (NSDictionary *story in self.activeFeedStories) {
if ([[story objectForKey:@"read_status"] intValue] != 0) {
continue;
}
NSString *feedIdStr = [NSString stringWithFormat:@"%@",[story objectForKey:@"story_feed_id"]];
NSDictionary *feed = [self.dictFeeds objectForKey:feedIdStr];
if (![feedsStories objectForKey:feedIdStr]) {
[feedsStories setObject:[NSMutableArray array] forKey:feedIdStr];
}
NSMutableArray *stories = [feedsStories objectForKey:feedIdStr];
[stories addObject:[story objectForKey:@"id"]];
[self markStoryRead:story feed:feed];
}
NSLog(@"feedsStories: %@", feedsStories);
return feedsStories;
}
- (void)markStoryRead:(NSString *)storyId feedId:(id)feedId {
NSString *feedIdStr = [NSString stringWithFormat:@"%@",feedId];
NSDictionary *feed = [self.dictFeeds objectForKey:feedIdStr];
NSDictionary *story = nil;
for (NSDictionary *s in self.activeFeedStories) {
if ([[s objectForKey:@"story_guid"] isEqualToString:storyId]) {
story = s;
break;
}
}
[self markStoryRead:story feed:feed];
}
- (void)markStoryRead:(NSDictionary *)story feed:(NSDictionary *)feed {
NSString *feedIdStr = [NSString stringWithFormat:@"%@", [feed objectForKey:@"id"]];
[story setValue:[NSNumber numberWithInt:1] forKey:@"read_status"];
self.visibleUnreadCount -= 1;
if (![self.recentlyReadFeeds containsObject:[story objectForKey:@"story_feed_id"]]) {
[self.recentlyReadFeeds addObject:[story objectForKey:@"story_feed_id"]];
}
@ -384,22 +425,32 @@
[feed setValue:[NSNumber numberWithInt:unreads] forKey:@"ng"];
}
[self.dictFeeds setValue:feed forKey:feedIdStr];
// NSLog(@"Marked read %d-%d: %@: %d", activeIndex, activeLocation, self.recentlyReadStories, score);
}
- (void)markActiveFeedAllRead {
id feedId = [self.activeFeed objectForKey:@"id"];
NSString *feedIdStr = [NSString stringWithFormat:@"%@",feedId];
[self markFeedAllRead:feedId];
}
- (void)markActiveFolderAllRead {
for (id feedId in [self.dictFolders objectForKey:self.activeFolder]) {
[self markFeedAllRead:feedId];
}
}
- (void)markFeedAllRead:(id)feedId {
NSString *feedIdStr = [NSString stringWithFormat:@"%@",feedId];
NSDictionary *feed = [self.dictFeeds objectForKey:feedIdStr];
[feed setValue:[NSNumber numberWithInt:0] forKey:@"ps"];
[feed setValue:[NSNumber numberWithInt:0] forKey:@"nt"];
[feed setValue:[NSNumber numberWithInt:0] forKey:@"ng"];
[self.dictFeeds setValue:feed forKey:feedIdStr];
[self.dictFeeds setValue:feed forKey:feedIdStr];
}
- (void)calculateStoryLocations {
self.visibleUnreadCount = 0;
self.activeFeedStoryLocations = [NSMutableArray array];
self.activeFeedStoryLocationIds = [NSMutableArray array];
for (int i=0; i < self.storyCount; i++) {
@ -409,6 +460,9 @@
NSNumber *location = [NSNumber numberWithInt:i];
[self.activeFeedStoryLocations addObject:location];
[self.activeFeedStoryLocationIds addObject:[story objectForKey:@"id"]];
if ([[story objectForKey:@"read_status"] intValue] == 0) {
self.visibleUnreadCount += 1;
}
}
}
}

View file

@ -335,7 +335,7 @@
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
NSLog(@"ContentOffset: %f %f", scrollView.contentOffset.x, scrollView.contentOffset.y);
// NSLog(@"ContentOffset: %f %f", scrollView.contentOffset.x, scrollView.contentOffset.y);
self.feedTitleGradient.frame = CGRectMake(scrollView.contentOffset.x < 0 ? -1 * scrollView.contentOffset.x : 0,
-1 * scrollView.contentOffset.y - self.feedTitleGradient.frame.size.height,
self.feedTitleGradient.frame.size.width,

View file

@ -1847,7 +1847,7 @@
// = Feed Header =
// ===============
update_header_counts: function(skip_sites) {
update_header_counts: function(skip_sites, unread_view) {
if (!skip_sites) {
var feeds_count = _.select(this.model.feeds, function(f) {
return f.active;
@ -1866,10 +1866,28 @@
return m;
}, {'positive': 0, 'negative': 0, 'neutral': 0});
_(['positive', 'neutral', 'negative']).each(function(level) {
// This is for .NB-feeds-header-count
var $count = $('.NB-feeds-header-'+level);
$count.text(unread_counts[level]);
$count.toggleClass('NB-empty', unread_counts[level] == 0);
});
if (this.model.preference('show_unread_counts_in_title')) {
var title = 'NewsBlur (';
var counts = [];
var unread_view = _.isNumber(unread_view) && unread_view || this.model.preference('unread_view');
if (unread_view <= -1) {
counts.push(unread_counts['negative']);
}
if (unread_view <= 0) {
counts.push(unread_counts['neutral']);
}
if (unread_view <= 1) {
counts.push(unread_counts['positive']);
}
title += counts.join('/') + ')';
document.title = title;
}
},
update_starred_count: function() {
@ -2812,7 +2830,7 @@
$story_titles.hover(function() {
var $this = $(this);
var menu_height = $this.hasClass('story') ? 150 : 270;
var menu_height = $this.hasClass('story') ? 190 : 270;
if ($this.offset().top > $(window).height() - menu_height) {
$this.addClass('NB-hover-inverse');
@ -4217,42 +4235,42 @@
])),
$.make('li', { className: 'NB-menu-manage-story-thirdparty' }, [
(NEWSBLUR.Preferences['story_share_facebook'] && $.make('div', { className: 'NB-menu-manage-thirdparty-icon NB-menu-manage-thirdparty-facebook'}).bind('mouseenter', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Send to Facebook').parent().addClass('NB-menu-manage-highlight-facebook');
$(e.target).siblings('.NB-menu-manage-title').text('Facebook').parent().addClass('NB-menu-manage-highlight-facebook');
}, this)).bind('mouseleave', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Send to Instapaper').parent().removeClass('NB-menu-manage-highlight-facebook');
$(e.target).siblings('.NB-menu-manage-title').text('Email story').parent().removeClass('NB-menu-manage-highlight-facebook');
}, this))),
(NEWSBLUR.Preferences['story_share_twitter'] && $.make('div', { className: 'NB-menu-manage-thirdparty-icon NB-menu-manage-thirdparty-twitter'}).bind('mouseenter', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Send to Twitter').parent().addClass('NB-menu-manage-highlight-twitter');
$(e.target).siblings('.NB-menu-manage-title').text('Twitter').parent().addClass('NB-menu-manage-highlight-twitter');
}, this)).bind('mouseleave', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Send to Instapaper').parent().removeClass('NB-menu-manage-highlight-twitter');
$(e.target).siblings('.NB-menu-manage-title').text('Email story').parent().removeClass('NB-menu-manage-highlight-twitter');
}, this))),
(NEWSBLUR.Preferences['story_share_readitlater'] && $.make('div', { className: 'NB-menu-manage-thirdparty-icon NB-menu-manage-thirdparty-readitlater'}).bind('mouseenter', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Read It Later').parent().addClass('NB-menu-manage-highlight-readitlater');
}, this)).bind('mouseleave', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Send to Instapaper').parent().removeClass('NB-menu-manage-highlight-readitlater');
$(e.target).siblings('.NB-menu-manage-title').text('Email story').parent().removeClass('NB-menu-manage-highlight-readitlater');
}, this))),
(NEWSBLUR.Preferences['story_share_pinboard'] && $.make('div', { className: 'NB-menu-manage-thirdparty-icon NB-menu-manage-thirdparty-pinboard'}).bind('mouseenter', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Pinboard').parent().addClass('NB-menu-manage-highlight-pinboard');
}, this)).bind('mouseleave', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Send to Instapaper').parent().removeClass('NB-menu-manage-highlight-pinboard');
$(e.target).siblings('.NB-menu-manage-title').text('Email story').parent().removeClass('NB-menu-manage-highlight-pinboard');
}, this))),
(NEWSBLUR.Preferences['story_share_googleplus'] && $.make('div', { className: 'NB-menu-manage-thirdparty-icon NB-menu-manage-thirdparty-googleplus'}).bind('mouseenter', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Google+').parent().addClass('NB-menu-manage-highlight-googleplus');
}, this)).bind('mouseleave', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Send to Instapaper').parent().removeClass('NB-menu-manage-highlight-googleplus');
$(e.target).siblings('.NB-menu-manage-title').text('Email story').parent().removeClass('NB-menu-manage-highlight-googleplus');
}, this))),
(NEWSBLUR.Preferences['story_share_email'] && $.make('div', { className: 'NB-menu-manage-thirdparty-icon NB-menu-manage-thirdparty-email'}).bind('mouseenter', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Send to email').parent().addClass('NB-menu-manage-highlight-email');
(NEWSBLUR.Preferences['story_share_instapaper'] && $.make('div', { className: 'NB-menu-manage-thirdparty-icon NB-menu-manage-thirdparty-instapaper'}).bind('mouseenter', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Instapaper').parent().addClass('NB-menu-manage-highlight-instapaper');
}, this)).bind('mouseleave', _.bind(function(e) {
$(e.target).siblings('.NB-menu-manage-title').text('Send to Instapaper').parent().removeClass('NB-menu-manage-highlight-email');
$(e.target).siblings('.NB-menu-manage-title').text('Email story').parent().removeClass('NB-menu-manage-highlight-instapaper');
}, this))),
// (NEWSBLUR.Preferences['story_share_readability'] && $.make('div', { className: 'NB-menu-manage-thirdparty-icon NB-menu-manage-thirdparty-readability'}).bind('mouseenter', _.bind(function(e) {
// $(e.target).siblings('.NB-menu-manage-title').text('Send to Readability').parent().addClass('NB-menu-manage-highlight-readability');
// $(e.target).siblings('.NB-menu-manage-title').text('Readability').parent().addClass('NB-menu-manage-highlight-readability');
// }, this)).bind('mouseleave', _.bind(function(e) {
// $(e.target).siblings('.NB-menu-manage-title').text('Send to Instapaper').parent().removeClass('NB-menu-manage-highlight-readability');
// $(e.target).siblings('.NB-menu-manage-title').text('Email story').parent().removeClass('NB-menu-manage-highlight-readability');
// }, this))),
$.make('div', { className: 'NB-menu-manage-image' }),
$.make('div', { className: 'NB-menu-manage-title' }, 'Send to Instapaper')
$.make('div', { className: 'NB-menu-manage-title' }, 'Email story')
]).bind('click', _.bind(function(e) {
e.preventDefault();
e.stopPropagation();
@ -4269,10 +4287,10 @@
this.send_story_to_pinboard(story.id);
} else if ($target.hasClass('NB-menu-manage-thirdparty-googleplus')) {
this.send_story_to_googleplus(story.id);
} else if ($target.hasClass('NB-menu-manage-thirdparty-email')) {
this.send_story_to_email(story.id);
} else {
} else if ($target.hasClass('NB-menu-manage-thirdparty-instapaper')) {
this.send_story_to_instapaper(story.id);
} else {
this.send_story_to_email(story.id);
}
}, this)),
$.make('li', { className: 'NB-menu-separator' }),
@ -4807,6 +4825,7 @@
this.switch_feed_view_unread_view(value);
this.show_feed_hidden_story_title_indicator();
this.show_story_titles_above_intelligence_level({'animate': true, 'follow': true});
this.update_header_counts(true);
},
move_intelligence_slider: function(direction) {
@ -4852,6 +4871,8 @@
.removeClass('unread_threshold_neutral')
.removeClass('unread_threshold_negative')
.addClass('unread_threshold_'+unread_view_name);
this.update_header_counts(true, unread_view);
},
get_unread_view_name: function(unread_view) {

View file

@ -255,6 +255,19 @@ _.extend(NEWSBLUR.ReaderPreferences.prototype, {
'Site sidebar order'
])
]),
$.make('div', { className: 'NB-preference NB-preference-showunreadcountsintitle' }, [
$.make('div', { className: 'NB-preference-options' }, [
$.make('div', [
$.make('input', { id: 'NB-preference-showunreadcountsintitle-1', type: 'checkbox', name: 'show_unread_counts_in_title', value: 0 }),
$.make('label', { 'for': 'NB-preference-showunreadcountsintitle-1' }, [
'Show unread counts in the window title'
])
])
]),
$.make('div', { className: 'NB-preference-label'}, [
'Window title'
])
]),
$.make('div', { className: 'NB-preference NB-preference-hidestorychanges' }, [
$.make('div', { className: 'NB-preference-options' }, [
$.make('div', [
@ -492,6 +505,12 @@ _.extend(NEWSBLUR.ReaderPreferences.prototype, {
return false;
}
});
$('input[name=show_unread_counts_in_title]', this.$modal).each(function() {
if (NEWSBLUR.Preferences.show_unread_counts_in_title) {
$(this).attr('checked', true);
return false;
}
});
$('input[name=hide_story_changes]', this.$modal).each(function() {
if ($(this).val() == NEWSBLUR.Preferences.hide_story_changes) {
$(this).attr('checked', true);
@ -565,6 +584,7 @@ _.extend(NEWSBLUR.ReaderPreferences.prototype, {
NEWSBLUR.reader.apply_story_styling(true);
NEWSBLUR.reader.apply_tipsy_titles();
NEWSBLUR.reader.show_stories_preference_in_feed_view();
NEWSBLUR.reader.update_header_counts();
if (self.original_preferences['feed_order'] != form['feed_order'] ||
self.original_preferences['folder_counts'] != form['folder_counts']) {
NEWSBLUR.reader.make_feeds();

View file

@ -249,7 +249,7 @@
- "Queue up to 5 stories or once every 10 seconds before firing, whichever comes first."
params:
- key: feeds_stories
desc: "Dictionary of feed_ids to an array of story_ids."
desc: "JSON serialized dictionary of feed_ids to an array of story_ids."
required: true
example: "{<br>12: ['story_id_1', 'story_id_2'],<br>24: ['story_id_3']<br>}"

View file

@ -59,7 +59,7 @@ class FetchFeed:
modified = None
etag = None
USER_AGENT = 'NewsBlur Feed Fetcher (%s subscriber%s) - %s' % (
USER_AGENT = 'NewsBlur Feed Fetcher (%s subscriber%s) - %s (Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_1) AppleWebKit/534.48.3 (KHTML, like Gecko) Version/5.1 Safari/534.48.3)' % (
self.feed.num_subscribers,
's' if self.feed.num_subscribers != 1 else '',
URL
@ -149,8 +149,8 @@ class ProcessFeed:
return FEED_ERRHTTP, ret_values
if self.fpf.bozo and isinstance(self.fpf.bozo_exception, feedparser.NonXMLContentType):
logging.debug(" ---> [%-30s] Feed is Non-XML. %s entries.%s Checking address..." % (unicode(self.feed)[:30], len(self.fpf.entries), ' Not' if self.fpf.entries else ''))
if not self.fpf.entries:
logging.debug(" ---> [%-30s] Feed is Non-XML. %s entries. Checking address..." % (unicode(self.feed)[:30], len(self.fpf.entries)))
fixed_feed = self.feed.check_feed_address_for_feed_link()
if not fixed_feed:
self.feed.save_feed_history(502, 'Non-xml feed', self.fpf.bozo_exception)
@ -159,7 +159,7 @@ class ProcessFeed:
self.feed.save()
return FEED_ERRPARSE, ret_values
elif self.fpf.bozo and isinstance(self.fpf.bozo_exception, xml.sax._exceptions.SAXException):
logging.debug(" ---> [%-30s] Feed is Bad XML (SAX). %s entries. Checking address..." % (unicode(self.feed)[:30], len(self.fpf.entries)))
logging.debug(" ---> [%-30s] Feed has SAX/XML parsing issues. %s entries.%s Checking address..." % (unicode(self.feed)[:30], len(self.fpf.entries), ' Not' if self.fpf.entries else ''))
if not self.fpf.entries:
fixed_feed = self.feed.check_feed_address_for_feed_link()
if not fixed_feed:

View file

@ -73,7 +73,7 @@ class URLGatekeeper:
def __init__(self):
self.rpcache = {} # a dictionary of RobotFileParser objects, by domain
self.urlopener = urllib.FancyURLopener()
self.urlopener.version = "NewsBlur Feed Finder"
self.urlopener.version = "NewsBlur Feed Finder (Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_1) AppleWebKit/534.48.3 (KHTML, like Gecko) Version/5.1 Safari/534.48.3)"
_debuglog(self.urlopener.version)
self.urlopener.addheaders = [('User-agent', self.urlopener.version)]
robotparser.URLopener.version = self.urlopener.version
@ -314,7 +314,7 @@ def feed(uri):
#todo: give preference to certain feed formats
feedlist = feeds(uri)
if feedlist:
feeds_no_comments = filter(lambda f: not 'comments' in f, feedlist)
feeds_no_comments = filter(lambda f: 'comments' not in f.lower(), feedlist)
if feeds_no_comments:
return feeds_no_comments[0]
return feedlist[0]