Merge branch 'android' of https://github.com/samuelclay/NewsBlur into android

This commit is contained in:
RyanBateman 2012-10-04 15:55:07 -04:00
commit 8a12d1c286
139 changed files with 7044 additions and 6654 deletions

4
.gitignore vendored
View file

@ -4,6 +4,8 @@ logs/*.pid
*.pyc
static/*
local_settings.py
celerybeat-schedule
celerybeat.pid
media/iphone/NewsBlur/build
media/iphone/build
build/
@ -24,8 +26,8 @@ media/maintenance.html.unused
config/settings
static.tgz
media/css/circular
config/settings
config/secrets
# ----------------------
# Android

View file

@ -95,7 +95,10 @@ class MClassifierFeed(mongo.Document):
def __unicode__(self):
user = User.objects.get(pk=self.user_id)
feed = Feed.objects.get(pk=self.feed_id) if self.feed_id else None
if self.feed_id:
feed = Feed.objects.get(pk=self.feed_id)
else:
feed = User.objects.get(pk=self.social_user_id)
return "%s - %s/%s: (%s) %s" % (user, self.feed_id, self.social_user_id, self.score, feed)
@ -132,15 +135,19 @@ def apply_classifier_tags(classifiers, story):
if score > 0: return classifier.score
return score
def apply_classifier_feeds(classifiers, feed, social_user_id=None):
def apply_classifier_feeds(classifiers, feed, social_user_ids=None):
if not feed: return 0
feed_id = feed if isinstance(feed, int) else feed.pk
if social_user_ids and not isinstance(social_user_ids, list):
social_user_ids = [social_user_ids]
for classifier in classifiers:
if classifier.feed_id == feed_id:
# print 'Feeds: %s -- %s' % (classifier.feed_id, feed.pk)
return classifier.score
if social_user_id and not classifier.feed_id and social_user_id == classifier.social_user_id:
if (social_user_ids and not classifier.feed_id and
classifier.social_user_id in social_user_ids):
return classifier.score
return 0

View file

@ -286,6 +286,12 @@ def share_story(request, token):
shared_story.save()
logging.user(profile.user, "~BM~FY~SBUpdating~SN shared story from site: ~SB%s: %s" % (story_url, comments))
socialsub = MSocialSubscription.objects.get(user_id=profile.user.pk,
subscription_user_id=profile.user.pk)
socialsub.mark_story_ids_as_read([shared_story.story_guid],
shared_story.story_feed_id,
request=request)
shared_story.publish_update_to_subscribers()
response = HttpResponse(json.encode({

View file

@ -227,18 +227,20 @@ class GoogleReaderImporter(Importer):
feeds_xml = self.send_request(sub_url)
else:
feeds_xml = self.xml
self.process_feeds(feeds_xml)
if feeds_xml:
self.process_feeds(feeds_xml)
def send_request(self, url):
user_tokens = OAuthToken.objects.filter(user=self.user)
if user_tokens.count():
user_token = user_tokens[0]
credential = pickle.loads(base64.b64decode(user_token.credential))
http = httplib2.Http()
http = credential.authorize(http)
content = http.request(url)
return content and content[1]
if user_token.credential:
credential = pickle.loads(base64.b64decode(user_token.credential))
http = httplib2.Http()
http = credential.authorize(http)
content = http.request(url)
return content and content[1]
def process_feeds(self, feeds_xml):
self.clear_feeds()

View file

@ -195,7 +195,10 @@ NewsBlur""" % {'user': self.user.username, 'feeds': subs.count()}
if not self.user.email or not self.send_emails:
return
if self.is_premium and not force:
sent_email, created = MSentEmail.objects.get_or_create(receiver_user_id=self.user.pk,
email_type='new_premium')
if not created and not force:
return
user = self.user

View file

@ -11,10 +11,10 @@ from utils import log as logging
class LoginForm(forms.Form):
username = forms.CharField(label=_("Username or Email"), max_length=30,
widget=forms.TextInput(attrs={'tabindex': 1}),
widget=forms.TextInput(attrs={'tabindex': 1, 'class': 'NB-input'}),
error_messages={'required': 'Please enter a username.'})
password = forms.CharField(label=_("Password"),
widget=forms.PasswordInput(attrs={'tabindex': 2}),
widget=forms.PasswordInput(attrs={'tabindex': 2, 'class': 'NB-input'}),
required=False)
# error_messages={'required': 'Please enter a password.'})
@ -69,17 +69,17 @@ class LoginForm(forms.Form):
class SignupForm(forms.Form):
username = forms.RegexField(regex=r'^\w+$',
max_length=30,
widget=forms.TextInput(),
widget=forms.TextInput(attrs={'class': 'NB-input'}),
label=_(u'username'),
error_messages={
'required': 'Please enter a username.',
'invalid': "Your username may only contain letters and numbers."
})
email = forms.EmailField(widget=forms.TextInput(attrs=dict(maxlength=75)),
email = forms.EmailField(widget=forms.TextInput(attrs={'maxlength': 75, 'class': 'NB-input'}),
label=_(u'email address'),
required=False)
# error_messages={'required': 'Please enter your email.'})
password = forms.CharField(widget=forms.PasswordInput(),
password = forms.CharField(widget=forms.PasswordInput(attrs={'class': 'NB-input'}),
label=_(u'password'),
required=False)
# error_messages={'required': 'Please enter a password.'})

View file

@ -555,7 +555,7 @@ class MUserStory(mongo.Document):
user_id = mongo.IntField()
feed_id = mongo.IntField()
read_date = mongo.DateTimeField()
story_id = mongo.StringField(unique_with=('user_id', 'feed_id'))
story_id = mongo.StringField()
story_date = mongo.DateTimeField()
story = mongo.ReferenceField(MStory, dbref=True)
found_story = mongo.GenericReferenceField()
@ -569,6 +569,7 @@ class MUserStory(mongo.Document):
],
'allow_inheritance': False,
'index_drop_dups': True,
'cascade': False,
}
def save(self, *args, **kwargs):

View file

@ -41,7 +41,6 @@ class CollectStats(Task):
def run(self, **kwargs):
logging.debug(" ---> Collecting stats...")
MStatistics.collect_statistics()
MStatistics.delete_old_stats()
class CollectFeedback(Task):
@ -50,4 +49,19 @@ class CollectFeedback(Task):
def run(self, **kwargs):
logging.debug(" ---> Collecting feedback...")
MFeedback.collect_feedback()
class CleanAnalytics(Task):
name = 'clean-analytics'
def run(self, **kwargs):
logging.debug(" ---> Cleaning analytics... %s page loads and %s feed fetches" % (
settings.MONGOANALYTICSDB.nbanalytics.page_loads.count(),
settings.MONGOANALYTICSDB.nbanalytics.feed_fetches.count(),
))
day_ago = datetime.datetime.utcnow() - datetime.timedelta(days=2)
settings.MONGOANALYTICSDB.nbanalytics.feed_fetches.remove({
"date": {"$lt": day_ago},
})
settings.MONGOANALYTICSDB.nbanalytics.page_loads.remove({
"date": {"$lt": day_ago},
})

View file

@ -30,7 +30,7 @@ from apps.reader.forms import SignupForm, LoginForm, FeatureForm
from apps.rss_feeds.models import MFeedIcon
from apps.statistics.models import MStatistics
try:
from apps.rss_feeds.models import Feed, MFeedPage, DuplicateFeed, MStory, MStarredStory, FeedLoadtime
from apps.rss_feeds.models import Feed, MFeedPage, DuplicateFeed, MStory, MStarredStory
except:
pass
from apps.social.models import MSharedStory, MSocialProfile, MSocialServices
@ -106,7 +106,7 @@ def welcome(request, **kwargs):
social_profile = MSocialProfile.get_user(user.pk)
if request.method == "POST":
if request.POST.get('submit') == 'login':
if request.POST.get('submit', '').startswith('log'):
login_form = LoginForm(request.POST, prefix='login')
signup_form = SignupForm(prefix='signup')
else:
@ -123,6 +123,7 @@ def welcome(request, **kwargs):
'signup_form' : signup_form,
'statistics' : statistics,
'social_profile' : social_profile,
'post_request' : request.method == 'POST',
}, "reader/welcome.xhtml"
@never_cache
@ -541,7 +542,6 @@ def load_single_feed(request, feed_id):
if timediff > 0.50 else "")
logging.user(request, "~FYLoading feed: ~SB%s%s (%s/%s) %s" % (
feed.feed_title[:22], ('~SN/p%s' % page) if page > 1 else '', order, read_filter, time_breakdown))
FeedLoadtime.objects.create(feed=feed, loadtime=timediff)
data = dict(stories=stories,
user_profiles=user_profiles,
@ -563,13 +563,34 @@ def load_feed_page(request, feed_id):
raise Http404
feed = Feed.get_by_id(feed_id)
if (feed.has_page and
not feed.has_page_exception and
settings.BACKED_BY_AWS['pages_on_s3'] and
feed.s3_page):
if settings.PROXY_S3_PAGES:
key = settings.S3_PAGES_BUCKET.get_key(feed.s3_pages_key)
compressed_data = key.get_contents_as_string()
response = HttpResponse(compressed_data, mimetype="text/html; charset=utf-8")
response['Content-Encoding'] = 'gzip'
logging.user(request, "~FYLoading original page, proxied: ~SB%s bytes" %
(len(compressed_data)))
return response
else:
logging.user(request, "~FYLoading original page, non-proxied")
return HttpResponseRedirect('//%s/%s' % (settings.S3_PAGES_BUCKET_NAME,
feed.s3_pages_key))
data = MFeedPage.get_data(feed_id=feed_id)
if not data or not feed.has_page or feed.has_page_exception:
logging.user(request, "~FYLoading original page, ~FRmissing")
return render(request, 'static/404_original_page.xhtml', {},
content_type='text/html',
status=404)
logging.user(request, "~FYLoading original page, from the db")
return HttpResponse(data, mimetype="text/html; charset=utf-8")
@json.json_view

View file

@ -6,8 +6,11 @@ import scipy.cluster
import urlparse
import struct
import operator
import gzip
import BmpImagePlugin, PngImagePlugin, Image
from boto.s3.key import Key
from StringIO import StringIO
from django.conf import settings
from apps.rss_feeds.models import MFeedPage, MFeedIcon
from utils.feed_functions import timelimit, TimeoutError
@ -28,7 +31,10 @@ class IconImporter(object):
if not self.force and self.feed.favicon_not_found:
# print 'Not found, skipping...'
return
if not self.force and not self.feed.favicon_not_found and self.feed_icon.icon_url:
if (not self.force and
not self.feed.favicon_not_found and
self.feed_icon.icon_url and
self.feed.s3_icon):
# print 'Found, but skipping...'
return
image, image_file, icon_url = self.fetch_image_from_page_data()
@ -46,15 +52,19 @@ class IconImporter(object):
color = self.determine_dominant_color_in_image(image)
image_str = self.string_from_image(image)
if (self.feed_icon.color != color or
if (self.force or
self.feed_icon.color != color or
self.feed_icon.data != image_str or
self.feed_icon.icon_url != icon_url or
self.feed_icon.not_found):
self.feed_icon.not_found or
(settings.BACKED_BY_AWS.get('icons_on_s3') and not self.feed.s3_icon)):
self.feed_icon.data = image_str
self.feed_icon.icon_url = icon_url
self.feed_icon.color = color
self.feed_icon.not_found = False
self.feed_icon.save()
if settings.BACKED_BY_AWS.get('icons_on_s3'):
self.save_to_s3(image_str)
self.feed.favicon_color = color
self.feed.favicon_not_found = False
else:
@ -63,7 +73,16 @@ class IconImporter(object):
self.feed.save()
return not self.feed.favicon_not_found
def save_to_s3(self, image_str):
k = Key(settings.S3_ICONS_BUCKET)
k.key = self.feed.s3_icons_key
k.set_metadata('Content-Type', 'image/png')
k.set_contents_from_string(image_str.decode('base64'))
k.set_acl('public-read')
self.feed.s3_icon = True
def load_icon(self, image_file, index=None):
'''
Load Windows ICO image.
@ -146,6 +165,15 @@ class IconImporter(object):
image_file = None
if self.page_data:
content = self.page_data
elif settings.BACKED_BY_AWS.get('pages_on_s3') and self.feed.s3_page:
key = settings.S3_PAGES_BUCKET.get_key(self.feed.s3_pages_key)
compressed_content = key.get_contents_as_string()
stream = StringIO(compressed_content)
gz = gzip.GzipFile(fileobj=stream)
try:
content = gz.read()
except IOError:
content = None
else:
content = MFeedPage.get_data(feed_id=self.feed.pk)
url = self._url_from_html(content)
@ -172,6 +200,9 @@ class IconImporter(object):
def get_image_from_url(self, url):
# print 'Requesting: %s' % url
if not url:
return None, None
@timelimit(30)
def _1(url):
try:

View file

@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'Feed.s3_page'
db.add_column('feeds', 's3_page',
self.gf('django.db.models.fields.NullBooleanField')(default=False, null=True, blank=True),
keep_default=False)
# Adding field 'Feed.s3_icon'
db.add_column('feeds', 's3_icon',
self.gf('django.db.models.fields.NullBooleanField')(default=False, null=True, blank=True),
keep_default=False)
def backwards(self, orm):
# Deleting field 'Feed.s3_page'
db.delete_column('feeds', 's3_page')
# Deleting field 'Feed.s3_icon'
db.delete_column('feeds', 's3_icon')
models = {
'rss_feeds.duplicatefeed': {
'Meta': {'object_name': 'DuplicateFeed'},
'duplicate_address': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'duplicate_feed_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}),
'duplicate_link': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'db_index': 'True'}),
'feed': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'duplicate_addresses'", 'to': "orm['rss_feeds.Feed']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'rss_feeds.feed': {
'Meta': {'ordering': "['feed_title']", 'object_name': 'Feed', 'db_table': "'feeds'"},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
'active_premium_subscribers': ('django.db.models.fields.IntegerField', [], {'default': '-1', 'db_index': 'True'}),
'active_subscribers': ('django.db.models.fields.IntegerField', [], {'default': '-1', 'db_index': 'True'}),
'average_stories_per_month': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'branch_from_feed': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rss_feeds.Feed']", 'null': 'True', 'blank': 'True'}),
'creation': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'days_to_trim': ('django.db.models.fields.IntegerField', [], {'default': '90'}),
'errors_since_good': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'etag': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'exception_code': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'favicon_color': ('django.db.models.fields.CharField', [], {'max_length': '6', 'null': 'True', 'blank': 'True'}),
'favicon_not_found': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'feed_address': ('django.db.models.fields.URLField', [], {'max_length': '255', 'db_index': 'True'}),
'feed_address_locked': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
'feed_link': ('django.db.models.fields.URLField', [], {'default': "''", 'max_length': '1000', 'null': 'True', 'blank': 'True'}),
'feed_link_locked': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'feed_title': ('django.db.models.fields.CharField', [], {'default': "'[Untitled]'", 'max_length': '255', 'null': 'True', 'blank': 'True'}),
'fetched_once': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'has_feed_exception': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'has_page': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'has_page_exception': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'hash_address_and_link': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '64', 'db_index': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_push': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
'known_good': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'last_load_time': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'last_modified': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'last_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
'min_to_decay': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'next_scheduled_update': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
'num_subscribers': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
'premium_subscribers': ('django.db.models.fields.IntegerField', [], {'default': '-1'}),
'queued_date': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}),
's3_icon': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
's3_page': ('django.db.models.fields.NullBooleanField', [], {'default': 'False', 'null': 'True', 'blank': 'True'}),
'stories_last_month': ('django.db.models.fields.IntegerField', [], {'default': '0'})
},
'rss_feeds.feeddata': {
'Meta': {'object_name': 'FeedData'},
'feed': ('utils.fields.AutoOneToOneField', [], {'related_name': "'data'", 'unique': 'True', 'to': "orm['rss_feeds.Feed']"}),
'feed_classifier_counts': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'feed_tagline': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'popular_authors': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
'popular_tags': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
'story_count_history': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'})
},
'rss_feeds.feedloadtime': {
'Meta': {'object_name': 'FeedLoadtime'},
'date_accessed': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'feed': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['rss_feeds.Feed']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'loadtime': ('django.db.models.fields.FloatField', [], {})
}
}
complete_apps = ['rss_feeds']

View file

@ -29,8 +29,7 @@ from utils.feed_functions import levenshtein_distance
from utils.feed_functions import timelimit, TimeoutError
from utils.feed_functions import relative_timesince
from utils.feed_functions import seconds_timesince
from utils.story_functions import strip_tags
from utils.diff import HTMLDiff
from utils.story_functions import strip_tags, htmldiff
ENTRY_NEW, ENTRY_UPDATED, ENTRY_SAME, ENTRY_ERR = range(4)
@ -69,6 +68,8 @@ class Feed(models.Model):
last_load_time = models.IntegerField(default=0)
favicon_color = models.CharField(max_length=6, null=True, blank=True)
favicon_not_found = models.BooleanField(default=False)
s3_page = models.NullBooleanField(default=False, blank=True, null=True)
s3_icon = models.NullBooleanField(default=False, blank=True, null=True)
class Meta:
db_table="feeds"
@ -87,14 +88,27 @@ class Feed(models.Model):
@property
def favicon_url(self):
if settings.BACKED_BY_AWS['icons_on_s3'] and self.s3_icon:
return "http://%s/%s.png" % (settings.S3_ICONS_BUCKET_NAME, self.pk)
return reverse('feed-favicon', kwargs={'feed_id': self.pk})
@property
def favicon_url_fqdn(self):
if settings.BACKED_BY_AWS['icons_on_s3'] and self.s3_icon:
return self.favicon_url
return "http://%s%s" % (
Site.objects.get_current().domain,
self.favicon_url
)
@property
def s3_pages_key(self):
return "%s.gz.html" % self.pk
@property
def s3_icons_key(self):
return "%s.png" % self.pk
def canonical(self, full=False, include_favicon=True):
feed = {
'id': self.pk,
@ -114,6 +128,8 @@ class Feed(models.Model):
'favicon_text_color': self.favicon_text_color(),
'favicon_fetching': self.favicon_fetching,
'favicon_url': self.favicon_url,
's3_page': self.s3_page,
's3_icon': self.s3_icon,
}
if include_favicon:
@ -780,8 +796,7 @@ class Feed(models.Model):
original_content = zlib.decompress(existing_story.story_content_z)
# print 'Type: %s %s' % (type(original_content), type(story_content))
if story_content and len(story_content) > 10:
diff = HTMLDiff(unicode(original_content), story_content)
story_content_diff = diff.getDiff()
story_content_diff = htmldiff(unicode(original_content), unicode(story_content))
else:
story_content_diff = original_content
# logging.debug("\t\tDiff: %s %s %s" % diff.getStats())
@ -1311,6 +1326,7 @@ class MStory(mongo.Document):
'index_drop_dups': True,
'ordering': ['-story_date'],
'allow_inheritance': False,
'cascade': False,
}
@property

View file

@ -6,6 +6,9 @@ import feedparser
import time
import urllib2
import httplib
import gzip
import StringIO
from boto.s3.key import Key
from django.conf import settings
from utils import log as logging
from apps.rss_feeds.models import MFeedPage
@ -77,6 +80,9 @@ class PageImporter(object):
response = requests.get(feed_link, headers=self.headers)
except requests.exceptions.TooManyRedirects:
response = requests.get(feed_link)
except AttributeError:
self.save_no_page()
return
try:
data = response.text
except (LookupError, TypeError):
@ -169,10 +175,35 @@ class PageImporter(object):
def save_page(self, html):
if html and len(html) > 100:
feed_page, created = MFeedPage.objects.get_or_create(feed_id=self.feed.pk, auto_save=True)
feed_page.page_data = html
if not created:
feed_page.save()
if settings.BACKED_BY_AWS.get('pages_on_s3'):
k = Key(settings.S3_PAGES_BUCKET)
k.key = self.feed.s3_pages_key
k.set_metadata('Content-Encoding', 'gzip')
k.set_metadata('Content-Type', 'text/html')
k.set_metadata('Access-Control-Allow-Origin', '*')
out = StringIO.StringIO()
f = gzip.GzipFile(fileobj=out, mode='w')
f.write(html)
f.close()
compressed_html = out.getvalue()
k.set_contents_from_string(compressed_html)
k.set_acl('public-read')
if not self.feed.s3_page:
try:
feed_page = MFeedPage.objects.get(feed_id=self.feed.pk)
feed_page.delete()
logging.debug(' --->> [%-30s] ~FYTransfering page data to S3...' % (self.feed))
except MFeedPage.DoesNotExist:
pass
self.feed.s3_page = True
self.feed.save()
else:
feed_page.save(force_insert=True)
return feed_page
try:
feed_page = MFeedPage.objects.get(feed_id=self.feed.pk)
feed_page.page_data = html
feed_page.save()
except MFeedPage.DoesNotExist:
feed_page = MFeedPage.objects.create(feed_id=self.feed.pk, page_data=html)
return feed_page

View file

@ -449,7 +449,6 @@ class MSocialProfile(mongo.Document):
logging.user(user, "~FMDisabled emails, skipping.")
return
if self.user_id == follower_user_id:
logging.user(user, "~FMDisabled emails, skipping.")
return
emails_sent = MSentEmail.objects.filter(receiver_user_id=user.pk,
@ -918,7 +917,7 @@ class MSocialSubscription(mongo.Document):
read_stories = MUserStory.objects(user_id=self.user_id,
feed_id__in=story_feed_ids,
story_id__in=story_ids)
read_stories_ids = [rs.story_id for rs in read_stories]
read_stories_ids = list(set(rs.story_id for rs in read_stories))
oldest_unread_story_date = now
unread_stories_db = []
@ -953,7 +952,7 @@ class MSocialSubscription(mongo.Document):
for story in stories:
scores = {
'feed' : apply_classifier_feeds(classifier_feeds, story['story_feed_id'],
social_user_id=self.subscription_user_id),
social_user_ids=self.subscription_user_id),
'author' : apply_classifier_authors(classifier_authors, story),
'tags' : apply_classifier_tags(classifier_tags, story),
'title' : apply_classifier_titles(classifier_titles, story),

View file

@ -145,7 +145,7 @@ def load_social_stories(request, user_id, username=None):
story['intelligence'] = {
'feed': apply_classifier_feeds(classifier_feeds, story['story_feed_id'],
social_user_id=social_user_id),
social_user_ids=social_user_id),
'author': apply_classifier_authors(classifier_authors, story),
'tags': apply_classifier_tags(classifier_tags, story),
'title': apply_classifier_titles(classifier_titles, story),
@ -248,6 +248,8 @@ def load_river_blurblog(request):
# Intelligence classifiers for all feeds involved
if story_feed_ids:
classifier_feeds = list(MClassifierFeed.objects(user_id=user.pk,
social_user_id__in=social_user_ids))
classifier_feeds = classifier_feeds + list(MClassifierFeed.objects(user_id=user.pk,
feed_id__in=story_feed_ids))
classifier_authors = list(MClassifierAuthor.objects(user_id=user.pk,
feed_id__in=story_feed_ids))
@ -260,11 +262,6 @@ def load_river_blurblog(request):
classifier_authors = []
classifier_titles = []
classifier_tags = []
classifiers = sort_classifiers_by_feed(user=user, feed_ids=story_feed_ids,
classifier_feeds=classifier_feeds,
classifier_authors=classifier_authors,
classifier_titles=classifier_titles,
classifier_tags=classifier_tags)
# Just need to format stories
for story in stories:
@ -282,7 +279,8 @@ def load_river_blurblog(request):
starred_date = localtime_for_timezone(starred_stories[story['id']], user.profile.timezone)
story['starred_date'] = format_story_link_date__long(starred_date, now)
story['intelligence'] = {
'feed': apply_classifier_feeds(classifier_feeds, story['story_feed_id']),
'feed': apply_classifier_feeds(classifier_feeds, story['story_feed_id'],
social_user_ids=story['friend_user_ids']),
'author': apply_classifier_authors(classifier_authors, story),
'tags': apply_classifier_tags(classifier_tags, story),
'title': apply_classifier_titles(classifier_titles, story),
@ -294,6 +292,11 @@ def load_river_blurblog(request):
story['shared_date'] = format_story_link_date__long(shared_date, now)
story['shared_comments'] = strip_tags(shared_stories[story['id']]['comments'])
classifiers = sort_classifiers_by_feed(user=user, feed_ids=story_feed_ids,
classifier_feeds=classifier_feeds,
classifier_authors=classifier_authors,
classifier_titles=classifier_titles,
classifier_tags=classifier_tags)
diff = time.time() - start
timediff = round(float(diff), 2)

View file

@ -8,5 +8,4 @@ class Command(BaseCommand):
def handle(self, *args, **options):
MStatistics.collect_statistics()
MStatistics.delete_old_stats()

View file

@ -1,10 +1,8 @@
import datetime
import mongoengine as mongo
import urllib2
from django.db.models import Avg, Count
from django.conf import settings
from apps.rss_feeds.models import MFeedFetchHistory, MPageFetchHistory, MFeedPushHistory
from apps.rss_feeds.models import FeedLoadtime
from apps.social.models import MSharedStory
from apps.profile.models import Profile
from utils import json_functions as json
@ -57,24 +55,22 @@ class MStatistics(mongo.Document):
@classmethod
def collect_statistics(cls):
now = datetime.datetime.now()
last_day = datetime.datetime.now() - datetime.timedelta(hours=24)
cls.collect_statistics_feeds_fetched(last_day)
cls.collect_statistics_feeds_fetched()
print "Feeds Fetched: %s" % (datetime.datetime.now() - now)
cls.collect_statistics_premium_users(last_day)
cls.collect_statistics_premium_users()
print "Premiums: %s" % (datetime.datetime.now() - now)
cls.collect_statistics_standard_users(last_day)
cls.collect_statistics_standard_users()
print "Standard users: %s" % (datetime.datetime.now() - now)
cls.collect_statistics_sites_loaded(last_day)
cls.collect_statistics_sites_loaded()
print "Sites loaded: %s" % (datetime.datetime.now() - now)
cls.collect_statistics_stories_shared(last_day)
cls.collect_statistics_stories_shared()
print "Stories shared: %s" % (datetime.datetime.now() - now)
cls.collect_statistics_for_db()
print "DB Stats: %s" % (datetime.datetime.now() - now)
@classmethod
def collect_statistics_feeds_fetched(cls, last_day=None):
if not last_day:
last_day = datetime.datetime.now() - datetime.timedelta(hours=24)
def collect_statistics_feeds_fetched(cls):
last_day = datetime.datetime.now() - datetime.timedelta(hours=24)
last_month = datetime.datetime.now() - datetime.timedelta(days=30)
feeds_fetched = MFeedFetchHistory.objects.filter(fetch_date__gte=last_day).count()
@ -100,19 +96,17 @@ class MStatistics(mongo.Document):
return feeds_fetched
@classmethod
def collect_statistics_premium_users(cls, last_day=None):
if not last_day:
last_day = datetime.datetime.now() - datetime.timedelta(hours=24)
def collect_statistics_premium_users(cls):
last_day = datetime.datetime.now() - datetime.timedelta(hours=24)
premium_users = Profile.objects.filter(last_seen_on__gte=last_day, is_premium=True).count()
cls.objects(key='premium_users').update_one(upsert=True, set__key='premium_users', set__value=premium_users)
return premium_users
@classmethod
def collect_statistics_standard_users(cls, last_day=None):
if not last_day:
last_day = datetime.datetime.now() - datetime.timedelta(hours=24)
def collect_statistics_standard_users(cls):
last_day = datetime.datetime.now() - datetime.timedelta(hours=24)
standard_users = Profile.objects.filter(last_seen_on__gte=last_day, is_premium=False).count()
cls.objects(key='standard_users').update_one(upsert=True, set__key='standard_users', set__value=standard_users)
@ -120,9 +114,7 @@ class MStatistics(mongo.Document):
return standard_users
@classmethod
def collect_statistics_sites_loaded(cls, last_day=None):
if not last_day:
last_day = datetime.datetime.now() - datetime.timedelta(hours=24)
def collect_statistics_sites_loaded(cls):
now = datetime.datetime.now()
sites_loaded = []
avg_time_taken = []
@ -130,13 +122,39 @@ class MStatistics(mongo.Document):
for hour in range(24):
start_hours_ago = now - datetime.timedelta(hours=hour)
end_hours_ago = now - datetime.timedelta(hours=hour+1)
aggregates = dict(count=Count('loadtime'), avg=Avg('loadtime'))
load_times = FeedLoadtime.objects.filter(
date_accessed__lte=start_hours_ago,
date_accessed__gte=end_hours_ago
).aggregate(**aggregates)
sites_loaded.append(load_times['count'] or 0)
avg_time_taken.append(load_times['avg'] or 0)
load_times = settings.MONGOANALYTICSDB.nbanalytics.page_loads.aggregate([{
"$match": {
"date": {
"$gte": end_hours_ago,
"$lte": start_hours_ago,
},
"path": {
"$in": [
"/reader/feed/",
"/social/stories/",
"/reader/river_stories/",
"/social/river_stories/",
]
}
},
}, {
"$group": {
"_id" : 1,
"count" : {"$sum": 1},
"avg" : {"$avg": "$duration"},
},
}])
count = 0
avg = 0
if load_times['result']:
count = load_times['result'][0]['count']
avg = load_times['result'][0]['avg']
sites_loaded.append(count)
avg_time_taken.append(avg)
sites_loaded.reverse()
avg_time_taken.reverse()
@ -152,9 +170,7 @@ class MStatistics(mongo.Document):
cls.objects(key=key).update_one(upsert=True, set__key=key, set__value=value)
@classmethod
def collect_statistics_stories_shared(cls, last_day=None):
if not last_day:
last_day = datetime.datetime.now() - datetime.timedelta(hours=24)
def collect_statistics_stories_shared(cls):
now = datetime.datetime.now()
stories_shared = []
@ -182,11 +198,6 @@ class MStatistics(mongo.Document):
lag = db_functions.mongo_max_replication_lag(settings.MONGODB)
cls.set('mongodb_replication_lag', lag)
@classmethod
def delete_old_stats(cls):
now = datetime.datetime.now()
old_age = now - datetime.timedelta(days=7)
FeedLoadtime.objects.filter(date_accessed__lte=old_age).delete()
class MFeedback(mongo.Document):
date = mongo.StringField()
@ -268,8 +279,21 @@ class MAnalyticsPageLoad(mongo.Document):
@classmethod
def clean_path(cls, path):
if path and path.startswith('/reader/feed/'):
if not path:
return
if path.startswith('/reader/feed'):
path = '/reader/feed/'
elif path.startswith('/social/stories'):
path = '/social/stories/'
elif path.startswith('/reader/river_stories'):
path = '/reader/river_stories/'
elif path.startswith('/social/river_stories'):
path = '/social/river_stories/'
elif path.startswith('/reader/page/'):
path = '/reader/page/'
elif path.startswith('/api/check_share_on_site'):
path = '/api/check_share_on_site/'
return path
@ -326,6 +350,9 @@ class MAnalyticsFetcher(mongo.Document):
@classmethod
def add(cls, feed_id, feed_fetch, feed_process,
page, icon, total, feed_code):
server_name = settings.SERVER_NAME
if 'app' in server_name: return
if icon and page:
icon -= page
if page and feed_process:
@ -334,7 +361,6 @@ class MAnalyticsFetcher(mongo.Document):
page -= feed_fetch
if feed_process and feed_fetch:
feed_process -= feed_fetch
server_name = settings.SERVER_NAME
cls.objects.create(feed_id=feed_id, feed_fetch=feed_fetch,
feed_process=feed_process,

View file

@ -50,7 +50,7 @@ javascripts:
- media/js/vendor/jquery.tipsy.js
- media/js/vendor/jquery.chosen.js
- media/js/vendor/jquery.effects.core.js
- media/js/vendor/jquery.effects.slide.js
- media/js/vendor/jquery.effects.slideOffscreen.js
# - media/js/vendor/jquery.linkify.js
- media/js/vendor/bootstrap.*.js
- media/js/vendor/audio.js
@ -65,7 +65,7 @@ javascripts:
- media/js/newsblur/reader/reader_utils.js
- media/js/newsblur/reader/reader.js
- media/js/newsblur/reader/*.js
- media/js/newsblur/static/*.js
- media/js/newsblur/welcome/*.js
mobile:
- media/js/vendor/jquery-1.7.2.js
- media/js/mobile/jquery.mobile-1.0b1.js

View file

@ -4,13 +4,12 @@
199.15.250.229 app02 app02.newsblur.com push
199.15.252.156 app03 app03.newsblur.com dev
199.15.252.109 app04 app04.newsblur.com www
# 199.15.253.218 db01 db01.newsblur.com
199.15.249.101 db01 db01.newsblur.com
199.15.252.50 db02 db02.newsblur.com
199.15.253.226 db03 db03.newsblur.com
# 199.15.253.226 db03 db03.newsblur.com
199.15.249.98 db04 db04.newsblur.com
199.15.249.99 db05 db05.newsblur.com
199.15.249.100 db06 db06.newsblur.com
199.15.249.101 db07 db07.newsblur.com
199.15.250.231 task01 task01.newsblur.com
199.15.250.250 task02 task02.newsblur.com
@ -19,3 +18,7 @@
199.15.252.106 task05 task05.newsblur.com
199.15.252.107 task06 task06.newsblur.com
199.15.252.108 task07 task07.newsblur.com
199.15.251.144 task08 task08.newsblur.com
199.15.251.154 task09 task09.newsblur.com
199.15.251.137 task10 task10.newsblur.com
199.15.251.155 task11 task11.newsblur.com

90
config/mongodb.ec2.conf Normal file
View file

@ -0,0 +1,90 @@
# mongodb.conf
# Where to store the data.
# Note: if you run mongodb as a non-root user (recommended) you may
# need to create and set permissions for this directory manually,
# e.g., if the parent directory isn't mutable by the mongodb user.
dbpath=/srv/db/mongodb
#where to log
logpath=/var/log/mongodb/mongodb.log
logappend=true
#port = 27017
slowms=100
rest = true
#profile = 2
# Enables periodic logging of CPU utilization and I/O wait
#cpu = true
# Turn on/off security. Off is currently the default
noauth = true
#auth = true
# Verbose logging output.
#verbose = true
# Inspect all client data for validity on receipt (useful for
# developing drivers)
#objcheck = true
# Enable db quota management
#quota = true
# Set oplogging level where n is
# 0=off (default)
# 1=W
# 2=R
# 3=both
# 7=W+some reads
#diaglog = 0
# Diagnostic/debugging option
#nocursors = true
# Ignore query hints
#nohints = true
# Disable the HTTP interface (Defaults to localhost:27018).
#nohttpinterface = true
# Turns off server-side scripting. This will result in greatly limited
# functionality
#noscripting = true
# Turns off table scans. Any query that would do a table scan fails.
#notablescan = true
# Disable data file preallocation.
#noprealloc = true
# Specify .ns file size for new databases.
# nssize = <size>
# Accout token for Mongo monitoring server.
#mms-token = <token>
# Server name for Mongo monitoring server.
#mms-name = <server-name>
# Ping interval for Mongo monitoring server.
#mms-interval = <seconds>
# Replication Options
# in master/slave replicated mongo databases, specify here whether
# this is a slave or master
#slave = true
#source = master.example.com
# Slave only: specify a single database to replicate
#only = master.example.com
# or
#master = true
#source = slave.example.com
# in replica set configuration, specify the name of the replica set
replSet = nbset

View file

@ -8,7 +8,7 @@
dbpath=/var/lib/mongodb
#where to log
logpath=/var/log/mongodb
logpath=/var/log/mongodb/mongodb.log
logappend=true

103
fabfile.py vendored
View file

@ -34,31 +34,43 @@ env.roledefs ={
'app': ['app01.newsblur.com',
'app02.newsblur.com',
'app03.newsblur.com',
'app04.newsblur.com'],
'app04.newsblur.com',
],
'dev': ['dev.newsblur.com'],
'web': ['app01.newsblur.com',
'app02.newsblur.com',
'app04.newsblur.com'],
'app04.newsblur.com',
],
'db': ['db01.newsblur.com',
'db02.newsblur.com',
'db03.newsblur.com',
'db04.newsblur.com',
'db05.newsblur.com',
'db06.newsblur.com'],
'db05.newsblur.com',
],
'task': ['task01.newsblur.com',
'task02.newsblur.com',
'task03.newsblur.com',
'task04.newsblur.com',
'task05.newsblur.com',
'task06.newsblur.com',
'task07.newsblur.com'],
'task07.newsblur.com',
'task08.newsblur.com',
'task09.newsblur.com',
'task10.newsblur.com',
'task11.newsblur.com',
],
'vps': ['task01.newsblur.com',
'task02.newsblur.com',
'task03.newsblur.com',
'task04.newsblur.com',
'task08.newsblur.com',
'task09.newsblur.com',
'task10.newsblur.com',
'task11.newsblur.com',
'app01.newsblur.com',
'app02.newsblur.com',
'app03.newsblur.com'],
'app03.newsblur.com',
],
}
# ================
@ -66,8 +78,8 @@ env.roledefs ={
# ================
def server():
env.NEWSBLUR_PATH = "/home/sclay/newsblur"
env.VENDOR_PATH = "/home/sclay/code"
env.NEWSBLUR_PATH = "/home/%s/newsblur" % env.user
env.VENDOR_PATH = "/home/%s/code" % env.user
def app():
server()
@ -92,6 +104,11 @@ def task():
def vps():
server()
env.roles = ['vps']
def ec2():
env.user = 'ubuntu'
env.key_filename = ['/Users/sclay/.ec2/sclay.pem']
server()
# ==========
# = Deploy =
@ -263,7 +280,6 @@ def setup_app():
setup_app_firewall()
setup_app_motd()
copy_app_settings()
copy_certificates()
configure_nginx()
setup_gunicorn(supervisor=True)
update_gunicorn()
@ -278,12 +294,15 @@ def setup_db():
setup_db_firewall()
setup_db_motd()
copy_task_settings()
setup_memcached()
setup_postgres(standby=False)
# setup_mongo()
# setup_memcached()
# setup_postgres(standby=False)
setup_mongo()
setup_gunicorn(supervisor=False)
setup_redis()
# setup_redis()
setup_db_munin()
if env.user == 'ubuntu':
setup_db_mdadm()
def setup_task():
setup_common()
@ -318,14 +337,15 @@ def setup_installs():
run('mkdir -p %s' % env.VENDOR_PATH)
def setup_user():
# run('useradd -c "NewsBlur" -m conesus -s /bin/zsh')
# run('useradd -c "NewsBlur" -m newsblur -s /bin/zsh')
# run('openssl rand -base64 8 | tee -a ~conesus/.password | passwd -stdin conesus')
run('mkdir -p ~/.ssh && chmod 700 ~/.ssh')
run('rm -fr ~/.ssh/id_dsa*')
run('ssh-keygen -t dsa -f ~/.ssh/id_dsa -N ""')
run('touch ~/.ssh/authorized_keys')
put("~/.ssh/id_dsa.pub", "authorized_keys")
run('mv authorized_keys ~/.ssh/')
run('echo `cat authorized_keys` >> ~/.ssh/authorized_keys')
run('rm authorized_keys')
def add_machine_to_ssh():
put("~/.ssh/id_dsa.pub", "local_keys")
@ -371,7 +391,7 @@ def setup_psycopg():
def setup_python():
# sudo('easy_install -U pip')
sudo('easy_install -U fabric django==1.3.1 readline pyflakes iconv celery django-celery django-celery-with-redis django-compress South django-extensions pymongo==2.2.0 stripe BeautifulSoup pyyaml nltk lxml oauth2 pytz boto seacucumber django_ses mongoengine redis requests django-subdomains psutil python-gflags')
sudo('easy_install -U fabric django==1.3.1 readline pyflakes iconv celery django-celery django-celery-with-redis django-compress South django-extensions pymongo==2.2.0 stripe BeautifulSoup pyyaml nltk lxml oauth2 pytz boto seacucumber django_ses mongoengine redis requests django-subdomains psutil python-gflags cssutils')
put('config/pystartup.py', '.pystartup')
# with cd(os.path.join(env.NEWSBLUR_PATH, 'vendor/cjson')):
@ -392,11 +412,12 @@ def setup_hosts():
def config_pgbouncer():
put('config/pgbouncer.conf', '/etc/pgbouncer/pgbouncer.ini', use_sudo=True)
put('config/pgbouncer_userlist.txt', '/etc/pgbouncer/userlist.txt', use_sudo=True)
# put('config/pgbouncer_userlist.txt', '/etc/pgbouncer/userlist.txt', use_sudo=True)
put('config/secrets/pgbouncer_auth.conf', '/etc/pgbouncer/userlist.txt', use_sudo=True)
sudo('echo "START=1" > /etc/default/pgbouncer')
sudo('su postgres -c "/etc/init.d/pgbouncer stop"', pty=False)
with settings(warn_only=True):
sudo('pkill pgbouncer')
sudo('pkill -9 pgbouncer')
run('sleep 2')
sudo('/etc/init.d/pgbouncer start', pty=False)
@ -442,15 +463,15 @@ def setup_forked_mongoengine():
with settings(warn_only=True):
run('git checkout master')
run('git branch -D dev')
run('git remote add sclay git://github.com/samuelclay/mongoengine.git')
run('git fetch sclay')
run('git checkout -b dev sclay/dev')
run('git pull sclay dev')
run('git remote add %s git://github.com/samuelclay/mongoengine.git' % env.user)
run('git fetch %s' % env.user)
run('git checkout -b dev %s/dev' % env.user)
run('git pull %s dev' % env.user)
def switch_forked_mongoengine():
with cd(os.path.join(env.VENDOR_PATH, 'mongoengine')):
run('git co dev')
run('git pull sclay dev --force')
run('git pull %s dev --force' % env.user)
# run('git checkout .')
# run('git checkout master')
# run('get branch -D dev')
@ -485,6 +506,7 @@ def configure_nginx():
sudo("chmod 0755 /etc/init.d/nginx")
sudo("/usr/sbin/update-rc.d -f nginx defaults")
sudo("/etc/init.d/nginx restart")
copy_certificates()
def setup_vps():
# VPS suffer from severe time drift. Force blunt hourly time recalibration.
@ -575,9 +597,18 @@ def setup_db_firewall():
sudo('ufw allow from 199.15.248.0/21 to any port 5432 ') # PostgreSQL
sudo('ufw allow from 199.15.248.0/21 to any port 27017') # MongoDB
sudo('ufw allow from 199.15.248.0/21 to any port 28017') # MongoDB web
# sudo('ufw allow from 199.15.248.0/21 to any port 5672 ') # RabbitMQ
sudo('ufw allow from 199.15.248.0/21 to any port 6379 ') # Redis
sudo('ufw allow from 199.15.248.0/21 to any port 11211 ') # Memcached
# EC2
sudo('ufw delete allow from 23.22.0.0/16 to any port 5432 ') # PostgreSQL
sudo('ufw delete allow from 23.22.0.0/16 to any port 27017') # MongoDB
sudo('ufw delete allow from 23.22.0.0/16 to any port 6379 ') # Redis
sudo('ufw delete allow from 23.22.0.0/16 to any port 11211 ') # Memcached
sudo('ufw allow from 23.20.0.0/16 to any port 5432 ') # PostgreSQL
sudo('ufw allow from 23.20.0.0/16 to any port 27017') # MongoDB
sudo('ufw allow from 23.20.0.0/16 to any port 6379 ') # Redis
sudo('ufw allow from 23.20.0.0/16 to any port 11211 ') # Memcached
sudo('ufw --force enable')
def setup_db_motd():
@ -628,7 +659,8 @@ def setup_mongo():
sudo('echo "deb http://downloads-distro.mongodb.org/repo/debian-sysvinit dist 10gen" >> /etc/apt/sources.list')
sudo('apt-get update')
sudo('apt-get -y install mongodb-10gen')
put('config/mongodb.prod.conf', '/etc/mongodb.conf', use_sudo=True)
put('config/mongodb.%s.conf' % ('prod' if env.user != 'ubuntu' else 'ec2'),
'/etc/mongodb.conf', use_sudo=True)
sudo('/etc/init.d/mongodb restart')
def setup_redis():
@ -651,8 +683,10 @@ def setup_db_munin():
sudo('cp -frs %s/config/munin/mongo* /etc/munin/plugins/' % env.NEWSBLUR_PATH)
sudo('cp -frs %s/config/munin/pg_* /etc/munin/plugins/' % env.NEWSBLUR_PATH)
with cd(env.VENDOR_PATH):
run('git clone git://github.com/samuel/python-munin.git')
run('sudo python python-munin/setup.py install')
with settings(warn_only=True):
run('git clone git://github.com/samuel/python-munin.git')
with cd(os.path.join(env.VENDOR_PATH, 'python-munin')):
run('sudo python setup.py install')
def enable_celerybeat():
with cd(env.NEWSBLUR_PATH):
@ -662,6 +696,19 @@ def enable_celerybeat():
sudo('supervisorctl reread')
sudo('supervisorctl update')
def setup_db_mdadm():
sudo('apt-get -y install xfsprogs mdadm')
sudo('yes | mdadm --create /dev/md0 --level=0 -c256 --raid-devices=4 /dev/xvdf /dev/xvdg /dev/xvdh /dev/xvdi')
sudo('mkfs.xfs /dev/md0')
sudo('mkdir -p /srv/db')
sudo('mount -t xfs -o rw,nobarrier,noatime,nodiratime /dev/md0 /srv/db')
sudo('mkdir -p /srv/db/mongodb')
sudo('chown mongodb.mongodb /srv/db/mongodb')
sudo("echo 'DEVICE /dev/xvdf /dev/xvdg /dev/xvdh /dev/xvdi' | sudo tee -a /etc/mdadm/mdadm.conf")
sudo("mdadm --examine --scan | sudo tee -a /etc/mdadm/mdadm.conf")
sudo("echo '/dev/md0 /srv/db xfs rw,nobarrier,noatime,nodiratime,noauto 0 0' | sudo tee -a /etc/fstab")
sudo("sudo update-initramfs -u -v -k `uname -r`")
# ================
# = Setup - Task =
# ================
@ -697,7 +744,7 @@ def restore_postgres(port=5432):
def restore_mongo():
backup_date = '2012-07-24-09-00'
run('PYTHONPATH=/home/sclay/newsblur python s3.py get backup_mongo_%s.tgz' % backup_date)
run('PYTHONPATH=/home/%s/newsblur python s3.py get backup_mongo_%s.tgz' % (env.user, backup_date))
run('tar -xf backup_mongo_%s.tgz' % backup_date)
run('mongorestore backup_mongo_%s' % backup_date)

View file

@ -39,6 +39,8 @@ OAUTH_SECRET = 'SECRET_KEY_FROM_GOOGLE'
S3_ACCESS_KEY = 'XXX'
S3_SECRET = 'SECRET'
S3_BACKUP_BUCKET = 'newsblur_backups'
S3_PAGES_BUCKET_NAME = 'pages-XXX.newsblur.com'
S3_ICONS_BUCKET_NAME = 'icons-XXX.newsblur.com'
STRIPE_SECRET = "YOUR-SECRET-API-KEY"
STRIPE_PUBLISHABLE = "YOUR-PUBLISHABLE-API-KEY"
@ -76,19 +78,19 @@ MONGODB_SLAVE = {
'host': '127.0.0.1'
}
# Celery RabbitMQ Broker
BROKER_HOST = "127.0.0.1"
# Celery RabbitMQ/Redis Broker
CELERY_REDIS_HOST = "127.0.0.1"
BROKER_URL = "redis://127.0.0.1:6379/0"
REDIS = {
'host': '127.0.0.1',
}
# AMQP - RabbitMQ server
BROKER_HOST = "db01.newsblur.com"
BROKER_PORT = 5672
BROKER_USER = "newsblur"
BROKER_PASSWORD = "newsblur"
BROKER_VHOST = "newsblurvhost"
BACKED_BY_AWS = {
'pages_on_s3': False,
'icons_on_s3': False,
'stories_on_dynamodb': False,
}
# ===========
# = Logging =

View file

@ -25,9 +25,9 @@
padding: 2px;
margin: 2px 4px 2px;
border: 1px solid #606060;
-moz-box-shadow:2px 2px 0 #D0D0D0;
-webkit-box-shadow:2px 2px 0 #D0D0D0;
box-shadow:2px 2px 0 #D0D0D0;
-moz-box-shadow:2px 2px 0 rgba(50, 50, 50, 0.15);
-webkit-box-shadow:2px 2px 0 rgba(50, 50, 50, 0.15);
box-shadow:2px 2px 0 rgba(50, 50, 50, 0.15);
}
/* ========== */
@ -117,9 +117,9 @@
padding: 2px;
margin: 0px 4px 6px;
border: 1px solid #606060;
-moz-box-shadow:2px 2px 0 #D0D0D0;
-webkit-box-shadow:2px 2px 0 #D0D0D0;
box-shadow:2px 2px 0 #D0D0D0;
-moz-box-shadow:2px 2px 0 rgba(50, 50, 50, 0.15);
-webkit-box-shadow:2px 2px 0 rgba(50, 50, 50, 0.15);
box-shadow:2px 2px 0 rgba(50, 50, 50, 0.15);
}
.NB-modal .NB-modal-field input[type=checkbox] {
@ -205,9 +205,9 @@
text-transform: uppercase;
margin: 2px 4px 2px;
border: 1px solid #606060;
-moz-box-shadow:2px 2px 0 #D0D0D0;
-webkit-box-shadow:2px 2px 0 #D0D0D0;
box-shadow:2px 2px 0 #D0D0D0;
-moz-box-shadow:2px 2px 0 rgba(50, 50, 50, 0.15);
-webkit-box-shadow:2px 2px 0 rgba(50, 50, 50, 0.15);
box-shadow:2px 2px 0 rgba(50, 50, 50, 0.15);
border-radius: 4px;
-moz-border-radius: 4px;
cursor: pointer;

View file

@ -16,7 +16,12 @@ body {
background-color: white;
-webkit-overflow-scrolling: touch;
}
.NB-layout {
overflow: hidden;
height: 100%;
width: 100%;
position: absolute;
}
a, a:active, a:hover, a:visited, button {
outline: none;
}
@ -367,7 +372,7 @@ body.NB-theme-serif #story_pane .NB-feed-story-content {
border-bottom: 1px solid #A0A0A0;
}
.NB-feedlists .NB-socialfeeds .feed .feed_title {
text-shadow: 0 1px 0 #DAE2E8;
text-shadow: 0 1px 0 rgba(250, 250, 250, .4);
}
.NB-feedlists .NB-socialfeeds img.feed_favicon {
border-radius: 3px;
@ -574,12 +579,11 @@ body.NB-theme-serif #story_pane .NB-feed-story-content {
line-height: 1.3em;
height: 14px;
overflow: hidden;
text-shadow: 0 1px 0 #EBF3FA;
text-shadow: 0 1px 0 rgba(250, 250, 250, .4);
}
.NB-feedlist .feed.selected .feed_title,
.NB-feedlist .feed.NB-selected .feed_title {
text-shadow: 0 1px 0 #FFC97D;
color: #000000;
}
@ -625,41 +629,50 @@ body.NB-theme-serif #story_pane .NB-feed-story-content {
display: block;
}
.NB-feedlist .folder .folder_title .feed_counts_floater {
.NB-feedlists .folder .folder_title .feed_counts_floater,
.NB-feeds-header .feed_counts_floater {
margin-right: 2px;
}
.NB-feedlist .folder .folder_title .NB-feedlist-collapse-icon {
.NB-feedlist .folder .folder_title .NB-feedlist-collapse-icon,
.NB-feeds-header .NB-feedlist-collapse-icon {
position: absolute;
top: 3px;
right: 4px;
width: 16px;
height: 16px;
background: transparent url('/media/embed/icons/silk/toggle_minus.png') no-repeat 0 0;
background: transparent url('/media/embed/reader/toggle_minus.png') no-repeat 0 0;
background-size: 16px;
opacity: .6;
display: none;
}
.NB-feedlist .folder.NB-folder-collapsed .folder_title .NB-feedlist-collapse-icon {
background-image: url('/media/embed/icons/silk/toggle_plus.png');
.NB-feedlist .folder.NB-folder-collapsed .folder_title .NB-feedlist-collapse-icon,
.NB-feeds-header.NB-folder-collapsed .NB-feedlist-collapse-icon {
background-image: url('/media/embed/reader/toggle_plus.png');
background-size: 16px;
}
.NB-feedlist .folder .folder_title:hover .NB-feedlist-collapse-icon:hover {
.NB-feedlist .folder .folder_title:hover .NB-feedlist-collapse-icon:hover,
.NB-feeds-header:hover .NB-feedlist-collapse-icon:hover {
opacity: 1;
}
.NB-feedlist .folder .folder_title:hover .NB-feedlist-collapse-icon {
.NB-feedlist .folder .folder_title:hover .NB-feedlist-collapse-icon,
.NB-feeds-header:hover .NB-feedlist-collapse-icon {
/*.NB-feedlist .folder.NB-showing-menu > .folder_title .NB-feedlist-collapse-icon {*/
display: block;
opacity: .6;
}
.NB-feedlist .folder .folder_title:hover .feed_counts_floater,
.NB-feeds-header:hover .feed_counts_floater,
.NB-feedlist .folder.NB-showing-menu > .folder_title .feed_counts_floater {
margin-right: 24px;
}
.NB-feedlist .folder .folder_title.NB-feedlist-folder-title-recently-collapsed:hover .feed_counts_floater {
.NB-feedlist .folder .folder_title.NB-feedlist-folder-title-recently-collapsed:hover .feed_counts_floater,
.NB-feeds-header.NB-feedlist-folder-title-recently-collapsed:hover .feed_counts_floater {
display: block;
}
@ -688,17 +701,29 @@ body.NB-theme-serif #story_pane .NB-feed-story-content {
display: none;
}
.NB-feedlist .feed.selected,
.NB-feedlist .feed.NB-selected {
background: #f6a828 url('/media/css/jquery-ui/images/ui-bg_highlight-hard_35_f6a828_1x100.png') 0 50% repeat-x;
border-top: 1px solid #A8A8A8;
border-bottom: 1px solid #A8A8A8;
.NB-feedlist .feed.NB-selected,
.NB-feeds-header.NB-selected {
background-color: #F6A828;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#F7BA55), to(#F6A828));
background: -moz-linear-gradient(center top , #F7BA55 0%, #F6A828 100%);
border-top: 1px solid #C59977;
border-bottom: 1px solid #C59977;
}
.NB-feedlist .folder.NB-selected > .folder_title {
background: #f6a828 url('/media/css/jquery-ui/images/ui-bg_highlight-hard_35_f6a828_1x100.png') 0 50% repeat-x;
border-top: 1px solid #A8A8A8;
border-bottom: 1px solid #A8A8A8;
background-color: #F6A828;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#F7BA55), to(#F6A828));
background: -moz-linear-gradient(center top , #F7BA55 0%, #F6A828 100%);
border-top: 1px solid #C59977;
border-bottom: 1px solid #C59977;
text-shadow: 1px 1px 0 #FAC898;
}
.NB-feedlist .feed.NB-feed-selector-selected {
background-color: #7AC0FE;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#A7D3FE), to(#7AC0FE));
background: -moz-linear-gradient(center top , #A7D3FE 0%, #7AC0FE 100%);
border-top: 1px solid #789FC6;
border-bottom: 1px solid #789FC6;
}
.NB-feedlist .NB-feeds-list-highlight {
@ -715,29 +740,31 @@ body.NB-theme-serif #story_pane .NB-feed-story-content {
border: none !important;
}
.NB-feedlist .folder_title .unread_count {
.NB-feedlists .folder_title .unread_count {
margin-top: -3px;
line-height: 20px;
text-shadow: none;
}
.NB-feedlist .folder_title .unread_count {
text-shadow: none;
.NB-feeds-header .unread_count {
margin-top: 2px;
line-height: 18px;
text-shadow: none;
}
.NB-feedlist-hide-read-feeds .NB-feedlist .feed {
display: none;
}
.NB-feedlist-hide-read-feeds .NB-feedlist.unread_view_positive .unread_positive {
.NB-feedlist-hide-read-feeds .NB-sidebar.unread_view_positive .unread_positive {
display: block;
}
.NB-feedlist-hide-read-feeds .NB-feedlist.unread_view_neutral .unread_positive,
.NB-feedlist-hide-read-feeds .NB-feedlist.unread_view_neutral .unread_neutral {
.NB-feedlist-hide-read-feeds .NB-sidebar.unread_view_neutral .unread_positive,
.NB-feedlist-hide-read-feeds .NB-sidebar.unread_view_neutral .unread_neutral {
display: block;
}
.NB-feedlist-hide-read-feeds .NB-feedlist.unread_view_negative .unread_positive,
.NB-feedlist-hide-read-feeds .NB-feedlist.unread_view_negative .unread_neutral,
.NB-feedlist-hide-read-feeds .NB-feedlist.unread_view_negative .unread_negative {
.NB-feedlist-hide-read-feeds .NB-sidebar.unread_view_negative .unread_positive,
.NB-feedlist-hide-read-feeds .NB-sidebar.unread_view_negative .unread_neutral,
.NB-feedlist-hide-read-feeds .NB-sidebar.unread_view_negative .unread_negative {
display: block;
}
@ -747,6 +774,20 @@ body.NB-theme-serif #story_pane .NB-feed-story-content {
.NB-feedlist-hide-read-feeds .NB-feedlist .feed.selected {
display: block;
}
#feed_list.NB-feedlist.NB-selector-active .feed,
.NB-sidebar .NB-socialfeeds-folder.NB-selector-active .feed {
display: none;
}
#feed_list.NB-feedlist.NB-selector-active .feed.NB-feed-selector-active,
.NB-socialfeeds-folder.NB-selector-active .feed.NB-feed-selector-active {
display: block;
opacity: 1;
}
.NB-feedlist.NB-selector-active .NB-folder-collapsed .folder,
.NB-socialfeeds-folder.NB-selector-active {
display: block !important;
opacity: 1 !important;
}
/* ================= */
/* = Unread Counts = */
@ -1183,7 +1224,8 @@ background: transparent;
}
#story_titles .NB-feedbar .folder ul.folder,
#story_titles .NB-feedbar .folder .NB-feedlist-collapse-icon {
#story_titles .NB-feedbar .folder .NB-feedlist-collapse-icon,
.NB-feeds-header .NB-feedlist-collapse-icon {
display: none;
}
@ -1291,6 +1333,7 @@ background: transparent;
color: #272727;
line-height: 1em;
background-color: white;
border-bottom: 1px solid #FFF;
}
.NB-story-pane-west #story_titles .story {
padding-right: 4px;
@ -1307,7 +1350,6 @@ background: transparent;
height: 20px;
left: 0;
top: 0;
/* background: transparent url('/media/embed/icons/silk/bullet_orange.png') no-repeat 6px 2px;*/
}
#story_titles .story.NB-story-positive .NB-storytitles-sentiment {
@ -1523,11 +1565,11 @@ background: transparent;
}
#story_titles .story.NB-selected {
color: #304080;
border-top: 1px solid #D7DDE6;
background: #dadada url('/media/css/jquery-ui/images/dadada_40x100_textures_03_highlight_soft_75.png') 0 50% repeat-x;
}
#story_titles .story.after_selected {
border-top: 1px solid #D7DDE6;
border-top: 1px solid #6EADF5;
border-bottom: 1px solid #6EADF5;
background-color: #D2E6FD;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#E9EFF5), to(#E2EEFB));
background: -moz-linear-gradient(center top , #E9EFF5 0%, #E2EEFB 100%);
}
#story_titles .NB-story-titles-end-stories-line {
@ -2041,19 +2083,25 @@ background: transparent;
#story_pane .NB-feed-story-view-narrow .NB-feed-story-content {
margin-right: 28px;
}
#story_pane .NB-feed-story-content ins {
.NB-modal-preferences ins,
#story_pane .NB-feed-story-content ins,
.NB-pref-hide-changes #story_pane .NB-story-show-changes .NB-feed-story-content ins {
text-decoration: underline;
color: #27452D;
color: #27652F;
}
#story_pane .NB-feed-story-content del {
.NB-modal-preferences del,
#story_pane .NB-feed-story-content del,
.NB-pref-hide-changes #story_pane .NB-story-show-changes .NB-feed-story-content del {
display: inline;
color: #661616;
color: #861616;
}
.NB-pref-hide-changes #story_pane .NB-feed-story-content ins {
.NB-pref-hide-changes #story_pane .NB-feed-story-content ins,
#story_pane .NB-story-hide-changes .NB-feed-story-content ins {
text-decoration: none;
color: inherit;
}
.NB-pref-hide-changes #story_pane .NB-feed-story-content del {
.NB-pref-hide-changes #story_pane .NB-feed-story-content del,
#story_pane .NB-story-hide-changes .NB-feed-story-content del {
display: none;
}
#story_pane .NB-feed-story-comments {
@ -2729,6 +2777,9 @@ background: transparent;
display: block;
font-size: 20px;
}
.NB-is-anonymous .content-pane .feed_counts_floater {
display: none;
}
.content-pane .feed_counts_floater .unread_count {
float: right;
@ -2833,7 +2884,7 @@ background: transparent;
.NB-feeds-header-container {
position: relative;
height: 20px;
height: 24px;
display: none;
overflow: hidden;
}
@ -2844,22 +2895,17 @@ background: transparent;
bottom: 0;
left: 0;
width: 100%;
height: 18px;
height: 22px;
border-top: 1px solid #303030;
border-bottom: 1px solid #E9E9E9;
padding-right: 2px;
font-size: 10px;
cursor: pointer;
overflow: hidden;
}
.NB-feeds-header.NB-selected {
background: #f6a828 url('/media/css/jquery-ui/images/ui-bg_highlight-hard_35_f6a828_1x100.png') 0 50% repeat-x;
}
.NB-feeds-header .NB-feeds-header-icon {
position: absolute;
top: 1px;
top: 3px;
left: 2px;
width: 16px;
height: 16px;
@ -2870,8 +2916,8 @@ background: transparent;
padding: 0 40px 2px 23px;
text-decoration: none;
color: #F0F0F0;
line-height: 18px;
height: 14px;
line-height: 22px;
height: 22px;
overflow: hidden;
text-shadow: 0 1px 0 #060607;
text-transform: uppercase;
@ -2881,6 +2927,10 @@ background: transparent;
color: #C1C1C1;
}
.NB-feeds-header.NB-selected {
border-top: 1px solid #303030;
}
.NB-feeds-header.NB-selected .NB-feeds-header-title {
text-shadow: 0 1px 0 #FFC97D;
color: #000000;
@ -3158,7 +3208,8 @@ background: transparent;
overflow: hidden;
}
#story_taskbar .NB-tryfeed-add,
#story_taskbar .NB-tryfeed-follow {
#story_taskbar .NB-tryfeed-follow,
#story_taskbar .NB-tryout-signup {
margin: 2px auto 0px;
width: 60px;
height: 14px;
@ -3229,10 +3280,6 @@ background: transparent;
left: 12px;
background: transparent url('/media/embed/icons/silk/arrow_down.png') no-repeat 0 0;
}
.NB-taskbar .task_button.task_button_signup .NB-task-image {
left: 12px;
background: transparent url('/media/embed/icons/media-devices/imac.png') no-repeat 0 0;
}
.NB-taskbar .task_button_background {
z-index: 0;
@ -3388,7 +3435,7 @@ form.opml_import_form input {
/* =============== */
#NB-splash {
z-index: 0;
z-index: 1;
position: absolute;
top: 0;
left: 0;
@ -3466,6 +3513,7 @@ form.opml_import_form input {
.NB-splash-info .NB-splash-links .NB-splash-link {
display: block;
overflow: hidden;
line-height: 12px;
margin: 0;
float: left;
@ -3490,6 +3538,7 @@ form.opml_import_form input {
}
.NB-splash-info .NB-splash-links .NB-splash-link.NB-splash-link-logo a {
padding-top: 0;
padding-bottom: 0;
margin-top: -1px;
}
@ -4469,6 +4518,9 @@ form.opml_import_form input {
.NB-account .NB-module.NB-module-account .NB-module-account-stats {
min-height: 0;
}
.NB-account .NB-module.NB-module-account .NB-module-stats-counts {
overflow: hidden;
}
.NB-account .NB-module.NB-module-account .NB-module-stats-count {
margin: 0;
}
@ -8626,3 +8678,32 @@ form.opml_import_form input {
.NB-interaction-sharedstory-content {
cursor: pointer;
}
/* ================= */
/* = Feed Selector = */
/* ================= */
.NB-feeds-selector {
display: none;
background-color: #434343;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#434343), to(#696969));
background: -moz-linear-gradient(center top , #4E4E4E 0%, #696969 100%);
border-bottom: 1px solid #E9E9E9;
overflow: hidden;
}
.NB-feeds-selector-input {
border-radius: 16px;
border: 1px solid #5F5F5F;
font-weight: bold;
width: 88%;
padding: 1px 8px;
margin: 4px auto;
display: block;
font-size: 11px;
outline: none;
}
.NB-feeds-selector-input:focus {
border: 1px solid #5F5F5F;
box-shadow: 0 1px 0 #B1B1B1;
}

View file

@ -401,21 +401,13 @@ header {
.NB-story-date {
position: absolute;
right: 24px;
right: 28px;
top: 12px;
font-size: 15px;
font-weight: bold;
color: silver;
color: #D0D4D7;
text-transform: uppercase;
}
@media all and (max-width: 800px) {
.NB-story-date {
top: 6px;
}
.NB-story-date-break {
display: block;
}
}
@media all and (max-width: 500px) {
.NB-story-date {
position: static;
@ -971,7 +963,6 @@ header {
}
.NB-story-share-label {
display: inline-block;
margin: 0 4px 0 0;
}
.NB-story-share-profiles {
display: inline-block;

601
media/css/welcome.css Normal file
View file

@ -0,0 +1,601 @@
.NB-welcome .NB-body-inner {
overflow: auto;
height: 100%;
width: 100%;
position: absolute;
}
.NB-welcome .NB-splash-info.NB-splash-top .NB-splash-title {
display: none;
}
.NB-welcome .NB-splash-info.NB-splash-top,
.NB-welcome .NB-splash-info.NB-splash-bottom {
height: 6px;
position: absolute;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#4B638C), to(#23364D));
background: -moz-linear-gradient(center top , #4B638C 0%, #23364D 100%);
border-bottom: 1px solid #000D41;
border-top: none;
}
.NB-welcome .NB-splash-info.NB-splash-bottom {
position: static;
}
.NB-welcome .NB-splash-info.NB-splash-bottom .NB-splash-links {
display: none;
}
.NB-welcome .NB-taskbar-sidebar-toggle-open {
display: none;
}
/* ========== */
/* = Common = */
/* ========== */
.NB-welcome .NB-inner {
width: 960px;
margin: 0 auto;
position: relative;
height: 100%;
}
.NB-welcome .NB-splash-links {
overflow: hidden;
}
.NB-button {
border: 1px solid #07360F;
font-size: 12px;
padding: 4px 12px;
text-transform: uppercase;
margin: 2px 4px 2px;
-moz-box-shadow:2px 2px 0 rgba(35, 35, 35, 0.4);
-webkit-box-shadow:2px 2px 0 rgba(35, 35, 35, 0.4);
box-shadow:2px 2px 0 rgba(35, 35, 35, 0.4);
border-radius: 4px;
-moz-border-radius: 4px;
cursor: pointer;
text-decoration: none;
color: #404040;
}
.NB-button:active {
-moz-box-shadow:1px 1px 0 rgba(35, 35, 35, 0.6);
-webkit-box-shadow:1px 1px 0 rgba(35, 35, 35, 0.6);
box-shadow:1px 1px 0 rgba(35, 35, 35, 0.6);
}
.NB-welcome-container.NB-welcome-tryout {
overflow-x: hidden;
}
/* ========== */
/* = Header = */
/* ========== */
.NB-welcome-header {
background-color: #238232;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#69A1D1), to(#E5B568));
background: -moz-linear-gradient(center top , #69A1D1 0%, #E5B568 100%) repeat scroll 0 0 transparent;
height: 400px;
color: white;
overflow: hidden;
box-shadow: 0 1px 2px rgba(10, 10, 10, 0.3);
}
.NB-welcome-header .NB-welcome-header-logo {
padding: 108px 0 0 0;
text-align: center;
width: 460px;
}
.NB-welcome-header .NB-welcome-header-tagline {
margin: 36px 0 0 0;
font-size: 22px;
text-shadow: 0 1px 0 rgba(35,35,35,0.35);
text-align: center;
width: 460px;
}
.NB-welcome-header .NB-welcome-header-tagline b {
padding: 2px 8px;
background-color: rgba(205, 205, 205, 0.1);
font-weight: normal;
border-radius: 4px;
}
.NB-welcome-header-image {
position: absolute;
right: 0;
bottom: 0;
}
.NB-welcome-header-image img.NB-1 {
height: 300px;
position: absolute;
bottom: -350px;
right: -40px;
border-radius: 2px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.NB-welcome-header-image.NB-active img.NB-1 {
bottom: 0px;
}
.NB-welcome-header-image img.NB-2 {
opacity: 0;
height: 300px;
position: absolute;
bottom: -300px;
right: 0;
border-radius: 2px;
margin-right: 100px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.NB-welcome-header-image img.NB-3 {
opacity: 0;
height: 300px;
position: absolute;
bottom: -300px;
right: -24px;
border-radius: 2px;
margin-right: 140px;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.NB-welcome-header-captions {
text-align: center;
margin: 0 auto;
position: absolute;
top: 0;
right: 32px;
overflow: hidden;
text-shadow: 0 1px 0 rgba(35,35,35,0.35);
padding-left: 64px;
}
.NB-welcome-header-caption {
float: left;
color: white;
cursor: pointer;
padding: 36px 12px 12px;
text-transform: uppercase;
}
.NB-welcome-header-caption.NB-welcome-header-caption-signin {
color: #E8F64A;
}
.NB-welcome-header-caption span {
padding: 2px 8px;
border-radius: 4px;
display: block;
-webkit-transition: all 1s ease-in-out;
-moz-transition: all 1s ease-in-out;
-o-transition: all 1s ease-in-out;
-ms-transition: all 1s ease-in-out;
}
.NB-welcome-header-caption:hover span {
-webkit-transition: all .25s ease-in-out;
-moz-transition: all .25s ease-in-out;
-o-transition: all .25s ease-in-out;
-ms-transition: all .25s ease-in-out;
}
.NB-welcome-header-caption.NB-active span {
color: #FDC85B;
background-color: rgba(235, 235, 235, 0.1);
}
.NB-welcome-header-actions {
margin: 36px 0 0;
line-height: 16px;
}
.NB-welcome-header-action {
float: left;
}
.NB-welcome-header-action-subtext {
text-align: center;
line-height: 24px;
padding: 0 12px;
font-size: 11px;
text-transform: uppercase;
color: white;
text-shadow: 0 1px 0 rgba(35, 35, 35, 0.4);
}
.NB-welcome-header-actions img {
vertical-align: top;
width: 16px;
height: 16px;
}
.NB-welcome-header-actions .NB-welcome-header-action-arrow {
display: none;
}
.NB-welcome-header-actions .NB-active .NB-welcome-header-action-bolt {
display: none;
}
.NB-welcome-header-actions .NB-active .NB-welcome-header-action-arrow {
display: block;
}
.NB-welcome-header-actions .NB-button {
float: left;
font-size: 16px;
padding: 6px 10px;
margin: 0 24px;
text-transform: none;
text-shadow: 0 1px 0 rgba(235, 235, 235, 0.4);
background-color: #F5FFE2;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#F5FFE2), to(#E8FACA));
background: -moz-linear-gradient(center top, #F5FFE2 0%, #E8FACA 100%);
}
.NB-welcome-header-actions .NB-button:hover {
background-color: #F4DD43;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#F4DD43), to(#F1D526));
background: -moz-linear-gradient(center top, #F4DD43 0%, #F1D526 100%);
}
.NB-welcome-header-actions .NB-button:active {
text-shadow: 0 1px 0 rgba(35, 35, 35, 0.4);
background-color: #E97326;
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#E97326), to(#D94B0D));
background: -moz-linear-gradient(center top, #E97326 0%, #D94B0D 100%);
color: white;
}
/* ================== */
/* = Login / Signup = */
/* ================== */
.NB-welcome-header-account {
position: absolute;
bottom: -350px;
right: 12px;
width: 400px;
border-top-left-radius: 8px;
border-top-right-radius: 8px;
background-color: rgba(245, 245, 245, 0.4);
padding: 36px 36px 12px;
}
.NB-welcome-header-account.NB-active {
bottom: 0;
}
.NB-welcome-header-account .NB-module-header-login {
width: 142px;
margin: 0 50px 0 0;
padding: 0 4px;
float: left;
text-shadow: 0 1px 0 rgba(35, 35, 35, 0.3);
}
.NB-welcome-header-account .NB-module-header-signup {
width: 142px;
margin: 0;
padding: 0 4px;
float: left;
text-shadow: 0 1px 0 rgba(35, 35, 35, 0.3);
}
.NB-welcome-header-account .NB-login {
padding: 0 4px;
margin: 12px 50px 0 12px;
width: 146px;
float: left;
}
.NB-welcome-header-account .NB-signup {
float: left;
width: 146px;
padding: 0 4px;
margin: 12px 0 0;
height: 206px;
overflow: hidden;
}
.NB-welcome-header-account .NB-import-signup {
float: left;
width: 146px;
padding: 0 0 64px 0;
margin: 0 24px 0 12px;
height: 206px;
overflow: hidden;
}
.NB-welcome-header-account .NB-import-signup-text {
text-align: center;
width: 190px;
}
.NB-welcome-header-account .NB-import-signup-text h3 {
margin-top: 48px;
color: #20843D;
}
.NB-welcome-header-account .NB-import-signup-text p {
color: #636363;
}
.NB-welcome-header-account .NB-signup-orline {
margin: 30px auto 24px;
text-align: center;
font-size: 14px;
line-height: 10px;
color: #F9F9F9;
font-weight: bold;
}
.NB-welcome-header-account .NB-signup-orline-reduced {
margin: 0px auto 0px;
}
.NB-welcome-header-account .NB-signup-orline .NB-signup-orline-or {
padding: 0 4px;
}
.NB-welcome-header-account .NB-signup-optional {
float: right;
text-transform: uppercase;
color: rgba(255, 255, 255, 0.4);
font-weight: bold;
font-size: 9px;
line-height: 17px;
}
.NB-welcome-header-account .NB-signup-hidden {
display: none;
}
.NB-welcome-header-account .NB-import-signup {
height: auto;
}
.NB-welcome-header-account .NB-signup .NB-signup-google {
margin: 12px auto;
display: block;
font-size: 12px;
text-align: center;
}
.NB-welcome-header-account input[type=text],
.NB-welcome-header-account input[type=password] {
border: 1px solid rgba(35, 35, 35, 0.5);
display:block;
font-size:13px;
margin:0 0 12px;
padding:5px;
width:134px;
}
.NB-welcome-header-account input[type=text]:focus,
.NB-welcome-header-account input[type=password]:focus {
border-color: #739BBE;
}
.NB-welcome-header-account input[type=submit] {
outline: none;
width: 146px; /* 174=input-width + 5*2=padding + 2=border */
margin: 4px 5px 0 0;
padding: 4px 10px 5px;
text-shadow: 0 -1px 0 rgba(35, 35, 35, 0.4);
font-weight: bold;
font-size: 12px;
border: 1px solid rgba(35, 35, 35, 0.5);
color: white;
cursor: pointer;
}
.NB-welcome-header-account input[type=hidden] {
display: none;
}
.NB-welcome-header-account label,
.NB-welcome-header-account .NB-account-label {
margin: 0;
color: #FFFFD5;
text-shadow: 0 1px 0 rgba(35, 35, 35, 0.3);
font-size: 12px;
display: block;
text-transform: uppercase;
}
.NB-welcome-header-account .NB-account-label {
font-weight: bold;
}
.NB-welcome-header-account .NB-account-text {
font-size: 13px;
color: #90A0B0;
margin-bottom: 30px;
}
.NB-welcome-header-account .NB-account-text a {
text-decoration: none;
color: #3E4773;
}
.NB-welcome-header-account .NB-account-text a:hover {
color: #0E1763;
}
.NB-welcome-header-account .errorlist {
list-style: none;
font-size: 12px;
color: #AF4D18;
font-weight: bold;
padding: 0;
}
/* ============ */
/* = Features = */
/* ============ */
.NB-welcome-features {
margin: 48px 0;
overflow: hidden;
}
.NB-welcome-features .NB-feature {
float: left;
width: 204px;
margin: 0 36px 0 0;
text-align: center;
}
.NB-welcome-features img {
width: 200px;
height: 175px;
box-shadow: 0 0 3px rgba(30, 30, 30, 0.3);
}
.NB-welcome-features .NB-feature-caption {
font-weight: bold;
font-size: 24px;
margin: 24px 0 0;
}
.NB-welcome-features .NB-feature-text {
color: #808080;
margin: 24px 0 0;
font-size: 15px;
line-height: 22px;
}
/* =========== */
/* = Pricing = */
/* =========== */
.NB-welcome-pricing {
overflow: hidden;
clear: both;
margin: 48px 0 0;
padding: 36px 0;
background-color: #FAFAFA;
border-top: 1px solid #F0F0F0;
border-bottom: 1px solid #F0F0F0;
}
.NB-welcome-pricing p {
line-height: 24px;
}
.NB-welcome-pricing .NB-price {
float: right;
margin: 0;
padding: 0;
line-height: 16px;
}
.NB-pricing {
margin: 12px 0;
width: 100%;
font-size: 14px;
}
.NB-pricing th,
.NB-pricing td {
padding: 10px 8px;
text-align: left;
line-height: 16px;
width: 33%;
vertical-align: top;
}
.NB-pricing th {
border-right: 1px solid #E4E4E4;
}
.NB-pricing td {
border-top: 1px solid #E4E4E4;
border-right: 1px solid #E4E4E4;
}
.NB-pricing .NB-last {
border-right: none;
}
.NB-pricing .NB-bold {
font-weight: bold;
}
.NB-pricing .NB-shiloh {
display: block;
float: left;
border: 1px solid #606060;
height: 54px;
margin: 0 12px 0 0;
}
/* ============ */
/* = Activity = */
/* ============ */
.NB-welcome-activity {
overflow: hidden;
clear: both;
padding: 36px 0;
}
.NB-welcome-activity .NB-module {
width: 47%;
}
.NB-welcome-activity .NB-module-features {
float: right;
}
.NB-welcome-activity .NB-module-stats {
float: left;
margin-right: 3%;
}
.NB-welcome-activity .NB-module .NB-module-header {
display: none;
}
.NB-welcome-activity .NB-module .NB-module-stats-counts {
overflow: hidden;
}
.NB-welcome-activity .NB-module-stats .NB-module-content-header-hour {
margin-top: 36px;
}
.NB-welcome-activity .NB-module-stats-count-graph {
margin-left: 4px;
}
.NB-welcome-activity .NB-graph-bar {
width: 4px;
}
.NB-welcome-activity .NB-graph-value {
width: 4px;
}
/* ========== */
/* = Footer = */
/* ========== */
.NB-welcome-footer {
overflow: hidden;
clear: both;
padding: 48px 0 52px;
color: #363C53;
line-height: 24px;
background-color: #FAFAFA;
border-top: 1px solid #F0F0F0;
}
.NB-welcome-footer .NB-footer-logo {
vertical-align: text-bottom;
position: relative;
bottom: -4px;
}
.NB-welcome-footer .NB-splash-link {
color: #363C53;
font-weight: bold;
text-shadow: 0 1px 0 rgba(245, 245, 245, 0.4);
}
.NB-welcome-footer .NB-splash-link:hover {
color: #822216;
}
.NB-welcome-footer .NB-footer-icons {
float: right;
}
.NB-welcome-footer .NB-footer-icons a {
line-height: 0;
margin: 0 12px 0 0;
float: right;
}
.NB-welcome-footer .NB-footer-icons a img {
vertical-align: middle;
width: 32px;
height: 32px;
position: relative;
bottom: -4px;
opacity: .8;
-webkit-transition: all .25s ease-in-out;
-moz-transition: all .25s ease-in-out;
-o-transition: all .25s ease-in-out;
-ms-transition: all .25s ease-in-out;
}
.NB-welcome-footer .NB-footer-icons a img:hover {
opacity: 1;
}
.NB-welcome-footer .NB-twitter-avatar {
width: 24px;
height: 24px;
border: none;
box-shadow: 0 0 1px #C0C0C0;
vertical-align: bottom;
position: relative;
bottom: 0;
margin: 0 2px 0 4px;
border-radius: 2px;
}

BIN
media/img/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
media/img/favicon_32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
media/img/favicon_64.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
media/img/logo_144.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 787 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 992 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -118,8 +118,8 @@
} else if ([category isEqualToString:@"comment_like"]) {
withUserUsername = [[activity objectForKey:@"with_user"] objectForKey:@"username"];
txt = [NSString stringWithFormat:@"%@ favorited %@'s comment on %@:\n%@", username, withUserUsername, title, comment];
} else if ([category isEqualToString:@"sharedstory"]) {
if ([content isEqualToString:@""] || content == nil) {
} else if ([category isEqualToString:@"sharedstory"]) {
if ([content class] == [NSNull class] || [content isEqualToString:@""] || content == nil) {
txt = [NSString stringWithFormat:@"%@ shared %@.", username, title];
} else {
txt = [NSString stringWithFormat:@"%@ shared %@:\n%@", username, title, comment];
@ -130,7 +130,7 @@
} else if ([category isEqualToString:@"feedsub"]) {
txt = [NSString stringWithFormat:@"You subscribed to %@.", content];
} else if ([category isEqualToString:@"signup"]) {
txt = [NSString stringWithFormat:@"You signed up for NewsBlur.", content];
txt = [NSString stringWithFormat:@"You signed up for NewsBlur."];
}
NSString *txtWithTime = [NSString stringWithFormat:@"%@\n%@", txt, time];

View file

@ -100,7 +100,6 @@
[self showFolderPicker];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
NSLog(@"%@", self.siteTable.frame);
self.siteTable.hidden = NO;
self.siteScrollView.frame = CGRectMake(self.siteScrollView.frame.origin.x,
self.siteScrollView.frame.origin.y,
@ -278,6 +277,7 @@
[request setDelegate:self];
[request setDidFinishSelector:@selector(requestFinished:)];
[request setDidFailSelector:@selector(requestFailed:)];
[request setTimeOutSeconds:30];
[request startAsynchronous];
}

View file

@ -66,7 +66,6 @@ static UIFont *indicatorFont = nil;
[backgroundColor set];
CGContextFillRect(context, r);
NSLog(@"WIDTH is %f", rect.size.width);
// set site title
UIColor *textColor;
UIFont *font;

View file

@ -122,14 +122,16 @@
[super viewWillAppear:animated];
if ((appDelegate.isSocialRiverView || appDelegate.isRiverView || appDelegate.isSocialView)) {
if ((appDelegate.isSocialRiverView ||
appDelegate.isSocialView ||
[appDelegate.activeFolder isEqualToString:@"everything"])) {
settingsButton.enabled = NO;
} else {
settingsButton.enabled = YES;
}
if (appDelegate.isSocialRiverView ||
[appDelegate.activeFolder isEqualToString:@"All Stories"]) {
[appDelegate.activeFolder isEqualToString:@"everything"]) {
feedMarkReadButton.enabled = NO;
} else {
feedMarkReadButton.enabled = YES;
@ -152,11 +154,6 @@
[appDelegate.storyDetailViewController clearStory];
[self checkScroll];
}
NSString *title = appDelegate.isRiverView ?
appDelegate.activeFolder :
[appDelegate.activeFeed objectForKey:@"feed_title"];
NSLog(@"title %@", title);
}
- (void)viewWillDisappear:(BOOL)animated {
@ -266,14 +263,6 @@
[self.storyTitlesTable reloadData];
[storyTitlesTable scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
}
int readStoriesCount = 0;
if (self.feedPage > 1) {
for (id story in appDelegate.activeFeedStories) {
if ([[story objectForKey:@"read_status"] intValue] == 1) {
readStoriesCount += 1;
}
}
}
NSString *theFeedDetailURL;
@ -284,11 +273,10 @@
self.feedPage];
} else {
theFeedDetailURL = [NSString stringWithFormat:
@"http://%@/reader/river_stories/?feeds=%@&page=%d&read_stories_count=%d",
@"http://%@/reader/river_stories/?feeds=%@&page=%d",
NEWSBLUR_URL,
[appDelegate.activeFolderFeeds componentsJoinedByString:@"&feeds="],
self.feedPage,
readStoriesCount];
self.feedPage];
}
[self cancelRequests];
@ -973,10 +961,11 @@
self.actionSheet_ = nil;
return;
}
NSString *title = appDelegate.isRiverView ?
appDelegate.activeFolder :
[appDelegate.activeFeed objectForKey:@"feed_title"];
UIActionSheet *options = [[UIActionSheet alloc]
NSString *title = appDelegate.isRiverView ?
appDelegate.activeFolder :
[appDelegate.activeFeed objectForKey:@"feed_title"];
UIActionSheet *options = [[UIActionSheet alloc]
initWithTitle:title
delegate:self
cancelButtonTitle:nil
@ -985,20 +974,19 @@
self.actionSheet_ = options;
if (![title isEqualToString:@"Everything"]) {
NSString *deleteText = [NSString stringWithFormat:@"Delete %@",
appDelegate.isRiverView ?
@"this entire folder" :
@"this site"];
[options addButtonWithTitle:deleteText];
options.destructiveButtonIndex = 0;
NSString *moveText = @"Move to another folder";
[options addButtonWithTitle:moveText];
NSString *deleteText = [NSString stringWithFormat:@"Delete %@",
appDelegate.isRiverView ?
@"this entire folder" :
@"this site"];
[options addButtonWithTitle:deleteText];
options.destructiveButtonIndex = 0;
NSString *moveText = @"Move to another folder";
[options addButtonWithTitle:moveText];
if (!appDelegate.isRiverView) {
NSString *fetchText = @"Insta-fetch stories";
[options addButtonWithTitle:fetchText];
}
options.cancelButtonIndex = [options addButtonWithTitle:@"Cancel"];

View file

@ -8,6 +8,7 @@
#import <UIKit/UIKit.h>
#import "NewsBlurAppDelegate.h"
#import "UnreadCountView.h"
#import "ABTableViewCell.h"
@class NewsBlurAppDelegate;
@ -20,8 +21,6 @@
int _positiveCount;
int _neutralCount;
int _negativeCount;
NSString *_positiveCountStr;
NSString *_neutralCountStr;
NSString *_negativeCountStr;
BOOL isSocial;
}
@ -33,8 +32,6 @@
@property (assign, nonatomic) int neutralCount;
@property (assign, nonatomic) int negativeCount;
@property (assign, nonatomic) BOOL isSocial;
@property (nonatomic) NSString *positiveCountStr;
@property (nonatomic) NSString *neutralCountStr;
@property (nonatomic) NSString *negativeCountStr;
@end

View file

@ -8,17 +8,10 @@
#import "NewsBlurAppDelegate.h"
#import "FeedTableCell.h"
#import "UnreadCountView.h"
#import "ABTableViewCell.h"
#import "UIView+TKCategory.h"
static UIFont *textFont = nil;
static UIFont *indicatorFont = nil;
static UIColor *indicatorWhiteColor = nil;
static UIColor *indicatorBlackColor = nil;
static UIColor *positiveBackgroundColor = nil;
static UIColor *neutralBackgroundColor = nil;
static UIColor *negativeBackgroundColor = nil;
static CGFloat *psColors = nil;
@implementation FeedTableCell
@ -28,24 +21,12 @@ static CGFloat *psColors = nil;
@synthesize positiveCount = _positiveCount;
@synthesize neutralCount = _neutralCount;
@synthesize negativeCount = _negativeCount;
@synthesize positiveCountStr;
@synthesize neutralCountStr;
@synthesize negativeCountStr;
@synthesize isSocial;
+ (void) initialize{
if (self == [FeedTableCell class]) {
textFont = [UIFont boldSystemFontOfSize:18];
indicatorFont = [UIFont boldSystemFontOfSize:12];
indicatorWhiteColor = [UIColor whiteColor];
indicatorBlackColor = [UIColor blackColor];
UIColor *ps = UIColorFromRGB(0x3B7613);
UIColor *nt = UIColorFromRGB(0xF9C72A);
UIColor *ng = UIColorFromRGB(0xCC2A2E);
positiveBackgroundColor = ps;
neutralBackgroundColor = nt;
negativeBackgroundColor = ng;
// UIColor *psGrad = UIColorFromRGB(0x559F4D);
// UIColor *ntGrad = UIColorFromRGB(0xE4AB00);
// UIColor *ngGrad = UIColorFromRGB(0x9B181B);
@ -64,7 +45,6 @@ static CGFloat *psColors = nil;
if (ps == _positiveCount) return;
_positiveCount = ps;
_positiveCountStr = [NSString stringWithFormat:@"%d", ps];
[self setNeedsDisplay];
}
@ -72,7 +52,6 @@ static CGFloat *psColors = nil;
if (nt == _neutralCount) return;
_neutralCount = nt;
_neutralCountStr = [NSString stringWithFormat:@"%d", nt];
[self setNeedsDisplay];
}
@ -115,89 +94,9 @@ static CGFloat *psColors = nil;
CGContextStrokePath(context);
}
CGRect rect = CGRectInset(r, 12, 12);
rect.size.width -= 18; // Scrollbar padding
int psWidth = _positiveCount == 0 ? 0 : _positiveCount < 10 ?
14 : _positiveCount < 100 ? 22 : 28;
int ntWidth = _neutralCount == 0 ? 0 : _neutralCount < 10 ?
14 : _neutralCount < 100 ? 22 : 28;
int ngWidth = _negativeCount == 0 ? 0 : _negativeCount < 10 ?
14 : _negativeCount < 100 ? 22 : 28;
int psOffset = _positiveCount == 0 ? 0 : psWidth - 20;
int ntOffset = _neutralCount == 0 ? 0 : ntWidth - 20;
int ngOffset = _negativeCount == 0 ? 0 : ngWidth - 20;
int psPadding = _positiveCount == 0 ? 0 : 2;
int ntPadding = _neutralCount == 0 ? 0 : 2;
if(_positiveCount > 0){
[positiveBackgroundColor set];
CGRect rr;
if (self.isSocial) {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
rr = CGRectMake(rect.size.width + rect.origin.x - psOffset, 14, psWidth, 17);
} else {
rr = CGRectMake(rect.size.width + rect.origin.x - psOffset, 10, psWidth, 17);
}
} else {
rr = CGRectMake(rect.size.width + rect.origin.x - psOffset, 9, psWidth, 17);
}
;
[UIView drawLinearGradientInRect:rr colors:psColors];
[UIView drawRoundRectangleInRect:rr withRadius:4];
[indicatorWhiteColor set];
CGSize size = [_positiveCountStr sizeWithFont:indicatorFont];
float x_pos = (rr.size.width - size.width) / 2;
float y_pos = (rr.size.height - size.height) / 2;
[_positiveCountStr
drawAtPoint:CGPointMake(rr.origin.x + x_pos, rr.origin.y + y_pos)
withFont:indicatorFont];
}
if(_neutralCount > 0 && appDelegate.selectedIntelligence <= 0){
[neutralBackgroundColor set];
CGRect rr;
if (self.isSocial) {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
rr = CGRectMake(rect.size.width + rect.origin.x - psWidth - psPadding - ntOffset, 14, ntWidth, 17);
} else {
rr = CGRectMake(rect.size.width + rect.origin.x - psWidth - psPadding - ntOffset, 10, ntWidth, 17);
}
} else {
rr = CGRectMake(rect.size.width + rect.origin.x - psWidth - psPadding - ntOffset, 9, ntWidth, 17);
}
[UIView drawRoundRectangleInRect:rr withRadius:4];
// [UIView drawLinearGradientInRect:rr colors:ntColors];
[indicatorBlackColor set];
CGSize size = [_neutralCountStr sizeWithFont:indicatorFont];
float x_pos = (rr.size.width - size.width) / 2;
float y_pos = (rr.size.height - size.height) / 2;
[_neutralCountStr
drawAtPoint:CGPointMake(rr.origin.x + x_pos, rr.origin.y + y_pos)
withFont:indicatorFont];
}
if(_negativeCount > 0 && appDelegate.selectedIntelligence <= -1){
[negativeBackgroundColor set];
CGRect rr = CGRectMake(rect.size.width + rect.origin.x - psWidth - psPadding - ntWidth - ntPadding - ngOffset, self.isSocial ? 14: 9, ngWidth, 17);
[UIView drawRoundRectangleInRect:rr withRadius:4];
// [UIView drawLinearGradientInRect:rr colors:ngColors];
[indicatorWhiteColor set];
CGSize size = [_negativeCountStr sizeWithFont:indicatorFont];
float x_pos = (rr.size.width - size.width) / 2;
float y_pos = (rr.size.height - size.height) / 2;
[_negativeCountStr
drawAtPoint:CGPointMake(rr.origin.x + x_pos, rr.origin.y + y_pos)
withFont:indicatorFont];
}
UnreadCountView *unreadCount = [UnreadCountView alloc];
[unreadCount drawInRect:r ps:_positiveCount nt:_neutralCount
listType:(isSocial ? NBFeedListSocial : NBFeedListFeed)];
UIColor *textColor = self.selected || self.highlighted ?
[UIColor blackColor]:
@ -215,14 +114,14 @@ static CGFloat *psColors = nil;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[self.feedFavicon drawInRect:CGRectMake(12.0, 5.0, 36.0, 36.0)];
[feedTitle
drawInRect:CGRectMake(56, 13, rect.size.width - psWidth - psPadding - ntWidth - ntPadding - ngWidth - 10 - 20, 20.0)
drawInRect:CGRectMake(56, 13, [unreadCount offsetWidth] - 10 - 20, 20.0)
withFont:font
lineBreakMode:UILineBreakModeTailTruncation
alignment:UITextAlignmentLeft];
} else {
[self.feedFavicon drawInRect:CGRectMake(9.0, 3.0, 32.0, 32.0)];
[feedTitle
drawInRect:CGRectMake(50, 11, rect.size.width - psWidth - psPadding - ntWidth - ntPadding - ngWidth - 10 - 20, 20.0)
drawInRect:CGRectMake(50, 11, [unreadCount offsetWidth] - 10 - 20, 20.0)
withFont:font
lineBreakMode:UILineBreakModeTailTruncation
alignment:UITextAlignmentLeft];
@ -230,16 +129,16 @@ static CGFloat *psColors = nil;
} else {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[self.feedFavicon drawInRect:CGRectMake(12.0, 9.0, 16.0, 16.0)];
[self.feedFavicon drawInRect:CGRectMake(12.0, 7.0, 16.0, 16.0)];
[feedTitle
drawInRect:CGRectMake(36.0, 9.0, rect.size.width - psWidth - psPadding - ntWidth - ntPadding - ngWidth - 10, 20.0)
drawInRect:CGRectMake(36.0, 7.0, [unreadCount offsetWidth] - 10, 20.0)
withFont:font
lineBreakMode:UILineBreakModeTailTruncation
alignment:UITextAlignmentLeft];
} else {
[self.feedFavicon drawInRect:CGRectMake(9.0, 9.0, 16.0, 16.0)];
[self.feedFavicon drawInRect:CGRectMake(9.0, 7.0, 16.0, 16.0)];
[feedTitle
drawInRect:CGRectMake(34.0, 9.0, rect.size.width - psWidth - psPadding - ntWidth - ntPadding - ngWidth - 10, 20.0)
drawInRect:CGRectMake(34.0, 7.0, [unreadCount offsetWidth] - 10, 20.0)
withFont:font
lineBreakMode:UILineBreakModeTailTruncation
alignment:UITextAlignmentLeft];

View file

@ -1,23 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1296</int>
<string key="IBDocument.SystemVersion">11E53</string>
<string key="IBDocument.InterfaceBuilderVersion">2182</string>
<string key="IBDocument.AppKitVersion">1138.47</string>
<string key="IBDocument.HIToolboxVersion">569.00</string>
<int key="IBDocument.SystemTarget">1536</int>
<string key="IBDocument.SystemVersion">12C54</string>
<string key="IBDocument.InterfaceBuilderVersion">2840</string>
<string key="IBDocument.AppKitVersion">1187.34</string>
<string key="IBDocument.HIToolboxVersion">625.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">1181</string>
<string key="NS.object.0">1926</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBUIView</string>
<string>IBUIImageView</string>
<string>IBUILabel</string>
<string>IBProxyObject</string>
<string>IBUIActivityIndicatorView</string>
<string>IBUISwitch</string>
<string>IBUIButton</string>
<string>IBUIImageView</string>
<string>IBUILabel</string>
<string>IBUISwitch</string>
<string>IBUIView</string>
</array>
<array key="IBDocument.PluginDependencies">
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
@ -69,10 +69,11 @@
<int key="IBUIContentMode">7</int>
<bool key="IBUIUserInteractionEnabled">NO</bool>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<string key="IBUIText">Connect with your friends to easily follow the stories that matter to them.</string>
<string key="IBUIText">Connect with your friends to easily follow them.</string>
<object class="NSColor" key="IBUITextColor" id="827336334">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MCAwIDAAA</bytes>
<string key="IBUIColorCocoaTouchKeyPath">darkTextColor</string>
</object>
<nil key="IBUIHighlightedColor"/>
<object class="NSColor" key="IBUIShadowColor" id="937995787">
@ -93,11 +94,12 @@
<double key="NSSize">18</double>
<int key="NSfFlags">16</int>
</object>
<double key="preferredMaxLayoutWidth">400</double>
</object>
<object class="IBUILabel" id="961208593">
<reference key="NSNextResponder" ref="874391841"/>
<int key="NSvFlags">303</int>
<string key="NSFrame">{{121, 421}, {298, 106}}</string>
<string key="NSFrame">{{116, 421}, {314, 106}}</string>
<reference key="NSSuperview" ref="874391841"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="436803934"/>
@ -124,6 +126,7 @@
<double key="NSSize">13</double>
<int key="NSfFlags">16</int>
</object>
<double key="preferredMaxLayoutWidth">314</double>
</object>
<object class="IBUIButton" id="406293652">
<reference key="NSNextResponder" ref="874391841"/>
@ -1521,6 +1524,7 @@
<string key="innerView">UIView</string>
<string key="intelligenceControl">UISegmentedControl</string>
<string key="noFocusMessage">UIView</string>
<string key="toolbarLeftMargin">UIBarButtonItem</string>
</dictionary>
<dictionary class="NSMutableDictionary" key="toOneOutletInfosByName">
<object class="IBToOneOutletInfo" key="appDelegate">
@ -1555,6 +1559,10 @@
<string key="name">noFocusMessage</string>
<string key="candidateClassName">UIView</string>
</object>
<object class="IBToOneOutletInfo" key="toolbarLeftMargin">
<string key="name">toolbarLeftMargin</string>
<string key="candidateClassName">UIBarButtonItem</string>
</object>
</dictionary>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
@ -1848,7 +1856,7 @@
<string key="IBDocument.TargetRuntimeIdentifier">IBIPadFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
<real value="1296" key="NS.object.0"/>
<real value="1536" key="NS.object.0"/>
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
@ -1857,6 +1865,6 @@
<string key="subtle-pattern-4.jpg">{1000, 1000}</string>
<string key="twitter.png">{184, 34}</string>
</dictionary>
<string key="IBCocoaTouchPluginVersion">1181</string>
<string key="IBCocoaTouchPluginVersion">1926</string>
</data>
</archive>

View file

@ -0,0 +1,23 @@
//
// FolderTitleView.h
// NewsBlur
//
// Created by Samuel Clay on 10/2/12.
// Copyright (c) 2012 NewsBlur. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "NewsBlurAppDelegate.h"
@class NewsBlurAppDelegate;
@interface FolderTitleView : UIControl {
NewsBlurAppDelegate *appDelegate;
}
@property (nonatomic) NewsBlurAppDelegate *appDelegate;
- (UIControl *)drawWithRect:(CGRect)rect inSection:(NSInteger)section;
@end

View file

@ -0,0 +1,164 @@
//
// FolderTitleView.m
// NewsBlur
//
// Created by Samuel Clay on 10/2/12.
// Copyright (c) 2012 NewsBlur. All rights reserved.
//
#import "NewsBlurAppDelegate.h"
#import "FolderTitleView.h"
#import "UnreadCountView.h"
@implementation FolderTitleView
@synthesize appDelegate;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
- (UIControl *)drawWithRect:(CGRect)rect inSection:(NSInteger)section {
self.appDelegate = (NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate];
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
NSString *folderName;
if (section == 0) {
folderName = @"river_blurblogs";
} else {
folderName = [appDelegate.dictFoldersArray objectAtIndex:section];
}
NSString *collapseKey = [NSString stringWithFormat:@"folderCollapsed:%@", folderName];
bool isFolderCollapsed = [userPreferences boolForKey:collapseKey];
// create the parent view that will hold header Label
UIControl* customView = [[UIControl alloc]
initWithFrame:rect];
UIView *borderTop = [[UIView alloc]
initWithFrame:CGRectMake(rect.origin.x, rect.origin.y,
rect.size.width, 1.0)];
borderTop.backgroundColor = UIColorFromRGB(0xe0e0e0);
borderTop.opaque = NO;
[customView addSubview:borderTop];
UIView *borderBottom = [[UIView alloc]
initWithFrame:CGRectMake(rect.origin.x, rect.size.height-1,
rect.size.width, 1.0)];
borderBottom.backgroundColor = [UIColorFromRGB(0xB7BDC6) colorWithAlphaComponent:0.5];
borderBottom.opaque = NO;
[customView addSubview:borderBottom];
UILabel * headerLabel = [[UILabel alloc] initWithFrame:CGRectZero];
customView.opaque = NO;
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.opaque = NO;
headerLabel.textColor = [UIColor colorWithRed:0.3 green:0.3 blue:0.3 alpha:1.0];
headerLabel.highlightedTextColor = [UIColor whiteColor];
headerLabel.font = [UIFont boldSystemFontOfSize:11];
headerLabel.frame = CGRectMake(36.0, 1.0, rect.size.width - 36, rect.size.height);
headerLabel.shadowColor = [UIColor colorWithRed:.94 green:0.94 blue:0.97 alpha:1.0];
headerLabel.shadowOffset = CGSizeMake(0.0, 1.0);
if (section == 0) {
headerLabel.text = @"ALL BLURBLOG STORIES";
// customView.backgroundColor = [UIColorFromRGB(0xD7DDE6)
// colorWithAlphaComponent:0.8];
} else if (section == 1) {
headerLabel.text = @"ALL STORIES";
// customView.backgroundColor = [UIColorFromRGB(0xE6DDD7)
// colorWithAlphaComponent:0.8];
} else {
headerLabel.text = [[appDelegate.dictFoldersArray objectAtIndex:section] uppercaseString];
// customView.backgroundColor = [UIColorFromRGB(0xD7DDE6)
// colorWithAlphaComponent:0.8];
}
customView.backgroundColor = [UIColorFromRGB(0xD7DDE6)
colorWithAlphaComponent:0.8];
[customView addSubview:headerLabel];
UIButton *invisibleHeaderButton = [UIButton buttonWithType:UIButtonTypeCustom];
invisibleHeaderButton.frame = CGRectMake(0, 0, customView.frame.size.width, customView.frame.size.height);
invisibleHeaderButton.alpha = .1;
invisibleHeaderButton.tag = section;
[invisibleHeaderButton addTarget:appDelegate.feedsViewController action:@selector(didSelectSectionHeader:) forControlEvents:UIControlEventTouchUpInside];
[customView addSubview:invisibleHeaderButton];
[invisibleHeaderButton addTarget:appDelegate.feedsViewController action:@selector(sectionTapped:) forControlEvents:UIControlEventTouchDown];
[invisibleHeaderButton addTarget:appDelegate.feedsViewController action:@selector(sectionUntapped:) forControlEvents:UIControlEventTouchUpInside];
[invisibleHeaderButton addTarget:appDelegate.feedsViewController action:@selector(sectionUntappedOutside:) forControlEvents:UIControlEventTouchUpOutside];
if (!appDelegate.hasNoSites) {
if (section != 1) {
UIImage *disclosureBorder = [UIImage imageNamed:@"disclosure_border.png"];
UIImageView *disclosureBorderView = [[UIImageView alloc] initWithImage:disclosureBorder];
disclosureBorderView.frame = CGRectMake(customView.frame.size.width - 30, -1, 29, 29);
[customView addSubview:disclosureBorderView];
}
UIButton *disclosureButton = [UIButton buttonWithType:UIButtonTypeCustom];
UIImage *disclosureImage = [UIImage imageNamed:@"disclosure.png"];
[disclosureButton setImage:disclosureImage forState:UIControlStateNormal];
disclosureButton.frame = CGRectMake(customView.frame.size.width - 30, -1, 29, 29);
if (section != 1) {
if (!isFolderCollapsed) {
disclosureButton.transform = CGAffineTransformMakeRotation(M_PI_2);
}
disclosureButton.tag = section;
[disclosureButton addTarget:appDelegate.feedsViewController action:@selector(didCollapseFolder:) forControlEvents:UIControlEventTouchUpInside];
}
[customView addSubview:disclosureButton];
}
UIImage *folderImage;
int folderImageViewX = 10;
if (section == 0) {
folderImage = [UIImage imageNamed:@"group.png"];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
folderImageViewX = 10;
} else {
folderImageViewX = 8;
}
} else if (section == 1) {
folderImage = [UIImage imageNamed:@"archive.png"];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
folderImageViewX = 10;
} else {
folderImageViewX = 7;
}
} else {
if (isFolderCollapsed) {
folderImage = [UIImage imageNamed:@"folder_collapsed.png"];
} else {
folderImage = [UIImage imageNamed:@"folder_2.png"];
}
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
} else {
folderImageViewX = 7;
}
}
UIImageView *folderImageView = [[UIImageView alloc] initWithImage:folderImage];
folderImageView.frame = CGRectMake(folderImageViewX, 3, 20, 20);
[customView addSubview:folderImageView];
[customView setAutoresizingMask:UIViewAutoresizingNone];
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextFillRect(context, rect);
UnreadCountView *unreadCount = [UnreadCountView alloc];
[unreadCount drawInRect:rect ps:123 nt:321
listType:NBFeedListFolder];
return customView;
}
@end

View file

@ -90,8 +90,6 @@
// Return YES for supported orientations
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
return YES;
} else if (UIInterfaceOrientationIsPortrait(interfaceOrientation)) {
return YES;
}
return NO;
}
@ -145,7 +143,7 @@
}
#pragma mark -
#pragma mark Loginp
#pragma mark Login
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
[textField resignFirstResponder];
@ -165,8 +163,6 @@
if(textField == usernameInput) {
[passwordInput becomeFirstResponder];
} else if (textField == passwordInput && [self.loginControl selectedSegmentIndex] == 0) {
NSLog(@"Password return");
NSLog(@"appdelegate:: %@", [self appDelegate]);
[self checkPassword];
} else if (textField == passwordInput && [self.loginControl selectedSegmentIndex] == 1) {
[emailInput becomeFirstResponder];

View file

@ -235,6 +235,8 @@
[self.folders addObject:@"— Top Level —"];
for (NSString *folder in appDelegate.dictFoldersArray) {
if ([folder isEqualToString:@"everything"]) continue;
if ([folder isEqualToString:@"river_blurblogs"]) continue;
if ([[folder trim] isEqualToString:@""]) continue;
if (appDelegate.isRiverView) {
if (![folder containsString:appDelegate.activeFolder]) {

View file

@ -73,6 +73,7 @@
NSString * activeUsername;
NSString * activeUserProfileId;
NSString * activeUserProfileName;
BOOL hasNoSites;
BOOL isRiverView;
BOOL isSocialView;
BOOL isSocialRiverView;
@ -147,6 +148,7 @@
@property (readwrite) NSString * activeUsername;
@property (readwrite) NSString * activeUserProfileId;
@property (readwrite) NSString * activeUserProfileName;
@property (nonatomic, readwrite) BOOL hasNoSites;
@property (nonatomic, readwrite) BOOL isRiverView;
@property (nonatomic, readwrite) BOOL isSocialView;
@property (nonatomic, readwrite) BOOL isSocialRiverView;

View file

@ -70,6 +70,7 @@
@synthesize activeUsername;
@synthesize activeUserProfileId;
@synthesize activeUserProfileName;
@synthesize hasNoSites;
@synthesize isRiverView;
@synthesize isSocialView;
@synthesize isSocialRiverView;
@ -509,9 +510,8 @@
- (void)adjustStoryDetailWebView {
// change UIWebView
int contentWidth = storyDetailViewController.view.frame.size.width;
NSLog(@"contentWidth is %i", contentWidth);
// NSLog(@"contentWidth is %i", contentWidth);
[storyDetailViewController changeWebViewWidth:contentWidth];
}
- (void)calibrateStoryTitles {
@ -545,7 +545,13 @@
- (void)loadStoryDetailView {
NSString *feedTitle;
if (self.isRiverView) {
feedTitle = self.activeFolder;
if ([self.activeFolder isEqualToString:@"river_blurblogs"]) {
feedTitle = @"All Shared Stories";
} else if ([self.activeFolder isEqualToString:@"everything"]) {
feedTitle = @"All Stories";
} else {
feedTitle = self.activeFolder;
}
} else {
feedTitle = [activeFeed objectForKey:@"feed_title"];
}
@ -758,11 +764,11 @@
int total = 0;
NSArray *folder;
if (!folderName && self.activeFolder == @"ALL BLURBLOG STORIES") {
if (!folderName && self.activeFolder == @"river_blurblogs") {
for (id feedId in self.dictSocialFeeds) {
total += [self unreadCountForFeed:feedId];
}
} else if (!folderName && self.activeFolder == @"ALL STORIES STORIES") {
} else if (!folderName && self.activeFolder == @"everything") {
for (id feedId in self.dictFeeds) {
total += [self unreadCountForFeed:feedId];
}
@ -960,7 +966,7 @@
}
- (void)markActiveFolderAllRead {
if (self.activeFolder == @"Everything") {
if (self.activeFolder == @"everything") {
for (NSString *folderName in self.dictFoldersArray) {
for (id feedId in [self.dictFolders objectForKey:folderName]) {
[self markFeedAllRead:feedId];
@ -1153,9 +1159,11 @@
- (UIView *)makeFeedTitle:(NSDictionary *)feed {
UILabel *titleLabel = [[UILabel alloc] init];
if (self.isSocialRiverView) {
titleLabel.text = [NSString stringWithFormat:@" All Blurblog Stories"];
titleLabel.text = [NSString stringWithFormat:@" All Shared Stories"];
} else if (self.isRiverView && [self.activeFolder isEqualToString:@"everything"]) {
titleLabel.text = [NSString stringWithFormat:@" All Stories"];
} else if (self.isRiverView) {
titleLabel.text = [NSString stringWithFormat:@" %@", self.activeFolder];
titleLabel.text = [NSString stringWithFormat:@" %@", self.activeFolder];
} else if (self.isSocialView) {
titleLabel.text = [NSString stringWithFormat:@" %@", [feed objectForKey:@"feed_title"]];
} else {

View file

@ -8,6 +8,7 @@
#import <UIKit/UIKit.h>
#import "NewsBlurAppDelegate.h"
#import "FolderTitleView.h"
#import "ASIHTTPRequest.h"
#import "PullToRefreshView.h"
#import "BaseViewController.h"
@ -27,7 +28,6 @@
NSMutableDictionary *visibleFeeds;
NSMutableDictionary *stillVisibleFeeds;
BOOL viewShowingAllFeeds;
BOOL hasNoSites;
PullToRefreshView *pull;
NSDate *lastUpdate;
NSCache *imageCache;
@ -52,7 +52,6 @@
@property (nonatomic) NSMutableDictionary *visibleFeeds;
@property (nonatomic) NSMutableDictionary *stillVisibleFeeds;
@property (nonatomic, readwrite) BOOL viewShowingAllFeeds;
@property (nonatomic, readwrite) BOOL hasNoSites;
@property (nonatomic) PullToRefreshView *pull;
@property (nonatomic) NSDate *lastUpdate;
@property (nonatomic) NSCache *imageCache;
@ -70,6 +69,7 @@
- (void)setUserAvatarLayout:(UIInterfaceOrientation)orientation;
- (void)didSelectSectionHeader:(UIButton *)button;
- (IBAction)selectIntelligence;
- (void)didCollapseFolder:(UIButton *)button;
- (void)changeToAllMode;
- (void)updateFeedsWithIntelligence:(int)previousLevel newLevel:(int)newLevel;
- (void)calculateFeedLocations:(BOOL)markVisible;

View file

@ -22,10 +22,11 @@
#import "UIBarButtonItem+WEPopover.h"
#define kPhoneTableViewRowHeight 36;
#define kTableViewRowHeight 36;
#define kPhoneTableViewRowHeight 32;
#define kTableViewRowHeight 32;
#define kBlurblogTableViewRowHeight 47;
#define kPhoneBlurblogTableViewRowHeight 39;
static const CGFloat kFolderTitleHeight = 28;
@interface NewsBlurViewController ()
@ -55,7 +56,6 @@
@synthesize currentRowAtIndexPath;
@synthesize noFocusMessage;
@synthesize toolbarLeftMargin;
@synthesize hasNoSites;
@synthesize updatedDictFeeds_;
@synthesize updatedDictSocialFeeds_;
@synthesize inPullToRefresh_;
@ -295,7 +295,7 @@
return [self informError:@"The server barfed!"];
}
self.hasNoSites = NO;
appDelegate.hasNoSites = NO;
NSString *responseString = [request responseString];
NSData *responseData=[responseString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
@ -393,10 +393,10 @@
allFolders = [[results objectForKey:@"flat_folders"] mutableCopy];
}
[allFolders setValue:socialFolder forKey:@""];
[allFolders setValue:socialFolder forKey:@"river_blurblogs"];
if (![[allFolders allKeys] containsObject:@" "]) {
[allFolders setValue:[[NSArray alloc] init] forKey:@" "];
if (![[allFolders allKeys] containsObject:@"everything"]) {
[allFolders setValue:[[NSArray alloc] init] forKey:@"everything"];
}
appDelegate.dictFolders = allFolders;
@ -434,6 +434,16 @@
}
appDelegate.dictFolders = sortedFolders;
[appDelegate.dictFoldersArray sortUsingSelector:@selector(caseInsensitiveCompare:)];
// Move River Blurblog and Everything to the top
if ([appDelegate.dictFoldersArray containsObject:@"river_blurblogs"]) {
[appDelegate.dictFoldersArray removeObject:@"river_blurblogs"];
[appDelegate.dictFoldersArray insertObject:@"river_blurblogs" atIndex:0];
}
if ([appDelegate.dictFoldersArray containsObject:@"everything"]) {
[appDelegate.dictFoldersArray removeObject:@"everything"];
[appDelegate.dictFoldersArray insertObject:@"everything" atIndex:1];
}
if (self.viewShowingAllFeeds) {
[self calculateFeedLocations:NO];
@ -445,7 +455,7 @@
if ([[appDelegate.dictFeeds allKeys] count] == 0 &&
[[appDelegate.dictSocialFeeds allKeys] count] == 0) {
self.hasNoSites = YES;
appDelegate.hasNoSites = YES;
}
[self.feedTitlesTable reloadData];
@ -567,7 +577,7 @@
- (void)switchSitesUnread {
NSDictionary *feed;
NSInteger intelligenceLevel = [appDelegate selectedIntelligence];
NSMutableArray *indexPaths = [NSMutableArray array];
@ -617,18 +627,25 @@
[self calculateFeedLocations:YES];
}
[self.feedTitlesTable beginUpdates];
if ([indexPaths count] > 0) {
if (self.viewShowingAllFeeds) {
[self.feedTitlesTable insertRowsAtIndexPaths:indexPaths
withRowAnimation:UITableViewRowAnimationNone];
} else {
[self.feedTitlesTable deleteRowsAtIndexPaths:indexPaths
withRowAnimation:UITableViewRowAnimationNone];
// @try {
[self.feedTitlesTable beginUpdates];
if ([indexPaths count] > 0) {
if (self.viewShowingAllFeeds) {
[self.feedTitlesTable insertRowsAtIndexPaths:indexPaths
withRowAnimation:UITableViewRowAnimationNone];
} else {
[self.feedTitlesTable deleteRowsAtIndexPaths:indexPaths
withRowAnimation:UITableViewRowAnimationNone];
}
}
}
[self.feedTitlesTable endUpdates];
[self.feedTitlesTable endUpdates];
// }
// @catch (NSException *exception) {
// NSLog(@"EXCEPTION: %@", exception);
// [self.feedTitlesTable beginUpdates];
// [self.feedTitlesTable endUpdates];
// [self.feedTitlesTable reloadData];
// }
CGPoint offset = CGPointMake(0, 0);
[self.feedTitlesTable setContentOffset:offset animated:YES];
@ -644,7 +661,7 @@
#pragma mark Table View - Feed List
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
if (self.hasNoSites) {
if (appDelegate.hasNoSites) {
return 2;
}
return [appDelegate.dictFoldersArray count];
@ -655,11 +672,12 @@
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (self.hasNoSites) {
if (appDelegate.hasNoSites) {
return 1;
}
NSString *folderName = [appDelegate.dictFoldersArray objectAtIndex:section];
return [[self.activeFeedLocations objectForKey:folderName] count];
}
@ -667,7 +685,7 @@
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// messaging when there are no sites
if (self.hasNoSites) {
if (appDelegate.hasNoSites) {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"EmptyCell"];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:nil];
@ -732,7 +750,7 @@
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.hasNoSites) {
if (appDelegate.hasNoSites) {
return;
}
@ -740,7 +758,14 @@
self.currentRowAtIndexPath = indexPath;
NSDictionary *feed;
NSString *folderName = [appDelegate.dictFoldersArray objectAtIndex:indexPath.section];
NSString *folderName;
if (indexPath.section == 0) {
folderName = @"river_blurblogs";
} else if (indexPath.section == 1) {
folderName = @"everything";
} else {
folderName = [appDelegate.dictFoldersArray objectAtIndex:indexPath.section];
}
NSArray *feeds = [appDelegate.dictFolders objectForKey:folderName];
NSArray *activeFolderFeeds = [self.activeFeedLocations objectForKey:folderName];
int location = [[activeFolderFeeds objectAtIndex:indexPath.row] intValue];
@ -772,7 +797,7 @@
- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath {
if (self.hasNoSites) {
if (appDelegate.hasNoSites) {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
return kBlurblogTableViewRowHeight;
} else {
@ -780,9 +805,21 @@
}
}
NSString *folderName = [appDelegate.dictFoldersArray objectAtIndex:indexPath.section];
NSString *folderName;
if (indexPath.section == 0) {
folderName = @"river_blurblogs";
} else {
folderName = [appDelegate.dictFoldersArray objectAtIndex:indexPath.section];
}
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
NSString *collapseKey = [NSString stringWithFormat:@"folderCollapsed:%@", folderName];
bool isFolderCollapsed = [userPreferences boolForKey:collapseKey];
if ([folderName isEqualToString:@""]) { // blurblogs
if (isFolderCollapsed) {
return 0;
}
if ([folderName isEqualToString:@"river_blurblogs"]) { // blurblogs
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
return kBlurblogTableViewRowHeight;
} else {
@ -800,113 +837,11 @@
- (UIView *)tableView:(UITableView *)tableView
viewForHeaderInSection:(NSInteger)section {
int headerLabelHeight, folderImageViewY, disclosureImageViewY;
// if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
headerLabelHeight = 27;
folderImageViewY = 3;
disclosureImageViewY = 7;
// } else {
// headerLabelHeight = 20;
// folderImageViewY = 0;
// disclosureImageViewY = 4;
// }
// create the parent view that will hold header Label
UIControl* customView = [[UIControl alloc]
initWithFrame:CGRectMake(0.0, 0.0,
tableView.bounds.size.width, headerLabelHeight + 1)];
UIView *borderTop = [[UIView alloc]
initWithFrame:CGRectMake(0.0, 0,
tableView.bounds.size.width, 1.0)];
borderTop.backgroundColor = UIColorFromRGB(0xe0e0e0);
borderTop.opaque = NO;
[customView addSubview:borderTop];
CGRect rect = CGRectMake(0.0, 0.0, tableView.bounds.size.width, kFolderTitleHeight);
UIView *folderTitle = [[FolderTitleView alloc] drawWithRect:rect inSection:section];
UIView *borderBottom = [[UIView alloc]
initWithFrame:CGRectMake(0.0, headerLabelHeight,
tableView.bounds.size.width, 1.0)];
borderBottom.backgroundColor = [UIColorFromRGB(0xB7BDC6) colorWithAlphaComponent:0.5];
borderBottom.opaque = NO;
[customView addSubview:borderBottom];
UILabel * headerLabel = [[UILabel alloc] initWithFrame:CGRectZero];
customView.opaque = NO;
headerLabel.backgroundColor = [UIColor clearColor];
headerLabel.opaque = NO;
headerLabel.textColor = [UIColor colorWithRed:0.3 green:0.3 blue:0.3 alpha:1.0];
headerLabel.highlightedTextColor = [UIColor whiteColor];
headerLabel.font = [UIFont boldSystemFontOfSize:11];
headerLabel.frame = CGRectMake(36.0, 1.0, 286.0, headerLabelHeight);
headerLabel.shadowColor = [UIColor colorWithRed:.94 green:0.94 blue:0.97 alpha:1.0];
headerLabel.shadowOffset = CGSizeMake(0.0, 1.0);
if (section == 0) {
headerLabel.text = @"ALL BLURBLOG STORIES";
// customView.backgroundColor = [UIColorFromRGB(0xD7DDE6)
// colorWithAlphaComponent:0.8];
} else if (section == 1) {
headerLabel.text = @"ALL STORIES";
// customView.backgroundColor = [UIColorFromRGB(0xE6DDD7)
// colorWithAlphaComponent:0.8];
} else {
headerLabel.text = [[appDelegate.dictFoldersArray objectAtIndex:section] uppercaseString];
// customView.backgroundColor = [UIColorFromRGB(0xD7DDE6)
// colorWithAlphaComponent:0.8];
}
customView.backgroundColor = [UIColorFromRGB(0xD7DDE6)
colorWithAlphaComponent:0.8];
[customView addSubview:headerLabel];
UIImage *folderImage;
int folderImageViewX = 10;
if (section == 0) {
folderImage = [UIImage imageNamed:@"group.png"];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
folderImageViewX = 10;
} else {
folderImageViewX = 8;
}
} else if (section == 1) {
folderImage = [UIImage imageNamed:@"archive.png"];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
folderImageViewX = 10;
} else {
folderImageViewX = 7;
}
} else {
folderImage = [UIImage imageNamed:@"folder_2.png"];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
} else {
folderImageViewX = 7;
}
}
UIImageView *folderImageView = [[UIImageView alloc] initWithImage:folderImage];
folderImageView.frame = CGRectMake(folderImageViewX, folderImageViewY, 20, 20);
[customView addSubview:folderImageView];
if (!self.hasNoSites) {
UIImage *disclosureImage = [UIImage imageNamed:@"disclosure.png"];
UIImageView *disclosureImageView = [[UIImageView alloc] initWithImage:disclosureImage];
disclosureImageView.frame = CGRectMake(customView.frame.size.width - 20, disclosureImageViewY, 9.0, 14.0);
[customView addSubview:disclosureImageView];
}
UIButton *invisibleHeaderButton = [UIButton buttonWithType:UIButtonTypeCustom];
invisibleHeaderButton.frame = CGRectMake(0, 0, customView.frame.size.width, customView.frame.size.height);
invisibleHeaderButton.alpha = .1;
invisibleHeaderButton.tag = section;
[invisibleHeaderButton addTarget:self action:@selector(didSelectSectionHeader:) forControlEvents:UIControlEventTouchUpInside];
[customView addSubview:invisibleHeaderButton];
[invisibleHeaderButton addTarget:self action:@selector(sectionTapped:) forControlEvents:UIControlEventTouchDown];
[invisibleHeaderButton addTarget:self action:@selector(sectionUntapped:) forControlEvents:UIControlEventTouchUpInside];
[invisibleHeaderButton addTarget:self action:@selector(sectionUntappedOutside:) forControlEvents:UIControlEventTouchUpOutside];
[customView setAutoresizingMask:UIViewAutoresizingNone];
return customView;
return folderTitle;
}
- (IBAction)sectionTapped:(UIButton *)button {
@ -950,9 +885,9 @@
appDelegate.isSocialRiverView = YES;
appDelegate.isRiverView = YES;
// add all the feeds from every NON blurblog folder
[appDelegate setActiveFolder:@"All Blurblog Stories"];
[appDelegate setActiveFolder:@"river_blurblogs"];
for (NSString *folderName in self.activeFeedLocations) {
if ([folderName isEqualToString:@""]) { // remove all blurblugs which is a blank folder name
if ([folderName isEqualToString:@"river_blurblogs"]) { // remove all blurblugs which is a blank folder name
NSArray *originalFolder = [appDelegate.dictFolders objectForKey:folderName];
NSArray *folderFeeds = [self.activeFeedLocations objectForKey:folderName];
for (int l=0; l < [folderFeeds count]; l++) {
@ -964,9 +899,9 @@
appDelegate.isSocialRiverView = NO;
appDelegate.isRiverView = YES;
// add all the feeds from every NON blurblog folder
[appDelegate setActiveFolder:@"All Stories"];
[appDelegate setActiveFolder:@"everything"];
for (NSString *folderName in self.activeFeedLocations) {
if (![folderName isEqualToString:@""]) { // remove all blurblugs which is a blank folder name
if (![folderName isEqualToString:@"river_blurblogs"]) {
NSArray *originalFolder = [appDelegate.dictFolders objectForKey:folderName];
NSArray *folderFeeds = [self.activeFeedLocations objectForKey:folderName];
for (int l=0; l < [folderFeeds count]; l++) {
@ -992,6 +927,35 @@
[appDelegate loadRiverFeedDetailView];
}
- (void)didCollapseFolder:(UIButton *)button {
NSString *folderName;
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
if (button.tag == 0) {
folderName = @"river_blurblogs";
} else {
folderName = [appDelegate.dictFoldersArray objectAtIndex:button.tag];
}
NSString *collapseKey = [NSString stringWithFormat:@"folderCollapsed:%@", folderName];
bool isFolderCollapsed = [userPreferences boolForKey:collapseKey];
if (isFolderCollapsed) {
// Expand folder
[userPreferences setBool:NO forKey:collapseKey];
} else {
// Collapse folder
[userPreferences setBool:YES forKey:collapseKey];
}
[userPreferences synchronize];
[self.feedTitlesTable reloadSections:[NSIndexSet indexSetWithIndex:button.tag]
withRowAnimation:UITableViewRowAnimationFade];
[self.feedTitlesTable beginUpdates];
[self.feedTitlesTable endUpdates];
}
- (void)changeToAllMode {
[self.intelligenceControl setSelectedSegmentIndex:0];
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
@ -1007,7 +971,7 @@
int selectedSegmentIndex = [self.intelligenceControl selectedSegmentIndex];
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
if (selectedSegmentIndex == 0) {
hud.labelText = @"All Stories";
[userPreferences setInteger:-1 forKey:@"selectedIntelligence"];
@ -1017,7 +981,7 @@
int previousLevel = appDelegate.selectedIntelligence;
[appDelegate setSelectedIntelligence:0];
[self updateFeedsWithIntelligence:previousLevel newLevel:0];
[self redrawUnreadCounts];
[self redrawUnreadCounts];
}
self.viewShowingAllFeeds = YES;
[self switchSitesUnread];
@ -1049,7 +1013,7 @@
[self redrawUnreadCounts];
}
[hud hide:YES afterDelay:0.75];
[hud hide:YES afterDelay:0.5];
// [self.feedTitlesTable reloadData];
}
@ -1147,16 +1111,22 @@
[self calculateFeedLocations:NO];
}
[self.feedTitlesTable beginUpdates];
if ([deleteIndexPaths count] > 0) {
[self.feedTitlesTable deleteRowsAtIndexPaths:deleteIndexPaths
withRowAnimation:UITableViewRowAnimationNone];
@try {
[self.feedTitlesTable beginUpdates];
if ([deleteIndexPaths count] > 0) {
[self.feedTitlesTable deleteRowsAtIndexPaths:deleteIndexPaths
withRowAnimation:UITableViewRowAnimationNone];
}
if ([insertIndexPaths count] > 0) {
[self.feedTitlesTable insertRowsAtIndexPaths:insertIndexPaths
withRowAnimation:UITableViewRowAnimationNone];
}
[self.feedTitlesTable endUpdates];
}
if ([insertIndexPaths count] > 0) {
[self.feedTitlesTable insertRowsAtIndexPaths:insertIndexPaths
withRowAnimation:UITableViewRowAnimationNone];
@catch (NSException *exception) {
NSLog(@"Exception: %@", exception);
[self.feedTitlesTable reloadData];
}
[self.feedTitlesTable endUpdates];
// scrolls to the top and fixes header rendering bug
CGPoint offsetOne = CGPointMake(0, 1);
@ -1186,7 +1156,7 @@
id feedId = [folder objectAtIndex:f];
NSString *feedIdStr = [NSString stringWithFormat:@"%@",feedId];
if ([folderName isEqualToString:@""]){
if ([folderName isEqualToString:@"river_blurblogs"]){
feed = [appDelegate.dictSocialFeeds objectForKey:feedIdStr];
} else {
feed = [appDelegate.dictFeeds objectForKey:feedIdStr];
@ -1201,7 +1171,7 @@
[feedLocations addObject:location];
} else {
int maxScore = [NewsBlurViewController computeMaxScoreForFeed:feed];
// if ([folderName isEqualToString:@""]){
// if ([folderName isEqualToString:@"river_blurblogs"]){
// NSLog(@"Computing score for %@: %d in %d (markVisible: %d)",
// [feed objectForKey:@"feed_title"], maxScore, appDelegate.selectedIntelligence, markVisible);
// }
@ -1216,10 +1186,6 @@
}
}
if ([folderName isEqualToString:@""]){
// NSLog(@"feedLocations count is %i: ", [feedLocations count]);
}
// NSLog(@"feedLocations %@", feedLocations);
[self.activeFeedLocations setObject:feedLocations forKey:folderName];
}

View file

@ -307,12 +307,10 @@
if ([self.followButton.currentTitle isEqualToString:@"Follow"]) {
urlString = [NSString stringWithFormat:@"http://%@/social/follow",
NEWSBLUR_URL,
[self.activeProfile objectForKey:@"user_id"]];
NEWSBLUR_URL];
} else {
urlString = [NSString stringWithFormat:@"http://%@/social/unfollow",
NEWSBLUR_URL,
[self.activeProfile objectForKey:@"user_id"]];
NEWSBLUR_URL];
}
NSURL *url = [NSURL URLWithString:urlString];

View file

@ -7,13 +7,15 @@
//
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import "WEPopoverController.h"
@class NewsBlurAppDelegate;
@class ASIHTTPRequest;
@interface StoryDetailViewController : UIViewController
<UIPopoverControllerDelegate, WEPopoverControllerDelegate> {
<UIPopoverControllerDelegate, WEPopoverControllerDelegate,
UIScrollViewDelegate> {
NewsBlurAppDelegate *appDelegate;
NSString *activeStoryId;
@ -29,7 +31,7 @@
UIToolbar *bottomPlaceholderToolbar;
UIBarButtonItem *buttonBack;
Class popoverClass;
BOOL pullingScrollview;
}
@property (nonatomic, strong) UIActivityIndicatorView *loadingIndicator;
@ -53,6 +55,7 @@
@property (nonatomic) IBOutlet UIBarButtonItem *originalStoryButton;
@property (nonatomic, strong) IBOutlet UIBarButtonItem *subscribeButton;
@property (nonatomic) IBOutlet UILabel *noStorySelectedLabel;
@property (nonatomic, assign) BOOL pullingScrollview;
- (void)setNextPreviousButtons;

View file

@ -52,6 +52,7 @@
@synthesize noStorySelectedLabel;
@synthesize buttonBack;
@synthesize bottomPlaceholderToolbar;
@synthesize pullingScrollview;
// private
@synthesize inTouchMove;
@ -799,36 +800,88 @@
feed = [appDelegate.dictFeeds objectForKey:feedIdStr];
}
self.feedTitleGradient = [appDelegate makeFeedTitleGradient:feed
self.feedTitleGradient = [appDelegate makeFeedTitleGradient:feed
withRect:CGRectMake(0, -1, 1024, 21)]; // 1024 hack for self.webView.frame.size.width
self.feedTitleGradient.tag = FEED_TITLE_GRADIENT_TAG; // Not attached yet. Remove old gradients, first.
[self.feedTitleGradient.layer setShadowColor:[[UIColor blackColor] CGColor]];
[self.feedTitleGradient.layer setShadowOffset:CGSizeMake(0, 0)];
[self.feedTitleGradient.layer setShadowOpacity:0];
[self.feedTitleGradient.layer setShadowRadius:12.0];
for (UIView *subview in self.webView.subviews) {
if (subview.tag == FEED_TITLE_GRADIENT_TAG) {
[subview removeFromSuperview];
}
}
for (NSObject *aSubView in [self.webView subviews]) {
if ([aSubView isKindOfClass:[UIScrollView class]]) {
UIScrollView * theScrollView = (UIScrollView *)aSubView;
if (appDelegate.isRiverView || appDelegate.isSocialView) {
theScrollView.contentInset = UIEdgeInsetsMake(19, 0, 0, 0);
theScrollView.scrollIndicatorInsets = UIEdgeInsetsMake(19, 0, 0, 0);
} else {
theScrollView.contentInset = UIEdgeInsetsMake(9, 0, 0, 0);
theScrollView.scrollIndicatorInsets = UIEdgeInsetsMake(9, 0, 0, 0);
}
[self.webView insertSubview:feedTitleGradient aboveSubview:theScrollView];
[theScrollView setContentOffset:CGPointMake(0, (appDelegate.isRiverView || appDelegate.isSocialView) ? -19 : -9) animated:NO];
break;
}
if (appDelegate.isRiverView || appDelegate.isSocialView) {
self.webView.scrollView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0);
self.webView.scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(20, 0, 0, 0);
} else {
self.webView.scrollView.contentInset = UIEdgeInsetsMake(9, 0, 0, 0);
self.webView.scrollView.scrollIndicatorInsets = UIEdgeInsetsMake(9, 0, 0, 0);
}
[self.webView insertSubview:feedTitleGradient aboveSubview:self.webView.scrollView];
[self.webView.scrollView setContentOffset:CGPointMake(0, (appDelegate.isRiverView ||
appDelegate.isSocialView) ? -20 : -9)
animated:NO];
[self.webView.scrollView addObserver:self forKeyPath:@"contentOffset"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:nil];
[self setNextPreviousButtons];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if (keyPath == @"contentOffset") {
if (self.webView.scrollView.contentOffset.y < -20) {
// Pulling
if (!pullingScrollview) {
pullingScrollview = YES;
[self.feedTitleGradient.layer setShadowOpacity:.5];
[self.webView insertSubview:self.feedTitleGradient belowSubview:self.webView.scrollView];
for (id subview in self.webView.scrollView.subviews) {
UIImageView *imgView = [subview isKindOfClass:[UIImageView class]] ?
(UIImageView*)subview : nil;
// image views whose image is 1px wide are shadow images, hide them
if (imgView && imgView.image.size.width == 1) {
imgView.hidden = YES;
}
}
}
float y = -1 * self.webView.scrollView.contentOffset.y - 20 - 1;
self.feedTitleGradient.frame = CGRectMake(0, y,
self.feedTitleGradient.frame.size.width,
self.feedTitleGradient.frame.size.height);
} else {
// Normal reading
if (pullingScrollview) {
pullingScrollview = NO;
[self.feedTitleGradient.layer setShadowOpacity:0];
[self.webView insertSubview:self.feedTitleGradient aboveSubview:self.webView.scrollView];
self.feedTitleGradient.frame = CGRectMake(0, -1,
self.feedTitleGradient.frame.size.width,
self.feedTitleGradient.frame.size.height);
for (id subview in self.webView.scrollView.subviews) {
UIImageView *imgView = [subview isKindOfClass:[UIImageView class]] ?
(UIImageView*)subview : nil;
// image views whose image is 1px wide are shadow images, hide them
if (imgView && imgView.image.size.width == 1) {
imgView.hidden = NO;
}
}
}
}
}
}
- (void)setActiveStory {
self.activeStoryId = [appDelegate.activeStory objectForKey:@"id"];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
@ -994,8 +1047,8 @@ shouldStartLoadWithRequest:(NSURLRequest *)request
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
// only adjust for the bar if user is scrolling
if (appDelegate.isRiverView || appDelegate.isSocialView) {
if (self.webView.scrollView.contentOffset.y == -19) {
y = y + 19;
if (self.webView.scrollView.contentOffset.y == -20) {
y = y + 20;
}
} else {
if (self.webView.scrollView.contentOffset.y == -9) {
@ -1231,11 +1284,11 @@ shouldStartLoadWithRequest:(NSURLRequest *)request
}
- (void)finishMarkAsRead:(ASIHTTPRequest *)request {
NSString *responseString = [request responseString];
NSDictionary *results = [[NSDictionary alloc]
initWithDictionary:[responseString JSONValue]];
NSLog(@"results in mark as read is %@", results);
}
// NSString *responseString = [request responseString];
// NSDictionary *results = [[NSDictionary alloc]
// initWithDictionary:[responseString JSONValue]];
// NSLog(@"results in mark as read is %@", results);
}
# pragma mark
# pragma mark Subscribing to blurblog

View file

@ -0,0 +1,32 @@
//
// UnreadCountView.h
// NewsBlur
//
// Created by Samuel Clay on 10/3/12.
// Copyright (c) 2012 NewsBlur. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "NewsBlurAppDelegate.h"
@class NewsBlurAppDelegate;
@interface UnreadCountView : UIView
typedef enum {
NBFeedListFeed = 1,
NBFeedListSocial = 2,
NBFeedListFolder = 3
} NBFeedListType;
@property (nonatomic) NewsBlurAppDelegate *appDelegate;
@property (assign, nonatomic) int psWidth;
@property (assign, nonatomic) int psPadding;
@property (assign, nonatomic) int ntWidth;
@property (assign, nonatomic) int ntPadding;
@property (assign, nonatomic) CGRect rect;
- (void)drawInRect:(CGRect)r ps:(int)ps nt:(int)nt listType:(NBFeedListType)listType;
- (int)offsetWidth;
@end

View file

@ -0,0 +1,127 @@
//
// UnreadCountView.m
// NewsBlur
//
// Created by Samuel Clay on 10/3/12.
// Copyright (c) 2012 NewsBlur. All rights reserved.
//
#import "UnreadCountView.h"
#import "UIView+TKCategory.h"
static UIFont *indicatorFont = nil;
static UIColor *indicatorWhiteColor = nil;
static UIColor *indicatorBlackColor = nil;
static UIColor *positiveBackgroundColor = nil;
static UIColor *neutralBackgroundColor = nil;
static UIColor *negativeBackgroundColor = nil;
@implementation UnreadCountView
@synthesize appDelegate;
@synthesize psWidth, psPadding, ntWidth, ntPadding;
@synthesize rect;
+ (void) initialize {
if (self == [UnreadCountView class]) {
indicatorFont = [UIFont boldSystemFontOfSize:12];
indicatorWhiteColor = [UIColor whiteColor];
indicatorBlackColor = [UIColor blackColor];
UIColor *ps = UIColorFromRGB(0x3B7613);
UIColor *nt = UIColorFromRGB(0xF9C72A);
UIColor *ng = UIColorFromRGB(0xCC2A2E);
positiveBackgroundColor = ps;
neutralBackgroundColor = nt;
negativeBackgroundColor = ng;
// UIColor *psGrad = UIColorFromRGB(0x559F4D);
// UIColor *ntGrad = UIColorFromRGB(0xE4AB00);
// UIColor *ngGrad = UIColorFromRGB(0x9B181B);
// const CGFloat* psTop = CGColorGetComponents(ps.CGColor);
// const CGFloat* psBot = CGColorGetComponents(psGrad.CGColor);
// CGFloat psGradient[] = {
// psTop[0], psTop[1], psTop[2], psTop[3],
// psBot[0], psBot[1], psBot[2], psBot[3]
// };
// psColors = psGradient;
}
}
- (void)drawInRect:(CGRect)r ps:(int)ps nt:(int)nt listType:(NBFeedListType)listType {
rect = CGRectInset(r, 12, 12);
rect.size.width -= 18; // Scrollbar padding
psWidth = ps == 0 ? 0 : ps < 10 ? 14 : ps < 100 ? 22 : 28;
ntWidth = nt == 0 ? 0 : nt < 10 ? 14 : nt < 100 ? 22 : 28;
int psOffset = ps == 0 ? 0 : psWidth - 20;
int ntOffset = nt == 0 ? 0 : ntWidth - 20;
psPadding = ps == 0 ? 0 : 2;
ntPadding = nt == 0 ? 0 : 2;
if (ps > 0){
[positiveBackgroundColor set];
CGRect rr;
if (listType == NBFeedListSocial) {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
rr = CGRectMake(rect.size.width + rect.origin.x - psOffset, 14, psWidth, 17);
} else {
rr = CGRectMake(rect.size.width + rect.origin.x - psOffset, 10, psWidth, 17);
}
} else {
rr = CGRectMake(rect.size.width + rect.origin.x - psOffset, 7, psWidth, 17);
}
[UIView drawRoundRectangleInRect:rr withRadius:4];
[indicatorWhiteColor set];
NSString *psStr = [NSString stringWithFormat:@"%d", ps];
CGSize size = [psStr sizeWithFont:indicatorFont];
float x_pos = (rr.size.width - size.width) / 2;
float y_pos = (rr.size.height - size.height) / 2;
[psStr
drawAtPoint:CGPointMake(rr.origin.x + x_pos, rr.origin.y + y_pos)
withFont:indicatorFont];
}
if (nt > 0 && appDelegate.selectedIntelligence <= 0){
[neutralBackgroundColor set];
CGRect rr;
if (listType == NBFeedListSocial) {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
rr = CGRectMake(rect.size.width + rect.origin.x - psWidth - psPadding - ntOffset, 14, ntWidth, 17);
} else {
rr = CGRectMake(rect.size.width + rect.origin.x - psWidth - psPadding - ntOffset, 10, ntWidth, 17);
}
} else {
rr = CGRectMake(rect.size.width + rect.origin.x - psWidth - psPadding - ntOffset, 7, ntWidth, 17);
}
[UIView drawRoundRectangleInRect:rr withRadius:4];
// [UIView drawLinearGradientInRect:rr colors:ntColors];
[indicatorBlackColor set];
NSString *ntStr = [NSString stringWithFormat:@"%d", nt];
CGSize size = [ntStr sizeWithFont:indicatorFont];
float x_pos = (rr.size.width - size.width) / 2;
float y_pos = (rr.size.height - size.height) / 2;
[ntStr
drawAtPoint:CGPointMake(rr.origin.x + x_pos, rr.origin.y + y_pos)
withFont:indicatorFont];
if (listType == NBFeedListFolder) {
NSLog(@"Drawing: %@", NSStringFromCGRect(r));
}
}
}
- (int)offsetWidth {
return rect.size.width - psWidth - psPadding - ntWidth - ntPadding;
}
@end

View file

@ -5,7 +5,7 @@
<key>application-identifier</key>
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
<key>get-task-allow</key>
<false/>
<true/>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>

View file

@ -102,7 +102,6 @@
43A4C3D715B00966008787B5 /* ABTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A4C3BA15B00966008787B5 /* ABTableViewCell.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A4C3D815B00966008787B5 /* Base64.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A4C3BC15B00966008787B5 /* Base64.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A4C3DA15B00966008787B5 /* GTMNString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A4C3C015B00966008787B5 /* GTMNString+HTML.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A4C3DB15B00966008787B5 /* libTestFlight.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 43A4C3C115B00966008787B5 /* libTestFlight.a */; };
43A4C3DC15B00966008787B5 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A4C3C215B00966008787B5 /* main.m */; };
43A4C3DD15B00966008787B5 /* MBProgressHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A4C3C415B00966008787B5 /* MBProgressHUD.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A4C3E115B00966008787B5 /* NSString+HTML.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A4C3CD15B00966008787B5 /* NSString+HTML.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
@ -367,6 +366,11 @@
FFAD4971144A386100BA6919 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FFAD4970144A386100BA6919 /* libz.dylib */; };
FFD1D7311459B63500E46F89 /* BaseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FFD1D7301459B63500E46F89 /* BaseViewController.m */; };
FFD887F01445F1E800385399 /* AddSiteAutocompleteCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FFD887EE1445F1E800385399 /* AddSiteAutocompleteCell.m */; };
FFDE35CC161B8F870034BFDE /* FolderTitleView.m in Sources */ = {isa = PBXBuildFile; fileRef = FFDE35CB161B8F870034BFDE /* FolderTitleView.m */; };
FFDE35D2161B9E600034BFDE /* disclosure_border.png in Resources */ = {isa = PBXBuildFile; fileRef = FFDE35D1161B9E600034BFDE /* disclosure_border.png */; };
FFDE35D6161CCABC0034BFDE /* folder_collapsed.png in Resources */ = {isa = PBXBuildFile; fileRef = FFDE35D4161CCABC0034BFDE /* folder_collapsed.png */; };
FFDE35D7161CCABC0034BFDE /* folder_collapsed@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFDE35D5161CCABC0034BFDE /* folder_collapsed@2x.png */; };
FFDE35DA161D12250034BFDE /* UnreadCountView.m in Sources */ = {isa = PBXBuildFile; fileRef = FFDE35D9161D12250034BFDE /* UnreadCountView.m */; };
FFE5322F144C8AC300ACFDE0 /* Utilities.m in Sources */ = {isa = PBXBuildFile; fileRef = FFE5322E144C8AC300ACFDE0 /* Utilities.m */; };
/* End PBXBuildFile section */
@ -498,7 +502,6 @@
43A4C3BE15B00966008787B5 /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMDefines.h; path = "Other Sources/GTMDefines.h"; sourceTree = "<group>"; };
43A4C3BF15B00966008787B5 /* GTMNString+HTML.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "GTMNString+HTML.h"; path = "Other Sources/GTMNString+HTML.h"; sourceTree = "<group>"; };
43A4C3C015B00966008787B5 /* GTMNString+HTML.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "GTMNString+HTML.m"; path = "Other Sources/GTMNString+HTML.m"; sourceTree = "<group>"; };
43A4C3C115B00966008787B5 /* libTestFlight.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libTestFlight.a; path = "Other Sources/libTestFlight.a"; sourceTree = "<group>"; };
43A4C3C215B00966008787B5 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = "Other Sources/main.m"; sourceTree = "<group>"; };
43A4C3C315B00966008787B5 /* MBProgressHUD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MBProgressHUD.h; path = "Other Sources/MBProgressHUD.h"; sourceTree = "<group>"; };
43A4C3C415B00966008787B5 /* MBProgressHUD.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = MBProgressHUD.m; path = "Other Sources/MBProgressHUD.m"; sourceTree = "<group>"; };
@ -882,6 +885,13 @@
FFD1D7301459B63500E46F89 /* BaseViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BaseViewController.m; sourceTree = "<group>"; };
FFD887ED1445F1E800385399 /* AddSiteAutocompleteCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddSiteAutocompleteCell.h; sourceTree = "<group>"; };
FFD887EE1445F1E800385399 /* AddSiteAutocompleteCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddSiteAutocompleteCell.m; sourceTree = "<group>"; };
FFDE35CA161B8F870034BFDE /* FolderTitleView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FolderTitleView.h; sourceTree = "<group>"; };
FFDE35CB161B8F870034BFDE /* FolderTitleView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FolderTitleView.m; sourceTree = "<group>"; };
FFDE35D1161B9E600034BFDE /* disclosure_border.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = disclosure_border.png; sourceTree = "<group>"; };
FFDE35D4161CCABC0034BFDE /* folder_collapsed.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = folder_collapsed.png; sourceTree = "<group>"; };
FFDE35D5161CCABC0034BFDE /* folder_collapsed@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "folder_collapsed@2x.png"; sourceTree = "<group>"; };
FFDE35D8161D12250034BFDE /* UnreadCountView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnreadCountView.h; sourceTree = "<group>"; };
FFDE35D9161D12250034BFDE /* UnreadCountView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UnreadCountView.m; sourceTree = "<group>"; };
FFE5322D144C8AC300ACFDE0 /* Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Utilities.h; sourceTree = "<group>"; };
FFE5322E144C8AC300ACFDE0 /* Utilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Utilities.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -903,7 +913,6 @@
78095E3F128EF35400230C8E /* CFNetwork.framework in Frameworks */,
78095E43128EF37E00230C8E /* MobileCoreServices.framework in Frameworks */,
78095E45128EF37E00230C8E /* SystemConfiguration.framework in Frameworks */,
43A4C3DB15B00966008787B5 /* libTestFlight.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -975,7 +984,6 @@
43A4C3BE15B00966008787B5 /* GTMDefines.h */,
43A4C3BF15B00966008787B5 /* GTMNString+HTML.h */,
43A4C3C015B00966008787B5 /* GTMNString+HTML.m */,
43A4C3C115B00966008787B5 /* libTestFlight.a */,
43A4C3C215B00966008787B5 /* main.m */,
43A4C3C315B00966008787B5 /* MBProgressHUD.h */,
43A4C3C415B00966008787B5 /* MBProgressHUD.m */,
@ -1119,6 +1127,10 @@
FFD887EE1445F1E800385399 /* AddSiteAutocompleteCell.m */,
FF5EA47C143B691000B7563D /* AddSiteViewController.h */,
FF5EA47D143B691000B7563D /* AddSiteViewController.m */,
FFDE35CA161B8F870034BFDE /* FolderTitleView.h */,
FFDE35CB161B8F870034BFDE /* FolderTitleView.m */,
FFDE35D8161D12250034BFDE /* UnreadCountView.h */,
FFDE35D9161D12250034BFDE /* UnreadCountView.m */,
);
name = Feeds;
sourceTree = "<group>";
@ -1139,6 +1151,9 @@
431B857615A132B600DCE497 /* Images */ = {
isa = PBXGroup;
children = (
FFDE35D4161CCABC0034BFDE /* folder_collapsed.png */,
FFDE35D5161CCABC0034BFDE /* folder_collapsed@2x.png */,
FFDE35D1161B9E600034BFDE /* disclosure_border.png */,
43BC458E15D9F75F00205B69 /* facebook_button_on.png */,
43BC458F15D9F75F00205B69 /* facebook_button_on@2x.png */,
43BC459015D9F75F00205B69 /* facebook_button.png */,
@ -2108,6 +2123,9 @@
43BC459515D9F76000205B69 /* facebook_button@2x.png in Resources */,
FF546DF71602930100948020 /* Default-568h@2x.png in Resources */,
FF546DF9160298E500948020 /* fleuron@2x.png in Resources */,
FFDE35D2161B9E600034BFDE /* disclosure_border.png in Resources */,
FFDE35D6161CCABC0034BFDE /* folder_collapsed.png in Resources */,
FFDE35D7161CCABC0034BFDE /* folder_collapsed@2x.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -2253,6 +2271,8 @@
438FEDE815D5B15F00E3B3C9 /* FollowGrid.m in Sources */,
43B232C015D5F61700D035B4 /* AuthorizeServicesViewController.m in Sources */,
43CE0F5F15DADB7F00608ED8 /* SiteCell.m in Sources */,
FFDE35CC161B8F870034BFDE /* FolderTitleView.m in Sources */,
FFDE35DA161D12250034BFDE /* UnreadCountView.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -2269,7 +2289,7 @@
);
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = Entitlements.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
CODE_SIGN_IDENTITY = "iPhone Developer: Samuel Clay (G9HFWP68T7)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
@ -2286,6 +2306,7 @@
"\"$(SRCROOT)\"",
"\"$(SRCROOT)/Other Sources\"",
);
OTHER_CPLUSPLUSFLAGS = "$(OTHER_CFLAGS)";
OTHER_LDFLAGS = (
"-all_load",
"-ObjC",
@ -2308,7 +2329,7 @@
);
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = Entitlements.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution: NewsBlur, Inc.";
CODE_SIGN_IDENTITY = "iPhone Developer: Samuel Clay (G9HFWP68T7)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: NewsBlur, Inc.";
COPY_PHASE_STRIP = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
@ -2328,7 +2349,7 @@
"-all_load",
);
PRODUCT_NAME = NewsBlur;
PROVISIONING_PROFILE = "3674B4BA-9006-4099-A608-673EC3103906";
PROVISIONING_PROFILE = "";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "3674B4BA-9006-4099-A608-673EC3103906";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;

View file

@ -2,6 +2,21 @@
<Bucket
type = "1"
version = "1.0">
<FileBreakpoints>
<FileBreakpoint
shouldBeEnabled = "No"
ignoreCount = "0"
continueAfterRunningActions = "No"
filePath = "Classes/NewsBlurViewController.m"
timestampString = "370996140.882983"
startingColumnNumber = "9223372036854775807"
endingColumnNumber = "9223372036854775807"
startingLineNumber = "594"
endingLineNumber = "594"
landmarkName = "-switchSitesUnread"
landmarkType = "5">
</FileBreakpoint>
</FileBreakpoints>
<SymbolicBreakpoints>
<SymbolicBreakpoint
shouldBeEnabled = "Yes"
@ -11,4 +26,13 @@
moduleName = "">
</SymbolicBreakpoint>
</SymbolicBreakpoints>
<ExceptionBreakpoints>
<ExceptionBreakpoint
shouldBeEnabled = "Yes"
ignoreCount = "0"
continueAfterRunningActions = "No"
scope = "0"
stopOnStyle = "0">
</ExceptionBreakpoint>
</ExceptionBreakpoints>
</Bucket>

View file

@ -1,30 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1296</int>
<string key="IBDocument.SystemVersion">11E53</string>
<string key="IBDocument.InterfaceBuilderVersion">2182</string>
<string key="IBDocument.AppKitVersion">1138.47</string>
<string key="IBDocument.HIToolboxVersion">569.00</string>
<int key="IBDocument.SystemTarget">1536</int>
<string key="IBDocument.SystemVersion">12C54</string>
<string key="IBDocument.InterfaceBuilderVersion">2840</string>
<string key="IBDocument.AppKitVersion">1187.34</string>
<string key="IBDocument.HIToolboxVersion">625.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">1181</string>
<string key="NS.object.0">1926</string>
</object>
<object class="NSArray" key="IBDocument.IntegratedClassDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>IBUIBarButtonItem</string>
<string>IBUISegmentedControl</string>
<string>IBUIPickerView</string>
<string>IBUILabel</string>
<string>IBUITextField</string>
<string>IBProxyObject</string>
<string>IBUIActivityIndicatorView</string>
<string>IBUIBarButtonItem</string>
<string>IBUIImageView</string>
<string>IBUILabel</string>
<string>IBUINavigationBar</string>
<string>IBUINavigationItem</string>
<string>IBUIPickerView</string>
<string>IBUIScrollView</string>
<string>IBUIView</string>
<string>IBUIImageView</string>
<string>IBUISegmentedControl</string>
<string>IBUITableView</string>
<string>IBUIActivityIndicatorView</string>
<string>IBUITextField</string>
<string>IBUIView</string>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
@ -51,7 +51,7 @@
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBUIImageView" id="567507966">
<reference key="NSNextResponder" ref="973185930"/>
<int key="NSvFlags">292</int>
<int key="NSvFlags">277</int>
<string key="NSFrame">{{0, 44}, {320, 416}}</string>
<reference key="NSSuperview" ref="973185930"/>
<reference key="NSWindow"/>
@ -70,7 +70,6 @@
<string key="NSFrame">{{111, 294}, {189, 52}}</string>
<reference key="NSSuperview" ref="973185930"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView"/>
<string key="NSReuseIdentifierKey">_NS:311</string>
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClipsSubviews">YES</bool>
@ -86,6 +85,7 @@
<object class="NSColor" key="IBUIShadowColor" id="444286937">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MCAwIDAAA</bytes>
<string key="IBUIColorCocoaTouchKeyPath">darkTextColor</string>
</object>
<int key="IBUIBaselineAdjustment">1</int>
<float key="IBUIMinimumFontSize">10</float>
@ -127,6 +127,7 @@
<int key="IBUITextAlignment">1</int>
<reference key="IBUIFontDescription" ref="992356106"/>
<reference key="IBUIFont" ref="24658222"/>
<double key="preferredMaxLayoutWidth">280</double>
</object>
<object class="IBUIActivityIndicatorView" id="900763914">
<reference key="NSNextResponder" ref="973185930"/>
@ -338,7 +339,7 @@
</object>
<object class="IBUIScrollView" id="39450128">
<reference key="NSNextResponder" ref="973185930"/>
<int key="NSvFlags">268</int>
<int key="NSvFlags">276</int>
<object class="NSMutableArray" key="NSSubviews">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBUITableView" id="919897576">
@ -351,7 +352,8 @@
<string key="NSReuseIdentifierKey">_NS:392</string>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MC42IDAuNiAwLjYgMC42AA</bytes>
<bytes key="NSRGB">MSAxIDEgMC42AA</bytes>
<string key="IBUIColorCocoaTouchKeyPath">lightTextColor</string>
</object>
<bool key="IBUIClipsSubviews">YES</bool>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
@ -5733,10 +5735,10 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>tapCategoryButton:</string>
<string>tapGoogleReaderButton</string>
<string>tapNewsBlurButton:</string>
<string>tapFacebookButton</string>
<string>tapNextButton</string>
<string>tapTwitterButton</string>
<string>toggleAutoFollowFriends:</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
@ -5750,124 +5752,96 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>tapCategoryButton:</string>
<string>tapGoogleReaderButton</string>
<string>tapNewsBlurButton:</string>
<string>tapFacebookButton</string>
<string>tapNextButton</string>
<string>tapTwitterButton</string>
<string>toggleAutoFollowFriends:</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBActionInfo">
<string key="name">tapCategoryButton:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">tapGoogleReaderButton</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">tapNewsBlurButton:</string>
<string key="name">tapFacebookButton</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">tapNextButton</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">tapTwitterButton</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">toggleAutoFollowFriends:</string>
<string key="candidateClassName">id</string>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>addFriendsView</string>
<string>addNewsBlurView</string>
<string>addSitesView</string>
<string>appDelegate</string>
<string>googleReaderButton</string>
<string>logo</string>
<string>facebookActivityIndicator</string>
<string>facebookButton</string>
<string>friendsLabel</string>
<string>nextButton</string>
<string>previousButton</string>
<string>toolbar</string>
<string>toolbarTitle</string>
<string>welcomeView</string>
<string>twitterActivityIndicator</string>
<string>twitterButton</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>UIView</string>
<string>UIView</string>
<string>UIView</string>
<string>NewsBlurAppDelegate</string>
<string>UIActivityIndicatorView</string>
<string>UIButton</string>
<string>UIImageView</string>
<string>UILabel</string>
<string>UIBarButtonItem</string>
<string>UIBarButtonItem</string>
<string>UIToolbar</string>
<string>UIActivityIndicatorView</string>
<string>UIButton</string>
<string>UIView</string>
</object>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>addFriendsView</string>
<string>addNewsBlurView</string>
<string>addSitesView</string>
<string>appDelegate</string>
<string>googleReaderButton</string>
<string>logo</string>
<string>facebookActivityIndicator</string>
<string>facebookButton</string>
<string>friendsLabel</string>
<string>nextButton</string>
<string>previousButton</string>
<string>toolbar</string>
<string>toolbarTitle</string>
<string>welcomeView</string>
<string>twitterActivityIndicator</string>
<string>twitterButton</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBToOneOutletInfo">
<string key="name">addFriendsView</string>
<string key="candidateClassName">UIView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">addNewsBlurView</string>
<string key="candidateClassName">UIView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">addSitesView</string>
<string key="candidateClassName">UIView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">appDelegate</string>
<string key="candidateClassName">NewsBlurAppDelegate</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">googleReaderButton</string>
<string key="name">facebookActivityIndicator</string>
<string key="candidateClassName">UIActivityIndicatorView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">facebookButton</string>
<string key="candidateClassName">UIButton</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">logo</string>
<string key="candidateClassName">UIImageView</string>
<string key="name">friendsLabel</string>
<string key="candidateClassName">UILabel</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">nextButton</string>
<string key="candidateClassName">UIBarButtonItem</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">previousButton</string>
<string key="candidateClassName">UIBarButtonItem</string>
<string key="name">twitterActivityIndicator</string>
<string key="candidateClassName">UIActivityIndicatorView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">toolbar</string>
<string key="candidateClassName">UIToolbar</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">toolbarTitle</string>
<string key="name">twitterButton</string>
<string key="candidateClassName">UIButton</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">welcomeView</string>
<string key="candidateClassName">UIView</string>
</object>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
@ -5882,38 +5856,27 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>tapCategoryButton:</string>
<string>tapGoogleReaderButton</string>
<string>tapNewsBlurButton:</string>
<string>tapNextButton</string>
<string>tapPopularButton:</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
</object>
</object>
<object class="NSMutableDictionary" key="actionInfosByName">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>tapCategoryButton:</string>
<string>tapGoogleReaderButton</string>
<string>tapNewsBlurButton:</string>
<string>tapNextButton</string>
<string>tapPopularButton:</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBActionInfo">
<string key="name">tapCategoryButton:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">tapGoogleReaderButton</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">tapNewsBlurButton:</string>
<string key="candidateClassName">id</string>
@ -5922,101 +5885,49 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<string key="name">tapNextButton</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">tapPopularButton:</string>
<string key="candidateClassName">id</string>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>addFriendsView</string>
<string>addNewsBlurView</string>
<string>addSitesView</string>
<string>appDelegate</string>
<string>googleReaderButton</string>
<string>logo</string>
<string>instructionsLabel</string>
<string>nextButton</string>
<string>previousButton</string>
<string>toolbar</string>
<string>toolbarTitle</string>
<string>welcomeView</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>UIView</string>
<string>UIView</string>
<string>UIView</string>
<string>NewsBlurAppDelegate</string>
<string>UIButton</string>
<string>UIImageView</string>
<string>UILabel</string>
<string>UIBarButtonItem</string>
<string>UIBarButtonItem</string>
<string>UIToolbar</string>
<string>UIButton</string>
<string>UIView</string>
</object>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>addFriendsView</string>
<string>addNewsBlurView</string>
<string>addSitesView</string>
<string>appDelegate</string>
<string>googleReaderButton</string>
<string>logo</string>
<string>instructionsLabel</string>
<string>nextButton</string>
<string>previousButton</string>
<string>toolbar</string>
<string>toolbarTitle</string>
<string>welcomeView</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBToOneOutletInfo">
<string key="name">addFriendsView</string>
<string key="candidateClassName">UIView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">addNewsBlurView</string>
<string key="candidateClassName">UIView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">addSitesView</string>
<string key="candidateClassName">UIView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">appDelegate</string>
<string key="candidateClassName">NewsBlurAppDelegate</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">googleReaderButton</string>
<string key="candidateClassName">UIButton</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">logo</string>
<string key="candidateClassName">UIImageView</string>
<string key="name">instructionsLabel</string>
<string key="candidateClassName">UILabel</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">nextButton</string>
<string key="candidateClassName">UIBarButtonItem</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">previousButton</string>
<string key="candidateClassName">UIBarButtonItem</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">toolbar</string>
<string key="candidateClassName">UIToolbar</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">toolbarTitle</string>
<string key="candidateClassName">UIButton</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">welcomeView</string>
<string key="candidateClassName">UIView</string>
</object>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
@ -6028,143 +5939,87 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<string key="className">FirstTimeUserAddSitesViewController</string>
<string key="superclassName">UIViewController</string>
<object class="NSMutableDictionary" key="actions">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>tapCategoryButton:</string>
<string>tapGoogleReaderButton</string>
<string>tapNewsBlurButton:</string>
<string>tapNextButton</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>id</string>
<string>id</string>
<string>id</string>
<string>id</string>
</object>
<string key="NS.key.0">tapNextButton</string>
<string key="NS.object.0">id</string>
</object>
<object class="NSMutableDictionary" key="actionInfosByName">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>tapCategoryButton:</string>
<string>tapGoogleReaderButton</string>
<string>tapNewsBlurButton:</string>
<string>tapNextButton</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBActionInfo">
<string key="name">tapCategoryButton:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">tapGoogleReaderButton</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">tapNewsBlurButton:</string>
<string key="candidateClassName">id</string>
</object>
<object class="IBActionInfo">
<string key="name">tapNextButton</string>
<string key="candidateClassName">id</string>
</object>
<string key="NS.key.0">tapNextButton</string>
<object class="IBActionInfo" key="NS.object.0">
<string key="name">tapNextButton</string>
<string key="candidateClassName">id</string>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>addFriendsView</string>
<string>addNewsBlurView</string>
<string>addSitesView</string>
<string>activityIndicator</string>
<string>appDelegate</string>
<string>categoriesTable</string>
<string>googleReaderButton</string>
<string>logo</string>
<string>googleReaderButtonWrapper</string>
<string>instructionLabel</string>
<string>nextButton</string>
<string>previousButton</string>
<string>toolbar</string>
<string>toolbarTitle</string>
<string>welcomeView</string>
<string>scrollView</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>UIView</string>
<string>UIView</string>
<string>UIView</string>
<string>UIActivityIndicatorView</string>
<string>NewsBlurAppDelegate</string>
<string>UIButton</string>
<string>UIImageView</string>
<string>UIBarButtonItem</string>
<string>UIBarButtonItem</string>
<string>UIToolbar</string>
<string>UITableView</string>
<string>UIButton</string>
<string>UIView</string>
<string>UILabel</string>
<string>UIBarButtonItem</string>
<string>UIScrollView</string>
</object>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>addFriendsView</string>
<string>addNewsBlurView</string>
<string>addSitesView</string>
<string>activityIndicator</string>
<string>appDelegate</string>
<string>categoriesTable</string>
<string>googleReaderButton</string>
<string>logo</string>
<string>googleReaderButtonWrapper</string>
<string>instructionLabel</string>
<string>nextButton</string>
<string>previousButton</string>
<string>toolbar</string>
<string>toolbarTitle</string>
<string>welcomeView</string>
<string>scrollView</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBToOneOutletInfo">
<string key="name">addFriendsView</string>
<string key="candidateClassName">UIView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">addNewsBlurView</string>
<string key="candidateClassName">UIView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">addSitesView</string>
<string key="candidateClassName">UIView</string>
<string key="name">activityIndicator</string>
<string key="candidateClassName">UIActivityIndicatorView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">appDelegate</string>
<string key="candidateClassName">NewsBlurAppDelegate</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">categoriesTable</string>
<string key="candidateClassName">UITableView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">googleReaderButton</string>
<string key="candidateClassName">UIButton</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">logo</string>
<string key="candidateClassName">UIImageView</string>
<string key="name">googleReaderButtonWrapper</string>
<string key="candidateClassName">UIView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">instructionLabel</string>
<string key="candidateClassName">UILabel</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">nextButton</string>
<string key="candidateClassName">UIBarButtonItem</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">previousButton</string>
<string key="candidateClassName">UIBarButtonItem</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">toolbar</string>
<string key="candidateClassName">UIToolbar</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">toolbarTitle</string>
<string key="candidateClassName">UIButton</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">welcomeView</string>
<string key="candidateClassName">UIView</string>
<string key="name">scrollView</string>
<string key="candidateClassName">UIScrollView</string>
</object>
</object>
</object>
@ -6192,12 +6047,16 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>appDelegate</string>
<string>footer</string>
<string>header</string>
<string>logo</string>
<string>nextButton</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NewsBlurAppDelegate</string>
<string>UILabel</string>
<string>UILabel</string>
<string>UIImageView</string>
<string>UIBarButtonItem</string>
</object>
@ -6207,6 +6066,8 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>appDelegate</string>
<string>footer</string>
<string>header</string>
<string>logo</string>
<string>nextButton</string>
</object>
@ -6216,6 +6077,14 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<string key="name">appDelegate</string>
<string key="candidateClassName">NewsBlurAppDelegate</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">footer</string>
<string key="candidateClassName">UILabel</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">header</string>
<string key="candidateClassName">UILabel</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">logo</string>
<string key="candidateClassName">UIImageView</string>
@ -6371,57 +6240,6 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<string key="minorKey">./Classes/FriendsListViewController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">GoogleReaderViewController</string>
<string key="superclassName">UIViewController</string>
<object class="NSMutableDictionary" key="actions">
<string key="NS.key.0">tapCancelButton:</string>
<string key="NS.object.0">id</string>
</object>
<object class="NSMutableDictionary" key="actionInfosByName">
<string key="NS.key.0">tapCancelButton:</string>
<object class="IBActionInfo" key="NS.object.0">
<string key="name">tapCancelButton:</string>
<string key="candidateClassName">id</string>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>appDelegate</string>
<string>webView</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NewsBlurAppDelegate</string>
<string>UIWebView</string>
</object>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>appDelegate</string>
<string>webView</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBToOneOutletInfo">
<string key="name">appDelegate</string>
<string key="candidateClassName">NewsBlurAppDelegate</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">webView</string>
<string key="candidateClassName">UIWebView</string>
</object>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/GoogleReaderViewController.h</string>
</object>
</object>
<object class="IBPartialClassDescription">
<string key="className">InteractionsModule</string>
<string key="superclassName">UIView</string>
@ -6817,7 +6635,6 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<string>fontSettingsViewController</string>
<string>friendsListViewController</string>
<string>ftuxNavigationController</string>
<string>googleReaderViewController</string>
<string>loginViewController</string>
<string>masterContainerViewController</string>
<string>moveSiteViewController</string>
@ -6844,7 +6661,6 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<string>FontSettingsViewController</string>
<string>FriendsListViewController</string>
<string>UINavigationController</string>
<string>GoogleReaderViewController</string>
<string>LoginViewController</string>
<string>NBContainerViewController</string>
<string>MoveSiteViewController</string>
@ -6874,7 +6690,6 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<string>fontSettingsViewController</string>
<string>friendsListViewController</string>
<string>ftuxNavigationController</string>
<string>googleReaderViewController</string>
<string>loginViewController</string>
<string>masterContainerViewController</string>
<string>moveSiteViewController</string>
@ -6943,10 +6758,6 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<string key="name">ftuxNavigationController</string>
<string key="candidateClassName">UINavigationController</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">googleReaderViewController</string>
<string key="candidateClassName">GoogleReaderViewController</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">loginViewController</string>
<string key="candidateClassName">LoginViewController</string>
@ -7057,6 +6868,8 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<string>homeButton</string>
<string>innerView</string>
<string>intelligenceControl</string>
<string>noFocusMessage</string>
<string>toolbarLeftMargin</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
@ -7067,6 +6880,8 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<string>UIBarButtonItem</string>
<string>UIView</string>
<string>UISegmentedControl</string>
<string>UIView</string>
<string>UIBarButtonItem</string>
</object>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
@ -7080,6 +6895,8 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<string>homeButton</string>
<string>innerView</string>
<string>intelligenceControl</string>
<string>noFocusMessage</string>
<string>toolbarLeftMargin</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
@ -7111,6 +6928,14 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<string key="name">intelligenceControl</string>
<string key="candidateClassName">UISegmentedControl</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">noFocusMessage</string>
<string key="candidateClassName">UIView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">toolbarLeftMargin</string>
<string key="candidateClassName">UIBarButtonItem</string>
</object>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
@ -7579,7 +7404,7 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaTouchFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
<real value="1296" key="NS.object.0"/>
<real value="1536" key="NS.object.0"/>
</object>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3</string>
@ -7591,6 +7416,6 @@ AAgAAAAIAAIACAACAAAAAgAAAAEAAQABAAE</bytes>
<string key="NS.key.0">Background.png</string>
<string key="NS.object.0">{320, 480}</string>
</object>
<string key="IBCocoaTouchPluginVersion">1181</string>
<string key="IBCocoaTouchPluginVersion">1926</string>
</data>
</archive>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -2,10 +2,10 @@
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1536</int>
<string key="IBDocument.SystemVersion">12B19</string>
<string key="IBDocument.SystemVersion">12C54</string>
<string key="IBDocument.InterfaceBuilderVersion">2840</string>
<string key="IBDocument.AppKitVersion">1187</string>
<string key="IBDocument.HIToolboxVersion">624.00</string>
<string key="IBDocument.AppKitVersion">1187.34</string>
<string key="IBDocument.HIToolboxVersion">625.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">1926</string>
@ -51,10 +51,6 @@
<int key="IBUIInterfaceOrientation">1</int>
<int key="interfaceOrientation">1</int>
</object>
<object class="IBUISimulatedSizeMetrics" key="IBUISimulatedDestinationMetrics" id="327432476">
<string key="IBUISimulatedSizeMetricsClass">IBUISimulatedFreeformSizeMetricsSentinel</string>
<string key="IBUIDisplayName">Freeform</string>
</object>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBUIHorizontal">NO</bool>
</object>
@ -214,7 +210,7 @@
<object class="IBUIWindow" id="117978783">
<reference key="NSNextResponder"/>
<int key="NSvFlags">256</int>
<string key="NSFrameSize">{320, 568}</string>
<string key="NSFrameSize">{320, 480}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
<object class="NSColor" key="IBUIBackgroundColor">
@ -224,8 +220,8 @@
<bool key="IBUIOpaque">NO</bool>
<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
<reference key="IBUISimulatedDestinationMetrics" ref="327432476"/>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
<bool key="IBUIResizesToFullScreen">YES</bool>
</object>
<object class="IBUINavigationController" id="1024563784">
<object class="IBUISimulatedStatusBarMetrics" key="IBUISimulatedStatusBarMetrics"/>
@ -248,7 +244,6 @@
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBUIViewController" id="121528350">
<object class="IBUINavigationItem" key="IBUINavigationItem" id="451803536">
<reference key="IBUINavigationBar"/>
<string key="IBUITitle">Title</string>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 320 B

After

Width:  |  Height:  |  Size: 832 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View file

@ -34,25 +34,30 @@ NEWSBLUR.Router = Backbone.Router.extend({
site_id = parseInt(site_id, 10);
var feed = NEWSBLUR.assets.get_feed(site_id);
if (feed) {
NEWSBLUR.reader.open_feed(site_id, {force: true});
NEWSBLUR.reader.open_feed(site_id, {router: true, force: true});
} else {
NEWSBLUR.reader.load_feed_in_tryfeed_view(site_id, {force: true, feed: {
feed_title: _.string.humanize(slug || "")
}});
NEWSBLUR.reader.load_feed_in_tryfeed_view(site_id, {
router: true,
force: true,
feed: {
feed_title: _.string.humanize(slug || "")
}
});
}
},
folder: function(folder_name) {
folder_name = folder_name.replace(/-/g, ' ');
// NEWSBLUR.log(["folder", folder_name]);
var options = {router: true};
if (folder_name == "everything") {
NEWSBLUR.reader.open_river_stories();
NEWSBLUR.reader.open_river_stories(null, null, options);
} else if (folder_name == "blurblogs") {
NEWSBLUR.reader.open_river_blurblogs_stories();
NEWSBLUR.reader.open_river_blurblogs_stories(options);
} else {
var folder = NEWSBLUR.assets.get_folder(folder_name);
if (folder) {
NEWSBLUR.reader.open_river_stories(folder.folder_view.$el, folder);
NEWSBLUR.reader.open_river_stories(folder.folder_view.$el, folder, options);
}
}
},
@ -61,14 +66,18 @@ NEWSBLUR.Router = Backbone.Router.extend({
// NEWSBLUR.log(["router:social", user_id, slug]);
var feed_id = "social:" + user_id;
if (NEWSBLUR.assets.get_feed(feed_id)) {
NEWSBLUR.reader.open_social_stories(feed_id, {force: true});
NEWSBLUR.reader.open_social_stories(feed_id, {router: true, force: true});
} else {
NEWSBLUR.reader.load_social_feed_in_tryfeed_view(feed_id, {force: true, feed: {
username: _.string.humanize(slug),
id: feed_id,
user_id: parseInt(user_id, 10),
feed_title: _.string.humanize(slug)
}});
NEWSBLUR.reader.load_social_feed_in_tryfeed_view(feed_id, {
router: true,
force: true,
feed: {
username: _.string.humanize(slug),
id: feed_id,
user_id: parseInt(user_id, 10),
feed_title: _.string.humanize(slug)
}
});
}
},

View file

@ -5,9 +5,14 @@ NEWSBLUR.Models.SocialSubscription = Backbone.Model.extend({
this.set('page_url', '/social/page/' + this.get('user_id'));
}
_.bindAll(this, 'on_change', 'on_remove');
_.bindAll(this, 'on_change', 'on_remove', 'update_counts');
// this.bind('change', this.on_change);
this.bind('remove', this.on_remove);
this.bind('change:ps', this.update_counts);
this.bind('change:nt', this.update_counts);
this.bind('change:ng', this.update_counts);
this.views = [];
},
@ -19,6 +24,10 @@ NEWSBLUR.Models.SocialSubscription = Backbone.Model.extend({
NEWSBLUR.log(["Remove Feed", this, this.views]);
_.each(this.views, function(view) { view.remove(); });
},
update_counts: function() {
NEWSBLUR.assets.social_feeds.trigger('change:counts');
},
is_social: function() {
return true;
@ -79,6 +88,22 @@ NEWSBLUR.Collections.SocialSubscriptions = Backbone.Collection.extend({
}).each(function(feed){
feed.set('selected', false);
});
},
unread_counts: function() {
var counts = this.reduce(function(counts, item) {
var feed_counts = item.unread_counts();
counts['ps'] += feed_counts['ps'];
counts['nt'] += feed_counts['nt'];
counts['ng'] += feed_counts['ng'];
return counts;
}, {
ps: 0,
nt: 0,
ng: 0
});
return counts;
}
});

View file

@ -32,10 +32,8 @@ NEWSBLUR.Models.Story = Backbone.Model.extend({
},
has_modifications: function() {
if (this.get('story_content').indexOf('<ins') != -1) {
return true;
} else if (NEWSBLUR.assets.preference('hide_story_changes') &&
this.get('story_content').indexOf('<del') != -1) {
if (this.get('story_content').indexOf('<ins') != -1 ||
this.get('story_content').indexOf('<del') != -1) {
return true;
}
return false;
@ -72,9 +70,9 @@ NEWSBLUR.Collections.Stories = Backbone.Collection.extend({
// = Actions =
// ===========
deselect: function(selected_story) {
deselect_other_stories: function(selected_story) {
this.any(function(story) {
if (story.get('selected') && story != selected_story) {
if (story.get('selected') && story.id != selected_story.id) {
story.set('selected', false);
return true;
}
@ -140,27 +138,27 @@ NEWSBLUR.Collections.Stories = Backbone.Collection.extend({
if (story.score() > 0) {
var active_count = Math.max(active_feed.get('ps') + (options.unread?1:-1), 0);
var story_count = Math.max(story_feed.get('ps') + (options.unread?1:-1), 0);
var story_count = story_feed && Math.max(story_feed.get('ps') + (options.unread?1:-1), 0);
active_feed.set('ps', active_count, {instant: true});
story_feed.set('ps', story_count, {instant: true});
if (story_feed) story_feed.set('ps', story_count, {instant: true});
_.each(friend_feeds, function(socialsub) {
var socialsub_count = Math.max(socialsub.get('ps') + (options.unread?1:-1), 0);
socialsub.set('ps', socialsub_count, {instant: true});
});
} else if (story.score() == 0) {
var active_count = Math.max(active_feed.get('nt') + (options.unread?1:-1), 0);
var story_count = Math.max(story_feed.get('nt') + (options.unread?1:-1), 0);
var story_count = story_feed && Math.max(story_feed.get('nt') + (options.unread?1:-1), 0);
active_feed.set('nt', active_count, {instant: true});
story_feed.set('nt', story_count, {instant: true});
if (story_feed) story_feed.set('nt', story_count, {instant: true});
_.each(friend_feeds, function(socialsub) {
var socialsub_count = Math.max(socialsub.get('nt') + (options.unread?1:-1), 0);
socialsub.set('nt', socialsub_count, {instant: true});
});
} else if (story.score() < 0) {
var active_count = Math.max(active_feed.get('ng') + (options.unread?1:-1), 0);
var story_count = Math.max(story_feed.get('ng') + (options.unread?1:-1), 0);
var story_count = story_feed && Math.max(story_feed.get('ng') + (options.unread?1:-1), 0);
active_feed.set('ng', active_count, {instant: true});
story_feed.set('ng', story_count, {instant: true});
if (story_feed) story_feed.set('ng', story_count, {instant: true});
_.each(friend_feeds, function(socialsub) {
var socialsub_count = Math.max(socialsub.get('ng') + (options.unread?1:-1), 0);
socialsub.set('ng', socialsub_count, {instant: true});
@ -304,7 +302,7 @@ NEWSBLUR.Collections.Stories = Backbone.Collection.extend({
detect_selected_story: function(selected_story, selected) {
if (selected) {
this.deselect(selected_story);
this.deselect_other_stories(selected_story);
this.active_story = selected_story;
NEWSBLUR.reader.active_story = selected_story;
this.previous_stories_stack.push(selected_story);

View file

@ -2,7 +2,9 @@
NEWSBLUR.Reader = Backbone.Router.extend({
init: function() {
init: function(options) {
var defaults = {};
// ===========
// = Globals =
@ -11,11 +13,14 @@
NEWSBLUR.assets = new NEWSBLUR.AssetModel();
this.model = NEWSBLUR.assets;
this.story_view = 'page';
this.options = _.extend({}, defaults, options);
this.$s = {
$body: $('body'),
$layout: $('.NB-layout'),
$sidebar: $('.NB-sidebar'),
$feed_lists: $('.NB-feedlists'),
$feed_list: $('#feed_list'),
$social_feeds: $('.NB-socialfeeds'),
$social_feeds: $('.NB-socialfeeds-folder'),
$story_titles: $('#story_titles'),
$content_pane: $('.content-pane'),
$story_taskbar: $('#story_taskbar'),
@ -41,7 +46,8 @@
'bouncing_callout': false,
'has_unfetched_feeds': false,
'count_unreads_after_import_working': false,
'import_from_google_reader_working': false
'import_from_google_reader_working': false,
'sidebar_closed': this.options.hide_sidebar
};
this.locks = {};
this.counts = {
@ -99,12 +105,14 @@
if (NEWSBLUR.Flags['start_import_from_google_reader']) {
this.start_import_from_google_reader();
}
NEWSBLUR.app.feed_list_header = new NEWSBLUR.Views.FeedListHeader({collection: NEWSBLUR.assets.feeds});
NEWSBLUR.app.sidebar_header = new NEWSBLUR.Views.SidebarHeader({collection: NEWSBLUR.assets.feeds});
NEWSBLUR.app.sidebar = new NEWSBLUR.Views.Sidebar();
NEWSBLUR.app.feed_list = new NEWSBLUR.Views.FeedList({el: this.$s.$feed_list[0]});
NEWSBLUR.app.story_titles = new NEWSBLUR.Views.StoryTitlesView({collection: NEWSBLUR.assets.stories});
NEWSBLUR.app.story_list = new NEWSBLUR.Views.StoryListView({collection: NEWSBLUR.assets.stories});
NEWSBLUR.app.original_tab_view = new NEWSBLUR.Views.OriginalTabView({collection: NEWSBLUR.assets.stories});
NEWSBLUR.app.story_tab_view = new NEWSBLUR.Views.StoryTabView({collection: NEWSBLUR.assets.stories});
NEWSBLUR.app.feed_selector = new NEWSBLUR.Views.FeedSelector();
this.load_intelligence_slider();
this.handle_mouse_indicator_hover();
@ -197,17 +205,17 @@
.removeClass('NB-story-pane-south')
.addClass('NB-story-pane-'+story_anchor);
this.layout.outerLayout = this.$s.$body.layout({
closable: true,
zIndex: 1,
fxName: "slide",
this.layout.outerLayout = this.$s.$layout.layout({
zIndex: 2,
fxName: "slideOffscreen",
fxSettings: { duration: 560, easing: "easeInOutQuint" },
center__paneSelector: ".right-pane",
west__paneSelector: ".left-pane",
west__size: this.model.preference('feed_pane_size'),
west__minSize: this.constants.MIN_FEED_LIST_SIZE,
west__onresize_end: $.rescope(this.save_feed_pane_size, this),
spacing_open: 4,
// west__initHidden: this.options.hide_sidebar,
west__spacing_open: this.options.hide_sidebar ? 1 : 6,
resizerDragOpacity: 0.6,
resizeWhileDragging: true,
enableCursorHotkey: false
@ -220,8 +228,9 @@
this.layout.leftLayout = $('.left-pane').layout({
closable: false,
resizeWhileDragging: true,
fxName: "slide",
fxName: "slideOffscreen",
fxSettings: { duration: 560, easing: "easeInOutQuint" },
animatePaneSizing: true,
north__paneSelector: ".left-north",
north__size: 18,
north__resizeable: false,
@ -249,7 +258,7 @@
south__spacing_closed: 0,
south__closable: true,
south__initClosed: true,
fxName: "slide",
fxName: "slideOffscreen",
fxSettings: { duration: 560, easing: "easeInOutQuint" },
enableCursorHotkey: false
});
@ -260,7 +269,7 @@
spacing_open: story_anchor == 'west' ? 4 : 10,
resizerDragOpacity: 0.6,
enableCursorHotkey: false,
fxName: "slide",
fxName: "slideOffscreen",
fxSettings: { duration: 560, easing: "easeInOutQuint" }
};
rightLayoutOptions[story_anchor+'__paneSelector'] = '.right-north';
@ -385,13 +394,13 @@
resize = true;
}
$('.right-pane').show();
$('#NB-splash').hide();
$('#NB-splash,.NB-splash').hide();
$('.NB-splash-info').hide();
$('#NB-splash-overlay').hide();
this.$s.$dashboard.addClass('NB-active');
if (resize) {
this.$s.$body.layout().resizeAll();
this.$s.$layout.layout().resizeAll();
}
if (NEWSBLUR.Globals.is_anonymous) {
this.setup_ftux_signup_callout();
@ -400,11 +409,9 @@
show_splash_page: function(skip_router) {
this.reset_feed();
NEWSBLUR.app.original_tab_view.unload_feed_iframe();
NEWSBLUR.app.story_tab_view.unload_story_iframe();
$('.right-pane').hide();
$('.NB-splash-info').show();
$('#NB-splash').show();
$('#NB-splash,.NB-splash').show();
$('#NB-splash-overlay').show();
this.$s.$dashboard.removeClass('NB-active');
if (!skip_router) {
@ -489,7 +496,6 @@
if (unread_count) {
var next_story = NEWSBLUR.assets.stories.get_next_unread_story();
if (next_story) {
this.counts['find_next_unread_on_page_of_feed_stories_load'] = 0;
next_story.set('selected', true);
@ -584,7 +590,7 @@
return this.show_next_folder(direction, $current_feed);
}
var $next_feed = this.get_next_feed(direction, $current_feed);
var $next_feed = this.get_next_feed(direction, $current_feed, {include_selected: true});
var next_feed_id = $next_feed.data('id');
if (next_feed_id && next_feed_id == this.active_feed) {
@ -604,15 +610,19 @@
this.open_river_stories($next_folder, folder && folder.model);
},
get_next_feed: function(direction, $current_feed) {
get_next_feed: function(direction, $current_feed, options) {
options = options || {};
var self = this;
var $feed_list = this.$s.$feed_list.add(this.$s.$social_feeds);
var $current_feed = $current_feed || $('.selected', $feed_list);
var $next_feed,
scroll;
var $feeds = $('.feed:visible:not(.NB-empty)', $feed_list).add('.NB-feedlists .feed.NB-selected');
var $feeds = $('.feed:visible:not(.NB-empty)', $feed_list);
if (!$current_feed.length) {
$current_feed = $('.feed:visible:not(.NB-empty)', $feed_list)[direction==1?'first':'last']();
if (options.include_selected) {
$feeds = $feeds.add('.NB-feedlists .feed.NB-selected');
}
$current_feed = $('.feed:visible:not(.NB-empty)', $feed_list)[direction==-1?'last':'first']();
$next_feed = $current_feed;
} else {
var current_feed = 0;
@ -746,9 +756,7 @@
find_story_with_action_preference_on_open_feed: function() {
var open_feed_action = this.model.preference('open_feed_action');
if (this.counts['page'] != 1) return;
if (open_feed_action == 'newest') {
if (!this.active_story && open_feed_action == 'newest') {
this.show_next_unread_story();
}
},
@ -965,7 +973,9 @@
// = Feed bar - Individual Feeds =
// ===============================
reset_feed: function() {
reset_feed: function(options) {
options = options || {};
$.extend(this.flags, {
'scrolling_by_selecting_story_title': false,
'page_view_showing_feed_view': false,
@ -1025,12 +1035,22 @@
if (this.flags['showing_feed_in_tryfeed_view'] || this.flags['showing_social_feed_in_tryfeed_view']) {
this.hide_tryfeed_view();
}
if (NEWSBLUR.Globals.is_anonymous) {
if (options.router) {
this.$s.$layout.layout().show('west', true);
this.$s.$layout.show();
}
this.hide_tryout_signup_button();
}
this.active_folder = null;
this.active_feed = null;
this.active_story = null;
NEWSBLUR.assets.stories.reset();
NEWSBLUR.app.feed_selector.hide_feed_selector();
NEWSBLUR.app.original_tab_view.unload_feed_iframe();
NEWSBLUR.app.story_tab_view.unload_story_iframe();
},
open_feed: function(feed_id, options) {
@ -1049,7 +1069,7 @@
this.flags['opening_feed'] = true;
if (options.try_feed || feed) {
this.reset_feed();
this.reset_feed(options);
this.hide_splash_page();
if (options.story_id) {
this.flags['select_story_in_feed'] = options.story_id;
@ -1086,6 +1106,7 @@
}, this), options.delay || 0);
} else {
NEWSBLUR.app.original_tab_view.unload_feed_iframe();
NEWSBLUR.app.story_tab_view.unload_story_iframe();
this.flags['iframe_prevented_from_loading'] = true;
}
this.setup_mousemove_on_views();
@ -1115,9 +1136,7 @@
this.active_feed = data.feed_id;
}
// NEWSBLUR.log(['post_open_feed', data.stories, this.flags]);
this.flags['opening_feed'] = false;
this.find_story_with_action_preference_on_open_feed();
NEWSBLUR.app.story_titles_header.show_feed_hidden_story_title_indicator(true);
this.show_story_titles_above_intelligence_level({'animate': false});
if (this.counts['find_next_unread_on_page_of_feed_stories_load']) {
@ -1129,15 +1148,20 @@
}
this.flags['story_titles_loaded'] = true;
if (first_load) {
this.make_story_titles_pane_counter();
this.find_story_with_action_preference_on_open_feed();
if (this.story_view == 'story' &&
!this.active_story &&
!this.counts['find_next_unread_on_page_of_feed_stories_load']) {
this.show_next_story(1);
}
this.make_story_titles_pane_counter();
}
this.hide_stories_progress_bar();
if (this.flags['showing_feed_in_tryfeed_view']) {
if (NEWSBLUR.Globals.is_anonymous) {
this.show_tryout_signup_button();
} else if (this.flags['showing_feed_in_tryfeed_view']) {
this.show_tryfeed_add_button();
this.correct_tryfeed_title();
}
@ -1258,10 +1282,12 @@
if (this.active_feed == 'starred') {
// NEWSBLUR.log(['post_open_starred_stories', data.stories.length, first_load]);
this.flags['opening_feed'] = false;
this.find_story_with_action_preference_on_open_feed();
if (this.counts['select_story_in_feed'] || this.flags['select_story_in_feed']) {
this.select_story_in_feed();
}
if (first_load) {
this.find_story_with_action_preference_on_open_feed();
}
this.show_story_titles_above_intelligence_level({'animate': false});
this.flags['story_titles_loaded'] = true;
}
@ -1279,7 +1305,7 @@
this.active_folder && this.active_folder.folder_view;
var folder_title = folder && folder.get('folder_title') || "Everything";
this.reset_feed();
this.reset_feed(options);
this.hide_splash_page();
this.active_folder = folder || new Backbone.Model({
folder_title: folder_title,
@ -1341,7 +1367,6 @@
}
this.flags['opening_feed'] = false;
NEWSBLUR.app.story_titles_header.show_feed_hidden_story_title_indicator(true);
this.find_story_with_action_preference_on_open_feed();
this.show_story_titles_above_intelligence_level({'animate': false});
this.flags['story_titles_loaded'] = true;
if (this.counts['find_next_unread_on_page_of_feed_stories_load']) {
@ -1351,8 +1376,13 @@
} else if (this.counts['select_story_in_feed'] || this.flags['select_story_in_feed']) {
this.select_story_in_feed();
}
this.hide_stories_progress_bar();
if (first_load) {
this.find_story_with_action_preference_on_open_feed();
}
this.hide_stories_progress_bar();
if (NEWSBLUR.Globals.is_anonymous) {
this.show_tryout_signup_button();
} else if (first_load) {
this.make_story_titles_pane_counter();
}
}
@ -1396,7 +1426,7 @@
var $story_titles = this.$s.$story_titles;
var folder_title = "Blurblogs";
this.reset_feed();
this.reset_feed(options);
this.hide_splash_page();
this.active_folder = new Backbone.Model({
@ -1454,7 +1484,6 @@
}
this.flags['opening_feed'] = false;
NEWSBLUR.app.story_titles_header.show_feed_hidden_story_title_indicator(true);
this.find_story_with_action_preference_on_open_feed();
this.show_story_titles_above_intelligence_level({'animate': false});
this.flags['story_titles_loaded'] = true;
if (this.counts['find_next_unread_on_page_of_feed_stories_load']) {
@ -1464,7 +1493,13 @@
} else if (this.counts['select_story_in_feed'] || this.flags['select_story_in_feed']) {
this.select_story_in_feed();
}
if (first_load) {
this.find_story_with_action_preference_on_open_feed();
}
this.hide_stories_progress_bar();
if (NEWSBLUR.Globals.is_anonymous) {
this.show_tryout_signup_button();
}
}
},
@ -1490,7 +1525,7 @@
return this.load_social_feed_in_tryfeed_view(socialsub, options);
}
this.reset_feed();
this.reset_feed(options);
this.hide_splash_page();
this.active_feed = feed.id;
@ -1553,7 +1588,6 @@
if (this.active_feed && NEWSBLUR.utils.is_feed_social(this.active_feed)) {
this.flags['opening_feed'] = false;
this.find_story_with_action_preference_on_open_feed();
this.show_story_titles_above_intelligence_level({'animate': false});
NEWSBLUR.app.story_titles_header.show_feed_hidden_story_title_indicator(true);
this.flags['story_titles_loaded'] = true;
@ -1564,9 +1598,13 @@
} else if (this.counts['find_last_unread_on_page_of_feed_stories_load']) {
this.show_last_unread_story(true);
}
if (first_load) {
this.find_story_with_action_preference_on_open_feed();
}
this.hide_stories_progress_bar();
if (this.flags['showing_social_feed_in_tryfeed_view']) {
if (NEWSBLUR.Globals.is_anonymous) {
this.show_tryout_signup_button();
} else if (this.flags['showing_social_feed_in_tryfeed_view']) {
this.show_tryfeed_follow_button();
this.correct_tryfeed_title();
}
@ -1711,6 +1749,17 @@
}
},
mark_active_story_read: function() {
if (!this.active_story) return;
var story_id = this.active_story.id;
var story = this.model.get_story(story_id);
if (this.active_story && !this.active_story.get('read_status')) {
NEWSBLUR.assets.stories.mark_read(story, {skip_delay: true});
} else if (this.active_story && this.active_story.get('read_status')) {
NEWSBLUR.assets.stories.mark_unread(story);
}
},
mark_feed_as_read: function(feed_id) {
feed_id = feed_id || this.active_feed;
@ -1753,14 +1802,16 @@
open_story_trainer: function(story_id, feed_id, options) {
options = options || {};
console.log(["open_story_trainer", story_id, feed_id, options]);
story_id = story_id || this.active_story && this.active_story.id;
feed_id = feed_id || (story_id && this.model.get_story(story_id).get('story_feed_id'));
// console.log(["open_story_trainer", story_id, feed_id, options]);
if (story_id && feed_id) {
options['feed_loaded'] = !this.flags['river_view'];
if (this.flags['social_view']) {
if (this.flags['social_view'] && !_.string.contains(this.active_feed, 'river:')) {
options['social_feed_id'] = this.active_feed;
} else if (this.flags['social_view'] && this.active_story.get('friend_user_ids')) {
options['social_feed_id'] = 'social:' + this.active_story.get('friend_user_ids')[0];
}
NEWSBLUR.classifier = new NEWSBLUR.ReaderClassifierStory(story_id, feed_id, options);
}
@ -2347,8 +2398,18 @@
}
},
toggle_sidebar: function() {
if (this.flags['sidebar_closed']) {
this.open_sidebar();
return true;
} else {
this.close_sidebar();
return false;
}
},
close_sidebar: function() {
this.$s.$body.layout().close('west');
this.$s.$layout.layout().hide('west');
this.resize_window();
this.flags['sidebar_closed'] = true;
$('.NB-taskbar-sidebar-toggle-open').stop().animate({
@ -2361,7 +2422,7 @@
},
open_sidebar: function() {
this.$s.$body.layout().open('west');
this.$s.$layout.layout().open('west');
this.resize_window();
this.flags['sidebar_closed'] = false;
$('.NB-taskbar-sidebar-toggle-open').stop().css({
@ -3020,7 +3081,6 @@
this.flags['feed_list_showing_manage_menu'] = false;
$(document).unbind('click.menu');
$(document).unbind('mouseup.menu');
$manage_menu_container.uncorner();
if (this.model.preference('show_tooltips')) {
$('.NB-task-manage').tipsy('enable');
}
@ -3403,7 +3463,7 @@
NEWSBLUR.app.story_titles_header.show_feed_hidden_story_title_indicator(true);
}
this.show_story_titles_above_intelligence_level({'animate': true, 'follow': true});
NEWSBLUR.app.feed_list_header.toggle_hide_read_preference();
NEWSBLUR.app.sidebar_header.toggle_hide_read_preference();
NEWSBLUR.app.feed_list.scroll_to_show_selected_feed();
NEWSBLUR.app.feed_list.scroll_to_show_selected_folder();
@ -3424,20 +3484,15 @@
switch_feed_view_unread_view: function(unread_view) {
if (!_.isNumber(unread_view)) unread_view = this.get_unread_view_score();
var $feed_list = this.$s.$feed_list;
var $social_feeds = this.$s.$social_feeds;
var $sidebar = this.$s.$sidebar;
var unread_view_name = this.get_unread_view_name(unread_view);
var $next_story_button = $('.task_story_next_unread');
var $story_title_indicator = $('.NB-story-title-indicator', this.$story_titles);
$feed_list.removeClass('unread_view_positive')
.removeClass('unread_view_neutral')
.removeClass('unread_view_negative')
.addClass('unread_view_'+unread_view_name);
$social_feeds.removeClass('unread_view_positive')
.removeClass('unread_view_neutral')
.removeClass('unread_view_negative')
.addClass('unread_view_'+unread_view_name);
$sidebar.removeClass('unread_view_positive')
.removeClass('unread_view_neutral')
.removeClass('unread_view_negative')
.addClass('unread_view_'+unread_view_name);
$next_story_button.removeClass('task_story_next_positive')
.removeClass('task_story_next_neutral')
@ -4224,7 +4279,7 @@
}, options.feed && options.feed.attributes);
var $tryfeed_container = this.$s.$tryfeed_header.closest('.NB-feeds-header-container');
this.reset_feed();
this.reset_feed(options);
feed = this.model.set_feed(feed_id, feed);
$('.NB-feeds-header-title', this.$s.$tryfeed_header).text(feed.get('feed_title'));
@ -4276,7 +4331,6 @@
},
show_tryfeed_add_button: function() {
NEWSBLUR.log(["show_tryfeed_add_button", this.$s.$story_taskbar.find('.NB-tryfeed-add:visible').length]);
if (this.$s.$story_taskbar.find('.NB-tryfeed-add:visible').length) return;
var $add = $.make('div', { className: 'NB-modal-submit' }, [
@ -4302,6 +4356,20 @@
$add.animate({'opacity': 1}, {'duration': 600});
},
show_tryout_signup_button: function() {
if (this.$s.$story_taskbar.find('.NB-tryout-signup:visible').length) return;
var $add = $.make('div', { className: 'NB-modal-submit' }, [
$.make('div', { className: 'NB-tryout-signup NB-modal-submit-green NB-modal-submit-button' }, 'Sign Up')
]).css({'opacity': 0});
this.$s.$story_taskbar.find('.NB-taskbar').append($add);
$add.animate({'opacity': 1}, {'duration': 600});
},
hide_tryout_signup_button: function() {
this.$s.$story_taskbar.find('.NB-tryout-signup:visible').remove();
},
add_recommended_feed: function(feed_id) {
feed_id = feed_id || this.active_feed;
var feed_address = this.model.get_feed(feed_id).get('feed_address');
@ -4444,25 +4512,7 @@
var self = this;
var stopPropagation = false;
// NEWSBLUR.log(['click', e, e.button]);
// = Feed Header ==================================================
$.targetIs(e, { tagSelector: '.NB-feeds-header-starred' }, function($t, $p){
e.preventDefault();
self.open_starred_stories();
});
$.targetIs(e, { tagSelector: '.NB-feeds-header-river-sites' }, function($t, $p){
e.preventDefault();
self.open_river_stories();
});
$.targetIs(e, { tagSelector: '.NB-feeds-header-river-blurblogs' }, function($t, $p){
e.preventDefault();
self.open_river_blurblogs_stories();
});
// = Stories ======================================================
// NEWSBLUR.log(['click', e, e.button]);
// = Taskbar ======================================================
@ -4894,10 +4944,6 @@
e.preventDefault();
self.show_previous_story();
});
$.targetIs(e, { tagSelector: '.task_button_signup' }, function($t, $p){
e.preventDefault();
self.show_splash_page();
});
$.targetIs(e, { tagSelector: '.NB-intelligence-slider-control' }, function($t, $p){
e.preventDefault();
var unread_value;
@ -4995,6 +5041,13 @@
var feed_id = self.active_feed;
self.follow_user_in_tryfeed(feed_id);
});
$.targetIs(e, { tagSelector: '.NB-tryout-signup' }, function($t, $p){
e.preventDefault();
self.show_splash_page();
if (NEWSBLUR.welcome) {
NEWSBLUR.welcome.show_signin_form();
}
});
// = Interactions Module ==========================================
@ -5222,11 +5275,7 @@
});
$document.bind('keydown', 'shift+u', function(e) {
e.preventDefault();
if (self.flags['sidebar_closed']) {
self.open_sidebar();
} else {
self.close_sidebar();
}
self.toggle_sidebar();
});
$document.bind('keydown', 'shift+t', function(e) {
e.preventDefault();
@ -5326,14 +5375,15 @@
});
$document.bind('keydown', 'u', function(e) {
e.preventDefault();
if (!self.active_story) return;
var story_id = self.active_story.id;
var story = self.model.get_story(story_id);
if (self.active_story && !self.active_story.get('read_status')) {
NEWSBLUR.assets.stories.mark_read(story, {skip_delay: true});
} else if (self.active_story && self.active_story.get('read_status')) {
NEWSBLUR.assets.stories.mark_unread(story);
}
self.mark_active_story_read();
});
$document.bind('keydown', 'm', function(e) {
e.preventDefault();
self.mark_active_story_read();
});
$document.bind('keydown', 'g', function(e) {
e.preventDefault();
NEWSBLUR.app.feed_selector.toggle();
});
$document.bind('keydown', 'shift+s', function(e) {
e.preventDefault();

View file

@ -57,7 +57,6 @@ NEWSBLUR.ReaderClassifierStory = function(story_id, feed_id, options) {
this.options = $.extend({}, defaults, options);
this.model = NEWSBLUR.assets;
this.runner_story();
console.log(["init story", feed_id, this.feed_id, options, this.options]);
};
var classifier_prototype = {
@ -121,7 +120,7 @@ var classifier_prototype = {
this.handle_cancel();
this.open_modal();
this.$modal.parent().bind('click.reader_classifer', $.rescope(this.handle_clicks, this));
console.log(["runner story", this.options, this.feed_id]);
if (!this.options.feed_loaded) {
_.defer(_.bind(function() {
this.load_single_feed_trainer();

View file

@ -1,197 +0,0 @@
NEWSBLUR.About = function(options) {
var defaults = {};
this.options = $.extend({}, defaults, options);
this.runner();
};
NEWSBLUR.About.prototype = {
runner: function() {
this.make_modal();
this.open_modal();
this.$modal.bind('click', $.rescope(this.handle_click, this));
},
make_modal: function() {
var self = this;
this.$modal = $.make('div', { className: 'NB-modal-about NB-modal-exception NB-modal' }, [
$.make('a', { href: '#faq', className: 'NB-link-about-faq' }, 'FAQ'),
$.make('h2', { className: 'NB-modal-title' }, 'About NewsBlur'),
$.make('div', { className: 'NB-fieldset NB-modal-submit' }, [
$.make('h5', [
$.make('div', { className: 'NB-exception-option-meta' }),
$.make('span', { className: 'NB-exception-option-option' }, 'What:'),
'A Feed Reader with Intelligence'
]),
$.make('div', { className: 'NB-fieldset-fields' }, [
$.make('ul', { className: 'NB-about-what' }, [
$.make('li', 'Read the original site or the RSS feed.'),
$.make('li', 'Automatically highlight stories you want to read.'),
$.make('li', { className: 'last' }, 'Filter out stories you don\'t want to read.')
])
])
]),
$.make('div', { className: 'NB-fieldset NB-modal-submit' }, [
$.make('h5', [
$.make('div', { className: 'NB-exception-option-meta' }),
$.make('span', { className: 'NB-exception-option-option' }, 'Who:'),
'A Labor of Love'
]),
$.make('div', { className: 'NB-fieldset-fields' }, [
$.make('ul', { className: 'NB-about-who' }, [
$.make('li', [
'Hand-crafted by: ',
$.make('a', { href: 'http://www.samuelclay.com' }, 'Samuel Clay')
]),
$.make('li', [
'Find him on Twitter: ',
$.make('a', { href: 'http://twitter.com/samuelclay' }, '@samuelclay')
]),
$.make('li', [
'E-mail: ',
$.make('a', { href: 'mailto:samuel@newsblur.com' }, 'samuel@newsblur.com')
]),
$.make('li', { className: 'last' }, [
'Made in: ',
$.make('a', { href: 'http://www.newyorkfieldguide.com' }, 'New York City')
])
])
])
]),
$.make('div', { className: 'NB-fieldset NB-modal-submit' }, [
$.make('h5', [
$.make('div', { className: 'NB-exception-option-meta' }),
$.make('span', { className: 'NB-exception-option-option' }, 'How:'),
'Server-side technologies'
]),
$.make('div', { className: 'NB-fieldset-fields' }, [
$.make('ul', { className: 'NB-about-server' }, [
$.make('li', [
$.make('a', { href: 'http://www.djangoproject.com' }, 'Django'),
': Web framework written in Python, used to serve all pages.'
]),
$.make('li', [
$.make('a', { href: 'http://ask.github.com/celery' }, 'Celery'),
' &amp; ',
$.make('a', { href: 'http://www.rabbitmq.com' }, 'RabbitMQ'),
': Asynchronous queueing server, used to fetch and parse RSS feeds.'
]),
$.make('li', [
$.make('a', { href: 'http://www.mongodb.com' }, 'MongoDB'),
', ',
$.make('a', { href: 'http://www.mongodb.com/pymongo' }, 'Pymongo'),
', &amp; ',
$.make('a', { href: 'http://www.github.com/hmarr/mongoengine' }, 'Mongoengine'),
': Non-relational database, used to store stories, read stories, feed/page fetch histories, and proxied sites.'
]),
$.make('li', { className: 'last' }, [
$.make('a', { href: 'http://www.postgresql.com' }, 'PostgreSQL'),
': Relational database, used to store feeds, subscriptions, and user accounts.'
])
])
])
]),
$.make('div', { className: 'NB-fieldset NB-modal-submit' }, [
$.make('h5', [
$.make('div', { className: 'NB-exception-option-meta' }),
$.make('span', { className: 'NB-exception-option-option' }, 'How:'),
'Client-side and design'
]),
$.make('div', { className: 'NB-fieldset-fields' }, [
$.make('ul', { className: 'NB-about-client' }, [
$.make('li', [
$.make('a', { href: 'http://www.jquery.com' }, 'jQuery'),
': Cross-browser compliant JavaScript code. IE works without effort.'
]),
$.make('li', [
$.make('a', { href: 'http://documentcloud.github.com/underscore/' }, 'Underscore.js'),
': Functional programming for JavaScript. Indispensible.'
]),
$.make('li', [
$.make('b', 'Miscellaneous jQuery Plugins:'),
' Everything from resizable layouts, to progress bars, sortables, date handling, colors, corners, JSON, animations. See the complete list on ',
$.make('a', { href: 'http://github.com/samuelclay/NewsBlur/' }, 'NewsBlur\'s GitHub repository')
])
])
])
]),
$.make('div', { className: 'NB-fieldset NB-modal-submit' }, [
$.make('h5', [
$.make('div', { className: 'NB-exception-option-meta' }),
$.make('span', { className: 'NB-exception-option-option' }, 'Why:'),
'What\'s the point of another RSS feed reader?'
]),
$.make('div', { className: 'NB-fieldset-fields' }, [
$.make('ul', { className: 'NB-about-why' }, [
$.make('li', [
'To learn how to build and scale the entire web stack: front-end JavaScript, HTML5 layout, back-end view processing, large dataset schema migrations, relational and non-relational database clusters across multiple servers, and getting multiple machines to talk to each other.'
]),
$.make('li', [
'All of this just to prove that I could do it.'
]),
$.make('li', [
'But most importantly, to meet future co-founders.'
])
])
])
])
]);
},
open_modal: function() {
var self = this;
this.$modal.modal({
'minWidth': 600,
'maxWidth': 600,
'overlayClose': true,
'onOpen': function (dialog) {
dialog.overlay.fadeIn(200, function () {
dialog.container.fadeIn(200);
dialog.data.fadeIn(200);
});
},
'onShow': function(dialog) {
$('#simplemodal-container').corner('6px');
},
'onClose': function(dialog, callback) {
dialog.data.hide().empty().remove();
dialog.container.hide().empty().remove();
dialog.overlay.fadeOut(200, function() {
dialog.overlay.empty().remove();
$.modal.close(callback);
});
$('.NB-modal-holder').empty().remove();
}
});
},
handle_cancel: function() {
var $cancel = $('.NB-modal-cancel', this.$modal);
$cancel.click(function(e) {
e.preventDefault();
$.modal.close();
});
},
// ===========
// = Actions =
// ===========
handle_click: function(elem, e) {
var self = this;
$.targetIs(e, { tagSelector: '.NB-link-about-faq' }, function($t, $p) {
e.preventDefault();
$.modal.close(function() {
NEWSBLUR.faq = new NEWSBLUR.Faq();
});
});
}
};

Some files were not shown because too many files have changed in this diff Show more