Merge remote-tracking branch 'upstream/master'

This commit is contained in:
ojiikun 2013-11-02 21:15:12 +00:00
commit 1748ac6b0d
250 changed files with 9159 additions and 67984 deletions

View file

@ -57,11 +57,15 @@ class Profile(models.Model):
return "%s <%s> (Premium: %s)" % (self.user, self.user.email, self.is_premium)
@property
def unread_cutoff(self):
if self.is_premium:
def unread_cutoff(self, force_premium=False):
if self.is_premium or force_premium:
return datetime.datetime.utcnow() - datetime.timedelta(days=settings.DAYS_OF_UNREAD)
return datetime.datetime.utcnow() - datetime.timedelta(days=settings.DAYS_OF_UNREAD_FREE)
@property
def unread_cutoff_premium(self):
return datetime.datetime.utcnow() - datetime.timedelta(days=settings.DAYS_OF_UNREAD)
def canonical(self):
return {

View file

@ -470,7 +470,8 @@ class UserSubscription(models.Model):
if cutoff_date:
cutoff_date = cutoff_date + datetime.timedelta(seconds=1)
else:
latest_story = MStory.objects(story_feed_id=self.feed.pk).order_by('-story_date').only('story_date').limit(1)
latest_story = MStory.objects(story_feed_id=self.feed.pk)\
.order_by('-story_date').only('story_date').limit(1)
if latest_story and len(latest_story) >= 1:
cutoff_date = (latest_story[0]['story_date']
+ datetime.timedelta(seconds=1))
@ -548,6 +549,7 @@ class UserSubscription(models.Model):
stories = cache.get('S:%s' % self.feed_id)
unread_story_hashes = self.story_hashes(user_id=self.user_id, feed_ids=[self.feed_id],
usersubs=[self],
read_filter='unread', group_by_feed=False,
cutoff_date=self.user.profile.unread_cutoff)
@ -607,6 +609,7 @@ class UserSubscription(models.Model):
feed_scores['neutral'] += 1
else:
unread_story_hashes = self.story_hashes(user_id=self.user_id, feed_ids=[self.feed_id],
usersubs=[self],
read_filter='unread', group_by_feed=False,
include_timestamps=True,
cutoff_date=self.user.profile.unread_cutoff)

View file

@ -172,7 +172,7 @@ def signup(request):
url = "https://%s%s" % (Site.objects.get_current().domain,
reverse('stripe-form'))
return HttpResponseRedirect(url)
return index(request)
@never_cache
@ -607,7 +607,7 @@ def load_single_feed(request, feed_id):
if not include_story_content:
del story['story_content']
story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
story['short_parsed_date'] = format_story_link_date__short(story_date, now)
story['short_parsed_date'] = format_story_link_date__short(story_date)
story['long_parsed_date'] = format_story_link_date__long(story_date, now)
if usersub:
story['read_status'] = 1
@ -677,9 +677,10 @@ def load_single_feed(request, feed_id):
# if page <= 1:
# import random
# if random.random() < .5:
# assert False
# time.sleep(random.randint(0, 3))
# if page == 2:
# assert False
return data
@ -783,7 +784,7 @@ def load_starred_stories(request):
for story in stories:
story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
story['short_parsed_date'] = format_story_link_date__short(story_date, now)
story['short_parsed_date'] = format_story_link_date__short(story_date)
story['long_parsed_date'] = format_story_link_date__long(story_date, now)
starred_date = localtime_for_timezone(story['starred_date'], user.profile.timezone)
story['starred_date'] = format_story_link_date__long(starred_date, now)
@ -945,7 +946,7 @@ def load_river_stories__redis(request):
story['story_hash'] not in unread_feed_story_hashes):
story['read_status'] = 1
story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
story['short_parsed_date'] = format_story_link_date__short(story_date, now)
story['short_parsed_date'] = format_story_link_date__short(story_date)
story['long_parsed_date'] = format_story_link_date__long(story_date, now)
if story['story_hash'] in starred_stories:
story['starred'] = True
@ -1256,9 +1257,16 @@ def mark_story_as_unread(request):
if story.story_date < request.user.profile.unread_cutoff:
data['code'] = -1
data['message'] = "Story is more than %s days old, cannot mark as unread." % (
settings.DAYS_OF_UNREAD if request.user.profile.is_premium else
settings.DAYS_OF_UNREAD_FREE)
if request.user.profile.is_premium:
data['message'] = "Story is more than %s days old, cannot mark as unread." % (
settings.DAYS_OF_UNREAD)
elif story.story_date > request.user.profile.unread_cutoff_premium:
data['message'] = "Story is more than %s days old. Premiums can mark unread up to 30 days." % (
settings.DAYS_OF_UNREAD_FREE)
else:
data['message'] = "Story is more than %s days old, cannot mark as unread." % (
settings.DAYS_OF_UNREAD_FREE)
return data
social_subs = MSocialSubscription.mark_dirty_sharing_story(user_id=request.user.pk,
story_feed_id=feed_id,

View file

@ -431,7 +431,7 @@ def original_text(request):
story_id = request.REQUEST.get('story_id')
feed_id = request.REQUEST.get('feed_id')
force = request.REQUEST.get('force', False)
story, _ = MStory.find_story(story_id=story_id, story_feed_id=feed_id)
if not story:

View file

@ -1911,18 +1911,22 @@ class MSharedStory(mongo.Document):
self.guid_hash[:6]
)
def generate_post_to_service_message(self, include_url=True):
def generate_post_to_service_message(self, truncate=None, include_url=True):
message = strip_tags(self.comments)
if not message or len(message) < 1:
message = self.story_title
if include_url:
message = truncate_chars(message, 92)
if include_url and truncate:
message = truncate_chars(message, truncate - 18 - 30)
feed = Feed.get_by_id(self.story_feed_id)
message += " (%s)" % truncate_chars(feed.feed_title, 18)
if truncate:
message += " (%s)" % truncate_chars(feed.feed_title, 18)
else:
message += " (%s)" % truncate_chars(feed.feed_title, 30)
if include_url:
message += " " + self.blurblog_permalink()
elif include_url:
message = truncate_chars(message, 116)
if truncate:
message = truncate_chars(message, truncate - 14)
message += " " + self.blurblog_permalink()
return message
@ -2521,7 +2525,7 @@ class MSocialServices(mongo.Document):
return profile
def post_to_twitter(self, shared_story):
message = shared_story.generate_post_to_service_message()
message = shared_story.generate_post_to_service_message(truncate=140)
try:
api = self.twitter_api()
@ -2555,7 +2559,7 @@ class MSocialServices(mongo.Document):
return True
def post_to_appdotnet(self, shared_story):
message = shared_story.generate_post_to_service_message()
message = shared_story.generate_post_to_service_message(truncate=256)
try:
api = self.appdotnet_api()

View file

@ -123,8 +123,8 @@ def load_social_stories(request, user_id, username=None):
story['social_user_id'] = social_user_id
# story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
shared_date = localtime_for_timezone(story['shared_date'], user.profile.timezone)
story['short_parsed_date'] = format_story_link_date__short(shared_date, now)
story['long_parsed_date'] = format_story_link_date__long(shared_date, now)
story['short_parsed_date'] = format_story_link_date__short(shared_date)
story['long_parsed_date'] = format_story_link_date__long(shared_date)
story['read_status'] = 1
if (read_filter == 'all' or query) and socialsub:
@ -275,7 +275,7 @@ def load_river_blurblog(request):
if story['story_hash'] not in unread_feed_story_hashes:
story['read_status'] = 1
story_date = localtime_for_timezone(story['story_date'], user.profile.timezone)
story['short_parsed_date'] = format_story_link_date__short(story_date, now)
story['short_parsed_date'] = format_story_link_date__short(story_date)
story['long_parsed_date'] = format_story_link_date__long(story_date, now)
if story['story_hash'] in starred_stories:
story['starred'] = True

View file

@ -1,6 +1,7 @@
package com.newsblur.activity;
import com.actionbarsherlock.app.SherlockPreferenceActivity;
import com.actionbarsherlock.view.MenuItem;
import com.newsblur.R;
import com.newsblur.util.PrefConstants;
@ -11,8 +12,19 @@ public class Settings extends SherlockPreferenceActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
super.getPreferenceManager().setSharedPreferencesName(PrefConstants.PREFERENCES);
addPreferencesFromResource(R.layout.activity_settings);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}

565
clients/ios/AFNetworking/AFHTTPClient.h Normal file → Executable file
View file

@ -1,17 +1,17 @@
// AFHTTPClient.h
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -21,25 +21,58 @@
// THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "AFURLConnectionOperation.h"
@class AFHTTPRequestOperation;
@protocol AFHTTPClientOperation;
@protocol AFMultipartFormData;
#import <Availability.h>
/**
Posted when network reachability changes.
The notification object is an `NSNumber` object containing the boolean value for the current network reachability.
This notification contains no information in the `userInfo` dictionary.
@warning In order for network reachability to be monitored, include the `SystemConfiguration` framework in the active target's "Link Binary With Library" build phase, and add `#import <SystemConfiguration/SystemConfiguration.h>` to the header prefix of the project (Prefix.pch).
*/
#ifdef _SYSTEMCONFIGURATION_H
extern NSString * const AFNetworkingReachabilityDidChangeNotification;
#endif
`AFHTTPClient` captures the common patterns of communicating with an web application over HTTP. It encapsulates information like base URL, authorization credentials, and HTTP headers, and uses them to construct and manage the execution of HTTP request operations.
/**
Specifies network reachability of the client to its `baseURL` domain.
## Automatic Content Parsing
Instances of `AFHTTPClient` may specify which types of requests it expects and should handle by registering HTTP operation classes for automatic parsing. Registered classes will determine whether they can handle a particular request, and then construct a request operation accordingly in `enqueueHTTPRequestOperationWithRequest:success:failure`.
## Subclassing Notes
In most cases, one should create an `AFHTTPClient` subclass for each website or web application that your application communicates with. It is often useful, also, to define a class method that returns a singleton shared HTTP client in each subclass, that persists authentication credentials and other configuration across the entire application.
## Methods to Override
To change the behavior of all url request construction for an `AFHTTPClient` subclass, override `requestWithMethod:path:parameters`.
To change the behavior of all request operation construction for an `AFHTTPClient` subclass, override `HTTPRequestOperationWithRequest:success:failure`.
## Default Headers
By default, `AFHTTPClient` sets the following HTTP headers:
- `Accept-Language: (comma-delimited preferred languages), en-us;q=0.8`
- `User-Agent: (generated user agent)`
You can override these HTTP headers or define new ones using `setDefaultHeader:value:`.
## URL Construction Using Relative Paths
Both `-requestWithMethod:path:parameters:` and `-multipartFormRequestWithMethod:path:parameters:constructingBodyWithBlock:` construct URLs from the path relative to the `-baseURL`, using `NSURL +URLWithString:relativeToURL:`. Below are a few examples of how `baseURL` and relative paths interact:
NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"];
[NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo
[NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz
[NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo
[NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo
[NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/
[NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/
Also important to note is that a trailing slash will be added to any `baseURL` without one, which would otherwise cause unexpected behavior when constructing URLs using paths without a leading slash.
## NSCoding / NSCopying Conformance
`AFHTTPClient` conforms to the `NSCoding` and `NSCopying` protocols, allowing operations to be archived to disk, and copied in memory, respectively. There are a few minor caveats to keep in mind, however:
- Archives and copies of HTTP clients will be initialized with an empty operation queue.
- NSCoding cannot serialize / deserialize block properties, so an archive of an HTTP client will not include any reachability callback block that may be set.
*/
#ifdef _SYSTEMCONFIGURATION_H
typedef enum {
AFNetworkReachabilityStatusUnknown = -1,
@ -47,94 +80,37 @@ typedef enum {
AFNetworkReachabilityStatusReachableViaWWAN = 1,
AFNetworkReachabilityStatusReachableViaWiFi = 2,
} AFNetworkReachabilityStatus;
#else
#pragma message("SystemConfiguration framework not found in project, or not included in precompiled header. Network reachability functionality will not be available.")
#endif
#ifndef __UTTYPE__
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#pragma message("MobileCoreServices framework not found in project, or not included in precompiled header. Automatic MIME type detection when uploading files in multipart requests will not be available.")
#else
#pragma message("CoreServices framework not found in project, or not included in precompiled header. Automatic MIME type detection when uploading files in multipart requests will not be available.")
#endif
#endif
/**
Specifies the method used to encode parameters into request body.
*/
typedef enum {
AFFormURLParameterEncoding,
AFJSONParameterEncoding,
AFPropertyListParameterEncoding,
} AFHTTPClientParameterEncoding;
/**
Returns a string, replacing certain characters with the equivalent percent escape sequence based on the specified encoding.
@param string The string to URL encode
@param encoding The encoding to use for the replacement. If you are uncertain of the correct encoding, you should use UTF-8 (NSUTF8StringEncoding), which is the encoding designated by RFC 3986 as the correct encoding for use in URLs.
@discussion The characters escaped are all characters that are not legal URL characters (based on RFC 3986), including any whitespace, punctuation, or special characters.
@return A URL-encoded string. If it does not need to be modified (no percent escape sequences are missing), this function may merely return string argument.
*/
extern NSString * AFURLEncodedStringFromStringWithEncoding(NSString *string, NSStringEncoding encoding);
@class AFHTTPRequestOperation;
@protocol AFMultipartFormData;
/**
Returns a query string constructed by a set of parameters, using the specified encoding.
@param parameters The parameters used to construct the query string
@param encoding The encoding to use in constructing the query string. If you are uncertain of the correct encoding, you should use UTF-8 (NSUTF8StringEncoding), which is the encoding designated by RFC 3986 as the correct encoding for use in URLs.
@discussion Query strings are constructed by collecting each key-value pair, URL-encoding a string representation of the key-value pair, and then joining the components with "&".
If a key-value pair has a an `NSArray` for its value, each member of the array will be represented in the format `key[]=value1&key[]value2`. Otherwise, the key-value pair will be formatted as "key=value". String representations of both keys and values are derived using the `-description` method. The constructed query string does not include the ? character used to delimit the query component.
@return A URL-encoded query string
*/
extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding encoding);
/**
`AFHTTPClient` captures the common patterns of communicating with an web application over HTTP. It encapsulates information like base URL, authorization credentials, and HTTP headers, and uses them to construct and manage the execution of HTTP request operations.
## Automatic Content Parsing
Instances of `AFHTTPClient` may specify which types of requests it expects and should handle by registering HTTP operation classes for automatic parsing. Registered classes will determine whether they can handle a particular request, and then construct a request operation accordingly in `enqueueHTTPRequestOperationWithRequest:success:failure`. See `AFHTTPClientOperation` for further details.
## Subclassing Notes
In most cases, one should create an `AFHTTPClient` subclass for each website or web application that your application communicates with. It is often useful, also, to define a class method that returns a singleton shared HTTP client in each subclass, that persists authentication credentials and other configuration across the entire application.
## Methods to Override
To change the behavior of all url request construction for an `AFHTTPClient` subclass, override `requestWithMethod:path:parameters`.
To change the behavior of all request operation construction for an `AFHTTPClient` subclass, override `HTTPRequestOperationWithRequest:success:failure`.
## Default Headers
By default, `AFHTTPClient` sets the following HTTP headers:
- `Accept-Encoding: gzip`
- `Accept-Language: ([NSLocale preferredLanguages]), en-us;q=0.8`
- `User-Agent: (generated user agent)`
You can override these HTTP headers or define new ones using `setDefaultHeader:value:`.
## URL Construction Using Relative Paths
Both `requestWithMethod:path:parameters` and `multipartFormRequestWithMethod:path:parameters:constructingBodyWithBlock:` construct URLs from the path relative to the `baseURL`, using `NSURL +URLWithString:relativeToURL:`. Below are a few examples of how `baseURL` and relative paths interract:
NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"];
[NSURL URLWithString:@"foo" relativeToURL:baseURL]; // http://example.com/v1/foo
[NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // http://example.com/v1/foo?bar=baz
[NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // http://example.com/foo
[NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // http://example.com/v1/foo
[NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // http://example.com/foo/
[NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // http://example2.com/
*/
@interface AFHTTPClient : NSObject
@interface AFHTTPClient : NSObject <NSCoding, NSCopying>
///---------------------------------------
/// @name Accessing HTTP Client Properties
///---------------------------------------
/**
The url used as the base for paths specified in methods such as `getPath:parameteres:success:failure`
The url used as the base for paths specified in methods such as `getPath:parameters:success:failure`
*/
@property (readonly, nonatomic, retain) NSURL *baseURL;
@property (readonly, nonatomic, strong) NSURL *baseURL;
/**
The string encoding used in constructing url requests. This is `NSUTF8StringEncoding` by default.
@ -144,44 +120,56 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete
/**
The `AFHTTPClientParameterEncoding` value corresponding to how parameters are encoded into a request body. This is `AFFormURLParameterEncoding` by default.
@warning JSON encoding will automatically use JSONKit, SBJSON, YAJL, or NextiveJSON, if provided. Otherwise, the built-in `NSJSONSerialization` class is used, if available (iOS 5.0 and Mac OS 10.7). If the build target does not either support `NSJSONSerialization` or include a third-party JSON library, a runtime exception will be thrown when attempting to encode parameters as JSON.
@warning Some nested parameter structures, such as a keyed array of hashes containing inconsistent keys (i.e. `@{@"": @[@{@"a" : @(1)}, @{@"b" : @(2)}]}`), cannot be unambiguously represented in query strings. It is strongly recommended that an unambiguous encoding, such as `AFJSONParameterEncoding`, is used when posting complicated or nondeterministic parameter structures.
*/
@property (nonatomic, assign) AFHTTPClientParameterEncoding parameterEncoding;
/**
The operation queue which manages operations enqueued by the HTTP client.
*/
@property (readonly, nonatomic, retain) NSOperationQueue *operationQueue;
@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue;
/**
The reachability status from the device to the current `baseURL` of the `AFHTTPClient`.
@warning This property requires the `SystemConfiguration` framework. Add it in the active target's "Link Binary With Library" build phase, and add `#import <SystemConfiguration/SystemConfiguration.h>` to the header prefix of the project (Prefix.pch).
@warning This property requires the `SystemConfiguration` framework. Add it in the active target's "Link Binary With Library" build phase, and add `#import <SystemConfiguration/SystemConfiguration.h>` to the header prefix of the project (`Prefix.pch`).
*/
#ifdef _SYSTEMCONFIGURATION_H
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;
#endif
/**
Default SSL pinning mode for each `AFHTTPRequestOperation` created by `HTTPRequestOperationWithRequest:success:failure:`.
*/
@property (nonatomic, assign) AFURLConnectionOperationSSLPinningMode defaultSSLPinningMode;
/**
Whether each `AFHTTPRequestOperation` created by `HTTPRequestOperationWithRequest:success:failure:` should accept an invalid SSL certificate.
If `_AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_` is set, this property defaults to `YES` for backwards compatibility. Otherwise, this property defaults to `NO`.
*/
@property (nonatomic, assign) BOOL allowsInvalidSSLCertificate;
///---------------------------------------------
/// @name Creating and Initializing HTTP Clients
///---------------------------------------------
/**
Creates and initializes an `AFHTTPClient` object with the specified base URL.
@param url The base URL for the HTTP client. This argument must not be nil.
@param url The base URL for the HTTP client. This argument must not be `nil`.
@return The newly-initialized HTTP client
*/
+ (AFHTTPClient *)clientWithBaseURL:(NSURL *)url;
+ (instancetype)clientWithBaseURL:(NSURL *)url;
/**
Initializes an `AFHTTPClient` object with the specified base URL.
@param url The base URL for the HTTP client. This argument must not be nil.
@discussion This is the designated initializer.
This is the designated initializer.
@param url The base URL for the HTTP client. This argument must not be `nil`.
@return The newly-initialized HTTP client
*/
- (id)initWithBaseURL:(NSURL *)url;
@ -192,10 +180,10 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete
/**
Sets a callback to be executed when the network availability of the `baseURL` host changes.
@param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`.
@warning This method requires the `SystemConfiguration` framework. Add it in the active target's "Link Binary With Library" build phase, and add `#import <SystemConfiguration/SystemConfiguration.h>` to the header prefix of the project (Prefix.pch).
@warning This method requires the `SystemConfiguration` framework. Add it in the active target's "Link Binary With Library" build phase, and add `#import <SystemConfiguration/SystemConfiguration.h>` to the header prefix of the project (`Prefix.pch`).
*/
#ifdef _SYSTEMCONFIGURATION_H
- (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block;
@ -207,23 +195,19 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete
/**
Attempts to register a subclass of `AFHTTPRequestOperation`, adding it to a chain to automatically generate request operations from a URL request.
When `enqueueHTTPRequestOperationWithRequest:success:failure` is invoked, each registered class is consulted in turn to see if it can handle the specific request. The first class to return `YES` when sent a `canProcessRequest:` message is used to create an operation using `initWithURLRequest:` and do `setCompletionBlockWithSuccess:failure:`. There is no guarantee that all registered classes will be consulted. Classes are consulted in the reverse order of their registration. Attempting to register an already-registered class will move it to the top of the list.
@param The subclass of `AFHTTPRequestOperation` to register
@return `YES` if the registration is successful, `NO` otherwise. The only failure condition is if `operationClass` does is not a subclass of `AFHTTPRequestOperation`.
@discussion When `enqueueHTTPRequestOperationWithRequest:success:failure` is invoked, each registered class is consulted in turn to see if it can handle the specific request. The first class to return `YES` when sent a `canProcessRequest:` message is used to create an operation using `initWithURLRequest:` and do `setCompletionBlockWithSuccess:failure:`. There is no guarantee that all registered classes will be consulted. Classes are consulted in the reverse order of their registration. Attempting to register an already-registered class will move it to the top of the list.
@see `AFHTTPClientOperation`
@param operationClass The subclass of `AFHTTPRequestOperation` to register
@return `YES` if the registration is successful, `NO` otherwise. The only failure condition is if `operationClass` is not a subclass of `AFHTTPRequestOperation`.
*/
- (BOOL)registerHTTPOperationClass:(Class)operationClass;
/**
Unregisters the specified subclass of `AFHTTPRequestOperation`.
@param The class conforming to the `AFHTTPClientOperation` protocol to unregister
@discussion After this method is invoked, `operationClass` is no longer consulted when `requestWithMethod:path:parameters` is invoked.
Unregisters the specified subclass of `AFHTTPRequestOperation` from the chain of classes consulted when `-requestWithMethod:path:parameters` is called.
@param operationClass The subclass of `AFHTTPRequestOperation` to register
*/
- (void)unregisterHTTPOperationClass:(Class)operationClass;
@ -233,72 +217,84 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete
/**
Returns the value for the HTTP headers set in request objects created by the HTTP client.
@param header The HTTP header to return the default value for
@return The default value for the HTTP header, or `nil` if unspecified
*/
- (NSString *)defaultValueForHeader:(NSString *)header;
/**
Sets the value for the HTTP headers set in request objects made by the HTTP client. If `nil`, removes the existing value for that header.
@param header The HTTP header to set a default value for
@param value The value set as default for the specified header, or `nil
*/
- (void)setDefaultHeader:(NSString *)header value:(NSString *)value;
- (void)setDefaultHeader:(NSString *)header
value:(NSString *)value;
/**
Sets the "Authorization" HTTP header set in request objects made by the HTTP client to a basic authentication value with Base64-encoded username and password. This overwrites any existing value for this header.
@param username The HTTP basic auth username
@param password The HTTP basic auth password
*/
- (void)setAuthorizationHeaderWithUsername:(NSString *)username password:(NSString *)password;
- (void)setAuthorizationHeaderWithUsername:(NSString *)username
password:(NSString *)password;
/**
Sets the "Authorization" HTTP header set in request objects made by the HTTP client to a token-based authentication value, such as an OAuth access token. This overwrites any existing value for this header.
@param token The authentication token
*/
- (void)setAuthorizationHeaderWithToken:(NSString *)token;
/**
Clears any existing value for the "Authorization" HTTP header.
*/
- (void)clearAuthorizationHeader;
///-------------------------------
/// @name Managing URL Credentials
///-------------------------------
/**
Set the default URL credential to be set for request operations.
@param credential The URL credential
*/
- (void)setDefaultCredential:(NSURLCredential *)credential;
///-------------------------------
/// @name Creating Request Objects
///-------------------------------
/**
Creates an `NSMutableURLRequest` object with the specified HTTP method and path.
If the HTTP method is `GET`, the parameters will be used to construct a url-encoded query string that is appended to the request's URL. Otherwise, the parameters will be encoded according to the value of the `parameterEncoding` property, and set as the request body.
@param method The HTTP method for the request, such as `GET`, `POST`, `PUT`, or `DELETE`.
@param path The path to be appended to the HTTP client's base URL and used as the request URL.
If the HTTP method is `GET`, `HEAD`, or `DELETE`, the parameters will be used to construct a url-encoded query string that is appended to the request's URL. Otherwise, the parameters will be encoded according to the value of the `parameterEncoding` property, and set as the request body.
@param method The HTTP method for the request, such as `GET`, `POST`, `PUT`, or `DELETE`. This parameter must not be `nil`.
@param path The path to be appended to the HTTP client's base URL and used as the request URL. If `nil`, no path will be appended to the base URL.
@param parameters The parameters to be either set as a query string for `GET` requests, or the request HTTP body.
@return An `NSMutableURLRequest` object
@return An `NSMutableURLRequest` object
*/
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
path:(NSString *)path
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
path:(NSString *)path
parameters:(NSDictionary *)parameters;
/**
Creates an `NSMutableURLRequest` object with the specified HTTP method and path, and constructs a `multipart/form-data` HTTP body, using the specified parameters and multipart form data block. See http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.2
Multipart form requests are automatically streamed, reading files directly from disk along with in-memory data in a single HTTP body. The resulting `NSMutableURLRequest` object has an `HTTPBodyStream` property, so refrain from setting `HTTPBodyStream` or `HTTPBody` on this request object, as it will clear out the multipart form body stream.
@param method The HTTP method for the request. Must be either `POST`, `PUT`, or `DELETE`.
@param method The HTTP method for the request. This parameter must not be `GET` or `HEAD`, or `nil`.
@param path The path to be appended to the HTTP client's base URL and used as the request URL.
@param parameters The parameters to be encoded and set in the request HTTP body.
@param block A block that takes a single argument and appends data to the HTTP body. The block argument is an object adopting the `AFMultipartFormData` protocol. This can be used to upload files, encode HTTP body as JSON or XML, or specify multiple values for the same parameter, as one might for array values.
@discussion The multipart form data is constructed synchronously in the specified block, so in cases where large amounts of data are being added to the request, you should consider performing this method in the background. Likewise, the form data is constructed in-memory, so it may be advantageous to instead write parts of the form data to a file and stream the request body using the `HTTPBodyStream` property of `NSURLRequest`.
@warning An exception will be raised if the specified method is not `POST`, `PUT` or `DELETE`.
@return An `NSMutableURLRequest` object
*/
- (NSMutableURLRequest *)multipartFormRequestWithMethod:(NSString *)method
@ -312,14 +308,14 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete
/**
Creates an `AFHTTPRequestOperation`.
In order to determine what kind of operation is created, each registered subclass conforming to the `AFHTTPClient` protocol is consulted (in reverse order of when they were specified) to see if it can handle the specific request. The first class to return `YES` when sent a `canProcessRequest:` message is used to generate an operation using `HTTPRequestOperationWithRequest:success:failure:`.
@param request The request object to be loaded asynchronously during execution of the operation.
@param urlRequest The request object to be loaded asynchronously during execution of the operation.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the created request operation and the object created from the response data of request.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the resonse data. This block has no return value and takes two arguments:, the created request operation and the `NSError` object describing the network or parsing error that occurred.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes two arguments:, the created request operation and the `NSError` object describing the network or parsing error that occurred.
*/
- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request
- (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
@ -329,7 +325,7 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete
/**
Enqueues an `AFHTTPRequestOperation` to the HTTP client's operation queue.
@param operation The HTTP request operation to be enqueued.
*/
- (void)enqueueHTTPRequestOperation:(AFHTTPRequestOperation *)operation;
@ -337,8 +333,10 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete
/**
Cancels all operations in the HTTP client's operation queue whose URLs match the specified HTTP method and path.
@param method The HTTP method to match for the cancelled requests, such as `GET`, `POST`, `PUT`, or `DELETE`. If `nil`, all request operations with URLs matching the path will be cancelled.
@param url The path to match for the cancelled requests.
This method only cancels `AFHTTPRequestOperations` whose request URL matches the HTTP client base URL with the path appended. For complete control over the lifecycle of enqueued operations, you can access the `operationQueue` property directly, which allows you to, for instance, cancel operations filtered by a predicate, or simply use `-cancelAllRequests`. Note that the operation queue may include non-HTTP operations, so be sure to check the type before attempting to directly introspect an operation's `request` property.
@param method The HTTP method to match for the cancelled requests, such as `GET`, `POST`, `PUT`, or `DELETE`. If `nil`, all request operations with URLs matching the path will be cancelled.
@param path The path appended to the HTTP client base URL to match against the cancelled requests. If `nil`, no path will be appended to the base URL.
*/
- (void)cancelAllHTTPOperationsWithMethod:(NSString *)method path:(NSString *)path;
@ -348,26 +346,26 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete
/**
Creates and enqueues an `AFHTTPRequestOperation` to the HTTP client's operation queue for each specified request object into a batch. When each request operation finishes, the specified progress block is executed, until all of the request operations have finished, at which point the completion block also executes.
Operations are created by passing the specified `NSURLRequest` objects in `requests`, using `-HTTPRequestOperationWithRequest:success:failure:`, with `nil` for both the `success` and `failure` parameters.
@param requests The `NSURLRequest` objects used to create and enqueue operations.
@param urlRequests The `NSURLRequest` objects used to create and enqueue operations.
@param progressBlock A block object to be executed upon the completion of each request operation in the batch. This block has no return value and takes two arguments: the number of operations that have already finished execution, and the total number of operations.
@param completionBlock A block object to be executed upon the completion of all of the request operations in the batch. This block has no return value and takes a single argument: the batched request operations.
@discussion Operations are created by passing the specified `NSURLRequest` objects in `requests`, using `-HTTPRequestOperationWithRequest:success:failure:`, with `nil` for both the `success` and `failure` parameters.
@param completionBlock A block object to be executed upon the completion of all of the request operations in the batch. This block has no return value and takes a single argument: the batched request operations.
*/
- (void)enqueueBatchOfHTTPRequestOperationsWithRequests:(NSArray *)requests
progressBlock:(void (^)(NSUInteger numberOfCompletedOperations, NSUInteger totalNumberOfOperations))progressBlock
- (void)enqueueBatchOfHTTPRequestOperationsWithRequests:(NSArray *)urlRequests
progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock
completionBlock:(void (^)(NSArray *operations))completionBlock;
/**
Enqueues the specified request operations into a batch. When each request operation finishes, the specified progress block is executed, until all of the request operations have finished, at which point the completion block also executes.
@param operations The request operations used to be batched and enqueued.
@param progressBlock A block object to be executed upon the completion of each request operation in the batch. This block has no return value and takes two arguments: the number of operations that have already finished execution, and the total number of operations.
@param completionBlock A block object to be executed upon the completion of all of the request operations in the batch. This block has no return value and takes a single argument: the batched request operations.
@param completionBlock A block object to be executed upon the completion of all of the request operations in the batch. This block has no return value and takes a single argument: the batched request operations.
*/
- (void)enqueueBatchOfHTTPRequestOperations:(NSArray *)operations
progressBlock:(void (^)(NSUInteger numberOfCompletedOperations, NSUInteger totalNumberOfOperations))progressBlock
- (void)enqueueBatchOfHTTPRequestOperations:(NSArray *)operations
progressBlock:(void (^)(NSUInteger numberOfFinishedOperations, NSUInteger totalNumberOfOperations))progressBlock
completionBlock:(void (^)(NSArray *operations))completionBlock;
///---------------------------
@ -376,13 +374,13 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete
/**
Creates an `AFHTTPRequestOperation` with a `GET` request, and enqueues it to the HTTP client's operation queue.
@param path The path to be appended to the HTTP client's base URL and used as the request URL.
@param parameters The parameters to be encoded and appended as the query string for the request URL.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the created request operation and the object created from the response data of request.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the resonse data. This block has no return value and takes two arguments:, the created request operation and the `NSError` object describing the network or parsing error that occurred.
@see HTTPRequestOperationWithRequest:success:failure
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes two arguments: the created request operation and the `NSError` object describing the network or parsing error that occurred.
@see -HTTPRequestOperationWithRequest:success:failure:
*/
- (void)getPath:(NSString *)path
parameters:(NSDictionary *)parameters
@ -391,126 +389,253 @@ extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *paramete
/**
Creates an `AFHTTPRequestOperation` with a `POST` request, and enqueues it to the HTTP client's operation queue.
@param path The path to be appended to the HTTP client's base URL and used as the request URL.
@param parameters The parameters to be encoded and set in the request HTTP body.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the created request operation and the object created from the response data of request.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the resonse data. This block has no return value and takes two arguments:, the created request operation and the `NSError` object describing the network or parsing error that occurred.
@see HTTPRequestOperationWithRequest:success:failure
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes two arguments: the created request operation and the `NSError` object describing the network or parsing error that occurred.
@see -HTTPRequestOperationWithRequest:success:failure:
*/
- (void)postPath:(NSString *)path
parameters:(NSDictionary *)parameters
- (void)postPath:(NSString *)path
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
/**
Creates an `AFHTTPRequestOperation` with a `PUT` request, and enqueues it to the HTTP client's operation queue.
@param path The path to be appended to the HTTP client's base URL and used as the request URL.
@param parameters The parameters to be encoded and set in the request HTTP body.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the created request operation and the object created from the response data of request.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the resonse data. This block has no return value and takes two arguments:, the created request operation and the `NSError` object describing the network or parsing error that occurred.
@see HTTPRequestOperationWithRequest:success:failure
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes two arguments: the created request operation and the `NSError` object describing the network or parsing error that occurred.
@see -HTTPRequestOperationWithRequest:success:failure:
*/
- (void)putPath:(NSString *)path
parameters:(NSDictionary *)parameters
- (void)putPath:(NSString *)path
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
/**
Creates an `AFHTTPRequestOperation` with a `DELETE` request, and enqueues it to the HTTP client's operation queue.
@param path The path to be appended to the HTTP client's base URL and used as the request URL.
@param parameters The parameters to be encoded and set in the request HTTP body.
@param parameters The parameters to be encoded and appended as the query string for the request URL.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the created request operation and the object created from the response data of request.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the resonse data. This block has no return value and takes two arguments:, the created request operation and the `NSError` object describing the network or parsing error that occurred.
@see HTTPRequestOperationWithRequest:success:failure
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes two arguments: the created request operation and the `NSError` object describing the network or parsing error that occurred.
@see -HTTPRequestOperationWithRequest:success:failure:
*/
- (void)deletePath:(NSString *)path
parameters:(NSDictionary *)parameters
- (void)deletePath:(NSString *)path
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
/**
Creates an `AFHTTPRequestOperation` with a `PATCH` request, and enqueues it to the HTTP client's operation queue.
@param path The path to be appended to the HTTP client's base URL and used as the request URL.
@param parameters The parameters to be encoded and set in the request HTTP body.
@param success A block object to be executed when the request operation finishes successfully. This block has no return value and takes two arguments: the created request operation and the object created from the response data of request.
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the resonse data. This block has no return value and takes two arguments:, the created request operation and the `NSError` object describing the network or parsing error that occurred.
@see HTTPRequestOperationWithRequest:success:failure
@param failure A block object to be executed when the request operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data. This block has no return value and takes two arguments: the created request operation and the `NSError` object describing the network or parsing error that occurred.
@see -HTTPRequestOperationWithRequest:success:failure:
*/
- (void)patchPath:(NSString *)path
parameters:(NSDictionary *)parameters
parameters:(NSDictionary *)parameters
success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
@end
#pragma mark -
///----------------
/// @name Constants
///----------------
/**
The `AFMultipartFormData` protocol defines the methods supported by the parameter in the block argument of `multipartFormRequestWithMethod:path:parameters:constructingBodyWithBlock:`.
## Network Reachability
The following constants are provided by `AFHTTPClient` as possible network reachability statuses.
enum {
AFNetworkReachabilityStatusUnknown,
AFNetworkReachabilityStatusNotReachable,
AFNetworkReachabilityStatusReachableViaWWAN,
AFNetworkReachabilityStatusReachableViaWiFi,
}
`AFNetworkReachabilityStatusUnknown`
The `baseURL` host reachability is not known.
`AFNetworkReachabilityStatusNotReachable`
The `baseURL` host cannot be reached.
`AFNetworkReachabilityStatusReachableViaWWAN`
The `baseURL` host can be reached via a cellular connection, such as EDGE or GPRS.
`AFNetworkReachabilityStatusReachableViaWiFi`
The `baseURL` host can be reached via a Wi-Fi connection.
### Keys for Notification UserInfo Dictionary
Strings that are used as keys in a `userInfo` dictionary in a network reachability status change notification.
`AFNetworkingReachabilityNotificationStatusItem`
A key in the userInfo dictionary in a `AFNetworkingReachabilityDidChangeNotification` notification.
The corresponding value is an `NSNumber` object representing the `AFNetworkReachabilityStatus` value for the current reachability status.
## Parameter Encoding
The following constants are provided by `AFHTTPClient` as possible methods for serializing parameters into query string or message body values.
enum {
AFFormURLParameterEncoding,
AFJSONParameterEncoding,
AFPropertyListParameterEncoding,
}
`AFFormURLParameterEncoding`
Parameters are encoded into field/key pairs in the URL query string for `GET` `HEAD` and `DELETE` requests, and in the message body otherwise. Dictionary keys are sorted with the `caseInsensitiveCompare:` selector of their description, in order to mitigate the possibility of ambiguous query strings being generated non-deterministically. See the warning for the `parameterEncoding` property for additional information.
`AFJSONParameterEncoding`
Parameters are encoded into JSON in the message body.
`AFPropertyListParameterEncoding`
Parameters are encoded into a property list in the message body.
*/
///----------------
/// @name Functions
///----------------
/**
Returns a query string constructed by a set of parameters, using the specified encoding.
Query strings are constructed by collecting each key-value pair, percent escaping a string representation of the key-value pair, and then joining the pairs with "&".
@see `AFHTTPClient -multipartFormRequestWithMethod:path:parameters:constructingBodyWithBlock:`
If a query string pair has a an `NSArray` for its value, each member of the array will be represented in the format `field[]=value1&field[]value2`. Otherwise, the pair will be formatted as "field=value". String representations of both keys and values are derived using the `-description` method. The constructed query string does not include the ? character used to delimit the query component.
@param parameters The parameters used to construct the query string
@param encoding The encoding to use in constructing the query string. If you are uncertain of the correct encoding, you should use UTF-8 (`NSUTF8StringEncoding`), which is the encoding designated by RFC 3986 as the correct encoding for use in URLs.
@return A percent-escaped query string
*/
extern NSString * AFQueryStringFromParametersWithEncoding(NSDictionary *parameters, NSStringEncoding encoding);
///--------------------
/// @name Notifications
///--------------------
/**
Posted when network reachability changes.
This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `AFNetworkingReachabilityNotificationStatusItem` key, representing the `AFNetworkReachabilityStatus` value for the current network reachability.
@warning In order for network reachability to be monitored, include the `SystemConfiguration` framework in the active target's "Link Binary With Library" build phase, and add `#import <SystemConfiguration/SystemConfiguration.h>` to the header prefix of the project (`Prefix.pch`).
*/
#ifdef _SYSTEMCONFIGURATION_H
extern NSString * const AFNetworkingReachabilityDidChangeNotification;
extern NSString * const AFNetworkingReachabilityNotificationStatusItem;
#endif
#pragma mark -
extern NSUInteger const kAFUploadStream3GSuggestedPacketSize;
extern NSTimeInterval const kAFUploadStream3GSuggestedDelay;
/**
The `AFMultipartFormData` protocol defines the methods supported by the parameter in the block argument of `AFHTTPClient -multipartFormRequestWithMethod:path:parameters:constructingBodyWithBlock:`.
*/
@protocol AFMultipartFormData
/**
Appends HTTP headers, followed by the encoded data and the multipart form boundary.
@param headers The HTTP headers to be appended to the form data.
@param body The data to be encoded and appended to the form data.
*/
- (void)appendPartWithHeaders:(NSDictionary *)headers body:(NSData *)body;
Appends the HTTP header `Content-Disposition: file; filename=#{generated filename}; name=#{name}"` and `Content-Type: #{generated mimeType}`, followed by the encoded file data and the multipart form boundary.
/**
Appends the HTTP headers `Content-Disposition: form-data; name=#{name}"`, followed by the encoded data and the multipart form boundary.
The filename and MIME type for this data in the form will be automatically generated, using the last path component of the `fileURL` and system associated MIME type for the `fileURL` extension, respectively.
@param data The data to be encoded and appended to the form data.
@param fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`.
@param name The name to be associated with the specified data. This parameter must not be `nil`.
@param error If an error occurs, upon return contains an `NSError` object that describes the problem.
@return `YES` if the file data was successfully appended, otherwise `NO`.
*/
- (void)appendPartWithFormData:(NSData *)data name:(NSString *)name;
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
name:(NSString *)name
error:(NSError * __autoreleasing *)error;
/**
Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary.
@param data The data to be encoded and appended to the form data.
@param name The name to be associated with the specified data. This parameter must not be `nil`.
@param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`.
@param filename The filename to be associated with the specified data. This parameter must not be `nil`.
*/
- (void)appendPartWithFileData:(NSData *)data name:(NSString *)name fileName:(NSString *)fileName mimeType:(NSString *)mimeType;
/**
Appends the HTTP header `Content-Disposition: file; filename=#{generated filename}; name=#{name}"` and `Content-Type: #{generated mimeType}`, followed by the encoded file data and the multipart form boundary.
@param fileURL The URL corresponding to the file whose content will be appended to the form.
@param fileURL The URL corresponding to the file whose content will be appended to the form. This parameter must not be `nil`.
@param name The name to be associated with the specified data. This parameter must not be `nil`.
@param fileName The file name to be used in the `Content-Disposition` header. This parameter must not be `nil`.
@param mimeType The declared MIME type of the file data. This parameter must not be `nil`.
@param error If an error occurs, upon return contains an `NSError` object that describes the problem.
@return `YES` if the file data was successfully appended, otherwise `NO`.
@discussion The filename and MIME type for this data in the form will be automatically generated, using `NSURLResponse` `-suggestedFilename` and `-MIMEType`, respectively.
@return `YES` if the file data was successfully appended otherwise `NO`.
*/
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL name:(NSString *)name error:(NSError **)error;
- (BOOL)appendPartWithFileURL:(NSURL *)fileURL
name:(NSString *)name
fileName:(NSString *)fileName
mimeType:(NSString *)mimeType
error:(NSError * __autoreleasing *)error;
/**
Appends encoded data to the form data.
Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the data from the input stream and the multipart form boundary.
@param inputStream The input stream to be appended to the form data
@param name The name to be associated with the specified input stream. This parameter must not be `nil`.
@param fileName The filename to be associated with the specified input stream. This parameter must not be `nil`.
@param length The length of the specified input stream in bytes.
@param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`.
*/
- (void)appendPartWithInputStream:(NSInputStream *)inputStream
name:(NSString *)name
fileName:(NSString *)fileName
length:(unsigned long long)length
mimeType:(NSString *)mimeType;
/**
Appends the HTTP header `Content-Disposition: file; filename=#{filename}; name=#{name}"` and `Content-Type: #{mimeType}`, followed by the encoded file data and the multipart form boundary.
@param data The data to be encoded and appended to the form data.
@param name The name to be associated with the specified data. This parameter must not be `nil`.
@param fileName The filename to be associated with the specified data. This parameter must not be `nil`.
@param mimeType The MIME type of the specified data. (For example, the MIME type for a JPEG image is image/jpeg.) For a list of valid MIME types, see http://www.iana.org/assignments/media-types/. This parameter must not be `nil`.
*/
- (void)appendData:(NSData *)data;
- (void)appendPartWithFileData:(NSData *)data
name:(NSString *)name
fileName:(NSString *)fileName
mimeType:(NSString *)mimeType;
/**
Appends a string to the form data.
@param string The string to be encoded and appended to the form data.
Appends the HTTP headers `Content-Disposition: form-data; name=#{name}"`, followed by the encoded data and the multipart form boundary.
@param data The data to be encoded and appended to the form data.
@param name The name to be associated with the specified data. This parameter must not be `nil`.
*/
- (void)appendString:(NSString *)string;
- (void)appendPartWithFormData:(NSData *)data
name:(NSString *)name;
/**
Appends HTTP headers, followed by the encoded data and the multipart form boundary.
@param headers The HTTP headers to be appended to the form data.
@param body The data to be encoded and appended to the form data.
*/
- (void)appendPartWithHeaders:(NSDictionary *)headers
body:(NSData *)body;
/**
Throttles request bandwidth by limiting the packet size and adding a delay for each chunk read from the upload stream.
When uploading over a 3G or EDGE connection, requests may fail with "request body stream exhausted". Setting a maximum packet size and delay according to the recommended values (`kAFUploadStream3GSuggestedPacketSize` and `kAFUploadStream3GSuggestedDelay`) lowers the risk of the input stream exceeding its allocated bandwidth. Unfortunately, as of iOS 6, there is no definite way to distinguish between a 3G, EDGE, or LTE connection. As such, it is not recommended that you throttle bandwidth based solely on network reachability. Instead, you should consider checking for the "request body stream exhausted" in a failure block, and then retrying the request with throttled bandwidth.
@param numberOfBytes Maximum packet size, in number of bytes. The default packet size for an input stream is 32kb.
@param delay Duration of delay each time a packet is read. By default, no delay is set.
*/
- (void)throttleBandwidthWithPacketSize:(NSUInteger)numberOfBytes
delay:(NSTimeInterval)delay;
@end

1282
clients/ios/AFNetworking/AFHTTPClient.m Normal file → Executable file

File diff suppressed because it is too large Load diff

83
clients/ios/AFNetworking/AFHTTPRequestOperation.h Normal file → Executable file
View file

@ -1,17 +1,17 @@
// AFHTTPRequestOperation.h
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -23,13 +23,6 @@
#import <Foundation/Foundation.h>
#import "AFURLConnectionOperation.h"
/**
Returns a set of MIME types detected in an HTTP `Accept` or `Content-Type` header.
*/
extern NSSet * AFContentTypesFromHTTPHeader(NSString *string);
extern NSString * AFCreateIncompleteDownloadDirectoryPath(void);
/**
`AFHTTPRequestOperation` is a subclass of `AFURLConnectionOperation` for requests using the HTTP or HTTPS protocols. It encapsulates the concept of acceptable status codes and content types, which determine the success or failure of a request.
*/
@ -42,31 +35,7 @@ extern NSString * AFCreateIncompleteDownloadDirectoryPath(void);
/**
The last HTTP response received by the operation's connection.
*/
@property (readonly, nonatomic, retain) NSHTTPURLResponse *response;
/**
Set a target file for the response, will stream directly into this destination.
Defaults to nil, which will use a memory stream. Will create a new outputStream on change.
Note: Changing this while the request is not in ready state will be ignored.
*/
@property (nonatomic, copy) NSString *responseFilePath;
/**
Expected total length. This is different than expectedContentLength if the file is resumed.
On regular requests, this is equal to self.response.expectedContentLength unless we resume a request.
Note: this can also be -1 if the file size is not sent (*)
*/
@property (assign, readonly) long long totalContentLength;
/**
Indicator for the file offset on partial/resumed downloads.
This is greater than zero if the file download is resumed.
*/
@property (assign, readonly) long long offsetContentLength;
@property (readonly, nonatomic, strong) NSHTTPURLResponse *response;
///----------------------------------------------------------
/// @name Managing And Checking For Acceptable HTTP Responses
@ -75,50 +44,50 @@ extern NSString * AFCreateIncompleteDownloadDirectoryPath(void);
/**
A Boolean value that corresponds to whether the status code of the response is within the specified set of acceptable status codes. Returns `YES` if `acceptableStatusCodes` is `nil`.
*/
@property (readonly) BOOL hasAcceptableStatusCode;
@property (nonatomic, readonly) BOOL hasAcceptableStatusCode;
/**
A Boolean value that corresponds to whether the MIME type of the response is among the specified set of acceptable content types. Returns `YES` if `acceptableContentTypes` is `nil`.
*/
@property (readonly) BOOL hasAcceptableContentType;
@property (nonatomic, readonly) BOOL hasAcceptableContentType;
/**
/**
The callback dispatch queue on success. If `NULL` (default), the main queue is used.
*/
@property (nonatomic, assign) dispatch_queue_t successCallbackQueue;
/**
/**
The callback dispatch queue on failure. If `NULL` (default), the main queue is used.
*/
@property (nonatomic, assign) dispatch_queue_t failureCallbackQueue;
///-------------------------------------------------------------
/// @name Managing Accceptable HTTP Status Codes & Content Types
///-------------------------------------------------------------
///------------------------------------------------------------
/// @name Managing Acceptable HTTP Status Codes & Content Types
///------------------------------------------------------------
/**
Returns an `NSIndexSet` object containing the ranges of acceptable HTTP status codes. When non-`nil`, the operation will set the `error` property to an error in `AFErrorDomain`. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
By default, this is the range 200 to 299, inclusive.
*/
+ (NSIndexSet *)acceptableStatusCodes;
/**
Adds status codes to the set of acceptable HTTP status codes returned by `+acceptableStatusCodes` in subsequent calls by this class and its descendents.
Adds status codes to the set of acceptable HTTP status codes returned by `+acceptableStatusCodes` in subsequent calls by this class and its descendants.
@param statusCodes The status codes to be added to the set of acceptable HTTP status codes
*/
+ (void)addAcceptableStatusCodes:(NSIndexSet *)statusCodes;
/**
Returns an `NSSet` object containing the acceptable MIME types. When non-`nil`, the operation will set the `error` property to an error in `AFErrorDomain`. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17
Returns an `NSSet` object containing the acceptable MIME types. When non-`nil`, the operation will set the `error` property to an error in `AFErrorDomain`. See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.17
By default, this is `nil`.
*/
+ (NSSet *)acceptableContentTypes;
/**
Adds content types to the set of acceptable MIME types returned by `+acceptableContentTypes` in subsequent calls by this class and its descendents.
Adds content types to the set of acceptable MIME types returned by `+acceptableContentTypes` in subsequent calls by this class and its descendants.
@param contentTypes The content types to be added to the set of acceptable MIME types
*/
@ -131,7 +100,7 @@ extern NSString * AFCreateIncompleteDownloadDirectoryPath(void);
/**
A Boolean value determining whether or not the class can process the specified request. For example, `AFJSONRequestOperation` may check to make sure the content type was `application/json` or the URL path extension was `.json`.
@param urlRequest The request that is determined to be supported or not supported for this class.
*/
+ (BOOL)canProcessRequest:(NSURLRequest *)urlRequest;
@ -142,13 +111,23 @@ extern NSString * AFCreateIncompleteDownloadDirectoryPath(void);
/**
Sets the `completionBlock` property with a block that executes either the specified success or failure block, depending on the state of the request on completion. If `error` returns a value, which can be caused by an unacceptable status code or content type, then `failure` is executed. Otherwise, `success` is executed.
This method should be overridden in subclasses in order to specify the response object passed into the success block.
@param success The block to be executed on the completion of a successful request. This block has no return value and takes two arguments: the receiver operation and the object constructed from the response data of the request.
@param failure The block to be executed on the completion of an unsuccessful request. This block has no return value and takes two arguments: the receiver operation and the error that occured during the request.
@discussion This method should be overridden in subclasses in order to specify the response object passed into the success block.
@param failure The block to be executed on the completion of an unsuccessful request. This block has no return value and takes two arguments: the receiver operation and the error that occurred during the request.
*/
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure;
@end
///----------------
/// @name Functions
///----------------
/**
Returns a set of MIME types detected in an HTTP `Accept` or `Content-Type` header.
*/
extern NSSet * AFContentTypesFromHTTPHeader(NSString *string);

288
clients/ios/AFNetworking/AFHTTPRequestOperation.m Normal file → Executable file
View file

@ -1,17 +1,17 @@
// AFHTTPRequestOperation.m
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -23,42 +23,50 @@
#import "AFHTTPRequestOperation.h"
#import <objc/runtime.h>
NSString * const kAFNetworkingIncompleteDownloadDirectoryName = @"Incomplete";
// Workaround for change in imp_implementationWithBlock() with Xcode 4.5
#if defined(__IPHONE_6_0) || defined(__MAC_10_8)
#define AF_CAST_TO_BLOCK id
#else
#define AF_CAST_TO_BLOCK __bridge void *
#endif
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wstrict-selector-match"
NSSet * AFContentTypesFromHTTPHeader(NSString *string) {
static NSCharacterSet *_skippedCharacterSet = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_skippedCharacterSet = [[NSCharacterSet characterSetWithCharactersInString:@" ,"] retain];
});
if (!string) {
return nil;
}
NSScanner *scanner = [NSScanner scannerWithString:string];
scanner.charactersToBeSkipped = _skippedCharacterSet;
NSMutableSet *mutableContentTypes = [NSMutableSet set];
while (![scanner isAtEnd]) {
NSString *contentType = nil;
if ([scanner scanUpToString:@";" intoString:&contentType]) {
[scanner scanUpToString:@"," intoString:nil];
NSArray *mediaRanges = [string componentsSeparatedByString:@","];
NSMutableSet *mutableContentTypes = [NSMutableSet setWithCapacity:mediaRanges.count];
[mediaRanges enumerateObjectsUsingBlock:^(NSString *mediaRange, __unused NSUInteger idx, __unused BOOL *stop) {
NSRange parametersRange = [mediaRange rangeOfString:@";"];
if (parametersRange.location != NSNotFound) {
mediaRange = [mediaRange substringToIndex:parametersRange.location];
}
if (contentType) {
[mutableContentTypes addObject:contentType];
mediaRange = [mediaRange stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if (mediaRange.length > 0) {
[mutableContentTypes addObject:mediaRange];
}
}
}];
return [NSSet setWithSet:mutableContentTypes];
}
static void AFSwizzleClassMethodWithImplementation(Class klass, SEL selector, IMP implementation) {
Method originalMethod = class_getClassMethod(klass, selector);
if (method_getImplementation(originalMethod) != implementation) {
class_replaceMethod(objc_getMetaClass([NSStringFromClass(klass) UTF8String]), selector, implementation, method_getTypeEncoding(originalMethod));
static void AFGetMediaTypeAndSubtypeWithString(NSString *string, NSString **type, NSString **subtype) {
if (!string) {
return;
}
NSScanner *scanner = [NSScanner scannerWithString:string];
[scanner setCharactersToBeSkipped:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
[scanner scanUpToString:@"/" intoString:type];
[scanner scanString:@"/" intoString:nil];
[scanner scanUpToString:@";" intoString:subtype];
}
static NSString * AFStringFromIndexSet(NSIndexSet *indexSet) {
@ -77,11 +85,11 @@ static NSString * AFStringFromIndexSet(NSIndexSet *indexSet) {
}
if (range.length == 1) {
[string appendFormat:@"%u", range.location];
[string appendFormat:@"%lu", (long)range.location];
} else {
NSUInteger firstIndex = range.location;
NSUInteger lastIndex = firstIndex + range.length - 1;
[string appendFormat:@"%u-%u", firstIndex, lastIndex];
[string appendFormat:@"%lu-%lu", (long)firstIndex, (long)lastIndex];
}
range.location = nextIndex;
@ -91,77 +99,66 @@ static NSString * AFStringFromIndexSet(NSIndexSet *indexSet) {
return string;
}
NSString * AFCreateIncompleteDownloadDirectoryPath(void) {
static NSString *incompleteDownloadPath;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSString *tempDirectory = NSTemporaryDirectory();
incompleteDownloadPath = [[tempDirectory stringByAppendingPathComponent:kAFNetworkingIncompleteDownloadDirectoryName] retain];
NSError *error = nil;
NSFileManager *fileMan = [[NSFileManager alloc] init];
if(![fileMan createDirectoryAtPath:incompleteDownloadPath withIntermediateDirectories:YES attributes:nil error:&error]) {
NSLog(@"Failed to create incomplete downloads directory at %@", incompleteDownloadPath);
}
[fileMan release];
});
return incompleteDownloadPath;
static void AFSwizzleClassMethodWithClassAndSelectorUsingBlock(Class klass, SEL selector, id block) {
Method originalMethod = class_getClassMethod(klass, selector);
IMP implementation = imp_implementationWithBlock((AF_CAST_TO_BLOCK)block);
class_replaceMethod(objc_getMetaClass([NSStringFromClass(klass) UTF8String]), selector, implementation, method_getTypeEncoding(originalMethod));
}
#pragma mark -
@interface AFHTTPRequestOperation ()
@property (readwrite, nonatomic, retain) NSURLRequest *request;
@property (readwrite, nonatomic, retain) NSHTTPURLResponse *response;
@property (readwrite, nonatomic, retain) NSError *HTTPError;
@property (assign) long long totalContentLength;
@property (assign) long long offsetContentLength;
@property (readwrite, nonatomic, strong) NSURLRequest *request;
@property (readwrite, nonatomic, strong) NSHTTPURLResponse *response;
@property (readwrite, nonatomic, strong) NSError *HTTPError;
@end
@implementation AFHTTPRequestOperation
@synthesize HTTPError = _HTTPError;
@synthesize responseFilePath = _responseFilePath;
@synthesize successCallbackQueue = _successCallbackQueue;
@synthesize failureCallbackQueue = _failureCallbackQueue;
@synthesize totalContentLength = _totalContentLength;
@synthesize offsetContentLength = _offsetContentLength;
@dynamic request;
@dynamic response;
- (void)dealloc {
[_HTTPError release];
if (_successCallbackQueue) {
if (_successCallbackQueue) {
#if !OS_OBJECT_USE_OBJC
dispatch_release(_successCallbackQueue);
#endif
_successCallbackQueue = NULL;
}
if (_failureCallbackQueue) {
dispatch_release(_failureCallbackQueue);
if (_failureCallbackQueue) {
#if !OS_OBJECT_USE_OBJC
dispatch_release(_failureCallbackQueue);
#endif
_failureCallbackQueue = NULL;
}
[super dealloc];
}
- (NSError *)error {
if (self.response && !self.HTTPError) {
if (![self hasAcceptableStatusCode]) {
if (!self.HTTPError && self.response) {
if (![self hasAcceptableStatusCode] || ![self hasAcceptableContentType]) {
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setValue:[NSString stringWithFormat:NSLocalizedString(@"Expected status code in (%@), got %d", nil), AFStringFromIndexSet([[self class] acceptableStatusCodes]), [self.response statusCode]] forKey:NSLocalizedDescriptionKey];
[userInfo setValue:self.responseString forKey:NSLocalizedRecoverySuggestionErrorKey];
[userInfo setValue:[self.request URL] forKey:NSURLErrorFailingURLErrorKey];
self.HTTPError = [[[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadServerResponse userInfo:userInfo] autorelease];
} else if ([self.responseData length] > 0 && ![self hasAcceptableContentType]) { // Don't invalidate content type if there is no content
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setValue:[NSString stringWithFormat:NSLocalizedString(@"Expected content type %@, got %@", nil), [[self class] acceptableContentTypes], [self.response MIMEType]] forKey:NSLocalizedDescriptionKey];
[userInfo setValue:[self.request URL] forKey:NSURLErrorFailingURLErrorKey];
self.HTTPError = [[[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo] autorelease];
[userInfo setValue:self.request forKey:AFNetworkingOperationFailingURLRequestErrorKey];
[userInfo setValue:self.response forKey:AFNetworkingOperationFailingURLResponseErrorKey];
if (![self hasAcceptableStatusCode]) {
NSUInteger statusCode = ([self.response isKindOfClass:[NSHTTPURLResponse class]]) ? (NSUInteger)[self.response statusCode] : 200;
[userInfo setValue:[NSString stringWithFormat:NSLocalizedStringFromTable(@"Expected status code in (%@), got %d", @"AFNetworking", nil), AFStringFromIndexSet([[self class] acceptableStatusCodes]), statusCode] forKey:NSLocalizedDescriptionKey];
self.HTTPError = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorBadServerResponse userInfo:userInfo];
} else if (![self hasAcceptableContentType]) {
// Don't invalidate content type if there is no content
if ([self.responseData length] > 0) {
[userInfo setValue:[NSString stringWithFormat:NSLocalizedStringFromTable(@"Expected content type %@, got %@", @"AFNetworking", nil), [[self class] acceptableContentTypes], [self.response MIMEType]] forKey:NSLocalizedDescriptionKey];
self.HTTPError = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo];
}
}
}
}
if (self.HTTPError) {
return self.HTTPError;
} else {
@ -169,113 +166,136 @@ NSString * AFCreateIncompleteDownloadDirectoryPath(void) {
}
}
- (NSStringEncoding)responseStringEncoding {
// When no explicit charset parameter is provided by the sender, media subtypes of the "text" type are defined to have a default charset value of "ISO-8859-1" when received via HTTP. Data in character sets other than "ISO-8859-1" or its subsets MUST be labeled with an appropriate charset value.
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.4.1
if (self.response && !self.response.textEncodingName && self.responseData && [self.response respondsToSelector:@selector(allHeaderFields)]) {
NSString *type = nil;
AFGetMediaTypeAndSubtypeWithString([[self.response allHeaderFields] valueForKey:@"Content-Type"], &type, nil);
if ([type isEqualToString:@"text"]) {
return NSISOLatin1StringEncoding;
}
}
return [super responseStringEncoding];
}
- (void)pause {
unsigned long long offset = 0;
unsigned long long offset = 0;
if ([self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey]) {
offset = [[self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] unsignedLongLongValue];
} else {
offset = [[self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey] length];
}
NSMutableURLRequest *mutableURLRequest = [[self.request mutableCopy] autorelease];
if ([[self.response allHeaderFields] valueForKey:@"ETag"]) {
NSMutableURLRequest *mutableURLRequest = [self.request mutableCopy];
if ([self.response respondsToSelector:@selector(allHeaderFields)] && [[self.response allHeaderFields] valueForKey:@"ETag"]) {
[mutableURLRequest setValue:[[self.response allHeaderFields] valueForKey:@"ETag"] forHTTPHeaderField:@"If-Range"];
}
[mutableURLRequest setValue:[NSString stringWithFormat:@"bytes=%llu-", offset] forHTTPHeaderField:@"Range"];
self.request = mutableURLRequest;
[super pause];
}
- (BOOL)hasAcceptableStatusCode {
return ![[self class] acceptableStatusCodes] || [[[self class] acceptableStatusCodes] containsIndex:[self.response statusCode]];
if (!self.response) {
return NO;
}
NSUInteger statusCode = ([self.response isKindOfClass:[NSHTTPURLResponse class]]) ? (NSUInteger)[self.response statusCode] : 200;
return ![[self class] acceptableStatusCodes] || [[[self class] acceptableStatusCodes] containsIndex:statusCode];
}
- (BOOL)hasAcceptableContentType {
return ![[self class] acceptableContentTypes] || [[[self class] acceptableContentTypes] containsObject:[self.response MIMEType]];
if (!self.response) {
return NO;
}
// Any HTTP/1.1 message containing an entity-body SHOULD include a Content-Type header field defining the media type of that body. If and only if the media type is not given by a Content-Type field, the recipient MAY attempt to guess the media type via inspection of its content and/or the name extension(s) of the URI used to identify the resource. If the media type remains unknown, the recipient SHOULD treat it as type "application/octet-stream".
// See http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html
NSString *contentType = [self.response MIMEType];
if (!contentType) {
contentType = @"application/octet-stream";
}
return ![[self class] acceptableContentTypes] || [[[self class] acceptableContentTypes] containsObject:contentType];
}
- (void)setSuccessCallbackQueue:(dispatch_queue_t)successCallbackQueue {
if (successCallbackQueue != _successCallbackQueue) {
if (_successCallbackQueue) {
#if !OS_OBJECT_USE_OBJC
dispatch_release(_successCallbackQueue);
#endif
_successCallbackQueue = NULL;
}
if (successCallbackQueue) {
#if !OS_OBJECT_USE_OBJC
dispatch_retain(successCallbackQueue);
#endif
_successCallbackQueue = successCallbackQueue;
}
}
}
}
- (void)setFailureCallbackQueue:(dispatch_queue_t)failureCallbackQueue {
if (failureCallbackQueue != _failureCallbackQueue) {
if (_failureCallbackQueue) {
#if !OS_OBJECT_USE_OBJC
dispatch_release(_failureCallbackQueue);
#endif
_failureCallbackQueue = NULL;
}
if (failureCallbackQueue) {
#if !OS_OBJECT_USE_OBJC
dispatch_retain(failureCallbackQueue);
#endif
_failureCallbackQueue = failureCallbackQueue;
}
}
}
}
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
self.completionBlock = ^ {
if ([self isCancelled]) {
return;
}
// completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"
self.completionBlock = ^{
if (self.error) {
if (failure) {
dispatch_async(self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{
dispatch_async(self.failureCallbackQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
if (success) {
dispatch_async(self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{
dispatch_async(self.successCallbackQueue ?: dispatch_get_main_queue(), ^{
success(self, self.responseData);
});
}
}
};
}
- (void)setResponseFilePath:(NSString *)responseFilePath {
if ([self isReady] && responseFilePath != _responseFilePath) {
[_responseFilePath release];
_responseFilePath = [responseFilePath retain];
if (responseFilePath) {
self.outputStream = [NSOutputStream outputStreamToFileAtPath:responseFilePath append:NO];
}else {
self.outputStream = [NSOutputStream outputStreamToMemory];
}
}
#pragma clang diagnostic pop
}
#pragma mark - AFHTTPRequestOperation
static id AFStaticClassValueImplementation(id self, SEL _cmd) {
return objc_getAssociatedObject([self class], _cmd);
}
+ (NSIndexSet *)acceptableStatusCodes {
return [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
}
+ (void)addAcceptableStatusCodes:(NSIndexSet *)statusCodes {
NSMutableIndexSet *mutableStatusCodes = [[[NSMutableIndexSet alloc] initWithIndexSet:[self acceptableStatusCodes]] autorelease];
NSMutableIndexSet *mutableStatusCodes = [[NSMutableIndexSet alloc] initWithIndexSet:[self acceptableStatusCodes]];
[mutableStatusCodes addIndexes:statusCodes];
SEL selector = @selector(acceptableStatusCodes);
AFSwizzleClassMethodWithImplementation([self class], selector, (IMP)AFStaticClassValueImplementation);
objc_setAssociatedObject([self class], selector, mutableStatusCodes, OBJC_ASSOCIATION_COPY_NONATOMIC);
AFSwizzleClassMethodWithClassAndSelectorUsingBlock([self class], @selector(acceptableStatusCodes), ^(__unused id _self) {
return mutableStatusCodes;
});
}
+ (NSSet *)acceptableContentTypes {
@ -283,53 +303,21 @@ static id AFStaticClassValueImplementation(id self, SEL _cmd) {
}
+ (void)addAcceptableContentTypes:(NSSet *)contentTypes {
NSMutableSet *mutableContentTypes = [[[NSMutableSet alloc] initWithSet:[self acceptableContentTypes] copyItems:YES] autorelease];
NSMutableSet *mutableContentTypes = [[NSMutableSet alloc] initWithSet:[self acceptableContentTypes] copyItems:YES];
[mutableContentTypes unionSet:contentTypes];
SEL selector = @selector(acceptableContentTypes);
AFSwizzleClassMethodWithImplementation([self class], selector, (IMP)AFStaticClassValueImplementation);
objc_setAssociatedObject([self class], selector, mutableContentTypes, OBJC_ASSOCIATION_COPY_NONATOMIC);
AFSwizzleClassMethodWithClassAndSelectorUsingBlock([self class], @selector(acceptableContentTypes), ^(__unused id _self) {
return mutableContentTypes;
});
}
+ (BOOL)canProcessRequest:(NSURLRequest *)request {
if ([[self class] isEqual:[AFHTTPRequestOperation class]]) {
return YES;
}
return [[self acceptableContentTypes] intersectsSet:AFContentTypesFromHTTPHeader([request valueForHTTPHeaderField:@"Accept"])];
}
#pragma mark - NSURLConnectionDelegate
- (void)connection:(NSURLConnection *)connection
didReceiveResponse:(NSURLResponse *)response
{
self.response = (NSHTTPURLResponse *)response;
// 206 = Partial Content.
long long totalContentLength = self.response.expectedContentLength;
long long fileOffset = 0;
if ([self.response statusCode] != 206) {
if ([self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey]) {
[self.outputStream setProperty:[NSNumber numberWithInteger:0] forKey:NSStreamFileCurrentOffsetKey];
} else {
if ([[self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey] length] > 0) {
self.outputStream = [NSOutputStream outputStreamToMemory];
}
}
}else {
NSString *contentRange = [self.response.allHeaderFields valueForKey:@"Content-Range"];
if ([contentRange hasPrefix:@"bytes"]) {
NSArray *bytes = [contentRange componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@" -/"]];
if ([bytes count] == 4) {
fileOffset = [[bytes objectAtIndex:1] longLongValue];
totalContentLength = [[bytes objectAtIndex:2] longLongValue] ?: -1; // if this is *, it's converted to 0, but -1 is default.
}
}
}
self.offsetContentLength = MAX(fileOffset, 0);
self.totalContentLength = totalContentLength;
[self.outputStream open];
}
@end
#pragma clang diagnostic pop

81
clients/ios/AFNetworking/AFImageRequestOperation.h Normal file → Executable file
View file

@ -1,17 +1,17 @@
// AFImageRequestOperation.h
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -25,19 +25,19 @@
#import <Availability.h>
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#import <UIKit/UIKit.h>
#elif __MAC_OS_X_VERSION_MIN_REQUIRED
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
#import <Cocoa/Cocoa.h>
#endif
/**
`AFImageRequestOperation` is a subclass of `AFHTTPRequestOperation` for downloading an processing images.
`AFImageRequestOperation` is a subclass of `AFHTTPRequestOperation` for downloading and processing images.
## Acceptable Content Types
By default, `AFImageRequestOperation` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage:
- `image/tiff`
- `image/jpeg`
- `image/gif`
@ -54,59 +54,60 @@
/**
An image constructed from the response data. If an error occurs during the request, `nil` will be returned, and the `error` property will be set to the error.
*/
#if __IPHONE_OS_VERSION_MIN_REQUIRED
@property (readonly, nonatomic, retain) UIImage *responseImage;
#elif __MAC_OS_X_VERSION_MIN_REQUIRED
@property (readonly, nonatomic, retain) NSImage *responseImage;
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
@property (readonly, nonatomic, strong) UIImage *responseImage;
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
@property (readonly, nonatomic, strong) NSImage *responseImage;
#endif
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
/**
The scale factor used when interpreting the image data to construct `responseImage`. Specifying a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a different scale factor changes the size of the image as reported by the size property. This is set to the value of `[[UIScreen mainScreen] scale]` by default, which automatically scales images for retina displays, for instance.
The scale factor used when interpreting the image data to construct `responseImage`. Specifying a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a different scale factor changes the size of the image as reported by the size property. This is set to the value of scale of the main screen by default, which automatically scales images for retina displays, for instance.
*/
@property (nonatomic, assign) CGFloat imageScale;
/**
Whether to automatically inflate response image data for compressed formats (such as PNG or JPEG). Enabling this can significantly improve drawing performance on iOS when used with `setCompletionBlockWithSuccess:failure:`, as it allows a bitmap representation to be constructed in the background rather than on the main thread. `YES` by default.
*/
@property (nonatomic, assign) BOOL automaticallyInflatesResponseImage;
#endif
/**
An image constructed from the response data. If an error occurs during the request, `nil` will be returned, and the `error` property will be set to the error.
*/
/**
Creates and returns an `AFImageRequestOperation` object and sets the specified success callback.
@param urlRequest The request object to be loaded asynchronously during execution of the operation.
@param success A block object to be executed when the request finishes successfully. This block has no return value and takes a single arguments, the image created from the response data of the request.
@param success A block object to be executed when the request finishes successfully. This block has no return value and takes a single argument, the image created from the response data of the request.
@return A new image request operation
*/
#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ (AFImageRequestOperation *)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(UIImage *image))success;
#elif __MAC_OS_X_VERSION_MIN_REQUIRED
+ (AFImageRequestOperation *)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSImage *image))success;
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+ (instancetype)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(UIImage *image))success;
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+ (instancetype)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSImage *image))success;
#endif
/**
Creates and returns an `AFImageRequestOperation` object and sets the specified success callback.
@param urlRequest The request object to be loaded asynchronously during execution of the operation.
@param imageProcessingBlock A block object to be executed after the image request finishes successfully, but before the image is returned in the `success` block. This block takes a single argument, the image loaded from the response body, and returns the processed image.
@param success A block object to be executed when the request finishes successfully, with a status code in the 2xx range, and with an acceptable content type (e.g. `image/png`). This block has no return value and takes three arguments: the request object of the operation, the response for the request, and the image created from the response data.
@param failure A block object to be executed when the request finishes unsuccessfully. This block has no return value and takes three arguments: the request object of the operation, the response for the request, and the error associated with the cause for the unsuccessful operation.
@return A new image request operation
*/
#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ (AFImageRequestOperation *)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
imageProcessingBlock:(UIImage *(^)(UIImage *image))imageProcessingBlock
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure;
#elif __MAC_OS_X_VERSION_MIN_REQUIRED
+ (AFImageRequestOperation *)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
imageProcessingBlock:(NSImage *(^)(NSImage *image))imageProcessingBlock
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure;
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+ (instancetype)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
imageProcessingBlock:(UIImage *(^)(UIImage *image))imageProcessingBlock
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure;
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+ (instancetype)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
imageProcessingBlock:(NSImage *(^)(NSImage *image))imageProcessingBlock
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure;
#endif
@end

227
clients/ios/AFNetworking/AFImageRequestOperation.m Normal file → Executable file
View file

@ -1,17 +1,17 @@
// AFImageRequestOperation.m
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -22,32 +22,117 @@
#import "AFImageRequestOperation.h"
static dispatch_queue_t af_image_request_operation_processing_queue;
static dispatch_queue_t image_request_operation_processing_queue() {
if (af_image_request_operation_processing_queue == NULL) {
af_image_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.image-request.processing", 0);
}
static dispatch_queue_t af_image_request_operation_processing_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_image_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.image-request.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_image_request_operation_processing_queue;
}
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#import <CoreGraphics/CoreGraphics.h>
static UIImage * AFImageWithDataAtScale(NSData *data, CGFloat scale) {
if ([UIImage instancesRespondToSelector:@selector(initWithData:scale:)]) {
return [[UIImage alloc] initWithData:data scale:scale];
} else {
UIImage *image = [[UIImage alloc] initWithData:data];
return [[UIImage alloc] initWithCGImage:[image CGImage] scale:scale orientation:image.imageOrientation];
}
}
static UIImage * AFInflatedImageFromResponseWithDataAtScale(NSHTTPURLResponse *response, NSData *data, CGFloat scale) {
if (!data || [data length] == 0) {
return nil;
}
CGImageRef imageRef = nil;
CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
if ([response.MIMEType isEqualToString:@"image/png"]) {
imageRef = CGImageCreateWithPNGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault);
} else if ([response.MIMEType isEqualToString:@"image/jpeg"]) {
imageRef = CGImageCreateWithJPEGDataProvider(dataProvider, NULL, true, kCGRenderingIntentDefault);
}
if (!imageRef) {
UIImage *image = AFImageWithDataAtScale(data, scale);
if (image.images) {
CGDataProviderRelease(dataProvider);
return image;
}
imageRef = CGImageCreateCopy([image CGImage]);
}
CGDataProviderRelease(dataProvider);
if (!imageRef) {
return nil;
}
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
size_t bytesPerRow = 0; // CGImageGetBytesPerRow() calculates incorrectly in iOS 5.0, so defer to CGBitmapContextCreate()
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
if (CGColorSpaceGetNumberOfComponents(colorSpace) == 3) {
int alpha = (bitmapInfo & kCGBitmapAlphaInfoMask);
if (alpha == kCGImageAlphaNone) {
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
bitmapInfo |= kCGImageAlphaNoneSkipFirst;
} else if (!(alpha == kCGImageAlphaNoneSkipFirst || alpha == kCGImageAlphaNoneSkipLast)) {
bitmapInfo &= ~kCGBitmapAlphaInfoMask;
bitmapInfo |= kCGImageAlphaPremultipliedFirst;
}
}
CGContextRef context = CGBitmapContextCreate(NULL, width, height, bitsPerComponent, bytesPerRow, colorSpace, bitmapInfo);
CGColorSpaceRelease(colorSpace);
if (!context) {
CGImageRelease(imageRef);
return [[UIImage alloc] initWithData:data];
}
CGRect rect = CGRectMake(0.0f, 0.0f, width, height);
CGContextDrawImage(context, rect, imageRef);
CGImageRef inflatedImageRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
UIImage *inflatedImage = [[UIImage alloc] initWithCGImage:inflatedImageRef scale:scale orientation:UIImageOrientationUp];
CGImageRelease(inflatedImageRef);
CGImageRelease(imageRef);
return inflatedImage;
}
#endif
@interface AFImageRequestOperation ()
#if __IPHONE_OS_VERSION_MIN_REQUIRED
@property (readwrite, nonatomic, retain) UIImage *responseImage;
#elif __MAC_OS_X_VERSION_MIN_REQUIRED
@property (readwrite, nonatomic, retain) NSImage *responseImage;
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
@property (readwrite, nonatomic, strong) UIImage *responseImage;
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
@property (readwrite, nonatomic, strong) NSImage *responseImage;
#endif
@end
@implementation AFImageRequestOperation
@synthesize responseImage = _responseImage;
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
@synthesize imageScale = _imageScale;
#endif
#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ (AFImageRequestOperation *)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(UIImage *image))success
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+ (instancetype)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(UIImage *image))success
{
return [self imageRequestOperationWithRequest:urlRequest imageProcessingBlock:nil success:^(NSURLRequest __unused *request, NSHTTPURLResponse __unused *response, UIImage *image) {
if (success) {
@ -55,9 +140,9 @@ static dispatch_queue_t image_request_operation_processing_queue() {
}
} failure:nil];
}
#elif __MAC_OS_X_VERSION_MIN_REQUIRED
+ (AFImageRequestOperation *)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSImage *image))success
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+ (instancetype)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSImage *image))success
{
return [self imageRequestOperationWithRequest:urlRequest imageProcessingBlock:nil success:^(NSURLRequest __unused *request, NSHTTPURLResponse __unused *response, NSImage *image) {
if (success) {
@ -68,23 +153,25 @@ static dispatch_queue_t image_request_operation_processing_queue() {
#endif
#if __IPHONE_OS_VERSION_MIN_REQUIRED
+ (AFImageRequestOperation *)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
imageProcessingBlock:(UIImage *(^)(UIImage *))imageProcessingBlock
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+ (instancetype)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
imageProcessingBlock:(UIImage *(^)(UIImage *))imageProcessingBlock
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure
{
AFImageRequestOperation *requestOperation = [[[AFImageRequestOperation alloc] initWithRequest:urlRequest] autorelease];
AFImageRequestOperation *requestOperation = [(AFImageRequestOperation *)[self alloc] initWithRequest:urlRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (success) {
UIImage *image = responseObject;
if (imageProcessingBlock) {
dispatch_async(image_request_operation_processing_queue(), ^(void) {
UIImage *processedImage = imageProcessingBlock(image);
dispatch_async(requestOperation.successCallbackQueue ? requestOperation.successCallbackQueue : dispatch_get_main_queue(), ^(void) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
dispatch_async(operation.successCallbackQueue ?: dispatch_get_main_queue(), ^(void) {
success(operation.request, operation.response, processedImage);
});
#pragma clang diagnostic pop
});
} else {
success(operation.request, operation.response, image);
@ -95,17 +182,17 @@ static dispatch_queue_t image_request_operation_processing_queue() {
failure(operation.request, operation.response, error);
}
}];
return requestOperation;
}
#elif __MAC_OS_X_VERSION_MIN_REQUIRED
+ (AFImageRequestOperation *)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
imageProcessingBlock:(NSImage *(^)(NSImage *))imageProcessingBlock
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+ (instancetype)imageRequestOperationWithRequest:(NSURLRequest *)urlRequest
imageProcessingBlock:(NSImage *(^)(NSImage *))imageProcessingBlock
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure
{
AFImageRequestOperation *requestOperation = [[[AFImageRequestOperation alloc] initWithRequest:urlRequest] autorelease];
AFImageRequestOperation *requestOperation = [(AFImageRequestOperation *)[self alloc] initWithRequest:urlRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (success) {
NSImage *image = responseObject;
@ -113,7 +200,7 @@ static dispatch_queue_t image_request_operation_processing_queue() {
dispatch_async(image_request_operation_processing_queue(), ^(void) {
NSImage *processedImage = imageProcessingBlock(image);
dispatch_async(requestOperation.successCallbackQueue ? requestOperation.successCallbackQueue : dispatch_get_main_queue(), ^(void) {
dispatch_async(operation.successCallbackQueue ?: dispatch_get_main_queue(), ^(void) {
success(operation.request, operation.response, processedImage);
});
});
@ -126,7 +213,7 @@ static dispatch_queue_t image_request_operation_processing_queue() {
failure(operation.request, operation.response, error);
}
}];
return requestOperation;
}
#endif
@ -136,54 +223,55 @@ static dispatch_queue_t image_request_operation_processing_queue() {
if (!self) {
return nil;
}
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
self.imageScale = [[UIScreen mainScreen] scale];
self.automaticallyInflatesResponseImage = YES;
#endif
return self;
}
- (void)dealloc {
[_responseImage release];
[super dealloc];
}
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
- (UIImage *)responseImage {
if (!_responseImage && [self.responseData length] > 0 && [self isFinished]) {
UIImage *image = [UIImage imageWithData:self.responseData];
self.responseImage = [UIImage imageWithCGImage:[image CGImage] scale:self.imageScale orientation:image.imageOrientation];
if (self.automaticallyInflatesResponseImage) {
self.responseImage = AFInflatedImageFromResponseWithDataAtScale(self.response, self.responseData, self.imageScale);
} else {
self.responseImage = AFImageWithDataAtScale(self.responseData, self.imageScale);
}
}
return _responseImage;
}
- (void)setImageScale:(CGFloat)imageScale {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfloat-equal"
if (imageScale == _imageScale) {
return;
}
#pragma clang diagnostic pop
_imageScale = imageScale;
self.responseImage = nil;
}
#elif __MAC_OS_X_VERSION_MIN_REQUIRED
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
- (NSImage *)responseImage {
if (!_responseImage && [self.responseData length] > 0 && [self isFinished]) {
// Ensure that the image is set to it's correct pixel width and height
NSBitmapImageRep *bitimage = [[NSBitmapImageRep alloc] initWithData:self.responseData];
self.responseImage = [[[NSImage alloc] initWithSize:NSMakeSize([bitimage pixelsWide], [bitimage pixelsHigh])] autorelease];
self.responseImage = [[NSImage alloc] initWithSize:NSMakeSize([bitimage pixelsWide], [bitimage pixelsHigh])];
[self.responseImage addRepresentation:bitimage];
[bitimage release];
}
return _responseImage;
}
#endif
#pragma mark - AFHTTPClientOperation
#pragma mark - AFHTTPRequestOperation
+ (NSSet *)acceptableContentTypes {
return [NSSet setWithObjects:@"image/tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/ico", @"image/x-icon", @"image/bmp", @"image/x-bmp", @"image/x-xbitmap", @"image/x-win-bitmap", nil];
@ -195,42 +283,43 @@ static dispatch_queue_t image_request_operation_processing_queue() {
dispatch_once(&onceToken, ^{
_acceptablePathExtension = [[NSSet alloc] initWithObjects:@"tif", @"tiff", @"jpg", @"jpeg", @"gif", @"png", @"ico", @"bmp", @"cur", nil];
});
return [_acceptablePathExtension containsObject:[[request URL] pathExtension]] || [super canProcessRequest:request];
return [_acceptablePathExtension containsObject:[[request URL] pathExtension]] || [super canProcessRequest:request];
}
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"
self.completionBlock = ^ {
if ([self isCancelled]) {
return;
}
dispatch_async(image_request_operation_processing_queue(), ^(void) {
if (self.error) {
if (failure) {
dispatch_async(self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{
dispatch_async(self.failureCallbackQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
} else {
if (success) {
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
UIImage *image = nil;
#elif __MAC_OS_X_VERSION_MIN_REQUIRED
#elif defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
NSImage *image = nil;
#endif
image = self.responseImage;
dispatch_async(self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{
dispatch_async(self.successCallbackQueue ?: dispatch_get_main_queue(), ^{
success(self, image);
});
}
}
});
};
});
};
#pragma clang diagnostic pop
}
@end

17
clients/ios/AFNetworking/AFJSONRequestOperation.h Normal file → Executable file
View file

@ -33,7 +33,7 @@
- `application/json`
- `text/json`
@warning JSON parsing will automatically use JSONKit, SBJSON, YAJL, or NextiveJSON, if provided. Otherwise, the built-in `NSJSONSerialization` class is used, if available (iOS 5.0 and Mac OS 10.7). If the build target does not either support `NSJSONSerialization` or include a third-party JSON library, a runtime exception will be thrown when attempting to parse a JSON request.
@warning JSON parsing will use the built-in `NSJSONSerialization` class.
*/
@interface AFJSONRequestOperation : AFHTTPRequestOperation
@ -44,8 +44,13 @@
/**
A JSON object constructed from the response data. If an error occurs while parsing, `nil` will be returned, and the `error` property will be set to the error.
*/
@property (readonly, nonatomic, retain) id responseJSON;
@property (readonly, nonatomic, strong) id responseJSON;
/**
Options for reading the response JSON data and creating the Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions".
*/
@property (nonatomic, assign) NSJSONReadingOptions JSONReadingOptions;
///----------------------------------
/// @name Creating Request Operations
///----------------------------------
@ -55,12 +60,12 @@
@param urlRequest The request object to be loaded asynchronously during execution of the operation
@param success A block object to be executed when the operation finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the JSON object created from the response data of request.
@param failure A block object to be executed when the operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the resonse data as JSON. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error describing the network or parsing error that occurred.
@param failure A block object to be executed when the operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data as JSON. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error describing the network or parsing error that occurred.
@return A new JSON request operation
*/
+ (AFJSONRequestOperation *)JSONRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, id JSON))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON))failure;
+ (instancetype)JSONRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, id JSON))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON))failure;
@end

88
clients/ios/AFNetworking/AFJSONRequestOperation.m Normal file → Executable file
View file

@ -1,17 +1,17 @@
// AFJSONRequestOperation.m
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -21,31 +21,34 @@
// THE SOFTWARE.
#import "AFJSONRequestOperation.h"
#import "AFJSONUtilities.h"
static dispatch_queue_t af_json_request_operation_processing_queue;
static dispatch_queue_t json_request_operation_processing_queue() {
if (af_json_request_operation_processing_queue == NULL) {
af_json_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.json-request.processing", 0);
}
static dispatch_queue_t af_json_request_operation_processing_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_json_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.json-request.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_json_request_operation_processing_queue;
}
@interface AFJSONRequestOperation ()
@property (readwrite, nonatomic, retain) id responseJSON;
@property (readwrite, nonatomic, retain) NSError *JSONError;
@property (readwrite, nonatomic, strong) id responseJSON;
@property (readwrite, nonatomic, strong) NSError *JSONError;
@property (readwrite, nonatomic, strong) NSRecursiveLock *lock;
@end
@implementation AFJSONRequestOperation
@synthesize responseJSON = _responseJSON;
@synthesize JSONReadingOptions = _JSONReadingOptions;
@synthesize JSONError = _JSONError;
@dynamic lock;
+ (AFJSONRequestOperation *)JSONRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, id JSON))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON))failure
+ (instancetype)JSONRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, id JSON))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON))failure
{
AFJSONRequestOperation *requestOperation = [[[self alloc] initWithRequest:urlRequest] autorelease];
AFJSONRequestOperation *requestOperation = [(AFJSONRequestOperation *)[self alloc] initWithRequest:urlRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (success) {
success(operation.request, operation.response, responseObject);
@ -55,29 +58,37 @@ static dispatch_queue_t json_request_operation_processing_queue() {
failure(operation.request, operation.response, error, [(AFJSONRequestOperation *)operation responseJSON]);
}
}];
return requestOperation;
}
- (void)dealloc {
[_responseJSON release];
[_JSONError release];
[super dealloc];
}
- (id)responseJSON {
[self.lock lock];
if (!_responseJSON && [self.responseData length] > 0 && [self isFinished] && !self.JSONError) {
NSError *error = nil;
if ([self.responseData length] == 0) {
self.responseJSON = nil;
} else {
self.responseJSON = AFJSONDecode(self.responseData, &error);
// Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
// See https://github.com/rails/rails/issues/1742
if (self.responseString && ![self.responseString isEqualToString:@" "]) {
// Workaround for a bug in NSJSONSerialization when Unicode character escape codes are used instead of the actual character
// See http://stackoverflow.com/a/12843465/157142
NSData *data = [self.responseString dataUsingEncoding:NSUTF8StringEncoding];
if (data) {
self.responseJSON = [NSJSONSerialization JSONObjectWithData:data options:self.JSONReadingOptions error:&error];
} else {
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
[userInfo setValue:@"Operation responseData failed decoding as a UTF-8 string" forKey:NSLocalizedDescriptionKey];
[userInfo setValue:[NSString stringWithFormat:@"Could not decode string: %@", self.responseString] forKey:NSLocalizedFailureReasonErrorKey];
error = [[NSError alloc] initWithDomain:AFNetworkingErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:userInfo];
}
}
self.JSONError = error;
}
[self.lock unlock];
return _responseJSON;
}
@ -102,37 +113,38 @@ static dispatch_queue_t json_request_operation_processing_queue() {
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"
self.completionBlock = ^ {
if ([self isCancelled]) {
return;
}
if (self.error) {
if (failure) {
dispatch_async(self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{
dispatch_async(self.failureCallbackQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
dispatch_async(json_request_operation_processing_queue(), ^{
id JSON = self.responseJSON;
if (self.JSONError) {
if (self.error) {
if (failure) {
dispatch_async(self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{
dispatch_async(self.failureCallbackQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
if (success) {
dispatch_async(self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{
dispatch_async(self.successCallbackQueue ?: dispatch_get_main_queue(), ^{
success(self, JSON);
});
}
}
}
});
}
};
};
#pragma clang diagnostic pop
}
@end

View file

@ -1,17 +1,17 @@
// AFNetworkActivityIndicatorManager.h
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -24,34 +24,41 @@
#import <Availability.h>
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#import <UIKit/UIKit.h>
/**
`AFNetworkActivityIndicatorManager` manages the state of the network activity indicator in the status bar. When enabled, it will listen for notifications indicating that a network request operation has started or finished, and start or stop animating the indicator accordingly. The number of active requests is incremented and decremented much like a stack or a semaphore, and the activity indicator will animate so long as that number is greater than zero.
@discussion By setting `isNetworkActivityIndicatorVisible` to `YES` for `sharedManager`, the network activity indicator will show and hide automatically as requests start and finish. You should not ever need to call `incrementActivityCount` or `decrementActivityCount` yourself.
You should enable the shared instance of `AFNetworkActivityIndicatorManager` when your application finishes launching. In `AppDelegate application:didFinishLaunchingWithOptions:` you can do so with the following code:
[[AFNetworkActivityIndicatorManager sharedManager] setEnabled:YES];
By setting `isNetworkActivityIndicatorVisible` to `YES` for `sharedManager`, the network activity indicator will show and hide automatically as requests start and finish. You should not ever need to call `incrementActivityCount` or `decrementActivityCount` yourself.
See the Apple Human Interface Guidelines section about the Network Activity Indicator for more information:
http://developer.apple.com/library/iOS/#documentation/UserExperience/Conceptual/MobileHIG/UIElementGuidelines/UIElementGuidelines.html#//apple_ref/doc/uid/TP40006556-CH13-SW44
*/
@interface AFNetworkActivityIndicatorManager : NSObject
/**
A Boolean value indicating whether the manager is enabled.
@discussion If YES, the manager will change status bar network activity indicator according to network operation notifications it receives. The default value is NO.
A Boolean value indicating whether the manager is enabled.
If YES, the manager will change status bar network activity indicator according to network operation notifications it receives. The default value is NO.
*/
@property (nonatomic, assign, getter = isEnabled) BOOL enabled;
/**
A Boolean value indicating whether the network activity indicator is currently displayed in the status bar.
*/
@property (readonly, nonatomic, assign) BOOL isNetworkActivityIndicatorVisible;
@property (readonly, nonatomic, assign) BOOL isNetworkActivityIndicatorVisible;
/**
Returns the shared network activity indicator manager object for the system.
@return The systemwide network activity indicator manager.
*/
+ (AFNetworkActivityIndicatorManager *)sharedManager;
+ (instancetype)sharedManager;
/**
Increments the number of active network requests. If this number was zero before incrementing, this will start animating the status bar network activity indicator.

View file

@ -1,17 +1,17 @@
// AFNetworkActivityIndicatorManager.m
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -24,15 +24,16 @@
#import "AFHTTPRequestOperation.h"
#if __IPHONE_OS_VERSION_MIN_REQUIRED
static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25;
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.17;
@interface AFNetworkActivityIndicatorManager ()
@property (readwrite, atomic, assign) NSInteger activityCount;
@property (readwrite, nonatomic, retain) NSTimer *activityIndicatorVisibilityTimer;
@property (readonly, getter = isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible;
@property (readwrite, nonatomic, assign) NSInteger activityCount;
@property (readwrite, nonatomic, strong) NSTimer *activityIndicatorVisibilityTimer;
@property (readonly, nonatomic, getter = isNetworkActivityIndicatorVisible) BOOL networkActivityIndicatorVisible;
- (void)updateNetworkActivityIndicatorVisibility;
- (void)updateNetworkActivityIndicatorVisibilityDelayed;
@end
@implementation AFNetworkActivityIndicatorManager
@ -41,35 +42,37 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25;
@synthesize enabled = _enabled;
@dynamic networkActivityIndicatorVisible;
+ (AFNetworkActivityIndicatorManager *)sharedManager {
+ (instancetype)sharedManager {
static AFNetworkActivityIndicatorManager *_sharedManager = nil;
static dispatch_once_t oncePredicate;
dispatch_once(&oncePredicate, ^{
_sharedManager = [[self alloc] init];
});
return _sharedManager;
}
+ (NSSet *)keyPathsForValuesAffectingIsNetworkActivityIndicatorVisible {
return [NSSet setWithObject:@"activityCount"];
}
- (id)init {
self = [super init];
if (!self) {
return nil;
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(incrementActivityCount) name:AFNetworkingOperationDidStartNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(decrementActivityCount) name:AFNetworkingOperationDidFinishNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkingOperationDidStart:) name:AFNetworkingOperationDidStartNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(networkingOperationDidFinish:) name:AFNetworkingOperationDidFinishNotification object:nil];
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_activityIndicatorVisibilityTimer invalidate];
[_activityIndicatorVisibilityTimer release]; _activityIndicatorVisibilityTimer = nil;
[super dealloc];
}
- (void)updateNetworkActivityIndicatorVisibilityDelayed {
@ -78,9 +81,9 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25;
if (![self isNetworkActivityIndicatorVisible]) {
[self.activityIndicatorVisibilityTimer invalidate];
self.activityIndicatorVisibilityTimer = [NSTimer timerWithTimeInterval:kAFNetworkActivityIndicatorInvisibilityDelay target:self selector:@selector(updateNetworkActivityIndicatorVisibility) userInfo:nil repeats:NO];
[[NSRunLoop currentRunLoop] addTimer:self.activityIndicatorVisibilityTimer forMode:NSRunLoopCommonModes];
[[NSRunLoop mainRunLoop] addTimer:self.activityIndicatorVisibilityTimer forMode:NSRunLoopCommonModes];
} else {
[self updateNetworkActivityIndicatorVisibility];
[self performSelectorOnMainThread:@selector(updateNetworkActivityIndicatorVisibility) withObject:nil waitUntilDone:NO modes:[NSArray arrayWithObject:NSRunLoopCommonModes]];
}
}
}
@ -90,9 +93,7 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25;
}
- (void)updateNetworkActivityIndicatorVisibility {
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:[self isNetworkActivityIndicatorVisible]];
});
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:[self isNetworkActivityIndicatorVisible]];
}
// Not exposed, but used if activityCount is set via KVC.
@ -104,7 +105,10 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25;
@synchronized(self) {
_activityCount = activityCount;
}
[self updateNetworkActivityIndicatorVisibilityDelayed];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateNetworkActivityIndicatorVisibilityDelayed];
});
}
- (void)incrementActivityCount {
@ -113,20 +117,39 @@ static NSTimeInterval const kAFNetworkActivityIndicatorInvisibilityDelay = 0.25;
_activityCount++;
}
[self didChangeValueForKey:@"activityCount"];
[self updateNetworkActivityIndicatorVisibilityDelayed];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateNetworkActivityIndicatorVisibilityDelayed];
});
}
- (void)decrementActivityCount {
[self willChangeValueForKey:@"activityCount"];
@synchronized(self) {
_activityCount--;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
_activityCount = MAX(_activityCount - 1, 0);
#pragma clang diagnostic pop
}
[self didChangeValueForKey:@"activityCount"];
[self updateNetworkActivityIndicatorVisibilityDelayed];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateNetworkActivityIndicatorVisibilityDelayed];
});
}
+ (NSSet *)keyPathsForValuesAffectingIsNetworkActivityIndicatorVisible {
return [NSSet setWithObject:@"activityCount"];
- (void)networkingOperationDidStart:(NSNotification *)notification {
AFURLConnectionOperation *connectionOperation = [notification object];
if (connectionOperation.request.URL) {
[self incrementActivityCount];
}
}
- (void)networkingOperationDidFinish:(NSNotification *)notification {
AFURLConnectionOperation *connectionOperation = [notification object];
if (connectionOperation.request.URL) {
[self decrementActivityCount];
}
}
@end

25
clients/ios/AFNetworking/AFNetworking.h Normal file → Executable file
View file

@ -24,21 +24,20 @@
#import <Availability.h>
#ifndef _AFNETWORKING_
#define _AFNETWORKING_
#define _AFNETWORKING_
#import "AFURLConnectionOperation.h"
#import "AFURLConnectionOperation.h"
#import "AFHTTPRequestOperation.h"
#import "AFJSONRequestOperation.h"
#import "AFXMLRequestOperation.h"
#import "AFPropertyListRequestOperation.h"
#import "AFHTTPClient.h"
#import "AFHTTPRequestOperation.h"
#import "AFJSONRequestOperation.h"
#import "AFXMLRequestOperation.h"
#import "AFPropertyListRequestOperation.h"
#import "AFHTTPClient.h"
#import "AFImageRequestOperation.h"
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#import "AFNetworkActivityIndicatorManager.h"
#import "UIImageView+AFNetworking.h"
#endif
#import "AFImageRequestOperation.h"
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#import "AFNetworkActivityIndicatorManager.h"
#import "UIImageView+AFNetworking.h"
#endif
#endif /* _AFNETWORKING_ */

View file

@ -1,17 +1,17 @@
// AFPropertyListRequestOperation.h
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -25,11 +25,11 @@
/**
`AFPropertyListRequestOperation` is a subclass of `AFHTTPRequestOperation` for downloading and deserializing objects with property list (plist) response data.
## Acceptable Content Types
By default, `AFPropertyListRequestOperation` accepts the following MIME types:
- `application/x-plist`
*/
@interface AFPropertyListRequestOperation : AFHTTPRequestOperation
@ -41,7 +41,7 @@
/**
An object deserialized from a plist constructed using the response data.
*/
@property (readonly, nonatomic, retain) id responsePropertyList;
@property (readonly, nonatomic) id responsePropertyList;
///--------------------------------------
/// @name Managing Property List Behavior
@ -54,15 +54,15 @@
/**
Creates and returns an `AFPropertyListRequestOperation` object and sets the specified success and failure callbacks.
@param urlRequest The request object to be loaded asynchronously during execution of the operation
@param success A block object to be executed when the operation finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the object deserialized from a plist constructed using the response data.
@param failure A block object to be executed when the operation finishes unsuccessfully, or that finishes successfully, but encountered an error while deserializing the object from a property list. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error describing the network or parsing error that occurred.
@return A new property list request operation
*/
+ (AFPropertyListRequestOperation *)propertyListRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, id propertyList))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id propertyList))failure;
+ (instancetype)propertyListRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, id propertyList))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id propertyList))failure;
@end

View file

@ -1,17 +1,17 @@
// AFPropertyListRequestOperation.m
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -22,19 +22,20 @@
#import "AFPropertyListRequestOperation.h"
static dispatch_queue_t af_property_list_request_operation_processing_queue;
static dispatch_queue_t property_list_request_operation_processing_queue() {
if (af_property_list_request_operation_processing_queue == NULL) {
af_property_list_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.property-list-request.processing", 0);
}
static dispatch_queue_t af_property_list_request_operation_processing_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_property_list_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.property-list-request.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_property_list_request_operation_processing_queue;
}
@interface AFPropertyListRequestOperation ()
@property (readwrite, nonatomic, retain) id responsePropertyList;
@property (readwrite, nonatomic) id responsePropertyList;
@property (readwrite, nonatomic, assign) NSPropertyListFormat propertyListFormat;
@property (readwrite, nonatomic, retain) NSError *propertyListError;
@property (readwrite, nonatomic) NSError *propertyListError;
@end
@implementation AFPropertyListRequestOperation
@ -43,11 +44,11 @@ static dispatch_queue_t property_list_request_operation_processing_queue() {
@synthesize propertyListFormat = _propertyListFormat;
@synthesize propertyListError = _propertyListError;
+ (AFPropertyListRequestOperation *)propertyListRequestOperationWithRequest:(NSURLRequest *)request
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, id propertyList))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id propertyList))failure
+ (instancetype)propertyListRequestOperationWithRequest:(NSURLRequest *)request
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, id propertyList))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id propertyList))failure
{
AFPropertyListRequestOperation *requestOperation = [[[self alloc] initWithRequest:request] autorelease];
AFPropertyListRequestOperation *requestOperation = [(AFPropertyListRequestOperation *)[self alloc] initWithRequest:request];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (success) {
success(operation.request, operation.response, responseObject);
@ -57,7 +58,7 @@ static dispatch_queue_t property_list_request_operation_processing_queue() {
failure(operation.request, operation.response, error, [(AFPropertyListRequestOperation *)operation responsePropertyList]);
}
}];
return requestOperation;
}
@ -66,17 +67,12 @@ static dispatch_queue_t property_list_request_operation_processing_queue() {
if (!self) {
return nil;
}
self.propertyListReadOptions = NSPropertyListImmutable;
return self;
}
- (void)dealloc {
[_responsePropertyList release];
[_propertyListError release];
[super dealloc];
}
- (id)responsePropertyList {
if (!_responsePropertyList && [self.responseData length] > 0 && [self isFinished]) {
@ -86,7 +82,7 @@ static dispatch_queue_t property_list_request_operation_processing_queue() {
self.propertyListFormat = format;
self.propertyListError = error;
}
return _responsePropertyList;
}
@ -111,37 +107,37 @@ static dispatch_queue_t property_list_request_operation_processing_queue() {
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"
self.completionBlock = ^ {
if ([self isCancelled]) {
return;
}
if (self.error) {
if (failure) {
dispatch_async(self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{
dispatch_async(self.failureCallbackQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
dispatch_async(property_list_request_operation_processing_queue(), ^(void) {
id propertyList = self.responsePropertyList;
if (self.propertyListError) {
if (failure) {
dispatch_async(self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{
dispatch_async(self.failureCallbackQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
if (success) {
dispatch_async(self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{
dispatch_async(self.successCallbackQueue ?: dispatch_get_main_queue(), ^{
success(self, propertyList);
});
}
}
}
});
}
};
};
#pragma clang diagnostic pop
}
@end

554
clients/ios/AFNetworking/AFURLConnectionOperation.h Normal file → Executable file
View file

@ -1,17 +1,17 @@
// AFURLConnectionOperation.h
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -22,12 +22,344 @@
#import <Foundation/Foundation.h>
#import <Availability.h>
/**
Indicates an error occured in AFNetworking.
`AFURLConnectionOperation` is a subclass of `NSOperation` that implements `NSURLConnection` delegate methods.
## Subclassing Notes
This is the base class of all network request operations. You may wish to create your own subclass in order to implement additional `NSURLConnection` delegate methods (see "`NSURLConnection` Delegate Methods" below), or to provide additional properties and/or class constructors.
If you are creating a subclass that communicates over the HTTP or HTTPS protocols, you may want to consider subclassing `AFHTTPRequestOperation` instead, as it supports specifying acceptable content types or status codes.
## NSURLConnection Delegate Methods
`AFURLConnectionOperation` implements the following `NSURLConnection` delegate methods:
- `connection:didReceiveResponse:`
- `connection:didReceiveData:`
- `connectionDidFinishLoading:`
- `connection:didFailWithError:`
- `connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:`
- `connection:willCacheResponse:`
- `connectionShouldUseCredentialStorage:`
- `connection:needNewBodyStream:`
- `connection:willSendRequestForAuthenticationChallenge:`
If any of these methods are overridden in a subclass, they _must_ call the `super` implementation first.
## Class Constructors
Class constructors, or methods that return an unowned instance, are the preferred way for subclasses to encapsulate any particular logic for handling the setup or parsing of response data. For instance, `AFJSONRequestOperation` provides `JSONRequestOperationWithRequest:success:failure:`, which takes block arguments, whose parameter on for a successful request is the JSON object initialized from the `response data`.
## Callbacks and Completion Blocks
The built-in `completionBlock` provided by `NSOperation` allows for custom behavior to be executed after the request finishes. It is a common pattern for class constructors in subclasses to take callback block parameters, and execute them conditionally in the body of its `completionBlock`. Make sure to handle cancelled operations appropriately when setting a `completionBlock` (i.e. returning early before parsing response data). See the implementation of any of the `AFHTTPRequestOperation` subclasses for an example of this.
Subclasses are strongly discouraged from overriding `setCompletionBlock:`, as `AFURLConnectionOperation`'s implementation includes a workaround to mitigate retain cycles, and what Apple rather ominously refers to as ["The Deallocation Problem"](http://developer.apple.com/library/ios/#technotes/tn2109/).
@discussion Error codes for AFNetworkingErrorDomain correspond to codes in NSURLErrorDomain.
## SSL Pinning
Relying on the CA trust model to validate SSL certificates exposes your app to security vulnerabilities, such as man-in-the-middle attacks. For applications that connect to known servers, SSL certificate pinning provides an increased level of security, by checking server certificate validity against those specified in the app bundle.
SSL with certificate pinning is strongly recommended for any application that transmits sensitive information to an external webservice.
When `defaultSSLPinningMode` is defined on `AFHTTPClient` and the Security framework is linked, connections will be validated on all matching certificates with a `.cer` extension in the bundle root.
## NSCoding & NSCopying Conformance
`AFURLConnectionOperation` conforms to the `NSCoding` and `NSCopying` protocols, allowing operations to be archived to disk, and copied in memory, respectively. However, because of the intrinsic limitations of capturing the exact state of an operation at a particular moment, there are some important caveats to keep in mind:
### NSCoding Caveats
- Encoded operations do not include any block or stream properties. Be sure to set `completionBlock`, `outputStream`, and any callback blocks as necessary when using `-initWithCoder:` or `NSKeyedUnarchiver`.
- Operations are paused on `encodeWithCoder:`. If the operation was encoded while paused or still executing, its archived state will return `YES` for `isReady`. Otherwise, the state of an operation when encoding will remain unchanged.
### NSCopying Caveats
- `-copy` and `-copyWithZone:` return a new operation with the `NSURLRequest` of the original. So rather than an exact copy of the operation at that particular instant, the copying mechanism returns a completely new instance, which can be useful for retrying operations.
- A copy of an operation will not include the `outputStream` of the original.
- Operation copies do not include `completionBlock`. `completionBlock` often strongly captures a reference to `self`, which would otherwise have the unintuitive side-effect of pointing to the _original_ operation when copied.
*/
typedef enum {
AFSSLPinningModeNone,
AFSSLPinningModePublicKey,
AFSSLPinningModeCertificate,
} AFURLConnectionOperationSSLPinningMode;
@interface AFURLConnectionOperation : NSOperation <NSURLConnectionDelegate,
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 50000) || \
(defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1080)
NSURLConnectionDataDelegate,
#endif
NSCoding, NSCopying>
///-------------------------------
/// @name Accessing Run Loop Modes
///-------------------------------
/**
The run loop modes in which the operation will run on the network thread. By default, this is a single-member set containing `NSRunLoopCommonModes`.
*/
@property (nonatomic, strong) NSSet *runLoopModes;
///-----------------------------------------
/// @name Getting URL Connection Information
///-----------------------------------------
/**
The request used by the operation's connection.
*/
@property (readonly, nonatomic, strong) NSURLRequest *request;
/**
The last response received by the operation's connection.
*/
@property (readonly, nonatomic, strong) NSURLResponse *response;
/**
The error, if any, that occurred in the lifecycle of the request.
*/
@property (readonly, nonatomic, strong) NSError *error;
/**
Whether the connection should accept an invalid SSL certificate.
If `_AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_` is set, this property defaults to `YES` for backwards compatibility. Otherwise, this property defaults to `NO`.
*/
@property (nonatomic, assign) BOOL allowsInvalidSSLCertificate;
///----------------------------
/// @name Getting Response Data
///----------------------------
/**
The data received during the request.
*/
@property (readonly, nonatomic, strong) NSData *responseData;
/**
The string representation of the response data.
*/
@property (readonly, nonatomic, copy) NSString *responseString;
/**
The string encoding of the response.
If the response does not specify a valid string encoding, `responseStringEncoding` will return `NSUTF8StringEncoding`.
*/
@property (readonly, nonatomic, assign) NSStringEncoding responseStringEncoding;
///-------------------------------
/// @name Managing URL Credentials
///-------------------------------
/**
Whether the URL connection should consult the credential storage for authenticating the connection. `YES` by default.
This is the value that is returned in the `NSURLConnectionDelegate` method `-connectionShouldUseCredentialStorage:`.
*/
@property (nonatomic, assign) BOOL shouldUseCredentialStorage;
/**
The credential used for authentication challenges in `-connection:didReceiveAuthenticationChallenge:`.
This will be overridden by any shared credentials that exist for the username or password of the request URL, if present.
*/
@property (nonatomic, strong) NSURLCredential *credential;
/**
The pinning mode which will be used for SSL connections. `AFSSLPinningModePublicKey` by default.
SSL Pinning requires that the Security framework is linked with the binary. See the "SSL Pinning" section in the `AFURLConnectionOperation`" header for more information.
*/
@property (nonatomic, assign) AFURLConnectionOperationSSLPinningMode SSLPinningMode;
///------------------------
/// @name Accessing Streams
///------------------------
/**
The input stream used to read data to be sent during the request.
This property acts as a proxy to the `HTTPBodyStream` property of `request`.
*/
@property (nonatomic, strong) NSInputStream *inputStream;
/**
The output stream that is used to write data received until the request is finished.
By default, data is accumulated into a buffer that is stored into `responseData` upon completion of the request. When `outputStream` is set, the data will not be accumulated into an internal buffer, and as a result, the `responseData` property of the completed request will be `nil`. The output stream will be scheduled in the network thread runloop upon being set.
*/
@property (nonatomic, strong) NSOutputStream *outputStream;
///---------------------------------------------
/// @name Managing Request Operation Information
///---------------------------------------------
/**
The user info dictionary for the receiver.
*/
@property (nonatomic, strong) NSDictionary *userInfo;
///------------------------------------------------------
/// @name Initializing an AFURLConnectionOperation Object
///------------------------------------------------------
/**
Initializes and returns a newly allocated operation object with a url connection configured with the specified url request.
This is the designated initializer.
@param urlRequest The request object to be used by the operation connection.
*/
- (id)initWithRequest:(NSURLRequest *)urlRequest;
///----------------------------------
/// @name Pausing / Resuming Requests
///----------------------------------
/**
Pauses the execution of the request operation.
A paused operation returns `NO` for `-isReady`, `-isExecuting`, and `-isFinished`. As such, it will remain in an `NSOperationQueue` until it is either cancelled or resumed. Pausing a finished, cancelled, or paused operation has no effect.
*/
- (void)pause;
/**
Whether the request operation is currently paused.
@return `YES` if the operation is currently paused, otherwise `NO`.
*/
- (BOOL)isPaused;
/**
Resumes the execution of the paused request operation.
Pause/Resume behavior varies depending on the underlying implementation for the operation class. In its base implementation, resuming a paused requests restarts the original request. However, since HTTP defines a specification for how to request a specific content range, `AFHTTPRequestOperation` will resume downloading the request from where it left off, instead of restarting the original request.
*/
- (void)resume;
///----------------------------------------------
/// @name Configuring Backgrounding Task Behavior
///----------------------------------------------
/**
Specifies that the operation should continue execution after the app has entered the background, and the expiration handler for that background task.
@param handler A handler to be called shortly before the applications remaining background time reaches 0. The handler is wrapped in a block that cancels the operation, and cleans up and marks the end of execution, unlike the `handler` parameter in `UIApplication -beginBackgroundTaskWithExpirationHandler:`, which expects this to be done in the handler itself. The handler is called synchronously on the main thread, thus blocking the applications suspension momentarily while the application is notified.
*/
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler;
#endif
///---------------------------------
/// @name Setting Progress Callbacks
///---------------------------------
/**
Sets a callback to be called when an undetermined number of bytes have been uploaded to the server.
@param block A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes three arguments: the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times, and will execute on the main thread.
*/
- (void)setUploadProgressBlock:(void (^)(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))block;
/**
Sets a callback to be called when an undetermined number of bytes have been downloaded from the server.
@param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times, and will execute on the main thread.
*/
- (void)setDownloadProgressBlock:(void (^)(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))block;
///-------------------------------------------------
/// @name Setting NSURLConnection Delegate Callbacks
///-------------------------------------------------
/**
Sets a block to be executed when the connection will authenticate a challenge in order to download its request, as handled by the `NSURLConnectionDelegate` method `connection:willSendRequestForAuthenticationChallenge:`.
@param block A block object to be executed when the connection will authenticate a challenge in order to download its request. The block has no return type and takes two arguments: the URL connection object, and the challenge that must be authenticated. This block must invoke one of the challenge-responder methods (NSURLAuthenticationChallengeSender protocol).
If `allowsInvalidSSLCertificate` is set to YES, `connection:willSendRequestForAuthenticationChallenge:` will attempt to have the challenge sender use credentials with invalid SSL certificates.
*/
- (void)setWillSendRequestForAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block;
/**
Sets a block to be executed when the server redirects the request from one URL to another URL, or when the request URL changed by the `NSURLProtocol` subclass handling the request in order to standardize its format, as handled by the `NSURLConnectionDelegate` method `connection:willSendRequest:redirectResponse:`.
@param block A block object to be executed when the request URL was changed. The block returns an `NSURLRequest` object, the URL request to redirect, and takes three arguments: the URL connection object, the the proposed redirected request, and the URL response that caused the redirect.
*/
- (void)setRedirectResponseBlock:(NSURLRequest * (^)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse))block;
/**
Sets a block to be executed to modify the response a connection will cache, if any, as handled by the `NSURLConnectionDelegate` method `connection:willCacheResponse:`.
@param block A block object to be executed to determine what response a connection will cache, if any. The block returns an `NSCachedURLResponse` object, the cached response to store in memory or `nil` to prevent the response from being cached, and takes two arguments: the URL connection object, and the cached response provided for the request.
*/
- (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block;
@end
///----------------
/// @name Constants
///----------------
/**
## SSL Pinning Options
The following constants are provided by `AFURLConnectionOperation` as possible SSL Pinning options.
enum {
AFSSLPinningModeNone,
AFSSLPinningModePublicKey,
AFSSLPinningModeCertificate,
}
`AFSSLPinningModeNone`
Do not pin SSL connections
`AFSSLPinningModePublicKey`
Pin SSL connections to certificate public key (SPKI).
`AFSSLPinningModeCertificate`
Pin SSL connections to exact certificate. This may cause problems when your certificate expires and needs re-issuance.
## User info dictionary keys
These keys may exist in the user info dictionary, in addition to those defined for NSError.
- `NSString * const AFNetworkingOperationFailingURLRequestErrorKey`
- `NSString * const AFNetworkingOperationFailingURLResponseErrorKey`
### Constants
`AFNetworkingOperationFailingURLRequestErrorKey`
The corresponding value is an `NSURLRequest` containing the request of the operation associated with an error. This key is only present in the `AFNetworkingErrorDomain`.
`AFNetworkingOperationFailingURLResponseErrorKey`
The corresponding value is an `NSURLResponse` containing the response of the operation associated with an error. This key is only present in the `AFNetworkingErrorDomain`.
## Error Domains
The following error domain is predefined.
- `NSString * const AFNetworkingErrorDomain`
### Constants
`AFNetworkingErrorDomain`
AFNetworking errors. Error codes for `AFNetworkingErrorDomain` correspond to codes in `NSURLErrorDomain`.
*/
extern NSString * const AFNetworkingErrorDomain;
extern NSString * const AFNetworkingOperationFailingURLRequestErrorKey;
extern NSString * const AFNetworkingOperationFailingURLResponseErrorKey;
///--------------------
/// @name Notifications
///--------------------
/**
Posted when an operation begins executing.
@ -38,215 +370,3 @@ extern NSString * const AFNetworkingOperationDidStartNotification;
Posted when an operation finishes.
*/
extern NSString * const AFNetworkingOperationDidFinishNotification;
/**
`AFURLConnectionOperation` is an `NSOperation` that implements NSURLConnection delegate methods.
## Subclassing Notes
This is the base class of all network request operations. You may wish to create your own subclass in order to implement additional `NSURLConnection` delegate methods (see "`NSURLConnection` Delegate Methods" below), or to provide additional properties and/or class constructors.
If you are creating a subclass that communicates over the HTTP or HTTPS protocols, you may want to consider subclassing `AFHTTPRequestOperation` instead, as it supports specifying acceptable content types or status codes.
## NSURLConnection Delegate Methods
`AFURLConnectionOperation` implements the following `NSURLConnection` delegate methods:
- `connection:didReceiveResponse:`
- `connection:didReceiveData:`
- `connectionDidFinishLoading:`
- `connection:didFailWithError:`
- `connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:`
- `connection:willCacheResponse:`
- `connection:canAuthenticateAgainstProtectionSpace:`
- `connection:didReceiveAuthenticationChallenge:`
If any of these methods are overriden in a subclass, they _must_ call the `super` implementation first.
## Class Constructors
Class constructors, or methods that return an unowned (zero retain count) instance, are the preferred way for subclasses to encapsulate any particular logic for handling the setup or parsing of response data. For instance, `AFJSONRequestOperation` provides `JSONRequestOperationWithRequest:success:failure:`, which takes block arguments, whose parameter on for a successful request is the JSON object initialized from the `response data`.
## Callbacks and Completion Blocks
The built-in `completionBlock` provided by `NSOperation` allows for custom behavior to be executed after the request finishes. It is a common pattern for class constructors in subclasses to take callback block parameters, and execute them conditionally in the body of its `completionBlock`. Make sure to handle cancelled operations appropriately when setting a `completionBlock` (e.g. returning early before parsing response data). See the implementation of any of the `AFHTTPRequestOperation` subclasses for an example of this.
@warning Subclasses are strongly discouraged from overriding `setCompletionBlock:`, as `AFURLConnectionOperation`'s implementation includes a workaround to mitigate retain cycles, and what Apple rather ominously refers to as "The Deallocation Problem" (See http://developer.apple.com/library/ios/technotes/tn2109/_index.html#//apple_ref/doc/uid/DTS40010274-CH1-SUBSECTION11)
@warning Attempting to load a `file://` URL in iOS 4 may result in an `NSInvalidArgumentException`, caused by the connection returning `NSURLResponse` rather than `NSHTTPURLResponse`, which is the behavior as of iOS 5.
*/
@interface AFURLConnectionOperation : NSOperation
///-------------------------------
/// @name Accessing Run Loop Modes
///-------------------------------
/**
The run loop modes in which the operation will run on the network thread. By default, this is a single-member set containing `NSRunLoopCommonModes`.
*/
@property (nonatomic, retain) NSSet *runLoopModes;
///-----------------------------------------
/// @name Getting URL Connection Information
///-----------------------------------------
/**
The request used by the operation's connection.
*/
@property (readonly, nonatomic, retain) NSURLRequest *request;
/**
The last response received by the operation's connection.
*/
@property (readonly, nonatomic, retain) NSURLResponse *response;
/**
The error, if any, that occured in the lifecycle of the request.
*/
@property (readonly, nonatomic, retain) NSError *error;
///----------------------------
/// @name Getting Response Data
///----------------------------
/**
The data received during the request.
*/
@property (readonly, nonatomic, retain) NSData *responseData;
/**
The string representation of the response data.
@discussion This method uses the string encoding of the response, or if UTF-8 if not specified, to construct a string from the response data.
*/
@property (readonly, nonatomic, copy) NSString *responseString;
///------------------------
/// @name Accessing Streams
///------------------------
/**
The input stream used to read data to be sent during the request.
@discussion This property acts as a proxy to the `HTTPBodyStream` property of `request`.
*/
@property (nonatomic, retain) NSInputStream *inputStream;
/**
The output stream that is used to write data received until the request is finished.
@discussion By default, data is accumulated into a buffer that is stored into `responseData` upon completion of the request. When `outputStream` is set, the data will not be accumulated into an internal buffer, and as a result, the `responseData` property of the completed request will be `nil`. The output stream will be scheduled in the network thread runloop upon being set.
*/
@property (nonatomic, retain) NSOutputStream *outputStream;
///------------------------------------------------------
/// @name Initializing an AFURLConnectionOperation Object
///------------------------------------------------------
/**
Initializes and returns a newly allocated operation object with a url connection configured with the specified url request.
@param urlRequest The request object to be used by the operation connection.
@discussion This is the designated initializer.
*/
- (id)initWithRequest:(NSURLRequest *)urlRequest;
///----------------------------------
/// @name Pausing / Resuming Requests
///----------------------------------
/**
Pauses the execution of the request operation.
@discussion A paused operation returns `NO` for `-isReady`, `-isExecuting`, and `-isFinished`. As such, it will remain in an `NSOperationQueue` until it is either cancelled or resumed. Pausing a finished or cancelled operation has no effect.
*/
- (void)pause;
/**
Whether the request operation is currently paused.
@return `YES` if the operation is currently paused, otherwise `NO`.
*/
- (BOOL)isPaused;
/**
Resumes the execution of the paused request operation.
@discussion Pause/Resume behavior varies depending on the underlying implementation for the operation class. In its base implementation, resuming a paused requests restarts the original request. However, since HTTP defines a specification for how to request a specific content range, `AFHTTPRequestOperation` will resume downloading the request from where it left off, instead of restarting the original request.
*/
- (void)resume;
///----------------------------------------------
/// @name Configuring Backgrounding Task Behavior
///----------------------------------------------
/**
Specifies that the operation should continue execution after the app has entered the background, and the expiration handler for that background task.
@param handler A handler to be called shortly before the applications remaining background time reaches 0. The handler is wrapped in a block that cancels the operation, and cleans up and marks the end of execution, unlike the `handler` parameter in `UIApplication -beginBackgroundTaskWithExpirationHandler:`, which expects this to be done in the handler itself. The handler is called synchronously on the main thread, thus blocking the applications suspension momentarily while the application is notified.
*/
#if __IPHONE_OS_VERSION_MIN_REQUIRED
- (void)setShouldExecuteAsBackgroundTaskWithExpirationHandler:(void (^)(void))handler;
#endif
///---------------------------------
/// @name Setting Progress Callbacks
///---------------------------------
/**
Sets a callback to be called when an undetermined number of bytes have been uploaded to the server.
@param block A block object to be called when an undetermined number of bytes have been uploaded to the server. This block has no return value and takes three arguments: the number of bytes written since the last time the upload progress block was called, the total bytes written, and the total bytes expected to be written during the request, as initially determined by the length of the HTTP body. This block may be called multiple times.
@see setDownloadProgressBlock
*/
- (void)setUploadProgressBlock:(void (^)(NSInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite))block;
/**
Sets a callback to be called when an undetermined number of bytes have been downloaded from the server.
@param block A block object to be called when an undetermined number of bytes have been downloaded from the server. This block has no return value and takes three arguments: the number of bytes read since the last time the download progress block was called, the total bytes read, and the total bytes expected to be read during the request, as initially determined by the expected content size of the `NSHTTPURLResponse` object. This block may be called multiple times.
@see setUploadProgressBlock
*/
- (void)setDownloadProgressBlock:(void (^)(NSInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead))block;
///-------------------------------------------------
/// @name Setting NSURLConnection Delegate Callbacks
///-------------------------------------------------
/**
Sets a block to be executed to determine whether the connection should be able to respond to a protection space's form of authentication, as handled by the `NSURLConnectionDelegate` method `connection:canAuthenticateAgainstProtectionSpace:`.
@param block A block object to be executed to determine whether the connection should be able to respond to a protection space's form of authentication. The block has a `BOOL` return type and takes two arguments: the URL connection object, and the protection space to authenticate against.
@discussion If `_AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_` is defined, `connection:canAuthenticateAgainstProtectionSpace:` will accept invalid SSL certificates, returning `YES` if the protection space authentication method is `NSURLAuthenticationMethodServerTrust`.
*/
- (void)setAuthenticationAgainstProtectionSpaceBlock:(BOOL (^)(NSURLConnection *connection, NSURLProtectionSpace *protectionSpace))block;
/**
Sets a block to be executed when the connection must authenticate a challenge in order to download its request, as handled by the `NSURLConnectionDelegate` method `connection:didReceiveAuthenticationChallenge:`.
@param block A block object to be executed when the connection must authenticate a challenge in order to download its request. The block has no return type and takes two arguments: the URL connection object, and the challenge that must be authenticated.
@discussion If `_AFNETWORKING_ALLOW_INVALID_SSL_CERTIFICATES_` is defined, `connection:didReceiveAuthenticationChallenge:` will attempt to have the challenge sender use credentials with invalid SSL certificates.
*/
- (void)setAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block;
/**
Sets a block to be executed when the server redirects the request from one URL to another URL, or when the request URL changed by the `NSURLProtocol` subclass handling the request in order to standardize its format, as handled by the `NSURLConnectionDelegate` method `connection:willSendRequest:redirectResponse:`.
@param block A block object to be executed when the request URL was changed. The block returns an `NSURLRequest` object, the URL request to redirect, and takes three arguments: the URL connection object, the the proposed redirected request, and the URL response that caused the redirect.
*/
- (void)setRedirectResponseBlock:(NSURLRequest * (^)(NSURLConnection *connection, NSURLRequest *request, NSURLResponse *redirectResponse))block;
/**
Sets a block to be executed to modify the response a connection will cache, if any, as handled by the `NSURLConnectionDelegate` method `connection:willCacheResponse:`.
@param block A block object to be executed to determine what response a connection will cache, if any. The block returns an `NSCachedURLResponse` object, the cached response to store in memory or `nil` to prevent the response from being cached, and takes two arguments: the URL connection object, and the cached response provided for the request.
*/
- (void)setCacheResponseBlock:(NSCachedURLResponse * (^)(NSURLConnection *connection, NSCachedURLResponse *cachedResponse))block;
@end

677
clients/ios/AFNetworking/AFURLConnectionOperation.m Normal file → Executable file

File diff suppressed because it is too large Load diff

46
clients/ios/AFNetworking/AFXMLRequestOperation.h Normal file → Executable file
View file

@ -1,17 +1,17 @@
// AFXMLRequestOperation.h
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -27,16 +27,16 @@
/**
`AFXMLRequestOperation` is a subclass of `AFHTTPRequestOperation` for downloading and working with XML response data.
## Acceptable Content Types
By default, `AFXMLRequestOperation` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types:
- `application/xml`
- `text/xml`
## Use With AFHTTPClient
When `AFXMLRequestOperation` is registered with `AFHTTPClient`, the response object in the success callback of `HTTPRequestOperationWithRequest:success:failure:` will be an instance of `NSXMLParser`. On platforms that support `NSXMLDocument`, you have the option to ignore the response object, and simply use the `responseXMLDocument` property of the operation argument of the callback.
*/
@interface AFXMLRequestOperation : AFHTTPRequestOperation
@ -48,42 +48,42 @@
/**
An `NSXMLParser` object constructed from the response data.
*/
@property (readonly, nonatomic, retain) NSXMLParser *responseXMLParser;
@property (readonly, nonatomic, strong) NSXMLParser *responseXMLParser;
#if __MAC_OS_X_VERSION_MIN_REQUIRED
#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
/**
An `NSXMLDocument` object constructed from the response data. If an error occurs while parsing, `nil` will be returned, and the `error` property will be set to the error.
*/
@property (readonly, nonatomic, retain) NSXMLDocument *responseXMLDocument;
@property (readonly, nonatomic, strong) NSXMLDocument *responseXMLDocument;
#endif
/**
Creates and returns an `AFXMLRequestOperation` object and sets the specified success and failure callbacks.
@param urlRequest The request object to be loaded asynchronously during execution of the operation
@param success A block object to be executed when the operation finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the XML parser constructed with the response data of request.
@param failure A block object to be executed when the operation finishes unsuccessfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error describing the network error that occurred.
@return A new XML request operation
*/
+ (AFXMLRequestOperation *)XMLParserRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSXMLParser *XMLParser))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSXMLParser *XMLParse))failure;
+ (instancetype)XMLParserRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSXMLParser *XMLParser))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSXMLParser *XMLParser))failure;
#if __MAC_OS_X_VERSION_MIN_REQUIRED
#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
/**
Creates and returns an `AFXMLRequestOperation` object and sets the specified success and failure callbacks.
@param urlRequest The request object to be loaded asynchronously during execution of the operation
@param success A block object to be executed when the operation finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the XML document created from the response data of request.
@param failure A block object to be executed when the operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the resonse data as XML. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error describing the network or parsing error that occurred.
@param failure A block object to be executed when the operation finishes unsuccessfully, or that finishes successfully, but encountered an error while parsing the response data as XML. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error describing the network or parsing error that occurred.
@return A new XML request operation
*/
+ (AFXMLRequestOperation *)XMLDocumentRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSXMLDocument *document))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSXMLDocument *document))failure;
+ (instancetype)XMLDocumentRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSXMLDocument *document))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSXMLDocument *document))failure;
#endif
@end

92
clients/ios/AFNetworking/AFXMLRequestOperation.m Normal file → Executable file
View file

@ -1,17 +1,17 @@
// AFXMLRequestOperation.m
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -24,35 +24,36 @@
#include <Availability.h>
static dispatch_queue_t af_xml_request_operation_processing_queue;
static dispatch_queue_t xml_request_operation_processing_queue() {
if (af_xml_request_operation_processing_queue == NULL) {
af_xml_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.xml-request.processing", 0);
}
static dispatch_queue_t af_xml_request_operation_processing_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_xml_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.xml-request.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_xml_request_operation_processing_queue;
}
@interface AFXMLRequestOperation ()
@property (readwrite, nonatomic, retain) NSXMLParser *responseXMLParser;
#if __MAC_OS_X_VERSION_MIN_REQUIRED
@property (readwrite, nonatomic, retain) NSXMLDocument *responseXMLDocument;
@property (readwrite, nonatomic, strong) NSXMLParser *responseXMLParser;
#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
@property (readwrite, nonatomic, strong) NSXMLDocument *responseXMLDocument;
#endif
@property (readwrite, nonatomic, retain) NSError *XMLError;
@property (readwrite, nonatomic, strong) NSError *XMLError;
@end
@implementation AFXMLRequestOperation
@synthesize responseXMLParser = _responseXMLParser;
#if __MAC_OS_X_VERSION_MIN_REQUIRED
#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
@synthesize responseXMLDocument = _responseXMLDocument;
#endif
@synthesize XMLError = _XMLError;
+ (AFXMLRequestOperation *)XMLParserRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSXMLParser *XMLParser))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSXMLParser *XMLParser))failure
+ (instancetype)XMLParserRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSXMLParser *XMLParser))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSXMLParser *XMLParser))failure
{
AFXMLRequestOperation *requestOperation = [[[self alloc] initWithRequest:urlRequest] autorelease];
AFXMLRequestOperation *requestOperation = [(AFXMLRequestOperation *)[self alloc] initWithRequest:urlRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if (success) {
success(operation.request, operation.response, responseObject);
@ -62,19 +63,19 @@ static dispatch_queue_t xml_request_operation_processing_queue() {
failure(operation.request, operation.response, error, [(AFXMLRequestOperation *)operation responseXMLParser]);
}
}];
return requestOperation;
}
#if __MAC_OS_X_VERSION_MIN_REQUIRED
+ (AFXMLRequestOperation *)XMLDocumentRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSXMLDocument *document))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSXMLDocument *document))failure
#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
+ (instancetype)XMLDocumentRequestOperationWithRequest:(NSURLRequest *)urlRequest
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSXMLDocument *document))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, NSXMLDocument *document))failure
{
AFXMLRequestOperation *requestOperation = [[[self alloc] initWithRequest:urlRequest] autorelease];
AFXMLRequestOperation *requestOperation = [[self alloc] initWithRequest:urlRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, __unused id responseObject) {
if (success) {
NSXMLDocument *XMLDocument = [(AFXMLRequestOperation *)operation responseXMLDocument];
NSXMLDocument *XMLDocument = [(AFXMLRequestOperation *)operation responseXMLDocument];
success(operation.request, operation.response, XMLDocument);
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
@ -83,39 +84,28 @@ static dispatch_queue_t xml_request_operation_processing_queue() {
failure(operation.request, operation.response, error, XMLDocument);
}
}];
return requestOperation;
}
#endif
- (void)dealloc {
[_responseXMLParser release];
#if __MAC_OS_X_VERSION_MIN_REQUIRED
[_responseXMLDocument release];
#endif
[_XMLError release];
[super dealloc];
}
- (NSXMLParser *)responseXMLParser {
if (!_responseXMLParser && [self.responseData length] > 0 && [self isFinished]) {
self.responseXMLParser = [[[NSXMLParser alloc] initWithData:self.responseData] autorelease];
self.responseXMLParser = [[NSXMLParser alloc] initWithData:self.responseData];
}
return _responseXMLParser;
}
#if __MAC_OS_X_VERSION_MIN_REQUIRED
#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
- (NSXMLDocument *)responseXMLDocument {
if (!_responseXMLDocument && [self.responseData length] > 0 && [self isFinished]) {
NSError *error = nil;
self.responseXMLDocument = [[[NSXMLDocument alloc] initWithData:self.responseData options:0 error:&error] autorelease];
self.responseXMLDocument = [[NSXMLDocument alloc] initWithData:self.responseData options:0 error:&error];
self.XMLError = error;
}
return _responseXMLDocument;
}
#endif
@ -132,7 +122,7 @@ static dispatch_queue_t xml_request_operation_processing_queue() {
- (void)cancel {
[super cancel];
self.responseXMLParser.delegate = nil;
}
@ -149,29 +139,29 @@ static dispatch_queue_t xml_request_operation_processing_queue() {
- (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
#pragma clang diagnostic ignored "-Wgnu"
self.completionBlock = ^ {
if ([self isCancelled]) {
return;
}
dispatch_async(xml_request_operation_processing_queue(), ^(void) {
NSXMLParser *XMLParser = self.responseXMLParser;
if (self.error) {
if (failure) {
dispatch_async(self.failureCallbackQueue ? self.failureCallbackQueue : dispatch_get_main_queue(), ^{
dispatch_async(self.failureCallbackQueue ?: dispatch_get_main_queue(), ^{
failure(self, self.error);
});
}
} else {
if (success) {
dispatch_async(self.successCallbackQueue ? self.successCallbackQueue : dispatch_get_main_queue(), ^{
dispatch_async(self.successCallbackQueue ?: dispatch_get_main_queue(), ^{
success(self, XMLParser);
});
}
}
}
});
};
};
#pragma clang diagnostic pop
}
@end

28
clients/ios/AFNetworking/UIImageView+AFNetworking.h Normal file → Executable file
View file

@ -1,17 +1,17 @@
// UIImageView+AFNetworking.h
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -35,34 +35,36 @@
/**
Creates and enqueues an image request operation, which asynchronously downloads the image from the specified URL, and sets it the request is finished. Any previous image request for the receiver will be cancelled. If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished.
@discussion By default, URL requests have a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set to use HTTP pipelining, and not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:`
By default, URL requests have a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:`
@param url The URL used for the image request.
*/
- (void)setImageWithURL:(NSURL *)url;
/**
Creates and enqueues an image request operation, which asynchronously downloads the image from the specified URL. Any previous image request for the receiver will be cancelled. If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished.
By default, URL requests have a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:`
@param url The URL used for the image request.
@param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes.
@discussion By default, URL requests have a cache policy of `NSURLCacheStorageAllowed` and a timeout interval of 30 seconds, and are set to use HTTP pipelining, and not handle cookies. To configure URL requests differently, use `setImageWithURLRequest:placeholderImage:success:failure:`
*/
- (void)setImageWithURL:(NSURL *)url
*/
- (void)setImageWithURL:(NSURL *)url
placeholderImage:(UIImage *)placeholderImage;
/**
Creates and enqueues an image request operation, which asynchronously downloads the image with the specified URL request object. Any previous image request for the receiver will be cancelled. If the image is cached locally, the image is set immediately, otherwise the specified placeholder image will be set immediately, and then the remote image will be set once the request is finished.
If a success block is specified, it is the responsibility of the block to set the image of the image view before returning. If no success block is specified, the default behavior of setting the image with `self.image = image` is executed.
@param urlRequest The URL request used for the image request.
@param placeholderImage The image to be set initially, until the image request finishes. If `nil`, the image view will not change its image until the image request finishes.
@param success A block to be executed when the image request operation finishes successfully, with a status code in the 2xx range, and with an acceptable content type (e.g. `image/png`). This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the image created from the response data of request. If the image was returned from cache, the request and response parameters will be `nil`.
@param failure A block object to be executed when the image request operation finishes unsuccessfully, or that finishes successfully. This block has no return value and takes three arguments: the request sent from the client, the response received from the server, and the error object describing the network or parsing error that occurred.
*/
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
placeholderImage:(UIImage *)placeholderImage
*/
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
placeholderImage:(UIImage *)placeholderImage
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure;

92
clients/ios/AFNetworking/UIImageView+AFNetworking.m Normal file → Executable file
View file

@ -1,17 +1,17 @@
// UIImageView+AFNetworking.m
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -23,7 +23,7 @@
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#if __IPHONE_OS_VERSION_MIN_REQUIRED
#if defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
#import "UIImageView+AFNetworking.h"
@interface AFImageCache : NSCache
@ -37,7 +37,7 @@
static char kAFImageRequestOperationObjectKey;
@interface UIImageView (_AFNetworking)
@property (readwrite, nonatomic, retain, setter = af_setImageRequestOperation:) AFImageRequestOperation *af_imageRequestOperation;
@property (readwrite, nonatomic, strong, setter = af_setImageRequestOperation:) AFImageRequestOperation *af_imageRequestOperation;
@end
@implementation UIImageView (_AFNetworking)
@ -58,12 +58,12 @@ static char kAFImageRequestOperationObjectKey;
+ (NSOperationQueue *)af_sharedImageRequestOperationQueue {
static NSOperationQueue *_af_imageRequestOperationQueue = nil;
if (!_af_imageRequestOperationQueue) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_af_imageRequestOperationQueue = [[NSOperationQueue alloc] init];
[_af_imageRequestOperationQueue setMaxConcurrentOperationCount:8];
}
[_af_imageRequestOperationQueue setMaxConcurrentOperationCount:NSOperationQueueDefaultMaxConcurrentOperationCount];
});
return _af_imageRequestOperationQueue;
}
@ -73,7 +73,7 @@ static char kAFImageRequestOperationObjectKey;
dispatch_once(&oncePredicate, ^{
_af_imageCache = [[AFImageCache alloc] init];
});
return _af_imageCache;
}
@ -83,61 +83,65 @@ static char kAFImageRequestOperationObjectKey;
[self setImageWithURL:url placeholderImage:nil];
}
- (void)setImageWithURL:(NSURL *)url
- (void)setImageWithURL:(NSURL *)url
placeholderImage:(UIImage *)placeholderImage
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:30.0];
[request setHTTPShouldHandleCookies:NO];
[request setHTTPShouldUsePipelining:YES];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
[request addValue:@"image/*" forHTTPHeaderField:@"Accept"];
[self setImageWithURLRequest:request placeholderImage:placeholderImage success:nil failure:nil];
}
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
placeholderImage:(UIImage *)placeholderImage
- (void)setImageWithURLRequest:(NSURLRequest *)urlRequest
placeholderImage:(UIImage *)placeholderImage
success:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image))success
failure:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error))failure
{
[self cancelImageRequestOperation];
UIImage *cachedImage = [[[self class] af_sharedImageCache] cachedImageForRequest:urlRequest];
if (cachedImage) {
self.image = cachedImage;
self.af_imageRequestOperation = nil;
if (success) {
success(nil, nil, cachedImage);
} else {
self.image = cachedImage;
}
} else {
self.image = placeholderImage;
AFImageRequestOperation *requestOperation = [[[AFImageRequestOperation alloc] initWithRequest:urlRequest] autorelease];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if ([[urlRequest URL] isEqual:[[self.af_imageRequestOperation request] URL]]) {
self.image = responseObject;
self.af_imageRequestOperation = nil;
}
if (success) {
success(operation.request, operation.response, responseObject);
self.af_imageRequestOperation = nil;
} else {
if (placeholderImage) {
self.image = placeholderImage;
}
AFImageRequestOperation *requestOperation = [[AFImageRequestOperation alloc] initWithRequest:urlRequest];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
if ([urlRequest isEqual:[self.af_imageRequestOperation request]]) {
if (success) {
success(operation.request, operation.response, responseObject);
} else if (responseObject) {
self.image = responseObject;
}
if (self.af_imageRequestOperation == operation) {
self.af_imageRequestOperation = nil;
}
}
[[[self class] af_sharedImageCache] cacheImage:responseObject forRequest:urlRequest];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if ([[urlRequest URL] isEqual:[[self.af_imageRequestOperation request] URL]]) {
self.af_imageRequestOperation = nil;
}
if ([urlRequest isEqual:[self.af_imageRequestOperation request]]) {
if (failure) {
failure(operation.request, operation.response, error);
}
if (failure) {
failure(operation.request, operation.response, error);
if (self.af_imageRequestOperation == operation) {
self.af_imageRequestOperation = nil;
}
}
}];
self.af_imageRequestOperation = requestOperation;
[[[self class] af_sharedImageRequestOperationQueue] addOperation:self.af_imageRequestOperation];
}
}
@ -165,7 +169,7 @@ static inline NSString * AFImageCacheKeyFromURLRequest(NSURLRequest *request) {
default:
break;
}
return [self objectForKey:AFImageCacheKeyFromURLRequest(request)];
}

View file

@ -4562,7 +4562,8 @@ static NSOperationQueue *sharedQueue = nil;
for (NSNumber *bytes in bandwidthUsageTracker) {
totalBytes += [bytes unsignedLongValue];
}
averageBandwidthUsedPerSecond = totalBytes/measurements;
if (measurements)
averageBandwidthUsedPerSecond = totalBytes/measurements;
}
+ (unsigned long)averageBandwidthUsedPerSecond
@ -4860,7 +4861,7 @@ static NSOperationQueue *sharedQueue = nil;
// RFC 2612 says max-age must override any Expires header
if (maxAge) {
return [[NSDate date] addTimeInterval:maxAge];
return [[NSDate date] dateByAddingTimeInterval:maxAge];
} else {
NSString *expires = [responseHeaders objectForKey:@"Expires"];
if (expires) {

View file

@ -24,6 +24,7 @@
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
activityLabel = nil;
faviconView = nil;
self.separatorInset = UIEdgeInsetsMake(0, 90, 0, 0);
// create favicon and label in view
UIImageView *favicon = [[UIImageView alloc] initWithFrame:CGRectZero];
@ -34,6 +35,7 @@
activity.backgroundColor = [UIColor whiteColor];
self.activityLabel = activity;
[self.contentView addSubview:activity];
topMargin = 15;
bottomMargin = 15;
@ -50,21 +52,21 @@
[super layoutSubviews];
// determine outer bounds
CGRect contentRect = self.contentView.bounds;
[self.activityLabel sizeToFit];
CGRect contentRect = self.frame;
CGRect labelFrame = self.activityLabel.frame;
// position label to bounds
CGRect labelRect = contentRect;
labelRect.origin.x = labelRect.origin.x + leftMargin + avatarSize + leftMargin;
labelRect.origin.y = labelRect.origin.y + topMargin - 1;
labelRect.size.width = contentRect.size.width - leftMargin - avatarSize - leftMargin - rightMargin;
labelRect.size.height = contentRect.size.height - topMargin - bottomMargin;
self.activityLabel.frame = labelRect;
[self.activityLabel sizeToFit];
labelFrame.origin.x = leftMargin*2 + avatarSize;
labelFrame.origin.y = topMargin - 1;
labelFrame.size.width = contentRect.size.width - leftMargin - avatarSize - leftMargin - rightMargin - 20;
labelFrame.size.height = contentRect.size.height - topMargin - bottomMargin;
self.activityLabel.frame = labelFrame;
}
- (int)setActivity:(NSDictionary *)activity withUserProfile:(NSDictionary *)userProfile withWidth:(int)width {
// must set the height again for dynamic height in heightForRowAtIndexPath in
CGRect activityLabelRect = self.activityLabel.bounds;
CGRect activityLabelRect = self.activityLabel.frame;
activityLabelRect.size.width = width - leftMargin - avatarSize - leftMargin - rightMargin;
self.activityLabel.frame = activityLabelRect;
@ -163,13 +165,12 @@
[attrStr addAttributes:@{NSForegroundColorAttributeName:UIColorFromRGB(0x666666)} range:[txtWithTime rangeOfString:comment]];
[attrStr addAttributes:@{NSForegroundColorAttributeName:UIColorFromRGB(0x999999)} range:[txtWithTime rangeOfString:time]];
[attrStr addAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"Helvetica" size:11]} range:[txtWithTime rangeOfString:time]];
NSMutableParagraphStyle* style= [NSMutableParagraphStyle new];
style.lineBreakMode = NSLineBreakByWordWrapping;
[attrStr addAttributes:@{NSParagraphStyleAttributeName: style} range:NSMakeRange(0, [txtWithTime length])];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
[attrStr addAttributes:@{NSParagraphStyleAttributeName: paragraphStyle} range:NSMakeRange(0, [txtWithTime length])];
NSRange commentRange = [txtWithTime rangeOfString:comment];
if (commentRange.location != NSNotFound) {
NSLog(@"Spacing: %@", comment);
commentRange.location -= 2;
commentRange.length = 1;
if ([[txtWithTime substringWithRange:commentRange] isEqualToString:@" "]) {

View file

@ -167,12 +167,12 @@
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int activitesCount = [appDelegate.userActivitiesArray count];
NSInteger activitesCount = [appDelegate.userActivitiesArray count];
return activitesCount + 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
int activitiesCount = [appDelegate.userActivitiesArray count];
NSInteger activitiesCount = [appDelegate.userActivitiesArray count];
int minimumHeight;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
minimumHeight = MINIMUM_ACTIVITY_HEIGHT_IPAD;
@ -193,9 +193,9 @@
NSMutableDictionary *userProfile = [appDelegate.dictSocialProfile mutableCopy];
[userProfile setValue:@"You" forKey:@"username"];
int height = [activityCell setActivity:[appDelegate.userActivitiesArray
objectAtIndex:(indexPath.row)]
NSDictionary *activity = [appDelegate.userActivitiesArray
objectAtIndex:(indexPath.row)];
int height = [activityCell setActivity:activity
withUserProfile:userProfile
withWidth:self.frame.size.width - 20];
return height;
@ -220,13 +220,17 @@
// add in loading cell
return [self makeLoadingCell];
} else {
NSMutableDictionary *userProfile = [appDelegate.dictSocialProfile mutableCopy];
[userProfile setValue:@"You" forKey:@"username"];
NSDictionary *activitiy = [appDelegate.userActivitiesArray
NSDictionary *activity = [appDelegate.userActivitiesArray
objectAtIndex:(indexPath.row)];
NSString *category = [activitiy objectForKey:@"category"];
[cell setActivity:activity
withUserProfile:userProfile
withWidth:self.frame.size.width - 20];
NSString *category = [activity objectForKey:@"category"];
if ([category isEqualToString:@"follow"]) {
cell.accessoryType = UITableViewCellAccessoryNone;
} else if ([category isEqualToString:@"signup"]){
@ -239,17 +243,13 @@
UIView *myBackView = [[UIView alloc] initWithFrame:self.frame];
myBackView.backgroundColor = UIColorFromRGB(NEWSBLUR_HIGHLIGHT_COLOR);
cell.selectedBackgroundView = myBackView;
// update the cell information
[cell setActivity: activitiy
withUserProfile:userProfile
withWidth:self.frame.size.width - 20];
}
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
int activitiesCount = [appDelegate.userActivitiesArray count];
NSInteger activitiesCount = [appDelegate.userActivitiesArray count];
if (indexPath.row < activitiesCount) {
NSDictionary *activity = [appDelegate.userActivitiesArray objectAtIndex:indexPath.row];
NSString *category = [activity objectForKey:@"category"];

View file

@ -64,13 +64,16 @@ static UIFont *indicatorFont = nil;
if (highlighted) {
textColor = UIColorFromRGB(0x686868); //0x686868
}
[textColor set];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
paragraphStyle.alignment = NSTextAlignmentLeft;
[self.siteTitle
[self.siteTitle
drawInRect:CGRectMake(leftMargin, 6, rect.size.width - rightMargin, 21)
withFont:font
lineBreakMode:NSLineBreakByTruncatingTail
alignment:NSTextAlignmentLeft];
withAttributes:@{NSFontAttributeName: font,
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: paragraphStyle}];
textColor = UIColorFromRGB(0x333333);
if (highlighted) {
@ -89,13 +92,13 @@ static UIFont *indicatorFont = nil;
if (highlighted) {
textColor = UIColorFromRGB(0x686868);
}
[textColor set];
paragraphStyle.alignment = NSTextAlignmentRight;
[self.siteSubscribers
drawInRect:CGRectMake(leftMargin + (rect.size.width - rightMargin) / 2 - 10, 42 + adjustForSocial, (rect.size.width - rightMargin) / 2 + 10, 15.0)
withFont:font
lineBreakMode:NSLineBreakByTruncatingTail
alignment:NSTextAlignmentRight];
withAttributes:@{NSFontAttributeName: font,
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: paragraphStyle}];
// feed bar
CGContextSetStrokeColor(context, CGColorGetComponents([self.feedColorBar CGColor]));

View file

@ -53,18 +53,24 @@
}
- (void)viewDidLoad {
UIImageView *folderImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"g_icn_folder.png"]];
folderImage.frame = CGRectMake(0, 0, 16, 16);
UIImageView *folderImage = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:@"g_icn_folder.png"]];
folderImage.frame = CGRectMake(0, 0, 24, 16);
[folderImage setContentMode:UIViewContentModeRight];
[inFolderInput setLeftView:folderImage];
[inFolderInput setLeftViewMode:UITextFieldViewModeAlways];
UIImageView *folderImage2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"g_icn_folder_rss.png"]];
folderImage2.frame = CGRectMake(0, 0, 16, 16);
UIImageView *folderImage2 = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:@"g_icn_folder_rss.png"]];
folderImage2.frame = CGRectMake(0, 0, 24, 16);
[folderImage2 setContentMode:UIViewContentModeRight];
[addFolderInput setLeftView:folderImage2];
[addFolderInput setLeftViewMode:UITextFieldViewModeAlways];
UIImageView *urlImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"world.png"]];
urlImage.frame = CGRectMake(0, 0, 16, 16);
UIImageView *urlImage = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:@"world.png"]];
urlImage.frame = CGRectMake(0, 0, 24, 16);
[urlImage setContentMode:UIViewContentModeRight];
[siteAddressInput setLeftView:urlImage];
[siteAddressInput setLeftViewMode:UITextFieldViewModeAlways];
@ -185,7 +191,7 @@
return;
}
int periodLoc = [phrase rangeOfString:@"."].location;
NSInteger periodLoc = [phrase rangeOfString:@"."].location;
if (periodLoc != NSNotFound && siteAddressInput.returnKeyType != UIReturnKeyDone) {
// URL
[siteAddressInput setReturnKeyType:UIReturnKeyDone];
@ -302,11 +308,11 @@
- (NSString *)extractParentFolder {
NSString *parent_folder = [inFolderInput text];
int folder_loc = [parent_folder rangeOfString:@" - " options:NSBackwardsSearch].location;
NSInteger folder_loc = [parent_folder rangeOfString:@" - " options:NSBackwardsSearch].location;
if ([parent_folder length] && folder_loc != NSNotFound) {
parent_folder = [parent_folder substringFromIndex:(folder_loc + 3)];
}
int top_level_loc = [parent_folder rangeOfString:@" Top Level " options:NSBackwardsSearch].location;
NSInteger top_level_loc = [parent_folder rangeOfString:@" Top Level " options:NSBackwardsSearch].location;
if (parent_folder.length && top_level_loc != NSNotFound) {
parent_folder = @"";
}
@ -425,7 +431,7 @@ numberOfRowsInComponent:(NSInteger)component {
#pragma mark Autocomplete sites
- (int)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [autocompleteResults count];
}

View file

@ -66,7 +66,7 @@
action: @selector(doCancelButton)];
self.navigationItem.leftBarButtonItem = cancelButton;
self.view.frame = CGRectMake(0, 0, 320, 416);
self.contentSizeForViewInPopover = self.view.frame.size;
self.preferredContentSize = self.view.frame.size;
}
}

View file

@ -20,3 +20,4 @@
- (void)informLoadingMessage:(NSString *)message;
@end

View file

@ -19,7 +19,7 @@
ActivityModule *activitiesModule;
UIWebView *feedbackWebView;
UIToolbar *toolbar;
UIToolbar *topToolbar;
UINavigationBar *topToolbar;
UISegmentedControl *segmentedButton;
}
@ -28,7 +28,7 @@
@property (nonatomic) IBOutlet ActivityModule *activitiesModule;
@property (nonatomic) IBOutlet UIWebView *feedbackWebView;
@property (nonatomic) IBOutlet UIToolbar *topToolbar;
@property (nonatomic) IBOutlet UINavigationBar *topToolbar;
@property (nonatomic) IBOutlet UIToolbar *toolbar;
@property (nonatomic) IBOutlet UISegmentedControl *segmentedButton;

View file

@ -42,11 +42,14 @@
self.feedbackWebView.delegate = self;
self.segmentedButton.selectedSegmentIndex = 0;
self.topToolbar.tintColor = [UIColor colorWithRed:0.16f green:0.36f blue:0.46 alpha:0.9];
// preload feedback
self.feedbackWebView.scalesPageToFit = YES;
[self.segmentedButton
setTitleTextAttributes:@{NSFontAttributeName:
[UIFont fontWithName:@"Helvetica-Bold" size:11.0f]}
forState:UIControlStateNormal];
NSString *urlAddress = FEEDBACK_URL;
//Create a URL object.
NSURL *url = [NSURL URLWithString:urlAddress];
@ -54,7 +57,10 @@
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
//Load the request in the UIWebView.
[self.feedbackWebView loadRequest:requestObj];
CGRect topToolbarFrame = self.topToolbar.frame;
topToolbarFrame.size.height += 20;
self.topToolbar.frame = topToolbarFrame;
}
- (void)viewDidUnload {
@ -86,7 +92,7 @@
# pragma mark Navigation
- (IBAction)tapSegmentedButton:(id)sender {
int selectedSegmentIndex = [self.segmentedButton selectedSegmentIndex];
NSInteger selectedSegmentIndex = [self.segmentedButton selectedSegmentIndex];
if (selectedSegmentIndex == 0) {
self.interactionsModule.hidden = NO;

View file

@ -51,11 +51,23 @@
- (void)viewWillAppear:(BOOL)animated {
[self.menuTableView reloadData];
[orderSegmentedControl
setTitleTextAttributes:@{NSFontAttributeName:
[UIFont fontWithName:@"Helvetica-Bold" size:11.0f]}
forState:UIControlStateNormal];
[orderSegmentedControl setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:0];
[orderSegmentedControl setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:1];
[orderSegmentedControl setSelectedSegmentIndex:0];
if ([appDelegate.activeOrder isEqualToString:@"oldest"]) {
[orderSegmentedControl setSelectedSegmentIndex:1];
}
[readFilterSegmentedControl
setTitleTextAttributes:@{NSFontAttributeName:
[UIFont fontWithName:@"Helvetica-Bold" size:11.0f]}
forState:UIControlStateNormal];
[readFilterSegmentedControl setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:0];
[readFilterSegmentedControl setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:1];
[readFilterSegmentedControl setSelectedSegmentIndex:0];
if ([appDelegate.activeReadFilter isEqualToString:@"unread"]) {
[readFilterSegmentedControl setSelectedSegmentIndex:1];
@ -147,7 +159,7 @@
return cell;
}
- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return kMenuOptionHeight;
}
@ -185,6 +197,7 @@
UITableViewCell *cell = [[UITableViewCell alloc] init];
cell.frame = CGRectMake(0, 0, 240, kMenuOptionHeight);
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.separatorInset = UIEdgeInsetsZero;
orderSegmentedControl.frame = CGRectMake(8, 7, cell.frame.size.width - 8*2,
kMenuOptionHeight - 7*2);
@ -200,6 +213,8 @@
UITableViewCell *cell = [[UITableViewCell alloc] init];
cell.frame = CGRectMake(0, 0, 240, kMenuOptionHeight);
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.separatorInset = UIEdgeInsetsZero;
readFilterSegmentedControl.frame = CGRectMake(8, 7, cell.frame.size.width - 8*2,
kMenuOptionHeight - 7*2);
[readFilterSegmentedControl setTitle:[@"All stories" uppercaseString] forSegmentAtIndex:0];

View file

@ -8,15 +8,16 @@
#import <UIKit/UIKit.h>
#import "NewsBlurAppDelegate.h"
#import "ABTableViewCell.h"
#import "NBSwipeableCell.h"
@interface FeedDetailTableCell : ABTableViewCell {
@interface FeedDetailTableCell : NBSwipeableCell {
NewsBlurAppDelegate *appDelegate;
// All views
NSString *storyTitle;
NSString *storyAuthor;
NSString *storyDate;
NSInteger storyTimestamp;
int storyScore;
BOOL isStarred;
BOOL isShared;
@ -31,18 +32,20 @@
UIColor *feedColorBar;
UIColor *feedColorBarTopBorder;
UIView *cellContent;
}
@property (nonatomic) NSString *siteTitle;
@property (nonatomic) UIImage *siteFavicon;
@property (readwrite) int storyScore;
@property (readwrite) BOOL isStarred;
@property (nonatomic, readwrite) BOOL isStarred;
@property (readwrite) BOOL isShared;
@property (nonatomic) NSString *storyTitle;
@property (nonatomic) NSString *storyAuthor;
@property (nonatomic) NSString *storyDate;
@property (nonatomic) NSInteger storyTimestamp;
@property (nonatomic) UIColor *feedColorBar;
@property (nonatomic) UIColor *feedColorBarTopBorder;
@ -52,6 +55,12 @@
@property (readwrite) BOOL isRiverOrSocial;
@property (readwrite) BOOL hasAlpha;
- (UIImage *)imageByApplyingAlpha:(UIImage *)image withAlpha:(CGFloat) alpha;
- (void)setupGestures;
@end
@interface FeedDetailTableCellView : UIView
@property (nonatomic) FeedDetailTableCell *cell;
@end

View file

@ -11,22 +11,25 @@
#import "ABTableViewCell.h"
#import "UIView+TKCategory.h"
#import "Utilities.h"
#import "MCSwipeTableViewCell.h"
static UIFont *textFont = nil;
static UIFont *indicatorFont = nil;
@class FeedDetailViewController;
@implementation FeedDetailTableCell
@synthesize storyTitle;
@synthesize storyAuthor;
@synthesize storyDate;
@synthesize storyTimestamp;
@synthesize storyScore;
@synthesize siteTitle;
@synthesize siteFavicon;
@synthesize isRead;
@synthesize isStarred;
@synthesize isShared;
@synthesize isStarred;
@synthesize isShort;
@synthesize isRiverOrSocial;
@synthesize feedColorBar;
@ -45,35 +48,82 @@ static UIFont *indicatorFont = nil;
}
}
- (void)drawContentView:(CGRect)r highlighted:(BOOL)highlighted {
int adjustForSocial = 3;
if (self.isRiverOrSocial) {
adjustForSocial = 20;
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
cellContent = [[FeedDetailTableCellView alloc] initWithFrame:self.frame];
cellContent.opaque = YES;
[self.contentView addSubview:cellContent];
}
return self;
}
- (void)drawRect:(CGRect)rect {
((FeedDetailTableCellView *)cellContent).cell = self;
cellContent.frame = rect;
[cellContent setNeedsDisplay];
}
- (void)setupGestures {
NSString *unreadIcon;
if (storyScore == -1) {
unreadIcon = @"g_icn_hidden.png";
} else if (storyScore == 1) {
unreadIcon = @"g_icn_focus.png";
} else {
unreadIcon = @"g_icn_unread.png";
}
UIColor *shareColor = self.isStarred ?
UIColorFromRGB(0xF69E89) :
UIColorFromRGB(0xA4D97B);
UIColor *readColor = self.isRead ?
UIColorFromRGB(0xBED49F) :
UIColorFromRGB(0xFFFFD2);
appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
[self setDelegate:(FeedDetailViewController <MCSwipeTableViewCellDelegate> *)appDelegate.feedDetailViewController];
[self setFirstStateIconName:@"clock.png"
firstColor:shareColor
secondStateIconName:nil
secondColor:nil
thirdIconName:unreadIcon
thirdColor:readColor
fourthIconName:nil
fourthColor:nil];
self.mode = MCSwipeTableViewCellModeSwitch;
self.shouldAnimatesIcons = NO;
}
@end
@implementation FeedDetailTableCellView
@synthesize cell;
- (void)drawRect:(CGRect)r {
int adjustForSocial = 3;
if (cell.isRiverOrSocial) {
adjustForSocial = 20;
}
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect rect = CGRectInset(r, 12, 12);
rect.size.width -= 18; // Scrollbar padding
if (!highlighted) {
UIColor *backgroundColor;
backgroundColor = UIColorFromRGB(0xf4f4f4);
[backgroundColor set];
}
UIColor *backgroundColor;
backgroundColor = cell.highlighted || cell.selected ?
UIColorFromRGB(0xFFFDEF) : UIColorFromRGB(0xf4f4f4);
[backgroundColor set];
CGContextFillRect(context, r);
if (highlighted) {
[NewsBlurAppDelegate fillGradient:r startColor:UIColorFromRGB(0xFFFDEF) endColor:UIColorFromRGB(0xFFFDDF)];
}
UIColor *textColor;
UIFont *font;
if (self.isRead) {
if (cell.isRead) {
font = [UIFont fontWithName:@"Helvetica" size:11];
textColor = UIColorFromRGB(0x808080);
} else {
@ -81,19 +131,21 @@ static UIFont *indicatorFont = nil;
textColor = UIColorFromRGB(0x606060);
}
if (highlighted) {
textColor = UIColorFromRGB(0x686868);
if (cell.highlighted || cell.selected) {
textColor = UIColorFromRGB(0x686868);
}
[textColor set];
if (self.isRiverOrSocial) {
[self.siteTitle
drawInRect:CGRectMake(leftMargin + 20, 7, rect.size.width - 20, 21)
withFont:font
lineBreakMode:NSLineBreakByTruncatingTail
alignment:NSTextAlignmentLeft];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
paragraphStyle.alignment = NSTextAlignmentLeft;
if (cell.isRiverOrSocial) {
[cell.siteTitle drawInRect:CGRectMake(leftMargin + 20, 7, rect.size.width - 20, 21)
withAttributes:@{NSFontAttributeName: font,
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: paragraphStyle}];
if (self.isRead) {
if (cell.isRead) {
font = [UIFont fontWithName:@"Helvetica" size:12];
textColor = UIColorFromRGB(0x606060);
@ -101,63 +153,65 @@ static UIFont *indicatorFont = nil;
textColor = UIColorFromRGB(0x333333);
font = [UIFont fontWithName:@"Helvetica-Bold" size:12];
}
if (highlighted) {
if (cell.highlighted || cell.selected) {
textColor = UIColorFromRGB(0x686868);
}
[textColor set];
}
// story title
CGSize theSize = [self.storyTitle sizeWithFont:font constrainedToSize:CGSizeMake(rect.size.width, 30.0) lineBreakMode:NSLineBreakByTruncatingTail];
// story title
CGSize theSize = [cell.storyTitle
boundingRectWithSize:CGSizeMake(rect.size.width, cell.isShort ? 15.0 : 30.0)
options:NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName: font,
NSParagraphStyleAttributeName: paragraphStyle}
context:nil].size;
int storyTitleY = 7 + adjustForSocial + ((30 - theSize.height)/2);
if (self.isShort) {
if (cell.isShort) {
storyTitleY = 7 + adjustForSocial + 2;
}
int storyTitleX = leftMargin;
if (self.isStarred) {
if (cell.isStarred) {
UIImage *savedIcon = [UIImage imageNamed:@"clock"];
[savedIcon drawInRect:CGRectMake(storyTitleX, storyTitleY - 1, 16, 16) blendMode:nil alpha:1];
storyTitleX += 20;
}
if (self.isShared) {
if (cell.isShared) {
UIImage *savedIcon = [UIImage imageNamed:@"menu_icn_share"];
[savedIcon drawInRect:CGRectMake(storyTitleX, storyTitleY - 1, 16, 16) blendMode:nil alpha:1];
storyTitleX += 20;
}
[self.storyTitle
drawInRect:CGRectMake(storyTitleX, storyTitleY, rect.size.width - storyTitleX + leftMargin, theSize.height)
withFont:font
lineBreakMode:NSLineBreakByTruncatingTail
alignment:NSTextAlignmentLeft];
[cell.storyTitle drawWithRect:CGRectMake(storyTitleX, storyTitleY, rect.size.width - storyTitleX + leftMargin, theSize.height)
options:NSStringDrawingTruncatesLastVisibleLine|NSStringDrawingUsesLineFragmentOrigin
attributes:@{NSFontAttributeName: font,
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: paragraphStyle}
context:nil];
int storyAuthorDateY = 41 + adjustForSocial;
if (self.isShort) {
if (cell.isShort) {
storyAuthorDateY -= 13;
}
// story author style
if (self.isRead) {
textColor = UIColorFromRGB(0x808080);
if (cell.isRead) {
textColor = UIColorFromRGB(0x959595);
font = [UIFont fontWithName:@"Helvetica" size:10];
} else {
textColor = UIColorFromRGB(0x959595);
textColor = UIColorFromRGB(0xA6A8A2);
font = [UIFont fontWithName:@"Helvetica-Bold" size:10];
}
if (highlighted) {
textColor = UIColorFromRGB(0x686868);
if (cell.highlighted || cell.selected) {
textColor = UIColorFromRGB(0x959595);
}
[textColor set];
[self.storyAuthor
[cell.storyAuthor
drawInRect:CGRectMake(leftMargin, storyAuthorDateY, (rect.size.width) / 2 - 10, 15.0)
withFont:font
lineBreakMode:NSLineBreakByTruncatingTail
alignment:NSTextAlignmentLeft];
withAttributes:@{NSFontAttributeName: font,
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: paragraphStyle}];
// story date
if (self.isRead) {
if (cell.isRead) {
textColor = UIColorFromRGB(0xbabdd1);
font = [UIFont fontWithName:@"Helvetica" size:10];
} else {
@ -165,118 +219,108 @@ static UIFont *indicatorFont = nil;
font = [UIFont fontWithName:@"Helvetica-Bold" size:10];
}
if (highlighted) {
textColor = UIColorFromRGB(0x686868);
if (cell.highlighted || cell.selected) {
if (cell.isRead) {
textColor = UIColorFromRGB(0xaaadc1);
} else {
textColor = UIColorFromRGB(0x5a5d91);
}
}
[textColor set];
[self.storyDate
drawInRect:CGRectMake(leftMargin + (rect.size.width) / 2 - 10, storyAuthorDateY, (rect.size.width) / 2 + 10, 15.0)
withFont:font
lineBreakMode:NSLineBreakByTruncatingTail
alignment:NSTextAlignmentRight];
paragraphStyle.alignment = NSTextAlignmentRight;
NSString *date = [Utilities formatShortDateFromTimestamp:cell.storyTimestamp];
[date
drawInRect:CGRectMake(leftMargin + (rect.size.width) / 2 - 10, storyAuthorDateY, (rect.size.width) / 2 + 10, 15.0)
withAttributes:@{NSFontAttributeName: font,
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: paragraphStyle}];
// feed bar
CGContextSetStrokeColor(context, CGColorGetComponents([self.feedColorBarTopBorder CGColor]));
if (self.isRead) {
CGContextSetStrokeColor(context, CGColorGetComponents([cell.feedColorBarTopBorder CGColor]));
if (cell.isRead) {
CGContextSetAlpha(context, 0.15);
}
CGContextSetLineWidth(context, 4.0f);
CGContextBeginPath(context);
CGContextMoveToPoint(context, 2.0f, 1.0f);
CGContextAddLineToPoint(context, 2.0f, self.frame.size.height - 1);
CGContextMoveToPoint(context, 2.0f, 0);
CGContextAddLineToPoint(context, 2.0f, cell.frame.size.height);
CGContextStrokePath(context);
CGContextSetStrokeColor(context, CGColorGetComponents([self.feedColorBar CGColor]));
CGContextSetStrokeColor(context, CGColorGetComponents([cell.feedColorBar CGColor]));
CGContextBeginPath(context);
CGContextMoveToPoint(context, 6.0f, 1.0f);
CGContextAddLineToPoint(context, 6.0, self.frame.size.height - 1);
CGContextMoveToPoint(context, 6.0f, 0);
CGContextAddLineToPoint(context, 6.0, cell.frame.size.height);
CGContextStrokePath(context);
// reset for borders
UIColor *white = UIColorFromRGB(0xffffff);
CGContextSetAlpha(context, 1.0);
CGContextSetLineWidth(context, 1.0f);
if (highlighted) {
if (cell.highlighted || cell.selected) {
// top border
UIColor *blue = UIColorFromRGB(0xF9F8F4);
CGContextSetStrokeColor(context, CGColorGetComponents([white CGColor]));
CGContextSetLineWidth(context, 1.0f);
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0, 0.5f);
CGContextAddLineToPoint(context, cell.bounds.size.width, 0.5f);
CGContextStrokePath(context);
CGFloat lineWidth = 0.5f;
CGContextSetLineWidth(context, lineWidth);
UIColor *blue = UIColorFromRGB(0xDFDDCF);
CGContextSetStrokeColor(context, CGColorGetComponents([blue CGColor]));
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0, 0.5f);
CGContextAddLineToPoint(context, self.bounds.size.width, 0.5f);
CGContextMoveToPoint(context, 0, 1.0f + 0.5f*lineWidth);
CGContextAddLineToPoint(context, cell.bounds.size.width, 1.0f + 0.5f*lineWidth);
CGContextStrokePath(context);
// bottom border
// bottom border
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0, self.bounds.size.height - 1.5f);
CGContextAddLineToPoint(context, self.bounds.size.width, self.bounds.size.height - 1.5f);
CGContextMoveToPoint(context, 0, cell.bounds.size.height - .5f*lineWidth);
CGContextAddLineToPoint(context, cell.bounds.size.width, cell.bounds.size.height - .5f*lineWidth);
CGContextStrokePath(context);
} else {
// top border
UIColor *white = UIColorFromRGB(0xffffff);
CGContextSetLineWidth(context, 1.0f);
CGContextSetStrokeColor(context, CGColorGetComponents([white CGColor]));
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0.0f, 0.5f);
CGContextAddLineToPoint(context, self.bounds.size.width, 0.5f);
CGContextAddLineToPoint(context, cell.bounds.size.width, 0.5f);
CGContextStrokePath(context);
}
// site favicon
if (self.isRead && !self.hasAlpha) {
if (self.isRiverOrSocial) {
self.siteFavicon = [self imageByApplyingAlpha:self.siteFavicon withAlpha:0.25];
if (cell.isRead && !cell.hasAlpha) {
if (cell.isRiverOrSocial) {
cell.siteFavicon = [cell imageByApplyingAlpha:cell.siteFavicon withAlpha:0.25];
}
self.hasAlpha = YES;
cell.hasAlpha = YES;
}
if (self.isRiverOrSocial) {
[self.siteFavicon drawInRect:CGRectMake(leftMargin, 6.0, 16.0, 16.0)];
if (cell.isRiverOrSocial) {
[cell.siteFavicon drawInRect:CGRectMake(leftMargin, 6.0, 16.0, 16.0)];
}
// story indicator
// story indicator
int storyIndicatorY = 4 + adjustForSocial;
if (self.isShort){
if (cell.isShort){
storyIndicatorY = 4 + adjustForSocial - 5 ;
}
UIImage *unreadIcon;
if (storyScore == -1) {
if (cell.storyScore == -1) {
unreadIcon = [UIImage imageNamed:@"g_icn_hidden"];
} else if (storyScore == 1) {
} else if (cell.storyScore == 1) {
unreadIcon = [UIImage imageNamed:@"g_icn_focus"];
} else {
unreadIcon = [UIImage imageNamed:@"g_icn_unread"];
}
[unreadIcon drawInRect:CGRectMake(15, storyIndicatorY + 14, 8, 8) blendMode:nil alpha:(self.isRead ? .15 : 1)];
[unreadIcon drawInRect:CGRectMake(15, storyIndicatorY + 14, 8, 8) blendMode:nil alpha:(cell.isRead ? .15 : 1)];
}
- (UIImage *)imageByApplyingAlpha:(UIImage *)image withAlpha:(CGFloat) alpha {
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0f);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect area = CGRectMake(0, 0, image.size.width, image.size.height);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -area.size.height);
CGContextSetBlendMode(ctx, kCGBlendModeMultiply);
CGContextSetAlpha(ctx, alpha);
CGContextDrawImage(ctx, area, image.CGImage);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
@end
@end

View file

@ -12,17 +12,19 @@
#import "BaseViewController.h"
#import "Utilities.h"
#import "WEPopoverController.h"
#import "TransparentToolbar.h"
#import "NBNotifier.h"
#import "MCSwipeTableViewCell.h"
@class NewsBlurAppDelegate;
@class FeedDetailTableCell;
@class MCSwipeTableViewCell;
@interface FeedDetailViewController : BaseViewController
<UITableViewDelegate, UITableViewDataSource,
UIActionSheetDelegate, UIAlertViewDelegate,
UIPopoverControllerDelegate, ASIHTTPRequestDelegate,
WEPopoverControllerDelegate> {
WEPopoverControllerDelegate, MCSwipeTableViewCellDelegate,
UIGestureRecognizerDelegate> {
NewsBlurAppDelegate *appDelegate;
int feedPage;
@ -45,10 +47,8 @@
@property (nonatomic) IBOutlet UIBarButtonItem * settingsBarButton;
@property (nonatomic) IBOutlet UIBarButtonItem * spacerBarButton;
@property (nonatomic) IBOutlet UIBarButtonItem * spacer2BarButton;
@property (nonatomic) IBOutlet UIBarButtonItem * spacer3BarButton;
@property (nonatomic) IBOutlet UIBarButtonItem * separatorBarButton;
@property (nonatomic) IBOutlet UIBarButtonItem * titleImageBarButton;
@property (nonatomic) IBOutlet TransparentToolbar * rightToolbar;
@property (nonatomic, retain) WEPopoverController *popoverController;
@property (nonatomic, retain) NBNotifier *notifier;
@ -76,6 +76,7 @@
- (void)setUserAvatarLayout:(UIInterfaceOrientation)orientation;
- (void)fadeSelectedCell;
- (void)loadStory:(FeedDetailTableCell *)cell atRow:(NSInteger)row;
- (void)redrawUnreadStory;
- (IBAction)doOpenMarkReadActionSheet:(id)sender;
- (IBAction)doOpenSettingsActionSheet:(id)sender;

View file

@ -24,11 +24,12 @@
#import "UIBarButtonItem+WEPopover.h"
#import "WEPopoverController.h"
#import "UIBarButtonItem+Image.h"
#import "TransparentToolbar.h"
#import "FeedDetailMenuViewController.h"
#import "NBNotifier.h"
#import "NBLoadingCell.h"
#import "FMDatabase.h"
#import "NBBarButtonItem.h"
#import "UIActivitiesControl.h"
#define kTableViewRowHeight 61;
#define kTableViewRiverRowHeight 81;
@ -49,8 +50,7 @@
@synthesize settingsBarButton;
@synthesize separatorBarButton;
@synthesize titleImageBarButton;
@synthesize spacerBarButton, spacer2BarButton, spacer3BarButton;
@synthesize rightToolbar;
@synthesize spacerBarButton, spacer2BarButton;
@synthesize appDelegate;
@synthesize feedPage;
@synthesize pageFetching;
@ -75,18 +75,12 @@
self.storyTitlesTable.backgroundColor = UIColorFromRGB(0xf4f4f4);
self.storyTitlesTable.separatorColor = UIColorFromRGB(0xE9E8E4);
rightToolbar = [[TransparentToolbar alloc]
initWithFrame:CGRectMake(0, 0, 76, 44)];
spacerBarButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
spacerBarButton.width = -12;
spacerBarButton.width = 0;
spacer2BarButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
spacer2BarButton.width = -10;
spacer3BarButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
spacer3BarButton.width = -10;
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
spacer2BarButton.width = 0;
UIImage *separatorImage = [UIImage imageNamed:@"bar-separator.png"];
separatorBarButton = [UIBarButtonItem barItemWithImage:separatorImage target:nil action:nil];
@ -94,12 +88,18 @@
UIImage *settingsImage = [UIImage imageNamed:@"nav_icn_settings.png"];
settingsBarButton = [UIBarButtonItem barItemWithImage:settingsImage target:self action:@selector(doOpenSettingsActionSheet:)];
UIImage *markreadImage = [UIImage imageNamed:@"markread.png"];
feedMarkReadButton = [UIBarButtonItem barItemWithImage:markreadImage target:self action:@selector(doOpenMarkReadActionSheet:)];
titleImageBarButton = [UIBarButtonItem alloc];
UILongPressGestureRecognizer *longpress = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:@selector(handleLongPress:)];
longpress.minimumPressDuration = 1.0;
longpress.delegate = self;
[self.storyTitlesTable addGestureRecognizer:longpress];
self.notifier = [[NBNotifier alloc] initWithTitle:@"Fetching stories..." inView:self.view];
[self.view addSubview:self.notifier];
}
@ -122,39 +122,49 @@
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
[self setUserAvatarLayout:orientation];
self.finishedAnimatingIn = NO;
[MBProgressHUD hideHUDForView:self.view animated:YES];
// set center title
UILabel *titleLabel = (UILabel *)[appDelegate makeFeedTitle:appDelegate.activeFeed];
self.navigationItem.titleView = titleLabel;
[MBProgressHUD hideHUDForView:self.view animated:NO];
// set right avatar title image
spacerBarButton.width = 0;
spacer2BarButton.width = 0;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
spacerBarButton.width = -6;
spacer2BarButton.width = 10;
}
if (appDelegate.isSocialView) {
UIButton *titleImageButton = [appDelegate makeRightFeedTitle:appDelegate.activeFeed];
[titleImageButton addTarget:self action:@selector(showUserProfile) forControlEvents:UIControlEventTouchUpInside];
titleImageBarButton.customView = titleImageButton;
[rightToolbar setItems: [NSArray arrayWithObjects:
spacerBarButton,
feedMarkReadButton,
spacer2BarButton,
separatorBarButton,
spacer3BarButton,
titleImageBarButton, nil]];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:rightToolbar];
titleImageBarButton.enabled = YES;
spacerBarButton.width = -6;
NSString *feedIdStr = [NSString stringWithFormat:@"%@", [appDelegate.activeFeed objectForKey:@"id"]];
UIImage *titleImage = [Utilities getImage:feedIdStr isSocial:YES];
titleImage = [Utilities roundCorneredImage:titleImage radius:6];
[((UIButton *)titleImageBarButton.customView).imageView removeFromSuperview];
titleImageBarButton = [UIBarButtonItem barItemWithImage:titleImage
target:self
action:@selector(showUserProfile)];
titleImageBarButton.customView.frame = CGRectMake(0, 0, 32, 32);
self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:
spacerBarButton,
titleImageBarButton,
spacer2BarButton,
separatorBarButton,
feedMarkReadButton, nil];
} else {
[rightToolbar setItems: [NSArray arrayWithObjects:
spacerBarButton,
feedMarkReadButton,
spacer2BarButton,
separatorBarButton,
spacer3BarButton,
settingsBarButton, nil]];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:rightToolbar];
self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:
spacerBarButton,
settingsBarButton,
spacer2BarButton,
separatorBarButton,
feedMarkReadButton,
nil];
}
// set center title
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone &&
!self.navigationItem.titleView) {
self.navigationItem.titleView = [appDelegate makeFeedTitle:appDelegate.activeFeed];
}
NSMutableArray *indexPaths = [NSMutableArray array];
NSLog(@"appDelegate.recentlyReadStoryLocations: %d - %@", self.isOffline, appDelegate.recentlyReadStoryLocations);
// NSLog(@"appDelegate.recentlyReadStoryLocations: %d - %@", self.isOffline, appDelegate.recentlyReadStoryLocations);
for (id i in appDelegate.recentlyReadStoryLocations) {
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[i intValue]
inSection:0];
@ -164,17 +174,15 @@
}
}
if ([indexPaths count] > 0 && [self.storyTitlesTable numberOfRowsInSection:0]) {
[self.storyTitlesTable beginUpdates];
[self.storyTitlesTable reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
[self.storyTitlesTable endUpdates];
//[self.storyTitlesTable reloadData];
// [self.storyTitlesTable beginUpdates];
// [self.storyTitlesTable reloadRowsAtIndexPaths:indexPaths withRowAnimation:UITableViewRowAnimationNone];
// [self.storyTitlesTable endUpdates];
[self.storyTitlesTable reloadData];
}
appDelegate.recentlyReadStoryLocations = [NSMutableArray array];
appDelegate.originalStoryCount = [appDelegate unreadCount];
[super viewWillAppear:animated];
if ((appDelegate.isSocialRiverView ||
appDelegate.isSocialView ||
[appDelegate.activeFolder isEqualToString:@"saved_stories"])) {
@ -192,7 +200,7 @@
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
[self.storyTitlesTable reloadData];
int location = appDelegate.locationOfActiveStory;
NSInteger location = appDelegate.locationOfActiveStory;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:location inSection:0];
if (indexPath && location >= 0) {
[self.storyTitlesTable selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionMiddle];
@ -216,19 +224,14 @@
[self testForTryFeed];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
- (void)viewDidDisappear:(BOOL)animated {
[self.popoverController dismissPopoverAnimated:YES];
self.popoverController = nil;
}
- (void)viewDidDisappear:(BOOL)animated {
}
- (void)fadeSelectedCell {
// have the selected cell deselect
int location = appDelegate.locationOfActiveStory;
NSInteger location = appDelegate.locationOfActiveStory;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:location inSection:0];
if (indexPath) {
@ -241,12 +244,12 @@
- (void)setUserAvatarLayout:(UIInterfaceOrientation)orientation {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone && appDelegate.isSocialView) {
if (UIInterfaceOrientationIsPortrait(orientation)) {
UIButton *avatar = (UIButton *)titleImageBarButton.customView;
NBBarButtonItem *avatar = (NBBarButtonItem *)titleImageBarButton.customView;
CGRect buttonFrame = avatar.frame;
buttonFrame.size = CGSizeMake(32, 32);
avatar.frame = buttonFrame;
} else {
UIButton *avatar = (UIButton *)titleImageBarButton.customView;
NBBarButtonItem *avatar = (NBBarButtonItem *)titleImageBarButton.customView;
CGRect buttonFrame = avatar.frame;
buttonFrame.size = CGSizeMake(28, 28);
avatar.frame = buttonFrame;
@ -254,12 +257,12 @@
}
}
#pragma mark -
#pragma mark Initialization
- (void)resetFeedDetail {
appDelegate.hasLoadedFeedDetail = NO;
self.navigationItem.titleView = nil;
self.pageFetching = NO;
self.pageFinished = NO;
self.isOffline = NO;
@ -295,7 +298,8 @@
- (void)beginOfflineTimer {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
if (!appDelegate.storyLocationsCount && self.feedPage == 1) {
if (!appDelegate.storyLocationsCount && !self.pageFinished &&
self.feedPage == 1 && !self.isOffline) {
self.isShowingOffline = YES;
self.isOffline = YES;
[self showLoadingNotifier];
@ -324,7 +328,7 @@
self.feedPage = page;
self.pageFetching = YES;
int storyCount = appDelegate.storyCount;
NSInteger storyCount = appDelegate.storyCount;
if (storyCount == 0) {
[self.storyTitlesTable reloadData];
[storyTitlesTable scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
@ -375,13 +379,11 @@
if (request.isCancelled) {
NSLog(@"Cancelled");
return;
} else if (self.feedPage == 1) {
} else {
self.isOffline = YES;
self.feedPage = 1;
[self loadOfflineStories];
[self showOfflineNotifier];
} else {
[self informError:[request error]];
self.pageFinished = YES;
}
[self.storyTitlesTable reloadData];
}];
@ -404,8 +406,8 @@
(unsigned long)NULL), ^(void) {
[appDelegate.database inDatabase:^(FMDatabase *db) {
NSArray *feedIds;
int limit = 12;
int offset = (self.feedPage - 1) * limit;
NSInteger limit = 12;
NSInteger offset = (self.feedPage - 1) * limit;
if (appDelegate.isRiverView) {
feedIds = appDelegate.activeFolderFeeds;
@ -427,11 +429,11 @@
} else {
readFilterSql = @"";
}
NSString *sql = [NSString stringWithFormat:@"SELECT * FROM stories s %@ WHERE s.story_feed_id IN (%@) ORDER BY s.story_timestamp %@ LIMIT %d OFFSET %d",
NSString *sql = [NSString stringWithFormat:@"SELECT * FROM stories s %@ WHERE s.story_feed_id IN (%@) ORDER BY s.story_timestamp %@ LIMIT %ld OFFSET %ld",
readFilterSql,
[feedIds componentsJoinedByString:@","],
orderSql,
limit, offset];
(long)limit, (long)offset];
FMResultSet *cursor = [db executeQuery:sql];
NSMutableArray *offlineStories = [NSMutableArray array];
@ -442,6 +444,7 @@
dataUsingEncoding:NSUTF8StringEncoding]
options:nil error:nil]];
}
[cursor close];
if ([appDelegate.activeReadFilter isEqualToString:@"all"]) {
NSString *unreadHashSql = [NSString stringWithFormat:@"SELECT s.story_hash FROM stories s INNER JOIN unread_hashes uh ON s.story_hash = uh.story_hash WHERE s.story_feed_id IN (%@)",
@ -457,9 +460,10 @@
[unreadStoryHashes setObject:[NSNumber numberWithBool:YES] forKey:[unreadHashCursor objectForColumnName:@"story_hash"]];
}
appDelegate.unreadStoryHashes = unreadStoryHashes;
[unreadHashCursor close];
}
dispatch_sync(dispatch_get_main_queue(), ^{
dispatch_async(dispatch_get_main_queue(), ^{
if (!self.isOffline) {
NSLog(@"Online before offline rendered. Tossing offline stories.");
return;
@ -499,7 +503,7 @@
if (!self.pageFetching && !self.pageFinished) {
self.feedPage = page;
self.pageFetching = YES;
int storyCount = appDelegate.storyCount;
NSInteger storyCount = appDelegate.storyCount;
if (storyCount == 0) {
[self.storyTitlesTable reloadData];
[storyTitlesTable scrollRectToVisible:CGRectMake(0, 0, 1, 1) animated:YES];
@ -566,15 +570,12 @@
if (request.isCancelled) {
NSLog(@"Cancelled");
return;
} else if (self.feedPage == 1) {
} else {
self.isOffline = YES;
self.isShowingOffline = NO;
self.feedPage = 1;
[self loadOfflineStories];
[self showOfflineNotifier];
} else {
[self informError:[request error]];
self.pageFinished = YES;
[self.storyTitlesTable reloadData];
}
}];
[request setCompletionBlock:^(void) {
@ -596,12 +597,11 @@
NSLog(@"Cancelled");
return;
} else if ([request responseStatusCode] >= 500) {
if (self.feedPage == 1) {
self.isOffline = YES;
self.isShowingOffline = NO;
[self loadOfflineStories];
[self showOfflineNotifier];
}
self.isOffline = YES;
self.isShowingOffline = NO;
self.feedPage = 1;
[self loadOfflineStories];
[self showOfflineNotifier];
if ([request responseStatusCode] == 503) {
[self informError:@"In maintenance mode"];
self.pageFinished = YES;
@ -646,7 +646,7 @@
for (id key in [newClassifiers allKeys]) {
[appDelegate.activeClassifiers setObject:[newClassifiers objectForKey:key] forKey:key];
}
} else {
} else if (newClassifiers) {
[appDelegate.activeClassifiers setObject:newClassifiers forKey:feedIdStr];
}
appDelegate.activePopularAuthors = [results objectForKey:@"feed_authors"];
@ -774,12 +774,12 @@
if ([storyIdStr isEqualToString:appDelegate.tryFeedStoryId]) {
NSDictionary *feed = [appDelegate.activeFeedStories objectAtIndex:i];
int score = [NewsBlurAppDelegate computeStoryScore:[feed objectForKey:@"intelligence"]];
NSInteger score = [NewsBlurAppDelegate computeStoryScore:[feed objectForKey:@"intelligence"]];
if (score < appDelegate.selectedIntelligence) {
[self changeIntelligence:score];
}
int locationOfStoryId = [appDelegate locationOfStoryId:storyIdStr];
NSInteger locationOfStoryId = [appDelegate locationOfStoryId:storyIdStr];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:locationOfStoryId inSection:0];
[self.storyTitlesTable selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionBottom];
@ -811,7 +811,7 @@
}
- (UITableViewCell *)makeLoadingCell {
int height = 40;
NSInteger height = 40;
UITableViewCell *cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:@"NoReuse"];
@ -831,6 +831,7 @@
fleuron.frame = CGRectMake(0, 0, self.view.frame.size.width, height);
fleuron.contentMode = UIViewContentModeCenter;
[cell.contentView addSubview:fleuron];
cell.backgroundColor = [UIColor clearColor];
return cell;
} else {//if ([appDelegate.storyLocationsCount]) {
NBLoadingCell *loadingCell = [[NBLoadingCell alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, height)];
@ -844,7 +845,7 @@
#pragma mark Table View - Feed List
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
int storyCount = appDelegate.storyLocationsCount;
NSInteger storyCount = appDelegate.storyLocationsCount;
// The +1 is for the finished/loading bar.
return storyCount + 1;
@ -856,23 +857,23 @@
NSString *cellIdentifier;
NSDictionary *feed ;
if (indexPath.row >= appDelegate.storyLocationsCount) {
return [self makeLoadingCell];
}
if (appDelegate.isRiverView || appDelegate.isSocialView) {
cellIdentifier = @"FeedRiverDetailCellIdentifier";
} else {
cellIdentifier = @"FeedDetailCellIdentifier";
}
FeedDetailTableCell *cell = (FeedDetailTableCell *)[tableView
dequeueReusableCellWithIdentifier:cellIdentifier];
FeedDetailTableCell *cell = (FeedDetailTableCell *)[tableView
dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[FeedDetailTableCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:nil];
}
if (indexPath.row >= appDelegate.storyLocationsCount) {
return [self makeLoadingCell];
}
NSDictionary *story = [self getStoryAtRow:indexPath.row];
id feedId = [story objectForKey:@"story_feed_id"];
@ -893,10 +894,11 @@
NSString *title = [story objectForKey:@"story_title"];
cell.storyTitle = [title stringByDecodingHTMLEntities];
cell.storyDate = [story objectForKey:@"short_parsed_date"];
cell.isStarred = [story objectForKey:@"starred"];
cell.isShared = [story objectForKey:@"shared"];
cell.storyTimestamp = [[story objectForKey:@"story_timestamp"] integerValue];
cell.isStarred = [[story objectForKey:@"starred"] boolValue];
cell.isShared = [[story objectForKey:@"shared"] boolValue];
if ([[story objectForKey:@"story_authors"] class] != [NSNull class]) {
cell.storyAuthor = [[story objectForKey:@"story_authors"] uppercaseString];
@ -930,19 +932,15 @@
// undread indicator
int score = [NewsBlurAppDelegate computeStoryScore:[story objectForKey:@"intelligence"]];
NSInteger score = [NewsBlurAppDelegate computeStoryScore:[story objectForKey:@"intelligence"]];
cell.storyScore = score;
if (!appDelegate.hasLoadedFeedDetail) {
cell.isRead = ([appDelegate.activeReadFilter isEqualToString:@"all"] &&
![[appDelegate.unreadStoryHashes objectForKey:[story objectForKey:@"story_hash"]] boolValue]) ||
[[appDelegate.recentlyReadStories objectForKey:[story objectForKey:@"story_hash"]] boolValue];
cell.isRead = ![appDelegate isStoryUnread:story];
// if (!appDelegate.hasLoadedFeedDetail) {
// NSLog(@"Offline: %d (%d/%d) - %@ - %@", cell.isRead, ![[appDelegate.unreadStoryHashes objectForKey:[story objectForKey:@"story_hash"]] boolValue], [[appDelegate.recentlyReadStories objectForKey:[story objectForKey:@"story_hash"]] boolValue], [story objectForKey:@"story_title"], [story objectForKey:@"story_hash"]);
} else {
cell.isRead = [[story objectForKey:@"read_status"] intValue] == 1 ||
[[appDelegate.recentlyReadStories objectForKey:[story objectForKey:@"story_hash"]] boolValue];
// } else {
// NSLog(@"Online: %d (%d/%d) - %@ - %@", cell.isRead, [[story objectForKey:@"read_status"] intValue] == 1, [[appDelegate.recentlyReadStories objectForKey:[story objectForKey:@"story_hash"]] boolValue], [story objectForKey:@"story_title"], [story objectForKey:@"story_hash"]);
}
// }
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad
@ -955,36 +953,40 @@
cell.isRiverOrSocial = YES;
}
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
int rowIndex = [appDelegate locationOfActiveStory];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
NSInteger rowIndex = [appDelegate locationOfActiveStory];
if (rowIndex == indexPath.row) {
[self.storyTitlesTable selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
}
[cell setupGestures];
return cell;
return cell;
}
- (void)loadStory:(FeedDetailTableCell *)cell atRow:(int)row {
cell.isRead = YES;
[cell setNeedsLayout];
int storyIndex = [appDelegate indexFromLocation:row];
- (void)loadStory:(FeedDetailTableCell *)cell atRow:(NSInteger)row {
NSInteger storyIndex = [appDelegate indexFromLocation:row];
appDelegate.activeStory = [[appDelegate activeFeedStories] objectAtIndex:storyIndex];
if ([appDelegate isStoryUnread:appDelegate.activeStory]) {
[self markStoryAsRead:appDelegate.activeStory];
}
[appDelegate loadStoryDetailView];
[self redrawUnreadStory];
}
- (void)redrawUnreadStory {
int rowIndex = [appDelegate locationOfActiveStory];
NSInteger rowIndex = [appDelegate locationOfActiveStory];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:0];
FeedDetailTableCell *cell = (FeedDetailTableCell*) [self.storyTitlesTable cellForRowAtIndexPath:indexPath];
cell.isRead = [[appDelegate.activeStory objectForKey:@"read_status"] boolValue];
cell.isRead = ![appDelegate isStoryUnread:appDelegate.activeStory];
cell.isShared = [[appDelegate.activeStory objectForKey:@"shared"] boolValue];
cell.isStarred = [[appDelegate.activeStory objectForKey:@"starred"] boolValue];
[cell setNeedsDisplay];
}
- (void)changeActiveStoryTitleCellLayout {
int rowIndex = [appDelegate locationOfActiveStory];
NSInteger rowIndex = [appDelegate locationOfActiveStory];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:0];
FeedDetailTableCell *cell = (FeedDetailTableCell*) [self.storyTitlesTable cellForRowAtIndexPath:indexPath];
cell.isRead = YES;
@ -1012,12 +1014,12 @@
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
int storyCount = appDelegate.storyLocationsCount;
NSInteger storyCount = appDelegate.storyLocationsCount;
if (storyCount && indexPath.row == storyCount) {
return 40;
} else if (appDelegate.isRiverView || appDelegate.isSocialView || appDelegate.isSocialRiverView) {
int height = kTableViewRiverRowHeight;
NSInteger height = kTableViewRiverRowHeight;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad
&& !appDelegate.masterContainerViewController.storyTitlesOnLeft
&& UIInterfaceOrientationIsPortrait(orientation)) {
@ -1025,7 +1027,7 @@
}
return height;
} else {
int height = kTableViewRowHeight;
NSInteger height = kTableViewRowHeight;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad
&& !appDelegate.masterContainerViewController.storyTitlesOnLeft
&& UIInterfaceOrientationIsPortrait(orientation)) {
@ -1034,7 +1036,10 @@
return height;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
// This will create a "invisible" footer
return 0.01f;
}
- (void)scrollViewDidScroll: (UIScrollView *)scroll {
[self checkScroll];
}
@ -1073,16 +1078,80 @@
}
- (NSDictionary *)getStoryAtRow:(NSInteger)indexPathRow {
int row = [[[appDelegate activeFeedStoryLocations] objectAtIndex:indexPathRow] intValue];
NSInteger row = [[[appDelegate activeFeedStoryLocations] objectAtIndex:indexPathRow] intValue];
return [appDelegate.activeFeedStories objectAtIndex:row];
}
#pragma mark - MCSwipeTableViewCellDelegate
// When the user starts swiping the cell this method is called
- (void)swipeTableViewCellDidStartSwiping:(MCSwipeTableViewCell *)cell {
// NSLog(@"Did start swiping the cell!");
}
// When the user is dragging, this method is called and return the dragged percentage from the border
- (void)swipeTableViewCell:(MCSwipeTableViewCell *)cell didSwipWithPercentage:(CGFloat)percentage {
// NSLog(@"Did swipe with percentage : %f", percentage);
}
- (void)swipeTableViewCell:(MCSwipeTableViewCell *)cell didEndSwipingSwipingWithState:(MCSwipeTableViewCellState)state mode:(MCSwipeTableViewCellMode)mode {
NSIndexPath *indexPath = [self.storyTitlesTable indexPathForCell:cell];
if (!indexPath) {
// This can happen if the user swipes on a cell that is being refreshed.
return;
}
NSInteger storyIndex = [appDelegate indexFromLocation:indexPath.row];
NSDictionary *story = [[appDelegate activeFeedStories] objectAtIndex:storyIndex];
if (state == MCSwipeTableViewCellState1) {
// Saved
if ([[story objectForKey:@"starred"] boolValue]) {
[self markStoryAsUnsaved:story];
} else {
[self markStoryAsSaved:story];
}
[self.storyTitlesTable reloadRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationFade];
} else if (state == MCSwipeTableViewCellState3) {
// Read
if ([[story objectForKey:@"read_status"] boolValue]) {
[self markStoryAsUnread:story];
} else {
[self markStoryAsRead:story];
}
[self.storyTitlesTable reloadRowsAtIndexPaths:@[indexPath]
withRowAnimation:UITableViewRowAnimationFade];
}
}
#pragma mark -
#pragma mark Feed Actions
- (void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer {
CGPoint p = [gestureRecognizer locationInView:self.storyTitlesTable];
NSIndexPath *indexPath = [self.storyTitlesTable indexPathForRowAtPoint:p];
FeedDetailTableCell *cell = (FeedDetailTableCell *)[self.storyTitlesTable cellForRowAtIndexPath:indexPath];
if (gestureRecognizer.state != UIGestureRecognizerStateBegan) return;
if (indexPath == nil) return;
NSDictionary *story = [self getStoryAtRow:indexPath.row];
appDelegate.activeStory = story;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[appDelegate.masterContainerViewController showSendToPopover:cell];
} else {
[self presentViewController:[UIActivitiesControl activityViewControllerForView:self]
animated:YES
completion:nil];
}
}
- (void)markFeedsReadWithAllStories:(BOOL)includeHidden {
if (!self.isOffline && appDelegate.isRiverView && includeHidden &&
if (appDelegate.isRiverView && includeHidden &&
[appDelegate.activeFolder isEqualToString:@"everything"]) {
// Mark folder as read
NSString *urlString = [NSString stringWithFormat:@"%@/reader/mark_all_as_read",
@ -1093,7 +1162,7 @@
[request startAsynchronous];
[appDelegate markActiveFolderAllRead];
} else if (!self.isOffline && appDelegate.isRiverView && includeHidden) {
} else if (appDelegate.isRiverView && includeHidden) {
// Mark folder as read
NSString *urlString = [NSString stringWithFormat:@"%@/reader/mark_feed_as_read",
NEWSBLUR_URL];
@ -1102,11 +1171,14 @@
for (id feed_id in [appDelegate.dictFolders objectForKey:appDelegate.activeFolder]) {
[request addPostValue:feed_id forKey:@"feed_id"];
}
[request setDelegate:nil];
[request setUserInfo:@{@"feeds": appDelegate.activeFolderFeeds}];
[request setDelegate:self];
[request setDidFinishSelector:@selector(finishMarkAllAsRead:)];
[request setDidFailSelector:@selector(requestFailed:)];
[request startAsynchronous];
[appDelegate markActiveFolderAllRead];
} else if (!self.isOffline && !appDelegate.isRiverView && includeHidden) {
} else if (!appDelegate.isRiverView && includeHidden) {
// Mark feed as read
NSString *urlString = [NSString stringWithFormat:@"%@/reader/mark_feed_as_read",
NEWSBLUR_URL];
@ -1115,6 +1187,7 @@
[request setPostValue:[appDelegate.activeFeed objectForKey:@"id"] forKey:@"feed_id"];
[request setDidFinishSelector:@selector(finishMarkAllAsRead:)];
[request setDidFailSelector:@selector(requestFailed:)];
[request setUserInfo:@{@"feeds": @[[appDelegate.activeFeed objectForKey:@"id"]]}];
[request setDelegate:self];
[request startAsynchronous];
[appDelegate markFeedAllRead:[appDelegate.activeFeed objectForKey:@"id"]];
@ -1127,49 +1200,12 @@
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:[feedsStories JSONRepresentation] forKey:@"feeds_stories"];
[request setDelegate:self];
[request setUserInfo:feedsStories];
[request setUserInfo:@{@"stories": feedsStories}];
[request setDidFinishSelector:@selector(finishMarkAllAsRead:)];
[request setDidFailSelector:@selector(requestFailedMarkStoryRead:)];
[request startAsynchronous];
} else {
// Must be offline and marking all as read, so load all stories.
NSMutableDictionary *feedsStories = [NSMutableDictionary dictionary];
[appDelegate.database inDatabase:^(FMDatabase *db) {
NSArray *feedIds;
if (appDelegate.isRiverView) {
feedIds = appDelegate.activeFolderFeeds;
} else if (appDelegate.activeFeed) {
feedIds = @[[appDelegate.activeFeed objectForKey:@"id"]];
} else {
return;
}
NSString *sql = [NSString stringWithFormat:@"SELECT u.story_feed_id, u.story_hash "
"FROM unread_hashes u WHERE u.story_feed_id IN (%@)",
[feedIds componentsJoinedByString:@","]];
FMResultSet *cursor = [db executeQuery:sql];
while ([cursor next]) {
NSDictionary *story = [cursor resultDictionary];
NSString *feedIdStr = [story objectForKey:@"story_feed_id"];
NSString *storyHash = [story objectForKey:@"story_hash"];
if (![feedsStories objectForKey:feedIdStr]) {
[feedsStories setObject:[NSMutableArray array] forKey:feedIdStr];
}
NSMutableArray *stories = [feedsStories objectForKey:feedIdStr];
[stories addObject:storyHash];
}
}];
for (NSString *feedId in [feedsStories allKeys]) {
[appDelegate markFeedAllRead:feedId];
}
[appDelegate queueReadStories:feedsStories];
}
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[appDelegate.navigationController popToRootViewControllerAnimated:YES];
[appDelegate.masterContainerViewController transitionFromFeedDetail];
@ -1183,23 +1219,20 @@
- (void)requestFailedMarkStoryRead:(ASIFormDataRequest *)request {
// [self informError:@"Failed to mark story as read"];
NSDictionary *feedsStories = request.userInfo;
[appDelegate queueReadStories:feedsStories];
[appDelegate markStoriesRead:[request.userInfo objectForKey:@"stories"]
inFeeds:[request.userInfo objectForKey:@"feeds"]
cutoffTimestamp:nil];
}
- (void)finishMarkAllAsRead:(ASIFormDataRequest *)request {
if (request.responseStatusCode != 200) {
[self requestFailedMarkStoryRead:request];
return;
}
// NSString *responseString = [request responseString];
// NSData *responseData = [responseString dataUsingEncoding:NSUTF8StringEncoding];
// NSError *error;
// NSDictionary *results = [NSJSONSerialization
// JSONObjectWithData:responseData
// options:kNilOptions
// error:&error];
if ([request.userInfo objectForKey:@"feeds"]) {
[appDelegate markFeedReadInCache:@[[request.userInfo objectForKey:@"feeds"]]];
}
}
- (IBAction)doOpenMarkReadActionSheet:(id)sender {
@ -1228,8 +1261,8 @@
self.actionSheet_ = options;
[appDelegate calculateStoryLocations];
int visibleUnreadCount = appDelegate.visibleUnreadCount;
int totalUnreadCount = [appDelegate unreadCount];
NSInteger visibleUnreadCount = appDelegate.visibleUnreadCount;
NSInteger totalUnreadCount = [appDelegate unreadCount];
NSArray *buttonTitles = nil;
BOOL showVisible = YES;
BOOL showEntire = YES;
@ -1243,8 +1276,8 @@
@"this site"];
NSString *visibleText = [NSString stringWithFormat:@"Mark %@ read",
visibleUnreadCount == 1 ? @"this story as" :
[NSString stringWithFormat:@"these %d stories",
visibleUnreadCount]];
[NSString stringWithFormat:@"these %ld stories",
(long)visibleUnreadCount]];
if (showVisible && showEntire) {
buttonTitles = [NSArray arrayWithObjects:visibleText, entireText, nil];
options.destructiveButtonIndex = 1;
@ -1272,8 +1305,8 @@
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
// NSLog(@"Action option #%d on %d", buttonIndex, actionSheet.tag);
if (actionSheet.tag == 1) {
int visibleUnreadCount = appDelegate.visibleUnreadCount;
int totalUnreadCount = [appDelegate unreadCount];
NSInteger visibleUnreadCount = appDelegate.visibleUnreadCount;
NSInteger totalUnreadCount = [appDelegate unreadCount];
BOOL showVisible = YES;
BOOL showEntire = YES;
// if ([appDelegate.activeFolder isEqualToString:@"everything"]) showEntire = NO;
@ -1303,7 +1336,7 @@
} else if (buttonIndex == 2) {
[self instafetchFeed];
}
}
}
}
- (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSInteger)buttonIndex {
@ -1328,7 +1361,7 @@
if ([self.popoverController respondsToSelector:@selector(setContainerViewProperties:)]) {
[self.popoverController setContainerViewProperties:[self improvedContainerViewProperties]];
}
int menuCount = [appDelegate.feedDetailMenuViewController.menuOptions count] + 2;
NSInteger menuCount = [appDelegate.feedDetailMenuViewController.menuOptions count] + 2;
[self.popoverController setPopoverContentSize:CGSizeMake(260, 38 * menuCount)];
[self.popoverController presentPopoverFromBarButtonItem:self.settingsBarButton
permittedArrowDirections:UIPopoverArrowDirectionUp
@ -1429,11 +1462,11 @@
- (void)showUserProfile {
appDelegate.activeUserProfileId = [NSString stringWithFormat:@"%@", [appDelegate.activeFeed objectForKey:@"user_id"]];
appDelegate.activeUserProfileName = [NSString stringWithFormat:@"%@", [appDelegate.activeFeed objectForKey:@"username"]];
[appDelegate showUserProfileModal:self.navigationItem.rightBarButtonItem];
[appDelegate showUserProfileModal:titleImageBarButton];
}
- (void)changeActiveFeedDetailRow {
int rowIndex = [appDelegate locationOfActiveStory];
NSInteger rowIndex = [appDelegate locationOfActiveStory];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:rowIndex inSection:0];
NSIndexPath *offsetIndexPath = [NSIndexPath indexPathForRow:rowIndex - 1 inSection:0];
@ -1455,6 +1488,169 @@
}
}
#pragma mark -
#pragma mark Story Actions - read
- (void)markStoryAsRead:(NSDictionary *)story {
NSString *urlString = [NSString stringWithFormat:@"%@/reader/mark_story_hashes_as_read",
NEWSBLUR_URL];
NSURL *url = [NSURL URLWithString:urlString];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:[story objectForKey:@"story_hash"]
forKey:@"story_hash"];
[request setDidFinishSelector:@selector(finishMarkAsRead:)];
[request setDidFailSelector:@selector(failedMarkAsRead:)];
[request setDelegate:self];
[request setUserInfo:story];
[request startAsynchronous];
if ([appDelegate.dictFeeds objectForKey:[NSString stringWithFormat:@"%@", [story objectForKey:@"story_feed_id"]]]) {
[appDelegate markStoryRead:[story objectForKey:@"story_hash"]
feedId:[story objectForKey:@"story_feed_id"]];
}
}
- (void)finishMarkAsRead:(ASIFormDataRequest *)request {
if ([request responseStatusCode] != 200) {
return [self failedMarkAsRead:request];
}
[appDelegate.storyPageControl refreshHeaders];
}
- (void)failedMarkAsRead:(ASIFormDataRequest *)request {
NSString *storyFeedId = [request.userInfo objectForKey:@"story_feed_id"];
NSString *storyHash = [request.userInfo objectForKey:@"story_hash"];
[appDelegate queueReadStories:@{storyFeedId: @[storyHash]}];
}
- (void)markStoryAsUnread:(NSDictionary *)story {
NSString *urlString = [NSString stringWithFormat:@"%@/reader/mark_story_as_unread",
NEWSBLUR_URL];
NSURL *url = [NSURL URLWithString:urlString];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:[story objectForKey:@"story_hash"]
forKey:@"story_id"];
[request setPostValue:[story objectForKey:@"story_feed_id"]
forKey:@"feed_id"];
[request setDidFinishSelector:@selector(finishMarkAsUnread:)];
[request setDidFailSelector:@selector(failedMarkAsUnread:)];
[request setDelegate:self];
[request setUserInfo:story];
[request startAsynchronous];
[appDelegate markStoryUnread:[story objectForKey:@"story_hash"]
feedId:[story objectForKey:@"story_feed_id"]];
}
- (void)finishMarkAsUnread:(ASIFormDataRequest *)request {
NSString *responseString = [request responseString];
NSData *responseData = [responseString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSDictionary *results = [NSJSONSerialization
JSONObjectWithData:responseData
options:kNilOptions
error:&error];
if ([request responseStatusCode] != 200 || [[results objectForKey:@"code"] integerValue] < 0) {
return [self failedMarkAsUnread:request];
}
appDelegate.storyPageControl.currentPage.isRecentlyUnread = YES;
[appDelegate.storyPageControl refreshHeaders];
}
- (void)failedMarkAsUnread:(ASIFormDataRequest *)request {
NSString *storyFeedId = [request.userInfo objectForKey:@"story_feed_id"];
NSString *storyHash = [request.userInfo objectForKey:@"story_hash"];
BOOL dequeued = [appDelegate dequeueReadStoryHash:storyHash inFeed:storyFeedId];
if (!dequeued) {
[self informError:@"Failed to unread story"];
[appDelegate markStoryRead:storyHash feedId:storyFeedId];
[self.storyTitlesTable reloadData];
} else {
[appDelegate.unreadStoryHashes setObject:[NSNumber numberWithBool:YES] forKey:storyHash];
[self.storyTitlesTable reloadData];
}
}
#pragma mark -
#pragma mark Story Actions - save
- (void)markStoryAsSaved:(NSDictionary *)story {
NSString *urlString = [NSString stringWithFormat:@"%@/reader/mark_story_as_starred",
NEWSBLUR_URL];
NSURL *url = [NSURL URLWithString:urlString];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:[story objectForKey:@"story_hash"]
forKey:@"story_id"];
[request setPostValue:[story objectForKey:@"story_feed_id"]
forKey:@"feed_id"];
[request setDidFinishSelector:@selector(finishMarkAsSaved:)];
[request setDidFailSelector:@selector(failedMarkAsSaved:)];
[request setDelegate:self];
[request setUserInfo:story];
[request startAsynchronous];
[appDelegate markStory:story asSaved:YES];
}
- (void)finishMarkAsSaved:(ASIFormDataRequest *)request {
if ([request responseStatusCode] != 200) {
return [self failedMarkAsSaved:request];
}
[appDelegate.storyPageControl refreshHeaders];
}
- (void)failedMarkAsSaved:(ASIFormDataRequest *)request {
[self informError:@"Failed to save story"];
[appDelegate markStory:request.userInfo asSaved:NO];
[self.storyTitlesTable reloadData];
}
- (void)markStoryAsUnsaved:(NSDictionary *)story {
NSString *urlString = [NSString stringWithFormat:@"%@/reader/mark_story_as_unstarred",
NEWSBLUR_URL];
NSURL *url = [NSURL URLWithString:urlString];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setPostValue:[story objectForKey:@"story_hash"]
forKey:@"story_id"];
[request setPostValue:[story objectForKey:@"story_feed_id"]
forKey:@"feed_id"];
[request setDidFinishSelector:@selector(finishMarkAsUnsaved:)];
[request setDidFailSelector:@selector(failedMarkAsUnsaved:)];
[request setDelegate:self];
[request setUserInfo:story];
[request startAsynchronous];
[appDelegate markStory:story asSaved:NO];
}
- (void)finishMarkAsUnsaved:(ASIFormDataRequest *)request {
if ([request responseStatusCode] != 200) {
return [self failedMarkAsUnsaved:request];
}
[appDelegate.storyPageControl refreshHeaders];
}
- (void)failedMarkAsUnsaved:(ASIFormDataRequest *)request {
[self informError:@"Failed to unsave story"];
[appDelegate markStory:request.userInfo asSaved:YES];
[self.storyTitlesTable reloadData];
}
#pragma mark -
#pragma mark instafetchFeed

View file

@ -9,11 +9,9 @@
#import <UIKit/UIKit.h>
#import "NewsBlurAppDelegate.h"
#import "UnreadCountView.h"
#import "ABTableViewCell.h"
#import "NBSwipeableCell.h"
@class NewsBlurAppDelegate;
@interface FeedTableCell : ABTableViewCell {
@interface FeedTableCell : NBSwipeableCell {
NewsBlurAppDelegate *appDelegate;
NSString *feedTitle;
@ -23,6 +21,8 @@
int _negativeCount;
NSString *_negativeCountStr;
BOOL isSocial;
UIView *cellContent;
UnreadCountView *unreadCount;
}
@property (nonatomic) NewsBlurAppDelegate *appDelegate;
@ -33,5 +33,17 @@
@property (assign, nonatomic) int negativeCount;
@property (assign, nonatomic) BOOL isSocial;
@property (nonatomic) NSString *negativeCountStr;
@property (nonatomic) UnreadCountView *unreadCount;
- (void)setupGestures;
- (void)redrawUnreadCounts;
@end
@interface FeedTableCellView : UIView
@property (nonatomic, weak) FeedTableCell *cell;
- (void)redrawUnreadCounts;
@end

View file

@ -23,36 +23,50 @@ static UIFont *textFont = nil;
@synthesize negativeCount = _negativeCount;
@synthesize negativeCountStr;
@synthesize isSocial;
@synthesize unreadCount;
+ (void) initialize{
if (self == [FeedTableCell class]) {
textFont = [UIFont boldSystemFontOfSize:18];
// UIColor *psGrad = UIColorFromRGB(0x559F4D);
// UIColor *ntGrad = UIColorFromRGB(0xE4AB00);
// UIColor *ngGrad = UIColorFromRGB(0x9B181B);
// const CGFloat* psTop = CGColorGetComponents(ps.CGColor);
// const CGFloat* psBot = CGColorGetComponents(psGrad.CGColor);
// CGFloat psGradient[] = {
// psTop[0], psTop[1], psTop[2], psTop[3],
// psBot[0], psBot[1], psBot[2], psBot[3]
// };
// psColors = psGradient;
}
}
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
unreadCount = [UnreadCountView alloc];
unreadCount.appDelegate = self.appDelegate;
self.unreadCount = unreadCount;
cellContent = [[FeedTableCellView alloc] initWithFrame:self.frame];
cellContent.opaque = YES;
[self.contentView addSubview:cellContent];
}
return self;
}
- (void)drawRect:(CGRect)rect {
((FeedTableCellView *)cellContent).cell = self;
cellContent.frame = rect;
[cellContent setNeedsDisplay];
[self setupGestures];
}
- (void) setPositiveCount:(int)ps {
if (ps == _positiveCount) return;
_positiveCount = ps;
[self setNeedsDisplay];
// [cellContent setNeedsDisplay];
}
- (void) setNeutralCount:(int)nt {
if (nt == _neutralCount) return;
_neutralCount = nt;
[self setNeedsDisplay];
// [cellContent setNeedsDisplay];
}
- (void) setNegativeCount:(int)ng {
@ -60,97 +74,124 @@ static UIFont *textFont = nil;
_negativeCount = ng;
_negativeCountStr = [NSString stringWithFormat:@"%d", ng];
[self setNeedsDisplay];
// [cellContent setNeedsDisplay];
}
- (void) drawContentView:(CGRect)r highlighted:(BOOL)highlighted {
- (void)setupGestures {
[self setDelegate:(NewsBlurViewController <MCSwipeTableViewCellDelegate> *)appDelegate.feedsViewController];
[self setFirstStateIconName:self.isSocial ? @"menu_icn_fetch_subscribers.png" : @"train.png"
firstColor:UIColorFromRGB(0xA4D97B)
secondStateIconName:nil
secondColor:nil
thirdIconName:@"g_icn_unread.png"
thirdColor:UIColorFromRGB(0xFFFFD2)
fourthIconName:nil
fourthColor:nil];
self.mode = MCSwipeTableViewCellModeSwitch;
self.shouldAnimatesIcons = NO;
}
- (void)redrawUnreadCounts {
[((FeedTableCellView *)cellContent) redrawUnreadCounts];
}
@end
@implementation FeedTableCellView
@synthesize cell;
- (void)drawRect:(CGRect)r {
CGContextRef context = UIGraphicsGetCurrentContext();
UIColor *backgroundColor;
backgroundColor = highlighted ?
UIColorFromRGB(NEWSBLUR_HIGHLIGHT_COLOR) :
self.isSocial ? UIColorFromRGB(0xE6ECE8) :
backgroundColor = cell.highlighted || cell.selected ?
UIColorFromRGB(0xFFFFD2) :
cell.isSocial ? UIColorFromRGB(0xE6ECE8) :
UIColorFromRGB(0xF7F8F5);
[backgroundColor set];
CGContextFillRect(context, r);
if (highlighted) {
[NewsBlurAppDelegate fillGradient:r startColor:UIColorFromRGB(0xFFFFD2) endColor:UIColorFromRGB(0xFDED8D)];
if (cell.highlighted || cell.selected) {
// [NewsBlurAppDelegate fillGradient:CGRectMake(r.origin.x, r.origin.y + 1, r.size.width, r.size.height - 1) startColor:UIColorFromRGB(0xFFFFD2) endColor:UIColorFromRGB(0xFDED8D)];
// top border
UIColor *highlightBorderColor = UIColorFromRGB(0xE3D0AE);
CGFloat lineWidth = 0.5f;
CGContextSetStrokeColor(context, CGColorGetComponents([highlightBorderColor CGColor]));
CGContextSetLineWidth(context, lineWidth);
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0, 0.5f);
CGContextMoveToPoint(context, 0, lineWidth*0.5f);
CGContextAddLineToPoint(context, r.size.width, 0.5f);
CGContextStrokePath(context);
// bottom border
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0, r.size.height - .5f);
CGContextAddLineToPoint(context, r.size.width, r.size.height - .5f);
CGContextSetLineWidth(context, lineWidth);
CGContextMoveToPoint(context, 0, r.size.height - .5f*lineWidth);
CGContextAddLineToPoint(context, r.size.width, r.size.height - .5f*lineWidth);
CGContextStrokePath(context);
}
UnreadCountView *unreadCount = [UnreadCountView alloc];
unreadCount.appDelegate = appDelegate;
[unreadCount drawInRect:r ps:_positiveCount nt:_neutralCount
listType:(isSocial ? NBFeedListSocial : NBFeedListFeed)];
[cell.unreadCount drawInRect:r ps:cell.positiveCount nt:cell.neutralCount
listType:(cell.isSocial ? NBFeedListSocial : NBFeedListFeed)];
UIColor *textColor = highlighted ?
UIColor *textColor = cell.highlighted || cell.selected ?
[UIColor blackColor]:
UIColorFromRGB(0x3a3a3a);
[textColor set];
UIFont *font;
if (self.negativeCount || self.neutralCount || self.positiveCount) {
if (cell.negativeCount || cell.neutralCount || cell.positiveCount) {
font = [UIFont fontWithName:@"HelveticaNeue-Bold" size:13.0];
} else {
font = [UIFont fontWithName:@"Helvetica" size:12.6];
}
if (isSocial) {
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
paragraphStyle.alignment = NSTextAlignmentLeft;
if (cell.isSocial) {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[self.feedFavicon drawInRect:CGRectMake(9.0, 2.0, 28.0, 28.0)];
[feedTitle
drawInRect:CGRectMake(46, 7, r.size.width - ([unreadCount offsetWidth] + 36) - 10 - 16, 20.0)
withFont:font
lineBreakMode:NSLineBreakByTruncatingTail
alignment:NSTextAlignmentLeft];
[cell.feedFavicon drawInRect:CGRectMake(9.0, 2.0, 28.0, 28.0)];
[cell.feedTitle drawInRect:CGRectMake(46, 7, r.size.width - ([cell.unreadCount offsetWidth] + 36) - 10 - 16, 20.0)
withAttributes:@{NSFontAttributeName: font,
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: paragraphStyle}];
} else {
[self.feedFavicon drawInRect:CGRectMake(9.0, 3.0, 26.0, 26.0)];
[feedTitle
drawInRect:CGRectMake(42, 7, r.size.width - ([unreadCount offsetWidth] + 36) - 10 - 12, 20.0)
withFont:font
lineBreakMode:NSLineBreakByTruncatingTail
alignment:NSTextAlignmentLeft];
[cell.feedFavicon drawInRect:CGRectMake(9.0, 3.0, 26.0, 26.0)];
[cell.feedTitle drawInRect:CGRectMake(42, 7, r.size.width - ([cell.unreadCount offsetWidth] + 36) - 10 - 12, 20.0)
withAttributes:@{NSFontAttributeName: font,
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: paragraphStyle}];
}
} else {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[self.feedFavicon drawInRect:CGRectMake(12.0, 7.0, 16.0, 16.0)];
[feedTitle
drawInRect:CGRectMake(36.0, 7.0, r.size.width - ([unreadCount offsetWidth] + 36) - 10, 20.0)
withFont:font
lineBreakMode:NSLineBreakByTruncatingTail
alignment:NSTextAlignmentLeft];
[cell.feedFavicon drawInRect:CGRectMake(12.0, 7.0, 16.0, 16.0)];
[cell.feedTitle drawInRect:CGRectMake(36.0, 7.0, r.size.width - ([cell.unreadCount offsetWidth] + 36) - 10, 20.0)
withAttributes:@{NSFontAttributeName: font,
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: paragraphStyle}];
} else {
[self.feedFavicon drawInRect:CGRectMake(9.0, 7.0, 16.0, 16.0)];
[feedTitle
drawInRect:CGRectMake(34.0, 7.0, r.size.width - ([unreadCount offsetWidth] + 36) - 10, 20.0)
withFont:font
lineBreakMode:NSLineBreakByTruncatingTail
alignment:NSTextAlignmentLeft];
[cell.feedFavicon drawInRect:CGRectMake(9.0, 7.0, 16.0, 16.0)];
[cell.feedTitle drawInRect:CGRectMake(34.0, 7.0, r.size.width - ([cell.unreadCount offsetWidth] + 36) - 10, 20.0)
withAttributes:@{NSFontAttributeName: font,
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: paragraphStyle}];
}
}
}
- (void)redrawUnreadCounts {
// [cell.unreadCount drawInRect:self.frame ps:cell.positiveCount nt:cell.neutralCount
// listType:(cell.isSocial ? NBFeedListSocial : NBFeedListFeed)];
cell.unreadCount.psCount = cell.positiveCount;
cell.unreadCount.ntCount = cell.neutralCount;
[cell.unreadCount setNeedsLayout];
}
@end

View file

@ -102,7 +102,7 @@
return cell;
}
- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 38;
}

View file

@ -200,7 +200,7 @@
}
- (void)updateSites {
self.instructionLabel.text = [NSString stringWithFormat:@"You are subscribed to %d sites", [[appDelegate.dictFeeds allKeys] count]];
self.instructionLabel.text = [NSString stringWithFormat:@"You are subscribed to %lu sites", (unsigned long)[[appDelegate.dictFeeds allKeys] count]];
NSString *msg = [NSString stringWithFormat:@"Imported %i site%@",
self.importedFeedCount_,
self.importedFeedCount_ == 1 ? @"" : @"s"];
@ -366,8 +366,8 @@
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
SiteCell *cell = [tableView
dequeueReusableCellWithIdentifier:@"ActivityCell"];
SiteCell *cell = [tableView
dequeueReusableCellWithIdentifier:@"ActivityCell"];
if (cell == nil) {
cell = [[SiteCell alloc]
initWithStyle:UITableViewCellStyleDefault

View file

@ -13,12 +13,17 @@
@class NewsBlurAppDelegate;
@interface FolderTitleView : UIView {
@interface FolderTitleView : UIView
<UIGestureRecognizerDelegate,
UIActionSheetDelegate> {
NewsBlurAppDelegate *appDelegate;
}
@property (assign, nonatomic) int section;
@property (nonatomic) NewsBlurAppDelegate *appDelegate;
@property (nonatomic) UnreadCountView *unreadCount;
@property (nonatomic) UIButton *invisibleHeaderButton;
- (void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer;
@end

View file

@ -8,6 +8,7 @@
#import <QuartzCore/QuartzCore.h>
#import "NewsBlurAppDelegate.h"
#import "NewsBlurViewController.h"
#import "FolderTitleView.h"
#import "UnreadCountView.h"
@ -16,6 +17,7 @@
@synthesize appDelegate;
@synthesize section;
@synthesize unreadCount;
@synthesize invisibleHeaderButton;
- (void)setNeedsDisplay {
[unreadCount setNeedsDisplay];
@ -25,7 +27,7 @@
- (void) drawRect:(CGRect)rect {
self.appDelegate = (NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate];
self.appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
CGContextRef context = UIGraphicsGetCurrentContext();
@ -84,21 +86,20 @@
CGContextSetStrokeColor(context, CGColorGetComponents([topColor CGColor]));
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0, 0.5f);
CGContextAddLineToPoint(context, rect.size.width, 0.5f);
CGContextMoveToPoint(context, 0, 0.25f);
CGContextAddLineToPoint(context, rect.size.width, 0.25f);
CGContextStrokePath(context);
// bottom border
UIColor *bottomColor = UIColorFromRGB(0xB7BBAA);
CGContextSetStrokeColor(context, CGColorGetComponents([bottomColor CGColor]));
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0, rect.size.height - .5f);
CGContextAddLineToPoint(context, rect.size.width, rect.size.height - .5f);
CGContextMoveToPoint(context, 0, rect.size.height-0.25f);
CGContextAddLineToPoint(context, rect.size.width, rect.size.height-0.25f);
CGContextStrokePath(context);
// Folder title
UIColor *textColor = [UIColor colorWithRed:0.3 green:0.3 blue:0.3 alpha:1.0];
[textColor set];
UIFont *font = [UIFont boldSystemFontOfSize:11];
NSString *folderTitle;
if (section == 0) {
@ -114,14 +115,17 @@
}
UIColor *shadowColor = UIColorFromRGB(0xF0F2E9);
CGContextSetShadowWithColor(context, CGSizeMake(0, 1), 0, [shadowColor CGColor]);
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
paragraphStyle.alignment = NSTextAlignmentLeft;
[folderTitle
drawInRect:CGRectMake(36.0, 10, rect.size.width - 36 - 36 - countWidth, 14)
withFont:font
lineBreakMode:NSLineBreakByTruncatingTail
alignment:NSTextAlignmentLeft];
withAttributes:@{NSFontAttributeName: font,
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: paragraphStyle}];
UIButton *invisibleHeaderButton = [UIButton buttonWithType:UIButtonTypeCustom];
invisibleHeaderButton = [UIButton buttonWithType:UIButtonTypeCustom];
invisibleHeaderButton.frame = CGRectMake(0, 0, customView.frame.size.width, customView.frame.size.height);
invisibleHeaderButton.alpha = .1;
invisibleHeaderButton.tag = section;
@ -159,6 +163,7 @@
UIImage *folderImage;
int folderImageViewX = 10;
BOOL allowLongPress = NO;
if (section == 0) {
folderImage = [UIImage imageNamed:@"ak-icon-global.png"];
@ -198,6 +203,7 @@
} else {
folderImageViewX = 7;
}
allowLongPress = YES;
}
[folderImage drawInRect:CGRectMake(folderImageViewX, 6, 20, 20)];
@ -208,6 +214,55 @@
} else {
[self addSubview:customView];
}
if (allowLongPress) {
UILongPressGestureRecognizer *longpress = [[UILongPressGestureRecognizer alloc]
initWithTarget:self action:@selector(handleLongPress:)];
longpress.minimumPressDuration = 1.0;
longpress.delegate = self;
[self addGestureRecognizer:longpress];
}
}
- (void)handleLongPress:(UILongPressGestureRecognizer *)gestureRecognizer {
if (gestureRecognizer.state != UIGestureRecognizerStateBegan) return;
if (section < 2) return;
NSString *folderTitle = [appDelegate.dictFoldersArray objectAtIndex:section];
UIActionSheet *markReadSheet = [[UIActionSheet alloc] initWithTitle:folderTitle
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:@"Mark folder as read"
otherButtonTitles:@"1 day", @"3 days", @"7 days", @"14 days", nil];
markReadSheet.accessibilityValue = folderTitle;
[markReadSheet showInView:appDelegate.feedsViewController.view];
}
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
NSString *folderTitle = actionSheet.accessibilityValue;
NSArray *feedIds = [appDelegate.dictFolders objectForKey:folderTitle];
switch (buttonIndex) {
case 0:
[appDelegate.feedsViewController markFeedsRead:feedIds cutoffDays:0];
break;
case 1:
[appDelegate.feedsViewController markFeedsRead:feedIds cutoffDays:1];
break;
case 2:
[appDelegate.feedsViewController markFeedsRead:feedIds cutoffDays:3];
break;
case 3:
[appDelegate.feedsViewController markFeedsRead:feedIds cutoffDays:7];
break;
case 4:
[appDelegate.feedsViewController markFeedsRead:feedIds cutoffDays:14];
break;
}
[appDelegate.feedsViewController sectionUntappedOutside:invisibleHeaderButton];
}
@end

View file

@ -178,7 +178,7 @@
return cell;
}
- (float)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return kMenuOptionHeight;
}
@ -231,11 +231,17 @@
UITableViewCell *cell = [[UITableViewCell alloc] init];
cell.frame = CGRectMake(0, 0, 240, kMenuOptionHeight);
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.separatorInset = UIEdgeInsetsZero;
fontStyleSegment.frame = CGRectMake(8, 4, cell.frame.size.width - 8*2, kMenuOptionHeight - 4*2);
[fontStyleSegment setTitle:@"Helvetica" forSegmentAtIndex:0];
[fontStyleSegment setTitle:@"Georgia" forSegmentAtIndex:1];
[fontStyleSegment setTitle:[@"Helvetica" uppercaseString] forSegmentAtIndex:0];
[fontStyleSegment setTitle:[@"Georgia" uppercaseString] forSegmentAtIndex:1];
[fontStyleSegment setTintColor:UIColorFromRGB(0x738570)];
[fontStyleSegment
setTitleTextAttributes:@{NSFontAttributeName:
[UIFont fontWithName:@"Helvetica-Bold" size:11.0f]}
forState:UIControlStateNormal];
[fontStyleSegment setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:0];
[fontStyleSegment setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:1];
[cell addSubview:fontStyleSegment];
@ -246,6 +252,7 @@
UITableViewCell *cell = [[UITableViewCell alloc] init];
cell.frame = CGRectMake(0, 0, 240, kMenuOptionHeight);
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.separatorInset = UIEdgeInsetsZero;
fontSizeSegment.frame = CGRectMake(8, 4, cell.frame.size.width - 8*2, kMenuOptionHeight - 4*2);
[fontSizeSegment setTitle:@"12pt" forSegmentAtIndex:0];
@ -254,6 +261,15 @@
[fontSizeSegment setTitle:@"15pt" forSegmentAtIndex:3];
[fontSizeSegment setTitle:@"17pt" forSegmentAtIndex:4];
[fontSizeSegment setTintColor:UIColorFromRGB(0x738570)];
[fontSizeSegment
setTitleTextAttributes:@{NSFontAttributeName:
[UIFont fontWithName:@"Helvetica-Bold" size:11.0f]}
forState:UIControlStateNormal];
[fontSizeSegment setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:0];
[fontSizeSegment setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:1];
[fontSizeSegment setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:2];
[fontSizeSegment setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:3];
[fontSizeSegment setContentOffset:CGSizeMake(0, 1) forSegmentAtIndex:4];
[cell addSubview:fontSizeSegment];

View file

@ -56,7 +56,7 @@
self.appDelegate = (NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate];
self.view.frame = CGRectMake(0, 0, 320, 416);
self.contentSizeForViewInPopover = self.view.frame.size;
self.preferredContentSize = self.view.frame.size;
}
- (void)viewDidUnload
@ -273,10 +273,10 @@ viewForHeaderInSection:(NSInteger)section {
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (self.inSearch_){
int userCount = [self.userProfiles count];
NSInteger userCount = [self.userProfiles count];
return userCount;
} else {
int userCount = [self.suggestedUserProfiles count];
NSInteger userCount = [self.suggestedUserProfiles count];
if (!userCount) {
return 3;
}
@ -304,7 +304,7 @@ viewForHeaderInSection:(NSInteger)section {
if (self.inSearch_){
int userProfileCount = [self.userProfiles count];
NSInteger userProfileCount = [self.userProfiles count];
if (userProfileCount) {
if (userProfileCount > indexPath.row) {
@ -379,7 +379,7 @@ viewForHeaderInSection:(NSInteger)section {
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath {
NSInteger currentRow = indexPath.row;
int row = currentRow;
NSInteger row = currentRow;
appDelegate.activeUserProfileId = [[self.userProfiles objectAtIndex:row] objectForKey:@"user_id"];
appDelegate.activeUserProfileName = [[self.userProfiles objectAtIndex:row] objectForKey:@"username"];
[self.friendSearchBar resignFirstResponder];

View file

@ -25,6 +25,7 @@
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
interactionLabel = nil;
avatarView = nil;
self.separatorInset = UIEdgeInsetsMake(0, 90, 0, 0);
// create the label and the avatar
UIImageView *avatar = [[UIImageView alloc] initWithFrame:CGRectZero];
@ -55,16 +56,19 @@
[super layoutSubviews];
// determine outer bounds
CGRect contentRect = self.contentView.bounds;
// position label to bounds
CGRect labelRect = contentRect;
labelRect.origin.x = labelRect.origin.x + leftMargin + avatarSize + leftMargin;
labelRect.origin.y = labelRect.origin.y + topMargin - 1;
labelRect.size.width = contentRect.size.width - leftMargin - avatarSize - leftMargin - rightMargin;
labelRect.size.height = contentRect.size.height - topMargin - bottomMargin;
self.interactionLabel.frame = labelRect;
[self.interactionLabel sizeToFit];
CGRect contentRect = self.frame;
CGRect labelFrame = self.interactionLabel.frame;
// position avatar to bounds
self.avatarView.frame = CGRectMake(leftMargin, topMargin, avatarSize, avatarSize);
// position label to bounds
labelFrame.origin.x = leftMargin*2 + avatarSize;
labelFrame.origin.y = topMargin - 1;
labelFrame.size.width = contentRect.size.width - leftMargin - avatarSize - leftMargin - rightMargin - 20;
labelFrame.size.height = contentRect.size.height - topMargin - bottomMargin;
self.interactionLabel.frame = labelFrame;
}
@ -113,11 +117,12 @@
NSString *txtWithTime = [NSString stringWithFormat:@"%@\n \n%@", txt, time];
NSMutableAttributedString* attrStr = [[NSMutableAttributedString alloc] initWithString:txtWithTime];
NSMutableParagraphStyle* style = [NSMutableParagraphStyle new];
style.lineBreakMode = NSLineBreakByWordWrapping;
style.alignment = NSTextAlignmentLeft;
style.lineSpacing = 1.0f;
[attrStr setAttributes:@{NSParagraphStyleAttributeName: style} range:NSMakeRange(0, [txtWithTime length])];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
paragraphStyle.alignment = NSTextAlignmentLeft;
paragraphStyle.lineSpacing = 1.0f;
[attrStr setAttributes:@{NSParagraphStyleAttributeName: paragraphStyle}
range:NSMakeRange(0, [txtWithTime length])];
[attrStr addAttributes:@{NSFontAttributeName:[UIFont fontWithName:@"Helvetica" size:13]} range:NSMakeRange(0, [txtWithTime length])];
if (self.highlighted) {

View file

@ -180,7 +180,7 @@
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
int userInteractions = [appDelegate.userInteractionsArray count];
NSInteger userInteractions = [appDelegate.userInteractionsArray count];
int minimumHeight;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
minimumHeight = MINIMUM_INTERACTION_HEIGHT_IPAD;
@ -210,7 +210,7 @@
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
int userInteractionsCount = [appDelegate.userInteractionsArray count];
NSInteger userInteractionsCount = [appDelegate.userInteractionsArray count];
return userInteractionsCount + 1;
}
@ -246,7 +246,7 @@
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
int userInteractions = [appDelegate.userInteractionsArray count];
NSInteger userInteractions = [appDelegate.userInteractionsArray count];
if (indexPath.row < userInteractions) {
NSDictionary *interaction = [appDelegate.userInteractionsArray objectAtIndex:indexPath.row];
NSString *category = [interaction objectForKey:@"category"];

View file

@ -48,7 +48,12 @@
self.emailInput.borderStyle = UITextBorderStyleRoundedRect;
self.signUpPasswordInput.borderStyle = UITextBorderStyleRoundedRect;
self.signUpUsernameInput.borderStyle = UITextBorderStyleRoundedRect;
[self.loginControl
setTitleTextAttributes:@{NSFontAttributeName:
[UIFont fontWithName:@"Helvetica-Bold" size:11.0f]}
forState:UIControlStateNormal];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
if (UIInterfaceOrientationIsPortrait(self.interfaceOrientation)) {
self.logInView.frame = CGRectMake(134, 180, 500, 300);
@ -218,6 +223,7 @@
[self.passwordInput setText:@""];
[self.signUpPasswordInput setText:@""];
[appDelegate reloadFeedsView:YES];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
@ -278,7 +284,7 @@
[self.signUpPasswordInput setText:@""];
// [appDelegate showFirstTimeUser];
[appDelegate reloadFeedsView:YES];
[self dismissViewControllerAnimated:NO completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
@ -357,6 +363,7 @@
// Login
usernameInput.frame = CGRectMake(20, 67, 280, 31);
usernameOrEmailLabel.alpha = 1.0;
usernameLabel.alpha = 0.0;
passwordInput.frame = CGRectMake(20, 129, 280, 31);
passwordLabel.frame = CGRectMake(21, 106, 212, 22);
@ -375,6 +382,7 @@
// Signup
usernameInput.frame = CGRectMake(20, 67, 130, 31);
usernameOrEmailLabel.alpha = 0.0;
usernameLabel.alpha = 1.0;
passwordInput.frame = CGRectMake(170, 67, 130, 31);
passwordLabel.frame = CGRectMake(171, 44, 212, 22);

View file

@ -21,6 +21,7 @@
self.textLabel.shadowColor = UIColorFromRGB(0xF0F0F0);
self.textLabel.shadowOffset = CGSizeMake(0, 1);
self.textLabel.font = [UIFont fontWithName:@"Helvetica-Bold" size:14.0];
[self setSeparatorInset:UIEdgeInsetsMake(0, 38, 0, 0)];
UIView *background = [[UIView alloc] init];
[background setBackgroundColor:UIColorFromRGB(0xFFFFFF)];
[self setBackgroundView:background];

View file

@ -29,7 +29,7 @@
- (NSArray *)pickerFolders;
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) IBOutlet FolderTextField *fromFolderInput;
@property (nonatomic) IBOutlet UITextField *fromFolderInput;
@property (nonatomic) IBOutlet FolderTextField *toFolderInput;
@property (nonatomic) IBOutlet UILabel *titleLabel;

View file

@ -36,15 +36,22 @@
- (void)viewDidLoad {
UIImageView *folderImage = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"g_icn_folder.png"]];
folderImage.frame = CGRectMake(-6, 0, 16, 16);
folderImage.frame = CGRectMake(0, 0, 24, 16);
[folderImage setContentMode:UIViewContentModeRight];
[toFolderInput setLeftView:folderImage];
[toFolderInput setLeftViewMode:UITextFieldViewModeAlways];
UIImageView *folderImage2 = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"g_icn_folder_rss.png"]];
folderImage2.frame = CGRectMake(-6, 0, 16, 16);
folderImage2.frame = CGRectMake(0, 0, 24, 16);
[folderImage2 setContentMode:UIViewContentModeRight];
[fromFolderInput setLeftView:folderImage2];
[fromFolderInput setLeftViewMode:UITextFieldViewModeAlways];
navBar.tintColor = [UIColor colorWithRed:0.16f green:0.36f blue:0.46 alpha:0.9];
CGRect frame = self.navBar.frame;
frame.size.height += 20;
self.navBar.frame = frame;
appDelegate = [NewsBlurAppDelegate sharedAppDelegate];
@ -71,7 +78,7 @@
[subview removeFromSuperview];
}
UIView *label = [appDelegate makeFeedTitle:appDelegate.activeFeed];
label.frame = CGRectMake(0,
label.frame = CGRectMake(8,
0,
200,
20);
@ -89,7 +96,7 @@
- (void)reload {
BOOL isTopLevel = [[appDelegate.activeFolder trim] isEqualToString:@""] ||
[appDelegate.activeFolder isEqual:@"everything"];
int row = 0;
NSInteger row = 0;
[toFolderInput setText:@""];
if (appDelegate.isRiverView) {
@ -295,15 +302,11 @@ numberOfRowsInComponent:(NSInteger)component {
@implementation FolderTextField
- (CGRect)textRectForBounds:(CGRect)bounds {
int margin = 18;
CGRect inset = CGRectMake(bounds.origin.x + margin, bounds.origin.y, bounds.size.width - margin, bounds.size.height);
return inset;
return CGRectInset(bounds, 24, 0);
}
- (CGRect)editingRectForBounds:(CGRect)bounds {
int margin = 18;
CGRect inset = CGRectMake(bounds.origin.x + margin, bounds.origin.y, bounds.size.width - margin, bounds.size.height);
return inset;
return CGRectInset(bounds, 24, 0);
}
@end

View file

@ -0,0 +1,15 @@
//
// NBBarButtonItem.h
// NewsBlur
//
// Created by Samuel Clay on 9/24/13.
// Copyright (c) 2013 NewsBlur. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface NBBarButtonItem : UIButton
@property (nonatomic, assign) BOOL onRightSide;
@end

View file

@ -0,0 +1,29 @@
//
// NBBarButtonItem.m
// NewsBlur
//
// Created by Samuel Clay on 9/24/13.
// Copyright (c) 2013 NewsBlur. All rights reserved.
//
#import "NBBarButtonItem.h"
@implementation NBBarButtonItem
@synthesize onRightSide;
- (UIEdgeInsets)alignmentRectInsets {
UIEdgeInsets insets;
if (![self isLeftButton] || self.onRightSide) {
insets = UIEdgeInsetsMake(0, 0, 0, 8.0f);
} else {
insets = UIEdgeInsetsMake(0, 8.0f, 0, 0);
}
return insets;
}
- (BOOL)isLeftButton {
return self.frame.origin.x < (self.window.frame.size.width / 2);
}
@end

View file

@ -16,6 +16,7 @@
#import "UserProfileViewController.h"
#import "InteractionCell.h"
#import "ActivityCell.h"
#import "FeedTableCell.h"
#import "FeedsMenuViewController.h"
#import "FeedDetailMenuViewController.h"
#import "FontSettingsViewController.h"
@ -25,8 +26,8 @@
#define NB_DEFAULT_MASTER_WIDTH 270
#define NB_DEFAULT_STORY_TITLE_HEIGHT 1004
#define NB_DEFAULT_SLIDER_INTERVAL 0.35
#define NB_DEFAULT_SLIDER_INTERVAL_OUT 0.35
#define NB_DEFAULT_SLIDER_INTERVAL 0.3
#define NB_DEFAULT_SLIDER_INTERVAL_OUT 0.3
#define NB_DEFAULT_SHARE_HEIGHT 144
#define NB_DEFAULT_STORY_TITLE_SNAP_THRESHOLD 60
@ -99,6 +100,8 @@
object:nil];
self.view.backgroundColor = UIColorFromRGB(0xC2C5BE);
self.navigationController.navigationBar.translucent = NO;
self.masterNavigationController.navigationBar.translucent = NO;
self.masterNavigationController = appDelegate.navigationController;
self.feedsViewController = appDelegate.feedsViewController;
@ -120,9 +123,11 @@
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:self.storyPageControl];
self.storyNavigationController = nav;
self.storyNavigationController.navigationBar.translucent = NO;
UINavigationController *shareNav = [[UINavigationController alloc] initWithRootViewController:self.shareViewController];
self.shareNavigationController = shareNav;
self.shareNavigationController.navigationBar.translucent = NO;
// set default y coordinate for feedDetailY from saved preferences
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
@ -140,12 +145,12 @@
// set up story titles stub
UIView * storyTitlesPlaceholder = [[UIView alloc] initWithFrame:CGRectZero];
storyTitlesPlaceholder.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;;
storyTitlesPlaceholder.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
storyTitlesPlaceholder.autoresizesSubviews = YES;
storyTitlesPlaceholder.backgroundColor = [UIColor whiteColor];
self.storyTitlesStub = storyTitlesPlaceholder;
[self.view addSubview:self.storyTitlesStub];
[self.view insertSubview:self.storyTitlesStub aboveSubview:self.storyNavigationController.view];
}
- (void)viewWillLayoutSubviews {
@ -202,10 +207,17 @@
[sender class] == [ActivityCell class]) {
InteractionCell *cell = (InteractionCell *)sender;
[popoverController presentPopoverFromRect:cell.bounds
inView:cell
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
[popoverController presentPopoverFromRect:cell.bounds
inView:cell
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
} else if ([sender class] == [FeedTableCell class]) {
FeedTableCell *cell = (FeedTableCell *)sender;
[popoverController presentPopoverFromRect:cell.bounds
inView:cell
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
} else if ([sender class] == [UIBarButtonItem class]) {
[popoverController presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionAny
@ -246,7 +258,7 @@
popoverController = [[UIPopoverController alloc]
initWithContentViewController:appDelegate.feedsMenuViewController];
[popoverController setDelegate:self];
int menuCount = [appDelegate.feedsMenuViewController.menuOptions count];
NSInteger menuCount = [appDelegate.feedsMenuViewController.menuOptions count];
[popoverController setPopoverContentSize:CGSizeMake(200, 38 * menuCount)];
[popoverController presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionAny
@ -266,7 +278,7 @@
[appDelegate.feedDetailMenuViewController buildMenuOptions];
popoverController.delegate = self;
int menuCount = [appDelegate.feedDetailMenuViewController.menuOptions count] + 2;
NSInteger menuCount = [appDelegate.feedDetailMenuViewController.menuOptions count] + 2;
[popoverController setPopoverContentSize:CGSizeMake(260, 38 * menuCount)];
[popoverController presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionAny
@ -308,6 +320,12 @@
[popoverController presentPopoverFromBarButtonItem:sender
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:NO];
} else if ([sender class] == [FeedTableCell class]) {
FeedTableCell *cell = (FeedTableCell *)sender;
[popoverController presentPopoverFromRect:cell.bounds
inView:cell
permittedArrowDirections:UIPopoverArrowDirectionAny
animated:YES];
} else {
CGRect frame = [sender CGRectValue];
[popoverController presentPopoverFromRect:frame
@ -361,7 +379,7 @@
- (void)adjustFeedDetailScreen {
CGRect vb = [self.view bounds];
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (UIInterfaceOrientationIsPortrait(orientation) && !self.storyTitlesOnLeft) {
// add the back button
@ -382,9 +400,6 @@
// remove the back button
self.storyPageControl.navigationItem.leftBarButtonItem = nil;
// remove center title
self.storyPageControl.navigationItem.titleView = nil;
if (![[self.masterNavigationController viewControllers] containsObject:self.feedDetailViewController]) {
[self.masterNavigationController pushViewController:self.feedDetailViewController animated:NO];
}
@ -396,6 +411,7 @@
- (void)adjustFeedDetailScreenForStoryTitles {
CGRect vb = [self.view bounds];
if (!self.storyTitlesOnLeft) {
if (self.storyTitlesYCoordinate > 890) {
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
@ -411,7 +427,7 @@
self.storyPageControl.navigationItem.leftBarButtonItem = nil;
// remove center title
self.storyPageControl.navigationItem.titleView = nil;
// self.storyPageControl.navigationItem.titleView = nil;
if (![[self.masterNavigationController viewControllers] containsObject:self.feedDetailViewController]) {
[self.masterNavigationController pushViewController:self.feedDetailViewController animated:NO];
@ -445,8 +461,8 @@
self.storyPageControl.navigationItem.leftBarButtonItem = self.storyPageControl.buttonBack;
// set center title
UIView *titleLabel = [appDelegate makeFeedTitle:appDelegate.activeFeed];
self.storyPageControl.navigationItem.titleView = titleLabel;
// UIView *titleLabel = [appDelegate makeFeedTitle:appDelegate.activeFeed];
// self.storyPageControl.navigationItem.titleView = titleLabel;
[UIView animateWithDuration:NB_DEFAULT_SLIDER_INTERVAL delay:0 options:UIViewAnimationOptionCurveEaseInOut animations:^{
// self.masterNavigationController.view.frame = CGRectMake(-NB_DEFAULT_MASTER_WIDTH, 0, NB_DEFAULT_MASTER_WIDTH, vb.size.height);
@ -488,49 +504,50 @@
// reset the storyDetailViewController components
self.storyPageControl.currentPage.webView.hidden = YES;
self.storyPageControl.nextPage.webView.hidden = YES;
self.storyPageControl.bottomPlaceholderToolbar.hidden = NO;
self.storyPageControl.bottomSize.hidden = NO;
self.storyPageControl.navigationItem.rightBarButtonItems = nil;
[self.storyPageControl resetPages];
int unreadCount = appDelegate.unreadCount;
[self.storyPageControl hidePages];
NSInteger unreadCount = appDelegate.unreadCount;
if (unreadCount == 0) {
self.storyPageControl.circularProgressView.percentage = 1;
} else {
self.storyPageControl.circularProgressView.percentage = 0;
}
// UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
// if (NO && UIInterfaceOrientationIsPortrait(orientation) && !self.storyTitlesOnLeft) {
// // CASE: story titles on bottom
// self.storyPageControl.navigationItem.leftBarButtonItem = self.storyPageControl.buttonBack;
//
// self.storyNavigationController.view.frame = CGRectMake(vb.size.width, 0, vb.size.width, storyTitlesYCoordinate);
// self.feedDetailViewController.view.frame = CGRectMake(vb.size.width,
// self.storyTitlesYCoordinate,
// vb.size.width,
// vb.size.height - storyTitlesYCoordinate);
// float largeTimeInterval = NB_DEFAULT_SLIDER_INTERVAL * ( vb.size.width - NB_DEFAULT_MASTER_WIDTH) / vb.size.width;
// float smallTimeInterval = NB_DEFAULT_SLIDER_INTERVAL * NB_DEFAULT_MASTER_WIDTH / vb.size.width;
//
// [UIView animateWithDuration:largeTimeInterval delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
// self.storyNavigationController.view.frame = CGRectMake(NB_DEFAULT_MASTER_WIDTH + 1, 0, vb.size.width, self.storyTitlesYCoordinate);
// self.feedDetailViewController.view.frame = CGRectMake(NB_DEFAULT_MASTER_WIDTH + 1,
// self.storyTitlesYCoordinate,
// vb.size.width,
// vb.size.height - storyTitlesYCoordinate);
// } completion:^(BOOL finished) {
// [UIView animateWithDuration:smallTimeInterval delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
// self.storyNavigationController.view.frame = CGRectMake(0, 0, vb.size.width, self.storyTitlesYCoordinate);
// self.feedDetailViewController.view.frame = CGRectMake(0, self.storyTitlesYCoordinate, vb.size.width, vb.size.height - storyTitlesYCoordinate);
// self.masterNavigationController.view.frame = CGRectMake(-NB_DEFAULT_MASTER_WIDTH, 0, NB_DEFAULT_MASTER_WIDTH, vb.size.height);
// } completion:^(BOOL finished) {
// [self.dashboardViewController.view removeFromSuperview];
// [self.masterNavigationController.view removeFromSuperview];
// }];
// }];
//
// UIView *titleLabel = [appDelegate makeFeedTitle:appDelegate.activeFeed];
// self.storyPageControl.navigationItem.titleView = titleLabel;
// } else {
UIView *titleLabel = [appDelegate makeFeedTitle:appDelegate.activeFeed];
self.storyPageControl.navigationItem.titleView = titleLabel;
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (UIInterfaceOrientationIsPortrait(orientation) && !self.storyTitlesOnLeft) {
// CASE: story titles on bottom
self.storyPageControl.navigationItem.leftBarButtonItem = self.storyPageControl.buttonBack;
self.storyPageControl.navigationItem.rightBarButtonItems = self.feedDetailViewController.navigationItem.rightBarButtonItems;
self.storyNavigationController.view.frame = CGRectMake(vb.size.width, 0, vb.size.width, storyTitlesYCoordinate);
self.feedDetailViewController.view.frame = CGRectMake(vb.size.width,
self.storyTitlesYCoordinate,
vb.size.width,
vb.size.height - storyTitlesYCoordinate);
float largeTimeInterval = NB_DEFAULT_SLIDER_INTERVAL * ( vb.size.width - NB_DEFAULT_MASTER_WIDTH) / vb.size.width;
float smallTimeInterval = NB_DEFAULT_SLIDER_INTERVAL * NB_DEFAULT_MASTER_WIDTH / vb.size.width;
[UIView animateWithDuration:largeTimeInterval delay:0 options:UIViewAnimationOptionCurveEaseIn animations:^{
self.storyNavigationController.view.frame = CGRectMake(NB_DEFAULT_MASTER_WIDTH + 1, 0, vb.size.width, self.storyTitlesYCoordinate);
self.feedDetailViewController.view.frame = CGRectMake(NB_DEFAULT_MASTER_WIDTH + 1,
self.storyTitlesYCoordinate,
vb.size.width,
vb.size.height - storyTitlesYCoordinate);
} completion:^(BOOL finished) {
[UIView animateWithDuration:smallTimeInterval delay:0 options:UIViewAnimationOptionCurveEaseOut animations:^{
self.storyNavigationController.view.frame = CGRectMake(0, 0, vb.size.width, self.storyTitlesYCoordinate);
self.feedDetailViewController.view.frame = CGRectMake(0, self.storyTitlesYCoordinate, vb.size.width, vb.size.height - storyTitlesYCoordinate);
self.masterNavigationController.view.frame = CGRectMake(-NB_DEFAULT_MASTER_WIDTH, 0, NB_DEFAULT_MASTER_WIDTH, vb.size.height);
} completion:^(BOOL finished) {
[self.dashboardViewController.view removeFromSuperview];
[self.masterNavigationController.view removeFromSuperview];
}];
}];
} else {
// CASE: story titles on left
[self.masterNavigationController
pushViewController:self.feedDetailViewController
@ -548,9 +565,10 @@
[self.dashboardViewController.view removeFromSuperview];
}];
self.storyPageControl.navigationItem.titleView = nil;
// self.storyPageControl.navigationItem.titleView = nil;
self.storyPageControl.navigationItem.leftBarButtonItem = nil;
// }
self.storyPageControl.navigationItem.rightBarButtonItem = nil;
}
}
- (void)transitionFromFeedDetail {
@ -693,11 +711,12 @@
CGRect vb = [self.view bounds];
// account for top toolbar and status bar
yCoordinate = yCoordinate + 44 + 20;
yCoordinate = yCoordinate + 64 + 20;
NSUserDefaults *userPreferences = [NSUserDefaults standardUserDefaults];
if (yCoordinate > 344 && yCoordinate <= (vb.size.height)) {
if (yCoordinate <= (vb.size.height)) {
yCoordinate = MAX(yCoordinate, 384);
self.storyTitlesYCoordinate = yCoordinate;
[userPreferences setInteger:yCoordinate forKey:@"storyTitlesYCoordinate"];
[userPreferences synchronize];
@ -735,7 +754,23 @@
0);
}
}
UITableView *stories = appDelegate.feedDetailViewController.storyTitlesTable;
NSInteger location = appDelegate.locationOfActiveStory;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:location inSection:0];
NSArray *visible = [stories visibleCells];
for (UITableViewCell *cell in visible) {
if ([stories indexPathForCell:cell].row == indexPath.row) {
indexPath = nil;
break;
}
}
if (indexPath && location >= 0) {
[stories selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionMiddle];
}
[appDelegate.feedDetailViewController.notifier setNeedsLayout];
}
-(void)keyboardWillShowOrHide:(NSNotification*)notification {
@ -767,7 +802,7 @@
NB_DEFAULT_SHARE_HEIGHT);
CGRect shareViewFrame = self.shareNavigationController.view.frame;
if (self.keyboardIsShown) {
if (self.keyboardIsShown && self.isSharingStory) {
if (UIInterfaceOrientationIsPortrait(orientation)) {
storyNavigationFrame.size.height = vb.size.height - NB_DEFAULT_SHARE_HEIGHT - keyboardFrame.size.height + 44;
shareViewFrame.origin.y = vb.size.height - NB_DEFAULT_SHARE_HEIGHT - keyboardFrame.size.height;
@ -775,12 +810,12 @@
storyNavigationFrame.size.height = vb.size.height - NB_DEFAULT_SHARE_HEIGHT - keyboardFrame.size.width + 44;
shareViewFrame.origin.y = vb.size.height - NB_DEFAULT_SHARE_HEIGHT - keyboardFrame.size.width;
}
} else {
} else if (self.isSharingStory) {
if (UIInterfaceOrientationIsPortrait(orientation)) {
storyNavigationFrame.size.height = vb.size.height - NB_DEFAULT_SHARE_HEIGHT + 44;
storyNavigationFrame.size.height = vb.size.height - NB_DEFAULT_SHARE_HEIGHT + 64;
shareViewFrame.origin.y = vb.size.height - NB_DEFAULT_SHARE_HEIGHT;
} else {
storyNavigationFrame.size.height = vb.size.height - NB_DEFAULT_SHARE_HEIGHT + 44;
storyNavigationFrame.size.height = vb.size.height - NB_DEFAULT_SHARE_HEIGHT + 64;
shareViewFrame.origin.y = vb.size.height - NB_DEFAULT_SHARE_HEIGHT;
}
}
@ -815,7 +850,7 @@
vb.size.height,
self.storyNavigationController.view.frame.size.width,
NB_DEFAULT_SHARE_HEIGHT);
if (UIInterfaceOrientationIsPortrait(orientation)) {
if (UIInterfaceOrientationIsPortrait(orientation) && !self.storyTitlesOnLeft) {
self.storyNavigationController.view.frame = CGRectMake(self.storyNavigationController.view.frame.origin.x,
0,
self.storyNavigationController.view.frame.size.width,

View file

@ -195,6 +195,9 @@
- (void)showIn:(float)time {
showing = YES;
CGRect frame = self.frame;
frame.size.width = self.view.frame.size.width;
self.frame = frame;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:time];

View file

@ -0,0 +1,15 @@
//
// NBSwipeableCell.h
// NewsBlur
//
// Created by Samuel Clay on 9/27/13.
// Copyright (c) 2013 NewsBlur. All rights reserved.
//
#import "MCSwipeTableViewCell.h"
@interface NBSwipeableCell : MCSwipeTableViewCell
- (UIImage *)imageByApplyingAlpha:(UIImage *)image withAlpha:(CGFloat) alpha;
@end

View file

@ -0,0 +1,65 @@
//
// NBSwipeableCell.m
// NewsBlur
//
// Created by Samuel Clay on 9/27/13.
// Copyright (c) 2013 NewsBlur. All rights reserved.
//
#import "NBSwipeableCell.h"
#import "MCSwipeTableViewCell.h"
@implementation NBSwipeableCell
- (void)setNeedsDisplay {
[super setNeedsDisplay];
for (UIView *view in self.contentView.subviews) {
[view setNeedsDisplay];
}
}
- (void)setNeedsLayout {
[super setNeedsLayout];
for (UIView *view in self.contentView.subviews) {
[view setNeedsLayout];
}
}
- (void) setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:NO];
if (animated) {
[CATransaction begin];
CATransition* animation = [CATransition animation];
animation.type = kCATransitionFade;
animation.duration = 0.6;
[animation setTimingFunction:[CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionDefault]];
[self.contentView.layer addAnimation:animation forKey:@"deselectRow"];
[CATransaction commit];
}
}
- (UIImage *)imageByApplyingAlpha:(UIImage *)image withAlpha:(CGFloat) alpha {
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0f);
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGRect area = CGRectMake(0, 0, image.size.width, image.size.height);
CGContextScaleCTM(ctx, 1, -1);
CGContextTranslateCTM(ctx, 0, -area.size.height);
CGContextSetBlendMode(ctx, kCGBlendModeMultiply);
CGContextSetAlpha(ctx, alpha);
CGContextDrawImage(ctx, area, image.CGImage);
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return newImage;
}
@end

View file

@ -75,6 +75,7 @@
MoveSiteViewController *moveSiteViewController;
TrainerViewController *trainerViewController;
OriginalStoryViewController *originalStoryViewController;
UINavigationController *originalStoryViewNavController;
UserProfileViewController *userProfileViewController;
IASKAppSettingsViewController *preferencesViewController;
@ -155,6 +156,7 @@
@property (nonatomic) UINavigationController *shareNavigationController;
@property (nonatomic) UINavigationController *trainNavigationController;
@property (nonatomic) UINavigationController *userProfileNavigationController;
@property (nonatomic) UINavigationController *originalStoryViewNavController;
@property (nonatomic) IBOutlet NBContainerViewController *masterContainerViewController;
@property (nonatomic) IBOutlet DashboardViewController *dashboardViewController;
@property (nonatomic) IBOutlet NewsBlurViewController *feedsViewController;
@ -258,7 +260,7 @@
- (void)setupReachability;
// social
- (NSDictionary *)getUser:(int)userId;
- (NSDictionary *)getUser:(NSInteger)userId;
- (void)showUserProfileModal:(id)sender;
- (void)pushUserProfile;
- (void)hideUserProfileModal;
@ -267,6 +269,7 @@
- (void)showMoveSite;
- (void)openTrainSite;
- (void)openTrainSiteWithFeedLoaded:(BOOL)feedLoaded from:(id)sender;
- (void)openTrainStory:(id)sender;
- (void)loadFeedDetailView;
- (void)loadTryFeedDetailView:(NSString *)feedId withStory:(NSString *)contentId isSocial:(BOOL)social withUser:(NSDictionary *)user showFindingStory:(BOOL)showHUD;
@ -294,17 +297,18 @@
- (void)showConnectToService:(NSString *)serviceName;
- (void)refreshUserProfile:(void(^)())callback;
- (int)indexOfNextUnreadStory;
- (int)locationOfNextUnreadStory;
- (int)indexOfNextStory;
- (int)locationOfNextStory;
- (int)indexOfActiveStory;
- (int)indexOfStoryId:(id)storyId;
- (int)locationOfActiveStory;
- (int)indexFromLocation:(int)location;
- (BOOL)isStoryUnread:(NSDictionary *)story;
- (NSInteger)indexOfNextUnreadStory;
- (NSInteger)locationOfNextUnreadStory;
- (NSInteger)indexOfNextStory;
- (NSInteger)locationOfNextStory;
- (NSInteger)indexOfActiveStory;
- (NSInteger)indexOfStoryId:(id)storyId;
- (NSInteger)locationOfActiveStory;
- (NSInteger)indexFromLocation:(NSInteger)location;
- (void)pushReadStory:(id)storyId;
- (id)popReadStory;
- (int)locationOfStoryId:(id)storyId;
- (NSInteger)locationOfStoryId:(id)storyId;
- (NSString *)activeOrder;
- (NSString *)activeReadFilter;
@ -314,10 +318,10 @@
- (void)addFeedUserProfiles:(NSArray *)activeFeedUserProfilesValue;
- (void)populateDictUnreadCounts;
- (int)unreadCount;
- (int)allUnreadCount;
- (int)unreadCountForFeed:(NSString *)feedId;
- (int)unreadCountForFolder:(NSString *)folderName;
- (NSInteger)unreadCount;
- (NSInteger)allUnreadCount;
- (NSInteger)unreadCountForFeed:(NSString *)feedId;
- (NSInteger)unreadCountForFolder:(NSString *)folderName;
- (UnreadCounts *)splitUnreadCountForFeed:(NSString *)feedId;
- (UnreadCounts *)splitUnreadCountForFolder:(NSString *)folderName;
- (void)markActiveStoryRead;
@ -327,12 +331,19 @@
- (void)markStoryRead:(NSDictionary *)story feed:(NSDictionary *)feed;
- (void)markStoryUnread:(NSString *)storyId feedId:(id)feedId;
- (void)markStoryUnread:(NSDictionary *)story feed:(NSDictionary *)feed;
- (void)markActiveStorySaved:(BOOL)saved;
- (void)markActiveFeedAllRead;
- (void)markActiveFolderAllRead;
- (void)markFeedAllRead:(id)feedId;
- (void)markFeedReadInCache:(NSArray *)feedIds;
- (void)markFeedReadInCache:(NSArray *)feedIds cutoffTimestamp:(NSInteger)cutoff;
- (void)markStoriesRead:(NSDictionary *)stories inFeeds:(NSArray *)feeds cutoffTimestamp:(NSInteger)cutoff;
- (void)requestFailedMarkStoryRead:(ASIFormDataRequest *)request;
- (void)finishMarkAllAsRead:(ASIHTTPRequest *)request;
- (void)markStory:story asSaved:(BOOL)saved;
- (void)calculateStoryLocations;
+ (int)computeStoryScore:(NSDictionary *)intelligence;
+ (NSInteger)computeStoryScore:(NSDictionary *)intelligence;
- (NSString *)extractFolderName:(NSString *)folderName;
- (NSString *)extractParentFolderName:(NSString *)folderName;
- (NSDictionary *)getFeed:(NSString *)feedId;
@ -341,15 +352,14 @@
+ (UIView *)makeGradientView:(CGRect)rect startColor:(NSString *)start endColor:(NSString *)end;
- (UIView *)makeFeedTitleGradient:(NSDictionary *)feed withRect:(CGRect)rect;
- (UIView *)makeFeedTitle:(NSDictionary *)feed;
- (UIButton *)makeRightFeedTitle:(NSDictionary *)feed;
- (void)toggleAuthorClassifier:(NSString *)author feedId:(NSString *)feedId;
- (void)toggleTagClassifier:(NSString *)tag feedId:(NSString *)feedId;
- (void)toggleTitleClassifier:(NSString *)title feedId:(NSString *)feedId score:(int)score;
- (void)toggleTitleClassifier:(NSString *)title feedId:(NSString *)feedId score:(NSInteger)score;
- (void)toggleFeedClassifier:(NSString *)feedId;
- (void)requestClassifierResponse:(ASIHTTPRequest *)request withFeed:(NSString *)feedId;
- (int)databaseSchemaVersion:(FMDatabase *)db;
- (NSInteger)databaseSchemaVersion:(FMDatabase *)db;
- (void)createDatabaseConnection;
- (void)setupDatabase:(FMDatabase *)db;
- (void)cancelOfflineQueue;
@ -359,9 +369,11 @@
- (BOOL)isReachabileForOffline;
- (void)storeUserProfiles:(NSArray *)userProfiles;
- (void)queueReadStories:(NSDictionary *)feedsStories;
- (BOOL)dequeueReadStoryHash:(NSString *)storyHash inFeed:(NSString *)storyFeedId;
- (void)flushQueuedReadStories:(BOOL)forceCheck withCallback:(void(^)())callback;
- (void)syncQueuedReadStories:(FMDatabase *)db withStories:(NSDictionary *)hashes withCallback:(void(^)())callback;
- (void)prepareActiveCachedImages:(FMDatabase *)db;
- (void)cleanImageCache;
- (void)deleteAllCachedImages;
@end

File diff suppressed because it is too large Load diff

View file

@ -15,16 +15,20 @@
#import "WEPopoverController.h"
#import "NBNotifier.h"
#import "IASKAppSettingsViewController.h"
#import "MCSwipeTableViewCell.h"
@class NewsBlurAppDelegate;
@interface NewsBlurViewController : BaseViewController
@interface NewsBlurViewController : BaseViewController
<UITableViewDelegate, UITableViewDataSource,
UIAlertViewDelegate, PullToRefreshViewDelegate,
ASIHTTPRequestDelegate, NSCacheDelegate,
WEPopoverControllerDelegate,
UIPopoverControllerDelegate,
IASKSettingsDelegate> {
IASKSettingsDelegate,
MCSwipeTableViewCellDelegate,
UIGestureRecognizerDelegate,
UIActionSheetDelegate> {
NewsBlurAppDelegate *appDelegate;
NSMutableDictionary * activeFeedLocations;
@ -60,6 +64,12 @@ IASKSettingsDelegate> {
@property (nonatomic) IBOutlet UIBarButtonItem * addBarButton;
@property (nonatomic) IBOutlet UIBarButtonItem * settingsBarButton;
@property (nonatomic) IBOutlet UIBarButtonItem * activitiesButton;
@property (nonatomic) IBOutlet UIBarButtonItem *userInfoBarButton;
@property (nonatomic) IBOutlet UIBarButtonItem *userAvatarButton;
@property (nonatomic) IBOutlet UILabel *neutralCount;
@property (nonatomic) IBOutlet UILabel *positiveCount;
@property (nonatomic) IBOutlet UILabel *userLabel;
@property (nonatomic) IBOutlet UIImageView *greenIcon;
@property (nonatomic) NSMutableDictionary *activeFeedLocations;
@property (nonatomic) NSMutableDictionary *stillVisibleFeeds;
@property (nonatomic) NSMutableDictionary *visibleFolders;
@ -71,6 +81,7 @@ IASKSettingsDelegate> {
@property (nonatomic) IBOutlet UISegmentedControl * intelligenceControl;
@property (nonatomic, retain) WEPopoverController *popoverController;
@property (nonatomic) NSIndexPath *currentRowAtIndexPath;
@property (nonatomic) NSInteger currentSection;
@property (strong, nonatomic) IBOutlet UIView *noFocusMessage;
@property (strong, nonatomic) IBOutlet UIBarButtonItem *toolbarLeftMargin;
@property (nonatomic, retain) NBNotifier *notifier;
@ -82,9 +93,12 @@ IASKSettingsDelegate> {
- (void)finishLoadingFeedList:(ASIHTTPRequest *)request;
- (void)finishLoadingFeedListWithDict:(NSDictionary *)results;
- (void)finishRefreshingFeedList:(ASIHTTPRequest *)request;
- (void)setUserAvatarLayout:(UIInterfaceOrientation)orientation;
- (void)didSelectSectionHeader:(UIButton *)button;
- (IBAction)selectIntelligence;
- (void)markFeedRead:(NSString *)feedId cutoffDays:(NSInteger)days;
- (void)markFeedsRead:(NSArray *)feedIds cutoffDays:(NSInteger)days;
- (void)requestFailedMarkStoryRead:(ASIFormDataRequest *)request;
- (void)finishMarkAllAsRead:(ASIHTTPRequest *)request;
- (void)didCollapseFolder:(UIButton *)button;
- (BOOL)isFeedVisible:(id)feedId;
- (void)changeToAllMode;
@ -102,7 +116,7 @@ IASKSettingsDelegate> {
- (void)refreshFeedList;
- (void)refreshFeedList:(id)feedId;
- (void)pullToRefreshViewShouldRefresh:(PullToRefreshView *)view;
- (void)loadOfflineFeeds;
- (void)loadOfflineFeeds:(BOOL)failed;
- (void)showUserProfile;
- (IBAction)showSettingsPopover:(id)sender;
- (IBAction)showInteractionsPopover:(id)sender;
@ -111,8 +125,8 @@ IASKSettingsDelegate> {
- (IBAction)tapAddSite:(id)sender;
- (void)resetToolbar;
- (void)layoutHeaderCounts:(UIInterfaceOrientation)orientation;
- (void)refreshHeaderCounts;
- (void)refreshHeaderCounts:(UIInterfaceOrientation)orientation;
- (void)settingsViewControllerDidEnd:(IASKAppSettingsViewController*)sender;
- (void)settingDidChange:(NSNotification*)notification;

File diff suppressed because it is too large Load diff

View file

@ -11,13 +11,13 @@
@class NewsBlurAppDelegate;
static const CGFloat kNavBarHeight = 58.0f;
static const CGFloat kNavBarHeight = 78.0f;
static const CGFloat kLabelHeight = 18.0f;
static const CGFloat kMargin = 6.0f;
static const CGFloat kSpacer = 2.0f;
static const CGFloat kLabelFontSize = 12.0f;
static const CGFloat kAddressHeight = 30.0f;
static const CGFloat kButtonWidth = 48.0f;
static const CGFloat kButtonWidth = 68.0f;
@interface OriginalStoryViewController : BaseViewController
<UIActionSheetDelegate, UITextFieldDelegate, UIWebViewDelegate> {
@ -37,6 +37,7 @@ static const CGFloat kButtonWidth = 48.0f;
}
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) IBOutlet UINavigationBar *navBar;
@property (nonatomic) IBOutlet UIBarButtonItem *closeButton;
@property (nonatomic) IBOutlet UIWebView *webView;
@property (nonatomic) IBOutlet UIBarButtonItem* back;
@ -47,6 +48,7 @@ static const CGFloat kButtonWidth = 48.0f;
@property (nonatomic) IBOutlet UITextField *pageUrl;
@property (nonatomic) IBOutlet UIToolbar *toolbar;
- (void)layoutNavBar;
- (void)layoutForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
- (IBAction) doCloseOriginalStoryViewController;
- (IBAction) doOpenActionSheet:(id)sender;

View file

@ -14,6 +14,7 @@
#import "MBProgressHUD.h"
#import "UIBarButtonItem+Image.h"
#import "UIActivitiesControl.h"
#import "NBBarButtonItem.h"
@implementation OriginalStoryViewController
@ -27,6 +28,7 @@
@synthesize pageTitle;
@synthesize pageUrl;
@synthesize toolbar;
@synthesize navBar;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
@ -37,6 +39,7 @@
- (void)viewWillAppear:(BOOL)animated {
// NSLog(@"Original Story View: %@", [appDelegate activeOriginalStoryURL]);
appDelegate.originalStoryViewNavController.navigationBar.hidden = YES;
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:appDelegate.activeOriginalStoryURL] ;
[self updateAddress:request];
@ -51,6 +54,10 @@
[HUD hide:YES afterDelay:2];
}
- (void)viewDidAppear:(BOOL)animated {
[self layoutNavBar];
}
- (void)viewWillDisappear:(BOOL)animated {
if ([self.webView isLoading]) {
[self.webView stopLoading];
@ -65,19 +72,11 @@
[self layoutForInterfaceOrientation:toInterfaceOrientation];
}
- (void)viewDidLoad {
CGRect navBarFrame = self.view.bounds;
navBarFrame.size.height = kNavBarHeight;
UINavigationBar *navBar = [[UINavigationBar alloc] initWithFrame:navBarFrame];
navBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
toolbar.autoresizingMask = toolbar.autoresizingMask | UIViewAutoresizingFlexibleHeight | UIViewContentModeBottom;
[navBar
setBackgroundImage:[UIImage imageNamed:@"toolbar_tall_background.png"]
forBarMetrics:UIBarMetricsDefault];
CGRect labelFrame = CGRectMake(kMargin, kSpacer,
navBar.bounds.size.width - 2*kMargin,
- (void)viewDidLoad {
CGRect labelFrame = CGRectMake(kMargin, kSpacer + 20,
navBar.bounds.size.width - 2*kMargin,
kLabelHeight);
UILabel *label = [[UILabel alloc] initWithFrame:labelFrame];
label.autoresizingMask = UIViewAutoresizingFlexibleWidth;
label.backgroundColor = [UIColor clearColor];
@ -90,29 +89,28 @@
[navBar addSubview:label];
self.pageTitle = label;
UIBarButtonItem *close = [[UIBarButtonItem alloc]
initWithTitle:@"Close"
style:UIBarButtonItemStyleBordered
target:self
UIBarButtonItem *close = [[UIBarButtonItem alloc]
initWithTitle:@"Close"
style:UIBarButtonItemStyleBordered
target:self
action:@selector(doCloseOriginalStoryViewController)];
close.width = kButtonWidth;
CGRect closeButtonFrame = CGRectMake(0,
kSpacer*2.0 + kLabelHeight - 7.0f,
CGRect closeButtonFrame = CGRectMake(-20,
kSpacer*2.0 + kLabelHeight - 7.0f + 20,
kButtonWidth + kMargin,
44.0);
TransparentToolbar* tools = [[TransparentToolbar alloc]
TransparentToolbar* tools = [[TransparentToolbar alloc]
initWithFrame:closeButtonFrame];
[tools setItems:[NSArray arrayWithObject:close] animated:NO];
[tools setTintColor:UIColorFromRGB(0x183353)];
[navBar addSubview:tools];
CGRect addressFrame = CGRectMake(closeButtonFrame.origin.x +
CGRect addressFrame = CGRectMake(closeButtonFrame.origin.x +
closeButtonFrame.size.width +
kMargin,
kSpacer*2.0 + kLabelHeight,
kMargin,
kSpacer*2.0 + kLabelHeight + 20,
labelFrame.size.width
- kButtonWidth - kMargin*2,
- kButtonWidth - kMargin*2 + 20,
kAddressHeight);
UITextField *address = [[UITextField alloc] initWithFrame:addressFrame];
address.autoresizingMask = UIViewAutoresizingFlexibleWidth;
@ -128,43 +126,43 @@
address.delegate = self;
[navBar addSubview:address];
self.pageUrl = address;
[self.view addSubview:navBar];
UIImage *backImage = [UIImage imageNamed:@"barbutton_back.png"];
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeCustom];
NBBarButtonItem *backButton = [NBBarButtonItem buttonWithType:UIButtonTypeCustom];
backButton.bounds = CGRectMake(0, 0, 44, 44);
[backButton setImage:backImage forState:UIControlStateNormal];
[backButton addTarget:self action:@selector(webViewGoBack:) forControlEvents:UIControlEventTouchUpInside];
[back setCustomView:backButton];
UIImage *forwardImage = [UIImage imageNamed:@"barbutton_forward.png"];
UIButton *forwardButton = [UIButton buttonWithType:UIButtonTypeCustom];
NBBarButtonItem *forwardButton = [NBBarButtonItem buttonWithType:UIButtonTypeCustom];
forwardButton.bounds = CGRectMake(0, 0, 44, 44);
[forwardButton setImage:forwardImage forState:UIControlStateNormal];
[forwardButton addTarget:self action:@selector(webViewGoForward:) forControlEvents:UIControlEventTouchUpInside];
[forward setCustomView:forwardButton];
UIImage *refreshImage = [UIImage imageNamed:@"barbutton_refresh.png"];
UIButton *refreshButton = [UIButton buttonWithType:UIButtonTypeCustom];
NBBarButtonItem *refreshButton = [NBBarButtonItem buttonWithType:UIButtonTypeCustom];
refreshButton.bounds = CGRectMake(0, 0, 44, 44);
[refreshButton setImage:refreshImage forState:UIControlStateNormal];
[refreshButton addTarget:self action:@selector(webViewRefresh:) forControlEvents:UIControlEventTouchUpInside];
[refresh setCustomView:refreshButton];
UIImage *sendtoImage = [UIImage imageNamed:@"barbutton_sendto.png"];
UIButton *sendtoButton = [UIButton buttonWithType:UIButtonTypeCustom];
NBBarButtonItem *sendtoButton = [NBBarButtonItem buttonWithType:UIButtonTypeCustom];
sendtoButton.bounds = CGRectMake(0, 0, 44, 44);
[sendtoButton setImage:sendtoImage forState:UIControlStateNormal];
[sendtoButton addTarget:self action:@selector(doOpenActionSheet:) forControlEvents:UIControlEventTouchUpInside];
[pageAction setCustomView:sendtoButton];
CGRect webViewFrame = CGRectMake(0,
navBarFrame.origin.y +
navBarFrame.size.height,
self.view.frame.size.width,
self.view.frame.size.height - kNavBarHeight - 44);
self.webView.frame = webViewFrame;
}
- (void)layoutNavBar {
CGRect navBarFrame = self.view.bounds;
navBarFrame.size.height = kNavBarHeight;
navBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
navBar.frame = navBarFrame;
navBar.translucent = NO;
toolbar.translucent = NO;
}
- (IBAction)webViewGoBack:(id)sender {
@ -180,6 +178,8 @@
}
- (void) layoutForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
[self layoutNavBar];
CGSize toolbarSize = [self.toolbar sizeThatFits:self.view.bounds.size];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
self.toolbar.frame = CGRectMake(-10.0f,

View file

@ -181,10 +181,14 @@
// Calculate the expected size based on the font and linebreak mode of your label
CGSize maximumLabelSize = CGSizeMake(width - kTopBadgeTextXCoordinate - 10, 60);
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.lineBreakMode = bio.lineBreakMode;
CGSize expectedLabelSize = [bio.text
sizeWithFont:bio.font
constrainedToSize:maximumLabelSize
lineBreakMode:bio.lineBreakMode];
boundingRectWithSize:maximumLabelSize
options:nil
attributes:@{NSFontAttributeName: bio.font,
NSParagraphStyleAttributeName: paragraphStyle}
context:nil].size;
CGRect newFrame = bio.frame;
newFrame.size.height = expectedLabelSize.height;
bio.frame = newFrame;

View file

@ -23,6 +23,7 @@
@property (nonatomic) IBOutlet UILabel *storyTitle;
@property (nonatomic) NSString * activeReplyId;
@property (nonatomic) NSString * activeCommentId;
@property (nonatomic) NSString * activeStoryId;
@property (nonatomic) NSString* currentType;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *keyboardHeight;

View file

@ -26,6 +26,7 @@
@synthesize appDelegate;
@synthesize activeReplyId;
@synthesize activeCommentId;
@synthesize activeStoryId;
@synthesize currentType;
@synthesize storyTitle;
@ -178,6 +179,8 @@
self.twitterButton.frame = CGRectMake(v.width - 20 - bW*3 - bP*2, o.y + c.height + bP, bW, bH);
self.facebookButton.frame = CGRectMake(v.width - 20 - bW*2 - bP*1, o.y + c.height + bP, bW, bH);
self.appdotnetButton.frame = CGRectMake(v.width - 20 - bW*1 - bP*0, o.y + c.height + bP, bW, bH);
[self onTextChange:nil];
}
- (IBAction)doCancelButton:(id)sender {
@ -270,8 +273,10 @@
// Don't bother to reset comment field for replies while on the same story.
// It'll get cleared out on a new story and when posting a reply.
if (!self.activeCommentId || ![self.activeCommentId isEqualToString:userId]) {
if (!self.activeCommentId || ![self.activeCommentId isEqualToString:userId] ||
!self.activeStoryId || ![self.activeStoryId isEqualToString:[appDelegate.activeStory objectForKey:@"id"]]) {
self.activeCommentId = userId;
self.activeStoryId = [appDelegate.activeStory objectForKey:@"id"];
self.commentField.text = @"";
}
} else if ([type isEqualToString: @"edit-share"]) {
@ -282,7 +287,7 @@
// get old comment
self.commentField.text = [self stringByStrippingHTML:[appDelegate.activeComment objectForKey:@"comments"]];
[submitButton setTitle:@"Save your comments"];
[submitButton setTitle:@"Share with comments"];
[submitButton setAction:(@selector(doShareThisStory:))];
} else if ([type isEqualToString: @"share"]) {
facebookButton.hidden = NO;
@ -333,9 +338,10 @@
}
if (appDelegate.isSocialRiverView) {
if ([appDelegate.activeStory objectForKey:@"friend_user_ids"] != nil) {
NSString *sourceUserIdStr = [NSString stringWithFormat:@"%@", [[appDelegate.activeStory objectForKey:@"friend_user_ids"] objectAtIndex:0]];
[request setPostValue:sourceUserIdStr forKey:@"source_user_id"];
if ([[appDelegate.activeStory objectForKey:@"friend_user_ids"] count] > 0) {
[request setPostValue:[NSString stringWithFormat:@"%@", [appDelegate.activeStory objectForKey:@"friend_user_ids"][0]] forKey:@"source_user_id"];
} else if ([[appDelegate.activeStory objectForKey:@"public_user_ids"] count] > 0) {
[request setPostValue:[NSString stringWithFormat:@"%@", [appDelegate.activeStory objectForKey:@"public_user_ids"][0]] forKey:@"source_user_id"];
}
} else {
if ([appDelegate.activeStory objectForKey:@"social_user_id"] != nil) {
@ -373,8 +379,8 @@
appDelegate.activeFeedUserProfiles = [DataUtilities
updateUserProfiles:appDelegate.activeFeedUserProfiles
withNewUserProfiles:userProfiles];
[appDelegate.feedDetailViewController redrawUnreadStory];
[self replaceStory:[results objectForKey:@"story"] withReplyId:nil];
[appDelegate.feedDetailViewController redrawUnreadStory];
}
# pragma mark
@ -483,8 +489,8 @@
-(void)onTextChange:(NSNotification*)notification {
NSString *text = self.commentField.text;
if ([self.submitButton.title isEqualToString:@"Share this story"] ||
[self.submitButton.title isEqualToString:@"Share with comments"]) {
if ([self.currentType isEqualToString: @"share"] ||
[self.currentType isEqualToString:@"edit-share"]) {
if (text.length) {
self.submitButton.title = @"Share with comments";
} else {

View file

@ -1,365 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="8.00">
<data>
<int key="IBDocument.SystemTarget">1552</int>
<string key="IBDocument.SystemVersion">12C3006</string>
<string key="IBDocument.InterfaceBuilderVersion">3084</string>
<string key="IBDocument.AppKitVersion">1187.34</string>
<string key="IBDocument.HIToolboxVersion">625.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="NS.object.0">2083</string>
</object>
<array key="IBDocument.IntegratedClassDependencies">
<string>IBProxyObject</string>
<string>IBUIButton</string>
<string>IBUITextView</string>
<string>IBUIView</string>
</array>
<array key="IBDocument.PluginDependencies">
<string>com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
</array>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<array class="NSMutableArray" key="IBDocument.RootObjects" id="1000">
<object class="IBProxyObject" id="841351856">
<string key="IBProxiedObjectIdentifier">IBFilesOwner</string>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
<object class="IBProxyObject" id="606714003">
<string key="IBProxiedObjectIdentifier">IBFirstResponder</string>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
<object class="IBUIView" id="766721923">
<reference key="NSNextResponder"/>
<int key="NSvFlags">319</int>
<array class="NSMutableArray" key="NSSubviews">
<object class="IBUITextView" id="993868796">
<reference key="NSNextResponder" ref="766721923"/>
<int key="NSvFlags">303</int>
<string key="NSFrame">{{20, 15}, {664, 39}}</string>
<reference key="NSSuperview" ref="766721923"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MSAxIDEAA</bytes>
</object>
<bool key="IBUIClipsSubviews">YES</bool>
<bool key="IBUIMultipleTouchEnabled">YES</bool>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<bool key="IBUIShowsHorizontalScrollIndicator">NO</bool>
<string key="IBUIText"/>
<object class="IBUITextInputTraits" key="IBUITextInputTraits">
<int key="IBUIAutocapitalizationType">2</int>
<string key="targetRuntimeIdentifier">IBCocoaTouchFramework</string>
</object>
<object class="IBUIFontDescription" key="IBUIFontDescription">
<int key="type">1</int>
<double key="pointSize">14</double>
</object>
<object class="NSFont" key="IBUIFont">
<string key="NSName">Helvetica</string>
<double key="NSSize">14</double>
<int key="NSfFlags">16</int>
</object>
</object>
<object class="IBUIButton" id="489631384">
<reference key="NSNextResponder" ref="766721923"/>
<int key="NSvFlags">265</int>
<string key="NSFrame">{{580, 62}, {48, 36}}</string>
<reference key="NSSuperview" ref="766721923"/>
<reference key="NSNextKeyView" ref="578316005"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<bool key="IBUIOpaque">NO</bool>
<int key="IBUITag">1</int>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<int key="IBUIContentHorizontalAlignment">0</int>
<int key="IBUIContentVerticalAlignment">0</int>
<double key="IBUIImageEdgeInsets.top">2</double>
<double key="IBUIImageEdgeInsets.bottom">2</double>
<double key="IBUIImageEdgeInsets.left">4</double>
<double key="IBUIImageEdgeInsets.right">4</double>
<object class="NSColor" key="IBUIHighlightedTitleColor" id="399517210">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
<object class="NSColor" key="IBUINormalTitleColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes>
</object>
<object class="NSColor" key="IBUINormalTitleShadowColor" id="485559184">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MC41AA</bytes>
</object>
<object class="NSCustomResource" key="IBUISelectedImage">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">story_share_twitter_active.png</string>
</object>
<object class="NSCustomResource" key="IBUINormalImage">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">story_share_twitter.png</string>
</object>
<object class="NSCustomResource" key="IBUISelectedBackgroundImage" id="591220768">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">white_spacer.png</string>
</object>
<object class="IBUIFontDescription" key="IBUIFontDescription" id="562180707">
<int key="type">2</int>
<double key="pointSize">15</double>
</object>
<object class="NSFont" key="IBUIFont" id="234883255">
<string key="NSName">Helvetica-Bold</string>
<double key="NSSize">15</double>
<int key="NSfFlags">16</int>
</object>
</object>
<object class="IBUIButton" id="290108663">
<reference key="NSNextResponder" ref="766721923"/>
<int key="NSvFlags">265</int>
<string key="NSFrame">{{524, 62}, {48, 36}}</string>
<reference key="NSSuperview" ref="766721923"/>
<reference key="NSNextKeyView" ref="489631384"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<bool key="IBUIOpaque">NO</bool>
<int key="IBUITag">3</int>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<int key="IBUIContentHorizontalAlignment">0</int>
<int key="IBUIContentVerticalAlignment">0</int>
<double key="IBUIImageEdgeInsets.top">2</double>
<double key="IBUIImageEdgeInsets.bottom">2</double>
<double key="IBUIImageEdgeInsets.left">4</double>
<double key="IBUIImageEdgeInsets.right">4</double>
<reference key="IBUIHighlightedTitleColor" ref="399517210"/>
<object class="NSColor" key="IBUINormalTitleColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes>
</object>
<reference key="IBUINormalTitleShadowColor" ref="485559184"/>
<object class="NSCustomResource" key="IBUISelectedImage">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">story_share_appnet_active.png</string>
</object>
<object class="NSCustomResource" key="IBUINormalImage">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">story_share_appnet.png</string>
</object>
<reference key="IBUISelectedBackgroundImage" ref="591220768"/>
<reference key="IBUIFontDescription" ref="562180707"/>
<reference key="IBUIFont" ref="234883255"/>
</object>
<object class="IBUIButton" id="578316005">
<reference key="NSNextResponder" ref="766721923"/>
<int key="NSvFlags">265</int>
<string key="NSFrame">{{636, 62}, {48, 36}}</string>
<reference key="NSSuperview" ref="766721923"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<bool key="IBUIOpaque">NO</bool>
<int key="IBUITag">2</int>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
<int key="IBUIContentHorizontalAlignment">0</int>
<int key="IBUIContentVerticalAlignment">0</int>
<double key="IBUIImageEdgeInsets.top">2</double>
<double key="IBUIImageEdgeInsets.bottom">2</double>
<double key="IBUIImageEdgeInsets.left">4</double>
<double key="IBUIImageEdgeInsets.right">4</double>
<reference key="IBUIHighlightedTitleColor" ref="399517210"/>
<object class="NSColor" key="IBUINormalTitleColor">
<int key="NSColorSpace">1</int>
<bytes key="NSRGB">MC4xOTYwNzg0MzQ2IDAuMzA5ODAzOTMyOSAwLjUyMTU2ODY1NgA</bytes>
</object>
<reference key="IBUINormalTitleShadowColor" ref="485559184"/>
<object class="NSCustomResource" key="IBUISelectedImage">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">story_share_facebook_active.png</string>
</object>
<object class="NSCustomResource" key="IBUINormalImage">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">story_share_facebook.png</string>
</object>
<reference key="IBUISelectedBackgroundImage" ref="591220768"/>
<reference key="IBUIFontDescription" ref="562180707"/>
<reference key="IBUIFont" ref="234883255"/>
</object>
</array>
<string key="NSFrameSize">{704, 100}</string>
<reference key="NSSuperview"/>
<object class="NSColor" key="IBUIBackgroundColor">
<int key="NSColorSpace">2</int>
<bytes key="NSRGB">MC45MDk4MDM5ODY1IDAuOTE3NjQ3MTIzMyAwLjg5NDExNzcxMwA</bytes>
</object>
<bool key="IBUIClearsContextBeforeDrawing">NO</bool>
<object class="IBUISimulatedSizeMetrics" key="IBUISimulatedDestinationMetrics">
<string key="IBUISimulatedSizeMetricsClass">IBUISimulatedFreeformSizeMetricsSentinel</string>
<string key="IBUIDisplayName">Freeform</string>
</object>
<string key="targetRuntimeIdentifier">IBIPadFramework</string>
</object>
</array>
<object class="IBObjectContainer" key="IBDocument.Objects">
<array class="NSMutableArray" key="connectionRecords">
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">view</string>
<reference key="source" ref="841351856"/>
<reference key="destination" ref="766721923"/>
</object>
<int key="connectionID">3</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">commentField</string>
<reference key="source" ref="841351856"/>
<reference key="destination" ref="993868796"/>
</object>
<int key="connectionID">29</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">twitterButton</string>
<reference key="source" ref="841351856"/>
<reference key="destination" ref="489631384"/>
</object>
<int key="connectionID">54</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">facebookButton</string>
<reference key="source" ref="841351856"/>
<reference key="destination" ref="578316005"/>
</object>
<int key="connectionID">55</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchOutletConnection" key="connection">
<string key="label">appdotnetButton</string>
<reference key="source" ref="841351856"/>
<reference key="destination" ref="290108663"/>
</object>
<int key="connectionID">56</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">doToggleButton:</string>
<reference key="source" ref="489631384"/>
<reference key="destination" ref="841351856"/>
<int key="IBEventType">7</int>
</object>
<int key="connectionID">58</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">doToggleButton:</string>
<reference key="source" ref="290108663"/>
<reference key="destination" ref="841351856"/>
<int key="IBEventType">7</int>
</object>
<int key="connectionID">57</int>
</object>
<object class="IBConnectionRecord">
<object class="IBCocoaTouchEventConnection" key="connection">
<string key="label">doToggleButton:</string>
<reference key="source" ref="578316005"/>
<reference key="destination" ref="841351856"/>
<int key="IBEventType">7</int>
</object>
<int key="connectionID">59</int>
</object>
</array>
<object class="IBMutableOrderedSet" key="objectRecords">
<array key="orderedObjects">
<object class="IBObjectRecord">
<int key="objectID">0</int>
<array key="object" id="0"/>
<reference key="children" ref="1000"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="841351856"/>
<reference key="parent" ref="0"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="606714003"/>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">2</int>
<reference key="object" ref="766721923"/>
<array class="NSMutableArray" key="children">
<reference ref="993868796"/>
<reference ref="489631384"/>
<reference ref="290108663"/>
<reference ref="578316005"/>
</array>
<reference key="parent" ref="0"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">19</int>
<reference key="object" ref="993868796"/>
<reference key="parent" ref="766721923"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">51</int>
<reference key="object" ref="489631384"/>
<reference key="parent" ref="766721923"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">52</int>
<reference key="object" ref="290108663"/>
<reference key="parent" ref="766721923"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">53</int>
<reference key="object" ref="578316005"/>
<reference key="parent" ref="766721923"/>
</object>
</array>
</object>
<dictionary class="NSMutableDictionary" key="flattenedProperties">
<string key="-1.CustomClassName">ShareViewController</string>
<string key="-1.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="-2.CustomClassName">UIResponder</string>
<string key="-2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="19.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="2.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<string key="51.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<real value="2" key="51.IBUIButtonInspectorSelectedEdgeInsetMetadataKey"/>
<real value="2" key="51.IBUIButtonInspectorSelectedStateConfigurationMetadataKey"/>
<string key="52.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<real value="0.0" key="52.IBUIButtonInspectorSelectedEdgeInsetMetadataKey"/>
<real value="2" key="52.IBUIButtonInspectorSelectedStateConfigurationMetadataKey"/>
<string key="53.IBPluginDependency">com.apple.InterfaceBuilder.IBCocoaTouchPlugin</string>
<real value="2" key="53.IBUIButtonInspectorSelectedEdgeInsetMetadataKey"/>
<real value="2" key="53.IBUIButtonInspectorSelectedStateConfigurationMetadataKey"/>
</dictionary>
<dictionary class="NSMutableDictionary" key="unlocalizedProperties"/>
<nil key="activeLocalization"/>
<dictionary class="NSMutableDictionary" key="localizations"/>
<nil key="sourceID"/>
<int key="maxID">59</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes"/>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBIPadFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencyDefaults">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS</string>
<real value="1552" key="NS.object.0"/>
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<dictionary class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
<string key="story_share_appnet.png">{12, 12}</string>
<string key="story_share_appnet_active.png">{12, 12}</string>
<string key="story_share_facebook.png">{12, 12}</string>
<string key="story_share_facebook_active.png">{12, 12}</string>
<string key="story_share_twitter.png">{12, 12}</string>
<string key="story_share_twitter_active.png">{12, 12}</string>
<string key="white_spacer.png">{1, 1}</string>
</dictionary>
<string key="IBCocoaTouchPluginVersion">2083</string>
</data>
</archive>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.iPad.XIB" version="3.0" toolsVersion="4510" systemVersion="12F37" targetRuntime="iOS.CocoaTouch.iPad" propertyAccessControl="none">
<dependencies>
<deployment defaultVersion="1792" identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="3742"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ShareViewController">
<connections>
<outlet property="appdotnetButton" destination="52" id="56"/>
<outlet property="commentField" destination="19" id="29"/>
<outlet property="facebookButton" destination="53" id="55"/>
<outlet property="twitterButton" destination="51" id="54"/>
<outlet property="view" destination="2" id="3"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="2">
<rect key="frame" x="0.0" y="0.0" width="704" height="100"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<subviews>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" showsHorizontalScrollIndicator="NO" id="19">
<rect key="frame" x="20" y="15" width="664" height="39"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<button opaque="NO" tag="1" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="51">
<rect key="frame" x="580" y="62" width="48" height="36"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<inset key="imageEdgeInsets" minX="4" minY="2" maxX="4" maxY="2"/>
<state key="normal" image="story_share_twitter.png">
<color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<state key="selected" image="story_share_twitter_active.png" backgroundImage="white_spacer.png"/>
<state key="highlighted">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="doToggleButton:" destination="-1" eventType="touchUpInside" id="58"/>
</connections>
</button>
<button opaque="NO" tag="3" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="52">
<rect key="frame" x="524" y="62" width="48" height="36"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<inset key="imageEdgeInsets" minX="4" minY="2" maxX="4" maxY="2"/>
<state key="normal" image="story_share_appnet.png">
<color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<state key="selected" image="story_share_appnet_active.png" backgroundImage="white_spacer.png"/>
<state key="highlighted">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="doToggleButton:" destination="-1" eventType="touchUpInside" id="57"/>
</connections>
</button>
<button opaque="NO" tag="2" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="53">
<rect key="frame" x="636" y="62" width="48" height="36"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="15"/>
<inset key="imageEdgeInsets" minX="4" minY="2" maxX="4" maxY="2"/>
<state key="normal" image="story_share_facebook.png">
<color key="titleColor" red="0.19607843459999999" green="0.30980393290000002" blue="0.52156865600000002" alpha="1" colorSpace="calibratedRGB"/>
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<state key="selected" image="story_share_facebook_active.png" backgroundImage="white_spacer.png"/>
<state key="highlighted">
<color key="titleColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="doToggleButton:" destination="-1" eventType="touchUpInside" id="59"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="0.94625342150000002" alpha="1" colorSpace="calibratedWhite"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
</view>
</objects>
<resources>
<image name="story_share_appnet.png" width="12" height="12"/>
<image name="story_share_appnet_active.png" width="12" height="12"/>
<image name="story_share_facebook.png" width="12" height="12"/>
<image name="story_share_facebook_active.png" width="12" height="12"/>
<image name="story_share_twitter.png" width="12" height="12"/>
<image name="story_share_twitter_active.png" width="12" height="12"/>
<image name="white_spacer.png" width="1" height="1"/>
</resources>
</document>

View file

@ -58,13 +58,14 @@ static UIFont *indicatorFont = nil;
font = [UIFont fontWithName:@"Helvetica-Bold" size:11];
textColor = UIColorFromRGB(0x606060);
[textColor set];
[self.siteTitle
drawInRect:CGRectMake(leftMargin + 20, 6, rect.size.width - 20, 21)
withFont:font
lineBreakMode:NSLineBreakByTruncatingTail
alignment:NSTextAlignmentLeft];
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByTruncatingTail;
paragraphStyle.alignment = NSTextAlignmentLeft;
[self.siteTitle drawInRect:CGRectMake(leftMargin + 20, 6, rect.size.width - 20, 21)
withAttributes:@{NSFontAttributeName: font,
NSForegroundColorAttributeName: textColor,
NSParagraphStyleAttributeName: paragraphStyle}];
// feed bar
CGContextSetStrokeColor(context, CGColorGetComponents([self.feedColorBar CGColor])); //feedColorBarTopBorder
@ -77,7 +78,7 @@ static UIFont *indicatorFont = nil;
CGContextSetStrokeColor(context, CGColorGetComponents([self.feedColorBar CGColor])); //feedColorBarTopBorder
CGContextSetLineWidth(context, 6.0f);
CGContextBeginPath(context);
float width = self.bounds.size.width - 20.0f;
float width = self.bounds.size.width - 3.0f;
CGContextMoveToPoint(context, width, 1.0f);
CGContextAddLineToPoint(context, width, self.frame.size.height);
CGContextStrokePath(context);

View file

@ -17,6 +17,7 @@
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
activityLabel = nil;
faviconView = nil;
self.separatorInset = UIEdgeInsetsMake(0, 52, 0, 0);
// create favicon and label in view
UIImageView *favicon = [[UIImageView alloc] initWithFrame:CGRectZero];
@ -42,25 +43,26 @@
[super layoutSubviews];
// determine outer bounds
CGRect contentRect = self.contentView.bounds;
[self.activityLabel sizeToFit];
CGRect contentRect = self.frame;
CGRect labelFrame = self.activityLabel.frame;
// position avatar to bounds
self.faviconView.frame = CGRectMake(leftMargin, topMargin, avatarSize, avatarSize);
// position label to bounds
CGRect labelRect = contentRect;
labelRect.origin.x = labelRect.origin.x + leftMargin + avatarSize + leftMargin;
labelRect.origin.y = labelRect.origin.y + topMargin - 1;
labelRect.size.width = contentRect.size.width - leftMargin - avatarSize - leftMargin - rightMargin;
labelRect.size.height = contentRect.size.height - topMargin - bottomMargin;
self.activityLabel.frame = labelRect;
labelFrame.origin.x = leftMargin*2 + avatarSize;
labelFrame.origin.y = topMargin - 1;
labelFrame.size.width = contentRect.size.width - leftMargin - avatarSize - leftMargin - rightMargin - 20;
labelFrame.size.height = contentRect.size.height - topMargin - bottomMargin;
self.activityLabel.frame = labelFrame;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
self.activityLabel.backgroundColor = UIColorFromRGB(0xd7dadf);
} else {
self.activityLabel.backgroundColor = UIColorFromRGB(0xf6f6f6);
}
self.activityLabel.backgroundColor = [UIColor clearColor];
[self.activityLabel sizeToFit];
}
@end

View file

@ -17,6 +17,7 @@
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
interactionLabel = nil;
avatarView = nil;
self.separatorInset = UIEdgeInsetsMake(0, 52, 0, 0);
// create favicon and label in view
UIImageView *favicon = [[UIImageView alloc] initWithFrame:CGRectZero];
@ -42,25 +43,26 @@
[super layoutSubviews];
// determine outer bounds
CGRect contentRect = self.contentView.bounds;
[self.interactionLabel sizeToFit];
CGRect contentRect = self.frame;
CGRect labelFrame = self.interactionLabel.frame;
// position avatar to bounds
self.avatarView.frame = CGRectMake(leftMargin, topMargin, avatarSize, avatarSize);
// position label to bounds
CGRect labelRect = contentRect;
labelRect.origin.x = labelRect.origin.x + leftMargin + avatarSize + leftMargin;
labelRect.origin.y = labelRect.origin.y + topMargin - 1;
labelRect.size.width = contentRect.size.width - leftMargin - avatarSize - leftMargin - rightMargin;
labelRect.size.height = contentRect.size.height - topMargin - bottomMargin;
self.interactionLabel.frame = labelRect;
labelFrame.origin.x = leftMargin*2 + avatarSize;
labelFrame.origin.y = topMargin - 1;
labelFrame.size.width = contentRect.size.width - leftMargin - avatarSize - leftMargin - rightMargin - 20;
labelFrame.size.height = contentRect.size.height - topMargin - bottomMargin;
self.interactionLabel.frame = labelFrame;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
self.interactionLabel.backgroundColor = UIColorFromRGB(0xd7dadf);
} else {
self.interactionLabel.backgroundColor = UIColorFromRGB(0xf6f6f6);
}
self.interactionLabel.backgroundColor = [UIColor clearColor];
[self.interactionLabel sizeToFit];
}
@end

View file

@ -18,7 +18,7 @@
NewsBlurAppDelegate *appDelegate;
NSString *activeStoryId;
NSDictionary *activeStory;
NSMutableDictionary *activeStory;
UIView *innerView;
UIWebView *webView;
NSInteger pageIndex;
@ -28,14 +28,15 @@
@property (nonatomic) IBOutlet NewsBlurAppDelegate *appDelegate;
@property (nonatomic) NSString *activeStoryId;
@property (nonatomic, readwrite) NSDictionary *activeStory;
@property (nonatomic, readwrite) NSMutableDictionary *activeStory;
@property (nonatomic) IBOutlet UIView *innerView;
@property (nonatomic) IBOutlet UIWebView *webView;
@property (nonatomic) IBOutlet UIView *feedTitleGradient;
@property (nonatomic) IBOutlet UILabel *noStorySelectedLabel;
@property (nonatomic) IBOutlet UIImageView *noStorySelectedImage;
@property (nonatomic) IBOutlet UIView *noStoryMessage;
@property (nonatomic, assign) BOOL pullingScrollview;
@property (nonatomic, assign) BOOL inTextView;
@property (nonatomic, assign) BOOL isRecentlyUnread;
@property NSInteger pageIndex;
@property (nonatomic) MBProgressHUD *storyHUD;

View file

@ -6,6 +6,7 @@
// Copyright 2010 __MyCompanyName__. All rights reserved.
//
#import <AVFoundation/AVFoundation.h>
#import "StoryDetailViewController.h"
#import "NewsBlurAppDelegate.h"
#import "NewsBlurViewController.h"
@ -32,12 +33,12 @@
@synthesize innerView;
@synthesize webView;
@synthesize feedTitleGradient;
@synthesize noStorySelectedLabel;
@synthesize noStorySelectedImage;
@synthesize noStoryMessage;
@synthesize pullingScrollview;
@synthesize pageIndex;
@synthesize storyHUD;
@synthesize inTextView;
@synthesize isRecentlyUnread;
#pragma mark -
@ -56,6 +57,10 @@
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
[audioSession setCategory:AVAudioSessionCategoryPlayback
error:nil];
self.webView.scalesPageToFit = YES;
self.webView.multipleTouchEnabled = NO;
@ -65,6 +70,7 @@
[self.webView.scrollView addObserver:self forKeyPath:@"contentOffset"
options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
context:nil];
self.pageIndex = -2;
self.inTextView = NO;
}
@ -75,7 +81,6 @@
- (void)viewDidUnload {
[self setInnerView:nil];
[self setNoStorySelectedLabel:nil];
[super viewDidUnload];
}
@ -98,16 +103,14 @@
- (void)initStory {
appDelegate.inStoryDetail = YES;
self.noStorySelectedLabel.hidden = YES;
self.noStorySelectedImage.hidden = YES;
self.noStoryMessage.hidden = YES;
self.webView.hidden = NO;
[appDelegate hideShareView:NO];
}
- (void)hideNoStoryMessage {
self.noStorySelectedLabel.hidden = YES;
self.noStorySelectedImage.hidden = YES;
self.noStoryMessage.hidden = YES;
}
- (void)drawStory {
@ -278,13 +281,13 @@
- (void)clearStory {
self.activeStoryId = nil;
[self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"about:blank"]]];
[MBProgressHUD hideHUDForView:self.webView animated:NO];
}
- (void)hideStory {
self.activeStoryId = nil;
self.webView.hidden = YES;
self.noStorySelectedLabel.hidden = NO;
self.noStorySelectedImage.hidden = NO;
self.noStoryMessage.hidden = NO;
}
#pragma mark -
@ -338,6 +341,13 @@
[self.activeStory objectForKey:@"starred_date"]];
}
NSString *storyUnread = @"";
if (self.isRecentlyUnread && [appDelegate isStoryUnread:self.activeStory]) {
NSInteger score = [NewsBlurAppDelegate computeStoryScore:[self.activeStory objectForKey:@"intelligence"]];
storyUnread = [NSString stringWithFormat:@"<div class=\"NB-story-unread NB-%@\"></div>",
score > 0 ? @"positive" : score < 0 ? @"negative" : @"neutral"];
}
NSString *storyTitle = [self.activeStory objectForKey:@"story_title"];
NSMutableDictionary *titleClassifiers = [[appDelegate.activeClassifiers objectForKey:feedId]
objectForKey:@"titles"];
@ -352,16 +362,23 @@
}
}
NSString *storyDate = [Utilities formatLongDateFromTimestamp:[[self.activeStory
objectForKey:@"story_timestamp"]
integerValue]];
NSString *storyHeader = [NSString stringWithFormat:@
"<div class=\"NB-header\"><div class=\"NB-header-inner\">"
"<div class=\"NB-story-title\">%@</div>"
"<div class=\"NB-story-title\">"
" %@"
" %@"
"</div>"
"<div class=\"NB-story-date\">%@</div>"
"%@"
"%@"
"%@"
"</div></div>",
storyUnread,
storyTitle,
[self.activeStory objectForKey:@"long_parsed_date"],
storyDate,
storyAuthor,
storyTags,
storyStarred];
@ -903,9 +920,9 @@
- (void)setActiveStoryAtIndex:(NSInteger)activeStoryIndex {
if (activeStoryIndex >= 0) {
self.activeStory = [appDelegate.activeFeedStories objectAtIndex:activeStoryIndex];
self.activeStory = [[appDelegate.activeFeedStories objectAtIndex:activeStoryIndex] mutableCopy];
} else {
self.activeStory = appDelegate.activeStory;
self.activeStory = [appDelegate.activeStory mutableCopy];
}
}
@ -988,7 +1005,7 @@ shouldStartLoadWithRequest:(NSURLRequest *)request
} else if ([action isEqualToString:@"share"]) {
[self openShareDialog];
return NO;
} else if ([action isEqualToString:@"train"]) {
} else if ([action isEqualToString:@"train"] && [urlComponents count] > 5) {
[self openTrainingDialog:[[urlComponents objectAtIndex:2] intValue]
yCoordinate:[[urlComponents objectAtIndex:3] intValue]
width:[[urlComponents objectAtIndex:4] intValue]
@ -1002,7 +1019,7 @@ shouldStartLoadWithRequest:(NSURLRequest *)request
NSString *tag = [NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:2]];
[self.appDelegate toggleTagClassifier:tag feedId:feedId];
return NO;
} else if ([action isEqualToString:@"show-profile"]) {
} else if ([action isEqualToString:@"show-profile"] && [urlComponents count] > 6) {
appDelegate.activeUserProfileId = [NSString stringWithFormat:@"%@", [urlComponents objectAtIndex:2]];
for (int i = 0; i < appDelegate.activeFeedUserProfiles.count; i++) {
@ -1437,6 +1454,7 @@ shouldStartLoadWithRequest:(NSURLRequest *)request
ASIFormDataRequest *request = [self formRequestWithURL:urlString];
[request addPostValue:[appDelegate.activeStory objectForKey:@"id"] forKey:@"story_id"];
[request addPostValue:[appDelegate.activeStory objectForKey:@"story_feed_id"] forKey:@"feed_id"];
[request setUserInfo:@{@"storyId": [appDelegate.activeStory objectForKey:@"id"]}];
[request setDidFinishSelector:@selector(finishFetchTextView:)];
[request setDidFailSelector:@selector(requestFailed:)];
[request setDelegate:self];
@ -1461,6 +1479,14 @@ shouldStartLoadWithRequest:(NSURLRequest *)request
return;
}
if (![[request.userInfo objectForKey:@"storyId"]
isEqualToString:[appDelegate.activeStory objectForKey:@"id"]]) {
[MBProgressHUD hideHUDForView:self.webView animated:YES];
self.inTextView = NO;
[appDelegate.storyPageControl setTextButton];
return;
}
NSString *originalText = [[[results objectForKey:@"original_text"]stringByReplacingOccurrencesOfString:@"\'" withString:@"\\'"]
stringByReplacingOccurrencesOfString:@"\n" withString:@" "];
NSString *jsString = [NSString stringWithFormat:@"document.getElementById('NB-story').innerHTML = '%@'; loadImages();",

View file

@ -10,14 +10,13 @@
#import "BaseViewController.h"
#import "NewsBlurAppDelegate.h"
#import "WEPopoverController.h"
#import "TransparentToolbar.h"
#import "THCircularProgressView.h"
@class NewsBlurAppDelegate;
@class ASIHTTPRequest;
@interface StoryPageControl : BaseViewController
<UIScrollViewDelegate, UIPopoverControllerDelegate, WEPopoverControllerDelegate> {
<UIScrollViewDelegate, UIPopoverControllerDelegate, UIGestureRecognizerDelegate, WEPopoverControllerDelegate> {
NewsBlurAppDelegate *appDelegate;
@ -26,9 +25,7 @@
UIButton *buttonNext;
UIButton *buttonText;
UIActivityIndicatorView *loadingIndicator;
UIToolbar *bottomPlaceholderToolbar;
UIBarButtonItem *buttonBack;
TransparentToolbar * rightToolbar;
UIView *traverseView;
UIView *progressView;
UIView *progressViewContainer;
@ -56,12 +53,11 @@
@property (nonatomic) IBOutlet UIButton *buttonSend;
@property (nonatomic) UIBarButtonItem *buttonBack;
@property (nonatomic) IBOutlet UIBarButtonItem *buttonAction;
@property (nonatomic) IBOutlet UIToolbar *bottomPlaceholderToolbar;
@property (nonatomic) IBOutlet UIView *bottomSize;
@property (nonatomic) IBOutlet UIBarButtonItem * spacerBarButton;
@property (nonatomic) IBOutlet UIBarButtonItem * spacer2BarButton;
@property (nonatomic) IBOutlet UIBarButtonItem * spacer3BarButton;
@property (nonatomic) IBOutlet UIBarButtonItem * separatorBarButton;
@property (nonatomic) IBOutlet TransparentToolbar * rightToolbar;
@property (nonatomic) IBOutlet UIView *traverseView;
@property (nonatomic) IBOutlet UIView *progressView;
@property (nonatomic) IBOutlet UIView *progressViewContainer;
@ -74,22 +70,24 @@
@property (assign) BOOL isDraggingScrollview;
@property (assign) BOOL waitingForNextUnreadFromServer;
@property (nonatomic) MBProgressHUD *storyHUD;
@property (nonatomic) int scrollingToPage;
@property (nonatomic) NSInteger scrollingToPage;
@property (nonatomic, strong) WEPopoverController *popoverController;
- (void)resizeScrollView;
- (void)applyNewIndex:(NSInteger)newIndex pageController:(StoryDetailViewController *)pageController;
- (void)layoutForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation;
- (void)adjustDragBar:(UIInterfaceOrientation)orientation;
- (void)transitionFromFeedDetail;
- (void)resetPages;
- (void)hidePages;
- (void)refreshPages;
- (void)refreshHeaders;
- (void)setStoryFromScroll;
- (void)setStoryFromScroll:(BOOL)force;
- (void)advanceToNextUnread;
- (void)updatePageWithActiveStory:(int)location;
- (void)updatePageWithActiveStory:(NSInteger)location;
- (void)changePage:(NSInteger)pageIndex;
- (void)changePage:(NSInteger)pageIndex animated:(BOOL)animated;
- (void)requestFailed:(ASIHTTPRequest *)request;
@ -105,6 +103,7 @@
- (void)finishMarkAsSaved:(ASIFormDataRequest *)request;
- (void)markStoryAsUnsaved;
- (void)finishMarkAsUnsaved:(ASIFormDataRequest *)request;
- (void)failedMarkAsUnread:(ASIFormDataRequest *)request;
- (void)subscribeToBlurblog;
- (IBAction)toggleFontSize:(id)sender;
@ -117,8 +116,8 @@
- (void)flashCheckmarkHud:(NSString *)messageType;
- (IBAction)openSendToDialog:(id)sender;
- (IBAction)doNextUnreadStory;
- (IBAction)doPreviousStory;
- (IBAction)doNextUnreadStory:(id)sender;
- (IBAction)doPreviousStory:(id)sender;
- (IBAction)tapProgressBar:(id)sender;
- (IBAction)toggleView:(id)sender;

View file

@ -22,7 +22,6 @@
#import "NBContainerViewController.h"
#import "DataUtilities.h"
#import "JSON.h"
#import "TransparentToolbar.h"
#import "UIBarButtonItem+Image.h"
#import "THCircularProgressView.h"
#import "FMDatabase.h"
@ -35,7 +34,6 @@
@synthesize circularProgressView;
@synthesize separatorBarButton;
@synthesize spacerBarButton, spacer2BarButton, spacer3BarButton;
@synthesize rightToolbar;
@synthesize buttonPrevious;
@synthesize buttonNext;
@synthesize buttonAction;
@ -45,7 +43,7 @@
@synthesize originalStoryButton;
@synthesize subscribeButton;
@synthesize buttonBack;
@synthesize bottomPlaceholderToolbar;
@synthesize bottomSize;
@synthesize popoverController;
@synthesize loadingIndicator;
@synthesize inTouchMove;
@ -66,9 +64,15 @@
}
- (void)viewDidLoad {
currentPage = [[StoryDetailViewController alloc] initWithNibName:@"StoryDetailViewController" bundle:nil];
nextPage = [[StoryDetailViewController alloc] initWithNibName:@"StoryDetailViewController" bundle:nil];
previousPage = [[StoryDetailViewController alloc] initWithNibName:@"StoryDetailViewController" bundle:nil];
currentPage = [[StoryDetailViewController alloc]
initWithNibName:@"StoryDetailViewController"
bundle:nil];
nextPage = [[StoryDetailViewController alloc]
initWithNibName:@"StoryDetailViewController"
bundle:nil];
previousPage = [[StoryDetailViewController alloc]
initWithNibName:@"StoryDetailViewController"
bundle:nil];
currentPage.appDelegate = appDelegate;
nextPage.appDelegate = appDelegate;
@ -101,36 +105,49 @@
percentage:20];
circularProgressView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
[self.traverseView addSubview:circularProgressView];
UIView *tapIndicator = [[UIView alloc] initWithFrame:CGRectMake(circularProgressView.frame.origin.x - circularProgressView.frame.size.width / 2, circularProgressView.frame.origin.y - circularProgressView.frame.size.height / 2, circularProgressView.frame.size.width*2, circularProgressView.frame.size.height*2)];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapProgressBar:)];
UIView *tapIndicator = [[UIView alloc]
initWithFrame:CGRectMake(circularProgressView.frame.origin.x -
circularProgressView.frame.size.width / 2,
circularProgressView.frame.origin.y -
circularProgressView.frame.size.height / 2,
circularProgressView.frame.size.width*2,
circularProgressView.frame.size.height*2)];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]
initWithTarget:self
action:@selector(tapProgressBar:)];
[tapIndicator addGestureRecognizer:tap];
tapIndicator.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
[self.traverseView insertSubview:tapIndicator aboveSubview:circularProgressView];
self.loadingIndicator.frame = self.circularProgressView.frame;
self.buttonNext.titleEdgeInsets = UIEdgeInsetsMake(0, 24, 0, 0);
rightToolbar = [[TransparentToolbar alloc]
initWithFrame:CGRectMake(0, 0, 80, 44)];
spacerBarButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
target:nil action:nil];
spacerBarButton.width = -12;
spacer2BarButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
spacer2BarButton.width = -4;
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
target:nil action:nil];
spacer2BarButton.width = -6;
spacer3BarButton = [[UIBarButtonItem alloc]
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
spacer3BarButton.width = -10;
initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace
target:nil action:nil];
spacer3BarButton.width = -6;
UIImage *separatorImage = [UIImage imageNamed:@"bar-separator.png"];
separatorBarButton = [UIBarButtonItem barItemWithImage:separatorImage target:nil action:nil];
separatorBarButton = [UIBarButtonItem barItemWithImage:separatorImage
target:nil
action:nil];
[separatorBarButton setEnabled:NO];
UIImage *settingsImage = [UIImage imageNamed:@"nav_icn_settings.png"];
fontSettingsButton = [UIBarButtonItem barItemWithImage:settingsImage target:self action:@selector(toggleFontSize:)];
fontSettingsButton = [UIBarButtonItem barItemWithImage:settingsImage
target:self
action:@selector(toggleFontSize:)];
UIImage *markreadImage = [UIImage imageNamed:@"original_button.png"];
originalStoryButton = [UIBarButtonItem barItemWithImage:markreadImage target:self action:@selector(showOriginalSubview:)];
originalStoryButton = [UIBarButtonItem barItemWithImage:markreadImage
target:self
action:@selector(showOriginalSubview:)];
UIBarButtonItem *subscribeBtn = [[UIBarButtonItem alloc]
initWithTitle:@"Follow User"
@ -143,19 +160,18 @@
// back button
UIBarButtonItem *backButton = [[UIBarButtonItem alloc]
initWithTitle:@"All Sites" style:UIBarButtonItemStyleBordered target:self action:@selector(transitionFromFeedDetail)];
initWithTitle:@"All Sites"
style:UIBarButtonItemStyleBordered
target:self
action:@selector(transitionFromFeedDetail)];
self.buttonBack = backButton;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
[rightToolbar setItems: [NSArray arrayWithObjects:
spacerBarButton,
fontSettingsButton,
spacer2BarButton,
separatorBarButton,
spacer3BarButton,
originalStoryButton, nil]];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:rightToolbar];
self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:
originalStoryButton,
separatorBarButton,
fontSettingsButton, nil];
}
[self.scrollView addObserver:self forKeyPath:@"contentOffset"
@ -168,17 +184,22 @@
- (void)viewWillAppear:(BOOL)animated {
[self setNextPreviousButtons];
[appDelegate adjustStoryDetailWebView];
self.navigationController.interactivePopGestureRecognizer.enabled = NO;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
if (!appDelegate.isSocialView) {
UIImage *titleImage;
if (appDelegate.isSocialRiverView && [appDelegate.activeFolder isEqualToString:@"river_global"]) {
if (appDelegate.isSocialRiverView && [appDelegate.activeFolder
isEqualToString:@"river_global"]) {
titleImage = [UIImage imageNamed:@"ak-icon-global.png"];
} else if (appDelegate.isSocialRiverView && [appDelegate.activeFolder isEqualToString:@"river_blurblogs"]) {
} else if (appDelegate.isSocialRiverView && [appDelegate.activeFolder
isEqualToString:@"river_blurblogs"]) {
titleImage = [UIImage imageNamed:@"ak-icon-blurblogs.png"];
} else if (appDelegate.isRiverView && [appDelegate.activeFolder isEqualToString:@"everything"]) {
} else if (appDelegate.isRiverView && [appDelegate.activeFolder
isEqualToString:@"everything"]) {
titleImage = [UIImage imageNamed:@"ak-icon-allstories.png"];
} else if (appDelegate.isRiverView && [appDelegate.activeFolder isEqualToString:@"saved_stories"]) {
} else if (appDelegate.isRiverView && [appDelegate.activeFolder
isEqualToString:@"saved_stories"]) {
titleImage = [UIImage imageNamed:@"clock.png"];
} else if (appDelegate.isRiverView) {
titleImage = [UIImage imageNamed:@"g_icn_folder.png"];
@ -216,17 +237,26 @@
self.traverseView.alpha = 1;
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
[self layoutForInterfaceOrientation:orientation];
[self adjustDragBar:orientation];
}
- (void)viewDidAppear:(BOOL)animated {
// set the subscribeButton flag
if (appDelegate.isTryFeedView) {
self.subscribeButton.title = [NSString stringWithFormat:@"Follow %@", [appDelegate.activeFeed objectForKey:@"username"]];
if (appDelegate.isTryFeedView && UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
self.subscribeButton.title = [NSString stringWithFormat:@"Follow %@",
[appDelegate.activeFeed objectForKey:@"username"]];
self.navigationItem.leftBarButtonItem = self.subscribeButton;
// self.subscribeButton.tintColor = UIColorFromRGB(0x0a6720);
}
appDelegate.isTryFeedView = NO;
[self applyNewIndex:previousPage.pageIndex pageController:previousPage];
previousPage.view.hidden = NO;
}
- (void)viewWillDisappear:(BOOL)animated {
previousPage.view.hidden = YES;
self.navigationController.interactivePopGestureRecognizer.enabled = YES;
}
- (void)transitionFromFeedDetail {
@ -237,7 +267,8 @@
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration {
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation
duration:(NSTimeInterval)duration {
if (UIInterfaceOrientationIsPortrait(toInterfaceOrientation)) {
NSLog(@"Rotate: %f,%f",self.view.frame.size.width,self.view.frame.size.height);
@ -246,6 +277,7 @@
}
[self layoutForInterfaceOrientation:toInterfaceOrientation];
[self adjustDragBar:toInterfaceOrientation];
}
- (void)layoutForInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
@ -258,15 +290,48 @@
}
}
- (void)adjustDragBar:(UIInterfaceOrientation)orientation {
CGRect scrollViewFrame = self.scrollView.frame;
CGRect traverseViewFrame = self.traverseView.frame;
if (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPad ||
UIInterfaceOrientationIsLandscape(orientation)) {
scrollViewFrame.size.height = self.view.frame.size.height;
self.bottomSize.hidden = YES;
} else {
scrollViewFrame.size.height = self.view.frame.size.height - 12;
self.bottomSize.hidden = NO;
}
self.scrollView.frame = scrollViewFrame;
traverseViewFrame.origin.y = scrollViewFrame.size.height - traverseViewFrame.size.height;
self.traverseView.frame = traverseViewFrame;
}
- (void)highlightButton:(UIButton *)b {
if (![b isKindOfClass:[UIButton class]]) return;
[b setHighlighted:YES];
}
- (void)unhighlightButton:(UIButton *)b {
if (![b isKindOfClass:[UIButton class]]) return;
[b setHighlighted:NO];
}
- (IBAction)beginTouchDown:(UIButton *)sender {
[self performSelector:@selector(highlightButton:) withObject:sender afterDelay:0.0];
}
- (IBAction)endTouchDown:(UIButton *)sender {
if (!sender) return;
[self performSelector:@selector(unhighlightButton:) withObject:sender afterDelay:0.2];
}
- (void)resetPages {
[currentPage clearStory];
[nextPage clearStory];
[previousPage clearStory];
[currentPage hideStory];
[nextPage hideStory];
[previousPage hideStory];
CGRect frame = self.scrollView.frame;
self.scrollView.contentSize = frame.size;
@ -278,11 +343,16 @@
currentPage.pageIndex = -2;
nextPage.pageIndex = -2;
previousPage.pageIndex = -2;
}
- (void)hidePages {
[currentPage hideStory];
[nextPage hideStory];
[previousPage hideStory];
}
- (void)refreshPages {
int pageIndex = currentPage.pageIndex;
NSInteger pageIndex = currentPage.pageIndex;
[self resizeScrollView];
[appDelegate adjustStoryDetailWebView];
currentPage.pageIndex = -2;
@ -294,6 +364,10 @@
}
- (void)refreshHeaders {
[currentPage setActiveStoryAtIndex:[appDelegate indexOfStoryId:currentPage.activeStoryId]];
[nextPage setActiveStoryAtIndex:[appDelegate indexOfStoryId:nextPage.activeStoryId]];
[previousPage setActiveStoryAtIndex:[appDelegate indexOfStoryId:previousPage.activeStoryId]];
[currentPage refreshHeader];
[nextPage refreshHeader];
[previousPage refreshHeader];
@ -311,13 +385,15 @@
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad && UIInterfaceOrientationIsPortrait(orientation)) {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad &&
UIInterfaceOrientationIsPortrait(orientation)) {
UITouch *theTouch = [touches anyObject];
if ([theTouch.view isKindOfClass: UIToolbar.class] || [theTouch.view isKindOfClass: UIView.class]) {
if ([theTouch.view isKindOfClass: UIToolbar.class] ||
[theTouch.view isKindOfClass: UIView.class]) {
self.inTouchMove = YES;
// CGPoint touchLocation = [theTouch locationInView:self.view];
// CGFloat y = touchLocation.y;
// [appDelegate.masterContainerViewController dragStoryToolbar:y];
CGPoint touchLocation = [theTouch locationInView:self.view];
CGFloat y = touchLocation.y;
[appDelegate.masterContainerViewController dragStoryToolbar:y];
}
}
}
@ -325,10 +401,12 @@
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad && UIInterfaceOrientationIsPortrait(orientation)) {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad &&
UIInterfaceOrientationIsPortrait(orientation)) {
UITouch *theTouch = [touches anyObject];
if (([theTouch.view isKindOfClass: UIToolbar.class] || [theTouch.view isKindOfClass: UIView.class]) && self.inTouchMove) {
if (([theTouch.view isKindOfClass: UIToolbar.class] ||
[theTouch.view isKindOfClass: UIView.class]) && self.inTouchMove) {
self.inTouchMove = NO;
[appDelegate.masterContainerViewController adjustFeedDetailScreenForStoryTitles];
}
@ -338,7 +416,8 @@
#pragma mark -
#pragma mark Side scroll view
- (void)applyNewIndex:(NSInteger)newIndex pageController:(StoryDetailViewController *)pageController {
- (void)applyNewIndex:(NSInteger)newIndex
pageController:(StoryDetailViewController *)pageController {
NSInteger pageCount = [[appDelegate activeFeedStoryLocations] count];
BOOL outOfBounds = newIndex >= pageCount || newIndex < 0;
@ -380,12 +459,12 @@
[appDelegate hideStoryDetailView];
}
} else if (!outOfBounds) {
int location = [appDelegate indexFromLocation:pageController.pageIndex];
NSInteger location = [appDelegate indexFromLocation:pageController.pageIndex];
[pageController setActiveStoryAtIndex:location];
[pageController clearStory];
if (self.isDraggingScrollview ||
self.scrollingToPage < 0 ||
abs(newIndex - self.scrollingToPage) <= 1) {
ABS(newIndex - self.scrollingToPage) <= 1) {
[pageController initStory];
[pageController drawStory];
} else {
@ -411,7 +490,7 @@
NSInteger upperNumber = lowerNumber + 1;
NSInteger previousNumber = lowerNumber - 1;
int storyCount = [appDelegate.activeFeedStoryLocations count];
NSInteger storyCount = [appDelegate.activeFeedStoryLocations count];
if (storyCount == 0 || lowerNumber > storyCount) return;
// NSLog(@"Did Scroll: %f = %d (%d/%d/%d)", fractionalPage, lowerNumber, previousPage.pageIndex, currentPage.pageIndex, nextPage.pageIndex);
@ -490,7 +569,10 @@
[self setStoryFromScroll];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context {
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad &&
[keyPath isEqual:@"contentOffset"] &&
self.isDraggingScrollview) {
@ -500,7 +582,7 @@
if (![appDelegate.activeFeedStories count]) return;
int storyIndex = [appDelegate indexFromLocation:nearestNumber];
NSInteger storyIndex = [appDelegate indexFromLocation:nearestNumber];
if (storyIndex != [appDelegate indexOfActiveStory]) {
appDelegate.activeStory = [appDelegate.activeFeedStories objectAtIndex:storyIndex];
[appDelegate changeActiveFeedDetailRow];
@ -536,8 +618,6 @@
[self setStoryFromScroll];
}
}
[self markStoryAsRead];
}
- (void)setStoryFromScroll {
@ -577,6 +657,7 @@
nextPage.webView.scrollView.scrollsToTop = NO;
previousPage.webView.scrollView.scrollsToTop = NO;
currentPage.webView.scrollView.scrollsToTop = YES;
currentPage.isRecentlyUnread = NO;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
appDelegate.feedDetailViewController.storyTitlesTable.scrollsToTop = NO;
}
@ -585,7 +666,7 @@
if (self.isDraggingScrollview || self.scrollingToPage == currentPage.pageIndex) {
if (currentPage.pageIndex == -2) return;
self.scrollingToPage = -1;
int storyIndex = [appDelegate indexFromLocation:currentPage.pageIndex];
NSInteger storyIndex = [appDelegate indexFromLocation:currentPage.pageIndex];
appDelegate.activeStory = [appDelegate.activeFeedStories objectAtIndex:storyIndex];
[self updatePageWithActiveStory:currentPage.pageIndex];
[self markStoryAsRead];
@ -598,23 +679,20 @@
}
self.waitingForNextUnreadFromServer = NO;
[self doNextUnreadStory];
[self doNextUnreadStory:nil];
}
- (void)updatePageWithActiveStory:(int)location {
- (void)updatePageWithActiveStory:(NSInteger)location {
[appDelegate pushReadStory:[appDelegate.activeStory objectForKey:@"id"]];
self.bottomPlaceholderToolbar.hidden = YES;
// self.bottomSize.hidden = YES;
[self.view setNeedsLayout];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[rightToolbar setItems: [NSArray arrayWithObjects:
spacerBarButton,
fontSettingsButton,
spacer2BarButton,
separatorBarButton,
spacer3BarButton,
originalStoryButton, nil]];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:rightToolbar];
self.navigationItem.rightBarButtonItems = [NSArray arrayWithObjects:
originalStoryButton,
separatorBarButton,
fontSettingsButton, nil];
}
[self setNextPreviousButtons];
@ -638,50 +716,33 @@
[self informError:@"The server barfed!"];
}
- (void)requestFailedMarkStoryRead:(ASIFormDataRequest *)request {
// [self informError:@"Failed to mark story as read"];
NSString *storyFeedId = [request.userInfo objectForKey:@"story_feed_id"];
NSString *storyHash = [request.userInfo objectForKey:@"story_hash"];
[appDelegate.database inDatabase:^(FMDatabase *db) {
[db executeUpdate:@"INSERT INTO queued_read_hashes "
"(story_feed_id, story_hash) VALUES "
"(?, ?)", storyFeedId, storyHash];
}];
appDelegate.hasQueuedReadStories = YES;
}
#pragma mark -
#pragma mark Actions
- (void)setNextPreviousButtons {
// setting up the PREV BUTTON
int readStoryCount = [appDelegate.readStories count];
NSInteger readStoryCount = [appDelegate.readStories count];
if (readStoryCount == 0 ||
(readStoryCount == 1 &&
[appDelegate.readStories lastObject] == [appDelegate.activeStory objectForKey:@"id"])) {
[buttonPrevious setEnabled:NO];
// buttonPrevious.alpha = 1.0f;
// [buttonAction setImage:[UIImage imageNamed:@"traverse_previous_off"]];
} else {
[buttonPrevious setEnabled:YES];
// buttonPrevious.alpha = 1.0f;
// [buttonAction setImage:[UIImage imageNamed:@"traverse_previous"]];
}
// setting up the NEXT UNREAD STORY BUTTON
buttonNext.enabled = YES;
int nextIndex = [appDelegate indexOfNextUnreadStory];
int unreadCount = [appDelegate unreadCount];
NSInteger nextIndex = [appDelegate indexOfNextUnreadStory];
NSInteger unreadCount = [appDelegate unreadCount];
if ((nextIndex == -1 && unreadCount > 0) ||
nextIndex != -1) {
[buttonNext setTitle:@"NEXT" forState:UIControlStateNormal];
[buttonNext setBackgroundImage:[UIImage imageNamed:@"traverse_next.png"] forState:UIControlStateNormal];
[buttonNext setTitle:[@"Next" uppercaseString] forState:UIControlStateNormal];
[buttonNext setBackgroundImage:[UIImage imageNamed:@"traverse_next.png"]
forState:UIControlStateNormal];
} else {
[buttonNext setTitle:@"DONE" forState:UIControlStateNormal];
[buttonNext setBackgroundImage:[UIImage imageNamed:@"traverse_done.png"] forState:UIControlStateNormal];
[buttonNext setTitle:[@"Done" uppercaseString] forState:UIControlStateNormal];
[buttonNext setBackgroundImage:[UIImage imageNamed:@"traverse_done.png"]
forState:UIControlStateNormal];
}
float unreads = (float)[appDelegate unreadCount];
@ -707,82 +768,34 @@
[buttonText setTitle:[@"Story" uppercaseString] forState:UIControlStateNormal];
[buttonText setBackgroundImage:[UIImage imageNamed:@"traverse_text_on.png"]
forState:nil];
self.buttonText.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -44);
self.buttonText.titleEdgeInsets = UIEdgeInsetsMake(0, 26, 0, 0);
} else {
[buttonText setTitle:[@"Text" uppercaseString] forState:UIControlStateNormal];
[buttonText setBackgroundImage:[UIImage imageNamed:@"traverse_text.png"]
forState:nil];
self.buttonText.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, -40);
self.buttonText.titleEdgeInsets = UIEdgeInsetsMake(0, 22, 0, 0);
}
}
- (void)markStoryAsRead {
// NSLog(@"[appDelegate.activeStory objectForKey:@read_status] intValue] %i", [[appDelegate.activeStory objectForKey:@"read_status"] intValue]);
if ([[appDelegate.activeStory objectForKey:@"read_status"] intValue] != 1 ||
[[appDelegate.unreadStoryHashes objectForKey:[appDelegate.activeStory objectForKey:@"story_hash"]] boolValue]) {
if (!appDelegate.activeStory) return;
if ([appDelegate isStoryUnread:appDelegate.activeStory]) {
[appDelegate markActiveStoryRead];
[self.currentPage refreshHeader];
[appDelegate.feedDetailViewController redrawUnreadStory];
NSString *urlString;
if (appDelegate.isSocialView || appDelegate.isSocialRiverView) {
urlString = [NSString stringWithFormat:@"%@/reader/mark_social_stories_as_read",
NEWSBLUR_URL];
} else {
urlString = [NSString stringWithFormat:@"%@/reader/mark_story_as_read",
NEWSBLUR_URL];
}
NSString *urlString = [NSString stringWithFormat:@"%@/reader/mark_story_hashes_as_read",
NEWSBLUR_URL];
NSURL *url = [NSURL URLWithString:urlString];
ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
if (appDelegate.isSocialRiverView) {
// grab the user id from the shared_by_friends
NSArray *storyId = [NSArray arrayWithObject:[appDelegate.activeStory objectForKey:@"id"]];
NSString *friendUserId;
if ([[appDelegate.activeStory objectForKey:@"shared_by_friends"] count]) {
friendUserId = [NSString stringWithFormat:@"%@",
[[appDelegate.activeStory objectForKey:@"shared_by_friends"] objectAtIndex:0]];
} else if ([[appDelegate.activeStory objectForKey:@"commented_by_friends"] count]) {
friendUserId = [NSString stringWithFormat:@"%@",
[[appDelegate.activeStory objectForKey:@"commented_by_friends"] objectAtIndex:0]];
} else {
friendUserId = [NSString stringWithFormat:@"%@",
[[appDelegate.activeStory objectForKey:@"share_user_ids"] objectAtIndex:0]];
}
NSDictionary *feedStory = [NSDictionary dictionaryWithObject:storyId
forKey:[NSString stringWithFormat:@"%@",
[appDelegate.activeStory objectForKey:@"story_feed_id"]]];
NSDictionary *usersFeedsStories = [NSDictionary dictionaryWithObject:feedStory
forKey:friendUserId];
[request setPostValue:[usersFeedsStories JSONRepresentation] forKey:@"users_feeds_stories"];
} else if (appDelegate.isSocialView) {
NSArray *storyId = [NSArray arrayWithObject:[appDelegate.activeStory objectForKey:@"id"]];
NSDictionary *feedStory = [NSDictionary dictionaryWithObject:storyId
forKey:[NSString stringWithFormat:@"%@",
[appDelegate.activeStory objectForKey:@"story_feed_id"]]];
NSDictionary *usersFeedsStories = [NSDictionary dictionaryWithObject:feedStory
forKey:[NSString stringWithFormat:@"%@",
[appDelegate.activeStory objectForKey:@"social_user_id"]]];
[request setPostValue:[usersFeedsStories JSONRepresentation] forKey:@"users_feeds_stories"];
} else {
[request setPostValue:[appDelegate.activeStory
objectForKey:@"story_hash"]
forKey:@"story_id"];
[request setPostValue:[appDelegate.activeStory
objectForKey:@"story_feed_id"]
forKey:@"feed_id"];
[request setUserInfo:@{@"story_feed_id":[appDelegate.activeStory
objectForKey:@"story_feed_id"],
@"story_hash":[appDelegate.activeStory
objectForKey:@"story_hash"]}];
}
[request setPostValue:[appDelegate.activeStory objectForKey:@"story_hash"]
forKey:@"story_hash"];
[request setUserInfo:@{@"story_feed_id":[appDelegate.activeStory
objectForKey:@"story_feed_id"],
@"story_hash":[appDelegate.activeStory
objectForKey:@"story_hash"]}];
[request setDidFinishSelector:@selector(finishMarkAsRead:)];
[request setDidFailSelector:@selector(requestFailedMarkStoryRead:)];
[request setDelegate:self];
@ -790,7 +803,6 @@
}
}
- (void)finishMarkAsRead:(ASIFormDataRequest *)request {
if ([request responseStatusCode] != 200) {
return [self requestFailedMarkStoryRead:request];
@ -802,7 +814,17 @@
// NSLog(@"results in mark as read is %@", results);
}
- (void)requestFailedMarkStoryRead:(ASIFormDataRequest *)request {
// [self informError:@"Failed to mark story as read"];
NSString *storyFeedId = [request.userInfo objectForKey:@"story_feed_id"];
NSString *storyHash = [request.userInfo objectForKey:@"story_hash"];
[appDelegate queueReadStories:@{storyFeedId: @[storyHash]}];
}
- (IBAction)openSendToDialog:(id)sender {
[self endTouchDown:sender];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[appDelegate.masterContainerViewController showSendToPopover:sender];
} else {
@ -836,7 +858,7 @@
return [self requestFailed:request];
}
[appDelegate markActiveStorySaved:YES];
[appDelegate markStory:appDelegate.activeStory asSaved:YES];
[appDelegate.feedDetailViewController redrawUnreadStory];
[self refreshHeaders];
[self.currentPage flashCheckmarkHud:@"saved"];
@ -868,7 +890,7 @@
return [self requestFailed:request];
}
[appDelegate markActiveStorySaved:NO];
[appDelegate markStory:appDelegate.activeStory asSaved:NO];
[appDelegate.feedDetailViewController redrawUnreadStory];
[self refreshHeaders];
[self.currentPage flashCheckmarkHud:@"unsaved"];
@ -889,15 +911,16 @@
forKey:@"feed_id"];
[request setDidFinishSelector:@selector(finishMarkAsUnread:)];
[request setDidFailSelector:@selector(requestFailed:)];
[request setDidFailSelector:@selector(failedMarkAsUnread:)];
[request setDelegate:self];
[request setUserInfo:appDelegate.activeStory];
[request startAsynchronous];
}
}
- (void)finishMarkAsUnread:(ASIFormDataRequest *)request {
if ([request responseStatusCode] != 200) {
return [self requestFailed:request];
return [self failedMarkAsUnread:request];
}
NSString *responseString = [request responseString];
@ -910,11 +933,32 @@
[appDelegate markActiveStoryUnread];
[appDelegate.feedDetailViewController redrawUnreadStory];
currentPage.isRecentlyUnread = YES;
[currentPage refreshHeader];
[self setNextPreviousButtons];
[self.currentPage flashCheckmarkHud:@"unread"];
}
- (void)failedMarkAsUnread:(ASIFormDataRequest *)request {
NSString *storyFeedId = [request.userInfo objectForKey:@"story_feed_id"];
NSString *storyHash = [request.userInfo objectForKey:@"story_hash"];
BOOL dequeued = [appDelegate dequeueReadStoryHash:storyHash inFeed:storyFeedId];
if (!dequeued) {
[self informError:@"Failed to unread story"];
[appDelegate markStoryRead:storyHash feedId:storyFeedId];
} else {
[appDelegate.unreadStoryHashes setObject:[NSNumber numberWithBool:YES] forKey:storyHash];
[appDelegate markActiveStoryUnread];
[appDelegate.feedDetailViewController redrawUnreadStory];
[self setNextPreviousButtons];
[self.currentPage flashCheckmarkHud:@"unread"];
}
[self refreshHeaders];
}
- (IBAction)showOriginalSubview:(id)sender {
[appDelegate.masterContainerViewController hidePopover];
@ -928,13 +972,13 @@
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeText;
hud.removeFromSuperViewOnHide = YES;
int unreadCount = appDelegate.unreadCount;
NSInteger unreadCount = appDelegate.unreadCount;
if (unreadCount == 0) {
hud.labelText = @"No unread stories";
} else if (unreadCount == 1) {
hud.labelText = @"1 story left";
} else {
hud.labelText = [NSString stringWithFormat:@"%i stories left", unreadCount];
hud.labelText = [NSString stringWithFormat:@"%li stories left", (long)unreadCount];
}
[hud hide:YES afterDelay:0.8];
}
@ -944,6 +988,8 @@
}
- (IBAction)toggleView:(id)sender {
[self endTouchDown:sender];
[self.currentPage fetchTextView];
}
@ -995,9 +1041,7 @@
self.storyHUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
self.storyHUD.labelText = msg;
self.storyHUD.margin = 20.0f;
self.currentPage.noStorySelectedLabel.hidden = YES;
self.nextPage.noStorySelectedLabel.hidden = YES;
self.previousPage.noStorySelectedLabel.hidden = YES;
self.currentPage.noStoryMessage.hidden = YES;
}
- (void)flashCheckmarkHud:(NSString *)messageType {
@ -1007,12 +1051,13 @@
#pragma mark -
#pragma mark Story Traversal
- (IBAction)doNextUnreadStory {
- (IBAction)doNextUnreadStory:(id)sender {
FeedDetailViewController *fdvc = self.appDelegate.feedDetailViewController;
int nextLocation = [appDelegate locationOfNextUnreadStory];
int unreadCount = [appDelegate unreadCount];
NSInteger nextLocation = [appDelegate locationOfNextUnreadStory];
NSInteger unreadCount = [appDelegate unreadCount];
[self.loadingIndicator stopAnimating];
[self endTouchDown:sender];
// NSLog(@"doNextUnreadStory: %d (out of %d)", nextLocation, unreadCount);
if (nextLocation == -1 && unreadCount > 0 &&
@ -1034,7 +1079,8 @@
}
}
- (IBAction)doPreviousStory {
- (IBAction)doPreviousStory:(id)sender {
[self endTouchDown:sender];
[self.loadingIndicator stopAnimating];
self.circularProgressView.hidden = NO;
id previousStoryId = [appDelegate popReadStory];
@ -1045,9 +1091,9 @@
animated:YES];
[appDelegate hideStoryDetailView];
} else {
int previousLocation = [appDelegate locationOfStoryId:previousStoryId];
NSInteger previousLocation = [appDelegate locationOfStoryId:previousStoryId];
if (previousLocation == -1) {
return [self doPreviousStory];
return [self doPreviousStory:sender];
}
// [appDelegate setActiveStory:[[appDelegate activeFeedStories]
// objectAtIndex:previousIndex]];

File diff suppressed because it is too large Load diff

View file

@ -27,6 +27,7 @@
TrainerWebView *webView;
IBOutlet UINavigationBar *navBar;
BOOL feedLoaded;
BOOL feedTrainer;
BOOL storyTrainer;
}
@ -37,6 +38,7 @@
@property (nonatomic) IBOutlet UINavigationBar *navBar;
@property (nonatomic, assign) BOOL feedTrainer;
@property (nonatomic, assign) BOOL storyTrainer;
@property (nonatomic, assign) BOOL feedLoaded;
- (void)refresh;
- (NSString *)makeTrainerHTML;

View file

@ -11,6 +11,7 @@
#import "StringHelper.h"
#import "Utilities.h"
#import "Base64.h"
#import "AFNetworking.h"
@implementation TrainerViewController
@ -20,6 +21,7 @@
@synthesize appDelegate;
@synthesize feedTrainer;
@synthesize storyTrainer;
@synthesize feedLoaded;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
@ -64,8 +66,47 @@
nil]];
UILabel *titleLabel = (UILabel *)[appDelegate makeFeedTitle:appDelegate.activeFeed];
navBar.topItem.titleView = titleLabel;
self.navigationItem.titleView = titleLabel;
[MBProgressHUD hideHUDForView:self.view animated:NO];
if (!feedLoaded) {
MBProgressHUD *HUD = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
HUD.labelText = @"Loading trainer...";
NSString *feedId = [NSString stringWithFormat:@"%@", [appDelegate.activeFeed objectForKey:@"id"]];
NSURL *url = [NSURL URLWithString:[NSString
stringWithFormat:@"%@/reader/feeds_trainer?feed_id=%@",
NEWSBLUR_URL, feedId]];
AFJSONRequestOperation *request = [AFJSONRequestOperation
JSONRequestOperationWithRequest:[NSURLRequest requestWithURL:url]
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
[MBProgressHUD hideHUDForView:self.view animated:YES];
NSDictionary *results = [JSON objectAtIndex:0];
NSMutableDictionary *newClassifiers = [[results objectForKey:@"classifiers"] mutableCopy];
[appDelegate.activeClassifiers setObject:newClassifiers
forKey:feedId];
appDelegate.activePopularAuthors = [results objectForKey:@"feed_authors"];
appDelegate.activePopularTags = [results objectForKey:@"feed_tags"];
[self renderTrainer];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(@"Failed fetch trainer.");
[self informError:@"Could not load trainer"];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[appDelegate.masterContainerViewController hidePopover];
} else {
[appDelegate.navigationController dismissViewControllerAnimated:YES completion:nil];
}
});
}];
[request start];
} else {
[self renderTrainer];
}
}
- (void)renderTrainer {
NSString *path = [[NSBundle mainBundle] bundlePath];
NSURL *baseURL = [NSURL fileURLWithPath:path];
[self.webView loadHTMLString:[self makeTrainerHTML] baseURL:baseURL];
@ -93,7 +134,7 @@
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.webView loadHTMLString:@"about:blank" baseURL:nil];
[self.webView loadHTMLString:@"" baseURL:[NSURL URLWithString:@"about:blank"]];
[[UIMenuController sharedMenuController] setMenuItems:nil];
}

View file

@ -108,24 +108,25 @@ const int COUNT_HEIGHT = 15;
NSString *psStr = [NSString stringWithFormat:@"%d", ps];
CGSize size = [psStr sizeWithFont:indicatorFont];
CGSize size = [psStr sizeWithAttributes:@{NSFontAttributeName: indicatorFont}];
float x_pos = (rr.size.width - size.width) / 2;
float y_pos = (rr.size.height - size.height) / 2;
UIColor *psColor;
if (blueCount) {
[indicatorBlackColor set];
psColor = indicatorBlackColor;
} else {
[positiveBackgroundShadowColor set];
psColor = positiveBackgroundShadowColor;
}
[psStr
drawAtPoint:CGPointMake(rr.origin.x + x_pos, rr.origin.y + y_pos + 1)
withFont:indicatorFont];
withAttributes:@{NSFontAttributeName: indicatorFont,
NSForegroundColorAttributeName: psColor}];
[indicatorWhiteColor set];
[psStr
drawAtPoint:CGPointMake(rr.origin.x + x_pos, rr.origin.y + y_pos)
withFont:indicatorFont];
withAttributes:@{NSFontAttributeName: indicatorFont,
NSForegroundColorAttributeName: indicatorWhiteColor}];
}
if (nt > 0 && appDelegate.selectedIntelligence <= 0) {
@ -151,19 +152,19 @@ const int COUNT_HEIGHT = 15;
[UIView drawRoundRectangleInRect:rr withRadius:4];
NSString *ntStr = [NSString stringWithFormat:@"%d", nt];
CGSize size = [ntStr sizeWithFont:indicatorFont];
CGSize size = [ntStr sizeWithAttributes:@{NSFontAttributeName: indicatorFont}];
float x_pos = (rr.size.width - size.width) / 2;
float y_pos = (rr.size.height - size.height) / 2;
[neutralBackgroundShadowColor set];
[ntStr
drawAtPoint:CGPointMake(rr.origin.x + x_pos, rr.origin.y + y_pos + 1)
withFont:indicatorFont];
withAttributes:@{NSFontAttributeName: indicatorFont,
NSForegroundColorAttributeName: neutralBackgroundShadowColor}];
[indicatorWhiteColor set];
[ntStr
drawAtPoint:CGPointMake(rr.origin.x + x_pos, rr.origin.y + y_pos)
withFont:indicatorFont];
withAttributes:@{NSFontAttributeName: indicatorFont,
NSForegroundColorAttributeName: indicatorWhiteColor}];
}
}

View file

@ -31,6 +31,7 @@
@property (nonatomic) NSArray *activitiesArray;
@property (nonatomic) NSString *activitiesUsername;
@property (nonatomic) NSDictionary *userProfile;
@property (nonatomic) ASIHTTPRequest *request;
- (void)getUserProfile;
- (void)requestFinished:(ASIHTTPRequest *)request;

View file

@ -24,6 +24,7 @@
@synthesize activitiesArray;
@synthesize activitiesUsername;
@synthesize userProfile;
@synthesize request;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
@ -34,6 +35,12 @@
return self;
}
- (void)dealloc {
self.profileTable.dataSource = nil;
self.profileTable.delegate = nil;
request.delegate = nil;
[request cancel];
}
- (void)viewDidLoad {
[super viewDidLoad];
@ -41,7 +48,7 @@
self.appDelegate = (NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate];
self.view.frame = self.view.bounds;
self.contentSizeForViewInPopover = self.view.frame.size;
self.preferredContentSize = self.view.frame.size;
self.view.backgroundColor = UIColorFromRGB(0xd7dadf);
@ -70,7 +77,7 @@
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
CGRect vb = self.view.bounds;
self.contentSizeForViewInPopover = self.view.frame.size;
self.preferredContentSize = self.view.frame.size;
self.view.frame = vb;
self.profileTable.frame = vb;
self.profileBadge.frame = CGRectMake(0, 0, vb.size.width, 140);
@ -90,7 +97,7 @@
- (void)getUserProfile {
self.view.frame = self.view.bounds;
self.contentSizeForViewInPopover = self.view.frame.size;
self.preferredContentSize = self.view.frame.size;
self.appDelegate = (NewsBlurAppDelegate *)[[UIApplication sharedApplication] delegate];
[MBProgressHUD hideHUDForView:self.view animated:YES];
@ -104,7 +111,7 @@
appDelegate.activeUserProfileId];
NSURL *url = [NSURL URLWithString:urlString];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request setDidFinishSelector:@selector(requestFinished:)];
@ -112,9 +119,9 @@
[request startAsynchronous];
}
- (void)requestFinished:(ASIHTTPRequest *)request {
- (void)requestFinished:(ASIHTTPRequest *)_request {
[MBProgressHUD hideHUDForView:self.view animated:YES];
NSString *responseString = [request responseString];
NSString *responseString = [_request responseString];
NSData *responseData=[responseString dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSDictionary *results = [NSJSONSerialization
@ -149,8 +156,8 @@
[self.view addSubview:self.profileTable];
}
- (void)requestFailed:(ASIHTTPRequest *)request {
NSError *error = [request error];
- (void)requestFailed:(ASIHTTPRequest *)_request {
NSError *error = [_request error];
NSLog(@"Error: %@", error);
[appDelegate informError:error];
}
@ -205,14 +212,6 @@
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
CGRect vb = self.view.bounds;
// you can only hardcode this due to limitation in apple API that doesn't give you width of grouped cell
int width = 300 - 20;
if (vb.size.width == 480) {
width = 460 - 20;
} else if (vb.size.width == 540) {
width = 478 - 20;
}
if (indexPath.section == 0) {
ProfileBadge *cell = [tableView
@ -224,7 +223,7 @@
reuseIdentifier:nil];
}
[cell refreshWithProfile:self.userProfile showStats:YES withWidth:width + 20];
[cell refreshWithProfile:self.userProfile showStats:YES withWidth:vb.size.width];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
@ -240,7 +239,7 @@
cell.accessoryType= UITableViewCellAccessoryDisclosureIndicator;
[cell setActivity:[self.activitiesArray objectAtIndex:(indexPath.row)]
withUserProfile:self.userProfile
withWidth:width];
withWidth:vb.size.width];
return cell;
} else {
@ -252,13 +251,13 @@
reuseIdentifier:@"FollowGridCellIdentifier"];
}
cell.selectionStyle = UITableViewCellSelectionStyleNone;
[cell refreshWithWidth:width];
[cell refreshWithWidth:vb.size.width];
return cell;
}
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
int activitiesCount = [self.activitiesArray count];
NSInteger activitiesCount = [self.activitiesArray count];
// badge is not tappable
if (indexPath.section == 0 || indexPath.section == 2) {

View file

@ -22,5 +22,7 @@ void drawLinearGradient(CGContextRef context, CGRect rect, CGColorRef startColor
+ (void)saveimagesToDisk;
+ (UIImage *)roundCorneredImage:(UIImage *)orig radius:(CGFloat)r;
+ (NSString *)md5:(NSString *)string;
+ (NSString *)formatLongDateFromTimestamp:(NSInteger)timestamp;
+ (NSString *)formatShortDateFromTimestamp:(NSInteger)timestamp;
@end

View file

@ -56,7 +56,7 @@ static NSMutableDictionary *imageCache;
+ (UIImage *)getImage:(NSString *)filename isSocial:(BOOL)isSocial {
UIImage *image;
if (filename && [[imageCache allKeys] containsObject:filename]) {
if (filename && [imageCache objectForKey:filename]) {
image = [imageCache objectForKey:filename];
}
@ -144,7 +144,7 @@ static NSMutableDictionary *imageCache;
+ (NSString *)md5:(NSString *)string {
const char *cStr = [string UTF8String];
unsigned char result[16];
CC_MD5( cStr, strlen(cStr), result ); // This is the md5 call
CC_MD5( cStr, (CC_LONG)strlen(cStr), result ); // This is the md5 call
return [NSString stringWithFormat:
@"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
result[0], result[1], result[2], result[3],
@ -154,4 +154,117 @@ static NSMutableDictionary *imageCache;
];
}
+ (NSString *)formatLongDateFromTimestamp:(NSInteger)timestamp {
if (!timestamp) timestamp = [[NSDate date] timeIntervalSince1970];
NSDate *date = [NSDate dateWithTimeIntervalSince1970:(double)timestamp];
static NSDateFormatter *dateFormatter = nil;
static NSDateFormatter *todayFormatter = nil;
static NSDateFormatter *yesterdayFormatter = nil;
static NSDateFormatter *formatterPeriod = nil;
NSDate *today = [NSDate date];
NSDateComponents *components = [[NSCalendar currentCalendar]
components:NSIntegerMax
fromDate:today];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
NSDate *midnight = [[NSCalendar currentCalendar] dateFromComponents:components];
NSDate *yesterday = [NSDate dateWithTimeInterval:-60*60*24 sinceDate:midnight];
if (!dateFormatter || !todayFormatter || !yesterdayFormatter || !formatterPeriod) {
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"EEEE, MMMM d'Sth', y h:mm"];
todayFormatter = [[NSDateFormatter alloc] init];
[todayFormatter setDateFormat:@"'Today', MMMM d'Sth' h:mm"];
yesterdayFormatter = [[NSDateFormatter alloc] init];
[yesterdayFormatter setDateFormat:@"'Yesterday', MMMM d'Sth' h:mm"];
formatterPeriod = [[NSDateFormatter alloc] init];
[formatterPeriod setDateFormat:@"a"];
}
NSString *dateString;
if ([date compare:midnight] == NSOrderedDescending) {
dateString = [NSString stringWithFormat:@"%@%@",
[todayFormatter stringFromDate:date],
[[formatterPeriod stringFromDate:date] lowercaseString]];
} else if ([date compare:yesterday] == NSOrderedDescending) {
dateString = [NSString stringWithFormat:@"%@%@",
[yesterdayFormatter stringFromDate:date],
[[formatterPeriod stringFromDate:date] lowercaseString]];
} else {
dateString = [NSString stringWithFormat:@"%@%@",
[dateFormatter stringFromDate:date],
[[formatterPeriod stringFromDate:date] lowercaseString]];
}
dateString = [dateString stringByReplacingOccurrencesOfString:@"Sth"
withString:[Utilities suffixForDayInDate:date]];
return dateString;
}
+ (NSString *)formatShortDateFromTimestamp:(NSInteger)timestamp {
if (!timestamp) timestamp = [[NSDate date] timeIntervalSince1970];
NSDate *date = [NSDate dateWithTimeIntervalSince1970:(double)timestamp];
static NSDateFormatter *dateFormatter = nil;
static NSDateFormatter *todayFormatter = nil;
static NSDateFormatter *yesterdayFormatter = nil;
static NSDateFormatter *formatterPeriod = nil;
NSDate *today = [NSDate date];
NSDateComponents *components = [[NSCalendar currentCalendar]
components:NSIntegerMax
fromDate:today];
[components setHour:0];
[components setMinute:0];
[components setSecond:0];
NSDate *midnight = [[NSCalendar currentCalendar] dateFromComponents:components];
NSDate *yesterday = [NSDate dateWithTimeInterval:-60*60*24 sinceDate:midnight];
if (!dateFormatter || !todayFormatter || !yesterdayFormatter || !formatterPeriod) {
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"dd LLL y, h:mm"];
todayFormatter = [[NSDateFormatter alloc] init];
[todayFormatter setDateFormat:@"h:mm"];
yesterdayFormatter = [[NSDateFormatter alloc] init];
[yesterdayFormatter setDateFormat:@"'Yesterday', h:mm"];
formatterPeriod = [[NSDateFormatter alloc] init];
[formatterPeriod setDateFormat:@"a"];
}
NSString *dateString;
if ([date compare:midnight] == NSOrderedDescending) {
dateString = [NSString stringWithFormat:@"%@%@",
[todayFormatter stringFromDate:date],
[[formatterPeriod stringFromDate:date] lowercaseString]];
} else if ([date compare:yesterday] == NSOrderedDescending) {
dateString = [NSString stringWithFormat:@"%@%@",
[yesterdayFormatter stringFromDate:date],
[[formatterPeriod stringFromDate:date] lowercaseString]];
} else {
dateString = [NSString stringWithFormat:@"%@%@",
[dateFormatter stringFromDate:date],
[[formatterPeriod stringFromDate:date] lowercaseString]];
}
return dateString;
}
+ (NSString *)suffixForDayInDate:(NSDate *)date {
NSInteger day = [[[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] components:NSDayCalendarUnit fromDate:date] day];
if (day == 11) {
return @"th";
} else if (day % 10 == 1) {
return @"st";
} else if (day % 10 == 2) {
return @"nd";
} else if (day % 10 == 3) {
return @"rd";
} else {
return @"th";
}
}
@end

View file

@ -49,6 +49,7 @@
dispatch_async(dispatch_get_main_queue(), ^{
[appDelegate.feedsViewController showDoneNotifier];
[appDelegate.feedsViewController hideNotifier];
[appDelegate cleanImageCache];
});
return NO;
}
@ -119,6 +120,8 @@
[cursor objectForColumnName:@"story_hash"],
[cursor objectForColumnName:@"story_timestamp"]]];
}
[cursor close];
[self updateProgress];
}];

View file

@ -61,8 +61,6 @@
}
});
return NO;
} else {
NSLog(@"Fetching Stories: %@", [hashes objectAtIndex:0]);
}
__block NSCondition *lock = [NSCondition new];
@ -74,7 +72,6 @@
success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
[self storeAllUnreadStories:JSON withHashes:hashes];
NSLog(@"Done Storing Stories: %@", [hashes objectAtIndex:0]);
[lock signal];
} failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
NSLog(@"Failed fetch all unreads.");
@ -89,7 +86,6 @@
[lock waitUntilDate:[NSDate dateWithTimeIntervalSinceNow:30]];
[lock unlock];
NSLog(@"Completed Fetching Stories: %@", [hashes objectAtIndex:0]);
return YES;
}
@ -121,6 +117,7 @@
[hashes addObject:[cursor objectForColumnName:@"story_hash"]];
}
[cursor close];
[self updateProgress];
}];
@ -141,14 +138,13 @@
(float)appDelegate.totalUnfetchedStoryCount);
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"appDelegate.remainingUnfetchedStoryCount %d (%f)", appDelegate.remainingUnfetchedStoryCount, progress);
// NSLog(@"appDelegate.remainingUnfetchedStoryCount %d (%f)", appDelegate.remainingUnfetchedStoryCount, progress);
[appDelegate.feedsViewController showSyncingNotifier:progress hoursBack:hours];
});
}
- (void)storeAllUnreadStories:(NSDictionary *)results withHashes:(NSArray *)hashes {
NSMutableArray *storyHashes = [hashes mutableCopy];
NSLog(@"storing hashes: %@", [storyHashes objectAtIndex:0]);
[appDelegate.database inDatabase:^(FMDatabase *db) {
BOOL anyInserted = NO;
for (NSDictionary *story in [results objectForKey:@"stories"]) {
@ -194,19 +190,24 @@
}
if (anyInserted) {
appDelegate.latestFetchedStoryDate = [[[[results objectForKey:@"stories"] lastObject]
objectForKey:@"story_timestamp"] intValue];
NSDictionary *lastStory;
if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"default_order"]
isEqualToString:@"oldest"]) {
lastStory = [[results objectForKey:@"stories"] firstObject];
} else {
lastStory = [[results objectForKey:@"stories"] lastObject];
}
appDelegate.latestFetchedStoryDate = [[lastStory
objectForKey:@"story_timestamp"]
intValue];
}
if ([storyHashes count]) {
NSLog(@"Failed to fetch stories: %@", storyHashes);
[db executeUpdate:[NSString stringWithFormat:@"DELETE FROM unread_hashes WHERE story_hash IN (\"%@\")",
[storyHashes componentsJoinedByString:@"\",\" "]]];
}
NSLog(@"Done inserting stories (in transaction)");
}];
NSLog(@"Done inserting stories");
[appDelegate storeUserProfiles:[results objectForKey:@"user_profiles"]];
}

View file

@ -66,29 +66,38 @@
];
}
}
}];
[appDelegate.database inTransaction:^(FMDatabase *db, BOOL *rollback) {
// Once all unread hashes are in, only keep under preference for offline limit
NSInteger offlineLimit = [[NSUserDefaults standardUserDefaults] integerForKey:@"offline_store_limit"];
NSInteger offlineLimit = [[NSUserDefaults standardUserDefaults]
integerForKey:@"offline_store_limit"];
NSString *order;
NSString *orderComp;
if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"default_order"] isEqualToString:@"oldest"]) {
if ([[[NSUserDefaults standardUserDefaults] objectForKey:@"default_order"]
isEqualToString:@"oldest"]) {
order = @"ASC";
orderComp = @">";
} else {
order = @"DESC";
orderComp = @"<";
}
FMResultSet *cursor = [db executeQuery:[NSString stringWithFormat:@"SELECT story_timestamp FROM unread_hashes ORDER BY story_timestamp %@ LIMIT 1 OFFSET %d", order, offlineLimit]];
NSString *lastStorySql = [NSString stringWithFormat:
@"SELECT story_timestamp FROM unread_hashes "
"ORDER BY story_timestamp %@ LIMIT 1 OFFSET %ld",
order, (long)offlineLimit];
FMResultSet *cursor = [db executeQuery:lastStorySql];
int offlineLimitTimestamp = 0;
while ([cursor next]) {
offlineLimitTimestamp = [cursor intForColumn:@"story_timestamp"];
break;
}
NSLog(@"Deleting stories over limit: %d - %d", offlineLimit, offlineLimitTimestamp);
[db executeUpdate:[NSString stringWithFormat:@"DELETE FROM unread_hashes WHERE story_timestamp %@ %d", orderComp, offlineLimitTimestamp]];
[db executeUpdate:[NSString stringWithFormat:@"DELETE FROM stories WHERE story_timestamp %@ %d", orderComp, offlineLimitTimestamp]];
[cursor close];
if (offlineLimitTimestamp) {
NSLog(@"Deleting stories over limit: %ld - %d", (long)offlineLimit, offlineLimitTimestamp);
[db executeUpdate:[NSString stringWithFormat:@"DELETE FROM unread_hashes WHERE story_timestamp %@ %d", orderComp, offlineLimitTimestamp]];
[db executeUpdate:[NSString stringWithFormat:@"DELETE FROM stories WHERE story_timestamp %@ %d", orderComp, offlineLimitTimestamp]];
}
}];
appDelegate.totalUnfetchedStoryCount = 0;

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>application-identifier</key>
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
<key>get-task-allow</key>
<false/>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)$(CFBundleIdentifier)</string>
</array>
</dict>
</plist>

View file

@ -9,35 +9,20 @@
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>logo_57.png</string>
<key>CFBundleIconFiles</key>
<array>
<string>logo_144.png</string>
<string>logo_72.png</string>
<string>logo_57.png</string>
<string>logo_114.png</string>
<string>Default-Portrait@2x~ipad.png</string>
<string>Default-Landscape@2x~ipad.png</string>
<string>Default-Portrait~ipad.png</string>
<string>Default-Landscape~ipad.png</string>
</array>
<key>CFBundleIcons</key>
<string>logo_120.png</string>
<key>CFBundleIcons~ipad</key>
<dict>
<key>CFBundlePrimaryIcon</key>
<dict>
<key>CFBundleIconFiles</key>
<array>
<string>logo_144.png</string>
<string>logo_72.png</string>
<string>logo_57.png</string>
<string>logo_114.png</string>
<string>Default-Portrait@2x~ipad.png</string>
<string>Default-Landscape@2x~ipad.png</string>
<string>Default-Portrait~ipad.png</string>
<string>Default-Landscape~ipad.png</string>
<string>logo_58</string>
<string>logo_29</string>
<string>logo_40</string>
<string>logo_80</string>
<string>logo_152</string>
<string>logo_76</string>
</array>
<key>UIPrerenderedIcon</key>
<true/>
</dict>
</dict>
<key>CFBundleIdentifier</key>
@ -49,7 +34,7 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.0.1</string>
<string>3.5</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@ -78,7 +63,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>3.0.1</string>
<string>3.5</string>
<key>FacebookAppID</key>
<string>230426707030569</string>
<key>LSRequiresIPhoneOS</key>
@ -87,8 +72,136 @@
<string>MainWindow</string>
<key>NSMainNibFile~ipad</key>
<string>MainWindow~ipad</string>
<key>UILaunchImages</key>
<array>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{320, 480}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{320, 568}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageName</key>
<string>Default</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{320, 480}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageName</key>
<string>launch_iphone_tall</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{320, 568}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageName</key>
<string>launch_iphone</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{320, 480}</string>
</dict>
</array>
<key>UILaunchImages~ipad</key>
<array>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageName</key>
<string>Default-landscape</string>
<key>UILaunchImageOrientation</key>
<string>Landscape</string>
<key>UILaunchImageSize</key>
<string>{768, 1024}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageName</key>
<string>Default-portrait</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{768, 1024}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageName</key>
<string>Default-portrait</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{768, 1024}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageName</key>
<string>launch_ipad_landscape</string>
<key>UILaunchImageOrientation</key>
<string>Landscape</string>
<key>UILaunchImageSize</key>
<string>{768, 1024}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageName</key>
<string>launch_ipad_portrait</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{768, 1024}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageName</key>
<string>launch_iphone_tall</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{320, 568}</string>
</dict>
<dict>
<key>UILaunchImageMinimumOSVersion</key>
<string>7.0</string>
<key>UILaunchImageName</key>
<string>launch_iphone</string>
<key>UILaunchImageOrientation</key>
<string>Portrait</string>
<key>UILaunchImageSize</key>
<string>{320, 480}</string>
</dict>
</array>
<key>UIPrerenderedIcon</key>
<true/>
<key>UIStatusBarHidden</key>
<false/>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleBlackOpaque</string>
<key>UISupportedInterfaceOrientations</key>

Binary file not shown.

View file

@ -11,7 +11,7 @@
<key>kind</key>
<string>software-package</string>
<key>url</key>
<string>http://www.newsblur.com/ios/NewsBlur.ipa</string>
<string>http://dev.newsblur.com/ios/NewsBlur.ipa</string>
</dict>
<dict>
<key>kind</key>
@ -19,7 +19,7 @@
<key>needs-shine</key>
<false/>
<key>url</key>
<string>http://www.newsblur.com/media/ios/Resources/logo_512.png</string>
<string>http://www.newsblur.com/media/ios/Resources/logos/logo_512.png</string>
</dict>
<dict>
<key>kind</key>
@ -27,7 +27,7 @@
<key>needs-shine</key>
<false/>
<key>url</key>
<string>http://www.newsblur.com/media/ios/Resources/logo_57.png</string>
<string>http://www.newsblur.com/media/ios/Resources/logos/logo_58.png</string>
</dict>
</array>
<key>metadata</key>
@ -35,7 +35,7 @@
<key>bundle-identifier</key>
<string>com.newsblur.NewsBlur</string>
<key>bundle-version</key>
<string>3.0.1</string>
<string>3.0.4</string>
<key>kind</key>
<string>software</string>
<key>title</key>

View file

@ -51,16 +51,16 @@
438FEDE815D5B15F00E3B3C9 /* FollowGrid.m in Sources */ = {isa = PBXBuildFile; fileRef = 438FEDE715D5B15F00E3B3C9 /* FollowGrid.m */; };
439DAB201590DA350019B0EB /* FeedsMenuViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 439DAB1E1590DA350019B0EB /* FeedsMenuViewController.m */; };
439DAB211590DA350019B0EB /* FeedsMenuViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 439DAB1F1590DA350019B0EB /* FeedsMenuViewController.xib */; };
43A3914415B73A7B0074B212 /* AFHTTPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3913015B73A7B0074B212 /* AFHTTPClient.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A3914515B73A7B0074B212 /* AFHTTPRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3913215B73A7B0074B212 /* AFHTTPRequestOperation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A3914615B73A7B0074B212 /* AFImageRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3913415B73A7B0074B212 /* AFImageRequestOperation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A3914715B73A7B0074B212 /* AFJSONRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3913615B73A7B0074B212 /* AFJSONRequestOperation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A3914415B73A7B0074B212 /* AFHTTPClient.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3913015B73A7B0074B212 /* AFHTTPClient.m */; };
43A3914515B73A7B0074B212 /* AFHTTPRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3913215B73A7B0074B212 /* AFHTTPRequestOperation.m */; };
43A3914615B73A7B0074B212 /* AFImageRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3913415B73A7B0074B212 /* AFImageRequestOperation.m */; };
43A3914715B73A7B0074B212 /* AFJSONRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3913615B73A7B0074B212 /* AFJSONRequestOperation.m */; };
43A3914815B73A7B0074B212 /* AFJSONUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3913815B73A7B0074B212 /* AFJSONUtilities.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A3914915B73A7B0074B212 /* AFNetworkActivityIndicatorManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3913A15B73A7B0074B212 /* AFNetworkActivityIndicatorManager.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A3914A15B73A7B0074B212 /* AFPropertyListRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3913D15B73A7B0074B212 /* AFPropertyListRequestOperation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A3914B15B73A7B0074B212 /* AFURLConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3913F15B73A7B0074B212 /* AFURLConnectionOperation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A3914C15B73A7B0074B212 /* AFXMLRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3914115B73A7B0074B212 /* AFXMLRequestOperation.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A3914D15B73A7B0074B212 /* UIImageView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3914315B73A7B0074B212 /* UIImageView+AFNetworking.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A3914915B73A7B0074B212 /* AFNetworkActivityIndicatorManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3913A15B73A7B0074B212 /* AFNetworkActivityIndicatorManager.m */; };
43A3914A15B73A7B0074B212 /* AFPropertyListRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3913D15B73A7B0074B212 /* AFPropertyListRequestOperation.m */; };
43A3914B15B73A7B0074B212 /* AFURLConnectionOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3913F15B73A7B0074B212 /* AFURLConnectionOperation.m */; };
43A3914C15B73A7B0074B212 /* AFXMLRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3914115B73A7B0074B212 /* AFXMLRequestOperation.m */; };
43A3914D15B73A7B0074B212 /* UIImageView+AFNetworking.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A3914315B73A7B0074B212 /* UIImageView+AFNetworking.m */; };
43A4BAC815C8663600F3B8D4 /* UIBarButtonItem+WEPopover.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A4BAC015C8663600F3B8D4 /* UIBarButtonItem+WEPopover.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A4BAC915C8663600F3B8D4 /* WEPopoverContainerView.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A4BAC215C8663600F3B8D4 /* WEPopoverContainerView.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
43A4BACA15C8663600F3B8D4 /* WEPopoverController.m in Sources */ = {isa = PBXBuildFile; fileRef = 43A4BAC415C8663600F3B8D4 /* WEPopoverController.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
@ -95,31 +95,14 @@
43A4C44F15B00A26008787B5 /* arrow@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C3F415B00A26008787B5 /* arrow@2x.png */; };
43A4C45215B00A26008787B5 /* Background.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C3F715B00A26008787B5 /* Background.png */; };
43A4C45315B00A26008787B5 /* Background@2X.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C3F815B00A26008787B5 /* Background@2X.png */; };
43A4C45B15B00A26008787B5 /* Default-Landscape.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C40015B00A26008787B5 /* Default-Landscape.png */; };
43A4C45C15B00A26008787B5 /* Default-Landscape@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C40115B00A26008787B5 /* Default-Landscape@2x.png */; };
43A4C45D15B00A26008787B5 /* Default-Landscape@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C40215B00A26008787B5 /* Default-Landscape@2x~ipad.png */; };
43A4C45E15B00A26008787B5 /* Default-Landscape~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C40315B00A26008787B5 /* Default-Landscape~ipad.png */; };
43A4C45F15B00A26008787B5 /* Default-Portrait.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C40415B00A26008787B5 /* Default-Portrait.png */; };
43A4C46015B00A26008787B5 /* Default-Portrait@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C40515B00A26008787B5 /* Default-Portrait@2x.png */; };
43A4C46115B00A26008787B5 /* Default-Portrait@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C40615B00A26008787B5 /* Default-Portrait@2x~ipad.png */; };
43A4C46215B00A26008787B5 /* Default-Portrait~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C40715B00A26008787B5 /* Default-Portrait~ipad.png */; };
43A4C46315B00A26008787B5 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C40815B00A26008787B5 /* Default.png */; };
43A4C46415B00A26008787B5 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C40915B00A26008787B5 /* Default@2x.png */; };
43A4C46615B00A26008787B5 /* disclosure.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C40B15B00A26008787B5 /* disclosure.png */; };
43A4C46B15B00A26008787B5 /* feeds_background.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C41015B00A26008787B5 /* feeds_background.png */; };
43A4C46C15B00A26008787B5 /* fleuron.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C41115B00A26008787B5 /* fleuron.png */; };
43A4C47A15B00A26008787B5 /* logo_57.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C41F15B00A26008787B5 /* logo_57.png */; };
43A4C47B15B00A26008787B5 /* logo_72.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C42015B00A26008787B5 /* logo_72.png */; };
43A4C47C15B00A26008787B5 /* logo_114.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C42115B00A26008787B5 /* logo_114.png */; };
43A4C47D15B00A26008787B5 /* logo_144.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C42215B00A26008787B5 /* logo_144.png */; };
43A4C47E15B00A26008787B5 /* logo_512.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C42315B00A26008787B5 /* logo_512.png */; };
43A4C47F15B00A26008787B5 /* logo_background.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C42415B00A26008787B5 /* logo_background.png */; };
43A4C48015B00A26008787B5 /* logo_newsblur_512.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C42515B00A26008787B5 /* logo_newsblur_512.png */; };
43A4C48115B00A26008787B5 /* logo_newsblur_blur.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C42615B00A26008787B5 /* logo_newsblur_blur.png */; };
43A4C49A15B00A26008787B5 /* warning.gif in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C43F15B00A26008787B5 /* warning.gif */; };
43A4C49B15B00A26008787B5 /* warning.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C44015B00A26008787B5 /* warning.png */; };
43A4C49C15B00A26008787B5 /* world.png in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C44115B00A26008787B5 /* world.png */; };
43A4C49E15B00CD3008787B5 /* Entitlements.entitlements in Resources */ = {isa = PBXBuildFile; fileRef = 43A4C49D15B00CD3008787B5 /* Entitlements.entitlements */; };
43ABBCAA15B53A1400EA3111 /* InteractionCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 43ABBCA915B53A1400EA3111 /* InteractionCell.m */; };
43AF417715C30B1600758366 /* 37x-Checkmark.png in Resources */ = {isa = PBXBuildFile; fileRef = 43AF417515C30B1600758366 /* 37x-Checkmark.png */; };
43AF417815C30B1600758366 /* 37x-Checkmark@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 43AF417615C30B1600758366 /* 37x-Checkmark@2x.png */; };
@ -147,10 +130,8 @@
43C1680B15B3D99B00428BA3 /* 7-location-place.png in Resources */ = {isa = PBXBuildFile; fileRef = 43C1680A15B3D99B00428BA3 /* 7-location-place.png */; };
43C3D40415D44EA30066D36D /* user_light.png in Resources */ = {isa = PBXBuildFile; fileRef = 43C3D40215D44EA30066D36D /* user_light.png */; };
43C3D40515D44EA30066D36D /* user_light@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 43C3D40315D44EA30066D36D /* user_light@2x.png */; };
43C3D40815D4680C0066D36D /* green_focus.png in Resources */ = {isa = PBXBuildFile; fileRef = 43C3D40715D4680C0066D36D /* green_focus.png */; };
43C3D40A15D468BA0066D36D /* all.png in Resources */ = {isa = PBXBuildFile; fileRef = 43C3D40915D468BA0066D36D /* all.png */; };
43C3D40C15D469010066D36D /* unread_yellow.png in Resources */ = {isa = PBXBuildFile; fileRef = 43C3D40B15D469010066D36D /* unread_yellow.png */; };
43C3D41015D46D2B0066D36D /* green_focus@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 43C3D40F15D46D2B0066D36D /* green_focus@2x.png */; };
43C3D41215D46D330066D36D /* all@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 43C3D41115D46D330066D36D /* all@2x.png */; };
43C95C49158BEC450086C69B /* GoogleReaderViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 43C95C47158BEC440086C69B /* GoogleReaderViewController.xib */; };
43CE0F5F15DADB7F00608ED8 /* SiteCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 43CE0F5E15DADB7F00608ED8 /* SiteCell.m */; };
@ -236,16 +217,8 @@
FF4151C416DEDF9D0013E84B /* markread@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF4151C216DEDF9D0013E84B /* markread@2x.png */; };
FF4151C716DEF1A80013E84B /* original_button.png in Resources */ = {isa = PBXBuildFile; fileRef = FF4151C516DEF1A80013E84B /* original_button.png */; };
FF4151C816DEF1A80013E84B /* original_button@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF4151C616DEF1A80013E84B /* original_button@2x.png */; };
FF4151E916DEF9960013E84B /* back_button_background.png in Resources */ = {isa = PBXBuildFile; fileRef = FF4151E116DEF9960013E84B /* back_button_background.png */; };
FF4151EA16DEF9960013E84B /* back_button_background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF4151E216DEF9960013E84B /* back_button_background@2x.png */; };
FF4151EB16DEF9960013E84B /* back_button_landscape_background.png in Resources */ = {isa = PBXBuildFile; fileRef = FF4151E316DEF9960013E84B /* back_button_landscape_background.png */; };
FF4151EC16DEF9960013E84B /* back_button_landscape_background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF4151E416DEF9960013E84B /* back_button_landscape_background@2x.png */; };
FF4151ED16DEF9960013E84B /* back_button_landscape_selected_background.png in Resources */ = {isa = PBXBuildFile; fileRef = FF4151E516DEF9960013E84B /* back_button_landscape_selected_background.png */; };
FF4151EE16DEF9960013E84B /* back_button_landscape_selected_background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF4151E616DEF9960013E84B /* back_button_landscape_selected_background@2x.png */; };
FF4151EF16DEF9960013E84B /* back_button_selected_background.png in Resources */ = {isa = PBXBuildFile; fileRef = FF4151E716DEF9960013E84B /* back_button_selected_background.png */; };
FF4151F016DEF9960013E84B /* back_button_selected_background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF4151E816DEF9960013E84B /* back_button_selected_background@2x.png */; };
FF468E3817FCFB970080325C /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FF468E3717FCFB970080325C /* AVFoundation.framework */; };
FF4EE9A4175EC9CF005891B5 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FF4EE9A3175EC9CF005891B5 /* libsqlite3.dylib */; };
FF546DF71602930100948020 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF546DF61602930100948020 /* Default-568h@2x.png */; };
FF546DF9160298E500948020 /* fleuron@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF546DF8160298E500948020 /* fleuron@2x.png */; };
FF5D3FFE1799F53C00349659 /* VUPinboardAccess.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5D3FEB1799F53C00349659 /* VUPinboardAccess.m */; };
FF5D3FFF1799F53C00349659 /* VUPinboardActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = FF5D3FED1799F53C00349659 /* VUPinboardActivity.m */; };
@ -276,7 +249,6 @@
FF6618C8176184560039913B /* NBNotifier.m in Sources */ = {isa = PBXBuildFile; fileRef = FF6618C7176184560039913B /* NBNotifier.m */; };
FF67D3B2168924C40057A7DA /* TrainerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FF67D3B1168924C40057A7DA /* TrainerViewController.m */; };
FF67D3B7168977690057A7DA /* TrainerViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = FF67D3B6168977690057A7DA /* TrainerViewController.xib */; };
FF67D3B916897AD80057A7DA /* TrainerViewController~ipad.xib in Resources */ = {isa = PBXBuildFile; fileRef = FF67D3B816897AD80057A7DA /* TrainerViewController~ipad.xib */; };
FF67D3BB168A70630057A7DA /* trainer.css in Resources */ = {isa = PBXBuildFile; fileRef = FF67D3BA168A70630057A7DA /* trainer.css */; };
FF67D3C1168A708D0057A7DA /* storyDetailView.css in Resources */ = {isa = PBXBuildFile; fileRef = FF67D3BD168A708D0057A7DA /* storyDetailView.css */; };
FF67D3CC168A73380057A7DA /* storyDetailView.js in Resources */ = {isa = PBXBuildFile; fileRef = FF67D3BE168A708D0057A7DA /* storyDetailView.js */; };
@ -317,6 +289,9 @@
FF85BF7116D6A90D002D334D /* ak-icon-global.png in Resources */ = {isa = PBXBuildFile; fileRef = FF85BF7016D6A90D002D334D /* ak-icon-global.png */; };
FF85BF7416D6A972002D334D /* ak-icon-allstories.png in Resources */ = {isa = PBXBuildFile; fileRef = FF85BF7216D6A972002D334D /* ak-icon-allstories.png */; };
FF85BF7516D6A972002D334D /* ak-icon-blurblogs.png in Resources */ = {isa = PBXBuildFile; fileRef = FF85BF7316D6A972002D334D /* ak-icon-blurblogs.png */; };
FF88F10D1811BAEC007FEE78 /* unread_green@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FF88F10B1811BAEC007FEE78 /* unread_green@2x.png */; };
FF88F10E1811BAEC007FEE78 /* unread_green.png in Resources */ = {isa = PBXBuildFile; fileRef = FF88F10C1811BAEC007FEE78 /* unread_green.png */; };
FF9B8BB217F2351A0036A41C /* NBBarButtonItem.m in Sources */ = {isa = PBXBuildFile; fileRef = FF9B8BB117F2351A0036A41C /* NBBarButtonItem.m */; };
FFAD4971144A386100BA6919 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FFAD4970144A386100BA6919 /* libz.dylib */; };
FFAF53661799D18B00C7FCCB /* RWInstapaperActivity.m in Sources */ = {isa = PBXBuildFile; fileRef = FFAF53461799D18B00C7FCCB /* RWInstapaperActivity.m */; };
FFAF53671799D18B00C7FCCB /* RWInstapaperActivityRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = FFAF53481799D18B00C7FCCB /* RWInstapaperActivityRequest.m */; };
@ -347,6 +322,13 @@
FFB5F50B17187B6F00C8B432 /* big_world.png in Resources */ = {isa = PBXBuildFile; fileRef = FFB5F50A17187B6F00C8B432 /* big_world.png */; };
FFB757FE1727098D001D132F /* menu_icn_mail.png in Resources */ = {isa = PBXBuildFile; fileRef = FFB757FC1727098D001D132F /* menu_icn_mail.png */; };
FFB757FF1727098D001D132F /* menu_icn_mail@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFB757FD1727098D001D132F /* menu_icn_mail@2x.png */; };
FFB9BE4717F4B65B00FE0A36 /* logo_120.png in Resources */ = {isa = PBXBuildFile; fileRef = FFB9BE4017F4B65B00FE0A36 /* logo_120.png */; };
FFB9BE4817F4B65B00FE0A36 /* logo_152.png in Resources */ = {isa = PBXBuildFile; fileRef = FFB9BE4117F4B65B00FE0A36 /* logo_152.png */; };
FFB9BE4917F4B65B00FE0A36 /* logo_29.png in Resources */ = {isa = PBXBuildFile; fileRef = FFB9BE4217F4B65B00FE0A36 /* logo_29.png */; };
FFB9BE4A17F4B65B00FE0A36 /* logo_40.png in Resources */ = {isa = PBXBuildFile; fileRef = FFB9BE4317F4B65B00FE0A36 /* logo_40.png */; };
FFB9BE4B17F4B65B00FE0A36 /* logo_58.png in Resources */ = {isa = PBXBuildFile; fileRef = FFB9BE4417F4B65B00FE0A36 /* logo_58.png */; };
FFB9BE4C17F4B65B00FE0A36 /* logo_76.png in Resources */ = {isa = PBXBuildFile; fileRef = FFB9BE4517F4B65B00FE0A36 /* logo_76.png */; };
FFB9BE4D17F4B65B00FE0A36 /* logo_80.png in Resources */ = {isa = PBXBuildFile; fileRef = FFB9BE4617F4B65B00FE0A36 /* logo_80.png */; };
FFC518B91768E59F00542719 /* g_icn_offline.png in Resources */ = {isa = PBXBuildFile; fileRef = FFC518B81768E59F00542719 /* g_icn_offline.png */; };
FFC5F30B16E2D2C2007AC72C /* story_share_appnet_active.png in Resources */ = {isa = PBXBuildFile; fileRef = FFC5F2FF16E2D2C2007AC72C /* story_share_appnet_active.png */; };
FFC5F30C16E2D2C2007AC72C /* story_share_appnet_active@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFC5F30016E2D2C2007AC72C /* story_share_appnet_active@2x.png */; };
@ -360,21 +342,16 @@
FFC5F31416E2D2C2007AC72C /* story_share_twitter_active@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFC5F30816E2D2C2007AC72C /* story_share_twitter_active@2x.png */; };
FFC5F31516E2D2C2007AC72C /* story_share_twitter.png in Resources */ = {isa = PBXBuildFile; fileRef = FFC5F30916E2D2C2007AC72C /* story_share_twitter.png */; };
FFC5F31616E2D2C2007AC72C /* story_share_twitter@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFC5F30A16E2D2C2007AC72C /* story_share_twitter@2x.png */; };
FFC5F31B16E2E9FB007AC72C /* toolbar_button_landscape_selected.png in Resources */ = {isa = PBXBuildFile; fileRef = FFC5F31716E2E9FB007AC72C /* toolbar_button_landscape_selected.png */; };
FFC5F31C16E2E9FB007AC72C /* toolbar_button_landscape.png in Resources */ = {isa = PBXBuildFile; fileRef = FFC5F31816E2E9FB007AC72C /* toolbar_button_landscape.png */; };
FFC5F31D16E2E9FB007AC72C /* toolbar_button_landscape@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFC5F31916E2E9FB007AC72C /* toolbar_button_landscape@2x.png */; };
FFC5F31E16E2E9FB007AC72C /* toolbar_button_selected_landscape@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFC5F31A16E2E9FB007AC72C /* toolbar_button_selected_landscape@2x.png */; };
FFC5F32016E2F24B007AC72C /* white_spacer.png in Resources */ = {isa = PBXBuildFile; fileRef = FFC5F31F16E2F24B007AC72C /* white_spacer.png */; };
FFCF51A316E020DD008C7C42 /* toolbar_background.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCF51A116E020DD008C7C42 /* toolbar_background.png */; };
FFCF51A416E020DD008C7C42 /* toolbar_background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCF51A216E020DD008C7C42 /* toolbar_background@2x.png */; };
FFCF51A716E028B0008C7C42 /* navbar_background.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCF51A516E028B0008C7C42 /* navbar_background.png */; };
FFCF51A816E028B0008C7C42 /* navbar_background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCF51A616E028B0008C7C42 /* navbar_background@2x.png */; };
FFCF51AB16E029E6008C7C42 /* navbar_landscape_background.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCF51A916E029E6008C7C42 /* navbar_landscape_background.png */; };
FFCF51AC16E029E6008C7C42 /* navbar_landscape_background@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCF51AA16E029E6008C7C42 /* navbar_landscape_background@2x.png */; };
FFCF51C516E0300F008C7C42 /* button_selected.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCF51C116E0300F008C7C42 /* button_selected.png */; };
FFCF51C616E0300F008C7C42 /* button_selected@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCF51C216E0300F008C7C42 /* button_selected@2x.png */; };
FFCF51C716E0300F008C7C42 /* button.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCF51C316E0300F008C7C42 /* button.png */; };
FFCF51C816E0300F008C7C42 /* button@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCF51C416E0300F008C7C42 /* button@2x.png */; };
FFCDD8F317F4BCB4000C6483 /* Default-568h@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCDD8ED17F4BCB4000C6483 /* Default-568h@2x.png */; };
FFCDD8F417F4BCB4000C6483 /* Default-landscape.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCDD8EE17F4BCB4000C6483 /* Default-landscape.png */; };
FFCDD8F517F4BCB4000C6483 /* Default-landscape@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCDD8EF17F4BCB4000C6483 /* Default-landscape@2x.png */; };
FFCDD8F617F4BCB4000C6483 /* Default-portrait.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCDD8F017F4BCB4000C6483 /* Default-portrait.png */; };
FFCDD8F717F4BCB4000C6483 /* Default-portrait@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCDD8F117F4BCB4000C6483 /* Default-portrait@2x.png */; };
FFCDD8F817F4BCB4000C6483 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCDD8F217F4BCB4000C6483 /* Default.png */; };
FFCDD8FA17F50C08000C6483 /* drag_icon.png in Resources */ = {isa = PBXBuildFile; fileRef = FFCDD8F917F50C08000C6483 /* drag_icon.png */; };
FFCDD8FE17F6368F000C6483 /* MCSwipeTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FFCDD8FD17F6368F000C6483 /* MCSwipeTableViewCell.m */; };
FFCDD90117F65A71000C6483 /* NBSwipeableCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FFCDD90017F65A71000C6483 /* NBSwipeableCell.m */; };
FFD1D7311459B63500E46F89 /* BaseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FFD1D7301459B63500E46F89 /* BaseViewController.m */; };
FFD887F01445F1E800385399 /* AddSiteAutocompleteCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FFD887EE1445F1E800385399 /* AddSiteAutocompleteCell.m */; };
FFDCA0AF16E80866000D8E0C /* DEComposeRuledView.m in Sources */ = {isa = PBXBuildFile; fileRef = FFDCA0A516E80866000D8E0C /* DEComposeRuledView.m */; };
@ -430,20 +407,20 @@
FFE816AD17E29D71008AF4B0 /* big_world_white.png in Resources */ = {isa = PBXBuildFile; fileRef = FFE816AC17E29D71008AF4B0 /* big_world_white.png */; };
FFECD019172B105800D45A62 /* UIActivitySafari.png in Resources */ = {isa = PBXBuildFile; fileRef = FFECD017172B105800D45A62 /* UIActivitySafari.png */; };
FFECD01A172B105800D45A62 /* UIActivitySafari@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFECD018172B105800D45A62 /* UIActivitySafari@2x.png */; };
FFF1E4B9177504CA00BF59D3 /* IASKAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E49B177504CA00BF59D3 /* IASKAppSettingsViewController.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FFF1E4BA177504CA00BF59D3 /* IASKAppSettingsWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E49D177504CA00BF59D3 /* IASKAppSettingsWebViewController.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FFF1E4BB177504CA00BF59D3 /* IASKSpecifierValuesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E49F177504CA00BF59D3 /* IASKSpecifierValuesViewController.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FFF1E4BC177504CA00BF59D3 /* IASKSettingsReader.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4A3177504CA00BF59D3 /* IASKSettingsReader.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FFF1E4BD177504CA00BF59D3 /* IASKSettingsStore.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4A5177504CA00BF59D3 /* IASKSettingsStore.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FFF1E4BE177504CA00BF59D3 /* IASKSettingsStoreFile.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4A7177504CA00BF59D3 /* IASKSettingsStoreFile.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FFF1E4BF177504CA00BF59D3 /* IASKSettingsStoreUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4A9177504CA00BF59D3 /* IASKSettingsStoreUserDefaults.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FFF1E4C0177504CA00BF59D3 /* IASKSpecifier.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4AB177504CA00BF59D3 /* IASKSpecifier.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FFF1E4C1177504CA00BF59D3 /* IASKPSSliderSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4AE177504CA00BF59D3 /* IASKPSSliderSpecifierViewCell.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FFF1E4C2177504CA00BF59D3 /* IASKPSTextFieldSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4B0177504CA00BF59D3 /* IASKPSTextFieldSpecifierViewCell.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FFF1E4C3177504CA00BF59D3 /* IASKPSTitleValueSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4B2177504CA00BF59D3 /* IASKPSTitleValueSpecifierViewCell.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FFF1E4C4177504CA00BF59D3 /* IASKSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4B4177504CA00BF59D3 /* IASKSlider.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FFF1E4C5177504CA00BF59D3 /* IASKSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4B6177504CA00BF59D3 /* IASKSwitch.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FFF1E4C6177504CA00BF59D3 /* IASKTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4B8177504CA00BF59D3 /* IASKTextField.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
FFF1E4B9177504CA00BF59D3 /* IASKAppSettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E49B177504CA00BF59D3 /* IASKAppSettingsViewController.m */; };
FFF1E4BA177504CA00BF59D3 /* IASKAppSettingsWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E49D177504CA00BF59D3 /* IASKAppSettingsWebViewController.m */; };
FFF1E4BB177504CA00BF59D3 /* IASKSpecifierValuesViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E49F177504CA00BF59D3 /* IASKSpecifierValuesViewController.m */; };
FFF1E4BC177504CA00BF59D3 /* IASKSettingsReader.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4A3177504CA00BF59D3 /* IASKSettingsReader.m */; };
FFF1E4BD177504CA00BF59D3 /* IASKSettingsStore.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4A5177504CA00BF59D3 /* IASKSettingsStore.m */; };
FFF1E4BE177504CA00BF59D3 /* IASKSettingsStoreFile.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4A7177504CA00BF59D3 /* IASKSettingsStoreFile.m */; };
FFF1E4BF177504CA00BF59D3 /* IASKSettingsStoreUserDefaults.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4A9177504CA00BF59D3 /* IASKSettingsStoreUserDefaults.m */; };
FFF1E4C0177504CA00BF59D3 /* IASKSpecifier.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4AB177504CA00BF59D3 /* IASKSpecifier.m */; };
FFF1E4C1177504CA00BF59D3 /* IASKPSSliderSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4AE177504CA00BF59D3 /* IASKPSSliderSpecifierViewCell.m */; };
FFF1E4C2177504CA00BF59D3 /* IASKPSTextFieldSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4B0177504CA00BF59D3 /* IASKPSTextFieldSpecifierViewCell.m */; };
FFF1E4C3177504CA00BF59D3 /* IASKPSTitleValueSpecifierViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4B2177504CA00BF59D3 /* IASKPSTitleValueSpecifierViewCell.m */; };
FFF1E4C4177504CA00BF59D3 /* IASKSlider.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4B4177504CA00BF59D3 /* IASKSlider.m */; };
FFF1E4C5177504CA00BF59D3 /* IASKSwitch.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4B6177504CA00BF59D3 /* IASKSwitch.m */; };
FFF1E4C6177504CA00BF59D3 /* IASKTextField.m in Sources */ = {isa = PBXBuildFile; fileRef = FFF1E4B8177504CA00BF59D3 /* IASKTextField.m */; };
FFF1E4C817750BDD00BF59D3 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = FFF1E4C717750BDD00BF59D3 /* Settings.bundle */; };
FFF1E4CB17750D2C00BF59D3 /* menu_icn_preferences.png in Resources */ = {isa = PBXBuildFile; fileRef = FFF1E4C917750D2C00BF59D3 /* menu_icn_preferences.png */; };
FFF1E4CC17750D2C00BF59D3 /* menu_icn_preferences@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FFF1E4CA17750D2C00BF59D3 /* menu_icn_preferences@2x.png */; };
@ -579,31 +556,14 @@
43A4C3F415B00A26008787B5 /* arrow@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "arrow@2x.png"; sourceTree = "<group>"; };
43A4C3F715B00A26008787B5 /* Background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Background.png; sourceTree = "<group>"; };
43A4C3F815B00A26008787B5 /* Background@2X.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Background@2X.png"; sourceTree = "<group>"; };
43A4C40015B00A26008787B5 /* Default-Landscape.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape.png"; sourceTree = "<group>"; };
43A4C40115B00A26008787B5 /* Default-Landscape@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape@2x.png"; sourceTree = "<group>"; };
43A4C40215B00A26008787B5 /* Default-Landscape@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape@2x~ipad.png"; sourceTree = "<group>"; };
43A4C40315B00A26008787B5 /* Default-Landscape~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape~ipad.png"; sourceTree = "<group>"; };
43A4C40415B00A26008787B5 /* Default-Portrait.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait.png"; sourceTree = "<group>"; };
43A4C40515B00A26008787B5 /* Default-Portrait@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait@2x.png"; sourceTree = "<group>"; };
43A4C40615B00A26008787B5 /* Default-Portrait@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait@2x~ipad.png"; sourceTree = "<group>"; };
43A4C40715B00A26008787B5 /* Default-Portrait~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait~ipad.png"; sourceTree = "<group>"; };
43A4C40815B00A26008787B5 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = "<group>"; };
43A4C40915B00A26008787B5 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = "<group>"; };
43A4C40B15B00A26008787B5 /* disclosure.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = disclosure.png; sourceTree = "<group>"; };
43A4C41015B00A26008787B5 /* feeds_background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = feeds_background.png; sourceTree = "<group>"; };
43A4C41115B00A26008787B5 /* fleuron.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = fleuron.png; sourceTree = "<group>"; };
43A4C41F15B00A26008787B5 /* logo_57.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_57.png; sourceTree = "<group>"; };
43A4C42015B00A26008787B5 /* logo_72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_72.png; sourceTree = "<group>"; };
43A4C42115B00A26008787B5 /* logo_114.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_114.png; sourceTree = "<group>"; };
43A4C42215B00A26008787B5 /* logo_144.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_144.png; sourceTree = "<group>"; };
43A4C42315B00A26008787B5 /* logo_512.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_512.png; sourceTree = "<group>"; };
43A4C42415B00A26008787B5 /* logo_background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_background.png; sourceTree = "<group>"; };
43A4C42515B00A26008787B5 /* logo_newsblur_512.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_newsblur_512.png; sourceTree = "<group>"; };
43A4C42615B00A26008787B5 /* logo_newsblur_blur.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_newsblur_blur.png; sourceTree = "<group>"; };
43A4C43F15B00A26008787B5 /* warning.gif */ = {isa = PBXFileReference; lastKnownFileType = image.gif; path = warning.gif; sourceTree = "<group>"; };
43A4C44015B00A26008787B5 /* warning.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = warning.png; sourceTree = "<group>"; };
43A4C44115B00A26008787B5 /* world.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = world.png; sourceTree = "<group>"; };
43A4C49D15B00CD3008787B5 /* Entitlements.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Entitlements.entitlements; sourceTree = "<group>"; };
43ABBCA815B53A1400EA3111 /* InteractionCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InteractionCell.h; sourceTree = "<group>"; };
43ABBCA915B53A1400EA3111 /* InteractionCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InteractionCell.m; sourceTree = "<group>"; };
43AF417515C30B1600758366 /* 37x-Checkmark.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "37x-Checkmark.png"; sourceTree = "<group>"; };
@ -633,10 +593,8 @@
43C1680A15B3D99B00428BA3 /* 7-location-place.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "7-location-place.png"; sourceTree = "<group>"; };
43C3D40215D44EA30066D36D /* user_light.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = user_light.png; sourceTree = "<group>"; };
43C3D40315D44EA30066D36D /* user_light@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "user_light@2x.png"; sourceTree = "<group>"; };
43C3D40715D4680C0066D36D /* green_focus.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = green_focus.png; sourceTree = "<group>"; };
43C3D40915D468BA0066D36D /* all.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = all.png; sourceTree = "<group>"; };
43C3D40B15D469010066D36D /* unread_yellow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = unread_yellow.png; sourceTree = "<group>"; };
43C3D40F15D46D2B0066D36D /* green_focus@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "green_focus@2x.png"; sourceTree = "<group>"; };
43C3D41115D46D330066D36D /* all@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "all@2x.png"; sourceTree = "<group>"; };
43C95C47158BEC440086C69B /* GoogleReaderViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = GoogleReaderViewController.xib; path = Classes/GoogleReaderViewController.xib; sourceTree = "<group>"; };
43CE0F5D15DADB7E00608ED8 /* SiteCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SiteCell.h; sourceTree = "<group>"; };
@ -769,16 +727,8 @@
FF4151C216DEDF9D0013E84B /* markread@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "markread@2x.png"; sourceTree = "<group>"; };
FF4151C516DEF1A80013E84B /* original_button.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = original_button.png; sourceTree = "<group>"; };
FF4151C616DEF1A80013E84B /* original_button@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "original_button@2x.png"; sourceTree = "<group>"; };
FF4151E116DEF9960013E84B /* back_button_background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = back_button_background.png; sourceTree = "<group>"; };
FF4151E216DEF9960013E84B /* back_button_background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "back_button_background@2x.png"; sourceTree = "<group>"; };
FF4151E316DEF9960013E84B /* back_button_landscape_background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = back_button_landscape_background.png; sourceTree = "<group>"; };
FF4151E416DEF9960013E84B /* back_button_landscape_background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "back_button_landscape_background@2x.png"; sourceTree = "<group>"; };
FF4151E516DEF9960013E84B /* back_button_landscape_selected_background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = back_button_landscape_selected_background.png; sourceTree = "<group>"; };
FF4151E616DEF9960013E84B /* back_button_landscape_selected_background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "back_button_landscape_selected_background@2x.png"; sourceTree = "<group>"; };
FF4151E716DEF9960013E84B /* back_button_selected_background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = back_button_selected_background.png; sourceTree = "<group>"; };
FF4151E816DEF9960013E84B /* back_button_selected_background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "back_button_selected_background@2x.png"; sourceTree = "<group>"; };
FF468E3717FCFB970080325C /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
FF4EE9A3175EC9CF005891B5 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; };
FF546DF61602930100948020 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
FF546DF8160298E500948020 /* fleuron@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "fleuron@2x.png"; sourceTree = "<group>"; };
FF5D3FEA1799F53C00349659 /* VUPinboardAccess.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VUPinboardAccess.h; sourceTree = "<group>"; };
FF5D3FEB1799F53C00349659 /* VUPinboardAccess.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VUPinboardAccess.m; sourceTree = "<group>"; };
@ -822,9 +772,8 @@
FF67D3B0168924C40057A7DA /* TrainerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TrainerViewController.h; sourceTree = "<group>"; };
FF67D3B1168924C40057A7DA /* TrainerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TrainerViewController.m; sourceTree = "<group>"; };
FF67D3B6168977690057A7DA /* TrainerViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TrainerViewController.xib; sourceTree = "<group>"; };
FF67D3B816897AD80057A7DA /* TrainerViewController~ipad.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = "TrainerViewController~ipad.xib"; path = "Resources-iPad/Classes/TrainerViewController~ipad.xib"; sourceTree = "<group>"; };
FF67D3BA168A70630057A7DA /* trainer.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = trainer.css; path = ../static/trainer.css; sourceTree = "<group>"; };
FF67D3BD168A708D0057A7DA /* storyDetailView.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = storyDetailView.css; path = ../static/storyDetailView.css; sourceTree = "<group>"; };
FF67D3BD168A708D0057A7DA /* storyDetailView.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = storyDetailView.css; path = ../static/storyDetailView.css; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.css; };
FF67D3BE168A708D0057A7DA /* storyDetailView.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = storyDetailView.js; path = ../static/storyDetailView.js; sourceTree = "<group>"; };
FF67D3BF168A708D0057A7DA /* zepto.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = zepto.js; path = ../static/zepto.js; sourceTree = "<group>"; };
FF67D3C4168A71870057A7DA /* trainer.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = trainer.js; path = ../static/trainer.js; sourceTree = "<group>"; };
@ -875,6 +824,10 @@
FF85BF7016D6A90D002D334D /* ak-icon-global.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ak-icon-global.png"; sourceTree = "<group>"; };
FF85BF7216D6A972002D334D /* ak-icon-allstories.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ak-icon-allstories.png"; sourceTree = "<group>"; };
FF85BF7316D6A972002D334D /* ak-icon-blurblogs.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "ak-icon-blurblogs.png"; sourceTree = "<group>"; };
FF88F10B1811BAEC007FEE78 /* unread_green@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "unread_green@2x.png"; sourceTree = "<group>"; };
FF88F10C1811BAEC007FEE78 /* unread_green.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = unread_green.png; sourceTree = "<group>"; };
FF9B8BB017F2351A0036A41C /* NBBarButtonItem.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NBBarButtonItem.h; sourceTree = "<group>"; };
FF9B8BB117F2351A0036A41C /* NBBarButtonItem.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NBBarButtonItem.m; sourceTree = "<group>"; };
FFAD4970144A386100BA6919 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
FFAF53451799D18B00C7FCCB /* RWInstapaperActivity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RWInstapaperActivity.h; sourceTree = "<group>"; };
FFAF53461799D18B00C7FCCB /* RWInstapaperActivity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RWInstapaperActivity.m; sourceTree = "<group>"; };
@ -938,6 +891,13 @@
FFB5F50A17187B6F00C8B432 /* big_world.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = big_world.png; sourceTree = "<group>"; };
FFB757FC1727098D001D132F /* menu_icn_mail.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = menu_icn_mail.png; sourceTree = "<group>"; };
FFB757FD1727098D001D132F /* menu_icn_mail@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu_icn_mail@2x.png"; sourceTree = "<group>"; };
FFB9BE4017F4B65B00FE0A36 /* logo_120.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_120.png; sourceTree = "<group>"; };
FFB9BE4117F4B65B00FE0A36 /* logo_152.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_152.png; sourceTree = "<group>"; };
FFB9BE4217F4B65B00FE0A36 /* logo_29.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_29.png; sourceTree = "<group>"; };
FFB9BE4317F4B65B00FE0A36 /* logo_40.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_40.png; sourceTree = "<group>"; };
FFB9BE4417F4B65B00FE0A36 /* logo_58.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_58.png; sourceTree = "<group>"; };
FFB9BE4517F4B65B00FE0A36 /* logo_76.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_76.png; sourceTree = "<group>"; };
FFB9BE4617F4B65B00FE0A36 /* logo_80.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo_80.png; sourceTree = "<group>"; };
FFC518B81768E59F00542719 /* g_icn_offline.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = g_icn_offline.png; sourceTree = "<group>"; };
FFC5F2FF16E2D2C2007AC72C /* story_share_appnet_active.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = story_share_appnet_active.png; sourceTree = "<group>"; };
FFC5F30016E2D2C2007AC72C /* story_share_appnet_active@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "story_share_appnet_active@2x.png"; sourceTree = "<group>"; };
@ -951,21 +911,18 @@
FFC5F30816E2D2C2007AC72C /* story_share_twitter_active@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "story_share_twitter_active@2x.png"; sourceTree = "<group>"; };
FFC5F30916E2D2C2007AC72C /* story_share_twitter.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = story_share_twitter.png; sourceTree = "<group>"; };
FFC5F30A16E2D2C2007AC72C /* story_share_twitter@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "story_share_twitter@2x.png"; sourceTree = "<group>"; };
FFC5F31716E2E9FB007AC72C /* toolbar_button_landscape_selected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = toolbar_button_landscape_selected.png; sourceTree = "<group>"; };
FFC5F31816E2E9FB007AC72C /* toolbar_button_landscape.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = toolbar_button_landscape.png; sourceTree = "<group>"; };
FFC5F31916E2E9FB007AC72C /* toolbar_button_landscape@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar_button_landscape@2x.png"; sourceTree = "<group>"; };
FFC5F31A16E2E9FB007AC72C /* toolbar_button_selected_landscape@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar_button_selected_landscape@2x.png"; sourceTree = "<group>"; };
FFC5F31F16E2F24B007AC72C /* white_spacer.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = white_spacer.png; sourceTree = "<group>"; };
FFCF51A116E020DD008C7C42 /* toolbar_background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = toolbar_background.png; sourceTree = "<group>"; };
FFCF51A216E020DD008C7C42 /* toolbar_background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "toolbar_background@2x.png"; sourceTree = "<group>"; };
FFCF51A516E028B0008C7C42 /* navbar_background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = navbar_background.png; sourceTree = "<group>"; };
FFCF51A616E028B0008C7C42 /* navbar_background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "navbar_background@2x.png"; sourceTree = "<group>"; };
FFCF51A916E029E6008C7C42 /* navbar_landscape_background.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = navbar_landscape_background.png; sourceTree = "<group>"; };
FFCF51AA16E029E6008C7C42 /* navbar_landscape_background@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "navbar_landscape_background@2x.png"; sourceTree = "<group>"; };
FFCF51C116E0300F008C7C42 /* button_selected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = button_selected.png; sourceTree = "<group>"; };
FFCF51C216E0300F008C7C42 /* button_selected@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "button_selected@2x.png"; sourceTree = "<group>"; };
FFCF51C316E0300F008C7C42 /* button.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = button.png; sourceTree = "<group>"; };
FFCF51C416E0300F008C7C42 /* button@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "button@2x.png"; sourceTree = "<group>"; };
FFCDD8ED17F4BCB4000C6483 /* Default-568h@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x.png"; sourceTree = "<group>"; };
FFCDD8EE17F4BCB4000C6483 /* Default-landscape.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-landscape.png"; sourceTree = "<group>"; };
FFCDD8EF17F4BCB4000C6483 /* Default-landscape@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-landscape@2x.png"; sourceTree = "<group>"; };
FFCDD8F017F4BCB4000C6483 /* Default-portrait.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-portrait.png"; sourceTree = "<group>"; };
FFCDD8F117F4BCB4000C6483 /* Default-portrait@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-portrait@2x.png"; sourceTree = "<group>"; };
FFCDD8F217F4BCB4000C6483 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = "<group>"; };
FFCDD8F917F50C08000C6483 /* drag_icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = drag_icon.png; sourceTree = "<group>"; };
FFCDD8FC17F6368F000C6483 /* MCSwipeTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MCSwipeTableViewCell.h; sourceTree = "<group>"; };
FFCDD8FD17F6368F000C6483 /* MCSwipeTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MCSwipeTableViewCell.m; sourceTree = "<group>"; };
FFCDD8FF17F65A71000C6483 /* NBSwipeableCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NBSwipeableCell.h; sourceTree = "<group>"; };
FFCDD90017F65A71000C6483 /* NBSwipeableCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NBSwipeableCell.m; sourceTree = "<group>"; };
FFD1D72F1459B63500E46F89 /* BaseViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseViewController.h; sourceTree = "<group>"; };
FFD1D7301459B63500E46F89 /* BaseViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BaseViewController.m; sourceTree = "<group>"; };
FFD887ED1445F1E800385399 /* AddSiteAutocompleteCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddSiteAutocompleteCell.h; sourceTree = "<group>"; };
@ -1076,6 +1033,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
FF468E3817FCFB970080325C /* AVFoundation.framework in Frameworks */,
78095E45128EF37E00230C8E /* SystemConfiguration.framework in Frameworks */,
FF4EE9A4175EC9CF005891B5 /* libsqlite3.dylib in Frameworks */,
FFDCA0C316E80952000D8E0C /* AdSupport.framework in Frameworks */,
FFDCA0C116E8094F000D8E0C /* Accounts.framework in Frameworks */,
@ -1091,7 +1050,6 @@
788EF356127E5BC80088EDC5 /* QuartzCore.framework in Frameworks */,
78095E3F128EF35400230C8E /* CFNetwork.framework in Frameworks */,
78095E43128EF37E00230C8E /* MobileCoreServices.framework in Frameworks */,
78095E45128EF37E00230C8E /* SystemConfiguration.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1133,7 +1091,6 @@
29B97323FDCFA39411CA2CEA /* Frameworks */,
19C28FACFE9D520D11CA2CBB /* Products */,
8D1107310486CEB800E47090 /* NewsBlur-iPhone-Info.plist */,
43A4C49D15B00CD3008787B5 /* Entitlements.entitlements */,
);
name = CustomTemplate;
sourceTree = "<group>";
@ -1141,6 +1098,7 @@
29B97315FDCFA39411CA2CEA /* Other Sources */ = {
isa = PBXGroup;
children = (
FFCDD8FB17F6368F000C6483 /* MCSwipeTableViewCell */,
FF5D40091799F70200349659 /* ARChromeActivity */,
FFAF53951799E69D00C7FCCB /* PocketSDK */,
FFAF53A61799E69D00C7FCCB /* ReadabilityActivity */,
@ -1184,6 +1142,7 @@
29B97323FDCFA39411CA2CEA /* Frameworks */ = {
isa = PBXGroup;
children = (
FF468E3717FCFB970080325C /* AVFoundation.framework */,
FF4EE9A3175EC9CF005891B5 /* libsqlite3.dylib */,
FFDCA0C216E80952000D8E0C /* AdSupport.framework */,
FFDCA0C016E8094F000D8E0C /* Accounts.framework */,
@ -1334,6 +1293,8 @@
431B857615A132B600DCE497 /* Images */ = {
isa = PBXGroup;
children = (
FFCDD8F917F50C08000C6483 /* drag_icon.png */,
FFB9BE3F17F4B65B00FE0A36 /* logos */,
FFE816AC17E29D71008AF4B0 /* big_world_white.png */,
FFE816AA17E280BD008AF4B0 /* ftux_tree.png */,
FF855B541794A53A0098D48A /* checkmark.png */,
@ -1403,10 +1364,6 @@
FF22FE4816E413C30046165A /* disclosure_border@2x.png */,
FF22FE4916E413C30046165A /* disclosure@2x.png */,
FFC5F31F16E2F24B007AC72C /* white_spacer.png */,
FFC5F31716E2E9FB007AC72C /* toolbar_button_landscape_selected.png */,
FFC5F31816E2E9FB007AC72C /* toolbar_button_landscape.png */,
FFC5F31916E2E9FB007AC72C /* toolbar_button_landscape@2x.png */,
FFC5F31A16E2E9FB007AC72C /* toolbar_button_selected_landscape@2x.png */,
FFC5F2FF16E2D2C2007AC72C /* story_share_appnet_active.png */,
FFC5F30016E2D2C2007AC72C /* story_share_appnet_active@2x.png */,
FFC5F30116E2D2C2007AC72C /* story_share_appnet.png */,
@ -1419,24 +1376,6 @@
FFC5F30816E2D2C2007AC72C /* story_share_twitter_active@2x.png */,
FFC5F30916E2D2C2007AC72C /* story_share_twitter.png */,
FFC5F30A16E2D2C2007AC72C /* story_share_twitter@2x.png */,
FFCF51C116E0300F008C7C42 /* button_selected.png */,
FFCF51C216E0300F008C7C42 /* button_selected@2x.png */,
FFCF51C316E0300F008C7C42 /* button.png */,
FFCF51C416E0300F008C7C42 /* button@2x.png */,
FFCF51A916E029E6008C7C42 /* navbar_landscape_background.png */,
FFCF51AA16E029E6008C7C42 /* navbar_landscape_background@2x.png */,
FFCF51A516E028B0008C7C42 /* navbar_background.png */,
FFCF51A616E028B0008C7C42 /* navbar_background@2x.png */,
FFCF51A116E020DD008C7C42 /* toolbar_background.png */,
FFCF51A216E020DD008C7C42 /* toolbar_background@2x.png */,
FF4151E116DEF9960013E84B /* back_button_background.png */,
FF4151E216DEF9960013E84B /* back_button_background@2x.png */,
FF4151E316DEF9960013E84B /* back_button_landscape_background.png */,
FF4151E416DEF9960013E84B /* back_button_landscape_background@2x.png */,
FF4151E516DEF9960013E84B /* back_button_landscape_selected_background.png */,
FF4151E616DEF9960013E84B /* back_button_landscape_selected_background@2x.png */,
FF4151E716DEF9960013E84B /* back_button_selected_background.png */,
FF4151E816DEF9960013E84B /* back_button_selected_background@2x.png */,
FF4151C516DEF1A80013E84B /* original_button.png */,
FF4151C616DEF1A80013E84B /* original_button@2x.png */,
FF4151C116DEDF9D0013E84B /* markread.png */,
@ -1484,10 +1423,10 @@
43821AD215D8703F0034A4EF /* 258-checkmark@2x.png */,
43C3D40B15D469010066D36D /* unread_yellow.png */,
FF29709316DD8CB000E92F85 /* unread_yellow@2x.png */,
FF88F10B1811BAEC007FEE78 /* unread_green@2x.png */,
FF88F10C1811BAEC007FEE78 /* unread_green.png */,
43C3D40915D468BA0066D36D /* all.png */,
43C3D41115D46D330066D36D /* all@2x.png */,
43C3D40715D4680C0066D36D /* green_focus.png */,
43C3D40F15D46D2B0066D36D /* green_focus@2x.png */,
43C3D40215D44EA30066D36D /* user_light.png */,
43C3D40315D44EA30066D36D /* user_light@2x.png */,
43A4BACC15C866FA00F3B8D4 /* popoverArrowDown.png */,
@ -1513,30 +1452,13 @@
43A4C3F415B00A26008787B5 /* arrow@2x.png */,
43A4C3F715B00A26008787B5 /* Background.png */,
43A4C3F815B00A26008787B5 /* Background@2X.png */,
43A4C40015B00A26008787B5 /* Default-Landscape.png */,
43A4C40115B00A26008787B5 /* Default-Landscape@2x.png */,
43A4C40215B00A26008787B5 /* Default-Landscape@2x~ipad.png */,
43A4C40315B00A26008787B5 /* Default-Landscape~ipad.png */,
43A4C40415B00A26008787B5 /* Default-Portrait.png */,
43A4C40515B00A26008787B5 /* Default-Portrait@2x.png */,
43A4C40615B00A26008787B5 /* Default-Portrait@2x~ipad.png */,
43A4C40715B00A26008787B5 /* Default-Portrait~ipad.png */,
FF546DF61602930100948020 /* Default-568h@2x.png */,
43A4C40815B00A26008787B5 /* Default.png */,
43A4C40915B00A26008787B5 /* Default@2x.png */,
43A4C40B15B00A26008787B5 /* disclosure.png */,
43A4C41015B00A26008787B5 /* feeds_background.png */,
43A4C41115B00A26008787B5 /* fleuron.png */,
FF546DF8160298E500948020 /* fleuron@2x.png */,
43B6A26F15B6952F00CEA2E6 /* group.png */,
43B6A27015B6952F00CEA2E6 /* group@2x.png */,
43A4C41F15B00A26008787B5 /* logo_57.png */,
43A4C42015B00A26008787B5 /* logo_72.png */,
43A4C42115B00A26008787B5 /* logo_114.png */,
43A4C42215B00A26008787B5 /* logo_144.png */,
43A4C42315B00A26008787B5 /* logo_512.png */,
43A4C42415B00A26008787B5 /* logo_background.png */,
43A4C42515B00A26008787B5 /* logo_newsblur_512.png */,
43A4C42615B00A26008787B5 /* logo_newsblur_blur.png */,
43A4C43F15B00A26008787B5 /* warning.gif */,
43A4C44015B00A26008787B5 /* warning.png */,
@ -1682,7 +1604,6 @@
FF1660C916D6E9B400AF8541 /* DashboardViewController~ipad.xib */,
43D0451F1565BC150085F811 /* MainWindow~ipad.xib */,
43D045281565BC150085F811 /* MoveSiteViewController~ipad.xib */,
FF67D3B816897AD80057A7DA /* TrainerViewController~ipad.xib */,
43D045221565BC150085F811 /* StoryDetailViewController~ipad.xib */,
437AA8C9159394E2005463F5 /* ShareViewController~ipad.xib */,
);
@ -1763,6 +1684,10 @@
FF6618C7176184560039913B /* NBNotifier.m */,
FF11045D176950F900502C29 /* NBLoadingCell.h */,
FF11045E176950F900502C29 /* NBLoadingCell.m */,
FF9B8BB017F2351A0036A41C /* NBBarButtonItem.h */,
FF9B8BB117F2351A0036A41C /* NBBarButtonItem.m */,
FFCDD8FF17F65A71000C6483 /* NBSwipeableCell.h */,
FFCDD90017F65A71000C6483 /* NBSwipeableCell.m */,
);
name = Foundation;
sourceTree = "<group>";
@ -2008,6 +1933,36 @@
path = "Other Sources/ReadabilityActivity";
sourceTree = "<group>";
};
FFB9BE3F17F4B65B00FE0A36 /* logos */ = {
isa = PBXGroup;
children = (
FFCDD8ED17F4BCB4000C6483 /* Default-568h@2x.png */,
FFCDD8EE17F4BCB4000C6483 /* Default-landscape.png */,
FFCDD8EF17F4BCB4000C6483 /* Default-landscape@2x.png */,
FFCDD8F017F4BCB4000C6483 /* Default-portrait.png */,
FFCDD8F117F4BCB4000C6483 /* Default-portrait@2x.png */,
FFCDD8F217F4BCB4000C6483 /* Default.png */,
FFB9BE4017F4B65B00FE0A36 /* logo_120.png */,
FFB9BE4117F4B65B00FE0A36 /* logo_152.png */,
FFB9BE4217F4B65B00FE0A36 /* logo_29.png */,
FFB9BE4317F4B65B00FE0A36 /* logo_40.png */,
FFB9BE4417F4B65B00FE0A36 /* logo_58.png */,
FFB9BE4517F4B65B00FE0A36 /* logo_76.png */,
FFB9BE4617F4B65B00FE0A36 /* logo_80.png */,
);
path = logos;
sourceTree = "<group>";
};
FFCDD8FB17F6368F000C6483 /* MCSwipeTableViewCell */ = {
isa = PBXGroup;
children = (
FFCDD8FC17F6368F000C6483 /* MCSwipeTableViewCell.h */,
FFCDD8FD17F6368F000C6483 /* MCSwipeTableViewCell.m */,
);
name = MCSwipeTableViewCell;
path = "Other Sources/MCSwipeTableViewCell";
sourceTree = "<group>";
};
FFDCA0A316E80866000D8E0C /* REComposeViewController */ = {
isa = PBXGroup;
children = (
@ -2127,6 +2082,11 @@
attributes = {
LastUpgradeCheck = 0500;
ORGANIZATIONNAME = NewsBlur;
TargetAttributes = {
1D6058900D05DD3D006BFB54 = {
DevelopmentTeam = U92APKK285;
};
};
};
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "NewsBlur" */;
compatibilityVersion = "Xcode 3.2";
@ -2200,36 +2160,21 @@
43763AD2158F90B100B3DBE2 /* FontSettingsViewController.xib in Resources */,
439DAB211590DA350019B0EB /* FeedsMenuViewController.xib in Resources */,
437AA8CB159394E2005463F5 /* ShareViewController~ipad.xib in Resources */,
FF88F10D1811BAEC007FEE78 /* unread_green@2x.png in Resources */,
43081E2215AFE84200B24D7A /* ShareViewController.xib in Resources */,
43A4C44E15B00A26008787B5 /* arrow.png in Resources */,
43A4C44F15B00A26008787B5 /* arrow@2x.png in Resources */,
43A4C45215B00A26008787B5 /* Background.png in Resources */,
43A4C45315B00A26008787B5 /* Background@2X.png in Resources */,
43A4C45B15B00A26008787B5 /* Default-Landscape.png in Resources */,
43A4C45C15B00A26008787B5 /* Default-Landscape@2x.png in Resources */,
43A4C45D15B00A26008787B5 /* Default-Landscape@2x~ipad.png in Resources */,
43A4C45E15B00A26008787B5 /* Default-Landscape~ipad.png in Resources */,
43A4C45F15B00A26008787B5 /* Default-Portrait.png in Resources */,
43A4C46015B00A26008787B5 /* Default-Portrait@2x.png in Resources */,
43A4C46115B00A26008787B5 /* Default-Portrait@2x~ipad.png in Resources */,
43A4C46215B00A26008787B5 /* Default-Portrait~ipad.png in Resources */,
43A4C46315B00A26008787B5 /* Default.png in Resources */,
43A4C46415B00A26008787B5 /* Default@2x.png in Resources */,
43A4C46615B00A26008787B5 /* disclosure.png in Resources */,
43A4C46B15B00A26008787B5 /* feeds_background.png in Resources */,
43A4C46C15B00A26008787B5 /* fleuron.png in Resources */,
43A4C47A15B00A26008787B5 /* logo_57.png in Resources */,
43A4C47B15B00A26008787B5 /* logo_72.png in Resources */,
43A4C47C15B00A26008787B5 /* logo_114.png in Resources */,
43A4C47D15B00A26008787B5 /* logo_144.png in Resources */,
43A4C47E15B00A26008787B5 /* logo_512.png in Resources */,
43A4C47F15B00A26008787B5 /* logo_background.png in Resources */,
43A4C48015B00A26008787B5 /* logo_newsblur_512.png in Resources */,
FFB9BE4717F4B65B00FE0A36 /* logo_120.png in Resources */,
43A4C48115B00A26008787B5 /* logo_newsblur_blur.png in Resources */,
43A4C49A15B00A26008787B5 /* warning.gif in Resources */,
43A4C49B15B00A26008787B5 /* warning.png in Resources */,
43A4C49C15B00A26008787B5 /* world.png in Resources */,
43A4C49E15B00CD3008787B5 /* Entitlements.entitlements in Resources */,
43C1680B15B3D99B00428BA3 /* 7-location-place.png in Resources */,
43B6A27515B6952F00CEA2E6 /* group.png in Resources */,
43B6A27615B6952F00CEA2E6 /* group@2x.png in Resources */,
@ -2242,6 +2187,7 @@
43A4BADC15C866FA00F3B8D4 /* popoverArrowDown@2x.png in Resources */,
43A4BADD15C866FA00F3B8D4 /* popoverArrowDownSimple.png in Resources */,
43A4BADE15C866FA00F3B8D4 /* popoverArrowLeft.png in Resources */,
FFB9BE4817F4B65B00FE0A36 /* logo_152.png in Resources */,
43A4BADF15C866FA00F3B8D4 /* popoverArrowLeft@2x.png in Resources */,
43A4BAE015C866FA00F3B8D4 /* popoverArrowLeftSimple.png in Resources */,
43A4BAE115C866FA00F3B8D4 /* popoverArrowRight.png in Resources */,
@ -2252,18 +2198,18 @@
43A4BAE615C866FA00F3B8D4 /* popoverArrowUpSimple.png in Resources */,
43A4BAE715C866FA00F3B8D4 /* popoverBg.png in Resources */,
43A4BAE815C866FA00F3B8D4 /* popoverBg@2x.png in Resources */,
FF88F10E1811BAEC007FEE78 /* unread_green.png in Resources */,
43A4BAE915C866FA00F3B8D4 /* popoverBgSimple.png in Resources */,
43A4BAEB15C893E300F3B8D4 /* FriendsListViewController.xib in Resources */,
432EBD0E15D1A2B00000729D /* fountain_pen_on.png in Resources */,
432EBD0F15D1A2B00000729D /* fountain_pen_on@2x.png in Resources */,
432EBD1615D1A7800000729D /* user_on.png in Resources */,
FFB9BE4C17F4B65B00FE0A36 /* logo_76.png in Resources */,
432EBD1715D1A7800000729D /* user_on@2x.png in Resources */,
43C3D40415D44EA30066D36D /* user_light.png in Resources */,
43C3D40515D44EA30066D36D /* user_light@2x.png in Resources */,
43C3D40815D4680C0066D36D /* green_focus.png in Resources */,
43C3D40A15D468BA0066D36D /* all.png in Resources */,
43C3D40C15D469010066D36D /* unread_yellow.png in Resources */,
43C3D41015D46D2B0066D36D /* green_focus@2x.png in Resources */,
43C3D41215D46D330066D36D /* all@2x.png in Resources */,
43B232C115D5F61700D035B4 /* AuthorizeServicesViewController.xib in Resources */,
430C4BC015D7208600B9F63B /* facebook.png in Resources */,
@ -2279,7 +2225,6 @@
43BC459315D9F76000205B69 /* facebook_button_on@2x.png in Resources */,
43BC459415D9F76000205B69 /* facebook_button.png in Resources */,
43BC459515D9F76000205B69 /* facebook_button@2x.png in Resources */,
FF546DF71602930100948020 /* Default-568h@2x.png in Resources */,
FF546DF9160298E500948020 /* fleuron@2x.png in Resources */,
FFDE35D2161B9E600034BFDE /* disclosure_border.png in Resources */,
FFDE35ED1627A1C40034BFDE /* FeedDetailMenuViewController.xib in Resources */,
@ -2292,9 +2237,10 @@
FF4130A0162CECAE00DDB6A7 /* email.png in Resources */,
FF6A23391644957800E15989 /* StoryPageControl.xib in Resources */,
FF67D3B7168977690057A7DA /* TrainerViewController.xib in Resources */,
FF67D3B916897AD80057A7DA /* TrainerViewController~ipad.xib in Resources */,
FF67D3BB168A70630057A7DA /* trainer.css in Resources */,
FFCDD8F617F4BCB4000C6483 /* Default-portrait.png in Resources */,
FF67D3C1168A708D0057A7DA /* storyDetailView.css in Resources */,
FFCDD8F317F4BCB4000C6483 /* Default-568h@2x.png in Resources */,
FF85BF6016D5A587002D334D /* nav_icn_activity_hover.png in Resources */,
FF85BF6116D5A587002D334D /* nav_icn_settings.png in Resources */,
FF85BF6316D5A5A8002D334D /* nav_icn_add.png in Resources */,
@ -2318,24 +2264,6 @@
FF4151C416DEDF9D0013E84B /* markread@2x.png in Resources */,
FF4151C716DEF1A80013E84B /* original_button.png in Resources */,
FF4151C816DEF1A80013E84B /* original_button@2x.png in Resources */,
FF4151E916DEF9960013E84B /* back_button_background.png in Resources */,
FF4151EA16DEF9960013E84B /* back_button_background@2x.png in Resources */,
FF4151EB16DEF9960013E84B /* back_button_landscape_background.png in Resources */,
FF4151EC16DEF9960013E84B /* back_button_landscape_background@2x.png in Resources */,
FF4151ED16DEF9960013E84B /* back_button_landscape_selected_background.png in Resources */,
FF4151EE16DEF9960013E84B /* back_button_landscape_selected_background@2x.png in Resources */,
FF4151EF16DEF9960013E84B /* back_button_selected_background.png in Resources */,
FF4151F016DEF9960013E84B /* back_button_selected_background@2x.png in Resources */,
FFCF51A316E020DD008C7C42 /* toolbar_background.png in Resources */,
FFCF51A416E020DD008C7C42 /* toolbar_background@2x.png in Resources */,
FFCF51A716E028B0008C7C42 /* navbar_background.png in Resources */,
FFCF51A816E028B0008C7C42 /* navbar_background@2x.png in Resources */,
FFCF51AB16E029E6008C7C42 /* navbar_landscape_background.png in Resources */,
FFCF51AC16E029E6008C7C42 /* navbar_landscape_background@2x.png in Resources */,
FFCF51C516E0300F008C7C42 /* button_selected.png in Resources */,
FFCF51C616E0300F008C7C42 /* button_selected@2x.png in Resources */,
FFCF51C716E0300F008C7C42 /* button.png in Resources */,
FFCF51C816E0300F008C7C42 /* button@2x.png in Resources */,
FFC5F30B16E2D2C2007AC72C /* story_share_appnet_active.png in Resources */,
FFC5F30C16E2D2C2007AC72C /* story_share_appnet_active@2x.png in Resources */,
FFC5F30D16E2D2C2007AC72C /* story_share_appnet.png in Resources */,
@ -2348,10 +2276,6 @@
FFC5F31416E2D2C2007AC72C /* story_share_twitter_active@2x.png in Resources */,
FFC5F31516E2D2C2007AC72C /* story_share_twitter.png in Resources */,
FFC5F31616E2D2C2007AC72C /* story_share_twitter@2x.png in Resources */,
FFC5F31B16E2E9FB007AC72C /* toolbar_button_landscape_selected.png in Resources */,
FFC5F31C16E2E9FB007AC72C /* toolbar_button_landscape.png in Resources */,
FFC5F31D16E2E9FB007AC72C /* toolbar_button_landscape@2x.png in Resources */,
FFC5F31E16E2E9FB007AC72C /* toolbar_button_selected_landscape@2x.png in Resources */,
FFC5F32016E2F24B007AC72C /* white_spacer.png in Resources */,
FF22FE4616E410A60046165A /* folder_collapse.png in Resources */,
FF22FE4716E410A60046165A /* folder_expand.png in Resources */,
@ -2367,6 +2291,7 @@
FF22FE6E16E554540046165A /* barbutton_refresh.png in Resources */,
FF22FE6F16E554540046165A /* barbutton_refresh@2x.png in Resources */,
FF22FE7216E554FD0046165A /* barbutton_sendto.png in Resources */,
FFB9BE4D17F4B65B00FE0A36 /* logo_80.png in Resources */,
FF22FE7316E554FD0046165A /* barbutton_sendto@2x.png in Resources */,
FF22FE7616E557D80046165A /* toolbar_tall_background.png in Resources */,
FF22FE7716E557D80046165A /* toolbar_tall_background@2x.png in Resources */,
@ -2392,8 +2317,11 @@
FFDD846616E8871A000AA0A2 /* menu_icn_train.png in Resources */,
FFDD846716E8871A000AA0A2 /* menu_icn_train@2x.png in Resources */,
FFDD846A16E88722000AA0A2 /* clock.png in Resources */,
FFCDD8F417F4BCB4000C6483 /* Default-landscape.png in Resources */,
FFDD846B16E88722000AA0A2 /* clock@2x.png in Resources */,
FFCDD8F717F4BCB4000C6483 /* Default-portrait@2x.png in Resources */,
FFDD847816E887D3000AA0A2 /* g_icn_focus.png in Resources */,
FFB9BE4B17F4B65B00FE0A36 /* logo_58.png in Resources */,
FFDD847916E887D3000AA0A2 /* g_icn_focus@2x.png in Resources */,
FFDD847A16E887D3000AA0A2 /* g_icn_folder_add.png in Resources */,
FFDD847B16E887D3000AA0A2 /* g_icn_folder_add@2x.png in Resources */,
@ -2409,6 +2337,7 @@
FFDD848716E8EB1E000AA0A2 /* share@2x.png in Resources */,
FFDD848A16E8EBF8000AA0A2 /* train.png in Resources */,
FFDD848B16E8EBF8000AA0A2 /* train@2x.png in Resources */,
FFB9BE4A17F4B65B00FE0A36 /* logo_40.png in Resources */,
FFDD848E16E8ED75000AA0A2 /* menu_icn_share.png in Resources */,
FFDD848F16E8ED75000AA0A2 /* menu_icn_share@2x.png in Resources */,
FFB5F50B17187B6F00C8B432 /* big_world.png in Resources */,
@ -2416,6 +2345,7 @@
FFB757FF1727098D001D132F /* menu_icn_mail@2x.png in Resources */,
FFECD019172B105800D45A62 /* UIActivitySafari.png in Resources */,
FFECD01A172B105800D45A62 /* UIActivitySafari@2x.png in Resources */,
FFB9BE4917F4B65B00FE0A36 /* logo_29.png in Resources */,
FF8364BB1755759A008F5C58 /* traverse_text.png in Resources */,
FF8364BC1755759A008F5C58 /* traverse_text@2x.png in Resources */,
FF8364BF1756949E008F5C58 /* traverse_text_on.png in Resources */,
@ -2434,6 +2364,7 @@
FFAF538F1799D1C800C7FCCB /* TUSafariActivity.strings in Resources */,
FFAF53901799D1C800C7FCCB /* Safari.png in Resources */,
FFAF53911799D1C800C7FCCB /* Safari@2x.png in Resources */,
FFCDD8F517F4BCB4000C6483 /* Default-landscape@2x.png in Resources */,
FFAF53921799D1C800C7FCCB /* Safari@2x~ipad.png in Resources */,
FFAF53931799D1C800C7FCCB /* Safari~ipad.png in Resources */,
FFAF53AD1799E69D00C7FCCB /* PocketActivity@2x~ipad.png in Resources */,
@ -2442,6 +2373,7 @@
FFAF53B01799E69D00C7FCCB /* PocketActivity~iphone.png in Resources */,
FFAF53B61799E69D00C7FCCB /* Readability-activity-iPad.png in Resources */,
FFAF53B71799E69D00C7FCCB /* Readability-activity-iPad@2x.png in Resources */,
FFCDD8F817F4BCB4000C6483 /* Default.png in Resources */,
FFAF53B81799E69D00C7FCCB /* Readability-activity-iPhone.png in Resources */,
FFAF53B91799E69D00C7FCCB /* Readability-activity-iPhone@2x.png in Resources */,
FF5D40051799F53C00349659 /* LICENSE in Resources */,
@ -2452,6 +2384,7 @@
FF5D40131799F70200349659 /* ARChromeActivity@2x~ipad.png in Resources */,
FF5D40141799F70200349659 /* ARChromeActivity~ipad.png in Resources */,
FF5D4017179A00B900349659 /* traverse_send.png in Resources */,
FFCDD8FA17F50C08000C6483 /* drag_icon.png in Resources */,
FF5D4018179A00B900349659 /* traverse_send@2x.png in Resources */,
FF5D401B179A03E700349659 /* traverse_previous_off.png in Resources */,
FF5D401C179A03E700349659 /* traverse_previous_off@2x.png in Resources */,
@ -2486,6 +2419,7 @@
78095E35128EF30D00230C8E /* ASIDownloadCache.m in Sources */,
78095E36128EF30D00230C8E /* ASIFormDataRequest.m in Sources */,
78095E37128EF30D00230C8E /* ASIHTTPRequest.m in Sources */,
FFCDD8FE17F6368F000C6483 /* MCSwipeTableViewCell.m in Sources */,
78095E38128EF30D00230C8E /* ASIInputStream.m in Sources */,
78095E39128EF30D00230C8E /* ASINetworkQueue.m in Sources */,
78095EC9128F30B500230C8E /* OriginalStoryViewController.m in Sources */,
@ -2495,6 +2429,7 @@
FFD887F01445F1E800385399 /* AddSiteAutocompleteCell.m in Sources */,
FFE5322F144C8AC300ACFDE0 /* Utilities.m in Sources */,
FFD1D7311459B63500E46F89 /* BaseViewController.m in Sources */,
FF9B8BB217F2351A0036A41C /* NBBarButtonItem.m in Sources */,
FF2D8CAF1488633A00057B80 /* Reachability.m in Sources */,
FF2D8CE514893BC000057B80 /* MoveSiteViewController.m in Sources */,
433323CD158968ED0025064D /* FirstTimeUserViewController.m in Sources */,
@ -2601,6 +2536,7 @@
FF5D3FFF1799F53C00349659 /* VUPinboardActivity.m in Sources */,
FF5D40001799F53C00349659 /* VUPinboardViewController.m in Sources */,
FF5D40011799F53C00349659 /* BBNetworkRequest.m in Sources */,
FFCDD90117F65A71000C6483 /* NBSwipeableCell.m in Sources */,
FF5D40021799F53C00349659 /* SSKeychain.m in Sources */,
FF5D40031799F53C00349659 /* VUDialogViewController.m in Sources */,
FF5D40041799F53C00349659 /* VUGradientButton.m in Sources */,
@ -2645,20 +2581,19 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = YES;
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = Entitlements.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution: NewsBlur, Inc. (HR7P97SD72)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: NewsBlur, Inc. (HR7P97SD72)";
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = NewsBlur_Prefix.pch;
GCC_THUMB_SUPPORT = NO;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_VERSION = "";
HEADER_SEARCH_PATHS = "";
INFOPLIST_FILE = "NewsBlur-iPhone-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)\"",
@ -2671,10 +2606,8 @@
"-all_load",
);
PRODUCT_NAME = NewsBlur;
PROVISIONING_PROFILE = "BF7FE540-5E58-4C3F-88D5-AD73981C42C0";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "BF7FE540-5E58-4C3F-88D5-AD73981C42C0";
PROVISIONING_PROFILE = "954389F4-8E6B-4312-9C44-7DCD5EAD40A5";
TARGETED_DEVICE_FAMILY = "1,2";
VALID_ARCHS = armv7;
"WARNING_CFLAGS[arch=*]" = "-Wall";
};
name = Debug;
@ -2683,18 +2616,17 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_INCLUDING_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
CODE_SIGN_ENTITLEMENTS = Entitlements.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution: NewsBlur, Inc. (HR7P97SD72)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: NewsBlur, Inc. (HR7P97SD72)";
COPY_PHASE_STRIP = YES;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = NewsBlur_Prefix.pch;
GCC_THUMB_SUPPORT = NO;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_VERSION = "";
HEADER_SEARCH_PATHS = "";
INFOPLIST_FILE = "NewsBlur-iPhone-Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
IPHONEOS_DEPLOYMENT_TARGET = 7.0;
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)\"",
@ -2706,19 +2638,17 @@
"-all_load",
);
PRODUCT_NAME = NewsBlur;
PROVISIONING_PROFILE = "BF7FE540-5E58-4C3F-88D5-AD73981C42C0";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "BF7FE540-5E58-4C3F-88D5-AD73981C42C0";
PROVISIONING_PROFILE = "954389F4-8E6B-4312-9C44-7DCD5EAD40A5";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
VALID_ARCHS = armv7;
};
name = Release;
};
C01FCF4F08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_IDENTITY = "iPhone Developer: Samuel Clay (3PN8E5365D)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Samuel Clay (3PN8E5365D)";
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = "compiler-default";
@ -2728,20 +2658,20 @@
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
ONLY_ACTIVE_ARCH = YES;
OTHER_LDFLAGS = "-ObjC";
PROVISIONING_PROFILE = "";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
PROVISIONING_PROFILE = "EB97D956-BB90-4F2F-9919-F71949B04B3F";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "EB97D956-BB90-4F2F-9919-F71949B04B3F";
RUN_CLANG_STATIC_ANALYZER = YES;
SDKROOT = iphoneos;
SDKROOT = iphoneos7.0;
STRIP_INSTALLED_PRODUCT = YES;
VALID_ARCHS = "arm64 armv7 armv7s";
VALID_ARCHS = "armv7s armv7 arm64";
};
name = Debug;
};
C01FCF5008A954540054247B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_IDENTITY = "iPhone Developer: Samuel Clay (3PN8E5365D)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: Samuel Clay (3PN8E5365D)";
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = "compiler-default";
@ -2749,13 +2679,14 @@
GCC_WARN_UNUSED_VARIABLE = YES;
HEADER_SEARCH_PATHS = "$(BUILT_PRODUCTS_DIR)/**";
IPHONEOS_DEPLOYMENT_TARGET = 6.0;
ONLY_ACTIVE_ARCH = NO;
OTHER_CFLAGS = "-DNS_BLOCK_ASSERTIONS=1";
OTHER_LDFLAGS = "-ObjC";
PROVISIONING_PROFILE = "";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "";
SDKROOT = iphoneos;
PROVISIONING_PROFILE = "EB97D956-BB90-4F2F-9919-F71949B04B3F";
"PROVISIONING_PROFILE[sdk=iphoneos*]" = "EB97D956-BB90-4F2F-9919-F71949B04B3F";
SDKROOT = iphoneos7.0;
STRIP_INSTALLED_PRODUCT = YES;
VALID_ARCHS = "arm64 armv7 armv7s";
VALID_ARCHS = "armv7s armv7 arm64";
};
name = Release;
};

View file

@ -2,6 +2,8 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "Underscore.h"
#import <SystemConfiguration/SystemConfiguration.h>
#import <MobileCoreServices/MobileCoreServices.h>
//#define DEBUG 1

View file

@ -59,22 +59,7 @@
@end
@interface IASKAppSettingsViewController : UITableViewController <IASKViewController, UITextFieldDelegate, MFMailComposeViewControllerDelegate> {
id<IASKSettingsDelegate> _delegate;
NSMutableArray *_viewList;
IASKSettingsReader *_settingsReader;
id<IASKSettingsStore> _settingsStore;
NSString *_file;
id _currentFirstResponder;
BOOL _showCreditsFooter;
BOOL _showDoneButton;
NSSet *_hiddenKeys;
}
@interface IASKAppSettingsViewController : UITableViewController <IASKViewController, UITextFieldDelegate, MFMailComposeViewControllerDelegate>
@property (nonatomic, assign) IBOutlet id delegate;
@property (nonatomic, copy) NSString *file;

View file

@ -27,9 +27,13 @@
#import "IASKSpecifierValuesViewController.h"
#import "IASKTextField.h"
static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.3;
static const CGFloat MINIMUM_SCROLL_FRACTION = 0.2;
static const CGFloat MAXIMUM_SCROLL_FRACTION = 0.8;
#if !__has_feature(objc_arc)
#error "IASK needs ARC"
#endif
static const CGFloat KEYBOARD_ANIMATION_DURATION = 0.3f;
static const CGFloat MINIMUM_SCROLL_FRACTION = 0.2f;
static const CGFloat MAXIMUM_SCROLL_FRACTION = 0.8f;
static NSString *kIASKCredits = @"Powered by InAppSettingsKit"; // Leave this as-is!!!
@ -40,9 +44,15 @@ static NSString *kIASKCredits = @"Powered by InAppSettingsKit"; // Leave this as
CGRect IASKCGRectSwap(CGRect rect);
@interface IASKAppSettingsViewController ()
@property (nonatomic, retain) NSMutableArray *viewList;
@property (nonatomic, retain) id currentFirstResponder;
@interface IASKAppSettingsViewController () {
IASKSettingsReader *_settingsReader;
id<IASKSettingsStore> _settingsStore;
id _currentFirstResponder;
__weak UIViewController *_currentChildViewController;
}
@property (nonatomic, strong) id currentFirstResponder;
- (void)_textChanged:(id)sender;
- (void)synchronizeSettings;
@ -51,16 +61,10 @@ CGRect IASKCGRectSwap(CGRect rect);
@end
@implementation IASKAppSettingsViewController
@synthesize delegate = _delegate;
@synthesize viewList = _viewList;
//synthesize properties from protocol
@synthesize settingsReader = _settingsReader;
@synthesize file = _file;
@synthesize currentFirstResponder = _currentFirstResponder;
@synthesize showCreditsFooter = _showCreditsFooter;
@synthesize showDoneButton = _showDoneButton;
@synthesize settingsStore = _settingsStore;
@synthesize hiddenKeys = _hiddenKeys;
@synthesize file = _file;
#pragma mark accessors
- (IASKSettingsReader*)settingsReader {
@ -81,20 +85,15 @@ CGRect IASKCGRectSwap(CGRect rect);
if (!_file) {
return @"Root";
}
return [[_file retain] autorelease];
return _file;
}
- (void)setFile:(NSString *)file {
if (file != _file) {
[_file release];
_file = [file copy];
}
_file = [file copy];
self.tableView.contentOffset = CGPointMake(0, 0);
self.settingsReader = nil; // automatically initializes itself
[_hiddenKeys release], _hiddenKeys = nil;
[self.tableView reloadData];
self.settingsReader = nil; // automatically initializes itself
_hiddenKeys = nil;
[self.tableView reloadData];
}
- (BOOL)isPad {
@ -117,8 +116,6 @@ CGRect IASKCGRectSwap(CGRect rect);
}
self = [super initWithStyle:UITableViewStyleGrouped];
if (self) {
self.tableView.delegate = self;
self.tableView.dataSource = self;
_showDoneButton = YES;
// If set to YES, will display credits for InAppSettingsKit creators
_showCreditsFooter = YES;
@ -135,24 +132,17 @@ CGRect IASKCGRectSwap(CGRect rect);
return [self initWithStyle:UITableViewStyleGrouped];
}
- (NSMutableArray *)viewList {
if (!_viewList) {
_viewList = [[NSMutableArray alloc] init];
[_viewList addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"IASKSpecifierValuesView", @"ViewName",nil]];
[_viewList addObject:[NSDictionary dictionaryWithObjectsAndKeys:@"IASKAppSettingsView", @"ViewName",nil]];
}
return _viewList;
}
- (void) viewDidLoad {
[super viewDidLoad];
if ([self isPad]) {
self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLineEtched;
}
[super viewDidLoad];
if ([self isPad]) {
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) // don't use etched style on iOS 7
#endif
self.tableView.separatorStyle = UITableViewCellSeparatorStyleSingleLineEtched;
}
UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(singleTapToEndEdit:)];
tapGesture.cancelsTouchesInView = NO;
[self.tableView addGestureRecognizer:tapGesture];
[tapGesture release];
}
- (void)viewDidUnload {
@ -161,7 +151,6 @@ CGRect IASKCGRectSwap(CGRect rect);
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.view = nil;
self.viewList = nil;
}
- (void)viewWillAppear:(BOOL)animated {
@ -175,13 +164,11 @@ CGRect IASKCGRectSwap(CGRect rect);
[self.tableView selectRowAtIndexPath:selectedIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone];
}
self.navigationItem.rightBarButtonItem = nil;
if (_showDoneButton) {
UIBarButtonItem *buttonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:@selector(dismiss:)];
self.navigationItem.rightBarButtonItem = buttonItem;
[buttonItem release];
}
if (!self.title) {
self.title = NSLocalizedString(@"Settings", @"");
@ -228,13 +215,6 @@ CGRect IASKCGRectSwap(CGRect rect);
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)setHiddenKeys:(NSSet *)theHiddenKeys {
[self setHiddenKeys:theHiddenKeys animated:NO];
}
@ -243,7 +223,7 @@ CGRect IASKCGRectSwap(CGRect rect);
- (void)setHiddenKeys:(NSSet*)theHiddenKeys animated:(BOOL)animated {
if (_hiddenKeys != theHiddenKeys) {
NSSet *oldHiddenKeys = _hiddenKeys;
_hiddenKeys = [theHiddenKeys retain];
_hiddenKeys = theHiddenKeys;
if (animated) {
[self.tableView beginUpdates];
@ -266,7 +246,7 @@ CGRect IASKCGRectSwap(CGRect rect);
// calculate sections to be deleted
NSMutableIndexSet *hideSections = [NSMutableIndexSet indexSet];
for (NSInteger section = 0; section < [self numberOfSectionsInTableView:self.tableView ]; section++) {
NSUInteger rowsInSection = 0;
NSInteger rowsInSection = 0;
for (NSIndexPath *indexPath in hideIndexPaths) {
if (indexPath.section == section) {
rowsInSection++;
@ -293,7 +273,7 @@ CGRect IASKCGRectSwap(CGRect rect);
// calculate sections to be inserted
NSMutableIndexSet *showSections = [NSMutableIndexSet indexSet];
for (NSInteger section = 0; section < [self.settingsReader numberOfSections]; section++) {
NSUInteger rowsInSection = 0;
NSInteger rowsInSection = 0;
for (NSIndexPath *indexPath in showIndexPaths) {
if (indexPath.section == section) {
rowsInSection++;
@ -304,7 +284,7 @@ CGRect IASKCGRectSwap(CGRect rect);
}
}
UITableViewRowAnimation animation = animated ? UITableViewRowAnimationTop : UITableViewRowAnimationNone;
UITableViewRowAnimation animation = animated ? UITableViewRowAnimationAutomatic : UITableViewRowAnimationNone;
[self.tableView deleteSections:hideSections withRowAnimation:animation];
[self.tableView deleteRowsAtIndexPaths:hideIndexPaths withRowAnimation:animation];
[self.tableView insertSections:showSections withRowAnimation:animation];
@ -314,28 +294,16 @@ CGRect IASKCGRectSwap(CGRect rect);
self.settingsReader.hiddenKeys = theHiddenKeys;
[self.tableView reloadData];
}
[oldHiddenKeys release];
}
IASKAppSettingsViewController *childViewController = [[self.viewList objectAtIndex:kIASKSpecifierChildViewControllerIndex] objectForKey:@"viewController"];
if(childViewController) {
[childViewController setHiddenKeys:theHiddenKeys animated:animated];
}
UIViewController *childViewController = _currentChildViewController;
if([childViewController respondsToSelector:@selector(setHiddenKeys:animated:)]) {
[(id)childViewController setHiddenKeys:theHiddenKeys animated:animated];
}
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_viewList release], _viewList = nil;
[_file release], _file = nil;
[_currentFirstResponder release], _currentFirstResponder = nil;
[_settingsReader release], _settingsReader = nil;
[_settingsStore release], _settingsStore = nil;
[_hiddenKeys release], _hiddenKeys = nil;
_delegate = nil;
[super dealloc];
}
@ -351,7 +319,7 @@ CGRect IASKCGRectSwap(CGRect rect);
}
- (void)toggledValue:(id)sender {
IASKSwitch *toggle = [[(IASKSwitch*)sender retain] autorelease];
IASKSwitch *toggle = (IASKSwitch*)sender;
IASKSpecifier *spec = [_settingsReader specifierForKey:[toggle key]];
if ([toggle isOn]) {
@ -377,7 +345,7 @@ CGRect IASKCGRectSwap(CGRect rect);
}
- (void)sliderChangedValue:(id)sender {
IASKSlider *slider = [[(IASKSlider*)sender retain] autorelease];
IASKSlider *slider = (IASKSlider*)sender;
[self.settingsStore setFloat:[slider value] forKey:[slider key]];
[[NSNotificationCenter defaultCenter] postNotificationName:kIASKAppSettingChanged
object:[slider key]
@ -435,10 +403,15 @@ CGRect IASKCGRectSwap(CGRect rect);
}
NSString *title;
if ((title = [self tableView:tableView titleForHeaderInSection:section])) {
CGSize size = [title sizeWithFont:[UIFont boldSystemFontOfSize:[UIFont labelFontSize]]
constrainedToSize:CGSizeMake(tableView.frame.size.width - 2*kIASKHorizontalPaddingGroupTitles, INFINITY)
lineBreakMode:NSLineBreakByWordWrapping];
return size.height+kIASKVerticalPaddingGroupTitles;
NSMutableParagraphStyle *paragraphStyle = [[NSMutableParagraphStyle defaultParagraphStyle] mutableCopy];
paragraphStyle.lineBreakMode = NSLineBreakByWordWrapping;
CGSize size = [title
boundingRectWithSize:CGSizeMake(tableView.frame.size.width - 2*kIASKHorizontalPaddingGroupTitles, INFINITY)
options:nil
attributes:@{NSFontAttributeName: [UIFont boldSystemFontOfSize:[UIFont labelFontSize]],
NSParagraphStyleAttributeName: paragraphStyle}
context:nil].size;
return roundf(size.height+kIASKVerticalPaddingGroupTitles);
}
return 0;
}
@ -467,7 +440,7 @@ CGRect IASKCGRectSwap(CGRect rect);
UITableViewCell *cell = nil;
if ([identifier isEqualToString:kIASKPSToggleSwitchSpecifier]) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kIASKPSToggleSwitchSpecifier];
cell.accessoryView = [[[IASKSwitch alloc] initWithFrame:CGRectMake(0, 0, 79, 27)] autorelease];
cell.accessoryView = [[IASKSwitch alloc] initWithFrame:CGRectMake(0, 0, 79, 27)];
[((IASKSwitch*)cell.accessoryView) addTarget:self action:@selector(toggledValue:) forControlEvents:UIControlEventValueChanged];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
@ -490,8 +463,8 @@ CGRect IASKCGRectSwap(CGRect rect);
} else {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
cell.textLabel.minimumScaleFactor = kIASKMinimumFontSize/[UIFont labelFontSize];
cell.detailTextLabel.minimumScaleFactor = kIASKMinimumFontSize/[UIFont labelFontSize];
cell.textLabel.minimumScaleFactor = kIASKMinimumFontSize/[UIFont labelFontSize];
cell.detailTextLabel.minimumScaleFactor = kIASKMinimumFontSize/[UIFont labelFontSize];
return cell;
}
@ -505,7 +478,7 @@ CGRect IASKCGRectSwap(CGRect rect);
UITableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:specifier.type];
if(nil == cell) {
cell = [[self newCellForIdentifier:specifier.type] autorelease];
cell = [self newCellForIdentifier:specifier.type];
}
if ([specifier.type isEqualToString:kIASKPSToggleSwitchSpecifier]) {
@ -614,7 +587,7 @@ CGRect IASKCGRectSwap(CGRect rect);
//create a set of specifier types that can't be selected
static NSSet* noSelectionTypes = nil;
if(nil == noSelectionTypes) {
noSelectionTypes = [[NSSet setWithObjects:kIASKPSToggleSwitchSpecifier, kIASKPSSliderSpecifier, nil] retain];
noSelectionTypes = [NSSet setWithObjects:kIASKPSToggleSwitchSpecifier, kIASKPSSliderSpecifier, nil];
}
IASKSpecifier *specifier = [self.settingsReader specifierForIndexPath:indexPath];
@ -631,115 +604,89 @@ CGRect IASKCGRectSwap(CGRect rect);
//switches and sliders can't be selected (should be captured by tableView:willSelectRowAtIndexPath: delegate method)
assert(![[specifier type] isEqualToString:kIASKPSToggleSwitchSpecifier]);
assert(![[specifier type] isEqualToString:kIASKPSSliderSpecifier]);
if ([[specifier type] isEqualToString:kIASKPSMultiValueSpecifier]) {
IASKSpecifierValuesViewController *targetViewController = [[self.viewList objectAtIndex:kIASKSpecifierValuesViewControllerIndex] objectForKey:@"viewController"];
if (targetViewController == nil) {
// the view controller has not been created yet, create it and set it to our viewList array
// create a new dictionary with the new view controller
NSMutableDictionary *newItemDict = [NSMutableDictionary dictionaryWithCapacity:3];
[newItemDict addEntriesFromDictionary: [self.viewList objectAtIndex:kIASKSpecifierValuesViewControllerIndex]]; // copy the title and explain strings
targetViewController = [[IASKSpecifierValuesViewController alloc] init];
// add the new view controller to the dictionary and then to the 'viewList' array
[newItemDict setObject:targetViewController forKey:@"viewController"];
[self.viewList replaceObjectAtIndex:kIASKSpecifierValuesViewControllerIndex withObject:newItemDict];
[targetViewController release];
// load the view controll back in to push it
targetViewController = [[self.viewList objectAtIndex:kIASKSpecifierValuesViewControllerIndex] objectForKey:@"viewController"];
}
IASKSpecifierValuesViewController *targetViewController = [[IASKSpecifierValuesViewController alloc] init];
[targetViewController setCurrentSpecifier:specifier];
targetViewController.settingsReader = self.settingsReader;
targetViewController.settingsStore = self.settingsStore;
_currentChildViewController = targetViewController;
[[self navigationController] pushViewController:targetViewController animated:YES];
}
else if ([[specifier type] isEqualToString:kIASKPSTextFieldSpecifier]) {
IASKPSTextFieldSpecifierViewCell *textFieldCell = (id)[tableView cellForRowAtIndexPath:indexPath];
[textFieldCell.textField becomeFirstResponder];
}
else if ([[specifier type] isEqualToString:kIASKPSChildPaneSpecifier]) {
} else if ([[specifier type] isEqualToString:kIASKPSTextFieldSpecifier]) {
IASKPSTextFieldSpecifierViewCell *textFieldCell = (id)[tableView cellForRowAtIndexPath:indexPath];
[textFieldCell.textField becomeFirstResponder];
} else if ([[specifier type] isEqualToString:kIASKPSChildPaneSpecifier]) {
Class vcClass = [specifier viewControllerClass];
if (vcClass) {
SEL initSelector = [specifier viewControllerSelector];
if (!initSelector) {
initSelector = @selector(init);
}
UIViewController * vc = [vcClass performSelector:@selector(alloc)];
[vc performSelector:initSelector withObject:[specifier file] withObject:[specifier key]];
if ([vc respondsToSelector:@selector(setDelegate:)]) {
[vc performSelector:@selector(setDelegate:) withObject:self.delegate];
}
if ([vc respondsToSelector:@selector(setSettingsStore:)]) {
[vc performSelector:@selector(setSettingsStore:) withObject:self.settingsStore];
}
UIViewController * vc = [vcClass alloc];
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
vc = [vc performSelector:initSelector withObject:[specifier file] withObject:[specifier key]];
#pragma clang diagnostic pop
if ([vc respondsToSelector:@selector(setDelegate:)]) {
[vc performSelector:@selector(setDelegate:) withObject:self.delegate];
}
if ([vc respondsToSelector:@selector(setSettingsStore:)]) {
[vc performSelector:@selector(setSettingsStore:) withObject:self.settingsStore];
}
[self.navigationController pushViewController:vc animated:YES];
[vc performSelector:@selector(release)];
return;
}
if (nil == [specifier file]) {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
return;
}
IASKAppSettingsViewController *targetViewController = [[self.viewList objectAtIndex:kIASKSpecifierChildViewControllerIndex] objectForKey:@"viewController"];
if (targetViewController == nil) {
// the view controller has not been created yet, create it and set it to our viewList array
// create a new dictionary with the new view controller
NSMutableDictionary *newItemDict = [NSMutableDictionary dictionaryWithCapacity:3];
[newItemDict addEntriesFromDictionary: [self.viewList objectAtIndex:kIASKSpecifierChildViewControllerIndex]]; // copy the title and explain strings
targetViewController = [[[self class] alloc] init];
targetViewController.showDoneButton = NO;
targetViewController.settingsStore = self.settingsStore;
targetViewController.delegate = self.delegate;
// add the new view controller to the dictionary and then to the 'viewList' array
[newItemDict setObject:targetViewController forKey:@"viewController"];
[self.viewList replaceObjectAtIndex:kIASKSpecifierChildViewControllerIndex withObject:newItemDict];
[targetViewController release];
// load the view controll back in to push it
targetViewController = [[self.viewList objectAtIndex:kIASKSpecifierChildViewControllerIndex] objectForKey:@"viewController"];
}
targetViewController.file = specifier.file;
targetViewController.hiddenKeys = self.hiddenKeys;
targetViewController.title = specifier.title;
IASKAppSettingsViewController *targetViewController = [[[self class] alloc] init];
targetViewController.showDoneButton = NO;
targetViewController.settingsStore = self.settingsStore;
targetViewController.delegate = self.delegate;
targetViewController.file = specifier.file;
targetViewController.hiddenKeys = self.hiddenKeys;
targetViewController.title = specifier.title;
targetViewController.showCreditsFooter = NO;
_currentChildViewController = targetViewController;
[[self navigationController] pushViewController:targetViewController animated:YES];
} else if ([[specifier type] isEqualToString:kIASKOpenURLSpecifier]) {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:specifier.file]];
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:specifier.file]];
} else if ([[specifier type] isEqualToString:kIASKButtonSpecifier]) {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if ([self.delegate respondsToSelector:@selector(settingsViewController:buttonTappedForSpecifier:)]) {
[self.delegate settingsViewController:self buttonTappedForSpecifier:specifier];
} else if ([self.delegate respondsToSelector:@selector(settingsViewController:buttonTappedForKey:)]) {
// deprecated, provided for backward compatibility
NSLog(@"InAppSettingsKit Warning: -settingsViewController:buttonTappedForKey: is deprecated. Please use -settingsViewController:buttonTappedForSpecifier:");
[self.delegate settingsViewController:self buttonTappedForKey:[specifier key]];
} else {
// legacy code, provided for backward compatibility
// the delegate mechanism above is much cleaner and doesn't leak
Class buttonClass = [specifier buttonClass];
SEL buttonAction = [specifier buttonAction];
if ([buttonClass respondsToSelector:buttonAction]) {
[buttonClass performSelector:buttonAction withObject:self withObject:[specifier key]];
NSLog(@"InAppSettingsKit Warning: Using IASKButtonSpecifier without implementing the delegate method is deprecated");
}
}
if ([self.delegate respondsToSelector:@selector(settingsViewController:buttonTappedForSpecifier:)]) {
[self.delegate settingsViewController:self buttonTappedForSpecifier:specifier];
} else if ([self.delegate respondsToSelector:@selector(settingsViewController:buttonTappedForKey:)]) {
// deprecated, provided for backward compatibility
NSLog(@"InAppSettingsKit Warning: -settingsViewController:buttonTappedForKey: is deprecated. Please use -settingsViewController:buttonTappedForSpecifier:");
[self.delegate settingsViewController:self buttonTappedForKey:[specifier key]];
} else {
// legacy code, provided for backward compatibility
// the delegate mechanism above is much cleaner and doesn't leak
Class buttonClass = [specifier buttonClass];
SEL buttonAction = [specifier buttonAction];
if ([buttonClass respondsToSelector:buttonAction]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[buttonClass performSelector:buttonAction withObject:self withObject:[specifier key]];
#pragma clang diagnostic pop
NSLog(@"InAppSettingsKit Warning: Using IASKButtonSpecifier without implementing the delegate method is deprecated");
}
}
} else if ([[specifier type] isEqualToString:kIASKMailComposeSpecifier]) {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if ([MFMailComposeViewController canSendMail]) {
MFMailComposeViewController *mailViewController = [[MFMailComposeViewController alloc] init];
mailViewController.navigationBar.barStyle = self.navigationController.navigationBar.barStyle;
mailViewController.navigationBar.tintColor = self.navigationController.navigationBar.tintColor;
mailViewController.navigationBar.tintColor = self.navigationController.navigationBar.tintColor;
mailViewController.navigationBar.titleTextAttributes =
self.navigationController.navigationBar.titleTextAttributes;
if ([specifier localizedObjectForKey:kIASKMailComposeSubject]) {
[mailViewController setSubject:[specifier localizedObjectForKey:kIASKMailComposeSubject]];
}
@ -758,7 +705,7 @@ CGRect IASKCGRectSwap(CGRect rect);
isHTML = [[[specifier specifierDict] objectForKey:kIASKMailComposeBodyIsHTML] boolValue];
}
if ([self.delegate respondsToSelector:@selector(settingsViewController:mailComposeBodyForSpecifier:)]) {
if ([self.delegate respondsToSelector:@selector(settingsViewController:mailComposeBodyForSpecifier:)]) {
[mailViewController setMessageBody:[self.delegate settingsViewController:self
mailComposeBodyForSpecifier:specifier] isHTML:isHTML];
}
@ -766,11 +713,11 @@ CGRect IASKCGRectSwap(CGRect rect);
[mailViewController setMessageBody:[specifier localizedObjectForKey:kIASKMailComposeBody] isHTML:isHTML];
}
}
UIViewController<MFMailComposeViewControllerDelegate> *vc = nil;
if ([self.delegate respondsToSelector:@selector(settingsViewController:viewControllerForMailComposeViewForSpecifier:)]) {
vc = [self.delegate settingsViewController:self viewControllerForMailComposeViewForSpecifier:specifier];
if ([self.delegate respondsToSelector:@selector(settingsViewController:viewControllerForMailComposeViewForSpecifier:)]) {
vc = [self.delegate settingsViewController:self viewControllerForMailComposeViewForSpecifier:specifier];
}
if (vc == nil) {
@ -778,20 +725,10 @@ CGRect IASKCGRectSwap(CGRect rect);
}
mailViewController.mailComposeDelegate = vc;
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)
//#pragma message "Now that we're iOS5 and up, remove this workaround"
#endif
if([vc respondsToSelector:@selector(presentViewController:animated:completion:)]) {
[vc presentViewController:mailViewController
animated:YES
completion:nil];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[vc presentModalViewController:mailViewController animated:YES];
#pragma clang diagnostic pop
}
[mailViewController release];
_currentChildViewController = mailViewController;
[vc presentViewController:mailViewController
animated:YES
completion:nil];
} else {
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:NSLocalizedString(@"Mail not configured", @"InAppSettingsKit")
@ -800,10 +737,9 @@ CGRect IASKCGRectSwap(CGRect rect);
cancelButtonTitle:NSLocalizedString(@"OK", @"InAppSettingsKit")
otherButtonTitles:nil];
[alert show];
[alert release];
}
} else if ([[specifier type] isEqualToString:kIASKCustomViewSpecifier] && [self.delegate respondsToSelector:@selector(settingsViewController:tableView:didSelectCustomViewSpecifier:)]) {
} else if ([[specifier type] isEqualToString:kIASKCustomViewSpecifier] && [self.delegate respondsToSelector:@selector(settingsViewController:tableView:didSelectCustomViewSpecifier:)]) {
[self.delegate settingsViewController:self tableView:tableView didSelectCustomViewSpecifier:specifier];
} else {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
@ -822,21 +758,10 @@ CGRect IASKCGRectSwap(CGRect rect);
mailComposeController:controller
didFinishWithResult:result
error:error];
}
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)
//#pragma message "Now that we're iOS5 and up, remove this workaround"
#endif
if([self respondsToSelector:@selector(dismissViewControllerAnimated:completion:)]) {
[self dismissViewControllerAnimated:YES
completion:nil];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[self dismissModalViewControllerAnimated:YES];
#pragma clang diagnostic pop
}
[self dismissViewControllerAnimated:YES
completion:nil];
}
#pragma mark -
@ -848,7 +773,7 @@ CGRect IASKCGRectSwap(CGRect rect);
}
- (void)_textChanged:(id)sender {
IASKTextField *text = [[(IASKTextField*)sender retain] autorelease];
IASKTextField *text = sender;
[_settingsStore setObject:[text text] forKey:[text key]];
[[NSNotificationCenter defaultCenter] postNotificationName:kIASKAppSettingChanged
object:[text key]
@ -884,8 +809,7 @@ static NSDictionary *oldUserDefaults = nil;
}
}
}
[oldUserDefaults release], oldUserDefaults = [currentDict retain];
oldUserDefaults = currentDict;
for (UITableViewCell *cell in self.tableView.visibleCells) {
if ([cell isKindOfClass:[IASKPSTextFieldSpecifierViewCell class]] && [((IASKPSTextFieldSpecifierViewCell*)cell).textField isFirstResponder]) {

View file

@ -46,13 +46,6 @@
self.view = webView;
}
- (void)dealloc {
[webView release], webView = nil;
[url release], url = nil;
[super dealloc];
}
- (void)viewWillAppear:(BOOL)animated {
[webView loadRequest:[NSURLRequest requestWithURL:self.url]];
}
@ -102,11 +95,10 @@
NSString *key = [[keyValue objectAtIndex:0] lowercaseString];
NSString *value = [keyValue objectAtIndex:1];
value = (NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault,
value = CFBridgingRelease(CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault,
(CFStringRef)value,
CFSTR(""),
kCFStringEncodingUTF8);
[value autorelease];
kCFStringEncodingUTF8));
if ([key isEqualToString:@"subject"]) {
[mailViewController setSubject:value];
@ -133,21 +125,10 @@
}
[mailViewController setToRecipients:toRecipients];
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)
//#pragma message "Now that we're iOS5 and up, remove this workaround"
#endif
if([self respondsToSelector:@selector(presentViewController:animated:completion:)]) {
[self presentViewController:mailViewController
animated:YES
completion:nil];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[self presentModalViewController:mailViewController animated:YES];
#pragma clang diagnostic pop
}
[mailViewController release];
[self presentViewController:mailViewController
animated:YES
completion:nil];
return NO;
}
@ -160,21 +141,8 @@
}
- (void)mailComposeController:(MFMailComposeViewController*)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError*)error {
#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 50000)
//#pragma message "Now that we're iOS5 and up, remove this workaround"
#endif
if([self respondsToSelector:@selector(dismissViewControllerAnimated:completion:)]) {
[self dismissViewControllerAnimated:YES
completion:nil];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
[self dismissModalViewControllerAnimated:YES];
#pragma clang diagnostic pop
}
[self dismissViewControllerAnimated:YES
completion:nil];
}
@end

View file

@ -110,17 +110,6 @@
self.tableView = nil;
}
- (void)dealloc {
[_currentSpecifier release], _currentSpecifier = nil;
[_checkedItem release], _checkedItem = nil;
[_settingsReader release], _settingsReader = nil;
[_settingsStore release], _settingsStore = nil;
[_tableView release], _tableView = nil;
[super dealloc];
}
#pragma mark -
#pragma mark UITableView delegates
@ -151,7 +140,7 @@
NSArray *titles = [_currentSpecifier multipleTitles];
if (!cell) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellValue] autorelease];
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kCellValue];
}
if ([indexPath isEqual:[self checkedItem]]) {
@ -197,7 +186,7 @@
#pragma mark Notifications
- (void)userDefaultsDidChange {
NSIndexPath *oldCheckedItem = [[self.checkedItem retain] autorelease];
NSIndexPath *oldCheckedItem = self.checkedItem;
if(_currentSpecifier) {
[self updateCheckedItem];
}

View file

@ -94,13 +94,17 @@
#define kIASKMinLabelWidth 97
#define kIASKMaxLabelWidth 240
#define kIASKMinValueWidth 35
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
#define kIASKPaddingLeft (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1 ? 14 : 9)
#else
#define kIASKPaddingLeft 9
#endif
#define kIASKPaddingRight 10
#define kIASKHorizontalPaddingGroupTitles 19
#define kIASKVerticalPaddingGroupTitles 15
#define kIASKLabelFontSize 17
#define kIASKgrayBlueColor [UIColor colorWithRed:0.318 green:0.4 blue:0.569 alpha:1.0]
#define kIASKgrayBlueColor [UIColor colorWithRed:0.318f green:0.4f blue:0.569f alpha:1.f]
#define kIASKMinimumFontSize 12.0f

View file

@ -28,14 +28,14 @@
applicationBundle:(NSBundle*) bundle {
self = [super init];
if (self) {
_applicationBundle = [bundle retain];
_applicationBundle = bundle;
NSString* plistFilePath = [self locateSettingsFile: fileName];
_settingsDictionary = [[NSDictionary dictionaryWithContentsOfFile:plistFilePath] retain];
_settingsDictionary = [NSDictionary dictionaryWithContentsOfFile:plistFilePath];
//store the bundle which we'll need later for getting localizations
NSString* settingsBundlePath = [plistFilePath stringByDeletingLastPathComponent];
_settingsBundle = [[NSBundle bundleWithPath:settingsBundlePath] retain];
_settingsBundle = [NSBundle bundleWithPath:settingsBundlePath];
// Look for localization file
self.localizationTable = [_settingsDictionary objectForKey:@"StringsTable"];
@ -67,22 +67,9 @@
return [self initWithFile:@"Root"];
}
- (void)dealloc {
[_localizationTable release], _localizationTable = nil;
[_settingsDictionary release], _settingsDictionary = nil;
[_dataSource release], _dataSource = nil;
[_settingsBundle release], _settingsBundle = nil;
[_hiddenKeys release], _hiddenKeys = nil;
[super dealloc];
}
- (void)setHiddenKeys:(NSSet *)anHiddenKeys {
if (_hiddenKeys != anHiddenKeys) {
id old = _hiddenKeys;
_hiddenKeys = [anHiddenKeys retain];
[old release];
_hiddenKeys = anHiddenKeys;
if (self.settingsDictionary) {
[self _reinterpretBundle:self.settingsDictionary];
@ -94,7 +81,7 @@
- (void)_reinterpretBundle:(NSDictionary*)settingsBundle {
NSArray *preferenceSpecifiers = [settingsBundle objectForKey:kIASKPreferenceSpecifiers];
NSInteger sectionCount = -1;
NSMutableArray *dataSource = [[[NSMutableArray alloc] init] autorelease];
NSMutableArray *dataSource = [NSMutableArray new];
for (NSDictionary *specifier in preferenceSpecifiers) {
if ([self.hiddenKeys containsObject:[specifier objectForKey:kIASKKey]]) {
@ -105,20 +92,17 @@
[newArray addObject:specifier];
[dataSource addObject:newArray];
[newArray release];
sectionCount++;
}
else {
if (sectionCount == -1) {
NSMutableArray *newArray = [[NSMutableArray alloc] init];
[dataSource addObject:newArray];
[newArray release];
sectionCount++;
}
IASKSpecifier *newSpecifier = [[IASKSpecifier alloc] initWithSpecifier:specifier];
[(NSMutableArray*)[dataSource objectAtIndex:sectionCount] addObject:newSpecifier];
[newSpecifier release];
}
}
[self setDataSource:dataSource];

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