mirror of
https://github.com/viq/NewsBlur.git
synced 2025-09-18 21:43:31 +00:00
Merge remote-tracking branch 'upstream/master' into defaultview
This commit is contained in:
commit
8a53515a09
58 changed files with 3073 additions and 1222 deletions
|
@ -19,6 +19,7 @@ urlpatterns = patterns('',
|
|||
url(r'^feed_unread_count', views.feed_unread_count, name='feed-unread-count'),
|
||||
url(r'^starred_stories', views.load_starred_stories, name='load-starred-stories'),
|
||||
url(r'^starred_story_hashes', views.starred_story_hashes, name='starred-story-hashes'),
|
||||
url(r'^starred_rss/(?P<user_id>\d+)/(?P<secret_token>\w+)/(?P<tag_slug>[-\w]+)?/?$', views.starred_stories_rss_feed, name='starred-stories-rss-feed'),
|
||||
url(r'^unread_story_hashes', views.unread_story_hashes, name='unread-story-hashes'),
|
||||
url(r'^mark_all_as_read', views.mark_all_as_read, name='mark-all-as-read'),
|
||||
url(r'^mark_story_as_read', views.mark_story_as_read, name='mark-story-as-read'),
|
||||
|
|
|
@ -3,6 +3,7 @@ import time
|
|||
import boto
|
||||
import redis
|
||||
import requests
|
||||
import zlib
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.shortcuts import render
|
||||
from django.contrib.auth.decorators import login_required
|
||||
|
@ -20,6 +21,7 @@ from django.core.mail import mail_admins
|
|||
from django.core.validators import email_re
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.contrib.sites.models import Site
|
||||
from django.utils import feedgenerator
|
||||
from mongoengine.queryset import OperationError
|
||||
from apps.recommendations.models import RecommendedFeed
|
||||
from apps.analyzer.models import MClassifierTitle, MClassifierAuthor, MClassifierFeed, MClassifierTag
|
||||
|
@ -29,7 +31,7 @@ from apps.analyzer.models import get_classifiers_for_user, sort_classifiers_by_f
|
|||
from apps.profile.models import Profile
|
||||
from apps.reader.models import UserSubscription, UserSubscriptionFolders, RUserStory, Feature
|
||||
from apps.reader.forms import SignupForm, LoginForm, FeatureForm
|
||||
from apps.rss_feeds.models import MFeedIcon
|
||||
from apps.rss_feeds.models import MFeedIcon, MStarredStoryCounts
|
||||
from apps.statistics.models import MStatistics
|
||||
# from apps.search.models import SearchStarredStory
|
||||
try:
|
||||
|
@ -257,7 +259,9 @@ def load_feeds(request):
|
|||
len(scheduled_feeds))
|
||||
ScheduleImmediateFetches.apply_async(kwargs=dict(feed_ids=scheduled_feeds))
|
||||
|
||||
starred_count = MStarredStory.objects(user_id=user.pk).count()
|
||||
starred_counts, starred_count = MStarredStoryCounts.user_counts(user.pk, include_total=True)
|
||||
if not starred_count and len(starred_counts):
|
||||
starred_count = MStarredStory.objects(user_id=user.pk).count()
|
||||
|
||||
social_params = {
|
||||
'user_id': user.pk,
|
||||
|
@ -283,6 +287,7 @@ def load_feeds(request):
|
|||
'user_profile': user.profile,
|
||||
'folders': json.decode(folders.folders),
|
||||
'starred_count': starred_count,
|
||||
'starred_counts': starred_counts,
|
||||
'categories': categories
|
||||
}
|
||||
return data
|
||||
|
@ -592,13 +597,16 @@ def load_single_feed(request, feed_id):
|
|||
starred_stories = MStarredStory.objects(user_id=user.pk,
|
||||
story_feed_id=feed.pk,
|
||||
story_hash__in=story_hashes)\
|
||||
.only('story_hash', 'starred_date')
|
||||
.only('story_hash', 'starred_date', 'user_tags')
|
||||
shared_stories = MSharedStory.objects(user_id=user.pk,
|
||||
story_feed_id=feed_id,
|
||||
story_hash__in=story_hashes)\
|
||||
.only('story_hash', 'shared_date', 'comments')
|
||||
starred_stories = dict([(story.story_hash, story.starred_date) for story in starred_stories])
|
||||
shared_stories = dict([(story.story_hash, dict(shared_date=story.shared_date, comments=story.comments))
|
||||
starred_stories = dict([(story.story_hash, dict(starred_date=story.starred_date,
|
||||
user_tags=story.user_tags))
|
||||
for story in starred_stories])
|
||||
shared_stories = dict([(story.story_hash, dict(shared_date=story.shared_date,
|
||||
comments=story.comments))
|
||||
for story in shared_stories])
|
||||
|
||||
checkpoint4 = time.time()
|
||||
|
@ -618,9 +626,10 @@ def load_single_feed(request, feed_id):
|
|||
story['read_status'] = 0
|
||||
if story['story_hash'] in starred_stories:
|
||||
story['starred'] = True
|
||||
starred_date = localtime_for_timezone(starred_stories[story['story_hash']],
|
||||
starred_date = localtime_for_timezone(starred_stories[story['story_hash']]['starred_date'],
|
||||
user.profile.timezone)
|
||||
story['starred_date'] = format_story_link_date__long(starred_date, now)
|
||||
story['user_tags'] = starred_stories[story['story_hash']]['user_tags']
|
||||
if story['story_hash'] in shared_stories:
|
||||
story['shared'] = True
|
||||
shared_date = localtime_for_timezone(shared_stories[story['story_hash']]['shared_date'],
|
||||
|
@ -742,6 +751,7 @@ def load_starred_stories(request):
|
|||
limit = int(request.REQUEST.get('limit', 10))
|
||||
page = int(request.REQUEST.get('page', 0))
|
||||
query = request.REQUEST.get('query')
|
||||
tag = request.REQUEST.get('tag')
|
||||
story_hashes = request.REQUEST.getlist('h')[:100]
|
||||
now = localtime_for_timezone(datetime.datetime.now(), user.profile.timezone)
|
||||
message = None
|
||||
|
@ -751,10 +761,20 @@ def load_starred_stories(request):
|
|||
# results = SearchStarredStory.query(user.pk, query)
|
||||
# story_ids = [result.db_id for result in results]
|
||||
if user.profile.is_premium:
|
||||
stories = MStarredStory.find_stories(query, user.pk, offset=offset, limit=limit)
|
||||
stories = MStarredStory.find_stories(query, user.pk, tag=tag, offset=offset, limit=limit)
|
||||
else:
|
||||
stories = []
|
||||
message = "You must be a premium subscriber to search."
|
||||
elif tag:
|
||||
if user.profile.is_premium:
|
||||
mstories = MStarredStory.objects(
|
||||
user_id=user.pk,
|
||||
user_tags__contains=tag
|
||||
).order_by('-starred_date')[offset:offset+limit]
|
||||
stories = Feed.format_stories(mstories)
|
||||
else:
|
||||
stories = []
|
||||
message = "You must be a premium subscriber to read saved stories by tag."
|
||||
elif story_hashes:
|
||||
mstories = MStarredStory.objects(
|
||||
user_id=user.pk,
|
||||
|
@ -831,6 +851,60 @@ def starred_story_hashes(request):
|
|||
|
||||
return dict(starred_story_hashes=story_hashes)
|
||||
|
||||
def starred_stories_rss_feed(request, user_id, secret_token, tag_slug):
|
||||
try:
|
||||
user = User.objects.get(pk=user_id)
|
||||
except User.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
try:
|
||||
tag_counts = MStarredStoryCounts.objects.get(user_id=user_id, slug=tag_slug)
|
||||
except MStarredStoryCounts.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
data = {}
|
||||
data['title'] = "Saved Stories - %s" % tag_counts.tag
|
||||
data['link'] = "%s%s" % (
|
||||
settings.NEWSBLUR_URL,
|
||||
reverse('saved-stories-tag', kwargs=dict(tag_name=tag_slug)))
|
||||
data['description'] = "Stories saved by %s on NewsBlur with the tag \"%s\"." % (user.username,
|
||||
tag_counts.tag)
|
||||
data['lastBuildDate'] = datetime.datetime.utcnow()
|
||||
data['generator'] = 'NewsBlur - %s' % settings.NEWSBLUR_URL
|
||||
data['docs'] = None
|
||||
data['author_name'] = user.username
|
||||
data['feed_url'] = "%s%s" % (
|
||||
settings.NEWSBLUR_URL,
|
||||
reverse('starred-stories-rss-feed',
|
||||
kwargs=dict(user_id=user_id, secret_token=secret_token, tag_slug=tag_slug)),
|
||||
)
|
||||
rss = feedgenerator.Atom1Feed(**data)
|
||||
|
||||
starred_stories = MStarredStory.objects(
|
||||
user_id=user.pk,
|
||||
user_tags__contains=tag_counts.tag
|
||||
).order_by('-starred_date')[:25]
|
||||
for starred_story in starred_stories:
|
||||
story_data = {
|
||||
'title': starred_story.story_title,
|
||||
'link': starred_story.story_permalink,
|
||||
'description': (starred_story.story_content_z and
|
||||
zlib.decompress(starred_story.story_content_z)),
|
||||
'author_name': starred_story.story_author_name,
|
||||
'categories': starred_story.story_tags,
|
||||
'unique_id': starred_story.story_guid,
|
||||
'pubdate': starred_story.starred_date,
|
||||
}
|
||||
rss.add_item(**story_data)
|
||||
|
||||
logging.user(request, "~FBGenerating ~SB%s~SN's saved story RSS feed (%s, %s stories): ~FM%s" % (
|
||||
user.username,
|
||||
tag_counts.tag,
|
||||
tag_counts.count,
|
||||
request.META.get('HTTP_USER_AGENT', "")[:24]
|
||||
))
|
||||
return HttpResponse(rss.writeString('utf-8'), content_type='application/rss+xml')
|
||||
|
||||
@json.json_view
|
||||
def load_river_stories__redis(request):
|
||||
limit = 12
|
||||
|
@ -913,7 +987,8 @@ def load_river_stories__redis(request):
|
|||
user_id=user.pk,
|
||||
story_feed_id__in=found_feed_ids
|
||||
).only('story_hash', 'starred_date')
|
||||
starred_stories = dict([(story.story_hash, story.starred_date)
|
||||
starred_stories = dict([(story.story_hash, dict(starred_date=story.starred_date,
|
||||
user_tags=story.user_tags))
|
||||
for story in starred_stories])
|
||||
else:
|
||||
starred_stories = {}
|
||||
|
@ -953,9 +1028,10 @@ def load_river_stories__redis(request):
|
|||
story['long_parsed_date'] = format_story_link_date__long(story_date, nowtz)
|
||||
if story['story_hash'] in starred_stories:
|
||||
story['starred'] = True
|
||||
starred_date = localtime_for_timezone(starred_stories[story['story_hash']],
|
||||
starred_date = localtime_for_timezone(starred_stories[story['story_hash']]['starred_date'],
|
||||
user.profile.timezone)
|
||||
story['starred_date'] = format_story_link_date__long(starred_date, now)
|
||||
story['user_tags'] = starred_stories[story['story_hash']]['user_tags']
|
||||
story['intelligence'] = {
|
||||
'feed': apply_classifier_feeds(classifier_feeds, story['story_feed_id']),
|
||||
'author': apply_classifier_authors(classifier_authors, story),
|
||||
|
@ -1685,11 +1761,13 @@ def iframe_buster(request):
|
|||
@ajax_login_required
|
||||
@json.json_view
|
||||
def mark_story_as_starred(request):
|
||||
code = 1
|
||||
feed_id = int(request.REQUEST['feed_id'])
|
||||
story_id = request.REQUEST['story_id']
|
||||
message = ""
|
||||
story, _ = MStory.find_story(story_feed_id=feed_id, story_id=story_id)
|
||||
code = 1
|
||||
feed_id = int(request.REQUEST['feed_id'])
|
||||
story_id = request.REQUEST['story_id']
|
||||
user_tags = request.REQUEST.getlist('user_tags')
|
||||
message = ""
|
||||
story, _ = MStory.find_story(story_feed_id=feed_id, story_id=story_id)
|
||||
|
||||
if not story:
|
||||
return {'code': -1, 'message': "Could not find story to save."}
|
||||
|
||||
|
@ -1698,24 +1776,31 @@ def mark_story_as_starred(request):
|
|||
story_db.pop('user_id', None)
|
||||
story_db.pop('starred_date', None)
|
||||
story_db.pop('id', None)
|
||||
story_db.pop('user_tags', None)
|
||||
now = datetime.datetime.now()
|
||||
story_values = dict(user_id=request.user.pk, starred_date=now, **story_db)
|
||||
story_values = dict(user_id=request.user.pk, starred_date=now, user_tags=user_tags, **story_db)
|
||||
starred_story, created = MStarredStory.objects.get_or_create(
|
||||
story_hash=story.story_hash,
|
||||
user_id=story_values.pop('user_id'),
|
||||
defaults=story_values)
|
||||
if created:
|
||||
logging.user(request, "~FCStarring: ~SB%s" % (story.story_title[:50]))
|
||||
MActivity.new_starred_story(user_id=request.user.pk,
|
||||
story_title=story.story_title,
|
||||
story_feed_id=feed_id,
|
||||
story_id=starred_story.story_guid)
|
||||
else:
|
||||
code = -1
|
||||
message = "Already saved this story."
|
||||
logging.user(request, "~FC~BRAlready stared:~SN~FC ~SB%s" % (story.story_title[:50]))
|
||||
starred_story.user_tags = user_tags
|
||||
starred_story.save()
|
||||
|
||||
return {'code': code, 'message': message}
|
||||
MStarredStoryCounts.count_tags_for_user(request.user.pk)
|
||||
starred_counts = MStarredStoryCounts.user_counts(request.user.pk)
|
||||
|
||||
if created:
|
||||
logging.user(request, "~FCStarring: ~SB%s (~FM~SB%s~FC~SN)" % (story.story_title[:32], starred_story.user_tags))
|
||||
else:
|
||||
logging.user(request, "~FCUpdating starred:~SN~FC ~SB%s~SN (~FM~SB%s~FC~SN)" % (story.story_title[:32], starred_story.user_tags))
|
||||
|
||||
return {'code': code, 'message': message, 'starred_counts': starred_counts}
|
||||
|
||||
@required_params('story_id')
|
||||
@ajax_login_required
|
||||
|
@ -1723,6 +1808,7 @@ def mark_story_as_starred(request):
|
|||
def mark_story_as_unstarred(request):
|
||||
code = 1
|
||||
story_id = request.POST['story_id']
|
||||
starred_counts = None
|
||||
|
||||
starred_story = MStarredStory.objects(user_id=request.user.pk, story_guid=story_id)
|
||||
if not starred_story:
|
||||
|
@ -1730,10 +1816,12 @@ def mark_story_as_unstarred(request):
|
|||
if starred_story:
|
||||
logging.user(request, "~FCUnstarring: ~SB%s" % (starred_story[0].story_title[:50]))
|
||||
starred_story.delete()
|
||||
MStarredStoryCounts.count_tags_for_user(request.user.pk)
|
||||
starred_counts = MStarredStoryCounts.user_counts(request.user.pk)
|
||||
else:
|
||||
code = -1
|
||||
|
||||
return {'code': code}
|
||||
return {'code': code, 'starred_counts': starred_counts}
|
||||
|
||||
@ajax_login_required
|
||||
@json.json_view
|
||||
|
|
|
@ -9,6 +9,7 @@ import zlib
|
|||
import hashlib
|
||||
import redis
|
||||
import pymongo
|
||||
import HTMLParser
|
||||
from collections import defaultdict
|
||||
from operator import itemgetter
|
||||
from bson.objectid import ObjectId
|
||||
|
@ -1215,6 +1216,8 @@ class Feed(models.Model):
|
|||
story['id'] = story_db.story_guid or story_db.story_date
|
||||
if hasattr(story_db, 'starred_date'):
|
||||
story['starred_date'] = story_db.starred_date
|
||||
if hasattr(story_db, 'user_tags'):
|
||||
story['user_tags'] = story_db.user_tags
|
||||
if hasattr(story_db, 'shared_date'):
|
||||
story['shared_date'] = story_db.shared_date
|
||||
if include_permalinks and hasattr(story_db, 'blurblog_permalink'):
|
||||
|
@ -1643,7 +1646,12 @@ class MStory(mongo.Document):
|
|||
@property
|
||||
def feed_guid_hash(self):
|
||||
return "%s:%s" % (self.story_feed_id, self.guid_hash)
|
||||
|
||||
|
||||
@property
|
||||
def decoded_story_title(self):
|
||||
h = HTMLParser.HTMLParser()
|
||||
return h.unescape(self.story_title)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
story_title_max = MStory._fields['story_title'].max_length
|
||||
story_content_type_max = MStory._fields['story_content_type'].max_length
|
||||
|
@ -1933,6 +1941,7 @@ class MStarredStory(mongo.Document):
|
|||
story_guid = mongo.StringField()
|
||||
story_hash = mongo.StringField()
|
||||
story_tags = mongo.ListField(mongo.StringField(max_length=250))
|
||||
user_tags = mongo.ListField(mongo.StringField(max_length=128))
|
||||
image_urls = mongo.ListField(mongo.StringField(max_length=1024))
|
||||
|
||||
meta = {
|
||||
|
@ -1967,12 +1976,26 @@ class MStarredStory(mongo.Document):
|
|||
db_id=str(self.id))
|
||||
|
||||
@classmethod
|
||||
def find_stories(cls, query, user_id, offset=0, limit=25):
|
||||
def find_stories(cls, query, user_id, tag=None, offset=0, limit=25):
|
||||
stories_db = cls.objects(
|
||||
Q(user_id=user_id) &
|
||||
(Q(story_title__icontains=query) |
|
||||
Q(story_author_name__icontains=query) |
|
||||
Q(story_tags__icontains=query))
|
||||
)
|
||||
if tag:
|
||||
stories_db = stories_db.filter(user_tags__contains=tag)
|
||||
|
||||
stories_db = stories_db.order_by('-starred_date')[offset:offset+limit]
|
||||
stories = Feed.format_stories(stories_db)
|
||||
|
||||
return stories
|
||||
|
||||
@classmethod
|
||||
def find_stories_by_user_tag(cls, user_tag, user_id, offset=0, limit=25):
|
||||
stories_db = cls.objects(
|
||||
Q(user_id=user_id),
|
||||
Q(user_tags__icontains=user_tag)
|
||||
).order_by('-starred_date')[offset:offset+limit]
|
||||
stories = Feed.format_stories(stories_db)
|
||||
|
||||
|
@ -2036,7 +2059,63 @@ class MStarredStory(mongo.Document):
|
|||
|
||||
return original_text
|
||||
|
||||
class MStarredStoryCounts(mongo.Document):
|
||||
user_id = mongo.IntField()
|
||||
tag = mongo.StringField(max_length=128, unique_with=['user_id'])
|
||||
slug = mongo.StringField(max_length=128)
|
||||
count = mongo.IntField()
|
||||
|
||||
meta = {
|
||||
'collection': 'starred_stories_counts',
|
||||
'indexes': ['user_id'],
|
||||
'ordering': ['tag'],
|
||||
'allow_inheritance': False,
|
||||
}
|
||||
|
||||
@property
|
||||
def rss_url(self, secret_token=None):
|
||||
if not secret_token:
|
||||
user = User.objects.select_related('profile').get(pk=self.user_id)
|
||||
secret_token = user.profile.secret_token
|
||||
|
||||
return "%s/reader/starred_rss/%s/%s/%s" % (settings.NEWSBLUR_URL, self.user_id,
|
||||
secret_token, self.slug)
|
||||
|
||||
@classmethod
|
||||
def user_counts(cls, user_id, include_total=False, try_counting=True):
|
||||
counts = cls.objects.filter(user_id=user_id)
|
||||
counts = [{'tag': c.tag, 'count': c.count, 'feed_address': c.rss_url} for c in counts]
|
||||
|
||||
if counts == [] and try_counting:
|
||||
cls.count_tags_for_user(user_id)
|
||||
return cls.user_counts(user_id, include_total=include_total,
|
||||
try_counting=False)
|
||||
|
||||
if include_total:
|
||||
for c in counts:
|
||||
if c['tag'] == "":
|
||||
return counts, c['count']
|
||||
return counts, 0
|
||||
|
||||
return counts
|
||||
|
||||
@classmethod
|
||||
def count_tags_for_user(cls, user_id):
|
||||
all_tags = MStarredStory.objects(user_id=user_id,
|
||||
user_tags__exists=True).item_frequencies('user_tags')
|
||||
user_tags = sorted([(k, v) for k, v in all_tags.items() if int(v) > 0],
|
||||
key=itemgetter(1),
|
||||
reverse=True)
|
||||
|
||||
cls.objects(user_id=user_id).delete()
|
||||
for tag, count in dict(user_tags).items():
|
||||
cls.objects.create(user_id=user_id, tag=tag, slug=slugify(tag), count=count)
|
||||
|
||||
total_stories_count = MStarredStory.objects(user_id=user_id).count()
|
||||
cls.objects.create(user_id=user_id, tag="", count=total_stories_count)
|
||||
|
||||
return dict(total=total_stories_count, tags=user_tags)
|
||||
|
||||
class MFetchHistory(mongo.Document):
|
||||
feed_id = mongo.IntField(unique=True)
|
||||
feed_fetch_history = mongo.DynamicField()
|
||||
|
|
|
@ -7,6 +7,7 @@ import re
|
|||
import mongoengine as mongo
|
||||
import random
|
||||
import requests
|
||||
import HTMLParser
|
||||
from collections import defaultdict
|
||||
from BeautifulSoup import BeautifulSoup
|
||||
from mongoengine.queryset import Q
|
||||
|
@ -1407,7 +1408,7 @@ class MSharedStory(mongo.Document):
|
|||
def __unicode__(self):
|
||||
user = User.objects.get(pk=self.user_id)
|
||||
return "%s: %s (%s)%s%s" % (user.username,
|
||||
self.story_title[:20],
|
||||
self.decoded_story_title[:20],
|
||||
self.story_feed_id,
|
||||
': ' if self.has_comments else '',
|
||||
self.comments[:20])
|
||||
|
@ -1420,6 +1421,11 @@ class MSharedStory(mongo.Document):
|
|||
def feed_guid_hash(self):
|
||||
return "%s:%s" % (self.story_feed_id or "0", self.guid_hash)
|
||||
|
||||
@property
|
||||
def decoded_story_title(self):
|
||||
h = HTMLParser.HTMLParser()
|
||||
return h.unescape(self.story_title)
|
||||
|
||||
def canonical(self):
|
||||
return {
|
||||
"user_id": self.user_id,
|
||||
|
@ -1634,7 +1640,7 @@ class MSharedStory(mongo.Document):
|
|||
|
||||
if interactive:
|
||||
feed = Feed.get_by_id(story.story_feed_id)
|
||||
accept_story = raw_input("%s / %s [Y/n]: " % (story.story_title, feed.title))
|
||||
accept_story = raw_input("%s / %s [Y/n]: " % (story.decoded_story_title, feed.title))
|
||||
if accept_story in ['n', 'N']: continue
|
||||
|
||||
story_db = dict([(k, v) for k, v in story._data.items()
|
||||
|
@ -1658,7 +1664,7 @@ class MSharedStory(mongo.Document):
|
|||
shared_feed_ids.append(story.story_feed_id)
|
||||
publish_new_stories = True
|
||||
logging.user(popular_user, "~FCSharing: ~SB~FM%s (%s shares, %s min)" % (
|
||||
story.story_title[:50],
|
||||
story.decoded_story_title[:50],
|
||||
story_info['count'],
|
||||
cutoff))
|
||||
|
||||
|
@ -1914,7 +1920,7 @@ class MSharedStory(mongo.Document):
|
|||
def generate_post_to_service_message(self, truncate=None, include_url=True):
|
||||
message = strip_tags(self.comments)
|
||||
if not message or len(message) < 1:
|
||||
message = self.story_title
|
||||
message = self.decoded_story_title
|
||||
if include_url and truncate:
|
||||
message = truncate_chars(message, truncate - 18 - 30)
|
||||
feed = Feed.get_by_id(self.story_feed_id)
|
||||
|
@ -2023,7 +2029,7 @@ class MSharedStory(mongo.Document):
|
|||
'story_feed': story_feed,
|
||||
'mute_url': mute_url,
|
||||
}
|
||||
story_title = self.story_title.replace('\n', ' ')
|
||||
story_title = self.decoded_story_title.replace('\n', ' ')
|
||||
|
||||
text = render_to_string('mail/email_reply.txt', data)
|
||||
html = pynliner.fromString(render_to_string('mail/email_reply.xhtml', data))
|
||||
|
@ -2038,7 +2044,7 @@ class MSharedStory(mongo.Document):
|
|||
logging.user(reply_user, "~BB~FM~SBSending %s/%s email%s for new reply: %s" % (
|
||||
sent_emails, len(notify_user_ids),
|
||||
'' if len(notify_user_ids) == 1 else 's',
|
||||
self.story_title[:30]))
|
||||
self.decoded_story_title[:30]))
|
||||
|
||||
self.emailed_replies.append(reply.reply_id)
|
||||
self.save()
|
||||
|
@ -2087,7 +2093,7 @@ class MSharedStory(mongo.Document):
|
|||
'story_feed': story_feed,
|
||||
'mute_url': mute_url,
|
||||
}
|
||||
story_title = self.story_title.replace('\n', ' ')
|
||||
story_title = self.decoded_story_title.replace('\n', ' ')
|
||||
|
||||
text = render_to_string('mail/email_reshare.txt', data)
|
||||
html = pynliner.fromString(render_to_string('mail/email_reshare.xhtml', data))
|
||||
|
@ -2103,7 +2109,7 @@ class MSharedStory(mongo.Document):
|
|||
|
||||
logging.user(reshare_user, "~BB~FM~SBSending %s email for story re-share: %s" % (
|
||||
original_user.username,
|
||||
self.story_title[:30]))
|
||||
self.decoded_story_title[:30]))
|
||||
|
||||
def calculate_image_sizes(self, force=False):
|
||||
if not self.story_content_z:
|
||||
|
@ -2286,7 +2292,7 @@ class MSocialServices(mongo.Document):
|
|||
return
|
||||
|
||||
twitter_user = api.me()
|
||||
self.twitter_picture_url = twitter_user.profile_image_url
|
||||
self.twitter_picture_url = twitter_user.profile_image_url_https
|
||||
self.twitter_username = twitter_user.screen_name
|
||||
self.twitter_refreshed_date = datetime.datetime.utcnow()
|
||||
self.syncing_twitter = False
|
||||
|
@ -2524,6 +2530,18 @@ class MSocialServices(mongo.Document):
|
|||
profile.save()
|
||||
return profile
|
||||
|
||||
def sync_twitter_photo(self):
|
||||
profile = MSocialProfile.get_user(self.user_id)
|
||||
|
||||
if profile.photo_service != "twitter":
|
||||
return
|
||||
|
||||
api = self.twitter_api()
|
||||
me = api.me()
|
||||
self.twitter_picture_url = me.profile_image_url_https
|
||||
self.save()
|
||||
self.set_photo('twitter')
|
||||
|
||||
def post_to_twitter(self, shared_story):
|
||||
message = shared_story.generate_post_to_service_message(truncate=140)
|
||||
|
||||
|
@ -2547,7 +2565,7 @@ class MSocialServices(mongo.Document):
|
|||
api.put_object('me', '%s:share' % settings.FACEBOOK_NAMESPACE,
|
||||
link=shared_story.blurblog_permalink(),
|
||||
type="link",
|
||||
name=shared_story.story_title,
|
||||
name=shared_story.decoded_story_title,
|
||||
description=content,
|
||||
website=shared_story.blurblog_permalink(),
|
||||
message=message,
|
||||
|
@ -2564,7 +2582,7 @@ class MSocialServices(mongo.Document):
|
|||
try:
|
||||
api = self.appdotnet_api()
|
||||
api.createPost(text=message, links=[{
|
||||
'text': shared_story.story_title,
|
||||
'text': shared_story.decoded_story_title,
|
||||
'url': shared_story.blurblog_permalink()
|
||||
}])
|
||||
except Exception, e:
|
||||
|
|
|
@ -110,11 +110,13 @@ def load_social_stories(request, user_id, username=None):
|
|||
|
||||
starred_stories = MStarredStory.objects(user_id=user.pk,
|
||||
story_hash__in=story_hashes)\
|
||||
.only('story_hash', 'starred_date')
|
||||
.only('story_hash', 'starred_date', 'user_tags')
|
||||
shared_stories = MSharedStory.objects(user_id=user.pk,
|
||||
story_hash__in=story_hashes)\
|
||||
.only('story_hash', 'shared_date', 'comments')
|
||||
starred_stories = dict([(story.story_hash, story.starred_date) for story in starred_stories])
|
||||
starred_stories = dict([(story.story_hash, dict(starred_date=story.starred_date,
|
||||
user_tags=story.user_tags))
|
||||
for story in starred_stories])
|
||||
shared_stories = dict([(story.story_hash, dict(shared_date=story.shared_date,
|
||||
comments=story.comments))
|
||||
for story in shared_stories])
|
||||
|
@ -135,9 +137,10 @@ def load_social_stories(request, user_id, username=None):
|
|||
|
||||
if story['story_hash'] in starred_stories:
|
||||
story['starred'] = True
|
||||
starred_date = localtime_for_timezone(starred_stories[story['story_hash']],
|
||||
starred_date = localtime_for_timezone(starred_stories[story['story_hash']]['starred_date'],
|
||||
user.profile.timezone)
|
||||
story['starred_date'] = format_story_link_date__long(starred_date, now)
|
||||
story['user_tags'] = starred_stories[story['story_hash']]['user_tags']
|
||||
if story['story_hash'] in shared_stories:
|
||||
story['shared'] = True
|
||||
story['shared_comments'] = strip_tags(shared_stories[story['story_hash']]['comments'])
|
||||
|
@ -239,8 +242,9 @@ def load_river_blurblog(request):
|
|||
starred_stories = MStarredStory.objects(
|
||||
user_id=user.pk,
|
||||
story_hash__in=story_hashes
|
||||
).only('story_hash', 'starred_date')
|
||||
starred_stories = dict([(story.story_hash, story.starred_date)
|
||||
).only('story_hash', 'starred_date', 'user_tags')
|
||||
starred_stories = dict([(story.story_hash, dict(starred_date=story.starred_date,
|
||||
user_tags=story.user_tags))
|
||||
for story in starred_stories])
|
||||
shared_stories = MSharedStory.objects(user_id=user.pk,
|
||||
story_hash__in=story_hashes)\
|
||||
|
@ -281,8 +285,9 @@ def load_river_blurblog(request):
|
|||
story['long_parsed_date'] = format_story_link_date__long(story_date, nowtz)
|
||||
if story['story_hash'] in starred_stories:
|
||||
story['starred'] = True
|
||||
starred_date = localtime_for_timezone(starred_stories[story['story_hash']], user.profile.timezone)
|
||||
starred_date = localtime_for_timezone(starred_stories[story['story_hash']]['starred_date'], user.profile.timezone)
|
||||
story['starred_date'] = format_story_link_date__long(starred_date, now)
|
||||
story['user_tags'] = starred_stories[story['story_hash']]['user_tags']
|
||||
story['intelligence'] = {
|
||||
'feed': apply_classifier_feeds(classifier_feeds, story['story_feed_id'],
|
||||
social_user_ids=story['friend_user_ids']),
|
||||
|
@ -1233,7 +1238,7 @@ def shared_stories_rss_feed(request, user_id, username):
|
|||
data['link'] = social_profile.blurblog_url
|
||||
data['description'] = "Stories shared by %s on NewsBlur." % user.username
|
||||
data['lastBuildDate'] = datetime.datetime.utcnow()
|
||||
data['generator'] = 'NewsBlur - http://www.newsblur.com'
|
||||
data['generator'] = 'NewsBlur - %s' % settings.NEWSBLUR_URL
|
||||
data['docs'] = None
|
||||
data['author_name'] = user.username
|
||||
data['feed_url'] = "http://%s%s" % (
|
||||
|
|
|
@ -53,6 +53,7 @@ javascripts:
|
|||
- media/js/vendor/jquery.chosen.js
|
||||
- media/js/vendor/jquery.effects.core.js
|
||||
- media/js/vendor/jquery.effects.slideOffscreen.js
|
||||
- media/js/vendor/tag-it.js
|
||||
- media/js/vendor/chart.js
|
||||
- media/js/vendor/audio.js
|
||||
- media/js/vendor/socket.io-client.*.js
|
||||
|
@ -119,6 +120,7 @@ stylesheets:
|
|||
- media/css/jquery-ui/jquery.theme.css
|
||||
- media/css/jquery.tipsy.css
|
||||
- media/css/vendor/bootstrap-progressbar.css
|
||||
- media/css/vendor/jquery.tagit.css
|
||||
- media/css/vendor/highlight.css
|
||||
- media/css/*.css
|
||||
mobile:
|
||||
|
|
|
@ -24,19 +24,19 @@ public class AllSharedStoriesReading extends Reading {
|
|||
|
||||
setTitle(getResources().getString(R.string.all_shared_stories));
|
||||
|
||||
Cursor folderCursor = contentResolver.query(FeedProvider.SOCIALCOUNT_URI, null, DatabaseConstants.getBlogSelectionFromState(currentState), null, null);
|
||||
int unreadCount = FeedUtils.getCursorUnreadCount(folderCursor, currentState);
|
||||
folderCursor.close();
|
||||
this.startingUnreadCount = unreadCount;
|
||||
this.currentUnreadCount = unreadCount;
|
||||
|
||||
readingAdapter = new MixedFeedsReadingAdapter(getSupportFragmentManager(), getContentResolver());
|
||||
|
||||
addStoryToMarkAsRead(readingAdapter.getStory(passedPosition));
|
||||
|
||||
getSupportLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getUnreadCount() {
|
||||
Cursor folderCursor = contentResolver.query(FeedProvider.SOCIALCOUNT_URI, null, DatabaseConstants.getBlogSelectionFromState(currentState), null, null);
|
||||
int c = FeedUtils.getCursorUnreadCount(folderCursor, currentState);
|
||||
folderCursor.close();
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int loaderId, Bundle bundle) {
|
||||
StoryOrder storyOrder = PrefsUtils.getStoryOrderForFolder(this, PrefConstants.ALL_SHARED_STORIES_FOLDER_NAME);
|
||||
|
|
|
@ -26,19 +26,19 @@ public class AllStoriesReading extends Reading {
|
|||
stories = contentResolver.query(FeedProvider.ALL_STORIES_URI, null, DatabaseConstants.getStorySelectionFromState(currentState), null, DatabaseConstants.getStorySortOrder(storyOrder));
|
||||
setTitle(getResources().getString(R.string.all_stories_row_title));
|
||||
|
||||
Cursor folderCursor = contentResolver.query(FeedProvider.FEED_COUNT_URI, null, DatabaseConstants.getBlogSelectionFromState(currentState), null, null);
|
||||
int unreadCount = FeedUtils.getCursorUnreadCount(folderCursor, currentState);
|
||||
folderCursor.close();
|
||||
this.startingUnreadCount = unreadCount;
|
||||
this.currentUnreadCount = unreadCount;
|
||||
|
||||
readingAdapter = new MixedFeedsReadingAdapter(getSupportFragmentManager(), getContentResolver());
|
||||
|
||||
addStoryToMarkAsRead(readingAdapter.getStory(passedPosition));
|
||||
|
||||
getSupportLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getUnreadCount() {
|
||||
Cursor folderCursor = contentResolver.query(FeedProvider.FEED_COUNT_URI, null, DatabaseConstants.getBlogSelectionFromState(currentState), null, null);
|
||||
int c = FeedUtils.getCursorUnreadCount(folderCursor, currentState);
|
||||
folderCursor.close();
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int loaderId, Bundle bundle) {
|
||||
StoryOrder storyOrder = PrefsUtils.getStoryOrderForFolder(this, PrefConstants.ALL_STORIES_FOLDER_NAME);
|
||||
|
|
|
@ -21,7 +21,6 @@ import com.newsblur.util.StoryOrder;
|
|||
public class FeedReading extends Reading {
|
||||
|
||||
String feedId;
|
||||
private Feed feed;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceBundle) {
|
||||
|
@ -33,25 +32,26 @@ public class FeedReading extends Reading {
|
|||
Cursor feedClassifierCursor = contentResolver.query(classifierUri, null, null, null, null);
|
||||
Classifier classifier = Classifier.fromCursor(feedClassifierCursor);
|
||||
|
||||
final Uri feedUri = FeedProvider.FEEDS_URI.buildUpon().appendPath(feedId).build();
|
||||
Uri feedUri = FeedProvider.FEEDS_URI.buildUpon().appendPath(feedId).build();
|
||||
Cursor feedCursor = contentResolver.query(feedUri, null, null, null, null);
|
||||
|
||||
feedCursor.moveToFirst();
|
||||
feed = Feed.fromCursor(feedCursor);
|
||||
Feed feed = Feed.fromCursor(feedCursor);
|
||||
feedCursor.close();
|
||||
setTitle(feed.title);
|
||||
|
||||
int unreadCount = FeedUtils.getFeedUnreadCount(this.feed, this.currentState);
|
||||
this.startingUnreadCount = unreadCount;
|
||||
this.currentUnreadCount = unreadCount;
|
||||
|
||||
readingAdapter = new FeedReadingAdapter(getSupportFragmentManager(), feed, classifier);
|
||||
|
||||
addStoryToMarkAsRead(readingAdapter.getStory(passedPosition));
|
||||
|
||||
getSupportLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getUnreadCount() {
|
||||
Uri feedUri = FeedProvider.FEEDS_URI.buildUpon().appendPath(feedId).build();
|
||||
Cursor feedCursor = contentResolver.query(feedUri, null, null, null, null);
|
||||
Feed feed = Feed.fromCursor(feedCursor);
|
||||
feedCursor.close();
|
||||
return FeedUtils.getFeedUnreadCount(feed, this.currentState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int loaderId, Bundle bundle) {
|
||||
Uri storiesURI = FeedProvider.FEED_STORIES_URI.buildUpon().appendPath(feedId).build();
|
||||
|
|
|
@ -27,19 +27,19 @@ public class FolderReading extends Reading {
|
|||
folderName = getIntent().getStringExtra(Reading.EXTRA_FOLDERNAME);
|
||||
setTitle(folderName);
|
||||
|
||||
Cursor folderCursor = contentResolver.query(FeedProvider.FOLDERS_URI.buildUpon().appendPath(folderName).build(), null, null, new String[] { DatabaseConstants.getFolderSelectionFromState(currentState) }, null);
|
||||
int unreadCount = FeedUtils.getCursorUnreadCount(folderCursor, currentState);
|
||||
folderCursor.close();
|
||||
this.startingUnreadCount = unreadCount;
|
||||
this.currentUnreadCount = unreadCount;
|
||||
|
||||
readingAdapter = new MixedFeedsReadingAdapter(getSupportFragmentManager(), getContentResolver());
|
||||
|
||||
addStoryToMarkAsRead(readingAdapter.getStory(passedPosition));
|
||||
|
||||
getSupportLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getUnreadCount() {
|
||||
Cursor folderCursor = contentResolver.query(FeedProvider.FOLDERS_URI.buildUpon().appendPath(folderName).build(), null, null, new String[] { DatabaseConstants.getFolderSelectionFromState(currentState) }, null);
|
||||
int c = FeedUtils.getCursorUnreadCount(folderCursor, currentState);
|
||||
folderCursor.close();
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int loaderId, Bundle bundle) {
|
||||
return new CursorLoader(this, FeedProvider.MULTIFEED_STORIES_URI, null, DatabaseConstants.getStorySelectionFromState(currentState), feedIds, DatabaseConstants.getStorySortOrder(PrefsUtils.getStoryOrderForFolder(this, folderName)));
|
||||
|
|
|
@ -71,6 +71,9 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
protected int passedPosition;
|
||||
protected int currentState;
|
||||
|
||||
protected final Object STORIES_MUTEX = new Object();
|
||||
protected Cursor stories;
|
||||
|
||||
protected ViewPager pager;
|
||||
protected Button overlayLeft, overlayRight;
|
||||
protected ProgressBar overlayProgress, overlayProgressRight, overlayProgressLeft;
|
||||
|
@ -79,20 +82,13 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
protected ReadingAdapter readingAdapter;
|
||||
protected ContentResolver contentResolver;
|
||||
private APIManager apiManager;
|
||||
protected Cursor stories;
|
||||
private boolean noMoreApiPages;
|
||||
private boolean stopLoading;
|
||||
protected volatile boolean requestedPage; // set high iff a syncservice request for stories is already in flight
|
||||
private int currentApiPage = 0;
|
||||
private Set<Story> storiesToMarkAsRead;
|
||||
|
||||
// unread counts for the circular progress overlay. set to nonzero to activate the progress indicator overlay
|
||||
// unread count for the circular progress overlay. set to nonzero to activate the progress indicator overlay
|
||||
protected int startingUnreadCount = 0;
|
||||
protected int currentUnreadCount = 0;
|
||||
|
||||
// A list of stories we have marked as read during this reading session. Needed to help keep track of unread
|
||||
// counts since it would be too costly to query and update the DB on every page change.
|
||||
private Set<Story> storiesAlreadySeen;
|
||||
|
||||
private float overlayRangeTopPx;
|
||||
private float overlayRangeBotPx;
|
||||
|
@ -116,9 +112,6 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
this.overlaySend = (Button) findViewById(R.id.reading_overlay_send);
|
||||
|
||||
fragmentManager = getSupportFragmentManager();
|
||||
|
||||
storiesToMarkAsRead = new HashSet<Story>();
|
||||
storiesAlreadySeen = new HashSet<Story>();
|
||||
|
||||
passedPosition = getIntent().getIntExtra(EXTRA_POSITION, 0);
|
||||
currentState = getIntent().getIntExtra(ItemsList.EXTRA_STATE, 0);
|
||||
|
@ -139,6 +132,7 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
// this likes to default to 'on' for some platforms
|
||||
enableProgressCircle(overlayProgressLeft, false);
|
||||
enableProgressCircle(overlayProgressRight, false);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -152,30 +146,34 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
|
||||
if (cursor != null) {
|
||||
readingAdapter.swapCursor(cursor);
|
||||
stories = cursor;
|
||||
}
|
||||
|
||||
if (this.pager == null) {
|
||||
// if this is the first time we've found a cursor, set up the pager
|
||||
setupPager();
|
||||
}
|
||||
|
||||
try {
|
||||
readingAdapter.notifyDataSetChanged();
|
||||
checkStoryCount(pager.getCurrentItem());
|
||||
if (this.unreadSearchLatch != null) {
|
||||
this.unreadSearchLatch.countDown();
|
||||
synchronized (STORIES_MUTEX) {
|
||||
if (cursor != null) {
|
||||
readingAdapter.swapCursor(cursor);
|
||||
stories = cursor;
|
||||
}
|
||||
ReadingItemFragment fragment = getReadingFragment();
|
||||
if (fragment != null ) {
|
||||
fragment.updateStory(readingAdapter.getStory(pager.getCurrentItem()));
|
||||
fragment.updateSaveButton();
|
||||
|
||||
if (this.pager == null) {
|
||||
// if this is the first time we've found a cursor, we know the onCreate chain is done
|
||||
this.startingUnreadCount = getUnreadCount();
|
||||
// set up the pager after the unread count, so the first mark-read doesn't happen too quickly
|
||||
setupPager();
|
||||
}
|
||||
|
||||
try {
|
||||
readingAdapter.notifyDataSetChanged();
|
||||
checkStoryCount(pager.getCurrentItem());
|
||||
if (this.unreadSearchLatch != null) {
|
||||
this.unreadSearchLatch.countDown();
|
||||
}
|
||||
ReadingItemFragment fragment = getReadingFragment();
|
||||
if (fragment != null ) {
|
||||
fragment.updateStory(readingAdapter.getStory(pager.getCurrentItem()));
|
||||
fragment.updateSaveButton();
|
||||
}
|
||||
} catch (IllegalStateException ise) {
|
||||
// sometimes the pager is already shutting down by the time the callback finishes
|
||||
finish();
|
||||
}
|
||||
} catch (IllegalStateException ise) {
|
||||
// sometimes the pager is already shutting down by the time the callback finishes
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,7 +184,7 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
pager.setOnPageChangeListener(this);
|
||||
pager.setAdapter(readingAdapter);
|
||||
|
||||
pager.setCurrentItem(passedPosition);
|
||||
pager.setCurrentItem(passedPosition, false);
|
||||
// setCurrentItem sometimes fails to pass the first page to the callback, so call it manually
|
||||
// for the first one.
|
||||
this.onPageSelected(passedPosition);
|
||||
|
@ -194,6 +192,11 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
this.enableOverlays();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the DB for the current unreadcount for this view.
|
||||
*/
|
||||
protected abstract int getUnreadCount();
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
super.onCreateOptionsMenu(menu);
|
||||
|
@ -264,6 +267,10 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
if (noMoreData) {
|
||||
this.noMoreApiPages = true;
|
||||
}
|
||||
updateCursor();
|
||||
}
|
||||
|
||||
private void updateCursor() {
|
||||
getSupportLoaderManager().restartLoader(0, null, this);
|
||||
}
|
||||
|
||||
|
@ -287,10 +294,9 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
this.pageHistory.add(story);
|
||||
}
|
||||
}
|
||||
addStoryToMarkAsRead(story);
|
||||
checkStoryCount(position);
|
||||
markStoryRead(story);
|
||||
}
|
||||
this.enableOverlays();
|
||||
checkStoryCount(position);
|
||||
}
|
||||
|
||||
// interface ScrollChangeListener
|
||||
|
@ -341,17 +347,16 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
private void enableOverlays() {
|
||||
this.setOverlayAlpha(1.0f);
|
||||
|
||||
|
||||
this.overlayLeft.setEnabled(this.getLastReadPosition(false) != -1);
|
||||
this.overlayRight.setText((this.currentUnreadCount > 0) ? R.string.overlay_next : R.string.overlay_done);
|
||||
this.overlayRight.setBackgroundResource((this.currentUnreadCount > 0) ? R.drawable.selector_overlay_bg_right : R.drawable.selector_overlay_bg_right_done);
|
||||
this.overlayRight.setText((getUnreadCount() > 0) ? R.string.overlay_next : R.string.overlay_done);
|
||||
this.overlayRight.setBackgroundResource((getUnreadCount() > 0) ? R.drawable.selector_overlay_bg_right : R.drawable.selector_overlay_bg_right_done);
|
||||
|
||||
if (this.startingUnreadCount == 0 ) {
|
||||
// sessions with no unreads just show a full progress bar
|
||||
this.overlayProgress.setMax(1);
|
||||
this.overlayProgress.setProgress(1);
|
||||
} else {
|
||||
int unreadProgress = this.startingUnreadCount - this.currentUnreadCount;
|
||||
int unreadProgress = this.startingUnreadCount - getUnreadCount();
|
||||
this.overlayProgress.setMax(this.startingUnreadCount);
|
||||
this.overlayProgress.setProgress(unreadProgress);
|
||||
}
|
||||
|
@ -368,7 +373,6 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
}
|
||||
|
||||
invalidateOptionsMenu();
|
||||
|
||||
}
|
||||
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
|
@ -413,62 +417,26 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
|
||||
protected abstract void triggerRefresh(int page);
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
flushStoriesMarkedRead();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
this.stopLoading = true;
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a story as having been read. The local DB and remote server will be updated
|
||||
* batch-wise when the activity pauses.
|
||||
*/
|
||||
protected void addStoryToMarkAsRead(Story story) {
|
||||
if (story == null) return;
|
||||
if (story.read) return;
|
||||
synchronized (this.storiesToMarkAsRead) {
|
||||
this.storiesToMarkAsRead.add(story);
|
||||
}
|
||||
// flush immediately if the batch reaches a sufficient size
|
||||
if (this.storiesToMarkAsRead.size() >= AppConstants.MAX_MARK_READ_BATCH) {
|
||||
flushStoriesMarkedRead();
|
||||
}
|
||||
if (!this.storiesAlreadySeen.contains(story)) {
|
||||
// only decrement the cached story count if the story wasn't already read
|
||||
this.storiesAlreadySeen.add(story);
|
||||
this.currentUnreadCount--;
|
||||
}
|
||||
this.enableOverlays();
|
||||
}
|
||||
|
||||
private void flushStoriesMarkedRead() {
|
||||
synchronized(this.storiesToMarkAsRead) {
|
||||
if (this.storiesToMarkAsRead.size() > 0) {
|
||||
FeedUtils.markStoriesAsRead(this.storiesToMarkAsRead, this);
|
||||
this.storiesToMarkAsRead.clear();
|
||||
}
|
||||
private void markStoryRead(Story story) {
|
||||
synchronized (STORIES_MUTEX) {
|
||||
FeedUtils.markStoryAsRead(story, this);
|
||||
updateCursor();
|
||||
}
|
||||
enableOverlays();
|
||||
}
|
||||
|
||||
private void markStoryUnread(Story story) {
|
||||
|
||||
// first, ensure the story isn't queued up to be marked as read
|
||||
this.storiesToMarkAsRead.remove(story);
|
||||
|
||||
// next, call the API to un-mark it as read, just in case we missed the batch
|
||||
// operation, or it was read long before now.
|
||||
FeedUtils.markStoryUnread(story, Reading.this, this.apiManager);
|
||||
|
||||
this.currentUnreadCount++;
|
||||
this.storiesAlreadySeen.remove(story);
|
||||
|
||||
this.enableOverlays();
|
||||
synchronized (STORIES_MUTEX) {
|
||||
FeedUtils.markStoryUnread(story, this);
|
||||
updateCursor();
|
||||
}
|
||||
enableOverlays();
|
||||
}
|
||||
|
||||
// NB: this callback is for the text size slider
|
||||
|
@ -492,7 +460,7 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
* Click handler for the righthand overlay nav button.
|
||||
*/
|
||||
public void overlayRight(View v) {
|
||||
if (this.currentUnreadCount == 0) {
|
||||
if (getUnreadCount() == 0) {
|
||||
// if there are no unread stories, go back to the feed list
|
||||
Intent i = new Intent(this, Main.class);
|
||||
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
|
||||
|
@ -506,7 +474,6 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
return null;
|
||||
}
|
||||
}.execute();
|
||||
//}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -530,7 +497,7 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
break unreadSearch;
|
||||
}
|
||||
} else {
|
||||
if ((candidate == pager.getCurrentItem()) || (story.read) || (this.storiesAlreadySeen.contains(story))) {
|
||||
if ((candidate == pager.getCurrentItem()) || (story.read) ) {
|
||||
candidate++;
|
||||
continue unreadSearch;
|
||||
} else {
|
||||
|
@ -612,8 +579,8 @@ public abstract class Reading extends NbFragmentActivity implements OnPageChange
|
|||
* Click handler for the progress indicator on the righthand overlay nav button.
|
||||
*/
|
||||
public void overlayCount(View v) {
|
||||
String unreadText = getString((this.currentUnreadCount == 1) ? R.string.overlay_count_toast_1 : R.string.overlay_count_toast_N);
|
||||
Toast.makeText(this, String.format(unreadText, this.currentUnreadCount), Toast.LENGTH_SHORT).show();
|
||||
String unreadText = getString((getUnreadCount() == 1) ? R.string.overlay_count_toast_1 : R.string.overlay_count_toast_N);
|
||||
Toast.makeText(this, String.format(unreadText, getUnreadCount()), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public void overlaySend(View v) {
|
||||
|
|
|
@ -26,6 +26,12 @@ public class SavedStoriesReading extends Reading {
|
|||
getSupportLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getUnreadCount() {
|
||||
// effectively disable the notion of unreads for this feed
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int loaderId, Bundle bundle) {
|
||||
return new CursorLoader(this, FeedProvider.STARRED_STORIES_URI, null, null, null, DatabaseConstants.getStorySortOrder(StoryOrder.NEWEST));
|
||||
|
|
|
@ -21,7 +21,6 @@ public class SocialFeedReading extends Reading {
|
|||
|
||||
private String userId;
|
||||
private String username;
|
||||
private SocialFeed socialFeed;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceBundle) {
|
||||
|
@ -30,22 +29,22 @@ public class SocialFeedReading extends Reading {
|
|||
userId = getIntent().getStringExtra(Reading.EXTRA_USERID);
|
||||
username = getIntent().getStringExtra(Reading.EXTRA_USERNAME);
|
||||
|
||||
Uri socialFeedUri = FeedProvider.SOCIAL_FEEDS_URI.buildUpon().appendPath(userId).build();
|
||||
socialFeed = SocialFeed.fromCursor(contentResolver.query(socialFeedUri, null, null, null, null));
|
||||
|
||||
setTitle(getIntent().getStringExtra(EXTRA_USERNAME));
|
||||
|
||||
int unreadCount = FeedUtils.getFeedUnreadCount(this.socialFeed, this.currentState);
|
||||
this.startingUnreadCount = unreadCount;
|
||||
this.currentUnreadCount = unreadCount;
|
||||
|
||||
readingAdapter = new MixedFeedsReadingAdapter(getSupportFragmentManager(), getContentResolver());
|
||||
|
||||
addStoryToMarkAsRead(readingAdapter.getStory(passedPosition));
|
||||
|
||||
getSupportLoaderManager().initLoader(0, null, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getUnreadCount() {
|
||||
Uri socialFeedUri = FeedProvider.SOCIAL_FEEDS_URI.buildUpon().appendPath(userId).build();
|
||||
Cursor cursor = contentResolver.query(socialFeedUri, null, null, null, null);
|
||||
SocialFeed socialFeed = SocialFeed.fromCursor(cursor);
|
||||
cursor.close();
|
||||
return FeedUtils.getFeedUnreadCount(socialFeed, this.currentState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int loaderId, Bundle bundle) {
|
||||
Uri storiesURI = FeedProvider.SOCIALFEED_STORIES_URI.buildUpon().appendPath(userId).build();
|
||||
|
|
|
@ -347,6 +347,18 @@ public class FeedProvider extends ContentProvider {
|
|||
public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
|
||||
return mdb.query(table, columns, selection, selectionArgs, groupBy, having, orderBy);
|
||||
}
|
||||
public void execSQL(String sql) {
|
||||
if (AppConstants.VERBOSE_LOG) {
|
||||
Log.d(LoggingDatabase.class.getName(), "execSQL: " + sql);
|
||||
}
|
||||
mdb.execSQL(sql);
|
||||
}
|
||||
public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
|
||||
return mdb.update(table, values, whereClause, whereArgs);
|
||||
}
|
||||
public long insertWithOnConflict(String table, String nullColumnHack, ContentValues initialValues, int conflictAlgorithm) {
|
||||
return mdb.insertWithOnConflict(table, nullColumnHack, initialValues, conflictAlgorithm);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -431,6 +443,11 @@ public class FeedProvider extends ContentProvider {
|
|||
selectionArgs = new String[] { uri.getLastPathSegment() };
|
||||
return db.query(DatabaseConstants.STORY_TABLE, DatabaseConstants.STORY_COLUMNS, selection, selectionArgs, null, null, sortOrder);
|
||||
|
||||
case INDIVIDUAL_STORY:
|
||||
selectionArgs = new String[] { uri.getLastPathSegment() };
|
||||
selection = DatabaseConstants.STORY_ID + " = ?";
|
||||
return db.query(DatabaseConstants.STORY_TABLE, DatabaseConstants.STORY_COLUMNS, selection, selectionArgs, null, null, sortOrder);
|
||||
|
||||
// Querying for all stories
|
||||
case ALL_STORIES:
|
||||
String allStoriesQuery = "SELECT " + TextUtils.join(",", DatabaseConstants.STORY_COLUMNS) + ", " + DatabaseConstants.FEED_TITLE + ", " +
|
||||
|
@ -598,7 +615,8 @@ public class FeedProvider extends ContentProvider {
|
|||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
final SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||
final SQLiteDatabase rwdb = databaseHelper.getWritableDatabase();
|
||||
final LoggingDatabase db = new LoggingDatabase(rwdb);
|
||||
|
||||
switch (uriMatcher.match(uri)) {
|
||||
case ALL_FEEDS:
|
||||
|
@ -617,10 +635,10 @@ public class FeedProvider extends ContentProvider {
|
|||
// In order to run a raw SQL query whereby we make decrement the column we need to a dynamic reference - something the usual content provider can't easily handle. Hence this circuitous hack.
|
||||
case FEED_COUNT:
|
||||
db.execSQL("UPDATE " + DatabaseConstants.FEED_TABLE + " SET " + selectionArgs[0] + " = " + selectionArgs[0] + " - 1 WHERE " + DatabaseConstants.FEED_ID + " = " + selectionArgs[1]);
|
||||
return 0;
|
||||
return 1;
|
||||
case SOCIALFEED_COUNT:
|
||||
db.execSQL("UPDATE " + DatabaseConstants.SOCIALFEED_TABLE + " SET " + selectionArgs[0] + " = " + selectionArgs[0] + " - 1 WHERE " + DatabaseConstants.SOCIAL_FEED_ID + " = " + selectionArgs[1]);
|
||||
return 0;
|
||||
return 1;
|
||||
case STARRED_STORIES_COUNT:
|
||||
int rows = db.update(DatabaseConstants.STARRED_STORY_COUNT_TABLE, values, null, null);
|
||||
if (rows == 0 ) {
|
||||
|
|
|
@ -79,24 +79,27 @@ public class Feed {
|
|||
return values;
|
||||
}
|
||||
|
||||
public static Feed fromCursor(Cursor childCursor) {
|
||||
public static Feed fromCursor(Cursor cursor) {
|
||||
if (cursor.isBeforeFirst()) {
|
||||
cursor.moveToFirst();
|
||||
}
|
||||
Feed feed = new Feed();
|
||||
feed.active = Boolean.parseBoolean(childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_ACTIVE)));
|
||||
feed.address = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_ADDRESS));
|
||||
feed.favicon = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_FAVICON));
|
||||
feed.faviconColor = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_FAVICON_COLOR));
|
||||
feed.faviconFade = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_FAVICON_FADE));
|
||||
feed.faviconBorder = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_FAVICON_BORDER));
|
||||
feed.faviconText = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_FAVICON_TEXT));
|
||||
feed.faviconUrl = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_FAVICON_URL));
|
||||
feed.feedId = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_ID));
|
||||
feed.feedLink = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_LINK));
|
||||
feed.lastUpdated = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_UPDATED_SECONDS));
|
||||
feed.negativeCount = childCursor.getInt(childCursor.getColumnIndex(DatabaseConstants.FEED_NEGATIVE_COUNT));
|
||||
feed.neutralCount = childCursor.getInt(childCursor.getColumnIndex(DatabaseConstants.FEED_NEUTRAL_COUNT));
|
||||
feed.positiveCount = childCursor.getInt(childCursor.getColumnIndex(DatabaseConstants.FEED_POSITIVE_COUNT));
|
||||
feed.subscribers = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_SUBSCRIBERS));
|
||||
feed.title = childCursor.getString(childCursor.getColumnIndex(DatabaseConstants.FEED_TITLE));
|
||||
feed.active = Boolean.parseBoolean(cursor.getString(cursor.getColumnIndex(DatabaseConstants.FEED_ACTIVE)));
|
||||
feed.address = cursor.getString(cursor.getColumnIndex(DatabaseConstants.FEED_ADDRESS));
|
||||
feed.favicon = cursor.getString(cursor.getColumnIndex(DatabaseConstants.FEED_FAVICON));
|
||||
feed.faviconColor = cursor.getString(cursor.getColumnIndex(DatabaseConstants.FEED_FAVICON_COLOR));
|
||||
feed.faviconFade = cursor.getString(cursor.getColumnIndex(DatabaseConstants.FEED_FAVICON_FADE));
|
||||
feed.faviconBorder = cursor.getString(cursor.getColumnIndex(DatabaseConstants.FEED_FAVICON_BORDER));
|
||||
feed.faviconText = cursor.getString(cursor.getColumnIndex(DatabaseConstants.FEED_FAVICON_TEXT));
|
||||
feed.faviconUrl = cursor.getString(cursor.getColumnIndex(DatabaseConstants.FEED_FAVICON_URL));
|
||||
feed.feedId = cursor.getString(cursor.getColumnIndex(DatabaseConstants.FEED_ID));
|
||||
feed.feedLink = cursor.getString(cursor.getColumnIndex(DatabaseConstants.FEED_LINK));
|
||||
feed.lastUpdated = cursor.getString(cursor.getColumnIndex(DatabaseConstants.FEED_UPDATED_SECONDS));
|
||||
feed.negativeCount = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.FEED_NEGATIVE_COUNT));
|
||||
feed.neutralCount = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.FEED_NEUTRAL_COUNT));
|
||||
feed.positiveCount = cursor.getInt(cursor.getColumnIndex(DatabaseConstants.FEED_POSITIVE_COUNT));
|
||||
feed.subscribers = cursor.getString(cursor.getColumnIndex(DatabaseConstants.FEED_SUBSCRIBERS));
|
||||
feed.title = cursor.getString(cursor.getColumnIndex(DatabaseConstants.FEED_TITLE));
|
||||
return feed;
|
||||
}
|
||||
|
||||
|
|
|
@ -120,6 +120,9 @@ public class Story implements Serializable {
|
|||
}
|
||||
|
||||
public static Story fromCursor(final Cursor cursor) {
|
||||
if (cursor.isBeforeFirst()) {
|
||||
cursor.moveToFirst();
|
||||
}
|
||||
Story story = new Story();
|
||||
story.authors = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_AUTHORS));
|
||||
story.content = cursor.getString(cursor.getColumnIndex(DatabaseConstants.STORY_CONTENT));
|
||||
|
|
|
@ -141,6 +141,13 @@ public class APIManager {
|
|||
return response.getResponse(gson, NewsBlurResponse.class);
|
||||
}
|
||||
|
||||
public NewsBlurResponse markStoryAsRead(String storyHash) {
|
||||
ValueMultimap values = new ValueMultimap();
|
||||
values.put(APIConstants.PARAMETER_STORY_HASH, storyHash);
|
||||
APIResponse response = post(APIConstants.URL_MARK_STORIES_READ, values, false);
|
||||
return response.getResponse(gson, NewsBlurResponse.class);
|
||||
}
|
||||
|
||||
public NewsBlurResponse markStoryAsStarred(final String feedId, final String storyId) {
|
||||
final ValueMultimap values = new ValueMultimap();
|
||||
values.put(APIConstants.PARAMETER_FEEDID, feedId);
|
||||
|
|
|
@ -25,10 +25,6 @@ public class AppConstants {
|
|||
|
||||
public static final String LAST_APP_VERSION = "LAST_APP_VERSION";
|
||||
|
||||
// the max number of mark-as-read ops to batch up before flushing to the server
|
||||
// set to 1 to effectively disable batching
|
||||
public static final int MAX_MARK_READ_BATCH = 1;
|
||||
|
||||
// a pref for the time we completed the last full sync of the feed/fodler list
|
||||
public static final String LAST_SYNC_TIME = "LAST_SYNC_TIME";
|
||||
|
||||
|
|
|
@ -198,11 +198,12 @@ public class FeedUtils {
|
|||
|
||||
}
|
||||
|
||||
public static void markStoryUnread( final Story story, final Context context, final APIManager apiManager ) {
|
||||
|
||||
public static void markStoryUnread(final Story story, final Context context) {
|
||||
// TODO: update DB, too
|
||||
new AsyncTask<Void, Void, NewsBlurResponse>() {
|
||||
@Override
|
||||
protected NewsBlurResponse doInBackground(Void... arg) {
|
||||
APIManager apiManager = new APIManager(context);
|
||||
return apiManager.markStoryAsUnread(story.feedId, story.storyHash);
|
||||
}
|
||||
@Override
|
||||
|
@ -214,7 +215,52 @@ public class FeedUtils {
|
|||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a single story as read on both the local DB and on the server.
|
||||
*/
|
||||
public static void markStoryAsRead(final Story story, final Context context) {
|
||||
if (story.read) { return; }
|
||||
|
||||
// it is imperative that we are idempotent. query the DB for a fresh copy of the story
|
||||
// to ensure it isn't already marked as read. if so, do not update feed counts
|
||||
Uri storyUri = FeedProvider.STORY_URI.buildUpon().appendPath(story.id).build();
|
||||
Cursor cursor = context.getContentResolver().query(storyUri, null, null, null, null);
|
||||
if (cursor.getCount() < 1) {
|
||||
Log.w(FeedUtils.class.getName(), "can't mark story as read, not found in DB: " + story.id);
|
||||
return;
|
||||
}
|
||||
Story freshStory = Story.fromCursor(cursor);
|
||||
cursor.close();
|
||||
if (freshStory.read) { return; }
|
||||
|
||||
// update the local object to show as read even before requeried
|
||||
story.read = true;
|
||||
|
||||
// first, update unread counts in the local DB
|
||||
ArrayList<ContentProviderOperation> updateOps = new ArrayList<ContentProviderOperation>();
|
||||
appendStoryReadOperations(story, updateOps);
|
||||
try {
|
||||
context.getContentResolver().applyBatch(FeedProvider.AUTHORITY, updateOps);
|
||||
} catch (Exception e) {
|
||||
Log.w(FeedUtils.class.getName(), "Could not update unread counts in local storage.", e);
|
||||
}
|
||||
|
||||
// next, update the server
|
||||
new AsyncTask<Void, Void, NewsBlurResponse>() {
|
||||
@Override
|
||||
protected NewsBlurResponse doInBackground(Void... arg) {
|
||||
APIManager apiManager = new APIManager(context);
|
||||
return apiManager.markStoryAsRead(story.storyHash);
|
||||
}
|
||||
@Override
|
||||
protected void onPostExecute(NewsBlurResponse result) {
|
||||
if (result.isError()) {
|
||||
Log.e(FeedUtils.class.getName(), "Could not update unread counts via API: " + result.getErrorMessage());
|
||||
}
|
||||
}
|
||||
}.execute();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -222,7 +268,6 @@ public class FeedUtils {
|
|||
* the local DB and on the server.
|
||||
*/
|
||||
public static void markStoriesAsRead( Collection<Story> stories, final Context context ) {
|
||||
|
||||
// the list of story hashes to mark read
|
||||
final ArrayList<String> storyHashes = new ArrayList<String>();
|
||||
// a list of local DB ops to perform
|
||||
|
@ -261,7 +306,6 @@ public class FeedUtils {
|
|||
for (Story story : stories) {
|
||||
story.read = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void appendStoryReadOperations(Story story, List<ContentProviderOperation> operations) {
|
||||
|
@ -276,20 +320,28 @@ public class FeedUtils {
|
|||
} else {
|
||||
selectionArgs = new String[] { DatabaseConstants.FEED_NEGATIVE_COUNT, story.feedId } ;
|
||||
}
|
||||
|
||||
operations.add(ContentProviderOperation.newUpdate(FeedProvider.FEED_COUNT_URI).withValues(emptyValues).withSelection("", selectionArgs).build());
|
||||
|
||||
if (!TextUtils.isEmpty(story.socialUserId)) {
|
||||
String[] socialSelectionArgs;
|
||||
if (story.getIntelligenceTotal() > 0) {
|
||||
socialSelectionArgs = new String[] { DatabaseConstants.SOCIAL_FEED_POSITIVE_COUNT, story.socialUserId } ;
|
||||
} else if (story.getIntelligenceTotal() == 0) {
|
||||
socialSelectionArgs = new String[] { DatabaseConstants.SOCIAL_FEED_NEUTRAL_COUNT, story.socialUserId } ;
|
||||
} else {
|
||||
socialSelectionArgs = new String[] { DatabaseConstants.SOCIAL_FEED_NEGATIVE_COUNT, story.socialUserId } ;
|
||||
}
|
||||
operations.add(ContentProviderOperation.newUpdate(FeedProvider.SOCIALCOUNT_URI).withValues(emptyValues).withSelection("", socialSelectionArgs).build());
|
||||
}
|
||||
HashSet<String> socialIds = new HashSet<String>();
|
||||
if (!TextUtils.isEmpty(story.socialUserId)) {
|
||||
socialIds.add(story.socialUserId);
|
||||
}
|
||||
if (story.friendUserIds != null) {
|
||||
for (String id : story.friendUserIds) {
|
||||
socialIds.add(id);
|
||||
}
|
||||
}
|
||||
for (String id : socialIds) {
|
||||
String[] socialSelectionArgs;
|
||||
if (story.getIntelligenceTotal() > 0) {
|
||||
socialSelectionArgs = new String[] { DatabaseConstants.SOCIAL_FEED_POSITIVE_COUNT, id } ;
|
||||
} else if (story.getIntelligenceTotal() == 0) {
|
||||
socialSelectionArgs = new String[] { DatabaseConstants.SOCIAL_FEED_NEUTRAL_COUNT, id } ;
|
||||
} else {
|
||||
socialSelectionArgs = new String[] { DatabaseConstants.SOCIAL_FEED_NEGATIVE_COUNT, id } ;
|
||||
}
|
||||
operations.add(ContentProviderOperation.newUpdate(FeedProvider.SOCIALCOUNT_URI).withValues(emptyValues).withSelection("", socialSelectionArgs).build());
|
||||
}
|
||||
|
||||
Uri storyUri = FeedProvider.STORY_URI.buildUpon().appendPath(story.id).build();
|
||||
ContentValues values = new ContentValues();
|
||||
|
|
|
@ -444,8 +444,8 @@ body {
|
|||
color: rgba(0, 0, 0, .4);
|
||||
opacity: .4;
|
||||
font-size: 16px;
|
||||
padding: 78px 24px 0;
|
||||
position: absolute;
|
||||
padding: 78px 16px 0;
|
||||
margin: 48px 0;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .4);
|
||||
top: 40%;
|
||||
width: 100%;
|
||||
|
@ -733,6 +733,10 @@ body {
|
|||
opacity: 1;
|
||||
display: block;
|
||||
}
|
||||
.NB-feedlist .feed.NB-no-hover .NB-feedlist-manage-icon,
|
||||
.NB-feedlist .folder_title.NB-no-hover .NB-feedlist-manage-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.NB-feedlists .folder .folder_title .feed_counts_floater,
|
||||
.NB-feeds-header .feed_counts_floater {
|
||||
|
@ -793,9 +797,15 @@ body {
|
|||
.NB-feedlist .feed.NB-toplevel:hover .feed_favicon {
|
||||
display: none;
|
||||
}
|
||||
.NB-feedlist .feed.NB-toplevel.NB-no-hover .feed_favicon {
|
||||
display: block;
|
||||
}
|
||||
.NB-feedlist .folder_title.NB-toplevel:hover {
|
||||
background: none;
|
||||
}
|
||||
.NB-feedlist .folder_title.NB-toplevel.NB-no-hover {
|
||||
background: inherit;
|
||||
}
|
||||
|
||||
.NB-feedlist .feed_counts {
|
||||
position: absolute;
|
||||
|
@ -809,11 +819,11 @@ body {
|
|||
.NB-feedlist .feed.NB-selected,
|
||||
.NB-feeds-header.NB-selected,
|
||||
.NB-feedlist .folder.NB-selected > .folder_title {
|
||||
background-color: #FDED8D;
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#FFFFD2), to(#FDED8D));
|
||||
background: -moz-linear-gradient(center top , #FFFFD2 0%, #FDED8D 100%);
|
||||
border-top: 1px solid #EBE0BE;
|
||||
border-bottom: 1px solid #E3D0AE;
|
||||
background-color: #FFFFD2;
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#FFFFD2), to(#F5F9B4));
|
||||
background: -moz-linear-gradient(center top , #FFFFD2 0%, #F5F9B4 100%);
|
||||
border-top: 1px solid #D6D682;
|
||||
border-bottom: 1px solid #D6D682;
|
||||
}
|
||||
.NB-feedlist .folder.NB-selected > .folder_title {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
|
||||
|
@ -1088,11 +1098,11 @@ body {
|
|||
|
||||
|
||||
.NB-feed-story-header-info ::-moz-selection {
|
||||
background: transparent;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.NB-feed-story-header-info ::selection {
|
||||
background: transparent;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#story_titles {
|
||||
|
@ -1757,10 +1767,7 @@ background: transparent;
|
|||
color: #304080;
|
||||
border-top: 1px solid #EFEEC3;
|
||||
border-bottom: 1px solid #EFEEC3;
|
||||
background-color: #FFFDDF;
|
||||
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#FFFDEF), to(#FFFDDF));
|
||||
background: -moz-linear-gradient(center top , #FFFDEF 0%, #FFFDDF 100%);
|
||||
background: linear-gradient(center top , #FFFDEF 0%, #FFFDDF 100%);
|
||||
background-color: #FFFEE2;
|
||||
}
|
||||
|
||||
#story_titles .NB-story-title:hover:not(.NB-selected) {
|
||||
|
@ -2075,11 +2082,11 @@ background: transparent;
|
|||
font-weight: bold;
|
||||
}
|
||||
.NB-feed-story-feed .NB-feed-story-header-title:hover {
|
||||
color: #DFE2F3;
|
||||
color: rgba(255, 255, 255, .8);
|
||||
}
|
||||
|
||||
.NB-inverse .NB-feed-story-feed .NB-feed-story-header-title:hover {
|
||||
color: #274C63;
|
||||
color: rgba(0, 0, 0, .6);
|
||||
}
|
||||
|
||||
/* =============================== */
|
||||
|
@ -2397,6 +2404,7 @@ background: transparent;
|
|||
.NB-feed-story .NB-feed-story-content {
|
||||
padding: 12px 0 0;
|
||||
max-width: 700px;
|
||||
min-height: 12px;
|
||||
}
|
||||
.NB-feed-story .NB-narrow-content .NB-feed-story-content {
|
||||
margin-right: 28px;
|
||||
|
@ -3070,6 +3078,92 @@ background: transparent;
|
|||
color: #90928B;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* ===================== */
|
||||
/* = Sideoption - Save = */
|
||||
/* ===================== */
|
||||
|
||||
.NB-sideoption-save-wrapper {
|
||||
height: 0px;
|
||||
overflow: hidden;
|
||||
display: none;
|
||||
}
|
||||
.NB-sideoption-save-wrapper ::-moz-selection {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.NB-sideoption-save-wrapper ::selection {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.NB-sideoption-save-wrapper.NB-active {
|
||||
display: block;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.NB-narrow-content .NB-sideoption-save-wrapper {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
.NB-sideoption-save {
|
||||
padding: 4px 12px 6px;
|
||||
border: 1px solid #DBE6EA;
|
||||
text-align: left;
|
||||
color: #606060;
|
||||
}
|
||||
.NB-sideoption-save .NB-sideoption-save-icon {
|
||||
float: left;
|
||||
margin: 2px 2px 0 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background: transparent url("/media/embed/reader/tag.png") no-repeat 0 0;
|
||||
background-size: 14px;
|
||||
}
|
||||
.NB-sideoption-save .NB-sideoption-save-title {
|
||||
text-transform: uppercase;
|
||||
font-size: 10px;
|
||||
text-align: left;
|
||||
text-shadow: 0 1px 0 #F6F6F6;
|
||||
color: #202020;
|
||||
}
|
||||
|
||||
.NB-sideoption-save .NB-sideoption-save-tag {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.NB-sideoption-save .NB-sideoption-save-populate {
|
||||
float: right;
|
||||
font-size: 9px;
|
||||
line-height: 11px;
|
||||
text-transform: uppercase;
|
||||
padding: 2px 4px;
|
||||
margin: 0 0 2px 0;
|
||||
border-radius: 2px;
|
||||
background-color: rgba(205, 205, 205, .5);
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
.NB-sideoption-save .NB-sideoption-save-populate:hover {
|
||||
background-color: rgba(205, 205, 205, .8);
|
||||
}
|
||||
.NB-sideoption-save .NB-sideoption-save-populate:active {
|
||||
background-color: rgba(205, 205, 205, 1);
|
||||
}
|
||||
.NB-tagging-autocomplete.ui-autocomplete {
|
||||
font-size: 11px;
|
||||
width: 150px !important;
|
||||
}
|
||||
|
||||
/* ====================== */
|
||||
/* = Sideoption - Share = */
|
||||
/* ====================== */
|
||||
|
||||
.NB-sideoption-share-wrapper {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
|
@ -3153,7 +3247,9 @@ background: transparent;
|
|||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
height: 52px;
|
||||
height: 52px;
|
||||
border-color: #C6C6C6;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.NB-sideoption-share .NB-sideoption-share-save {
|
||||
font-size: 10px;
|
||||
|
@ -3495,9 +3591,12 @@ background: transparent;
|
|||
.NB-feeds-header-river-container .NB-feeds-header {
|
||||
border-bottom: 1px solid #B7BBAA;
|
||||
}
|
||||
.NB-feeds-header-starred.NB-feeds-header {
|
||||
.NB-feeds-header-starred-container {
|
||||
border-top: 1px solid #B7BBAA;
|
||||
}
|
||||
.NB-feeds-header-starred.NB-feeds-header {
|
||||
border-bottom: 1px solid #B7BBAA;
|
||||
}
|
||||
.NB-feeds-header-river-container .NB-feeds-header.NB-empty .NB-feeds-header-count {
|
||||
display: none;
|
||||
}
|
||||
|
@ -3512,13 +3611,23 @@ background: transparent;
|
|||
}
|
||||
|
||||
.NB-feeds-header-starred .NB-feeds-header-count {
|
||||
background-color: #11448B;
|
||||
background-color: #506B9A;
|
||||
display: block;
|
||||
padding: 3px 4px 1px;
|
||||
margin: 3px 3px 0 0;
|
||||
padding: 2px 3px 2px;
|
||||
margin: 3px 3px 0 1px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .2);
|
||||
|
||||
}
|
||||
.NB-starred-feeds .unread_count_positive {
|
||||
background-color: #506B9A;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .2);
|
||||
}
|
||||
|
||||
.NB-starred-feeds .feed {
|
||||
border-top: 1px solid #E9EBEE;
|
||||
border-bottom: 1px solid #E9EBEE;
|
||||
background-color: #E9EBEE;
|
||||
}
|
||||
.NB-feeds-header-starred.NB-empty .NB-feeds-header-count {
|
||||
display: none;
|
||||
}
|
||||
|
@ -7466,6 +7575,13 @@ form.opml_import_form input {
|
|||
.NB-modal-exception .NB-fieldset-fields .NB-error {
|
||||
padding: 6px 0 6px 4px;
|
||||
}
|
||||
.NB-modal-feed-settings .NB-exception-option-status {
|
||||
color: #3945C0;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.NB-modal-feed-settings .NB-preference-label {
|
||||
float: left;
|
||||
margin: 6px 0;
|
||||
|
@ -7475,30 +7591,7 @@ form.opml_import_form input {
|
|||
float: left;
|
||||
overflow: hidden;
|
||||
}
|
||||
.NB-modal-feed-settings .NB-preference-options div {
|
||||
float: left;
|
||||
margin: 0 12px;
|
||||
}
|
||||
.NB-modal-feed-settings .NB-preference-options input[type=radio] {
|
||||
float: left;
|
||||
margin: 10px 4px;
|
||||
}
|
||||
|
||||
.NB-modal-feed-settings .NB-preference-options label {
|
||||
padding-left: 4px;
|
||||
margin: 0 0 4px 0;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
}
|
||||
.NB-modal-feed-settings .NB-preference-options img {
|
||||
height: 31px;
|
||||
}
|
||||
.NB-modal-feed-settings .NB-exception-option-status {
|
||||
color: #3945C0;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* ===================== */
|
||||
/* = Feedchooser Modal = */
|
||||
|
@ -8215,16 +8308,6 @@ form.opml_import_form input {
|
|||
border-radius: 3px;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .1);
|
||||
}
|
||||
.NB-modal-preferences .NB-preference-view {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
.NB-modal-preferences .NB-preference-view img {
|
||||
height: 31px;
|
||||
min-width: 50px;
|
||||
}
|
||||
.NB-modal-preferences .NB-preference-view .NB-preference-options input[type="radio"] {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.NB-modal-preferences .NB-preference-story-pane-position input {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
@ -8402,6 +8485,52 @@ form.opml_import_form input {
|
|||
font-size: 12px;
|
||||
width: 100px;
|
||||
}
|
||||
.NB-modal-preferences .NB-preference-autoopenfolder .NB-folders {
|
||||
max-width: 240px;
|
||||
}
|
||||
.NB-modal-preferences .NB-preference .NB-preference-options.NB-view-settings input[type=radio] {
|
||||
margin: 3px 6px 0 2px;
|
||||
}
|
||||
|
||||
|
||||
.NB-view-settings.NB-preference-options div {
|
||||
float: left;
|
||||
margin: 0 6px 0 0;
|
||||
}
|
||||
.NB-view-settings.NB-preference-options input[type=radio] {
|
||||
float: left;
|
||||
margin: 2px 6px 0 0px;
|
||||
}
|
||||
.NB-view-settings.NB-preference-options label {
|
||||
margin: 0 0 4px 0;
|
||||
float: left;
|
||||
cursor: pointer;
|
||||
text-transform: uppercase;
|
||||
font-size: 12px;
|
||||
color: #303030;
|
||||
display: block;
|
||||
padding: 4px 6px;
|
||||
border: 1px solid rgba(0,0,0,.2);
|
||||
border-radius: 3px;
|
||||
}
|
||||
.NB-preference-options.NB-view-settings img {
|
||||
float: left;
|
||||
width: 18px;
|
||||
height: 15px;
|
||||
padding: 1px 0 0 0;
|
||||
margin: 0 4px 0 0;
|
||||
}
|
||||
.NB-preference-options.NB-view-settings .NB-view-title {
|
||||
margin: 0;
|
||||
padding: 1px 0 0 0;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.NB-preference-options.NB-view-settings div {
|
||||
float: left;
|
||||
margin: 0 6px 0 0;
|
||||
}
|
||||
|
||||
|
||||
/* ================== */
|
||||
/* = Account Dialog = */
|
||||
|
|
110
media/css/vendor/jquery.tagit.css
vendored
Executable file
110
media/css/vendor/jquery.tagit.css
vendored
Executable file
|
@ -0,0 +1,110 @@
|
|||
ul.tagit {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: #C6C6C6;
|
||||
background: inherit;
|
||||
margin-left: inherit; /* usually we don't want the regular ul margins. */
|
||||
margin-right: inherit;
|
||||
padding: 5px 5px 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
ul.tagit li.tagit-choice {
|
||||
display: block;
|
||||
margin: 2px 5px 2px 0;
|
||||
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
|
||||
float: left;
|
||||
font-weight: normal;
|
||||
font-size: 9px;
|
||||
border-radius: 4px;
|
||||
line-height: 14px;
|
||||
|
||||
padding: 1px 16px 2px 5px;
|
||||
margin: 0 4px 4px 0;
|
||||
background: none;
|
||||
background-color: rgba(0, 0, 0, .1);
|
||||
color: #959B8B;
|
||||
/* text-shadow: 0 1px 0 rgba(255, 255, 255, .5);*/
|
||||
border: 1px solid transparent;
|
||||
border-color: rgba(255, 255, 255, .3) transparent rgba(0, 0, 0, .1);
|
||||
|
||||
}
|
||||
|
||||
ul.tagit li.tagit-new {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
ul.tagit li.tagit-choice a.tagit-label {
|
||||
text-decoration: none;
|
||||
}
|
||||
ul.tagit li.tagit-choice .tagit-close {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: .1em;
|
||||
top: 50%;
|
||||
margin-top: -8px;
|
||||
line-height: 17px;
|
||||
}
|
||||
|
||||
/* used for some custom themes that don't need image icons */
|
||||
ul.tagit li.tagit-choice .tagit-close .text-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
ul.tagit li.tagit-choice a.tagit-close {
|
||||
text-decoration: none;
|
||||
}
|
||||
ul.tagit li.tagit-choice .tagit-close {
|
||||
right: .4em;
|
||||
}
|
||||
ul.tagit li.tagit-choice .ui-icon {
|
||||
display: none;
|
||||
}
|
||||
ul.tagit li.tagit-choice .tagit-close .text-icon {
|
||||
display: inline;
|
||||
font-family: arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 16px;
|
||||
color: #777;
|
||||
}
|
||||
ul.tagit li.tagit-choice:hover, ul.tagit li.tagit-choice.remove {
|
||||
background-color: #EDADAF;
|
||||
border-color: #D6565B;
|
||||
color: white;
|
||||
}
|
||||
ul.tagit li.tagit-choice:active {
|
||||
background-color: #E6888D;
|
||||
border-color: #CA404A;
|
||||
color: white;
|
||||
}
|
||||
|
||||
ul.tagit li:hover a.tagit-close .text-icon {
|
||||
color: #722;
|
||||
}
|
||||
ul.tagit input[type="text"] {
|
||||
color: #333333;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
font-size: 11px;
|
||||
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
|
||||
border: none;
|
||||
padding: 0 1px 5px 1px;
|
||||
width: inherit;
|
||||
background-color: inherit;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
ul.tagit li.tagit-choice input {
|
||||
display: block;
|
||||
float: left;
|
||||
margin: 2px 5px 2px 0;
|
||||
}
|
BIN
media/img/reader/tag.png
Normal file
BIN
media/img/reader/tag.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 704 B |
|
@ -14,6 +14,7 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
|
|||
this.folders = new NEWSBLUR.Collections.Folders([]);
|
||||
this.favicons = {};
|
||||
this.stories = new NEWSBLUR.Collections.Stories();
|
||||
this.starred_feeds = new NEWSBLUR.Collections.StarredFeeds();
|
||||
this.queued_read_stories = {};
|
||||
this.classifiers = {};
|
||||
this.friends = {};
|
||||
|
@ -226,23 +227,49 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
|
|||
|
||||
mark_story_as_starred: function(story_id, callback) {
|
||||
var self = this;
|
||||
this.starred_count += 1;
|
||||
var story = this.get_story(story_id);
|
||||
story.set('starred', true);
|
||||
var selected = this.starred_feeds.selected();
|
||||
|
||||
var pre_callback = function(data) {
|
||||
self.starred_feeds.reset(data.starred_counts, {parse: true});
|
||||
|
||||
if (selected) {
|
||||
self.starred_feeds.get(selected).set('selected', true);
|
||||
}
|
||||
|
||||
if (callback) callback(data);
|
||||
};
|
||||
|
||||
this.make_request('/reader/mark_story_as_starred', {
|
||||
story_id: story_id,
|
||||
feed_id: story.get('story_feed_id')
|
||||
}, callback);
|
||||
story_id: story_id,
|
||||
feed_id: story.get('story_feed_id'),
|
||||
user_tags: story.get('user_tags')
|
||||
}, pre_callback);
|
||||
},
|
||||
|
||||
mark_story_as_unstarred: function(story_id, callback) {
|
||||
var self = this;
|
||||
this.starred_count -= 1;
|
||||
var story = this.get_story(story_id);
|
||||
story.set('starred', false);
|
||||
var selected = this.starred_feeds.selected();
|
||||
|
||||
var pre_callback = function(data) {
|
||||
self.starred_feeds.reset(data.starred_counts, {parse: true});
|
||||
|
||||
if (selected && self.starred_feeds.get(selected)) {
|
||||
self.starred_feeds.get(selected).set('selected', true);
|
||||
}
|
||||
|
||||
if (callback) callback(data);
|
||||
};
|
||||
|
||||
var pre_callback = function(data) {
|
||||
self.starred_feeds.reset(data.starred_counts, {parse: true, update: true});
|
||||
if (callback) callback(data);
|
||||
};
|
||||
|
||||
this.make_request('/reader/mark_story_as_unstarred', {
|
||||
story_id: story_id
|
||||
}, callback);
|
||||
}, pre_callback);
|
||||
},
|
||||
|
||||
mark_feed_as_read: function(feed_id, cutoff_timestamp, direction, mark_active, callback) {
|
||||
|
@ -397,7 +424,8 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
|
|||
|
||||
self.folders.reset(_.compact(subscriptions.folders), {parse: true});
|
||||
self.starred_count = subscriptions.starred_count;
|
||||
self.social_feeds.reset(subscriptions.social_feeds);
|
||||
self.starred_feeds.reset(subscriptions.starred_counts, {parse: true});
|
||||
self.social_feeds.reset(subscriptions.social_feeds, {parse: true});
|
||||
self.user_profile.set(subscriptions.social_profile);
|
||||
self.social_services = subscriptions.social_services;
|
||||
|
||||
|
@ -514,10 +542,7 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
|
|||
feed_title: data.feed_title || this.active_feed.get('feed_title'),
|
||||
updated: data.updated || this.active_feed.get('updated'),
|
||||
feed_address: data.feed_address || this.active_feed.get('feed_address')
|
||||
}, {silent: true});
|
||||
if (this.active_feed.hasChanged()) {
|
||||
this.active_feed.change();
|
||||
}
|
||||
});
|
||||
}
|
||||
this.feed_id = feed_id;
|
||||
this.starred_stories = data.starred_stories;
|
||||
|
@ -555,7 +580,7 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
|
|||
});
|
||||
},
|
||||
|
||||
fetch_starred_stories: function(page, callback, error_callback, first_load) {
|
||||
fetch_starred_stories: function(page, tag, callback, error_callback, first_load) {
|
||||
var self = this;
|
||||
|
||||
var pre_callback = function(data) {
|
||||
|
@ -566,7 +591,8 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
|
|||
|
||||
this.make_request('/reader/starred_stories', {
|
||||
page: page,
|
||||
query: NEWSBLUR.reader.flags.search
|
||||
query: NEWSBLUR.reader.flags.search,
|
||||
tag: tag
|
||||
}, pre_callback, error_callback, {
|
||||
'ajax_group': (page ? 'feed_page' : 'feed'),
|
||||
'request_type': 'GET'
|
||||
|
@ -848,8 +874,10 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
|
|||
get_feed: function(feed_id) {
|
||||
var self = this;
|
||||
|
||||
if (_.string.include(feed_id, 'social:')) {
|
||||
if (_.string.startsWith(feed_id, 'social:')) {
|
||||
return this.social_feeds.get(feed_id);
|
||||
} else if (_.string.startsWith(feed_id, 'starred:')) {
|
||||
return this.starred_feeds.get(feed_id);
|
||||
} else {
|
||||
return this.feeds.get(feed_id);
|
||||
}
|
||||
|
@ -870,6 +898,18 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
|
|||
return this.feeds;
|
||||
},
|
||||
|
||||
get_social_feeds: function() {
|
||||
var self = this;
|
||||
|
||||
return this.social_feeds;
|
||||
},
|
||||
|
||||
get_starred_feeds: function() {
|
||||
var self = this;
|
||||
|
||||
return this.starred_feeds;
|
||||
},
|
||||
|
||||
get_folders: function() {
|
||||
var self = this;
|
||||
|
||||
|
|
|
@ -69,21 +69,52 @@ NEWSBLUR.Modal.prototype = {
|
|||
$.modal.close(callback);
|
||||
},
|
||||
|
||||
make_feed_chooser: function() {
|
||||
make_feed_chooser: function(options) {
|
||||
options = options || {};
|
||||
var $chooser = $.make('select', { name: 'feed', className: 'NB-modal-feed-chooser' });
|
||||
var $feeds_optgroup = $.make('optgroup', { label: "Sites" });
|
||||
var $social_feeds_optgroup = $.make('optgroup', { label: "Blurblogs" });
|
||||
var $starred_feeds_optgroup = $.make('optgroup', { label: "Saved Tags" });
|
||||
var current_feed_id = this.feed_id;
|
||||
this.feeds = this.model.get_feeds();
|
||||
|
||||
this.feeds.each(function(feed) {
|
||||
var make_feed_option = function(feed) {
|
||||
if (!feed.get('feed_title')) return;
|
||||
|
||||
var $option = $.make('option', { value: feed.id }, feed.get('feed_title'));
|
||||
$option.appendTo($chooser);
|
||||
$option.appendTo(feed.is_starred() ? $starred_feeds_optgroup :
|
||||
feed.is_social() ? $social_feeds_optgroup :
|
||||
$feeds_optgroup);
|
||||
|
||||
if (feed.id == current_feed_id) {
|
||||
$option.attr('selected', true);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.feeds = this.model.get_feeds();
|
||||
this.feeds.each(make_feed_option);
|
||||
|
||||
if (!options.skip_social) {
|
||||
this.social_feeds = this.model.get_social_feeds();
|
||||
this.social_feeds.each(make_feed_option);
|
||||
}
|
||||
|
||||
if (!options.skip_starred) {
|
||||
this.starred_feeds = this.model.get_starred_feeds();
|
||||
this.starred_feeds.each(make_feed_option);
|
||||
}
|
||||
|
||||
$('option', $feeds_optgroup).tsort();
|
||||
$('option', $social_feeds_optgroup).tsort();
|
||||
$('option', $starred_feeds_optgroup).tsort();
|
||||
|
||||
$chooser.append($feeds_optgroup);
|
||||
if (!options.skip_social) {
|
||||
$chooser.append($social_feeds_optgroup);
|
||||
}
|
||||
if (!options.skip_starred) {
|
||||
$chooser.append($starred_feeds_optgroup);
|
||||
}
|
||||
|
||||
$('option', $chooser).tsort();
|
||||
return $chooser;
|
||||
},
|
||||
|
||||
|
|
|
@ -6,6 +6,10 @@ NEWSBLUR.Router = Backbone.Router.extend({
|
|||
"site/:site_id/:slug": "site",
|
||||
"site/:site_id/": "site",
|
||||
"site/:site_id": "site",
|
||||
"saved": "starred",
|
||||
"saved/:tag": "starred",
|
||||
"folder/saved": "starred",
|
||||
"folder/saved/:tag": "starred",
|
||||
"folder/:folder_name": "folder",
|
||||
"folder/:folder_name/": "folder",
|
||||
"social/:user_id/:slug": "social",
|
||||
|
@ -24,7 +28,7 @@ NEWSBLUR.Router = Backbone.Router.extend({
|
|||
},
|
||||
|
||||
site: function(site_id, slug) {
|
||||
NEWSBLUR.log(["site", site_id, slug]);
|
||||
// NEWSBLUR.log(["site", site_id, slug]);
|
||||
site_id = parseInt(site_id, 10);
|
||||
var feed = NEWSBLUR.assets.get_feed(site_id);
|
||||
if (feed) {
|
||||
|
@ -40,9 +44,18 @@ NEWSBLUR.Router = Backbone.Router.extend({
|
|||
}
|
||||
},
|
||||
|
||||
starred: function(tag) {
|
||||
options = {
|
||||
router: true,
|
||||
tag: tag
|
||||
};
|
||||
console.log(["starred", options, tag]);
|
||||
NEWSBLUR.reader.open_starred_stories(options);
|
||||
},
|
||||
|
||||
folder: function(folder_name) {
|
||||
folder_name = folder_name.replace(/-/g, ' ');
|
||||
NEWSBLUR.log(["folder", folder_name]);
|
||||
// NEWSBLUR.log(["folder", folder_name]);
|
||||
var options = {router: true};
|
||||
if (folder_name == "everything") {
|
||||
NEWSBLUR.reader.open_river_stories(null, null, options);
|
||||
|
|
|
@ -78,6 +78,14 @@ NEWSBLUR.Models.Feed = Backbone.Model.extend({
|
|||
return true;
|
||||
},
|
||||
|
||||
parent_folder_names: function() {
|
||||
var names = _.compact(_.flatten(_.map(this.folders, function(folder) {
|
||||
return folder.parent_folder_names();
|
||||
})));
|
||||
|
||||
return names;
|
||||
},
|
||||
|
||||
rename: function(new_title) {
|
||||
this.set('feed_title', new_title);
|
||||
NEWSBLUR.assets.rename_feed(this.id, new_title);
|
||||
|
@ -107,6 +115,10 @@ NEWSBLUR.Models.Feed = Backbone.Model.extend({
|
|||
return true;
|
||||
},
|
||||
|
||||
is_starred: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
is_light: function() {
|
||||
var is_light = this._is_light;
|
||||
if (!_.isUndefined(is_light)) {
|
||||
|
|
|
@ -204,6 +204,16 @@ NEWSBLUR.Collections.Folders = Backbone.Collection.extend({
|
|||
return names;
|
||||
},
|
||||
|
||||
parent_folder_names: function() {
|
||||
var names = [this.options.title];
|
||||
if (this.parent_folder) {
|
||||
var parents = _.compact(_.flatten(this.parent_folder.parent_folder_names()));
|
||||
names = names.concat(parents);
|
||||
}
|
||||
|
||||
return names;
|
||||
},
|
||||
|
||||
feed_ids_in_folder: function() {
|
||||
return _.compact(_.flatten(this.map(function(item) {
|
||||
return item.feed_ids_in_folder();
|
||||
|
@ -242,15 +252,17 @@ NEWSBLUR.Collections.Folders = Backbone.Collection.extend({
|
|||
});
|
||||
},
|
||||
|
||||
unread_counts: function(sum_total) {
|
||||
unread_counts: function(sum_total, seen_feeds) {
|
||||
if (!seen_feeds) seen_feeds = [];
|
||||
var counts = this.reduce(function(counts, item) {
|
||||
if (item.is_feed()) {
|
||||
if (item.is_feed() && !_.contains(seen_feeds, item.feed.id)) {
|
||||
var feed_counts = item.feed.unread_counts();
|
||||
counts['ps'] += feed_counts['ps'];
|
||||
counts['nt'] += feed_counts['nt'];
|
||||
counts['ng'] += feed_counts['ng'];
|
||||
seen_feeds.push(item.feed.id);
|
||||
} else if (item.is_folder()) {
|
||||
var folder_counts = item.folders.unread_counts();
|
||||
var folder_counts = item.folders.unread_counts(false, seen_feeds);
|
||||
counts['ps'] += folder_counts['ps'];
|
||||
counts['nt'] += folder_counts['nt'];
|
||||
counts['ng'] += folder_counts['ng'];
|
||||
|
|
|
@ -37,6 +37,10 @@ NEWSBLUR.Models.SocialSubscription = Backbone.Model.extend({
|
|||
return false;
|
||||
},
|
||||
|
||||
is_starred: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
unread_counts: function() {
|
||||
return {
|
||||
ps: this.get('ps') || 0,
|
||||
|
|
83
media/js/newsblur/models/starred_counts.js
Normal file
83
media/js/newsblur/models/starred_counts.js
Normal file
|
@ -0,0 +1,83 @@
|
|||
NEWSBLUR.Models.StarredFeed = Backbone.Model.extend({
|
||||
|
||||
initialize: function() {
|
||||
this.set('feed_title', this.get('tag'));
|
||||
this.views = [];
|
||||
},
|
||||
|
||||
is_social: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
is_feed: function() {
|
||||
return false;
|
||||
},
|
||||
|
||||
is_starred: function() {
|
||||
return true;
|
||||
},
|
||||
|
||||
unread_counts: function() {
|
||||
return {
|
||||
ps: this.get('count') || 0,
|
||||
nt: 0,
|
||||
ng: 0
|
||||
};
|
||||
},
|
||||
|
||||
tag_slug: function() {
|
||||
return Inflector.sluggify(this.get('tag'));
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
NEWSBLUR.Collections.StarredFeeds = Backbone.Collection.extend({
|
||||
|
||||
model: NEWSBLUR.Models.StarredFeed,
|
||||
|
||||
parse: function(models) {
|
||||
_.each(models, function(feed) {
|
||||
feed.id = 'starred:' + feed.tag;
|
||||
// feed.selected = false;
|
||||
feed.ps = feed.count;
|
||||
});
|
||||
return models;
|
||||
},
|
||||
|
||||
comparator: function(a, b) {
|
||||
var sort_order = NEWSBLUR.reader.model.preference('feed_order');
|
||||
var title_a = a.get('feed_title') || '';
|
||||
var title_b = b.get('feed_title') || '';
|
||||
title_a = title_a.toLowerCase();
|
||||
title_b = title_b.toLowerCase();
|
||||
|
||||
if (sort_order == 'MOSTUSED') {
|
||||
var opens_a = a.get('count');
|
||||
var opens_b = b.get('count');
|
||||
if (opens_a > opens_b) return -1;
|
||||
if (opens_a < opens_b) return 1;
|
||||
}
|
||||
|
||||
// if (!sort_order || sort_order == 'ALPHABETICAL')
|
||||
if (title_a > title_b) return 1;
|
||||
else if (title_a < title_b) return -1;
|
||||
return 0;
|
||||
},
|
||||
|
||||
selected: function() {
|
||||
return this.detect(function(feed) { return feed.get('selected'); });
|
||||
},
|
||||
|
||||
deselect: function() {
|
||||
this.chain().select(function(feed) {
|
||||
return feed.get('selected');
|
||||
}).each(function(feed){
|
||||
feed.set('selected', false);
|
||||
});
|
||||
},
|
||||
|
||||
all_tags: function() {
|
||||
return this.pluck('tag');
|
||||
}
|
||||
|
||||
});
|
|
@ -5,6 +5,8 @@ NEWSBLUR.Models.Story = Backbone.Model.extend({
|
|||
this.bind('change:shared_comments', this.populate_comments);
|
||||
this.bind('change:comments', this.populate_comments);
|
||||
this.bind('change:comment_count', this.populate_comments);
|
||||
this.bind('change:starred', this.change_starred);
|
||||
this.bind('change:user_tags', this.change_user_tags);
|
||||
this.populate_comments();
|
||||
this.story_permalink = this.get('story_permalink');
|
||||
this.story_title = this.get('story_title');
|
||||
|
@ -16,7 +18,7 @@ NEWSBLUR.Models.Story = Backbone.Model.extend({
|
|||
},
|
||||
|
||||
score: function() {
|
||||
if (NEWSBLUR.reader.active_feed == 'starred') {
|
||||
if (NEWSBLUR.reader.flags['starred_view']) {
|
||||
return 1;
|
||||
} else {
|
||||
return NEWSBLUR.utils.compute_story_score(this);
|
||||
|
@ -43,16 +45,6 @@ NEWSBLUR.Models.Story = Backbone.Model.extend({
|
|||
return NEWSBLUR.assets.stories.mark_read(this, options);
|
||||
},
|
||||
|
||||
star_story: function() {
|
||||
this.set('starred', !this.get('starred'));
|
||||
if (this.get('starred')) {
|
||||
NEWSBLUR.assets.mark_story_as_starred(this.id);
|
||||
} else {
|
||||
NEWSBLUR.assets.mark_story_as_unstarred(this.id);
|
||||
}
|
||||
NEWSBLUR.reader.update_starred_count();
|
||||
},
|
||||
|
||||
open_story_in_new_tab: function(background) {
|
||||
this.mark_read({skip_delay: true});
|
||||
|
||||
|
@ -88,6 +80,77 @@ NEWSBLUR.Models.Story = Backbone.Model.extend({
|
|||
if (model.collection) {
|
||||
model.collection.detect_selected_story(model, selected);
|
||||
}
|
||||
},
|
||||
|
||||
// =================
|
||||
// = Saved Stories =
|
||||
// =================
|
||||
|
||||
toggle_starred: function() {
|
||||
this.set('user_tags', this.existing_tags(), {silent: true});
|
||||
|
||||
if (!this.get('starred')) {
|
||||
NEWSBLUR.assets.starred_count += 1;
|
||||
this.set('starred', true);
|
||||
} else {
|
||||
NEWSBLUR.assets.starred_count -= 1;
|
||||
this.set('starred', false);
|
||||
}
|
||||
NEWSBLUR.reader.update_starred_count();
|
||||
},
|
||||
|
||||
change_starred: function() {
|
||||
if (this.get('starred')) {
|
||||
NEWSBLUR.assets.mark_story_as_starred(this.id);
|
||||
} else {
|
||||
NEWSBLUR.assets.mark_story_as_unstarred(this.id);
|
||||
}
|
||||
},
|
||||
|
||||
change_user_tags: function(tags, options, etc) {
|
||||
NEWSBLUR.assets.mark_story_as_starred(this.id);
|
||||
},
|
||||
|
||||
existing_tags: function() {
|
||||
var tags = this.get('user_tags');
|
||||
|
||||
if (!tags) {
|
||||
tags = this.folder_tags();
|
||||
}
|
||||
|
||||
return tags || [];
|
||||
},
|
||||
|
||||
unused_story_tags: function() {
|
||||
var tags = _.reduce(this.get('user_tags') || [], function(m, t) {
|
||||
return _.without(m, t);
|
||||
}, this.get('story_tags'));
|
||||
return tags;
|
||||
},
|
||||
|
||||
folder_tags: function() {
|
||||
var folder_tags = [];
|
||||
var feed_id = this.get('story_feed_id');
|
||||
var feed = NEWSBLUR.assets.get_feed(feed_id);
|
||||
if (feed) {
|
||||
folder_tags = feed.parent_folder_names();
|
||||
}
|
||||
return folder_tags;
|
||||
},
|
||||
|
||||
all_tags: function() {
|
||||
var tags = [];
|
||||
var story_tags = this.get('story_tags') || [];
|
||||
var user_tags = this.get('user_tags') || [];
|
||||
var folder_tags = this.folder_tags();
|
||||
var existing_tags = NEWSBLUR.assets.starred_feeds.all_tags();
|
||||
var all_tags = _.unique(_.compact(_.reduce([
|
||||
story_tags, user_tags, folder_tags, existing_tags
|
||||
], function(x, m) {
|
||||
return m.concat(x);
|
||||
}, [])));
|
||||
|
||||
return all_tags;
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
$feed_lists: $('.NB-feedlists'),
|
||||
$feed_list: $('#feed_list'),
|
||||
$social_feeds: $('.NB-socialfeeds-folder'),
|
||||
$starred_feeds: $('.NB-starred-folder'),
|
||||
$story_titles: $('#story_titles'),
|
||||
$story_titles_header: $('.NB-story-titles-header'),
|
||||
$content_pane: $('.content-pane'),
|
||||
|
@ -224,8 +225,10 @@
|
|||
if ((north && north.width() < 640) ||
|
||||
(content_width && content_width < 780)) {
|
||||
$windows.addClass('NB-narrow');
|
||||
this.flags.narrow_content = true;
|
||||
} else {
|
||||
$windows.removeClass('NB-narrow');
|
||||
this.flags.narrow_content = false;
|
||||
}
|
||||
|
||||
this.apply_tipsy_titles();
|
||||
|
@ -1101,6 +1104,7 @@
|
|||
'unread_threshold_temporarily': null,
|
||||
'river_view': false,
|
||||
'social_view': false,
|
||||
'starred_view': false,
|
||||
'select_story_in_feed': null,
|
||||
'global_blurblogs': false,
|
||||
'reloading_feeds': false
|
||||
|
@ -1129,7 +1133,7 @@
|
|||
});
|
||||
|
||||
if (_.isUndefined(options.search)) {
|
||||
delete this.flags.search;
|
||||
this.flags.search = "";
|
||||
this.flags.searching = false;
|
||||
}
|
||||
this.model.flags['no_more_stories'] = false;
|
||||
|
@ -1140,6 +1144,7 @@
|
|||
this.$s.$river_global_header.removeClass('NB-selected');
|
||||
this.$s.$tryfeed_header.removeClass('NB-selected');
|
||||
this.model.feeds.deselect();
|
||||
this.model.starred_feeds.deselect();
|
||||
if (_.string.contains(this.active_feed, 'social:')) {
|
||||
this.model.social_feeds.deselect();
|
||||
}
|
||||
|
@ -1178,7 +1183,10 @@
|
|||
reload_feed: function(options) {
|
||||
options = options || {};
|
||||
|
||||
if (this.active_feed == 'starred') {
|
||||
if (this.flags['starred_view'] && this.flags['starred_tag']) {
|
||||
options['tag'] = this.flags['starred_tag'];
|
||||
this.open_starred_stories(options);
|
||||
} else if (this.flags['starred_view']) {
|
||||
this.open_starred_stories(options);
|
||||
} else if (this.flags['social_view'] &&
|
||||
this.active_feed == 'river:blurblogs') {
|
||||
|
@ -1407,7 +1415,7 @@
|
|||
$list.removeClass('NB-active');
|
||||
}
|
||||
|
||||
if (feed_id == 'starred') {
|
||||
if (this.flags['starred_view']) {
|
||||
$page_tab.addClass('NB-disabled');
|
||||
}
|
||||
|
||||
|
@ -1487,13 +1495,30 @@
|
|||
|
||||
this.reset_feed(options);
|
||||
this.hide_splash_page();
|
||||
this.active_feed = 'starred';
|
||||
if (options.story_id) {
|
||||
this.flags['select_story_in_feed'] = options.story_id;
|
||||
}
|
||||
|
||||
this.iframe_scroll = null;
|
||||
this.$s.$starred_header.addClass('NB-selected');
|
||||
if (options.tag && !options.model) {
|
||||
var model = NEWSBLUR.assets.starred_feeds.detect(function(feed) {
|
||||
return feed.tag_slug() == options.tag || feed.get('tag') == options.tag;
|
||||
});
|
||||
if (model) {
|
||||
options.model = model;
|
||||
options.tag = model.get('tag');
|
||||
}
|
||||
}
|
||||
if (options.tag) {
|
||||
this.active_feed = options.model.id;
|
||||
this.flags['starred_tag'] = options.model.get('tag');
|
||||
options.model.set('selected', true);
|
||||
} else {
|
||||
this.active_feed = 'starred';
|
||||
this.$s.$starred_header.addClass('NB-selected');
|
||||
this.flags['starred_tag'] = null;
|
||||
}
|
||||
this.flags['starred_view'] = true;
|
||||
this.$s.$body.addClass('NB-view-river');
|
||||
this.flags.river_view = true;
|
||||
$('.task_view_page', this.$s.$taskbar).addClass('NB-disabled');
|
||||
|
@ -1512,12 +1537,24 @@
|
|||
}
|
||||
NEWSBLUR.app.taskbar_info.hide_stories_error();
|
||||
|
||||
this.model.fetch_starred_stories(1, _.bind(this.post_open_starred_stories, this),
|
||||
this.model.fetch_starred_stories(1, this.flags['starred_tag'], _.bind(this.post_open_starred_stories, this),
|
||||
NEWSBLUR.app.taskbar_info.show_stories_error, true);
|
||||
|
||||
if (!options.silent) {
|
||||
var url = "/saved";
|
||||
if (options.model) {
|
||||
url += "/" + options.model.tag_slug();
|
||||
}
|
||||
if (window.location.pathname != url) {
|
||||
NEWSBLUR.log(["Navigating to url", url]);
|
||||
NEWSBLUR.router.navigate(url);
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
post_open_starred_stories: function(data, first_load) {
|
||||
if (this.active_feed == 'starred') {
|
||||
if (this.flags['starred_view']) {
|
||||
// NEWSBLUR.log(['post_open_starred_stories', data.stories.length, first_load]);
|
||||
this.flags['opening_feed'] = false;
|
||||
if (this.counts['select_story_in_feed'] || this.flags['select_story_in_feed']) {
|
||||
|
@ -2279,8 +2316,8 @@
|
|||
NEWSBLUR.app.story_titles.show_loading(options);
|
||||
}
|
||||
|
||||
if (this.active_feed == 'starred') {
|
||||
this.model.fetch_starred_stories(this.counts['page'], _.bind(this.post_open_starred_stories, this),
|
||||
if (this.flags['starred_view']) {
|
||||
this.model.fetch_starred_stories(this.counts['page'], this.flags['starred_tag'], _.bind(this.post_open_starred_stories, this),
|
||||
NEWSBLUR.app.taskbar_info.show_stories_error, false);
|
||||
} else if (this.flags['social_view'] && _.contains(['river:blurblogs', 'river:global'], this.active_feed)) {
|
||||
this.model.fetch_river_blurblogs_stories(this.active_feed,
|
||||
|
@ -2936,6 +2973,16 @@
|
|||
if (feed_id && unread_count == 0) {
|
||||
$('.NB-menu-manage-feed-mark-read', $manage_menu).addClass('NB-disabled');
|
||||
}
|
||||
} else if (type == 'starred') {
|
||||
$manage_menu = $.make('ul', { className: 'NB-menu-manage NB-menu-manage-feed' }, [
|
||||
$.make('li', { className: 'NB-menu-separator-inverse' }),
|
||||
$.make('li', { className: 'NB-menu-item NB-menu-manage-feed-settings' }, [
|
||||
$.make('div', { className: 'NB-menu-manage-image' }),
|
||||
$.make('div', { className: 'NB-menu-manage-title' }, 'Tag settings')
|
||||
])
|
||||
]);
|
||||
$manage_menu.data('feed_id', feed_id);
|
||||
$manage_menu.data('$feed', $item);
|
||||
} else if (type == 'folder') {
|
||||
$manage_menu = $.make('ul', { className: 'NB-menu-manage NB-menu-manage-folder' }, [
|
||||
$.make('li', { className: 'NB-menu-separator-inverse' }),
|
||||
|
@ -3198,6 +3245,9 @@
|
|||
} else if (type == 'socialfeed') {
|
||||
feed_id = options.feed_id;
|
||||
inverse = options.inverse || $item.hasClass("NB-hover-inverse");
|
||||
} else if (type == 'starred') {
|
||||
feed_id = options.feed_id;
|
||||
inverse = options.inverse || $item.hasClass("NB-hover-inverse");
|
||||
} else if (type == 'story') {
|
||||
story_id = options.story_id;
|
||||
if ($item.hasClass('NB-hover-inverse')) inverse = true;
|
||||
|
@ -3244,15 +3294,16 @@
|
|||
$manage_menu_container.css('z-index', $("#simplemodal-container").css('z-index'));
|
||||
}
|
||||
$('.NB-task-manage').addClass('NB-hover');
|
||||
} else if (type == 'feed' || type == 'folder' || type == 'story' || type == 'socialfeed') {
|
||||
} else if (type == 'feed' || type == 'folder' || type == 'story' ||
|
||||
type == 'socialfeed' || type == 'starred') {
|
||||
var left, top;
|
||||
NEWSBLUR.log(['menu open', $item, inverse, toplevel, type]);
|
||||
// NEWSBLUR.log(['menu open', $item, inverse, toplevel, type]);
|
||||
if (inverse) {
|
||||
var $align = $item;
|
||||
if (type == 'feed') {
|
||||
left = toplevel ? 2 : -22;
|
||||
top = toplevel ? 1 : 3;
|
||||
} else if (type == 'socialfeed') {
|
||||
} else if (type == 'socialfeed' || type == 'starred') {
|
||||
left = 2;
|
||||
top = 2;
|
||||
} else if (type == 'folder') {
|
||||
|
@ -3282,7 +3333,7 @@
|
|||
left = toplevel ? 0 : -2;
|
||||
top = toplevel ? 20 : 19;
|
||||
$align = $('.NB-feedlist-manage-icon', $item);
|
||||
} else if (type == 'socialfeed') {
|
||||
} else if (type == 'socialfeed' || type == 'starred') {
|
||||
left = toplevel ? 0 : -18;
|
||||
top = toplevel ? 20 : 21;
|
||||
$align = $('.NB-feedlist-manage-icon', $item);
|
||||
|
@ -3307,7 +3358,8 @@
|
|||
$manage_menu_container.stop().css({'display': 'block', 'opacity': 1});
|
||||
|
||||
// Create and position the arrow tab
|
||||
if (type == 'feed' || type == 'folder' || type == 'story' || type == 'socialfeed') {
|
||||
if (type == 'feed' || type == 'folder' || type == 'story' ||
|
||||
type == 'socialfeed' || type == 'starred') {
|
||||
var $arrow = $.make('div', { className: 'NB-menu-manage-arrow' }, [
|
||||
$.make('div', { className: 'NB-icon' })
|
||||
]);
|
||||
|
@ -3371,7 +3423,7 @@
|
|||
// Hide menu on scroll.
|
||||
var $scroll;
|
||||
this.flags['feed_list_showing_manage_menu'] = true;
|
||||
if (type == 'feed' || type == 'socialfeed') {
|
||||
if (type == 'feed' || type == 'socialfeed' || type == 'starred') {
|
||||
$scroll = this.$s.$feed_list.parent();
|
||||
} else if (type == 'story') {
|
||||
$scroll = this.$s.$story_titles.add(this.$s.$feed_scroll);
|
||||
|
@ -3761,7 +3813,7 @@
|
|||
$.make('br'),
|
||||
unread_view >= 1 ? 'Switch to All or Unread.' : ""
|
||||
]);
|
||||
$(".NB-sidebar.NB-feedlists").prepend($empty);
|
||||
this.$s.$feed_list.after($empty);
|
||||
}
|
||||
// $focus.css('display', show_focus ? 'block' : 'none');
|
||||
// if (!show_focus) {
|
||||
|
@ -3806,8 +3858,7 @@
|
|||
NEWSBLUR.app.sidebar_header.toggle_hide_read_preference();
|
||||
NEWSBLUR.app.sidebar_header.count();
|
||||
NEWSBLUR.assets.folders.update_all_folder_visibility();
|
||||
NEWSBLUR.app.feed_list.scroll_to_show_selected_feed();
|
||||
NEWSBLUR.app.feed_list.scroll_to_show_selected_folder();
|
||||
NEWSBLUR.app.feed_list.scroll_to_selected();
|
||||
|
||||
$('.NB-active', $slider).removeClass('NB-active');
|
||||
if (real_value < 0) {
|
||||
|
@ -3890,7 +3941,7 @@
|
|||
feed_id = feed_id || this.active_feed;
|
||||
var feed = this.model.get_feed(feed_id);
|
||||
|
||||
if (feed_id == 'starred') {
|
||||
if (this.flags['starred_view']) {
|
||||
// Umm, no. Not yet.
|
||||
} else if (feed) {
|
||||
return feed.unread_counts();
|
||||
|
@ -5133,7 +5184,7 @@
|
|||
e.preventDefault();
|
||||
var story_id = $t.closest('.NB-menu-manage-story').data('story_id');
|
||||
var story = NEWSBLUR.assets.get_story(story_id);
|
||||
story.star_story();
|
||||
story.toggle_starred();
|
||||
});
|
||||
$.targetIs(e, { tagSelector: '.NB-menu-manage-feed-exception' }, function($t, $p){
|
||||
e.preventDefault();
|
||||
|
@ -5760,7 +5811,7 @@
|
|||
if (self.active_story) {
|
||||
var story_id = self.active_story.id;
|
||||
var story = NEWSBLUR.assets.get_story(story_id);
|
||||
story.star_story();
|
||||
story.toggle_starred();
|
||||
}
|
||||
});
|
||||
$document.bind('keypress', '+', function(e) {
|
||||
|
|
|
@ -45,7 +45,9 @@ _.extend(NEWSBLUR.ReaderFeedException.prototype, {
|
|||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
$(".NB-exception-option-page", this.$modal).toggle(this.feed.is_feed() || this.feed.is_social());
|
||||
$(".NB-view-setting-original", this.$modal).toggle(this.feed.is_feed() || this.feed.is_social());
|
||||
if (this.feed.get('exception_type')) {
|
||||
this.$modal.removeClass('NB-modal-feed-settings');
|
||||
} else {
|
||||
|
@ -56,10 +58,13 @@ _.extend(NEWSBLUR.ReaderFeedException.prototype, {
|
|||
},
|
||||
|
||||
get_feed_settings: function() {
|
||||
if (this.feed.is_starred()) return;
|
||||
|
||||
var $loading = $('.NB-modal-loading', this.$modal);
|
||||
$loading.addClass('NB-active');
|
||||
|
||||
var settings_fn = this.options.social_feed ? this.model.get_social_settings : this.model.get_feed_settings;
|
||||
var settings_fn = this.options.social_feed ? this.model.get_social_settings :
|
||||
this.model.get_feed_settings;
|
||||
settings_fn.call(this.model, this.feed_id, _.bind(this.populate_settings, this));
|
||||
},
|
||||
|
||||
|
@ -90,7 +95,7 @@ _.extend(NEWSBLUR.ReaderFeedException.prototype, {
|
|||
$.make('img', { className: 'NB-modal-feed-image feed_favicon', src: $.favicon(this.feed) }),
|
||||
$.make('div', { className: 'NB-modal-feed-heading' }, [
|
||||
$.make('span', { className: 'NB-modal-feed-title' }, this.feed.get('feed_title')),
|
||||
$.make('span', { className: 'NB-modal-feed-subscribers' },Inflector.pluralize(' subscriber', this.feed.get('num_subscribers'), true))
|
||||
(this.feed.get('num_subscribers') && $.make('span', { className: 'NB-modal-feed-subscribers' },Inflector.pluralize(' subscriber', this.feed.get('num_subscribers'), true)))
|
||||
])
|
||||
]),
|
||||
$.make('div', { className: 'NB-fieldset NB-exception-option NB-exception-option-view NB-modal-submit NB-settings-only' }, [
|
||||
|
@ -104,23 +109,33 @@ _.extend(NEWSBLUR.ReaderFeedException.prototype, {
|
|||
$.make('div', { className: 'NB-preference-label'}, [
|
||||
'Reading view'
|
||||
]),
|
||||
$.make('div', { className: 'NB-preference-options' }, [
|
||||
$.make('div', [
|
||||
$.make('input', { id: 'NB-preference-view-1', type: 'radio', name: 'view_settings', value: 'page' }),
|
||||
$.make('div', { className: 'NB-preference-options NB-view-settings' }, [
|
||||
$.make('div', { className: "NB-view-setting-original" }, [
|
||||
$.make('label', { 'for': 'NB-preference-view-1' }, [
|
||||
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL+'/img/reader/preferences_view_original.png' })
|
||||
$.make('input', { id: 'NB-preference-view-1', type: 'radio', name: 'view_settings', value: 'page' }),
|
||||
$.make("img", { src: NEWSBLUR.Globals.MEDIA_URL+'/img/icons/circular/nav_story_original_active.png' }),
|
||||
$.make("div", { className: "NB-view-title" }, "Original")
|
||||
])
|
||||
]),
|
||||
$.make('div', [
|
||||
$.make('input', { id: 'NB-preference-view-2', type: 'radio', name: 'view_settings', value: 'feed' }),
|
||||
$.make('label', { 'for': 'NB-preference-view-2' }, [
|
||||
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL+'/img/reader/preferences_view_feed.png' })
|
||||
$.make('input', { id: 'NB-preference-view-2', type: 'radio', name: 'view_settings', value: 'feed' }),
|
||||
$.make("img", { src: NEWSBLUR.Globals.MEDIA_URL+'/img/icons/circular/nav_story_feed_active.png' }),
|
||||
$.make("div", { className: "NB-view-title" }, "Feed")
|
||||
])
|
||||
]),
|
||||
$.make('div', [
|
||||
$.make('input', { id: 'NB-preference-view-3', type: 'radio', name: 'view_settings', value: 'story' }),
|
||||
$.make('label', { 'for': 'NB-preference-view-3' }, [
|
||||
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL+'/img/reader/preferences_view_story.png' })
|
||||
$.make('input', { id: 'NB-preference-view-3', type: 'radio', name: 'view_settings', value: 'text' }),
|
||||
$.make("img", { src: NEWSBLUR.Globals.MEDIA_URL+'/img/icons/circular/nav_story_text_active.png' }),
|
||||
$.make("div", { className: "NB-view-title" }, "Text")
|
||||
])
|
||||
]),
|
||||
$.make('div', [
|
||||
$.make('label', { 'for': 'NB-preference-view-4' }, [
|
||||
$.make('input', { id: 'NB-preference-view-4', type: 'radio', name: 'view_settings', value: 'story' }),
|
||||
$.make("img", { src: NEWSBLUR.Globals.MEDIA_URL+'/img/icons/circular/nav_story_story_active.png' }),
|
||||
$.make("div", { className: "NB-view-title" }, "Story")
|
||||
])
|
||||
])
|
||||
])
|
||||
|
@ -156,14 +171,14 @@ _.extend(NEWSBLUR.ReaderFeedException.prototype, {
|
|||
]),
|
||||
$.make('input', { type: 'text', id: 'NB-exception-input-address', className: 'NB-exception-input-address NB-input', name: 'feed_address', value: this.feed.get('feed_address') })
|
||||
]),
|
||||
(!this.options.social_feed && $.make('div', { className: 'NB-exception-submit-wrapper' }, [
|
||||
(this.feed.is_feed() && $.make('div', { className: 'NB-exception-submit-wrapper' }, [
|
||||
$.make('div', { className: 'NB-modal-submit-button NB-modal-submit-green NB-modal-submit-address' }, 'Parse this RSS/XML Feed'),
|
||||
$.make('div', { className: 'NB-error' }),
|
||||
$.make('div', { className: 'NB-exception-feed-history' })
|
||||
]))
|
||||
])
|
||||
]),
|
||||
$.make('div', { className: 'NB-fieldset NB-exception-option NB-exception-option-page NB-modal-submit' }, [
|
||||
($.make('div', { className: 'NB-fieldset NB-exception-option NB-exception-option-page NB-modal-submit' }, [
|
||||
$.make('h5', [
|
||||
$.make('div', { className: 'NB-exception-option-meta' }),
|
||||
$.make('span', { className: 'NB-exception-option-option NB-exception-only' }, 'Option 3:'),
|
||||
|
@ -178,13 +193,13 @@ _.extend(NEWSBLUR.ReaderFeedException.prototype, {
|
|||
]),
|
||||
$.make('input', { type: 'text', id: 'NB-exception-input-link', className: 'NB-exception-input-link NB-input', name: 'feed_link', value: this.feed.get('feed_link') })
|
||||
]),
|
||||
(!this.options.social_feed && $.make('div', { className: 'NB-exception-submit-wrapper' }, [
|
||||
(this.feed.is_feed() && $.make('div', { className: 'NB-exception-submit-wrapper' }, [
|
||||
$.make('div', { className: 'NB-modal-submit-button NB-modal-submit-green NB-modal-submit-link' }, 'Fetch Feed From Website'),
|
||||
$.make('div', { className: 'NB-error' }),
|
||||
$.make('div', { className: 'NB-exception-page-history' })
|
||||
]))
|
||||
])
|
||||
]),
|
||||
])),
|
||||
$.make('div', { className: 'NB-fieldset NB-exception-option NB-exception-option-delete NB-exception-block-only NB-modal-submit' }, [
|
||||
$.make('h5', [
|
||||
$.make('span', { className: 'NB-exception-option-option NB-exception-only' }, 'Option 4:'),
|
||||
|
|
|
@ -321,23 +321,33 @@ _.extend(NEWSBLUR.ReaderPreferences.prototype, {
|
|||
]),
|
||||
$.make('div', { className: 'NB-tab NB-tab-feeds' }, [
|
||||
$.make('div', { className: 'NB-preference NB-preference-view' }, [
|
||||
$.make('div', { className: 'NB-preference-options' }, [
|
||||
$.make('div', [
|
||||
$.make('input', { id: 'NB-preference-view-1', type: 'radio', name: 'default_view', value: 'page' }),
|
||||
$.make('div', { className: 'NB-preference-options NB-view-settings' }, [
|
||||
$.make('div', { className: "NB-view-setting-original" }, [
|
||||
$.make('label', { 'for': 'NB-preference-view-1' }, [
|
||||
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL+'/img/reader/preferences_view_original.png' })
|
||||
$.make('input', { id: 'NB-preference-view-1', type: 'radio', name: 'default_view', value: 'page' }),
|
||||
$.make("img", { src: NEWSBLUR.Globals.MEDIA_URL+'/img/icons/circular/nav_story_original_active.png' }),
|
||||
$.make("div", { className: "NB-view-title" }, "Original")
|
||||
])
|
||||
]),
|
||||
$.make('div', [
|
||||
$.make('input', { id: 'NB-preference-view-2', type: 'radio', name: 'default_view', value: 'feed' }),
|
||||
$.make('label', { 'for': 'NB-preference-view-2' }, [
|
||||
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL+'/img/reader/preferences_view_feed.png' })
|
||||
$.make('input', { id: 'NB-preference-view-2', type: 'radio', name: 'default_view', value: 'feed' }),
|
||||
$.make("img", { src: NEWSBLUR.Globals.MEDIA_URL+'/img/icons/circular/nav_story_feed_active.png' }),
|
||||
$.make("div", { className: "NB-view-title" }, "Feed")
|
||||
])
|
||||
]),
|
||||
$.make('div', [
|
||||
$.make('input', { id: 'NB-preference-view-3', type: 'radio', name: 'default_view', value: 'story' }),
|
||||
$.make('label', { 'for': 'NB-preference-view-3' }, [
|
||||
$.make('img', { src: NEWSBLUR.Globals.MEDIA_URL+'/img/reader/preferences_view_story.png' })
|
||||
$.make('input', { id: 'NB-preference-view-3', type: 'radio', name: 'default_view', value: 'text' }),
|
||||
$.make("img", { src: NEWSBLUR.Globals.MEDIA_URL+'/img/icons/circular/nav_story_text_active.png' }),
|
||||
$.make("div", { className: "NB-view-title" }, "Text")
|
||||
])
|
||||
]),
|
||||
$.make('div', [
|
||||
$.make('label', { 'for': 'NB-preference-view-4' }, [
|
||||
$.make('input', { id: 'NB-preference-view-4', type: 'radio', name: 'default_view', value: 'story' }),
|
||||
$.make("img", { src: NEWSBLUR.Globals.MEDIA_URL+'/img/icons/circular/nav_story_story_active.png' }),
|
||||
$.make("div", { className: "NB-view-title" }, "Story")
|
||||
])
|
||||
])
|
||||
]),
|
||||
|
|
|
@ -31,7 +31,7 @@ _.extend(NEWSBLUR.ReaderRecommendFeed.prototype, {
|
|||
|
||||
this.$modal = $.make('div', { className: 'NB-modal-recommend NB-modal' }, [
|
||||
$.make('div', { className: 'NB-modal-feed-chooser-container'}, [
|
||||
this.make_feed_chooser()
|
||||
this.make_feed_chooser({skip_starred: true, skip_social: true})
|
||||
]),
|
||||
$.make('div', { className: 'NB-modal-loading' }),
|
||||
$.make('h2', { className: 'NB-modal-title' }, [
|
||||
|
|
|
@ -34,7 +34,7 @@ _.extend(NEWSBLUR.ReaderStatistics.prototype, {
|
|||
|
||||
this.$modal = $.make('div', { className: 'NB-modal-statistics NB-modal' }, [
|
||||
$.make('div', { className: 'NB-modal-feed-chooser-container'}, [
|
||||
this.make_feed_chooser()
|
||||
this.make_feed_chooser({skip_starred: true})
|
||||
]),
|
||||
$.make('div', { className: 'NB-modal-loading' }),
|
||||
$.make('h2', { className: 'NB-modal-title' }, 'Statistics & History'),
|
||||
|
|
|
@ -71,11 +71,13 @@ NEWSBLUR.Views.SocialPageComments = Backbone.View.extend({
|
|||
format: "html"
|
||||
}, _.bind(function(template) {
|
||||
var $template = $($.trim(template));
|
||||
var $header = this.make('div', {
|
||||
"class": 'NB-story-comments-public-header-wrapper'
|
||||
}, this.make('div', {
|
||||
"class": 'NB-story-comments-public-header'
|
||||
}, Inflector.pluralize(' public comment', $('.NB-story-comment', $template).length, true)));
|
||||
var $header = $.make('div', {
|
||||
className: 'NB-story-comments-public-header-wrapper'
|
||||
}, [
|
||||
$.make('div', {
|
||||
className: 'NB-story-comments-public-header'
|
||||
}, Inflector.pluralize(' public comment', $('.NB-story-comment', $template).length, true))
|
||||
]);
|
||||
|
||||
this.$(".NB-story-comments-public-teaser-wrapper").replaceWith($template);
|
||||
$template.before($header);
|
||||
|
|
|
@ -32,8 +32,12 @@ NEWSBLUR.Views.FeedList = Backbone.View.extend({
|
|||
NEWSBLUR.assets.social_feeds.bind('reset', _.bind(function() {
|
||||
this.make_social_feeds();
|
||||
}, this));
|
||||
NEWSBLUR.assets.social_feeds.bind('change:selected', this.selected, this);
|
||||
NEWSBLUR.assets.feeds.bind('change:selected', this.selected, this);
|
||||
NEWSBLUR.assets.starred_feeds.bind('reset', _.bind(function(models, options) {
|
||||
this.make_starred_tags(options);
|
||||
}, this));
|
||||
NEWSBLUR.assets.social_feeds.bind('change:selected', this.scroll_to_selected, this);
|
||||
NEWSBLUR.assets.feeds.bind('change:selected', this.scroll_to_selected, this);
|
||||
NEWSBLUR.assets.starred_feeds.bind('change:selected', this.scroll_to_selected, this);
|
||||
if (!NEWSBLUR.assets.folders.size()) {
|
||||
NEWSBLUR.assets.load_feeds();
|
||||
}
|
||||
|
@ -106,7 +110,7 @@ NEWSBLUR.Views.FeedList = Backbone.View.extend({
|
|||
_.defer(_.bind(function() {
|
||||
NEWSBLUR.reader.open_dialog_after_feeds_loaded();
|
||||
NEWSBLUR.reader.toggle_focus_in_slider();
|
||||
this.selected();
|
||||
this.scroll_to_selected();
|
||||
if (NEWSBLUR.reader.socket) {
|
||||
NEWSBLUR.reader.send_socket_active_feeds();
|
||||
} else {
|
||||
|
@ -156,6 +160,37 @@ NEWSBLUR.Views.FeedList = Backbone.View.extend({
|
|||
$('.NB-module-stats-count-following .NB-module-stats-count-number').text(profile.get('following_count'));
|
||||
},
|
||||
|
||||
make_starred_tags: function(options) {
|
||||
options = options || {};
|
||||
var $starred_feeds = $('.NB-starred-feeds', this.$s.$starred_feeds);
|
||||
var $feeds = _.compact(NEWSBLUR.assets.starred_feeds.map(function(feed) {
|
||||
if (feed.get('tag') == "") return;
|
||||
var feed_view = new NEWSBLUR.Views.FeedTitleView({
|
||||
model: feed,
|
||||
type: 'feed',
|
||||
depth: 0,
|
||||
starred_tag: true
|
||||
}).render();
|
||||
feed.views.push(feed_view);
|
||||
return feed_view.el;
|
||||
}));
|
||||
|
||||
$starred_feeds.empty().css({
|
||||
'display': 'block',
|
||||
'opacity': options.update ? 1 : 0
|
||||
});
|
||||
$starred_feeds.html($feeds);
|
||||
if (NEWSBLUR.assets.starred_feeds.length) {
|
||||
$('.NB-feeds-header-starred-container').css({
|
||||
'display': 'block',
|
||||
'opacity': 0
|
||||
}).animate({'opacity': 1}, {'duration': options.update ? 0 : 700});
|
||||
}
|
||||
|
||||
var collapsed = NEWSBLUR.app.sidebar.check_starred_collapsed({skip_animation: true});
|
||||
$starred_feeds.animate({'opacity': 1}, {'duration': (collapsed || options.update) ? 0 : 700});
|
||||
},
|
||||
|
||||
load_router: function() {
|
||||
if (!NEWSBLUR.router) {
|
||||
NEWSBLUR.router = new NEWSBLUR.Router;
|
||||
|
@ -218,51 +253,25 @@ NEWSBLUR.Views.FeedList = Backbone.View.extend({
|
|||
// = Actions =
|
||||
// ===========
|
||||
|
||||
selected: function(model, value, options) {
|
||||
var feed_view;
|
||||
options = options || {};
|
||||
|
||||
if (!model) {
|
||||
model = NEWSBLUR.assets.feeds.selected() || NEWSBLUR.assets.social_feeds.selected();
|
||||
}
|
||||
if (!model || !model.get('selected')) return;
|
||||
|
||||
if (options.$feed) {
|
||||
feed_view = _.detect(model.views, function(view) {
|
||||
return view.el == options.$feed[0];
|
||||
});
|
||||
}
|
||||
if (!feed_view) {
|
||||
feed_view = _.detect(model.views, _.bind(function(view) {
|
||||
return !!view.$el.closest(this.$s.$feed_lists).length;
|
||||
}, this));
|
||||
}
|
||||
|
||||
if (feed_view) {
|
||||
_.defer(_.bind(function() {
|
||||
this.scroll_to_show_selected_feed(feed_view);
|
||||
}, this));
|
||||
}
|
||||
},
|
||||
|
||||
scroll_to_show_selected_feed: function(feed_view) {
|
||||
scroll_to_show_selected_feed: function() {
|
||||
var $feed_lists = this.$s.$feed_lists;
|
||||
if (!feed_view) {
|
||||
var model = NEWSBLUR.assets.feeds.selected() || NEWSBLUR.assets.social_feeds.selected();
|
||||
if (!model || !model.get('selected')) return;
|
||||
var feed_view = _.detect(model.views, _.bind(function(view) {
|
||||
return !!view.$el.closest(this.$s.$feed_lists).length;
|
||||
}, this));
|
||||
if (!feed_view) return;
|
||||
}
|
||||
var is_feed_visible = $feed_lists.isScrollVisible(feed_view.$el);
|
||||
|
||||
if (!is_feed_visible) {
|
||||
var model = NEWSBLUR.assets.feeds.selected() ||
|
||||
NEWSBLUR.assets.social_feeds.selected() ||
|
||||
NEWSBLUR.assets.starred_feeds.selected();
|
||||
if (!model) return;
|
||||
var feed_view = _.detect(model.views, _.bind(function(view) {
|
||||
return !!view.$el.closest(this.$s.$feed_lists).length;
|
||||
}, this));
|
||||
if (!feed_view) return;
|
||||
|
||||
if (!$feed_lists.isScrollVisible(feed_view.$el)) {
|
||||
var scroll = feed_view.$el.position().top;
|
||||
var container = $feed_lists.scrollTop();
|
||||
var height = $feed_lists.outerHeight();
|
||||
$feed_lists.scrollTop(scroll+container-height/5);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
scroll_to_show_highlighted_feed: function() {
|
||||
|
@ -281,31 +290,40 @@ NEWSBLUR.Views.FeedList = Backbone.View.extend({
|
|||
}
|
||||
},
|
||||
|
||||
scroll_to_show_selected_folder: function(folder_view) {
|
||||
scroll_to_show_selected_folder: function() {
|
||||
var $feed_lists = this.$s.$feed_lists;
|
||||
var $selected_view;
|
||||
|
||||
if (!folder_view) {
|
||||
var folder = NEWSBLUR.assets.folders.selected();
|
||||
if (!folder || !folder.get('selected')) return;
|
||||
folder_view = folder.folder_view;
|
||||
if (!folder_view) return;
|
||||
var folder = NEWSBLUR.assets.folders.selected();
|
||||
if (folder) {
|
||||
$selected_view = folder.folder_view.$el;
|
||||
$selected_view = $selected_view.find('.folder_title').eq(0);
|
||||
}
|
||||
|
||||
var $folder_title = folder_view.$el.find('.folder_title').eq(0);
|
||||
var is_folder_visible = $feed_lists.isScrollVisible($folder_title);
|
||||
// NEWSBLUR.log(["scroll_to_show_selected_folder", folder_view, folder_view.$el, $feed_lists, is_folder_visible]);
|
||||
|
||||
if (!$selected_view && NEWSBLUR.reader.active_feed == 'river:') {
|
||||
$selected_view = NEWSBLUR.reader.$s.$river_sites_header.closest(".NB-feeds-header-container");
|
||||
} else if (!$selected_view && NEWSBLUR.reader.active_feed == 'starred') {
|
||||
$selected_view = NEWSBLUR.reader.$s.$starred_header.closest(".NB-feeds-header-container");
|
||||
}
|
||||
if (!$selected_view) return;
|
||||
|
||||
var is_folder_visible = $feed_lists.isScrollVisible($selected_view);
|
||||
|
||||
if (!is_folder_visible) {
|
||||
var scroll = folder_view.$el.position().top;
|
||||
var scroll = $selected_view.position().top;
|
||||
var container = $feed_lists.scrollTop();
|
||||
var height = $feed_lists.outerHeight();
|
||||
$feed_lists.scrollTop(scroll+container-height/5);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
scroll_to_selected: function() {
|
||||
this.scroll_to_show_selected_feed();
|
||||
this.scroll_to_show_selected_folder();
|
||||
var found = this.scroll_to_show_selected_feed();
|
||||
if (!found) {
|
||||
this.scroll_to_show_selected_folder();
|
||||
}
|
||||
},
|
||||
|
||||
start_sorting: function() {
|
||||
|
|
|
@ -81,7 +81,7 @@ NEWSBLUR.Views.FeedSelector = Backbone.View.extend({
|
|||
});
|
||||
|
||||
var feeds = NEWSBLUR.assets.feeds.filter(function(feed){
|
||||
return _.string.contains(feed.get('feed_title').toLowerCase(), input) || feed.id == input;
|
||||
return _.string.contains(feed.get('feed_title') && feed.get('feed_title').toLowerCase(), input) || feed.id == input;
|
||||
});
|
||||
var socialsubs = NEWSBLUR.assets.social_feeds.filter(function(feed){
|
||||
return _.string.contains(feed.get('feed_title').toLowerCase(), input) ||
|
||||
|
|
|
@ -64,7 +64,7 @@ NEWSBLUR.Views.FeedTitleView = Backbone.View.extend({
|
|||
render: function() {
|
||||
var feed = this.model;
|
||||
var extra_classes = this.extra_classes();
|
||||
var $feed = $(_.template('<<%= list_type %> class="feed <% if (selected) { %>selected<% } %> <%= extra_classes %> <% if (toplevel) { %>NB-toplevel<% } %>" data-id="<%= feed.id %>">\
|
||||
var $feed = $(_.template('<<%= list_type %> class="feed <% if (selected) { %>selected<% } %> <%= extra_classes %> <% if (toplevel) { %>NB-toplevel<% } %> <% if (disable_hover) { %>NB-no-hover<% } %>" data-id="<%= feed.id %>">\
|
||||
<div class="feed_counts">\
|
||||
</div>\
|
||||
<% if (type == "story") { %>\
|
||||
|
@ -102,10 +102,11 @@ NEWSBLUR.Views.FeedTitleView = Backbone.View.extend({
|
|||
', {
|
||||
feed : feed,
|
||||
type : this.options.type,
|
||||
disable_hover : this.options.disable_hover,
|
||||
extra_classes : extra_classes,
|
||||
toplevel : this.options.depth == 0,
|
||||
list_type : this.options.type == 'feed' ? 'li' : 'div',
|
||||
selected : this.model.get('selected') || NEWSBLUR.reader.active_feed == this.model.id
|
||||
selected : this.model.get('selected')
|
||||
}));
|
||||
|
||||
if (this.options.type == 'story') {
|
||||
|
@ -263,6 +264,11 @@ NEWSBLUR.Views.FeedTitleView = Backbone.View.extend({
|
|||
NEWSBLUR.reader.open_feed_exception_modal(this.model.id);
|
||||
} else if (this.model.is_social()) {
|
||||
NEWSBLUR.reader.open_social_stories(this.model.id, {force: true, $feed: this.$el});
|
||||
} else if (this.model.is_starred()) {
|
||||
NEWSBLUR.reader.open_starred_stories({
|
||||
tag: this.model.tag_slug(),
|
||||
model: this.model
|
||||
});
|
||||
} else {
|
||||
NEWSBLUR.reader.open_feed(this.model.id, {$feed: this.$el});
|
||||
}
|
||||
|
@ -274,18 +280,22 @@ NEWSBLUR.Views.FeedTitleView = Backbone.View.extend({
|
|||
var dblclick_pref = NEWSBLUR.assets.preference('doubleclick_feed');
|
||||
if (dblclick_pref == "ignore") return;
|
||||
if (this.options.type == "story") return;
|
||||
if (this.options.starred_tag) return;
|
||||
if ($('.NB-modal-feedchooser').is(':visible')) return;
|
||||
|
||||
this.flags.double_click = true;
|
||||
_.delay(_.bind(function() {
|
||||
this.flags.double_click = false;
|
||||
}, this), 500);
|
||||
|
||||
|
||||
if (dblclick_pref == "open_and_read") {
|
||||
NEWSBLUR.reader.mark_feed_as_read(this.model.id);
|
||||
}
|
||||
window.open(this.model.get('feed_link'), '_blank');
|
||||
window.focus();
|
||||
|
||||
if (this.model.get('feed_link')) {
|
||||
window.open(this.model.get('feed_link'), '_blank');
|
||||
window.focus();
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
@ -297,11 +307,12 @@ NEWSBLUR.Views.FeedTitleView = Backbone.View.extend({
|
|||
},
|
||||
|
||||
mark_feed_as_read: function(e, days) {
|
||||
if (this.options.starred_tag) return;
|
||||
if (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
|
||||
this.flags.double_click = true;
|
||||
_.delay(_.bind(function() {
|
||||
this.flags.double_click = false;
|
||||
|
@ -359,11 +370,14 @@ NEWSBLUR.Views.FeedTitleView = Backbone.View.extend({
|
|||
|
||||
show_manage_menu: function(e) {
|
||||
if (this.options.feed_chooser) return;
|
||||
|
||||
|
||||
var feed_type = this.model.is_social() ? 'socialfeed' :
|
||||
this.model.is_starred() ? 'starred' :
|
||||
'feed';
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
NEWSBLUR.log(["showing manage menu", this.model.is_social() ? 'socialfeed' : 'feed', $(this.el), this, e.which, e.button]);
|
||||
NEWSBLUR.reader.show_manage_menu(this.model.is_social() ? 'socialfeed' : 'feed', this.$el, {
|
||||
|
||||
NEWSBLUR.reader.show_manage_menu(feed_type, this.$el, {
|
||||
feed_id: this.model.id,
|
||||
toplevel: this.options.depth == 0,
|
||||
rightclick: e.which >= 2
|
||||
|
|
|
@ -89,7 +89,7 @@ NEWSBLUR.Views.Folder = Backbone.View.extend({
|
|||
// console.log(["Not a feed or folder", item]);
|
||||
}
|
||||
}));
|
||||
$feeds.push(this.make('li', { 'class': 'feed NB-empty' }));
|
||||
$feeds.push($.make('li', { className: 'feed NB-empty' }));
|
||||
this.$('.folder').append($feeds);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ NEWSBLUR.Views.Sidebar = Backbone.View.extend({
|
|||
el: '.NB-sidebar',
|
||||
|
||||
events: {
|
||||
"click .NB-feeds-header-starred .NB-feedlist-collapse-icon": "collapse_starred_stories",
|
||||
"click .NB-feeds-header-starred": "open_starred_stories",
|
||||
"click .NB-feeds-header-river-sites": "open_river_stories",
|
||||
"click .NB-feeds-header-river-blurblogs .NB-feedlist-collapse-icon": "collapse_river_blurblog",
|
||||
|
@ -16,6 +17,37 @@ NEWSBLUR.Views.Sidebar = Backbone.View.extend({
|
|||
// = Actions =
|
||||
// ===========
|
||||
|
||||
check_starred_collapsed: function(options) {
|
||||
options = options || {};
|
||||
var collapsed = _.contains(NEWSBLUR.Preferences.collapsed_folders, 'starred');
|
||||
|
||||
if (collapsed) {
|
||||
this.show_collapsed_starred(options);
|
||||
}
|
||||
|
||||
return collapsed;
|
||||
},
|
||||
|
||||
show_collapsed_starred: function(options) {
|
||||
options = options || {};
|
||||
var $header = NEWSBLUR.reader.$s.$starred_header;
|
||||
var $folder = this.$('.NB-starred-folder');
|
||||
|
||||
$header.addClass('NB-folder-collapsed');
|
||||
|
||||
if (!options.skip_animation) {
|
||||
$header.addClass('NB-feedlist-folder-title-recently-collapsed');
|
||||
$header.one('mouseover', function() {
|
||||
$header.removeClass('NB-feedlist-folder-title-recently-collapsed');
|
||||
});
|
||||
} else {
|
||||
$folder.css({
|
||||
display: 'none',
|
||||
opacity: 0
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
check_river_blurblog_collapsed: function(options) {
|
||||
options = options || {};
|
||||
var show_folder_counts = NEWSBLUR.assets.preference('folder_counts');
|
||||
|
@ -151,6 +183,48 @@ NEWSBLUR.Views.Sidebar = Backbone.View.extend({
|
|||
return false;
|
||||
},
|
||||
|
||||
collapse_starred_stories: function(e, options) {
|
||||
e.stopPropagation();
|
||||
options = options || {};
|
||||
|
||||
var $header = NEWSBLUR.reader.$s.$starred_header;
|
||||
var $folder = this.$('.NB-starred-folder');
|
||||
|
||||
// Hiding / Collapsing
|
||||
if (options.force_collapse ||
|
||||
($folder.length &&
|
||||
$folder.eq(0).is(':visible'))) {
|
||||
NEWSBLUR.assets.collapsed_folders('starred', true);
|
||||
$header.addClass('NB-folder-collapsed');
|
||||
$folder.animate({'opacity': 0}, {
|
||||
'queue': false,
|
||||
'duration': options.force_collapse ? 0 : 200,
|
||||
'complete': _.bind(function() {
|
||||
this.show_collapsed_starred();
|
||||
$folder.slideUp({
|
||||
'duration': 270,
|
||||
'easing': 'easeOutQuart'
|
||||
});
|
||||
}, this)
|
||||
});
|
||||
}
|
||||
// Showing / Expanding
|
||||
else if ($folder.length &&
|
||||
(!$folder.eq(0).is(':visible'))) {
|
||||
NEWSBLUR.assets.collapsed_folders('starred', false);
|
||||
$header.removeClass('NB-folder-collapsed');
|
||||
$folder.css({'opacity': 0}).slideDown({
|
||||
'duration': 240,
|
||||
'easing': 'easeInOutCubic',
|
||||
'complete': function() {
|
||||
$folder.animate({'opacity': 1}, {'queue': false, 'duration': 200});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
open_river_blurblogs_stories: function() {
|
||||
return NEWSBLUR.reader.open_river_blurblogs_stories();
|
||||
},
|
||||
|
|
|
@ -129,6 +129,10 @@ NEWSBLUR.Views.StoryComment = Backbone.View.extend({
|
|||
NEWSBLUR.reader.open_social_profile_modal(this.model.get("user_id"));
|
||||
},
|
||||
|
||||
toggle_feed_story_save_dialog: function() {
|
||||
this.story.story_save_view.toggle_feed_story_save_dialog();
|
||||
},
|
||||
|
||||
toggle_feed_story_share_dialog: function() {
|
||||
this.story.story_share_view.toggle_feed_story_share_dialog();
|
||||
},
|
||||
|
|
|
@ -23,7 +23,7 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
|
|||
"click .NB-feed-story-tag" : "save_classifier",
|
||||
"click .NB-feed-story-author" : "save_classifier",
|
||||
"click .NB-feed-story-train" : "open_story_trainer",
|
||||
"click .NB-feed-story-save" : "star_story",
|
||||
"click .NB-feed-story-save" : "toggle_starred",
|
||||
"click .NB-story-comments-label" : "scroll_to_comments",
|
||||
"click .NB-story-content-expander" : "expand_story"
|
||||
},
|
||||
|
@ -33,7 +33,7 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
|
|||
this.model.bind('change', this.toggle_classes, this);
|
||||
this.model.bind('change:read_status', this.toggle_read_status, this);
|
||||
this.model.bind('change:selected', this.toggle_selected, this);
|
||||
this.model.bind('change:starred', this.toggle_starred, this);
|
||||
this.model.bind('change:starred', this.render_starred, this);
|
||||
this.model.bind('change:intelligence', this.render_header, this);
|
||||
this.model.bind('change:intelligence', this.toggle_intelligence, this);
|
||||
this.model.bind('change:shared', this.render_comments, this);
|
||||
|
@ -66,12 +66,15 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
|
|||
render: function() {
|
||||
var params = this.get_render_params();
|
||||
params['story_header'] = this.story_header_template(params);
|
||||
this.share_view = new NEWSBLUR.Views.StoryShareView({
|
||||
this.sideoptions_view = new NEWSBLUR.Views.StorySideoptionsView({
|
||||
model: this.model,
|
||||
el: this.el
|
||||
});
|
||||
this.save_view = this.sideoptions_view.save_view;
|
||||
this.share_view = this.sideoptions_view.share_view;
|
||||
|
||||
params['story_share_view'] = this.share_view.template({
|
||||
params['story_save_view'] = this.sideoptions_view.save_view.render();
|
||||
params['story_share_view'] = this.sideoptions_view.share_view.template({
|
||||
story: this.model,
|
||||
social_services: NEWSBLUR.assets.social_services,
|
||||
profile: NEWSBLUR.assets.user_profile
|
||||
|
@ -91,11 +94,24 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
|
|||
return this;
|
||||
},
|
||||
|
||||
render_starred_tags: function() {
|
||||
if (this.model.get('starred')) {
|
||||
this.save_view.toggle_feed_story_save_dialog();
|
||||
}
|
||||
},
|
||||
|
||||
resize_starred_tags: function() {
|
||||
if (this.model.get('starred')) {
|
||||
this.save_view.reset_height({immediate: true});
|
||||
}
|
||||
},
|
||||
|
||||
attach_handlers: function() {
|
||||
this.watch_images_for_story_height();
|
||||
this.attach_audio_handler();
|
||||
this.attach_syntax_highlighter_handler();
|
||||
this.attach_fitvid_handler();
|
||||
this.render_starred_tags();
|
||||
},
|
||||
|
||||
render_header: function(model, value, options) {
|
||||
|
@ -203,6 +219,7 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
|
|||
<div class="NB-sideoption-icon"> </div>\
|
||||
<div class="NB-sideoption-title"><%= story.get("starred") ? "Saved" : "Save this story" %></div>\
|
||||
</div>\
|
||||
<%= story_save_view %>\
|
||||
<div class="NB-sideoption NB-feed-story-share">\
|
||||
<div class="NB-sideoption-icon"> </div>\
|
||||
<div class="NB-sideoption-title"><%= story.get("shared") ? "Shared" : "Share this story" %></div>\
|
||||
|
@ -417,6 +434,10 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
|
|||
},
|
||||
|
||||
watch_images_for_story_height: function() {
|
||||
this.model.on('change:images_loaded', _.bind(function() {
|
||||
this.resize_starred_tags();
|
||||
}, this));
|
||||
|
||||
if (!this.is_truncatable()) return;
|
||||
|
||||
this.truncate_delay = 100;
|
||||
|
@ -498,7 +519,7 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
|
|||
});
|
||||
},
|
||||
|
||||
toggle_starred: function() {
|
||||
render_starred: function() {
|
||||
var story = this.model;
|
||||
var $sideoption_title = this.$('.NB-feed-story-save .NB-sideoption-title');
|
||||
|
||||
|
@ -713,8 +734,8 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
|
|||
NEWSBLUR.reader.open_story_trainer(this.model.id, feed_id, options);
|
||||
},
|
||||
|
||||
star_story: function() {
|
||||
this.model.star_story();
|
||||
toggle_starred: function() {
|
||||
this.model.toggle_starred();
|
||||
},
|
||||
|
||||
scroll_to_comments: function() {
|
||||
|
|
|
@ -34,7 +34,6 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
|
|||
feed_view_story_positions_keys: []
|
||||
};
|
||||
this.flags = {
|
||||
feed_view_images_loaded: {},
|
||||
mousemove_timeout: false
|
||||
};
|
||||
this.counts = {
|
||||
|
@ -224,21 +223,23 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
|
|||
|
||||
if (!NEWSBLUR.assets.stories.visible().length) {
|
||||
this.show_explainer_no_stories();
|
||||
return;
|
||||
// return;
|
||||
}
|
||||
|
||||
var pane_height = NEWSBLUR.reader.$s.$story_pane.height();
|
||||
var indicator_position = NEWSBLUR.assets.preference('lock_mouse_indicator');
|
||||
var endbar_height = 20;
|
||||
if (indicator_position) {
|
||||
if (indicator_position &&
|
||||
_.contains(['full', 'split'], NEWSBLUR.assets.preference('story_layout'))) {
|
||||
var last_visible_story = _.last(NEWSBLUR.assets.stories.visible());
|
||||
var last_story_height = last_visible_story && last_visible_story.story_view && last_visible_story.story_view.$el.height() || 100;
|
||||
var last_story_offset = _.last(this.cache.feed_view_story_positions_keys);
|
||||
var last_story_offset = _.last(this.cache.feed_view_story_positions_keys) || 0;
|
||||
endbar_height = pane_height - indicator_position - last_story_height;
|
||||
if (endbar_height <= 20) endbar_height = 20;
|
||||
|
||||
var empty_space = pane_height - last_story_offset - last_story_height - endbar_height;
|
||||
if (empty_space > 0) endbar_height += empty_space + 1;
|
||||
// console.log(["endbar height full/split", endbar_height, empty_space, pane_height, last_story_offset, last_story_height]);
|
||||
}
|
||||
|
||||
this.$('.NB-end-line').remove();
|
||||
|
@ -246,11 +247,13 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
|
|||
var last_story = NEWSBLUR.assets.stories.last();
|
||||
if (!last_story.get('selected')) return;
|
||||
}
|
||||
|
||||
endbar_height /= 2; // Splitting padding between top and bottom
|
||||
var $end_stories_line = $.make('div', {
|
||||
className: 'NB-end-line'
|
||||
}, [
|
||||
$.make('div', { className: 'NB-fleuron' })
|
||||
]).css('paddingBottom', endbar_height);
|
||||
]).css('paddingBottom', endbar_height).css('paddingTop', endbar_height);
|
||||
|
||||
this.$el.append($end_stories_line);
|
||||
},
|
||||
|
@ -377,15 +380,24 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
|
|||
},
|
||||
|
||||
append_river_premium_only_notification: function() {
|
||||
var $notice = $.make('div', { className: 'NB-feed-story-premium-only' }, [
|
||||
$.make('div', { className: 'NB-feed-story-premium-only-text'}, [
|
||||
'The full River of News is a ',
|
||||
var message = [
|
||||
'The full River of News is a ',
|
||||
$.make('a', { href: '#', className: 'NB-splash-link' }, 'premium feature'),
|
||||
'.'
|
||||
];
|
||||
if (NEWSBLUR.reader.flags['starred_view']) {
|
||||
message = [
|
||||
'Reading saved stories by tag is a ',
|
||||
$.make('a', { href: '#', className: 'NB-splash-link' }, 'premium feature'),
|
||||
'.'
|
||||
])
|
||||
];
|
||||
}
|
||||
var $notice = $.make('div', { className: 'NB-feed-story-premium-only' }, [
|
||||
$.make('div', { className: 'NB-feed-story-premium-only-text'}, message)
|
||||
]);
|
||||
this.$('.NB-feed-story-premium-only').remove();
|
||||
this.$(".NB-end-line").append($notice);
|
||||
console.log(["append_search_premium_only_notification", this.$(".NB-end-line")]);
|
||||
},
|
||||
|
||||
append_search_premium_only_notification: function() {
|
||||
|
@ -405,14 +417,17 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
|
|||
// =============
|
||||
|
||||
is_feed_loaded_for_location_fetch: function() {
|
||||
var images_begun = _.keys(this.flags.feed_view_images_loaded).length;
|
||||
var images_begun = NEWSBLUR.assets.stories.any(function(s) {
|
||||
return !_.isUndefined(s.get('images_loaded'));
|
||||
});
|
||||
if (images_begun) {
|
||||
var images_loaded = _.keys(this.flags.feed_view_images_loaded).length &&
|
||||
_.all(_.values(this.flags.feed_view_images_loaded), _.identity);
|
||||
return !!images_loaded;
|
||||
var images_loaded = NEWSBLUR.assets.stories.all(function(s) {
|
||||
return s.get('images_loaded');
|
||||
});
|
||||
return images_loaded;
|
||||
}
|
||||
|
||||
return !!images_begun;
|
||||
return images_begun;
|
||||
},
|
||||
|
||||
prefetch_story_locations_in_feed_view: function() {
|
||||
|
@ -420,7 +435,7 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
|
|||
var stories = NEWSBLUR.assets.stories;
|
||||
if (!_.contains(['split', 'full'], NEWSBLUR.assets.preference('story_layout'))) return;
|
||||
|
||||
// NEWSBLUR.log(['Prefetching Feed', this.flags['feed_view_positions_calculated'], this.flags.feed_view_images_loaded, (_.keys(this.flags.feed_view_images_loaded).length > 0 || this.cache.feed_view_story_positions_keys.length > 0), _.keys(this.flags.feed_view_images_loaded).length, _.values(this.flags.feed_view_images_loaded), this.is_feed_loaded_for_location_fetch()]);
|
||||
// NEWSBLUR.log(['Prefetching Feed', this.flags['feed_view_positions_calculated'], this.is_feed_loaded_for_location_fetch()]);
|
||||
|
||||
if (!NEWSBLUR.assets.stories.size()) return;
|
||||
|
||||
|
@ -451,7 +466,7 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
|
|||
if (this.is_feed_loaded_for_location_fetch()) {
|
||||
this.fetch_story_locations_in_feed_view({'reset_timer': true});
|
||||
} else {
|
||||
// NEWSBLUR.log(['Still loading feed view...', _.keys(this.flags.feed_view_images_loaded).length, this.cache.feed_view_story_positions_keys.length, this.flags.feed_view_images_loaded]);
|
||||
// NEWSBLUR.log(['Still loading feed view...', this.cache.feed_view_story_positions_keys.length]);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -540,17 +555,17 @@ NEWSBLUR.Views.StoryListView = Backbone.View.extend({
|
|||
var image_count = story.story_view.$('.NB-feed-story-content img').length;
|
||||
if (!image_count) {
|
||||
// NEWSBLUR.log(["No images", story.get('story_title')]);
|
||||
this.flags.feed_view_images_loaded[story.id] = true;
|
||||
} else if (!this.flags.feed_view_images_loaded[story.id]) {
|
||||
story.set('images_loaded', true);
|
||||
} else if (!story.get('images_loaded')) {
|
||||
// Progressively load the images in each story, so that when one story
|
||||
// loads, the position is calculated and the next story can calculate
|
||||
// its position (after its own images are loaded).
|
||||
this.flags.feed_view_images_loaded[story.id] = false;
|
||||
story.set('images_loaded', false);
|
||||
(function(story, image_count) {
|
||||
story.story_view.$('.NB-feed-story-content img').load(function() {
|
||||
// NEWSBLUR.log(['Loaded image', story.get('story_title'), image_count]);
|
||||
if (image_count <= 1) {
|
||||
NEWSBLUR.app.story_list.flags.feed_view_images_loaded[story.id] = true;
|
||||
story.set('images_loaded', true);
|
||||
} else {
|
||||
image_count--;
|
||||
}
|
||||
|
|
242
media/js/newsblur/views/story_save_view.js
Normal file
242
media/js/newsblur/views/story_save_view.js
Normal file
|
@ -0,0 +1,242 @@
|
|||
NEWSBLUR.Views.StorySaveView = Backbone.View.extend({
|
||||
|
||||
events: {
|
||||
"click .NB-sideoption-save-populate" : "populate_story_tags"
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
_.bindAll(this, 'toggle_feed_story_save_dialog');
|
||||
this.sideoptions_view = this.options.sideoptions_view;
|
||||
this.model.story_save_view = this;
|
||||
this.model.bind('change:starred', this.toggle_feed_story_save_dialog);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return this.template({
|
||||
story: this.model,
|
||||
tags: this.model.existing_tags(),
|
||||
story_tags: this.model.unused_story_tags(),
|
||||
social_services: NEWSBLUR.assets.social_services,
|
||||
profile: NEWSBLUR.assets.user_profile
|
||||
});
|
||||
},
|
||||
|
||||
template: _.template('\
|
||||
<div class="NB-sideoption-save-wrapper <% if (story.get("starred")) { %>NB-active<% } %>">\
|
||||
<div class="NB-sideoption-save">\
|
||||
<% if (story_tags.length) { %>\
|
||||
<div class="NB-sideoption-save-populate">\
|
||||
Add <%= Inflector.pluralize("story tag", story_tags.length, true) %>\
|
||||
</div>\
|
||||
<% } %>\
|
||||
<div class="NB-sideoption-save-icon"></div>\
|
||||
<div class="NB-sideoption-save-title">\
|
||||
Tags:\
|
||||
</div>\
|
||||
<ul class="NB-sideoption-save-tag">\
|
||||
<% _.each(tags, function(tag) { %>\
|
||||
<li><%= tag %></li>\
|
||||
<% }) %>\
|
||||
</ul>\
|
||||
</div>\
|
||||
</div>\
|
||||
'),
|
||||
|
||||
populate_story_tags: function() {
|
||||
var $populate = this.$('.NB-sideoption-save-populate');
|
||||
var $tag_input = this.$('.NB-sideoption-save-tag');
|
||||
var tags = this.model.get('story_tags');
|
||||
|
||||
$populate.fadeOut(500);
|
||||
_.each(tags, function(tag) {
|
||||
$tag_input.tagit('createTag', tag, null, true);
|
||||
});
|
||||
|
||||
this.toggle_feed_story_save_dialog({resize_open:true});
|
||||
this.save_tags();
|
||||
},
|
||||
|
||||
toggle_feed_story_save_dialog: function(options) {
|
||||
options = options || {};
|
||||
var self = this;
|
||||
var feed_id = this.model.get('story_feed_id');
|
||||
var $sideoption = this.$('.NB-sideoption.NB-feed-story-save');
|
||||
var $save_wrapper = this.$('.NB-sideoption-save-wrapper');
|
||||
var $tag_input = this.$('.NB-sideoption-save-tag');
|
||||
|
||||
if (options.close || !this.model.get('starred')) {
|
||||
// Close
|
||||
this.is_open = false;
|
||||
this.resize({close: true});
|
||||
} else {
|
||||
// Open/resize
|
||||
this.is_open = true;
|
||||
if (!options.resize_open) {
|
||||
this.$('.NB-error').remove();
|
||||
}
|
||||
$tag_input.tagit({
|
||||
fieldName: "tags",
|
||||
availableTags: this.model.all_tags(),
|
||||
autocomplete: {delay: 0, minLength: 0},
|
||||
showAutocompleteOnFocus: false,
|
||||
createTagOnBlur: false,
|
||||
removeConfirmation: true,
|
||||
caseSensitive: false,
|
||||
allowDuplicates: false,
|
||||
allowSpaces: true,
|
||||
readOnly: false,
|
||||
tagLimit: null,
|
||||
singleField: false,
|
||||
singleFieldDelimiter: ',',
|
||||
singleFieldNode: null,
|
||||
tabIndex: null,
|
||||
|
||||
afterTagAdded: function(event, options) {
|
||||
options = options || {};
|
||||
if (!options.duringInitialization) {
|
||||
self.resize({change_tag: true});
|
||||
self.save_tags();
|
||||
}
|
||||
},
|
||||
afterTagRemoved: function(event, duringInitialization) {
|
||||
options = options || {};
|
||||
if (!options.duringInitialization) {
|
||||
self.resize({change_tag: true});
|
||||
self.save_tags();
|
||||
}
|
||||
}
|
||||
});
|
||||
$tag_input.tagit('addClassAutocomplete', 'NB-tagging-autocomplete');
|
||||
|
||||
if (options.animate_scroll) {
|
||||
var $scroll_container = NEWSBLUR.reader.$s.$story_titles;
|
||||
if (_.contains(['split', 'full'], NEWSBLUR.assets.preference('story_layout'))) {
|
||||
$scroll_container = this.model.latest_story_detail_view.$el.parent();
|
||||
}
|
||||
$scroll_container.stop().scrollTo(this.$el, {
|
||||
duration: 600,
|
||||
queue: false,
|
||||
easing: 'easeInOutQuint',
|
||||
offset: this.model.latest_story_detail_view.$el.height() -
|
||||
$scroll_container.height()
|
||||
});
|
||||
}
|
||||
|
||||
this.resize(options);
|
||||
}
|
||||
},
|
||||
|
||||
resize: function(options) {
|
||||
options = options || {};
|
||||
var $sideoption_container = this.$('.NB-feed-story-sideoptions-container');
|
||||
var $save_wrapper = this.$('.NB-sideoption-save-wrapper');
|
||||
var $save_content = this.$('.NB-sideoption-save');
|
||||
var $story_content = this.$('.NB-feed-story-content,.NB-story-content');
|
||||
var $story_comments = this.$('.NB-feed-story-comments');
|
||||
var $sideoption = this.$('.NB-feed-story-save');
|
||||
var $tag_input = this.$('.NB-sideoption-save-tag');
|
||||
|
||||
var $save_clone = $save_wrapper.clone();
|
||||
$save_wrapper.after($save_clone.css({
|
||||
'height': options.close ? 0 : 'auto',
|
||||
'position': 'absolute',
|
||||
'visibility': 'hidden',
|
||||
'display': 'block'
|
||||
}));
|
||||
var sideoption_content_height = $save_clone.height();
|
||||
$save_clone.remove();
|
||||
var new_sideoptions_height = $sideoption_container.height() - $save_wrapper.height() + sideoption_content_height;
|
||||
if (!options.close) {
|
||||
$sideoption.addClass('NB-active');
|
||||
$save_wrapper.addClass('NB-active');
|
||||
}
|
||||
|
||||
if (!options.resize_open && !options.close && !options.change_tag) {
|
||||
$save_wrapper.css('height', '0px');
|
||||
}
|
||||
$save_wrapper.animate({
|
||||
'height': sideoption_content_height
|
||||
}, {
|
||||
'duration': options.immediate ? 0 : 350,
|
||||
'easing': 'easeInOutQuint',
|
||||
'queue': false,
|
||||
'complete': _.bind(function() {
|
||||
if ($tag_input.length == 1) {
|
||||
$tag_input.focus();
|
||||
}
|
||||
if (NEWSBLUR.app.story_list) {
|
||||
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
|
||||
}
|
||||
if (options.close) {
|
||||
$sideoption.removeClass('NB-active');
|
||||
$save_wrapper.removeClass('NB-active');
|
||||
}
|
||||
}, this)
|
||||
});
|
||||
|
||||
var sideoptions_height = $sideoption_container.height();
|
||||
var content_height = $story_content.height();
|
||||
var comments_height = $story_comments.height();
|
||||
var left_height = content_height + comments_height;
|
||||
var original_height = $story_content.data('original_height') || content_height;
|
||||
if (!NEWSBLUR.reader.flags.narrow_content &&
|
||||
!options.close && !options.force && new_sideoptions_height >= original_height) {
|
||||
// Sideoptions too big, embiggen left side
|
||||
$story_content.stop(true, true).animate({
|
||||
'height': new_sideoptions_height
|
||||
}, {
|
||||
'duration': 350,
|
||||
'easing': 'easeInOutQuint',
|
||||
'queue': false,
|
||||
'complete': function() {
|
||||
if (NEWSBLUR.app.story_list) {
|
||||
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!$story_content.data('original_height')) {
|
||||
$story_content.data('original_height', content_height);
|
||||
}
|
||||
} else if (!NEWSBLUR.reader.flags.narrow_content) {
|
||||
// Content is bigger, move content back to normal
|
||||
if ($story_content.data('original_height') && !this.sideoptions_view.share_view.is_open) {
|
||||
$story_content.stop(true, true).animate({
|
||||
'height': $story_content.data('original_height')
|
||||
}, {
|
||||
'duration': 300,
|
||||
'easing': 'easeInOutQuint',
|
||||
'queue': false,
|
||||
'complete': function() {
|
||||
if (NEWSBLUR.app.story_list) {
|
||||
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (this.sideoptions_view.share_view.is_open) {
|
||||
this.sideoptions_view.share_view.resize();
|
||||
}
|
||||
}
|
||||
|
||||
if (NEWSBLUR.app.story_list) {
|
||||
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
|
||||
}
|
||||
},
|
||||
|
||||
reset_height: function() {
|
||||
var $story_content = this.$('.NB-feed-story-content,.NB-story-content');
|
||||
|
||||
// Reset story content height to get an accurate height measurement.
|
||||
$story_content.stop(true, true).css('height', 'auto');
|
||||
$story_content.removeData('original_height');
|
||||
|
||||
this.resize({change_tag: true});
|
||||
},
|
||||
|
||||
save_tags: function() {
|
||||
var $tag_input = this.$('.NB-sideoption-save-tag');
|
||||
|
||||
var user_tags = $tag_input.tagit('assignedTags');
|
||||
this.model.set('user_tags', user_tags);
|
||||
}
|
||||
|
||||
});
|
|
@ -12,6 +12,7 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
|
|||
},
|
||||
|
||||
initialize: function() {
|
||||
this.sideoptions_view = this.options.sideoptions_view;
|
||||
this.model.story_share_view = this;
|
||||
},
|
||||
|
||||
|
@ -67,38 +68,11 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
|
|||
if (options.close ||
|
||||
($sideoption.hasClass('NB-active') && !options.resize_open)) {
|
||||
// Close
|
||||
$share.animate({
|
||||
'height': 0
|
||||
}, {
|
||||
'duration': 300,
|
||||
'easing': 'easeInOutQuint',
|
||||
'queue': false,
|
||||
'complete': _.bind(function() {
|
||||
this.$('.NB-error').remove();
|
||||
if (NEWSBLUR.app.story_list) {
|
||||
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
|
||||
}
|
||||
}, this)
|
||||
});
|
||||
$comment_input.blur();
|
||||
$sideoption.removeClass('NB-active');
|
||||
if ($story_content.data('original_height')) {
|
||||
$story_content.animate({
|
||||
'height': $story_content.data('original_height')
|
||||
}, {
|
||||
'duration': 300,
|
||||
'easing': 'easeInOutQuint',
|
||||
'queue': false,
|
||||
'complete': function() {
|
||||
if (NEWSBLUR.app.story_list) {
|
||||
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
|
||||
}
|
||||
}
|
||||
});
|
||||
$story_content.removeData('original_height');
|
||||
}
|
||||
this.is_open = false;
|
||||
this.resize({close: true});
|
||||
} else {
|
||||
// Open/resize
|
||||
this.is_open = true;
|
||||
if (!options.resize_open) {
|
||||
this.$('.NB-error').remove();
|
||||
}
|
||||
|
@ -108,14 +82,6 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
|
|||
$facebook_button.removeClass('NB-active');
|
||||
$appdotnet_button.removeClass('NB-active');
|
||||
this.update_share_button_label();
|
||||
|
||||
var $share_clone = $share.clone();
|
||||
var dialog_height = $share_clone.css({
|
||||
'height': 'auto',
|
||||
'position': 'absolute',
|
||||
'visibility': 'hidden'
|
||||
}).appendTo($share.parent()).height();
|
||||
$share_clone.remove();
|
||||
|
||||
if (options.animate_scroll) {
|
||||
var $scroll_container = NEWSBLUR.reader.$s.$story_titles;
|
||||
|
@ -130,61 +96,9 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
|
|||
$scroll_container.height()
|
||||
});
|
||||
}
|
||||
$share.animate({
|
||||
'height': dialog_height
|
||||
}, {
|
||||
'duration': options.immediate ? 0 : 350,
|
||||
'easing': 'easeInOutQuint',
|
||||
'queue': false,
|
||||
'complete': _.bind(function() {
|
||||
if ($comment_input.length == 1) {
|
||||
$comment_input.focus();
|
||||
}
|
||||
if (NEWSBLUR.app.story_list) {
|
||||
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
|
||||
}
|
||||
|
||||
}, this)
|
||||
});
|
||||
|
||||
var sideoptions_height = $sideoption_container.outerHeight(true);
|
||||
var wrapper_height = $story_wrapper.height();
|
||||
var content_height = $story_content.height();
|
||||
var content_outerheight = $story_content.outerHeight(true);
|
||||
var comments_height = $story_comments.outerHeight(true);
|
||||
var container_offset = $sideoption_container.length &&
|
||||
($sideoption_container.position().top - 32);
|
||||
|
||||
if (content_outerheight + comments_height < sideoptions_height) {
|
||||
$story_content.css('height', $sideoption_container.height());
|
||||
$story_content.animate({
|
||||
'height': sideoptions_height + dialog_height - comments_height
|
||||
}, {
|
||||
'duration': 350,
|
||||
'easing': 'easeInOutQuint',
|
||||
'queue': false,
|
||||
'complete': function() {
|
||||
if (NEWSBLUR.app.story_list) {
|
||||
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
|
||||
}
|
||||
}
|
||||
}).data('original_height', content_height);
|
||||
} else if (sideoptions_height + dialog_height > wrapper_height) {
|
||||
$story_content.animate({
|
||||
'height': content_height + dialog_height - container_offset
|
||||
}, {
|
||||
'duration': 350,
|
||||
'easing': 'easeInOutQuint',
|
||||
'queue': false,
|
||||
'complete': function() {
|
||||
if (NEWSBLUR.app.story_list) {
|
||||
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
|
||||
}
|
||||
}
|
||||
}).data('original_height', content_height);
|
||||
} else if (NEWSBLUR.app.story_list) {
|
||||
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
|
||||
}
|
||||
this.resize(options);
|
||||
|
||||
var share = _.bind(function(e) {
|
||||
e.preventDefault();
|
||||
this.mark_story_as_shared({'source': 'sideoption'});
|
||||
|
@ -196,6 +110,99 @@ NEWSBLUR.Views.StoryShareView = Backbone.View.extend({
|
|||
}
|
||||
},
|
||||
|
||||
resize: function(options) {
|
||||
options = options || {};
|
||||
var $sideoption_container = this.$('.NB-feed-story-sideoptions-container');
|
||||
var $share_wrapper = this.$('.NB-sideoption-share-wrapper');
|
||||
var $share_content = this.$('.NB-sideoption-share');
|
||||
var $story_content = this.$('.NB-feed-story-content,.NB-story-content');
|
||||
var $story_comments = this.$('.NB-feed-story-comments');
|
||||
var $sideoption = this.$('.NB-sideoption.NB-feed-story-share');
|
||||
|
||||
var $share_clone = $share_wrapper.clone();
|
||||
$share_wrapper.after($share_clone.css({
|
||||
'height': options.close ? 0 : 'auto',
|
||||
'position': 'absolute',
|
||||
'visibility': 'hidden',
|
||||
'display': 'block'
|
||||
}));
|
||||
var sideoption_content_height = $share_clone.height();
|
||||
$share_clone.remove();
|
||||
var new_sideoptions_height = $sideoption_container.height() - $share_wrapper.height() + sideoption_content_height;
|
||||
|
||||
if (!options.close) {
|
||||
$share_wrapper.addClass('NB-active');
|
||||
$sideoption.addClass('NB-active');
|
||||
}
|
||||
|
||||
$share_wrapper.animate({
|
||||
'height': sideoption_content_height
|
||||
}, {
|
||||
'duration': options.immediate ? 0 : 350,
|
||||
'easing': 'easeInOutQuint',
|
||||
'queue': false,
|
||||
'complete': _.bind(function() {
|
||||
if (NEWSBLUR.app.story_list) {
|
||||
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
|
||||
}
|
||||
if (options.close) {
|
||||
$share_wrapper.removeClass('NB-active');
|
||||
$sideoption.removeClass('NB-active');
|
||||
}
|
||||
}, this)
|
||||
});
|
||||
|
||||
var sideoptions_height = $sideoption_container.height();
|
||||
var content_height = $story_content.height();
|
||||
var comments_height = $story_comments.height();
|
||||
var left_height = content_height + comments_height;
|
||||
var original_height = $story_content.data('original_height') || content_height;
|
||||
|
||||
if (!NEWSBLUR.reader.flags.narrow_content &&
|
||||
!options.close && new_sideoptions_height >= original_height) {
|
||||
// Sideoptions too big, embiggen left side
|
||||
$story_content.animate({
|
||||
'height': new_sideoptions_height
|
||||
}, {
|
||||
'duration': 350,
|
||||
'easing': 'easeInOutQuint',
|
||||
'queue': false,
|
||||
'complete': function() {
|
||||
if (NEWSBLUR.app.story_list) {
|
||||
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!$story_content.data('original_height')) {
|
||||
$story_content.data('original_height', content_height);
|
||||
}
|
||||
} else if (!NEWSBLUR.reader.flags.narrow_content) {
|
||||
// Content is bigger, move content back to normal
|
||||
if ($story_content.data('original_height') && !this.sideoptions_view.save_view.is_open) {
|
||||
$story_content.animate({
|
||||
'height': $story_content.data('original_height')
|
||||
}, {
|
||||
'duration': 300,
|
||||
'easing': 'easeInOutQuint',
|
||||
'queue': false,
|
||||
'complete': function() {
|
||||
if (NEWSBLUR.app.story_list) {
|
||||
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (this.sideoptions_view &&
|
||||
this.sideoptions_view.save_view &&
|
||||
this.sideoptions_view.save_view.is_open) {
|
||||
this.sideoptions_view.save_view.resize();
|
||||
}
|
||||
}
|
||||
|
||||
if (NEWSBLUR.app.story_list) {
|
||||
NEWSBLUR.app.story_list.fetch_story_locations_in_feed_view();
|
||||
}
|
||||
},
|
||||
|
||||
mark_story_as_shared: function(options) {
|
||||
options = options || {};
|
||||
var $share_button = this.$('.NB-sideoption-share-save');
|
||||
|
|
16
media/js/newsblur/views/story_sideoptions_view.js
Normal file
16
media/js/newsblur/views/story_sideoptions_view.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
NEWSBLUR.Views.StorySideoptionsView = Backbone.View.extend({
|
||||
|
||||
initialize: function() {
|
||||
this.save_view = new NEWSBLUR.Views.StorySaveView({
|
||||
model: this.model,
|
||||
el: this.el,
|
||||
sideoptions_view: this
|
||||
});
|
||||
this.share_view = new NEWSBLUR.Views.StoryShareView({
|
||||
model: this.model,
|
||||
el: this.el,
|
||||
sideoptions_view: this
|
||||
});
|
||||
}
|
||||
|
||||
});
|
|
@ -76,6 +76,7 @@ NEWSBLUR.Views.StoryTitleView = Backbone.View.extend({
|
|||
});
|
||||
this.text_view.fetch_and_render(this.model, temporary_text);
|
||||
this.$(".NB-story-detail").html(this.text_view.$el);
|
||||
this.story_detail.render_starred_tags();
|
||||
} else {
|
||||
this.story_detail = new NEWSBLUR.Views.StoryDetailView({
|
||||
model: this.model,
|
||||
|
|
|
@ -32,18 +32,22 @@ NEWSBLUR.Views.StoryTitlesHeader = Backbone.View.extend({
|
|||
NEWSBLUR.reader.active_folder &&
|
||||
(NEWSBLUR.reader.active_folder.get('fake') || !NEWSBLUR.reader.active_folder.get('folder_title'));
|
||||
|
||||
if (NEWSBLUR.reader.active_feed == 'starred') {
|
||||
if (NEWSBLUR.reader.flags['starred_view']) {
|
||||
$view = $(_.template('\
|
||||
<div class="NB-folder NB-no-hover">\
|
||||
<div class="NB-search-container"></div>\
|
||||
<div class="NB-starred-icon"></div>\
|
||||
<div class="NB-feedlist-manage-icon"></div>\
|
||||
<div class="folder_title_text">Saved Stories</div>\
|
||||
<span class="folder_title_text">Saved Stories<% if (tag) { %> - <%= tag %><% } %></span>\
|
||||
</div>\
|
||||
', {}));
|
||||
', {
|
||||
tag: NEWSBLUR.reader.flags['starred_tag']
|
||||
}));
|
||||
this.search_view = new NEWSBLUR.Views.FeedSearchView({
|
||||
feedbar_view: this
|
||||
}).render();
|
||||
$view.prepend(this.search_view.$el);
|
||||
this.search_view.blur_search();
|
||||
$(".NB-search-container", $view).html(this.search_view.$el);
|
||||
} else if (this.showing_fake_folder) {
|
||||
$view = $(_.template('\
|
||||
<div class="NB-folder NB-no-hover">\
|
||||
|
@ -128,7 +132,8 @@ NEWSBLUR.Views.StoryTitlesHeader = Backbone.View.extend({
|
|||
var $indicator = this.$('.NB-story-title-indicator');
|
||||
var unread_hidden_stories;
|
||||
if (NEWSBLUR.reader.flags['river_view']) {
|
||||
unread_hidden_stories = NEWSBLUR.reader.active_folder.folders &&
|
||||
unread_hidden_stories = NEWSBLUR.reader.active_folder &&
|
||||
NEWSBLUR.reader.active_folder.folders &&
|
||||
NEWSBLUR.reader.active_folder.folders.unread_counts &&
|
||||
NEWSBLUR.reader.active_folder.folders.unread_counts().ng;
|
||||
} else {
|
||||
|
|
|
@ -67,12 +67,20 @@ NEWSBLUR.Views.StoryTitlesView = Backbone.View.extend({
|
|||
},
|
||||
|
||||
append_river_premium_only_notification: function() {
|
||||
var $notice = $.make('div', { className: 'NB-feed-story-premium-only' }, [
|
||||
$.make('div', { className: 'NB-feed-story-premium-only-text'}, [
|
||||
'The full River of News is a ',
|
||||
var message = [
|
||||
'The full River of News is a ',
|
||||
$.make('a', { href: '#', className: 'NB-splash-link' }, 'premium feature'),
|
||||
'.'
|
||||
];
|
||||
if (NEWSBLUR.reader.flags['starred_view']) {
|
||||
message = [
|
||||
'Reading saved stories by tag is a ',
|
||||
$.make('a', { href: '#', className: 'NB-splash-link' }, 'premium feature'),
|
||||
'.'
|
||||
])
|
||||
];
|
||||
}
|
||||
var $notice = $.make('div', { className: 'NB-feed-story-premium-only' }, [
|
||||
$.make('div', { className: 'NB-feed-story-premium-only-text'}, message)
|
||||
]);
|
||||
this.$('.NB-feed-story-premium-only').remove();
|
||||
this.$(".NB-end-line").append($notice);
|
||||
|
@ -193,14 +201,17 @@ NEWSBLUR.Views.StoryTitlesView = Backbone.View.extend({
|
|||
if (NEWSBLUR.assets.preference('story_layout') == 'list') {
|
||||
var pane_height = NEWSBLUR.reader.$s.$story_titles.height();
|
||||
var endbar_height = 20;
|
||||
var last_story_height = 100;
|
||||
var last_story_height = 280;
|
||||
endbar_height = pane_height - last_story_height;
|
||||
if (endbar_height <= 20) endbar_height = 20;
|
||||
|
||||
var empty_space = pane_height - last_story_height - endbar_height;
|
||||
if (empty_space > 0) endbar_height += empty_space + 1;
|
||||
|
||||
|
||||
endbar_height /= 2; // Splitting padding between top and bottom
|
||||
$end_stories_line.css('paddingBottom', endbar_height);
|
||||
$end_stories_line.css('paddingTop', endbar_height);
|
||||
// console.log(["endbar height list", endbar_height, empty_space, pane_height, last_story_height]);
|
||||
}
|
||||
|
||||
this.$el.append($end_stories_line);
|
||||
|
|
File diff suppressed because it is too large
Load diff
3
media/js/vendor/jquery.newsblur.js
vendored
3
media/js/vendor/jquery.newsblur.js
vendored
|
@ -257,7 +257,8 @@ NEWSBLUR.log = function(msg) {
|
|||
else if (empty_on_missing) return 'data:image/png;base64,R0lGODlhAQABAIAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
|
||||
else if (_.isNumber(feed.id)) return NEWSBLUR.URLs.favicon.replace('{id}', feed.id);
|
||||
else if (feed.get('favicon_url')) return feed.get('favicon_url');
|
||||
return NEWSBLUR.Globals.MEDIA_URL + '/img/silk/circular/world.png';
|
||||
else if (feed.is_starred()) return NEWSBLUR.Globals.MEDIA_URL + '/img/reader/tag.png';
|
||||
return NEWSBLUR.Globals.MEDIA_URL + '/img/icons/circular/world.png';
|
||||
},
|
||||
|
||||
deepCopy: function(obj) {
|
||||
|
|
557
media/js/vendor/tag-it.js
vendored
Executable file
557
media/js/vendor/tag-it.js
vendored
Executable file
|
@ -0,0 +1,557 @@
|
|||
/*
|
||||
* jQuery UI Tag-it!
|
||||
*
|
||||
* @version v2.0 (06/2011)
|
||||
*
|
||||
* Copyright 2011, Levy Carneiro Jr.
|
||||
* Released under the MIT license.
|
||||
* http://aehlke.github.com/tag-it/LICENSE
|
||||
*
|
||||
* Homepage:
|
||||
* http://aehlke.github.com/tag-it/
|
||||
*
|
||||
* Authors:
|
||||
* Levy Carneiro Jr.
|
||||
* Martin Rehfeld
|
||||
* Tobias Schmidt
|
||||
* Skylar Challand
|
||||
* Alex Ehlke
|
||||
*
|
||||
* Maintainer:
|
||||
* Alex Ehlke - Twitter: @aehlke
|
||||
*
|
||||
* Dependencies:
|
||||
* jQuery v1.4+
|
||||
* jQuery UI v1.8+
|
||||
*/
|
||||
(function($) {
|
||||
|
||||
$.widget('ui.tagit', {
|
||||
options: {
|
||||
allowDuplicates : false,
|
||||
caseSensitive : true,
|
||||
fieldName : 'tags',
|
||||
placeholderText : null, // Sets `placeholder` attr on input field.
|
||||
readOnly : false, // Disables editing.
|
||||
removeConfirmation: false, // Require confirmation to remove tags.
|
||||
tagLimit : null, // Max number of tags allowed (null for unlimited).
|
||||
createTagOnBlur : true, // Create a tag when input loses focus.
|
||||
|
||||
// Used for autocomplete, unless you override `autocomplete.source`.
|
||||
availableTags : [],
|
||||
|
||||
// Use to override or add any options to the autocomplete widget.
|
||||
//
|
||||
// By default, autocomplete.source will map to availableTags,
|
||||
// unless overridden.
|
||||
autocomplete: {},
|
||||
|
||||
// Shows autocomplete before the user even types anything.
|
||||
showAutocompleteOnFocus: false,
|
||||
|
||||
// When enabled, quotes are unneccesary for inputting multi-word tags.
|
||||
allowSpaces: false,
|
||||
|
||||
// The below options are for using a single field instead of several
|
||||
// for our form values.
|
||||
//
|
||||
// When enabled, will use a single hidden field for the form,
|
||||
// rather than one per tag. It will delimit tags in the field
|
||||
// with singleFieldDelimiter.
|
||||
//
|
||||
// The easiest way to use singleField is to just instantiate tag-it
|
||||
// on an INPUT element, in which case singleField is automatically
|
||||
// set to true, and singleFieldNode is set to that element. This
|
||||
// way, you don't need to fiddle with these options.
|
||||
singleField: false,
|
||||
|
||||
// This is just used when preloading data from the field, and for
|
||||
// populating the field with delimited tags as the user adds them.
|
||||
singleFieldDelimiter: ',',
|
||||
|
||||
// Set this to an input DOM node to use an existing form field.
|
||||
// Any text in it will be erased on init. But it will be
|
||||
// populated with the text of tags as they are created,
|
||||
// delimited by singleFieldDelimiter.
|
||||
//
|
||||
// If this is not set, we create an input node for it,
|
||||
// with the name given in settings.fieldName.
|
||||
singleFieldNode: null,
|
||||
|
||||
// Whether to animate tag removals or not.
|
||||
animate: true,
|
||||
|
||||
// Optionally set a tabindex attribute on the input that gets
|
||||
// created for tag-it.
|
||||
tabIndex: null,
|
||||
|
||||
// Event callbacks.
|
||||
beforeTagAdded : null,
|
||||
afterTagAdded : null,
|
||||
|
||||
beforeTagRemoved : null,
|
||||
afterTagRemoved : null,
|
||||
|
||||
onTagClicked : null,
|
||||
onTagLimitExceeded : null,
|
||||
|
||||
|
||||
// DEPRECATED:
|
||||
//
|
||||
// /!\ These event callbacks are deprecated and WILL BE REMOVED at some
|
||||
// point in the future. They're here for backwards-compatibility.
|
||||
// Use the above before/after event callbacks instead.
|
||||
onTagAdded : null,
|
||||
onTagRemoved: null,
|
||||
// `autocomplete.source` is the replacement for tagSource.
|
||||
tagSource: null
|
||||
// Do not use the above deprecated options.
|
||||
},
|
||||
|
||||
_create: function() {
|
||||
// for handling static scoping inside callbacks
|
||||
var that = this;
|
||||
|
||||
// There are 2 kinds of DOM nodes this widget can be instantiated on:
|
||||
// 1. UL, OL, or some element containing either of these.
|
||||
// 2. INPUT, in which case 'singleField' is overridden to true,
|
||||
// a UL is created and the INPUT is hidden.
|
||||
if (this.element.is('input')) {
|
||||
this.tagList = $('<ul></ul>').insertAfter(this.element);
|
||||
this.options.singleField = true;
|
||||
this.options.singleFieldNode = this.element;
|
||||
this.element.css('display', 'none');
|
||||
} else {
|
||||
this.tagList = this.element.find('ul, ol').andSelf().last();
|
||||
}
|
||||
|
||||
this.tagInput = $('<input type="text" />').addClass('ui-widget-content');
|
||||
|
||||
if (this.options.readOnly) this.tagInput.attr('disabled', 'disabled');
|
||||
|
||||
if (this.options.tabIndex) {
|
||||
this.tagInput.attr('tabindex', this.options.tabIndex);
|
||||
}
|
||||
|
||||
if (this.options.placeholderText) {
|
||||
this.tagInput.attr('placeholder', this.options.placeholderText);
|
||||
}
|
||||
|
||||
if (!this.options.autocomplete.source) {
|
||||
this.options.autocomplete.source = function(search, showChoices) {
|
||||
var filter = search.term.toLowerCase();
|
||||
var choices = $.grep(this.options.availableTags, function(element) {
|
||||
// Only match autocomplete options that begin with the search term.
|
||||
// (Case insensitive.)
|
||||
return (element.toLowerCase().indexOf(filter) === 0);
|
||||
});
|
||||
if (!this.options.allowDuplicates) {
|
||||
choices = this._subtractArray(choices, this.assignedTags());
|
||||
}
|
||||
showChoices(choices);
|
||||
};
|
||||
}
|
||||
|
||||
if (this.options.showAutocompleteOnFocus) {
|
||||
this.tagInput.focus(function(event, ui) {
|
||||
that._showAutocomplete();
|
||||
});
|
||||
|
||||
if (typeof this.options.autocomplete.minLength === 'undefined') {
|
||||
this.options.autocomplete.minLength = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Bind autocomplete.source callback functions to this context.
|
||||
if ($.isFunction(this.options.autocomplete.source)) {
|
||||
this.options.autocomplete.source = $.proxy(this.options.autocomplete.source, this);
|
||||
}
|
||||
|
||||
// DEPRECATED.
|
||||
if ($.isFunction(this.options.tagSource)) {
|
||||
this.options.tagSource = $.proxy(this.options.tagSource, this);
|
||||
}
|
||||
|
||||
this.tagList
|
||||
.addClass('tagit')
|
||||
.addClass('ui-widget ui-widget-content ui-corner-all')
|
||||
// Create the input field.
|
||||
.append($('<li class="tagit-new"></li>').append(this.tagInput))
|
||||
.click(function(e) {
|
||||
var target = $(e.target);
|
||||
if (target.hasClass('tagit-label')) {
|
||||
var tag = target.closest('.tagit-choice');
|
||||
if (!tag.hasClass('removed')) {
|
||||
that._trigger('onTagClicked', e, {tag: tag, tagLabel: that.tagLabel(tag)});
|
||||
}
|
||||
} else {
|
||||
// Sets the focus() to the input field, if the user
|
||||
// clicks anywhere inside the UL. This is needed
|
||||
// because the input field needs to be of a small size.
|
||||
that.tagInput.focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Single field support.
|
||||
var addedExistingFromSingleFieldNode = false;
|
||||
if (this.options.singleField) {
|
||||
if (this.options.singleFieldNode) {
|
||||
// Add existing tags from the input field.
|
||||
var node = $(this.options.singleFieldNode);
|
||||
var tags = node.val().split(this.options.singleFieldDelimiter);
|
||||
node.val('');
|
||||
$.each(tags, function(index, tag) {
|
||||
that.createTag(tag, null, true);
|
||||
addedExistingFromSingleFieldNode = true;
|
||||
});
|
||||
} else {
|
||||
// Create our single field input after our list.
|
||||
this.options.singleFieldNode = $('<input type="hidden" style="display:none;" value="" name="' + this.options.fieldName + '" />');
|
||||
this.tagList.after(this.options.singleFieldNode);
|
||||
}
|
||||
}
|
||||
|
||||
// Add existing tags from the list, if any.
|
||||
if (!addedExistingFromSingleFieldNode) {
|
||||
this.tagList.children('li').each(function() {
|
||||
if (!$(this).hasClass('tagit-new')) {
|
||||
that.createTag($(this).text(), $(this).attr('class'), true);
|
||||
$(this).remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Events.
|
||||
this.tagInput
|
||||
.keydown(function(event) {
|
||||
// Backspace is not detected within a keypress, so it must use keydown.
|
||||
if (event.which == $.ui.keyCode.BACKSPACE && that.tagInput.val() === '') {
|
||||
var tag = that._lastTag();
|
||||
if (!that.options.removeConfirmation || tag.hasClass('remove')) {
|
||||
// When backspace is pressed, the last tag is deleted.
|
||||
that.removeTag(tag);
|
||||
} else if (that.options.removeConfirmation) {
|
||||
tag.addClass('remove ui-state-highlight');
|
||||
}
|
||||
} else if (that.options.removeConfirmation) {
|
||||
that._lastTag().removeClass('remove ui-state-highlight');
|
||||
}
|
||||
|
||||
// Comma/Space/Enter are all valid delimiters for new tags,
|
||||
// except when there is an open quote or if setting allowSpaces = true.
|
||||
// Tab will also create a tag, unless the tag input is empty,
|
||||
// in which case it isn't caught.
|
||||
if (
|
||||
event.which === $.ui.keyCode.COMMA ||
|
||||
event.which === $.ui.keyCode.ENTER ||
|
||||
(
|
||||
event.which == $.ui.keyCode.TAB &&
|
||||
that.tagInput.val() !== ''
|
||||
) ||
|
||||
(
|
||||
event.which == $.ui.keyCode.SPACE &&
|
||||
that.options.allowSpaces !== true &&
|
||||
(
|
||||
$.trim(that.tagInput.val()).replace( /^s*/, '' ).charAt(0) != '"' ||
|
||||
(
|
||||
$.trim(that.tagInput.val()).charAt(0) == '"' &&
|
||||
$.trim(that.tagInput.val()).charAt($.trim(that.tagInput.val()).length - 1) == '"' &&
|
||||
$.trim(that.tagInput.val()).length - 1 !== 0
|
||||
)
|
||||
)
|
||||
)
|
||||
) {
|
||||
// Enter submits the form if there's no text in the input.
|
||||
if (!(event.which === $.ui.keyCode.ENTER && that.tagInput.val() === '')) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
// Autocomplete will create its own tag from a selection and close automatically.
|
||||
var menu = that.tagInput.autocomplete('widget').data('ui-menu');
|
||||
if (!(that.tagInput.data('autocomplete-open') &&
|
||||
that.tagInput.data('autocomplete-focus'))) {
|
||||
that.createTag(that._cleanedInput());
|
||||
}
|
||||
}
|
||||
}).blur(function(e){
|
||||
// Create a tag when the element loses focus.
|
||||
// If autocomplete is enabled and suggestion was clicked, don't add it.
|
||||
if (!this.createTagOnBlur) return;
|
||||
|
||||
if (!(that.tagInput.data('autocomplete-open') &&
|
||||
that.tagInput.data('autocomplete-focus'))) {
|
||||
that.createTag(that._cleanedInput());
|
||||
}
|
||||
});
|
||||
|
||||
// Autocomplete.
|
||||
if (this.options.availableTags || this.options.tagSource || this.options.autocomplete.source) {
|
||||
var autocompleteOptions = {
|
||||
select: function(event, ui) {
|
||||
that.createTag(ui.item.value);
|
||||
// Preventing the tag input to be updated with the chosen value.
|
||||
return false;
|
||||
}
|
||||
};
|
||||
$.extend(autocompleteOptions, this.options.autocomplete);
|
||||
// tagSource is deprecated, but takes precedence here since autocomplete.source is set by default,
|
||||
// while tagSource is left null by default.
|
||||
autocompleteOptions.source = this.options.tagSource || autocompleteOptions.source;
|
||||
this.tagInput.autocomplete(autocompleteOptions).bind('autocompleteopen', function(event, ui) {
|
||||
that.tagInput.data('autocomplete-open', true);
|
||||
}).bind('autocompleteclose', function(event, ui) {
|
||||
that.tagInput.data('autocomplete-open', false);
|
||||
}).bind('autocompletefocus', function(event, ui) {
|
||||
that.tagInput.data('autocomplete-focus', true);
|
||||
}).bind('autocompletefocus', function(event, ui) {
|
||||
that.tagInput.data('autocomplete-focus', false);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
_cleanedInput: function() {
|
||||
// Returns the contents of the tag input, cleaned and ready to be passed to createTag
|
||||
return $.trim(this.tagInput.val().replace(/^"(.*)"$/, '$1'));
|
||||
},
|
||||
|
||||
_lastTag: function() {
|
||||
return this.tagList.find('.tagit-choice:last:not(.removed)');
|
||||
},
|
||||
|
||||
_tags: function() {
|
||||
return this.tagList.find('.tagit-choice:not(.removed)');
|
||||
},
|
||||
|
||||
assignedTags: function() {
|
||||
// Returns an array of tag string values
|
||||
var that = this;
|
||||
var tags = [];
|
||||
if (this.options.singleField) {
|
||||
tags = $(this.options.singleFieldNode).val().split(this.options.singleFieldDelimiter);
|
||||
if (tags[0] === '') {
|
||||
tags = [];
|
||||
}
|
||||
} else {
|
||||
this._tags().each(function() {
|
||||
tags.push(that.tagLabel(this));
|
||||
});
|
||||
}
|
||||
return tags;
|
||||
},
|
||||
|
||||
_updateSingleTagsField: function(tags) {
|
||||
// Takes a list of tag string values, updates this.options.singleFieldNode.val to the tags delimited by this.options.singleFieldDelimiter
|
||||
$(this.options.singleFieldNode).val(tags.join(this.options.singleFieldDelimiter)).trigger('change');
|
||||
},
|
||||
|
||||
_subtractArray: function(a1, a2) {
|
||||
var result = [];
|
||||
for (var i = 0; i < a1.length; i++) {
|
||||
if ($.inArray(a1[i], a2) == -1) {
|
||||
result.push(a1[i]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
tagLabel: function(tag) {
|
||||
// Returns the tag's string label.
|
||||
if (this.options.singleField) {
|
||||
return $(tag).find('.tagit-label:first').text();
|
||||
} else {
|
||||
return $(tag).find('input:first').val();
|
||||
}
|
||||
},
|
||||
|
||||
_showAutocomplete: function() {
|
||||
this.tagInput.autocomplete('search', '');
|
||||
},
|
||||
|
||||
addClassAutocomplete: function(className) {
|
||||
this.tagInput.autocomplete('widget').addClass(className);
|
||||
},
|
||||
|
||||
_findTagByLabel: function(name) {
|
||||
var that = this;
|
||||
var tag = null;
|
||||
this._tags().each(function(i) {
|
||||
if (that._formatStr(name) == that._formatStr(that.tagLabel(this))) {
|
||||
tag = $(this);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
return tag;
|
||||
},
|
||||
|
||||
_isNew: function(name) {
|
||||
return !this._findTagByLabel(name);
|
||||
},
|
||||
|
||||
_formatStr: function(str) {
|
||||
if (this.options.caseSensitive) {
|
||||
return str;
|
||||
}
|
||||
return $.trim(str.toLowerCase());
|
||||
},
|
||||
|
||||
_effectExists: function(name) {
|
||||
return Boolean($.effects && ($.effects[name] || ($.effects.effect && $.effects.effect[name])));
|
||||
},
|
||||
|
||||
createTag: function(value, additionalClass, duringInitialization) {
|
||||
var that = this;
|
||||
|
||||
value = $.trim(value);
|
||||
|
||||
if (this.tagInput.data('autocomplete-open')) {
|
||||
this.tagInput.autocomplete('close');
|
||||
}
|
||||
|
||||
if(this.options.preprocessTag) {
|
||||
value = this.options.preprocessTag(value);
|
||||
}
|
||||
|
||||
if (value === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.options.allowDuplicates && !this._isNew(value)) {
|
||||
var existingTag = this._findTagByLabel(value);
|
||||
if (this._trigger('onTagExists', null, {
|
||||
existingTag: existingTag,
|
||||
duringInitialization: duringInitialization
|
||||
}) !== false) {
|
||||
if (this._effectExists('highlight')) {
|
||||
existingTag.effect('highlight');
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.options.tagLimit && this._tags().length >= this.options.tagLimit) {
|
||||
this._trigger('onTagLimitExceeded', null, {duringInitialization: duringInitialization});
|
||||
return false;
|
||||
}
|
||||
|
||||
var label = $(this.options.onTagClicked ? '<a class="tagit-label"></a>' : '<span class="tagit-label"></span>').text(value);
|
||||
|
||||
// Create tag.
|
||||
var tag = $('<li></li>')
|
||||
.addClass('tagit-choice ui-widget-content ui-state-default ui-corner-all')
|
||||
.addClass(additionalClass)
|
||||
.append(label)
|
||||
.click(function(e) {
|
||||
// Removes a tag when the little 'x' is clicked.
|
||||
that.removeTag(tag);
|
||||
});
|
||||
|
||||
if (this.options.readOnly){
|
||||
tag.addClass('tagit-choice-read-only');
|
||||
} else {
|
||||
tag.addClass('tagit-choice-editable');
|
||||
// Button for removing the tag.
|
||||
var removeTagIcon = $('<span></span>')
|
||||
.addClass('ui-icon ui-icon-close');
|
||||
var removeTag = $('<a><span class="text-icon">\xd7</span></a>') // \xd7 is an X
|
||||
.addClass('tagit-close')
|
||||
.append(removeTagIcon);
|
||||
tag.append(removeTag);
|
||||
}
|
||||
|
||||
// Unless options.singleField is set, each tag has a hidden input field inline.
|
||||
if (!this.options.singleField) {
|
||||
var escapedValue = label.html();
|
||||
tag.append('<input type="hidden" style="display:none;" value="' + escapedValue + '" name="' + this.options.fieldName + '" />');
|
||||
}
|
||||
|
||||
if (this._trigger('beforeTagAdded', null, {
|
||||
tag: tag,
|
||||
tagLabel: this.tagLabel(tag),
|
||||
duringInitialization: duringInitialization
|
||||
}) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.options.singleField) {
|
||||
var tags = this.assignedTags();
|
||||
tags.push(value);
|
||||
this._updateSingleTagsField(tags);
|
||||
}
|
||||
|
||||
// DEPRECATED.
|
||||
this._trigger('onTagAdded', null, tag);
|
||||
|
||||
this.tagInput.val('');
|
||||
|
||||
// Insert tag.
|
||||
this.tagInput.parent().before(tag);
|
||||
|
||||
this._trigger('afterTagAdded', null, {
|
||||
tag: tag,
|
||||
tagLabel: this.tagLabel(tag),
|
||||
duringInitialization: duringInitialization
|
||||
});
|
||||
|
||||
if (this.options.showAutocompleteOnFocus && !duringInitialization) {
|
||||
setTimeout(function () { that._showAutocomplete(); }, 0);
|
||||
}
|
||||
},
|
||||
|
||||
removeTag: function(tag, animate) {
|
||||
animate = typeof animate === 'undefined' ? this.options.animate : animate;
|
||||
|
||||
tag = $(tag);
|
||||
|
||||
// DEPRECATED.
|
||||
this._trigger('onTagRemoved', null, tag);
|
||||
|
||||
if (this._trigger('beforeTagRemoved', null, {tag: tag, tagLabel: this.tagLabel(tag)}) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.options.singleField) {
|
||||
var tags = this.assignedTags();
|
||||
var removedTagLabel = this.tagLabel(tag);
|
||||
tags = $.grep(tags, function(el){
|
||||
return el != removedTagLabel;
|
||||
});
|
||||
this._updateSingleTagsField(tags);
|
||||
}
|
||||
|
||||
if (animate) {
|
||||
tag.addClass('removed'); // Excludes this tag from _tags.
|
||||
var hide_args = this._effectExists('blind') ? ['blind', {direction: 'horizontal'}, 'fast'] : ['fast'];
|
||||
|
||||
var thisTag = this;
|
||||
hide_args.push(function() {
|
||||
tag.remove();
|
||||
thisTag._trigger('afterTagRemoved', null, {tag: tag, tagLabel: thisTag.tagLabel(tag)});
|
||||
});
|
||||
|
||||
tag.fadeOut('fast').hide.apply(tag, hide_args).dequeue();
|
||||
} else {
|
||||
tag.remove();
|
||||
this._trigger('afterTagRemoved', null, {tag: tag, tagLabel: this.tagLabel(tag)});
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
removeTagByLabel: function(tagLabel, animate) {
|
||||
var toRemove = this._findTagByLabel(tagLabel);
|
||||
if (!toRemove) {
|
||||
throw "No such tag exists with the name '" + tagLabel + "'";
|
||||
}
|
||||
this.removeTag(toRemove, animate);
|
||||
},
|
||||
|
||||
removeAll: function() {
|
||||
// Removes all tags.
|
||||
var that = this;
|
||||
this._tags().each(function(index, tag) {
|
||||
that.removeTag(tag, false);
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
})(jQuery);
|
||||
|
|
@ -388,7 +388,7 @@ CELERYBEAT_SCHEDULE = {
|
|||
},
|
||||
'activate-next-new-user': {
|
||||
'task': 'activate-next-new-user',
|
||||
'schedule': datetime.timedelta(minutes=2),
|
||||
'schedule': datetime.timedelta(minutes=8),
|
||||
'options': {'queue': 'beat_tasks'},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
|
||||
NEWSBLUR.Bookmarklet.prototype = {
|
||||
|
||||
|
||||
// ==================
|
||||
// = Initialization =
|
||||
// ==================
|
||||
|
@ -62,8 +61,7 @@
|
|||
this.attach_css();
|
||||
this.make_modal();
|
||||
this.open_modal();
|
||||
this.get_page_content();
|
||||
|
||||
|
||||
this.$modal.bind('click', $.rescope(this.handle_clicks, this));
|
||||
|
||||
var $comment = $('textarea[name=newsblur_comment]', this.$modal);
|
||||
|
@ -75,6 +73,8 @@
|
|||
$content.bind('keyup', $.rescope(this.update_share_button_title, this));
|
||||
$comment.bind('keydown', 'ctrl+return', $.rescope(this.share_story, this));
|
||||
$comment.bind('keydown', 'meta+return', $.rescope(this.share_story, this));
|
||||
|
||||
this.get_page_content();
|
||||
},
|
||||
|
||||
make_modal: function() {
|
||||
|
|
|
@ -83,13 +83,19 @@
|
|||
|
||||
<div class="NB-feeds-header-container NB-feeds-header-starred-container">
|
||||
<div class="NB-feeds-header NB-feeds-header-starred NB-empty">
|
||||
<div class="NB-feeds-header-count unread_count"></div>
|
||||
<div class="NB-feeds-header-count feed_counts_floater unread_count"></div>
|
||||
<div class="NB-feeds-header-icon"></div>
|
||||
<div class="NB-feedlist-collapse-icon"></div>
|
||||
<div class="NB-feeds-header-title">
|
||||
Saved Stories
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="NB-starred-folder">
|
||||
<ul class="NB-starred-feeds NB-feedlist"></ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="left-center-footer">
|
||||
|
|
2
urls.py
2
urls.py
|
@ -11,6 +11,8 @@ urlpatterns = patterns('',
|
|||
(r'^try/?', reader_views.index),
|
||||
(r'^site/(?P<feed_id>\d+)?', reader_views.index),
|
||||
(r'^folder/(?P<folder_name>\d+)?', reader_views.index),
|
||||
url(r'^saved/(?P<tag_name>\d+)?', reader_views.index, name='saved-stories-tag'),
|
||||
(r'^saved/?', reader_views.index),
|
||||
(r'^social/\d+/.*?', reader_views.index),
|
||||
(r'^user/.*?', reader_views.index),
|
||||
(r'^null/.*?', reader_views.index),
|
||||
|
|
|
@ -46,7 +46,7 @@ def format_story_link_date__long(date, now=None):
|
|||
if not now:
|
||||
now = datetime.datetime.now()
|
||||
date = date.replace(tzinfo=None)
|
||||
midnight = midnight_today()
|
||||
midnight = midnight_today(now)
|
||||
parsed_date = DateFormat(date)
|
||||
|
||||
if date >= midnight:
|
||||
|
|
Loading…
Add table
Reference in a new issue