266 lines
7.7 KiB
Objective-C

// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS 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 "FBSDKGameRequestDialog.h"
#import "FBSDKCoreKit+Internal.h"
#import "FBSDKGameRequestFrictionlessRecipientCache.h"
#import "FBSDKShareConstants.h"
#import "FBSDKShareError.h"
#import "FBSDKShareUtility.h"
@interface FBSDKGameRequestDialog () <FBSDKWebDialogDelegate>
@end
@implementation FBSDKGameRequestDialog
{
BOOL _dialogIsFrictionless;
FBSDKWebDialog *_webDialog;
}
#define FBSDK_APP_REQUEST_METHOD_NAME @"apprequests"
#pragma mark - Class Methods
static FBSDKGameRequestFrictionlessRecipientCache *_recipientCache = nil;
+ (void)initialize
{
if (self == [FBSDKGameRequestDialog class]) {
_recipientCache = [[FBSDKGameRequestFrictionlessRecipientCache alloc] init];
}
}
+ (instancetype)showWithContent:(FBSDKGameRequestContent *)content delegate:(id<FBSDKGameRequestDialogDelegate>)delegate
{
FBSDKGameRequestDialog *dialog = [[self alloc] init];
dialog.content = content;
dialog.delegate = delegate;
[dialog show];
return dialog;
}
#pragma mark - Object Lifecycle
- (instancetype)init
{
if ((self = [super init])) {
_webDialog = [[FBSDKWebDialog alloc] init];
_webDialog.delegate = self;
_webDialog.name = FBSDK_APP_REQUEST_METHOD_NAME;
}
return self;
}
- (void)dealloc
{
_webDialog.delegate = nil;
}
#pragma mark - Public Methods
- (BOOL)canShow
{
return YES;
}
- (BOOL)show
{
NSError *error;
if (![self canShow]) {
error = [FBSDKShareError errorWithCode:FBSDKShareDialogNotAvailableErrorCode
message:@"Game request dialog is not available."];
[_delegate gameRequestDialog:self didFailWithError:error];
return NO;
}
if (![self validateWithError:&error]) {
[_delegate gameRequestDialog:self didFailWithError:error];
return NO;
}
FBSDKGameRequestContent *content = self.content;
if (error) {
return NO;
}
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
[FBSDKInternalUtility dictionary:parameters setObject:[content.recipients componentsJoinedByString:@","] forKey:@"to"];
[FBSDKInternalUtility dictionary:parameters setObject:content.message forKey:@"message"];
[FBSDKInternalUtility dictionary:parameters setObject:[self _actionTypeNameForActionType:content.actionType] forKey:@"action_type"];
[FBSDKInternalUtility dictionary:parameters setObject:content.objectID forKey:@"object_id"];
[FBSDKInternalUtility dictionary:parameters setObject:[self _filtersNameForFilters:content.filters] forKey:@"filters"];
[FBSDKInternalUtility dictionary:parameters setObject:[content.recipientSuggestions componentsJoinedByString:@","] forKey:@"suggestions"];
[FBSDKInternalUtility dictionary:parameters setObject:content.data forKey:@"data"];
[FBSDKInternalUtility dictionary:parameters setObject:content.title forKey:@"title"];
// check if we are sending to a specific set of recipients. if we are and they are all frictionless recipients, we
// can perform this action without displaying the web dialog
_webDialog.deferVisibility = NO;
NSArray *recipients = content.recipients;
if (_frictionlessRequestsEnabled && recipients) {
// specify these parameters to get the frictionless recipients from the dialog when it is presented
parameters[@"frictionless"] = @YES;
parameters[@"get_frictionless_recipients"] = @YES;
_dialogIsFrictionless = YES;
if ([_recipientCache recipientsAreFrictionless:recipients]) {
_webDialog.deferVisibility = YES;
}
}
_webDialog.parameters = parameters;
[_webDialog show];
[FBSDKInternalUtility registerTransientObject:self];
return YES;
}
- (BOOL)validateWithError:(NSError *__autoreleasing *)errorRef
{
return [FBSDKShareUtility validateGameRequestContent:self.content error:errorRef];
}
#pragma mark - FBSDKWebDialogDelegate
- (void)webDialog:(FBSDKWebDialog *)webDialog didCompleteWithResults:(NSDictionary *)results
{
if (_webDialog != webDialog) {
return;
}
if (_dialogIsFrictionless && results) {
[_recipientCache updateWithResults:results];
}
[self _cleanUp];
NSError *error = [FBSDKShareError errorWithCode:[FBSDKTypeUtility unsignedIntegerValue:results[@"error_code"]]
message:[FBSDKTypeUtility stringValue:results[@"error_message"]]];
if (!error.code) {
// reformat "to[x]" keys into an array.
int counter = 0;
NSMutableArray *toArray = [NSMutableArray array];
while (true) {
NSString *key = [NSString stringWithFormat:@"to[%d]", counter++];
if (results[key]) {
[toArray addObject:results[key]];
} else {
break;
}
}
if (toArray.count) {
NSMutableDictionary *mutableResults = [results mutableCopy];
mutableResults[@"to"] = toArray;
results = mutableResults;
}
}
[self _handleCompletionWithDialogResults:results error:error];
[FBSDKInternalUtility unregisterTransientObject:self];
}
- (void)webDialog:(FBSDKWebDialog *)webDialog didFailWithError:(NSError *)error
{
if (_webDialog != webDialog) {
return;
}
[self _cleanUp];
[self _handleCompletionWithDialogResults:nil error:error];
[FBSDKInternalUtility unregisterTransientObject:self];
}
- (void)webDialogDidCancel:(FBSDKWebDialog *)webDialog
{
if (_webDialog != webDialog) {
return;
}
[self _cleanUp];
[_delegate gameRequestDialogDidCancel:self];
[FBSDKInternalUtility unregisterTransientObject:self];
}
#pragma mark - Helper Methods
- (void)_cleanUp
{
_dialogIsFrictionless = NO;
}
- (void)_handleCompletionWithDialogResults:(NSDictionary *)results error:(NSError *)error
{
if (!_delegate) {
return;
}
switch (error.code) {
case 0:{
[_delegate gameRequestDialog:self didCompleteWithResults:results];
break;
}
case 4201:{
[_delegate gameRequestDialogDidCancel:self];
break;
}
default:{
[_delegate gameRequestDialog:self didFailWithError:error];
break;
}
}
if (error) {
return;
} else {
}
}
- (NSString *)_actionTypeNameForActionType:(FBSDKGameRequestActionType)actionType
{
switch (actionType) {
case FBSDKGameRequestActionTypeNone:{
return nil;
}
case FBSDKGameRequestActionTypeSend:{
return @"send";
}
case FBSDKGameRequestActionTypeAskFor:{
return @"askfor";
}
case FBSDKGameRequestActionTypeTurn:{
return @"turn";
}
default:{
return nil;
}
}
}
- (NSString *)_filtersNameForFilters:(FBSDKGameRequestFilter)filters
{
switch (filters) {
case FBSDKGameRequestFilterNone:{
return nil;
}
case FBSDKGameRequestFilterAppUsers:{
return @"app_users";
}
case FBSDKGameRequestFilterAppNonUsers:{
return @"app_non_users";
}
default:{
return nil;
}
}
}
@end