mirror of
https://github.com/samuelclay/NewsBlur.git
synced 2025-08-21 05:45:13 +00:00
306 lines
16 KiB
Objective-C
Executable file
306 lines
16 KiB
Objective-C
Executable file
//
|
|
// OSKAppDotNetUtility.m
|
|
// Overshare
|
|
//
|
|
// Created by Jared Sinclair on 10/10/13.
|
|
// Copyright (c) 2013 Overshare Kit. All rights reserved.
|
|
//
|
|
|
|
#import "OSKAppDotNetUtility.h"
|
|
|
|
#import "OSKActivity.h"
|
|
#import "OSKManagedAccountCredential.h"
|
|
#import "OSKShareableContentItem.h"
|
|
#import "OSKApplicationCredential.h"
|
|
#import "OSKLogger.h"
|
|
#import "NSDictionary+OSKModel.h"
|
|
#import "OSKManagedAccount.h"
|
|
#import "NSMutableURLRequest+OSKUtilities.h"
|
|
#import "NSHTTPURLResponse+OSKUtilities.h"
|
|
#import "UIImage+OSKUtilities.h"
|
|
|
|
static NSString * OSKAppDotNetUtility_BaseURL = @"https://alpha-api.app.net/";
|
|
static NSString * OSKAppDotNetUtility_URL_FetchMe = @"stream/0/users/me?access_token=%@";
|
|
static NSString * OSKAppDotNetUtility_URL_WriteNewPost = @"stream/0/posts?access_token=%@&include_post_annotations=1";
|
|
|
|
#define kAccountID @"id"
|
|
#define kUsername @"username"
|
|
#define kName @"name"
|
|
#define kAvatarObject @"avatar_image"
|
|
#define kAvatarURL @"url"
|
|
|
|
@implementation OSKAppDotNetUtility
|
|
|
|
#pragma mark - Write Post
|
|
|
|
+ (void)postContentItem:(OSKMicroblogPostContentItem *)item withCredential:(OSKManagedAccountCredential *)credential appCredential:(OSKApplicationCredential *)appCredential completion:(void(^)(BOOL success, NSError *error))completion {
|
|
UIBackgroundTaskIdentifier backgroundTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
|
|
if(completion) {
|
|
completion(NO, nil);
|
|
}
|
|
}];
|
|
|
|
if (item.images.count) {
|
|
[self uploadImages:item.images accountCredential:credential appCredential:appCredential completion:^(NSArray *fileDictionaries, NSError *error) {
|
|
[self _postContentItem:item fileAPIDictionaries:fileDictionaries withCredential:credential appCredential:appCredential completion:^(BOOL success, NSError *error) {
|
|
if (completion) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
completion(success, nil);
|
|
[[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier];
|
|
});
|
|
}
|
|
}];
|
|
}];
|
|
} else {
|
|
[self _postContentItem:item fileAPIDictionaries:nil withCredential:credential appCredential:appCredential completion:^(BOOL success, NSError *error) {
|
|
if (completion) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
completion(success, nil);
|
|
[[UIApplication sharedApplication] endBackgroundTask:backgroundTaskIdentifier];
|
|
});
|
|
}
|
|
}];
|
|
}
|
|
}
|
|
|
|
+ (void)_postContentItem:(OSKMicroblogPostContentItem *)item fileAPIDictionaries:(NSArray *)dictionaries withCredential:(OSKManagedAccountCredential *)credential appCredential:(OSKApplicationCredential *)appCredential completion:(void(^)(BOOL success, NSError *error))completion {
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
NSString *subPath = [NSString stringWithFormat:OSKAppDotNetUtility_URL_WriteNewPost, credential.token];
|
|
NSString *fullPath = [NSString stringWithFormat:@"%@%@", OSKAppDotNetUtility_BaseURL, subPath];
|
|
NSDictionary *dictionaryRep = [self _dictionaryRepresentationForContentItem:item attachedImageDictionaries:dictionaries];
|
|
|
|
NSURLSession *sesh = [NSURLSession sharedSession];
|
|
|
|
NSMutableURLRequest *request = [NSMutableURLRequest osk_requestWithMethod:@"POST" URLString:fullPath parameters:dictionaryRep serialization:OSKParameterSerializationType_HTTPBody_JSON];
|
|
|
|
[[sesh dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
|
if (completion) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
NSError *theError = error;
|
|
if ([NSHTTPURLResponse statusCodeAcceptableForResponse:response] == NO && error == nil) {
|
|
theError = [NSError errorWithDomain:@"OSKAppDotNetUtility" code:400 userInfo:@{NSLocalizedFailureReasonErrorKey:[NSString stringWithFormat:@"Request failed: %@", response.description]}];
|
|
}
|
|
if (theError) {
|
|
OSKLog(@"Failed to send App.net post: %@", error);
|
|
}
|
|
completion((theError == nil), theError);
|
|
});
|
|
}
|
|
}] resume];
|
|
});
|
|
}
|
|
|
|
+ (NSDictionary *)_dictionaryRepresentationForContentItem:(OSKMicroblogPostContentItem *)item attachedImageDictionaries:(NSArray *)dictionaries {
|
|
NSMutableDictionary *rep = [[NSMutableDictionary alloc] init];
|
|
if (dictionaries.count) {
|
|
NSArray *fileReps = [self _annotationDictionariesForFileAPIDictionaries:dictionaries];
|
|
if (fileReps.count) {
|
|
[rep setObject:fileReps forKey:@"annotations"];
|
|
}
|
|
}
|
|
[rep setObject:item.text forKey:@"text"];
|
|
return rep;
|
|
}
|
|
|
|
+ (NSArray *)_annotationDictionariesForFileAPIDictionaries:(NSArray *)fileDictionaries {
|
|
NSMutableArray *annotations = [[NSMutableArray alloc] initWithCapacity:fileDictionaries.count];
|
|
for (NSDictionary *aFileDictionary in fileDictionaries) {
|
|
NSString *fileID = [aFileDictionary osk_nonNullStringIDForKey:@"id"];
|
|
NSString *fileToken = [aFileDictionary objectForKey:@"file_token"];
|
|
if (fileToken.length && fileID.length) { // Prevent crash if fileID or fileToken are nil
|
|
NSMutableDictionary *valueDictionary = [[NSMutableDictionary alloc] init];
|
|
NSMutableDictionary *fileDict = [[NSMutableDictionary alloc] init];
|
|
[fileDict setValue:fileID forKey:@"file_id"];
|
|
[fileDict setValue:fileToken forKey:@"file_token"];
|
|
[fileDict setValue:@"oembed" forKey:@"format"];
|
|
[valueDictionary setValue:fileDict forKey:@"+net.app.core.file"];
|
|
NSDictionary *annotation = @{@"type":@"net.app.core.oembed",
|
|
@"value":valueDictionary};
|
|
[annotations addObject:annotation];
|
|
}
|
|
}
|
|
return annotations;
|
|
}
|
|
|
|
#pragma mark - Users & Accounts
|
|
|
|
+ (void)fetchUserDataWithCredential:(OSKManagedAccountCredential *)credential
|
|
appCredential:(OSKApplicationCredential *)appCredential
|
|
completion:(void(^)(NSDictionary *userDictionary, NSError *error))completion {
|
|
NSString *path = [NSString stringWithFormat:OSKAppDotNetUtility_URL_FetchMe, credential.token];
|
|
NSString *fullPath = [NSString stringWithFormat:@"%@%@", OSKAppDotNetUtility_BaseURL, path];
|
|
|
|
NSURLSession *sesh = [NSURLSession sharedSession];
|
|
|
|
NSMutableURLRequest *request = [NSMutableURLRequest osk_requestWithMethod:@"GET" URLString:fullPath parameters:nil serialization:OSKParameterSerializationType_HTTPBody_FormData];
|
|
|
|
[[sesh dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
|
NSError *theError = error;
|
|
if ([NSHTTPURLResponse statusCodeAcceptableForResponse:response] == NO && error == nil) {
|
|
theError = [NSError errorWithDomain:@"OSKAppDotNetUtility" code:400 userInfo:@{NSLocalizedFailureReasonErrorKey:[NSString stringWithFormat:@"Request failed: %@", response.description]}];
|
|
}
|
|
if (theError == nil) {
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
NSDictionary *userDictionary = nil;
|
|
if (data) {
|
|
NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
|
userDictionary = [responseDictionary objectForKey:@"data"];
|
|
}
|
|
|
|
//There are times when the values aren't present in the dictionary
|
|
NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
|
|
if ([userDictionary objectForKey:kAccountID]) {
|
|
userInfo[OSKAppDotNetUtility_UserInfoKey_accountID] = [userDictionary osk_nonNullStringIDForKey:kAccountID];
|
|
}
|
|
if ([userDictionary objectForKey:kUsername]) {
|
|
userInfo[OSKAppDotNetUtility_UserInfoKey_username] = [userDictionary osk_nonNullObjectForKey:kUsername];
|
|
}
|
|
if ([userDictionary objectForKey:kName]) {
|
|
userInfo[OSKAppDotNetUtility_UserInfoKey_name] = [userDictionary osk_nonNullObjectForKey:kName];
|
|
}
|
|
|
|
if ([userDictionary objectForKey:kAvatarObject])
|
|
{
|
|
NSDictionary *avatarObject = [userDictionary osk_nonNullObjectForKey:kAvatarObject];
|
|
if ([avatarObject objectForKey:kAvatarURL]) {
|
|
userInfo[OSKAppDotNetUtility_UserInfoKey_avatarURL] = [avatarObject osk_nonNullObjectForKey:kAvatarURL];
|
|
}
|
|
}
|
|
if (completion) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
completion(userInfo, nil);
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
completion(nil, theError);
|
|
}
|
|
}] resume];
|
|
}
|
|
|
|
+ (void)createNewUserWithAccessToken:(NSString *)token appCredential:(OSKApplicationCredential *)appCredential completion:(void(^)(OSKManagedAccount *account, NSError *error))completion {
|
|
NSString *identifier = [OSKManagedAccount generateNewOvershareAccountIdentifier];
|
|
OSKManagedAccountCredential *accountCredential = nil;
|
|
accountCredential = [[OSKManagedAccountCredential alloc] initWithOvershareAccountIdentifier:identifier accessToken:token];
|
|
[self fetchUserDataWithCredential:accountCredential appCredential:appCredential completion:^(NSDictionary *userDictionary, NSError *error) {
|
|
OSKManagedAccount *account = nil;
|
|
if (userDictionary) {
|
|
account = [self _createAccountWithUserInfo:userDictionary
|
|
accountCredential:accountCredential
|
|
accountIdentifier:identifier];
|
|
}
|
|
if (account) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
completion(account,nil);
|
|
});
|
|
}
|
|
else {
|
|
OSKLog(@"Unable to create account for App.net, error fetching user info: %@", error);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
completion(nil, error);
|
|
});
|
|
}
|
|
}];
|
|
}
|
|
|
|
+ (OSKManagedAccount *)_createAccountWithUserInfo:(NSDictionary *)userInfo accountCredential:(OSKManagedAccountCredential *)credential accountIdentifier:(NSString *)identifier {
|
|
OSKManagedAccount *account = nil;
|
|
account = [[OSKManagedAccount alloc] initWithOvershareAccountIdentifier:identifier
|
|
activityType:OSKActivityType_API_AppDotNet
|
|
credential:credential];
|
|
[account setUsername:userInfo[OSKAppDotNetUtility_UserInfoKey_username]];
|
|
[account setFullName:userInfo[OSKAppDotNetUtility_UserInfoKey_name]];
|
|
[account setAccountID:userInfo[OSKAppDotNetUtility_UserInfoKey_accountID]];
|
|
return account;
|
|
}
|
|
|
|
#pragma mark - File Upload
|
|
|
|
+ (void)uploadImages:(NSArray *)images accountCredential:(OSKManagedAccountCredential *)credential appCredential:(OSKApplicationCredential *)appCredential completion:(void (^)(NSArray *fileDictionaries, NSError *error))completion {
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
NSMutableArray *fileDictionaries = [[NSMutableArray alloc] initWithCapacity:images.count];
|
|
__block NSError *firstError = nil;
|
|
dispatch_queue_t queue = dispatch_get_global_queue(0,0);
|
|
dispatch_group_t group = dispatch_group_create();
|
|
for (UIImage *image in images) {
|
|
dispatch_group_async(group,queue,^{
|
|
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
|
|
[self uploadImage:image accountCredential:credential appCredential:appCredential completion:^(NSDictionary *fileDictionary, NSError *uploadError) {
|
|
if (fileDictionary && [fileDictionary isKindOfClass:[NSDictionary class]]) {
|
|
[fileDictionaries addObject:fileDictionary];
|
|
} else {
|
|
if (firstError == nil) {
|
|
firstError = uploadError;
|
|
}
|
|
}
|
|
dispatch_semaphore_signal(sema);
|
|
}];
|
|
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
|
|
});
|
|
}
|
|
dispatch_group_notify(group,queue,^{
|
|
if (completion) {
|
|
completion(fileDictionaries, firstError);
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
+ (void)uploadImage:(UIImage *)image accountCredential:(OSKManagedAccountCredential *)credential appCredential:(OSKApplicationCredential *)appCredential completion:(void (^)(NSDictionary *fileDictionary, NSError *error))completion {
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
NSDictionary *parameters = @{@"type":@"net.overshare.image",
|
|
@"mime_type":@"image/jpeg",
|
|
@"kind":@"image",
|
|
@"public":@(YES)};
|
|
parameters = [[NSDictionary alloc] initWithDictionary:parameters];
|
|
NSString *path = [NSString stringWithFormat:@"stream/0/files?access_token=%@", credential.token];
|
|
NSString *fullPath = [NSString stringWithFormat:@"%@%@", OSKAppDotNetUtility_BaseURL, path];
|
|
NSString *mimeType = @"image/jpeg";
|
|
NSString *suffix = @"jpg";
|
|
NSString *dateSuffix = [self _todaysDateSuffix];
|
|
CGFloat quality = [UIImage osk_recommendedUploadQuality:image];
|
|
NSData *imageData = UIImageJPEGRepresentation(image, quality);
|
|
NSString *appName = appCredential.appName.copy;
|
|
NSString *filename = [NSString stringWithFormat:@"Image_from_%@_%@.%@", appName, dateSuffix, suffix];
|
|
|
|
NSURLSession *sesh = [NSURLSession sharedSession];
|
|
|
|
NSMutableURLRequest *request = nil;
|
|
NSData *requestData = nil;
|
|
request = [NSMutableURLRequest osk_MultipartFormUploadRequestWithMethod:@"POST" URLString:fullPath parameters:parameters uploadData:imageData filename:filename formName:@"content" mimeType:mimeType serialization:OSKParameterSerializationType_HTTPBody_JSON bodyData:&requestData];
|
|
|
|
[[sesh uploadTaskWithRequest:request fromData:requestData completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
|
|
if (error == nil) {
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
NSError *error = nil;
|
|
NSDictionary *responseDictionary = nil;
|
|
if (data) {
|
|
responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
|
|
}
|
|
if (completion) {
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
completion([responseDictionary objectForKey:@"data"], error);
|
|
});
|
|
}
|
|
});
|
|
} else {
|
|
OSKLog(@"OSKAppDotNetUtility: Image upload failed: %@", error);
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
completion(nil, error);
|
|
});
|
|
}
|
|
}] resume];
|
|
});
|
|
}
|
|
|
|
+ (NSString *)_todaysDateSuffix {
|
|
NSDate *today = [NSDate date];
|
|
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
|
|
formatter.dateFormat = @"yyyy-MM-dd";
|
|
NSString *suffix = [formatter stringFromDate:today];
|
|
return suffix;
|
|
}
|
|
|
|
@end
|
|
|
|
|