Update to unsplash (#938)

* Switch photos from 500px to Unsplash

* Good bye comment view :(

* Update license headers
This commit is contained in:
Garrett Moon 2018-05-23 20:47:42 -07:00 committed by GitHub
parent 8b890f07be
commit 9ccba7fe74
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 187 additions and 1238 deletions

View File

@ -14,12 +14,7 @@
767A5F111CAA3BFE004CDA8D /* tabBarIcons in Resources */ = {isa = PBXBuildFile; fileRef = 767A5F101CAA3BFE004CDA8D /* tabBarIcons */; };
767A5F131CAA3C66004CDA8D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 767A5F121CAA3C66004CDA8D /* Assets.xcassets */; };
768843801CAA37EF00D8629E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 768843681CAA37EF00D8629E /* AppDelegate.m */; };
768843811CAA37EF00D8629E /* CommentFeedModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 768843691CAA37EF00D8629E /* CommentFeedModel.m */; };
768843821CAA37EF00D8629E /* CommentModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7688436A1CAA37EF00D8629E /* CommentModel.m */; };
768843831CAA37EF00D8629E /* CommentsNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 7688436B1CAA37EF00D8629E /* CommentsNode.m */; };
768843841CAA37EF00D8629E /* CommentView.m in Sources */ = {isa = PBXBuildFile; fileRef = 7688436C1CAA37EF00D8629E /* CommentView.m */; };
768843851CAA37EF00D8629E /* ImageURLModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 7688436D1CAA37EF00D8629E /* ImageURLModel.m */; };
768843881CAA37EF00D8629E /* LocationModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 768843701CAA37EF00D8629E /* LocationModel.m */; };
768843891CAA37EF00D8629E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 768843711CAA37EF00D8629E /* main.m */; };
7688438B1CAA37EF00D8629E /* PhotoCellNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 768843731CAA37EF00D8629E /* PhotoCellNode.m */; };
7688438C1CAA37EF00D8629E /* PhotoCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 768843741CAA37EF00D8629E /* PhotoCollectionViewCell.m */; };
@ -52,12 +47,7 @@
767A5F101CAA3BFE004CDA8D /* tabBarIcons */ = {isa = PBXFileReference; lastKnownFileType = folder; path = tabBarIcons; sourceTree = "<group>"; };
767A5F121CAA3C66004CDA8D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
768843511CAA37EF00D8629E /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = AppDelegate.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
768843521CAA37EF00D8629E /* CommentFeedModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = CommentFeedModel.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
768843531CAA37EF00D8629E /* CommentModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = CommentModel.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
768843541CAA37EF00D8629E /* CommentsNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = CommentsNode.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
768843551CAA37EF00D8629E /* CommentView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = CommentView.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
768843561CAA37EF00D8629E /* ImageURLModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = ImageURLModel.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
768843591CAA37EF00D8629E /* LocationModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = LocationModel.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
7688435B1CAA37EF00D8629E /* PhotoCellNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = PhotoCellNode.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
7688435C1CAA37EF00D8629E /* PhotoCollectionViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = PhotoCollectionViewCell.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
7688435D1CAA37EF00D8629E /* PhotoFeedModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = PhotoFeedModel.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
@ -69,12 +59,7 @@
768843661CAA37EF00D8629E /* Utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Utilities.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
768843671CAA37EF00D8629E /* Sample.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Sample.pch; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
768843681CAA37EF00D8629E /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = AppDelegate.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
768843691CAA37EF00D8629E /* CommentFeedModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = CommentFeedModel.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
7688436A1CAA37EF00D8629E /* CommentModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = CommentModel.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
7688436B1CAA37EF00D8629E /* CommentsNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = CommentsNode.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
7688436C1CAA37EF00D8629E /* CommentView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = CommentView.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
7688436D1CAA37EF00D8629E /* ImageURLModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = ImageURLModel.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
768843701CAA37EF00D8629E /* LocationModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = LocationModel.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
768843711CAA37EF00D8629E /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = main.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
768843731CAA37EF00D8629E /* PhotoCellNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PhotoCellNode.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
768843741CAA37EF00D8629E /* PhotoCollectionViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PhotoCollectionViewCell.m; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objc; };
@ -227,14 +212,8 @@
768843781CAA37EF00D8629E /* PhotoModel.m */,
768843561CAA37EF00D8629E /* ImageURLModel.h */,
7688436D1CAA37EF00D8629E /* ImageURLModel.m */,
768843521CAA37EF00D8629E /* CommentFeedModel.h */,
768843691CAA37EF00D8629E /* CommentFeedModel.m */,
768843531CAA37EF00D8629E /* CommentModel.h */,
7688436A1CAA37EF00D8629E /* CommentModel.m */,
768843631CAA37EF00D8629E /* UserModel.h */,
7688437B1CAA37EF00D8629E /* UserModel.m */,
768843591CAA37EF00D8629E /* LocationModel.h */,
768843701CAA37EF00D8629E /* LocationModel.m */,
768843661CAA37EF00D8629E /* Utilities.h */,
7688437E1CAA37EF00D8629E /* Utilities.m */,
);
@ -257,8 +236,6 @@
768843791CAA37EF00D8629E /* PhotoTableViewCell.m */,
7688435C1CAA37EF00D8629E /* PhotoCollectionViewCell.h */,
768843741CAA37EF00D8629E /* PhotoCollectionViewCell.m */,
768843551CAA37EF00D8629E /* CommentView.h */,
7688436C1CAA37EF00D8629E /* CommentView.m */,
);
name = UIKit;
sourceTree = "<group>";
@ -272,8 +249,6 @@
CC6350BA1E1C482D002BC613 /* TailLoadingNode.m */,
7688435B1CAA37EF00D8629E /* PhotoCellNode.h */,
768843731CAA37EF00D8629E /* PhotoCellNode.m */,
768843541CAA37EF00D8629E /* CommentsNode.h */,
7688436B1CAA37EF00D8629E /* CommentsNode.m */,
);
name = ASDK;
sourceTree = "<group>";
@ -393,7 +368,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
showEnvVarsInLog = 0;
};
F012A6F39E0149F18F564F50 /* [CP] Copy Pods Resources */ = {
@ -422,20 +397,15 @@
7688438C1CAA37EF00D8629E /* PhotoCollectionViewCell.m in Sources */,
768843921CAA37EF00D8629E /* PhotoFeedViewController.m in Sources */,
76229A781CBB79E000B62CEF /* WindowWithStatusBarUnderlay.m in Sources */,
768843821CAA37EF00D8629E /* CommentModel.m in Sources */,
768843831CAA37EF00D8629E /* CommentsNode.m in Sources */,
768843961CAA37EF00D8629E /* Utilities.m in Sources */,
E5F128F01E09625400B4335F /* PhotoFeedBaseController.m in Sources */,
768843931CAA37EF00D8629E /* UserModel.m in Sources */,
CC5532171E15CC1E0011C01F /* ASCollectionSectionController.m in Sources */,
768843801CAA37EF00D8629E /* AppDelegate.m in Sources */,
768843811CAA37EF00D8629E /* CommentFeedModel.m in Sources */,
CCEDDDD7200C4C0E00FFCD0A /* TextureConfigDelegate.m in Sources */,
7688438E1CAA37EF00D8629E /* PhotoFeedNodeController.m in Sources */,
CC6350BB1E1C482D002BC613 /* TailLoadingNode.m in Sources */,
CC85250F1E36B392008EABE6 /* FeedHeaderNode.m in Sources */,
768843841CAA37EF00D8629E /* CommentView.m in Sources */,
768843881CAA37EF00D8629E /* LocationModel.m in Sources */,
768843901CAA37EF00D8629E /* PhotoModel.m in Sources */,
768843911CAA37EF00D8629E /* PhotoTableViewCell.m in Sources */,
CC00D1571E15912F004E5502 /* PhotoFeedListKitViewController.m in Sources */,

View File

@ -1,37 +0,0 @@
//
// CommentFeedModel.h
// Sample
//
// Created by Hannah Troisi on 3/9/16.
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#import "CommentModel.h"
@interface CommentFeedModel : NSObject
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithPhotoID:(NSString *)photoID NS_DESIGNATED_INITIALIZER;
- (NSUInteger)numberOfItemsInFeed;
- (CommentModel *)objectAtIndex:(NSUInteger)index;
- (NSUInteger)numberOfCommentsForPhoto;
- (BOOL)numberOfCommentsForPhotoExceedsInteger:(NSUInteger)number;
- (NSAttributedString *)viewAllCommentsAttributedString;
- (void)requestPageWithCompletionBlock:(void (^)(NSArray *))block;
- (void)refreshFeedWithCompletionBlock:(void (^)(NSArray *))block;
@end

View File

@ -1,206 +0,0 @@
//
// CommentFeedModel.m
// Sample
//
// Created by Hannah Troisi on 3/9/16.
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#import "CommentFeedModel.h"
#import "Utilities.h"
#define NUM_COMMENTS_TO_SHOW 3
#define fiveHundredPX_ENDPOINT_HOST @"https://api.500px.com/v1/"
#define fiveHundredPX_ENDPOINT_COMMENTS @"photos/4928401/comments"
#define fiveHundredPX_ENDPOINT_SEARCH @"photos/search?geo=" //latitude,longitude,radius<units>
#define fiveHundredPX_ENDPOINT_USER @"photos?user_id="
#define fiveHundredPX_CONSUMER_KEY_PARAM @"&consumer_key=Fi13GVb8g53sGvHICzlram7QkKOlSDmAmp9s9aqC"
@implementation CommentFeedModel
{
NSMutableArray *_comments; // array of CommentModel objects
NSString *_photoID;
NSString *_urlString;
NSUInteger _currentPage;
NSUInteger _totalPages;
NSUInteger _totalItems;
BOOL _fetchPageInProgress;
BOOL _refreshFeedInProgress;
}
#pragma mark - Properties
- (NSMutableArray *)comments
{
return _comments;
}
#pragma mark - Lifecycle
- (instancetype)initWithPhotoID:(NSString *)photoID
{
self = [super init];
if (self) {
_photoID = photoID;
_currentPage = 0;
_totalPages = 0;
_totalItems = 0;
_comments = [[NSMutableArray alloc] init];
_urlString = [NSString stringWithFormat:@"https://api.500px.com/v1/photos/%@/comments?",photoID];
}
return self;
}
#pragma mark - Instance Methods
- (NSUInteger)numberOfItemsInFeed
{
return [_comments count];
}
- (CommentModel *)objectAtIndex:(NSUInteger)index
{
return [_comments objectAtIndex:index];
}
- (NSUInteger)numberOfCommentsForPhoto
{
return _totalItems;
}
- (BOOL)numberOfCommentsForPhotoExceedsInteger:(NSUInteger)number
{
return (_totalItems > number);
}
- (NSAttributedString *)viewAllCommentsAttributedString
{
NSString *string = [NSString stringWithFormat:@"View all %@ comments", [NSNumber numberWithUnsignedInteger:_totalItems]];
NSAttributedString *attrString = [NSAttributedString attributedStringWithString:string fontSize:14 color:[UIColor lightGrayColor] firstWordColor:nil];
return attrString;
}
- (void)requestPageWithCompletionBlock:(void (^)(NSArray *))block
{
// only one fetch at a time
if (_fetchPageInProgress) {
return;
} else {
_fetchPageInProgress = YES;
[self fetchPageWithCompletionBlock:block];
}
}
- (void)refreshFeedWithCompletionBlock:(void (^)(NSArray *))block
{
// only one fetch at a time
if (_refreshFeedInProgress) {
return;
} else {
_refreshFeedInProgress = YES;
_currentPage = 0;
// FIXME: blow away any other requests in progress
[self fetchPageWithCompletionBlock:^(NSArray *newPhotos) {
if (block) {
block(newPhotos);
}
_refreshFeedInProgress = NO;
} replaceData:YES];
}
}
#pragma mark - Helper Methods
- (void)fetchPageWithCompletionBlock:(void (^)(NSArray *))block
{
[self fetchPageWithCompletionBlock:block replaceData:NO];
}
- (void)fetchPageWithCompletionBlock:(void (^)(NSArray *))block replaceData:(BOOL)replaceData
{
// early return if reached end of pages
if (_totalPages) {
if (_currentPage == _totalPages) {
return;
}
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableArray *newComments = [NSMutableArray array];
NSUInteger nextPage = _currentPage + 1;
NSString *urlAdditions = [NSString stringWithFormat:@"page=%lu", (unsigned long)nextPage];
NSURL *url = [NSURL URLWithString:[_urlString stringByAppendingString:urlAdditions]];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
NSURLSessionDataTask *task = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data) {
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
if ([response isKindOfClass:[NSDictionary class]]) {
_currentPage = [[response valueForKeyPath:@"current_page"] integerValue];
_totalPages = [[response valueForKeyPath:@"total_pages"] integerValue];
_totalItems = [[response valueForKeyPath:@"total_items"] integerValue];
NSArray *comments = [response valueForKeyPath:@"comments"];
if ([comments isKindOfClass:[NSArray class]]) {
NSUInteger numComments = [comments count];
if (numComments > NUM_COMMENTS_TO_SHOW) {
comments = [comments subarrayWithRange:(NSRange){numComments-NUM_COMMENTS_TO_SHOW, NUM_COMMENTS_TO_SHOW}];
}
for (NSDictionary *commentDictionary in comments) {
if ([response isKindOfClass:[NSDictionary class]]) {
CommentModel *comment = [[CommentModel alloc] initWithDictionary:commentDictionary];
if (comment) {
[newComments addObject:comment];
}
}
}
}
}
}
dispatch_async(dispatch_get_main_queue(), ^{
_fetchPageInProgress = NO;
if (replaceData) {
_comments = [newComments mutableCopy];
} else {
[_comments addObjectsFromArray:newComments];
}
if (block) {
block(newComments);
}
});
}];
[task resume];
});
}
@end

View File

@ -1,35 +0,0 @@
//
// CommentModel.h
// Sample
//
// Created by Hannah Troisi on 3/9/16.
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
@interface CommentModel : NSObject
@property (nonatomic, assign, readonly) NSUInteger ID;
@property (nonatomic, assign, readonly) NSUInteger commenterID;
@property (nonatomic, strong, readonly) NSString *commenterUsername;
@property (nonatomic, strong, readonly) NSString *commenterAvatarURL;
@property (nonatomic, strong, readonly) NSString *body;
@property (nonatomic, strong, readonly) NSString *uploadDateString;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithDictionary:(NSDictionary *)photoDictionary NS_DESIGNATED_INITIALIZER;
- (NSAttributedString *)commentAttributedString;
- (NSAttributedString *)uploadDateAttributedStringWithFontSize:(CGFloat)size;
@end

View File

@ -1,62 +0,0 @@
//
// CommentModel.m
// Sample
//
// Created by Hannah Troisi on 3/9/16.
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#import "CommentModel.h"
#import "Utilities.h"
@implementation CommentModel
{
NSDictionary *_dictionaryRepresentation;
NSString *_uploadDateRaw;
}
#pragma mark - Lifecycle
- (instancetype)initWithDictionary:(NSDictionary *)photoDictionary
{
self = [super init];
if (self) {
_dictionaryRepresentation = photoDictionary;
_ID = [[photoDictionary objectForKey:@"id"] integerValue];
_commenterID = [[photoDictionary objectForKey:@"user_id"] integerValue];
_commenterUsername = [photoDictionary valueForKeyPath:@"user.username"];
_commenterAvatarURL = [photoDictionary valueForKeyPath:@"user.userpic_url"];
_body = [photoDictionary objectForKey:@"body"];
_uploadDateRaw = [photoDictionary valueForKeyPath:@"created_at"];
_uploadDateString = [NSString elapsedTimeStringSinceDate:_uploadDateRaw];
}
return self;
}
#pragma mark - Instance Methods
- (NSAttributedString *)commentAttributedString
{
NSString *commentString = [NSString stringWithFormat:@"%@ %@",[_commenterUsername lowercaseString], _body];
return [NSAttributedString attributedStringWithString:commentString fontSize:14 color:[UIColor darkGrayColor] firstWordColor:[UIColor darkBlueColor]];
}
- (NSAttributedString *)uploadDateAttributedStringWithFontSize:(CGFloat)size;
{
return [NSAttributedString attributedStringWithString:self.uploadDateString fontSize:size color:[UIColor lightGrayColor] firstWordColor:nil];
}
@end

View File

@ -1,28 +0,0 @@
//
// CommentView.h
// Sample
//
// Created by Hannah Troisi on 3/9/16.
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#import "CommentFeedModel.h"
@interface CommentView : UIView
+ (CGFloat)heightForCommentFeedModel:(CommentFeedModel *)feed withWidth:(CGFloat)width;
- (void)updateWithCommentFeedModel:(CommentFeedModel *)feed;
@end

View File

@ -1,149 +0,0 @@
//
// CommentView.m
// Sample
//
// Created by Hannah Troisi on 3/9/16.
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#import "CommentView.h"
#import "PhotoFeedModel.h"
#import "Utilities.h"
#define INTER_COMMENT_SPACING 5
#define NUM_COMMENTS_TO_SHOW 3
@implementation CommentView
{
CommentFeedModel *_commentFeed;
NSMutableArray <UILabel *> *_commentLabels;
}
#pragma mark - Class Methods
+ (CGFloat)heightForCommentFeedModel:(CommentFeedModel *)feed withWidth:(CGFloat)width
{
NSAttributedString *string;
CGRect rect;
CGFloat height = 0;
BOOL addViewAllCommentsLabel = [feed numberOfCommentsForPhotoExceedsInteger:NUM_COMMENTS_TO_SHOW];
if (addViewAllCommentsLabel) {
string = [feed viewAllCommentsAttributedString];
rect = [string boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
height += rect.size.height;
}
NSUInteger numCommentsInFeed = [feed numberOfItemsInFeed];
for (int i = 0; i < numCommentsInFeed; i++) {
string = [[feed objectAtIndex:i] commentAttributedString];
rect = [string boundingRectWithSize:CGSizeMake(width, CGFLOAT_MAX)
options:NSStringDrawingUsesLineFragmentOrigin
context:nil];
height += rect.size.height + INTER_COMMENT_SPACING;
}
return roundf(height);
}
#pragma mark - Lifecycle
- (instancetype)init
{
self = [super init];
if (self) {
_commentLabels = [[NSMutableArray alloc] init];
}
return self;
}
- (void)layoutSubviews
{
[super layoutSubviews];
CGSize boundsSize = self.bounds.size;
CGRect rect = CGRectMake(0, 0, boundsSize.width, -INTER_COMMENT_SPACING);
for (UILabel *commentsLabel in _commentLabels) {
rect.origin.y += rect.size.height + INTER_COMMENT_SPACING;
rect.size = [commentsLabel sizeThatFits:CGSizeMake(boundsSize.width, CGFLOAT_MAX)];
commentsLabel.frame = rect;
}
}
#pragma mark - Instance Methods
- (void)updateWithCommentFeedModel:(CommentFeedModel *)feed
{
_commentFeed = feed;
[self removeCommentLabels];
if (_commentFeed) {
[self createCommentLabels];
BOOL addViewAllCommentsLabel = [feed numberOfCommentsForPhotoExceedsInteger:NUM_COMMENTS_TO_SHOW];
NSAttributedString *commentLabelString;
int labelsIndex = 0;
if (addViewAllCommentsLabel) {
commentLabelString = [_commentFeed viewAllCommentsAttributedString];
[[_commentLabels objectAtIndex:labelsIndex] setAttributedText:commentLabelString];
labelsIndex++;
}
NSUInteger numCommentsInFeed = [_commentFeed numberOfItemsInFeed];
for (int feedIndex = 0; feedIndex < numCommentsInFeed; feedIndex++) {
commentLabelString = [[_commentFeed objectAtIndex:feedIndex] commentAttributedString];
[[_commentLabels objectAtIndex:labelsIndex] setAttributedText:commentLabelString];
labelsIndex++;
}
[self setNeedsLayout];
}
}
#pragma mark - Helper Methods
- (void)removeCommentLabels
{
for (UILabel *commentLabel in _commentLabels) {
[commentLabel removeFromSuperview];
}
[_commentLabels removeAllObjects];
}
- (void)createCommentLabels
{
BOOL addViewAllCommentsLabel = [_commentFeed numberOfCommentsForPhotoExceedsInteger:NUM_COMMENTS_TO_SHOW];
NSUInteger numCommentsInFeed = [_commentFeed numberOfItemsInFeed];
NSUInteger numLabelsToAdd = (addViewAllCommentsLabel) ? numCommentsInFeed + 1 : numCommentsInFeed;
for (NSUInteger i = 0; i < numLabelsToAdd; i++) {
UILabel *commentLabel = [[UILabel alloc] init];
commentLabel.numberOfLines = 3;
[_commentLabels addObject:commentLabel];
[self addSubview:commentLabel];
}
}
@end

View File

@ -1,27 +0,0 @@
//
// CommentsNode.h
// Sample
//
// Created by Hannah Troisi on 3/21/16.
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import "CommentFeedModel.h"
@interface CommentsNode : ASDisplayNode
- (void)updateWithCommentFeedModel:(CommentFeedModel *)feed;
@end

View File

@ -1,106 +0,0 @@
//
// CommentsNode.m
// Sample
//
// Created by Hannah Troisi on 3/21/16.
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#import "CommentsNode.h"
#define INTER_COMMENT_SPACING 5
#define NUM_COMMENTS_TO_SHOW 3
@implementation CommentsNode
{
CommentFeedModel *_commentFeed;
NSMutableArray <ASTextNode *> *_commentNodes;
}
#pragma mark - Lifecycle
- (instancetype)init
{
self = [super init];
if (self) {
self.automaticallyManagesSubnodes = YES;
_commentNodes = [[NSMutableArray alloc] init];
}
return self;
}
- (ASLayoutSpec *)layoutSpecThatFits:(ASSizeRange)constrainedSize
{
return [ASStackLayoutSpec
stackLayoutSpecWithDirection:ASStackLayoutDirectionVertical
spacing:INTER_COMMENT_SPACING
justifyContent:ASStackLayoutJustifyContentStart
alignItems:ASStackLayoutAlignItemsStretch
children:[_commentNodes copy]];
}
#pragma mark - Instance Methods
- (void)updateWithCommentFeedModel:(CommentFeedModel *)feed
{
_commentFeed = feed;
[_commentNodes removeAllObjects];
if (_commentFeed) {
[self createCommentLabels];
BOOL addViewAllCommentsLabel = [feed numberOfCommentsForPhotoExceedsInteger:NUM_COMMENTS_TO_SHOW];
NSAttributedString *commentLabelString;
int labelsIndex = 0;
if (addViewAllCommentsLabel) {
commentLabelString = [_commentFeed viewAllCommentsAttributedString];
[_commentNodes[labelsIndex] setAttributedText:commentLabelString];
labelsIndex++;
}
NSUInteger numCommentsInFeed = [_commentFeed numberOfItemsInFeed];
for (int feedIndex = 0; feedIndex < numCommentsInFeed; feedIndex++) {
commentLabelString = [[_commentFeed objectAtIndex:feedIndex] commentAttributedString];
[_commentNodes[labelsIndex] setAttributedText:commentLabelString];
labelsIndex++;
}
[self setNeedsLayout];
}
}
#pragma mark - Helper Methods
- (void)createCommentLabels
{
BOOL addViewAllCommentsLabel = [_commentFeed numberOfCommentsForPhotoExceedsInteger:NUM_COMMENTS_TO_SHOW];
NSUInteger numCommentsInFeed = [_commentFeed numberOfItemsInFeed];
NSUInteger numLabelsToAdd = (addViewAllCommentsLabel) ? numCommentsInFeed + 1 : numCommentsInFeed;
for (NSUInteger i = 0; i < numLabelsToAdd; i++) {
ASTextNode *commentLabel = [[ASTextNode alloc] init];
commentLabel.layerBacked = YES;
commentLabel.maximumNumberOfLines = 3;
[_commentNodes addObject:commentLabel];
}
}
@end

View File

@ -1,33 +0,0 @@
//
// LocationModel.h
// Sample
//
// Created by Hannah Troisi on 2/26/16.
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#import "CoreLocation/CoreLocation.h"
@interface LocationModel : NSObject
@property (nonatomic, assign, readonly) CLLocationCoordinate2D coordinates;
@property (nonatomic, strong, readonly) CLPlacemark *placemark;
@property (nonatomic, strong, readonly) NSString *locationString;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWith500pxPhoto:(NSDictionary *)dictionary NS_DESIGNATED_INITIALIZER;
- (void)reverseGeocodedLocationWithCompletionBlock:(void (^)(LocationModel *))blockName;
@end

View File

@ -1,146 +0,0 @@
//
// LocationModel.m
// Sample
//
// Created by Hannah Troisi on 2/26/16.
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
#import "LocationModel.h"
#import <CoreLocation/CLGeocoder.h>
@implementation LocationModel
{
BOOL _placemarkFetchInProgress;
void (^_placemarkCallbackBlock)(LocationModel *);
}
#pragma mark - Lifecycle
- (nullable instancetype)initWith500pxPhoto:(NSDictionary *)dictionary
{
NSNumber *latitude = [dictionary objectForKey:@"latitude"];
NSNumber *longitude = [dictionary objectForKey:@"longitude"];
// early return if location is "<null>"
if (![latitude isKindOfClass:[NSNumber class]] || ![longitude isKindOfClass:[NSNumber class]]) {
return nil;
}
self = [super init];
if (self) {
// set coordiantes
_coordinates = CLLocationCoordinate2DMake([latitude floatValue], [longitude floatValue]);
// get CLPlacemark with MKReverseGeocoder
[self beginReverseGeocodingLocationFromCoordinates];
}
return self;
}
#pragma mark - Instance Methods
// return location placemark if fetched, else set completion block for fetch finish
- (void)reverseGeocodedLocationWithCompletionBlock:(void (^)(LocationModel *))blockName
{
if (_placemark) {
// call block if placemark already fetched
if (blockName) {
blockName(self);
}
} else {
// set placemark reverse geocoding completion block
_placemarkCallbackBlock = blockName;
// if fetch not in progress, begin
if (!_placemarkFetchInProgress) {
[self beginReverseGeocodingLocationFromCoordinates];
}
}
}
#pragma mark - Helper Methods
- (void)beginReverseGeocodingLocationFromCoordinates
{
if (_placemarkFetchInProgress) {
return;
}
_placemarkFetchInProgress = YES;
CLLocation *location = [[CLLocation alloc] initWithLatitude:_coordinates.latitude longitude:_coordinates.longitude];
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder reverseGeocodeLocation:location completionHandler:^(NSArray<CLPlacemark *> * _Nullable placemarks, NSError * _Nullable error) {
// completion handler gets called on main thread
_placemark = [placemarks lastObject];
_locationString = [self locationStringFromCLPlacemark];
// check if completion block set, call it - DO NOT CALL A NIL BLOCK!
if (_placemarkCallbackBlock) {
// call the block with arguments
_placemarkCallbackBlock(self);
}
}];
}
- (nullable NSString *)locationStringFromCLPlacemark
{
// early return if no location info
if (!_placemark)
{
return nil;
}
// @property (nonatomic, readonly, copy, nullable) NSString *name; // eg. Apple Inc.
// @property (nonatomic, readonly, copy, nullable) NSString *thoroughfare; // street name, eg. Infinite Loop
// @property (nonatomic, readonly, copy, nullable) NSString *subThoroughfare; // eg. 1
// @property (nonatomic, readonly, copy, nullable) NSString *locality; // city, eg. Cupertino
// @property (nonatomic, readonly, copy, nullable) NSString *subLocality; // neighborhood, common name, eg. Mission District
// @property (nonatomic, readonly, copy, nullable) NSString *administrativeArea; // state, eg. CA
// @property (nonatomic, readonly, copy, nullable) NSString *subAdministrativeArea; // county, eg. Santa Clara
// @property (nonatomic, readonly, copy, nullable) NSString *postalCode; // zip code, eg. 95014
// @property (nonatomic, readonly, copy, nullable) NSString *ISOcountryCode; // eg. US
// @property (nonatomic, readonly, copy, nullable) NSString *country; // eg. United States
// @property (nonatomic, readonly, copy, nullable) NSString *inlandWater; // eg. Lake Tahoe
// @property (nonatomic, readonly, copy, nullable) NSString *ocean; // eg. Pacific Ocean
// @property (nonatomic, readonly, copy, nullable) NSArray<NSString *> *areasOfInterest; // eg. Golden Gate Park
NSString *locationString;
if (_placemark.inlandWater) {
locationString = _placemark.inlandWater;
} else if (_placemark.subLocality && _placemark.locality) {
locationString = [NSString stringWithFormat:@"%@, %@", _placemark.subLocality, _placemark.locality];
} else if (_placemark.administrativeArea && _placemark.subAdministrativeArea) {
locationString = [NSString stringWithFormat:@"%@, %@", _placemark.subAdministrativeArea, _placemark.administrativeArea];
} else if (_placemark.country) {
locationString = _placemark.country;
} else {
locationString = @"ERROR";
}
return locationString;
}
@end

View File

@ -1,23 +1,20 @@
//
// PhotoCellNode.h
// Sample
//
// Created by Hannah Troisi on 2/17/16.
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import <CoreLocation/CLLocation.h>
#import "PhotoModel.h"
#import <AsyncDisplayKit/AsyncDisplayKit.h>
#import "PhotoTableViewCell.h" // PhotoTableViewCellProtocol

View File

@ -1,20 +1,18 @@
//
// PhotoCellNode.m
// Sample
//
// Created by Hannah Troisi on 2/17/16.
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import "PhotoCellNode.h"
@ -23,7 +21,6 @@
#import <AsyncDisplayKit/ASDisplayNode+Beta.h>
#import "Utilities.h"
#import "CommentsNode.h"
#import "PINImageView+PINRemoteImage.h"
#import "PINButton+PINRemoteImage.h"
@ -50,7 +47,6 @@
@implementation PhotoCellNode
{
PhotoModel *_photoModel;
CommentsNode *_photoCommentsNode;
ASNetworkImageNode *_userAvatarImageNode;
ASNetworkImageNode *_photoImageNode;
ASTextNode *_userNameLabel;
@ -89,26 +85,13 @@
_photoLocationLabel = [[ASTextNode alloc] init];
_photoLocationLabel.maximumNumberOfLines = 1;
[photo.location reverseGeocodedLocationWithCompletionBlock:^(LocationModel *locationModel) {
// check and make sure this is still relevant for this cell (and not an old cell)
// make sure to use _photoModel instance variable as photo may change when cell is reused,
// where as local variable will never change
if (locationModel == _photoModel.location) {
_photoLocationLabel.attributedText = [photo locationAttributedStringWithFontSize:FONT_SIZE];
[self setNeedsLayout];
}
}];
_photoLocationLabel.attributedText = [photo locationAttributedStringWithFontSize:FONT_SIZE];
_photoTimeIntervalSincePostLabel = [self createLayerBackedTextNodeWithString:[photo uploadDateAttributedStringWithFontSize:FONT_SIZE]];
_photoLikesLabel = [self createLayerBackedTextNodeWithString:[photo likesAttributedStringWithFontSize:FONT_SIZE]];
_photoDescriptionLabel = [self createLayerBackedTextNodeWithString:[photo descriptionAttributedStringWithFontSize:FONT_SIZE]];
_photoDescriptionLabel.maximumNumberOfLines = 3;
_photoCommentsNode = [[CommentsNode alloc] init];
_photoCommentsNode.layerBacked = YES;
// instead of adding everything addSubnode:
self.automaticallyManagesSubnodes = YES;
@ -175,7 +158,7 @@
// Create the last stack before assembling everything: the Footer Stack contains the description and comments.
ASStackLayoutSpec *footerStack = [ASStackLayoutSpec verticalStackLayoutSpec];
footerStack.spacing = VERTICAL_BUFFER;
footerStack.children = @[_photoLikesLabel, _photoDescriptionLabel, _photoCommentsNode];
footerStack.children = @[_photoLikesLabel, _photoDescriptionLabel];
// Main Vertical Stack: contains header, large main photo with fixed aspect ratio, and footer.
ASStackLayoutSpec *verticalStack = [ASStackLayoutSpec verticalStackLayoutSpec];
@ -268,8 +251,7 @@
alignItems:ASStackLayoutAlignItemsStretch
children:@[
_photoLikesLabel,
_photoDescriptionLabel,
_photoCommentsNode
_photoDescriptionLabel
]]
]
]];
@ -282,10 +264,6 @@
- (void)didEnterPreloadState
{
[super didEnterPreloadState];
[_photoModel.commentFeed refreshFeedWithCompletionBlock:^(NSArray *newComments) {
[self loadCommentsForPhoto:_photoModel];
}];
}
#pragma mark - Network Image Delegate
@ -311,15 +289,6 @@
return textNode;
}
- (void)loadCommentsForPhoto:(PhotoModel *)photo
{
if (photo.commentFeed.numberOfItemsInFeed > 0) {
[_photoCommentsNode updateWithCommentFeedModel:photo.commentFeed];
[self setNeedsLayout];
}
}
- (void)setupYogaLayoutIfNeeded
{
#if YOGA_LAYOUT

View File

@ -1,20 +1,18 @@
//
// PhotoFeedBaseController.h
// Sample
//
// Created by Huy Nguyen on 20/12/16.
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import <AsyncDisplayKit/AsyncDisplayKit.h>
@ -34,6 +32,5 @@
#pragma mark - Subclasses must override these methods
- (void)loadPage;
- (void)requestCommentsForPhotos:(NSArray *)newPhotos;
@end

View File

@ -1,21 +1,18 @@
//
// PhotoFeedBaseController.m
// Sample
//
// Created by Huy Nguyen on 20/12/16.
// Copyright © 2016 Facebook. All rights reserved.
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import "PhotoFeedBaseController.h"
@ -60,7 +57,6 @@
[_activityIndicatorView stopAnimating];
[self.tableView reloadData];
[self requestCommentsForPhotos:newPhotos];
// immediately start second larger fetch
[self loadPage];
@ -116,9 +112,4 @@
NSAssert(NO, @"Subclasses must override this method");
}
- (void)requestCommentsForPhotos:(NSArray *)newPhotos
{
NSAssert(NO, @"Subclasses must override this method");
}
@end

View File

@ -1,20 +1,18 @@
//
// PhotoFeedModel.h
// Sample
//
// Created by Hannah Troisi on 2/28/16.
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import "PhotoModel.h"
@ -38,7 +36,6 @@ typedef NS_ENUM(NSInteger, PhotoFeedModelType) {
- (PhotoModel *)objectAtIndex:(NSUInteger)index;
- (NSInteger)indexOfPhotoModel:(PhotoModel *)photoModel;
- (void)updatePhotoFeedModelTypeLocationCoordinates:(CLLocationCoordinate2D)coordinate radiusInMiles:(NSUInteger)radius;
- (void)updatePhotoFeedModelTypeUserId:(NSUInteger)userID;
- (void)clearFeed;

View File

@ -18,11 +18,12 @@
#import "PhotoFeedModel.h"
#import "ImageURLModel.h"
#define fiveHundredPX_ENDPOINT_HOST @"https://api.500px.com/v1/"
#define fiveHundredPX_ENDPOINT_POPULAR @"photos?feature=popular&exclude=Nude,People,Fashion&sort=rating&image_size=3&include_store=store_download&include_states=voted"
#define fiveHundredPX_ENDPOINT_SEARCH @"photos/search?geo=" //latitude,longitude,radius<units>
#define fiveHundredPX_ENDPOINT_USER @"photos?user_id="
#define fiveHundredPX_CONSUMER_KEY_PARAM @"&consumer_key=Fi13GVb8g53sGvHICzlram7QkKOlSDmAmp9s9aqC" // PLEASE REQUEST YOUR OWN 500PX CONSUMER KEY
#define unsplash_ENDPOINT_HOST @"https://api.unsplash.com/"
#define unsplash_ENDPOINT_POPULAR @"photos?order_by=popular"
#define unsplash_ENDPOINT_SEARCH @"photos/search?geo=" //latitude,longitude,radius<units>
#define unsplash_ENDPOINT_USER @"photos?user_id="
#define unsplash_CONSUMER_KEY_PARAM @"&client_id=3b99a69cee09770a4a0bbb870b437dbda53efb22f6f6de63714b71c4df7c9642" // PLEASE REQUEST YOUR OWN UNSPLASH CONSUMER KEY
#define unsplash_IMAGES_PER_PAGE 30
@implementation PhotoFeedModel
{
@ -40,8 +41,6 @@
BOOL _refreshFeedInProgress;
NSURLSessionDataTask *_task;
CLLocationCoordinate2D _location;
NSUInteger _locationRadius;
NSUInteger _userID;
}
@ -61,21 +60,21 @@
NSString *apiEndpointString;
switch (type) {
case (PhotoFeedModelTypePopular):
apiEndpointString = fiveHundredPX_ENDPOINT_POPULAR;
apiEndpointString = unsplash_ENDPOINT_POPULAR;
break;
case (PhotoFeedModelTypeLocation):
apiEndpointString = fiveHundredPX_ENDPOINT_SEARCH;
apiEndpointString = unsplash_ENDPOINT_SEARCH;
break;
case (PhotoFeedModelTypeUserPhotos):
apiEndpointString = fiveHundredPX_ENDPOINT_USER;
apiEndpointString = unsplash_ENDPOINT_USER;
break;
default:
break;
}
_urlString = [[fiveHundredPX_ENDPOINT_HOST stringByAppendingString:apiEndpointString] stringByAppendingString:fiveHundredPX_CONSUMER_KEY_PARAM];
_urlString = [[unsplash_ENDPOINT_HOST stringByAppendingString:apiEndpointString] stringByAppendingString:unsplash_CONSUMER_KEY_PARAM];
}
return self;
@ -108,24 +107,14 @@
return [_photos indexOfObjectIdenticalTo:photoModel];
}
- (void)updatePhotoFeedModelTypeLocationCoordinates:(CLLocationCoordinate2D)coordinate radiusInMiles:(NSUInteger)radius;
{
_location = coordinate;
_locationRadius = radius;
NSString *locationString = [NSString stringWithFormat:@"%f,%f,%lumi", coordinate.latitude, coordinate.longitude, (unsigned long)radius];
_urlString = [fiveHundredPX_ENDPOINT_HOST stringByAppendingString:fiveHundredPX_ENDPOINT_SEARCH];
_urlString = [[_urlString stringByAppendingString:locationString] stringByAppendingString:fiveHundredPX_CONSUMER_KEY_PARAM];
}
- (void)updatePhotoFeedModelTypeUserId:(NSUInteger)userID
{
_userID = userID;
NSString *userString = [NSString stringWithFormat:@"%lu", (long)userID];
_urlString = [fiveHundredPX_ENDPOINT_HOST stringByAppendingString:fiveHundredPX_ENDPOINT_USER];
_urlString = [unsplash_ENDPOINT_HOST stringByAppendingString:unsplash_ENDPOINT_USER];
_urlString = [[_urlString stringByAppendingString:userString] stringByAppendingString:@"&sort=created_at&image_size=3&include_store=store_download&include_states=voted"];
_urlString = [_urlString stringByAppendingString:fiveHundredPX_CONSUMER_KEY_PARAM];
_urlString = [_urlString stringByAppendingString:unsplash_CONSUMER_KEY_PARAM];
}
- (void)clearFeed
@ -191,7 +180,7 @@
}
}
NSUInteger numPhotos = (numResults < 100) ? numResults : 100;
NSUInteger numPhotos = (numResults < unsplash_IMAGES_PER_PAGE) ? numResults : unsplash_IMAGES_PER_PAGE;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableArray *newPhotos = [NSMutableArray array];
@ -200,23 +189,28 @@
@synchronized(self) {
NSUInteger nextPage = _currentPage + 1;
NSString *imageSizeParam = [ImageURLModel imageParameterForClosestImageSize:_imageSize];
NSString *urlAdditions = [NSString stringWithFormat:@"&page=%lu&rpp=%lu%@", (unsigned long)nextPage, (long)numPhotos, imageSizeParam];
NSString *urlAdditions = [NSString stringWithFormat:@"&page=%lu&per_page=%lu%@", (unsigned long)nextPage, (long)numPhotos, imageSizeParam];
NSURL *url = [NSURL URLWithString:[_urlString stringByAppendingString:urlAdditions]];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
_task = [session dataTaskWithURL:url completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data) {
NSDictionary *response = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
if ([response isKindOfClass:[NSDictionary class]]) {
_currentPage = [[response valueForKeyPath:@"current_page"] integerValue];
_totalPages = [[response valueForKeyPath:@"total_pages"] integerValue];
_totalItems = [[response valueForKeyPath:@"total_items"] integerValue];
@synchronized(self) {
NSHTTPURLResponse *httpResponse = nil;
if (data && [response isKindOfClass:[NSHTTPURLResponse class]]) {
httpResponse = (NSHTTPURLResponse *)response;
NSArray *objects = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
NSArray *photos = [response valueForKeyPath:@"photos"];
if ([photos isKindOfClass:[NSArray class]]) {
if ([objects isKindOfClass:[NSArray class]]) {
_currentPage = nextPage;
_totalItems = [[httpResponse allHeaderFields][@"x-total"] integerValue];
_totalPages = _totalItems / unsplash_IMAGES_PER_PAGE; // default per page is 10
if (_totalItems % unsplash_IMAGES_PER_PAGE != 0) {
_totalPages += 1;
}
NSArray *photos = objects;
for (NSDictionary *photoDictionary in photos) {
if ([response isKindOfClass:[NSDictionary class]]) {
PhotoModel *photo = [[PhotoModel alloc] initWith500pxPhoto:photoDictionary];
if ([photoDictionary isKindOfClass:[NSDictionary class]]) {
PhotoModel *photo = [[PhotoModel alloc] initWithUnsplashPhoto:photoDictionary];
if (photo) {
if (replaceData || ![_ids containsObject:photo.photoID]) {
[newPhotos addObject:photo];
@ -228,18 +222,21 @@
}
}
}
dispatch_async(dispatch_get_main_queue(), ^{
if (replaceData) {
_photos = [newPhotos mutableCopy];
_ids = [newIDs mutableCopy];
} else {
[_photos addObjectsFromArray:newPhotos];
[_ids addObjectsFromArray:newIDs];
@synchronized(self) {
if (replaceData) {
_photos = [newPhotos mutableCopy];
_ids = [newIDs mutableCopy];
} else {
[_photos addObjectsFromArray:newPhotos];
[_ids addObjectsFromArray:newIDs];
}
if (block) {
block(newPhotos);
}
_fetchPageInProgress = NO;
}
if (block) {
block(newPhotos);
}
_fetchPageInProgress = NO;
});
}];
[_task resume];

View File

@ -66,7 +66,6 @@
[self.photoFeed requestPageWithCompletionBlock:^(NSArray *newPhotos){
[self insertNewRows:newPhotos];
[self requestCommentsForPhotos:newPhotos];
if (context) {
[context completeBatchFetching:YES];
}
@ -85,11 +84,6 @@
[self loadPageWithContext:nil];
}
- (void)requestCommentsForPhotos:(NSArray *)newPhotos
{
// Do nothing (#1530).
}
#pragma mark - ASTableDataSource methods
- (NSInteger)tableNode:(ASTableNode *)tableNode numberOfRowsInSection:(NSInteger)section

View File

@ -1,27 +1,24 @@
//
// PhotoFeedViewController.m
// Sample
//
// Created by Hannah Troisi on 2/17/16.
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import "PhotoFeedViewController.h"
#import "Utilities.h"
#import "PhotoTableViewCell.h"
#import "PhotoFeedModel.h"
#import "CommentView.h"
#define AUTO_TAIL_LOADING_NUM_SCREENFULS 2.5
@ -73,35 +70,9 @@
{
[self.photoFeed requestPageWithCompletionBlock:^(NSArray *newPhotos){
[self insertNewRows:newPhotos];
[self requestCommentsForPhotos:newPhotos];
} numResultsToReturn:20];
}
- (void)requestCommentsForPhotos:(NSArray *)newPhotos
{
for (PhotoModel *photo in newPhotos) {
[photo.commentFeed refreshFeedWithCompletionBlock:^(NSArray *newComments) {
NSInteger rowNum = [self.photoFeed indexOfPhotoModel:photo];
NSIndexPath *cellPath = [NSIndexPath indexPathForRow:rowNum inSection:0];
PhotoTableViewCell *cell = [_tableView cellForRowAtIndexPath:cellPath];
if (cell) {
[cell loadCommentsForPhoto:photo];
[_tableView beginUpdates];
[_tableView endUpdates];
// adjust scrollView contentOffset if inserting above visible cells
NSIndexPath *visibleCellPath = [_tableView indexPathForCell:_tableView.visibleCells.firstObject];
if (cellPath.row < visibleCellPath.row) {
CGFloat commentViewHeight = [CommentView heightForCommentFeedModel:photo.commentFeed withWidth:self.view.bounds.size.width];
_tableView.contentOffset = CGPointMake(_tableView.contentOffset.x, _tableView.contentOffset.y + commentViewHeight);
}
}
}];
}
}
#pragma mark - UITableViewDataSource methods
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section

View File

@ -1,25 +1,21 @@
//
// PhotoModel.h
// Sample
//
// Created by Hannah Troisi on 2/26/16.
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import "UserModel.h"
#import "LocationModel.h"
#import "CommentFeedModel.h"
#import <IGListKit/IGListKit.h>
@interface PhotoModel : NSObject <IGListDiffable>
@ -27,16 +23,15 @@
@property (nonatomic, strong, readonly) NSURL *URL;
@property (nonatomic, strong, readonly) NSString *photoID;
@property (nonatomic, strong, readonly) NSString *uploadDateString;
@property (nonatomic, strong, readonly) NSString *title;
@property (nonatomic, strong, readonly) NSString *descriptionText;
@property (nonatomic, assign, readonly) NSUInteger commentsCount;
@property (nonatomic, assign, readonly) NSUInteger likesCount;
@property (nonatomic, strong, readonly) LocationModel *location;
@property (nonatomic, strong, readonly) NSString *location;
@property (nonatomic, strong, readonly) UserModel *ownerUserProfile;
@property (nonatomic, strong, readonly) CommentFeedModel *commentFeed;
@property (nonatomic, assign, readonly) NSUInteger width;
@property (nonatomic, assign, readonly) NSUInteger height;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWith500pxPhoto:(NSDictionary *)photoDictionary NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithUnsplashPhoto:(NSDictionary *)photoDictionary NS_DESIGNATED_INITIALIZER;
- (NSAttributedString *)descriptionAttributedStringWithFontSize:(CGFloat)size;
- (NSAttributedString *)uploadDateAttributedStringWithFontSize:(CGFloat)size;

View File

@ -22,41 +22,30 @@
{
NSDictionary *_dictionaryRepresentation;
NSString *_uploadDateRaw;
CommentFeedModel *_commentFeed;
}
#pragma mark - Properties
- (CommentFeedModel *)commentFeed
{
if (!_commentFeed) {
_commentFeed = [[CommentFeedModel alloc] initWithPhotoID:_photoID];
}
return _commentFeed;
}
#pragma mark - Lifecycle
- (instancetype)initWith500pxPhoto:(NSDictionary *)photoDictionary
- (instancetype)initWithUnsplashPhoto:(NSDictionary *)photoDictionary
{
self = [super init];
if (self) {
_dictionaryRepresentation = photoDictionary;
_uploadDateRaw = [photoDictionary objectForKey:@"created_at"];
_photoID = [[photoDictionary objectForKey:@"id"] description];
_title = [photoDictionary objectForKey:@"title"];
_descriptionText = [photoDictionary valueForKeyPath:@"name"];
_commentsCount = [[photoDictionary objectForKey:@"comments_count"] integerValue];
_likesCount = [[photoDictionary objectForKey:@"positive_votes_count"] integerValue];
_photoID = [photoDictionary objectForKey:@"id"];
_descriptionText = [photoDictionary valueForKeyPath:@"description"];
_likesCount = [[photoDictionary objectForKey:@"likes"] integerValue];
_location = [photoDictionary objectForKey:@"location"];
NSString *urlString = [[photoDictionary objectForKey:@"image_url"] firstObject];
NSString *urlString = [photoDictionary objectForKey:@"urls"][@"regular"];
_URL = urlString ? [NSURL URLWithString:urlString] : nil;
_location = [[LocationModel alloc] initWith500pxPhoto:photoDictionary];
_ownerUserProfile = [[UserModel alloc] initWith500pxPhoto:photoDictionary];
_ownerUserProfile = [[UserModel alloc] initWithUnsplashPhoto:photoDictionary];
_uploadDateString = [NSString elapsedTimeStringSinceDate:_uploadDateRaw];
_height = [[photoDictionary objectForKey:@"height"] integerValue];
_width = [[photoDictionary objectForKey:@"width"] integerValue];
}
return self;
@ -88,7 +77,7 @@
- (NSAttributedString *)locationAttributedStringWithFontSize:(CGFloat)size
{
return [NSAttributedString attributedStringWithString:self.location.locationString fontSize:size color:[UIColor lightBlueColor] firstWordColor:nil];
return [NSAttributedString attributedStringWithString:self.location fontSize:size color:[UIColor lightBlueColor] firstWordColor:nil];
}
- (NSString *)description

View File

@ -1,23 +1,20 @@
//
// PhotoTableViewCell.h
// Sample
//
// Created by Hannah Troisi on 2/17/16.
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import <CoreLocation/CLLocation.h>
#import "PhotoModel.h"
@interface PhotoTableViewCell : UITableViewCell
@ -25,6 +22,5 @@
+ (CGFloat)heightForPhotoModel:(PhotoModel *)photo withWidth:(CGFloat)width;
- (void)updateCellWithPhotoObject:(PhotoModel *)photo;
- (void)loadCommentsForPhoto:(PhotoModel *)photo;
@end

View File

@ -1,27 +1,24 @@
//
// PhotoTableViewCell.m
// Sample
//
// Created by Hannah Troisi on 2/17/16.
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import "PhotoTableViewCell.h"
#import "Utilities.h"
#import "PINImageView+PINRemoteImage.h"
#import "PINButton+PINRemoteImage.h"
#import "CommentView.h"
#define DEBUG_PHOTOCELL_LAYOUT 0
#define USE_UIKIT_AUTOLAYOUT 1
@ -36,7 +33,6 @@
@implementation PhotoTableViewCell
{
PhotoModel *_photoModel;
CommentView *_photoCommentsView;
UIImageView *_userAvatarImageView;
UIImageView *_photoImageView;
@ -66,9 +62,7 @@
options:NSStringDrawingUsesLineFragmentOrigin
context:nil].size.height;
CGFloat commentViewHeight = [CommentView heightForCommentFeedModel:photo.commentFeed withWidth:availableWidth];
return HEADER_HEIGHT + photoHeight + likesHeight + descriptionHeight + commentViewHeight + (4 * VERTICAL_BUFFER);
return HEADER_HEIGHT + photoHeight + likesHeight + descriptionHeight + (4 * VERTICAL_BUFFER);
}
#pragma mark - Lifecycle
@ -78,10 +72,9 @@
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
_photoCommentsView = [[CommentView alloc] init];
_userAvatarImageView = [[UIImageView alloc] init];
_photoImageView = [[UIImageView alloc] init];
_photoImageView.contentMode = UIViewContentModeScaleAspectFill;
_userNameLabel = [[UILabel alloc] init];
_photoLocationLabel = [[UILabel alloc] init];
_photoTimeIntervalSincePostLabel = [[UILabel alloc] init];
@ -89,7 +82,6 @@
_photoDescriptionLabel = [[UILabel alloc] init];
_photoDescriptionLabel.numberOfLines = 3;
[self addSubview:_photoCommentsView];
[self addSubview:_userAvatarImageView];
[self addSubview:_photoImageView];
[self addSubview:_userNameLabel];
@ -99,7 +91,6 @@
[self addSubview:_photoDescriptionLabel];
#if USE_UIKIT_AUTOLAYOUT
[_photoCommentsView setTranslatesAutoresizingMaskIntoConstraints:NO];
[_userAvatarImageView setTranslatesAutoresizingMaskIntoConstraints:NO];
[_photoImageView setTranslatesAutoresizingMaskIntoConstraints:NO];
[_userNameLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
@ -107,7 +98,6 @@
[_photoTimeIntervalSincePostLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[_photoLikesLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[_photoDescriptionLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[_photoCommentsView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self setupConstraints];
[self updateConstraints];
@ -118,7 +108,6 @@
_userNameLabel.backgroundColor = [UIColor greenColor];
_photoLocationLabel.backgroundColor = [UIColor greenColor];
_photoTimeIntervalSincePostLabel.backgroundColor = [UIColor greenColor];
_photoCommentsView.backgroundColor = [UIColor greenColor];
_photoDescriptionLabel.backgroundColor = [UIColor greenColor];
_photoLikesLabel.backgroundColor = [UIColor greenColor];
#endif
@ -313,31 +302,6 @@
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:-HORIZONTAL_BUFFER]];
// _photoCommentsView
[self addConstraint:[NSLayoutConstraint constraintWithItem:_photoCommentsView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:_photoDescriptionLabel
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:VERTICAL_BUFFER]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_photoCommentsView
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:_photoCommentsView.superview
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:HORIZONTAL_BUFFER]];
[self addConstraint:[NSLayoutConstraint constraintWithItem:_photoCommentsView
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:_photoCommentsView.superview
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:-HORIZONTAL_BUFFER]];
}
- (void)updateConstraints
@ -404,11 +368,6 @@
rect.size.width = MIN(boundsSize.width - HORIZONTAL_BUFFER * 2, rect.size.width);
rect.origin.y = CGRectGetMaxY(_photoLikesLabel.frame) + VERTICAL_BUFFER;
_photoDescriptionLabel.frame = rect;
rect.size = _photoCommentsView.bounds.size;
rect.size.width = boundsSize.width - HORIZONTAL_BUFFER * 2;
rect.origin.y = CGRectGetMaxY(_photoDescriptionLabel.frame) + VERTICAL_BUFFER;
_photoCommentsView.frame = rect;
#endif
}
@ -416,9 +375,6 @@
{
[super prepareForReuse];
_photoCommentsView.frame = CGRectZero; // next cell might not have a _photoCommentsView
[_photoCommentsView updateWithCommentFeedModel:nil];
_userAvatarImageView.image = nil;
_photoImageView.image = nil;
_userNameLabel.attributedText = nil;
@ -453,23 +409,15 @@
}];
[self downloadAndProcessUserAvatarForPhoto:photo];
[self loadCommentsForPhoto:photo];
[self reverseGeocodeLocationForPhoto:photo];
}
- (void)loadCommentsForPhoto:(PhotoModel *)photo
{
if (photo.commentFeed.numberOfItemsInFeed > 0) {
[_photoCommentsView updateWithCommentFeedModel:photo.commentFeed];
CGRect frame = _photoCommentsView.frame;
CGFloat availableWidth = (self.bounds.size.width - HORIZONTAL_BUFFER * 2);
frame.size.width = availableWidth;
frame.size.height = [CommentView heightForCommentFeedModel:photo.commentFeed withWidth:availableWidth];
_photoCommentsView.frame = frame;
//update location
_photoLocationLabel.attributedText = [photo locationAttributedStringWithFontSize:FONT_SIZE];
[_photoLocationLabel sizeToFit];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateConstraints];
[self setNeedsLayout];
}
});
}
#pragma mark - Helper Methods
@ -482,23 +430,4 @@
}];
}
- (void)reverseGeocodeLocationForPhoto:(PhotoModel *)photo
{
[photo.location reverseGeocodedLocationWithCompletionBlock:^(LocationModel *locationModel) {
// check and make sure this is still relevant for this cell (and not an old cell)
// make sure to use _photoModel instance variable as photo may change when cell is reused,
// where as local variable will never change
if (locationModel == _photoModel.location) {
_photoLocationLabel.attributedText = [photo locationAttributedStringWithFontSize:FONT_SIZE];
[_photoLocationLabel sizeToFit];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateConstraints];
[self setNeedsLayout];
});
}
}];
}
@end

View File

@ -1,35 +1,30 @@
//
// UserModel.h
// Sample
//
// Created by Hannah Troisi on 2/26/16.
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
@interface UserModel : NSObject
@property (nonatomic, strong, readonly) NSDictionary *dictionaryRepresentation;
@property (nonatomic, assign, readonly) NSUInteger userID;
@property (nonatomic, assign, readonly) NSString *userID;
@property (nonatomic, strong, readonly) NSString *username;
@property (nonatomic, strong, readonly) NSString *firstName;
@property (nonatomic, strong, readonly) NSString *lastName;
@property (nonatomic, strong, readonly) NSString *fullName;
@property (nonatomic, strong, readonly) NSString *city;
@property (nonatomic, strong, readonly) NSString *state;
@property (nonatomic, strong, readonly) NSString *country;
@property (nonatomic, strong, readonly) NSString *location;
@property (nonatomic, strong, readonly) NSString *about;
@property (nonatomic, strong, readonly) NSString *domain;
@property (nonatomic, strong, readonly) NSURL *userPicURL;
@property (nonatomic, assign, readonly) NSUInteger photoCount;
@property (nonatomic, assign, readonly) NSUInteger galleriesCount;
@ -39,7 +34,7 @@
@property (nonatomic, assign, readonly) BOOL following;
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWith500pxPhoto:(NSDictionary *)dictionary NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithUnsplashPhoto:(NSDictionary *)dictionary NS_DESIGNATED_INITIALIZER;
- (NSAttributedString *)usernameAttributedStringWithFontSize:(CGFloat)size;
- (NSAttributedString *)fullNameAttributedStringWithFontSize:(CGFloat)size;
@ -48,4 +43,4 @@
- (void)downloadCompleteUserDataWithCompletionBlock:(void(^)(UserModel *))block;
@end
@end

View File

@ -1,20 +1,18 @@
//
// UserModel.m
// Sample
//
// Created by Hannah Troisi on 2/26/16.
// Texture
//
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
// This source code is licensed under the BSD-style license found in the
// LICENSE file in the root directory of this source tree. An additional grant
// of patent rights can be found in the PATENTS file in the same directory.
// LICENSE file in the /ASDK-Licenses directory of this source tree. An additional
// grant of patent rights can be found in the PATENTS file in the same directory.
//
// 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
// FACEBOOK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
// Modifications to this file made after 4/13/2017 are: Copyright (c) through the present,
// Pinterest, Inc. Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
#import "UserModel.h"
@ -29,7 +27,7 @@
#pragma mark - Lifecycle
- (instancetype)initWith500pxPhoto:(NSDictionary *)dictionary
- (instancetype)initWithUnsplashPhoto:(NSDictionary *)dictionary
{
self = [super init];
@ -110,7 +108,7 @@
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// fetch JSON data from server
NSString *urlString = [NSString stringWithFormat:@"https://api.500px.com/v1/users/show?id=%lu&consumer_key=Fi13GVb8g53sGvHICzlram7QkKOlSDmAmp9s9aqC", (unsigned long)_userID];
NSString *urlString = [NSString stringWithFormat:@"https://api.500px.com/v1/users/show?id=%@&consumer_key=Fi13GVb8g53sGvHICzlram7QkKOlSDmAmp9s9aqC", _userID];
NSURL *url = [NSURL URLWithString:urlString];
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
@ -148,30 +146,23 @@
return;
}
_userID = [[self guardJSONElement:[userDictionary objectForKey:@"id"]] integerValue];
_userID = [self guardJSONElement:[userDictionary objectForKey:@"id"]];
_username = [[self guardJSONElement:[userDictionary objectForKey:@"username"]] lowercaseString];
if ([_username isKindOfClass:[NSNumber class]]) {
if (_username == nil) {
_username = @"Anonymous";
}
_firstName = [self guardJSONElement:[userDictionary objectForKey:@"firstname"]];
_lastName = [self guardJSONElement:[userDictionary objectForKey:@"lastname"]];
_fullName = [self guardJSONElement:[userDictionary objectForKey:@"fullname"]];
_city = [self guardJSONElement:[userDictionary objectForKey:@"city"]];
_state = [self guardJSONElement:[userDictionary objectForKey:@"state"]];
_country = [self guardJSONElement:[userDictionary objectForKey:@"country"]];
_about = [self guardJSONElement:[userDictionary objectForKey:@"about"]];
_domain = [self guardJSONElement:[userDictionary objectForKey:@"domain"]];
_photoCount = [[self guardJSONElement:[userDictionary objectForKey:@"photos_count"]] integerValue];
_galleriesCount = [[self guardJSONElement:[userDictionary objectForKey:@"galleries_count"]] integerValue];
_affection = [[self guardJSONElement:[userDictionary objectForKey:@"affection"]] integerValue];
_friendsCount = [[self guardJSONElement:[userDictionary objectForKey:@"friends_count"]] integerValue];
_followersCount = [[self guardJSONElement:[userDictionary objectForKey:@"followers_count"]] integerValue];
_following = [[self guardJSONElement:[userDictionary objectForKey:@"following"]] boolValue];
_firstName = [self guardJSONElement:[userDictionary objectForKey:@"first_name"]];
_lastName = [self guardJSONElement:[userDictionary objectForKey:@"last_name"]];
_fullName = [self guardJSONElement:[userDictionary objectForKey:@"name"]];
_location = [self guardJSONElement:[userDictionary objectForKey:@"location"]];
_about = [self guardJSONElement:[userDictionary objectForKey:@"bio"]];
_photoCount = [[self guardJSONElement:[userDictionary objectForKey:@"total_photos"]] integerValue];
_galleriesCount = [[self guardJSONElement:[userDictionary objectForKey:@"total_collections"]] integerValue];
_dictionaryRepresentation = userDictionary;
NSString *urlString = [self guardJSONElement:[userDictionary objectForKey:@"userpic_url"]];
NSString *urlString = [self guardJSONElement:[userDictionary objectForKey:@"profile_image"][@"medium"]];
_userPicURL = urlString ? [NSURL URLWithString:urlString] : nil;
}