mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-09-18 21:50:56 +00:00
Merge remote-tracking branch 'upstream/master' into profile
This commit is contained in:
commit
414a921f72
78 changed files with 16678 additions and 15530 deletions
|
@ -322,6 +322,7 @@ class Profile(models.Model):
|
|||
'API_USERNAME': settings.PAYPAL_API_USERNAME,
|
||||
'API_PASSWORD': settings.PAYPAL_API_PASSWORD,
|
||||
'API_SIGNATURE': settings.PAYPAL_API_SIGNATURE,
|
||||
'API_CA_CERTS': False,
|
||||
}
|
||||
paypal = PayPalInterface(**paypal_opts)
|
||||
transactions = PayPalIPN.objects.filter(custom=self.user.username,
|
||||
|
@ -362,6 +363,7 @@ class Profile(models.Model):
|
|||
'API_USERNAME': settings.PAYPAL_API_USERNAME,
|
||||
'API_PASSWORD': settings.PAYPAL_API_PASSWORD,
|
||||
'API_SIGNATURE': settings.PAYPAL_API_SIGNATURE,
|
||||
'API_CA_CERTS': False,
|
||||
}
|
||||
paypal = PayPalInterface(**paypal_opts)
|
||||
transaction = transactions[0]
|
||||
|
|
|
@ -19,6 +19,7 @@ urlpatterns = patterns('',
|
|||
url(r'^payment_history/?', views.payment_history, name='profile-payment-history'),
|
||||
url(r'^cancel_premium/?', views.cancel_premium, name='profile-cancel-premium'),
|
||||
url(r'^refund_premium/?', views.refund_premium, name='profile-refund-premium'),
|
||||
url(r'^never_expire_premium/?', views.never_expire_premium, name='profile-never-expire-premium'),
|
||||
url(r'^upgrade_premium/?', views.upgrade_premium, name='profile-upgrade-premium'),
|
||||
url(r'^update_payment_history/?', views.update_payment_history, name='profile-update-payment-history'),
|
||||
url(r'^delete_account/?', views.delete_account, name='profile-delete-account'),
|
||||
|
|
|
@ -23,6 +23,7 @@ from apps.profile.forms import RedeemCodeForm
|
|||
from apps.reader.forms import SignupForm, LoginForm
|
||||
from apps.rss_feeds.models import MStarredStory, MStarredStoryCounts
|
||||
from apps.social.models import MSocialServices, MActivity, MSocialProfile
|
||||
from apps.analyzer.models import MClassifierTitle, MClassifierAuthor, MClassifierFeed, MClassifierTag
|
||||
from utils import json_functions as json
|
||||
from utils.user_functions import ajax_login_required
|
||||
from utils.view_functions import render_to
|
||||
|
@ -407,6 +408,7 @@ def payment_history(request):
|
|||
|
||||
history = PaymentHistory.objects.filter(user=user)
|
||||
statistics = {
|
||||
"created_date": user.date_joined,
|
||||
"last_seen_date": user.profile.last_seen_on,
|
||||
"timezone": unicode(user.profile.timezone),
|
||||
"stripe_id": user.profile.stripe_id,
|
||||
|
@ -415,6 +417,12 @@ def payment_history(request):
|
|||
"email": user.email,
|
||||
"read_story_count": RUserStory.read_story_count(user.pk),
|
||||
"feed_opens": UserSubscription.objects.filter(user=user).aggregate(sum=Sum('feed_opens'))['sum'],
|
||||
"training": {
|
||||
'title': MClassifierTitle.objects.filter(user_id=user.pk).count(),
|
||||
'tag': MClassifierTag.objects.filter(user_id=user.pk).count(),
|
||||
'author': MClassifierAuthor.objects.filter(user_id=user.pk).count(),
|
||||
'feed': MClassifierFeed.objects.filter(user_id=user.pk).count(),
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -459,6 +467,19 @@ def upgrade_premium(request):
|
|||
|
||||
return {'code': 1 if upgraded else -1}
|
||||
|
||||
@staff_member_required
|
||||
@ajax_login_required
|
||||
@json.json_view
|
||||
def never_expire_premium(request):
|
||||
user_id = request.REQUEST.get('user_id')
|
||||
user = User.objects.get(pk=user_id)
|
||||
if user.profile.is_premium:
|
||||
user.profile.premium_expire = None
|
||||
user.profile.save()
|
||||
return {'code': 1}
|
||||
|
||||
return {'code': -1}
|
||||
|
||||
@staff_member_required
|
||||
@ajax_login_required
|
||||
@json.json_view
|
||||
|
|
|
@ -423,9 +423,9 @@ class UserSubscription(models.Model):
|
|||
|
||||
@classmethod
|
||||
def identify_deleted_feed_users(cls, old_feed_id):
|
||||
users = UserSubscriptionFolders.objects.filter(folders__contains="5636682").only('user')
|
||||
users = UserSubscriptionFolders.objects.filter(folders__contains=old_feed_id).only('user')
|
||||
user_ids = [usf.user_id for usf in users]
|
||||
f = open('users.txt', 'w')
|
||||
f = open('utils/backups/users.txt', 'w')
|
||||
f.write('\n'.join([str(u) for u in user_ids]))
|
||||
|
||||
return user_ids
|
||||
|
|
|
@ -194,7 +194,6 @@ class Feed(models.Model):
|
|||
feed['tagline'] = self.data.feed_tagline
|
||||
feed['feed_tags'] = json.decode(self.data.popular_tags) if self.data.popular_tags else []
|
||||
feed['feed_authors'] = json.decode(self.data.popular_authors) if self.data.popular_authors else []
|
||||
|
||||
|
||||
return feed
|
||||
|
||||
|
@ -622,6 +621,16 @@ class Feed(models.Model):
|
|||
self.save()
|
||||
|
||||
return errors, non_errors
|
||||
|
||||
def count_redirects_in_history(self, fetch_type='feed', fetch_history=None):
|
||||
logging.debug(' ---> [%-30s] Counting redirects in history...' % (unicode(self)[:30]))
|
||||
if not fetch_history:
|
||||
fetch_history = MFetchHistory.feed(self.pk)
|
||||
fh = fetch_history[fetch_type+'_fetch_history']
|
||||
redirects = [h for h in fh if h['status_code'] and int(h['status_code']) in (301, 302)]
|
||||
non_redirects = [h for h in fh if h['status_code'] and int(h['status_code']) not in (301, 302)]
|
||||
|
||||
return redirects, non_redirects
|
||||
|
||||
def count_subscribers(self, verbose=False):
|
||||
SUBSCRIBER_EXPIRE = datetime.datetime.now() - datetime.timedelta(days=settings.SUBSCRIBER_EXPIRE)
|
||||
|
|
|
@ -1840,6 +1840,7 @@ class MSharedStory(mongo.Document):
|
|||
profile_user_ids = set()
|
||||
for story in stories:
|
||||
story['friend_comments'] = []
|
||||
story['friend_shares'] = []
|
||||
story['public_comments'] = []
|
||||
story['reply_count'] = 0
|
||||
if check_all or story['comment_count']:
|
||||
|
@ -1895,6 +1896,23 @@ class MSharedStory(mongo.Document):
|
|||
story['share_user_ids'] = story['friend_user_ids'] + story['public_user_ids']
|
||||
if story.get('source_user_id'):
|
||||
profile_user_ids.add(story['source_user_id'])
|
||||
shared_stories = []
|
||||
if story['shared_by_friends']:
|
||||
params = {
|
||||
'story_hash': story['story_hash'],
|
||||
'user_id__in': story['shared_by_friends'],
|
||||
}
|
||||
shared_stories = cls.objects.filter(**params)
|
||||
for shared_story in shared_stories:
|
||||
comments = shared_story.comments_with_author()
|
||||
story['reply_count'] += len(comments['replies'])
|
||||
story['friend_shares'].append(comments)
|
||||
profile_user_ids = profile_user_ids.union([reply['user_id']
|
||||
for reply in comments['replies']])
|
||||
if comments.get('source_user_id'):
|
||||
profile_user_ids.add(comments['source_user_id'])
|
||||
if comments.get('liking_users'):
|
||||
profile_user_ids = profile_user_ids.union(comments['liking_users'])
|
||||
|
||||
profiles = MSocialProfile.objects.filter(user_id__in=list(profile_user_ids))
|
||||
profiles = [profile.canonical(compact=True) for profile in profiles]
|
||||
|
@ -1920,7 +1938,7 @@ class MSharedStory(mongo.Document):
|
|||
for u, user_id in enumerate(story['shared_by_public']):
|
||||
if user_id not in profiles: continue
|
||||
stories[s]['shared_by_public'][u] = profiles[user_id]
|
||||
for comment_set in ['friend_comments', 'public_comments']:
|
||||
for comment_set in ['friend_comments', 'public_comments', 'friend_shares']:
|
||||
for c, comment in enumerate(story[comment_set]):
|
||||
if comment['user_id'] not in profiles: continue
|
||||
stories[s][comment_set][c]['user'] = profiles[comment['user_id']]
|
||||
|
@ -2743,6 +2761,10 @@ class MInteraction(mongo.Document):
|
|||
self.category, self.content and self.content[:20])
|
||||
|
||||
def canonical(self):
|
||||
story_hash = None
|
||||
if self.story_feed_id:
|
||||
story_hash = MStory.ensure_story_hash(self.content_id, story_feed_id=self.story_feed_id)
|
||||
|
||||
return {
|
||||
'date': self.date,
|
||||
'category': self.category,
|
||||
|
@ -2752,6 +2774,7 @@ class MInteraction(mongo.Document):
|
|||
'feed_id': self.feed_id,
|
||||
'story_feed_id': self.story_feed_id,
|
||||
'content_id': self.content_id,
|
||||
'story_hash': story_hash,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
|
@ -2867,11 +2890,12 @@ class MInteraction(mongo.Document):
|
|||
cls.publish_update_to_subscribers(user_id)
|
||||
|
||||
@classmethod
|
||||
def new_comment_like(cls, liking_user_id, comment_user_id, story_id, story_title, comments):
|
||||
def new_comment_like(cls, liking_user_id, comment_user_id, story_id, story_feed_id, story_title, comments):
|
||||
cls.objects.get_or_create(user_id=comment_user_id,
|
||||
with_user_id=liking_user_id,
|
||||
category="comment_like",
|
||||
feed_id="social:%s" % comment_user_id,
|
||||
story_feed_id=story_feed_id,
|
||||
content_id=story_id,
|
||||
defaults={
|
||||
"title": story_title,
|
||||
|
@ -2974,6 +2998,10 @@ class MActivity(mongo.Document):
|
|||
return "<%s> %s - %s" % (user.username, self.category, self.content and self.content[:20])
|
||||
|
||||
def canonical(self):
|
||||
story_hash = None
|
||||
if self.story_feed_id:
|
||||
story_hash = MStory.ensure_story_hash(self.content_id, story_feed_id=self.story_feed_id)
|
||||
|
||||
return {
|
||||
'date': self.date,
|
||||
'category': self.category,
|
||||
|
@ -2984,6 +3012,7 @@ class MActivity(mongo.Document):
|
|||
'feed_id': self.feed_id or self.story_feed_id,
|
||||
'story_feed_id': self.story_feed_id or self.feed_id,
|
||||
'content_id': self.content_id,
|
||||
'story_hash': story_hash,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
|
@ -3109,11 +3138,12 @@ class MActivity(mongo.Document):
|
|||
original.delete()
|
||||
|
||||
@classmethod
|
||||
def new_comment_like(cls, liking_user_id, comment_user_id, story_id, story_title, comments):
|
||||
def new_comment_like(cls, liking_user_id, comment_user_id, story_id, story_feed_id, story_title, comments):
|
||||
cls.objects.get_or_create(user_id=liking_user_id,
|
||||
with_user_id=comment_user_id,
|
||||
category="comment_like",
|
||||
feed_id="social:%s" % comment_user_id,
|
||||
story_feed_id=story_feed_id,
|
||||
content_id=story_id,
|
||||
defaults={
|
||||
"title": story_title,
|
||||
|
|
|
@ -1185,11 +1185,13 @@ def like_comment(request):
|
|||
MActivity.new_comment_like(liking_user_id=request.user.pk,
|
||||
comment_user_id=comment['user_id'],
|
||||
story_id=story_id,
|
||||
story_feed_id=feed_id,
|
||||
story_title=shared_story.story_title,
|
||||
comments=shared_story.comments)
|
||||
MInteraction.new_comment_like(liking_user_id=request.user.pk,
|
||||
comment_user_id=comment['user_id'],
|
||||
story_id=story_id,
|
||||
story_feed_id=feed_id,
|
||||
story_title=shared_story.story_title,
|
||||
comments=shared_story.comments)
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.newsblur"
|
||||
android:versionCode="93"
|
||||
android:versionName="4.3.0b2" >
|
||||
android:versionCode="94"
|
||||
android:versionName="4.3.1b1" >
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="11"
|
||||
|
|
BIN
clients/android/NewsBlur/libs/butterknife-7.0.0-SNAPSHOT.jar
Normal file
BIN
clients/android/NewsBlur/libs/butterknife-7.0.0-SNAPSHOT.jar
Normal file
Binary file not shown.
|
@ -1,26 +0,0 @@
|
|||
# To enable ProGuard in your project, edit project.properties
|
||||
# to define the proguard.config property as described in that file.
|
||||
#
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in ${sdk.dir}/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the ProGuard
|
||||
# include property in project.properties.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# We're open-source; dont' obfuscate.
|
||||
-dontobfuscate
|
||||
|
||||
-dontwarn com.squareup.okhttp.**
|
||||
-dontwarn okio.**
|
|
@ -7,8 +7,6 @@
|
|||
# "ant.properties", and override values to adapt the script to your
|
||||
# project structure.
|
||||
#
|
||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
||||
proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
||||
|
||||
# Project target.
|
||||
target=android-21
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.7 KiB |
|
@ -133,15 +133,15 @@
|
|||
|
||||
<LinearLayout
|
||||
android:id="@+id/comment_replies_container"
|
||||
android:orientation="horizontal"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<View
|
||||
<View
|
||||
android:id="@+id/comment_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
style="?storyCommentDivider" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
|
||||
<LinearLayout
|
||||
android:id="@+id/reading_friend_comment_header"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
|
@ -73,8 +74,7 @@
|
|||
android:paddingBottom="5dp"
|
||||
android:textStyle="bold"
|
||||
android:textSize="10sp"
|
||||
style="?commentsHeader"
|
||||
/>
|
||||
style="?commentsHeader"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/reading_friend_header_bottom_border"
|
||||
|
@ -83,7 +83,7 @@
|
|||
android:layout_below="@id/reading_friend_comment_total"
|
||||
android:background="@color/lightgray"/>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/reading_friend_comment_container"
|
||||
|
@ -92,15 +92,58 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/reading_friend_emptyshare_header"
|
||||
android:layout_below="@id/reading_friend_comment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:id="@+id/reading_friend_share_header_top_border"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:background="@color/lightgray"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingRight="12dp"
|
||||
android:id="@+id/reading_friend_emptyshare_total"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="5dp"
|
||||
android:textStyle="bold"
|
||||
android:textSize="10sp"
|
||||
style="?commentsHeader"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/reading_friend_share_header_bottom_border"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_below="@id/reading_friend_comment_total"
|
||||
android:background="@color/lightgray"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/reading_friend_emptyshare_container"
|
||||
android:layout_below="@id/reading_friend_emptyshare_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:orientation="vertical"/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/reading_public_comment_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/comment_divider"
|
||||
android:layout_below="@id/reading_friend_emptyshare_container"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical">
|
||||
|
||||
|
@ -139,9 +182,8 @@
|
|||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginRight="12dp"
|
||||
android:layout_below="@id/reading_public_comment_header"
|
||||
android:orientation="vertical">
|
||||
android:orientation="vertical"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</merge>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<item android:id="@+id/menu_shared"
|
||||
android:title="@string/menu_share"/>
|
||||
<item android:id="@+id/menu_send_story"
|
||||
android:title="@string/menu_send_story"/>
|
||||
|
||||
<item android:id="@+id/menu_mark_story_as_unread"
|
||||
android:title="@string/menu_mark_unread" />
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
|
||||
<item android:id="@+id/menu_shared"
|
||||
android:title="@string/menu_share"/>
|
||||
<item android:id="@+id/menu_send_story"
|
||||
android:title="@string/menu_send_story"/>
|
||||
|
||||
<item android:id="@+id/menu_mark_story_as_unread"
|
||||
android:title="@string/menu_mark_unread" />
|
||||
|
|
|
@ -18,10 +18,9 @@
|
|||
android:showAsAction="never"
|
||||
android:title="@string/menu_original"/>
|
||||
<item
|
||||
android:id="@+id/menu_shared"
|
||||
android:icon="@drawable/share"
|
||||
android:id="@+id/menu_send_story"
|
||||
android:showAsAction="never"
|
||||
android:title="@string/menu_share"/>
|
||||
android:title="@string/menu_send_story"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_textsize"
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
|
||||
<string name="loading">Loading…</string>
|
||||
<string name="orig_text_loading">Fetching text…</string>
|
||||
<string name="edit">Shared</string>
|
||||
|
||||
<string name="follow_error">There was a problem following the user. Check your internet connection and try again.</string>
|
||||
<string name="unfollow_error">There was a problem unfollowing the user. Check your internet connection and try again.</string>
|
||||
|
@ -46,21 +45,18 @@
|
|||
<string name="read_stories_title">Read Stories</string>
|
||||
<string name="saved_stories_title">Saved Stories</string>
|
||||
|
||||
<string name="now">now</string>
|
||||
|
||||
<string name="error_saving_classifier">There was an error. Check your internet connection.</string>
|
||||
|
||||
<string name="replied">Reply posted</string>
|
||||
<string name="error_replying">There was an error replying</string>
|
||||
<string name="share">\"%1$s\" - %2$s</string>
|
||||
<string name="share_comment_hint">Comment (Optional)</string>
|
||||
<string name="shared">Story shared</string>
|
||||
<string name="error_sharing">There was an problem sharing this story.</string>
|
||||
<string name="share_newsblur">Share \"%s\" to your Blurblog?</string>
|
||||
|
||||
<string name="share_this">SHARE THIS STORY</string>
|
||||
<string name="already_shared">SHARED</string>
|
||||
<string name="share_this_story">Share this story</string>
|
||||
<string name="update_shared">Update comment</string>
|
||||
|
||||
<string name="save_this">SAVE THIS STORY</string>
|
||||
<string name="unsave_this">UNSAVE THIS STORY</string>
|
||||
<string name="share_this">SHARE THIS STORY</string>
|
||||
|
||||
<string name="overlay_next">NEXT</string>
|
||||
<string name="overlay_done">DONE</string>
|
||||
|
@ -99,7 +95,7 @@
|
|||
<string name="menu_profile">Profile</string>
|
||||
<string name="menu_refresh">Refresh</string>
|
||||
<string name="menu_original">View original</string>
|
||||
<string name="menu_share">Send to…</string>
|
||||
<string name="menu_send_story">Send to…</string>
|
||||
<string name="menu_mark_feed_as_read">Mark feed as read</string>
|
||||
<string name="menu_delete_feed">Delete feed</string>
|
||||
<string name="menu_mark_folder_as_read">Mark folder as read</string>
|
||||
|
@ -148,11 +144,9 @@
|
|||
<string name="add_facebook">Add Facebook friends</string>
|
||||
<string name="need_to_login"><u>I need to log in!</u></string>
|
||||
<string name="need_to_register"><u>I need to register</u></string>
|
||||
<string name="share_this_story">Share this story</string>
|
||||
<string name="comment_favourited">Comment favorited</string>
|
||||
<string name="error_liking_comment">Error favoriting comment</string>
|
||||
<string name="public_comment_count">%d PUBLIC COMMENTS</string>
|
||||
<string name="friends_comments_count">%d COMMENTS</string>
|
||||
<string name="friends_shares_count">%d SHARES</string>
|
||||
<string name="unknown_user">Unknown User</string>
|
||||
<string name="delete_feed_message">Delete feed \"%s\"?</string>
|
||||
|
||||
|
|
|
@ -17,6 +17,9 @@ import android.view.Window;
|
|||
import android.widget.AbsListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.FindView;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.fragment.FeedIntelligenceSelectorFragment;
|
||||
import com.newsblur.fragment.FolderListFragment;
|
||||
|
@ -35,10 +38,10 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
|
||||
private FolderListFragment folderFeedList;
|
||||
private FragmentManager fragmentManager;
|
||||
private TextView overlayStatusText;
|
||||
private boolean isLightTheme;
|
||||
private SwipeRefreshLayout swipeLayout;
|
||||
private boolean wasSwipeEnabled = false;
|
||||
@FindView(R.id.main_sync_status) TextView overlayStatusText;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -52,6 +55,8 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
getWindow().setBackgroundDrawableResource(android.R.color.transparent);
|
||||
|
||||
setContentView(R.layout.activity_main);
|
||||
ButterKnife.bind(this);
|
||||
|
||||
getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
|
||||
|
||||
swipeLayout = (SwipeRefreshLayout)findViewById(R.id.swipe_container);
|
||||
|
@ -63,8 +68,6 @@ public class Main extends NbActivity implements StateChangedListener, SwipeRefre
|
|||
folderFeedList.setRetainInstance(true);
|
||||
((FeedIntelligenceSelectorFragment) fragmentManager.findFragmentByTag("feedIntelligenceSelector")).setState(folderFeedList.currentState);
|
||||
|
||||
this.overlayStatusText = (TextView) findViewById(R.id.main_sync_status);
|
||||
|
||||
// make sure the interval sync is scheduled, since we are the root Activity
|
||||
BootReceiver.scheduleSyncService(this);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,9 @@ import android.widget.SeekBar;
|
|||
import android.widget.SeekBar.OnSeekBarChangeListener;
|
||||
import android.widget.Toast;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.FindView;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.fragment.ReadingItemFragment;
|
||||
|
@ -73,11 +76,17 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
protected final Object STORIES_MUTEX = new Object();
|
||||
protected Cursor stories;
|
||||
|
||||
private View contentView; // we use this a ton, so cache it
|
||||
protected ViewPager pager;
|
||||
protected Button overlayLeft, overlayRight;
|
||||
protected ProgressBar overlayProgress, overlayProgressRight, overlayProgressLeft;
|
||||
protected Button overlayText, overlaySend;
|
||||
@FindView(android.R.id.content) View contentView; // we use this a ton, so cache it
|
||||
@FindView(R.id.reading_overlay_left) Button overlayLeft;
|
||||
@FindView(R.id.reading_overlay_right) Button overlayRight;
|
||||
@FindView(R.id.reading_overlay_progress) ProgressBar overlayProgress;
|
||||
@FindView(R.id.reading_overlay_progress_right) ProgressBar overlayProgressRight;
|
||||
@FindView(R.id.reading_overlay_progress_left) ProgressBar overlayProgressLeft;
|
||||
@FindView(R.id.reading_overlay_text) Button overlayText;
|
||||
@FindView(R.id.reading_overlay_send) Button overlaySend;
|
||||
|
||||
ViewPager pager;
|
||||
|
||||
protected FragmentManager fragmentManager;
|
||||
protected ReadingAdapter readingAdapter;
|
||||
private boolean stopLoading;
|
||||
|
@ -104,14 +113,7 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
super.onCreate(savedInstanceBundle);
|
||||
|
||||
setContentView(R.layout.activity_reading);
|
||||
this.contentView = findViewById(android.R.id.content);
|
||||
this.overlayLeft = (Button) findViewById(R.id.reading_overlay_left);
|
||||
this.overlayRight = (Button) findViewById(R.id.reading_overlay_right);
|
||||
this.overlayProgress = (ProgressBar) findViewById(R.id.reading_overlay_progress);
|
||||
this.overlayProgressRight = (ProgressBar) findViewById(R.id.reading_overlay_progress_right);
|
||||
this.overlayProgressLeft = (ProgressBar) findViewById(R.id.reading_overlay_progress_left);
|
||||
this.overlayText = (Button) findViewById(R.id.reading_overlay_text);
|
||||
this.overlaySend = (Button) findViewById(R.id.reading_overlay_send);
|
||||
ButterKnife.bind(this);
|
||||
|
||||
fragmentManager = getFragmentManager();
|
||||
|
||||
|
@ -221,7 +223,7 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
}
|
||||
|
||||
private void setupPager() {
|
||||
pager = (ViewPager) findViewById(R.id.reading_pager);
|
||||
pager = (ViewPager) findViewById(R.id.reading_pager);
|
||||
pager.setPageMargin(UIUtils.convertDPsToPixels(getApplicationContext(), 1));
|
||||
if (PrefsUtils.isLightThemeSelected(this)) {
|
||||
pager.setPageMarginDrawable(R.drawable.divider_light);
|
||||
|
@ -285,11 +287,11 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
startActivity(i);
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_reading_sharenewsblur) {
|
||||
DialogFragment newFragment = ShareDialogFragment.newInstance(getReadingFragment(), story, getReadingFragment().previouslySavedShareText, readingAdapter.getSourceUserId());
|
||||
DialogFragment newFragment = ShareDialogFragment.newInstance(story, readingAdapter.getSourceUserId());
|
||||
newFragment.show(getFragmentManager(), "dialog");
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_shared) {
|
||||
FeedUtils.shareStory(story, this);
|
||||
} else if (item.getItemId() == R.id.menu_send_story) {
|
||||
FeedUtils.sendStory(story, this);
|
||||
return true;
|
||||
} else if (item.getItemId() == R.id.menu_textsize) {
|
||||
TextSizeDialogFragment textSize = TextSizeDialogFragment.newInstance(PrefsUtils.getTextSize(this));
|
||||
|
@ -592,9 +594,18 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
unreadSearchStarted = true;
|
||||
}
|
||||
|
||||
int candidate = 0;
|
||||
boolean unreadFound = false;
|
||||
// start searching just after the current story
|
||||
int candidate = pager.getCurrentItem() + 1;
|
||||
unreadSearch:while (!unreadFound) {
|
||||
// if we've reached the end of the list, loop back to the beginning
|
||||
if (candidate >= readingAdapter.getCount()) {
|
||||
candidate = 0;
|
||||
}
|
||||
// if we have looped all the way around to the story we are on, there aren't any left
|
||||
if (candidate == pager.getCurrentItem()) {
|
||||
break unreadSearch;
|
||||
}
|
||||
Story story = readingAdapter.getStory(candidate);
|
||||
if (this.stopLoading) {
|
||||
// this activity was ended before we finished. just stop.
|
||||
|
@ -603,13 +614,14 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
}
|
||||
// iterate through the stories in our cursor until we find an unread one
|
||||
if (story != null) {
|
||||
if ((candidate == pager.getCurrentItem()) || (story.read) ) {
|
||||
if (story.read) {
|
||||
candidate++;
|
||||
continue unreadSearch;
|
||||
} else {
|
||||
unreadFound = true;
|
||||
}
|
||||
}
|
||||
// if we didn't continue or break, the cursor probably changed out from under us, so stop.
|
||||
break unreadSearch;
|
||||
}
|
||||
|
||||
|
@ -632,7 +644,7 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
} else {
|
||||
// trigger a check to see if there are any more to search before proceeding. By leaving the
|
||||
// unreadSearchActive flag high, this method will be called again when a new cursor is loaded
|
||||
this.checkStoryCount(candidate+1);
|
||||
this.checkStoryCount(readingAdapter.getCount()+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -682,7 +694,7 @@ public abstract class Reading extends NbActivity implements OnPageChangeListener
|
|||
public void overlaySend(View v) {
|
||||
if ((readingAdapter == null) || (pager == null)) return;
|
||||
Story story = readingAdapter.getStory(pager.getCurrentItem());
|
||||
FeedUtils.shareStory(story, this);
|
||||
FeedUtils.sendStory(story, this);
|
||||
}
|
||||
|
||||
public void overlayText(View v) {
|
||||
|
|
|
@ -31,7 +31,9 @@ import com.newsblur.util.ReadFilter;
|
|||
import com.newsblur.util.StateFilter;
|
||||
import com.newsblur.util.StoryOrder;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
|
@ -137,9 +139,14 @@ public class BlurDatabaseHelper {
|
|||
}
|
||||
|
||||
public void cleanupFeedsFolders() {
|
||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.FEED_TABLE, null, null);}
|
||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.FOLDER_TABLE, null, null);}
|
||||
synchronized (RW_MUTEX) {dbRW.delete(DatabaseConstants.SOCIALFEED_TABLE, null, null);}
|
||||
synchronized (RW_MUTEX) {
|
||||
dbRW.delete(DatabaseConstants.FEED_TABLE, null, null);
|
||||
dbRW.delete(DatabaseConstants.FOLDER_TABLE, null, null);
|
||||
dbRW.delete(DatabaseConstants.SOCIALFEED_TABLE, null, null);
|
||||
dbRW.delete(DatabaseConstants.SOCIALFEED_STORY_MAP_TABLE, null, null);
|
||||
dbRW.delete(DatabaseConstants.COMMENT_TABLE, null, null);
|
||||
dbRW.delete(DatabaseConstants.REPLY_TABLE, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void vacuum() {
|
||||
|
@ -320,31 +327,70 @@ public class BlurDatabaseHelper {
|
|||
// handle comments
|
||||
List<ContentValues> commentValues = new ArrayList<ContentValues>();
|
||||
List<ContentValues> replyValues = new ArrayList<ContentValues>();
|
||||
// track which comments were seen, so replies can be cleared before re-insertion. there isn't
|
||||
// enough data to de-dupe them for an insert/update operation
|
||||
List<String> freshCommentIds = new ArrayList<String>();
|
||||
for (Story story : apiResponse.stories) {
|
||||
for (Comment comment : story.publicComments) {
|
||||
comment.storyId = story.id;
|
||||
comment.id = TextUtils.concat(story.id, story.feedId, comment.userId).toString();
|
||||
// we need a primary key for comments, so construct one
|
||||
comment.id = Comment.constructId(story.id, story.feedId, comment.userId);
|
||||
commentValues.add(comment.getValues());
|
||||
for (Reply reply : comment.replies) {
|
||||
reply.commentId = comment.id;
|
||||
reply.id = reply.constructId();
|
||||
replyValues.add(reply.getValues());
|
||||
}
|
||||
freshCommentIds.add(comment.id);
|
||||
}
|
||||
for (Comment comment : story.friendsComments) {
|
||||
comment.storyId = story.id;
|
||||
comment.id = TextUtils.concat(story.id, story.feedId, comment.userId).toString();
|
||||
// we need a primary key for comments, so construct one
|
||||
comment.id = Comment.constructId(story.id, story.feedId, comment.userId);
|
||||
comment.byFriend = true;
|
||||
commentValues.add(comment.getValues());
|
||||
for (Reply reply : comment.replies) {
|
||||
reply.commentId = comment.id;
|
||||
reply.id = reply.constructId();
|
||||
replyValues.add(reply.getValues());
|
||||
}
|
||||
freshCommentIds.add(comment.id);
|
||||
}
|
||||
for (Comment comment : story.friendsShares) {
|
||||
comment.isPseudo = true;
|
||||
comment.storyId = story.id;
|
||||
// we need a primary key for comments, so construct one
|
||||
comment.id = Comment.constructId(story.id, story.feedId, comment.userId);
|
||||
comment.byFriend = true;
|
||||
commentValues.add(comment.getValues());
|
||||
for (Reply reply : comment.replies) {
|
||||
reply.commentId = comment.id;
|
||||
reply.id = reply.constructId();
|
||||
replyValues.add(reply.getValues());
|
||||
}
|
||||
freshCommentIds.add(comment.id);
|
||||
}
|
||||
}
|
||||
deleteRepliesForComments(freshCommentIds);
|
||||
bulkInsertValues(DatabaseConstants.COMMENT_TABLE, commentValues);
|
||||
bulkInsertValues(DatabaseConstants.REPLY_TABLE, replyValues);
|
||||
}
|
||||
|
||||
private void deleteRepliesForComments(Collection<String> commentIds) {
|
||||
// NB: attempting to do this with a "WHERE col IN (vector)" for speed can cause errors on some versions of sqlite
|
||||
synchronized (RW_MUTEX) {
|
||||
dbRW.beginTransaction();
|
||||
try {
|
||||
for (String commentId : commentIds) {
|
||||
dbRW.delete(DatabaseConstants.REPLY_TABLE, DatabaseConstants.REPLY_COMMENTID + " = ?", new String[]{commentId});
|
||||
}
|
||||
dbRW.setTransactionSuccessful();
|
||||
} finally {
|
||||
dbRW.endTransaction();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Folder getFolder(String folderName) {
|
||||
String[] selArgs = new String[] {folderName};
|
||||
String selection = DatabaseConstants.FOLDER_NAME + " = ?";
|
||||
|
@ -623,6 +669,33 @@ public class BlurDatabaseHelper {
|
|||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_HASH + " = ?", new String[]{hash});}
|
||||
}
|
||||
|
||||
public void setStoryShared(String hash) {
|
||||
// get a fresh copy of the story from the DB so we can append to the shared ID set
|
||||
Cursor c = dbRO.query(DatabaseConstants.STORY_TABLE,
|
||||
new String[]{DatabaseConstants.STORY_SHARED_USER_IDS},
|
||||
DatabaseConstants.STORY_HASH + " = ?",
|
||||
new String[]{hash},
|
||||
null, null, null);
|
||||
if ((c == null)||(c.getCount() < 1)) {
|
||||
Log.w(this.getClass().getName(), "story removed before finishing mark-shared");
|
||||
closeQuietly(c);
|
||||
return;
|
||||
}
|
||||
c.moveToFirst();
|
||||
String[] sharedUserIds = TextUtils.split(c.getString(c.getColumnIndex(DatabaseConstants.STORY_SHARED_USER_IDS)), ",");
|
||||
closeQuietly(c);
|
||||
|
||||
// the new id to append to the shared list (the current user)
|
||||
String currentUser = PrefsUtils.getUserDetails(context).id;
|
||||
|
||||
// append to set and update DB
|
||||
Set<String> newIds = new HashSet<String>(Arrays.asList(sharedUserIds));
|
||||
newIds.add(currentUser);
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(DatabaseConstants.STORY_SHARED_USER_IDS, TextUtils.join(",", newIds));
|
||||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.STORY_TABLE, values, DatabaseConstants.STORY_HASH + " = ?", new String[]{hash});}
|
||||
}
|
||||
|
||||
public String getStoryText(String hash) {
|
||||
String q = "SELECT " + DatabaseConstants.STORY_TEXT_STORY_TEXT +
|
||||
" FROM " + DatabaseConstants.STORY_TEXT_TABLE +
|
||||
|
@ -852,7 +925,7 @@ public class BlurDatabaseHelper {
|
|||
public List<Comment> getComments(String storyId) {
|
||||
String[] selArgs = new String[] {storyId};
|
||||
String selection = DatabaseConstants.COMMENT_STORYID + " = ?";
|
||||
Cursor c = dbRO.query(DatabaseConstants.COMMENT_TABLE, DatabaseConstants.COMMENT_COLUMNS, selection, selArgs, null, null, null);
|
||||
Cursor c = dbRO.query(DatabaseConstants.COMMENT_TABLE, null, selection, selArgs, null, null, null);
|
||||
List<Comment> comments = new ArrayList<Comment>(c.getCount());
|
||||
while (c.moveToNext()) {
|
||||
comments.add(Comment.fromCursor(c));
|
||||
|
@ -864,7 +937,7 @@ public class BlurDatabaseHelper {
|
|||
public Comment getComment(String storyId, String userId) {
|
||||
String selection = DatabaseConstants.COMMENT_STORYID + " = ? AND " + DatabaseConstants.COMMENT_USERID + " = ?";
|
||||
String[] selArgs = new String[] {storyId, userId};
|
||||
Cursor c = dbRO.query(DatabaseConstants.COMMENT_TABLE, DatabaseConstants.COMMENT_COLUMNS, selection, selArgs, null, null, null);
|
||||
Cursor c = dbRO.query(DatabaseConstants.COMMENT_TABLE, null, selection, selArgs, null, null, null);
|
||||
if (c.getCount() < 1) return null;
|
||||
c.moveToFirst();
|
||||
Comment comment = Comment.fromCursor(c);
|
||||
|
@ -872,6 +945,54 @@ public class BlurDatabaseHelper {
|
|||
return comment;
|
||||
}
|
||||
|
||||
public void insertUpdateComment(String storyId, String feedId, String commentText) {
|
||||
// we can only insert comments as the currently logged-in user
|
||||
String userId = PrefsUtils.getUserDetails(context).id;
|
||||
|
||||
Comment comment = new Comment();
|
||||
comment.id = Comment.constructId(storyId, feedId, userId);
|
||||
comment.storyId = storyId;
|
||||
comment.userId = userId;
|
||||
comment.commentText = commentText;
|
||||
comment.byFriend = true;
|
||||
if (TextUtils.isEmpty(commentText)) {
|
||||
comment.isPseudo = true;
|
||||
}
|
||||
synchronized (RW_MUTEX) {dbRW.insertWithOnConflict(DatabaseConstants.COMMENT_TABLE, null, comment.getValues(), SQLiteDatabase.CONFLICT_REPLACE);}
|
||||
}
|
||||
|
||||
public void setCommentLiked(String storyId, String userId, String feedId, boolean liked) {
|
||||
String commentKey = Comment.constructId(storyId, feedId, userId);
|
||||
// get a fresh copy of the story from the DB so we can append to the shared ID set
|
||||
Cursor c = dbRO.query(DatabaseConstants.COMMENT_TABLE,
|
||||
new String[]{DatabaseConstants.COMMENT_LIKING_USERS},
|
||||
DatabaseConstants.COMMENT_ID + " = ?",
|
||||
new String[]{commentKey},
|
||||
null, null, null);
|
||||
if ((c == null)||(c.getCount() < 1)) {
|
||||
Log.w(this.getClass().getName(), "story removed before finishing mark-shared");
|
||||
closeQuietly(c);
|
||||
return;
|
||||
}
|
||||
c.moveToFirst();
|
||||
String[] likingUserIds = TextUtils.split(c.getString(c.getColumnIndex(DatabaseConstants.COMMENT_LIKING_USERS)), ",");
|
||||
closeQuietly(c);
|
||||
|
||||
// the new id to append/remove from the liking list (the current user)
|
||||
String currentUser = PrefsUtils.getUserDetails(context).id;
|
||||
|
||||
// append to set and update DB
|
||||
Set<String> newIds = new HashSet<String>(Arrays.asList(likingUserIds));
|
||||
if (liked) {
|
||||
newIds.add(currentUser);
|
||||
} else {
|
||||
newIds.remove(currentUser);
|
||||
}
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(DatabaseConstants.COMMENT_LIKING_USERS, TextUtils.join(",", newIds));
|
||||
synchronized (RW_MUTEX) {dbRW.update(DatabaseConstants.COMMENT_TABLE, values, DatabaseConstants.COMMENT_ID + " = ?", new String[]{commentKey});}
|
||||
}
|
||||
|
||||
public UserProfile getUserProfile(String userId) {
|
||||
String[] selArgs = new String[] {userId};
|
||||
String selection = DatabaseConstants.USER_USERID + " = ?";
|
||||
|
@ -884,7 +1005,7 @@ public class BlurDatabaseHelper {
|
|||
public List<Reply> getCommentReplies(String commentId) {
|
||||
String[] selArgs = new String[] {commentId};
|
||||
String selection = DatabaseConstants.REPLY_COMMENTID+ " = ?";
|
||||
Cursor c = dbRO.query(DatabaseConstants.REPLY_TABLE, DatabaseConstants.REPLY_COLUMNS, selection, selArgs, null, null, DatabaseConstants.REPLY_DATE + " DESC");
|
||||
Cursor c = dbRO.query(DatabaseConstants.REPLY_TABLE, null, selection, selArgs, null, null, DatabaseConstants.REPLY_DATE + " ASC");
|
||||
List<Reply> replies = new ArrayList<Reply>(c.getCount());
|
||||
while (c.moveToNext()) {
|
||||
replies.add(Reply.fromCursor(c));
|
||||
|
@ -893,6 +1014,16 @@ public class BlurDatabaseHelper {
|
|||
return replies;
|
||||
}
|
||||
|
||||
public void replyToComment(String storyId, String feedId, String commentUserId, String replyText) {
|
||||
Reply reply = new Reply();
|
||||
reply.commentId = Comment.constructId(storyId, feedId, commentUserId);
|
||||
reply.text = replyText;
|
||||
reply.userId = PrefsUtils.getUserDetails(context).id;
|
||||
reply.date = new Date();
|
||||
reply.id = reply.constructId();
|
||||
synchronized (RW_MUTEX) {dbRW.insertWithOnConflict(DatabaseConstants.REPLY_TABLE, null, reply.getValues(), SQLiteDatabase.CONFLICT_REPLACE);}
|
||||
}
|
||||
|
||||
public static void closeQuietly(Cursor c) {
|
||||
if (c == null) return;
|
||||
try {c.close();} catch (Exception e) {;}
|
||||
|
|
|
@ -113,6 +113,7 @@ public class DatabaseConstants {
|
|||
public static final String COMMENT_SHAREDDATE = "comment_shareddate";
|
||||
public static final String COMMENT_BYFRIEND = "comment_byfriend";
|
||||
public static final String COMMENT_USERID = "comment_userid";
|
||||
public static final String COMMENT_ISPSEUDO = "comment_ispseudo";
|
||||
|
||||
public static final String REPLY_TABLE = "comment_replies";
|
||||
public static final String REPLY_ID = BaseColumns._ID;
|
||||
|
@ -131,11 +132,17 @@ public class DatabaseConstants {
|
|||
public static final String ACTION_UNSAVE = "unsave";
|
||||
public static final String ACTION_SHARE = "share";
|
||||
public static final String ACTION_UNSHARE = "unshare";
|
||||
public static final String ACTION_COMMENT = "comment";
|
||||
public static final String ACTION_LIKE_COMMENT = "like_comment";
|
||||
public static final String ACTION_UNLIKE_COMMENT = "unlike_comment";
|
||||
public static final String ACTION_REPLY = "reply";
|
||||
public static final String ACTION_COMMENT_TEXT = "comment_text";
|
||||
public static final String ACTION_STORY_HASH = "story_hash";
|
||||
public static final String ACTION_FEED_ID = "feed_id";
|
||||
public static final String ACTION_INCLUDE_OLDER = "include_older";
|
||||
public static final String ACTION_INCLUDE_NEWER = "include_newer";
|
||||
public static final String ACTION_STORY_ID = "story_id";
|
||||
public static final String ACTION_SOURCE_USER_ID = "source_user_id";
|
||||
public static final String ACTION_COMMENT_ID = "comment_id";
|
||||
|
||||
static final String FOLDER_SQL = "CREATE TABLE " + FOLDER_TABLE + " (" +
|
||||
FOLDER_NAME + TEXT + " PRIMARY KEY, " +
|
||||
|
@ -189,7 +196,8 @@ public class DatabaseConstants {
|
|||
COMMENT_BYFRIEND + TEXT + ", " +
|
||||
COMMENT_STORYID + TEXT + ", " +
|
||||
COMMENT_TEXT + TEXT + ", " +
|
||||
COMMENT_USERID + TEXT +
|
||||
COMMENT_USERID + TEXT + ", " +
|
||||
COMMENT_ISPSEUDO + TEXT +
|
||||
")";
|
||||
|
||||
static final String REPLY_SQL = "CREATE TABLE " + REPLY_TABLE + " (" +
|
||||
|
@ -266,11 +274,17 @@ public class DatabaseConstants {
|
|||
ACTION_UNSAVE + INTEGER + " DEFAULT 0, " +
|
||||
ACTION_SHARE + INTEGER + " DEFAULT 0, " +
|
||||
ACTION_UNSHARE + INTEGER + " DEFAULT 0, " +
|
||||
ACTION_COMMENT + TEXT + ", " +
|
||||
ACTION_LIKE_COMMENT + INTEGER + " DEFAULT 0, " +
|
||||
ACTION_UNLIKE_COMMENT + INTEGER + " DEFAULT 0, " +
|
||||
ACTION_REPLY + INTEGER + " DEFAULT 0, " +
|
||||
ACTION_COMMENT_TEXT + TEXT + ", " +
|
||||
ACTION_STORY_HASH + TEXT + ", " +
|
||||
ACTION_FEED_ID + TEXT + ", " +
|
||||
ACTION_INCLUDE_OLDER + INTEGER + ", " +
|
||||
ACTION_INCLUDE_NEWER + INTEGER +
|
||||
ACTION_INCLUDE_NEWER + INTEGER + ", " +
|
||||
ACTION_STORY_ID + TEXT + ", " +
|
||||
ACTION_SOURCE_USER_ID + TEXT + ", " +
|
||||
ACTION_COMMENT_ID + TEXT +
|
||||
")";
|
||||
|
||||
public static final String[] FEED_COLUMNS = {
|
||||
|
@ -282,14 +296,6 @@ public class DatabaseConstants {
|
|||
SOCIAL_FEED_ID, SOCIAL_FEED_USERNAME, SOCIAL_FEED_TITLE, SOCIAL_FEED_ICON, SOCIAL_FEED_POSITIVE_COUNT, SOCIAL_FEED_NEUTRAL_COUNT, SOCIAL_FEED_NEGATIVE_COUNT
|
||||
};
|
||||
|
||||
public static final String[] COMMENT_COLUMNS = {
|
||||
COMMENT_ID, COMMENT_STORYID, COMMENT_TEXT, COMMENT_BYFRIEND, COMMENT_USERID, COMMENT_DATE, COMMENT_LIKING_USERS, COMMENT_SHAREDDATE, COMMENT_SOURCE_USERID
|
||||
};
|
||||
|
||||
public static final String[] REPLY_COLUMNS = {
|
||||
REPLY_COMMENTID, REPLY_DATE, REPLY_ID, REPLY_SHORTDATE, REPLY_TEXT, REPLY_USERID
|
||||
};
|
||||
|
||||
public static final String SUM_STORY_TOTAL = "storyTotal";
|
||||
private static String STORY_SUM_TOTAL = " CASE " +
|
||||
"WHEN MAX(" + STORY_INTELLIGENCE_AUTHORS + "," + STORY_INTELLIGENCE_TAGS + "," + STORY_INTELLIGENCE_TITLE + ") > 0 " +
|
||||
|
|
|
@ -12,6 +12,7 @@ import com.newsblur.database.DatabaseConstants;
|
|||
public class Comment implements Serializable {
|
||||
private static final long serialVersionUID = -2018705258520565390L;
|
||||
|
||||
// we almost always override API version with sensible PK constructed by concating story, feed, and user IDs
|
||||
public String id;
|
||||
|
||||
@SerializedName("comments")
|
||||
|
@ -36,8 +37,12 @@ public class Comment implements Serializable {
|
|||
|
||||
public String storyId;
|
||||
|
||||
// not vended by API, but we set it depending on which comment block of the response in which it appeared
|
||||
public boolean byFriend = false;
|
||||
|
||||
// means this "comment" is actually a text-less share, which is identical to a comment, but included in a different list in the story member
|
||||
public boolean isPseudo = false;
|
||||
|
||||
public ContentValues getValues() {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(DatabaseConstants.COMMENT_DATE, date);
|
||||
|
@ -49,11 +54,11 @@ public class Comment implements Serializable {
|
|||
values.put(DatabaseConstants.COMMENT_SOURCE_USERID, sourceUserId);
|
||||
values.put(DatabaseConstants.COMMENT_USERID, userId);
|
||||
values.put(DatabaseConstants.COMMENT_ID, id);
|
||||
values.put(DatabaseConstants.COMMENT_ISPSEUDO, isPseudo ? "true" : "false");
|
||||
return values;
|
||||
}
|
||||
|
||||
public static Comment fromCursor(final Cursor cursor) {
|
||||
|
||||
Comment comment = new Comment();
|
||||
comment.date = cursor.getString(cursor.getColumnIndex(DatabaseConstants.COMMENT_DATE));
|
||||
comment.sharedDate = cursor.getString(cursor.getColumnIndex(DatabaseConstants.COMMENT_SHAREDDATE));
|
||||
|
@ -65,8 +70,12 @@ public class Comment implements Serializable {
|
|||
comment.likingUsers = TextUtils.split(likingUsers, ",");
|
||||
comment.sourceUserId = cursor.getString(cursor.getColumnIndex(DatabaseConstants.COMMENT_SOURCE_USERID));
|
||||
comment.id = cursor.getString(cursor.getColumnIndex(DatabaseConstants.COMMENT_ID));
|
||||
|
||||
comment.isPseudo = Boolean.parseBoolean(cursor.getString(cursor.getColumnIndex(DatabaseConstants.COMMENT_ISPSEUDO)));
|
||||
return comment;
|
||||
}
|
||||
|
||||
}
|
||||
public static String constructId(String storyId, String feedId, String userId) {
|
||||
return TextUtils.concat(feedId, storyId, userId).toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.util.Date;
|
|||
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import com.newsblur.database.DatabaseConstants;
|
||||
|
@ -24,6 +25,7 @@ public class Reply {
|
|||
@SerializedName("date")
|
||||
public Date date;
|
||||
|
||||
// NB: this is the commentId that we generate, not the API one
|
||||
public String commentId;
|
||||
|
||||
public ContentValues getValues() {
|
||||
|
@ -47,4 +49,9 @@ public class Reply {
|
|||
reply.userId = cursor.getString(cursor.getColumnIndex(DatabaseConstants.REPLY_USERID));
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
|
||||
// construct a string we can internally use as a PK
|
||||
public String constructId() {
|
||||
return TextUtils.concat(commentId, "_", Long.toString(date.getTime())).toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,6 +79,10 @@ public class Story implements Serializable {
|
|||
@SerializedName("friend_comments")
|
||||
public Comment[] friendsComments;
|
||||
|
||||
// these are pseudo-comments that allow replying to empty shares
|
||||
@SerializedName("friend_shares")
|
||||
public Comment[] friendsShares;
|
||||
|
||||
@SerializedName("intelligence")
|
||||
public Intelligence intelligence = new Intelligence();
|
||||
|
||||
|
|
|
@ -12,6 +12,10 @@ import android.view.ViewGroup;
|
|||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.FindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.domain.Classifier;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
|
@ -25,12 +29,16 @@ public class ClassifierDialogFragment extends DialogFragment {
|
|||
private static final String CALLBACK = "callback";
|
||||
private static final String CLASSIFIER = "classifier";
|
||||
|
||||
@FindView(R.id.tag_negative) ImageView classifyNegative;
|
||||
@FindView(R.id.tag_positive) ImageView classifyPositive;
|
||||
@FindView(R.id.dialog_message) TextView message;
|
||||
|
||||
private String key, feedId;
|
||||
private Classifier classifier;
|
||||
private int classifierType;
|
||||
|
||||
private TagUpdateCallback tagCallback;
|
||||
|
||||
private Map<String,Integer> typeHashMap;
|
||||
|
||||
public static ClassifierDialogFragment newInstance(TagUpdateCallback callbackInterface, final String feedId, final Classifier classifier, final String key, final int classifierType) {
|
||||
ClassifierDialogFragment frag = new ClassifierDialogFragment();
|
||||
|
@ -64,49 +72,10 @@ public class ClassifierDialogFragment extends DialogFragment {
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
||||
View v = inflater.inflate(R.layout.fragment_classify_dialog, container, false);
|
||||
final TextView message = (TextView) v.findViewById(R.id.dialog_message);
|
||||
message.setText(key);
|
||||
ButterKnife.bind(this, v);
|
||||
|
||||
final ImageView classifyPositive = (ImageView) v.findViewById(R.id.tag_positive);
|
||||
final ImageView classifyNegative = (ImageView) v.findViewById(R.id.tag_negative);
|
||||
typeHashMap = classifier.getMapForType(classifierType);
|
||||
|
||||
final Map<String,Integer> typeHashMap = classifier.getMapForType(classifierType);
|
||||
|
||||
setupTypeUI(classifyPositive, classifyNegative, typeHashMap);
|
||||
|
||||
classifyNegative.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int classifierAction = (typeHashMap.containsKey(key) && typeHashMap.get(key) == Classifier.DISLIKE) ? Classifier.CLEAR_DISLIKE : Classifier.DISLIKE;
|
||||
FeedUtils.updateClassifier(feedId, key, classifier, classifierType, classifierAction, getActivity());
|
||||
tagCallback.updateTagView(key, classifierType,classifierAction);
|
||||
ClassifierDialogFragment.this.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
message.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
FeedUtils.updateClassifier(feedId, key, classifier, classifierType, Classifier.CLEAR_LIKE, getActivity());
|
||||
tagCallback.updateTagView(key, classifierType, Classifier.CLEAR_LIKE);
|
||||
ClassifierDialogFragment.this.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
classifyPositive.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int classifierAction = (typeHashMap.containsKey(key) && typeHashMap.get(key) == Classifier.LIKE) ? Classifier.CLEAR_LIKE : Classifier.LIKE;
|
||||
FeedUtils.updateClassifier(feedId, key, classifier, classifierType, classifierAction, getActivity());
|
||||
tagCallback.updateTagView(key, classifierType,classifierAction);
|
||||
ClassifierDialogFragment.this.dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
private void setupTypeUI(final ImageView classifyPositive, final ImageView classifyNegative, final Map<String, Integer> typeHashMap) {
|
||||
if (typeHashMap != null && typeHashMap.containsKey(key)) {
|
||||
switch (typeHashMap.get(key)) {
|
||||
case Classifier.LIKE:
|
||||
|
@ -116,8 +85,33 @@ public class ClassifierDialogFragment extends DialogFragment {
|
|||
classifyNegative.setImageResource(R.drawable.tag_negative_already);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
message.setText(key);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@OnClick(R.id.tag_negative) void onClickNegative() {
|
||||
int classifierAction = (typeHashMap.containsKey(key) && typeHashMap.get(key) == Classifier.DISLIKE) ? Classifier.CLEAR_DISLIKE : Classifier.DISLIKE;
|
||||
FeedUtils.updateClassifier(feedId, key, classifier, classifierType, classifierAction, getActivity());
|
||||
tagCallback.updateTagView(key, classifierType,classifierAction);
|
||||
ClassifierDialogFragment.this.dismiss();
|
||||
}
|
||||
|
||||
@OnClick(R.id.tag_positive) void onClickPositive() {
|
||||
int classifierAction = (typeHashMap.containsKey(key) && typeHashMap.get(key) == Classifier.LIKE) ? Classifier.CLEAR_LIKE : Classifier.LIKE;
|
||||
FeedUtils.updateClassifier(feedId, key, classifier, classifierType, classifierAction, getActivity());
|
||||
tagCallback.updateTagView(key, classifierType,classifierAction);
|
||||
ClassifierDialogFragment.this.dismiss();
|
||||
}
|
||||
|
||||
@OnClick(R.id.dialog_message) void onClickNeutral() {
|
||||
FeedUtils.updateClassifier(feedId, key, classifier, classifierType, Classifier.CLEAR_LIKE, getActivity());
|
||||
tagCallback.updateTagView(key, classifierType, Classifier.CLEAR_LIKE);
|
||||
ClassifierDialogFragment.this.dismiss();
|
||||
}
|
||||
|
||||
|
||||
public interface TagUpdateCallback extends Serializable{
|
||||
public void updateTagView(String value, int classifierType, int classifierAction);
|
||||
|
|
|
@ -10,6 +10,10 @@ import android.view.Window;
|
|||
import android.view.WindowManager;
|
||||
import android.widget.RadioButton;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.FindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.util.DefaultFeedView;
|
||||
import com.newsblur.util.DefaultFeedViewChangedListener;
|
||||
|
@ -17,10 +21,12 @@ import com.newsblur.util.DefaultFeedViewChangedListener;
|
|||
/**
|
||||
* Created by mark on 09/01/2014.
|
||||
*/
|
||||
public class DefaultFeedViewDialogFragment extends DialogFragment implements View.OnClickListener {
|
||||
public class DefaultFeedViewDialogFragment extends DialogFragment {
|
||||
|
||||
private static String CURRENT_VIEW = "currentView";
|
||||
private DefaultFeedView currentValue;
|
||||
@FindView(R.id.radio_story) RadioButton storyButton;
|
||||
@FindView(R.id.radio_text) RadioButton textButton;
|
||||
|
||||
public static DefaultFeedViewDialogFragment newInstance(DefaultFeedView currentValue) {
|
||||
DefaultFeedViewDialogFragment dialog = new DefaultFeedViewDialogFragment();
|
||||
|
@ -40,11 +46,9 @@ public class DefaultFeedViewDialogFragment extends DialogFragment implements Vie
|
|||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
||||
currentValue = (DefaultFeedView) getArguments().getSerializable(CURRENT_VIEW);
|
||||
View v = inflater.inflate(R.layout.defaultfeedview_dialog, null);
|
||||
RadioButton storyButton = (RadioButton) v.findViewById(R.id.radio_story);
|
||||
storyButton.setOnClickListener(this);
|
||||
ButterKnife.bind(this, v);
|
||||
|
||||
storyButton.setChecked(currentValue == DefaultFeedView.STORY);
|
||||
RadioButton textButton = (RadioButton) v.findViewById(R.id.radio_text);
|
||||
textButton.setOnClickListener(this);
|
||||
textButton.setChecked(currentValue == DefaultFeedView.TEXT);
|
||||
|
||||
getDialog().getWindow().setFlags(WindowManager.LayoutParams.FLAG_DITHER, WindowManager.LayoutParams.FLAG_DITHER);
|
||||
|
@ -54,19 +58,18 @@ public class DefaultFeedViewDialogFragment extends DialogFragment implements Vie
|
|||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
DefaultFeedViewChangedListener listener = (DefaultFeedViewChangedListener)getActivity();
|
||||
if (v.getId() == R.id.radio_story) {
|
||||
if (currentValue == DefaultFeedView.TEXT) {
|
||||
listener.defaultFeedViewChanged(DefaultFeedView.STORY);
|
||||
}
|
||||
} else {
|
||||
if (currentValue == DefaultFeedView.STORY) {
|
||||
listener.defaultFeedViewChanged(DefaultFeedView.TEXT);
|
||||
}
|
||||
@OnClick(R.id.radio_story) void selectStory() {
|
||||
if (currentValue != DefaultFeedView.STORY) {
|
||||
((DefaultFeedViewChangedListener) getActivity()).defaultFeedViewChanged(DefaultFeedView.STORY);
|
||||
}
|
||||
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_text) void selectText() {
|
||||
if (currentValue != DefaultFeedView.TEXT) {
|
||||
((DefaultFeedViewChangedListener) getActivity()).defaultFeedViewChanged(DefaultFeedView.TEXT);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,8 +19,13 @@ import android.view.View;
|
|||
import android.view.View.OnCreateContextMenuListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ExpandableListView;
|
||||
import android.widget.ExpandableListView.OnChildClickListener;
|
||||
import android.widget.ExpandableListView.OnGroupClickListener;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.FindView;
|
||||
import butterknife.OnChildClick;
|
||||
import butterknife.OnGroupClick;
|
||||
import butterknife.OnGroupCollapse;
|
||||
import butterknife.OnGroupExpand;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.AllStoriesItemsList;
|
||||
|
@ -42,12 +47,8 @@ import com.newsblur.util.PrefsUtils;
|
|||
import com.newsblur.util.StateFilter;
|
||||
import com.newsblur.util.UIUtils;
|
||||
|
||||
public class FolderListFragment extends NbFragment implements OnGroupClickListener,
|
||||
OnChildClickListener,
|
||||
OnCreateContextMenuListener,
|
||||
LoaderManager.LoaderCallbacks<Cursor>,
|
||||
ExpandableListView.OnGroupCollapseListener,
|
||||
ExpandableListView.OnGroupExpandListener {
|
||||
public class FolderListFragment extends NbFragment implements OnCreateContextMenuListener,
|
||||
LoaderManager.LoaderCallbacks<Cursor> {
|
||||
|
||||
private static final int SOCIALFEEDS_LOADER = 1;
|
||||
private static final int FOLDERS_LOADER = 2;
|
||||
|
@ -57,7 +58,7 @@ public class FolderListFragment extends NbFragment implements OnGroupClickListen
|
|||
private FolderListAdapter adapter;
|
||||
public StateFilter currentState = StateFilter.SOME;
|
||||
private SharedPreferences sharedPreferences;
|
||||
private ExpandableListView list;
|
||||
@FindView(R.id.folderfeed_list) ExpandableListView list;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -139,7 +140,8 @@ public class FolderListFragment extends NbFragment implements OnGroupClickListen
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_folderfeedlist, container);
|
||||
list = (ExpandableListView) v.findViewById(R.id.folderfeed_list);
|
||||
ButterKnife.bind(this, v);
|
||||
|
||||
list.setGroupIndicator(getResources().getDrawable(R.drawable.transparent));
|
||||
list.setOnCreateContextMenuListener(this);
|
||||
|
||||
|
@ -150,10 +152,6 @@ public class FolderListFragment extends NbFragment implements OnGroupClickListen
|
|||
|
||||
list.setChildDivider(getActivity().getResources().getDrawable(R.drawable.divider_light));
|
||||
list.setAdapter(adapter);
|
||||
list.setOnGroupClickListener(this);
|
||||
list.setOnChildClickListener(this);
|
||||
list.setOnGroupCollapseListener(this);
|
||||
list.setOnGroupExpandListener(this);
|
||||
|
||||
// Main activity needs to listen for scrolls to prevent refresh from firing unnecessarily
|
||||
list.setOnScrollListener((android.widget.AbsListView.OnScrollListener) getActivity());
|
||||
|
@ -249,8 +247,7 @@ public class FolderListFragment extends NbFragment implements OnGroupClickListen
|
|||
hasUpdated();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onGroupClick(ExpandableListView list, View group, int groupPosition, long id) {
|
||||
@OnGroupClick(R.id.folderfeed_list) boolean onGroupClick(ExpandableListView list, View group, int groupPosition, long id) {
|
||||
if (adapter.isFolderRoot(groupPosition)) {
|
||||
Intent i = new Intent(getActivity(), AllStoriesItemsList.class);
|
||||
i.putExtra(ItemsList.EXTRA_STATE, currentState);
|
||||
|
@ -279,8 +276,7 @@ public class FolderListFragment extends NbFragment implements OnGroupClickListen
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGroupExpand(int groupPosition) {
|
||||
@OnGroupExpand(R.id.folderfeed_list) void onGroupExpand(int groupPosition) {
|
||||
// these shouldn't ever be collapsible
|
||||
if (adapter.isFolderRoot(groupPosition)) return;
|
||||
if (adapter.isRowSavedStories(groupPosition)) return;
|
||||
|
@ -296,8 +292,7 @@ public class FolderListFragment extends NbFragment implements OnGroupClickListen
|
|||
checkOpenFolderPreferences();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGroupCollapse(int groupPosition) {
|
||||
@OnGroupCollapse(R.id.folderfeed_list) void onGroupCollapse(int groupPosition) {
|
||||
// these shouldn't ever be collapsible
|
||||
if (adapter.isFolderRoot(groupPosition)) return;
|
||||
if (adapter.isRowSavedStories(groupPosition)) return;
|
||||
|
@ -311,8 +306,7 @@ public class FolderListFragment extends NbFragment implements OnGroupClickListen
|
|||
adapter.forceRecount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onChildClick(ExpandableListView list, View childView, int groupPosition, int childPosition, long id) {
|
||||
@OnChildClick(R.id.folderfeed_list) boolean onChildClick(ExpandableListView list, View childView, int groupPosition, int childPosition, long id) {
|
||||
String childName = adapter.getChild(groupPosition, childPosition);
|
||||
if (groupPosition == FolderListAdapter.ALL_SHARED_STORIES_GROUP_POSITION) {
|
||||
SocialFeed socialFeed = adapter.getSocialFeed(childName);
|
||||
|
|
|
@ -24,6 +24,9 @@ import android.widget.AdapterView.OnItemClickListener;
|
|||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.FindView;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.ItemsList;
|
||||
import com.newsblur.database.DatabaseConstants;
|
||||
|
@ -43,7 +46,7 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
|
|||
public static int ITEMLIST_LOADER = 0x01;
|
||||
|
||||
protected ItemsList activity;
|
||||
protected ListView itemList;
|
||||
@FindView(R.id.itemlistfragment_list) ListView itemList;
|
||||
protected StoryItemsAdapter adapter;
|
||||
protected DefaultFeedView defaultFeedView;
|
||||
protected StateFilter currentState;
|
||||
|
@ -55,7 +58,7 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
|
|||
// loading indicator for when stories are present and fresh (at bottom of list)
|
||||
protected ProgressThrobber footerProgressView;
|
||||
// loading indicator for when no stories are loaded yet (instead of list)
|
||||
protected ProgressThrobber emptyProgressView;
|
||||
@FindView(R.id.empty_view_loading_throb) ProgressThrobber emptyProgressView;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -68,8 +71,8 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_itemlist, null);
|
||||
itemList = (ListView) v.findViewById(R.id.itemlistfragment_list);
|
||||
emptyProgressView = (ProgressThrobber) v.findViewById(R.id.empty_view_loading_throb);
|
||||
ButterKnife.bind(this, v);
|
||||
|
||||
emptyProgressView.setColors(getResources().getColor(R.color.refresh_1),
|
||||
getResources().getColor(R.color.refresh_2),
|
||||
getResources().getColor(R.color.refresh_3),
|
||||
|
@ -148,10 +151,6 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
|
|||
}
|
||||
|
||||
private void updateLoadingMessage() {
|
||||
View v = this.getView();
|
||||
if (v == null) return; // we might have beat construction?
|
||||
|
||||
ListView itemList = (ListView) v.findViewById(R.id.itemlistfragment_list);
|
||||
if (itemList == null) {
|
||||
Log.w(this.getClass().getName(), "ItemListFragment does not have the expected ListView.");
|
||||
return;
|
||||
|
@ -168,10 +167,6 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
|
|||
}
|
||||
|
||||
public void scrollToTop() {
|
||||
View v = this.getView();
|
||||
if (v == null) return; // we might have beat construction?
|
||||
|
||||
ListView itemList = (ListView) v.findViewById(R.id.itemlistfragment_list);
|
||||
if (itemList == null) {
|
||||
Log.w(this.getClass().getName(), "ItemListFragment does not have the expected ListView.");
|
||||
return;
|
||||
|
@ -293,8 +288,8 @@ public abstract class ItemListFragment extends NbFragment implements OnScrollLis
|
|||
FeedUtils.markFeedsRead(getFeedSet(), null, story.timestamp, activity);
|
||||
return true;
|
||||
|
||||
case R.id.menu_shared:
|
||||
FeedUtils.shareStory(story, activity);
|
||||
case R.id.menu_send_story:
|
||||
FeedUtils.sendStory(story, activity);
|
||||
return true;
|
||||
|
||||
case R.id.menu_save_story:
|
||||
|
|
|
@ -16,6 +16,9 @@ import android.widget.ProgressBar;
|
|||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.FindView;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.Login;
|
||||
import com.newsblur.activity.Main;
|
||||
|
@ -27,9 +30,11 @@ import com.newsblur.util.UIUtils;
|
|||
public class LoginProgressFragment extends Fragment {
|
||||
|
||||
private APIManager apiManager;
|
||||
private TextView updateStatus, retrievingFeeds;
|
||||
private ImageView loginProfilePicture;
|
||||
private ProgressBar feedProgress, loggingInProgress;
|
||||
@FindView(R.id.login_logging_in) TextView updateStatus;
|
||||
@FindView(R.id.login_retrieving_feeds) TextView retrievingFeeds;
|
||||
@FindView(R.id.login_profile_picture) ImageView loginProfilePicture;
|
||||
@FindView(R.id.login_feed_progress) ProgressBar feedProgress;
|
||||
@FindView(R.id.login_logging_in_progress) ProgressBar loggingInProgress;
|
||||
private LoginTask loginTask;
|
||||
private String username;
|
||||
private String password;
|
||||
|
@ -56,12 +61,7 @@ public class LoginProgressFragment extends Fragment {
|
|||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_loginprogress, null);
|
||||
|
||||
updateStatus = (TextView) v.findViewById(R.id.login_logging_in);
|
||||
retrievingFeeds = (TextView) v.findViewById(R.id.login_retrieving_feeds);
|
||||
feedProgress = (ProgressBar) v.findViewById(R.id.login_feed_progress);
|
||||
loggingInProgress = (ProgressBar) v.findViewById(R.id.login_logging_in_progress);
|
||||
loginProfilePicture = (ImageView) v.findViewById(R.id.login_profile_picture);
|
||||
ButterKnife.bind(this, v);
|
||||
|
||||
loginTask = new LoginTask();
|
||||
loginTask.execute();
|
||||
|
@ -70,7 +70,6 @@ public class LoginProgressFragment extends Fragment {
|
|||
}
|
||||
|
||||
private class LoginTask extends AsyncTask<Void, Void, NewsBlurResponse> {
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
Animation a = AnimationUtils.loadAnimation(getActivity(), R.anim.text_up);
|
||||
|
@ -104,7 +103,6 @@ public class LoginProgressFragment extends Fragment {
|
|||
|
||||
Intent startMain = new Intent(getActivity(), Main.class);
|
||||
c.startActivity(startMain);
|
||||
|
||||
} else {
|
||||
UIUtils.safeToast(c, result.getErrorMessage(), Toast.LENGTH_LONG);
|
||||
startActivity(new Intent(c, Login.class));
|
||||
|
|
|
@ -16,29 +16,28 @@ import android.widget.TextView;
|
|||
import android.widget.TextView.OnEditorActionListener;
|
||||
import android.widget.ViewSwitcher;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.FindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.LoginProgress;
|
||||
import com.newsblur.activity.RegisterProgress;
|
||||
|
||||
public class LoginRegisterFragment extends Fragment implements OnClickListener {
|
||||
public class LoginRegisterFragment extends Fragment {
|
||||
|
||||
private EditText username, password;
|
||||
private ViewSwitcher viewSwitcher;
|
||||
|
||||
private EditText register_username, register_password, register_email;
|
||||
@FindView(R.id.login_username) EditText username;
|
||||
@FindView(R.id.login_password) EditText password;
|
||||
@FindView(R.id.registration_username) EditText register_username;
|
||||
@FindView(R.id.registration_password) EditText register_password;
|
||||
@FindView(R.id.registration_email) EditText register_email;
|
||||
@FindView(R.id.login_viewswitcher) ViewSwitcher viewSwitcher;
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
final View v = inflater.inflate(R.layout.fragment_loginregister, container, false);
|
||||
ButterKnife.bind(this, v);
|
||||
|
||||
final Button loginButton = (Button) v.findViewById(R.id.login_button);
|
||||
final Button registerButton = (Button) v.findViewById(R.id.registration_button);
|
||||
loginButton.setOnClickListener(this);
|
||||
registerButton.setOnClickListener(this);
|
||||
|
||||
username = (EditText) v.findViewById(R.id.login_username);
|
||||
password = (EditText) v.findViewById(R.id.login_password);
|
||||
|
||||
password.setOnEditorActionListener(new OnEditorActionListener() {
|
||||
@Override
|
||||
public boolean onEditorAction(TextView arg0, int actionId, KeyEvent event) {
|
||||
|
@ -49,10 +48,6 @@ public class LoginRegisterFragment extends Fragment implements OnClickListener {
|
|||
}
|
||||
});
|
||||
|
||||
register_username = (EditText) v.findViewById(R.id.registration_username);
|
||||
register_password = (EditText) v.findViewById(R.id.registration_password);
|
||||
register_email = (EditText) v.findViewById(R.id.registration_email);
|
||||
|
||||
register_email.setOnEditorActionListener(new OnEditorActionListener() {
|
||||
@Override
|
||||
public boolean onEditorAction(TextView arg0, int actionId, KeyEvent event) {
|
||||
|
@ -63,44 +58,10 @@ public class LoginRegisterFragment extends Fragment implements OnClickListener {
|
|||
}
|
||||
});
|
||||
|
||||
viewSwitcher = (ViewSwitcher) v.findViewById(R.id.login_viewswitcher);
|
||||
|
||||
TextView changeToLogin = (TextView) v.findViewById(R.id.login_change_to_login);
|
||||
TextView changeToRegister = (TextView) v.findViewById(R.id.login_change_to_register);
|
||||
|
||||
changeToLogin.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View arg0) {
|
||||
viewSwitcher.showPrevious();
|
||||
}
|
||||
});
|
||||
|
||||
changeToRegister.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View arg0) {
|
||||
viewSwitcher.showNext();
|
||||
}
|
||||
});
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View viewClicked) {
|
||||
if (viewClicked.getId() == R.id.login_button) {
|
||||
logIn();
|
||||
} else if (viewClicked.getId() == R.id.registration_button) {
|
||||
signUp();
|
||||
}
|
||||
}
|
||||
|
||||
private void logIn() {
|
||||
@OnClick(R.id.login_button) void logIn() {
|
||||
if (!TextUtils.isEmpty(username.getText().toString())) {
|
||||
Intent i = new Intent(getActivity(), LoginProgress.class);
|
||||
i.putExtra("username", username.getText().toString());
|
||||
|
@ -109,7 +70,7 @@ public class LoginRegisterFragment extends Fragment implements OnClickListener {
|
|||
}
|
||||
}
|
||||
|
||||
private void signUp() {
|
||||
@OnClick(R.id.registration_button) void signUp() {
|
||||
Intent i = new Intent(getActivity(), RegisterProgress.class);
|
||||
i.putExtra("username", register_username.getText().toString());
|
||||
i.putExtra("password", register_password.getText().toString());
|
||||
|
@ -117,5 +78,12 @@ public class LoginRegisterFragment extends Fragment implements OnClickListener {
|
|||
startActivity(i);
|
||||
}
|
||||
|
||||
@OnClick(R.id.login_change_to_login) void showLogin() {
|
||||
viewSwitcher.showPrevious();
|
||||
}
|
||||
|
||||
@OnClick(R.id.login_change_to_register) void showRegister() {
|
||||
viewSwitcher.showNext();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,25 +1,29 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.app.DialogFragment;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.RadioButton;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.FindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.util.ReadFilter;
|
||||
import com.newsblur.util.ReadFilterChangedListener;
|
||||
|
||||
public class ReadFilterDialogFragment extends DialogFragment implements OnClickListener {
|
||||
public class ReadFilterDialogFragment extends DialogFragment {
|
||||
|
||||
private static String CURRENT_FILTER = "currentFilter";
|
||||
private ReadFilter currentValue;
|
||||
@FindView(R.id.radio_all) RadioButton allButton;
|
||||
@FindView(R.id.radio_unread) RadioButton unreadButton;
|
||||
|
||||
public static ReadFilterDialogFragment newInstance(ReadFilter currentValue) {
|
||||
ReadFilterDialogFragment dialog = new ReadFilterDialogFragment();
|
||||
|
@ -39,11 +43,9 @@ public class ReadFilterDialogFragment extends DialogFragment implements OnClickL
|
|||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
||||
currentValue = (ReadFilter) getArguments().getSerializable(CURRENT_FILTER);
|
||||
View v = inflater.inflate(R.layout.readfilter_dialog, null);
|
||||
RadioButton allButton = (RadioButton) v.findViewById(R.id.radio_all);
|
||||
allButton.setOnClickListener(this);
|
||||
ButterKnife.bind(this, v);
|
||||
|
||||
allButton.setChecked(currentValue == ReadFilter.ALL);
|
||||
RadioButton unreadButton = (RadioButton) v.findViewById(R.id.radio_unread);
|
||||
unreadButton.setOnClickListener(this);
|
||||
unreadButton.setChecked(currentValue == ReadFilter.UNREAD);
|
||||
|
||||
getDialog().getWindow().setFlags(WindowManager.LayoutParams.FLAG_DITHER, WindowManager.LayoutParams.FLAG_DITHER);
|
||||
|
@ -53,19 +55,18 @@ public class ReadFilterDialogFragment extends DialogFragment implements OnClickL
|
|||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
ReadFilterChangedListener listener = (ReadFilterChangedListener)getActivity();
|
||||
if (v.getId() == R.id.radio_all) {
|
||||
if (currentValue == ReadFilter.UNREAD) {
|
||||
listener.readFilterChanged(ReadFilter.ALL);
|
||||
}
|
||||
} else {
|
||||
if (currentValue == ReadFilter.ALL) {
|
||||
listener.readFilterChanged(ReadFilter.UNREAD);
|
||||
}
|
||||
@OnClick(R.id.radio_all) void selectAll() {
|
||||
if (currentValue != ReadFilter.ALL) {
|
||||
((ReadFilterChangedListener) getActivity()).readFilterChanged(ReadFilter.ALL);
|
||||
}
|
||||
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_unread) void selectUnread() {
|
||||
if (currentValue != ReadFilter.UNREAD) {
|
||||
((ReadFilterChangedListener) getActivity()).readFilterChanged(ReadFilter.UNREAD);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,14 +31,16 @@ import android.widget.ImageView;
|
|||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.FindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.NewsBlurApplication;
|
||||
import com.newsblur.activity.Reading;
|
||||
import com.newsblur.domain.Classifier;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.domain.UserDetails;
|
||||
import com.newsblur.network.APIManager;
|
||||
import com.newsblur.network.SetupCommentSectionTask;
|
||||
import com.newsblur.service.NBSyncService;
|
||||
import com.newsblur.util.DefaultFeedView;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
|
@ -59,28 +61,28 @@ import java.util.regex.Pattern;
|
|||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class ReadingItemFragment extends NbFragment implements ClassifierDialogFragment.TagUpdateCallback, ShareDialogFragment.SharedCallbackDialog {
|
||||
public class ReadingItemFragment extends NbFragment implements ClassifierDialogFragment.TagUpdateCallback {
|
||||
|
||||
public static final String TEXT_SIZE_CHANGED = "textSizeChanged";
|
||||
public static final String TEXT_SIZE_VALUE = "textSizeChangeValue";
|
||||
public Story story;
|
||||
private LayoutInflater inflater;
|
||||
private APIManager apiManager;
|
||||
private ImageLoader imageLoader;
|
||||
private String feedColor, feedTitle, feedFade, feedBorder, feedIconUrl, faviconText;
|
||||
private Classifier classifier;
|
||||
private NewsblurWebview web;
|
||||
private BroadcastReceiver receiver;
|
||||
private TextView itemAuthors;
|
||||
private TextView itemFeed;
|
||||
@FindView(R.id.reading_item_authors) TextView itemAuthors;
|
||||
@FindView(R.id.reading_feed_title) TextView itemFeed;
|
||||
private boolean displayFeedDetails;
|
||||
private FlowLayout tagContainer;
|
||||
private View view;
|
||||
private UserDetails user;
|
||||
public String previouslySavedShareText;
|
||||
private ImageView feedIcon;
|
||||
private Reading activity;
|
||||
private DefaultFeedView selectedFeedView;
|
||||
@FindView(R.id.save_story_button) Button saveButton;
|
||||
@FindView(R.id.share_story_button) Button shareButton;
|
||||
|
||||
/** The story HTML, as provided by the 'content' element of the stories API. */
|
||||
private String storyContent;
|
||||
|
@ -126,7 +128,6 @@ public class ReadingItemFragment extends NbFragment implements ClassifierDialogF
|
|||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
imageLoader = ((NewsBlurApplication) getActivity().getApplicationContext()).getImageLoader();
|
||||
apiManager = new APIManager(getActivity());
|
||||
story = getArguments() != null ? (Story) getArguments().getSerializable("story") : null;
|
||||
|
||||
inflater = getActivity().getLayoutInflater();
|
||||
|
@ -182,21 +183,17 @@ public class ReadingItemFragment extends NbFragment implements ClassifierDialogF
|
|||
}
|
||||
|
||||
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
|
||||
|
||||
view = inflater.inflate(R.layout.fragment_readingitem, null);
|
||||
ButterKnife.bind(this, view);
|
||||
|
||||
web = (NewsblurWebview) view.findViewById(R.id.reading_webview);
|
||||
registerForContextMenu(web);
|
||||
|
||||
setupItemMetadata();
|
||||
setupShareButton();
|
||||
setupSaveButton();
|
||||
updateShareButton();
|
||||
updateSaveButton();
|
||||
|
||||
if (story.sharedUserIds.length > 0 || story.commentCount > 0 ) {
|
||||
view.findViewById(R.id.reading_share_bar).setVisibility(View.VISIBLE);
|
||||
view.findViewById(R.id.share_bar_underline).setVisibility(View.VISIBLE);
|
||||
setupItemCommentsAndShares(view);
|
||||
}
|
||||
setupItemCommentsAndShares();
|
||||
|
||||
NonfocusScrollview scrollView = (NonfocusScrollview) view.findViewById(R.id.reading_scrollview);
|
||||
scrollView.registerScrollChangeListener(this.activity);
|
||||
|
@ -261,51 +258,38 @@ public class ReadingItemFragment extends NbFragment implements ClassifierDialogF
|
|||
super.onCreateContextMenu(menu, v, menuInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupSaveButton() {
|
||||
final Button saveButton = (Button) view.findViewById(R.id.save_story_button);
|
||||
saveButton.setText(story.starred ? R.string.unsave_this : R.string.save_this);
|
||||
|
||||
saveButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (story.starred) {
|
||||
FeedUtils.setStorySaved(story, false, activity);
|
||||
} else {
|
||||
FeedUtils.setStorySaved(story,true, activity);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@OnClick(R.id.save_story_button) void clickSave() {
|
||||
if (story.starred) {
|
||||
FeedUtils.setStorySaved(story, false, activity);
|
||||
} else {
|
||||
FeedUtils.setStorySaved(story,true, activity);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSaveButton() {
|
||||
if (view == null) { return; }
|
||||
Button saveButton = (Button) view.findViewById(R.id.save_story_button);
|
||||
if (saveButton == null) { return; }
|
||||
if (saveButton == null) return;
|
||||
saveButton.setText(story.starred ? R.string.unsave_this : R.string.save_this);
|
||||
}
|
||||
|
||||
private void setupShareButton() {
|
||||
Button shareButton = (Button) view.findViewById(R.id.share_story_button);
|
||||
@OnClick(R.id.share_story_button) void clickShare() {
|
||||
DialogFragment newFragment = ShareDialogFragment.newInstance(story, sourceUserId);
|
||||
newFragment.show(getFragmentManager(), "dialog");
|
||||
}
|
||||
|
||||
private void updateShareButton() {
|
||||
if (shareButton == null) return;
|
||||
for (String userId : story.sharedUserIds) {
|
||||
if (TextUtils.equals(userId, user.id)) {
|
||||
shareButton.setText(R.string.edit);
|
||||
break;
|
||||
shareButton.setText(R.string.already_shared);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
shareButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
DialogFragment newFragment = ShareDialogFragment.newInstance(ReadingItemFragment.this, story, previouslySavedShareText, sourceUserId);
|
||||
newFragment.show(getFragmentManager(), "dialog");
|
||||
}
|
||||
});
|
||||
shareButton.setText(R.string.share_this);
|
||||
}
|
||||
|
||||
private void setupItemCommentsAndShares(final View view) {
|
||||
new SetupCommentSectionTask(getActivity(), view, getFragmentManager(), inflater, apiManager, story, imageLoader).execute();
|
||||
private void setupItemCommentsAndShares() {
|
||||
new SetupCommentSectionTask(getActivity(), view, getFragmentManager(), inflater, story, imageLoader).execute();
|
||||
}
|
||||
|
||||
private void setupItemMetadata() {
|
||||
|
@ -313,8 +297,6 @@ public class ReadingItemFragment extends NbFragment implements ClassifierDialogF
|
|||
View feedHeaderBorder = view.findViewById(R.id.item_feed_border);
|
||||
TextView itemTitle = (TextView) view.findViewById(R.id.reading_item_title);
|
||||
TextView itemDate = (TextView) view.findViewById(R.id.reading_item_date);
|
||||
itemAuthors = (TextView) view.findViewById(R.id.reading_item_authors);
|
||||
itemFeed = (TextView) view.findViewById(R.id.reading_feed_title);
|
||||
feedIcon = (ImageView) view.findViewById(R.id.reading_feed_icon);
|
||||
|
||||
if (TextUtils.equals(feedColor, "#null") || TextUtils.equals(feedFade, "#null")) {
|
||||
|
@ -460,7 +442,9 @@ public class ReadingItemFragment extends NbFragment implements ClassifierDialogF
|
|||
|
||||
public void handleUpdate() {
|
||||
updateSaveButton();
|
||||
updateShareButton();
|
||||
reloadStoryContent();
|
||||
setupItemCommentsAndShares();
|
||||
}
|
||||
|
||||
private void loadOriginalText() {
|
||||
|
@ -625,74 +609,6 @@ public class ReadingItemFragment extends NbFragment implements ClassifierDialogF
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sharedCallback(String sharedText, boolean hasAlreadyBeenShared) {
|
||||
try {
|
||||
view.findViewById(R.id.reading_share_bar).setVisibility(View.VISIBLE);
|
||||
view.findViewById(R.id.share_bar_underline).setVisibility(View.VISIBLE);
|
||||
|
||||
if (!hasAlreadyBeenShared) {
|
||||
|
||||
if (!TextUtils.isEmpty(sharedText)) {
|
||||
View commentView = inflater.inflate(R.layout.include_comment, null);
|
||||
commentView.setTag(SetupCommentSectionTask.COMMENT_VIEW_BY + user.id);
|
||||
|
||||
TextView commentText = (TextView) commentView.findViewById(R.id.comment_text);
|
||||
commentText.setTag("commentBy" + user.id);
|
||||
commentText.setText(sharedText);
|
||||
|
||||
TextView commentLocation = (TextView) commentView.findViewById(R.id.comment_location);
|
||||
if (!TextUtils.isEmpty(user.location)) {
|
||||
commentLocation.setText(user.location.toUpperCase());
|
||||
} else {
|
||||
commentLocation.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (PrefsUtils.getUserImage(getActivity()) != null) {
|
||||
ImageView commentImage = (ImageView) commentView.findViewById(R.id.comment_user_image);
|
||||
commentImage.setImageBitmap(UIUtils.roundCorners(PrefsUtils.getUserImage(getActivity()), 10f));
|
||||
}
|
||||
|
||||
TextView commentSharedDate = (TextView) commentView.findViewById(R.id.comment_shareddate);
|
||||
commentSharedDate.setText(R.string.now);
|
||||
|
||||
TextView commentUsername = (TextView) commentView.findViewById(R.id.comment_username);
|
||||
commentUsername.setText(user.username);
|
||||
|
||||
((LinearLayout) view.findViewById(R.id.reading_friend_comment_container)).addView(commentView);
|
||||
|
||||
ViewUtils.setupCommentCount(getActivity(), view, story.commentCount + 1);
|
||||
|
||||
final ImageView image = ViewUtils.createSharebarImage(getActivity(), imageLoader, user.photoUrl, user.id);
|
||||
((FlowLayout) view.findViewById(R.id.reading_social_commentimages)).addView(image);
|
||||
|
||||
} else {
|
||||
ViewUtils.setupShareCount(getActivity(), view, story.sharedUserIds.length + 1);
|
||||
final ImageView image = ViewUtils.createSharebarImage(getActivity(), imageLoader, user.photoUrl, user.id);
|
||||
((FlowLayout) view.findViewById(R.id.reading_social_shareimages)).addView(image);
|
||||
}
|
||||
} else {
|
||||
View commentViewForUser = view.findViewWithTag(SetupCommentSectionTask.COMMENT_VIEW_BY + user.id);
|
||||
TextView commentText = (TextView) view.findViewWithTag(SetupCommentSectionTask.COMMENT_BY + user.id);
|
||||
commentText.setText(sharedText);
|
||||
|
||||
TextView commentDateText = (TextView) view.findViewWithTag(SetupCommentSectionTask.COMMENT_DATE_BY + user.id);
|
||||
commentDateText.setText(R.string.now);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// this entire method does not respect context state and can be triggered on stale fragments. it should
|
||||
// be replaced with a proper Loader, or it will always risk crashing the application
|
||||
Log.w(this.getClass().getName(), "async error in callback", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setPreviouslySavedShareText(String previouslySavedShareText) {
|
||||
this.previouslySavedShareText = previouslySavedShareText;
|
||||
}
|
||||
|
||||
private class ImmersiveViewHandler extends GestureDetector.SimpleOnGestureListener implements View.OnSystemUiVisibilityChangeListener {
|
||||
private View view;
|
||||
|
||||
|
|
|
@ -14,6 +14,10 @@ import android.widget.ImageView;
|
|||
import android.widget.Toast;
|
||||
import android.widget.ViewSwitcher;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.FindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.AddSocial;
|
||||
import com.newsblur.activity.Login;
|
||||
|
@ -28,9 +32,9 @@ public class RegisterProgressFragment extends Fragment {
|
|||
private String password;
|
||||
private String email;
|
||||
private RegisterTask registerTask;
|
||||
private ViewSwitcher switcher;
|
||||
private Button next;
|
||||
private ImageView registerProgressLogo;
|
||||
@FindView(R.id.register_viewswitcher) ViewSwitcher switcher;
|
||||
@FindView(R.id.registering_next_1) Button next;
|
||||
@FindView(R.id.registerprogress_logo) ImageView registerProgressLogo;
|
||||
|
||||
public static RegisterProgressFragment getInstance(String username, String password, String email) {
|
||||
RegisterProgressFragment fragment = new RegisterProgressFragment();
|
||||
|
@ -51,19 +55,15 @@ public class RegisterProgressFragment extends Fragment {
|
|||
username = getArguments().getString("username");
|
||||
password = getArguments().getString("password");
|
||||
email = getArguments().getString("email");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_registerprogress, null);
|
||||
switcher = (ViewSwitcher) v.findViewById(R.id.register_viewswitcher);
|
||||
ButterKnife.bind(this, v);
|
||||
|
||||
registerProgressLogo = (ImageView) v.findViewById(R.id.registerprogress_logo);
|
||||
registerProgressLogo.startAnimation(AnimationUtils.loadAnimation(getActivity(), R.anim.rotate));
|
||||
|
||||
next = (Button) v.findViewById(R.id.registering_next_1);
|
||||
|
||||
if (registerTask != null) {
|
||||
switcher.showNext();
|
||||
} else {
|
||||
|
@ -71,17 +71,14 @@ public class RegisterProgressFragment extends Fragment {
|
|||
registerTask.execute();
|
||||
}
|
||||
|
||||
next.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View arg0) {
|
||||
Intent i = new Intent(getActivity(), AddSocial.class);
|
||||
startActivity(i);
|
||||
}
|
||||
});
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@OnClick(R.id.registering_next_1) void next() {
|
||||
Intent i = new Intent(getActivity(), AddSocial.class);
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
private class RegisterTask extends AsyncTask<Void, Void, RegisterResponse> {
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,6 +15,7 @@ import android.widget.Toast;
|
|||
import com.newsblur.R;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.network.APIManager;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
|
||||
public class ReplyDialogFragment extends DialogFragment {
|
||||
|
||||
|
@ -25,9 +26,6 @@ public class ReplyDialogFragment extends DialogFragment {
|
|||
private String commentUserId, commentUsername;
|
||||
private Story story;
|
||||
|
||||
private APIManager apiManager;
|
||||
|
||||
|
||||
public static ReplyDialogFragment newInstance(final Story story, final String commentUserId, final String commentUsername) {
|
||||
ReplyDialogFragment frag = new ReplyDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
|
@ -40,17 +38,14 @@ public class ReplyDialogFragment extends DialogFragment {
|
|||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
story = (Story) getArguments().getSerializable(STORY);
|
||||
|
||||
commentUserId = getArguments().getString(COMMENT_USER_ID);
|
||||
commentUsername = getArguments().getString(COMMENT_USERNAME);
|
||||
|
||||
final Activity activity = getActivity();
|
||||
apiManager = new APIManager(activity);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
final String shareString = getResources().getString(R.string.reply_to);
|
||||
String shareString = getResources().getString(R.string.reply_to);
|
||||
builder.setTitle(String.format(shareString, getArguments().getString(COMMENT_USERNAME)));
|
||||
|
||||
LayoutInflater layoutInflater = LayoutInflater.from(activity);
|
||||
|
@ -61,23 +56,7 @@ public class ReplyDialogFragment extends DialogFragment {
|
|||
builder.setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
|
||||
new AsyncTask<Void, Void, Boolean>() {
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... arg) {
|
||||
return apiManager.replyToComment(story.id, story.feedId, commentUserId, reply.getText().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
if (result) {
|
||||
Toast.makeText(activity, R.string.replied, Toast.LENGTH_LONG).show();
|
||||
} else {
|
||||
Toast.makeText(activity, R.string.error_replying, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
ReplyDialogFragment.this.dismiss();
|
||||
};
|
||||
}.execute();
|
||||
FeedUtils.replyToComment(story.id, story.feedId, commentUserId, reply.getText().toString(), activity);
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() {
|
||||
|
|
|
@ -0,0 +1,336 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.app.DialogFragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.Profile;
|
||||
import com.newsblur.domain.Comment;
|
||||
import com.newsblur.domain.Reply;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.domain.UserDetails;
|
||||
import com.newsblur.domain.UserProfile;
|
||||
import com.newsblur.fragment.ReplyDialogFragment;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.ImageLoader;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
import com.newsblur.util.ViewUtils;
|
||||
import com.newsblur.view.FlowLayout;
|
||||
|
||||
public class SetupCommentSectionTask extends AsyncTask<Void, Void, Void> {
|
||||
|
||||
private ArrayList<View> publicCommentViews;
|
||||
private ArrayList<View> friendCommentViews;
|
||||
private ArrayList<View> friendShareViews;
|
||||
|
||||
private final Story story;
|
||||
private final LayoutInflater inflater;
|
||||
private final ImageLoader imageLoader;
|
||||
private WeakReference<View> viewHolder;
|
||||
private final Context context;
|
||||
private UserDetails user;
|
||||
private final FragmentManager manager;
|
||||
private List<Comment> comments;
|
||||
|
||||
public SetupCommentSectionTask(Context context, View view, FragmentManager manager, LayoutInflater inflater, Story story, ImageLoader imageLoader) {
|
||||
this.context = context;
|
||||
this.manager = manager;
|
||||
this.inflater = inflater;
|
||||
this.story = story;
|
||||
this.imageLoader = imageLoader;
|
||||
viewHolder = new WeakReference<View>(view);
|
||||
user = PrefsUtils.getUserDetails(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... arg0) {
|
||||
comments = FeedUtils.dbHelper.getComments(story.id);
|
||||
|
||||
publicCommentViews = new ArrayList<View>();
|
||||
friendCommentViews = new ArrayList<View>();
|
||||
friendShareViews = new ArrayList<View>();
|
||||
|
||||
for (final Comment comment : comments) {
|
||||
// skip public comments if they are disabled
|
||||
if (!comment.byFriend && !PrefsUtils.showPublicComments(context)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UserProfile commentUser = FeedUtils.dbHelper.getUserProfile(comment.userId);
|
||||
// rarely, we get a comment but never got the user's profile, so we can't display it
|
||||
if (commentUser == null) {
|
||||
Log.w(this.getClass().getName(), "cannot display comment from missing user ID: " + comment.userId);
|
||||
continue;
|
||||
}
|
||||
|
||||
View commentView = inflater.inflate(R.layout.include_comment, null);
|
||||
TextView commentText = (TextView) commentView.findViewById(R.id.comment_text);
|
||||
commentText.setText(Html.fromHtml(comment.commentText));
|
||||
ImageView commentImage = (ImageView) commentView.findViewById(R.id.comment_user_image);
|
||||
|
||||
TextView commentSharedDate = (TextView) commentView.findViewById(R.id.comment_shareddate);
|
||||
// TODO: this uses hard-coded "ago" values, which will be wrong when reading prefetched stories
|
||||
if (comment.sharedDate != null) {
|
||||
commentSharedDate.setText(comment.sharedDate + " ago");
|
||||
}
|
||||
|
||||
final FlowLayout favouriteContainer = (FlowLayout) commentView.findViewById(R.id.comment_favourite_avatars);
|
||||
final ImageView favouriteIcon = (ImageView) commentView.findViewById(R.id.comment_favourite_icon);
|
||||
final ImageView replyIcon = (ImageView) commentView.findViewById(R.id.comment_reply_icon);
|
||||
|
||||
if (comment.likingUsers != null) {
|
||||
if (Arrays.asList(comment.likingUsers).contains(user.id)) {
|
||||
favouriteIcon.setImageResource(R.drawable.have_favourite);
|
||||
}
|
||||
|
||||
for (String id : comment.likingUsers) {
|
||||
ImageView favouriteImage = new ImageView(context);
|
||||
UserProfile user = FeedUtils.dbHelper.getUserProfile(id);
|
||||
imageLoader.displayImage(user.photoUrl, favouriteImage, 10f);
|
||||
favouriteContainer.addView(favouriteImage);
|
||||
}
|
||||
|
||||
// users cannot fave their own comments. attempting to do so will actually queue a fatally invalid API call
|
||||
if (TextUtils.equals(comment.userId, user.id)) {
|
||||
favouriteIcon.setVisibility(View.GONE);
|
||||
} else {
|
||||
favouriteIcon.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (!Arrays.asList(comment.likingUsers).contains(user.id)) {
|
||||
FeedUtils.likeComment(story, comment.userId, context);
|
||||
} else {
|
||||
FeedUtils.unlikeComment(story, comment.userId, context);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
replyIcon.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (story != null) {
|
||||
UserProfile user = FeedUtils.dbHelper.getUserProfile(comment.userId);
|
||||
|
||||
DialogFragment newFragment = ReplyDialogFragment.newInstance(story, comment.userId, user.username);
|
||||
newFragment.show(manager, "dialog");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
List<Reply> replies = FeedUtils.dbHelper.getCommentReplies(comment.id);
|
||||
for (Reply reply : replies) {
|
||||
View replyView = inflater.inflate(R.layout.include_reply, null);
|
||||
TextView replyText = (TextView) replyView.findViewById(R.id.reply_text);
|
||||
replyText.setText(Html.fromHtml(reply.text));
|
||||
ImageView replyImage = (ImageView) replyView.findViewById(R.id.reply_user_image);
|
||||
|
||||
final UserProfile replyUser = FeedUtils.dbHelper.getUserProfile(reply.userId);
|
||||
if (replyUser != null) {
|
||||
imageLoader.displayImage(replyUser.photoUrl, replyImage);
|
||||
replyImage.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent i = new Intent(context, Profile.class);
|
||||
i.putExtra(Profile.USER_ID, replyUser.userId);
|
||||
context.startActivity(i);
|
||||
}
|
||||
});
|
||||
|
||||
TextView replyUsername = (TextView) replyView.findViewById(R.id.reply_username);
|
||||
replyUsername.setText(replyUser.username);
|
||||
} else {
|
||||
TextView replyUsername = (TextView) replyView.findViewById(R.id.reply_username);
|
||||
replyUsername.setText(R.string.unknown_user);
|
||||
}
|
||||
|
||||
if (reply.shortDate != null) {
|
||||
TextView replySharedDate = (TextView) replyView.findViewById(R.id.reply_shareddate);
|
||||
replySharedDate.setText(reply.shortDate + " ago");
|
||||
}
|
||||
|
||||
((LinearLayout) commentView.findViewById(R.id.comment_replies_container)).addView(replyView);
|
||||
}
|
||||
|
||||
TextView commentUsername = (TextView) commentView.findViewById(R.id.comment_username);
|
||||
commentUsername.setText(commentUser.username);
|
||||
String userPhoto = commentUser.photoUrl;
|
||||
|
||||
TextView commentLocation = (TextView) commentView.findViewById(R.id.comment_location);
|
||||
if (!TextUtils.isEmpty(commentUser.location)) {
|
||||
commentLocation.setText(commentUser.location.toUpperCase());
|
||||
} else {
|
||||
commentLocation.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(comment.sourceUserId)) {
|
||||
commentImage.setVisibility(View.INVISIBLE);
|
||||
ImageView usershareImage = (ImageView) commentView.findViewById(R.id.comment_user_reshare_image);
|
||||
ImageView sourceUserImage = (ImageView) commentView.findViewById(R.id.comment_sharesource_image);
|
||||
sourceUserImage.setVisibility(View.VISIBLE);
|
||||
usershareImage.setVisibility(View.VISIBLE);
|
||||
commentImage.setVisibility(View.INVISIBLE);
|
||||
|
||||
|
||||
UserProfile sourceUser = FeedUtils.dbHelper.getUserProfile(comment.sourceUserId);
|
||||
if (sourceUser != null) {
|
||||
imageLoader.displayImage(sourceUser.photoUrl, sourceUserImage, 10f);
|
||||
imageLoader.displayImage(userPhoto, usershareImage, 10f);
|
||||
}
|
||||
} else {
|
||||
imageLoader.displayImage(userPhoto, commentImage, 10f);
|
||||
}
|
||||
|
||||
commentImage.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent i = new Intent(context, Profile.class);
|
||||
i.putExtra(Profile.USER_ID, comment.userId);
|
||||
context.startActivity(i);
|
||||
}
|
||||
});
|
||||
|
||||
if (comment.isPseudo) {
|
||||
friendShareViews.add(commentView);
|
||||
} else if (comment.byFriend) {
|
||||
friendCommentViews.add(commentView);
|
||||
} else {
|
||||
publicCommentViews.add(commentView);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void onPostExecute(Void result) {
|
||||
View view = viewHolder.get();
|
||||
if (view == null) return; // fragment was dismissed before we rendered
|
||||
|
||||
if (story.sharedUserIds.length > 0 || publicCommentViews.size() > 0 || friendCommentViews.size() > 0) {
|
||||
view.findViewById(R.id.reading_share_bar).setVisibility(View.VISIBLE);
|
||||
view.findViewById(R.id.share_bar_underline).setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
view.findViewById(R.id.reading_share_bar).setVisibility(View.GONE);
|
||||
view.findViewById(R.id.share_bar_underline).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
FlowLayout sharedGrid = (FlowLayout) view.findViewById(R.id.reading_social_shareimages);
|
||||
FlowLayout commentGrid = (FlowLayout) view.findViewById(R.id.reading_social_commentimages);
|
||||
|
||||
TextView friendCommentTotal = ((TextView) view.findViewById(R.id.reading_friend_comment_total));
|
||||
TextView friendShareTotal = ((TextView) view.findViewById(R.id.reading_friend_emptyshare_total));
|
||||
TextView publicCommentTotal = ((TextView) view.findViewById(R.id.reading_public_comment_total));
|
||||
|
||||
int actualCommentCount = comments.size() - friendShareViews.size(); // comment-less shares are modeled as comments, exclude them
|
||||
ViewUtils.setupCommentCount(context, view, actualCommentCount);
|
||||
ViewUtils.setupShareCount(context, view, story.sharedUserIds.length);
|
||||
|
||||
Set<String> commentingUserIds = new HashSet<String>();
|
||||
for (Comment comment : comments) {
|
||||
if (!comment.isPseudo) {
|
||||
commentingUserIds.add(comment.userId);
|
||||
}
|
||||
}
|
||||
|
||||
sharedGrid.removeAllViews();
|
||||
for (String userId : story.sharedUserIds) {
|
||||
// only show an icon in either the share grid or the comment grid, not both
|
||||
if (!commentingUserIds.contains(userId)) {
|
||||
UserProfile user = FeedUtils.dbHelper.getUserProfile(userId);
|
||||
if (user != null) {
|
||||
ImageView image = ViewUtils.createSharebarImage(context, imageLoader, user.photoUrl, user.userId);
|
||||
sharedGrid.addView(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commentGrid.removeAllViews();
|
||||
for (String userId : commentingUserIds) {
|
||||
UserProfile user = FeedUtils.dbHelper.getUserProfile(userId);
|
||||
ImageView image = ViewUtils.createSharebarImage(context, imageLoader, user.photoUrl, user.userId);
|
||||
commentGrid.addView(image);
|
||||
}
|
||||
|
||||
if (publicCommentViews.size() > 0) {
|
||||
String commentCount = context.getString(R.string.public_comment_count);
|
||||
if (publicCommentViews.size() == 1) {
|
||||
commentCount = commentCount.substring(0, commentCount.length() - 1);
|
||||
}
|
||||
publicCommentTotal.setText(String.format(commentCount, publicCommentViews.size()));
|
||||
view.findViewById(R.id.reading_public_comment_header).setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
view.findViewById(R.id.reading_public_comment_header).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (friendCommentViews.size() > 0) {
|
||||
String commentCount = context.getString(R.string.friends_comments_count);
|
||||
if (friendCommentViews.size() == 1) {
|
||||
commentCount = commentCount.substring(0, commentCount.length() - 1);
|
||||
}
|
||||
friendCommentTotal.setText(String.format(commentCount, friendCommentViews.size()));
|
||||
view.findViewById(R.id.reading_friend_comment_header).setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
view.findViewById(R.id.reading_friend_comment_header).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (friendShareViews.size() > 0) {
|
||||
String commentCount = context.getString(R.string.friends_shares_count);
|
||||
if (friendShareViews.size() == 1) {
|
||||
commentCount = commentCount.substring(0, commentCount.length() - 1);
|
||||
}
|
||||
friendShareTotal.setText(String.format(commentCount, friendShareViews.size()));
|
||||
view.findViewById(R.id.reading_friend_emptyshare_header).setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
view.findViewById(R.id.reading_friend_emptyshare_header).setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
LinearLayout publicCommentListContainer = (LinearLayout) view.findViewById(R.id.reading_public_comment_container);
|
||||
publicCommentListContainer.removeAllViews();
|
||||
for (int i = 0; i < publicCommentViews.size(); i++) {
|
||||
if (i == publicCommentViews.size() - 1) {
|
||||
publicCommentViews.get(i).findViewById(R.id.comment_divider).setVisibility(View.GONE);
|
||||
}
|
||||
publicCommentListContainer.addView(publicCommentViews.get(i));
|
||||
}
|
||||
|
||||
LinearLayout friendCommentListContainer = (LinearLayout) view.findViewById(R.id.reading_friend_comment_container);
|
||||
friendCommentListContainer.removeAllViews();
|
||||
for (int i = 0; i < friendCommentViews.size(); i++) {
|
||||
if (i == friendCommentViews.size() - 1) {
|
||||
friendCommentViews.get(i).findViewById(R.id.comment_divider).setVisibility(View.GONE);
|
||||
}
|
||||
friendCommentListContainer.addView(friendCommentViews.get(i));
|
||||
}
|
||||
|
||||
LinearLayout friendShareListContainer = (LinearLayout) view.findViewById(R.id.reading_friend_emptyshare_container);
|
||||
friendShareListContainer.removeAllViews();
|
||||
for (int i = 0; i < friendShareViews.size(); i++) {
|
||||
if (i == friendShareViews.size() - 1) {
|
||||
friendShareViews.get(i).findViewById(R.id.comment_divider).setVisibility(View.GONE);
|
||||
}
|
||||
friendShareListContainer.addView(friendShareViews.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,12 +1,9 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.app.DialogFragment;
|
||||
import android.text.Html;
|
||||
|
@ -14,40 +11,29 @@ import android.text.TextUtils;
|
|||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.domain.Comment;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.domain.UserDetails;
|
||||
import com.newsblur.network.APIManager;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
import com.newsblur.util.UIUtils;
|
||||
|
||||
public class ShareDialogFragment extends DialogFragment {
|
||||
|
||||
private static final String STORY = "story";
|
||||
private static final String CALLBACK= "callback";
|
||||
private static final String PREVIOUSLY_SAVED_SHARE_TEXT = "previouslySavedComment";
|
||||
private static final String SOURCE_USER_ID = "sourceUserId";
|
||||
private APIManager apiManager;
|
||||
private SharedCallbackDialog callback;
|
||||
private Story story;
|
||||
private UserDetails user;
|
||||
private boolean hasBeenShared = false;
|
||||
private Comment previousComment;
|
||||
private String previouslySavedShareText;
|
||||
private boolean hasShared = false;
|
||||
private EditText commentEditText;
|
||||
private String sourceUserId;
|
||||
|
||||
public static ShareDialogFragment newInstance(final SharedCallbackDialog sharedCallback, final Story story, final String previouslySavedShareText, final String sourceUserId) {
|
||||
public static ShareDialogFragment newInstance(final Story story, final String sourceUserId) {
|
||||
ShareDialogFragment frag = new ShareDialogFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putSerializable(STORY, story);
|
||||
args.putString(PREVIOUSLY_SAVED_SHARE_TEXT, previouslySavedShareText);
|
||||
args.putSerializable(CALLBACK, sharedCallback);
|
||||
args.putString(SOURCE_USER_ID, sourceUserId);
|
||||
frag.setArguments(args);
|
||||
return frag;
|
||||
|
@ -55,16 +41,12 @@ public class ShareDialogFragment extends DialogFragment {
|
|||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
|
||||
final Activity activity = getActivity();
|
||||
story = (Story) getArguments().getSerializable(STORY);
|
||||
callback = (SharedCallbackDialog) getArguments().getSerializable(CALLBACK);
|
||||
user = PrefsUtils.getUserDetails(activity);
|
||||
previouslySavedShareText = getArguments().getString(PREVIOUSLY_SAVED_SHARE_TEXT);
|
||||
sourceUserId = getArguments().getString(SOURCE_USER_ID);
|
||||
|
||||
apiManager = new APIManager(getActivity());
|
||||
|
||||
boolean hasBeenShared = false;
|
||||
for (String sharedUserId : story.sharedUserIds) {
|
||||
if (TextUtils.equals(user.id, sharedUserId)) {
|
||||
hasBeenShared = true;
|
||||
|
@ -77,8 +59,7 @@ public class ShareDialogFragment extends DialogFragment {
|
|||
}
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
|
||||
final String shareString = getResources().getString(R.string.share_newsblur);
|
||||
builder.setTitle(String.format(shareString, Html.fromHtml(story.title)));
|
||||
builder.setTitle(String.format(getResources().getString(R.string.share_newsblur), Html.fromHtml(story.title)));
|
||||
|
||||
LayoutInflater layoutInflater = LayoutInflater.from(activity);
|
||||
View replyView = layoutInflater.inflate(R.layout.share_dialog, null);
|
||||
|
@ -87,45 +68,18 @@ public class ShareDialogFragment extends DialogFragment {
|
|||
|
||||
int positiveButtonText = R.string.share_this_story;
|
||||
if (hasBeenShared) {
|
||||
positiveButtonText = R.string.edit;
|
||||
positiveButtonText = R.string.update_shared;
|
||||
if (previousComment != null ) {
|
||||
commentEditText.setText(previousComment.commentText);
|
||||
}
|
||||
} else if (!TextUtils.isEmpty(previouslySavedShareText)) {
|
||||
commentEditText.setText(previouslySavedShareText);
|
||||
}
|
||||
|
||||
builder.setPositiveButton(positiveButtonText, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
|
||||
final String shareComment = commentEditText.getText().toString();
|
||||
|
||||
new AsyncTask<Void, Void, Boolean>() {
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... arg) {
|
||||
// If story.sourceUsedId is set then we should use that as the sourceUserId for the share.
|
||||
// Otherwise, use the sourceUsedId passed to the fragment.
|
||||
if (story.sourceUserId == null) {
|
||||
return apiManager.shareStory(story.id, story.feedId, shareComment, sourceUserId);
|
||||
} else {
|
||||
return apiManager.shareStory(story.id, story.feedId, shareComment, story.sourceUserId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
if (result) {
|
||||
hasShared = true;
|
||||
UIUtils.safeToast(activity, R.string.shared, Toast.LENGTH_LONG);
|
||||
callback.sharedCallback(shareComment, hasBeenShared);
|
||||
} else {
|
||||
UIUtils.safeToast(activity, R.string.error_sharing, Toast.LENGTH_LONG);
|
||||
}
|
||||
|
||||
ShareDialogFragment.this.dismiss();
|
||||
};
|
||||
}.execute();
|
||||
String shareComment = commentEditText.getText().toString();
|
||||
FeedUtils.shareStory(story, shareComment, sourceUserId, activity);
|
||||
ShareDialogFragment.this.dismiss();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() {
|
||||
|
@ -136,19 +90,5 @@ public class ShareDialogFragment extends DialogFragment {
|
|||
});
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
if (!hasShared && commentEditText.length() > 0) {
|
||||
previouslySavedShareText = commentEditText.getText().toString();
|
||||
callback.setPreviouslySavedShareText(previouslySavedShareText);
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
public interface SharedCallbackDialog extends Serializable{
|
||||
public void setPreviouslySavedShareText(String previouslySavedShareText);
|
||||
public void sharedCallback(String sharedText, boolean alreadyShared);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,25 +1,29 @@
|
|||
package com.newsblur.fragment;
|
||||
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.app.DialogFragment;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.RadioButton;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.FindView;
|
||||
import butterknife.OnClick;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.util.StoryOrder;
|
||||
import com.newsblur.util.StoryOrderChangedListener;
|
||||
|
||||
public class StoryOrderDialogFragment extends DialogFragment implements OnClickListener {
|
||||
public class StoryOrderDialogFragment extends DialogFragment {
|
||||
|
||||
private static String CURRENT_ORDER = "currentOrder";
|
||||
private StoryOrder currentValue;
|
||||
@FindView(R.id.radio_newest) RadioButton newestButton;
|
||||
@FindView(R.id.radio_oldest) RadioButton oldestButton;
|
||||
|
||||
public static StoryOrderDialogFragment newInstance(StoryOrder currentValue) {
|
||||
StoryOrderDialogFragment dialog = new StoryOrderDialogFragment();
|
||||
|
@ -39,11 +43,9 @@ public class StoryOrderDialogFragment extends DialogFragment implements OnClickL
|
|||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
|
||||
currentValue = (StoryOrder) getArguments().getSerializable(CURRENT_ORDER);
|
||||
View v = inflater.inflate(R.layout.storyorder_dialog, null);
|
||||
RadioButton newestButton = (RadioButton) v.findViewById(R.id.radio_newest);
|
||||
newestButton.setOnClickListener(this);
|
||||
ButterKnife.bind(this, v);
|
||||
|
||||
newestButton.setChecked(currentValue == StoryOrder.NEWEST);
|
||||
RadioButton oldestButton = (RadioButton) v.findViewById(R.id.radio_oldest);
|
||||
oldestButton.setOnClickListener(this);
|
||||
oldestButton.setChecked(currentValue == StoryOrder.OLDEST);
|
||||
|
||||
getDialog().getWindow().setFlags(WindowManager.LayoutParams.FLAG_DITHER, WindowManager.LayoutParams.FLAG_DITHER);
|
||||
|
@ -53,19 +55,18 @@ public class StoryOrderDialogFragment extends DialogFragment implements OnClickL
|
|||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
StoryOrderChangedListener listener = (StoryOrderChangedListener)getActivity();
|
||||
if (v.getId() == R.id.radio_oldest) {
|
||||
if (currentValue == StoryOrder.NEWEST) {
|
||||
listener.storyOrderChanged(StoryOrder.OLDEST);
|
||||
}
|
||||
} else {
|
||||
if (currentValue == StoryOrder.OLDEST) {
|
||||
listener.storyOrderChanged(StoryOrder.NEWEST);
|
||||
}
|
||||
@OnClick(R.id.radio_newest) void selectNewest() {
|
||||
if (currentValue != StoryOrder.NEWEST) {
|
||||
((StoryOrderChangedListener) getActivity()).storyOrderChanged(StoryOrder.NEWEST);
|
||||
}
|
||||
|
||||
dismiss();
|
||||
}
|
||||
|
||||
@OnClick(R.id.radio_oldest) void selectOldest() {
|
||||
if (currentValue != StoryOrder.OLDEST) {
|
||||
((StoryOrderChangedListener) getActivity()).storyOrderChanged(StoryOrder.OLDEST);
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -334,7 +334,7 @@ public class APIManager {
|
|||
}
|
||||
}
|
||||
|
||||
public Boolean shareStory(final String storyId, final String feedId, final String comment, final String sourceUserId) {
|
||||
public NewsBlurResponse shareStory(final String storyId, final String feedId, final String comment, final String sourceUserId) {
|
||||
final ContentValues values = new ContentValues();
|
||||
if (!TextUtils.isEmpty(comment)) {
|
||||
values.put(APIConstants.PARAMETER_SHARE_COMMENT, comment);
|
||||
|
@ -345,12 +345,8 @@ public class APIManager {
|
|||
values.put(APIConstants.PARAMETER_FEEDID, feedId);
|
||||
values.put(APIConstants.PARAMETER_STORYID, storyId);
|
||||
|
||||
final APIResponse response = post(APIConstants.URL_SHARE_STORY, values);
|
||||
if (!response.isError()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
APIResponse response = post(APIConstants.URL_SHARE_STORY, values);
|
||||
return response.getResponse(gson, NewsBlurResponse.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -373,6 +369,8 @@ public class APIManager {
|
|||
|
||||
// note: this response is complex enough, we have to do a custom parse in the FFR
|
||||
FeedFolderResponse result = new FeedFolderResponse(response.getResponseBody(), gson);
|
||||
// bind a litle extra instrumentation to this response, since it powers the feedback link
|
||||
result.connTime = response.connectTime;
|
||||
result.readTime = response.readTime;
|
||||
return result;
|
||||
}
|
||||
|
@ -480,32 +478,32 @@ public class APIManager {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean favouriteComment(String storyId, String commentId, String feedId) {
|
||||
public NewsBlurResponse favouriteComment(String storyId, String commentUserId, String feedId) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(APIConstants.PARAMETER_STORYID, storyId);
|
||||
values.put(APIConstants.PARAMETER_STORY_FEEDID, feedId);
|
||||
values.put(APIConstants.PARAMETER_COMMENT_USERID, commentId);
|
||||
final APIResponse response = post(APIConstants.URL_LIKE_COMMENT, values);
|
||||
return (!response.isError());
|
||||
values.put(APIConstants.PARAMETER_COMMENT_USERID, commentUserId);
|
||||
APIResponse response = post(APIConstants.URL_LIKE_COMMENT, values);
|
||||
return response.getResponse(gson, NewsBlurResponse.class);
|
||||
}
|
||||
|
||||
public Boolean unFavouriteComment(String storyId, String commentId, String feedId) {
|
||||
public NewsBlurResponse unFavouriteComment(String storyId, String commentUserId, String feedId) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(APIConstants.PARAMETER_STORYID, storyId);
|
||||
values.put(APIConstants.PARAMETER_STORY_FEEDID, feedId);
|
||||
values.put(APIConstants.PARAMETER_COMMENT_USERID, commentId);
|
||||
final APIResponse response = post(APIConstants.URL_UNLIKE_COMMENT, values);
|
||||
return (!response.isError());
|
||||
values.put(APIConstants.PARAMETER_COMMENT_USERID, commentUserId);
|
||||
APIResponse response = post(APIConstants.URL_UNLIKE_COMMENT, values);
|
||||
return response.getResponse(gson, NewsBlurResponse.class);
|
||||
}
|
||||
|
||||
public boolean replyToComment(String storyId, String storyFeedId, String commentUserId, String reply) {
|
||||
public NewsBlurResponse replyToComment(String storyId, String storyFeedId, String commentUserId, String reply) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(APIConstants.PARAMETER_STORYID, storyId);
|
||||
values.put(APIConstants.PARAMETER_STORY_FEEDID, storyFeedId);
|
||||
values.put(APIConstants.PARAMETER_COMMENT_USERID, commentUserId);
|
||||
values.put(APIConstants.PARAMETER_REPLY_TEXT, reply);
|
||||
final APIResponse response = post(APIConstants.URL_REPLY_TO, values);
|
||||
return (!response.isError());
|
||||
APIResponse response = post(APIConstants.URL_REPLY_TO, values);
|
||||
return response.getResponse(gson, NewsBlurResponse.class);
|
||||
}
|
||||
|
||||
public boolean addFeed(String feedUrl, String folderName) {
|
||||
|
|
|
@ -28,6 +28,7 @@ public class APIResponse {
|
|||
private String errorMessage;
|
||||
private String cookie;
|
||||
private String responseBody;
|
||||
public long connectTime;
|
||||
public long readTime;
|
||||
|
||||
/**
|
||||
|
@ -47,7 +48,9 @@ public class APIResponse {
|
|||
this.errorMessage = context.getResources().getString(R.string.error_unset_message);
|
||||
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
Response response = httpClient.newCall(request).execute();
|
||||
connectTime = System.currentTimeMillis() - startTime;
|
||||
if (response.isSuccessful()) {
|
||||
|
||||
if (response.code() != expectedReturnCode) {
|
||||
|
@ -60,7 +63,7 @@ public class APIResponse {
|
|||
this.cookie = response.header("Set-Cookie");
|
||||
|
||||
try {
|
||||
long startTime = System.currentTimeMillis();
|
||||
startTime = System.currentTimeMillis();
|
||||
this.responseBody = response.body().string();
|
||||
readTime = System.currentTimeMillis() - startTime;
|
||||
} catch (Exception e) {
|
||||
|
@ -82,6 +85,10 @@ public class APIResponse {
|
|||
}
|
||||
}
|
||||
|
||||
if (AppConstants.VERBOSE_LOG_NET) {
|
||||
Log.d(this.getClass().getName(), String.format("called %s in %dms and %dms to read %dB", request.urlString(), connectTime, readTime, responseBody.length()));
|
||||
}
|
||||
|
||||
} else {
|
||||
Log.e(this.getClass().getName(), "API call unsuccessful, error code" + response.code());
|
||||
this.isError = true;
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
package com.newsblur.network;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.AsyncTask;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.domain.Comment;
|
||||
import com.newsblur.domain.UserDetails;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
import com.newsblur.util.UIUtils;
|
||||
import com.newsblur.view.FlowLayout;
|
||||
|
||||
public class LikeCommentTask extends AsyncTask<Void, Void, Boolean>{
|
||||
|
||||
final WeakReference<ImageView> favouriteIconViewHolder;
|
||||
|
||||
private final APIManager apiManager;
|
||||
private final String storyId;
|
||||
private final Comment comment;
|
||||
private final String feedId;
|
||||
private final Context context;
|
||||
private final String userId;
|
||||
private Bitmap userImage;
|
||||
private WeakReference<FlowLayout> favouriteAvatarHolder;
|
||||
private UserDetails user;
|
||||
|
||||
public LikeCommentTask(final Context context, final APIManager apiManager, final ImageView favouriteIcon, final FlowLayout favouriteAvatarLayout, final String storyId, final Comment comment, final String feedId, final String userId) {
|
||||
this.apiManager = apiManager;
|
||||
this.storyId = storyId;
|
||||
this.comment = comment;
|
||||
this.feedId = feedId;
|
||||
this.context = context;
|
||||
this.userId = userId;
|
||||
|
||||
favouriteAvatarHolder = new WeakReference<FlowLayout>(favouriteAvatarLayout);
|
||||
favouriteIconViewHolder = new WeakReference<ImageView>(favouriteIcon);
|
||||
|
||||
userImage = PrefsUtils.getUserImage(context);
|
||||
user = PrefsUtils.getUserDetails(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
return apiManager.favouriteComment(storyId, comment.userId, feedId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
if (favouriteIconViewHolder.get() != null) {
|
||||
if (result.booleanValue()) {
|
||||
favouriteIconViewHolder.get().setImageResource(R.drawable.have_favourite);
|
||||
|
||||
if (userImage != null) {
|
||||
ImageView favouriteImage = new ImageView(context);
|
||||
favouriteImage.setTag(user.id);
|
||||
userImage = UIUtils.roundCorners(userImage, 10f);
|
||||
favouriteImage.setImageBitmap(userImage);
|
||||
favouriteAvatarHolder.get().addView(favouriteImage);
|
||||
}
|
||||
|
||||
String[] newArray = new String[comment.likingUsers.length + 1];
|
||||
System.arraycopy(comment.likingUsers, 0, newArray, 0, comment.likingUsers.length);
|
||||
newArray[newArray.length - 1] = userId;
|
||||
comment.likingUsers = newArray;
|
||||
|
||||
Toast.makeText(context, R.string.comment_favourited, Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(context, R.string.error_liking_comment, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,294 +0,0 @@
|
|||
package com.newsblur.network;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.AsyncTask;
|
||||
import android.app.DialogFragment;
|
||||
import android.app.FragmentManager;
|
||||
import android.text.Html;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.activity.Profile;
|
||||
import com.newsblur.domain.Comment;
|
||||
import com.newsblur.domain.Reply;
|
||||
import com.newsblur.domain.Story;
|
||||
import com.newsblur.domain.UserDetails;
|
||||
import com.newsblur.domain.UserProfile;
|
||||
import com.newsblur.fragment.ReplyDialogFragment;
|
||||
import com.newsblur.util.FeedUtils;
|
||||
import com.newsblur.util.ImageLoader;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
import com.newsblur.util.ViewUtils;
|
||||
import com.newsblur.view.FlowLayout;
|
||||
|
||||
public class SetupCommentSectionTask extends AsyncTask<Void, Void, Void> {
|
||||
public static final String COMMENT_BY = "commentBy";
|
||||
public static final String COMMENT_DATE_BY = "commentDateBy";
|
||||
public static final String COMMENT_VIEW_BY = "commentViewBy";
|
||||
|
||||
private ArrayList<View> publicCommentViews;
|
||||
private ArrayList<View> friendCommentViews;
|
||||
private final APIManager apiManager;
|
||||
|
||||
private final Story story;
|
||||
private final LayoutInflater inflater;
|
||||
private final ImageLoader imageLoader;
|
||||
private WeakReference<View> viewHolder;
|
||||
private final Context context;
|
||||
private UserDetails user;
|
||||
private final FragmentManager manager;
|
||||
private List<Comment> comments;
|
||||
|
||||
public SetupCommentSectionTask(final Context context, final View view, final FragmentManager manager, LayoutInflater inflater, final APIManager apiManager, final Story story, final ImageLoader imageLoader) {
|
||||
this.context = context;
|
||||
this.manager = manager;
|
||||
this.inflater = inflater;
|
||||
this.apiManager = apiManager;
|
||||
this.story = story;
|
||||
this.imageLoader = imageLoader;
|
||||
viewHolder = new WeakReference<View>(view);
|
||||
user = PrefsUtils.getUserDetails(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... arg0) {
|
||||
comments = FeedUtils.dbHelper.getComments(story.id);
|
||||
|
||||
publicCommentViews = new ArrayList<View>();
|
||||
friendCommentViews = new ArrayList<View>();
|
||||
|
||||
for (final Comment comment : comments) {
|
||||
// skip public comments if they are disabled
|
||||
if (!comment.byFriend && !PrefsUtils.showPublicComments(context)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
UserProfile commentUser = FeedUtils.dbHelper.getUserProfile(comment.userId);
|
||||
// rarely, we get a comment but never got the user's profile, so we can't display it
|
||||
if (commentUser == null) {
|
||||
Log.w(this.getClass().getName(), "cannot display comment from missing user ID: " + comment.userId);
|
||||
continue;
|
||||
}
|
||||
|
||||
View commentView = inflater.inflate(R.layout.include_comment, null);
|
||||
commentView.setTag(COMMENT_VIEW_BY + comment.userId);
|
||||
|
||||
TextView commentText = (TextView) commentView.findViewById(R.id.comment_text);
|
||||
|
||||
commentText.setText(Html.fromHtml(comment.commentText));
|
||||
commentText.setTag(COMMENT_BY + comment.userId);
|
||||
|
||||
ImageView commentImage = (ImageView) commentView.findViewById(R.id.comment_user_image);
|
||||
|
||||
TextView commentSharedDate = (TextView) commentView.findViewById(R.id.comment_shareddate);
|
||||
commentSharedDate.setText(comment.sharedDate + " ago");
|
||||
commentSharedDate.setTag(COMMENT_DATE_BY + comment.userId);
|
||||
|
||||
final FlowLayout favouriteContainer = (FlowLayout) commentView.findViewById(R.id.comment_favourite_avatars);
|
||||
final ImageView favouriteIcon = (ImageView) commentView.findViewById(R.id.comment_favourite_icon);
|
||||
final ImageView replyIcon = (ImageView) commentView.findViewById(R.id.comment_reply_icon);
|
||||
|
||||
if (comment.likingUsers != null) {
|
||||
if (Arrays.asList(comment.likingUsers).contains(user.id)) {
|
||||
favouriteIcon.setImageResource(R.drawable.have_favourite);
|
||||
}
|
||||
|
||||
for (String id : comment.likingUsers) {
|
||||
ImageView favouriteImage = new ImageView(context);
|
||||
|
||||
UserProfile user = FeedUtils.dbHelper.getUserProfile(id);
|
||||
|
||||
imageLoader.displayImage(user.photoUrl, favouriteImage, 10f);
|
||||
favouriteImage.setTag(id);
|
||||
|
||||
favouriteContainer.addView(favouriteImage);
|
||||
}
|
||||
|
||||
favouriteIcon.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (!Arrays.asList(comment.likingUsers).contains(user.id)) {
|
||||
new LikeCommentTask(context, apiManager, favouriteIcon, favouriteContainer, story.id, comment, story.feedId, user.id).execute();
|
||||
} else {
|
||||
new UnLikeCommentTask(context, apiManager, favouriteIcon, favouriteContainer, story.id, comment, story.feedId, user.id).execute();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
replyIcon.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (story != null) {
|
||||
UserProfile user = FeedUtils.dbHelper.getUserProfile(comment.userId);
|
||||
|
||||
DialogFragment newFragment = ReplyDialogFragment.newInstance(story, comment.userId, user.username);
|
||||
newFragment.show(manager, "dialog");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
List<Reply> replies = FeedUtils.dbHelper.getCommentReplies(comment.id);
|
||||
for (Reply reply : replies) {
|
||||
View replyView = inflater.inflate(R.layout.include_reply, null);
|
||||
TextView replyText = (TextView) replyView.findViewById(R.id.reply_text);
|
||||
replyText.setText(Html.fromHtml(reply.text));
|
||||
ImageView replyImage = (ImageView) replyView.findViewById(R.id.reply_user_image);
|
||||
|
||||
final UserProfile replyUser = FeedUtils.dbHelper.getUserProfile(reply.userId);
|
||||
if (replyUser != null) {
|
||||
imageLoader.displayImage(replyUser.photoUrl, replyImage);
|
||||
replyImage.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent i = new Intent(context, Profile.class);
|
||||
i.putExtra(Profile.USER_ID, replyUser.userId);
|
||||
context.startActivity(i);
|
||||
}
|
||||
});
|
||||
|
||||
TextView replyUsername = (TextView) replyView.findViewById(R.id.reply_username);
|
||||
replyUsername.setText(replyUser.username);
|
||||
} else {
|
||||
TextView replyUsername = (TextView) replyView.findViewById(R.id.reply_username);
|
||||
replyUsername.setText(R.string.unknown_user);
|
||||
}
|
||||
|
||||
TextView replySharedDate = (TextView) replyView.findViewById(R.id.reply_shareddate);
|
||||
replySharedDate.setText(reply.shortDate + " ago");
|
||||
|
||||
((LinearLayout) commentView.findViewById(R.id.comment_replies_container)).addView(replyView);
|
||||
}
|
||||
|
||||
TextView commentUsername = (TextView) commentView.findViewById(R.id.comment_username);
|
||||
commentUsername.setText(commentUser.username);
|
||||
String userPhoto = commentUser.photoUrl;
|
||||
|
||||
TextView commentLocation = (TextView) commentView.findViewById(R.id.comment_location);
|
||||
if (!TextUtils.isEmpty(commentUser.location)) {
|
||||
commentLocation.setText(commentUser.location.toUpperCase());
|
||||
} else {
|
||||
commentLocation.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(comment.sourceUserId)) {
|
||||
commentImage.setVisibility(View.INVISIBLE);
|
||||
ImageView usershareImage = (ImageView) commentView.findViewById(R.id.comment_user_reshare_image);
|
||||
ImageView sourceUserImage = (ImageView) commentView.findViewById(R.id.comment_sharesource_image);
|
||||
sourceUserImage.setVisibility(View.VISIBLE);
|
||||
usershareImage.setVisibility(View.VISIBLE);
|
||||
commentImage.setVisibility(View.INVISIBLE);
|
||||
|
||||
|
||||
UserProfile sourceUser = FeedUtils.dbHelper.getUserProfile(comment.sourceUserId);
|
||||
if (sourceUser != null) {
|
||||
imageLoader.displayImage(sourceUser.photoUrl, sourceUserImage, 10f);
|
||||
imageLoader.displayImage(userPhoto, usershareImage, 10f);
|
||||
}
|
||||
} else {
|
||||
imageLoader.displayImage(userPhoto, commentImage, 10f);
|
||||
}
|
||||
|
||||
if (comment.byFriend) {
|
||||
friendCommentViews.add(commentView);
|
||||
} else {
|
||||
publicCommentViews.add(commentView);
|
||||
}
|
||||
|
||||
commentImage.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent i = new Intent(context, Profile.class);
|
||||
i.putExtra(Profile.USER_ID, comment.userId);
|
||||
context.startActivity(i);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void onPostExecute(Void result) {
|
||||
if (viewHolder.get() != null) {
|
||||
FlowLayout sharedGrid = (FlowLayout) viewHolder.get().findViewById(R.id.reading_social_shareimages);
|
||||
FlowLayout commentGrid = (FlowLayout) viewHolder.get().findViewById(R.id.reading_social_commentimages);
|
||||
|
||||
TextView friendCommentTotal = ((TextView) viewHolder.get().findViewById(R.id.reading_friend_comment_total));
|
||||
TextView publicCommentTotal = ((TextView) viewHolder.get().findViewById(R.id.reading_public_comment_total));
|
||||
|
||||
ViewUtils.setupCommentCount(context, viewHolder.get(), comments.size());
|
||||
ViewUtils.setupShareCount(context, viewHolder.get(), story.sharedUserIds.length);
|
||||
|
||||
Set<String> commentIds = new HashSet<String>();
|
||||
for (Comment comment : comments) {
|
||||
commentIds.add(comment.userId);
|
||||
}
|
||||
|
||||
for (String userId : story.sharedUserIds) {
|
||||
if (!commentIds.contains(userId)) {
|
||||
UserProfile user = FeedUtils.dbHelper.getUserProfile(userId);
|
||||
if (user != null) {
|
||||
ImageView image = ViewUtils.createSharebarImage(context, imageLoader, user.photoUrl, user.userId);
|
||||
sharedGrid.addView(image);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Comment comment : comments) {
|
||||
UserProfile user = FeedUtils.dbHelper.getUserProfile(comment.userId);
|
||||
ImageView image = ViewUtils.createSharebarImage(context, imageLoader, user.photoUrl, user.userId);
|
||||
commentGrid.addView(image);
|
||||
}
|
||||
|
||||
if (publicCommentViews.size() > 0) {
|
||||
String commentCount = context.getString(R.string.public_comment_count);
|
||||
if (publicCommentViews.size() == 1) {
|
||||
commentCount = commentCount.substring(0, commentCount.length() - 1);
|
||||
}
|
||||
publicCommentTotal.setText(String.format(commentCount, publicCommentViews.size()));
|
||||
viewHolder.get().findViewById(R.id.reading_public_comment_header).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
if (friendCommentViews.size() > 0) {
|
||||
String commentCount = context.getString(R.string.friends_comments_count);
|
||||
if (friendCommentViews.size() == 1) {
|
||||
commentCount = commentCount.substring(0, commentCount.length() - 1);
|
||||
}
|
||||
friendCommentTotal.setText(String.format(commentCount, friendCommentViews.size()));
|
||||
viewHolder.get().findViewById(R.id.reading_friend_comment_header).setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
for (int i = 0; i < publicCommentViews.size(); i++) {
|
||||
if (i == publicCommentViews.size() - 1) {
|
||||
publicCommentViews.get(i).findViewById(R.id.comment_divider).setVisibility(View.GONE);
|
||||
}
|
||||
((LinearLayout) viewHolder.get().findViewById(R.id.reading_public_comment_container)).addView(publicCommentViews.get(i));
|
||||
}
|
||||
|
||||
for (int i = 0; i < friendCommentViews.size(); i++) {
|
||||
if (i == friendCommentViews.size() - 1) {
|
||||
friendCommentViews.get(i).findViewById(R.id.comment_divider).setVisibility(View.GONE);
|
||||
}
|
||||
((LinearLayout) viewHolder.get().findViewById(R.id.reading_friend_comment_container)).addView(friendCommentViews.get(i));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
package com.newsblur.network;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.newsblur.R;
|
||||
import com.newsblur.domain.Comment;
|
||||
import com.newsblur.domain.UserDetails;
|
||||
import com.newsblur.util.PrefsUtils;
|
||||
import com.newsblur.view.FlowLayout;
|
||||
|
||||
public class UnLikeCommentTask extends AsyncTask<Void, Void, Boolean>{
|
||||
|
||||
private static final String TAG = "LikeCommentTask";
|
||||
final WeakReference<ImageView> favouriteIconViewHolder;
|
||||
private final APIManager apiManager;
|
||||
private final String storyId;
|
||||
private final Comment comment;
|
||||
private final String feedId;
|
||||
private final Context context;
|
||||
private final String userId;
|
||||
private WeakReference<FlowLayout> favouriteAvatarHolder;
|
||||
private UserDetails user;
|
||||
|
||||
public UnLikeCommentTask(final Context context, final APIManager apiManager, final ImageView favouriteIcon, final FlowLayout favouriteAvatarContainer, final String storyId, final Comment comment, final String feedId, final String userId) {
|
||||
this.apiManager = apiManager;
|
||||
this.storyId = storyId;
|
||||
this.comment = comment;
|
||||
this.feedId = feedId;
|
||||
this.context = context;
|
||||
this.userId = userId;
|
||||
|
||||
favouriteIconViewHolder = new WeakReference<ImageView>(favouriteIcon);
|
||||
favouriteAvatarHolder = new WeakReference<FlowLayout>(favouriteAvatarContainer);
|
||||
|
||||
user = PrefsUtils.getUserDetails(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
return apiManager.unFavouriteComment(storyId, comment.userId, feedId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
if (favouriteIconViewHolder.get() != null) {
|
||||
if (result.booleanValue()) {
|
||||
favouriteIconViewHolder.get().setImageResource(R.drawable.favourite);
|
||||
|
||||
View v = favouriteAvatarHolder.get().findViewWithTag(user.id);
|
||||
favouriteAvatarHolder.get().removeView(v);
|
||||
|
||||
ArrayList<String> likingUsers = new ArrayList<String>();
|
||||
for (String user : comment.likingUsers) {
|
||||
if (!TextUtils.equals(user, userId) && TextUtils.isEmpty(user)) {
|
||||
likingUsers.add(user);
|
||||
}
|
||||
}
|
||||
String[] newArray = new String[likingUsers.size()];
|
||||
likingUsers.toArray(newArray);
|
||||
comment.likingUsers = newArray;
|
||||
|
||||
Toast.makeText(context, "Removed favourite", Toast.LENGTH_SHORT).show();
|
||||
} else {
|
||||
Toast.makeText(context, "Error removing favorite from comment", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,8 +22,10 @@ import com.newsblur.util.AppConstants;
|
|||
|
||||
public class FeedFolderResponse {
|
||||
|
||||
/** Helper variable so users of the parser can pass along how long it took to read the JSON stream, for instrumentation. */
|
||||
/** Helper variables so users of the parser can pass along instrumentation. */
|
||||
public long connTime;
|
||||
public long readTime;
|
||||
public long parseTime;
|
||||
|
||||
public Set<Folder> folders;
|
||||
public Set<Feed> feeds;
|
||||
|
@ -35,6 +37,7 @@ public class FeedFolderResponse {
|
|||
public int starredCount;
|
||||
|
||||
public FeedFolderResponse(String json, Gson gson) {
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject asJsonObject = parser.parse(json).getAsJsonObject();
|
||||
|
@ -93,6 +96,8 @@ public class FeedFolderResponse {
|
|||
folders.add(emptyRootFolder);
|
||||
Log.d( this.getClass().getName(), "root folder was missing. added it.");
|
||||
}
|
||||
|
||||
parseTime = System.currentTimeMillis() - startTime;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,6 +5,7 @@ import java.text.DateFormat;
|
|||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
@ -15,8 +16,15 @@ import com.google.gson.JsonElement;
|
|||
import com.google.gson.JsonParseException;
|
||||
|
||||
public class DateStringTypeAdapter implements JsonDeserializer<Date> {
|
||||
// 2012-07-23 02:43:02
|
||||
private final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
private final DateFormat df;
|
||||
|
||||
public DateStringTypeAdapter() {
|
||||
// API sends dates like 2012-07-23 02:43:02
|
||||
df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
// API doesn't indicate TZ, but it is UTC
|
||||
df.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date deserialize(JsonElement element, Type type, JsonDeserializationContext arg2) throws JsonParseException {
|
||||
|
|
|
@ -69,6 +69,10 @@ public class ImagePrefetchService extends SubService {
|
|||
return ImageQueue.size();
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
ImageQueue.clear();
|
||||
}
|
||||
|
||||
public static boolean running() {
|
||||
return Running;
|
||||
}
|
||||
|
|
|
@ -97,7 +97,9 @@ public class NBSyncService extends Service {
|
|||
|
||||
private volatile static boolean isMemoryLow = false;
|
||||
private static long lastFeedCount = 0L;
|
||||
private static long lastFFConnMillis = 0L;
|
||||
private static long lastFFReadMillis = 0L;
|
||||
private static long lastFFParseMillis = 0L;
|
||||
private static long lastFFWriteMillis = 0L;
|
||||
|
||||
/** Feed set that we need to sync immediately for the UI. */
|
||||
|
@ -386,7 +388,7 @@ public class NBSyncService extends Service {
|
|||
ExhaustedFeeds.clear();
|
||||
FeedPagesSeen.clear();
|
||||
FeedStoriesSeen.clear();
|
||||
UnreadsService.clearHashes();
|
||||
UnreadsService.clear();
|
||||
RecountCandidates.clear();
|
||||
|
||||
FeedFolderResponse feedResponse = apiManager.getFolderFeedMapping(true);
|
||||
|
@ -402,7 +404,9 @@ public class NBSyncService extends Service {
|
|||
return;
|
||||
}
|
||||
|
||||
lastFFConnMillis = feedResponse.connTime;
|
||||
lastFFReadMillis = feedResponse.readTime;
|
||||
lastFFParseMillis = feedResponse.parseTime;
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
isPremium = feedResponse.isPremium;
|
||||
|
@ -838,6 +842,19 @@ public class NBSyncService extends Service {
|
|||
HaltNow = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets any internal temp vars or queues. Called when switching accounts.
|
||||
*/
|
||||
public static void clearState() {
|
||||
clearPendingStoryRequest();
|
||||
FollowupActions.clear();
|
||||
RecountCandidates.clear();
|
||||
resetFeeds();
|
||||
OriginalTextService.clear();
|
||||
UnreadsService.clear();
|
||||
ImagePrefetchService.clear();
|
||||
}
|
||||
|
||||
public static void resumeFromInterrupt() {
|
||||
HaltNow = false;
|
||||
}
|
||||
|
@ -879,7 +896,11 @@ public class NBSyncService extends Service {
|
|||
|
||||
public static String getSpeedInfo() {
|
||||
StringBuilder s = new StringBuilder();
|
||||
s.append(lastFeedCount).append(" in ").append(lastFFReadMillis).append(" and ").append(lastFFWriteMillis);
|
||||
s.append(lastFeedCount).append(" feeds in ");
|
||||
s.append(" conn:").append(lastFFConnMillis);
|
||||
s.append(" read:").append(lastFFReadMillis);
|
||||
s.append(" parse:").append(lastFFParseMillis);
|
||||
s.append(" store:").append(lastFFWriteMillis);
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -70,6 +70,11 @@ public class OriginalTextService extends SubService {
|
|||
return (Hashes.size() + PriorityHashes.size());
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
Hashes.clear();
|
||||
PriorityHashes.clear();
|
||||
}
|
||||
|
||||
public static boolean running() {
|
||||
return Running;
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ public class UnreadsService extends SubService {
|
|||
return true;
|
||||
}
|
||||
|
||||
public static void clearHashes() {
|
||||
public static void clear() {
|
||||
StoryHashQueue.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ public class FeedUtils {
|
|||
protected Void doInBackground(Void... arg) {
|
||||
ReadingAction ra = (saved ? ReadingAction.saveStory(story.storyHash) : ReadingAction.unsaveStory(story.storyHash));
|
||||
ra.doLocal(dbHelper);
|
||||
NbActivity.updateAllActivities();
|
||||
NbActivity.updateAllActivities(true);
|
||||
dbHelper.enqueueAction(ra);
|
||||
triggerSync(context);
|
||||
return null;
|
||||
|
@ -167,7 +167,6 @@ public class FeedUtils {
|
|||
}
|
||||
|
||||
public static void updateClassifier(final String feedId, final String key, final Classifier classifier, final int classifierType, final int classifierAction, final Context context) {
|
||||
|
||||
// first, update the server
|
||||
new AsyncTask<Void, Void, NewsBlurResponse>() {
|
||||
@Override
|
||||
|
@ -190,7 +189,7 @@ public class FeedUtils {
|
|||
dbHelper.insertClassifier(classifier);
|
||||
}
|
||||
|
||||
public static void shareStory(Story story, Context context) {
|
||||
public static void sendStory(Story story, Context context) {
|
||||
if (story == null ) { return; }
|
||||
Intent intent = new Intent(android.content.Intent.ACTION_SEND);
|
||||
intent.setType("text/plain");
|
||||
|
@ -199,7 +198,42 @@ public class FeedUtils {
|
|||
final String shareString = context.getResources().getString(R.string.share);
|
||||
intent.putExtra(Intent.EXTRA_TEXT, String.format(shareString, new Object[] { Html.fromHtml(story.title),
|
||||
story.permalink }));
|
||||
context.startActivity(Intent.createChooser(intent, "Share using"));
|
||||
context.startActivity(Intent.createChooser(intent, "Send using"));
|
||||
}
|
||||
|
||||
public static void shareStory(Story story, String comment, String sourceUserId, Context context) {
|
||||
if (story.sourceUserId != null) {
|
||||
sourceUserId = story.sourceUserId;
|
||||
}
|
||||
ReadingAction ra = ReadingAction.shareStory(story.storyHash, story.id, story.feedId, sourceUserId, comment);
|
||||
dbHelper.enqueueAction(ra);
|
||||
ra.doLocal(dbHelper);
|
||||
NbActivity.updateAllActivities(true);
|
||||
triggerSync(context);
|
||||
}
|
||||
|
||||
public static void likeComment(Story story, String commentUserId, Context context) {
|
||||
ReadingAction ra = ReadingAction.likeComment(story.id, commentUserId, story.feedId);
|
||||
dbHelper.enqueueAction(ra);
|
||||
ra.doLocal(dbHelper);
|
||||
NbActivity.updateAllActivities(true);
|
||||
triggerSync(context);
|
||||
}
|
||||
|
||||
public static void unlikeComment(Story story, String commentUserId, Context context) {
|
||||
ReadingAction ra = ReadingAction.unlikeComment(story.id, commentUserId, story.feedId);
|
||||
dbHelper.enqueueAction(ra);
|
||||
ra.doLocal(dbHelper);
|
||||
NbActivity.updateAllActivities(true);
|
||||
triggerSync(context);
|
||||
}
|
||||
|
||||
public static void replyToComment(String storyId, String feedId, String commentUserId, String replyText, Context context) {
|
||||
ReadingAction ra = ReadingAction.replyToComment(storyId, feedId, commentUserId, replyText);
|
||||
dbHelper.enqueueAction(ra);
|
||||
ra.doLocal(dbHelper);
|
||||
NbActivity.updateAllActivities(true);
|
||||
triggerSync(context);
|
||||
}
|
||||
|
||||
public static FeedSet feedSetFromFolderName(String folderName) {
|
||||
|
|
|
@ -99,6 +99,7 @@ public class PrefsUtils {
|
|||
|
||||
public static void logout(Context context) {
|
||||
NBSyncService.softInterrupt();
|
||||
NBSyncService.clearState();
|
||||
|
||||
// wipe the prefs store
|
||||
context.getSharedPreferences(PrefConstants.PREFERENCES, 0).edit().clear().commit();
|
||||
|
@ -114,6 +115,7 @@ public class PrefsUtils {
|
|||
|
||||
public static void clearPrefsAndDbForLoginAs(Context context) {
|
||||
NBSyncService.softInterrupt();
|
||||
NBSyncService.clearState();
|
||||
|
||||
// wipe the prefs store except for the cookie and login keys since we need to
|
||||
// authenticate further API calls
|
||||
|
|
|
@ -18,7 +18,11 @@ public class ReadingAction {
|
|||
MARK_READ,
|
||||
MARK_UNREAD,
|
||||
SAVE,
|
||||
UNSAVE
|
||||
UNSAVE,
|
||||
SHARE,
|
||||
REPLY,
|
||||
LIKE_COMMENT,
|
||||
UNLIKE_COMMENT
|
||||
};
|
||||
|
||||
private ActionType type;
|
||||
|
@ -26,6 +30,11 @@ public class ReadingAction {
|
|||
private FeedSet feedSet;
|
||||
private Long olderThan;
|
||||
private Long newerThan;
|
||||
private String storyId;
|
||||
private String feedId;
|
||||
private String sourceUserId;
|
||||
private String commentReplyText; // used for both comments and replies
|
||||
private String commentUserId;
|
||||
|
||||
private ReadingAction() {
|
||||
; // must use helpers
|
||||
|
@ -68,6 +77,45 @@ public class ReadingAction {
|
|||
return ra;
|
||||
}
|
||||
|
||||
public static ReadingAction shareStory(String hash, String storyId, String feedId, String sourceUserId, String commentReplyText) {
|
||||
ReadingAction ra = new ReadingAction();
|
||||
ra.type = ActionType.SHARE;
|
||||
ra.storyHash = hash;
|
||||
ra.storyId = storyId;
|
||||
ra.feedId = feedId;
|
||||
ra.sourceUserId = sourceUserId;
|
||||
ra.commentReplyText = commentReplyText;
|
||||
return ra;
|
||||
}
|
||||
|
||||
public static ReadingAction likeComment(String storyId, String commentUserId, String feedId) {
|
||||
ReadingAction ra = new ReadingAction();
|
||||
ra.type = ActionType.LIKE_COMMENT;
|
||||
ra.storyId = storyId;
|
||||
ra.commentUserId = commentUserId;
|
||||
ra.feedId = feedId;
|
||||
return ra;
|
||||
}
|
||||
|
||||
public static ReadingAction unlikeComment(String storyId, String commentUserId, String feedId) {
|
||||
ReadingAction ra = new ReadingAction();
|
||||
ra.type = ActionType.UNLIKE_COMMENT;
|
||||
ra.storyId = storyId;
|
||||
ra.commentUserId = commentUserId;
|
||||
ra.feedId = feedId;
|
||||
return ra;
|
||||
}
|
||||
|
||||
public static ReadingAction replyToComment(String storyId, String feedId, String commentUserId, String commentReplyText) {
|
||||
ReadingAction ra = new ReadingAction();
|
||||
ra.type = ActionType.REPLY;
|
||||
ra.storyId = storyId;
|
||||
ra.commentUserId = commentUserId;
|
||||
ra.feedId = feedId;
|
||||
ra.commentReplyText = commentReplyText;
|
||||
return ra;
|
||||
}
|
||||
|
||||
public ContentValues toContentValues() {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(DatabaseConstants.ACTION_TIME, System.currentTimeMillis());
|
||||
|
@ -101,6 +149,37 @@ public class ReadingAction {
|
|||
values.put(DatabaseConstants.ACTION_STORY_HASH, storyHash);
|
||||
break;
|
||||
|
||||
case SHARE:
|
||||
values.put(DatabaseConstants.ACTION_SHARE, 1);
|
||||
values.put(DatabaseConstants.ACTION_STORY_HASH, storyHash);
|
||||
values.put(DatabaseConstants.ACTION_STORY_ID, storyId);
|
||||
values.put(DatabaseConstants.ACTION_FEED_ID, feedId);
|
||||
values.put(DatabaseConstants.ACTION_SOURCE_USER_ID, sourceUserId);
|
||||
values.put(DatabaseConstants.ACTION_COMMENT_TEXT, commentReplyText);
|
||||
break;
|
||||
|
||||
case LIKE_COMMENT:
|
||||
values.put(DatabaseConstants.ACTION_LIKE_COMMENT, 1);
|
||||
values.put(DatabaseConstants.ACTION_STORY_ID, storyId);
|
||||
values.put(DatabaseConstants.ACTION_FEED_ID, feedId);
|
||||
values.put(DatabaseConstants.ACTION_COMMENT_ID, commentUserId);
|
||||
break;
|
||||
|
||||
case UNLIKE_COMMENT:
|
||||
values.put(DatabaseConstants.ACTION_UNLIKE_COMMENT, 1);
|
||||
values.put(DatabaseConstants.ACTION_STORY_ID, storyId);
|
||||
values.put(DatabaseConstants.ACTION_FEED_ID, feedId);
|
||||
values.put(DatabaseConstants.ACTION_COMMENT_ID, commentUserId);
|
||||
break;
|
||||
|
||||
case REPLY:
|
||||
values.put(DatabaseConstants.ACTION_REPLY, 1);
|
||||
values.put(DatabaseConstants.ACTION_STORY_ID, storyId);
|
||||
values.put(DatabaseConstants.ACTION_FEED_ID, feedId);
|
||||
values.put(DatabaseConstants.ACTION_COMMENT_ID, commentUserId);
|
||||
values.put(DatabaseConstants.ACTION_COMMENT_TEXT, commentReplyText);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new IllegalStateException("cannot serialise uknown type of action.");
|
||||
|
||||
|
@ -135,6 +214,29 @@ public class ReadingAction {
|
|||
} else if (c.getInt(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_UNSAVE)) == 1) {
|
||||
ra.type = ActionType.UNSAVE;
|
||||
ra.storyHash = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_HASH));
|
||||
} else if (c.getInt(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_SHARE)) == 1) {
|
||||
ra.type = ActionType.SHARE;
|
||||
ra.storyHash = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_HASH));
|
||||
ra.storyId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_ID));
|
||||
ra.feedId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_FEED_ID));
|
||||
ra.sourceUserId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_SOURCE_USER_ID));
|
||||
ra.commentReplyText = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_COMMENT_TEXT));
|
||||
} else if (c.getInt(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_LIKE_COMMENT)) == 1) {
|
||||
ra.type = ActionType.LIKE_COMMENT;
|
||||
ra.storyId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_ID));
|
||||
ra.feedId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_FEED_ID));
|
||||
ra.commentUserId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_COMMENT_ID));
|
||||
} else if (c.getInt(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_UNLIKE_COMMENT)) == 1) {
|
||||
ra.type = ActionType.UNLIKE_COMMENT;
|
||||
ra.storyId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_ID));
|
||||
ra.feedId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_FEED_ID));
|
||||
ra.commentUserId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_COMMENT_ID));
|
||||
} else if (c.getInt(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_REPLY)) == 1) {
|
||||
ra.type = ActionType.REPLY;
|
||||
ra.storyId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_STORY_ID));
|
||||
ra.feedId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_FEED_ID));
|
||||
ra.commentUserId = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_COMMENT_ID));
|
||||
ra.commentReplyText = c.getString(c.getColumnIndexOrThrow(DatabaseConstants.ACTION_COMMENT_TEXT));
|
||||
} else {
|
||||
throw new IllegalStateException("cannot deserialise uknown type of action.");
|
||||
}
|
||||
|
@ -164,6 +266,18 @@ public class ReadingAction {
|
|||
case UNSAVE:
|
||||
return apiManager.markStoryAsUnstarred(storyHash);
|
||||
|
||||
case SHARE:
|
||||
return apiManager.shareStory(storyId, feedId, commentReplyText, sourceUserId);
|
||||
|
||||
case LIKE_COMMENT:
|
||||
return apiManager.favouriteComment(storyId, commentUserId, feedId);
|
||||
|
||||
case UNLIKE_COMMENT:
|
||||
return apiManager.unFavouriteComment(storyId, commentUserId, feedId);
|
||||
|
||||
case REPLY:
|
||||
return apiManager.replyToComment(storyId, feedId, commentUserId, commentReplyText);
|
||||
|
||||
default:
|
||||
|
||||
}
|
||||
|
@ -197,6 +311,23 @@ public class ReadingAction {
|
|||
dbHelper.setStoryStarred(storyHash, false);
|
||||
break;
|
||||
|
||||
case SHARE:
|
||||
dbHelper.setStoryShared(storyHash);
|
||||
dbHelper.insertUpdateComment(storyId, feedId, commentReplyText);
|
||||
break;
|
||||
|
||||
case LIKE_COMMENT:
|
||||
dbHelper.setCommentLiked(storyId, commentUserId, feedId, true);
|
||||
break;
|
||||
|
||||
case UNLIKE_COMMENT:
|
||||
dbHelper.setCommentLiked(storyId, commentUserId, feedId, false);
|
||||
break;
|
||||
|
||||
case REPLY:
|
||||
dbHelper.replyToComment(storyId, feedId, commentUserId, commentReplyText);
|
||||
break;
|
||||
|
||||
default:
|
||||
// not all actions have these, which is fine
|
||||
}
|
||||
|
|
|
@ -1513,7 +1513,21 @@
|
|||
sourceName]];
|
||||
|
||||
[[UIApplication sharedApplication] openURL:activityURL];
|
||||
return;
|
||||
return;
|
||||
} else if ([[preferences stringForKey:@"story_browser"] isEqualToString:@"opera_mini"] &&
|
||||
[[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"opera-http://"]]) {
|
||||
|
||||
|
||||
NSString *operaURL;
|
||||
NSRange prefix = [[url absoluteString] rangeOfString: @"http"];
|
||||
if (NSNotFound != prefix.location) {
|
||||
operaURL = [[url absoluteString]
|
||||
stringByReplacingCharactersInRange: prefix
|
||||
withString: @"opera-http"];
|
||||
}
|
||||
|
||||
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:operaURL]];
|
||||
return;
|
||||
} else {
|
||||
self.activeOriginalStoryURL = url;
|
||||
|
||||
|
|
|
@ -414,6 +414,7 @@
|
|||
<string>In-app browser</string>
|
||||
<string>Safari</string>
|
||||
<string>Chrome</string>
|
||||
<string>Opera Mini</string>
|
||||
</array>
|
||||
<key>DefaultValue</key>
|
||||
<string>inapp</string>
|
||||
|
@ -422,6 +423,7 @@
|
|||
<string>inapp</string>
|
||||
<string>safari</string>
|
||||
<string>chrome</string>
|
||||
<string>opera_mini</string>
|
||||
</array>
|
||||
<key>Key</key>
|
||||
<string>story_browser</string>
|
||||
|
|
|
@ -434,6 +434,7 @@
|
|||
<string>In-app browser</string>
|
||||
<string>Safari</string>
|
||||
<string>Chrome</string>
|
||||
<string>Opera Mini</string>
|
||||
</array>
|
||||
<key>DefaultValue</key>
|
||||
<string>inapp</string>
|
||||
|
@ -442,6 +443,7 @@
|
|||
<string>inapp</string>
|
||||
<string>safari</string>
|
||||
<string>chrome</string>
|
||||
<string>opera_mini</string>
|
||||
</array>
|
||||
<key>Key</key>
|
||||
<string>story_browser</string>
|
||||
|
|
|
@ -86,7 +86,7 @@ backend gunicorn
|
|||
backend gunicorn_counts
|
||||
balance roundrobin
|
||||
option httpchk GET /_haproxychk
|
||||
server gunicorndebug 127.0.0.1:8000 check inter 2000ms
|
||||
server gunicorndebug 127.0.0.1:8000 check inter 600000ms
|
||||
|
||||
backend maintenance
|
||||
option httpchk HEAD /maintenance HTTP/1.1\r\nHost:\ www
|
||||
|
|
|
@ -108,8 +108,8 @@ a:active {
|
|||
|
||||
blockquote {
|
||||
background-color: #F0F0F0;
|
||||
border-left: 1px solid #9b9b9b;
|
||||
padding: 0.5em 2em;
|
||||
border-left: 1px solid #9B9B9B;
|
||||
padding: .5em 2em;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
|
@ -187,7 +187,7 @@ blockquote {
|
|||
box-sizing: border-box;
|
||||
}
|
||||
.NB-blue-button:hover {
|
||||
border: 1px solid #3573a5;
|
||||
border: 1px solid #3573A5;
|
||||
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #5aa1d8), color-stop(100%, #4d98d2));
|
||||
background-image: -webkit-linear-gradient(#5aa1d8, #4d98d2);
|
||||
background-image: -moz-linear-gradient(#5aa1d8, #4d98d2);
|
||||
|
@ -195,7 +195,7 @@ blockquote {
|
|||
background-image: linear-gradient(#5aa1d8, #4d98d2);
|
||||
}
|
||||
.NB-blue-button:active {
|
||||
border: 1px solid #2b5c84;
|
||||
border: 1px solid #2B5C84;
|
||||
background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #4d98d2), color-stop(100%, #4d98d2));
|
||||
background-image: -webkit-linear-gradient(#4d98d2, #4d98d2);
|
||||
background-image: -moz-linear-gradient(#4d98d2, #4d98d2);
|
||||
|
@ -569,8 +569,8 @@ blockquote {
|
|||
}
|
||||
|
||||
.NB-story {
|
||||
border-left: 4px solid #6a6a6a;
|
||||
border-right: 4px solid #6a6a6a;
|
||||
border-left: 4px solid #6A6A6A;
|
||||
border-right: 4px solid #6A6A6A;
|
||||
background: #fff;
|
||||
-webkit-box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.15);
|
||||
-moz-box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.15);
|
||||
|
@ -797,7 +797,7 @@ blockquote {
|
|||
z-index: 10;
|
||||
margin: 0 30px;
|
||||
background: #f6f6f6;
|
||||
border: 1px solid #cccccc;
|
||||
border: 1px solid #CCC;
|
||||
padding: 0 20px;
|
||||
border-top: 0;
|
||||
}
|
||||
|
@ -908,6 +908,19 @@ blockquote {
|
|||
display: none;
|
||||
}
|
||||
}
|
||||
.NB-story-comments-container .NB-story-comments-shares .NB-story-comment .NB-story-comment-author-container {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.NB-story-comments-container .NB-story-comments-shares .NB-user-avatar img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.NB-story-comments-container .NB-story-comments-shares .NB-user-avatar.NB-story-comment-reshare img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.NB-story-comments-container .NB-story-comment-reply {
|
||||
margin-top: 15px;
|
||||
padding: 15px 0 0px 44px;
|
||||
|
@ -1169,6 +1182,10 @@ blockquote {
|
|||
border-bottom: 1px solid #bdbdbd;
|
||||
padding: 3px 0;
|
||||
}
|
||||
.NB-story-comments-container .NB-story-comments-public-header-wrapper.NB-public-top {
|
||||
margin-top: 0;
|
||||
padding-top: 9px;
|
||||
}
|
||||
.NB-story-comments-container .NB-story-comment-likes {
|
||||
overflow: hidden;
|
||||
height: 14px;
|
||||
|
@ -1222,10 +1239,10 @@ blockquote {
|
|||
overflow: hidden;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.6);
|
||||
-webkit-transition: all 0.32s ease-out;
|
||||
-moz-transition: all 0.32s ease-out;
|
||||
-o-transition: all 0.32s ease-out;
|
||||
-ms-transition: all 0.32s ease-out;
|
||||
-webkit-transition: all .32s ease-out;
|
||||
-moz-transition: all .32s ease-out;
|
||||
-o-transition: all .32s ease-out;
|
||||
-ms-transition: all .32s ease-out;
|
||||
}
|
||||
.NB-page-controls-next .NB-page-controls-text,
|
||||
.NB-page-controls-end .NB-page-controls-text {
|
||||
|
@ -1302,7 +1319,7 @@ footer {
|
|||
right: 60px;
|
||||
display: inline-block;
|
||||
border-right: 7px solid rgba(0, 0, 0, 0);
|
||||
border-bottom: 7px solid #cccccc;
|
||||
border-bottom: 7px solid #CCC;
|
||||
border-left: 7px solid rgba(0, 0, 0, 0);
|
||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||
content: '';
|
||||
|
@ -1313,7 +1330,7 @@ footer {
|
|||
right: 61px;
|
||||
display: inline-block;
|
||||
border-right: 6px solid rgba(0, 0, 0, 0);
|
||||
border-bottom: 6px solid white;
|
||||
border-bottom: 6px solid #FFF;
|
||||
border-left: 6px solid rgba(0, 0, 0, 0);
|
||||
content: '';
|
||||
}
|
||||
|
@ -1342,7 +1359,7 @@ footer {
|
|||
}
|
||||
.popover .popover-title {
|
||||
padding: 16px 14px;
|
||||
border-bottom: 1px solid silver;
|
||||
border-bottom: 1px solid #C0C0C0;
|
||||
position: relative;
|
||||
}
|
||||
.popover .popover-content {
|
||||
|
@ -1351,7 +1368,7 @@ footer {
|
|||
}
|
||||
.popover input {
|
||||
border: 1px solid #bdbdbd;
|
||||
border: 1px solid #bdbdbd;
|
||||
border: 1px solid #BDBDBD;
|
||||
padding: 5px;
|
||||
width: 100%;
|
||||
margin-bottom: 8px;
|
||||
|
|
|
@ -67,7 +67,7 @@ $default-box-shadow-blur: 1px;
|
|||
&:active {
|
||||
border: 1px solid $border-color-active;
|
||||
@include background-image(linear-gradient(#e9e9e9, #e9e9e9));
|
||||
@include single-box-shadow(#c1c1c1, 0px, 0px, 10px, false, true);
|
||||
@include single-box-shadow($color: #c1c1c1, $hoff:0px, $voff:0px, $blur:10px, $inset:true);
|
||||
color: $text-color;
|
||||
}
|
||||
@media screen and (max-width: 580px) {
|
||||
|
@ -101,7 +101,7 @@ $default-box-shadow-blur: 1px;
|
|||
&:active {
|
||||
border: 1px solid #2B5C84;
|
||||
@include background-image(linear-gradient(#4d98d2, #4d98d2));
|
||||
@include single-box-shadow(#1f74b8, 0px, 0px, 10px, false, true);
|
||||
@include single-box-shadow($color: #1f74b8, $hoff:0px, $voff:0px, $blur:10px, $inset:true);
|
||||
}
|
||||
@media screen and (max-width: 580px) {
|
||||
padding: 2px 6px;
|
||||
|
@ -234,7 +234,7 @@ blockquote {
|
|||
|
||||
|
||||
.NB-header {
|
||||
@include single-box-shadow(#7d7d7d, 0px, 0px, 10px, false, false);
|
||||
@include single-box-shadow($color: #7d7d7d, $hoff:0px, $voff:0px, $blur:10px, $inset:false);
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
|
@ -563,7 +563,7 @@ blockquote {
|
|||
border-left: 4px solid #6A6A6A;
|
||||
border-right: 4px solid #6A6A6A;
|
||||
background: #fff;
|
||||
@include single-box-shadow(rgba(0, 0, 0, .15), 0px, 0px, 10px, false, false);
|
||||
@include single-box-shadow($color: rgba(0, 0, 0, .15), $hoff:0px, $voff:0px, $blur:10px, $inset:false);
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
|
@ -829,14 +829,13 @@ blockquote {
|
|||
@include border-radius(3px, 3px);
|
||||
/* @include single-box-shadow; gets in the way of transparent PNGs */
|
||||
|
||||
@media screen and ( max-width: 580px) {
|
||||
@media screen and ( max-width: 580px) {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.NB-user-avatar.NB-story-comment-reshare img {
|
||||
top: 27px;
|
||||
left: 0px;
|
||||
|
@ -916,6 +915,25 @@ blockquote {
|
|||
}
|
||||
}
|
||||
|
||||
.NB-story-comments-shares {
|
||||
.NB-story-comment {
|
||||
.NB-story-comment-author-container {
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.NB-user-avatar img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.NB-user-avatar.NB-story-comment-reshare img {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.NB-story-comment-reply {
|
||||
margin-top: 15px;
|
||||
padding: 15px 0 0px 44px;
|
||||
|
@ -1088,6 +1106,11 @@ blockquote {
|
|||
font-size: $smallest-font-size;
|
||||
border-bottom: 1px solid $border-color;
|
||||
padding: 3px 0;
|
||||
|
||||
&.NB-public-top {
|
||||
margin-top: 0;
|
||||
padding-top: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
.NB-story-comment-likes {
|
||||
|
|
|
@ -3213,6 +3213,9 @@ body {
|
|||
left: 6px;
|
||||
z-index: 1;
|
||||
}
|
||||
.NB-story-comment .NB-story-comment-friend-share .NB-story-comment-author-avatar.NB-user-avatar.NB-story-comment-reshare {
|
||||
top: 10px;
|
||||
}
|
||||
.NB-story-comment .NB-story-comment-author-avatar.NB-user-avatar img {
|
||||
border-radius: 6px;
|
||||
margin: 2px 0 0 1px;
|
||||
|
@ -3223,10 +3226,21 @@ body {
|
|||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
.NB-story-comment .NB-story-comment-friend-share .NB-story-comment-author-avatar.NB-user-avatar img {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
.NB-story-comment .NB-story-comment-friend-share .NB-story-comment-author-avatar.NB-user-avatar.NB-story-comment-reshare img {
|
||||
margin-left: 0px;
|
||||
}
|
||||
.NB-story-comment .NB-story-comment-author-container {
|
||||
overflow: hidden;
|
||||
margin: 6px 0 0;
|
||||
}
|
||||
.NB-story-comment .NB-story-comment-friend-share .NB-story-comment-author-container {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.NB-story-comment .NB-story-comment-reshares {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
@ -3283,7 +3297,8 @@ body {
|
|||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
.NB-story-comment .NB-story-comment-likes-users .NB-story-share-profile .NB-user-avatar img {
|
||||
.NB-story-comment .NB-story-comment-likes-users .NB-story-share-profile .NB-user-avatar img,
|
||||
.NB-story-comment .NB-story-comment-friend-share .NB-story-comment-likes-users .NB-story-share-profile .NB-user-avatar img {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
@ -3536,6 +3551,11 @@ body {
|
|||
height: 22px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.NB-story-comment-friend-share .NB-story-share-profile .NB-user-avatar img.NB-user-avatar-image {
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
}
|
||||
|
||||
.NB-story-share-profile .NB-user-avatar img.NB-user-avatar-private {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
|
@ -7678,6 +7698,12 @@ form.opml_import_form input {
|
|||
word-wrap: break-word;
|
||||
overflow: hidden;
|
||||
}
|
||||
.NB-modal-admin .NB-admin-training-counts span {
|
||||
padding-right: 8px;
|
||||
}
|
||||
.NB-modal-admin .NB-admin-training-counts span.NB-grey {
|
||||
color: #D0D0D0;
|
||||
}
|
||||
|
||||
/* ===================== */
|
||||
/* = Email Story Modal = */
|
||||
|
@ -11127,8 +11153,18 @@ form.opml_import_form input {
|
|||
.NB-interaction .NB-splash-link:hover {
|
||||
color: #405BA8;
|
||||
}
|
||||
.NB-interaction-content {
|
||||
font-size: 11px;
|
||||
padding-top: 4px;
|
||||
line-height: 16px;
|
||||
color: #9d9d9d;
|
||||
}
|
||||
.NB-interaction-content a {
|
||||
color: #A6A9B0;
|
||||
}
|
||||
.NB-interaction:active:not(.NB-disabled) .NB-interaction-content .NB-splash-link {
|
||||
color: #A85B40;
|
||||
color: #405BA8;
|
||||
text-decoration: none;
|
||||
}
|
||||
.NB-interaction-photo {
|
||||
position: absolute;
|
||||
|
@ -11144,7 +11180,7 @@ form.opml_import_form input {
|
|||
height: 16px;
|
||||
}
|
||||
.NB-interaction-date {
|
||||
color: #929697;
|
||||
color: #5e828b;
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
padding: 5px 0 0;
|
||||
|
@ -11159,12 +11195,6 @@ form.opml_import_form input {
|
|||
padding: 2px 0 0 0;
|
||||
opacity: .9;
|
||||
}
|
||||
.NB-interaction-content {
|
||||
font-size: 11px;
|
||||
padding-top: 4px;
|
||||
line-height: 16px;
|
||||
color: #b6b6b6;
|
||||
}
|
||||
.NB-interaction-username {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
|
|
@ -1641,6 +1641,10 @@ NEWSBLUR.AssetModel = Backbone.Router.extend({
|
|||
this.make_request('/profile/refund_premium', data, callback, error_callback);
|
||||
},
|
||||
|
||||
never_expire_premium: function(data, callback, error_callback) {
|
||||
this.make_request('/profile/never_expire_premium', data, callback, error_callback);
|
||||
},
|
||||
|
||||
delete_saved_stories: function(timestamp, callback, error_callback) {
|
||||
var self = this;
|
||||
var pre_callback = function(data) {
|
||||
|
|
|
@ -14,6 +14,7 @@ NEWSBLUR.Models.Story = Backbone.Model.extend({
|
|||
|
||||
populate_comments: function(story, collection) {
|
||||
this.friend_comments = new NEWSBLUR.Collections.Comments(this.get('friend_comments'));
|
||||
this.friend_shares = new NEWSBLUR.Collections.Comments(this.get('friend_shares'));
|
||||
this.public_comments = new NEWSBLUR.Collections.Comments(this.get('public_comments'));
|
||||
},
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
NEWSBLUR.ReaderUserAdmin = function(options) {
|
||||
var defaults = {};
|
||||
var defaults = {
|
||||
width: 700
|
||||
};
|
||||
|
||||
this.options = $.extend({}, defaults, options);
|
||||
this.model = NEWSBLUR.assets;
|
||||
|
@ -78,6 +80,7 @@ _.extend(NEWSBLUR.ReaderUserAdmin.prototype, {
|
|||
]));
|
||||
$actions.append($.make('div', { className: "NB-modal-submit-button NB-modal-submit-green NB-admin-action-refund", style: "float: left" }, "Full Refund"));
|
||||
$actions.append($.make('div', { className: "NB-modal-submit-button NB-modal-submit-green NB-admin-action-refund-partial", style: "float: left" }, "Refund $12"));
|
||||
$actions.append($.make('div', { className: "NB-modal-submit-button NB-modal-submit-green NB-admin-action-never-expire", style: "float: left" }, "Never expire"));
|
||||
} else {
|
||||
$actions.append($.make('div', { className: "NB-modal-submit-button NB-modal-submit-green NB-admin-action-upgrade" }, "Upgrade to premium"));
|
||||
}
|
||||
|
@ -86,20 +89,29 @@ _.extend(NEWSBLUR.ReaderUserAdmin.prototype, {
|
|||
$actions.append($.make('div', { className: "NB-modal-submit-button NB-modal-submit-green NB-admin-action-opml", style: "float: left" }, "OPML"));
|
||||
|
||||
$statistics.append($.make('dl', [
|
||||
$.make('dt', 'Stripe Id:'),
|
||||
$.make('dd', $.make('a', { href: "https://manage.stripe.com/customers/" + data.statistics.stripe_id, className: 'NB-splash-link' }, data.statistics.stripe_id)),
|
||||
$.make('dt', 'Created:'),
|
||||
$.make('dd', data.statistics.created_date),
|
||||
$.make('dt', 'Last seen:'),
|
||||
$.make('dd', data.statistics.last_seen_date),
|
||||
$.make('dt', 'Timezone:'),
|
||||
$.make('dd', data.statistics.timezone),
|
||||
$.make('dt', 'Email:'),
|
||||
$.make('dd', data.statistics.email),
|
||||
$.make('dt', 'Stripe Id:'),
|
||||
$.make('dd', $.make('a', { href: "https://manage.stripe.com/customers/" + data.statistics.stripe_id, className: 'NB-splash-link' }, data.statistics.stripe_id)),
|
||||
$.make('dt', 'Feeds:'),
|
||||
$.make('dd', Inflector.commas(data.statistics.feeds)),
|
||||
$.make('dt', 'Feed opens:'),
|
||||
$.make('dd', Inflector.commas(data.statistics.feed_opens)),
|
||||
$.make('dt', 'Read Stories:'),
|
||||
$.make('dd', Inflector.commas(data.statistics.read_story_count))
|
||||
$.make('dd', Inflector.commas(data.statistics.read_story_count)),
|
||||
$.make('dt', 'Training:'),
|
||||
$.make('dd', { className: 'NB-admin-training-counts' }, [
|
||||
$.make('span', { className: data.statistics.training.title ? '' : 'NB-grey' }, 'Title: ' + data.statistics.training.title),
|
||||
$.make('span', { className: data.statistics.training.author ? '' : 'NB-grey' }, 'Author: ' + data.statistics.training.author),
|
||||
$.make('span', { className: data.statistics.training.tag ? '' : 'NB-grey' }, 'Tag: ' + data.statistics.training.tag),
|
||||
$.make('span', { className: data.statistics.training.feed ? '' : 'NB-grey' }, 'Feed: ' + data.statistics.training.feed)
|
||||
])
|
||||
]));
|
||||
$(window).resize();
|
||||
}, this));
|
||||
|
@ -135,6 +147,17 @@ _.extend(NEWSBLUR.ReaderUserAdmin.prototype, {
|
|||
$(".NB-admin-action-refund").replaceWith($.make('div', 'Error: ' + JSON.stringify(data)));
|
||||
});
|
||||
});
|
||||
$.targetIs(e, { tagSelector: '.NB-admin-action-never-expire' }, function($t, $p) {
|
||||
e.preventDefault();
|
||||
|
||||
NEWSBLUR.assets.never_expire_premium({
|
||||
'user_id': self.user.get('user_id')
|
||||
}, function(data) {
|
||||
self.fetch_payment_history();
|
||||
}, function(data) {
|
||||
$(".NB-admin-action-never-expire").replaceWith($.make('div', 'Error: ' + JSON.stringify(data)));
|
||||
});
|
||||
});
|
||||
$.targetIs(e, { tagSelector: '.NB-admin-action-upgrade' }, function($t, $p) {
|
||||
e.preventDefault();
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ NEWSBLUR.Views.StoryComment = Backbone.View.extend({
|
|||
var has_likes = _.any(this.model.get('liking_users'));
|
||||
var liked = _.contains(this.model.get('liking_users'), NEWSBLUR.Globals.user_id);
|
||||
|
||||
var $comment = $.make('div', [
|
||||
var $comment = $.make('div', { className: (this.options.friend_share ? "NB-story-comment-friend-share" : "") }, [
|
||||
$.make('div', { className: 'NB-story-comment-author-avatar NB-user-avatar ' + reshare_class }, [
|
||||
$.make('img', { src: this.user.get('photo_url') })
|
||||
]),
|
||||
|
|
|
@ -17,8 +17,10 @@ NEWSBLUR.Views.StoryCommentsView = Backbone.View.extend({
|
|||
}));
|
||||
this.render_teaser();
|
||||
this.render_comments_friends();
|
||||
this.render_shares_friends();
|
||||
this.render_comments_public();
|
||||
this.$el.toggleClass('NB-hidden', !this.model.get('comment_count'));
|
||||
this.$el.toggleClass('NB-hidden', (!this.model.get('comment_count') &&
|
||||
!this.model.get('share_count_friends')));
|
||||
}
|
||||
|
||||
return this;
|
||||
|
@ -41,7 +43,7 @@ NEWSBLUR.Views.StoryCommentsView = Backbone.View.extend({
|
|||
var $thumb = NEWSBLUR.Views.ProfileThumb.create(user_id).render().el;
|
||||
$comments_public.append($thumb);
|
||||
});
|
||||
if (!this.model.friend_comments.length && !this.model.public_comments.length) {
|
||||
if (!this.model.friend_comments.length && !this.model.public_comments.length && !this.model.friend_shares.length) {
|
||||
this.$el.hide();
|
||||
}
|
||||
|
||||
|
@ -86,6 +88,30 @@ NEWSBLUR.Views.StoryCommentsView = Backbone.View.extend({
|
|||
this.$el.append($comment);
|
||||
}, this));
|
||||
},
|
||||
|
||||
render_shares_friends: function() {
|
||||
var shares_without_comments = this.model.get('shared_by_friends');
|
||||
if (shares_without_comments.length <= 0) return;
|
||||
|
||||
var $header = $.make('div', {
|
||||
className: 'NB-story-comments-public-header-wrapper'
|
||||
}, $.make('div', {
|
||||
className: 'NB-story-comments-public-header NB-module-header'
|
||||
}, [
|
||||
Inflector.pluralize(' share', shares_without_comments.length, true)
|
||||
]));
|
||||
|
||||
this.$el.append($header);
|
||||
|
||||
this.model.friend_shares.each(_.bind(function(comment) {
|
||||
var $comment = new NEWSBLUR.Views.StoryComment({
|
||||
model: comment,
|
||||
story: this.model,
|
||||
friend_share: true
|
||||
}).render().el;
|
||||
this.$el.append($comment);
|
||||
}, this));
|
||||
},
|
||||
|
||||
render_comments_public: function() {
|
||||
if (!this.model.get('comment_count_public') || !this.model.get('comment_count')) return;
|
||||
|
|
|
@ -280,7 +280,8 @@ NEWSBLUR.Views.StoryDetailView = Backbone.View.extend({
|
|||
var $original_comments = this.$('.NB-feed-story-comments-container,.NB-feed-story-comments');
|
||||
var $original_shares = this.$('.NB-feed-story-shares-container,.NB-feed-story-shares');
|
||||
if (this.model.get("comment_count") || this.model.get("share_count")) {
|
||||
this.comments_view = new NEWSBLUR.Views.StoryCommentsView({model: this.model}).render();
|
||||
var comments_view = new NEWSBLUR.Views.StoryCommentsView({model: this.model});
|
||||
this.comments_view = comments_view.render();
|
||||
var $comments = this.comments_view.el;
|
||||
$original_comments.replaceWith($comments);
|
||||
var $shares = $('.NB-story-comments-shares-teaser-wrapper', $comments);
|
||||
|
|
|
@ -28,7 +28,7 @@ app.get /^\/rss_feeds\/icon\/(\d+)\/?/, (req, res) =>
|
|||
feed_id = parseInt(req.params, 10)
|
||||
etag = req.header('If-None-Match')
|
||||
@collection.findOne _id: feed_id, (err, docs) ->
|
||||
console.log "Req: #{feed_id}, etag: #{etag}/#{docs?.color}"
|
||||
console.log "Req: #{feed_id}, etag: #{etag}/#{docs?.color} (err: #{err}, docs? #{!!(docs and docs.data)})"
|
||||
if not err and etag and docs and docs?.color == etag
|
||||
res.send 304
|
||||
else if not err and docs and docs.data
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
return _this.collection.findOne({
|
||||
_id: feed_id
|
||||
}, function(err, docs) {
|
||||
console.log("Req: " + feed_id + ", etag: " + etag + "/" + (docs != null ? docs.color : void 0));
|
||||
console.log("Req: " + feed_id + ", etag: " + etag + "/" + (docs != null ? docs.color : void 0) + " (err: " + err + ", docs? " + (!!(docs && docs.data)) + ")");
|
||||
if (!err && etag && docs && (docs != null ? docs.color : void 0) === etag) {
|
||||
return res.send(304);
|
||||
} else if (!err && docs && docs.data) {
|
||||
|
|
|
@ -15,11 +15,11 @@ SECURE = !!process.env.NODE_SSL
|
|||
if SECURE
|
||||
privateKey = fs.readFileSync('./config/certificates/newsblur.com.key').toString()
|
||||
certificate = fs.readFileSync('./config/certificates/newsblur.com.crt').toString()
|
||||
ca = fs.readFileSync('./config/certificates/intermediate.crt').toString()
|
||||
# ca = fs.readFileSync('./config/certificates/intermediate.crt').toString()
|
||||
io = require('socket.io').listen 8889
|
||||
key: privateKey
|
||||
cert: certificate
|
||||
ca: ca
|
||||
# ca: ca
|
||||
else
|
||||
io = require('socket.io').listen 8888
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Generated by CoffeeScript 1.4.0
|
||||
(function() {
|
||||
var REDIS_SERVER, SECURE, ca, certificate, fs, io, log, privateKey, redis;
|
||||
var REDIS_SERVER, SECURE, certificate, fs, io, log, privateKey, redis;
|
||||
|
||||
fs = require('fs');
|
||||
|
||||
|
@ -15,11 +15,9 @@
|
|||
if (SECURE) {
|
||||
privateKey = fs.readFileSync('./config/certificates/newsblur.com.key').toString();
|
||||
certificate = fs.readFileSync('./config/certificates/newsblur.com.crt').toString();
|
||||
ca = fs.readFileSync('./config/certificates/intermediate.crt').toString();
|
||||
io = require('socket.io').listen(8889, {
|
||||
key: privateKey,
|
||||
cert: certificate,
|
||||
ca: ca
|
||||
cert: certificate
|
||||
});
|
||||
} else {
|
||||
io = require('socket.io').listen(8888);
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
{{ username }} replied to <span class="NB-interaction-username NB-splash-link" data-user-id="{{ activity.with_user_id }}">{{ activity.with_user.username }}</span>:
|
||||
</div>
|
||||
<div class="NB-interaction-content">
|
||||
"<span class="NB-interaction-reply-content" data-story-id="{{ activity.content_id }}">{{ activity.content|safe|truncatewords:16 }}</span>"
|
||||
"<span class="NB-interaction-reply-content" data-story-id="{{ activity.content_id }}">{{ activity.content|safe|truncatewords:128 }}</span>"
|
||||
</div>
|
||||
<div class="NB-interaction-date">
|
||||
{{ activity.time_since }} ago
|
||||
|
@ -48,7 +48,7 @@
|
|||
{{ username }} favorited <span class="NB-interaction-username NB-splash-link" data-user-id="{{ activity.with_user_id }}">{{ activity.with_user.username }}</span>'s comments on <span class="NB-interaction-sharedstory-title NB-splash-link">{{ activity.title|safe|truncatewords:6 }}</span>:
|
||||
</div>
|
||||
<div class="NB-interaction-content">
|
||||
"<span class="NB-interaction-sharedstory-content">{{ activity.content|truncatewords:16 }}</span>"
|
||||
"<span class="NB-interaction-sharedstory-content">{{ activity.content|truncatewords:8 }}</span>"
|
||||
</div>
|
||||
<div class="NB-interaction-date">
|
||||
{{ activity.time_since }} ago
|
||||
|
@ -62,7 +62,7 @@
|
|||
</div>
|
||||
{% if activity.content %}
|
||||
<div class="NB-interaction-content">
|
||||
"<span class="NB-interaction-sharedstory-content">{{ activity.content|safe|truncatewords:16 }}</span>"
|
||||
"<span class="NB-interaction-sharedstory-content">{{ activity.content|safe|truncatewords:36 }}</span>"
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="NB-interaction-date">
|
||||
|
@ -73,7 +73,7 @@
|
|||
{% if activity.category == 'star' %}
|
||||
<img class="NB-interaction-photo" src="{{ MEDIA_URL }}img/icons/circular/clock.png">
|
||||
<div class="NB-interaction-title">
|
||||
You saved "<span class="NB-interaction-starred-story-title NB-splash-link">{{ activity.content|safe|truncatewords:8 }}</span>".
|
||||
You saved "<span class="NB-interaction-starred-story-title NB-splash-link">{{ activity.content|safe|truncatewords:6 }}</span>".
|
||||
</div>
|
||||
<div class="NB-interaction-date">
|
||||
{{ activity.time_since }} ago
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
</span> replied to your {% if interaction.category == 'comment_reply' %}comment{% else %}reply{% endif %}:
|
||||
</div>
|
||||
<div class="NB-interaction-content">
|
||||
<span class="NB-interaction-reply-content" data-social-user-id="{{ interaction.feed_id }}">{{ interaction.content|safe|truncatewords:16 }}</span>
|
||||
<span class="NB-interaction-reply-content" data-social-user-id="{{ interaction.feed_id }}">{{ interaction.content|safe|truncatewords:128 }}</span>
|
||||
</div>
|
||||
<div class="NB-interaction-date">
|
||||
{{ interaction.time_since }} ago
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
</div>
|
||||
|
||||
<div class="NB-story-comments-container">
|
||||
{% if story.comment_count or not story.shared_by_user %}
|
||||
{% if story.comment_count or not story.shared_by_user or story.friend_share_count %}
|
||||
{% render_story_comments story %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
|
|
@ -7,6 +7,14 @@
|
|||
{% render_story_comment story comment %}
|
||||
{% endfor %}
|
||||
|
||||
{% if story.share_count_friends %}
|
||||
<div class="NB-story-comments-shares">
|
||||
{% for comment in story.friend_shares %}
|
||||
{% render_story_comment story comment %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="NB-story-comment-edit NB-story-comment {% if story.shared_by_user %}NB-hidden{% endif %}" data-user-id="{{ user_social_profile.user_id }}">
|
||||
<a href="{{ user_social_profile.blurblog_url }}" class="NB-user-avatar">
|
||||
<img src="{% if user_social_profile.photo_url %}{{ user_social_profile.photo_url }}{% else %}{{ MEDIA_URL }}/img/circular/circular_avatar.png{% endif %}">
|
||||
|
@ -27,7 +35,7 @@
|
|||
|
||||
<div class="NB-story-comments-public">
|
||||
{% if story.comment_count_public %}
|
||||
<div class="NB-story-comments-public-header-wrapper">
|
||||
<div class="NB-story-comments-public-header-wrapper {% if story.shared_by_user and not story.friend_comments %}NB-public-top{% endif %}">
|
||||
<div class="NB-story-comments-public-header">
|
||||
{{ story.comment_count_public }} public comment{{ story.comment_count_public|pluralize }}
|
||||
</div>
|
||||
|
|
29696
utils/backups/users.txt
29696
utils/backups/users.txt
File diff suppressed because it is too large
Load diff
|
@ -287,9 +287,13 @@ class ProcessFeed:
|
|||
return FEED_SAME, ret_values
|
||||
|
||||
# 302: Temporary redirect: ignore
|
||||
# 301: Permanent redirect: save it
|
||||
# 301: Permanent redirect: save it (after 20 tries)
|
||||
if self.fpf.status == 301:
|
||||
if not self.fpf.href.endswith('feedburner.com/atom.xml'):
|
||||
if self.fpf.href.endswith('feedburner.com/atom.xml'):
|
||||
return FEED_ERRHTTP, ret_values
|
||||
redirects, non_redirects = self.feed.count_redirects_in_history('feed')
|
||||
self.feed.save_feed_history(self.fpf.status, "HTTP Redirect (%d to go)" % (20-len(redirects)))
|
||||
if len(redirects) >= 20 or len(non_redirects) == 0:
|
||||
self.feed.feed_address = self.fpf.href
|
||||
if not self.feed.known_good:
|
||||
self.feed.fetched_once = True
|
||||
|
@ -359,8 +363,14 @@ class ProcessFeed:
|
|||
self.feed.data.feed_tagline = utf8encode(tagline)
|
||||
self.feed.data.save()
|
||||
if not self.feed.feed_link_locked:
|
||||
self.feed.feed_link = self.fpf.feed.get('link') or self.fpf.feed.get('id') or self.feed.feed_link
|
||||
|
||||
new_feed_link = self.fpf.feed.get('link') or self.fpf.feed.get('id') or self.feed.feed_link
|
||||
if new_feed_link != self.feed.feed_link:
|
||||
logging.debug(" ---> [%-30s] ~SB~FRFeed's page is different: %s to %s" % (self.feed.title[:30], self.feed.feed_link, new_feed_link))
|
||||
redirects, non_redirects = self.feed.count_redirects_in_history('page')
|
||||
self.feed.save_page_history(301, "HTTP Redirect (%s to go)" % (20-len(redirects)))
|
||||
if len(redirects) >= 20 or len(non_redirects) == 0:
|
||||
self.feed.feed_link = new_feed_link
|
||||
|
||||
self.feed = self.feed.save()
|
||||
|
||||
# Determine if stories aren't valid and replace broken guids
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import select
|
||||
import subprocess
|
||||
|
@ -46,34 +47,44 @@ def create_streams_for_roles(role, role2, command=None, path=None):
|
|||
path = "/srv/newsblur/logs/newsblur.log"
|
||||
if not command:
|
||||
command = "tail -f"
|
||||
for hostname in (hosts[role] + hosts[role2]):
|
||||
if isinstance(hostname, dict):
|
||||
address = hostname['address']
|
||||
hostname = hostname['name']
|
||||
elif ':' in hostname:
|
||||
hostname, address = hostname.split(':', 1)
|
||||
elif isinstance(hostname, tuple):
|
||||
hostname, address = hostname[0], hostname[1]
|
||||
else:
|
||||
address = hostname
|
||||
if any(h in hostname for h in IGNORE_HOSTS): continue
|
||||
if hostname in found: continue
|
||||
if 'ec2' in hostname:
|
||||
s = subprocess.Popen(["ssh",
|
||||
"-i", os.path.expanduser(os.path.join(fabfile.env.SECRETS_PATH,
|
||||
"keys/ec2.pem")),
|
||||
address, "%s %s" % (command, path)], stdout=subprocess.PIPE)
|
||||
else:
|
||||
s = subprocess.Popen(["ssh", "-l", NEWSBLUR_USERNAME,
|
||||
"-i", os.path.expanduser(os.path.join(fabfile.env.SECRETS_PATH,
|
||||
"keys/newsblur.key")),
|
||||
address, "%s %s" % (command, path)], stdout=subprocess.PIPE)
|
||||
s.name = hostname
|
||||
streams.append(s)
|
||||
found.add(hostname)
|
||||
|
||||
if role in hosts:
|
||||
for hostname in (hosts[role] + hosts[role2]):
|
||||
if any(h in hostname for h in IGNORE_HOSTS) and role != 'push': continue
|
||||
follow_host(streams, found, hostname, command, path)
|
||||
else:
|
||||
host = role
|
||||
role = re.search(r'([^0-9]+)', host).group()
|
||||
for hostname in hosts[role]:
|
||||
if hostname['name'] == host:
|
||||
follow_host(streams, found, hostname, command, path)
|
||||
|
||||
return streams
|
||||
|
||||
def follow_host(streams, found, hostname, command=None, path=None):
|
||||
if isinstance(hostname, dict):
|
||||
address = hostname['address']
|
||||
hostname = hostname['name']
|
||||
elif ':' in hostname:
|
||||
hostname, address = hostname.split(':', 1)
|
||||
elif isinstance(hostname, tuple):
|
||||
hostname, address = hostname[0], hostname[1]
|
||||
else:
|
||||
address = hostname
|
||||
if hostname in found: return
|
||||
if 'ec2' in hostname:
|
||||
s = subprocess.Popen(["ssh",
|
||||
"-i", os.path.expanduser(os.path.join(fabfile.env.SECRETS_PATH,
|
||||
"keys/ec2.pem")),
|
||||
address, "%s %s" % (command, path)], stdout=subprocess.PIPE)
|
||||
else:
|
||||
s = subprocess.Popen(["ssh", "-l", NEWSBLUR_USERNAME,
|
||||
"-i", os.path.expanduser(os.path.join(fabfile.env.SECRETS_PATH,
|
||||
"keys/newsblur.key")),
|
||||
address, "%s %s" % (command, path)], stdout=subprocess.PIPE)
|
||||
s.name = hostname
|
||||
streams.append(s)
|
||||
found.add(hostname)
|
||||
|
||||
def read_streams(streams):
|
||||
while True:
|
||||
r, _, _ = select.select(
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import tlnb
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
tlnb.main(role="task", role2="ec2task")
|
||||
role = "task"
|
||||
if len(sys.argv) > 1:
|
||||
role = sys.argv[1]
|
||||
tlnb.main(role=role, role2="ec2task")
|
||||
|
2
vendor/paypalapi/settings.py
vendored
2
vendor/paypalapi/settings.py
vendored
|
@ -116,7 +116,7 @@ class PayPalConfig(object):
|
|||
|
||||
# Set the CA_CERTS location. This can either be a None, a bool, or a
|
||||
# string path.
|
||||
if kwargs.get('API_CA_CERTS'):
|
||||
if 'API_CA_CERTS' in kwargs:
|
||||
self.API_CA_CERTS = kwargs['API_CA_CERTS']
|
||||
|
||||
if isinstance(self.API_CA_CERTS, basestring) and not os.path.exists(self.API_CA_CERTS):
|
||||
|
|
Loading…
Add table
Reference in a new issue