NewsBlur/clients/ios/Classes/PremiumViewController.m

400 lines
16 KiB
Mathematica
Raw Normal View History

//
// PremiumViewController.m
// NewsBlur
//
// Created by Samuel Clay on 11/9/17.
// Copyright © 2017 NewsBlur. All rights reserved.
//
#import "PremiumViewController.h"
2017-11-10 17:57:50 -08:00
#import "NewsBlur-Swift.h"
#define kPremium24ProductIdentifier @"newsblur_premium_auto_renew_24"
#define kPremium36ProductIdentifier @"newsblur_premium_auto_renew_36"
@interface PremiumViewController ()
@end
@implementation PremiumViewController
@synthesize appDelegate;
@synthesize productsTable;
@synthesize reasonsTable;
@synthesize spinner;
@synthesize navigationBar;
@synthesize doneButton;
@synthesize restoreButton;
2017-11-10 17:37:31 -08:00
@synthesize freeView;
@synthesize premiumView;
@synthesize confettiView;
@synthesize productsHeight;
2017-11-15 10:47:57 -08:00
@synthesize labelTitle;
@synthesize labelSubtitle;
2017-11-15 11:17:55 -08:00
@synthesize labelPremiumTitle;
@synthesize labelPremiumExpire;
- (void)viewDidLoad {
[super viewDidLoad];
products = [NSArray array];
reasons = @[@[@"Enable every site by going premium", @"g_icn_buffer"],
@[@"Sites updated up to 10x more often", @"g_icn_lightning"],
@[@"River of News (reading by folder)", @"g_icn_folder_black"],
@[@"Search sites and folders", @"g_icn_search_black"],
@[@"Save stories with searchable tags", @"g_icn_tag_black"],
@[@"Privacy options for your blurblog", @"g_icn_privacy"],
@[@"Custom RSS feeds for folders and saved stories", @"g_icn_folder_black"],
@[@"Text view conveniently extracts the story", @"g_icn_textview_black"],
@[@"You feed Shiloh, my poor, hungry dog, for a month", @"g_icn_eating"],
];
2017-11-10 17:37:31 -08:00
UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithTitle: @"Done"
style: UIBarButtonItemStylePlain
target: self
action: @selector(closeDialog:)];
[self.navigationItem setLeftBarButtonItem:cancelButton];
self.productsTable.tableFooterView = [UIView new];
self.reasonsTable.tableFooterView = [self makeShilohCell];
self.productsTable.separatorColor = [UIColor clearColor];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
UIBarButtonItem *restoreButton = [[UIBarButtonItem alloc] initWithTitle: @"Restore"
style: UIBarButtonItemStylePlain
target: self
action: @selector(restorePurchase:)];
[self.navigationItem setRightBarButtonItem:restoreButton];
2017-11-28 16:22:46 -08:00
self.navigationItem.title = @"NewsBlur Premium";
[self loadProducts];
2017-11-15 10:47:57 -08:00
[self updateTheme];
[confettiView setNeedsLayout];
[confettiView startConfetti];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[confettiView setNeedsLayout];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[confettiView stopConfetti];
}
- (void)closeDialog:(id)sender {
[self dismissViewControllerAnimated:YES completion:nil];
}
2017-11-15 10:47:57 -08:00
- (void)updateTheme {
[super updateTheme];
self.productsTable.backgroundColor = UIColorFromRGB(0xf4f4f4);
self.reasonsTable.backgroundColor = UIColorFromRGB(0xf4f4f4);
self.view.backgroundColor = UIColorFromRGB(0xf4f4f4);
self.labelTitle.textColor = UIColorFromRGB(0x0c0c0c);
self.labelSubtitle.textColor = UIColorFromRGB(0x0c0c0c);
2017-11-15 11:17:55 -08:00
self.labelPremiumExpire.textColor = UIColorFromRGB(0x0c0c0c);
self.labelPremiumTitle.textColor = UIColorFromRGB(0x0c0c0c);
self.labelPremiumExpire.shadowColor = UIColorFromRGB(0xf4f4f4);
self.labelPremiumTitle.shadowColor = UIColorFromRGB(0xf4f4f4);
2017-11-15 10:47:57 -08:00
[self.productsTable reloadData];
[self.reasonsTable reloadData];
}
#pragma mark - StoreKit
- (void)loadProducts {
[spinner startAnimating];
productsTable.hidden = YES;
if ([SKPaymentQueue canMakePayments]){
SKProductsRequest *productsRequest = [[SKProductsRequest alloc]
2017-11-10 17:37:31 -08:00
initWithProductIdentifiers:[NSSet setWithObjects:
kPremium24ProductIdentifier,
kPremium36ProductIdentifier, nil]];
productsRequest.delegate = self;
2017-11-10 17:37:31 -08:00
request = productsRequest;
[productsRequest start];
} else {
NSLog(@"User cannot make payments due to parental controls");
}
2017-11-10 17:37:31 -08:00
if (appDelegate.isPremium) {
freeView.hidden = YES;
premiumView.hidden = NO;
self.navigationItem.rightBarButtonItem = nil;
if (appDelegate.premiumExpire != 0) {
NSDate *date = [NSDate dateWithTimeIntervalSince1970:appDelegate.premiumExpire];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"MMMM d, yyyy"];
labelPremiumExpire.text = [NSString stringWithFormat:@"Your premium subscription will renew on %@", [dateFormatter stringFromDate:date]];
} else {
labelPremiumExpire.text = @"Your premium subscription is set to never expire. Whoa!";
}
2017-11-10 17:37:31 -08:00
} else {
freeView.hidden = NO;
premiumView.hidden = YES;
}
}
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
SKProduct *validProduct = nil;
NSUInteger count = [response.products count];
if (count > 0){
products = response.products;
spinner.hidden = YES;
productsTable.hidden = NO;
[productsTable reloadData];
} else if (!validProduct) {
NSLog(@"No products available");
//this is called if your product id is not valid, this shouldn't be called unless that happens.
}
}
- (void)purchase:(SKProduct *)product {
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:payment];
2017-11-10 17:37:31 -08:00
productsTable.hidden = YES;
spinner.hidden = NO;
}
- (IBAction)restorePurchase:(id)sender {
productsTable.hidden = YES;
spinner.hidden = NO;
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}
- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
NSLog(@"received restored transactions: %lu", (unsigned long)queue.transactions.count);
for (SKPaymentTransaction *transaction in queue.transactions) {
if (transaction.transactionState == SKPaymentTransactionStateRestored) {
NSLog(@"Transaction state -> Restored");
//NSString *productID = transaction.payment.productIdentifier;
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
2017-11-10 17:37:31 -08:00
[self finishTransaction:transaction];
return;
}
}
}
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
for (SKPaymentTransaction *transaction in transactions) {
switch (transaction.transactionState) {
case SKPaymentTransactionStatePurchasing: NSLog(@"Transaction state -> Purchasing");
//called when the user is in the process of purchasing, do not add any of your own code here.
break;
case SKPaymentTransactionStatePurchased:
//this is called when the user has successfully purchased the package (Cha-Ching!)
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSLog(@"Transaction state -> Purchased");
2017-11-10 17:37:31 -08:00
[self finishTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
NSLog(@"Transaction state -> Restored");
//add the same code as you did from SKPaymentTransactionStatePurchased here
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
2017-11-10 17:37:31 -08:00
[self finishTransaction:transaction];
break;
case SKPaymentTransactionStateDeferred:
NSLog(@"Transaction state -> Deferred");
case SKPaymentTransactionStateFailed:
NSLog(@"Transaction state -> Failed");
//called when the transaction does not finish
if (transaction.error.code == SKErrorPaymentCancelled) {
NSLog(@"Transaction state -> Cancelled");
//the user cancelled the payment ;(
}
[self informError:@"Transaction failed!"];
productsTable.hidden = NO;
spinner.hidden = YES;
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}
}
}
- (void)finishTransaction:(SKPaymentTransaction *)transaction {
2017-11-10 17:37:31 -08:00
productsTable.hidden = YES;
spinner.hidden = NO;
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [NSData dataWithContentsOfURL:receiptURL];
if (!receipt) {
NSLog(@" No receipt found!");
[self informError:@"No receipt found"];
// return;
2017-11-10 17:37:31 -08:00
}
NSString *urlString = [NSString stringWithFormat:@"%@/profile/save_ios_receipt/",
appDelegate.url];
NSDictionary *params = @{
2018-07-22 17:18:22 -04:00
// @"receipt": [receipt base64EncodedStringWithOptions:0],
@"transaction_identifier": transaction.originalTransaction.transactionIdentifier,
@"product_identifier": transaction.payment.productIdentifier,
2017-11-10 17:37:31 -08:00
};
[appDelegate.networkManager POST:urlString parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"Sent iOS receipt: %@", params);
productsTable.hidden = NO;
spinner.hidden = YES;
NSDictionary *results = (NSDictionary *)responseObject;
appDelegate.isPremium = [[results objectForKey:@"is_premium"] integerValue] == 1;
id premiumExpire = [results objectForKey:@"premium_expire"];
if (premiumExpire && ![premiumExpire isKindOfClass:[NSNull class]] && premiumExpire != 0) {
appDelegate.premiumExpire = [premiumExpire integerValue];
2017-11-10 17:37:31 -08:00
}
2017-11-10 17:57:50 -08:00
[self loadProducts];
[appDelegate reloadFeedsView:YES];
2017-11-10 17:37:31 -08:00
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"Failed to send receipt: %@", params);
productsTable.hidden = NO;
spinner.hidden = YES;
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)task.response;
[self informError:error statusCode:httpResponse.statusCode];
2017-11-10 17:57:50 -08:00
[self loadProducts];
2017-11-10 17:37:31 -08:00
}];
}
#pragma mark - Table Delegate
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == reasonsTable) {
return [reasons count];
} else if (tableView == productsTable) {
return [products count];
}
return 0;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell;
if (tableView == reasonsTable) {
static NSString *ReasonsCellIndentifier = @"PremiumReasonsCell";
cell = [tableView dequeueReusableCellWithIdentifier:ReasonsCellIndentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ReasonsCellIndentifier];
}
2017-11-15 10:47:57 -08:00
cell.backgroundColor = UIColorFromRGB(0xf4f4f4);
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.textLabel.text = reasons[indexPath.row][0];
2017-11-10 17:37:31 -08:00
cell.textLabel.font = [UIFont systemFontOfSize:14.f weight:UIFontWeightLight];
2017-11-15 10:47:57 -08:00
cell.textLabel.textColor = UIColorFromRGB(0x0c0c0c);
2017-11-10 17:37:31 -08:00
cell.textLabel.numberOfLines = 2;
CGSize itemSize = CGSizeMake(18, 18);
cell.imageView.image = [UIImage imageNamed:reasons[indexPath.row][1]];
cell.imageView.contentMode = UIViewContentModeScaleAspectFill;
cell.imageView.clipsToBounds = NO;
UIGraphicsBeginImageContextWithOptions(itemSize, NO, UIScreen.mainScreen.scale);
CGRect imageRect = CGRectMake(0.0, 0.0, itemSize.width, itemSize.height);
[cell.imageView.image drawInRect:imageRect];
cell.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
} else { //} if (tableView == productsTable) {
static NSString *CellIndentifier = @"PremiumCell";
cell = [tableView dequeueReusableCellWithIdentifier:CellIndentifier];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIndentifier];
}
SKProduct *product = products[indexPath.row];
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
2017-11-15 10:47:57 -08:00
cell.backgroundColor = UIColorFromRGB(0xf4f4f4);
cell.textLabel.textColor = UIColorFromRGB(0x203070);
cell.textLabel.numberOfLines = 2;
2017-11-15 10:47:57 -08:00
cell.detailTextLabel.textColor = UIColorFromRGB(0x0c0c0c);
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
[formatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[formatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[formatter setLocale:product.priceLocale];
2017-11-30 15:24:26 -08:00
if (!product.localizedTitle) {
cell.textLabel.text = [NSString stringWithFormat:@"NewsBlur Premium Subscription"];
} else {
cell.textLabel.text = [NSString stringWithFormat:@"%@", product.localizedTitle];
}
2017-11-28 16:22:46 -08:00
cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ per year (%@/month)", [formatter stringFromNumber:product.price], [formatter stringFromNumber:@(round([product.price doubleValue] / 12.f))]];;
UILabel *label = [[UILabel alloc] init];
label.text = @"👉🏽";
label.opaque = NO;
label.backgroundColor = UIColor.clearColor;
label.font = [UIFont systemFontOfSize:18];
CGSize measuredSize = [label.text sizeWithAttributes:@{NSFontAttributeName: label.font}];
label.frame = CGRectMake(0, 0, measuredSize.width, measuredSize.height);
UIGraphicsBeginImageContextWithOptions(label.bounds.size, label.opaque, 0.0);
[label.layer renderInContext:UIGraphicsGetCurrentContext()];
cell.imageView.image = UIGraphicsGetImageFromCurrentImageContext();
}
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 0;
}
- (UIView *)makeShilohCell {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 96+12+12)];
UIImageView *imgView = [[UIImageView alloc] init];
imgView.translatesAutoresizingMaskIntoConstraints = NO;
imgView.tag = 1;
imgView.contentMode = UIViewContentModeScaleAspectFit;
[view addSubview:imgView];
[view addConstraint:[NSLayoutConstraint constraintWithItem:imgView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0]];
[view addConstraint:[NSLayoutConstraint constraintWithItem:imgView attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:view attribute:NSLayoutAttributeTop multiplier:1.0 constant:12]];
2017-11-14 22:21:21 -08:00
[imgView addConstraint:[NSLayoutConstraint constraintWithItem:imgView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:96]];
UIImageView *_imgView = (UIImageView *)[view viewWithTag:1];
_imgView.image = [UIImage imageNamed:@"Shiloh.jpg"];
return view;
}
2017-11-10 17:37:31 -08:00
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (tableView == productsTable) {
[self purchase:products[indexPath.row]];
}
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
@end