mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-06 05:37:48 +00:00
GEEKING
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// FeaturedGamesScreenController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 5/23/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
|
||||
#import "RBBaseViewController.h"
|
||||
|
||||
@interface FeaturedGamesScreenController : RBBaseViewController<UIWebViewDelegate>
|
||||
|
||||
-(void)loadGames;
|
||||
-(void)fetchSiteAlertBanner:(UIWebView*)webView;
|
||||
@end
|
||||
@@ -0,0 +1,910 @@
|
||||
//
|
||||
// FeaturedGamesScreenController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 5/23/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AFHTTPRequestOperation.h"
|
||||
#import "FeaturedGamesScreenController.h"
|
||||
#import "GameSortCarouselViewController.h"
|
||||
#import "GameSortHorizontalViewController.h"
|
||||
#import "GameSortResultsScreenController.h"
|
||||
#import "GameSearchResultsScreenController.h"
|
||||
#import "RBWebGamePreviewScreenController.h"
|
||||
#import "iOSSettingsService.h"
|
||||
#import "MBProgressHUD.h"
|
||||
#import "RBXFunctions.h"
|
||||
#import "RBXUIUtil.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RobloxData.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "RobloxWebUtility.h"
|
||||
#import "Flurry.h"
|
||||
#import "UIPopOverController+Helpers.h"
|
||||
#import "UIViewController+Helpers.h"
|
||||
#import "UIScrollView+Auto.h"
|
||||
#import "FastLog.h"
|
||||
#import "UserInfo.h"
|
||||
#import "LoginManager.h"
|
||||
#import "UIView+Position.h"
|
||||
|
||||
#define VERTICAL_SPACING 20.0f
|
||||
#define BUTTON_WIDTH 170.0f
|
||||
#define BUTTON_HEIGHT 30.0f
|
||||
#define GAME_SORT_CONTROLLER_VIEW_TAG 921
|
||||
|
||||
DYNAMIC_FASTINTVARIABLE(ROMAItemsInCarousel, 0)
|
||||
|
||||
//---METRICS---
|
||||
#define GS_openRobux @"GAMES SCREEN - Open Robux"
|
||||
#define GS_openBuildersClub @"GAMES SCREEN - Open Builders Club"
|
||||
#define GS_openSettings @"GAMES SCREEN - Open Settings"
|
||||
#define GS_openLogout @"GAMES SCREEN - Open Logout"
|
||||
#define GS_openGameDetail @"GAMES SCREEN - Open Game Detail"
|
||||
#define GS_launchGame @"GAMES SCREEN - Launch Game"
|
||||
|
||||
#define GS_searchGames @"GAMES SCREEN - Search Games"
|
||||
#define GS_gameCategoryRecommended @"GAMES SCREEN - See All Recommended"
|
||||
#define GS_gameCategoryPopular @"GAMES SCREEN - See All Popular"
|
||||
#define GS_gameCategoryTopEarning @"GAMES SCREEN - See All Top Earning"
|
||||
#define GS_gameCategoryTopPaid @"GAMES SCREEN - See All Top Paid"
|
||||
#define GS_gameCategoryTopRated @"GAMES SCREEN - See All Top Rated"
|
||||
#define GS_gameCategoryBuildersClub @"GAMES SCREEN - See All Builders Club"
|
||||
|
||||
#pragma mark - OptionListTableViewController class
|
||||
|
||||
@protocol OptionListTableViewControllerDelegate <NSObject>
|
||||
@required
|
||||
- (void) selectedOption:(id)option;
|
||||
@end
|
||||
|
||||
@interface OptionListTableViewController : UITableViewController
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *options;
|
||||
@property (nonatomic, weak) id<OptionListTableViewControllerDelegate> delegate;
|
||||
|
||||
@end
|
||||
|
||||
@implementation OptionListTableViewController
|
||||
|
||||
-(id)initWithStyle:(UITableViewStyle)style
|
||||
{
|
||||
if ([super initWithStyle:style] != nil) {
|
||||
|
||||
//Make row selections persist.
|
||||
self.clearsSelectionOnViewWillAppear = NO;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) setOptions:(NSMutableArray *)options {
|
||||
_options = options;
|
||||
|
||||
//Calculate how tall the view should be by multiplying
|
||||
//the individual row height by the total number of rows.
|
||||
NSInteger rowsCount = [self.options count];
|
||||
NSInteger singleRowHeight = [self.tableView.delegate tableView:self.tableView
|
||||
heightForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
|
||||
NSInteger totalRowsHeight = rowsCount * singleRowHeight;
|
||||
|
||||
//Calculate how wide the view should be by finding how
|
||||
//wide each string is expected to be
|
||||
CGFloat largestLabelWidth = 0;
|
||||
for (id option in self.options) {
|
||||
|
||||
if ([option isKindOfClass:[RBXGameAttribute class]]) {
|
||||
RBXGameAttribute *attribute = (RBXGameAttribute *)option;
|
||||
|
||||
//Checks size of text using the default font for UITableViewCell's textLabel.
|
||||
NSLog(@"Option (Attribute Title): %@", attribute.title);
|
||||
|
||||
// CGSize labelSize = CGSizeMake(100, 30);
|
||||
CGSize labelSize = [attribute.title sizeWithAttributes:
|
||||
@{NSFontAttributeName:
|
||||
[UIFont systemFontOfSize:14.0f]}];
|
||||
if (labelSize.width > largestLabelWidth) {
|
||||
largestLabelWidth = labelSize.width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Add a little padding to the width
|
||||
CGFloat popoverWidth = largestLabelWidth + 100;
|
||||
|
||||
//Set the property to tell the popover container how big this view will be.
|
||||
self.preferredContentSize = CGSizeMake(popoverWidth, totalRowsHeight);
|
||||
}
|
||||
|
||||
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
return 50;
|
||||
}
|
||||
|
||||
#pragma mark - Table view data source
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
||||
{
|
||||
// Return the number of sections.
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
// Return the number of rows in the section.
|
||||
return [self.options count];
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
static NSString *CellIdentifier = @"Cell";
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
|
||||
if (cell == nil) {
|
||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
|
||||
}
|
||||
|
||||
id data = [self.options objectAtIndex:indexPath.row];
|
||||
|
||||
// Configure the cell...
|
||||
if ([data isKindOfClass:[RBXGameAttribute class]]) {
|
||||
RBXGameAttribute *attribute = (RBXGameAttribute *)data;
|
||||
cell.textLabel.text = attribute.title;
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - Table view delegate
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
|
||||
id data = [self.options objectAtIndex:indexPath.row];
|
||||
|
||||
// Configure the cell...
|
||||
if ([data isKindOfClass:[RBXGameAttribute class]]) {
|
||||
//Notify the delegate if it exists.
|
||||
if (_delegate != nil) {
|
||||
[_delegate selectedOption:data];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface iPadDropdownButtonView : UIView
|
||||
|
||||
- (void) deselectedState;
|
||||
- (void) selectedState;
|
||||
|
||||
@end
|
||||
|
||||
@interface iPadDropdownButtonView ()
|
||||
|
||||
@property (nonatomic, strong) UIButton *button;
|
||||
@property (nonatomic, strong) UIImageView *imageView;
|
||||
|
||||
@property (nonatomic, strong) UIImage *arrowDark;
|
||||
@property (nonatomic, strong) UIImage *arrowLite;
|
||||
|
||||
@end
|
||||
|
||||
@implementation iPadDropdownButtonView
|
||||
|
||||
- (instancetype) initWithFrame:(CGRect)frame title:(NSString *)title {
|
||||
NSLog(@"frame: %@", NSStringFromCGRect(frame));
|
||||
|
||||
if (self == [super initWithFrame:frame]) {
|
||||
// init here
|
||||
}
|
||||
|
||||
// Round the edges of the view
|
||||
self.layer.cornerRadius = 5.0;
|
||||
|
||||
CGFloat w = frame.size.width;
|
||||
CGFloat h = frame.size.height;
|
||||
|
||||
// Create the button
|
||||
// Be careful when adjusting the width of the imageview and the button.
|
||||
self.button = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, w * .87, h)];
|
||||
self.button.clipsToBounds = YES;
|
||||
[self.button setTitle:title forState:UIControlStateNormal];
|
||||
[self addSubview:self.button];
|
||||
|
||||
// Create the drop down indicator arrow
|
||||
// Be careful when adjusting the width of the imageview and the button.
|
||||
self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(CGRectGetMaxX(self.button.frame), 10, w * .07, 10)];
|
||||
[self addSubview:self.imageView];
|
||||
|
||||
// Enable user interaction on the view so that the button is able to receive tap events
|
||||
[self setUserInteractionEnabled:YES];
|
||||
|
||||
// Further setup
|
||||
[self setupView];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) setupView {
|
||||
// Left align the text within the button
|
||||
self.button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
|
||||
|
||||
// Adjust the left content margin within the button
|
||||
self.button.contentEdgeInsets = UIEdgeInsetsMake(0, 10, 0, 0);
|
||||
|
||||
// Round the edges of the button
|
||||
self.button.layer.cornerRadius = 5.0;
|
||||
|
||||
[RBXUIUtil increaseTappableAreaOfButton:self.button];
|
||||
|
||||
// Setup arrow images
|
||||
self.arrowDark = [UIImage imageNamed:@"down_arrow_dark.png"];
|
||||
self.arrowLite = [UIImage imageNamed:@"down_arrow_light.png"];
|
||||
|
||||
// Put the button in the default deselected state
|
||||
[self deselectedState];
|
||||
}
|
||||
|
||||
- (void) deselectedState {
|
||||
|
||||
[self.imageView setImage:self.arrowDark];
|
||||
[self.imageView setContentMode:UIViewContentModeScaleAspectFit];
|
||||
|
||||
[self.button setBackgroundColor:[UIColor whiteColor]];
|
||||
[self.button setTitleColor:[RobloxTheme colorGray2] forState:UIControlStateNormal];
|
||||
|
||||
[self setBackgroundColor:[UIColor whiteColor]];
|
||||
}
|
||||
|
||||
- (void) selectedState {
|
||||
|
||||
[self.imageView setImage:self.arrowLite];
|
||||
[self.imageView setContentMode:UIViewContentModeScaleAspectFit];
|
||||
|
||||
[self.button setBackgroundColor:[RobloxTheme colorBlue2]];
|
||||
[self.button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
|
||||
|
||||
[self setBackgroundColor:[RobloxTheme colorBlue2]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#pragma mark - FeaturedGamesScreenController class
|
||||
|
||||
@interface FeaturedGamesScreenController () <UISearchBarDelegate, UISearchDisplayDelegate, OptionListTableViewControllerDelegate, UIPopoverControllerDelegate>
|
||||
|
||||
@property (nonatomic, strong) OptionListTableViewController *optionListTableViewController;
|
||||
@property (nonatomic, strong) UIPopoverController *optionListPopoverController;
|
||||
@property (nonatomic, strong) UIScrollView *scrollView;
|
||||
|
||||
@property (nonatomic, strong) RBXGameSort *selectedGameSort;
|
||||
@property (nonatomic, strong) RBXGameGenre *selectedGameGenre;
|
||||
|
||||
@property (nonatomic, strong) RBXGameSort *lastSelectedSort;
|
||||
@property (nonatomic, strong) RBXGameGenre *lastSelectedGenre;
|
||||
|
||||
@property (nonatomic, strong) NSMutableArray *allSorts;
|
||||
@property (nonatomic, strong) NSArray *allGenres;
|
||||
|
||||
@property (nonatomic, strong) NSArray *sortsData;
|
||||
@property (nonatomic) CGFloat bottom;
|
||||
|
||||
@property (nonatomic, strong) iPadDropdownButtonView *sortsView;
|
||||
@property (nonatomic, strong) iPadDropdownButtonView *genresView;
|
||||
|
||||
@property (nonatomic, strong) MBProgressHUD *activityIndicator;
|
||||
|
||||
@end
|
||||
|
||||
@implementation FeaturedGamesScreenController
|
||||
{
|
||||
//Views and Controllers
|
||||
UIWebView* _siteAlertWebView;
|
||||
GameSortCarouselViewController* _featuredGamesController;
|
||||
NSMutableArray* _gamesCategoriesControllers;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - View Functions
|
||||
|
||||
- (void) viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
//Initialize and style the view
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
[self addSearchIconWithSearchType:SearchResultGames andFlurryEvent:nil];
|
||||
self.view.backgroundColor = [RobloxTheme lightBackground];
|
||||
self.navigationItem.title = NSLocalizedString(@"GameWord", nil);
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
|
||||
//initialize some variables
|
||||
_gamesCategoriesControllers = [NSMutableArray array];
|
||||
|
||||
//check if the user is logged in to determine if we should add icons or observers
|
||||
if ([UserInfo CurrentPlayer].userLoggedIn)
|
||||
[self addRobuxIconWithFlurryEvent:GS_openRobux
|
||||
andBCIconWithFlurryEvent:GS_openBuildersClub];
|
||||
else
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(addIcons:) name:RBX_NOTIFY_LOGIN_SUCCEEDED object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(refreshViews) name:RBX_NOTIFY_LOGIN_SUCCEEDED object:nil];
|
||||
|
||||
// Site alert banner webview
|
||||
iOSSettingsService* iOSSettings = [[RobloxWebUtility sharedInstance] getCachediOSSettings];
|
||||
if (iOSSettings->GetValueEnableSiteAlertBanner() )
|
||||
{
|
||||
_siteAlertWebView = [[UIWebView alloc]initWithFrame:CGRectMake(0.0f, 10.0f, 1024.0f, 30.0f)];
|
||||
_siteAlertWebView.hidden = true;
|
||||
_siteAlertWebView.delegate = self;
|
||||
[self fetchSiteAlertBanner:_siteAlertWebView];
|
||||
|
||||
[self.scrollView addSubview:_siteAlertWebView];
|
||||
}
|
||||
|
||||
// Scroll view container
|
||||
// 2015.0902 - Adjust the Y position of the scroll view to acommodate a possible siteAlertWebView and the sort/genre buttons
|
||||
float bannerBottom = (_siteAlertWebView.hidden == NO) ? _siteAlertWebView.bottom + BUTTON_HEIGHT + VERTICAL_SPACING : 0;
|
||||
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, bannerBottom, self.view.width, self.view.height)];
|
||||
[_scrollView setScrollEnabled:YES];
|
||||
_scrollView.clipsToBounds = NO; // <--- ????
|
||||
[self.view addSubview:_scrollView];
|
||||
|
||||
[self refreshViews];
|
||||
}
|
||||
|
||||
- (void) showActivityIndicator {
|
||||
|
||||
if ([RBXFunctions isEmpty:self.activityIndicator]) {
|
||||
self.activityIndicator = [[MBProgressHUD alloc] initWithView:self.view];
|
||||
[self.view addSubview:self.activityIndicator];
|
||||
}
|
||||
|
||||
FeaturedGamesScreenController __weak *weakSelf = self;
|
||||
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[weakSelf.activityIndicator show:YES];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) hideActivityIndicator {
|
||||
FeaturedGamesScreenController __weak *weakSelf = self;
|
||||
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[weakSelf.activityIndicator hide:YES];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) viewDidLayoutSubviews {
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
_scrollView.frame = self.view.bounds;
|
||||
|
||||
|
||||
[_scrollView setContentSizeForDirection:UIScrollViewDirectionVertical];
|
||||
}
|
||||
|
||||
- (void) viewWillAppear:(BOOL)animated {
|
||||
[[RBXEventReporter sharedInstance] reportScreenLoaded:RBXAContextGames];
|
||||
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
[super viewWillAppear:animated];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark - Initialization Functions
|
||||
|
||||
- (void) fetchSiteAlertBanner:(UIWebView*)webView {
|
||||
NSString* urlAsString = [NSString stringWithFormat:@"%@/alerts/alert-info",
|
||||
[RobloxInfo getApiBaseUrl]];
|
||||
NSLog(@"%@", urlAsString);
|
||||
NSURL *url = [[NSURL alloc] initWithString:urlAsString];
|
||||
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
|
||||
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
|
||||
|
||||
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
|
||||
operation.responseSerializer = [AFJSONResponseSerializer serializer];
|
||||
|
||||
FeaturedGamesScreenController __weak *weakSelf = self;
|
||||
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
|
||||
{
|
||||
NSDictionary* jsonResults = (NSDictionary*) responseObject;
|
||||
if (jsonResults != nil)
|
||||
{
|
||||
bool visible = [[jsonResults valueForKey:@"IsVisible"] intValue] > 0;
|
||||
|
||||
if (visible)
|
||||
{
|
||||
NSString* html = [jsonResults valueForKey:@"Text"];
|
||||
|
||||
// If the string starts with [alertColor, we know it is not an Announcement, and will have its own bg color
|
||||
if ([html rangeOfString:@"[alertColor"].location == 0 )
|
||||
{
|
||||
NSError *error = nil;
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\Q[alertColor=\\E(.*?)\\Q]\\E" options:NSRegularExpressionCaseInsensitive error:&error];
|
||||
html = [regex stringByReplacingMatchesInString:html options:0 range:NSMakeRange(0, [html length]) withTemplate:@"<body style='font-family: sans-serif; font-style: bold; color: #FFFFFF; border-bottom: 2px solid #333333; text-align:center; background-color: $1;'>"];
|
||||
html = [NSString stringWithFormat:@"%@%s", html, "</body>"];
|
||||
}
|
||||
else
|
||||
html = [NSString stringWithFormat:@"%s%@%s", "<body style='font-family: sans-serif; font-style: bold; color: #FFFFFF; border-bottom: 2px solid #333333; text-align:center; background-color: #FF3030;'>", html, "</body>"];
|
||||
|
||||
webView.hidden = false;
|
||||
[webView loadHTMLString:html baseURL:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
webView.hidden = true;
|
||||
}
|
||||
|
||||
[weakSelf refreshViews];
|
||||
}
|
||||
}
|
||||
failure:^(AFHTTPRequestOperation *operation, NSError *error)
|
||||
{
|
||||
NSLog(@"SiteAlert request failed.");
|
||||
webView.hidden = true;
|
||||
}];
|
||||
[operation start];
|
||||
}
|
||||
|
||||
- (void) loadGames {
|
||||
// 2015.0902 ajain - If the selected sort is the Default sort, then we use the loadGames method to show the original arrangement of games,
|
||||
// which is five rows of games of different sort types based on genre -- Popular, Top Earning, Top Rated, Recommended, and Featured.
|
||||
|
||||
__weak FeaturedGamesScreenController* weakSelf = self;
|
||||
|
||||
// Update the "bottom" variable which is used as a Y-axis reference point to layout content in the scrollview
|
||||
self.bottom = CGRectGetMaxY(self.sortsView.frame) + VERTICAL_SPACING;
|
||||
|
||||
//initialize the carousel
|
||||
if( DFInt::ROMAItemsInCarousel > 0 )
|
||||
{
|
||||
if (!_featuredGamesController)
|
||||
{
|
||||
_featuredGamesController = [[GameSortCarouselViewController alloc] initWithFrame:CGRectMake(0.0f, CGRectGetMaxY(self.sortsView.frame) + 50 + VERTICAL_SPACING, _scrollView.width, 180.0f)];
|
||||
_featuredGamesController.gameSelectedHandler = ^(RBXGameData* gameData) { [weakSelf showGameDetails:gameData]; };
|
||||
}
|
||||
|
||||
if ([RBXFunctions isEmpty:_featuredGamesController.view.superview])
|
||||
{
|
||||
[_scrollView addSubview:_featuredGamesController.view];
|
||||
}
|
||||
|
||||
self.bottom = CGRectGetMaxY(_featuredGamesController.carousel.frame) + 50 + VERTICAL_SPACING;
|
||||
}
|
||||
|
||||
[self showActivityIndicator];
|
||||
|
||||
//loop through all the controllers and reload the data
|
||||
[RobloxData fetchDefaultSorts:0 completion:^(NSArray *sorts)
|
||||
{
|
||||
[weakSelf hideActivityIndicator];
|
||||
|
||||
_sortsData = sorts;
|
||||
|
||||
// 2015.0902 ajain - We call reloadGameSortsCollectionController to layout the five rows of sorts based on genre.
|
||||
[weakSelf reloadGameSortsCollectionController];
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
- (void) reloadGameSortsCollectionController {
|
||||
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
|
||||
[self resetViews];
|
||||
|
||||
//reference frame is out of scope and read only, copy it locally and continue
|
||||
CGFloat localBottom = self.bottom;
|
||||
|
||||
int i = 0;
|
||||
while (i < _sortsData.count || i < _gamesCategoriesControllers.count)
|
||||
{
|
||||
if (i < _sortsData.count && i < _gamesCategoriesControllers.count)
|
||||
{
|
||||
//if we have a sort AND a category, update the view with the new sort data
|
||||
GameSortHorizontalViewController* gshvc = _gamesCategoriesControllers[i];
|
||||
[gshvc setAnalyticsLocation:RBXALocationGames andContext:RBXAContextMain];
|
||||
[gshvc setSort:_sortsData[i] andGenre:self.selectedGameGenre];
|
||||
}
|
||||
else if (i < _sortsData.count)
|
||||
{
|
||||
FeaturedGamesScreenController __weak *weakSelf = self;
|
||||
|
||||
//we have more data than sorts, add a new game category controller
|
||||
GameSortHorizontalViewController* controller = [[GameSortHorizontalViewController alloc] initWithNibName:@"GameSortHorizontalViewController" bundle:nil];
|
||||
controller.startIndex = (i == 0) ? DFInt::ROMAItemsInCarousel : 0;
|
||||
[controller setAnalyticsLocation:RBXALocationGames andContext:RBXAContextMain];
|
||||
controller.gameSelectedHandler = ^(RBXGameData* gameData) {
|
||||
[weakSelf showGameDetails:gameData];
|
||||
};
|
||||
controller.seeAllHandler = ^(NSNumber* sortID) {
|
||||
[weakSelf showFullListForCategory:sortID];
|
||||
};
|
||||
[controller.view setFrame:CGRectMake(0, localBottom, _scrollView.width, controller.view.height)];
|
||||
[controller setSort:self.sortsData[i] andGenre:self.selectedGameGenre];
|
||||
|
||||
//update the bottom of the reference frame
|
||||
localBottom = controller.view.bottom + VERTICAL_SPACING;
|
||||
|
||||
// set a tag so we can identify these views for removal when we re-load sorts or filters
|
||||
controller.view.tag = GAME_SORT_CONTROLLER_VIEW_TAG;
|
||||
|
||||
//keep a reference to the controller and add it to the screen
|
||||
[_gamesCategoriesControllers addObject:controller];
|
||||
[_scrollView addSubview:controller.view];
|
||||
|
||||
}
|
||||
else// if (i < _gamesCategoriesControllers.count)
|
||||
{
|
||||
//we have more game category controllers than data, remove the category
|
||||
GameSortHorizontalViewController* controller = _gamesCategoriesControllers[i];
|
||||
[controller removeFromParentViewController];
|
||||
[_gamesCategoriesControllers removeObjectAtIndex:i];
|
||||
}
|
||||
|
||||
//increment the counter and loop
|
||||
i++;
|
||||
}
|
||||
|
||||
[_featuredGamesController setGameSort:self.selectedGameSort];
|
||||
|
||||
[self updateDropDownButtonTitles];
|
||||
|
||||
//update the scrollview size, just to be safe
|
||||
[_scrollView setContentSizeForDirection:UIScrollViewDirectionVertical];
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - Delegate and Notification Functions
|
||||
|
||||
- (void) addIcons:(NSNotification*) notification {
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[self addRobuxIconWithFlurryEvent:GS_openRobux
|
||||
andBCIconWithFlurryEvent:GS_openBuildersClub];
|
||||
[self loadGames];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBX_NOTIFY_LOGIN_SUCCEEDED object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeIcons:) name:RBX_NOTIFY_LOGGED_OUT object:nil];
|
||||
}];
|
||||
}
|
||||
-(void) removeIcons:(NSNotification*) notification {
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[self removeRobuxAndBCIcons];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBX_NOTIFY_LOGGED_OUT object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(addIcons:) name:RBX_NOTIFY_LOGIN_SUCCEEDED object:nil];
|
||||
}];
|
||||
}
|
||||
- (BOOL) webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType {
|
||||
//In case someone clicks on the button in the site banner, open up the result in the browser
|
||||
if ( inType == UIWebViewNavigationTypeLinkClicked ) {
|
||||
[[UIApplication sharedApplication] openURL:[inRequest URL]];
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
#pragma mark - Navigation Functions
|
||||
|
||||
- (void) showGameDetails:(RBXGameData*) gameData {
|
||||
[Flurry logEvent:GS_openGameDetail];
|
||||
|
||||
[self pushGameDetailWithGameData:gameData];
|
||||
}
|
||||
- (void) showFullListForCategory:(NSNumber*)selectedSort {
|
||||
[self performSegueWithIdentifier:@"sortResults" sender:selectedSort];
|
||||
|
||||
//Report the flurry event
|
||||
NSInteger sortID = selectedSort.integerValue;
|
||||
switch (sortID)
|
||||
{
|
||||
case (RBXGameSortTypeRecommended): { [Flurry logEvent:GS_gameCategoryRecommended]; } break;
|
||||
case (RBXGameSortTypePopular): { [Flurry logEvent:GS_gameCategoryPopular]; } break;
|
||||
case (RBXGameSortTypeTopEarning): { [Flurry logEvent:GS_gameCategoryTopEarning]; } break;
|
||||
case (RBXGameSortTypeTopPaid): { [Flurry logEvent:GS_gameCategoryTopRated]; } break;
|
||||
case (RBXGameSortTypeBuildersClub): { [Flurry logEvent:GS_gameCategoryBuildersClub];} break;
|
||||
}
|
||||
|
||||
}
|
||||
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
||||
if( [segue.identifier isEqualToString:@"sortResults"] )
|
||||
{
|
||||
GameSortResultsScreenController* controller = segue.destinationViewController;
|
||||
controller.selectedSort = (NSNumber*) sender;
|
||||
}
|
||||
else if( [segue.identifier isEqualToString:@"searchResults"] )
|
||||
{
|
||||
GameSearchResultsScreenController* controller = segue.destinationViewController;
|
||||
controller.keywords = (NSString*) sender;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - OptionListTableViewControllerDelegate method
|
||||
|
||||
- (void) selectedOption:(id)option {
|
||||
NSLog(@"the selected option is %@", option);
|
||||
|
||||
if ([option isKindOfClass:[RBXGameSort class]]) {
|
||||
self.selectedGameSort = (RBXGameSort *)option;
|
||||
} else if ([option isKindOfClass:[RBXGameGenre class]]) {
|
||||
self.selectedGameGenre = (RBXGameGenre *)option;
|
||||
}
|
||||
|
||||
NSLog(@"Selected Sort: %@", self.selectedGameSort.title);
|
||||
NSLog(@"Selected Genre: %@", self.selectedGameGenre.title);
|
||||
|
||||
[self updateGames];
|
||||
|
||||
if (self.optionListPopoverController) {
|
||||
[self.optionListPopoverController dismissPopoverAnimated:YES];
|
||||
self.optionListPopoverController = nil;
|
||||
}
|
||||
|
||||
[self updateDropDownButtonTitles];
|
||||
}
|
||||
|
||||
#pragma mark - Drop Down List Methods
|
||||
|
||||
- (void) createDropDownMenuButtons {
|
||||
CGFloat yAdj = (_siteAlertWebView.hidden == NO) ? CGRectGetMaxY(_siteAlertWebView.frame) : 0;
|
||||
|
||||
if ([RBXFunctions isEmpty:self.sortsView]) {
|
||||
self.sortsView = [[iPadDropdownButtonView alloc] initWithFrame:CGRectMake(20, yAdj + 10, BUTTON_WIDTH, BUTTON_HEIGHT) title:@"Default"];
|
||||
[self.scrollView addSubview:self.sortsView];
|
||||
}
|
||||
[self.sortsView.button addTarget:self action:@selector(dropDownListButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
|
||||
self.sortsView.tag = 111;
|
||||
self.sortsView.button.showsTouchWhenHighlighted = YES;
|
||||
|
||||
if ([RBXFunctions isEmpty:self.genresView]) {
|
||||
self.genresView = [[iPadDropdownButtonView alloc] initWithFrame:CGRectMake(CGRectGetMaxX(self.sortsView.frame) + 10, yAdj + 10, BUTTON_WIDTH, BUTTON_HEIGHT) title:@"All"];
|
||||
[self.scrollView addSubview:self.genresView];
|
||||
}
|
||||
[self.genresView.button addTarget:self action:@selector(dropDownListButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
|
||||
self.genresView.tag = 112;
|
||||
self.genresView.button.showsTouchWhenHighlighted = YES;
|
||||
|
||||
// 2015.0902 ajain - Initialize the selected game attribute values
|
||||
self.selectedGameSort = [RBXGameSort DefaultSort];
|
||||
self.selectedGameGenre = self.allGenres.firstObject;
|
||||
|
||||
[self updateDropDownButtonTitles];
|
||||
|
||||
// Update the "bottom" variable which is used as a Y-axis reference point to layout content in the scrollview
|
||||
self.bottom = CGRectGetMaxY(self.sortsView.frame) + VERTICAL_SPACING;
|
||||
}
|
||||
|
||||
- (void) updateDropDownButtonTitles {
|
||||
[self.sortsView.button setTitle:self.selectedGameSort.title forState:UIControlStateNormal];
|
||||
[self.genresView.button setTitle:self.selectedGameGenre.title forState:UIControlStateNormal];
|
||||
|
||||
if (self.selectedGameSort.sortID.integerValue == RBXGameSortTypeDefault) {
|
||||
[self.sortsView deselectedState];
|
||||
}
|
||||
|
||||
RBXGameGenre *allGenre = self.allGenres.firstObject;
|
||||
if (self.selectedGameGenre.genreID.integerValue == allGenre.genreID.integerValue) {
|
||||
[self.genresView deselectedState];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) dropDownListButtonTapped:(id)sender {
|
||||
if ([sender isKindOfClass:[UIButton class]]) {
|
||||
|
||||
UIView *containerView = ((UIView *)sender).superview;
|
||||
|
||||
if (containerView == self.sortsView || containerView == self.genresView) {
|
||||
if ([RBXFunctions isEmpty:self.optionListTableViewController]) {
|
||||
self.optionListTableViewController = [[OptionListTableViewController alloc] initWithStyle:UITableViewStylePlain];
|
||||
self.optionListTableViewController.delegate = self;
|
||||
}
|
||||
|
||||
if (containerView == self.sortsView) {
|
||||
[self.sortsView selectedState];
|
||||
|
||||
NSMutableArray *updatedOptions = [NSMutableArray arrayWithArray:self.allSorts];
|
||||
if (![RBXFunctions isEmpty:self.selectedGameSort]) {
|
||||
[updatedOptions insertObject:[RBXGameSort DefaultSort] atIndex:0];
|
||||
}
|
||||
|
||||
self.optionListTableViewController.options = updatedOptions;
|
||||
} else if (containerView == self.genresView) {
|
||||
[self.genresView selectedState];
|
||||
|
||||
self.optionListTableViewController.options = [self.allGenres mutableCopy];
|
||||
}
|
||||
|
||||
if ([RBXFunctions isEmpty:self.optionListPopoverController]) {
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
// Setup the UIPopoverController
|
||||
self.optionListPopoverController = [[UIPopoverController alloc] initWithContentViewController:self.optionListTableViewController];
|
||||
[self.optionListPopoverController presentPopoverFromRect:containerView.frame inView:self.view permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
|
||||
self.optionListPopoverController.delegate = self;
|
||||
|
||||
// Assign the passthrough views - the views that can receive touches even when a popover is displayed
|
||||
[self.optionListPopoverController setPassthroughViews:@[self.sortsView, self.genresView]];
|
||||
|
||||
// Refresh the options available
|
||||
[self.optionListTableViewController.tableView reloadData];
|
||||
}];
|
||||
} else {
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[self.optionListPopoverController dismissPopoverAnimated:NO completionHandler:^{
|
||||
[self updateDropDownButtonTitles];
|
||||
|
||||
// Recursively call the method when a dropdown button is pressed while a popover is being displayed
|
||||
[self dropDownListButtonTapped:sender];
|
||||
}];
|
||||
|
||||
self.optionListPopoverController = nil;
|
||||
|
||||
// Important to nil out the ListTableViewController otherwise the tableviewcontroller will be reused and you will get
|
||||
// weird refresh issues when toggling between two different drop down menus.
|
||||
self.optionListTableViewController = nil;
|
||||
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 2015.0902 ajain - Restore the previous game attribute selections
|
||||
- (void) revertSelectedAttributes {
|
||||
self.selectedGameSort = self.lastSelectedSort;
|
||||
self.selectedGameGenre = self.lastSelectedGenre;
|
||||
}
|
||||
|
||||
// 2015.0902 ajain - Update the game attribute's with the latest valid selections
|
||||
- (void) updateLastSelectedAttributes {
|
||||
self.lastSelectedSort = self.selectedGameSort;
|
||||
self.lastSelectedGenre = self.selectedGameGenre;
|
||||
}
|
||||
|
||||
- (void) updateGames
|
||||
{
|
||||
NSUInteger maxNumItems = 40;
|
||||
|
||||
if (self.selectedGameSort.sortID.integerValue == @(RBXGameSortTypeDefault).integerValue) {
|
||||
[self loadGames];
|
||||
return;
|
||||
}
|
||||
|
||||
[self showActivityIndicator];
|
||||
|
||||
FeaturedGamesScreenController __weak *weakSelf = self;
|
||||
|
||||
[RobloxData fetchGameListWithSortID:self.selectedGameSort.sortID
|
||||
genreID:self.selectedGameGenre.genreID
|
||||
playerID:[UserInfo CurrentPlayer].userId
|
||||
fromIndex:0
|
||||
numGames:maxNumItems
|
||||
thumbSize:RBX_SCALED_DEVICE_SIZE(455, 256)
|
||||
completion:^(NSArray *games)
|
||||
{
|
||||
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[weakSelf hideActivityIndicator];
|
||||
|
||||
NSLog(@"gamesArray count for sort:%@ : %ld", self.selectedGameSort.title, (unsigned long)games.count);
|
||||
|
||||
if ([RBXFunctions isEmpty:games]) {
|
||||
|
||||
NSMutableString *message = [NSMutableString stringWithFormat:@"There are no games"];
|
||||
if (![RBXFunctions isEmpty:weakSelf.selectedGameSort]) {
|
||||
NSString *sortDesc = [NSString stringWithFormat:@"\nin Sort: %@", weakSelf.selectedGameSort.title];
|
||||
[message appendString:sortDesc];
|
||||
}
|
||||
|
||||
if (![RBXFunctions isEmpty:weakSelf.selectedGameGenre]) {
|
||||
NSString *genreDesc = [NSString stringWithFormat:@" in Genre: %@", weakSelf.selectedGameGenre.title];
|
||||
[message appendString:genreDesc];
|
||||
}
|
||||
|
||||
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Ooops"
|
||||
message:message
|
||||
delegate:nil
|
||||
cancelButtonTitle:@"OK"
|
||||
otherButtonTitles:nil];
|
||||
[alert show];
|
||||
|
||||
// 2015.0902 ajain - Revert the newly selected attributes to what they were before because the new selections are not valid.
|
||||
// Must be done AFTER showing the alert view, so the alert view displays the incorrect values.
|
||||
[weakSelf revertSelectedAttributes];
|
||||
|
||||
return;
|
||||
|
||||
} else {
|
||||
|
||||
// 2015.0902 ajain - Update the last selections to what the previous selections were
|
||||
[weakSelf updateLastSelectedAttributes];
|
||||
[weakSelf updateDropDownButtonTitles];
|
||||
}
|
||||
|
||||
[weakSelf reloadGameCollectionFullScreenWithGames:games];
|
||||
}];
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) reloadGameCollectionFullScreenWithGames:(NSArray *)games {
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
//reference frame is out of scope and read only, copy it locally and continue
|
||||
CGFloat localBottom = self.bottom;
|
||||
|
||||
// 2015.0902 ajain - repurposed the reloadGameSortsCollectionController method
|
||||
|
||||
[self resetViews];
|
||||
|
||||
NSNumber *playerID = [UserInfo CurrentPlayer].userId;
|
||||
GameSortResultsScreenController *controller = [GameSortResultsScreenController gameSortResultsScreenControllerWithSort:self.selectedGameSort genre:self.selectedGameGenre playerID:playerID];
|
||||
|
||||
//we have more data than sorts, add a new game category controller
|
||||
[controller.view setFrame:CGRectMake(0, localBottom, _scrollView.width, controller.view.height)];
|
||||
|
||||
//update the bottom of the reference frame
|
||||
localBottom = controller.view.bottom + VERTICAL_SPACING;
|
||||
|
||||
// set a tag so we can identify these views for removal when we re-load sorts or filters
|
||||
controller.view.tag = GAME_SORT_CONTROLLER_VIEW_TAG;
|
||||
|
||||
//keep a reference to the controller and add it to the screen
|
||||
[_gamesCategoriesControllers addObject:controller];
|
||||
[_scrollView addSubview:controller.view];
|
||||
|
||||
//update the scrollview size, just to be safe
|
||||
[_scrollView setContentSizeForDirection:UIScrollViewDirectionVertical];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) loadGameAttributeLists {
|
||||
[RobloxData fetchAllSorts:^(NSArray *sorts) {
|
||||
self.allSorts = [NSMutableArray arrayWithArray:sorts];
|
||||
}];
|
||||
|
||||
// 2015.0904 ajain - The implementation of this method [RBXGameGenre allGenres] is a stop gap feature until the
|
||||
// web team is able to make an endpoint that returns the current genres. At that time, we will create a
|
||||
// proper [RobloxData fetchAllGenres:] method.
|
||||
self.allGenres = [RBXGameGenre allGenres];
|
||||
}
|
||||
|
||||
- (void) refreshViews {
|
||||
// 2015.0902 - Adjust the Y position of the scroll view to acommodate a possible siteAlertWebView and the sort/genre buttons
|
||||
float bannerBottom = (_siteAlertWebView.hidden == NO) ? _siteAlertWebView.bottom + BUTTON_HEIGHT + VERTICAL_SPACING : 0;
|
||||
|
||||
// Update scrollview frame to adjust for siteAlertView
|
||||
[RBXUIUtil view:_scrollView setOrigin:CGPointMake(0, bannerBottom)];
|
||||
[RBXUIUtil view:_scrollView setSize:CGSizeMake(self.view.width, self.view.width)];
|
||||
|
||||
[self loadGameAttributeLists];
|
||||
[self createDropDownMenuButtons];
|
||||
[self loadGames];
|
||||
}
|
||||
|
||||
- (void) resetViews {
|
||||
|
||||
[_gamesCategoriesControllers removeAllObjects];
|
||||
for (UIView *subview in _scrollView.subviews) {
|
||||
if (subview.tag == GAME_SORT_CONTROLLER_VIEW_TAG) {
|
||||
[subview removeFromSuperview];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#pragma mark - OptionListPopoverDelegate method
|
||||
|
||||
- (BOOL)popoverControllerShouldDismissPopover:(UIPopoverController *)popoverController {
|
||||
[self updateDropDownButtonTitles];
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) popoverControllerDidDismissPopover:(UIPopoverController *)popoverController {
|
||||
[self updateDropDownButtonTitles];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// FriendsRequestsDetailController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 6/19/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface FriendsRequestsDetailController : UIViewController
|
||||
|
||||
- (void) loadData;
|
||||
- (void) refreshRequests:(UIRefreshControl*)refreshControl;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,353 @@
|
||||
//
|
||||
// FriendsRequestsDetailController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 6/19/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "FriendsRequestsDetailController.h"
|
||||
#import "RobloxImageView.h"
|
||||
#import "RobloxData.h"
|
||||
#import "UserInfo.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "RBProfileViewController.h"
|
||||
#import "Flurry.h"
|
||||
#include "RBActivityIndicatorView.h"
|
||||
#import "UIView+Position.h"
|
||||
|
||||
#define AVATAR_SIZE CGSizeMake(110, 110)
|
||||
#define ITEMS_PER_REQUEST 1000
|
||||
#define SPINNER_ICON_FRAME CGRectMake(0, 0, 32, 32)
|
||||
|
||||
//---METRICS---
|
||||
#define FRDC_acceptFriendRequest @"FRIEND REQUEST SCREEN - Accept Friend Request"
|
||||
#define FRDC_declineFriendRequest @"FRIEND REQUEST SCREEN - Decline Friend Reqeust"
|
||||
#define FRDC_refreshList @"FRIEND REQUEST SCREEN - Refresh Requests List"
|
||||
#define FRDC_openFriendProfile @"FRIEND REQUEST SCREEN - Open Friend Request Profile"
|
||||
|
||||
#pragma mark FriendRequestTableCell
|
||||
@interface FriendRequestTableCell : UITableViewCell
|
||||
@property (strong, nonatomic) RBXFriendInfo* friendInfo;
|
||||
@property (strong, nonatomic) IBOutlet RobloxImageView *friendAvatar;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *friendNameLabel;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *friendPointsLabel;
|
||||
@property (strong, nonatomic) IBOutlet UIButton *acceptButton;
|
||||
@property (strong, nonatomic) IBOutlet UIButton *doNotAcceptButton;
|
||||
@end
|
||||
|
||||
@implementation FriendRequestTableCell
|
||||
- (void)setFriendInfo:(RBXFriendInfo *)friendInfo
|
||||
{
|
||||
_friendInfo = friendInfo;
|
||||
|
||||
// Stylize items
|
||||
self.friendNameLabel.font = [UIFont fontWithName:@"SourceSansPro-Semibold" size:14];
|
||||
self.friendNameLabel.textColor = [UIColor colorWithWhite:(0x34/255.0f) alpha:1.0f];
|
||||
|
||||
self.friendPointsLabel.font = [UIFont fontWithName:@"SourceSansPro-Regular" size:14];
|
||||
self.friendPointsLabel.textColor = [UIColor colorWithWhite:(0x34/255.0f) alpha:1.0f];
|
||||
self.friendPointsLabel.hidden = YES;
|
||||
|
||||
self.friendNameLabel.text = friendInfo.username;
|
||||
[self.friendAvatar loadAvatarForUserID:[friendInfo.userID integerValue] prefetchedURL:friendInfo.avatarURL urlIsFinal:friendInfo.avatarIsFinal withSize:AVATAR_SIZE completion:nil];
|
||||
}
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark FriendsRequestsDetailController
|
||||
|
||||
@interface FriendsRequestsDetailController () <UITableViewDelegate, UITableViewDataSource>
|
||||
|
||||
@end
|
||||
|
||||
@implementation FriendsRequestsDetailController
|
||||
{
|
||||
IBOutlet UITableView* _requestsTable;
|
||||
IBOutlet UILabel* _noRequestsLabel;
|
||||
UIRefreshControl* _refreshIndicator;
|
||||
RBActivityIndicatorView* _loadingSpinner;
|
||||
BOOL _initialized;
|
||||
NSMutableArray* _requests;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = [RobloxTheme lightBackground];
|
||||
|
||||
_requests = [NSMutableArray array];
|
||||
|
||||
_initialized = NO;
|
||||
_requestsTable.backgroundColor = [UIColor clearColor];
|
||||
_requestsTable.delegate = self;
|
||||
_requestsTable.dataSource = self;
|
||||
_requestsTable.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
|
||||
_refreshIndicator = [[UIRefreshControl alloc] init];
|
||||
[_refreshIndicator addTarget:self action:@selector(refreshRequests:) forControlEvents:UIControlEventValueChanged];
|
||||
[_requestsTable addSubview:_refreshIndicator];
|
||||
|
||||
_loadingSpinner = [[RBActivityIndicatorView alloc] initWithFrame:SPINNER_ICON_FRAME];
|
||||
[self.view addSubview:_loadingSpinner];
|
||||
|
||||
_noRequestsLabel.text = NSLocalizedString(@"NoNewRequests", nil);
|
||||
_noRequestsLabel.textAlignment = NSTextAlignmentCenter;
|
||||
_noRequestsLabel.hidden = YES;
|
||||
}
|
||||
|
||||
- (void) viewDidLayoutSubviews
|
||||
{
|
||||
//center the label
|
||||
[_noRequestsLabel centerInFrame:self.view.frame];
|
||||
|
||||
//center the loading spinner
|
||||
[_loadingSpinner centerInFrame:self.view.frame];
|
||||
}
|
||||
|
||||
- (void) loadData
|
||||
{
|
||||
if(!_initialized)
|
||||
{
|
||||
_initialized = YES;
|
||||
_requestsTable.hidden = YES;
|
||||
[_loadingSpinner startAnimating];
|
||||
|
||||
UserInfo* userInfo = [UserInfo CurrentPlayer];
|
||||
[RobloxData fetchUserFriends:[NSNumber numberWithInteger:[userInfo.userId integerValue]]
|
||||
friendType:RBXFriendTypeFriendRequest
|
||||
startIndex:0
|
||||
numItems:ITEMS_PER_REQUEST
|
||||
avatarSize:AVATAR_SIZE completion:^(NSUInteger totalFriends, NSArray *friends)
|
||||
{
|
||||
[_requests removeAllObjects];
|
||||
[_requests addObjectsFromArray:friends];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[_loadingSpinner stopAnimating];
|
||||
_noRequestsLabel.hidden = ([_requests count] > 0);
|
||||
_requestsTable.hidden = NO;
|
||||
[_requestsTable reloadData];
|
||||
});
|
||||
}];
|
||||
}
|
||||
}
|
||||
- (void) refreshRequests:(UIRefreshControl*)refreshControl
|
||||
{
|
||||
if (refreshControl)
|
||||
[Flurry logEvent:FRDC_refreshList];
|
||||
|
||||
//make a request to pull down the messages from the server
|
||||
UserInfo* userInfo = [UserInfo CurrentPlayer];
|
||||
[RobloxData fetchUserFriends:[NSNumber numberWithInteger:[userInfo.userId integerValue]]
|
||||
friendType:RBXFriendTypeFriendRequest
|
||||
startIndex:0
|
||||
numItems:ITEMS_PER_REQUEST
|
||||
avatarSize:AVATAR_SIZE completion:^(NSUInteger totalFriends, NSArray *friends)
|
||||
{
|
||||
//loop through the list of messages and look for differences between the two sets
|
||||
//since invitationIDs are sorted chronologically on the server, we can easily check what messages are new, the same, or missing in the new list
|
||||
NSMutableArray* mergedRequests = [NSMutableArray arrayWithCapacity:([friends count] + [_requests count])];
|
||||
NSMutableArray* setToAdd = [NSMutableArray arrayWithCapacity:[friends count]];
|
||||
NSMutableArray* setToRemove = [NSMutableArray arrayWithCapacity:[_requests count]];
|
||||
|
||||
int i = 0; int j = 0;
|
||||
while (i < friends.count && j < _requests.count)
|
||||
{
|
||||
RBXFriendInfo* requestNew = friends[i];
|
||||
RBXFriendInfo* requestOld = _requests[j];
|
||||
if ([requestNew invitationID] > [requestOld invitationID])
|
||||
{
|
||||
[setToAdd addObject:[NSIndexPath indexPathForRow:i inSection:0]];
|
||||
[mergedRequests addObject:friends[i]];
|
||||
i++;
|
||||
}
|
||||
else if ([requestNew invitationID] == [requestOld invitationID])
|
||||
{
|
||||
[mergedRequests addObject:friends[i]];
|
||||
i++; j++;
|
||||
}
|
||||
else
|
||||
{
|
||||
[setToRemove addObject:[NSIndexPath indexPathForRow:j inSection:0]];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
//handle the remaining messages : add the new ones, remove the old ones
|
||||
for (; i < friends.count; i++)
|
||||
{
|
||||
[setToAdd addObject:[NSIndexPath indexPathForRow:i inSection:0]];
|
||||
[mergedRequests addObject:friends[i]];
|
||||
}
|
||||
for (; j < _requests.count; j++)
|
||||
{
|
||||
[setToRemove addObject:[NSIndexPath indexPathForRow:j inSection:0]];
|
||||
}
|
||||
|
||||
//assign the combined list of messages to the table array
|
||||
_requests = mergedRequests;
|
||||
|
||||
//stop the animation and refresh the table
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
_noRequestsLabel.hidden = ([_requests count] > 0);
|
||||
|
||||
if (refreshControl)
|
||||
[refreshControl endRefreshing];
|
||||
|
||||
if (_initialized)
|
||||
{
|
||||
//animate the change
|
||||
[_requestsTable beginUpdates];
|
||||
[_requestsTable deleteRowsAtIndexPaths:setToRemove withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
[_requestsTable insertRowsAtIndexPaths:setToAdd withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
[_requestsTable endUpdates];
|
||||
}
|
||||
else
|
||||
{
|
||||
//or simply
|
||||
[_requestsTable reloadData];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Actions
|
||||
|
||||
-(FriendRequestTableCell*)parentCellForView:(id)theView
|
||||
{
|
||||
id viewSuperView = [theView superview];
|
||||
while (viewSuperView != nil)
|
||||
{
|
||||
if ([viewSuperView isKindOfClass:[FriendRequestTableCell class]])
|
||||
{
|
||||
return (FriendRequestTableCell*)viewSuperView;
|
||||
}
|
||||
else
|
||||
{
|
||||
viewSuperView = [viewSuperView superview];
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
-(void)acceptFriendRequest:(id)sender
|
||||
{
|
||||
[Flurry logEvent:FRDC_acceptFriendRequest];
|
||||
|
||||
FriendRequestTableCell* cell = [self parentCellForView:sender];
|
||||
NSIndexPath* cellIndex = [_requestsTable indexPathForCell:cell];
|
||||
[cell acceptButton].enabled = NO;
|
||||
[cell doNotAcceptButton].enabled = NO;
|
||||
|
||||
RBXFriendInfo* request = _requests[cellIndex.row];
|
||||
NSInteger invitationID = request.invitationID;
|
||||
NSInteger targetUserID = request.userID.integerValue;
|
||||
|
||||
[RobloxData acceptFriendRequest:invitationID
|
||||
withTargetUserID:targetUserID
|
||||
completion:^(BOOL success)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
[_requests removeObjectAtIndex:cellIndex.row];
|
||||
[_requestsTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:cellIndex] withRowAnimation:UITableViewRowAnimationNone];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RBX_NOTIFY_FRIENDS_UPDATED object:nil];
|
||||
|
||||
//update the UI elements
|
||||
_noRequestsLabel.hidden = ([_requests count] > 0);
|
||||
}
|
||||
|
||||
//re-enable the buttons
|
||||
[cell acceptButton].enabled = YES;
|
||||
[cell doNotAcceptButton].enabled = YES;
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
-(void)declineFriendRequest:(id)sender
|
||||
{
|
||||
[Flurry logEvent:FRDC_declineFriendRequest];
|
||||
|
||||
FriendRequestTableCell* cell = [self parentCellForView:sender];
|
||||
NSIndexPath* cellIndex = [_requestsTable indexPathForCell:cell];
|
||||
[cell acceptButton].enabled = NO;
|
||||
[cell doNotAcceptButton].enabled = NO;
|
||||
|
||||
RBXFriendInfo* request = _requests[cellIndex.row];
|
||||
NSInteger invitationID = request.invitationID;
|
||||
NSInteger targetUserID = request.userID.integerValue;
|
||||
|
||||
[RobloxData declineFriendRequest:invitationID
|
||||
withTargetUserID:targetUserID
|
||||
completion:^(BOOL success)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
if(success)
|
||||
{
|
||||
[_requests removeObjectAtIndex:cellIndex.row];
|
||||
[_requestsTable deleteRowsAtIndexPaths:[NSArray arrayWithObject:cellIndex] withRowAnimation:UITableViewRowAnimationNone];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RBX_NOTIFY_FRIENDS_UPDATED object:nil];
|
||||
|
||||
//update the UI elements
|
||||
_noRequestsLabel.hidden = ([_requests count] > 0);
|
||||
}
|
||||
|
||||
//re-enable the buttons
|
||||
[cell acceptButton].enabled = YES;
|
||||
[cell doNotAcceptButton].enabled = YES;
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark UITableView delegate
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
return _requests.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
static NSString* CellIdentifier = @"ReuseCell";
|
||||
FriendRequestTableCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
|
||||
RBXFriendInfo* friendInfo = _requests[indexPath.row];
|
||||
cell.friendInfo = friendInfo;
|
||||
[cell.acceptButton addTarget:self action:@selector(acceptFriendRequest:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[cell.acceptButton setEnabled:YES];
|
||||
[cell.doNotAcceptButton addTarget:self action:@selector(declineFriendRequest:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[cell.doNotAcceptButton setEnabled:YES];
|
||||
cell.backgroundColor = [UIColor clearColor];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
[Flurry logEvent:FRDC_openFriendProfile];
|
||||
RBXFriendInfo* friendInfo = _requests[indexPath.row];
|
||||
|
||||
RBProfileViewController *viewController = [self.storyboard instantiateViewControllerWithIdentifier:@"RBOthersProfileViewController"];
|
||||
viewController.userId = friendInfo.userID;
|
||||
[self.navigationController pushViewController:viewController animated:YES];
|
||||
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// GameBadgeViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 10/16/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBModalPopUpViewController.h"
|
||||
#import "RobloxData.h"
|
||||
|
||||
@interface GameBadgeViewController : RBModalPopUpViewController
|
||||
|
||||
-(id) initWithBadgeInfo:(RBXBadgeInfo*)aBadge;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,185 @@
|
||||
//
|
||||
// GameBadgeViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 10/16/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "GameBadgeViewController.h"
|
||||
#import "RBActivityIndicatorView.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "RobloxImageView.h"
|
||||
|
||||
#define DEFAULT_VIEW_SIZE CGRectMake(0, 0, 600, 390)
|
||||
|
||||
@interface GameBadgeViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation GameBadgeViewController
|
||||
{
|
||||
IBOutlet UINavigationItem* _navBar;
|
||||
|
||||
IBOutlet RobloxImageView* _assetThumbnail;
|
||||
IBOutlet RobloxImageView* _avatarThumbnail;
|
||||
|
||||
IBOutlet UILabel* _lblCreatorWord;
|
||||
IBOutlet UILabel* _lblCreatorName;
|
||||
IBOutlet UILabel* _lblCreatedWord;
|
||||
IBOutlet UILabel* _lblCreatedDate;
|
||||
IBOutlet UILabel* _lblUpdatedWord;
|
||||
IBOutlet UILabel* _lblUpdatedDate;
|
||||
|
||||
IBOutlet UILabel* _lblRarityWord;
|
||||
IBOutlet UILabel* _lblRarityDescription;
|
||||
IBOutlet UILabel* _lblWonYesterdayWord;
|
||||
IBOutlet UILabel* _lblWonYesterdayCount;
|
||||
IBOutlet UILabel* _lblWonEverWord;
|
||||
IBOutlet UILabel* _lblWonEverCount;
|
||||
|
||||
IBOutlet UITextView* _lblDescription;
|
||||
|
||||
RBXBadgeInfo* _badgeInfo;
|
||||
}
|
||||
|
||||
-(id) initWithBadgeInfo:(RBXBadgeInfo*)aBadge
|
||||
{
|
||||
self = [super initWithNibName:@"GameBadgeViewController" bundle:nil];
|
||||
self.modalPresentationStyle = UIModalPresentationFormSheet;
|
||||
|
||||
_badgeInfo = aBadge;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
//localized the strings
|
||||
[_lblCreatorWord setText:NSLocalizedString(@"BadgeCreatorWord", nil)];
|
||||
[_lblCreatedWord setText:NSLocalizedString(@"BadgeCreatedWord", nil)];
|
||||
[_lblUpdatedWord setText:NSLocalizedString(@"BadgeUpdatedWord", nil)];
|
||||
[_lblRarityWord setText:NSLocalizedString(@"BadgeRarityWord", nil)];
|
||||
[_lblWonYesterdayWord setText:NSLocalizedString(@"BadgeWonYesterdayWord", nil)];
|
||||
[_lblWonEverWord setText:NSLocalizedString(@"BadgeWonEverWord", nil)];
|
||||
|
||||
|
||||
[_navBar setTitle:_badgeInfo.name != nil ? _badgeInfo.name : @"Badge"];
|
||||
|
||||
//populate the labels and images with the badge information
|
||||
_lblCreatorName.hidden = YES;
|
||||
_lblCreatedDate.hidden = YES;
|
||||
_lblCreatedWord.hidden = YES;
|
||||
_lblUpdatedDate.hidden = YES;
|
||||
_lblUpdatedWord.hidden = YES;
|
||||
//[self showLabel:_lblCreatedDate andTitle:_lblCreatedWord ifStringExists:_badgeInfo.badgeCreatedDate];
|
||||
//[self showLabel:_lblUpdatedDate andTitle:_lblUpdatedWord ifStringExists:_badgeInfo.badgeUpdatedDate];
|
||||
[self showLabel:_lblWonEverCount andTitle:_lblWonEverWord ifStringExists:_badgeInfo.badgeTotalAwardedEver];
|
||||
[self showLabel:_lblWonYesterdayCount andTitle:_lblWonYesterdayWord ifStringExists:_badgeInfo.badgeTotalAwardedYesterday];
|
||||
|
||||
//handle special cases
|
||||
if (_badgeInfo.badgeDescription)
|
||||
{
|
||||
[_lblDescription setText:_badgeInfo.badgeDescription];
|
||||
//[RobloxTheme applyToGamePreviewDescription:_lblDescription];
|
||||
_lblDescription.font = [UIFont fontWithName:@"SourceSansPro-Regular" size:20];
|
||||
}
|
||||
else
|
||||
_lblDescription.hidden = YES;
|
||||
|
||||
|
||||
|
||||
if (_badgeInfo.badgeRarity && _badgeInfo.badgeRarityName)
|
||||
[_lblRarityDescription setText:[NSString stringWithFormat:@"%.2f : %@", _badgeInfo.badgeRarity, _badgeInfo.badgeRarityName]];
|
||||
else
|
||||
{
|
||||
_lblRarityDescription.hidden = YES;
|
||||
_lblRarityWord.hidden = YES;
|
||||
}
|
||||
|
||||
|
||||
[self loadBadgeImage];
|
||||
[self loadCreatorImage];
|
||||
}
|
||||
- (void)viewWillLayoutSubviews
|
||||
{
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
self.view.superview.bounds = DEFAULT_VIEW_SIZE;
|
||||
}
|
||||
|
||||
-(void) loadBadgeImage
|
||||
{
|
||||
//load the badge image
|
||||
RBActivityIndicatorView* loadingSpinner1 = [[RBActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 32, 32)];
|
||||
[self.view addSubview:loadingSpinner1];
|
||||
[loadingSpinner1 startAnimating];
|
||||
[loadingSpinner1 centerInFrame:_assetThumbnail.frame];
|
||||
|
||||
[_assetThumbnail loadBadgeWithURL:_badgeInfo.imageURL withSize:_assetThumbnail.frame.size completion:^
|
||||
{
|
||||
[loadingSpinner1 stopAnimating];
|
||||
[loadingSpinner1 removeFromSuperview];
|
||||
}];
|
||||
|
||||
//poll the server for a higher resolution badge image
|
||||
/*[RobloxData fetchURLForAssetID:_badgeInfo.badgeAssetId withSize:CGSizeMake(230, 230) completion:^(NSString *imageURL)
|
||||
{
|
||||
//any image request for badges outside the fixed 75x75 throws a db error
|
||||
NSLog(@"found better resolution @ %@ vs original @ %@", imageURL, _badgeInfo.imageURL);
|
||||
if (imageURL)
|
||||
[_assetThumbnail loadBadgeWithURL:imageURL withSize:_assetThumbnail.frame.size completion:nil];
|
||||
}];*/
|
||||
}
|
||||
|
||||
-(void) loadCreatorImage
|
||||
{
|
||||
NSNumber* creatorID = [NSNumber numberWithInteger:_badgeInfo.badgeCreatorId.integerValue];
|
||||
creatorID = creatorID.integerValue == 0 ? [NSNumber numberWithInt:1] : creatorID;
|
||||
|
||||
RBActivityIndicatorView* loadingSpinner2 = [[RBActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 16, 16)];
|
||||
[self.view addSubview:loadingSpinner2];
|
||||
[loadingSpinner2 startAnimating];
|
||||
[loadingSpinner2 centerInFrame:_avatarThumbnail.frame];
|
||||
|
||||
//load the image of the creator
|
||||
[_avatarThumbnail loadAvatarForUserID:creatorID.intValue withSize:[RobloxTheme sizeProfilePictureMedium] completion:^
|
||||
{
|
||||
[loadingSpinner2 stopAnimating];
|
||||
[loadingSpinner2 removeFromSuperview];
|
||||
}];
|
||||
|
||||
[RobloxData fetchUserProfile:creatorID
|
||||
avatarSize:[RobloxTheme sizeProfilePictureMedium]
|
||||
completion:^(RBXUserProfileInfo *profile)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self showLabel:_lblCreatorName andTitle:_lblCreatorWord ifStringExists:profile.username];
|
||||
});
|
||||
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
- (IBAction)tabCloseButton:(id)sender
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
|
||||
-(void) showLabel:(UILabel*)aLabel
|
||||
andTitle:(UILabel*)titleLabel
|
||||
ifStringExists:(NSString*)someText
|
||||
{
|
||||
bool exists = (someText != nil) && (someText.length > 0);
|
||||
aLabel.hidden = !exists;
|
||||
titleLabel.hidden = !exists;
|
||||
|
||||
if (exists)
|
||||
[aLabel setText:someText];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,183 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6245" systemVersion="13F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment defaultVersion="1792" identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6238"/>
|
||||
</dependencies>
|
||||
<customFonts key="customFonts">
|
||||
<mutableArray key="SourceSansPro-Regular.ttf">
|
||||
<string>SourceSansPro-Regular</string>
|
||||
<string>SourceSansPro-Regular</string>
|
||||
<string>SourceSansPro-Regular</string>
|
||||
<string>SourceSansPro-Regular</string>
|
||||
<string>SourceSansPro-Regular</string>
|
||||
<string>SourceSansPro-Regular</string>
|
||||
<string>SourceSansPro-Regular</string>
|
||||
<string>SourceSansPro-Regular</string>
|
||||
<string>SourceSansPro-Regular</string>
|
||||
<string>SourceSansPro-Regular</string>
|
||||
<string>SourceSansPro-Regular</string>
|
||||
<string>SourceSansPro-Regular</string>
|
||||
<string>SourceSansPro-Regular</string>
|
||||
</mutableArray>
|
||||
</customFonts>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="GameBadgeViewController">
|
||||
<connections>
|
||||
<outlet property="_assetThumbnail" destination="eCf-C3-Og4" id="a63-m7-FIr"/>
|
||||
<outlet property="_avatarThumbnail" destination="pXv-Oi-Tye" id="pSJ-Pu-m2o"/>
|
||||
<outlet property="_lblCreatedDate" destination="teZ-62-BD8" id="SFc-Yt-cP3"/>
|
||||
<outlet property="_lblCreatedWord" destination="5U5-Ev-hiu" id="uFY-hc-Ysv"/>
|
||||
<outlet property="_lblCreatorName" destination="fA7-58-W69" id="Xbv-yg-Kvh"/>
|
||||
<outlet property="_lblCreatorWord" destination="5IA-8F-1he" id="wXK-WF-ZAg"/>
|
||||
<outlet property="_lblDescription" destination="KJ5-3Z-pid" id="fyq-Gc-jAw"/>
|
||||
<outlet property="_lblRarityDescription" destination="VjG-Qz-PqH" id="HCN-gF-BTv"/>
|
||||
<outlet property="_lblRarityWord" destination="k4d-ll-Gni" id="3gH-6C-JBj"/>
|
||||
<outlet property="_lblUpdatedDate" destination="URa-KY-mZp" id="3Ft-XW-mck"/>
|
||||
<outlet property="_lblUpdatedWord" destination="qkt-Vr-M5u" id="wId-lZ-zvU"/>
|
||||
<outlet property="_lblWonEverCount" destination="uzt-xc-Cm4" id="Jpb-Zb-2dT"/>
|
||||
<outlet property="_lblWonEverWord" destination="hvc-eD-QTY" id="Vnf-Ue-FON"/>
|
||||
<outlet property="_lblWonYesterdayCount" destination="obw-y6-c6E" id="YHP-fc-Ymx"/>
|
||||
<outlet property="_lblWonYesterdayWord" destination="xnw-zI-9aX" id="Gdd-iB-GZP"/>
|
||||
<outlet property="_navBar" destination="X5X-uN-GM0" id="MYe-ba-Ile"/>
|
||||
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="390"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dqj-LN-75Y" userLabel="Detail View">
|
||||
<rect key="frame" x="0.0" y="45" width="600" height="204"/>
|
||||
<subviews>
|
||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" fixedFrame="YES" image="Separator" translatesAutoresizingMaskIntoConstraints="NO" id="SXk-BK-ZfC">
|
||||
<rect key="frame" x="252" y="63" width="324" height="1"/>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pXv-Oi-Tye" customClass="RobloxImageView">
|
||||
<rect key="frame" x="252" y="13" width="48" height="48"/>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="_RARITY_:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="k4d-ll-Gni">
|
||||
<rect key="frame" x="252" y="94" width="145" height="26"/>
|
||||
<fontDescription key="fontDescription" name="SourceSansPro-Regular" family="Source Sans Pro" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="_RARITY_PERCENT_" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="VjG-Qz-PqH">
|
||||
<rect key="frame" x="405" y="94" width="171" height="26"/>
|
||||
<fontDescription key="fontDescription" name="SourceSansPro-Regular" family="Source Sans Pro" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="_WON_YESTERDAY_COUNT_" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="obw-y6-c6E">
|
||||
<rect key="frame" x="405" y="111" width="171" height="26"/>
|
||||
<fontDescription key="fontDescription" name="SourceSansPro-Regular" family="Source Sans Pro" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="_WON_YESTERDAY_:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="xnw-zI-9aX">
|
||||
<rect key="frame" x="252" y="111" width="145" height="26"/>
|
||||
<fontDescription key="fontDescription" name="SourceSansPro-Regular" family="Source Sans Pro" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="_WON_EVER_:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hvc-eD-QTY">
|
||||
<rect key="frame" x="252" y="128" width="145" height="26"/>
|
||||
<fontDescription key="fontDescription" name="SourceSansPro-Regular" family="Source Sans Pro" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="_CREATOR_:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5IA-8F-1he">
|
||||
<rect key="frame" x="308" y="13" width="106" height="17"/>
|
||||
<fontDescription key="fontDescription" name="SourceSansPro-Regular" family="Source Sans Pro" pointSize="14"/>
|
||||
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="_CREATOR_NAME_" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fA7-58-W69">
|
||||
<rect key="frame" x="422" y="13" width="154" height="17"/>
|
||||
<fontDescription key="fontDescription" name="SourceSansPro-Regular" family="Source Sans Pro" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="_CREATED_:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="5U5-Ev-hiu">
|
||||
<rect key="frame" x="308" y="28" width="106" height="17"/>
|
||||
<fontDescription key="fontDescription" name="SourceSansPro-Regular" family="Source Sans Pro" pointSize="14"/>
|
||||
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="_CREATED_DATE_" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="teZ-62-BD8">
|
||||
<rect key="frame" x="422" y="28" width="154" height="17"/>
|
||||
<fontDescription key="fontDescription" name="SourceSansPro-Regular" family="Source Sans Pro" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="_UPDATED_:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qkt-Vr-M5u">
|
||||
<rect key="frame" x="308" y="44" width="106" height="17"/>
|
||||
<fontDescription key="fontDescription" name="SourceSansPro-Regular" family="Source Sans Pro" pointSize="14"/>
|
||||
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="_UPDATED_DATE_" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="URa-KY-mZp">
|
||||
<rect key="frame" x="422" y="44" width="154" height="17"/>
|
||||
<fontDescription key="fontDescription" name="SourceSansPro-Regular" family="Source Sans Pro" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="_WON_EVER_COUNT_" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="uzt-xc-Cm4">
|
||||
<rect key="frame" x="405" y="128" width="171" height="26"/>
|
||||
<fontDescription key="fontDescription" name="SourceSansPro-Regular" family="Source Sans Pro" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eCf-C3-Og4" customClass="RobloxImageView">
|
||||
<rect key="frame" x="20" y="47" width="200" height="200"/>
|
||||
</imageView>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="M9R-xc-bfi" userLabel="Description View">
|
||||
<rect key="frame" x="0.0" y="251" width="600" height="140"/>
|
||||
<subviews>
|
||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" image="Separator" translatesAutoresizingMaskIntoConstraints="NO" id="zOa-FV-iY3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="1"/>
|
||||
</imageView>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" editable="NO" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="KJ5-3Z-pid">
|
||||
<rect key="frame" x="24" y="0.0" width="552" height="140"/>
|
||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
|
||||
<fontDescription key="fontDescription" name="SourceSansPro-Regular" family="Source Sans Pro" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.96078431372549022" green="0.96078431372549022" blue="0.96078431372549022" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
<navigationBar contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tmx-FW-b34">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
|
||||
<items>
|
||||
<navigationItem title="Game Badge Title" id="X5X-uN-GM0">
|
||||
<barButtonItem key="leftBarButtonItem" image="Close Button" id="mjE-w9-BkH">
|
||||
<button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="Vjf-AT-Zwm">
|
||||
<rect key="frame" x="16" y="7" width="17" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<state key="normal" image="Close Button">
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="tabCloseButton:" destination="-1" eventType="touchUpInside" id="qZb-Cy-QqC"/>
|
||||
</connections>
|
||||
</button>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
</items>
|
||||
</navigationBar>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="193" y="317.5"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="Close Button" width="17" height="17"/>
|
||||
<image name="Separator" width="491" height="1"/>
|
||||
</resources>
|
||||
</document>
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// GamePreviewDescriptionViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 10/6/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RBModalPopUpViewController.h"
|
||||
|
||||
|
||||
@interface GamePreviewDescriptionViewController : RBModalPopUpViewController <UIScrollViewDelegate>
|
||||
|
||||
@property NSString* gameDescription;
|
||||
@property UILabel* lblDescription;
|
||||
@property UIScrollView* svScroll;
|
||||
|
||||
-(id) initWithDescription:(NSString*)aDescription;
|
||||
@end
|
||||
@@ -0,0 +1,68 @@
|
||||
//
|
||||
// GamePreviewDescriptionViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 10/6/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "GamePreviewDescriptionViewController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RobloxHUD.h"
|
||||
|
||||
#define CONTENT_VIEW_SIZE CGRectMake(0, 0, 540, 320)
|
||||
#define CONTENT_MARGIN_SIZE CGRectMake(16, 16, 508, 288)
|
||||
|
||||
@implementation GamePreviewDescriptionViewController
|
||||
|
||||
-(id) initWithDescription:(NSString *)aDescription
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_gameDescription = aDescription;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
//view delegate functions
|
||||
-(void) viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.view.backgroundColor = [RobloxTheme lightBackground];
|
||||
|
||||
UIButton* close = [RobloxTheme applyCloseButtonToUINavigationItem:self.navigationItem];
|
||||
[close addTarget:self action:@selector(didPressClose:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
[self.navigationItem setTitle:NSLocalizedString(@"DescriptionWord", nil)];
|
||||
[RobloxTheme applyToModalPopupNavBar:self.navigationController.navigationBar];
|
||||
|
||||
//initialize the label
|
||||
_lblDescription = [[UILabel alloc] initWithFrame:CONTENT_MARGIN_SIZE];
|
||||
[_lblDescription setText:_gameDescription];
|
||||
[_lblDescription setFont:[UIFont fontWithName:@"SourceSansPro-Regular" size:16]];
|
||||
_lblDescription.numberOfLines = 0;
|
||||
[_lblDescription sizeToFit];
|
||||
|
||||
//initialize the scrollview
|
||||
CGRect contentSize = _lblDescription.frame;
|
||||
contentSize.size.height += 20;
|
||||
_svScroll = [[UIScrollView alloc] initWithFrame:CONTENT_VIEW_SIZE];
|
||||
[_svScroll addSubview:_lblDescription];
|
||||
[_svScroll setContentSize:contentSize.size];
|
||||
_svScroll.delegate = self;
|
||||
[self.view addSubview:_svScroll];
|
||||
}
|
||||
-(void) viewWillLayoutSubviews
|
||||
{
|
||||
[super viewWillLayoutSubviews];
|
||||
//self.navigationController.view.bounds = CONTENT_VIEW_SIZE;
|
||||
_svScroll.frame = self.view.bounds;
|
||||
_svScroll.clipsToBounds = NO;
|
||||
}
|
||||
|
||||
- (void) didPressClose:(id)sender { [self dismissViewControllerAnimated:YES completion:nil]; }
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// GamePreviewScreenController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by alichtin on 5/30/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RobloxData.h"
|
||||
|
||||
@interface GamePreviewScreenController : UIViewController<UITextViewDelegate, UIScrollViewDelegate>
|
||||
|
||||
@property (strong, nonatomic) RBXGameData* gameData;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,508 @@
|
||||
//
|
||||
// GamePreviewScreenController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by alichtin on 5/30/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "GamePreviewScreenController.h"
|
||||
#import "GamePreviewDescriptionViewController.h"
|
||||
#import "RobloxImageView.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "Flurry.h"
|
||||
#import "UIViewController+Helpers.h"
|
||||
#import "UIScrollView+Auto.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "GameConsumableCollectionViewController.h"
|
||||
#import "LeaderboardsViewController.h"
|
||||
#import "RBVotesView.h"
|
||||
#import "RBFavoritesView.h"
|
||||
#import "RBGameViewController.h"
|
||||
#import "RBBadgesViewController.h"
|
||||
#import "RBProfileViewController.h"
|
||||
#import "RBConfirmPurchaseViewController.h"
|
||||
#import "RBActivityIndicatorView.h"
|
||||
#import "RobloxImageView.h"
|
||||
#import "YTPlayerView.h"
|
||||
|
||||
#define THUMBNAIL_SIZE RBX_SCALED_DEVICE_SIZE(455, 256)
|
||||
#define AVATAR_SIZE CGSizeMake(100, 100)
|
||||
|
||||
#define DETAILS_Y_OFFSET 22.0f
|
||||
|
||||
#define SEGMENT_DETAIL_INDEX 0
|
||||
#define SEGMENT_LEADERBOARD_INDEX 1
|
||||
|
||||
//---METRICS---
|
||||
#define GPSC_closeButtonTouched @"GAME PREVIEW SCREEN - Close Button Touched"
|
||||
#define GPSC_playButtonTouched @"GAME PREVIEW SCREEN - Play Button Touched"
|
||||
|
||||
@interface GamePreviewScreenController ()
|
||||
- (void)setUpMaskLayer;
|
||||
@end
|
||||
|
||||
@implementation GamePreviewScreenController
|
||||
{
|
||||
NSNumber* _creatorID;
|
||||
|
||||
UISegmentedControl* _segmentedControl;
|
||||
|
||||
IBOutlet UIScrollView* _detailsScrollView;
|
||||
IBOutlet UIView* _leaderboardView;
|
||||
IBOutlet UIView* _moreDetailsView;
|
||||
|
||||
// Details view items
|
||||
IBOutlet UIPageControl* _pcItems;
|
||||
IBOutlet UIScrollView* _svThumbnails;
|
||||
NSArray* thumbnails;
|
||||
IBOutlet UILabel* _gameTitle;
|
||||
IBOutlet UILabel *_builderTitle;
|
||||
IBOutlet UIButton *_builderButton;
|
||||
IBOutlet UITextView *_description;
|
||||
IBOutlet UIButton *_readMoreButton;
|
||||
IBOutlet UIButton *_playButton;
|
||||
IBOutlet RobloxImageView* _builderAvatar;
|
||||
IBOutlet RBFavoritesView* _favoritesView;
|
||||
|
||||
IBOutlet UILabel* _moreTitle;
|
||||
IBOutlet UILabel* _visitsTitle;
|
||||
IBOutlet UILabel* _createdTitle;
|
||||
IBOutlet UILabel* _updatedTitle;
|
||||
IBOutlet UILabel* _maxPlayersTitle;
|
||||
IBOutlet UILabel* _genreTitle;
|
||||
IBOutlet UILabel* _visitsValue;
|
||||
IBOutlet UILabel* _createdValue;
|
||||
IBOutlet UILabel* _updatedValue;
|
||||
IBOutlet UILabel* _maxPlayersValue;
|
||||
IBOutlet UILabel* _genreValue;
|
||||
|
||||
IBOutlet RBVotesView* _votesView;
|
||||
|
||||
// Leaderboard view items
|
||||
IBOutlet RobloxImageView* _leaderboardGameThumbnail;
|
||||
IBOutlet UILabel* _leaderboardGameTitle;
|
||||
|
||||
GameConsumableCollectionViewController* _gearCollectionViewController;
|
||||
GameConsumableCollectionViewController* _passesCollectionViewController;
|
||||
RBBadgesViewController* _badgesViewController;
|
||||
|
||||
CAGradientLayer* maskLayer;
|
||||
|
||||
RBXGameData* _firstGameData;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
_firstGameData = self.gameData;
|
||||
|
||||
self.view.backgroundColor = [RobloxTheme lightBackground];
|
||||
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
|
||||
// Save this value, since its not provided in the full game details function
|
||||
_creatorID = _gameData.creatorID;
|
||||
|
||||
_segmentedControl = [[UISegmentedControl alloc] init];
|
||||
[_segmentedControl insertSegmentWithTitle:NSLocalizedString(@"DetailsWord", nil) atIndex:0 animated:NO];
|
||||
[_segmentedControl insertSegmentWithTitle:NSLocalizedString(@"LeaderboardsWord", nil) atIndex:1 animated:NO];
|
||||
[_segmentedControl addTarget:self action:@selector(onSegmentedControlValueChanged) forControlEvents:UIControlEventValueChanged];
|
||||
[_segmentedControl setSelectedSegmentIndex:SEGMENT_DETAIL_INDEX];
|
||||
[_segmentedControl setEnabled:NO forSegmentAtIndex:SEGMENT_LEADERBOARD_INDEX];
|
||||
[_segmentedControl sizeToFit];
|
||||
self.navigationItem.titleView = _segmentedControl;
|
||||
[self onSegmentedControlValueChanged];
|
||||
|
||||
[self initializeGameDetails];
|
||||
|
||||
[self layoutDetailsElements];
|
||||
}
|
||||
|
||||
- (void) initializeGameDetails
|
||||
{
|
||||
[RobloxTheme applyToGamePreviewTitle:_gameTitle];
|
||||
[RobloxTheme applyToGamePreviewDetailTitle:_builderTitle];
|
||||
[RobloxTheme applyToGamePreviewDescription:_description];
|
||||
[RobloxTheme applyToGamePreviewBuilderButton:_builderButton];
|
||||
|
||||
[RobloxTheme applyToGameSortTitle:_moreTitle];
|
||||
[RobloxTheme applyToGamePreviewMoreInfoTitle:_visitsTitle];
|
||||
[RobloxTheme applyToGamePreviewMoreInfoTitle:_createdTitle];
|
||||
[RobloxTheme applyToGamePreviewMoreInfoTitle:_updatedTitle];
|
||||
[RobloxTheme applyToGamePreviewMoreInfoTitle:_maxPlayersTitle];
|
||||
[RobloxTheme applyToGamePreviewMoreInfoTitle:_genreTitle];
|
||||
[RobloxTheme applyToGamePreviewMoreInfoValue:_visitsValue];
|
||||
[RobloxTheme applyToGamePreviewMoreInfoValue:_createdValue];
|
||||
[RobloxTheme applyToGamePreviewMoreInfoValue:_updatedValue];
|
||||
[RobloxTheme applyToGamePreviewMoreInfoValue:_maxPlayersValue];
|
||||
[RobloxTheme applyToGamePreviewMoreInfoValue:_genreValue];
|
||||
|
||||
_builderTitle.text = [NSString stringWithFormat:@"%@:", NSLocalizedString(@"BuilderWord", nil)];
|
||||
[_readMoreButton setTitle:NSLocalizedString(@"ReadMoreWord", nil) forState:UIControlStateNormal];
|
||||
|
||||
if( _firstGameData.userOwns == NO && _firstGameData.price > 0 )
|
||||
{
|
||||
NSString* buyAccessButtonTitle = NSLocalizedString(@"BuyAccessPhrase", nil);
|
||||
[_playButton setTitle:buyAccessButtonTitle forState:UIControlStateNormal];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_playButton setTitle:NSLocalizedString(@"PlayButtonLabel", nil) forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
_moreTitle.text = [NSLocalizedString(@"MoreDetailsPhrase", nil) uppercaseString];
|
||||
_visitsTitle.text = NSLocalizedString(@"VisitsWord", nil);
|
||||
_createdTitle.text = NSLocalizedString(@"CreatedWord", nil);
|
||||
_updatedTitle.text = NSLocalizedString(@"UpdatedWord", nil);
|
||||
_maxPlayersTitle.text = NSLocalizedString(@"MaxPlayersPhrase", nil);
|
||||
_genreTitle.text = NSLocalizedString(@"GenreWord", nil);
|
||||
|
||||
_visitsValue.text = @"";
|
||||
_createdValue.text = @"";
|
||||
_updatedValue.text = @"";
|
||||
_maxPlayersValue.text = @"";
|
||||
_genreValue.text = @"";
|
||||
|
||||
[_votesView setVotesForGame:_gameData];
|
||||
|
||||
_gameTitle.text = _gameData.title;
|
||||
|
||||
[_builderButton setEnabled:NO];
|
||||
[_builderButton setTitle:@"" forState:UIControlStateNormal];
|
||||
_description.text = @"";
|
||||
|
||||
_description.delegate = self;
|
||||
|
||||
[_builderAvatar loadAvatarForUserID:[_gameData.creatorID integerValue] withSize:AVATAR_SIZE completion:nil];
|
||||
|
||||
__weak GamePreviewScreenController* weakSelf = self;
|
||||
|
||||
[RobloxData fetchGameDetails:_gameData.placeID completion:^(RBXGameData *game)
|
||||
{
|
||||
if(game != nil)
|
||||
{
|
||||
_gameData = game;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[_favoritesView setFavoritesForGame:game];
|
||||
|
||||
[_builderButton setEnabled:YES];
|
||||
[_builderButton setTitle:[NSString stringWithFormat:@"%@ >", game.creatorName] forState:UIControlStateNormal];
|
||||
|
||||
_description.text = game.gameDescription;
|
||||
bool isTextLongerThanThreeLines = [[game.gameDescription componentsSeparatedByString:@"\n"] count] >= 3;
|
||||
bool isTextTooLong = [game.gameDescription length] > 90;
|
||||
_readMoreButton.hidden = !(isTextLongerThanThreeLines || isTextTooLong);
|
||||
|
||||
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
|
||||
[numberFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
|
||||
[numberFormatter setMaximumFractionDigits:0];
|
||||
|
||||
_visitsValue.text = [numberFormatter stringFromNumber:[NSNumber numberWithUnsignedInteger:_gameData.visited]];
|
||||
_createdValue.text = _gameData.dateCreated;
|
||||
_updatedValue.text = _gameData.dateUpdated;
|
||||
_maxPlayersValue.text = [NSString stringWithFormat:@"%lu", (unsigned long)_gameData.maxPlayers];
|
||||
_genreValue.text = _gameData.assetGenre;
|
||||
|
||||
[_votesView setVotesForGame:_gameData];
|
||||
[_favoritesView setFavoritesForGame:_gameData];
|
||||
|
||||
[weakSelf initializeGameLeaderboards];
|
||||
});
|
||||
}
|
||||
}];
|
||||
|
||||
_gearCollectionViewController = [[GameConsumableCollectionViewController alloc] initWithNibName:@"GameConsumableCollectionViewController" bundle:nil];
|
||||
_gearCollectionViewController.view.frame = CGRectMake(0, 0, 1024, 245);
|
||||
[_gearCollectionViewController fetchGearForPlaceID:_gameData.placeID gameTitle:_gameData.title completion:^{
|
||||
[weakSelf layoutDetailsElements];
|
||||
}];
|
||||
_gearCollectionViewController.view.hidden = YES;
|
||||
[_detailsScrollView addSubview:_gearCollectionViewController.view];
|
||||
[self addChildViewController:_gearCollectionViewController];
|
||||
|
||||
_passesCollectionViewController = [[GameConsumableCollectionViewController alloc] initWithNibName:@"GameConsumableCollectionViewController" bundle:nil];
|
||||
_passesCollectionViewController.view.frame = CGRectMake(0, 0, 1024, 245);
|
||||
[_passesCollectionViewController fetchPassesForPlaceID:_gameData.placeID gameTitle:_gameData.title completion:^{
|
||||
[weakSelf layoutDetailsElements];
|
||||
}];
|
||||
_passesCollectionViewController.view.hidden = YES;
|
||||
[_detailsScrollView addSubview:_passesCollectionViewController.view];
|
||||
[self addChildViewController:_passesCollectionViewController];
|
||||
|
||||
_badgesViewController = [[RBBadgesViewController alloc] initWithNibName:@"RBBadgesViewController" bundle:nil];
|
||||
_badgesViewController.gameID = _gameData.placeID;
|
||||
_badgesViewController.completionHandler = ^{
|
||||
[weakSelf layoutDetailsElements];
|
||||
};
|
||||
_badgesViewController.view.hidden = YES;
|
||||
[_detailsScrollView addSubview:_badgesViewController.view];
|
||||
[self addChildViewController:_badgesViewController];
|
||||
|
||||
|
||||
//add in the thumbnails for the game
|
||||
[RobloxData fetchThumbnailsForPlace:_gameData.placeID
|
||||
withSize:THUMBNAIL_SIZE
|
||||
completion:^(NSArray *gameThumbnails)
|
||||
{
|
||||
//loop through all the thumbnails and parse them out
|
||||
for (RBXThumbnail* rbxThumb in gameThumbnails)
|
||||
{
|
||||
if (rbxThumb.assetTypeId == RBXThumbnailImage)
|
||||
{
|
||||
//add the image to the scroll
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
CGRect framePosition = CGRectMake(_svThumbnails.subviews.count * _svThumbnails.frame.size.width,
|
||||
0,
|
||||
_svThumbnails.frame.size.width,
|
||||
_svThumbnails.frame.size.height);
|
||||
RobloxImageView* rbxImage = [[RobloxImageView alloc] initWithFrame:framePosition];
|
||||
|
||||
//load the image with the thumbnail's asset ID
|
||||
[rbxImage loadWithAssetID:rbxThumb.assetId
|
||||
withPrefetchedURL:rbxThumb.assetURL
|
||||
withFinalURL:rbxThumb.assetIsFinal
|
||||
withSize:framePosition.size
|
||||
completion:nil];
|
||||
[_svThumbnails addSubview:rbxImage];
|
||||
});
|
||||
}
|
||||
else if (rbxThumb.assetTypeId == RBXThumbnailVideo)
|
||||
{
|
||||
//we have a video thumbnail
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
CGRect framePosition = CGRectMake(_svThumbnails.subviews.count * _svThumbnails.frame.size.width,
|
||||
0,
|
||||
_svThumbnails.frame.size.width,
|
||||
_svThumbnails.frame.size.height);
|
||||
YTPlayerView* ytThumbnail = [[YTPlayerView alloc] initWithFrame:framePosition];
|
||||
NSDictionary* playerSettings = [NSDictionary dictionaryWithObjects:@[[NSNumber numberWithInt:0], [NSNumber numberWithInt:1], [NSNumber numberWithInt:0]]
|
||||
forKeys:@[@"autoplay", @"controls", @"rel"]];
|
||||
|
||||
//load the video thumbnail
|
||||
[ytThumbnail loadWithVideoId:rbxThumb.assetHash playerVars:playerSettings];
|
||||
[_svThumbnails addSubview:ytThumbnail];
|
||||
});
|
||||
}
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[_pcItems setNumberOfPages:_svThumbnails.subviews.count];
|
||||
[_svThumbnails setContentSize:CGSizeMake(_svThumbnails.frame.size.width * [_svThumbnails.subviews count], _svThumbnails.frame.size.height)];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) initializeGameLeaderboards
|
||||
{
|
||||
[RobloxTheme applyToGamePreviewTitle:_leaderboardGameTitle];
|
||||
|
||||
_leaderboardGameTitle.text = _gameData.title;
|
||||
|
||||
[_leaderboardGameThumbnail loadThumbnailForGame:_gameData withSize:THUMBNAIL_SIZE completion:nil];
|
||||
|
||||
LeaderboardsViewController* leaderboardController = [self.storyboard instantiateViewControllerWithIdentifier:@"LeaderboardViewController"];
|
||||
leaderboardController.timeFilter = RBXLeaderboardsTimeFilterDay;
|
||||
leaderboardController.distributorID = _gameData.universeID;
|
||||
leaderboardController.onLeaderboardsLoaded = ^(BOOL hasLeaderboards)
|
||||
{
|
||||
// Only enable leaderboards when the leaderboard are fully loaded
|
||||
if(hasLeaderboards)
|
||||
{
|
||||
[_segmentedControl setEnabled:YES forSegmentAtIndex:SEGMENT_LEADERBOARD_INDEX];
|
||||
}
|
||||
};
|
||||
[self addChildViewController:leaderboardController];
|
||||
[_leaderboardView addSubview:leaderboardController.view];
|
||||
[leaderboardController updateLeaderboards];
|
||||
leaderboardController.view.position = CGPointMake(28, 132);
|
||||
}
|
||||
|
||||
- (void) onSegmentedControlValueChanged
|
||||
{
|
||||
_detailsScrollView.hidden = _segmentedControl.selectedSegmentIndex != 0;
|
||||
_leaderboardView.hidden = _segmentedControl.selectedSegmentIndex != 1;
|
||||
}
|
||||
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
||||
{
|
||||
if ([scrollView isEqual:_svThumbnails])
|
||||
{
|
||||
//update the page control item
|
||||
CGFloat pageWidth = _svThumbnails.frame.size.width;
|
||||
int page = floor((_svThumbnails.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
|
||||
_pcItems.currentPage = page;
|
||||
}
|
||||
else
|
||||
{
|
||||
[CATransaction begin];
|
||||
[CATransaction setDisableActions:YES];
|
||||
maskLayer.position = CGPointMake(0, scrollView.contentOffset.y);
|
||||
[CATransaction commit];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setUpMaskLayer
|
||||
{
|
||||
maskLayer = [CAGradientLayer layer];
|
||||
maskLayer.frame = _readMoreButton.bounds;
|
||||
maskLayer.colors = @[(id)[UIColor colorWithWhite:1.0 alpha:1.0].CGColor,
|
||||
(id)[UIColor colorWithWhite:1.0 alpha:1.0].CGColor,
|
||||
(id)[UIColor colorWithWhite:1.0 alpha:0.0].CGColor];
|
||||
maskLayer.startPoint = CGPointMake(1.0, 0.5);
|
||||
maskLayer.endPoint = CGPointMake(0, 0.5);
|
||||
|
||||
[_readMoreButton.layer insertSublayer:maskLayer atIndex:0];
|
||||
}
|
||||
|
||||
- (void)viewWillLayoutSubviews
|
||||
{
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
[self setUpMaskLayer];
|
||||
}
|
||||
|
||||
- (IBAction)playButtonTouched:(id)sender
|
||||
{
|
||||
[Flurry logEvent:GPSC_playButtonTouched];
|
||||
|
||||
// Exit if we are in background or transitioning
|
||||
if( ![RBGameViewController isAppRunning] )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int typeId = [_gameData.placeID intValue];
|
||||
if (typeId > 0)
|
||||
{
|
||||
if(_firstGameData.userOwns == NO && _firstGameData.price > 0)
|
||||
{
|
||||
RBConfirmPurchaseViewController* controller = [[RBConfirmPurchaseViewController alloc] initWithNibName:@"RBConfirmPurchaseViewController" bundle:nil];
|
||||
controller.modalPresentationStyle = UIModalPresentationFormSheet;
|
||||
controller.thumbnailAssetID = _firstGameData.placeID;
|
||||
controller.productName = _firstGameData.title;
|
||||
controller.productID = _firstGameData.productID;
|
||||
controller.price = _firstGameData.price;
|
||||
[self presentViewController:controller animated:YES completion:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
RBGameViewController* controller = [[RBGameViewController alloc] initWithLaunchParams:[RBXGameLaunchParams InitParamsForJoinPlace:[_firstGameData.placeID intValue]]];
|
||||
[self presentViewController:controller animated:NO completion:nil];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxHUD showMessage:NSLocalizedString(@"GeneralGameStartError", nil)];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)readMoreButtonTouched:(id)sender
|
||||
{
|
||||
GamePreviewDescriptionViewController* gpdvcPopUp = [[GamePreviewDescriptionViewController alloc] initWithDescription:_gameData.gameDescription];
|
||||
UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:gpdvcPopUp];
|
||||
navigation.modalPresentationStyle = UIModalPresentationFormSheet;
|
||||
[self.navigationController presentViewController:navigation animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (IBAction)didTapPageDot:(id)sender
|
||||
{
|
||||
[_svThumbnails setContentOffset:CGPointMake(_svThumbnails.frame.size.width * _pcItems.currentPage, 0) animated:YES];
|
||||
}
|
||||
|
||||
-(void) handleStartGameFailure
|
||||
{
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
}
|
||||
|
||||
-(void) handleStartGameSuccess
|
||||
{
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
}
|
||||
|
||||
- (void) viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onGamePurchased:) name:RBX_NOTIFY_GAME_PURCHASED object:nil];
|
||||
}
|
||||
|
||||
- (void) onGamePurchased:(NSNotification*)notification
|
||||
{
|
||||
_firstGameData.userOwns = YES;
|
||||
_gameData.userOwns = YES;
|
||||
|
||||
[_playButton setTitle:NSLocalizedString(@"PlayButtonLabel", nil) forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
- (IBAction)builderButtonTouchUpInside:(id)sender
|
||||
{
|
||||
RBProfileViewController *viewController = [self.storyboard instantiateViewControllerWithIdentifier:@"RBOthersProfileViewController"];
|
||||
viewController.userId = _creatorID;
|
||||
[self.navigationController pushViewController:viewController animated:YES];
|
||||
}
|
||||
|
||||
-(void)processTap:(UIGestureRecognizer*)gestureRecognizer
|
||||
{
|
||||
if(gestureRecognizer.state == UIGestureRecognizerStateEnded)
|
||||
{
|
||||
CGPoint location = [gestureRecognizer locationInView:nil]; //Passing nil gives us coordinates in the window
|
||||
|
||||
//Convert tap location into the local view's coordinate system. If outside, dismiss the view.
|
||||
if (![self.view pointInside:[self.view convertPoint:location fromView:self.view.window] withEvent:nil])
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) layoutDetailsElements
|
||||
{
|
||||
CGFloat referenceY = 405.0f;
|
||||
|
||||
if(_gearCollectionViewController.numItems > 0)
|
||||
{
|
||||
_gearCollectionViewController.view.position = CGPointMake(0, referenceY);
|
||||
referenceY += _gearCollectionViewController.view.frame.size.height + DETAILS_Y_OFFSET;
|
||||
_gearCollectionViewController.view.hidden = NO;
|
||||
}
|
||||
else
|
||||
_gearCollectionViewController.view.hidden = YES;
|
||||
|
||||
_moreDetailsView.y = referenceY;
|
||||
referenceY += _moreDetailsView.frame.size.height + DETAILS_Y_OFFSET;
|
||||
|
||||
if(_passesCollectionViewController.numItems > 0)
|
||||
{
|
||||
_passesCollectionViewController.view.position = CGPointMake(0, referenceY);
|
||||
referenceY += _passesCollectionViewController.view.frame.size.height + DETAILS_Y_OFFSET;
|
||||
_passesCollectionViewController.view.hidden = NO;
|
||||
}
|
||||
else
|
||||
_passesCollectionViewController.view.hidden = YES;
|
||||
|
||||
if( _badgesViewController != nil && _badgesViewController.numItems > 0 )
|
||||
{
|
||||
_badgesViewController.view.position = CGPointMake(28, referenceY);
|
||||
_badgesViewController.view.hidden = NO;
|
||||
}
|
||||
else
|
||||
_badgesViewController.view.hidden = YES;
|
||||
|
||||
[_detailsScrollView setContentSizeForDirection:UIScrollViewDirectionVertical];
|
||||
_detailsScrollView.contentSize = CGSizeMake(_detailsScrollView.contentSize.width, _detailsScrollView.contentSize.height + DETAILS_Y_OFFSET);
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// GameSearchResultsScreenController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 6/11/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RBBaseViewController.h"
|
||||
|
||||
@interface GameSearchResultsScreenController : RBBaseViewController
|
||||
|
||||
@property(strong, nonatomic) NSString* keywords;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// GameSearchResultsScreenController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 6/11/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "GameSearchResultsScreenController.h"
|
||||
#import "RBWebGamePreviewScreenController.h"
|
||||
#import "GamesCollectionView.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "RobloxData.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "UIViewController+Helpers.h"
|
||||
#import "Flurry.h"
|
||||
|
||||
//---METRICS---
|
||||
#define GSRSC_showGameDetails @"GAME SEARCH RESULTS SCREEN - Show Game Details"
|
||||
#define GSRSC_showSearchResults @"GAME SEARCH RESULTS SCREEN - Show Search Results"
|
||||
|
||||
@interface GameSearchResultsScreenController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation GameSearchResultsScreenController
|
||||
{
|
||||
GamesCollectionView* _collectionView;
|
||||
}
|
||||
|
||||
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
|
||||
{
|
||||
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
|
||||
if (self) {
|
||||
// Custom initialization
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = [RobloxTheme lightBackground];
|
||||
self.navigationItem.title = NSLocalizedString(@"SearchResultsPhrase", nil);
|
||||
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
|
||||
[self addSearchIconWithSearchType:SearchResultGames andFlurryEvent:nil];
|
||||
|
||||
_collectionView = [[GamesCollectionView alloc] init];
|
||||
[self.view addSubview:_collectionView];
|
||||
|
||||
[_collectionView loadGamesForKeywords:_keywords];
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews
|
||||
{
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
_collectionView.frame = self.view.bounds;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// GameSortResultsScreenController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by alichtin on 5/30/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RBBaseViewController.h"
|
||||
|
||||
@interface GameSortResultsScreenController : RBBaseViewController
|
||||
|
||||
@property(strong, nonatomic) NSNumber* selectedSort;
|
||||
@property (nonatomic, strong) RBXGameGenre *selectedGenre;
|
||||
@property(strong, nonatomic) NSNumber *playerID;
|
||||
|
||||
+ (instancetype) gameSortResultsScreenControllerWithSort:(RBXGameSort *)sort genre:(RBXGameGenre *)genre playerID:(NSNumber *)playerID;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,134 @@
|
||||
//
|
||||
// GameSortResultsScreenController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by alichtin on 5/30/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "GameSortResultsScreenController.h"
|
||||
#import "RBWebGamePreviewScreenController.h"
|
||||
#import "GameSearchResultsScreenController.h"
|
||||
#import "GamesCollectionView.h"
|
||||
#import "RobloxData.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "ExtendedSegmentedControl.h"
|
||||
#import "Flurry.h"
|
||||
#import "RBXEventReporter.h"
|
||||
|
||||
#define MAX_SELECTOR_ITEMS 4
|
||||
|
||||
//---METRICS---
|
||||
#define GSRSC_showSort @"GAME SORT RESULTS SCREEN - Show Sort"
|
||||
#define GSRSC_showGameDetails @"GAME SORT RESULTS SCREEN - Show Game Details"
|
||||
#define GSRSC_showSearchResults @"GAME SORT RESULTS SCREEN - Show Search Results"
|
||||
|
||||
@implementation GameSortResultsScreenController
|
||||
{
|
||||
IBOutlet ExtendedSegmentedControl* _sortSelector;
|
||||
|
||||
NSArray* _sorts;
|
||||
NSMutableArray* _collectionViews;
|
||||
}
|
||||
|
||||
+ (instancetype) gameSortResultsScreenController {
|
||||
return [[UIStoryboard storyboardWithName:@"MainStoryboard_iPad" bundle:nil] instantiateViewControllerWithIdentifier:@"GameSortResultsScreenController"];
|
||||
}
|
||||
|
||||
+ (instancetype) gameSortResultsScreenControllerWithSort:(RBXGameSort *)sort genre:(RBXGameGenre *)genre playerID:(NSNumber *)playerID {
|
||||
GameSortResultsScreenController *controller = [[UIStoryboard storyboardWithName:@"MainStoryboard_iPad" bundle:nil] instantiateViewControllerWithIdentifier:@"GameSortResultsScreenController"];
|
||||
controller.selectedSort = sort.sortID;
|
||||
controller.selectedGenre = genre;
|
||||
controller.playerID = playerID;
|
||||
|
||||
return controller;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = [RobloxTheme lightBackground];
|
||||
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
|
||||
[self addSearchIconWithSearchType:SearchResultGames andFlurryEvent:nil];
|
||||
|
||||
[_sortSelector removeAllSegments];
|
||||
[_sortSelector setMaxVisibleItems:MAX_SELECTOR_ITEMS];
|
||||
|
||||
[RobloxData fetchAllSorts:^(NSArray *sorts)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
_sorts = sorts;
|
||||
[self initSortViews];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews
|
||||
{
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
for(GamesCollectionView* collection in _collectionViews)
|
||||
{
|
||||
[collection setFrame:self.view.bounds];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[[RBXEventReporter sharedInstance] reportScreenLoaded:RBXAContextGamesSeeAll];
|
||||
}
|
||||
|
||||
- (void) initSortViews
|
||||
{
|
||||
_collectionViews = [NSMutableArray array];
|
||||
|
||||
int selectedIndex = -1;
|
||||
for(int i = 0; i < _sorts.count; ++i)
|
||||
{
|
||||
RBXGameSort* sort = _sorts[i];
|
||||
|
||||
[_sortSelector insertSegmentWithTitle:sort.title atIndex:i animated:NO];
|
||||
|
||||
GamesCollectionView* collection = [[GamesCollectionView alloc] init];
|
||||
[_collectionViews addObject:collection];
|
||||
|
||||
if([sort.sortID isEqualToNumber:_selectedSort])
|
||||
{
|
||||
selectedIndex = i;
|
||||
[self showSort:selectedIndex];
|
||||
}
|
||||
}
|
||||
|
||||
[_sortSelector setSelectedSegmentIndex:selectedIndex];
|
||||
}
|
||||
|
||||
- (IBAction)sortSelectionChanged:(id)sender
|
||||
{
|
||||
[self showSort:_sortSelector.selectedSegmentIndex];
|
||||
}
|
||||
|
||||
- (void)showSort:(int)sortIndex
|
||||
{
|
||||
[Flurry logEvent:GSRSC_showSort];
|
||||
for(int i = 0; i < _collectionViews.count; ++i)
|
||||
{
|
||||
GamesCollectionView* view = _collectionViews[i];
|
||||
[view removeFromSuperview];
|
||||
}
|
||||
|
||||
RBXGameSort* sort = _sorts[sortIndex];
|
||||
GamesCollectionView* selectedView = _collectionViews[sortIndex];
|
||||
[self.view addSubview:selectedView];
|
||||
[selectedView loadGamesForSort:sort.sortID playerID:self.playerID genreID:self.selectedGenre.genreID];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// HomeScreenController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 5/23/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RBXEventReporter.h"
|
||||
|
||||
@interface HomeScreenController : UITabBarController<UITabBarControllerDelegate>
|
||||
|
||||
-(RBXAnalyticsCustomData) getCurrentTabContext;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,205 @@
|
||||
//
|
||||
// HomeScreenController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 5/23/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "HomeScreenController.h"
|
||||
#import "UserInfo.h"
|
||||
#import "SignUpScreenController.h"
|
||||
#import "Flurry.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RBMobileWebViewController.h"
|
||||
#import "LoginManager.h"
|
||||
#import "ABTestManager.h"
|
||||
#import "RBMoreViewController.h"
|
||||
#import "RobloxNotifications.h"
|
||||
|
||||
//---METRICS---
|
||||
#define HSC_signUpControllerFromPlayNow @"HOME SCREEN - Sign Up Pressed While Guest"
|
||||
|
||||
@interface HomeScreenController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation HomeScreenController
|
||||
{
|
||||
NSInteger selectedIndexLogin;
|
||||
}
|
||||
|
||||
-(void) viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(gotLoginSuccessfulNotification:)
|
||||
name:RBX_NOTIFY_LOGIN_SUCCEEDED
|
||||
object:nil ];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(gotLogoutNotification:)
|
||||
name:RBX_NOTIFY_LOGGED_OUT
|
||||
object:nil ];
|
||||
}
|
||||
|
||||
-(void) viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.delegate = self;
|
||||
|
||||
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys: [RobloxInfo getUserAgentString], @"UserAgent", nil];
|
||||
[[NSUserDefaults standardUserDefaults] registerDefaults:dictionary];
|
||||
|
||||
|
||||
//check if the user is logged in, if not make the Games Page the landing page
|
||||
if ([UserInfo CurrentPlayer].userLoggedIn == NO)
|
||||
[self setSelectedIndex:1];
|
||||
|
||||
//set the images
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
[((UIViewController*)self.viewControllers[0]).tabBarItem setImage:[[UIImage imageNamed:@"Icon Home Off"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[0]).tabBarItem setSelectedImage:[[UIImage imageNamed:@"Icon Home On"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[1]).tabBarItem setImage:[[UIImage imageNamed:@"Icon Game Off"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[1]).tabBarItem setSelectedImage:[[UIImage imageNamed:@"Icon Game On"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[2]).tabBarItem setImage:[[UIImage imageNamed:@"Icon Catalog Off"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[2]).tabBarItem setSelectedImage:[[UIImage imageNamed:@"Icon Catalog On"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[3]).tabBarItem setImage:[[UIImage imageNamed:@"Icon Friends Off"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[3]).tabBarItem setSelectedImage:[[UIImage imageNamed:@"Icon Friends On"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[4]).tabBarItem setImage:[[UIImage imageNamed:@"Icon Messages Off"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[4]).tabBarItem setSelectedImage:[[UIImage imageNamed:@"Icon Messages On"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[5]).tabBarItem setImage:[[UIImage imageNamed:@"Icon More Off"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[5]).tabBarItem setSelectedImage:[[UIImage imageNamed:@"Icon More On"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
}
|
||||
else
|
||||
{
|
||||
[((UIViewController*)self.viewControllers[0]).tabBarItem setImage:[[UIImage imageNamed:@"Icon Home Off"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[0]).tabBarItem setSelectedImage:[[UIImage imageNamed:@"Icon Home On"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[1]).tabBarItem setImage:[[UIImage imageNamed:@"Icon Game Off"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[1]).tabBarItem setSelectedImage:[[UIImage imageNamed:@"Icon Game On"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[2]).tabBarItem setImage:[[UIImage imageNamed:@"Icon Friends Off"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[2]).tabBarItem setSelectedImage:[[UIImage imageNamed:@"Icon Friends On"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[3]).tabBarItem setImage:[[UIImage imageNamed:@"Icon Messages Off"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[3]).tabBarItem setSelectedImage:[[UIImage imageNamed:@"Icon Messages On"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[4]).tabBarItem setImage:[[UIImage imageNamed:@"Icon More Off"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
[((UIViewController*)self.viewControllers[4]).tabBarItem setSelectedImage:[[UIImage imageNamed:@"Icon More On"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-(RBXAnalyticsCustomData) getCurrentTabContext
|
||||
{
|
||||
RBXAnalyticsCustomData currentTab = RBXACustomTabGames;
|
||||
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
switch (self.selectedIndex)
|
||||
{
|
||||
case 0 : { currentTab = RBXACustomTabHome; } break;
|
||||
case 1 : { currentTab = RBXACustomTabGames; } break;
|
||||
case 2 : { currentTab = RBXACustomTabCatalog; } break;
|
||||
case 3 : { currentTab = RBXACustomTabFriends; } break;
|
||||
case 4 : { currentTab = RBXACustomTabMessages; } break;
|
||||
case 5 : {
|
||||
currentTab = RBXACustomTabMore;
|
||||
|
||||
if ([self.viewControllers[5] respondsToSelector:@selector(getMostRecentTab)])
|
||||
currentTab = [((RBMoreViewController*)self.viewControllers[5]) getMostRecentTab];
|
||||
} break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (self.selectedIndex)
|
||||
{
|
||||
case 0 : { currentTab = RBXACustomTabHome; } break;
|
||||
case 1 : { currentTab = RBXACustomTabGames; } break;
|
||||
case 2 : { currentTab = RBXACustomTabFriends; } break;
|
||||
case 3 : { currentTab = RBXACustomTabMessages; } break;
|
||||
case 4 : {
|
||||
currentTab = RBXACustomTabMore;
|
||||
|
||||
if ([self.viewControllers[4] respondsToSelector:@selector(getMostRecentTab)])
|
||||
currentTab = [((RBMoreViewController*)self.viewControllers[4]) getMostRecentTab];
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
return currentTab;
|
||||
}
|
||||
|
||||
|
||||
- (BOOL)tabBarController:(UITabBarController *)tabBarController shouldSelectViewController:(UIViewController *)viewController
|
||||
{
|
||||
NSUInteger indexOfTab = [self.viewControllers indexOfObject:viewController];
|
||||
if (self.selectedIndex == indexOfTab && self.viewControllers.count > 1)
|
||||
{
|
||||
//drill back to the original screen
|
||||
UINavigationController* tappedVC = (UINavigationController*)viewController;
|
||||
UIViewController* rootVC = tappedVC.viewControllers[0];
|
||||
[tappedVC popToViewController:rootVC animated:YES];
|
||||
|
||||
//if it is a webview, reload the original url
|
||||
if ([rootVC isKindOfClass:[RBMobileWebViewController class]])
|
||||
{
|
||||
[(RBMobileWebViewController*)rootVC reloadWebPage];
|
||||
}
|
||||
else if ([viewController isEqual:[[self viewControllers] objectAtIndex:1]] && [RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
//if we are on the games page, reload the games
|
||||
if ([rootVC respondsToSelector:@selector(loadGames)])
|
||||
[rootVC performSelector:@selector(loadGames)];
|
||||
|
||||
}
|
||||
//return NO;
|
||||
}
|
||||
|
||||
UserInfo* userInfo = [UserInfo CurrentPlayer];
|
||||
if(!userInfo.userLoggedIn && ![viewController isEqual:[[self viewControllers] objectAtIndex:1]])
|
||||
{
|
||||
NSString* controllerName = [[LoginManager sharedInstance] isFacebookEnabled] ? @"SignUpScreenControllerWithSocial" : @"SignUpScreenController";
|
||||
|
||||
|
||||
//ask the user to sign up or log in to see these pages
|
||||
[Flurry logEvent:HSC_signUpControllerFromPlayNow];
|
||||
NSString* storyboardName = [RobloxInfo getStoryboardName];
|
||||
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
|
||||
SignUpScreenController* controller = (SignUpScreenController*)[storyboard instantiateViewControllerWithIdentifier:controllerName];
|
||||
controller.modalPresentationStyle = UIModalPresentationFormSheet;
|
||||
[self.navigationController presentViewController:controller animated:YES completion:nil];
|
||||
selectedIndexLogin = [self.viewControllers indexOfObject:viewController];
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
-(void)tabBarController:(UITabBarController *)tabBarController didSelectViewController:(UIViewController *)viewController
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportTabButtonClick:[self getCurrentTabContext]];
|
||||
}
|
||||
|
||||
|
||||
-(void) gotLoginSuccessfulNotification:(NSNotification*) notification
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self setSelectedIndex:selectedIndexLogin];
|
||||
});
|
||||
}
|
||||
-(void) gotLogoutNotification:(NSNotification*) notification
|
||||
{
|
||||
if ([[ABTestManager sharedInstance] IsInTestMobileGuestMode])
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self setSelectedIndex:1];
|
||||
});
|
||||
}
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// InfoScreenController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "NonRotatableViewController.h"
|
||||
|
||||
@interface InfoScreenController : NonRotatableViewController <UIWebViewDelegate>
|
||||
@property (retain, nonatomic) IBOutlet UITextView *txtDeviceInfo;
|
||||
@property (retain, nonatomic) IBOutlet UITextView *txtRobloxInfo;
|
||||
@end
|
||||
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// InfoScreenController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "InfoScreenController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "TermsAgreementController.h"
|
||||
|
||||
#define DEFAULT_VIEW_SIZE CGRectMake(0, 0, 540, 360)
|
||||
|
||||
@interface InfoScreenController ()
|
||||
@end
|
||||
|
||||
@implementation InfoScreenController
|
||||
{
|
||||
IBOutlet UINavigationBar *_navBar;
|
||||
IBOutlet UIWebView *_finePrint;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// View delegates
|
||||
////////////////////////////////////////////////////////////////
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
// Stylize elements
|
||||
[RobloxTheme applyToModalPopupNavBar:_navBar];
|
||||
|
||||
// Localize strings
|
||||
_navBar.topItem.title = NSLocalizedString(@"InfoWord", nil);
|
||||
|
||||
[_txtRobloxInfo setText:NSLocalizedString(@"InfoDisclaimerPhrase", nil)];
|
||||
|
||||
//get the device info
|
||||
NSString* urlString = [[RobloxInfo getApiBaseUrl] stringByAppendingString:@"/reference/deviceinfo"];
|
||||
NSURL *url = [NSURL URLWithString: urlString];
|
||||
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:url
|
||||
cachePolicy:NSURLRequestReloadIgnoringCacheData
|
||||
timeoutInterval:60*7];
|
||||
[RobloxInfo setDefaultHTTPHeadersForRequest:theRequest];
|
||||
[theRequest setHTTPMethod:@"GET"];
|
||||
|
||||
NSData *receivedData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:nil error:NULL];
|
||||
if (receivedData)
|
||||
{
|
||||
NSString *deviceInfo = [[NSString alloc] initWithData:receivedData encoding:NSUTF8StringEncoding];
|
||||
|
||||
NSCharacterSet *trim = [NSCharacterSet characterSetWithCharactersInString:@"{}\""];
|
||||
|
||||
deviceInfo = [[deviceInfo componentsSeparatedByCharactersInSet:trim] componentsJoinedByString:@" "];
|
||||
deviceInfo = [deviceInfo stringByReplacingOccurrencesOfString:@"Type :" withString:@":"];
|
||||
deviceInfo = [deviceInfo stringByReplacingOccurrencesOfString:@"," withString:@"\n"];
|
||||
_txtDeviceInfo.text = deviceInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
_txtDeviceInfo.text = @"";
|
||||
}
|
||||
|
||||
// Fine print
|
||||
NSString *htmlFile = [[NSBundle mainBundle] pathForResource:@"SignUpDisclamer" ofType:@"html" inDirectory:nil];
|
||||
if(htmlFile)
|
||||
{
|
||||
NSString* htmlContents = [NSString stringWithContentsOfFile:htmlFile encoding:NSUTF8StringEncoding error:NULL];
|
||||
htmlContents = [htmlContents stringByReplacingOccurrencesOfString:@"textPlaceholder" withString:NSLocalizedString(@"HomeFinePrintWords", nil)];
|
||||
[_finePrint loadData:[htmlContents dataUsingEncoding:NSUTF8StringEncoding] MIMEType:@"text/html" textEncodingName:@"UTF-8" baseURL:[NSURL URLWithString:@""]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)viewWillLayoutSubviews
|
||||
{
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
self.view.superview.bounds = DEFAULT_VIEW_SIZE;
|
||||
}
|
||||
- (void) viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[_finePrint stopLoading];
|
||||
}
|
||||
|
||||
- (BOOL)disablesAutomaticKeyboardDismissal
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Action delegates
|
||||
////////////////////////////////////////////////////////////////
|
||||
- (IBAction)closeController:(id)sender
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Fine print
|
||||
////////////////////////////////////////////////////////////////
|
||||
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
|
||||
{
|
||||
NSString* urlRequestString = [[request URL] absoluteString];
|
||||
NSRange finePrintInit = [urlRequestString rangeOfString:@"file"];
|
||||
if(finePrintInit.location != NSNotFound)
|
||||
return YES;
|
||||
|
||||
[self performSegueWithIdentifier:@"FinePrintSegue" sender:urlRequestString];
|
||||
//[[UIApplication sharedApplication] openURL:request.URL];
|
||||
|
||||
return NO;
|
||||
}
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
|
||||
{
|
||||
if([segue.identifier isEqualToString:@"FinePrintSegue"])
|
||||
{
|
||||
TermsAgreementController *controller = (TermsAgreementController *)segue.destinationViewController;
|
||||
controller.url = sender;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// LoadingScreenViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 9/27/15.
|
||||
// Copyright © 2015 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
@interface LoadingScreenViewController : UIViewController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,235 @@
|
||||
//
|
||||
// LoadingScreenViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 9/27/15.
|
||||
// Copyright © 2015 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "AppDelegateROMA.h"
|
||||
|
||||
#import "ABTestManager.h"
|
||||
#import "LoadingScreenViewController.h"
|
||||
#import "LoginManager.h"
|
||||
#import "RBActivityIndicatorView.h"
|
||||
#import "RBXFunctions.h"
|
||||
#import "RobloxData.h"
|
||||
#import "RobloxCachedFlags.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxWebUtility.h"
|
||||
#import "SessionReporter.h"
|
||||
#import "StoreManager.h"
|
||||
#import "UpgradeCheckHelper.h"
|
||||
#import "UserInfo.h"
|
||||
|
||||
#import <AdColony/AdColony.h>
|
||||
|
||||
DYNAMIC_FASTSTRING(AdColonyAppId);
|
||||
DYNAMIC_FASTSTRING(AdColonyZoneId);
|
||||
|
||||
///TO DO: MIGRATE ALL INITIALIZATION CODE THAT DOES NOT REQUIRE KNOWLEDGE
|
||||
/// OF THE APP STATE OUT OF THE APP DELEGATE AND INTO THIS SCREEN
|
||||
|
||||
@interface LoadingScreenViewController ()
|
||||
|
||||
@property IBOutlet UILabel* lblMessage;
|
||||
@property IBOutlet RBActivityIndicatorView* loadingSpinner;
|
||||
@property IBOutlet UIImageView* imgLogo;
|
||||
|
||||
@property (nonatomic) int completedTasks;
|
||||
|
||||
@property (nonatomic) BOOL initializingDevice;
|
||||
|
||||
@property (nonatomic) NSDate* initializationStartTime;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LoadingScreenViewController
|
||||
|
||||
//Life Cycle Functions
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[_lblMessage setText:NSLocalizedString(@"LoadingWord", nil)];
|
||||
[_lblMessage setHidden:YES];
|
||||
|
||||
[_loadingSpinner setHidden:YES];
|
||||
}
|
||||
|
||||
-(void) viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
[RBXFunctions dispatchOnMainThread:^
|
||||
{
|
||||
[_loadingSpinner setHidden:NO];
|
||||
[_loadingSpinner startAnimating];
|
||||
[_lblMessage setHidden:NO];
|
||||
}];
|
||||
|
||||
[self initializeDevice];
|
||||
}
|
||||
|
||||
//Task Functions
|
||||
-(void) initializeDevice
|
||||
{
|
||||
///USE THIS FUNCTION TO PERFORM ANY ACTIONS AND CONVEY MESSAGES TO THE USER
|
||||
_initializationStartTime = [NSDate date];
|
||||
|
||||
|
||||
//STEP 1) we need to call this on start up, to create/save a browser tracker
|
||||
[self updateMessage:NSLocalizedString(@"LoadingMessageInitDevice", nil)];
|
||||
NSDate* reportingTime = [NSDate date];
|
||||
[RobloxData initializeBrowserTrackerWithCompletion:^(bool success, NSString* browserTracker)
|
||||
{
|
||||
//keep track of how long it took to initalize the browser tracker, we cannot report it yet
|
||||
NSTimeInterval initializeTime = [[NSDate date] timeIntervalSinceDate:reportingTime];
|
||||
|
||||
//Configure the analytics
|
||||
[[RBXEventReporter sharedInstance] reportAppLaunch:RBXAContextAppLaunch]; //this is timestamped to measure the time from launch to "ready"
|
||||
|
||||
|
||||
// Update all client settings
|
||||
// NOTE - analytic reporting for flag loading is already handled within this function
|
||||
[self updateAllClientSettingsWithCompletion:^
|
||||
{
|
||||
//now that we have client flags, we can send reports out send out analytic reports
|
||||
[[SessionReporter sharedInstance] postStartupPayloadForEvent:@"deviceInitialize" completionTime:initializeTime];
|
||||
|
||||
//initialize some flag settings
|
||||
[self initCrashlytics];
|
||||
[self initAdColony];
|
||||
|
||||
//fetch the AB Tests (legacy synchronous operation)
|
||||
[self initABTests];
|
||||
|
||||
//last of all
|
||||
[self doTaskAutoLogin];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
-(void) updateAllClientSettingsWithCompletion:(void(^)(void))handler
|
||||
{
|
||||
[self updateMessage:NSLocalizedString(@"LoadingMessageInitFlags", nil)];
|
||||
|
||||
//Code Migrated from AppDelegateROMA - 2/25/2016
|
||||
[RBXFunctions dispatchOnMainThread:^
|
||||
{
|
||||
RobloxWebUtility * robloxWebUtility = [RobloxWebUtility sharedInstance];
|
||||
|
||||
//Make sure that the base URL has been set
|
||||
//NOTE- This must be initialized or else the WebUtility will crash
|
||||
[RobloxInfo getBaseUrl];
|
||||
|
||||
//update the settings
|
||||
[robloxWebUtility updateAllClientSettingsWithReporting:YES withCompletion:handler];
|
||||
}];
|
||||
}
|
||||
-(void) initCrashlytics
|
||||
{
|
||||
//Code Migrated from AppDelegateROMA - 2/25/2016
|
||||
[self updateMessage:NSLocalizedString(@"LoadingMessageInitCrashlytics", nil)];
|
||||
|
||||
[RBXFunctions dispatchOnMainThread:^
|
||||
{
|
||||
iOSSettingsService* iss = [[RobloxWebUtility sharedInstance] getCachediOSSettings];
|
||||
[[RobloxCachedFlags sharedInstance] setInt:@"CrashlyticsPercentage" withValue:iss->GetValueCrashlyticsPercentage()];
|
||||
[[RobloxCachedFlags sharedInstance] sync];
|
||||
}];
|
||||
|
||||
}
|
||||
-(void) initAdColony
|
||||
{
|
||||
//Code Migrated from AppDelegateROMA - 2/25/2016
|
||||
[self updateMessage:NSLocalizedString(@"LoadingMessageInitAdColony", nil)];
|
||||
|
||||
[RBXFunctions dispatchOnMainThread:^
|
||||
{
|
||||
NSString* adColonyAppId = [NSString stringWithUTF8String:DFString::AdColonyAppId.c_str()];
|
||||
NSString* adColonyZoneId = [NSString stringWithUTF8String:DFString::AdColonyZoneId.c_str()];
|
||||
|
||||
[AdColony configureWithAppID:adColonyAppId zoneIDs:@[adColonyZoneId] delegate:nil logging:YES];
|
||||
|
||||
}];
|
||||
}
|
||||
-(void) initABTests
|
||||
{
|
||||
// initalize the AB Test - this may take a bit - it is a synchronous request
|
||||
[self updateMessage:NSLocalizedString(@"LoadingMessageInitExperiments", nil)];
|
||||
|
||||
NSDate* fetchingExperimentsStart = [NSDate date];
|
||||
[[ABTestManager sharedInstance] fetchExperimentsForBrowserTracker];
|
||||
NSTimeInterval fetchExperimentsTime = [[NSDate date] timeIntervalSinceDate:fetchingExperimentsStart];
|
||||
|
||||
//report how long it took to initialize the AB Tests
|
||||
[[SessionReporter sharedInstance] postStartupPayloadForEvent:@"fetchABTestExperiments" completionTime:fetchExperimentsTime];
|
||||
}
|
||||
-(void) doTaskAutoLogin
|
||||
{
|
||||
//attempt to automatically log in
|
||||
NSString* message;
|
||||
if ([[LoginManager sharedInstance] hasLoginCredentials] || [[LoginManager sharedInstance] hasSocialLoginCredentials] || [LoginManager sessionLoginEnabled])
|
||||
message = NSLocalizedString(@"LoadingMessageAutoLogin", nil);
|
||||
|
||||
if (message)
|
||||
{
|
||||
[self updateMessage:message];
|
||||
|
||||
NSDate* autoLoginTaskStart = [NSDate date];
|
||||
[[LoginManager sharedInstance] processStartupAutoLogin:^(NSError *loginError)
|
||||
{
|
||||
if ([LoginManager sessionLoginEnabled])
|
||||
{
|
||||
//report how long a session login took
|
||||
[[SessionReporter sharedInstance] postStartupPayloadForEvent:@"fetchUserInfo" completionTime:[[NSDate date] timeIntervalSinceDate:autoLoginTaskStart]];
|
||||
}
|
||||
|
||||
|
||||
//check for a successful login
|
||||
if ([RBXFunctions isEmpty:loginError])
|
||||
{
|
||||
// Initialize Store Manager on login, so that any pending transaction from last session are queued
|
||||
GetStoreMgr;
|
||||
}
|
||||
|
||||
|
||||
//regarless if we are successfully logged in or not, go to the Welcome Screen
|
||||
//The Welcome Screen will decide what screen to display
|
||||
[self goToWelcomeScreen];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
//the app has no stored credentials and session login is not enabled, escape and go to the Welcome Screen
|
||||
[self goToWelcomeScreen];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Helper Functions
|
||||
-(void) updateMessage:(NSString*)message
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{ [_lblMessage setText:message]; }];
|
||||
}
|
||||
|
||||
//Navigation Functions
|
||||
-(void) goToWelcomeScreen
|
||||
{
|
||||
//report the total time for startup
|
||||
[[SessionReporter sharedInstance] postStartupPayloadForEvent:@"startupFinished" completionTime:[[NSDate date] timeIntervalSinceDate:_initializationStartTime]];
|
||||
|
||||
//We are done here, let's move on
|
||||
//if (_lblMessage.text == nil || _lblMessage.text.length == 0)
|
||||
// [_lblMessage setText:NSLocalizedString(@"LoadingMessageComplete", nil)];
|
||||
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[self performSegueWithIdentifier:@"ShowWelcomeScreen" sender:self];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// LoginScreenController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 5/20/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "NonRotatableViewController.h"
|
||||
|
||||
typedef enum : NSUInteger {
|
||||
LoginScreenDismissalUnknown = 0,
|
||||
LoginScreenDismissalCancelled = 1,
|
||||
LoginScreenDismissalLoginSuccess = 2,
|
||||
LoginScreenDismissalLoginFailed = 3
|
||||
} LoginScreenDismissalType;
|
||||
|
||||
typedef void(^DismissalCompletionHandler)(LoginScreenDismissalType dismissType, NSError *loginError);
|
||||
|
||||
@interface LoginScreenController : NonRotatableViewController <UIGestureRecognizerDelegate>
|
||||
|
||||
@property (nonatomic, copy) DismissalCompletionHandler dismissalCompletionHandler;
|
||||
|
||||
-(void) setUsername:(NSString*)username andPassword:(NSString*)password;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,403 @@
|
||||
//
|
||||
// LoginScreenController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 5/20/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
#import "AppDelegateROMA.h"
|
||||
#import "LoginScreenController.h"
|
||||
#import "UserInfo.h"
|
||||
#import "LoginManager.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "RobloxAlert.h"
|
||||
#import "StoreManager.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RBResetPasswordViewController.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "Flurry.h"
|
||||
#import "KeychainItemWrapper.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "RobloxWebUtility.h"
|
||||
#import "iOSSettingsService.h"
|
||||
#import "RBCaptchaViewController.h"
|
||||
#import "RBCaptchaV2ViewController.h"
|
||||
#import "NonRotatableNavigationController.h"
|
||||
#import "RBXEventReporter.h"
|
||||
#import "RBValidTextField.h"
|
||||
#import "SignUpScreenController.h"
|
||||
#import "RBXFunctions.h"
|
||||
|
||||
#define PASSWORD_RESET_URL @"/Login/ResetPasswordRequest.aspx"
|
||||
#ifndef kCFCoreFoundationVersionNumber_iOS_8_0
|
||||
#define kCFCoreFoundationVersionNumber_iOS_8_0 1129.15
|
||||
#endif
|
||||
|
||||
//---METRICS---
|
||||
#define LSC_loginSelected @"LOG IN SCREEN - Log In Selected"
|
||||
#define LSC_forgotPasswordSelected @"LOG IN SCREEN - Forgot Password Selected"
|
||||
|
||||
@interface LoginScreenController ()
|
||||
//public properties
|
||||
@property IBOutlet RBValidTextField* username;
|
||||
@property IBOutlet RBValidTextField* password;
|
||||
|
||||
@end
|
||||
|
||||
@implementation LoginScreenController
|
||||
{
|
||||
//private properties
|
||||
IBOutlet UILabel* _loginTitle;
|
||||
IBOutlet UILabel* _notAMemberLabel;
|
||||
IBOutlet UIButton* _notAMemberButton;
|
||||
IBOutlet UIButton* _forgotPasswordButton;
|
||||
IBOutlet UIButton* _loginButton;
|
||||
IBOutlet UIButton* _cancelButton;
|
||||
IBOutlet UIView* _whiteView;
|
||||
|
||||
UITapGestureRecognizer* _touches;
|
||||
|
||||
NSString* _segueUsername;
|
||||
NSString* _seguePassword;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
#pragma mark - View functions
|
||||
////////////////////////////////////////////////////////////////
|
||||
- (void) viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
_touches = [[UITapGestureRecognizer alloc] init];
|
||||
[_touches setNumberOfTouchesRequired:1];
|
||||
[_touches setNumberOfTapsRequired:1];
|
||||
[_touches setDelegate:self];
|
||||
[_touches setEnabled:YES];
|
||||
[self.view addGestureRecognizer:_touches];
|
||||
|
||||
|
||||
if (![RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
[_whiteView.layer setShadowColor:[UIColor blackColor].CGColor];
|
||||
[_whiteView.layer setShadowOpacity:0.4];
|
||||
[_whiteView.layer setShadowRadius:2.0];
|
||||
[_whiteView.layer setShadowOffset:CGSizeMake(0.0, 0.5)];
|
||||
}
|
||||
|
||||
__weak LoginScreenController* weakself = self;
|
||||
|
||||
// Stylize elements
|
||||
[_username setTitle:NSLocalizedString(@"UsernameWord", nil)];
|
||||
[_username setNextResponder:_password];
|
||||
if (_segueUsername != nil && _segueUsername.length > 0)
|
||||
{
|
||||
[_username setText:_segueUsername];
|
||||
_segueUsername = nil;
|
||||
}
|
||||
else if ([UserInfo CurrentPlayer].username != nil)
|
||||
[_username setText:[UserInfo CurrentPlayer].username];
|
||||
|
||||
[_password setTitle:NSLocalizedString(@"PasswordWord", nil)];
|
||||
[_password setProtectedTextEntry:YES];
|
||||
[_password setExitOnEnterBlock:^{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[weakself login:weakself.password];
|
||||
});
|
||||
}];
|
||||
if (_seguePassword != nil && _seguePassword.length > 0)
|
||||
{
|
||||
[_password setText:_seguePassword];
|
||||
_seguePassword = nil;
|
||||
}
|
||||
//else if ([[LoginManager sharedInstance] getRememberPassword])
|
||||
// [_password setText:[UserInfo CurrentPlayer].password];
|
||||
|
||||
[_loginTitle setText:[NSLocalizedString(@"LoginWord", nil) uppercaseString]];
|
||||
[_loginButton setTitle:NSLocalizedString(@"LoginTitle", nil) forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToModalSubmitButton:_loginButton];
|
||||
|
||||
[_cancelButton setTitle:NSLocalizedString(@"CancelWord", nil) forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToModalCancelButton:_cancelButton];
|
||||
|
||||
[_notAMemberLabel setFont:[RobloxTheme fontBodySmall]];
|
||||
[_notAMemberLabel setText:NSLocalizedString(@"NotAMemberPhrase", nil)];
|
||||
[_notAMemberButton setTitle:NSLocalizedString(@"SignupWord", nil) forState:UIControlStateNormal];
|
||||
[_notAMemberButton.titleLabel setFont:[RobloxTheme fontBodySmall]];
|
||||
[_notAMemberButton addTarget:self action:@selector(didPressSignUp) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
[_forgotPasswordButton setTitle:NSLocalizedString(@"Forgot Password?", nil) forState:UIControlStateNormal];
|
||||
[_forgotPasswordButton.titleLabel setFont:[RobloxTheme fontBodySmall]];
|
||||
[_forgotPasswordButton addTarget:self action:@selector(didPressForgotPassword) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
if ([self isForgotPasswordLinkEnabled])
|
||||
_forgotPasswordButton.hidden = NO;
|
||||
else
|
||||
_forgotPasswordButton.hidden = ![RobloxInfo thisDeviceIsATablet];
|
||||
}
|
||||
- (void) viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
[self resignFirstResponder];
|
||||
|
||||
[[LoginManager sharedInstance] setRememberPassword:YES];
|
||||
}
|
||||
- (void) viewWillLayoutSubviews {
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
BOOL isPreiOS8 = NSFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_8_0;
|
||||
if (isPreiOS8 && [RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
self.view.superview.bounds = CGRectMake(0, 0, 540, 296);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
#pragma mark - Accessors and Mutators
|
||||
////////////////////////////////////////////////////////////////
|
||||
- (BOOL) isForgotPasswordLinkEnabled
|
||||
{
|
||||
iOSSettingsService* iOSSettings = [[RobloxWebUtility sharedInstance] getCachediOSSettings];
|
||||
return iOSSettings->GetValueEnableLinkForgottenPassword();
|
||||
}
|
||||
|
||||
-(void) setUsername:(NSString *)username andPassword:(NSString *)password
|
||||
{
|
||||
if (_username)
|
||||
[_username setText:username ? username : @""];
|
||||
else
|
||||
_segueUsername = username;
|
||||
|
||||
|
||||
if (_password)
|
||||
[_password setText:password ? password : @""];
|
||||
else
|
||||
_seguePassword = password;
|
||||
}
|
||||
|
||||
- (void) executeDismissalCompletionBlockWithDismissType:(LoginScreenDismissalType)dismissType error:(NSError *)loginError {
|
||||
if (nil != self.dismissalCompletionHandler) {
|
||||
self.dismissalCompletionHandler(dismissType, loginError);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
#pragma mark - UI Actions
|
||||
////////////////////////////////////////////////////////////////
|
||||
- (IBAction)closeController:(id)sender {
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonClose
|
||||
withContext:RBXAContextLogin];
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
[self executeDismissalCompletionBlockWithDismissType:LoginScreenDismissalCancelled error:nil];
|
||||
}
|
||||
|
||||
- (IBAction)login:(id)sender {
|
||||
|
||||
bool hasUsername = _username.text.length > 0;
|
||||
bool hasPassword = _password.text.length > 0;
|
||||
if (!hasUsername)
|
||||
{
|
||||
//[RobloxAlert RobloxAlertWithMessage:NSLocalizedString(@"UsernameMissing", nil)];
|
||||
[_username showError:NSLocalizedString(@"UsernameMissing", nil)];
|
||||
[_username markAsInvalid];
|
||||
[_username becomeFirstResponder];
|
||||
[[RBXEventReporter sharedInstance] reportFormFieldValidation:RBXAFieldUsername
|
||||
withContext:RBXAContextLogin
|
||||
withError:RBXAErrorMissingRequiredField];
|
||||
}
|
||||
if (!hasPassword)
|
||||
{
|
||||
//[RobloxAlert RobloxAlertWithMessage:NSLocalizedString(@"PasswordMissing", nil)];
|
||||
[_password showError:NSLocalizedString(@"PasswordMissing", nil)];
|
||||
[_password markAsInvalid];
|
||||
|
||||
if (hasUsername)
|
||||
[_password becomeFirstResponder];
|
||||
|
||||
[[RBXEventReporter sharedInstance] reportFormFieldValidation:RBXAFieldPassword
|
||||
withContext:RBXAContextLogin
|
||||
withError:RBXAErrorMissingRequiredField];
|
||||
}
|
||||
|
||||
if (hasUsername && hasPassword)
|
||||
{
|
||||
[Flurry logEvent:LSC_loginSelected];
|
||||
[self showLoggingIn];
|
||||
|
||||
[[LoginManager sharedInstance] loginWithUsername:_username.text password:_password.text completionBlock:^(NSError *loginError) {
|
||||
if ([RBXFunctions isEmpty:loginError]) {
|
||||
// Initialize Store Manager on login, so that any pending transaction from last session are queued
|
||||
GetStoreMgr;
|
||||
|
||||
//save the username and password to the keychain
|
||||
if ([LoginManager sessionLoginEnabled] == NO) {
|
||||
KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:[[[NSBundle mainBundle] bundleIdentifier] stringByAppendingString:@"RobloxLogin"] accessGroup:nil];
|
||||
[keychainItem setObject:[UserInfo CurrentPlayer].password forKey:(__bridge id)kSecValueData];
|
||||
[keychainItem setObject:[UserInfo CurrentPlayer].username forKey:(__bridge id)kSecAttrAccount];
|
||||
}
|
||||
|
||||
//update the UI
|
||||
[self stopShowLoggingIn]; // <-- this is sent on the main thread already
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
//clear out the password
|
||||
_password.text = @"";
|
||||
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
[self executeDismissalCompletionBlockWithDismissType:LoginScreenDismissalLoginSuccess error:nil];
|
||||
});
|
||||
} else {
|
||||
NSString* errorMessage = loginError.domain;
|
||||
|
||||
if (!errorMessage || errorMessage.length == 0)
|
||||
errorMessage = NSLocalizedString(@"UnknownLoginError", nil);
|
||||
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[self stopShowLoggingIn];
|
||||
|
||||
//only clear the password text for specific errors
|
||||
if ([errorMessage isEqualToString:NSLocalizedString(@"InvalidUsernameOrPw", nil)])
|
||||
{
|
||||
_password.text = @"";
|
||||
[_password markAsInvalid];
|
||||
[_password showError:errorMessage];
|
||||
|
||||
[_password becomeFirstResponder];
|
||||
//[RobloxHUD prompt:errorMessage withTitle:NSLocalizedString(@"ErrorWord",nil)];
|
||||
}
|
||||
else if ([errorMessage isEqualToString:NSLocalizedString(@"TooManyAttempts", nil)])
|
||||
{
|
||||
//open up a captcha so we can attempt to log in again
|
||||
NonRotatableNavigationController* navigation = [LoginManager CaptchaForLoginWithUsername:_username.text
|
||||
andV1Completion:^(bool success, NSString *message)
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^
|
||||
{
|
||||
if (success == YES)
|
||||
{
|
||||
[self login:nil];
|
||||
}
|
||||
}];
|
||||
|
||||
}
|
||||
andV2Completion:^(NSError *captchaError)
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^
|
||||
{
|
||||
if ([RBXFunctions isEmpty:captchaError])
|
||||
{
|
||||
[self login:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxHUD prompt:errorMessage withTitle:NSLocalizedString(@"ErrorWord", nil)];
|
||||
[self executeDismissalCompletionBlockWithDismissType:LoginScreenDismissalLoginFailed error:loginError];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
[self presentViewController:navigation animated:YES completion:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxHUD prompt:errorMessage withTitle:NSLocalizedString(@"ErrorWord", nil)];
|
||||
}
|
||||
|
||||
//Why is this completion block being told to execute? The screen isn't being dismissed - Kyler 1/8/2015
|
||||
//[self executeDismissalCompletionBlockWithDismissType:LoginScreenDismissalLoginFailed error:loginError];
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didPressForgotPassword {
|
||||
[Flurry logEvent:LSC_forgotPasswordSelected];
|
||||
NSString* baseURL = [RobloxInfo getWWWBaseUrl];
|
||||
baseURL = [baseURL stringByAppendingString:PASSWORD_RESET_URL];
|
||||
|
||||
NSURL* url = [NSURL URLWithString:baseURL];
|
||||
|
||||
RBResetPasswordViewController* controller = [[RBResetPasswordViewController alloc] initWithURL:url andTitle:NSLocalizedString(@"Reset Password", nil)];
|
||||
UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:controller];
|
||||
UIViewController *presenter = self.presentingViewController;
|
||||
|
||||
[self dismissViewControllerAnimated:NO completion:nil];
|
||||
[self executeDismissalCompletionBlockWithDismissType:LoginScreenDismissalCancelled error:nil];
|
||||
[presenter presentViewController:navigation animated:YES completion:nil];
|
||||
}
|
||||
- (void)didPressSignUp{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonSignup withContext:RBXAContextLogin];
|
||||
|
||||
NSString* controllerName;
|
||||
if ([[LoginManager sharedInstance] isFacebookEnabled])
|
||||
controllerName = @"SignUpScreenControllerWithSocial";
|
||||
else if ([LoginManager apiProxyEnabled])
|
||||
controllerName = @"SignUpAPIScreenController";
|
||||
else
|
||||
controllerName = @"SignUpScreenController";
|
||||
|
||||
UIViewController *presenter = self.presentingViewController;
|
||||
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:[RobloxInfo getStoryboardName] bundle:nil];
|
||||
SignUpScreenController* controller = (SignUpScreenController*)[storyboard instantiateViewControllerWithIdentifier:controllerName];
|
||||
|
||||
BOOL isPreiOS8 = NSFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_8_0;
|
||||
if (isPreiOS8 && [RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:^{
|
||||
[presenter presentViewController:controller animated:YES completion:nil];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
[presenter presentViewController:controller animated:YES completion:nil];
|
||||
}
|
||||
|
||||
[self executeDismissalCompletionBlockWithDismissType:LoginScreenDismissalCancelled error:nil];
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
#pragma mark - Delegate Functions
|
||||
////////////////////////////////////////////////////////////////
|
||||
- (BOOL)disablesAutomaticKeyboardDismissal {
|
||||
return NO;
|
||||
}
|
||||
-(void) resignAllResponders
|
||||
{
|
||||
[self.view endEditing:YES];
|
||||
//RBValidTextField* activeField;
|
||||
//if ([_username isEditing]) activeField = _username;
|
||||
//else if ([_password isEditing]) activeField = _password;
|
||||
//
|
||||
//if (activeField)
|
||||
// [activeField resignFirstResponder];
|
||||
}
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
|
||||
{
|
||||
UIView* touchedView = touch.view;
|
||||
if (touchedView == self.view || touchedView == _whiteView)
|
||||
[self resignAllResponders];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Login
|
||||
////////////////////////////////////////////////////////////////
|
||||
- (void) showLoggingIn
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_username endEditing:YES];
|
||||
[_password endEditing:YES];
|
||||
|
||||
[RobloxHUD showSpinnerWithLabel:NSLocalizedString(@"LoggingIn", nil) dimBackground:YES];
|
||||
});
|
||||
}
|
||||
- (void) stopShowLoggingIn
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[RobloxHUD hideSpinner:NO];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// MessagesDetailController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 6/19/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RobloxData.h"
|
||||
#import "RobloxImageView.h"
|
||||
#import "RBBaseViewController.h"
|
||||
|
||||
@interface MessageCell : UITableViewCell
|
||||
@property (strong, nonatomic) IBOutlet UIImageView* unreadIndicator;
|
||||
@property (strong, nonatomic) IBOutlet RobloxImageView *playerAvatar;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *playerNameLabel;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *dateLabel;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *messageLabel;
|
||||
@property (strong, nonatomic) RBXMessageInfo* messageData;
|
||||
@property (nonatomic) BOOL isRead;
|
||||
@end
|
||||
|
||||
@interface MessagesDetailController : RBBaseViewController
|
||||
|
||||
@property (nonatomic) RBXMessageType typeOfMessages;
|
||||
|
||||
- (void) loadData;
|
||||
- (void) refreshMessages:(UIRefreshControl*)refreshControl;
|
||||
@end
|
||||
@@ -0,0 +1,530 @@
|
||||
//
|
||||
// MessagesDetailController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 6/19/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MessagesDetailController.h"
|
||||
#import "RBMessageComposeScreenController.h"
|
||||
#import "RobloxImageView.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "NSString+stripHtml.h"
|
||||
#import <UIKit/NSAttributedString.h>
|
||||
#import "Flurry.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "RBActivityIndicatorView.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "UserInfo.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RBInfiniteTableView.h"
|
||||
#import "RBProfileViewController.h"
|
||||
#import "RBMobileWebViewController.h"
|
||||
#import "RBWebGamePreviewScreenController.h"
|
||||
|
||||
#define SPINNER_ICON_FRAME CGRectMake(0, 0, 32, 32)
|
||||
#define ITEMS_PER_REQUEST 20
|
||||
#define AVATAR_SIZE CGSizeMake(110, 110)
|
||||
|
||||
//---METRICS---
|
||||
#define MDC_didSelectMessage @"MESSAGES DETAIL SCREEN - Message Selected"
|
||||
#define MDC_didRefreshMessages @"MESSAGES DETAIL SCREEN - Messages Refreshed"
|
||||
#define MDC_openExternalLink @"MESSAGES DETAIL SCREEN - Open External Link"
|
||||
#define MDC_openLocalLink @"MESSAGES DETAIL SCREEN - Open Local Link"
|
||||
#define MDC_openProfile @"MESSAGES DETAIL SCREEN - Open Profile"
|
||||
#define MDC_openGameDetail @"MESSAGES DETAIL SCREEN - Open Game Detail"
|
||||
#define MDC_launchGame @"MESSAGES DETAIL SCREEN - Launch Game"
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Custom Cell
|
||||
|
||||
@implementation MessageCell
|
||||
|
||||
- (void) setMessageData:(RBXMessageInfo *)messageData
|
||||
{
|
||||
_messageData = messageData;
|
||||
|
||||
if(_messageData != nil)
|
||||
{
|
||||
self.messageLabel.text = messageData.subject;
|
||||
self.dateLabel.text = messageData.date;
|
||||
self.playerNameLabel.text = messageData.senderUsername;
|
||||
self.isRead = messageData.isRead;
|
||||
self.playerAvatar.hidden = NO;
|
||||
[self.playerAvatar loadAvatarForUserID:[messageData.senderUserID integerValue] withSize:AVATAR_SIZE completion:nil];
|
||||
|
||||
NSMutableAttributedString* message = [[NSMutableAttributedString alloc] init];
|
||||
[message appendAttributedString:[[NSAttributedString alloc] initWithString:self.messageData.subject
|
||||
attributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[UIColor colorWithWhite:(0x34/255.0f) alpha:1.0f], NSForegroundColorAttributeName,
|
||||
[UIFont fontWithName:@"SourceSansPro-Semibold" size:12], NSFontAttributeName,
|
||||
nil]]];
|
||||
|
||||
[message appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" - %@", [self.messageData.body stringByStrippingHTML]]
|
||||
attributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[UIColor colorWithWhite:(0x97/255.0f) alpha:1.0f], NSForegroundColorAttributeName,
|
||||
[UIFont fontWithName:@"SourceSansPro-Regular" size:12], NSFontAttributeName,
|
||||
nil]]];
|
||||
self.messageLabel.attributedText = message;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.messageLabel.text = @"";
|
||||
self.dateLabel.text = @"";
|
||||
self.playerNameLabel.text = @"";
|
||||
self.isRead = YES;
|
||||
self.playerAvatar.hidden = YES;
|
||||
self.messageLabel.attributedText = nil;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setIsRead:(BOOL)isRead
|
||||
{
|
||||
_isRead = isRead;
|
||||
|
||||
// Create subject/message preview
|
||||
//[RobloxTheme applyToMessagePreview:self isRead:isRead];
|
||||
self.playerNameLabel.textColor = [UIColor colorWithWhite:(0x34/255.0f) alpha:1.0f];
|
||||
|
||||
self.dateLabel.font = [UIFont fontWithName:@"SourceSansPro-Regular" size:12];
|
||||
self.dateLabel.textColor = [UIColor colorWithWhite:(0x97/255.0f) alpha:1.0f];
|
||||
|
||||
|
||||
if(isRead)
|
||||
{
|
||||
self.playerNameLabel.font = [UIFont fontWithName:@"SourceSansPro-Regular" size:14];
|
||||
self.unreadIndicator.opaque = NO;
|
||||
self.unreadIndicator.alpha = 1;
|
||||
[UIView animateWithDuration:0.3 animations:^
|
||||
{
|
||||
self.unreadIndicator.alpha = 0;
|
||||
}
|
||||
completion:^(BOOL finished)
|
||||
{
|
||||
self.unreadIndicator.opaque = YES;
|
||||
self.unreadIndicator.hidden = YES;
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.playerNameLabel.font = [UIFont fontWithName:@"SourceSansPro-Semibold" size:14];
|
||||
self.unreadIndicator.hidden = NO;
|
||||
self.unreadIndicator.alpha = 1;
|
||||
self.unreadIndicator.opaque = YES;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Detail controller
|
||||
|
||||
@interface MessagesDetailController () <RBInfiniteTableViewDelegate, UIWebViewDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation MessagesDetailController
|
||||
{
|
||||
// Left panel
|
||||
IBOutlet UILabel* _inboxTitle;
|
||||
IBOutlet RBInfiniteTableView* _messagesTable;
|
||||
UIRefreshControl* _refreshIndicator;
|
||||
|
||||
// Right panel
|
||||
IBOutlet UIView* _messageContainer;
|
||||
IBOutlet UIWebView* _messageBody;
|
||||
IBOutlet UILabel* _messageSubject;
|
||||
IBOutlet UILabel* _messageDetails;
|
||||
IBOutlet UIButton* _replyButton;
|
||||
IBOutlet UIButton* _archiveButton; //also doubles as an unarchiveButton
|
||||
IBOutlet UIButton* _builderButton;
|
||||
IBOutlet UIButton* _reportAbuseButton;
|
||||
IBOutlet RobloxImageView* _messageImage;
|
||||
RBXMessageInfo* _selectedMessage;
|
||||
RBActivityIndicatorView* _loadingSpinner;
|
||||
|
||||
BOOL _initialized;
|
||||
NSMutableArray* _messages;
|
||||
RBXAnalyticsCustomData _inboxType;
|
||||
}
|
||||
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = [RobloxTheme lightBackground];
|
||||
|
||||
// Stylize message
|
||||
_messageSubject.font = [UIFont fontWithName:@"SourceSansPro-Semibold" size:14];
|
||||
_messageSubject.textColor = [UIColor colorWithWhite:(0x34/255.0f) alpha:1.0f];
|
||||
|
||||
_messageDetails.font = [UIFont fontWithName:@"SourceSansPro-Semibold" size:12];
|
||||
_messageDetails.textColor = [UIColor colorWithWhite:(0x97/255.0f) alpha:1.0f];
|
||||
|
||||
_messageContainer.hidden = YES;
|
||||
_messageSubject.text = @"";
|
||||
_messageDetails.text = @"";
|
||||
_messageBody.delegate = self;
|
||||
[_messageBody loadHTMLString:@"" baseURL:nil];
|
||||
|
||||
//pull to refresh controller
|
||||
_refreshIndicator = [[UIRefreshControl alloc] init];
|
||||
[_refreshIndicator addTarget:self action:@selector(refreshMessages:) forControlEvents:UIControlEventValueChanged];
|
||||
[_messagesTable addSubview:_refreshIndicator];
|
||||
|
||||
_loadingSpinner = [[RBActivityIndicatorView alloc] initWithFrame:SPINNER_ICON_FRAME];
|
||||
[self.view addSubview:_loadingSpinner];
|
||||
|
||||
_messagesTable.infiniteDelegate = self;
|
||||
|
||||
_messages = [NSMutableArray array];
|
||||
|
||||
[RobloxTheme applyToTableHeaderTitle:_inboxTitle];
|
||||
|
||||
_builderButton.hidden = YES;
|
||||
[_builderButton setTitle:@">" forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToGamePreviewBuilderButton:_builderButton];
|
||||
[RobloxTheme applyToGamePreviewBuilderButton:_archiveButton];
|
||||
[RobloxTheme applyToGamePreviewBuilderButton:_reportAbuseButton];
|
||||
|
||||
_initialized = NO;
|
||||
|
||||
//set the Flurry events
|
||||
[self setFlurryEventsForExternalLinkEvent:MDC_openExternalLink
|
||||
andWebViewEvent:MDC_openLocalLink
|
||||
andOpenProfileEvent:MDC_openProfile
|
||||
andOpenGameDetailEvent:MDC_openGameDetail];
|
||||
_inboxType = RBXACustomSectionInbox;
|
||||
}
|
||||
- (void) viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[[RBXEventReporter sharedInstance] reportScreenLoaded:RBXAContextMessages];
|
||||
}
|
||||
- (void) viewDidLayoutSubviews
|
||||
{
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
//center the loading spinner in the right hand column
|
||||
[_loadingSpinner centerInFrame:_messageContainer.frame];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Mutators
|
||||
-(void) setTypeOfMessages:(RBXMessageType)typeOfMessages
|
||||
{
|
||||
_typeOfMessages = typeOfMessages;
|
||||
switch (_typeOfMessages)
|
||||
{
|
||||
case RBXMessageTypeArchive:
|
||||
{
|
||||
_inboxType = RBXACustomSectionArchive;
|
||||
_inboxTitle.text = NSLocalizedString(@"ArchivedMessagesWord", nil);
|
||||
[_archiveButton setTitle:NSLocalizedString(@"UnarchivedMessagesWord", nil) forState:UIControlStateNormal];
|
||||
} break;
|
||||
|
||||
case RBXMessageTypeSent:
|
||||
{
|
||||
_inboxType = RBXACustomSectionSent;
|
||||
_inboxTitle.text = NSLocalizedString(@"SentMessagesWord", nil);
|
||||
[_archiveButton setTitle:NSLocalizedString(@"ArchivedMessagesWord", nil) forState:UIControlStateNormal];
|
||||
} break;
|
||||
|
||||
default:
|
||||
{
|
||||
_inboxType = RBXACustomSectionInbox;
|
||||
_inboxTitle.text = NSLocalizedString(@"InboxWord", nil);
|
||||
[_archiveButton setTitle:NSLocalizedString(@"ArchivedMessagesWord", nil) forState:UIControlStateNormal];
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Data Functions
|
||||
|
||||
- (void) loadData
|
||||
{
|
||||
if(!_initialized)
|
||||
{
|
||||
_initialized = YES;
|
||||
[_loadingSpinner startAnimating];
|
||||
|
||||
[_messagesTable loadElementsAsync];
|
||||
}
|
||||
}
|
||||
- (void) refreshMessages:(UIRefreshControl*)refreshControl
|
||||
{
|
||||
if (refreshControl)
|
||||
{
|
||||
[Flurry logEvent:MDC_didRefreshMessages];
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonRefresh withContext:RBXAContextMessages withCustomData:_inboxType];
|
||||
}
|
||||
|
||||
//make a request to pull down the messages from the server
|
||||
[RobloxData fetchMessages:_typeOfMessages pageNumber:0 pageSize:ITEMS_PER_REQUEST completion:^(NSArray *messages)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
//loop through the list of messages and look for differences between the two sets
|
||||
//since messageIDs are sorted chronologically on the server, we can easily check what messages are new, the same, or missing in the new list
|
||||
NSMutableArray* mergedMessages = [NSMutableArray arrayWithCapacity:([messages count] + [_messages count])];
|
||||
NSMutableArray* setToAdd = [NSMutableArray arrayWithCapacity:[messages count]];
|
||||
NSMutableArray* setToRemove = [NSMutableArray arrayWithCapacity:[_messages count]];
|
||||
|
||||
int i = 0; int j = 0;
|
||||
while (i < messages.count && j < _messages.count)
|
||||
{
|
||||
RBXMessageInfo* messageNew = messages[i];
|
||||
RBXMessageInfo* messageOld = _messages[j];
|
||||
if ([messageNew messageID] > [messageOld messageID])
|
||||
{
|
||||
[setToAdd addObject:[NSIndexPath indexPathForRow:i inSection:0]];
|
||||
[mergedMessages addObject:messages[i]];
|
||||
i++;
|
||||
}
|
||||
else if ([messageNew messageID] == [messageOld messageID])
|
||||
{
|
||||
[mergedMessages addObject:messages[i]];
|
||||
i++; j++;
|
||||
}
|
||||
else
|
||||
{
|
||||
[setToRemove addObject:[NSIndexPath indexPathForRow:j inSection:0]];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
//handle the remaining messages : add the new ones, remove the old ones
|
||||
for (; i < messages.count; i++)
|
||||
{
|
||||
[setToAdd addObject:[NSIndexPath indexPathForRow:i inSection:0]];
|
||||
[mergedMessages addObject:messages[i]];
|
||||
}
|
||||
for (; j < _messages.count; j++)
|
||||
[setToRemove addObject:[NSIndexPath indexPathForRow:j inSection:0]];
|
||||
|
||||
//assign the combined list of messages to the table array
|
||||
_messages = mergedMessages;
|
||||
|
||||
//stop the animation and refresh the table
|
||||
if (refreshControl)
|
||||
[refreshControl endRefreshing];
|
||||
|
||||
if (_initialized)
|
||||
{
|
||||
@try
|
||||
{
|
||||
//animate the change
|
||||
[_messagesTable beginUpdates];
|
||||
[_messagesTable deleteRowsAtIndexPaths:setToRemove withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
[_messagesTable insertRowsAtIndexPaths:setToAdd withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
[_messagesTable endUpdates];
|
||||
}
|
||||
@catch (NSException* e)
|
||||
{
|
||||
NSLog(@"Failed to animate the refresh changes");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_initialized = YES;
|
||||
}
|
||||
|
||||
//mark messages as unread if they have changed
|
||||
[_messagesTable reloadData];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Table delegates
|
||||
|
||||
- (void) asyncRequestItemsForTableView:(RBInfiniteTableView*)tableView numItemsToRequest:(NSUInteger)itemsToRequest completionHandler:(void(^)())completionHandler
|
||||
{
|
||||
if(_messagesTable.numItems > _messages.count)
|
||||
{
|
||||
void(^block)(NSArray*) = ^(NSArray* messages)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[_loadingSpinner stopAnimating];
|
||||
[_messages addObjectsFromArray:messages];
|
||||
|
||||
completionHandler();
|
||||
});
|
||||
};
|
||||
|
||||
int curPageNum = floor(_messages.count / itemsToRequest);
|
||||
int remainder = _messages.count - (curPageNum * itemsToRequest);
|
||||
int requestAmt = itemsToRequest - remainder;
|
||||
[RobloxData fetchMessages:_typeOfMessages pageNumber:curPageNum pageSize:requestAmt completion:block];
|
||||
//[RobloxData fetchMessages:_typeOfMessages startIndex:_messages.count numItems:itemsToRequest completion:block];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSUInteger)numItemsInInfiniteTableView:(RBInfiniteTableView*)tableView
|
||||
{
|
||||
return _messages.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell*) infiniteTableView:(RBInfiniteTableView*)tableView cellForItemAtIndexPath:(NSIndexPath*)indexPath
|
||||
{
|
||||
static NSString* CellIdentifier = @"MessageCell";
|
||||
|
||||
MessageCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
|
||||
if(indexPath.row < _messages.count)
|
||||
{
|
||||
RBXMessageInfo* message = _messages[indexPath.row];
|
||||
cell.messageData = message;
|
||||
cell.isRead = (_typeOfMessages == RBXMessageTypeSent) ? YES : [message isRead];
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.messageData = nil;
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void) infiniteTableView:(RBInfiniteTableView*)tableView didSelectItemAtIndexPath:(NSIndexPath*)indexPath
|
||||
{
|
||||
[Flurry logEvent:MDC_didSelectMessage];
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonReadMessage withContext:RBXAContextMessages withCustomData:_inboxType];
|
||||
if (_messages.count < indexPath.row)
|
||||
return;
|
||||
_selectedMessage = _messages[indexPath.row];
|
||||
|
||||
_messageSubject.text = _selectedMessage.subject;
|
||||
_messageDetails.text = [NSString stringWithFormat:@"%@ %@, %@", NSLocalizedString(@"ByWord", nil), _selectedMessage.senderUsername, _selectedMessage.date];
|
||||
|
||||
//check if the message is from Roblox or a system message to the user, and hide the Reply button
|
||||
bool isFromRoblox = [_selectedMessage.senderUserID isEqualToNumber:[NSNumber numberWithInt:1]];
|
||||
bool isSystemMessage = [_selectedMessage.senderUserID isEqualToNumber:[UserInfo CurrentPlayer].userId];
|
||||
_replyButton.hidden = (isFromRoblox || isSystemMessage);
|
||||
_reportAbuseButton.hidden = YES; //(isFromRoblox || isSystemMessage);
|
||||
_archiveButton.hidden = (isFromRoblox || isSystemMessage);
|
||||
|
||||
NSString* htmlMessage = [NSString stringWithFormat:@"<span style=\"font-family: %@; font-size: %i; color: %@\"> %@ </span>",
|
||||
@"SourceSansPro-Regular",
|
||||
12,
|
||||
@"#343434",
|
||||
_selectedMessage.body];
|
||||
|
||||
if (!isFromRoblox && !isSystemMessage)
|
||||
{
|
||||
//search the message for clickable links
|
||||
NSError* error;
|
||||
NSDataDetector* urlDetector = [[NSDataDetector alloc] initWithTypes:NSTextCheckingTypeLink error:&error];
|
||||
NSArray* foundURLs = [urlDetector matchesInString:htmlMessage options:0 range:NSMakeRange(0, [htmlMessage length])];
|
||||
|
||||
//encase the URLs in <a> tags
|
||||
for (int i = foundURLs.count - 1; i >= 0; i--)
|
||||
{
|
||||
NSTextCheckingResult* result = foundURLs[i];
|
||||
NSString* urlString = [[NSString stringWithFormat:@"%@", result.URL] stringByRemovingPercentEncoding];
|
||||
htmlMessage = [htmlMessage stringByReplacingCharactersInRange:result.range
|
||||
withString:[NSString stringWithFormat:@"<a href=\"%@\">%@</a>", urlString , urlString]];
|
||||
}
|
||||
}
|
||||
|
||||
//load the html message to the screen
|
||||
[_messageBody loadHTMLString:htmlMessage baseURL:nil];
|
||||
|
||||
_messageImage.animateInOptions = RBXImageViewAnimateInAlways;
|
||||
[_messageImage loadAvatarForUserID:[_selectedMessage.senderUserID integerValue] withSize:AVATAR_SIZE completion:nil];
|
||||
|
||||
_messageContainer.hidden = NO;
|
||||
|
||||
// Mark the message as read
|
||||
if(!_selectedMessage.isRead)
|
||||
{
|
||||
[RobloxData markMessageAsRead:_selectedMessage.messageID completion:^(BOOL success)
|
||||
{
|
||||
if(success)
|
||||
{
|
||||
RBXMessageInfo* weakRefMessage = _selectedMessage;
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
MessageCell* cell = (MessageCell*) [tableView cellForRowAtIndexPath:indexPath];
|
||||
[cell setIsRead:YES];
|
||||
weakRefMessage.isRead = YES;
|
||||
|
||||
//decrement the message badge
|
||||
if (_typeOfMessages == RBXMessageTypeInbox)
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RBX_NOTIFY_READ_MESSAGE object:self];
|
||||
});
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
|
||||
//allow the user to link to the message sender's profile
|
||||
_builderButton.hidden = isSystemMessage;
|
||||
}
|
||||
|
||||
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
|
||||
{
|
||||
if( [segue.identifier isEqualToString:@"replyToMessage"] )
|
||||
{
|
||||
RBXMessageInfo* message = _messages[[_messagesTable indexPathForSelectedRow].row];
|
||||
|
||||
RBMessageComposeScreenController* controller = segue.destinationViewController;
|
||||
[controller replyToMessage:message];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Button Actions
|
||||
- (IBAction)builderButtonTouchUpInside:(id)sender
|
||||
{
|
||||
[Flurry logEvent:MDC_openProfile];
|
||||
[self pushProfileControllerWithUserID:_selectedMessage.senderUserID];
|
||||
}
|
||||
- (IBAction)archiveButtonTouchUpInside:(id)sender
|
||||
{
|
||||
if (_typeOfMessages != RBXMessageTypeArchive)
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonArchive withContext:RBXAContextMessages withCustomDataString:@"archive"];
|
||||
[RobloxData archiveMessage:_selectedMessage.messageID completion:^(BOOL success)
|
||||
{
|
||||
if (success)
|
||||
[self refreshMessages:nil];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonArchive withContext:RBXAContextMessages withCustomDataString:@"unarchive"];
|
||||
[RobloxData unarchiveMessage:_selectedMessage.messageID completion:^(BOOL success)
|
||||
{
|
||||
if (success)
|
||||
[self refreshMessages:nil];
|
||||
}];
|
||||
}
|
||||
}
|
||||
- (IBAction)reportAbuseButtonTouchUpInside:(id)sender
|
||||
{
|
||||
//Report the message
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark WebViewDelegate
|
||||
|
||||
-(BOOL) webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType
|
||||
{
|
||||
if (inType == UIWebViewNavigationTypeLinkClicked)
|
||||
{
|
||||
[self handleWebRequestWithPopout:inRequest.URL];
|
||||
}
|
||||
return inType != UIWebViewNavigationTypeLinkClicked;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// MessagesScreenController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 6/18/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RBBaseViewController.h"
|
||||
|
||||
@interface MessagesMasterController : RBBaseViewController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,251 @@
|
||||
//
|
||||
// MessagesScreenController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 6/18/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MessagesMasterController.h"
|
||||
#import "MessagesDetailController.h"
|
||||
#import "NotificationsDetailController.h"
|
||||
#import "FriendsRequestsDetailController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxData.h"
|
||||
#import "UIViewController+Helpers.h"
|
||||
#import "Flurry.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "RBXMessagesPollingService.h"
|
||||
#import "UITabBarItem+CustomBadge.h"
|
||||
|
||||
|
||||
//---METRICS---
|
||||
#define MMC_inboxSelected @"MESSAGES MASTER SCREEN - Inbox Selected"
|
||||
#define MMC_notificationsSelected @"MESSAGES MASTER SCREEN - Notifications Selected"
|
||||
#define MMC_friendRequestsSelected @"MESSAGES MASTER SCREEN - Friend Requests Selected"
|
||||
#define MMC_openRobux @"MESSAGES MASTER SCREEN - Open Robux"
|
||||
#define MMC_openBuildersClub @"MESSAGES MASTER SCREEN - Open Builders Club"
|
||||
#define MMC_openSettings @"MESSAGES MASTER SCREEN - Open Settings"
|
||||
#define MMC_openLogout @"MESSAGES MASTER SCREEN - Open Logout"
|
||||
|
||||
@interface MessagesMasterController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation MessagesMasterController
|
||||
{
|
||||
IBOutlet UISegmentedControl* _messageTypeSelector;
|
||||
|
||||
MessagesDetailController* _messagesController;
|
||||
MessagesDetailController* _messagesSentController;
|
||||
MessagesDetailController* _messagesArchiveController;
|
||||
NotificationsDetailController* _notificationsController;
|
||||
//FriendsRequestsDetailController* _friendsRequestsController;
|
||||
}
|
||||
|
||||
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
|
||||
{
|
||||
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
|
||||
if (self) {
|
||||
[self initNotificationPolling];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self) {
|
||||
[self initNotificationPolling];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[self setViewTheme:RBXThemeSocial];
|
||||
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
|
||||
[_messageTypeSelector removeAllSegments];
|
||||
|
||||
// Inbox
|
||||
{
|
||||
[_messageTypeSelector insertSegmentWithTitle:NSLocalizedString(@"InboxWord", nil) atIndex:_messageTypeSelector.numberOfSegments animated:NO];
|
||||
|
||||
_messagesController = [self.storyboard instantiateViewControllerWithIdentifier:@"MessagesDetailController"];
|
||||
_messagesController.view.hidden = YES;
|
||||
_messagesController.typeOfMessages = RBXMessageTypeInbox;
|
||||
[self addChildViewController:_messagesController];
|
||||
[self.view addSubview:_messagesController.view];
|
||||
}
|
||||
|
||||
//Sent Messages
|
||||
{
|
||||
[_messageTypeSelector insertSegmentWithTitle:NSLocalizedString(@"SentMessagesWord", nil) atIndex:_messageTypeSelector.numberOfSegments animated:NO];
|
||||
|
||||
_messagesSentController = [self.storyboard instantiateViewControllerWithIdentifier:@"MessagesDetailController"];
|
||||
_messagesSentController.view.hidden = YES;
|
||||
_messagesSentController.typeOfMessages = RBXMessageTypeSent;
|
||||
[self addChildViewController:_messagesSentController];
|
||||
[self.view addSubview:_messagesSentController.view];
|
||||
}
|
||||
|
||||
//Archive
|
||||
{
|
||||
[_messageTypeSelector insertSegmentWithTitle:NSLocalizedString(@"ArchivedMessagesWord", nil) atIndex:_messageTypeSelector.numberOfSegments animated:NO];
|
||||
|
||||
_messagesArchiveController = [self.storyboard instantiateViewControllerWithIdentifier:@"MessagesDetailController"];
|
||||
_messagesArchiveController.view.hidden = YES;
|
||||
_messagesArchiveController.typeOfMessages = RBXMessageTypeArchive;
|
||||
[self addChildViewController:_messagesArchiveController];
|
||||
[self.view addSubview:_messagesArchiveController.view];
|
||||
}
|
||||
|
||||
// Notifications
|
||||
{
|
||||
[_messageTypeSelector insertSegmentWithTitle:NSLocalizedString(@"NotificationsWord", nil) atIndex:_messageTypeSelector.numberOfSegments animated:NO];
|
||||
|
||||
_notificationsController = [self.storyboard instantiateViewControllerWithIdentifier:@"NotificationsDetailController"];
|
||||
_notificationsController.view.hidden = YES;
|
||||
[self addChildViewController:_notificationsController];
|
||||
[self.view addSubview:_notificationsController.view];
|
||||
}
|
||||
|
||||
// Friends requests
|
||||
// {
|
||||
// [_messageTypeSelector insertSegmentWithTitle:NSLocalizedString(@"FriendRequestsPhrase", nil) atIndex:_messageTypeSelector.numberOfSegments animated:NO];
|
||||
//
|
||||
// _friendsRequestsController = [self.storyboard instantiateViewControllerWithIdentifier:@"FriendsRequestsDetailController"];
|
||||
// _friendsRequestsController.view.hidden = YES;
|
||||
// [self addChildViewController:_friendsRequestsController];
|
||||
// [self.view addSubview:_friendsRequestsController.view];
|
||||
// }
|
||||
|
||||
[_messageTypeSelector setSelectedSegmentIndex:0];
|
||||
[self showDetailControllerForIndex:0];
|
||||
|
||||
|
||||
// Add navigation items
|
||||
[self addRobuxIconWithFlurryEvent:MMC_openRobux
|
||||
andBCIconWithFlurryEvent:MMC_openBuildersClub];
|
||||
|
||||
//[self.navigationController.tabBarItem setImage:[[UIImage imageNamed:@"Icon Messages Off"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
//[self.navigationController.tabBarItem setSelectedImage:[[UIImage imageNamed:@"Icon Messages On"] imageWithRenderingMode:[RobloxTheme iconColoringMode]]];
|
||||
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[self setViewTheme:RBXThemeSocial];
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[[RBXEventReporter sharedInstance] reportScreenLoaded:RBXAContextMessages];
|
||||
}
|
||||
|
||||
-(void) dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)viewWillLayoutSubviews
|
||||
{
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
CGRect rect = self.view.bounds;
|
||||
_messagesController.view.frame = rect;
|
||||
_notificationsController.view.frame = rect;
|
||||
//_friendsRequestsController.view.frame = rect;
|
||||
}
|
||||
|
||||
- (IBAction)timeFilterSelected:(id)sender
|
||||
{
|
||||
[self showDetailControllerForIndex:_messageTypeSelector.selectedSegmentIndex];
|
||||
}
|
||||
|
||||
- (void) showDetailControllerForIndex:(NSInteger)index
|
||||
{
|
||||
_messagesController.view.hidden = YES;
|
||||
_messagesSentController.view.hidden = YES;
|
||||
_messagesArchiveController.view.hidden = YES;
|
||||
_notificationsController.view.hidden = YES;
|
||||
//_friendsRequestsController.view.hidden = YES;
|
||||
|
||||
switch (index)
|
||||
{
|
||||
case 0: // Inbox
|
||||
{
|
||||
[Flurry logEvent:MMC_inboxSelected];
|
||||
_messagesController.view.hidden = NO;
|
||||
[_messagesController loadData];
|
||||
break;
|
||||
}
|
||||
case 1: //Sent
|
||||
{
|
||||
//[Flurry logEvent:MMC_inboxSelected];
|
||||
_messagesSentController.view.hidden = NO;
|
||||
[_messagesSentController loadData];
|
||||
break;
|
||||
}
|
||||
case 2: //Archived
|
||||
{
|
||||
//[Flurry logEvent:MMC_inboxSelected];
|
||||
_messagesArchiveController.view.hidden = NO;
|
||||
[_messagesArchiveController loadData];
|
||||
break;
|
||||
}
|
||||
case 3: // Notifications
|
||||
{
|
||||
[Flurry logEvent:MMC_notificationsSelected];
|
||||
_notificationsController.view.hidden = NO;
|
||||
[_notificationsController loadData];
|
||||
break;
|
||||
}
|
||||
// case 4: // Friend Requests
|
||||
// {
|
||||
// [Flurry logEvent:MMC_friendRequestsSelected];
|
||||
// _friendsRequestsController.view.hidden = NO;
|
||||
// [_friendsRequestsController loadData];
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
-(void) initNotificationPolling
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateBadge) name:RBX_NOTIFY_NEW_MESSAGES_TOTAL object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateBadgeWithRefesh) name:RBX_NOTIFY_INBOX_UPDATED object:nil];
|
||||
[self updateBadge];
|
||||
}
|
||||
-(void) updateBadge
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
//dispatch the update command to the main thread for instant update
|
||||
int total = [[RBXMessagesPollingService sharedInstance] totalMessages];
|
||||
if ([self navigationController])
|
||||
if ([[self navigationController] tabBarItem])
|
||||
{
|
||||
//this should be cleaned up at some point to use a RobloxTheme constant. Roblox Theme needs to be cleaned up.
|
||||
[self.navigationController.tabBarItem setBadgeValue:((total == 0) ? nil : [NSString stringWithFormat:@"%i",total])];
|
||||
//[[[self navigationController] tabBarItem] setCustomBadgeValue:((total == 0) ? nil : [NSString stringWithFormat:@"%i",total])
|
||||
// withColor:[UIColor colorWithRed:0.2549f green:0.3882f blue:0.6f alpha:1.0f]];
|
||||
}
|
||||
});
|
||||
}
|
||||
-(void) updateBadgeWithRefesh
|
||||
{
|
||||
//there has been an update in the total notifications, update the lists
|
||||
if (_messagesController) [_messagesController refreshMessages:nil];
|
||||
if (_notificationsController) [_notificationsController refreshMessages:nil];
|
||||
//if (_friendsRequestsController) [_friendsRequestsController refreshRequests:nil];
|
||||
|
||||
[self updateBadge];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// NotificationsDetailController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 6/19/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RobloxData.h"
|
||||
#import "RBBaseViewController.h"
|
||||
|
||||
@interface NotificationCell : UITableViewCell
|
||||
@property (strong, nonatomic) IBOutlet UIImageView* unreadIndicator;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *playerNameLabel;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *dateLabel;
|
||||
@property (strong, nonatomic) IBOutlet UILabel *messageLabel;
|
||||
@property (strong, nonatomic) RBXMessageInfo* messageData;
|
||||
@property (nonatomic) BOOL isRead;
|
||||
@end
|
||||
|
||||
@interface NotificationsDetailController : RBBaseViewController
|
||||
|
||||
- (void) loadData;
|
||||
- (void) refreshMessages:(UIRefreshControl*)refreshControl;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,360 @@
|
||||
//
|
||||
// MessagesDetailController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 6/19/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "NotificationsDetailController.h"
|
||||
#import "RobloxImageView.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "NSString+stripHtml.h"
|
||||
#import <UIKit/NSAttributedString.h>
|
||||
#import "Flurry.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "RBActivityIndicatorView.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "RBXEventReporter.h"
|
||||
|
||||
#define AVATAR_SIZE CGSizeMake(110, 110)
|
||||
#define SPINNER_ICON_FRAME CGRectMake(0, 0, 32, 32)
|
||||
|
||||
//---METRICS---
|
||||
#define NDC_didPressNotification @"NOTIFICATIONS DETAIL SCREEN - Notification Selected"
|
||||
#define NDC_didRefreshMessages @"NOTIFICATIONS DETAIL SCREEN - Notifications Refreshed"
|
||||
#define NDC_openExternalLink @"NOTIFICATIONS DETAIL SCREEN - Open External Link"
|
||||
#define NDC_openLocalLink @"NOTIFICATIONS DETAIL SCREEN - Open Local Link"
|
||||
#define NDC_openProfile @"NOTIFICATIONS DETAIL SCREEN - Open Profile"
|
||||
#define NDC_openGameDetail @"NOTIFICATIONS DETAIL SCREEN - Open Game Detail"
|
||||
#define NDC_launchGame @"NOTIFICATIONS DETAIL SCREEN - Launch Game"
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Custom Cell
|
||||
|
||||
@implementation NotificationCell
|
||||
|
||||
- (void) setMessageData:(RBXMessageInfo *)messageData
|
||||
{
|
||||
_messageData = messageData;
|
||||
|
||||
self.messageLabel.text = messageData.subject;
|
||||
self.dateLabel.text = messageData.date;
|
||||
self.playerNameLabel.text = messageData.senderUsername;
|
||||
self.isRead = messageData.isRead;
|
||||
|
||||
NSMutableAttributedString* message = [[NSMutableAttributedString alloc] init];
|
||||
[message appendAttributedString:[[NSAttributedString alloc] initWithString:self.messageData.subject
|
||||
attributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[UIColor colorWithWhite:(0x34/255.0f) alpha:1.0f], NSForegroundColorAttributeName,
|
||||
[UIFont fontWithName:@"SourceSansPro-Semibold" size:12], NSFontAttributeName,
|
||||
nil]]];
|
||||
|
||||
[message appendAttributedString:[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" - %@", [self.messageData.message stringByStrippingHTML]]
|
||||
attributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[UIColor colorWithWhite:(0x97/255.0f) alpha:1.0f], NSForegroundColorAttributeName,
|
||||
[UIFont fontWithName:@"SourceSansPro-Regular" size:12], NSFontAttributeName,
|
||||
nil]]];
|
||||
self.messageLabel.attributedText = message;
|
||||
}
|
||||
|
||||
- (void)setIsRead:(BOOL)isRead
|
||||
{
|
||||
_isRead = isRead;
|
||||
|
||||
// Create subject/message preview
|
||||
//[RobloxTheme applyToNotificationPreview:self isRead:isRead];
|
||||
self.playerNameLabel.textColor = [UIColor colorWithWhite:(0x34/255.0f) alpha:1.0f];
|
||||
|
||||
self.dateLabel.font = [UIFont fontWithName:@"SourceSansPro-Regular" size:12];
|
||||
self.dateLabel.textColor = [UIColor colorWithWhite:(0x97/255.0f) alpha:1.0f];
|
||||
|
||||
if(isRead)
|
||||
{
|
||||
self.playerNameLabel.font = [UIFont fontWithName:@"SourceSansPro-Regular" size:14];
|
||||
self.unreadIndicator.opaque = NO;
|
||||
self.unreadIndicator.alpha = 1;
|
||||
[UIView animateWithDuration:0.3 animations:^
|
||||
{
|
||||
self.unreadIndicator.alpha = 0;
|
||||
}
|
||||
completion:^(BOOL finished)
|
||||
{
|
||||
self.unreadIndicator.opaque = YES;
|
||||
self.unreadIndicator.hidden = YES;
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.playerNameLabel.font = [UIFont fontWithName:@"SourceSansPro-Semibold" size:14];
|
||||
self.unreadIndicator.hidden = NO;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Detail controller
|
||||
|
||||
@interface NotificationsDetailController () <UITableViewDelegate, UITableViewDataSource, UIWebViewDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation NotificationsDetailController
|
||||
{
|
||||
// Left panel
|
||||
IBOutlet UILabel* _title;
|
||||
IBOutlet UITableView* _messagesTable;
|
||||
UIRefreshControl* _refreshIndicator;
|
||||
|
||||
// Right panel
|
||||
IBOutlet UIView* _messageContainer;
|
||||
IBOutlet UILabel* _messageSubject;
|
||||
IBOutlet UILabel* _messageDetails;
|
||||
IBOutlet UIWebView* _messageBody;
|
||||
RBActivityIndicatorView* _loadingSpinner;
|
||||
|
||||
BOOL _initialized;
|
||||
NSMutableArray* _messages;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = [RobloxTheme lightBackground];
|
||||
|
||||
// Stylize message
|
||||
_messageSubject.font = [UIFont fontWithName:@"SourceSansPro-Semibold" size:14];
|
||||
_messageSubject.textColor = [UIColor colorWithWhite:(0x34/255.0f) alpha:1.0f];
|
||||
|
||||
_messageDetails.font = [UIFont fontWithName:@"SourceSansPro-Semibold" size:12];
|
||||
_messageDetails.textColor = [UIColor colorWithWhite:(0x97/255.0f) alpha:1.0f];
|
||||
|
||||
_messageContainer.hidden = YES;
|
||||
_messageSubject.text = @"";
|
||||
_messageDetails.text = @"";
|
||||
_messageBody.delegate = self;
|
||||
[_messageBody loadHTMLString:@"" baseURL:nil];
|
||||
|
||||
//pull to refresh controller
|
||||
_refreshIndicator = [[UIRefreshControl alloc] init];
|
||||
[_refreshIndicator addTarget:self action:@selector(refreshMessages:) forControlEvents:UIControlEventValueChanged];
|
||||
[_messagesTable addSubview:_refreshIndicator];
|
||||
|
||||
_loadingSpinner = [[RBActivityIndicatorView alloc] initWithFrame:SPINNER_ICON_FRAME];
|
||||
[self.view addSubview:_loadingSpinner];
|
||||
|
||||
_messagesTable.dataSource = self;
|
||||
_messagesTable.delegate = self;
|
||||
|
||||
_messages = [NSMutableArray array];
|
||||
|
||||
_title.text = NSLocalizedString(@"NotificationsWord", nil);
|
||||
[RobloxTheme applyToTableHeaderTitle:_title];
|
||||
|
||||
_initialized = NO;
|
||||
|
||||
//set the flurry events
|
||||
[self setFlurryEventsForExternalLinkEvent:NDC_openExternalLink
|
||||
andWebViewEvent:NDC_openLocalLink
|
||||
andOpenProfileEvent:NDC_openProfile
|
||||
andOpenGameDetailEvent:NDC_openGameDetail];
|
||||
}
|
||||
-(void) viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[[RBXEventReporter sharedInstance] reportScreenLoaded:RBXAContextMessages];
|
||||
}
|
||||
|
||||
- (void) viewDidLayoutSubviews
|
||||
{
|
||||
//center the loading spinner in the right hand column
|
||||
[_loadingSpinner centerInFrame:_messageContainer.frame];
|
||||
}
|
||||
|
||||
- (void) loadData
|
||||
{
|
||||
if(!_initialized)
|
||||
{
|
||||
_initialized = YES;
|
||||
[_loadingSpinner startAnimating];
|
||||
|
||||
[RobloxData fetchNotificationsWithCompletion:^(NSArray *messages)
|
||||
{
|
||||
[_messages addObjectsFromArray:messages];
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[_loadingSpinner stopAnimating];
|
||||
[_messagesTable reloadData];
|
||||
});
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) refreshMessages:(UIRefreshControl*)refreshControl
|
||||
{
|
||||
if (refreshControl)
|
||||
{
|
||||
[Flurry logEvent:NDC_didRefreshMessages];
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonRefresh withContext:RBXAContextMessages withCustomData:RBXACustomSectionNotifications];
|
||||
}
|
||||
|
||||
//make a request to pull down the messages from the server
|
||||
[RobloxData fetchNotificationsWithCompletion:^(NSArray *messages)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
//loop through the list of messages and look for differences between the two sets
|
||||
//since messageIDs are sorted chronologically on the server, we can easily check what messages are new, the same, or missing in the new list
|
||||
NSMutableArray* mergedMessages = [NSMutableArray arrayWithCapacity:([messages count] + [_messages count])];
|
||||
NSMutableArray* setToAdd = [NSMutableArray arrayWithCapacity:[messages count]];
|
||||
NSMutableArray* setToRemove = [NSMutableArray arrayWithCapacity:[_messages count]];
|
||||
|
||||
int i = 0; int j = 0;
|
||||
while (i < messages.count && j < _messages.count)
|
||||
{
|
||||
RBXMessageInfo* messageNew = messages[i];
|
||||
RBXMessageInfo* messageOld = _messages[j];
|
||||
if ([messageNew messageID] > [messageOld messageID])
|
||||
{
|
||||
[setToAdd addObject:[NSIndexPath indexPathForRow:i inSection:0]];
|
||||
[mergedMessages addObject:messages[i]];
|
||||
i++;
|
||||
}
|
||||
else if ([messageNew messageID] == [messageOld messageID])
|
||||
{
|
||||
[mergedMessages addObject:messages[i]];
|
||||
i++; j++;
|
||||
}
|
||||
else
|
||||
{
|
||||
[setToRemove addObject:[NSIndexPath indexPathForRow:j inSection:0]];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
//handle the remaining messages
|
||||
for (; i < messages.count; i++)
|
||||
{
|
||||
[setToAdd addObject:[NSIndexPath indexPathForRow:i inSection:0]];
|
||||
[mergedMessages addObject:messages[i]];
|
||||
}
|
||||
for (; j < _messages.count; j++)
|
||||
[setToRemove addObject:[NSIndexPath indexPathForRow:j inSection:0]];
|
||||
|
||||
|
||||
//assign the combined list of messages to the table array
|
||||
_messages = mergedMessages;
|
||||
|
||||
//stop the animation and refresh the table
|
||||
|
||||
if (refreshControl)
|
||||
[refreshControl endRefreshing];
|
||||
|
||||
if (_initialized)
|
||||
{
|
||||
//animate the change
|
||||
@try
|
||||
{
|
||||
[_messagesTable beginUpdates];
|
||||
[_messagesTable deleteRowsAtIndexPaths:setToRemove withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
[_messagesTable insertRowsAtIndexPaths:setToAdd withRowAnimation:UITableViewRowAnimationAutomatic];
|
||||
[_messagesTable endUpdates];
|
||||
}
|
||||
@catch (NSException* e)
|
||||
{
|
||||
NSLog(@"Failed to animate the update to the notifications");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_initialized = YES;
|
||||
}
|
||||
|
||||
//mark messages as unread if they have changed
|
||||
[_messagesTable reloadData];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Table delegates
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
|
||||
{
|
||||
return _messages.count;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
static NSString* CellIdentifier = @"NotificationCell";
|
||||
RBXMessageInfo* message = _messages[indexPath.row];
|
||||
NotificationCell* cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
|
||||
[cell setMessageData:message];
|
||||
[cell setIsRead:[message isRead]];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonReadMessage withContext:RBXAContextMessages withCustomData:RBXACustomSectionNotifications];
|
||||
[Flurry logEvent:NDC_didPressNotification];
|
||||
RBXMessageInfo* message = _messages[indexPath.row];
|
||||
|
||||
_messageSubject.text = message.subject;
|
||||
_messageDetails.text = [NSString stringWithFormat:@"%@ %@, %@", NSLocalizedString(@"ByWord", nil), message.senderUsername, message.date];
|
||||
|
||||
NSString* htmlMessage = [NSString stringWithFormat:@"<span style=\"font-family: %@; font-size: %i; color: %@\">%@</span>",
|
||||
@"SourceSansPro-Regular",
|
||||
12,
|
||||
@"#343434",
|
||||
message.message];
|
||||
[_messageBody loadHTMLString:htmlMessage baseURL:nil];
|
||||
|
||||
_messageContainer.hidden = NO;
|
||||
|
||||
// Mark the message as read
|
||||
if(!message.isRead)
|
||||
{
|
||||
[RobloxData markMessageAsRead:message.messageID completion:^(BOOL success)
|
||||
{
|
||||
if(success)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
NotificationCell* cell = (NotificationCell*) [tableView cellForRowAtIndexPath:indexPath];
|
||||
[cell setIsRead:YES];
|
||||
[message setIsRead:YES];
|
||||
|
||||
//refresh the notification badge
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RBX_NOTIFY_READ_MESSAGE object:self];
|
||||
});
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark WebViewDelegate
|
||||
|
||||
-(BOOL) webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType
|
||||
{
|
||||
if (inType == UIWebViewNavigationTypeLinkClicked)
|
||||
{
|
||||
[self handleWebRequestWithPopout:inRequest.URL];
|
||||
}
|
||||
|
||||
return inType != UIWebViewNavigationTypeLinkClicked;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// RBAccountManagerEmailViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 9/26/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#include "RBModalPopUpViewController.h"
|
||||
#include "RBValidTextField.h"
|
||||
|
||||
@interface RBAccountManagerEmailViewController : RBModalPopUpViewController <UIGestureRecognizerDelegate>
|
||||
@property IBOutlet UILabel* lblTitle;
|
||||
@property IBOutlet UILabel* lblAlertReason;
|
||||
@property IBOutlet UIImageView* imgAlert;
|
||||
@property IBOutlet UIButton* btnSave;
|
||||
@property IBOutlet UIButton* btnCancel;
|
||||
|
||||
@property IBOutlet RBValidTextField* txtEmail;
|
||||
@property IBOutlet RBValidTextField* txtPassword;
|
||||
|
||||
//phone outlets
|
||||
@property IBOutlet UIView* whiteView;
|
||||
|
||||
-(IBAction)saveEmailChange:(id)sender;
|
||||
-(IBAction)closeController:(id)sender;
|
||||
@end
|
||||
@@ -0,0 +1,355 @@
|
||||
//
|
||||
// RBAccountManagerEmailViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 9/26/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBAccountManagerEmailViewController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "UserInfo.h"
|
||||
#import "LoginManager.h"
|
||||
#import "RobloxData.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "SignUpVerifier.h"
|
||||
#import "RBXFunctions.h"
|
||||
|
||||
#define DEFAULT_VIEW_SIZE CGRectMake(0, 0, 540, 344)
|
||||
@interface RBAccountManagerEmailViewController ()
|
||||
@property (nonatomic, strong) NSString* hiddenEmail;
|
||||
@end
|
||||
|
||||
//----------------CHANGE EMAIL SCREEN----------------
|
||||
@implementation RBAccountManagerEmailViewController
|
||||
{
|
||||
UITapGestureRecognizer* _touches;
|
||||
}
|
||||
|
||||
#pragma mark Lifecycle Functions
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
//configure the modal popup superclass
|
||||
[self shouldAddCloseButton:NO];
|
||||
[self shouldApplyModalTheme:NO];
|
||||
[super viewDidLoad];
|
||||
|
||||
_touches = [[UITapGestureRecognizer alloc] init];
|
||||
[_touches setNumberOfTouchesRequired:1];
|
||||
[_touches setNumberOfTapsRequired:1];
|
||||
[_touches setDelegate:self];
|
||||
[_touches setEnabled:YES];
|
||||
[self.view addGestureRecognizer:_touches];
|
||||
|
||||
|
||||
//figure out what to ask the user for
|
||||
NSString* titleText;
|
||||
if ([UserInfo CurrentPlayer].userEmail != nil)
|
||||
{
|
||||
_hiddenEmail = [[SignupVerifier sharedInstance] obfuscateEmail:[UserInfo CurrentPlayer].userEmail];
|
||||
[_txtEmail setText:_hiddenEmail];
|
||||
[_btnSave setTitle:NSLocalizedString(@"SaveWord",nil) forState:UIControlStateNormal ];
|
||||
titleText = NSLocalizedString(@"ChangeEmailWord", nil);
|
||||
}
|
||||
else
|
||||
{
|
||||
[_btnSave setTitle:NSLocalizedString(@"AddWord", nil) forState:UIControlStateNormal];
|
||||
titleText = NSLocalizedString(@"AddEmailWord", nil);
|
||||
}
|
||||
|
||||
[_lblTitle setText:titleText];
|
||||
[_lblAlertReason setText:NSLocalizedString(@"AddEmailSuggestion", nil)];
|
||||
[_lblAlertReason setFont:[RobloxTheme fontBody]];
|
||||
[_lblAlertReason setTextColor:[RobloxTheme colorRed3]];
|
||||
|
||||
if (![RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
[_whiteView.layer setShadowColor:[UIColor blackColor].CGColor];
|
||||
[_whiteView.layer setShadowOpacity:0.4];
|
||||
[_whiteView.layer setShadowRadius:2.0];
|
||||
[_whiteView.layer setShadowOffset:CGSizeMake(0.0, 0.5)];
|
||||
//[_lblTitle setText:titleText];
|
||||
}
|
||||
//else
|
||||
//{
|
||||
// self.navigationItem.title = titleText;
|
||||
//}
|
||||
|
||||
[_btnCancel setTitle:NSLocalizedString(@"CancelWord",nil) forState:UIControlStateNormal ];
|
||||
[RobloxTheme applyToModalCancelButton:_btnCancel];
|
||||
[RobloxTheme applyToModalSubmitButton:_btnSave];
|
||||
|
||||
__weak RBAccountManagerEmailViewController* weakSelf = self;
|
||||
|
||||
|
||||
|
||||
//[_txtEmail setText:[UserInfo CurrentPlayer].userEmail ? [UserInfo CurrentPlayer].userEmail : nil];
|
||||
[_txtEmail setTitle:[UserInfo CurrentPlayer].userOver13 ? NSLocalizedString(@"EmailWord", nil) : NSLocalizedString(@"EmailUnder13Word", nil)];
|
||||
[_txtEmail setHint:NSLocalizedString(@"ChangeEmailExample", nil)];
|
||||
[_txtEmail setKeyboardType:UIKeyboardTypeEmailAddress];
|
||||
[_txtEmail setNextResponder:_txtPassword];
|
||||
[_txtEmail setValidationBlock:^{
|
||||
if ([weakSelf matchesOriginalEmail:weakSelf.txtEmail.text])
|
||||
{
|
||||
[weakSelf.txtEmail markAsInvalid];
|
||||
[weakSelf.txtEmail showError:NSLocalizedString(@"ErrorSameEmail", nil)];
|
||||
return;
|
||||
}
|
||||
|
||||
[[SignupVerifier sharedInstance] checkIfValidEmail:weakSelf.txtEmail.text completion:^(BOOL success, NSString *message)
|
||||
{
|
||||
if (success)
|
||||
[weakSelf.txtEmail markAsValid];
|
||||
else
|
||||
{
|
||||
[weakSelf.txtEmail markAsInvalid];
|
||||
[weakSelf.txtEmail showError:message];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
|
||||
[_txtPassword setTitle:NSLocalizedString(@"PasswordCurrentWord", nil)];
|
||||
[_txtPassword setHint:NSLocalizedString(@"PasswordCurrentPhrase", nil)];
|
||||
[_txtPassword setProtectedTextEntry:YES];
|
||||
[_txtPassword setValidationBlock:^{
|
||||
if (weakSelf.txtPassword.text && weakSelf.txtPassword.text.length > 0)
|
||||
[weakSelf.txtPassword markAsValid];
|
||||
else
|
||||
[weakSelf.txtPassword markAsNormal];
|
||||
}];
|
||||
|
||||
|
||||
}
|
||||
-(void) viewWillLayoutSubviews
|
||||
{
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
|
||||
if ([UserInfo CurrentPlayer].userEmail == nil)
|
||||
{
|
||||
CGSize textSize = [_lblAlertReason.text sizeWithAttributes:@{NSFontAttributeName:_lblAlertReason.font}];
|
||||
int numRows = ceilf(textSize.width / _lblAlertReason.width);
|
||||
|
||||
_imgAlert.hidden = NO;
|
||||
_lblAlertReason.hidden = NO;
|
||||
[_lblAlertReason setY:_lblTitle.bottom];
|
||||
[_lblAlertReason setHeight:MAX(20.0, MIN(textSize.height * numRows, 60.0))]; //clamp the height
|
||||
[_lblAlertReason setNumberOfLines:numRows];
|
||||
[_lblAlertReason sizeToFit];
|
||||
[_imgAlert setY:_lblAlertReason.center.y - (_imgAlert.height * 0.5) ];
|
||||
|
||||
int contentHeight = _lblTitle.height + _lblAlertReason.height + _txtPassword.height + _txtPassword.height + _btnCancel.height;
|
||||
int margin = (([RobloxInfo thisDeviceIsATablet] ? self.view.height : _whiteView.height) - contentHeight) / 5 ;
|
||||
|
||||
//move the other text fields down to compensate
|
||||
[_txtEmail setY:_lblAlertReason.bottom + margin];
|
||||
[_txtPassword setY:_txtEmail.bottom + margin];
|
||||
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
float lowest = _txtPassword.bottom + margin;
|
||||
[_btnCancel setY:lowest];
|
||||
[_btnSave setY:lowest];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_imgAlert.hidden = YES;
|
||||
_lblAlertReason.hidden = YES;
|
||||
|
||||
int contentHeight = _lblTitle.height + _txtPassword.height + _txtPassword.height + _btnCancel.height;
|
||||
int margin = (([RobloxInfo thisDeviceIsATablet] ? self.view.height : _whiteView.height) - contentHeight) / 4 ;
|
||||
|
||||
//move the other text fields down to compensate
|
||||
[_txtEmail setY:_lblTitle.bottom + (margin * 0.5)];
|
||||
[_txtPassword setY:_txtEmail.bottom + margin];
|
||||
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
float lowest = self.view.height - (_btnCancel.height + 10);
|
||||
[_btnCancel setY:lowest];
|
||||
[_btnSave setY:lowest];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
#pragma mark Helper Functions
|
||||
|
||||
-(void) disableUI
|
||||
{
|
||||
[_btnCancel setEnabled:NO];
|
||||
[_btnSave setEnabled:NO];
|
||||
[RobloxHUD showSpinnerWithLabel:NSLocalizedString(@"ChangeEmailVerb", nil) dimBackground:YES];
|
||||
}
|
||||
-(void) enableUI
|
||||
{
|
||||
[_btnCancel setEnabled:YES];
|
||||
[_btnSave setEnabled:YES];
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
|
||||
}
|
||||
-(bool) matchesOriginalEmail:(NSString*)email
|
||||
{
|
||||
if (![UserInfo CurrentPlayer].userEmail)
|
||||
return NO;
|
||||
|
||||
bool matchesHidden = [email isEqualToString:_hiddenEmail];
|
||||
bool matchesExisting = [email isEqualToString:[UserInfo CurrentPlayer].userEmail];
|
||||
return (matchesHidden || matchesExisting);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark UI Actions
|
||||
- (void)returnToPrevious:(id)sender { NSLog(@"Returning to previous"); [self.navigationController popViewControllerAnimated:YES]; }
|
||||
- (IBAction)saveEmailChange:(id)sender
|
||||
{
|
||||
//[_txtEmail resignFirstResponder];
|
||||
//[_txtPassword resignFirstResponder];
|
||||
[RBXFunctions dispatchOnMainThread:^{ [self disableUI]; }];
|
||||
|
||||
[RBXFunctions dispatchAfter:1.0 onMainThread:^
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///CHECKS ROUND 1 - check if any required fields are missing
|
||||
|
||||
//check if the required field is missing
|
||||
bool hasEmail = _txtEmail.text && _txtEmail.text.length > 0;
|
||||
bool hasPassword = _txtPassword.text && _txtPassword.text.length > 0;
|
||||
bool invalidEmail = NO;
|
||||
|
||||
if (!hasEmail)
|
||||
{
|
||||
//[RobloxHUD showMessage:NSLocalizedString(@"FieldsIncomplete", nil)];
|
||||
[_txtEmail showError:NSLocalizedString(@"MissingEmailWord", nil)];
|
||||
[_txtEmail markAsInvalid];
|
||||
|
||||
[_txtEmail becomeFirstResponder];
|
||||
}
|
||||
else
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///CHECKS ROUND 1.5 - check that the email is valid
|
||||
if ([self matchesOriginalEmail:_txtEmail.text])
|
||||
{
|
||||
[_txtEmail markAsInvalid];
|
||||
[_txtEmail showError:NSLocalizedString(@"ErrorSameEmail", nil)];
|
||||
|
||||
[_txtEmail becomeFirstResponder];
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasPassword)
|
||||
{
|
||||
//[RobloxHUD showMessage:NSLocalizedString(@"FieldsIncomplete", nil)];
|
||||
[_txtPassword showError:NSLocalizedString(@"PasswordCurrentPhrase", nil)];
|
||||
[_txtPassword markAsInvalid];
|
||||
|
||||
//goto the missing field
|
||||
if (![_txtEmail isFirstResponder])
|
||||
[_txtPassword becomeFirstResponder];
|
||||
}
|
||||
|
||||
|
||||
//do not proceed until checks 1 and 1.5 succeed
|
||||
if (!hasEmail || !hasPassword || invalidEmail)
|
||||
{
|
||||
[self enableUI];
|
||||
return;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///CHECKS ROUND2 - validation checks
|
||||
if (!_txtEmail.isValidated)
|
||||
{
|
||||
[_txtEmail becomeFirstResponder];
|
||||
[self enableUI];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//display the loading spinner
|
||||
//[RobloxHUD showSpinnerWithLabel:NSLocalizedString(@"ChangeEmailVerb", nil) dimBackground:YES];
|
||||
|
||||
//send the request and alert the player with the response
|
||||
[RobloxData changeUserEmail:_txtEmail.text
|
||||
withPassword:_txtPassword.text
|
||||
andCompletion:^(BOOL success, NSString *message)
|
||||
{
|
||||
NSString* title = NSLocalizedString(@"ErrorWord", nil);
|
||||
if (success)
|
||||
{
|
||||
//clear the UI
|
||||
[_txtPassword setText:@""];
|
||||
title = NSLocalizedString(@"SuccessWord", nil);
|
||||
|
||||
[UserInfo CurrentPlayer].userEmail = _txtEmail.text;
|
||||
[[UserInfo CurrentPlayer] UpdateAccountInfo];
|
||||
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
_hiddenEmail = [[SignupVerifier sharedInstance] obfuscateEmail:_txtEmail.text];
|
||||
[_txtEmail setText:_hiddenEmail];
|
||||
}];
|
||||
|
||||
//hide the warning and resize the view
|
||||
[UIView animateWithDuration:1.0
|
||||
animations:^
|
||||
{
|
||||
_imgAlert.alpha = 0.0;
|
||||
_lblAlertReason.alpha = 0.0;
|
||||
}
|
||||
completion:^(BOOL finished)
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[_lblTitle setText:NSLocalizedString(@"ChangeEmailWord", nil)];
|
||||
[self viewWillLayoutSubviews];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[_txtEmail markAsInvalid];
|
||||
[_txtEmail showError:message];
|
||||
[_txtEmail becomeFirstResponder];
|
||||
}];
|
||||
}
|
||||
|
||||
//tell the user what's going on
|
||||
//[RobloxHUD hideSpinner:YES];
|
||||
[self enableUI];
|
||||
[RobloxHUD prompt:message withTitle:title];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
- (IBAction)closeController:(id)sender {
|
||||
//[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonClose withContext:RBXAContextLogin];
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
- (void) resignAllResponders
|
||||
{
|
||||
[self.view endEditing:YES];
|
||||
//[_txtEmail resignFirstResponder];
|
||||
//[_txtPassword resignFirstResponder];
|
||||
}
|
||||
|
||||
//Delegate functions
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
|
||||
{
|
||||
//NSLog(@"GestureRecognizer : %@", touch);
|
||||
UIView* touchedView = touch.view;
|
||||
if (touchedView == self.view || touchedView == _whiteView)
|
||||
{
|
||||
[self resignAllResponders];
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// RBAccountManagerPasswordViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 9/26/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#include "RBModalPopUpViewController.h"
|
||||
#include "RBValidTextField.h"
|
||||
|
||||
//---------------------Change User Password Screen------------------------------
|
||||
@interface RBAccountManagerPasswordViewController : RBModalPopUpViewController <UIGestureRecognizerDelegate>
|
||||
|
||||
@property IBOutlet RBValidTextField* txtPasswordNew;
|
||||
@property IBOutlet RBValidTextField* txtPasswordConfirm;
|
||||
@property IBOutlet RBValidTextField* txtPasswordOld;
|
||||
|
||||
@property IBOutlet UIButton* btnSave;
|
||||
@property IBOutlet UIButton* btnCancel;
|
||||
@property IBOutlet UIView* whiteView;
|
||||
@property IBOutlet UILabel* lblTitle;
|
||||
|
||||
-(IBAction)savePasswordChange:(id)sender;
|
||||
@end
|
||||
@@ -0,0 +1,459 @@
|
||||
//
|
||||
// RBAccountManagerPasswordViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 9/26/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBAccountManagerPasswordViewController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "UserInfo.h"
|
||||
#import "LoginManager.h"
|
||||
#import "RobloxData.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "SignUpVerifier.h"
|
||||
#import "RobloxAlert.h"
|
||||
#import "RBCaptchaViewController.h"
|
||||
#import "RBCaptchaV2ViewController.h"
|
||||
#import "NonRotatableNavigationController.h"
|
||||
#import "RBXFunctions.h"
|
||||
#import "RobloxNotifications.h"
|
||||
|
||||
#define DEFAULT_VIEW_SIZE CGRectMake(0, 0, 540, 344)
|
||||
|
||||
@interface RBAccountManagerPasswordViewController ()
|
||||
|
||||
@end
|
||||
|
||||
|
||||
//-------------CHANGE PASSWORD SCREEN------------
|
||||
@implementation RBAccountManagerPasswordViewController
|
||||
{
|
||||
NSString* usernameCopy;
|
||||
UITapGestureRecognizer* _touches;
|
||||
bool accountHasPassword;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
//configure the modal popup superclass
|
||||
[self shouldAddCloseButton:NO];
|
||||
[self shouldApplyModalTheme:NO];
|
||||
[super viewDidLoad];
|
||||
|
||||
accountHasPassword = [[UserInfo CurrentPlayer] userHasSetPassword] || ![RBXFunctions isEmptyString:[UserInfo CurrentPlayer].password];
|
||||
|
||||
|
||||
_touches = [[UITapGestureRecognizer alloc] init];
|
||||
[_touches setNumberOfTouchesRequired:1];
|
||||
[_touches setNumberOfTapsRequired:1];
|
||||
[_touches setDelegate:self];
|
||||
[_touches setEnabled:YES];
|
||||
[self.view addGestureRecognizer:_touches];
|
||||
|
||||
//NSString* titleText = NSLocalizedString(@"ChangePasswordWord", nil);
|
||||
[_lblTitle setText:accountHasPassword ? NSLocalizedString(@"ChangePasswordWord", nil) : NSLocalizedString(@"AddPassword", nil)];
|
||||
if (![RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
[RobloxTheme applyShadowToView:_whiteView];
|
||||
|
||||
//[_lblTitle setText:titleText];
|
||||
}
|
||||
|
||||
__weak RBAccountManagerPasswordViewController* weakSelf = self;
|
||||
|
||||
[_txtPasswordNew setTitle:NSLocalizedString(@"PasswordNewWord", nil)];
|
||||
[_txtPasswordNew setHint:NSLocalizedString(@"PasswordRequirements", nil)];
|
||||
[_txtPasswordNew setProtectedTextEntry:YES];
|
||||
[_txtPasswordNew setNextResponder:_txtPasswordConfirm];
|
||||
[_txtPasswordNew setValidationBlock:^{
|
||||
[[SignupVerifier sharedInstance] checkIfValidPassword:weakSelf.txtPasswordNew.text
|
||||
withUsername:[UserInfo CurrentPlayer].username
|
||||
completion:^(BOOL success, NSString *validMessage)
|
||||
{
|
||||
if (success)
|
||||
[weakSelf.txtPasswordNew markAsValid];
|
||||
else
|
||||
{
|
||||
[weakSelf.txtPasswordNew markAsInvalid];
|
||||
[weakSelf.txtPasswordNew showError:validMessage];
|
||||
}
|
||||
}];
|
||||
|
||||
if (weakSelf.txtPasswordConfirm.text.length > 0)
|
||||
{
|
||||
[[SignupVerifier sharedInstance] checkIfPasswordsMatch:weakSelf.txtPasswordNew.text
|
||||
withVerification:weakSelf.txtPasswordConfirm.text
|
||||
completion:^(BOOL passwordsMatch, NSString *matchMessage)
|
||||
{
|
||||
if (passwordsMatch)
|
||||
[weakSelf.txtPasswordConfirm markAsValid];
|
||||
else
|
||||
{
|
||||
[weakSelf.txtPasswordConfirm markAsInvalid];
|
||||
[weakSelf.txtPasswordConfirm showError:matchMessage];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}];
|
||||
|
||||
[_txtPasswordConfirm setTitle:NSLocalizedString(@"PasswordConfirmWord", nil)];
|
||||
[_txtPasswordConfirm setHint:NSLocalizedString(@"PasswordConfirmPhrase", nil)];
|
||||
[_txtPasswordConfirm setProtectedTextEntry:YES];
|
||||
[_txtPasswordConfirm setNextResponder:_txtPasswordOld];
|
||||
[_txtPasswordConfirm setValidationBlock:^{
|
||||
[[SignupVerifier sharedInstance] checkIfPasswordsMatch:weakSelf.txtPasswordNew.text
|
||||
withVerification:weakSelf.txtPasswordConfirm.text
|
||||
completion:^(BOOL passwordsMatch, NSString *message)
|
||||
{
|
||||
if (passwordsMatch)
|
||||
[weakSelf.txtPasswordConfirm markAsValid];
|
||||
else
|
||||
{
|
||||
[weakSelf.txtPasswordConfirm markAsInvalid];
|
||||
[weakSelf.txtPasswordConfirm showError:message];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
|
||||
[_txtPasswordOld setTitle:NSLocalizedString(@"PasswordCurrentWord", nil)];
|
||||
[_txtPasswordOld setHint:NSLocalizedString(@"PasswordCurrentPhrase", nil)];
|
||||
[_txtPasswordOld setProtectedTextEntry:YES];
|
||||
[_txtPasswordOld setValidationBlock:^{
|
||||
if (weakSelf.txtPasswordOld.text.length >= 1)
|
||||
[weakSelf.txtPasswordOld markAsValid];
|
||||
}];
|
||||
//[_txtPasswordOld setExitOnEnterBlock:^{
|
||||
// [weakSelf savePasswordChange:weakSelf.txtPasswordOld];
|
||||
//}];
|
||||
[_txtPasswordOld setHidden:!accountHasPassword];
|
||||
|
||||
[_btnSave setTitle:NSLocalizedString(@"SaveWord",nil) forState:UIControlStateNormal ];
|
||||
[RobloxTheme applyToModalSubmitButton:_btnSave];
|
||||
|
||||
[_btnCancel setTitle:NSLocalizedString(@"CancelWord",nil) forState:UIControlStateNormal ];
|
||||
[RobloxTheme applyToModalCancelButton:_btnCancel];
|
||||
|
||||
usernameCopy = [NSString stringWithFormat:@"%@", [UserInfo CurrentPlayer].username];
|
||||
}
|
||||
|
||||
|
||||
//Action functions
|
||||
- (IBAction)savePasswordChange:(id)sender
|
||||
{
|
||||
//dismiss the keyboard if it is up
|
||||
[self resignAllResponders];
|
||||
|
||||
if (accountHasPassword)
|
||||
[self savePassword];
|
||||
else
|
||||
[self addPassword];
|
||||
}
|
||||
-(void) savePassword
|
||||
{
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///CHECKS ROUND 1 - check if any required fields are missing
|
||||
|
||||
bool hasPasswordNew = (_txtPasswordNew.text.length > 0);
|
||||
bool hasPasswordConfirm = (_txtPasswordConfirm.text.length > 0);
|
||||
bool hasPasswordOld = (_txtPasswordOld.text.length > 0);
|
||||
if (!hasPasswordNew)
|
||||
{
|
||||
[_txtPasswordNew markAsInvalid];
|
||||
[_txtPasswordNew showError:NSLocalizedString(@"PasswordMissing", nil)];
|
||||
|
||||
[_txtPasswordNew becomeFirstResponder];
|
||||
}
|
||||
if (!hasPasswordConfirm)
|
||||
{
|
||||
[_txtPasswordConfirm markAsInvalid];
|
||||
[_txtPasswordConfirm showError:NSLocalizedString(@"PasswordConfirmPhrase", nil)];
|
||||
|
||||
if (hasPasswordNew)
|
||||
[_txtPasswordConfirm becomeFirstResponder];
|
||||
}
|
||||
if (!hasPasswordOld)
|
||||
{
|
||||
[_txtPasswordOld markAsInvalid];
|
||||
[_txtPasswordOld showError:NSLocalizedString(@"PasswordCurrentPhrase", nil)];
|
||||
|
||||
if (hasPasswordNew && hasPasswordConfirm)
|
||||
[_txtPasswordOld becomeFirstResponder];
|
||||
}
|
||||
|
||||
//do not proceed until we have all three
|
||||
if (!hasPasswordNew || !hasPasswordConfirm || !hasPasswordOld)
|
||||
return;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///CHECKS ROUND 2 - check that the new passwords match
|
||||
if (![_txtPasswordNew.text isEqualToString:_txtPasswordConfirm.text])
|
||||
{
|
||||
[_txtPasswordConfirm markAsInvalid];
|
||||
[_txtPasswordConfirm showError:NSLocalizedString(@"VerifyPasswordWrong", nil)];
|
||||
|
||||
[_txtPasswordConfirm becomeFirstResponder];
|
||||
return;
|
||||
}
|
||||
|
||||
//-- THESE CHECKS ARE A SECURITY HAZARD --
|
||||
//check that the old password is correct
|
||||
//if (![[UserInfo CurrentPlayer].password isEqualToString:[_txtUserPasswordCurrent text]])
|
||||
//{
|
||||
// [RobloxHUD showMessage:NSLocalizedString(@"VerifyError", nil)];
|
||||
// return;
|
||||
//}
|
||||
|
||||
//check that the new password is not the same as the old one
|
||||
//if ([[UserInfo CurrentPlayer].password isEqualToString:[_txtUserPasswordNew text]])
|
||||
//{
|
||||
// [RobloxHUD showMessage:NSLocalizedString(@"ErrorSamePassword", nil)];
|
||||
// return;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///CHECKS ROUND 3 - check that all fields are validated
|
||||
if (!_txtPasswordNew.isValidated) { [_txtPasswordNew becomeFirstResponder]; return; }
|
||||
if (!_txtPasswordConfirm.isValidated) { [_txtPasswordConfirm becomeFirstResponder]; return; }
|
||||
if (!_txtPasswordOld.isValidated) { [_txtPasswordOld becomeFirstResponder]; return; }
|
||||
|
||||
|
||||
//display the loading spinner
|
||||
[RobloxHUD showSpinnerWithLabel:NSLocalizedString(@"ChangePasswordVerb", nil) dimBackground:YES];
|
||||
|
||||
//looks like everything is good, send the request
|
||||
[RobloxData changeUserOldPassword:_txtPasswordOld.text
|
||||
toNewPassword:_txtPasswordNew.text
|
||||
withConfirmation:_txtPasswordConfirm.text
|
||||
andCompletion:^(BOOL success, NSString *message)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
//now we need to log the user out and log back in with the new credentials
|
||||
//do this all under the hood
|
||||
|
||||
//once we have reset the session by logging the user out and back in with the new credentials,
|
||||
//then we can hide the spinner and alert the user that the password change was a success
|
||||
//display the message to the user
|
||||
|
||||
[[LoginManager sharedInstance] loginWithUsername:usernameCopy password:_txtPasswordNew.text completionBlock:^(NSError *loginError) {
|
||||
if ([RBXFunctions isEmpty:loginError]) {
|
||||
// login successful
|
||||
[self didCompleteReLogin];
|
||||
} else {
|
||||
// login failure
|
||||
[self didFailReLoginWithError:loginError];
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
//it didn't work for some reason, hide the loading spinner and display the message to the user
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
[RobloxHUD prompt:message withTitle:NSLocalizedString(@"ErrorWord", nil)];
|
||||
[_txtPasswordNew setText:@""];
|
||||
[_txtPasswordConfirm setText:@""];
|
||||
[_txtPasswordOld setText:@""];
|
||||
}];
|
||||
}
|
||||
}];
|
||||
});
|
||||
}
|
||||
-(void) addPassword
|
||||
{
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.0 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///CHECKS ROUND 1 - check if any required fields are missing
|
||||
|
||||
bool hasPasswordNew = (_txtPasswordNew.text.length > 0);
|
||||
bool hasPasswordConfirm = (_txtPasswordConfirm.text.length > 0);
|
||||
if (!hasPasswordNew)
|
||||
{
|
||||
[_txtPasswordNew markAsInvalid];
|
||||
[_txtPasswordNew showError:NSLocalizedString(@"PasswordMissing", nil)];
|
||||
|
||||
[_txtPasswordNew becomeFirstResponder];
|
||||
}
|
||||
if (!hasPasswordConfirm)
|
||||
{
|
||||
[_txtPasswordConfirm markAsInvalid];
|
||||
[_txtPasswordConfirm showError:NSLocalizedString(@"PasswordConfirmPhrase", nil)];
|
||||
|
||||
if (hasPasswordNew)
|
||||
[_txtPasswordConfirm becomeFirstResponder];
|
||||
}
|
||||
|
||||
//do not proceed until we have all three
|
||||
if (!hasPasswordNew || !hasPasswordConfirm)
|
||||
return;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///CHECKS ROUND 2 - check that the new passwords match
|
||||
if (![_txtPasswordNew.text isEqualToString:_txtPasswordConfirm.text])
|
||||
{
|
||||
[_txtPasswordConfirm markAsInvalid];
|
||||
[_txtPasswordConfirm showError:NSLocalizedString(@"VerifyPasswordWrong", nil)];
|
||||
|
||||
[_txtPasswordConfirm becomeFirstResponder];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
///CHECKS ROUND 3 - check that all fields are validated
|
||||
if (!_txtPasswordNew.isValidated) { [_txtPasswordNew becomeFirstResponder]; return; }
|
||||
if (!_txtPasswordConfirm.isValidated) { [_txtPasswordConfirm becomeFirstResponder]; return; }
|
||||
|
||||
|
||||
//display the loading spinner
|
||||
[RobloxHUD showSpinnerWithLabel:NSLocalizedString(@"ChangePasswordVerb", nil) dimBackground:YES];
|
||||
|
||||
//looks like everything is good, send the request
|
||||
[RobloxData changeUserOldPassword:_txtPasswordConfirm.text
|
||||
toNewPassword:_txtPasswordNew.text
|
||||
withConfirmation:_txtPasswordConfirm.text
|
||||
andCompletion:^(BOOL success, NSString *message)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
//now we need to log the user out and log back in with the new credentials
|
||||
//do this all under the hood
|
||||
|
||||
//once we have reset the session by logging the user out and back in with the new credentials,
|
||||
//then we can hide the spinner and alert the user that the password change was a success
|
||||
//display the message to the user
|
||||
|
||||
[[LoginManager sharedInstance] loginWithUsername:usernameCopy password:_txtPasswordNew.text completionBlock:^(NSError *loginError) {
|
||||
if ([RBXFunctions isEmpty:loginError]) {
|
||||
// login successful
|
||||
[self didCompleteReLogin];
|
||||
} else {
|
||||
// login failure
|
||||
[self didFailReLoginWithError:loginError];
|
||||
}
|
||||
}];
|
||||
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
accountHasPassword = YES;
|
||||
[_txtPasswordOld setHidden:NO];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
//it didn't work for some reason, hide the loading spinner and display the message to the user
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
[RobloxHUD prompt:message withTitle:NSLocalizedString(@"ErrorWord", nil)];
|
||||
[_txtPasswordNew setText:@""];
|
||||
[_txtPasswordConfirm setText:@""];
|
||||
[_txtPasswordOld setText:@""];
|
||||
}];
|
||||
}
|
||||
}];
|
||||
});
|
||||
}
|
||||
- (IBAction)closeController:(id)sender
|
||||
{
|
||||
//[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonClose withContext:RBXAContextLogin];
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
//Notification functions
|
||||
- (void) didCompleteReLogin
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
[RobloxHUD prompt:NSLocalizedString(@"SuccessChangePassword", nil) withTitle:NSLocalizedString(@"SuccessWord", nil)];
|
||||
[self.navigationController popViewControllerAnimated:NO];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) didFailReLoginWithError:(NSError *)reloginError
|
||||
{
|
||||
//This is really unusual if it happens, password change succeeds, but relogin fails?
|
||||
NSString* failureReason = reloginError.domain ? reloginError.domain : NSLocalizedString(@"UnknownError", nil);
|
||||
if ([failureReason isEqualToString:NSLocalizedString(@"TooManyAttempts", nil)])
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
//open up a captcha so we can attempt to log in again
|
||||
UIViewController* controller;
|
||||
if ([LoginManager apiProxyEnabled] == YES) {
|
||||
controller = [RBCaptchaV2ViewController CaptchaV2ForLoginWithUsername:[UserInfo CurrentPlayer].username completionHandler:^(NSError *captchaError) {
|
||||
if ([RBXFunctions isEmpty:captchaError]) {
|
||||
[self savePasswordChange:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxHUD prompt:failureReason withTitle:NSLocalizedString(@"ErrorWord", nil)];
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
controller = [RBCaptchaViewController CaptchaWithCompletionHandler:^(bool success, NSString *message) {
|
||||
if (success == YES) {
|
||||
[self savePasswordChange:nil];
|
||||
}
|
||||
}];
|
||||
}
|
||||
NonRotatableNavigationController* navigation = [[NonRotatableNavigationController alloc] initWithRootViewController:controller];
|
||||
navigation.modalPresentationStyle = UIModalPresentationFormSheet;
|
||||
[self presentViewController:navigation animated:YES completion:nil];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
//we could not log the player back in. Apologize and log them out
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
//log the player out
|
||||
[[LoginManager sharedInstance] logoutRobloxUser];
|
||||
|
||||
[RobloxHUD prompt:NSLocalizedString(@"ErrorCannotReLogin", nil) withTitle:NSLocalizedString(@"ErrorUnknownTitle",nil)];
|
||||
|
||||
//dismiss the view
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
[(UINavigationController*)self.navigationController.presentingViewController popToRootViewControllerAnimated:YES];
|
||||
[self dismissViewControllerAnimated:NO completion:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self.tabBarController.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
|
||||
{
|
||||
UITouch* touch = [[event allTouches] anyObject];
|
||||
if ([[touch view] isEqual:self.view])
|
||||
[self resignAllResponders];
|
||||
|
||||
[super touchesBegan:touches withEvent:event];
|
||||
}
|
||||
- (void) resignAllResponders
|
||||
{
|
||||
[self.view endEditing:YES];
|
||||
//[_txtPasswordNew resignFirstResponder];
|
||||
//[_txtPasswordConfirm resignFirstResponder];
|
||||
//[_txtPasswordOld resignFirstResponder];
|
||||
}
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
|
||||
{
|
||||
UIView* touchedView = touch.view;
|
||||
if (touchedView == self.view || touchedView == _whiteView)
|
||||
[self resignAllResponders];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,39 @@
|
||||
//
|
||||
// RBAccountManagerSocialViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 9/14/15.
|
||||
// Copyright © 2015 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface RBSocialLinkView : UIView
|
||||
|
||||
-(void) initWithSocialName:(NSString*)socialName
|
||||
iconName:(NSString*)iconName
|
||||
providerName:(NSString*)providerName
|
||||
andRefToController:(UIViewController*)controllerRef;
|
||||
|
||||
-(void) setIsConnected:(BOOL)connectedToIdentity;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@interface RBAccountManagerSocialViewController : UIViewController
|
||||
|
||||
@property IBOutlet UILabel* lblTitle;
|
||||
@property IBOutlet UILabel* lblWarning;
|
||||
@property IBOutlet UIView* whiteView;
|
||||
@property IBOutlet UIButton* btnCancel;
|
||||
@property IBOutlet UIButton* btnUpdate;
|
||||
|
||||
@property IBOutlet RBSocialLinkView* rbsFacebook;
|
||||
@property IBOutlet RBSocialLinkView* rbsTwitter;
|
||||
@property IBOutlet RBSocialLinkView* rbsGPlus;
|
||||
|
||||
- (IBAction)updateInfo:(id)sender;
|
||||
- (IBAction)closeController:(id)sender;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,425 @@
|
||||
//
|
||||
// RBAccountManagerSocialViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 9/14/15.
|
||||
// Copyright © 2015 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBAccountManagerSocialViewController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "UserInfo.h"
|
||||
#import "LoginManager.h"
|
||||
#import "RobloxAlert.h"
|
||||
#import "RBActivityIndicatorView.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "iOSSettingsService.h"
|
||||
#import "RBXEventReporter.h"
|
||||
|
||||
DYNAMIC_FASTFLAGVARIABLE(EnableFacebookConnection, true);
|
||||
DYNAMIC_FASTFLAGVARIABLE(EnableTwitterConnection, false);
|
||||
DYNAMIC_FASTFLAGVARIABLE(EnableGooglePlusConnection, false);
|
||||
|
||||
@interface RBSocialLinkView ()
|
||||
@property (nonatomic) BOOL isConnected;
|
||||
@property (nonatomic, retain) NSString* providerID;
|
||||
|
||||
@property (nonatomic, retain) UILabel* lblSocialPlatformName;
|
||||
@property (nonatomic, retain) UILabel* lblConnectedUserName;
|
||||
@property (nonatomic, retain) UIButton* btnConnect;
|
||||
@property (nonatomic, retain) UIButton* btnDisconnect;
|
||||
@property (nonatomic, retain) RBActivityIndicatorView* spinner;
|
||||
|
||||
@property (weak, nonatomic) UIViewController* controllerRef;
|
||||
@end
|
||||
|
||||
@implementation RBSocialLinkView
|
||||
|
||||
-(void) initWithSocialName:(NSString*)socialName
|
||||
iconName:(NSString*)iconName
|
||||
providerName:(NSString*)providerName
|
||||
andRefToController:(UIViewController*)controllerRef
|
||||
{
|
||||
_controllerRef = controllerRef;
|
||||
_providerID = providerName;
|
||||
|
||||
_spinner = [[RBActivityIndicatorView alloc] init];
|
||||
[_spinner setHidden:YES];
|
||||
|
||||
_lblConnectedUserName = [[UILabel alloc] init];
|
||||
[_lblConnectedUserName setText:@""];
|
||||
[_lblConnectedUserName setFont:[RobloxTheme fontBodySmall]];
|
||||
[_lblConnectedUserName setNumberOfLines:2];
|
||||
[_lblConnectedUserName setLineBreakMode:NSLineBreakByWordWrapping];
|
||||
[_lblConnectedUserName setTextAlignment:NSTextAlignmentCenter];
|
||||
|
||||
_lblSocialPlatformName = [[UILabel alloc] init];
|
||||
[_lblSocialPlatformName setText:socialName];
|
||||
[_lblSocialPlatformName setFont:[RobloxTheme fontBody]];
|
||||
|
||||
_btnConnect = [[UIButton alloc] init];
|
||||
[_btnConnect setTitle:NSLocalizedString(@"ConnectWord", nil) forState:UIControlStateNormal];
|
||||
//[_btnConnect setImage:[UIImage imageNamed:iconName] forState:UIControlStateNormal];
|
||||
[_btnConnect addTarget:self action:@selector(connect:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[RobloxTheme applyToModalSubmitButton:_btnConnect];
|
||||
|
||||
_btnDisconnect = [[UIButton alloc] init];
|
||||
[_btnDisconnect setTitle:NSLocalizedString(@"DisconnectWord", nil) forState:UIControlStateNormal];
|
||||
//[_btnDisconnect setImage:[UIImage imageNamed:iconName] forState:UIControlStateNormal];
|
||||
[_btnDisconnect addTarget:self action:@selector(disconnect:) forControlEvents:UIControlEventTouchUpInside];
|
||||
[RobloxTheme applyToModalCancelButton:_btnDisconnect];
|
||||
|
||||
[self addSubview:_spinner];
|
||||
[self addSubview:_lblSocialPlatformName];
|
||||
[self addSubview:_lblConnectedUserName];
|
||||
[self addSubview:_btnConnect];
|
||||
[self addSubview:_btnDisconnect];
|
||||
}
|
||||
|
||||
-(void) layoutSubviews
|
||||
{
|
||||
[super layoutSubviews];
|
||||
|
||||
CGSize cellThird = CGSizeMake(self.frame.size.width * 0.33, self.frame.size.height);
|
||||
|
||||
[_lblSocialPlatformName setFrame:CGRectMake(0, 0, cellThird.width, cellThird.height)];
|
||||
[_lblConnectedUserName setFrame:CGRectMake(cellThird.width * 2, 0, cellThird.width, cellThird.height)];
|
||||
|
||||
[_btnConnect setFrame:CGRectMake(cellThird.width * 1, 0, cellThird.width, cellThird.height)];
|
||||
[_btnDisconnect setFrame:CGRectMake(cellThird.width * 1, 0, cellThird.width, cellThird.height)];
|
||||
|
||||
|
||||
//float imgHeight = cellThird.height * 0.25;
|
||||
//int margin = 5;
|
||||
//[_btnConnect setImageEdgeInsets:UIEdgeInsetsMake(imgHeight, margin, imgHeight, cellThird.width - (imgHeight + margin))];
|
||||
//[_btnConnect setTitleEdgeInsets:UIEdgeInsetsMake(0, imgHeight + (margin * 2), 0, margin)];
|
||||
|
||||
[_spinner setFrame:CGRectMake(self.center.x - (cellThird.height * 0.5), 0, cellThird.height, cellThird.height)];
|
||||
}
|
||||
|
||||
-(void) setIsConnected:(BOOL)connectedToIdentity {
|
||||
_isConnected = connectedToIdentity;
|
||||
|
||||
[_btnConnect setHidden:_isConnected];
|
||||
[_btnDisconnect setHidden:!_isConnected];
|
||||
[_lblConnectedUserName setHidden:!_isConnected];
|
||||
|
||||
[_spinner setHidden:YES];
|
||||
|
||||
if (_isConnected)
|
||||
{
|
||||
NSString* identity = [[UserInfo CurrentPlayer] getNameConnectedToIdentity:_providerID];
|
||||
[_lblConnectedUserName setText:identity ? identity : @""];
|
||||
}
|
||||
else
|
||||
[_lblConnectedUserName setText:@""];
|
||||
}
|
||||
|
||||
//button actions
|
||||
-(void) connect:(id)sender {
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonConnect withContext:RBXAContextSettingsSocial withCustomDataString:_providerID];
|
||||
|
||||
[_btnConnect setHidden:YES];
|
||||
[_btnDisconnect setHidden:YES];
|
||||
[_spinner setHidden:NO];
|
||||
[_spinner startAnimating];
|
||||
|
||||
|
||||
//refresh the userInfo first
|
||||
try
|
||||
{
|
||||
[[LoginManager sharedInstance] doSocialFetchGigyaInfoWithUID:[[UserInfo CurrentPlayer] GigyaUID]
|
||||
isLoggingIn:NO
|
||||
withCompletion:^(bool success, NSString *message)
|
||||
{
|
||||
//an optimization is to check if they are already connected, then bail out
|
||||
if ([[UserInfo CurrentPlayer] isConnectedToIdentity:_providerID])
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
//They are already connected to something, refresh their settings for them
|
||||
[_spinner stopAnimating];
|
||||
[_spinner setHidden:YES];
|
||||
[RobloxAlert RobloxAlertWithMessage:NSLocalizedString(@"SocialConnectErrorAlreadyConnected", nil)];
|
||||
[self setIsConnected:YES];
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//now connect it
|
||||
[[LoginManager sharedInstance] doSocialConnect:_controllerRef
|
||||
toProvider:_providerID
|
||||
withCompletion:^(bool success, NSString *message)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[_spinner stopAnimating];
|
||||
[_spinner setHidden:YES];
|
||||
|
||||
if (success)
|
||||
{
|
||||
[self setIsConnected:YES];
|
||||
NSString* successMessage = [NSString stringWithFormat:NSLocalizedString(@"YouAreNowConnectedPhrase", nil),_providerID];
|
||||
[RobloxAlert RobloxAlertWithMessage:successMessage];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (message)
|
||||
[RobloxAlert RobloxAlertWithMessage:message];
|
||||
[self setIsConnected:NO];
|
||||
}
|
||||
|
||||
});
|
||||
}];
|
||||
}];
|
||||
}
|
||||
catch (NSException* exception)
|
||||
{
|
||||
//catch any weird last second exceptions because Gigya is awful
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[_spinner stopAnimating];
|
||||
[_spinner setHidden:YES];
|
||||
[RobloxAlert RobloxAlertWithMessage:NSLocalizedString(@"SocialConnectErrorUnexpectedError", nil)];
|
||||
[self setIsConnected:NO];
|
||||
});
|
||||
}
|
||||
}
|
||||
-(void) disconnect:(id)sender {
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonDisconnect withContext:RBXAContextSettingsSocial withCustomDataString:_providerID];
|
||||
|
||||
[_btnConnect setHidden:YES];
|
||||
[_btnDisconnect setHidden:YES];
|
||||
[_spinner setHidden:NO];
|
||||
[_spinner startAnimating];
|
||||
|
||||
//refresh the userInfo first
|
||||
try
|
||||
{
|
||||
[[LoginManager sharedInstance] doSocialFetchGigyaInfoWithUID:[[UserInfo CurrentPlayer] GigyaUID]
|
||||
isLoggingIn:NO
|
||||
withCompletion:^(bool success, NSString *message)
|
||||
{
|
||||
//an optimization is to check if they are already disconnected, then bail out
|
||||
if ([[UserInfo CurrentPlayer] isConnectedToIdentity:_providerID] == false)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_spinner stopAnimating];
|
||||
[_spinner setHidden:YES];
|
||||
[self setIsConnected:NO];
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
//now disconnect from everything
|
||||
[[LoginManager sharedInstance] doSocialDisconnect:_providerID
|
||||
withCompletion:^(bool success, NSString *message)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[_spinner stopAnimating];
|
||||
[_spinner setHidden:YES];
|
||||
|
||||
if (success)
|
||||
[self setIsConnected:NO];
|
||||
else
|
||||
{
|
||||
if (message)
|
||||
[RobloxAlert RobloxAlertWithMessage:message];
|
||||
[self setIsConnected:YES];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}];
|
||||
|
||||
}
|
||||
catch (NSException* exception)
|
||||
{
|
||||
//catch any weird last second exceptions because Gigya is awful
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[_spinner stopAnimating];
|
||||
[_spinner setHidden:YES];
|
||||
[RobloxAlert RobloxAlertWithMessage:NSLocalizedString(@"SocialConnectErrorUnexpectedError", nil)];
|
||||
[self setIsConnected:NO];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
|
||||
@interface RBAccountManagerSocialViewController()
|
||||
@property (nonatomic, retain) RBActivityIndicatorView* loadingSpinner;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBAccountManagerSocialViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
[[RBXEventReporter sharedInstance] reportScreenLoaded:RBXAContextSettingsSocial];
|
||||
|
||||
[self initializeUIElements];
|
||||
|
||||
//Refresh the user's account info
|
||||
[_loadingSpinner startAnimating];
|
||||
[_loadingSpinner setHidden:NO];
|
||||
|
||||
[self fetchSocialInformationWithCallback:^(bool success, NSString *message)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_loadingSpinner stopAnimating];
|
||||
[_loadingSpinner setHidden:YES];
|
||||
|
||||
if (success)
|
||||
{
|
||||
[_rbsFacebook setIsConnected:[[UserInfo CurrentPlayer] isConnectedToFacebook]];
|
||||
[_rbsTwitter setIsConnected:[[UserInfo CurrentPlayer] isConnectedToTwitter]];
|
||||
[_rbsGPlus setIsConnected:[[UserInfo CurrentPlayer] isConnectedToGooglePlus]];
|
||||
|
||||
[self toggleIdentityHidden:NO];
|
||||
[_lblWarning setHidden:YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_lblWarning setHidden:NO];
|
||||
[self toggleIdentityHidden:YES];
|
||||
}
|
||||
});
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
#pragma mark Odd Functions
|
||||
-(void) fetchSocialInformationWithCallback:(void(^)(bool success, NSString* message))handler
|
||||
{
|
||||
NSString* gigyaUID = [[UserInfo CurrentPlayer] GigyaUID];
|
||||
if (gigyaUID)
|
||||
{
|
||||
//refresh all the user info and see if anything has changed from the server side
|
||||
[[LoginManager sharedInstance] doSocialFetchGigyaInfoWithUID:[[UserInfo CurrentPlayer] GigyaUID]
|
||||
isLoggingIn:NO
|
||||
withCompletion:handler];
|
||||
}
|
||||
else
|
||||
{
|
||||
//we clearly don't have ANY information at all, time to get it
|
||||
[[LoginManager sharedInstance] doSocialNotifyGigyaLoginWithContext:RBXAContextSettingsSocial withCompletion:handler];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark UI Functions
|
||||
-(void) initializeUIElements
|
||||
{
|
||||
if (![RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
[RobloxTheme applyShadowToView:_whiteView];
|
||||
|
||||
_loadingSpinner = [[RBActivityIndicatorView alloc] initWithFrame:CGRectMake(_whiteView.center.x-16, _whiteView.center.y-16, 32, 32)];
|
||||
}
|
||||
else
|
||||
{
|
||||
_loadingSpinner = [[RBActivityIndicatorView alloc] initWithFrame:CGRectMake(self.view.center.x-16, self.view.center.y-16, 32, 32)];
|
||||
}
|
||||
|
||||
[self.view addSubview:_loadingSpinner];
|
||||
[_loadingSpinner startAnimating];
|
||||
|
||||
[_lblTitle setText:NSLocalizedString(@"SocialWord", nil)];
|
||||
[_lblWarning setText:NSLocalizedString(@"SocialErrorLoadingConnectionsPhrase", nil)];
|
||||
[_lblWarning setHidden:YES];
|
||||
|
||||
//Buttons
|
||||
[_btnCancel setTitle:NSLocalizedString(@"CancelWord", nil) forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToModalCancelButton:_btnCancel];
|
||||
[_btnUpdate setTitle:NSLocalizedString(@"RefreshWord", nil) forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToModalSubmitButton:_btnUpdate];
|
||||
|
||||
//Social Identities
|
||||
//Facebook
|
||||
[_rbsFacebook initWithSocialName:NSLocalizedString(@"FacebookWord", nil)
|
||||
iconName:@"Social-FB-Icon"
|
||||
providerName:[LoginManager ProviderNameFacebook]
|
||||
andRefToController:self];
|
||||
[_rbsTwitter initWithSocialName:NSLocalizedString(@"TwitterWord", nil)
|
||||
iconName:@"Social-Twitter-Icon"
|
||||
providerName:[LoginManager ProviderNameTwitter]
|
||||
andRefToController:self];
|
||||
[_rbsGPlus initWithSocialName:NSLocalizedString(@"GooglePlusWord", nil)
|
||||
iconName:@"Social-Google-Icon"
|
||||
providerName:[LoginManager ProviderNameGooglePlus]
|
||||
andRefToController:self];
|
||||
|
||||
|
||||
//Hide all buttons
|
||||
[self toggleIdentityHidden:YES];
|
||||
}
|
||||
-(void) toggleIdentityHidden:(BOOL)hidden
|
||||
{
|
||||
if (!hidden)
|
||||
{
|
||||
//Only reveal buttons that the server says can be revealed
|
||||
[_rbsFacebook setHidden:DFFlag::EnableFacebookConnection ? NO : YES];
|
||||
[_rbsTwitter setHidden:DFFlag::EnableTwitterConnection ? NO : YES];
|
||||
[_rbsGPlus setHidden:DFFlag::EnableGooglePlusConnection ? NO : YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_rbsFacebook setHidden:YES];
|
||||
[_rbsTwitter setHidden:YES];
|
||||
[_rbsGPlus setHidden:YES];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma mark UI Actions
|
||||
- (IBAction)updateInfo:(id)sender
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonRefresh withContext:RBXAContextSettingsSocial];
|
||||
|
||||
[_loadingSpinner setHidden:NO];
|
||||
[_loadingSpinner startAnimating];
|
||||
[_loadingSpinner setY:(_btnUpdate.center.y - (_loadingSpinner.height * 0.5))];
|
||||
[_lblWarning setHidden:YES];
|
||||
|
||||
//define a callback really quick
|
||||
void (^handler)(bool,NSString*) = ^(bool success, NSString* message)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[_loadingSpinner setHidden:YES];
|
||||
[_loadingSpinner stopAnimating];
|
||||
|
||||
if (success)
|
||||
{
|
||||
[_rbsFacebook setIsConnected:[[UserInfo CurrentPlayer] isConnectedToFacebook]];
|
||||
[_rbsTwitter setIsConnected:[[UserInfo CurrentPlayer] isConnectedToTwitter]];
|
||||
[_rbsGPlus setIsConnected:[[UserInfo CurrentPlayer] isConnectedToGooglePlus]];
|
||||
|
||||
[self toggleIdentityHidden:NO];
|
||||
[_lblWarning setHidden:YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self toggleIdentityHidden:YES];
|
||||
[_lblWarning setHidden:NO];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
[self fetchSocialInformationWithCallback:handler];
|
||||
}
|
||||
- (IBAction)closeController:(id)sender
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonClose withContext:RBXAContextSettingsSocial];
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// RBAccountManagerViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 9/23/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
//-----------------------Navigation View Controller--------------------------------
|
||||
@interface RBAccountManagerViewController : UITableViewController <UIGestureRecognizerDelegate, UITableViewDelegate>
|
||||
|
||||
//individual cell labels - because Static cells don't use prototypes
|
||||
@property IBOutlet UILabel* lblCellPassword;
|
||||
@property IBOutlet UILabel* lblCellEmail;
|
||||
@property IBOutlet UILabel* lblCellUpgrade;
|
||||
@property IBOutlet UILabel* lblSocial;
|
||||
@property IBOutlet UILabel* lblLogOut;
|
||||
|
||||
|
||||
//Warnings
|
||||
@property IBOutlet UIImageView* imgWarningEmail;
|
||||
@property IBOutlet UIImageView* imgWarningPassword;
|
||||
@property IBOutlet UILabel* lblWarningEmail;
|
||||
@property IBOutlet UILabel* lblWarningPassword;
|
||||
|
||||
@property UITapGestureRecognizer* gestureRecognizer;
|
||||
- (void)didTapOutside:(UIGestureRecognizer*)sender;
|
||||
- (IBAction)dismissView:(id)sender;
|
||||
@end
|
||||
@@ -0,0 +1,189 @@
|
||||
//
|
||||
// RBAccountManagerViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Christian Hresko on 6/17/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
#import "RBAccountManagerViewController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "UserInfo.h"
|
||||
#import "LoginManager.h"
|
||||
#import "UIView+Position.h"
|
||||
|
||||
#ifndef kCFCoreFoundationVersionNumber_iOS_8_0
|
||||
#define kCFCoreFoundationVersionNumber_iOS_8_0 1129.15
|
||||
#endif
|
||||
#define DEFAULT_VIEW_SIZE CGRectMake(0, 0, 540, 344)
|
||||
|
||||
@interface RBAccountManagerViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBAccountManagerViewController
|
||||
|
||||
//View Functions
|
||||
- (void)viewDidLoad{
|
||||
[super viewDidLoad];
|
||||
|
||||
[self.tableView setDelegate:self];
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
//assign some localized strings
|
||||
self.navigationItem.title = NSLocalizedString(@"AccountSettingsWord", nil);
|
||||
|
||||
//Cells
|
||||
[_lblSocial setText:NSLocalizedString(@"SocialWord", nil)];
|
||||
[_lblCellEmail setText:NSLocalizedString(@"EmailWord", nil)];
|
||||
[_lblWarningEmail setText:NSLocalizedString(@"MissingEmailWord", nil)];
|
||||
[_lblLogOut setText:NSLocalizedString(@"LogoutWord", nil)];
|
||||
|
||||
//Warnings
|
||||
[_lblCellPassword setText:NSLocalizedString(@"PasswordWord", nil)];
|
||||
[_lblWarningPassword setText:NSLocalizedString(@"MissingPasswordWord", nil)];
|
||||
|
||||
|
||||
// Stylize elements
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
UIButton* close = [RobloxTheme applyCloseButtonToUINavigationItem:self.navigationItem];
|
||||
[close addTarget:self action:@selector(dismissView:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
[RobloxTheme applyToModalPopupNavBar:self.navigationController.navigationBar];
|
||||
|
||||
_gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapOutside:)];
|
||||
[_gestureRecognizer setNumberOfTapsRequired:1];
|
||||
[_gestureRecognizer setCancelsTouchesInView:NO];
|
||||
[_gestureRecognizer setDelegate:self];
|
||||
}
|
||||
|
||||
|
||||
|
||||
// This will remove extra separators from tableview
|
||||
self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
|
||||
}
|
||||
- (void)viewWillAppear:(BOOL)animated{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
bool showEmailAlert = NO;
|
||||
bool showPasswordAlert = NO;
|
||||
|
||||
if ([UserInfo CurrentPlayer].accountNotifications)
|
||||
{
|
||||
showEmailAlert = [UserInfo CurrentPlayer].accountNotifications.emailNotificationEnabled && [UserInfo CurrentPlayer].userEmail == nil;
|
||||
showPasswordAlert = [UserInfo CurrentPlayer].accountNotifications.passwordNotificationEnabled && [UserInfo CurrentPlayer].password == nil;
|
||||
}
|
||||
|
||||
_imgWarningEmail.hidden = !showEmailAlert;
|
||||
_lblWarningEmail.hidden = !showEmailAlert;
|
||||
_imgWarningPassword.hidden = !showPasswordAlert;
|
||||
_lblWarningPassword.hidden = !showPasswordAlert;
|
||||
}
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
[self.view.window addGestureRecognizer:_gestureRecognizer];
|
||||
}
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
[self.view.window removeGestureRecognizer:_gestureRecognizer];
|
||||
}
|
||||
- (void)viewWillLayoutSubviews{
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
BOOL isPreiOS8 = NSFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_8_0;
|
||||
if (isPreiOS8 && [RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
self.navigationController.view.superview.bounds = DEFAULT_VIEW_SIZE;
|
||||
}
|
||||
|
||||
|
||||
[self adjustLabelInCell:_lblSocial];
|
||||
[self adjustLabelInCell:_lblCellEmail];
|
||||
[self adjustLabelInCell:_lblCellPassword];
|
||||
[self adjustLabelInCell:_lblLogOut];
|
||||
|
||||
[self adjustWarningInCell:_imgWarningEmail withLabel:_lblWarningEmail];
|
||||
[self adjustWarningInCell:_imgWarningPassword withLabel:_lblWarningPassword];
|
||||
}
|
||||
|
||||
|
||||
//Helper Functions
|
||||
- (void) adjustLabelInCell:(UILabel*)aLabel {
|
||||
if (!aLabel)
|
||||
return;
|
||||
|
||||
//move the cell down to the baseline of its parent view
|
||||
[aLabel setX:aLabel.superview.width * 0.1];
|
||||
[aLabel setWidth:aLabel.superview.width * 0.4];
|
||||
[aLabel setY:(aLabel.superview.superview.height * 0.5) - (aLabel.height * 0.5)];
|
||||
|
||||
//style the label
|
||||
[aLabel setTextColor:[RobloxTheme colorGray2]];
|
||||
}
|
||||
- (void) adjustWarningInCell:(UIImageView*)anImage withLabel:(UILabel*)aLabel {
|
||||
if (aLabel)
|
||||
{
|
||||
[aLabel setSize:CGSizeMake(90, aLabel.superview.height)];
|
||||
[aLabel setRight:aLabel.superview.width];
|
||||
[aLabel setY:0];
|
||||
[aLabel setTextColor:[RobloxTheme colorRed3]];
|
||||
}
|
||||
if (anImage)
|
||||
{
|
||||
//move the cell down to the baseline of its parent view
|
||||
[anImage setSize:CGSizeMake(22, 22)];
|
||||
[anImage setY:(anImage.superview.superview.height * 0.5) - (anImage.height * 0.5)];
|
||||
[anImage setRight:aLabel.x - 10];
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
- (void)dismissView:(id)sender { [self dismissViewControllerAnimated:YES completion:nil]; }
|
||||
|
||||
//Tap Recognition
|
||||
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer { return YES; }
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch { return YES; }
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { return YES; }
|
||||
- (void) didTapOutside:(UIGestureRecognizer*)sender
|
||||
{
|
||||
//check the position of the tap and dismiss the view if it lies outside the bounds of the view
|
||||
if (sender.state == UIGestureRecognizerStateEnded)
|
||||
{
|
||||
UIView* root = self.view.window.rootViewController.view;
|
||||
CGPoint location = [sender locationInView:root];
|
||||
location = [self.view convertPoint:location fromView:root];
|
||||
if (![self.view pointInside:location withEvent:nil])
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Delegate Functions
|
||||
- (CGFloat)tableView:(nonnull UITableView *)tableView heightForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
|
||||
if (![[LoginManager sharedInstance] isFacebookEnabled])
|
||||
if (indexPath.row == 0 && indexPath.section == 0)
|
||||
return 0.0;
|
||||
|
||||
return 60.0;
|
||||
}
|
||||
-(void) tableView:(nonnull UITableView *)tableView willDisplayCell:(nonnull UITableViewCell *)cell forRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
|
||||
if (![[LoginManager sharedInstance] isFacebookEnabled])
|
||||
if (indexPath.row == 0 && indexPath.section == 0)
|
||||
{
|
||||
[cell setHidden:YES];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,51 @@
|
||||
//
|
||||
// RBBaseViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Christian Hresko on 6/23/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RobloxData.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RBXEventReporter.h"
|
||||
|
||||
typedef NS_ENUM(NSInteger, RBXWebRequestReturnStatus)
|
||||
{
|
||||
RBXWebRequestReturnScreenPush = 0,
|
||||
RBXWebRequestReturnWebRequest = 1,
|
||||
RBXWebRequestReturnUnknown = 2,
|
||||
RBXWebRequestReturnFilter = 3
|
||||
};
|
||||
|
||||
@interface RBBaseViewController : UIViewController
|
||||
|
||||
- (void) setViewTheme:(RBXTheme)theme;
|
||||
|
||||
// Flurry Events for Subclasses
|
||||
- (void) setFlurryEventsForExternalLinkEvent:(NSString*)webExternalLinkEvent
|
||||
andWebViewEvent:(NSString*)webLocalLinkEvent
|
||||
andOpenProfileEvent:(NSString*)webProfileLinkEvent
|
||||
andOpenGameDetailEvent:(NSString*)webGameDetailLinkEvent;
|
||||
|
||||
// Navigation Bar Style Functions
|
||||
- (void) removeRobuxAndBCIcons;
|
||||
- (void) addRobuxIconWithFlurryEvent:(NSString*)RobuxEvent
|
||||
andBCIconWithFlurryEvent:(NSString*)BCEvent;
|
||||
- (void) addSearchIconWithSearchType:(SearchResultType)searchType
|
||||
andFlurryEvent:(NSString*)searchEvent;
|
||||
- (void) didPressEditAccount;
|
||||
|
||||
// Push ROMA views by parsing URLs in web URLs
|
||||
-(RBXWebRequestReturnStatus) handleWebRequest:(NSURL*)aRequest;
|
||||
-(void) handleWebRequestWithPopout:(NSURL*)aRequest;
|
||||
-(void) pushWebControllerwithURL:(NSString*)stringURL andTheme:(RBXTheme)theme;
|
||||
-(void) pushWebControllerwithURL:(NSString*)stringURL andTitle:(NSString*)pageTitle andTheme:(RBXTheme)theme;
|
||||
-(void) pushProfileControllerWithUserID:(NSNumber*)userID;
|
||||
-(void) pushProfileControllerWithURL:(NSString*)stringURL;
|
||||
-(void) pushGameDetailWithURL:(NSString*)stringURL;
|
||||
-(void) pushGameDetailWithGameData:(RBXGameData*)game;
|
||||
-(void) pushSearchResultsType:(SearchResultType)resultType
|
||||
withKeyword:(NSString*)searchKeywords;
|
||||
@end
|
||||
@@ -0,0 +1,533 @@
|
||||
//
|
||||
// RBBaseViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Christian Hresko on 6/23/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBBaseViewController.h"
|
||||
#import "RBBarButtonMenu.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "UserInfo.h"
|
||||
#import "RBPurchaseViewController.h"
|
||||
#import "RBAccountManagerViewController.h"
|
||||
#import "UIAlertView+Blocks.h"
|
||||
#import "LoginManager.h"
|
||||
#import "Flurry.h"
|
||||
#import "RBMobileWebViewController.h"
|
||||
#import "RBGameViewController.h"
|
||||
#import "RBProfileViewController.h"
|
||||
#import "RBModalPopUpViewController.h"
|
||||
#import "RBWebGamePreviewScreenController.h"
|
||||
#import "RBWebProfileViewController.h"
|
||||
#import "SearchResultCollectionViewController.h"
|
||||
#import "NativeSearchNavItem.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "UIView+Position.h"
|
||||
|
||||
|
||||
//---METRICS---
|
||||
@interface RBBaseViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBBaseViewController
|
||||
|
||||
- (void) setViewTheme:(RBXTheme)theme
|
||||
{
|
||||
viewTheme = theme;
|
||||
[RobloxTheme applyTheme:viewTheme toViewController:self quickly:YES];
|
||||
}
|
||||
|
||||
- (void) setFlurryEventsForExternalLinkEvent:(NSString*)webExternalLinkEvent
|
||||
andWebViewEvent:(NSString*)webLocalLinkEvent
|
||||
andOpenProfileEvent:(NSString*)webProfileLinkEvent
|
||||
andOpenGameDetailEvent:(NSString*)webGameDetailLinkEvent
|
||||
{
|
||||
_flurryOpenExternalLinkEvent = webExternalLinkEvent;
|
||||
_flurryOpenWebViewEvent = webLocalLinkEvent;
|
||||
_flurryOpenProfileEvent = webProfileLinkEvent;
|
||||
_flurryOpenGameDetailEvent = webGameDetailLinkEvent;
|
||||
}
|
||||
|
||||
- (void) viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
if (viewTheme)
|
||||
[RobloxTheme applyTheme:viewTheme toViewController:self quickly:YES];
|
||||
|
||||
if (addSearchListeners)
|
||||
{
|
||||
//Games
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(displaySelectedGame:) name:RBXNotificationGameSelected object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(displayGameSearchResults:) name:RBXNotificationSearchGames object:nil];
|
||||
|
||||
//Users
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(displayUserSearchResults:) name:RBXNotificationSearchUsers object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(displaySelectedUser:) name:RBXNotificationUserSelected object:nil];
|
||||
}
|
||||
}
|
||||
- (void) viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
if (viewTheme)
|
||||
[RobloxTheme applyTheme:viewTheme toViewController:self quickly:NO];
|
||||
}
|
||||
- (void) viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
if (addSearchListeners)
|
||||
{
|
||||
//Games
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBXNotificationGameSelected object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBXNotificationSearchGames object:nil];
|
||||
|
||||
//Users
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBXNotificationSearchUsers object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBXNotificationUserSelected object:nil];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Navigation buttons
|
||||
- (void) addRobuxIconWithFlurryEvent:(NSString*)RobuxEvent
|
||||
andBCIconWithFlurryEvent:(NSString*)BCEvent
|
||||
{
|
||||
//define some events to be fired
|
||||
_flurryEventRobux = RobuxEvent;
|
||||
_flurryEventBuildersClub = BCEvent;
|
||||
|
||||
UIImage* rbxImage = [UIImage imageNamed:@"Icon Robux Off"];
|
||||
UIImage* rbxImageDown = [UIImage imageNamed:@"Icon Robux On"];
|
||||
UIImage* bcImage = [UIImage imageNamed:@"Icon BC Off"];
|
||||
UIImage* bcImageDown = [UIImage imageNamed:@"Icon BC On"];
|
||||
|
||||
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
//create the buttons
|
||||
UIBarButtonItem* buyRBXButtonItem = [[UIBarButtonItem alloc] initWithImage:rbxImage
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(didPressBuyRobux)];
|
||||
[buyRBXButtonItem setBackgroundImage:rbxImageDown forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
|
||||
|
||||
UIBarButtonItem* buyBCButtonItem = [[UIBarButtonItem alloc] initWithImage:bcImage
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(didPressBuildersClub)];
|
||||
[buyBCButtonItem setBackgroundImage:bcImageDown forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
|
||||
|
||||
//insert the buttons at the head of the list of existing buttons
|
||||
NSMutableArray* buttons = [NSMutableArray arrayWithArray:@[buyBCButtonItem, buyRBXButtonItem]];
|
||||
[buttons addObjectsFromArray:self.navigationItem.rightBarButtonItems];
|
||||
[self.navigationItem setRightBarButtonItems:buttons];
|
||||
}
|
||||
else
|
||||
{
|
||||
//If we're on phone, we don't have as much space
|
||||
CGRect buttonFrame = CGRectMake(0, 0, 28, 28);
|
||||
CGRect layoutFrame = CGRectMake(0, 0, 64, 28);
|
||||
|
||||
//so make a custom view to space the icons closer
|
||||
UIButton* btnRBX = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[btnRBX setImage:rbxImage forState:UIControlStateNormal];
|
||||
[btnRBX setImage:rbxImageDown forState:UIControlStateSelected];
|
||||
[btnRBX addTarget:self action:@selector(didPressBuyRobux) forControlEvents:UIControlEventTouchUpInside];
|
||||
[btnRBX setFrame:buttonFrame];
|
||||
|
||||
UIButton* btnBC = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[btnBC setImage:bcImage forState:UIControlStateNormal];
|
||||
[btnBC setImage:bcImageDown forState:UIControlStateSelected];
|
||||
[btnBC addTarget:self action:@selector(didPressBuildersClub) forControlEvents:UIControlEventTouchUpInside];
|
||||
[btnBC setFrame:buttonFrame];
|
||||
[btnBC setX:layoutFrame.size.width - btnBC.width];
|
||||
|
||||
UIView* customLayout = [[UIView alloc] initWithFrame:layoutFrame];
|
||||
[customLayout addSubview:btnBC];
|
||||
[customLayout addSubview:btnRBX];
|
||||
|
||||
UIBarButtonItem* customBtn = [[UIBarButtonItem alloc] initWithCustomView:customLayout];
|
||||
|
||||
NSMutableArray* buttons = [NSMutableArray arrayWithArray:@[customBtn]];
|
||||
[buttons addObjectsFromArray:self.navigationItem.rightBarButtonItems];
|
||||
[self.navigationItem setRightBarButtonItems:buttons];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
- (void) addSearchIconWithSearchType:(SearchResultType)searchType
|
||||
andFlurryEvent:(NSString*)searchEvent
|
||||
{
|
||||
addSearchListeners = YES;
|
||||
bool compact = ![RobloxInfo thisDeviceIsATablet];
|
||||
NativeSearchNavItem* searchItem = [[NativeSearchNavItem alloc] initWithSearchType:searchType andContainer:self compactMode:compact];
|
||||
NSArray* rightNavItems = self.navigationItem.rightBarButtonItems;
|
||||
NSMutableArray* items = [NSMutableArray arrayWithArray:rightNavItems];
|
||||
[items addObject:searchItem];
|
||||
[self.navigationItem setRightBarButtonItems:items];
|
||||
}
|
||||
- (void) addLogoutButton
|
||||
{
|
||||
UIBarButtonItem* logoutButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"Logout Button"]
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(didPressLogOut)];
|
||||
|
||||
//insert the buttons at the head of the list of existing buttons
|
||||
NSMutableArray* buttons = [NSMutableArray arrayWithObject:logoutButton];
|
||||
[buttons addObjectsFromArray:self.navigationItem.rightBarButtonItems];
|
||||
[self.navigationItem setRightBarButtonItems:buttons];
|
||||
}
|
||||
|
||||
//Navigation Button Events
|
||||
- (void)didPressEditAccount
|
||||
{
|
||||
#define ACCOUNT_SETTINGS_POPUP_SIZE CGRectMake(0, 0, 540, 344)
|
||||
if (_flurryEventEditAccount)
|
||||
[Flurry logEvent:_flurryEventEditAccount];
|
||||
|
||||
NSString* storyboardName = [RobloxInfo getStoryboardName];
|
||||
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
|
||||
RBAccountManagerViewController* controller = [storyboard instantiateViewControllerWithIdentifier:@"RBAccountManagerController"];
|
||||
UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:controller];
|
||||
navigation.modalPresentationStyle = UIModalPresentationFormSheet;
|
||||
[self.navigationController presentViewController:navigation animated:YES completion:nil];
|
||||
navigation.view.superview.bounds = ACCOUNT_SETTINGS_POPUP_SIZE;
|
||||
}
|
||||
- (void)didPressLogOut
|
||||
{
|
||||
if (_flurryEventLogOut)
|
||||
[Flurry logEvent:_flurryEventLogOut];
|
||||
UIAlertView* view = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Log Out", nil)
|
||||
message:NSLocalizedString(@"Are you sure you want to log out?", nil)
|
||||
delegate:nil
|
||||
cancelButtonTitle:NSLocalizedString(@"Cancel", nil)
|
||||
otherButtonTitles:NSLocalizedString(@"Log Out", nil) , nil];
|
||||
|
||||
RBBaseViewController* strongSelf = self;
|
||||
[view showWithCompletion:^(UIAlertView *alertView, NSInteger buttonIndex)
|
||||
{
|
||||
switch (buttonIndex)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
[[LoginManager sharedInstance] doLogout];
|
||||
|
||||
[strongSelf.tabBarController.navigationController popViewControllerAnimated:YES];
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}];
|
||||
}
|
||||
- (void) didPressBuyRobux
|
||||
{
|
||||
CGRect PURCHASE_ROBUX_POPUP_SIZE = [RobloxInfo thisDeviceIsATablet] ? CGRectMake(0, 0, 540, 344) : CGRectMake(0, 0, 300, 344);
|
||||
if (_flurryEventRobux)
|
||||
[Flurry logEvent:_flurryEventRobux];
|
||||
|
||||
NSString* baseURL = [RobloxInfo getBaseUrl];
|
||||
NSString* url = [baseURL stringByAppendingString:@"mobile-app-upgrades/native-ios/robux"];
|
||||
if (![RobloxInfo thisDeviceIsATablet])
|
||||
url = [url stringByReplacingOccurrencesOfString:@"m." withString:@"www."];
|
||||
|
||||
NSString* titleString = [NSString stringWithFormat:@"%@ : R$%@", NSLocalizedString(@"CurrentRobuxBalanceWord", nil), [UserInfo CurrentPlayer].Robux];
|
||||
RBPurchaseViewController* controller = [[RBPurchaseViewController alloc] initWithURL:[NSURL URLWithString:url] andTitle:titleString];
|
||||
UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:controller];
|
||||
navigation.modalPresentationStyle = UIModalPresentationFormSheet;
|
||||
[self.navigationController presentViewController:navigation animated:YES completion:nil];
|
||||
navigation.view.superview.bounds = PURCHASE_ROBUX_POPUP_SIZE;
|
||||
}
|
||||
- (void) didPressBuildersClub
|
||||
{
|
||||
if (_flurryEventBuildersClub)
|
||||
[Flurry logEvent:_flurryEventBuildersClub];
|
||||
|
||||
NSString* baseUrl = [RobloxInfo getBaseUrl];
|
||||
NSString* url = [baseUrl stringByAppendingString:@"mobile-app-upgrades/native-ios/bc"];
|
||||
if (![RobloxInfo thisDeviceIsATablet])
|
||||
url = [url stringByReplacingOccurrencesOfString:@"m." withString:@"www."];
|
||||
|
||||
//NSString* BCLevelKey = @"NBCWord";
|
||||
//[UserInfo CurrentPlayer].bcMember
|
||||
NSString* titleString = NSLocalizedString(@"Builders Club", nil); //[NSString stringWithFormat:@"%@ : %@", NSLocalizedString(@"CurrentBuildersClubWord", nil), NSLocalizedString(BCLevelKey, nil)];
|
||||
RBPurchaseViewController* controller = [[RBPurchaseViewController alloc] initWithURL:[NSURL URLWithString:url] andTitle:titleString];
|
||||
UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:controller];
|
||||
navigation.modalPresentationStyle = UIModalPresentationFormSheet;
|
||||
[self.navigationController presentViewController:navigation animated:YES completion:nil];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//pushing view controllers
|
||||
-(RBXWebRequestReturnStatus) handleWebRequest:(NSURL*)aRequest
|
||||
{
|
||||
//typically called from a webview shouldLoadWithRequest delegate function
|
||||
//returns RBXWebRequestReturnWebRequest when the webview can safely load the requested URL
|
||||
//returns RBXWebRequestReturnScreenPush when the request has opened another view
|
||||
//returns RBXWebRequestReturnUnknown when the domain is unknown and should be handled by pushing out to the browser
|
||||
|
||||
NSString* lcaseHost = [[aRequest host] lowercaseString];
|
||||
NSString* aRequestString = [[NSString stringWithFormat:@"%@", aRequest] stringByRemovingPercentEncoding];
|
||||
aRequestString = [aRequestString lowercaseString];
|
||||
|
||||
//check the domain of the URL to see if it is a ROBLOX URL
|
||||
bool isRobloxURL = [lcaseHost rangeOfString:@"roblox"].location != NSNotFound;
|
||||
|
||||
//also check that the url isn't a Roblox User's URL from a message
|
||||
bool isUserURL = [aRequestString rangeOfString:@"user.aspx?id="].location != NSNotFound;
|
||||
|
||||
if (isRobloxURL || isUserURL)
|
||||
{
|
||||
if (isUserURL)
|
||||
{
|
||||
[self pushProfileControllerWithURL:aRequestString];
|
||||
return RBXWebRequestReturnScreenPush;
|
||||
}
|
||||
else if ([aRequestString rangeOfString:@"/users/"].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeCreative];
|
||||
}
|
||||
else if ([aRequestString rangeOfString:@"-place?id="].location != NSNotFound)
|
||||
{
|
||||
[self pushGameDetailWithURL:aRequestString];
|
||||
return RBXWebRequestReturnScreenPush;
|
||||
}
|
||||
else if ([aRequestString rangeOfString:@"-item?id="].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
}
|
||||
else if ([aRequestString rangeOfString:@"/catalog/"].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
}
|
||||
else if ([aRequestString rangeOfString:@"groups.aspx"].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeSocial];
|
||||
}
|
||||
else if ([aRequestString rangeOfString:@"stuff.aspx"].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
}
|
||||
else if ([aRequestString rangeOfString:@"inventory"].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
}
|
||||
else if ([aRequestString rangeOfString:@"/forum/"].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeSocial];
|
||||
}
|
||||
return RBXWebRequestReturnWebRequest;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Not exactly sure what we found... just open it in an external web view.
|
||||
//[[UIApplication sharedApplication] openURL:aRequest];
|
||||
return RBXWebRequestReturnUnknown;
|
||||
}
|
||||
|
||||
return RBXWebRequestReturnWebRequest;
|
||||
}
|
||||
|
||||
-(void) handleWebRequestWithPopout:(NSURL*)aRequest
|
||||
{
|
||||
NSString* lcaseHost = [[aRequest host] lowercaseString];
|
||||
NSString* aRequestString = [[NSString stringWithFormat:@"%@", aRequest] stringByRemovingPercentEncoding];
|
||||
aRequestString = [aRequestString lowercaseString];
|
||||
|
||||
bool isRobloxURL = [lcaseHost rangeOfString:@"roblox"].location != NSNotFound;
|
||||
bool isUserURL = [aRequestString rangeOfString:@"user.aspx?id="].location != NSNotFound;
|
||||
|
||||
if (isRobloxURL || isUserURL)
|
||||
{
|
||||
if (isUserURL)
|
||||
{
|
||||
[self pushProfileControllerWithURL:aRequestString];
|
||||
}
|
||||
else if ([aRequestString rangeOfString:@"-place?id="].location != NSNotFound)
|
||||
{
|
||||
[self pushGameDetailWithURL:aRequestString];
|
||||
}
|
||||
else if ([aRequestString rangeOfString:@"-item?id="].location != NSNotFound)
|
||||
{
|
||||
[self pushWebControllerwithURL:aRequestString andTheme:RBXThemeGame];
|
||||
}
|
||||
else if ([aRequestString rangeOfString:@"groups.aspx"].location != NSNotFound)
|
||||
{
|
||||
[self pushWebControllerwithURL:aRequestString andTheme:RBXThemeSocial];
|
||||
}
|
||||
else if ([aRequestString rangeOfString:@"stuff.aspx"].location != NSNotFound)
|
||||
{
|
||||
[self pushWebControllerwithURL:aRequestString andTheme:RBXThemeGame];
|
||||
}
|
||||
else
|
||||
{
|
||||
//Not exactly sure what we found... just open it in an external web view.
|
||||
if (_flurryOpenExternalLinkEvent)
|
||||
[Flurry logEvent:_flurryOpenExternalLinkEvent];
|
||||
[[UIApplication sharedApplication] openURL:aRequest];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_flurryOpenExternalLinkEvent)
|
||||
[Flurry logEvent:_flurryOpenExternalLinkEvent];
|
||||
[[UIApplication sharedApplication] openURL:aRequest];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) pushWebControllerwithURL:(NSString*)stringURL andTheme:(RBXTheme)theme
|
||||
{
|
||||
[self pushWebControllerwithURL:stringURL andTitle:nil andTheme:theme];
|
||||
}
|
||||
-(void) pushWebControllerwithURL:(NSString*)stringURL andTitle:(NSString*)pageTitle andTheme:(RBXTheme)theme
|
||||
{
|
||||
if (_flurryOpenWebViewEvent)
|
||||
[Flurry logEvent:_flurryOpenWebViewEvent];
|
||||
|
||||
RBMobileWebViewController* aWebScreen = [[RBMobileWebViewController alloc] initWithNavButtons:NO];
|
||||
[aWebScreen.view setFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height)];
|
||||
[aWebScreen setUrl:stringURL];
|
||||
[aWebScreen setViewTheme:theme];
|
||||
|
||||
if (pageTitle)
|
||||
[aWebScreen setTitle:pageTitle];
|
||||
|
||||
[self.navigationController pushViewController:aWebScreen animated:YES];
|
||||
}
|
||||
|
||||
//Profile
|
||||
-(void) pushProfileControllerWithUserID:(NSNumber*)userID
|
||||
{
|
||||
if (_flurryOpenProfileEvent)
|
||||
[Flurry logEvent:_flurryOpenProfileEvent];
|
||||
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
NSString* storyboardName = [RobloxInfo getStoryboardName];
|
||||
UIStoryboard* sb = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
|
||||
RBProfileViewController* builderProfile = (RBProfileViewController*) [sb instantiateViewControllerWithIdentifier:@"RBOthersProfileViewController"];
|
||||
builderProfile.userId = userID;
|
||||
[self.navigationController pushViewController:builderProfile animated:YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
RBWebProfileViewController* builderProfile = [[RBWebProfileViewController alloc] initWithNavButtons:YES];
|
||||
builderProfile.userId = userID;
|
||||
[builderProfile setViewTheme:RBXThemeCreative];
|
||||
[self.navigationController pushViewController:builderProfile animated:YES];
|
||||
}
|
||||
}
|
||||
-(void) pushProfileControllerWithURL:(NSString*)stringURL
|
||||
{
|
||||
//NOTE - flurry events are handled in other functions
|
||||
NSArray* stringParts = [stringURL componentsSeparatedByString:@"user.aspx?id="];
|
||||
|
||||
if ([stringParts count] <= 1)
|
||||
{
|
||||
//failed to parse the url, fallback and use a mobile web view
|
||||
[self pushWebControllerwithURL:stringURL andTheme:RBXThemeCreative];
|
||||
return;
|
||||
}
|
||||
NSNumber* userID = [NSNumber numberWithInt:[stringParts[1] integerValue]];
|
||||
[self pushProfileControllerWithUserID:userID];
|
||||
}
|
||||
|
||||
//Games
|
||||
-(void) pushGameDetailWithURL:(NSString*)stringURL
|
||||
{
|
||||
//load up a game
|
||||
NSArray* stringParts = [stringURL componentsSeparatedByString:@"?id="];
|
||||
if ([stringParts count] <= 1)
|
||||
{
|
||||
//failed to parse the url, fallback and use a mobile web view
|
||||
[self pushWebControllerwithURL:stringURL andTheme:RBXThemeGame];
|
||||
}
|
||||
else
|
||||
{
|
||||
//grab the game details
|
||||
[RobloxData fetchGameDetails:stringParts[1] completion:^(RBXGameData *game)
|
||||
{
|
||||
[self pushGameDetailWithGameData:game];
|
||||
}];
|
||||
|
||||
}
|
||||
}
|
||||
-(void) pushGameDetailWithGameData:(RBXGameData*)game
|
||||
{
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
if (_flurryOpenGameDetailEvent)
|
||||
[Flurry logEvent:_flurryOpenGameDetailEvent];
|
||||
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
RBWebGamePreviewScreenController* aGameScreen = [[RBWebGamePreviewScreenController alloc] init];
|
||||
aGameScreen.gameData = game;
|
||||
[self.navigationController pushViewController:aGameScreen animated:YES];
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
[self pushGameWithID:game.placeID.intValue isUser:NO];
|
||||
}
|
||||
}
|
||||
-(void) pushGameWithID:(int)placeID isUser:(BOOL)isUser
|
||||
{
|
||||
RBGameViewController* controller = [[RBGameViewController alloc] init];
|
||||
controller.placeID = placeID;
|
||||
controller.isUser = isUser;
|
||||
[self presentViewController:controller animated:NO completion:nil];
|
||||
}
|
||||
|
||||
//Search
|
||||
-(void) pushSearchResultsType:(SearchResultType)resultType
|
||||
withKeyword:(NSString*)searchKeywords
|
||||
{
|
||||
SearchResultCollectionViewController* aCollectionViewController = [[SearchResultCollectionViewController alloc] initWithKeyword:searchKeywords andSearchType:resultType];
|
||||
[self.navigationController pushViewController:aCollectionViewController animated:YES];
|
||||
}
|
||||
|
||||
|
||||
//search result functions
|
||||
- (void) displaySelectedGame:(NSNotification*)notification
|
||||
{
|
||||
RBXGameData* notificationData = (RBXGameData*)[notification.userInfo objectForKey:@"gameData"];
|
||||
[self pushGameDetailWithGameData:notificationData];
|
||||
}
|
||||
- (void) displayGameSearchResults:(NSNotification*) notification
|
||||
{
|
||||
//[Flurry logEvent:GS_searchGames];
|
||||
NSString* keywords = [notification.userInfo objectForKey:@"keywords"];
|
||||
//[self performSegueWithIdentifier:@"searchResults" sender:keywords]; //NOTE- THIS WAY IS DEPRECATED BUT IT OPENS A SPECIFIC SEARCH RESULT SCREEN
|
||||
//GameSearchResultsScreenController* controller = segue.destinationViewController;
|
||||
//controller.keywords = (NSString*) sender;
|
||||
|
||||
//This is how it should be done - later
|
||||
[self pushSearchResultsType:SearchResultGames withKeyword:keywords];
|
||||
}
|
||||
- (void) displaySelectedUser:(NSNotification*) notification
|
||||
{
|
||||
RBXUserSearchInfo* user = (RBXUserSearchInfo*)[notification.userInfo objectForKey:@"userData"];
|
||||
[self pushProfileControllerWithUserID:user.userId];
|
||||
}
|
||||
- (void) displayUserSearchResults:(NSNotification*) notification
|
||||
{
|
||||
//NSLog(@"Displaying User Search Results with notification : %@", notification);
|
||||
NSString* searchKeywords = (NSString*)[notification.userInfo objectForKey:@"keywords"];
|
||||
[self pushSearchResultsType:SearchResultUsers withKeyword:searchKeywords];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,784 @@
|
||||
//
|
||||
// RBBaseViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Christian Hresko on 6/23/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBBaseViewController.h"
|
||||
#import "RBBarButtonMenu.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "UserInfo.h"
|
||||
#import "RBPurchaseViewController.h"
|
||||
#import "RBAccountManagerViewController.h"
|
||||
#import "UIAlertView+Blocks.h"
|
||||
#import "LoginManager.h"
|
||||
#import "Flurry.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "iOSSettingsService.h"
|
||||
#import "RobloxWebUtility.h"
|
||||
#import "NonRotatableNavigationController.h"
|
||||
#import "LoginManager.h"
|
||||
#import "RobloxAlert.h"
|
||||
|
||||
#import "RBMobileWebViewController.h"
|
||||
#import "RBGameViewController.h"
|
||||
#import "RBProfileViewController.h"
|
||||
#import "RBModalPopUpViewController.h"
|
||||
#import "RBWebGamePreviewScreenController.h"
|
||||
#import "RBWebProfileViewController.h"
|
||||
#import "SearchResultCollectionViewController.h"
|
||||
#import "NativeSearchNavItem.h"
|
||||
#import "FeaturedGamesScreenController.h"
|
||||
#import "GameSortResultsScreenController.h"
|
||||
#import "NativeSearchNavItem.h"
|
||||
#import "RBTabBarController.h"
|
||||
|
||||
#import "RBXFunctions.h"
|
||||
|
||||
DYNAMIC_FASTFLAGVARIABLE(EnableNativeGamesPage, false);
|
||||
DYNAMIC_FASTFLAGVARIABLE(EnableNativeProfilePage, false);
|
||||
DYNAMIC_FASTFLAGVARIABLE(EnableNativeSearchGameResults, false);
|
||||
DYNAMIC_FASTFLAGVARIABLE(EnableNativeSearchPeopleResults, false);
|
||||
|
||||
//---METRICS---
|
||||
|
||||
@interface RBBaseViewController ()
|
||||
|
||||
@property (nonatomic, strong) NSString *flurryEventRobux;
|
||||
@property (nonatomic, strong) NSString *flurryEventBuildersClub;
|
||||
@property (nonatomic, strong) NSString *flurryEventEditAccount;
|
||||
@property (nonatomic, strong) NSString *flurryEventLogOut;
|
||||
@property (nonatomic, strong) NSString *flurryEventSearch;
|
||||
@property (nonatomic, strong) NSString *flurryOpenExternalLinkEvent;
|
||||
@property (nonatomic, strong) NSString *flurryOpenProfileEvent;
|
||||
@property (nonatomic, strong) NSString *flurryOpenWebViewEvent;
|
||||
@property (nonatomic, strong) NSString *flurryOpenGameDetailEvent;
|
||||
@property (nonatomic, strong) NSString *flurryOpenSearchCatalog;
|
||||
@property (nonatomic, strong) NSString *flurryOpenSearchGames;
|
||||
@property (nonatomic, strong) NSString *flurryOpenSearchGroups;
|
||||
@property (nonatomic, strong) NSString *flurryOpenSearch;
|
||||
|
||||
@property (nonatomic, strong) UIBarButtonItem *buyRBXButtonItem;
|
||||
@property (nonatomic, strong) UIBarButtonItem *buyBCButtonItem;
|
||||
|
||||
@property (nonatomic) BOOL addSearchListeners;
|
||||
|
||||
@property (nonatomic) RBXTheme viewTheme;
|
||||
@property (nonatomic) RBXAnalyticsCustomData screenEnumName;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBBaseViewController
|
||||
|
||||
- (void) setViewTheme:(RBXTheme)theme
|
||||
{
|
||||
_viewTheme = theme;
|
||||
[RobloxTheme applyTheme:_viewTheme toViewController:self quickly:YES];
|
||||
}
|
||||
|
||||
- (void) setFlurryEventsForExternalLinkEvent:(NSString*)webExternalLinkEvent
|
||||
andWebViewEvent:(NSString*)webLocalLinkEvent
|
||||
andOpenProfileEvent:(NSString*)webProfileLinkEvent
|
||||
andOpenGameDetailEvent:(NSString*)webGameDetailLinkEvent
|
||||
{
|
||||
self.flurryOpenExternalLinkEvent = webExternalLinkEvent;
|
||||
self.flurryOpenWebViewEvent = webLocalLinkEvent;
|
||||
self.flurryOpenProfileEvent = webProfileLinkEvent;
|
||||
self.flurryOpenGameDetailEvent = webGameDetailLinkEvent;
|
||||
}
|
||||
|
||||
- (void) viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
if (self.viewTheme)
|
||||
[RobloxTheme applyTheme:self.viewTheme toViewController:self quickly:YES];
|
||||
|
||||
if (self.addSearchListeners)
|
||||
{
|
||||
//Games
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(displaySelectedGame:) name:RBX_NOTIFY_GAME_SELECTED object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(displayGameSearchResults:) name:RBX_NOTIFY_SEARCH_GAMES object:nil];
|
||||
|
||||
//Users
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(displayUserSearchResults:) name:RBX_NOTIFY_SEARCH_USERS object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(displaySelectedUser:) name:RBX_NOTIFY_USER_SELECTED object:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
if (self.viewTheme)
|
||||
[RobloxTheme applyTheme:self.viewTheme toViewController:self quickly:NO];
|
||||
}
|
||||
|
||||
- (void) viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
if (self.addSearchListeners)
|
||||
{
|
||||
//Games
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBX_NOTIFY_GAME_SELECTED object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBX_NOTIFY_SEARCH_GAMES object:nil];
|
||||
|
||||
//Users
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBX_NOTIFY_SEARCH_USERS object:nil];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBX_NOTIFY_USER_SELECTED object:nil];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
//Navigation buttons
|
||||
- (void) removeRobuxAndBCIcons
|
||||
{
|
||||
NSMutableArray* barItems = [self.navigationItem.rightBarButtonItems mutableCopy];
|
||||
if (barItems.count >= [RobloxInfo thisDeviceIsATablet] ? 2 : 1)
|
||||
{
|
||||
[barItems removeObjectAtIndex:0];
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
[barItems removeObjectAtIndex:0];
|
||||
|
||||
//assign the updated list to the navigation item
|
||||
[self.navigationItem setRightBarButtonItems:barItems];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) addRobuxIconWithFlurryEvent:(NSString*)RobuxEvent
|
||||
andBCIconWithFlurryEvent:(NSString*)BCEvent
|
||||
{
|
||||
//define some events to be fired
|
||||
self.flurryEventRobux = RobuxEvent;
|
||||
self.flurryEventBuildersClub = BCEvent;
|
||||
|
||||
UIImage* rbxImage = [UIImage imageNamed:@"Icon Robux Off"];
|
||||
UIImage* rbxImageDown = [UIImage imageNamed:@"Icon Robux On"];
|
||||
UIImage* bcImage = [UIImage imageNamed:@"Icon BC Off"];
|
||||
UIImage* bcImageDown = [UIImage imageNamed:@"Icon BC On"];
|
||||
|
||||
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
//create the buttons
|
||||
if ([RBXFunctions isEmpty:self.buyRBXButtonItem]) {
|
||||
self.buyRBXButtonItem = [[UIBarButtonItem alloc] initWithImage:rbxImage
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(didPressBuyRobux)];
|
||||
}
|
||||
[self.buyRBXButtonItem setBackgroundImage:rbxImageDown forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
|
||||
|
||||
if ([RBXFunctions isEmpty:self.buyBCButtonItem]) {
|
||||
self.buyBCButtonItem = [[UIBarButtonItem alloc] initWithImage:bcImage
|
||||
style:UIBarButtonItemStylePlain
|
||||
target:self
|
||||
action:@selector(didPressBuildersClub)];
|
||||
}
|
||||
[self.buyBCButtonItem setBackgroundImage:bcImageDown forState:UIControlStateSelected barMetrics:UIBarMetricsDefault];
|
||||
|
||||
//insert the buttons at the head of the list of existing buttons
|
||||
NSMutableArray* buttons = [NSMutableArray arrayWithArray:@[self.buyRBXButtonItem, self.buyBCButtonItem]];
|
||||
[buttons addObjectsFromArray:self.navigationItem.rightBarButtonItems];
|
||||
[self.navigationItem setRightBarButtonItems:buttons];
|
||||
}
|
||||
else
|
||||
{
|
||||
//If we're on phone, we don't have as much space
|
||||
CGRect buttonFrame = CGRectMake(0, 0, 28, 44);
|
||||
CGRect layoutFrame = CGRectMake(0, 0, 64, 44);
|
||||
|
||||
//so make a custom view to space the icons closer
|
||||
UIButton* btnRBX = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[btnRBX setImage:rbxImage forState:UIControlStateNormal];
|
||||
[btnRBX setImage:rbxImageDown forState:UIControlStateSelected];
|
||||
[btnRBX addTarget:self action:@selector(didPressBuyRobux) forControlEvents:UIControlEventTouchUpInside];
|
||||
[btnRBX setFrame:buttonFrame];
|
||||
|
||||
UIButton* btnBC = [UIButton buttonWithType:UIButtonTypeCustom];
|
||||
[btnBC setImage:bcImage forState:UIControlStateNormal];
|
||||
[btnBC setImage:bcImageDown forState:UIControlStateSelected];
|
||||
[btnBC addTarget:self action:@selector(didPressBuildersClub) forControlEvents:UIControlEventTouchUpInside];
|
||||
[btnBC setFrame:buttonFrame];
|
||||
[btnBC setX:layoutFrame.size.width - btnBC.width];
|
||||
|
||||
UIView* customLayout = [[UIView alloc] initWithFrame:layoutFrame];
|
||||
[customLayout addSubview:btnBC];
|
||||
[customLayout addSubview:btnRBX];
|
||||
|
||||
UIBarButtonItem* customBtn = [[UIBarButtonItem alloc] initWithCustomView:customLayout];
|
||||
|
||||
NSMutableArray* buttons = [NSMutableArray arrayWithArray:@[customBtn]];
|
||||
[buttons addObjectsFromArray:self.navigationItem.rightBarButtonItems];
|
||||
[self.navigationItem setRightBarButtonItems:buttons];
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
- (void) addSearchIconWithSearchType:(SearchResultType)searchType
|
||||
andFlurryEvent:(NSString*)searchEvent
|
||||
{
|
||||
self.addSearchListeners = YES;
|
||||
bool compact = ![RobloxInfo thisDeviceIsATablet];
|
||||
NativeSearchNavItem* searchItem = [[NativeSearchNavItem alloc] initWithSearchType:searchType andContainer:self compactMode:compact];
|
||||
NSArray* rightNavItems = self.navigationItem.rightBarButtonItems;
|
||||
NSMutableArray* items = [NSMutableArray arrayWithArray:rightNavItems];
|
||||
[items addObject:searchItem];
|
||||
[self.navigationItem setRightBarButtonItems:items];
|
||||
}
|
||||
|
||||
//Navigation Button Events
|
||||
- (void)didPressEditAccount
|
||||
{
|
||||
#define ACCOUNT_SETTINGS_POPUP_SIZE CGRectMake(0, 0, 540, 344)
|
||||
if (self.flurryEventEditAccount)
|
||||
[Flurry logEvent:self.flurryEventEditAccount];
|
||||
|
||||
NSString* storyboardName = [RobloxInfo getStoryboardName];
|
||||
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
|
||||
RBAccountManagerViewController* controller = [storyboard instantiateViewControllerWithIdentifier:@"RBAccountManagerController"];
|
||||
UINavigationController* navigation = [[UINavigationController alloc] initWithRootViewController:controller];
|
||||
navigation.modalPresentationStyle = UIModalPresentationFormSheet;
|
||||
[self.navigationController presentViewController:navigation animated:YES completion:nil];
|
||||
navigation.view.superview.bounds = ACCOUNT_SETTINGS_POPUP_SIZE;
|
||||
}
|
||||
|
||||
- (void) didPressBuyRobux
|
||||
{
|
||||
CGRect PURCHASE_ROBUX_POPUP_SIZE = [RobloxInfo thisDeviceIsATablet] ? CGRectMake(0, 0, 540, 344) : CGRectMake(0, 0, 300, 344);
|
||||
|
||||
if (self.screenEnumName)
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonRobux
|
||||
withContext:RBXAContextMain
|
||||
withCustomData:self.screenEnumName];
|
||||
if (self.flurryEventRobux)
|
||||
[Flurry logEvent:self.flurryEventRobux];
|
||||
|
||||
RBPurchaseViewController* controller = [[RBPurchaseViewController alloc] initWithRobuxPurchasing];
|
||||
NonRotatableNavigationController* navigation = [[NonRotatableNavigationController alloc] initWithRootViewController:controller];
|
||||
navigation.modalPresentationStyle = UIModalPresentationFormSheet;
|
||||
[self.navigationController presentViewController:navigation animated:YES completion:nil];
|
||||
navigation.view.superview.bounds = PURCHASE_ROBUX_POPUP_SIZE;
|
||||
|
||||
if ([self.navigationController.tabBarController respondsToSelector:@selector(getCurrentTabContext)])
|
||||
{
|
||||
RBXAnalyticsCustomData cd = [((RBTabBarController*)self.navigationController.tabBarController) getCurrentTabContext];
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonRobux withContext:RBXAContextMain withCustomData:cd];
|
||||
}
|
||||
}
|
||||
- (void) didPressBuildersClub
|
||||
{
|
||||
if (self.screenEnumName)
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonRobux
|
||||
withContext:RBXAContextMain
|
||||
withCustomData:self.screenEnumName ];
|
||||
if (self.flurryEventBuildersClub)
|
||||
[Flurry logEvent:self.flurryEventBuildersClub];
|
||||
|
||||
RBPurchaseViewController* controller = [[RBPurchaseViewController alloc] initWithBCPurchasing];
|
||||
NonRotatableNavigationController* navigation = [[NonRotatableNavigationController alloc] initWithRootViewController:controller];
|
||||
navigation.modalPresentationStyle = UIModalPresentationFormSheet;
|
||||
[self.navigationController presentViewController:navigation animated:YES completion:nil];
|
||||
|
||||
if ([self.navigationController.tabBarController respondsToSelector:@selector(getCurrentTabContext)])
|
||||
{
|
||||
RBXAnalyticsCustomData cd = [((RBTabBarController*)self.navigationController.tabBarController) getCurrentTabContext];
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonBuildersClub withContext:RBXAContextMain withCustomData:cd];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//pushing view controllers
|
||||
-(RBXWebRequestReturnStatus) handleWebRequest:(NSURL*)aRequest
|
||||
{
|
||||
//typically called from a webview shouldLoadWithRequest delegate function
|
||||
//returns RBXWebRequestReturnWebRequest when the webview can safely load the requested URL
|
||||
//returns RBXWebRequestReturnScreenPush when the request has opened another view
|
||||
//returns RBXWebRequestReturnUnknown when the domain is unknown and should be handled by pushing out to the browser
|
||||
|
||||
NSString* aRequestString = [[NSString stringWithFormat:@"%@", aRequest] stringByRemovingPercentEncoding];
|
||||
aRequestString = [aRequestString lowercaseString];
|
||||
|
||||
//do a quick check that the url isn't a Roblox User's URL from a message (FRIEND REQUEST: ACCEPTED)
|
||||
// sometimes they don't fit the regular formatting style
|
||||
if ([aRequestString rangeOfString:@"/user.aspx?id="].location != NSNotFound)
|
||||
{
|
||||
[self pushProfileControllerWithURL:aRequestString];
|
||||
return RBXWebRequestReturnScreenPush;
|
||||
}
|
||||
|
||||
//check the domain of the URL to see if it is a ROBLOX URL
|
||||
// if it is, then we can handle it in-app
|
||||
NSString* lcaseHost = [[aRequest host] lowercaseString];
|
||||
if ([lcaseHost rangeOfString:@"roblox"].location != NSNotFound)
|
||||
{
|
||||
NSUInteger extenstionStart = [aRequestString rangeOfString:@".com"].location + 4;
|
||||
if (extenstionStart == NSNotFound)
|
||||
return RBXWebRequestReturnWebRequest; // <-- this should never get hit but let's be safe
|
||||
NSString* extension = [aRequestString substringFromIndex:extenstionStart];
|
||||
|
||||
//NOTE - THIS SHOULD BE CLEANED UP TO USE A DICTIONARY WITH INSTANT LOOKUP
|
||||
//THE MORE CASES THERE ARE HERE, THE LONGER EACH LOAD REQUEST TAKES.
|
||||
//-Kyler 2015
|
||||
|
||||
if ([extension rangeOfString:@"/newlogin"].location != NSNotFound)
|
||||
{
|
||||
//NOTE- sometime there is this really weird case where the user gets logged out from the website, but not the app
|
||||
//So might as well just log the user out and apologize
|
||||
[[LoginManager sharedInstance] logoutRobloxUser];
|
||||
[RobloxAlert RobloxAlertWithMessage:NSLocalizedString(@"ErrorUnknownLogout", nil)];
|
||||
[self.tabBarController.navigationController popToRootViewControllerAnimated:YES];
|
||||
|
||||
|
||||
//prevent the page from loading
|
||||
return RBXWebRequestReturnFilter;
|
||||
}
|
||||
else if ([extension rangeOfString:@"home"].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeSocial];
|
||||
}
|
||||
else if ([extension rangeOfString:@"/users/"].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeCreative];
|
||||
}
|
||||
else if ([extension rangeOfString:@"-place?id="].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
}
|
||||
else if ([extension rangeOfString:@"/games"].location != NSNotFound)
|
||||
{
|
||||
//we might have a native page to handle
|
||||
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
}
|
||||
else if ([extension rangeOfString:@"-item?id="].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
}
|
||||
else if ([extension rangeOfString:@"/catalog"].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
}
|
||||
else if ([extension rangeOfString:@"groups.aspx"].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeSocial];
|
||||
}
|
||||
else if ([extension rangeOfString:@"stuff.aspx"].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
}
|
||||
else if ([extension rangeOfString:@"inventory"].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
}
|
||||
else if ([extension rangeOfString:@"/forum"].location != NSNotFound)
|
||||
{
|
||||
[self setViewTheme:RBXThemeSocial];
|
||||
}
|
||||
return RBXWebRequestReturnWebRequest;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Not exactly sure what we found... just open it in an external web view.
|
||||
//[[UIApplication sharedApplication] openURL:aRequest];
|
||||
return RBXWebRequestReturnUnknown;
|
||||
}
|
||||
|
||||
return RBXWebRequestReturnWebRequest;
|
||||
}
|
||||
|
||||
-(void) handleWebRequestWithPopout:(NSURL*)aRequest
|
||||
{
|
||||
NSString* lcaseHost = [[aRequest host] lowercaseString];
|
||||
NSString* aRequestString = [[NSString stringWithFormat:@"%@", aRequest] stringByRemovingPercentEncoding];
|
||||
aRequestString = [aRequestString lowercaseString];
|
||||
|
||||
//do a quick check that the url isn't a Roblox User's URL from a message (FRIEND REQUEST: ACCEPTED)
|
||||
// sometimes they don't fit the regular formatting style
|
||||
if ([aRequestString rangeOfString:@"/user.aspx?id="].location != NSNotFound)
|
||||
{
|
||||
//NOTE- in this case, the url will look like this: "applewebdata://461243cd-ad85-4622-b586-c390076a4102/user.aspx?id=59257875"
|
||||
[self pushProfileControllerWithURL:aRequestString];
|
||||
return;
|
||||
}
|
||||
|
||||
//check the domain of the URL to see if it is a ROBLOX URL
|
||||
// if it is, then we can handle it in-app
|
||||
if ([lcaseHost rangeOfString:@"roblox"].location != NSNotFound)
|
||||
{
|
||||
NSUInteger extenstionStart = [aRequestString rangeOfString:@".com"].location + 4;
|
||||
if (extenstionStart == NSNotFound)
|
||||
{
|
||||
[[UIApplication sharedApplication] openURL:aRequest];// <-- this should never get hit but let's be safe
|
||||
return;
|
||||
}
|
||||
NSString* extension = [aRequestString substringFromIndex:extenstionStart];
|
||||
|
||||
|
||||
if ([extension rangeOfString:@"/newlogin"].location != NSNotFound)
|
||||
{
|
||||
//prevent the page from loading
|
||||
return;
|
||||
}
|
||||
else if ([extension rangeOfString:@"/home"].location != NSNotFound)
|
||||
{
|
||||
[self pushWebControllerwithURL:aRequestString andTheme:RBXThemeSocial];
|
||||
}
|
||||
else if ([extension rangeOfString:@"/users/"].location != NSNotFound)
|
||||
{
|
||||
[self pushProfileControllerWithURL:aRequestString];
|
||||
}
|
||||
else if ([extension rangeOfString:@"-place?id="].location != NSNotFound)
|
||||
{
|
||||
[self pushGameDetailWithURL:aRequestString];
|
||||
}
|
||||
else if ([extension rangeOfString:@"/games"].location != NSNotFound)
|
||||
{
|
||||
if (DFFlag::EnableNativeGamesPage)
|
||||
[self pushUpdatedGameDetailWithURL:aRequestString];
|
||||
else
|
||||
[self pushGameDetailWithURL:aRequestString];
|
||||
}
|
||||
else if ([extension rangeOfString:@"-item?id="].location != NSNotFound)
|
||||
{
|
||||
[self pushWebControllerwithURL:aRequestString andTheme:RBXThemeGame];
|
||||
}
|
||||
else if ([extension rangeOfString:@"groups.aspx"].location != NSNotFound)
|
||||
{
|
||||
[self pushWebControllerwithURL:aRequestString andTheme:RBXThemeSocial];
|
||||
}
|
||||
else if ([extension rangeOfString:@"stuff.aspx"].location != NSNotFound)
|
||||
{
|
||||
[self pushWebControllerwithURL:aRequestString andTheme:RBXThemeGame];
|
||||
}
|
||||
else
|
||||
{
|
||||
//Not exactly sure what we found... just open it in an external web view.
|
||||
if (self.flurryOpenExternalLinkEvent)
|
||||
[Flurry logEvent:self.flurryOpenExternalLinkEvent];
|
||||
[[UIApplication sharedApplication] openURL:aRequest];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (self.flurryOpenExternalLinkEvent)
|
||||
[Flurry logEvent:self.flurryOpenExternalLinkEvent];
|
||||
[[UIApplication sharedApplication] openURL:aRequest];
|
||||
}
|
||||
}
|
||||
|
||||
-(void) pushWebControllerwithURL:(NSString*)stringURL andTheme:(RBXTheme)theme
|
||||
{
|
||||
//load the url if we are already on a webview
|
||||
if ([self.class isSubclassOfClass:RBMobileWebViewController.class])
|
||||
{
|
||||
[((RBMobileWebViewController*)self) loadURL:stringURL screenURL:NO];
|
||||
[self setViewTheme:theme];
|
||||
}
|
||||
else
|
||||
[self pushWebControllerwithURL:stringURL andTitle:nil andTheme:theme];
|
||||
}
|
||||
-(void) pushWebControllerwithURL:(NSString*)stringURL andTitle:(NSString*)pageTitle andTheme:(RBXTheme)theme
|
||||
{
|
||||
if (self.flurryOpenWebViewEvent)
|
||||
[Flurry logEvent:self.flurryOpenWebViewEvent];
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
RBMobileWebViewController* aWebScreen = [[RBMobileWebViewController alloc] initWithNavButtons:NO];
|
||||
[aWebScreen.view setFrame:CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height)];
|
||||
[aWebScreen setUrl:stringURL];
|
||||
[aWebScreen setViewTheme:theme];
|
||||
|
||||
if (pageTitle)
|
||||
[aWebScreen setTitle:pageTitle];
|
||||
|
||||
[self.navigationController pushViewController:aWebScreen animated:YES];
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
//Profile
|
||||
-(void) pushProfileControllerWithUserID:(NSNumber*)userID
|
||||
{
|
||||
if (self.flurryOpenProfileEvent)
|
||||
[Flurry logEvent:self.flurryOpenProfileEvent];
|
||||
|
||||
if (DFFlag::EnableNativeProfilePage && [RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
NSString* storyboardName = [RobloxInfo getStoryboardName];
|
||||
UIStoryboard* sb = [UIStoryboard storyboardWithName:storyboardName bundle:nil];
|
||||
RBProfileViewController* builderProfile = (RBProfileViewController*) [sb instantiateViewControllerWithIdentifier:@"RBOthersProfileViewController"];
|
||||
builderProfile.userId = userID;
|
||||
[self.navigationController pushViewController:builderProfile animated:YES];
|
||||
}
|
||||
else
|
||||
{
|
||||
//check if we are a web page, just load the url for the player, don't push a view
|
||||
if ([self isKindOfClass:RBMobileWebViewController.class])
|
||||
{
|
||||
NSString* url = [NSString stringWithFormat:@"%@users/%@/profile", [RobloxInfo getWWWBaseUrl], userID.stringValue];
|
||||
[((RBMobileWebViewController*)self) loadURL:url screenURL:NO ];
|
||||
[self setViewTheme:RBXThemeSocial];
|
||||
}
|
||||
else
|
||||
{
|
||||
RBWebProfileViewController* builderProfile = [[RBWebProfileViewController alloc] initWithNavButtons:YES];
|
||||
builderProfile.userId = userID;
|
||||
[builderProfile setViewTheme:RBXThemeCreative];
|
||||
[self.navigationController pushViewController:builderProfile animated:YES];
|
||||
}
|
||||
}
|
||||
}
|
||||
-(void) pushProfileControllerWithURL:(NSString*)stringURL
|
||||
{
|
||||
//check what style of url we have
|
||||
NSString* unparsedID;
|
||||
if ([stringURL rangeOfString:@"/user.aspx?id="].location != NSNotFound)
|
||||
{
|
||||
//Do we have a legacy url? "applewebdata://461243cd-ad85-4622-b586-c390076a4102/user.aspx?id=59257875"
|
||||
NSArray* stringParts = [stringURL componentsSeparatedByString:@"/user.aspx?id="];
|
||||
if ([stringParts count] > 1)
|
||||
unparsedID = stringParts[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSRange newIDRange = [stringURL rangeOfString:@"/users/"];
|
||||
if (newIDRange.location != NSNotFound)
|
||||
{
|
||||
//or do we have a new url? http://www.watrbx.wtf/users/68465808/profile
|
||||
NSString* endOfString = [stringURL substringFromIndex:(newIDRange.location + newIDRange.length)];
|
||||
|
||||
NSRange indexOfSlash = [endOfString rangeOfString:@"/profile"];
|
||||
|
||||
//parse out the userID if we can find it
|
||||
if (indexOfSlash.location != NSNotFound)
|
||||
unparsedID = [endOfString substringToIndex:indexOfSlash.location];
|
||||
}
|
||||
}
|
||||
|
||||
//NOTE - flurry events are handled in other functions
|
||||
if (unparsedID)
|
||||
{
|
||||
NSNumber* userID = [NSNumber numberWithLongLong:[unparsedID longLongValue]];
|
||||
[self pushProfileControllerWithUserID:userID];
|
||||
}
|
||||
else
|
||||
{
|
||||
//failed to parse the url, fallback and use a mobile web view
|
||||
[self pushWebControllerwithURL:stringURL andTheme:RBXThemeCreative];
|
||||
}
|
||||
}
|
||||
|
||||
//Games
|
||||
-(void) pushGameDetailWithURL:(NSString*)stringURL
|
||||
{
|
||||
//load up a game
|
||||
//NSArray* stringParts = [stringURL componentsSeparatedByString:@"?id="];
|
||||
//if ([stringParts count] <= 1)
|
||||
//{
|
||||
//failed to parse the url, fallback and use a mobile web view
|
||||
[self pushWebControllerwithURL:stringURL andTheme:RBXThemeGame];
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
//grab the game details
|
||||
// RBBaseViewController __weak *weakSelf = self;
|
||||
|
||||
// [RobloxData fetchGameDetails:stringParts[1] completion:^(RBXGameData *game)
|
||||
// {
|
||||
// [weakSelf pushGameDetailWithGameData:game];
|
||||
// }];
|
||||
|
||||
//}
|
||||
}
|
||||
-(void) pushUpdatedGameDetailWithURL:(NSString*)stringURL
|
||||
{
|
||||
//load up a game
|
||||
NSString* regex = [NSString stringWithFormat:@"/games/.*/"];
|
||||
NSRange range = [stringURL rangeOfString:regex options:NSRegularExpressionSearch];
|
||||
if (range.location == NSNotFound)
|
||||
{
|
||||
NSRange sortFilter = [stringURL rangeOfString:@"sortfilter="];
|
||||
if (sortFilter.location != NSNotFound)
|
||||
{
|
||||
//we might have a specific sort to look at...
|
||||
NSString* sortName = [stringURL substringFromIndex:(sortFilter.location + sortFilter.length)];
|
||||
sortName = [sortName substringToIndex:[sortName rangeOfString:@"&"].location];
|
||||
|
||||
if ([sortName isEqualToString:@"default"])
|
||||
{
|
||||
[self pushGamesPage];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self pushGameSortWithSortId:[NSNumber numberWithInteger:sortName.integerValue]];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//looks like it is just a regular games page
|
||||
[self pushGamesPage];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//we have game details so grab the game ID, first remove the /games/ and the / at the end
|
||||
NSString* gameID = [stringURL substringFromIndex:(range.location + 7)];
|
||||
gameID = [gameID substringToIndex:(range.length - 8)];
|
||||
|
||||
RBBaseViewController __weak *weakSelf = self;
|
||||
|
||||
[RobloxData fetchGameDetails:gameID completion:^(RBXGameData *game)
|
||||
{
|
||||
if (game)
|
||||
[weakSelf pushGameDetailWithGameData:game];
|
||||
else
|
||||
[weakSelf pushWebControllerwithURL:stringURL andTheme:RBXThemeGame];
|
||||
}];
|
||||
|
||||
}
|
||||
}
|
||||
-(void) pushGameDetailWithGameData:(RBXGameData*)game
|
||||
{
|
||||
if ([RobloxInfo thisDeviceIsATablet] || [self isGameDetailEnabledOnPhone])
|
||||
{
|
||||
if (self.flurryOpenGameDetailEvent)
|
||||
[Flurry logEvent:self.flurryOpenGameDetailEvent];
|
||||
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
if ([self.class isSubclassOfClass:RBMobileWebViewController.class])
|
||||
{
|
||||
//if we are a webview, just load the game url
|
||||
NSString* gameURL = [NSString stringWithFormat:@"%@PlaceItem.aspx?id=%@", [RobloxInfo getWWWBaseUrl], game.placeID];
|
||||
((RBMobileWebViewController*)self).url = gameURL;
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
}
|
||||
else
|
||||
{
|
||||
RBWebGamePreviewScreenController* aGameScreen = [[RBWebGamePreviewScreenController alloc] init];
|
||||
aGameScreen.gameData = game;
|
||||
[self.navigationController pushViewController:aGameScreen animated:YES];
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
RBGameViewController* controller = [[RBGameViewController alloc] initWithLaunchParams:[RBXGameLaunchParams InitParamsForJoinPlace:game.placeID.integerValue]];
|
||||
[self presentViewController:controller animated:NO completion:nil];
|
||||
});
|
||||
}
|
||||
}
|
||||
-(void) pushGamesPage
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIViewController* controller;
|
||||
if ([RobloxInfo thisDeviceIsATablet] && DFFlag::EnableNativeGamesPage)
|
||||
{
|
||||
controller = [[FeaturedGamesScreenController alloc] init];
|
||||
}
|
||||
else
|
||||
{
|
||||
controller = [[RBMobileWebViewController alloc] initWithNavButtons:NO];
|
||||
((RBMobileWebViewController*)controller).url = [[RobloxInfo getBaseUrl] stringByAppendingString:@"/games"];
|
||||
}
|
||||
|
||||
[self.navigationController pushViewController:controller animated:YES];
|
||||
});
|
||||
}
|
||||
-(void) pushGameSortWithSortId:(NSNumber*)sortId
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
GameSortResultsScreenController* controller = [[GameSortResultsScreenController alloc] init];
|
||||
controller.selectedSort = sortId;
|
||||
|
||||
[self.navigationController pushViewController:controller animated:YES];
|
||||
});
|
||||
}
|
||||
|
||||
//Search
|
||||
-(void) pushSearchResultsType:(SearchResultType)resultType
|
||||
withKeyword:(NSString*)searchKeywords
|
||||
{
|
||||
SearchResultCollectionViewController* aCollectionViewController = [[SearchResultCollectionViewController alloc] initWithKeyword:searchKeywords andSearchType:resultType];
|
||||
[self.navigationController pushViewController:aCollectionViewController animated:YES];
|
||||
}
|
||||
|
||||
|
||||
//search result functions
|
||||
- (void) displaySelectedGame:(NSNotification*)notification
|
||||
{
|
||||
RBXGameData* notificationData = (RBXGameData*)[notification.userInfo objectForKey:@"gameData"];
|
||||
[self pushGameDetailWithGameData:notificationData];
|
||||
}
|
||||
- (void) displayGameSearchResults:(NSNotification*) notification
|
||||
{
|
||||
//[Flurry logEvent:GS_searchGames];
|
||||
NSString* keywords = [notification.userInfo objectForKey:@"keywords"];
|
||||
if (!keywords || keywords.length == 0)
|
||||
return;
|
||||
|
||||
//This is how it should be done - later
|
||||
if (DFFlag::EnableNativeSearchGameResults)
|
||||
{
|
||||
[self pushSearchResultsType:SearchResultGames withKeyword:keywords];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString* url = [NSString stringWithFormat:@"%@games/?Keyword=%@",
|
||||
[RobloxInfo getWWWBaseUrl],
|
||||
[keywords stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
|
||||
[self pushWebControllerwithURL:url andTheme:RBXThemeGame];
|
||||
}
|
||||
|
||||
}
|
||||
- (void) displaySelectedUser:(NSNotification*) notification
|
||||
{
|
||||
RBXUserSearchInfo* user = (RBXUserSearchInfo*)[notification.userInfo objectForKey:@"userData"];
|
||||
[self pushProfileControllerWithUserID:user.userId];
|
||||
}
|
||||
- (void) displayUserSearchResults:(NSNotification*) notification
|
||||
{
|
||||
//NSLog(@"Displaying User Search Results with notification : %@", notification);
|
||||
NSString* keywords = (NSString*)[notification.userInfo objectForKey:@"keywords"];
|
||||
if (!keywords || keywords.length == 0)
|
||||
return;
|
||||
|
||||
if (DFFlag::EnableNativeSearchPeopleResults)
|
||||
{
|
||||
[self pushSearchResultsType:SearchResultUsers withKeyword:keywords];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSString* url = [NSString stringWithFormat:@"%@%@%@",
|
||||
[RobloxInfo getBaseUrl],
|
||||
[RobloxInfo thisDeviceIsATablet] ? @"search/users?keyword=" : @"people?search=",
|
||||
[keywords stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
|
||||
[self pushWebControllerwithURL:url andTheme:RBXThemeSocial];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//IOS Flags
|
||||
- (BOOL)isGameDetailEnabledOnPhone
|
||||
{
|
||||
iOSSettingsService* iOSSettings = [[RobloxWebUtility sharedInstance] getCachediOSSettings];
|
||||
return iOSSettings->GetValueEnableWebPageGameDetail();
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// RBCatalogMasterController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/3/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RBMobileWebViewController.h"
|
||||
|
||||
@interface RBCatalogScreenController : RBMobileWebViewController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// RBCatalogMasterController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/3/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBCatalogScreenController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
|
||||
#define CS_openRobux @"CATALOG SCREEN - Open Robux"
|
||||
#define CS_openBuildersClub @"CATALOG SCREEN - Open Builders Club"
|
||||
#define CS_openSettings @"CATALOG SCREEN - Open Settings"
|
||||
#define CS_openLogout @"CATALOG SCREEN - Open Logout"
|
||||
#define CS_catalogSearch @"CATALOG SCREEN - Catalog Search"
|
||||
#define CS_openExternalLink @"CATALOG SCREEN - Open External Link"
|
||||
#define CS_openProfile @"CATALOG SCREEN - Open Profile"
|
||||
#define CS_openGameDetail @"CATALOG SCREEN - Open Game Detail"
|
||||
#define CS_launchGame @"CATALOG SCREEN - Launch Game"
|
||||
|
||||
@interface RBCatalogScreenController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBCatalogScreenController
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
|
||||
self.navigationItem.title = NSLocalizedString(@"CatalogWord", nil);
|
||||
|
||||
[self setUrl:[[RobloxInfo getBaseUrl] stringByAppendingString:@"catalog/"]]; //???
|
||||
|
||||
[self addRobuxIconWithFlurryEvent:CS_openRobux
|
||||
andBCIconWithFlurryEvent:CS_openBuildersClub];
|
||||
|
||||
[self setFlurryPageLoadEvent:CS_catalogSearch];
|
||||
[self setFlurryGameLaunchEvent:CS_launchGame]; //don't know when this would ever happen, but I imagine there are ads all over the place
|
||||
[self setFlurryEventsForExternalLinkEvent:CS_openExternalLink
|
||||
andWebViewEvent:nil
|
||||
andOpenProfileEvent:CS_openProfile
|
||||
andOpenGameDetailEvent:CS_openGameDetail];
|
||||
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
[super viewWillAppear:animated];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// RBPurchaseConsumableViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by alichtin on 10/1/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RobloxData.h"
|
||||
#include "RBModalPopUpViewController.h"
|
||||
|
||||
@interface RBConfirmPurchaseViewController : RBModalPopUpViewController
|
||||
|
||||
@property(strong, nonatomic) NSString* thumbnailAssetID;
|
||||
@property(strong, nonatomic) NSString* productName;
|
||||
@property(nonatomic) NSUInteger productID;
|
||||
@property(nonatomic) NSUInteger price;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,144 @@
|
||||
//
|
||||
// RBConfirmPurchaseViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by alichtin on 10/1/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBConfirmPurchaseViewController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxImageView.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "RobloxData.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "RBVotesView.h"
|
||||
#import "UserInfo.h"
|
||||
#import "RBFavoritesView.h"
|
||||
#import "RBProfileViewController.h"
|
||||
#import "RobloxNotifications.h"
|
||||
|
||||
#define DEFAULT_VIEW_SIZE CGSizeMake(600, 254)
|
||||
#define ASSET_THUMBNAIL_SIZE CGSizeMake(420, 230)
|
||||
#define SELLER_THUMBNAIL_SIZE CGSizeMake(110, 110)
|
||||
|
||||
@interface RBConfirmPurchaseViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBConfirmPurchaseViewController
|
||||
{
|
||||
IBOutlet UINavigationBar* _navBar;
|
||||
|
||||
IBOutlet RobloxImageView* _assetThumbnail;
|
||||
IBOutlet UIView* _confirmPurchaseView;
|
||||
IBOutlet UITextView* _confirmPurchaseTitle;
|
||||
IBOutlet UITextView* _confirmPurchaseSubtitle;
|
||||
IBOutlet UIButton* _confirmPurchaseButton;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.preferredContentSize = DEFAULT_VIEW_SIZE;
|
||||
|
||||
[RobloxTheme applyToModalPopupNavBar:_navBar];
|
||||
[RobloxTheme applyToBuyWithRobuxButton:_confirmPurchaseButton];
|
||||
|
||||
[self presentConfirmPurchaseScreen];
|
||||
}
|
||||
|
||||
- (void) presentConfirmPurchaseScreen
|
||||
{
|
||||
_navBar.topItem.title = self.productName;
|
||||
|
||||
[_assetThumbnail loadWithAssetID:self.thumbnailAssetID withSize:ASSET_THUMBNAIL_SIZE completion:nil];
|
||||
|
||||
NSString* priceText = [NSString stringWithFormat:@"\nR$%lu", (unsigned long)self.price];
|
||||
NSString* confirmTitleText = [NSString stringWithFormat:NSLocalizedString(@"ConfirmPurchaseTitle", nil), priceText];
|
||||
|
||||
// Decorate title
|
||||
{
|
||||
NSMutableAttributedString* confirmTitle = [[NSMutableAttributedString alloc] initWithString:confirmTitleText
|
||||
attributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[UIColor colorWithWhite:(0x80/255.0f) alpha:1.0f],
|
||||
NSForegroundColorAttributeName,
|
||||
[UIFont fontWithName:@"SourceSansPro-Regular" size:18],
|
||||
NSFontAttributeName,
|
||||
nil]];
|
||||
|
||||
NSRange priceRange = [confirmTitleText rangeOfString:priceText];
|
||||
[confirmTitle beginEditing];
|
||||
[confirmTitle addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:(0x18/255.0) green:(0xB6/255.0) blue:(0x5B/255.0) alpha:1.0f] range:priceRange];
|
||||
[confirmTitle endEditing];
|
||||
|
||||
_confirmPurchaseTitle.attributedText = confirmTitle;
|
||||
}
|
||||
|
||||
// Decorate subtitle
|
||||
{
|
||||
UserInfo* currentPlayer = [UserInfo CurrentPlayer];
|
||||
NSInteger currentBalance = [currentPlayer.rbxBal integerValue];
|
||||
NSInteger remainingBalance = currentBalance > self.price ? (currentBalance - self.price) : 0;
|
||||
|
||||
NSString* remainingBalanceText = [NSString stringWithFormat:@"R$%ld", (long)remainingBalance];
|
||||
NSString* confirmSubtitleText = [NSString stringWithFormat:NSLocalizedString(@"BalanceSubtitlePhrase", nil), remainingBalanceText];
|
||||
|
||||
NSMutableAttributedString* confirmSubtitle = [[NSMutableAttributedString alloc] initWithString:confirmSubtitleText
|
||||
attributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[UIColor colorWithWhite:(0x80/255.0f) alpha:1.0f],
|
||||
NSForegroundColorAttributeName,
|
||||
[UIFont fontWithName:@"SourceSansPro-Regular" size:14],
|
||||
NSFontAttributeName,
|
||||
nil]];
|
||||
|
||||
NSRange priceRange = [confirmSubtitleText rangeOfString:remainingBalanceText];
|
||||
[confirmSubtitle beginEditing];
|
||||
[confirmSubtitle addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:(0x18/255.0) green:(0xB6/255.0) blue:(0x5B/255.0) alpha:1.0f] range:priceRange];
|
||||
[confirmSubtitle endEditing];
|
||||
|
||||
_confirmPurchaseSubtitle.attributedText = confirmSubtitle;
|
||||
}
|
||||
|
||||
[_confirmPurchaseButton setTitle:NSLocalizedString(@"BuyWord", nil) forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
- (IBAction)confirmPurchaseTouchUpInside:(id)sender
|
||||
{
|
||||
[RobloxHUD showSpinnerWithLabel:@"" dimBackground:YES];
|
||||
|
||||
[RobloxData
|
||||
purchaseProduct:self.productID
|
||||
currencyType:RBXCurrencyTypeRobux
|
||||
purchasePrice:self.price
|
||||
completion:^(BOOL success, NSString *errorMessage)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
if(success)
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RBX_NOTIFY_ROBUX_UPDATED object:nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RBX_NOTIFY_GAME_ITEMS_UPDATED object:nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RBX_NOTIFY_GAME_PURCHASED object:nil];
|
||||
}
|
||||
else if(errorMessage)
|
||||
{
|
||||
[RobloxHUD showMessage:errorMessage];
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxHUD showMessage:NSLocalizedString(@"UnknownError", nil)];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)closeButtonTouchUpInside:(id)sender
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6245" systemVersion="14A388a" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment defaultVersion="1792" identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6238"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="RBConfirmPurchaseViewController">
|
||||
<connections>
|
||||
<outlet property="_assetThumbnail" destination="eCf-C3-Og4" id="zoe-aL-xtv"/>
|
||||
<outlet property="_confirmPurchaseButton" destination="nhS-Vu-Y4v" id="2Rw-uT-0Cs"/>
|
||||
<outlet property="_confirmPurchaseSubtitle" destination="3bO-CM-14i" id="JkV-va-CkV"/>
|
||||
<outlet property="_confirmPurchaseTitle" destination="qOT-Jv-ZQO" id="U4C-2u-T43"/>
|
||||
<outlet property="_confirmPurchaseView" destination="oa7-hZ-XLa" id="v60-z3-9LT"/>
|
||||
<outlet property="_navBar" destination="tmx-FW-b34" id="HIQ-dM-skM"/>
|
||||
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="254"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<navigationBar contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tmx-FW-b34">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
|
||||
<items>
|
||||
<navigationItem title="Title" id="X5X-uN-GM0">
|
||||
<barButtonItem key="leftBarButtonItem" image="Close Button" id="mjE-w9-BkH">
|
||||
<button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="Vjf-AT-Zwm">
|
||||
<rect key="frame" x="16" y="7" width="17" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<state key="normal" image="Close Button">
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="closeButtonTouchUpInside:" destination="-1" eventType="touchUpInside" id="vaT-3J-4WD"/>
|
||||
</connections>
|
||||
</button>
|
||||
<connections>
|
||||
<action selector="closeButtonSelected:" destination="-1" id="5Hw-Fm-QBG"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
</items>
|
||||
</navigationBar>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oa7-hZ-XLa" userLabel="Confirm Purchase View">
|
||||
<rect key="frame" x="0.0" y="47" width="600" height="207"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nhS-Vu-Y4v">
|
||||
<rect key="frame" x="365" y="100" width="128" height="36"/>
|
||||
<state key="normal" title="__BUY__" backgroundImage="Buy Button Robux">
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="confirmPurchaseTouchUpInside:" destination="-1" eventType="touchUpInside" id="QMX-ts-jea"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" editable="NO" usesAttributedText="YES" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qOT-Jv-ZQO">
|
||||
<rect key="frame" x="277" y="21" width="304" height="77"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<attributedString key="attributedText">
|
||||
<fragment>
|
||||
<string key="content">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut </string>
|
||||
<attributes>
|
||||
<font key="NSFont" size="14" name="HelveticaNeue"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
</attributedString>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" editable="NO" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3bO-CM-14i">
|
||||
<rect key="frame" x="264" y="158" width="322" height="32"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eCf-C3-Og4" customClass="RobloxImageView">
|
||||
<rect key="frame" x="20" y="47" width="200" height="200"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="193" y="248"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="Buy Button Robux" width="128" height="36"/>
|
||||
<image name="Close Button" width="17" height="17"/>
|
||||
</resources>
|
||||
</document>
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// RBPurchaseConsumableViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by alichtin on 10/1/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RobloxData.h"
|
||||
#include "RBModalPopUpViewController.h"
|
||||
|
||||
@interface RBConsumablePurchaseViewController : RBModalPopUpViewController
|
||||
|
||||
@property(strong, nonatomic) NSString* gameTitle;
|
||||
|
||||
@property(strong, nonatomic) RBXGameGear* gearData;
|
||||
@property(strong, nonatomic) RBXGamePass* passData;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,306 @@
|
||||
//
|
||||
// RBPurchaseConsumableViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by alichtin on 10/1/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBConsumablePurchaseViewController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxImageView.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "RobloxData.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "RBVotesView.h"
|
||||
#import "UserInfo.h"
|
||||
#import "RBFavoritesView.h"
|
||||
#import "RBProfileViewController.h"
|
||||
#import "RobloxNotifications.h"
|
||||
|
||||
#define DEFAULT_VIEW_SIZE CGRectMake(0, 0, 600, 512)
|
||||
#define ASSET_THUMBNAIL_SIZE CGSizeMake(420, 230)
|
||||
#define SELLER_THUMBNAIL_SIZE CGSizeMake(110, 110)
|
||||
|
||||
@interface RBConsumablePurchaseViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBConsumablePurchaseViewController
|
||||
{
|
||||
IBOutlet UINavigationBar* _navBar;
|
||||
IBOutlet UIView* _detailView;
|
||||
IBOutlet UIView* _descriptionView;
|
||||
|
||||
IBOutlet RobloxImageView* _assetThumbnail;
|
||||
IBOutlet RobloxImageView* _sellerThumbnail;
|
||||
IBOutlet UILabel* _sellerLabel;
|
||||
IBOutlet UIButton* _sellerButton;
|
||||
IBOutlet UIButton* _chevronButton;
|
||||
IBOutlet UIButton* _buyWithRobuxButton;
|
||||
IBOutlet UIButton* _buyWithTicketsButton;
|
||||
IBOutlet UILabel* _descriptionTitleLabel;
|
||||
IBOutlet UITextView* _descriptionTextView;
|
||||
IBOutlet RBVotesView* _passVotesView;
|
||||
IBOutlet RBFavoritesView* _favoritesView;
|
||||
|
||||
IBOutlet UIView* _confirmPurchaseView;
|
||||
IBOutlet UITextView* _confirmPurchaseTitle;
|
||||
IBOutlet UITextView* _confirmPurchaseSubtitle;
|
||||
IBOutlet UIButton* _confirmPurchaseButton;
|
||||
|
||||
RBXCurrencyType _purchaseCurrency;
|
||||
NSUInteger _purchasePrice;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[RobloxTheme applyToModalPopupNavBar:_navBar];
|
||||
[RobloxTheme applyToConsumableSellerTitle:_sellerLabel];
|
||||
[RobloxTheme applyToConsumableSellerButton:_sellerButton];
|
||||
[RobloxTheme applyToBuyWithRobuxButton:_buyWithRobuxButton];
|
||||
[RobloxTheme applyToBuyWithTixButton:_buyWithTicketsButton];
|
||||
[RobloxTheme applyToConsumableDescriptionTitle:_descriptionTitleLabel];
|
||||
[RobloxTheme applyToConsumableDescriptionTextView:_descriptionTextView];
|
||||
[RobloxTheme applyToBuyWithRobuxButton:_confirmPurchaseButton];
|
||||
|
||||
if(_gearData != nil)
|
||||
{
|
||||
_navBar.topItem.title = _gearData.name;
|
||||
|
||||
[_assetThumbnail loadWithAssetID:[_gearData.assetID stringValue] withSize:ASSET_THUMBNAIL_SIZE completion:nil];
|
||||
[_sellerThumbnail loadAvatarForUserID:[_gearData.sellerID intValue] withSize:SELLER_THUMBNAIL_SIZE completion:nil];
|
||||
|
||||
_sellerLabel.text = [NSLocalizedString(@"ByWord", nil) stringByAppendingString:@": "];
|
||||
[_sellerButton setTitle:_gearData.sellerName forState:UIControlStateNormal];
|
||||
|
||||
_buyWithRobuxButton.hidden = _gearData.priceInRobux == 0 || _gearData.userOwns;
|
||||
_buyWithTicketsButton.hidden = _gearData.priceInTickets == 0 || _gearData.userOwns;
|
||||
|
||||
if(_buyWithRobuxButton.hidden == NO)
|
||||
{
|
||||
[_buyWithRobuxButton setTitle:[NSString stringWithFormat:@" %lu", (unsigned long)_gearData.priceInRobux] forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
if(_buyWithTicketsButton.hidden == NO)
|
||||
{
|
||||
[_buyWithTicketsButton setTitle:[NSString stringWithFormat:@" %lu", (unsigned long)_gearData.priceInTickets] forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
[_favoritesView setFavoritesForGear:_gearData];
|
||||
|
||||
_descriptionTitleLabel.text = NSLocalizedString(@"DescriptionWord", nil);
|
||||
_descriptionTextView.text = _gearData.gearDescription;
|
||||
|
||||
_passVotesView.hidden = YES;
|
||||
}
|
||||
else if(_passData != nil)
|
||||
{
|
||||
_navBar.topItem.title = _passData.passName;
|
||||
|
||||
[_assetThumbnail loadWithAssetID:[_passData.passID stringValue] withSize:ASSET_THUMBNAIL_SIZE completion:nil];
|
||||
_sellerThumbnail.hidden = YES;
|
||||
|
||||
_sellerLabel.text = [NSString stringWithFormat:@"%@: %@", NSLocalizedString(@"GamePassForPhrase", nil), _gameTitle];
|
||||
_sellerLabel.width = _sellerLabel.width + 52;
|
||||
_sellerLabel.x = 252;
|
||||
_sellerButton.hidden = YES;
|
||||
_chevronButton.hidden = YES;
|
||||
|
||||
[_passVotesView setVotesForPass:_passData];
|
||||
|
||||
_buyWithRobuxButton.hidden = _passData.priceInRobux == 0 || _passData.userOwns;
|
||||
_buyWithTicketsButton.hidden = _passData.priceInTickets == 0 || _passData.userOwns;
|
||||
|
||||
if(_buyWithRobuxButton.hidden == NO)
|
||||
{
|
||||
[_buyWithRobuxButton setTitle:[NSString stringWithFormat:@" %lu", (unsigned long)_passData.priceInRobux] forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
if(_buyWithTicketsButton.hidden == NO)
|
||||
{
|
||||
[_buyWithTicketsButton setTitle:[NSString stringWithFormat:@" %lu", (unsigned long)_passData.priceInTickets] forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
[_favoritesView setFavoritesForPass:_passData];
|
||||
|
||||
_descriptionTitleLabel.text = NSLocalizedString(@"DescriptionWord", nil);
|
||||
_descriptionTextView.text = _passData.passDescription;
|
||||
}
|
||||
|
||||
_confirmPurchaseView.hidden = YES;
|
||||
[_confirmPurchaseButton setTitle:NSLocalizedString(@"BuyWord", nil) forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
- (void)viewWillLayoutSubviews
|
||||
{
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
self.view.superview.bounds = DEFAULT_VIEW_SIZE;
|
||||
}
|
||||
|
||||
- (void) goToConfirmPurchase
|
||||
{
|
||||
NSString* itemName;
|
||||
NSString* priceText = [NSString stringWithFormat:(_purchaseCurrency == RBXCurrencyTypeRobux ? @"R$%lu" : @"TX$%lu"), (unsigned long)_purchasePrice];
|
||||
NSString* confirmTitleText;
|
||||
|
||||
if(_gearData != nil)
|
||||
{
|
||||
itemName = _gearData.name;
|
||||
|
||||
confirmTitleText = [NSString stringWithFormat:NSLocalizedString(@"ConfirmGearPurchaseTitle", nil),
|
||||
_gearData.name,
|
||||
_gearData.sellerName,
|
||||
priceText];
|
||||
}
|
||||
else // if(_passData != nil)
|
||||
{
|
||||
itemName = _passData.passName;
|
||||
|
||||
confirmTitleText = [NSString stringWithFormat:NSLocalizedString(@"ConfirmPassPurchaseTitle", nil),
|
||||
_passData.passName,
|
||||
priceText];
|
||||
}
|
||||
|
||||
// Decorate title
|
||||
{
|
||||
NSMutableAttributedString* confirmTitle = [[NSMutableAttributedString alloc] initWithString:confirmTitleText
|
||||
attributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[UIColor colorWithWhite:(0x80/255.0f) alpha:1.0f],
|
||||
NSForegroundColorAttributeName,
|
||||
[UIFont fontWithName:@"SourceSansPro-Regular" size:18],
|
||||
NSFontAttributeName,
|
||||
nil]];
|
||||
|
||||
NSRange nameRange = [confirmTitleText rangeOfString:itemName];
|
||||
NSRange priceRange = [confirmTitleText rangeOfString:priceText];
|
||||
[confirmTitle beginEditing];
|
||||
[confirmTitle addAttribute:NSFontAttributeName value:[UIFont fontWithName:@"SourceSansPro-Semibold" size:18] range:nameRange];
|
||||
[confirmTitle addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:(0x18/255.0) green:(0xB6/255.0) blue:(0x5B/255.0) alpha:1.0f] range:priceRange];
|
||||
[confirmTitle endEditing];
|
||||
|
||||
_confirmPurchaseTitle.attributedText = confirmTitle;
|
||||
}
|
||||
|
||||
// Decorate subtitle
|
||||
{
|
||||
UserInfo* currentPlayer = [UserInfo CurrentPlayer];
|
||||
NSInteger currentBalance = [(_purchaseCurrency == RBXCurrencyTypeRobux ? currentPlayer.rbxBal : currentPlayer.tikBal) integerValue];
|
||||
NSInteger remainingBalance = currentBalance > _purchasePrice ? (currentBalance - _purchasePrice) : 0;
|
||||
|
||||
NSString* remainingBalanceText = [NSString stringWithFormat:(_purchaseCurrency == RBXCurrencyTypeRobux ? @"R$%lu" : @"TX$%lu"), (long)remainingBalance];
|
||||
NSString* confirmSubtitleText = [NSString stringWithFormat:NSLocalizedString(@"BalanceSubtitlePhrase", nil), remainingBalanceText];
|
||||
|
||||
NSMutableAttributedString* confirmSubtitle = [[NSMutableAttributedString alloc] initWithString:confirmSubtitleText
|
||||
attributes:[NSDictionary dictionaryWithObjectsAndKeys:
|
||||
[UIColor colorWithWhite:(0x80/255.0f) alpha:1.0f],
|
||||
NSForegroundColorAttributeName,
|
||||
[UIFont fontWithName:@"SourceSansPro-Regular" size:14],
|
||||
NSFontAttributeName,
|
||||
nil]];
|
||||
|
||||
NSRange priceRange = [confirmSubtitleText rangeOfString:remainingBalanceText];
|
||||
[confirmSubtitle beginEditing];
|
||||
[confirmSubtitle addAttribute:NSForegroundColorAttributeName value:[UIColor colorWithRed:(0x18/255.0) green:(0xB6/255.0) blue:(0x5B/255.0) alpha:1.0f] range:priceRange];
|
||||
[confirmSubtitle endEditing];
|
||||
|
||||
_confirmPurchaseSubtitle.attributedText = confirmSubtitle;
|
||||
}
|
||||
|
||||
// Animate in
|
||||
[UIView animateWithDuration:0.2 animations:^
|
||||
{
|
||||
_detailView.alpha = 0.0f;
|
||||
}
|
||||
completion:^(BOOL finished)
|
||||
{
|
||||
_detailView.hidden = YES;
|
||||
|
||||
_confirmPurchaseView.hidden = NO;
|
||||
_confirmPurchaseView.alpha = 0;
|
||||
|
||||
CGFloat sourcePosY = _confirmPurchaseView.y;
|
||||
_confirmPurchaseView.y = sourcePosY + 20;
|
||||
|
||||
[UIView animateWithDuration:0.4 animations:^
|
||||
{
|
||||
_confirmPurchaseView.alpha = 1;
|
||||
_confirmPurchaseView.y = sourcePosY;
|
||||
}
|
||||
completion:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)buyWithRobuxTouchUpInside:(id)sender
|
||||
{
|
||||
_purchaseCurrency = RBXCurrencyTypeRobux;
|
||||
_purchasePrice = (_gearData != nil) ? _gearData.priceInRobux : _passData.priceInRobux;
|
||||
[self goToConfirmPurchase];
|
||||
}
|
||||
|
||||
- (IBAction)buyWithTixTouchUpInside:(id)sender
|
||||
{
|
||||
_purchaseCurrency = RBXCurrencyTypeTickets;
|
||||
_purchasePrice = (_gearData != nil) ? _gearData.priceInTickets : _passData.priceInTickets;
|
||||
[self goToConfirmPurchase];
|
||||
}
|
||||
|
||||
- (IBAction)confirmPurchaseTouchUpInside:(id)sender
|
||||
{
|
||||
[RobloxHUD showSpinnerWithLabel:@"" dimBackground:YES];
|
||||
|
||||
NSUInteger productID = _gearData != nil ? [_gearData.productID unsignedIntegerValue]
|
||||
: [_passData.productID unsignedIntegerValue];
|
||||
|
||||
[RobloxData
|
||||
purchaseProduct:productID
|
||||
currencyType:_purchaseCurrency
|
||||
purchasePrice:_purchasePrice
|
||||
completion:^(BOOL success, NSString *errorMessage)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
if(success)
|
||||
{
|
||||
if (_gearData != nil)
|
||||
_gearData.userOwns = YES;
|
||||
else
|
||||
_passData.userOwns = YES;
|
||||
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RBX_NOTIFY_ROBUX_UPDATED object:nil];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RBX_NOTIFY_GAME_ITEMS_UPDATED object:nil];
|
||||
|
||||
}
|
||||
else if(errorMessage)
|
||||
{
|
||||
[RobloxHUD showMessage:errorMessage];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)closeButtonTouchUpInside:(id)sender
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (IBAction)sellerButtonTouchUpInside:(id)sender
|
||||
{
|
||||
if(_gearData != nil)
|
||||
{
|
||||
NSString *storyboardName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIMainStoryboardFile"];
|
||||
UIStoryboard* mainStoryboard = [UIStoryboard storyboardWithName:storyboardName bundle:[NSBundle mainBundle]];
|
||||
|
||||
RBProfileViewController *viewController = (RBProfileViewController*) [mainStoryboard instantiateViewControllerWithIdentifier:@"RBOthersProfileViewController"];
|
||||
|
||||
viewController.userId = _gearData.sellerID;
|
||||
|
||||
[self.navigationController pushViewController:viewController animated:YES];
|
||||
}
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,198 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6254" systemVersion="14A389" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6247"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="RBConsumablePurchaseViewController">
|
||||
<connections>
|
||||
<outlet property="_assetThumbnail" destination="eCf-C3-Og4" id="zoe-aL-xtv"/>
|
||||
<outlet property="_buyWithRobuxButton" destination="lUi-M0-atO" id="5Vm-iI-cQc"/>
|
||||
<outlet property="_buyWithTicketsButton" destination="MMt-xS-6AY" id="KTq-mZ-RMJ"/>
|
||||
<outlet property="_chevronButton" destination="vv4-kN-Bhj" id="Poy-Q1-UzV"/>
|
||||
<outlet property="_confirmPurchaseButton" destination="nhS-Vu-Y4v" id="2Rw-uT-0Cs"/>
|
||||
<outlet property="_confirmPurchaseSubtitle" destination="3bO-CM-14i" id="JkV-va-CkV"/>
|
||||
<outlet property="_confirmPurchaseTitle" destination="qOT-Jv-ZQO" id="U4C-2u-T43"/>
|
||||
<outlet property="_confirmPurchaseView" destination="oa7-hZ-XLa" id="v60-z3-9LT"/>
|
||||
<outlet property="_descriptionTextView" destination="KJ5-3Z-pid" id="0eb-Gi-poi"/>
|
||||
<outlet property="_descriptionTitleLabel" destination="hsw-Sq-6I9" id="xqF-1m-owt"/>
|
||||
<outlet property="_descriptionView" destination="M9R-xc-bfi" id="T8E-FY-Ck4"/>
|
||||
<outlet property="_detailView" destination="dqj-LN-75Y" id="Hqt-qw-7ez"/>
|
||||
<outlet property="_favoritesView" destination="4sY-eF-MAx" id="mfX-wq-xcV"/>
|
||||
<outlet property="_navBar" destination="tmx-FW-b34" id="HIQ-dM-skM"/>
|
||||
<outlet property="_passVotesView" destination="S63-69-PcA" id="dLT-Po-WXe"/>
|
||||
<outlet property="_sellerButton" destination="MaC-9i-vKc" id="WBy-fF-8LK"/>
|
||||
<outlet property="_sellerLabel" destination="fA7-58-W69" id="ebA-D0-lbz"/>
|
||||
<outlet property="_sellerThumbnail" destination="pXv-Oi-Tye" id="wsw-9N-hQ6"/>
|
||||
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="512"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<navigationBar contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tmx-FW-b34">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="44"/>
|
||||
<items>
|
||||
<navigationItem title="Title" id="X5X-uN-GM0">
|
||||
<barButtonItem key="leftBarButtonItem" image="Close Button" id="mjE-w9-BkH">
|
||||
<button key="customView" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="Vjf-AT-Zwm">
|
||||
<rect key="frame" x="16" y="7" width="17" height="22"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<state key="normal" image="Close Button">
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="closeButtonTouchUpInside:" destination="-1" eventType="touchUpInside" id="vaT-3J-4WD"/>
|
||||
</connections>
|
||||
</button>
|
||||
<connections>
|
||||
<action selector="closeButtonSelected:" destination="-1" id="5Hw-Fm-QBG"/>
|
||||
</connections>
|
||||
</barButtonItem>
|
||||
</navigationItem>
|
||||
</items>
|
||||
</navigationBar>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="dqj-LN-75Y" userLabel="Detail View">
|
||||
<rect key="frame" x="0.0" y="45" width="600" height="204"/>
|
||||
<subviews>
|
||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" fixedFrame="YES" image="Separator" translatesAutoresizingMaskIntoConstraints="NO" id="SXk-BK-ZfC">
|
||||
<rect key="frame" x="252" y="63" width="324" height="1"/>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="_SELLER_TITLE_" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="fA7-58-W69">
|
||||
<rect key="frame" x="308" y="29" width="260" height="26"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pXv-Oi-Tye" customClass="RobloxImageView">
|
||||
<rect key="frame" x="252" y="13" width="48" height="48"/>
|
||||
</imageView>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="right" contentVerticalAlignment="center" lineBreakMode="headTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="vv4-kN-Bhj" userLabel="Chevron button">
|
||||
<rect key="frame" x="415" y="32" width="14" height="22"/>
|
||||
<fontDescription key="fontDescription" name="HelveticaNeue-Bold" family="Helvetica Neue" pointSize="15"/>
|
||||
<state key="normal" image="Right Arrow pressed"/>
|
||||
<connections>
|
||||
<action selector="sellerButtonTouchUpInside:" destination="-1" eventType="touchUpInside" id="hOD-hn-y7m"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="MaC-9i-vKc">
|
||||
<rect key="frame" x="325" y="30" width="100" height="25"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="18"/>
|
||||
<state key="normal" title="ROBLOX">
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="sellerButtonTouchUpInside:" destination="-1" eventType="touchUpInside" id="Duy-CO-ObO"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="MMt-xS-6AY">
|
||||
<rect key="frame" x="423" y="140" width="128" height="36"/>
|
||||
<state key="normal" title=" Button" image="Tix Icon" backgroundImage="Buy Button Tix">
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="buyWithTixTouchUpInside:" destination="-1" eventType="touchUpInside" id="7Xq-pb-Q68"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="lUi-M0-atO">
|
||||
<rect key="frame" x="277" y="140" width="129" height="36"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<color key="tintColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<state key="normal" title=" Button" image="Icon Robux Off" backgroundImage="Buy Button Robux">
|
||||
<color key="titleShadowColor" red="1" green="1" blue="1" alpha="1" colorSpace="calibratedRGB"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="buyWithRobuxTouchUpInside:" destination="-1" eventType="touchUpInside" id="BNr-6z-4hg"/>
|
||||
</connections>
|
||||
</button>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="S63-69-PcA" customClass="RBVotesView">
|
||||
<rect key="frame" x="383" y="79" width="192" height="29"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="4sY-eF-MAx" customClass="RBFavoritesView">
|
||||
<rect key="frame" x="258" y="79" width="122" height="27"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<view hidden="YES" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oa7-hZ-XLa" userLabel="Confirm Purchase View">
|
||||
<rect key="frame" x="0.0" y="47" width="600" height="207"/>
|
||||
<subviews>
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nhS-Vu-Y4v">
|
||||
<rect key="frame" x="365" y="100" width="128" height="36"/>
|
||||
<state key="normal" title="__BUY__" backgroundImage="Buy Button Robux">
|
||||
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="confirmPurchaseTouchUpInside:" destination="-1" eventType="touchUpInside" id="QMX-ts-jea"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" editable="NO" usesAttributedText="YES" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="qOT-Jv-ZQO">
|
||||
<rect key="frame" x="277" y="21" width="304" height="77"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<attributedString key="attributedText">
|
||||
<fragment>
|
||||
<string key="content">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut </string>
|
||||
<attributes>
|
||||
<font key="NSFont" size="14" name="HelveticaNeue"/>
|
||||
<paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
|
||||
</attributes>
|
||||
</fragment>
|
||||
</attributedString>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" editable="NO" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="3bO-CM-14i">
|
||||
<rect key="frame" x="264" y="158" width="322" height="32"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="eCf-C3-Og4" customClass="RobloxImageView">
|
||||
<rect key="frame" x="20" y="47" width="200" height="200"/>
|
||||
</imageView>
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="M9R-xc-bfi" userLabel="Description View">
|
||||
<rect key="frame" x="0.0" y="251" width="600" height="261"/>
|
||||
<subviews>
|
||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" image="Separator" translatesAutoresizingMaskIntoConstraints="NO" id="zOa-FV-iY3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="600" height="1"/>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="_DESCRIPTION_TITLE_" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hsw-Sq-6I9">
|
||||
<rect key="frame" x="28" y="13" width="270" height="23"/>
|
||||
<fontDescription key="fontDescription" name="HelveticaNeue" family="Helvetica Neue" pointSize="17"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" fixedFrame="YES" editable="NO" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="KJ5-3Z-pid">
|
||||
<rect key="frame" x="28" y="51" width="552" height="202"/>
|
||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="0.96078431372549022" green="0.96078431372549022" blue="0.96078431372549022" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
|
||||
<nil key="simulatedStatusBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="193" y="377"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="Buy Button Robux" width="128" height="36"/>
|
||||
<image name="Buy Button Tix" width="128" height="36"/>
|
||||
<image name="Close Button" width="17" height="17"/>
|
||||
<image name="Icon Robux Off" width="28" height="28"/>
|
||||
<image name="Right Arrow pressed" width="12" height="21"/>
|
||||
<image name="Separator" width="491" height="1"/>
|
||||
<image name="Tix Icon" width="24" height="24"/>
|
||||
</resources>
|
||||
</document>
|
||||
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// RBFullFriendListScreenController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/23/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RobloxData.h"
|
||||
|
||||
@interface RBFullFriendListScreenController : UIViewController
|
||||
|
||||
typedef NS_ENUM(NSUInteger, SeeAllListType)
|
||||
{
|
||||
RBListTypeFriends = 0,
|
||||
RBListTypeFollowers = 1,
|
||||
RBListTypeFollowing = 2
|
||||
};
|
||||
|
||||
@property (strong, nonatomic) NSNumber* playerID;
|
||||
@property (strong, nonatomic) NSString* playerName;
|
||||
@property (nonatomic) SeeAllListType listTypeValue;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,341 @@
|
||||
//
|
||||
// RBFullFriendListScreenController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/23/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBFullFriendListScreenController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxData.h"
|
||||
#import "RBPlayerThumbnailCell.h"
|
||||
#import "RBProfileViewController.h"
|
||||
|
||||
#define FRIENDCELL_ID @"FriendCell"
|
||||
|
||||
#define FRIEND_AVATAR_SIZE CGSizeMake(110, 110)
|
||||
#define ITEM_SIZE CGSizeMake(90, 108)
|
||||
|
||||
#define FRIENDS_PER_REQUEST 60
|
||||
#define START_REQUEST_THRESHOLD 10 // The next request will start when there are 10 elements left
|
||||
|
||||
#define FOLLOWERS_PER_PAGE 50
|
||||
|
||||
@interface RBFullFriendListScreenController () <UICollectionViewDataSource, UICollectionViewDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBFullFriendListScreenController
|
||||
{
|
||||
NSMutableArray* _friends;
|
||||
|
||||
UICollectionView* _collectionView;
|
||||
|
||||
BOOL _requestInProgress;
|
||||
BOOL _friendsListComplete;
|
||||
NSUInteger _numItemsInCollectionView;
|
||||
}
|
||||
|
||||
-(id)init
|
||||
{
|
||||
_listTypeValue = RBListTypeFriends; //unless overwritten, this should be the default
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = [RobloxTheme lightBackground];
|
||||
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
|
||||
self.title = [self useRelevantTitle];
|
||||
|
||||
// Initialize the collection view
|
||||
UICollectionViewFlowLayout *flowLayout = [[UICollectionViewFlowLayout alloc] init];
|
||||
[flowLayout setItemSize:ITEM_SIZE];
|
||||
[flowLayout setScrollDirection:UICollectionViewScrollDirectionVertical];
|
||||
[flowLayout setMinimumLineSpacing:42.0f];
|
||||
[flowLayout setMinimumInteritemSpacing:6.0f];
|
||||
[flowLayout setSectionInset:UIEdgeInsetsMake(24, 35, 24, 35)];
|
||||
|
||||
_collectionView = [[UICollectionView alloc] initWithFrame:self.view.bounds collectionViewLayout:flowLayout];
|
||||
_collectionView.dataSource = self;
|
||||
_collectionView.delegate = self;
|
||||
_collectionView.backgroundColor = [UIColor clearColor];
|
||||
_collectionView.backgroundView = nil;
|
||||
[_collectionView registerNib:[UINib nibWithNibName:@"RBPlayerThumbnailCell" bundle:nil] forCellWithReuseIdentifier:FRIENDCELL_ID];
|
||||
|
||||
UIBarButtonItem* backButton = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStyleBordered target:nil action:nil];
|
||||
[self.navigationItem setBackBarButtonItem:backButton];
|
||||
|
||||
[self.view addSubview:_collectionView];
|
||||
|
||||
[self clearCollectionView];
|
||||
|
||||
[self fetchFriends];
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews
|
||||
{
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
[_collectionView setFrame:self.view.bounds];
|
||||
}
|
||||
|
||||
- (void) clearCollectionView
|
||||
{
|
||||
_requestInProgress = NO;
|
||||
_friendsListComplete = NO;
|
||||
_numItemsInCollectionView = 0;
|
||||
_friends = [NSMutableArray array];
|
||||
[_collectionView reloadData];
|
||||
}
|
||||
|
||||
- (NSString*) useRelevantTitle
|
||||
{
|
||||
NSString* aTitle = @"";
|
||||
|
||||
switch (_listTypeValue)
|
||||
{
|
||||
case RBListTypeFollowing: { aTitle = [NSString stringWithFormat:NSLocalizedString(@"FollowingTitleFormat", nil)]; }
|
||||
break;
|
||||
|
||||
case RBListTypeFollowers: { aTitle = [NSString stringWithFormat:NSLocalizedString(@"FollowersTitleFormat", nil), self.playerName]; }
|
||||
break;
|
||||
|
||||
default: { aTitle = [NSString stringWithFormat:NSLocalizedString(@"FriendsTitleFormat", nil), self.playerName]; }
|
||||
break;
|
||||
}
|
||||
return aTitle;
|
||||
}
|
||||
|
||||
//THIS IS A MISNOMER- THIS IS A CONTEXT SPECIFIC SEARCH
|
||||
//IT MIGHT FETCH FRIENDS BUT IT CAN ALSO FETCH OTHER THINGS LIKE FOLLOWERS AND FOLLOWING TOO
|
||||
- (void) fetchFriends
|
||||
{
|
||||
_friends = [NSMutableArray array];
|
||||
[_collectionView reloadData];
|
||||
|
||||
switch (_listTypeValue)
|
||||
{
|
||||
case RBListTypeFriends:
|
||||
{
|
||||
[RobloxData fetchUserFriends:self.playerID
|
||||
friendType:RBXFriendTypeAllFriends
|
||||
startIndex:0
|
||||
numItems:FRIENDS_PER_REQUEST
|
||||
avatarSize:FRIEND_AVATAR_SIZE
|
||||
completion:^(NSUInteger totalFriends, NSArray *friends)
|
||||
{
|
||||
if(friends)
|
||||
{
|
||||
[self initializeElementsWithArray:friends];
|
||||
}
|
||||
}];
|
||||
}
|
||||
break;
|
||||
case RBListTypeFollowing:
|
||||
{
|
||||
[RobloxData fetchMyFollowingAtPage:1
|
||||
avatarSize:FRIEND_AVATAR_SIZE
|
||||
withCompletion:^(bool success, NSArray *following) {
|
||||
if (following)
|
||||
[self initializeElementsWithArray:following];
|
||||
}];
|
||||
}
|
||||
break;
|
||||
case RBListTypeFollowers:
|
||||
{
|
||||
[RobloxData fetchFollowersForUser:_playerID
|
||||
atPage:1
|
||||
avatarSize:FRIEND_AVATAR_SIZE
|
||||
withCompletion:^(bool success, NSArray *followers) {
|
||||
if (followers)
|
||||
[self initializeElementsWithArray:followers];
|
||||
}];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) fetchMoreFriends
|
||||
{
|
||||
if(!_friendsListComplete && !_requestInProgress && _numItemsInCollectionView > _friends.count)
|
||||
{
|
||||
_requestInProgress = YES;
|
||||
|
||||
switch (_listTypeValue)
|
||||
{
|
||||
case RBListTypeFriends:
|
||||
{
|
||||
NSUInteger itemsToRequest = _numItemsInCollectionView - _friends.count;
|
||||
[RobloxData fetchUserFriends:self.playerID
|
||||
friendType:RBXFriendTypeAllFriends
|
||||
startIndex:_friends.count
|
||||
numItems:itemsToRequest
|
||||
avatarSize:FRIEND_AVATAR_SIZE
|
||||
completion:^(NSUInteger totalFriends, NSArray *friends) {
|
||||
[self updateVisualElementsWithArray:friends];
|
||||
}];
|
||||
}
|
||||
break;
|
||||
case RBListTypeFollowing:
|
||||
{
|
||||
int page = _friends.count / FOLLOWERS_PER_PAGE;
|
||||
[RobloxData fetchMyFollowingAtPage:page
|
||||
avatarSize:FRIEND_AVATAR_SIZE
|
||||
withCompletion:^(bool success, NSArray *following) {
|
||||
[self updateVisualElementsWithArray:following];
|
||||
}];
|
||||
}
|
||||
break;
|
||||
case RBListTypeFollowers:
|
||||
{
|
||||
int page = _friends.count / FOLLOWERS_PER_PAGE;
|
||||
[RobloxData fetchFollowersForUser:_playerID
|
||||
atPage:page
|
||||
avatarSize:FRIEND_AVATAR_SIZE
|
||||
withCompletion:^(bool success, NSArray *followers) {
|
||||
[self updateVisualElementsWithArray:followers];
|
||||
}];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void) initializeElementsWithArray:(NSArray*)friends
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
_numItemsInCollectionView = friends.count;
|
||||
[_friends addObjectsFromArray:friends];
|
||||
[_collectionView reloadData];
|
||||
});
|
||||
}
|
||||
-(void) updateVisualElementsWithArray:(NSArray*)friends
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
NSInteger rangeFrom = _friends.count;
|
||||
NSInteger rangeTo = rangeFrom + friends.count;
|
||||
|
||||
[_friends addObjectsFromArray:friends];
|
||||
|
||||
// Update visible elements
|
||||
NSArray* visibleCells = [_collectionView indexPathsForVisibleItems];
|
||||
NSMutableArray* cellsToUpdate = [NSMutableArray array];
|
||||
for(NSIndexPath* indexPath in visibleCells)
|
||||
{
|
||||
BOOL inRange = indexPath.row >= rangeFrom && indexPath.row < rangeTo;
|
||||
if(inRange)
|
||||
[cellsToUpdate addObject:indexPath];
|
||||
}
|
||||
[_collectionView reloadItemsAtIndexPaths:cellsToUpdate];
|
||||
|
||||
NSUInteger itemsToRequest = _numItemsInCollectionView - _friends.count;
|
||||
_friendsListComplete = friends.count < itemsToRequest;
|
||||
if(_friendsListComplete)
|
||||
{
|
||||
// If the list is already completed,
|
||||
// remove the remaining placeholder empty cells
|
||||
NSUInteger totalElements = _numItemsInCollectionView;
|
||||
|
||||
_numItemsInCollectionView = _friends.count;
|
||||
|
||||
[_collectionView performBatchUpdates:^
|
||||
{
|
||||
NSMutableArray* indexes = [NSMutableArray array];
|
||||
for(NSUInteger i = _friends.count; i < totalElements; ++i)
|
||||
{
|
||||
[indexes addObject:[NSIndexPath indexPathForRow:i inSection:0]];
|
||||
}
|
||||
[_collectionView deleteItemsAtIndexPaths:indexes];
|
||||
}
|
||||
completion:nil];
|
||||
}
|
||||
|
||||
_requestInProgress = NO;
|
||||
|
||||
[self fetchMoreFriends];
|
||||
});
|
||||
}
|
||||
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
|
||||
{
|
||||
return _numItemsInCollectionView;
|
||||
}
|
||||
|
||||
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
RBPlayerThumbnailCell *cell = [_collectionView dequeueReusableCellWithReuseIdentifier:FRIENDCELL_ID forIndexPath:indexPath];
|
||||
if(indexPath.row < _friends.count)
|
||||
{
|
||||
RBXFriendInfo* friendData = _friends[indexPath.row];
|
||||
if([friendData isKindOfClass:[NSNull class]])
|
||||
[cell setFriendInfo:nil];
|
||||
else
|
||||
[cell setFriendInfo:friendData];
|
||||
}
|
||||
else
|
||||
{
|
||||
[cell setFriendInfo:nil];
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
|
||||
{
|
||||
NSArray* indexes = [_collectionView indexPathsForVisibleItems];
|
||||
NSIndexPath* maxIndex = nil;
|
||||
for(NSIndexPath* index in indexes)
|
||||
{
|
||||
if(maxIndex == nil || maxIndex.row < index.row)
|
||||
{
|
||||
maxIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
if(_friendsListComplete == NO && maxIndex.row + START_REQUEST_THRESHOLD > _numItemsInCollectionView)
|
||||
{
|
||||
int totalAdded = (_listTypeValue == RBListTypeFriends) ? FRIENDS_PER_REQUEST : FOLLOWERS_PER_PAGE;
|
||||
_numItemsInCollectionView += totalAdded;
|
||||
|
||||
[_collectionView performBatchUpdates:^
|
||||
{
|
||||
NSMutableArray* indexes = [NSMutableArray array];
|
||||
for(NSUInteger i = _numItemsInCollectionView - totalAdded; i < _numItemsInCollectionView; ++i)
|
||||
{
|
||||
[indexes addObject:[NSIndexPath indexPathForRow:i inSection:0]];
|
||||
}
|
||||
[_collectionView insertItemsAtIndexPaths:indexes];
|
||||
}
|
||||
completion:nil];
|
||||
|
||||
[self fetchMoreFriends];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if(indexPath.row < _friends.count)
|
||||
{
|
||||
RBProfileViewController *viewController = (RBProfileViewController*) [self.navigationController.storyboard instantiateViewControllerWithIdentifier:@"RBOthersProfileViewController"];
|
||||
|
||||
RBXFriendInfo* friendInfo = _friends[indexPath.row];
|
||||
viewController.userId = friendInfo.userID;
|
||||
|
||||
[self.navigationController pushViewController:viewController animated:YES];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// RBGameViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 10/22/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "PlaceLauncher.h"
|
||||
|
||||
typedef void(^RBGameGameCompletionHandler)();
|
||||
|
||||
@interface RBGameViewController : UIViewController
|
||||
|
||||
@property RBXGameLaunchParams* launchParams;
|
||||
@property (nonatomic, copy) RBGameGameCompletionHandler completionHandler;
|
||||
|
||||
- (id) initWithLaunchParams:(RBXGameLaunchParams*)parameters;
|
||||
|
||||
+ (BOOL) isAppRunning;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// RBGameViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 10/22/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBGameViewController.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RobloxGoogleAnalytics.h"
|
||||
|
||||
@interface RBGameViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBGameViewController
|
||||
|
||||
- (id) initWithLaunchParams:(RBXGameLaunchParams *)parameters
|
||||
{
|
||||
self = [super init];
|
||||
if (self)
|
||||
_launchParams = parameters;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma ACCESSORS
|
||||
#pragma mark
|
||||
+ (BOOL) isAppRunning
|
||||
{
|
||||
return [[PlaceLauncher sharedInstance] appActive];
|
||||
}
|
||||
|
||||
|
||||
#pragma VIEW FUNCTIONS
|
||||
#pragma mark
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.frame = [UIScreen mainScreen].bounds;
|
||||
self.view.backgroundColor = [UIColor clearColor];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didLeaveGame:) name:RBX_NOTIFY_GAME_DID_LEAVE object:nil];
|
||||
|
||||
[self launchGame];
|
||||
}
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)didLeaveGame:(NSNotification*)notification
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self dismissViewControllerAnimated:NO
|
||||
completion:^
|
||||
{
|
||||
if(_completionHandler)
|
||||
{
|
||||
_completionHandler();
|
||||
}
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
#pragma GAME FUNCTIONS
|
||||
#pragma mark
|
||||
-(void)launchGame
|
||||
{
|
||||
if( !hasMinMemory(512) )
|
||||
{
|
||||
[RobloxHUD showMessage:NSLocalizedString(@"UnsupportedDevicePlayError", nil)];
|
||||
return;
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RBX_NOTIFY_GAME_JOINED object:nil];
|
||||
[[NSUserDefaults standardUserDefaults] setObject:@"tryGameJoin" forKey:@"RobloxGameState"];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
|
||||
[RobloxHUD showSpinnerWithLabel:NSLocalizedString(@"LaunchGame", nil) dimBackground:YES];
|
||||
|
||||
// Delay execution of the start block to display the spinner properly
|
||||
// TODO: Start the game in the place launcher in a separate thread (not UI)
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.25f * NSEC_PER_SEC), dispatch_get_main_queue(), ^
|
||||
{
|
||||
[[PlaceLauncher sharedInstance] startGame:_launchParams controller:self presentGameAutomatically:YES];
|
||||
});
|
||||
|
||||
[RobloxGoogleAnalytics setPageViewTracking:@"Visit/Try/Join"];
|
||||
}
|
||||
|
||||
-(void) handleStartGameFailure
|
||||
{
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
}
|
||||
|
||||
-(void) handleStartGameSuccess
|
||||
{
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// RBGroupsMasterController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/3/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RBMobileWebViewController.h"
|
||||
|
||||
@interface RBGroupsScreenController : RBMobileWebViewController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// RBGroupsMasterController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/3/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBGroupsScreenController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "Flurry.h"
|
||||
|
||||
#define GS_openRobux @"GROUPS SCREEN - Open Robux"
|
||||
#define GS_openBuildersClub @"GROUPS SCREEN - Open Builders Club"
|
||||
#define GS_openSettings @"GROUPS SCREEN - Open Settings"
|
||||
#define GS_openLogout @"GROUPS SCREEN - Open Logout"
|
||||
#define GS_groupSearch @"GROUPS SCREEN - Group Search"
|
||||
#define GS_groupPageLink @"GROUPS SCREEN - Group Page Link"
|
||||
#define GS_openExternalLink @"GROUPS SCREEN - Open External Link"
|
||||
#define GS_openProfile @"GROUPS SCREEN - Open Profile"
|
||||
#define GS_openGameDetail @"GROUPS SCREEN - Open Game Detail"
|
||||
#define GS_launchGame @"GROUPS SCREEN - Launch Game"
|
||||
|
||||
@interface RBGroupsScreenController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBGroupsScreenController
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.navigationItem.title = NSLocalizedString(@"GroupsWord", nil);
|
||||
|
||||
BOOL tablet = [RobloxInfo thisDeviceIsATablet];
|
||||
self.url = [[RobloxInfo getBaseUrl] stringByAppendingString:(tablet == YES) ? @"My/Groups.aspx" : @"my-groups"];
|
||||
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
[self addRobuxIconWithFlurryEvent:GS_openRobux
|
||||
andBCIconWithFlurryEvent:GS_openBuildersClub];
|
||||
|
||||
[self setFlurryPageLoadEvent:GS_groupPageLink];
|
||||
[self setFlurryGameLaunchEvent:GS_launchGame];
|
||||
[self setFlurryEventsForExternalLinkEvent:GS_openExternalLink
|
||||
andWebViewEvent:nil
|
||||
andOpenProfileEvent:GS_openProfile
|
||||
andOpenGameDetailEvent:GS_openGameDetail];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// RBHomeScreenController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 12/1/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBMobileWebViewController.h"
|
||||
|
||||
@interface RBHomeScreenController : RBMobileWebViewController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// RBHomeScreenController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 12/1/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBHomeScreenController.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RobloxTheme.h"
|
||||
|
||||
#define HS_openRobux @"HOME SCREEN - Open Robux"
|
||||
#define HS_openBuildersClub @"HOME SCREEN - Open Builders Club"
|
||||
#define HS_openSettings @"HOME SCREEN - Open Settings"
|
||||
#define HS_openLogout @"HOME SCREEN - Open Logout"
|
||||
#define HS_homePageLink @"HOME SCREEN - Home Page Link"
|
||||
#define HS_openExternalLink @"HOME SCREEN - Open External Link"
|
||||
#define HS_openProfile @"HOME SCREEN - Open Profile"
|
||||
#define HS_openGameDetail @"HOME SCREEN - Open Game Detail"
|
||||
#define HS_launchGame @"HOME SCREEN - Launch Game"
|
||||
|
||||
@interface RBHomeScreenController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBHomeScreenController
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.navigationItem.title = NSLocalizedString(@"HomeWord", nil);
|
||||
|
||||
self.url = [[RobloxInfo getWWWBaseUrl] stringByAppendingString:@"home"];
|
||||
|
||||
[self setViewTheme:RBXThemeSocial];
|
||||
|
||||
//if ([RobloxInfo thisDeviceIsATablet])
|
||||
[self addRobuxIconWithFlurryEvent:HS_openRobux
|
||||
andBCIconWithFlurryEvent:HS_openBuildersClub];
|
||||
|
||||
[self setFlurryGameLaunchEvent:HS_launchGame];
|
||||
[self setFlurryPageLoadEvent:HS_homePageLink];
|
||||
[self setFlurryEventsForExternalLinkEvent:HS_openExternalLink
|
||||
andWebViewEvent:nil
|
||||
andOpenProfileEvent:HS_openProfile
|
||||
andOpenGameDetailEvent:HS_openGameDetail];
|
||||
|
||||
[self addSearchIconWithSearchType:SearchResultUsers andFlurryEvent:nil];
|
||||
|
||||
|
||||
}
|
||||
|
||||
- (void) viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[self setViewTheme:RBXThemeSocial];
|
||||
[super viewWillAppear:animated];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// RBInventoryMasterController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/3/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RBMobileWebViewController.h"
|
||||
|
||||
@interface RBInventoryScreenController : RBMobileWebViewController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// RBInventoryMasterController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/3/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBInventoryScreenController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "UserInfo.h"
|
||||
|
||||
#define ISC_openRobux @"INVENTORY SCREEN - Open Robux"
|
||||
#define ISC_openBuildersClub @"INVENTORY SCREEN - Open Builders Club"
|
||||
#define ISC_openSettings @"INVENTORY SCREEN - Open Settings"
|
||||
#define ISC_openLogout @"INVENTORY SCREEN - Open Logout"
|
||||
#define ISC_inventoryPageLink @"INVENTORY SCREEN - Inventory Page Link"
|
||||
#define ISC_openExternalLink @"INVENTORY SCREEN - Open External Link"
|
||||
#define ISC_openProfile @"INVENTORY SCREEN - Open Profile"
|
||||
#define ISC_openGameDetail @"INVENTORY SCREEN - Open Game Detail"
|
||||
#define ISC_launchGame @"INVENTORY SCREEN - Launch Game"
|
||||
|
||||
@interface RBInventoryScreenController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBInventoryScreenController
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
self.navigationItem.title = NSLocalizedString(@"InventoryWord", nil);
|
||||
|
||||
self.url = [NSString stringWithFormat:@"%@users/%@/inventory#!/hats", [RobloxInfo getWWWBaseUrl], [UserInfo CurrentPlayer].userId];
|
||||
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
[self addRobuxIconWithFlurryEvent:ISC_openRobux
|
||||
andBCIconWithFlurryEvent:ISC_openBuildersClub];
|
||||
|
||||
[self setFlurryPageLoadEvent:ISC_inventoryPageLink];
|
||||
[self setFlurryGameLaunchEvent:ISC_launchGame];
|
||||
[self setFlurryEventsForExternalLinkEvent:ISC_openExternalLink
|
||||
andWebViewEvent:nil
|
||||
andOpenProfileEvent:ISC_openProfile
|
||||
andOpenGameDetailEvent:ISC_openGameDetail];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// RBLogoutViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 12/16/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBModalPopUpViewController.h"
|
||||
|
||||
@interface RBLogoutViewController : RBModalPopUpViewController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,106 @@
|
||||
//
|
||||
// RBLogoutViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 12/16/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBLogoutViewController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "LoginManager.h"
|
||||
#import "ABTestManager.h"
|
||||
#import "RBXEventReporter.h"
|
||||
#import "UserInfo.h"
|
||||
|
||||
@interface RBLogoutViewController ()
|
||||
@property IBOutlet UILabel *lblLogOutBody;
|
||||
@property IBOutlet UIButton *btnCancel;
|
||||
@property IBOutlet UIButton *btnLogOut;
|
||||
@property IBOutlet UIImageView* imgFrown;
|
||||
@property IBOutlet UIView* whiteView;
|
||||
@property IBOutlet UILabel* lblTitle;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBLogoutViewController
|
||||
|
||||
-(void)viewDidLoad
|
||||
{
|
||||
//configure the modal popup superclass
|
||||
[self shouldAddCloseButton:NO];
|
||||
[self shouldApplyModalTheme:NO];
|
||||
[super viewDidLoad];
|
||||
|
||||
//NSString* titleText = NSLocalizedString(@"LogOutTitle", nil);
|
||||
[_lblTitle setText:NSLocalizedString(@"LogoutWord", nil)];
|
||||
if (![RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
[_whiteView.layer setShadowColor:[UIColor blackColor].CGColor];
|
||||
[_whiteView.layer setShadowOpacity:0.4];
|
||||
[_whiteView.layer setShadowRadius:2.0];
|
||||
[_whiteView.layer setShadowOffset:CGSizeMake(0.0, 0.5)];
|
||||
|
||||
_lblLogOutBody.font = [RobloxTheme fontH4];
|
||||
}
|
||||
else
|
||||
{
|
||||
_lblLogOutBody.font = [UIFont fontWithName:@"SourceSansPro-Regular" size:28];
|
||||
}
|
||||
|
||||
|
||||
|
||||
_lblLogOutBody.text = NSLocalizedString(@"LogOutBody", nil);
|
||||
|
||||
|
||||
[_btnCancel setTitle:NSLocalizedString(@"CancelWord", nil) forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToModalCancelButton:_btnCancel];
|
||||
|
||||
[_btnLogOut setTitle:NSLocalizedString(@"LogOutTitle", nil) forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToModalSubmitButton:_btnLogOut];
|
||||
|
||||
[_imgFrown.layer setShadowColor:[UIColor blackColor].CGColor];
|
||||
[_imgFrown.layer setShadowOpacity:0.2f];
|
||||
[_imgFrown.layer setShadowOffset:CGSizeMake(-1.0, 0.5)];
|
||||
}
|
||||
-(void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportScreenLoaded:RBXAContextLogout];
|
||||
}
|
||||
|
||||
- (IBAction)onTouchUpInsideCancel:(id)sender {
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonClose
|
||||
withContext:RBXAContextLogout];
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
- (IBAction)onTouchUpInsideLogout:(id)sender {
|
||||
[[UserInfo CurrentPlayer] setUserLoggedIn:NO];
|
||||
[[LoginManager sharedInstance] logoutRobloxUser];
|
||||
|
||||
if ([RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
UINavigationController* pc = (UINavigationController*)self.presentingViewController;
|
||||
[self.navigationController dismissViewControllerAnimated:YES completion:^
|
||||
{
|
||||
if (![[ABTestManager sharedInstance] IsInTestMobileGuestMode])
|
||||
[pc popToViewController:pc.viewControllers[1] animated:YES];
|
||||
|
||||
//the home screen controller will handle the log out notification
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
if (![[ABTestManager sharedInstance] IsInTestMobileGuestMode])
|
||||
{
|
||||
UINavigationController* baseNavigation = self.tabBarController.navigationController;
|
||||
[baseNavigation popToViewController:baseNavigation.viewControllers[1] animated:YES];
|
||||
}
|
||||
else
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// RBMainNavigationController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 2/25/15.
|
||||
// Copyright (c) 2015 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface RBMainNavigationController : UINavigationController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// RBMainNavigationController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 2/25/15.
|
||||
// Copyright (c) 2015 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBMainNavigationController.h"
|
||||
#import "PlaceLauncher.h"
|
||||
#import "RobloxNotifications.h"
|
||||
|
||||
@interface RBMainNavigationController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBMainNavigationController
|
||||
{
|
||||
UIInterfaceOrientationMask phoneMask;
|
||||
}
|
||||
|
||||
|
||||
-(id) initWithCoder:(nonnull NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
phoneMask = UIInterfaceOrientationMaskPortrait;
|
||||
|
||||
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(updateRotatePermission:)
|
||||
name:RBX_NOTIFY_GAME_FINISHED_LOADING
|
||||
object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(updateRotatePermission:)
|
||||
name:RBX_NOTIFY_GAME_DID_LEAVE
|
||||
object:nil];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
-(void) dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
-(void) updateRotatePermission:(NSNotification*)notification
|
||||
{
|
||||
//in iOS 9, the app does not rotate the keyboard properly when getting into game
|
||||
//so when leaving game, we need to reset to the app defaults
|
||||
phoneMask = ([notification.name isEqualToString:RBX_NOTIFY_GAME_FINISHED_LOADING]) ? UIInterfaceOrientationMaskLandscape : UIInterfaceOrientationMaskPortrait;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[UIViewController attemptRotationToDeviceOrientation];
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
-(BOOL)shouldAutorotate
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
#ifdef __IPHONE_9_0
|
||||
-(UIInterfaceOrientationMask) supportedInterfaceOrientations
|
||||
#else
|
||||
-(NSUInteger)supportedInterfaceOrientations
|
||||
#endif
|
||||
{
|
||||
return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) ? phoneMask
|
||||
: UIInterfaceOrientationMaskLandscape;
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// RBMessageComposeScreenController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by alichtin on 9/18/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RobloxData.h"
|
||||
|
||||
@interface RBMessageComposeScreenController : UIViewController
|
||||
|
||||
- (void) replyToMessage:(RBXMessageInfo*)message;
|
||||
|
||||
- (void) sendMessageToUser:(NSNumber*)userID name:(NSString*)userName;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,216 @@
|
||||
//
|
||||
// RBMessageComposeScreenController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by alichtin on 9/18/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBMessageComposeScreenController.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxData.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "UIAlertView+Blocks.h"
|
||||
#import "RobloxImageView.h"
|
||||
#import "RBXEventReporter.h"
|
||||
|
||||
#define DEFAULT_VIEW_SIZE CGRectMake(0, 0, 510.f, 315.f)
|
||||
#define NAV_BAR_HEIGHT 46.0
|
||||
#define AVATAR_SIZE CGSizeMake(110, 110)
|
||||
|
||||
@interface RBMessageComposeScreenController () <UIWebViewDelegate, UIScrollViewDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBMessageComposeScreenController
|
||||
{
|
||||
RBXMessageInfo* _replyToMessage;
|
||||
NSNumber* _recipientID;
|
||||
NSString* _recipientName;
|
||||
|
||||
IBOutlet UINavigationBar* _navBar;
|
||||
IBOutlet UITextView *_messageTextView;
|
||||
IBOutlet UIWebView *_conversationBody;
|
||||
IBOutlet RobloxImageView *_friendAvatar;
|
||||
IBOutlet UIView *_conversationView;
|
||||
|
||||
NSString* analyticsResponseType;
|
||||
}
|
||||
|
||||
- (void) replyToMessage:(RBXMessageInfo*)message;
|
||||
{
|
||||
_replyToMessage = message;
|
||||
_recipientID = message.senderUserID;
|
||||
_recipientName = message.senderUsername;
|
||||
|
||||
analyticsResponseType = @"reply";
|
||||
}
|
||||
|
||||
- (void) sendMessageToUser:(NSNumber*)userID name:(NSString*)userName
|
||||
{
|
||||
_recipientID = userID;
|
||||
_recipientName = userName;
|
||||
|
||||
analyticsResponseType = @"sendNew";
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
// Stylize items
|
||||
[RobloxTheme applyToModalPopupNavBar:_navBar];
|
||||
|
||||
// ReplyTo setup
|
||||
if(_replyToMessage != nil)
|
||||
{
|
||||
[_friendAvatar loadAvatarForUserID:[_replyToMessage.senderUserID integerValue] withSize:AVATAR_SIZE completion:nil];
|
||||
|
||||
NSString* htmlMessage = [NSString stringWithFormat:@"<div style='border-radius: 5px; background-color:#bee7ff; padding:10px; padding-top:0px;'>"
|
||||
@" <p>"
|
||||
@" <span style='font-family: SourceSansPro-Semibold; font-size: 12; color: #474747'>%@</span>"
|
||||
@" <span style='font-family: SourceSansPro-Regular; font-size: 12; color: #808080'>%@ %@</span>"
|
||||
@" </p>"
|
||||
@" <p style='font-family: SourceSansPro-Regular; font-size: 14; color: #343434'>"
|
||||
@" %@"
|
||||
@" </p>"
|
||||
@"</div>",
|
||||
_replyToMessage.senderUsername,
|
||||
NSLocalizedString(@"WroteAtPhrase", nil),
|
||||
_replyToMessage.date,
|
||||
_replyToMessage.body];
|
||||
[_conversationBody loadHTMLString:htmlMessage baseURL:nil];
|
||||
_conversationBody.delegate = self;
|
||||
_conversationBody.scrollView.delegate = self;
|
||||
|
||||
_navBar.topItem.title = [NSString stringWithFormat:NSLocalizedString(@"ReplyToPhrase", nil), _recipientName];
|
||||
}
|
||||
else
|
||||
{
|
||||
_conversationView.hidden = YES;
|
||||
_messageTextView.frame = DEFAULT_VIEW_SIZE;
|
||||
_messageTextView.y = NAV_BAR_HEIGHT;
|
||||
_messageTextView.height = _messageTextView.height - NAV_BAR_HEIGHT;
|
||||
|
||||
_navBar.topItem.title = [NSString stringWithFormat:NSLocalizedString(@"MessageToPhrase", nil), _recipientName];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
//due to an issue in timing, it is best to wait for the presentation to finish before
|
||||
//setting the text to become the first responder, so just hold on a tic
|
||||
[_messageTextView performSelector:@selector(becomeFirstResponder) withObject:nil afterDelay:0.25f];
|
||||
}
|
||||
|
||||
- (void)viewWillLayoutSubviews
|
||||
{
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
if (SYSTEM_VERSION_LESS_THAN(@"8"))
|
||||
{
|
||||
self.view.superview.bounds = DEFAULT_VIEW_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews
|
||||
{
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
if(_replyToMessage == nil)
|
||||
{
|
||||
_messageTextView.frame = CGRectUnion(_messageTextView.frame, _conversationView.frame);
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)closeButtonTouched:(id) sender
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonClose withContext:RBXAContextDraftMessage withCustomDataString:analyticsResponseType];
|
||||
|
||||
if ([_messageTextView.text length] > 0)
|
||||
{
|
||||
//warn the user before the close
|
||||
UIAlertView* aWarning = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"MessageReplyConfirmExitTitle", nil)
|
||||
message:NSLocalizedString(@"MessageReplyConfirmExitMessage", nil)
|
||||
delegate:nil
|
||||
cancelButtonTitle:NSLocalizedString(@"CancelWord", nil)
|
||||
otherButtonTitles:NSLocalizedString(@"MessageReplyConfirmExitWord", nil), nil];
|
||||
|
||||
[aWarning showWithCompletion:^(UIAlertView *alertView, NSInteger buttonIndex)
|
||||
{
|
||||
switch (buttonIndex)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)sendButtonTouched:(id)sender
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonSubmit withContext:RBXAContextDraftMessage withCustomDataString:analyticsResponseType];
|
||||
[RobloxHUD showSpinnerWithLabel:@"" dimBackground:YES];
|
||||
|
||||
[_messageTextView resignFirstResponder];
|
||||
|
||||
if(_replyToMessage != nil)
|
||||
{
|
||||
[RobloxData sendMessage:_messageTextView.text
|
||||
subject:_replyToMessage.subject
|
||||
recipientID:_recipientID
|
||||
replyMessageID:[NSNumber numberWithInteger:_replyToMessage.messageID]
|
||||
completion:^(BOOL success, NSString* errorMessage)
|
||||
{
|
||||
[self onSendMessageComplete:success errorMessage:errorMessage];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxData sendMessage:_messageTextView.text
|
||||
subject:@"Message"
|
||||
recipientID:_recipientID
|
||||
replyMessageID:nil
|
||||
completion:^(BOOL success, NSString* errorMessage)
|
||||
{
|
||||
[self onSendMessageComplete:success errorMessage:errorMessage];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)onSendMessageComplete:(BOOL)success errorMessage:(NSString*)errorMessage
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
|
||||
if(success)
|
||||
{
|
||||
[RobloxHUD showMessage:NSLocalizedString(@"MessageSentPhrase", nil)];
|
||||
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1.5f * NSEC_PER_SEC), dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxHUD showMessage:errorMessage];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// RBMobileWebViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Christian Hresko on 6/23/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RBBaseViewController.h"
|
||||
#import "RBXWebViewProtocol.h"
|
||||
#import "RBHybridWebViewProtocol.h"
|
||||
|
||||
@interface RBMobileWebViewController : RBBaseViewController
|
||||
|
||||
@property (nonatomic, strong) UIView <RBXWebViewProtocol, RBHybridWebViewProtocol> *rbxWebView;
|
||||
@property(strong, nonatomic) NSString* url;
|
||||
@property(nonatomic) BOOL showNavButtons;
|
||||
@property(nonatomic) BOOL isPopover;
|
||||
@property(nonatomic) int placeID;
|
||||
|
||||
- (id) initWithNavButtons:(BOOL)showButtons;
|
||||
- (void) loadURL:(NSString*)url;
|
||||
- (void) loadURL:(NSString*)url screenURL:(bool)shouldFilter;
|
||||
- (void) reloadWebPage;
|
||||
- (void) setToShowWholeScreen:(BOOL)showAll;
|
||||
|
||||
//flurry events
|
||||
- (void) setFlurryGameLaunchEvent:(NSString*)gameLaunchEvent;
|
||||
- (void) setFlurryPageLoadEvent:(NSString*)pageLaunchEvent;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,663 @@
|
||||
//
|
||||
// RBMobileWebViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Christian Hresko on 6/23/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBMobileWebViewController.h"
|
||||
|
||||
#import "Flurry.h"
|
||||
#import "MBProgressHUD.h"
|
||||
#import "NativeSearchNavItem.h"
|
||||
#import "NSDictionary+Parsing.h"
|
||||
#import "RBGameViewController.h"
|
||||
#import "RBHybridBridge.h"
|
||||
#import "RBHybridWebView.h"
|
||||
#import "RBHybridWKWebView.h"
|
||||
#import "RBXFunctions.h"
|
||||
#import "RobloxAlert.h"
|
||||
#import "RobloxGoogleAnalytics.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "StoreManager.h"
|
||||
#import "UIViewController+Helpers.h"
|
||||
#import "UIView+Position.h"
|
||||
|
||||
DYNAMIC_FASTFLAGVARIABLE(EnableWebKit, false);
|
||||
|
||||
@interface RBMobileWebViewController () <UIWebViewDelegate, UIAlertViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler>
|
||||
|
||||
@property (nonatomic, strong) MBProgressHUD *spinner;
|
||||
@property (nonatomic, strong) UISearchBar *searchBar;
|
||||
@property (nonatomic, strong) UIButton *backButton;
|
||||
@property (nonatomic, strong) UIButton *fwdButton;
|
||||
@property (nonatomic) BOOL showWholeScreen;
|
||||
|
||||
@property (nonatomic, strong) NSString *flurryGameLaunchEvent;
|
||||
@property (nonatomic, strong) NSString *flurryPageEvent;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBMobileWebViewController
|
||||
|
||||
- (void) initDefaults
|
||||
{
|
||||
if (self)
|
||||
{
|
||||
self.showNavButtons = YES;
|
||||
self.isPopover = NO;
|
||||
}
|
||||
}
|
||||
- (id) init
|
||||
{
|
||||
self = [super init];
|
||||
[self initDefaults];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id) initWithNavButtons:(BOOL)showButtons
|
||||
{
|
||||
self = [self init];
|
||||
if (self)
|
||||
{
|
||||
self.showNavButtons = showButtons;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
[self initDefaults];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
|
||||
{
|
||||
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
|
||||
[self initDefaults];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) initializeViews
|
||||
{
|
||||
if ([RBXFunctions isEmpty:self.rbxWebView])
|
||||
{
|
||||
if ([self isWebkitEnabled] && NSClassFromString(@"WKWebView")) {
|
||||
WKWebViewConfiguration *webviewConfig = [[WKWebViewConfiguration alloc]init];
|
||||
WKUserContentController *userController = [[WKUserContentController alloc]init];
|
||||
|
||||
/*
|
||||
The name of the script message handler "RobloxWKHybrid" is very intentional. It must match the value in the file
|
||||
RobloxHybrid\src\common\bridge.ios.js on the line "window.webkit.messageHandlers.RobloxWKHybrid.postMessage"
|
||||
*/
|
||||
[userController addScriptMessageHandler:self name:@"RobloxWKHybrid"];
|
||||
webviewConfig.userContentController = userController;
|
||||
|
||||
self.rbxWebView = [[RBHybridWKWebView alloc] initWithFrame:CGRectZero configuration:webviewConfig];
|
||||
|
||||
} else {
|
||||
self.rbxWebView = [[RBHybridWebView alloc] init];
|
||||
}
|
||||
|
||||
[self.view addSubview:self.rbxWebView];
|
||||
self.rbxWebView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
self.rbxWebView.multipleTouchEnabled = NO;
|
||||
self.rbxWebView.contentMode = UIViewContentModeScaleAspectFit;
|
||||
self.rbxWebView.controller = self;
|
||||
[self.rbxWebView setDelegateController:self];
|
||||
[[RBHybridBridge sharedInstance] registerWebView:self.rbxWebView];
|
||||
}
|
||||
|
||||
if ([RBXFunctions isEmpty:self.spinner])
|
||||
{
|
||||
self.spinner = [[MBProgressHUD alloc] initWithView:self.rbxWebView];
|
||||
self.spinner.clipsToBounds = NO;
|
||||
self.spinner.removeFromSuperViewOnHide = NO;
|
||||
self.spinner.dimBackground = NO;
|
||||
}
|
||||
|
||||
if ([RBXFunctions isEmpty:self.backButton])
|
||||
{
|
||||
self.backButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 12.0, 21.0)];
|
||||
[self.backButton addTarget:self action:@selector(didPressBack) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.backButton setImage:[UIImage imageNamed:@"Left Arrow Normal"] forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
if ([RBXFunctions isEmpty:self.fwdButton])
|
||||
{
|
||||
self.fwdButton = [[UIButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 12.0, 21.0)];
|
||||
[self.fwdButton addTarget:self action:@selector(didPressForward) forControlEvents:UIControlEventTouchUpInside];
|
||||
[self.fwdButton setImage:[UIImage imageNamed:@"Right Arrow Normal"] forState:UIControlStateNormal];
|
||||
}
|
||||
|
||||
self.showWholeScreen = NO;
|
||||
}
|
||||
|
||||
- (void)didPressBack
|
||||
{
|
||||
if (self.rbxWebView.canGoBackInHistory)
|
||||
{
|
||||
[self.rbxWebView goBackInHistory];
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOL isPushedController = self.navigationController.viewControllers.count > 1;
|
||||
if (isPushedController)
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)didPressForward
|
||||
{
|
||||
if (self.rbxWebView.canGoForwardInHistory) {
|
||||
[self.rbxWebView goForwardInHistory];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewWillLayoutSubviews
|
||||
{
|
||||
[super viewWillLayoutSubviews];
|
||||
[self.spinner centerInFrame:self.rbxWebView.frame];
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[self initializeViews];
|
||||
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
|
||||
[RobloxTheme applyToGamesNavBar:self.navigationController.navigationBar];
|
||||
|
||||
[self.rbxWebView setFrame:self.view.bounds];
|
||||
|
||||
[self.spinner setFrame:self.rbxWebView.frame];
|
||||
[self.rbxWebView addSubview:self.spinner];
|
||||
[self.spinner hide:NO];
|
||||
|
||||
if(self.showNavButtons)
|
||||
{
|
||||
UIBarButtonItem* space = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
|
||||
[space setWidth:44.0];
|
||||
|
||||
self.navigationItem.leftBarButtonItems = @[[[UIBarButtonItem alloc] initWithCustomView:self.backButton],
|
||||
space,
|
||||
[[UIBarButtonItem alloc] initWithCustomView:self.fwdButton]];
|
||||
}
|
||||
else if(self.isPopover)
|
||||
{
|
||||
UIImage* closeImage = [UIImage imageNamed:@"Close Button"];
|
||||
UIButton* closeButton = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, closeImage.size.width, closeImage.size.height)];
|
||||
[closeButton addTarget:self action:@selector(didPressClose) forControlEvents:UIControlEventTouchUpInside];
|
||||
[closeButton setImage:closeImage forState:UIControlStateNormal];
|
||||
|
||||
self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:closeButton];
|
||||
}
|
||||
|
||||
[self reloadWebPage];
|
||||
}
|
||||
|
||||
- (void)didPressClose
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void) viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
// 2015.0928 ajain - This is a fail-safe so that just in case the webview is deallocated in the viewWillDisappear method
|
||||
// on its way to displaying the in-game view, then we re-create the webview.
|
||||
if ([RBXFunctions isEmpty:self.rbxWebView]) {
|
||||
[self initializeViews];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated
|
||||
{
|
||||
[super viewDidAppear:animated];
|
||||
|
||||
[self updateNavButtonsState];
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
NSLog(@"RBMobileWebViewController dealloc");
|
||||
}
|
||||
|
||||
- (void) setUrl:(NSString *)url
|
||||
{
|
||||
_url = url;
|
||||
|
||||
[self loadURL:self.url];
|
||||
}
|
||||
- (void) loadURL:(NSString*)url
|
||||
{
|
||||
[self loadURL:url screenURL:YES];
|
||||
}
|
||||
|
||||
- (void) loadURL:(NSString *)url screenURL:(bool)shouldFilter
|
||||
{
|
||||
//a one-off function for exposing access to the webview
|
||||
if (![RBXFunctions isEmptyString:url] && ![RBXFunctions isEmpty:self.rbxWebView])
|
||||
{
|
||||
NSURL* urlFromString = [NSURL URLWithString:url];
|
||||
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:urlFromString];
|
||||
[RobloxInfo setDefaultHTTPHeadersForRequest:request];
|
||||
|
||||
if (shouldFilter)
|
||||
{
|
||||
//screen all urls that the webview has been told to load
|
||||
RBXWebRequestReturnStatus requestStatus = [self handleWebRequest:urlFromString];
|
||||
switch (requestStatus)
|
||||
{
|
||||
case RBXWebRequestReturnFilter: { return; } break;
|
||||
case RBXWebRequestReturnScreenPush: { return; } break;
|
||||
case RBXWebRequestReturnUnknown: { return; } break;
|
||||
case RBXWebRequestReturnWebRequest: { [self.rbxWebView loadRequest:request]; } break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[self.rbxWebView loadRequest:request];
|
||||
}
|
||||
}
|
||||
}
|
||||
- (void) reloadWebPage
|
||||
{
|
||||
[self loadURL:self.url];
|
||||
}
|
||||
|
||||
//Accessors
|
||||
- (bool) isWebkitEnabled
|
||||
{
|
||||
return DFFlag::EnableWebKit;
|
||||
}
|
||||
|
||||
//Mutators
|
||||
- (void) setFlurryGameLaunchEvent:(NSString*)gameLaunchEvent
|
||||
{
|
||||
_flurryGameLaunchEvent = gameLaunchEvent;
|
||||
}
|
||||
- (void) setFlurryPageLoadEvent:(NSString*)pageLaunchEvent
|
||||
{
|
||||
_flurryPageEvent = pageLaunchEvent;
|
||||
}
|
||||
|
||||
#pragma mark - Common UIWebView and WKWebView methods
|
||||
|
||||
- (void) setToShowWholeScreen:(BOOL)showAll
|
||||
{
|
||||
_showWholeScreen = showAll;
|
||||
[self.rbxWebView enableScrolling:YES];
|
||||
[self.rbxWebView enableMultipleTouch:YES];
|
||||
}
|
||||
|
||||
- (BOOL) shouldStartNavigationActionWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
|
||||
{
|
||||
[self updateNavButtonsState];
|
||||
|
||||
id storeManager = GetStoreMgr;
|
||||
if ([storeManager isKindOfClass:[StoreManager class]])
|
||||
{
|
||||
if([storeManager checkForInAppPurchases:request navigationType:navigationType])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
if([self checkForGameLaunch:request])
|
||||
return NO;
|
||||
|
||||
//Screen all clicked links to ensure that they are safe to be displayed
|
||||
if (navigationType == UIWebViewNavigationTypeLinkClicked)
|
||||
{
|
||||
RBXWebRequestReturnStatus requestStatus = [self handleWebRequest:request.URL];
|
||||
switch (requestStatus)
|
||||
{
|
||||
case (RBXWebRequestReturnWebRequest):
|
||||
{
|
||||
//looks like a regular Roblox URL
|
||||
return YES;
|
||||
} break;
|
||||
case (RBXWebRequestReturnScreenPush):
|
||||
{
|
||||
//the url has been handled by another view
|
||||
return NO;
|
||||
} break;
|
||||
case (RBXWebRequestReturnUnknown):
|
||||
{
|
||||
//not quite sure what domain we've hit here
|
||||
[[UIApplication sharedApplication] openURL:request.URL];
|
||||
return NO;
|
||||
} break;
|
||||
case (RBXWebRequestReturnFilter):
|
||||
{
|
||||
return NO;
|
||||
}break;
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
//Game Launcher Stuff
|
||||
-(BOOL) checkForGameLaunch:(NSURLRequest *)request
|
||||
{
|
||||
NSURL *url = request.URL;
|
||||
NSString *requestUrlString = url.absoluteString;
|
||||
|
||||
// check that we are trying to launch a game
|
||||
if ( [requestUrlString rangeOfString:@"/games/start?"].location == NSNotFound
|
||||
&& [requestUrlString rangeOfString:@"robloxmobile://placeID="].location == NSNotFound)
|
||||
return NO;
|
||||
|
||||
NSArray* components = [requestUrlString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"=&?/"]];
|
||||
NSString* placeId;
|
||||
RBXGameLaunchParams* params;
|
||||
|
||||
// Parse out our args and launch a game if we find either placeid or userID
|
||||
for (int i = 0; i < components.count; i++)
|
||||
{
|
||||
NSString* component = [components objectAtIndex:i];
|
||||
component = [component lowercaseString];
|
||||
|
||||
if ([component isEqualToString:@"placeid"])
|
||||
{
|
||||
placeId = [components objectAtIndex:(i+1)];
|
||||
if (params)
|
||||
{
|
||||
//if params is already initialized, it means that for some reason,
|
||||
// the placeid component was not the first parameter parsed.
|
||||
//So, simply update the placeholder targetId with the real one.
|
||||
params.targetId = placeId.integerValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
//if params is not initialized, create a default for joining a place
|
||||
params = [RBXGameLaunchParams InitParamsForJoinPlace:placeId.integerValue];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
else if ([component isEqualToString:@"userid"])
|
||||
{
|
||||
//if there is a userid parameter, then there won't be other parameters. Grab it and escape
|
||||
NSString* userId = [components objectAtIndex:(i+1)];
|
||||
params = [RBXGameLaunchParams InitParamsForFollowUser:userId.integerValue];
|
||||
break;
|
||||
}
|
||||
else if ([component isEqualToString:@"gameinstanceid"])
|
||||
{
|
||||
NSString* gameInstanceId = [components objectAtIndex:(i+1)];
|
||||
if (placeId)
|
||||
{
|
||||
//if the placeId value has already been parsed, we have all the necessary parameters
|
||||
// to initialize our Launch Parameters
|
||||
params = [RBXGameLaunchParams InitParamsForJoinGameInstance:placeId.integerValue withInstanceID:gameInstanceId];
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
//if placeId has not yet been parsed for some reason, create the launch parameters
|
||||
// with a placeholder value. We will parse out the proper placeId value later.
|
||||
params = [RBXGameLaunchParams InitParamsForJoinGameInstance:-1 withInstanceID:gameInstanceId];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if ([component isEqualToString:@"accesscode"])
|
||||
{
|
||||
NSString* accessCode = [components objectAtIndex:(i+1)];
|
||||
if (placeId)
|
||||
{
|
||||
//if the placeId value has already been parsed, we have all the necessary parameters
|
||||
// to initialize our Launch Parameters
|
||||
params = [RBXGameLaunchParams InitParamsForJoinPrivateServer:placeId.integerValue withAccessCode:accessCode];
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
//if placeId has not yet been parsed for some reason, create the launch parameters
|
||||
// with a placeholder value. We will parse out the proper placeId value later.
|
||||
params = [RBXGameLaunchParams InitParamsForJoinPrivateServer:-1 withAccessCode:accessCode];
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//check if we have valid args to start a game, if not, exit
|
||||
if (params)
|
||||
{
|
||||
RBMobileWebViewController __weak *weakSelf = self;
|
||||
|
||||
if (params.joinRequestType == JOIN_GAME_REQUEST_USERID)
|
||||
{
|
||||
//we need the placeID to check if we can play a game, use the userID to fetch the user presence to fetch the gameID
|
||||
[RobloxData fetchPresenceForUser:[NSNumber numberWithInt:params.targetId] withCompletion:^(RBXUserPresence* presence)
|
||||
{
|
||||
//[self checkPlayable:presence.gameID.integerValue withLaunchParameters:params];
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
RBGameViewController* controller = [[RBGameViewController alloc] initWithLaunchParams:params];
|
||||
[weakSelf presentViewController:controller animated:NO completion:nil];
|
||||
}];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
//[self checkPlayable:placeId.integerValue withLaunchParameters:params];
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
RBGameViewController* controller = [[RBGameViewController alloc] initWithLaunchParams:params];
|
||||
[weakSelf presentViewController:controller animated:NO completion:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
-(void) checkPlayable:(int)placeId withLaunchParameters:(RBXGameLaunchParams*)launchParams
|
||||
{
|
||||
//check if the game is playable
|
||||
NSString* isPlayableString = [NSString stringWithFormat:@"%@/game/is-playable?placeId=%i", [RobloxInfo getApiBaseUrl], placeId];
|
||||
|
||||
NSURL *playableURL = [NSURL URLWithString: isPlayableString];
|
||||
NSMutableURLRequest *theRequest = [NSMutableURLRequest requestWithURL:playableURL
|
||||
cachePolicy:NSURLRequestReloadIgnoringCacheData
|
||||
timeoutInterval:60*7];
|
||||
|
||||
[RobloxInfo setDefaultHTTPHeadersForRequest:theRequest];
|
||||
[theRequest setHTTPMethod:@"GET"];
|
||||
|
||||
RBMobileWebViewController __weak *weakSelf = self;
|
||||
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
|
||||
[NSURLConnection sendAsynchronousRequest:theRequest queue:queue completionHandler:
|
||||
^(NSURLResponse *response, NSData *data, NSError *error)
|
||||
{
|
||||
[weakSelf processPlayableData:response playData:data playError:error withLaunchParams:launchParams];
|
||||
}];
|
||||
}
|
||||
-(void) processPlayableData:(NSURLResponse*)response playData:(NSData*)data playError:(NSError*)error withLaunchParams:(RBXGameLaunchParams*)parameters
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
NSHTTPURLResponse* urlResponse = ( NSHTTPURLResponse*) response;
|
||||
if ([urlResponse statusCode] == 200) // successful response
|
||||
{
|
||||
|
||||
NSDictionary* dict = [[NSDictionary alloc] init];
|
||||
NSError* error = nil;
|
||||
dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
|
||||
if (!error) // No Error processsing json data from our servers
|
||||
{
|
||||
NSNumber * n = [dict valueForKey:@"isPlayable"];
|
||||
BOOL success = [n boolValue];
|
||||
|
||||
if (success)
|
||||
{
|
||||
RBGameViewController* controller = [[RBGameViewController alloc] initWithLaunchParams:parameters];
|
||||
[self presentViewController:controller animated:NO completion:nil];
|
||||
if (self.flurryGameLaunchEvent)
|
||||
[Flurry logEvent:self.flurryGameLaunchEvent];
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxHUD showMessage:NSLocalizedString(@"GameNotPlayable", nil)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[RobloxAlert RobloxAlertWithMessage:NSLocalizedString(@"ConnectionErrorGameRestricted", nil)];
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
- (void) updateNavButtonsState
|
||||
{
|
||||
BOOL isPushedController = self.navigationController.viewControllers.count > 1;
|
||||
self.backButton.enabled = self.rbxWebView.canGoBackInHistory || isPushedController;
|
||||
self.fwdButton.enabled = self.rbxWebView.canGoForwardInHistory;
|
||||
}
|
||||
|
||||
#pragma mark - WKWebView Delegate Methods
|
||||
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
|
||||
{
|
||||
if ([message.body isKindOfClass:[NSDictionary class]]) {
|
||||
NSDictionary *messageBody = (NSDictionary *)message.body;
|
||||
|
||||
NSString *jsCommand = [messageBody stringForKey:@"command"];
|
||||
NSNumber *requestId = [messageBody numberForKey:@"requestId"];
|
||||
|
||||
[self.rbxWebView executeJavascriptCommand:jsCommand requestID:requestId.integerValue];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)())completionHandler
|
||||
{
|
||||
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:message
|
||||
message:nil
|
||||
preferredStyle:UIAlertControllerStyleAlert];
|
||||
[alertController addAction:[UIAlertAction actionWithTitle:@"OK"
|
||||
style:UIAlertActionStyleCancel
|
||||
handler:^(UIAlertAction *action) {
|
||||
completionHandler();
|
||||
}]];
|
||||
|
||||
[self presentViewController:alertController animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
|
||||
{
|
||||
if (nil != decisionHandler) {
|
||||
decisionHandler(WKNavigationResponsePolicyAllow);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
|
||||
{
|
||||
BOOL allowNavigationAction = [self shouldStartNavigationActionWithRequest:navigationAction.request navigationType:navigationAction.navigationType];
|
||||
if (nil != decisionHandler) {
|
||||
if (allowNavigationAction) {
|
||||
decisionHandler(WKNavigationActionPolicyAllow);
|
||||
} else {
|
||||
decisionHandler(WKNavigationActionPolicyCancel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL) shouldStartDecidePolicy: (NSURLRequest *) request
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void) webView:(WKWebView *)webView didCommitNavigation:(WKNavigation *)navigation
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[self updateNavButtonsState];
|
||||
[self.spinner show:YES];
|
||||
}];
|
||||
|
||||
if (self.flurryPageEvent)
|
||||
[Flurry logEvent:self.flurryPageEvent];
|
||||
}
|
||||
|
||||
- (void) webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[self updateNavButtonsState];
|
||||
[self.spinner hide:YES];
|
||||
}];
|
||||
|
||||
//scale the page to fit the view it is shoved into
|
||||
if (self.showWholeScreen)
|
||||
{
|
||||
CGSize contentSize = webView.scrollView.contentSize;
|
||||
CGSize viewSize = self.view.bounds.size;
|
||||
|
||||
float rw = viewSize.width / contentSize.width;
|
||||
|
||||
webView.scrollView.minimumZoomScale = rw;
|
||||
//webView.scrollView.maximumZoomScale = rw;
|
||||
webView.scrollView.zoomScale = rw;
|
||||
}
|
||||
}
|
||||
|
||||
- (void) webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error
|
||||
{
|
||||
[self updateNavButtonsState];
|
||||
|
||||
[self.spinner hide:YES];
|
||||
}
|
||||
|
||||
#pragma mark - UIWebView Delegate Methods
|
||||
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
|
||||
{
|
||||
BOOL val = [self shouldStartNavigationActionWithRequest:request navigationType:navigationType];
|
||||
return val;
|
||||
}
|
||||
|
||||
- (void)webViewDidStartLoad:(UIWebView *)webView
|
||||
{
|
||||
[self updateNavButtonsState];
|
||||
|
||||
if (self.flurryPageEvent)
|
||||
[Flurry logEvent:self.flurryPageEvent];
|
||||
|
||||
[self.spinner show:YES];
|
||||
}
|
||||
|
||||
- (void)webViewDidFinishLoad:(UIWebView *)webView
|
||||
{
|
||||
[self updateNavButtonsState];
|
||||
[self.spinner hide:YES];
|
||||
|
||||
//scale the page to fit the view it is shoved into
|
||||
if (self.showWholeScreen)
|
||||
{
|
||||
CGSize contentSize = webView.scrollView.contentSize;
|
||||
CGSize viewSize = self.view.bounds.size;
|
||||
|
||||
float rw = viewSize.width / contentSize.width;
|
||||
|
||||
webView.scrollView.minimumZoomScale = rw;
|
||||
//webView.scrollView.maximumZoomScale = rw;
|
||||
webView.scrollView.zoomScale = rw;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
|
||||
{
|
||||
[self updateNavButtonsState];
|
||||
|
||||
[self.spinner hide:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,31 @@
|
||||
//
|
||||
// RBMoreViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 12/9/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RBBaseViewController.h"
|
||||
#import "MoreTileButton.h"
|
||||
|
||||
@interface RBMoreViewController : RBBaseViewController
|
||||
|
||||
@property IBOutlet MoreTileButton* btnCatalog;
|
||||
@property IBOutlet MoreTileButton* btnProfile;
|
||||
@property IBOutlet MoreTileButton* btnCharacter;
|
||||
@property IBOutlet MoreTileButton* btnTrade;
|
||||
@property IBOutlet MoreTileButton* btnForum;
|
||||
@property IBOutlet MoreTileButton* btnSettings;
|
||||
|
||||
@property NSMutableArray* events;
|
||||
@property IBOutlet MoreTileButton* btnEvent1;
|
||||
@property IBOutlet MoreTileButton* btnEvent2;
|
||||
@property IBOutlet MoreTileButton* btnEvent3;
|
||||
@property IBOutlet MoreTileButton* btnEvent4;
|
||||
@property IBOutlet UILabel* lblEvents;
|
||||
|
||||
-(RBXAnalyticsCustomData) getMostRecentTab;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,38 @@
|
||||
//
|
||||
// RBMoreViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 12/9/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBMoreViewController.h"
|
||||
#import "MoreTileButton.h"
|
||||
|
||||
@interface RBMoreViewController ()
|
||||
|
||||
@property IBOutlet UIButton* btnBC;
|
||||
@property IBOutlet UIButton* btnRBX;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBMoreViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
|
||||
#pragma mark
|
||||
#pragma Button Actions
|
||||
-(IBAction)openScreenFromSegue:(id)sender
|
||||
{
|
||||
if ([sender isKindOfClass:[MoreTileButton class]])
|
||||
{
|
||||
MoreTileButton* btn = sender;
|
||||
[self performSegueWithIdentifier:btn.segueName sender:self];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,436 @@
|
||||
//
|
||||
// RBMoreViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 12/9/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBMoreViewController.h"
|
||||
#import "MoreTileButton.h"
|
||||
#import "RBMobileWebViewController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "UITabBarItem+CustomBadge.h"
|
||||
#import "RBXMessagesPollingService.h"
|
||||
#import "iOSSettingsService.h"
|
||||
#import "RobloxWebUtility.h"
|
||||
#import "RBXFunctions.h"
|
||||
|
||||
#import "RBGroupsScreenController.h"
|
||||
#import "RBInventoryScreenController.h"
|
||||
#import "RBProfileViewController.h"
|
||||
#import "RBWebMessagesViewController.h"
|
||||
#import "RBWebProfileViewController.h"
|
||||
|
||||
#define MS_openRobux @"MORE SCREEN - Open Robux"
|
||||
#define MS_openBuildersClub @"MORE SCREEN - Open Builders Club"
|
||||
#define BLOG_openRobux @"BLOG SCREEN - Open Robux"
|
||||
#define BLOG_openBuildersClub @"BLOG SCREEN - Open Builders Club"
|
||||
#define CHARACTER_openRobux @"CHARACTER SCREEN - Open Robux"
|
||||
#define CHARACTER_openBuildersClub @"CHARACTER SCREEN - Open Builders Club"
|
||||
#define FORUM_openRobux @"FORUM SCREEN - Open Robux"
|
||||
#define FORUM_openBuildersClub @"FORUM SCREEN - Open Builders Club"
|
||||
#define SETTINGS_openRobux @"SETTINGS SCREEN - Open Robux"
|
||||
#define SETTINGS_openBuildersClub @"SETTINGS SCREEN - Open Builders Club"
|
||||
#define TRADE_openRobux @"TRADE SCREEN - Open Robux"
|
||||
#define TRADE_openBuildersClub @"TRADE SCREEN - Open Builders Club"
|
||||
|
||||
DYNAMIC_FASTFLAGVARIABLE(EnablePinchToZoomOnSponsored, true);
|
||||
|
||||
@interface RBMoreViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBMoreViewController
|
||||
{
|
||||
RBXAnalyticsCustomData mostRecentTab;
|
||||
}
|
||||
|
||||
-(id) initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
[self initNotificationPolling];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
//localize strings
|
||||
[self setTitle:NSLocalizedString(@"MoreWord", nil)];
|
||||
[_lblEvents setText:NSLocalizedString(@"EventsWord", nil)];
|
||||
[_lblEvents setFont:[RobloxTheme fontH4]];
|
||||
|
||||
//apply themes
|
||||
[self setViewTheme:RBXThemeGeneric];
|
||||
[self.view setBackgroundColor:[RobloxTheme lightBackground]];
|
||||
|
||||
_btnEvent1.hidden = YES;
|
||||
_btnEvent2.hidden = YES;
|
||||
_btnEvent3.hidden = YES;
|
||||
_btnEvent4.hidden = YES;
|
||||
|
||||
[self fetchEvents];
|
||||
}
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[[RBXEventReporter sharedInstance] reportScreenLoaded:RBXAContextMain];
|
||||
|
||||
[self updateBadge];
|
||||
|
||||
if (![RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
[_btnCharacter setIsEnabled:[self isCharacterLinkEnabled]];
|
||||
[_btnForum setIsEnabled:[self isForumLinkEnabled]];
|
||||
[_btnTrade setIsEnabled:[self isTradeLinkEnabled]];
|
||||
}
|
||||
|
||||
[self fetchEvents];
|
||||
|
||||
//set a default
|
||||
mostRecentTab = RBXACustomTabMore;
|
||||
}
|
||||
|
||||
-(RBXAnalyticsCustomData) getMostRecentTab
|
||||
{
|
||||
return mostRecentTab;
|
||||
}
|
||||
|
||||
#pragma mark
|
||||
#pragma Button Actions
|
||||
-(IBAction)openBlog:(id)sender
|
||||
{
|
||||
mostRecentTab = RBXACustomTabBlog;
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonMoreButton withContext:RBXAContextMain withCustomData:mostRecentTab];
|
||||
|
||||
//this URL is responsive and should be fine
|
||||
RBMobileWebViewController* moreWeb = [self makeWebControllerwithURL:@"http://blog.watrbx.wtf"
|
||||
withTitle:NSLocalizedString(@"BlogWord", nil)
|
||||
andTheme:RBXThemeSocial
|
||||
addIcons:YES
|
||||
withRobuxFlurryEvent:BLOG_openRobux
|
||||
andBCFlurryEvent:BLOG_openBuildersClub];
|
||||
[self.navigationController pushViewController:moreWeb animated:YES];
|
||||
}
|
||||
-(IBAction)openCharacter:(id)sender
|
||||
{
|
||||
mostRecentTab = RBXACustomTabCharacter;
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonMoreButton withContext:RBXAContextMain withCustomData:mostRecentTab];
|
||||
|
||||
//this will cause problems on iPhone
|
||||
NSString* url = [[RobloxInfo getWWWBaseUrl] stringByAppendingString:@"My/Character.aspx"];
|
||||
|
||||
RBMobileWebViewController* moreWeb = [self makeWebControllerwithURL:url
|
||||
withTitle:NSLocalizedString(@"CharacterWord", nil)
|
||||
andTheme:RBXThemeCreative
|
||||
addIcons:YES
|
||||
withRobuxFlurryEvent:CHARACTER_openRobux
|
||||
andBCFlurryEvent:CHARACTER_openBuildersClub];
|
||||
[self.navigationController pushViewController:moreWeb animated:YES];
|
||||
}
|
||||
-(IBAction)openForum:(id)sender
|
||||
{
|
||||
mostRecentTab = RBXACustomTabForum;
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonMoreButton withContext:RBXAContextMain withCustomData:mostRecentTab];
|
||||
|
||||
//this runs risks on iPhone
|
||||
NSString* url = [[RobloxInfo getWWWBaseUrl] stringByAppendingString:@"Forum/default.aspx"];
|
||||
|
||||
RBMobileWebViewController* moreWeb = [self makeWebControllerwithURL:url
|
||||
withTitle:NSLocalizedString(@"ForumWord", nil)
|
||||
andTheme:RBXThemeSocial
|
||||
addIcons:YES
|
||||
withRobuxFlurryEvent:FORUM_openRobux
|
||||
andBCFlurryEvent:FORUM_openBuildersClub];
|
||||
[self.navigationController pushViewController:moreWeb animated:YES];
|
||||
}
|
||||
-(IBAction)openGroups:(id)sender
|
||||
{
|
||||
mostRecentTab = RBXACustomTabGroups;
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonMoreButton withContext:RBXAContextMain withCustomData:mostRecentTab];
|
||||
|
||||
//RBGroupsScreenController* screen = [[RBGroupsScreenController alloc] initWithNavButtons:YES];
|
||||
//[self.navigationController pushViewController:screen animated:YES];
|
||||
|
||||
NSString* url = [[RobloxInfo getBaseUrl] stringByAppendingString:[RobloxInfo thisDeviceIsATablet] ? @"My/Groups.aspx" : @"my-groups"];
|
||||
RBMobileWebViewController* moreWeb = [self makeWebControllerwithURL:url
|
||||
withTitle:NSLocalizedString(@"GroupsWord", nil)
|
||||
andTheme:RBXThemeSocial
|
||||
addIcons:YES
|
||||
withRobuxFlurryEvent:nil
|
||||
andBCFlurryEvent:nil];
|
||||
[self.navigationController pushViewController:moreWeb animated:YES];
|
||||
}
|
||||
-(IBAction)openHelp:(id)sender
|
||||
{
|
||||
mostRecentTab = RBXACustomTabHelp;
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonMoreButton withContext:RBXAContextMain withCustomData:mostRecentTab];
|
||||
|
||||
//NSString* helpURL = [NSString stringWithFormat:@"http://%@.help.watrbx.wtf/hc/%@"] //figure out how to localize this url?!!
|
||||
RBMobileWebViewController* moreWeb = [self makeWebControllerwithURL:@"http://en.help.watrbx.wtf/hc/en-us"
|
||||
withTitle:NSLocalizedString(@"HelpWord", nil)
|
||||
andTheme:RBXThemeGeneric
|
||||
addIcons:NO
|
||||
withRobuxFlurryEvent:nil
|
||||
andBCFlurryEvent:nil];
|
||||
[self.navigationController pushViewController:moreWeb animated:YES];
|
||||
}
|
||||
-(IBAction)openInventory:(id)sender
|
||||
{
|
||||
mostRecentTab = RBXACustomTabInventory;
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonMoreButton withContext:RBXAContextMain withCustomData:mostRecentTab];
|
||||
|
||||
//RBInventoryScreenController* screen = [[RBInventoryScreenController alloc] initWithNavButtons:YES];
|
||||
//[self.navigationController pushViewController:screen animated:YES];
|
||||
|
||||
NSString* url = [NSString stringWithFormat:@"%@users/%@/inventory#!/hats", [RobloxInfo getWWWBaseUrl], [UserInfo CurrentPlayer].userId];
|
||||
|
||||
RBMobileWebViewController* moreWeb = [self makeWebControllerwithURL:url
|
||||
withTitle:NSLocalizedString(@"InventoryWord", nil)
|
||||
andTheme:RBXThemeGame
|
||||
addIcons:YES
|
||||
withRobuxFlurryEvent:nil
|
||||
andBCFlurryEvent:nil];
|
||||
[self.navigationController pushViewController:moreWeb animated:YES];
|
||||
}
|
||||
-(IBAction)openCatalog:(id)sender
|
||||
{
|
||||
mostRecentTab = RBXACustomTabCatalog;
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonMoreButton withContext:RBXAContextMain withCustomData:mostRecentTab];
|
||||
|
||||
NSString* url = [[RobloxInfo getBaseUrl] stringByAppendingString:@"catalog/"];
|
||||
|
||||
RBMobileWebViewController* moreWeb = [self makeWebControllerwithURL:url
|
||||
withTitle:NSLocalizedString(@"CatalogWord", nil)
|
||||
andTheme:RBXThemeGame
|
||||
addIcons:YES
|
||||
withRobuxFlurryEvent:CHARACTER_openRobux
|
||||
andBCFlurryEvent:CHARACTER_openRobux];
|
||||
[self.navigationController pushViewController:moreWeb animated:YES];
|
||||
}
|
||||
-(IBAction)openProfile:(id)sender
|
||||
{
|
||||
mostRecentTab = RBXACustomTabProfile;
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonMoreButton withContext:RBXAContextMain withCustomData:mostRecentTab];
|
||||
|
||||
[self pushProfileControllerWithUserID:[UserInfo CurrentPlayer].userId];
|
||||
}
|
||||
-(IBAction)openSettings:(id)sender
|
||||
{
|
||||
mostRecentTab = RBXACustomTabSettings;
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonMoreButton withContext:RBXAContextMain withCustomData:mostRecentTab];
|
||||
|
||||
//if ([RobloxInfo thisDeviceIsATablet])
|
||||
// [self didPressEditAccount];
|
||||
//else
|
||||
[self performSegueWithIdentifier:@"pushSettings" sender:self];
|
||||
}
|
||||
-(IBAction)openTrade:(id)sender
|
||||
{
|
||||
mostRecentTab = RBXACustomTabTrade;
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonMoreButton withContext:RBXAContextMain withCustomData:mostRecentTab];
|
||||
|
||||
//THIS WILL CAUSE PROBLEMS ON iPhone
|
||||
NSString* url = [[RobloxInfo getWWWBaseUrl] stringByAppendingString:@"My/Money.aspx"];
|
||||
|
||||
RBMobileWebViewController* moreWeb = [self makeWebControllerwithURL:url
|
||||
withTitle:NSLocalizedString(@"TradeWord", nil)
|
||||
andTheme:RBXThemeSocial
|
||||
addIcons:YES
|
||||
withRobuxFlurryEvent:TRADE_openRobux
|
||||
andBCFlurryEvent:TRADE_openBuildersClub];
|
||||
[self.navigationController pushViewController:moreWeb animated:YES];
|
||||
}
|
||||
-(IBAction)openEvent:(id)sender
|
||||
{
|
||||
mostRecentTab = RBXACustomTabEvent;
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonMoreButton withContext:RBXAContextMain withCustomData:mostRecentTab];
|
||||
|
||||
MoreTileButton* btnSender = sender;
|
||||
RBXSponsoredEvent* eventInfo = (RBXSponsoredEvent*)btnSender.extraInfo;
|
||||
|
||||
//open the page held at the url
|
||||
NSString* url = [[RobloxInfo getWWWBaseUrl] stringByAppendingString:eventInfo.eventPageURLExtension];
|
||||
|
||||
RBMobileWebViewController* moreWeb = [self makeWebControllerwithURL:url
|
||||
withTitle:eventInfo.eventName
|
||||
andTheme:RBXThemeGame
|
||||
addIcons:YES
|
||||
withRobuxFlurryEvent:nil
|
||||
andBCFlurryEvent:nil];
|
||||
[moreWeb setToShowWholeScreen: DFFlag::EnablePinchToZoomOnSponsored];
|
||||
[self.navigationController pushViewController:moreWeb animated:YES];
|
||||
}
|
||||
|
||||
-(RBMobileWebViewController*) makeWebControllerwithURL:(NSString*)stringURL
|
||||
withTitle:(NSString*)pageTitle
|
||||
andTheme:(RBXTheme)viewTheme
|
||||
addIcons:(BOOL)addIcons
|
||||
withRobuxFlurryEvent:(NSString*)flurryRobux
|
||||
andBCFlurryEvent:(NSString*)flurryBC
|
||||
|
||||
{
|
||||
RBMobileWebViewController* moreWeb = [[RBMobileWebViewController alloc] initWithNavButtons:YES];
|
||||
|
||||
[moreWeb setViewTheme:viewTheme];
|
||||
[moreWeb setUrl:stringURL];
|
||||
[moreWeb setTitle:pageTitle];
|
||||
|
||||
if (addIcons)
|
||||
[moreWeb addRobuxIconWithFlurryEvent:flurryRobux andBCIconWithFlurryEvent:flurryBC];
|
||||
|
||||
return moreWeb;
|
||||
}
|
||||
|
||||
#pragma mark
|
||||
#pragma Helper Functions
|
||||
-(void) initMoreButton:(MoreTileButton*)aButton withEvent:(RBXSponsoredEvent*)eventData
|
||||
{
|
||||
if (!aButton) return;
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
if (eventData)
|
||||
{
|
||||
UIImage* eventImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:eventData.eventLogoURLString]]];
|
||||
[aButton setImageName:eventImage];
|
||||
[aButton setExtraInfo:eventData];
|
||||
aButton.hidden = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
aButton.hidden = YES;
|
||||
}
|
||||
});
|
||||
}
|
||||
-(void) fetchEvents
|
||||
{
|
||||
[RobloxData fetchSponsoredEvents:^(NSArray *events)
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^
|
||||
{
|
||||
_lblEvents.hidden = (events.count == 0);
|
||||
|
||||
RBXSponsoredEvent* evt1 = (events.count >= 1) ? events[0] : nil;
|
||||
RBXSponsoredEvent* evt2 = (events.count >= 2) ? events[1] : nil;
|
||||
RBXSponsoredEvent* evt3 = (events.count >= 3) ? events[2] : nil;
|
||||
RBXSponsoredEvent* evt4 = (events.count >= 4) ? events[3] : nil;
|
||||
|
||||
//format phone event buttons
|
||||
if (![RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
float contentWidth = _btnCatalog.width;
|
||||
|
||||
switch (events.count)
|
||||
{
|
||||
case 1: {
|
||||
//one big wide button
|
||||
[_btnEvent1 setWidth:contentWidth];
|
||||
}break;
|
||||
case 2: {
|
||||
//two stacked wide buttons
|
||||
[_btnEvent1 setWidth:contentWidth];
|
||||
[_btnEvent3 setWidth:contentWidth];
|
||||
|
||||
evt2 = nil;
|
||||
evt3 = events[1];
|
||||
}break;
|
||||
case 3: {
|
||||
//one big wide button
|
||||
//two small buttons below
|
||||
[_btnEvent1 setWidth:contentWidth];
|
||||
[_btnEvent3 setWidth:_btnEvent4.width];
|
||||
|
||||
evt2 = nil;
|
||||
evt3 = events[1];
|
||||
evt4 = events[2];
|
||||
}break;
|
||||
default:
|
||||
{
|
||||
//four small buttons
|
||||
[_btnEvent1 setWidth:_btnEvent4.width];
|
||||
[_btnEvent2 setWidth:_btnEvent4.width];
|
||||
[_btnEvent3 setWidth:_btnEvent4.width];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//update the button's target - passing nil hides the button
|
||||
[self initMoreButton:_btnEvent1 withEvent:evt1];
|
||||
[self initMoreButton:_btnEvent2 withEvent:evt2];
|
||||
[self initMoreButton:_btnEvent3 withEvent:evt3];
|
||||
[self initMoreButton:_btnEvent4 withEvent:evt4];
|
||||
}];
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark
|
||||
#pragma iOS Settings Flags
|
||||
- (BOOL)isCharacterLinkEnabled
|
||||
{
|
||||
iOSSettingsService* iOSSettings = [[RobloxWebUtility sharedInstance] getCachediOSSettings];
|
||||
return iOSSettings->GetValueEnableLinkCharacter();
|
||||
}
|
||||
- (BOOL)isForumLinkEnabled
|
||||
{
|
||||
iOSSettingsService* iOSSettings = [[RobloxWebUtility sharedInstance] getCachediOSSettings];
|
||||
return iOSSettings->GetValueEnableLinkForum();
|
||||
}
|
||||
- (BOOL)isTradeLinkEnabled
|
||||
{
|
||||
iOSSettingsService* iOSSettings = [[RobloxWebUtility sharedInstance] getCachediOSSettings];
|
||||
return iOSSettings->GetValueEnableLinkTrade();
|
||||
}
|
||||
|
||||
#pragma mark
|
||||
#pragma Notification Polling functions
|
||||
-(void) initNotificationPolling
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateBadge) name:RBX_NOTIFY_LOGGED_OUT object:nil];
|
||||
[self updateBadge];
|
||||
}
|
||||
-(void) updateBadge
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
if (![UserInfo CurrentPlayer].userLoggedIn)
|
||||
{
|
||||
[_btnSettings setBadgeValue:nil];
|
||||
|
||||
if ([self navigationController] && [[self navigationController] tabBarItem])
|
||||
[self.navigationController.tabBarItem setBadgeValue:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
//dispatch the update command to the main thread for instant update
|
||||
int totalSettings = 0;
|
||||
if ([UserInfo CurrentPlayer].accountNotifications)
|
||||
{
|
||||
totalSettings += ([UserInfo CurrentPlayer].accountNotifications.passwordNotificationEnabled && [UserInfo CurrentPlayer].password == nil) ? 1 : 0;
|
||||
totalSettings += ([UserInfo CurrentPlayer].accountNotifications.emailNotificationEnabled && [UserInfo CurrentPlayer].userEmail == nil)? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
//add the badge to the settings button
|
||||
if (_btnSettings)
|
||||
[_btnSettings setBadgeValue:(totalSettings <= 0) ? nil : [NSString stringWithFormat:@"%i",totalSettings]];
|
||||
|
||||
//add the badge to the navigational tab bar
|
||||
if ([self navigationController] && [[self navigationController] tabBarItem])
|
||||
{
|
||||
[self.navigationController.tabBarItem setBadgeValue:((totalSettings <= 0) ? nil : [NSString stringWithFormat:@"%i",totalSettings])];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// RBProfileViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Christian Hresko on 6/6/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RBBaseViewController.h"
|
||||
#import "RobloxData.h"
|
||||
|
||||
@interface RBProfileViewController : RBBaseViewController<UICollectionViewDelegate, UICollectionViewDataSource>
|
||||
|
||||
@property (strong, nonatomic) NSNumber* userId;
|
||||
@property (strong, nonatomic) RBXUserProfileInfo* profile;
|
||||
|
||||
- (void)updateFriendshipButton;
|
||||
- (void)updateFollowButton;
|
||||
@end
|
||||
@@ -0,0 +1,830 @@
|
||||
//
|
||||
// RBProfileViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Christian Hresko on 6/6/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBProfileViewController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "LoginManager.h"
|
||||
#import "WelcomeScreenController.h"
|
||||
#import "RBPurchaseViewController.h"
|
||||
#import "RBAccountManagerViewController.h"
|
||||
#import "RobloxImageView.h"
|
||||
#import "UIAlertView+Blocks.h"
|
||||
#import "RobloxData.h"
|
||||
#import "UserInfo.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "Flurry.h"
|
||||
#import "RBbarButtonMenu.h"
|
||||
#import "UIScrollView+Auto.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "GameSortHorizontalViewController.h"
|
||||
#import "GameThumbnailCell.h"
|
||||
#import "RBPlayerThumbnailCell.h"
|
||||
#import "RBFullFriendListScreenController.h"
|
||||
#import "RBWebGamePreviewScreenController.h"
|
||||
#import "RBSingleSortScreenController.h"
|
||||
#import "RBMessageComposeScreenController.H"
|
||||
#import "GameBadgeViewController.h"
|
||||
|
||||
#define ROBLOXBADGECELL_ID @"RobloxBadgeCell"
|
||||
#define PLAYERBADGECELL_ID @"PlayerBadgeCell"
|
||||
#define FRIENDCELL_ID @"FriendCell"
|
||||
#define BADGECELL_SUPP @"BadgeCellSupp"
|
||||
#define FRIENDCELL_SUPP @"FriendCellSupp"
|
||||
|
||||
#define GAME_ITEM_SIZE CGSizeMake(225, 155)
|
||||
#define GAME_THUMBNAIL_SIZE CGSizeMake(420, 230)
|
||||
#define PROFILE_IMAGE_SIZE CGSizeMake(352, 352)
|
||||
#define FRIEND_AVATAR_SIZE CGSizeMake(110, 110)
|
||||
#define BADGE_SIZE CGSizeMake(110, 110) // Player badges are fixed sized, so the request
|
||||
|
||||
#define MAX_ELEMENTS_PER_REQUEST 20
|
||||
|
||||
//---METRICS---
|
||||
#define PVC_didPressBuyRobux @"PROFILE SCREEN - Did Press Buy Robux"
|
||||
#define PVC_didPressBuildersClub @"PROFILE SCREEN - Did Press Builders Club"
|
||||
#define PVC_didPressEditAccount @"PROFILE SCREEN - Did Press Edit Account"
|
||||
#define PVC_didPressLogOut @"PROFILE SCREEN - Did Press Log Out"
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Collection View Cells
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@interface RBXPlayerBadgeCell : UICollectionViewCell
|
||||
|
||||
@property (weak, nonatomic) IBOutlet RobloxImageView* badgeImage;
|
||||
@property (strong, nonatomic) RBXBadgeInfo* badgeInfo;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBXPlayerBadgeCell
|
||||
|
||||
- (void)setBadgeInfo:(RBXBadgeInfo *)badgeInfo
|
||||
{
|
||||
_badgeInfo = badgeInfo;
|
||||
|
||||
self.badgeImage.animateInOptions = RBXImageViewAnimateInAlways;
|
||||
[self.badgeImage loadBadgeWithURL:badgeInfo.imageURL withSize:BADGE_SIZE completion:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface RBXRobloxBadgeCell : UICollectionViewCell
|
||||
|
||||
@property (weak, nonatomic) IBOutlet RobloxImageView* badgeImage;
|
||||
@property (strong, nonatomic) RBXBadgeInfo* badgeInfo;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBXRobloxBadgeCell
|
||||
|
||||
- (void)setBadgeInfo:(RBXBadgeInfo *)badgeInfo
|
||||
{
|
||||
_badgeInfo = badgeInfo;
|
||||
|
||||
self.badgeImage.animateInOptions = RBXImageViewAnimateInAlways;
|
||||
[self.badgeImage loadBadgeWithURL:badgeInfo.imageURL withSize:BADGE_SIZE completion:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Profile View Controller
|
||||
|
||||
@interface RBProfileViewController () <UIActionSheetDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBProfileViewController
|
||||
{
|
||||
RBXUserProfileInfo* _profile;
|
||||
|
||||
NSMutableArray* _playerBadges;
|
||||
NSMutableArray* _robloxBadges;
|
||||
NSMutableArray* _friends;
|
||||
|
||||
GameSortHorizontalViewController* _recentlyPlayedViewController;
|
||||
GameSortHorizontalViewController* _myGamesViewController;
|
||||
GameSortHorizontalViewController* _favoriteGamesViewController;
|
||||
|
||||
IBOutlet UIView* _headerContainer;
|
||||
IBOutlet RobloxImageView* _avatarImageView;
|
||||
IBOutlet UILabel* _userNameLabel;
|
||||
IBOutlet UILabel* _robuxTitle;
|
||||
IBOutlet UILabel* _ticketsTitle;
|
||||
IBOutlet UILabel* _friendsTitle;
|
||||
IBOutlet UILabel* _robuxLabel;
|
||||
IBOutlet UILabel* _ticketsLabel;
|
||||
IBOutlet UILabel* _friendsLabel;
|
||||
IBOutlet UILabel* _badgesHeaderTitle;
|
||||
IBOutlet UILabel* _friendsHeaderTitle;
|
||||
IBOutlet UIButton* _seeAllFriendsButton;
|
||||
IBOutlet UICollectionView* _badgesCollectionView;
|
||||
IBOutlet UICollectionView* _friendsCollectionView;
|
||||
IBOutlet UIButton *_friendshipButton;
|
||||
IBOutlet UIButton *_sendMessageButton;
|
||||
|
||||
IBOutlet UIScrollView* _scrollView;
|
||||
IBOutlet UIView* _friendsContainerView;
|
||||
IBOutlet UIView* _badgesContainerView;
|
||||
}
|
||||
|
||||
- (id)initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
self.title = NSLocalizedString(@"Profile", nil);
|
||||
|
||||
_playerBadges = [NSMutableArray array];
|
||||
_robloxBadges = [NSMutableArray array];
|
||||
_friends = [NSMutableArray array];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
|
||||
// Localize, initialize and stylize controls
|
||||
_friendsTitle.text = NSLocalizedString(@"FriendsWord", nil);
|
||||
_badgesHeaderTitle.text = [NSLocalizedString(@"BadgesWord", nil) uppercaseString];
|
||||
_friendsHeaderTitle.text = [NSLocalizedString(@"FriendsWord", nil) uppercaseString];
|
||||
_userNameLabel.text = @"";
|
||||
_friendsLabel.text = @"";
|
||||
|
||||
_sendMessageButton.hidden = YES;
|
||||
_friendshipButton.hidden = YES;
|
||||
|
||||
UIBarButtonItem* backButton = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStyleBordered target:nil action:nil];
|
||||
[self.navigationItem setBackBarButtonItem:backButton];
|
||||
|
||||
// Some controls are not in the view if this user is not the one logged in
|
||||
if( [self isSessionUser] )
|
||||
{
|
||||
_robuxTitle.text = [NSLocalizedString(@"RobuxWord", nil) uppercaseString];
|
||||
_ticketsTitle.text = NSLocalizedString(@"TicketsWord", nil);
|
||||
_robuxLabel.text = @"";
|
||||
_ticketsLabel.text = @"";
|
||||
|
||||
[RobloxTheme applyToProfileHeaderSubtitle:_robuxTitle];
|
||||
[RobloxTheme applyToProfileHeaderSubtitle:_ticketsTitle];
|
||||
|
||||
[RobloxTheme applyToProfileHeaderValue:_robuxLabel];
|
||||
[RobloxTheme applyToProfileHeaderValue:_ticketsLabel];
|
||||
|
||||
[self addRobuxIconWithFlurryEvent:PVC_didPressBuyRobux
|
||||
andBCIconWithFlurryEvent:PVC_didPressBuildersClub
|
||||
andEditIconWithFlurryEvent:PVC_didPressEditAccount
|
||||
andLogOutWithFlurryEvent:PVC_didPressLogOut];
|
||||
}
|
||||
else
|
||||
{
|
||||
[_friendshipButton addTarget:self action:@selector(friendshipButtonTouchUpInside:) forControlEvents:UIControlEventTouchUpInside];
|
||||
}
|
||||
|
||||
[_friendsCollectionView registerNib:[UINib nibWithNibName:@"RBPlayerThumbnailCell" bundle:nil] forCellWithReuseIdentifier:FRIENDCELL_ID];
|
||||
|
||||
self.view.backgroundColor = [RobloxTheme lightBackground];
|
||||
|
||||
[RobloxTheme applyToProfileHeaderSubtitle:_friendsTitle];
|
||||
[RobloxTheme applyToProfileHeaderValue:_friendsLabel];
|
||||
[RobloxTheme applyToProfileHeaderValue:_userNameLabel];
|
||||
[RobloxTheme applyToTableHeaderTitle:_badgesHeaderTitle];
|
||||
[RobloxTheme applyToTableHeaderTitle:_friendsHeaderTitle];
|
||||
[RobloxTheme applyToGameSortSeeAllButton:_seeAllFriendsButton];
|
||||
|
||||
[_seeAllFriendsButton setTitle:[NSLocalizedString(@"SeeAllPhrase", nil) uppercaseString] forState:UIControlStateNormal];
|
||||
|
||||
[self requestUserInfo];
|
||||
|
||||
[self setUpCollectionViews];
|
||||
|
||||
// Set header background and border
|
||||
_headerContainer.backgroundColor = [UIColor whiteColor];
|
||||
CALayer* headerLayer = _headerContainer.layer;
|
||||
headerLayer.shadowOpacity = 0.25;
|
||||
headerLayer.shadowColor = [UIColor colorWithWhite:(0x6B/255.f) alpha:1.0].CGColor;
|
||||
headerLayer.shadowOffset = CGSizeMake(0,0);
|
||||
headerLayer.shadowRadius = 2.0;
|
||||
}
|
||||
|
||||
- (void) viewDidLayoutSubviews
|
||||
{
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
[_scrollView setContentSizeForDirection:UIScrollViewDirectionVertical];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onFriendsUpdated:) name:RBXNotificationFriendsUpdated object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onRobuxUpdated:) name:RBXNotificationRobuxUpdated object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(fetchPointsAndUpdate) name:RBXNotificationDidLeaveGame object:nil];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void) onFriendsUpdated:(NSNotification *)notification
|
||||
{
|
||||
NSNumber* userID = [self getUserID];
|
||||
[self fetchFriendsForUser:userID];
|
||||
}
|
||||
|
||||
- (void)onRobuxUpdated:(NSNotification*)notification
|
||||
{
|
||||
[self fetchMyProfileAndUpdateRobux:YES];
|
||||
}
|
||||
|
||||
- (BOOL)isSessionUser
|
||||
{
|
||||
return self.userId == nil;
|
||||
}
|
||||
|
||||
- (NSNumber*)getUserID
|
||||
{
|
||||
if([self isSessionUser])
|
||||
{
|
||||
UserInfo* userInfo = [UserInfo CurrentPlayer];
|
||||
return [NSNumber numberWithInt:[userInfo.userId intValue]];
|
||||
}
|
||||
else
|
||||
return self.userId;
|
||||
}
|
||||
|
||||
- (void)fetchMyProfileAndUpdateRobux:(BOOL)updateRobux
|
||||
{
|
||||
[RobloxData fetchMyProfileWithSize:PROFILE_IMAGE_SIZE completion:^(RBXUserProfileInfo *profile)
|
||||
{
|
||||
_profile = profile;
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
if (updateRobux)
|
||||
{
|
||||
_robuxLabel.text = [NSString stringWithFormat:@"%d", profile.robux];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self showUserInfo:profile];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)fetchPointsAndUpdate
|
||||
{
|
||||
[RobloxData fetchMyProfileWithSize:PROFILE_IMAGE_SIZE completion:^(RBXUserProfileInfo *profile)
|
||||
{
|
||||
// dispatch_async(dispatch_get_main_queue(), ^{
|
||||
// self.pointsLabel.text = profile.displayPoints;
|
||||
// });
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)fetchFriendsForUser:(NSNumber *)userID
|
||||
{
|
||||
// ** Request friends **
|
||||
_friends = [NSMutableArray array];
|
||||
[_friendsCollectionView reloadData];
|
||||
|
||||
[RobloxData fetchUserFriends:userID friendType:RBXFriendTypeAllFriends startIndex:0 numItems:MAX_ELEMENTS_PER_REQUEST avatarSize:FRIEND_AVATAR_SIZE completion:^(NSUInteger totalFriends, NSArray *friends)
|
||||
{
|
||||
if(friends)
|
||||
{
|
||||
[_friends addObjectsFromArray:friends];
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
_friendsLabel.text = [NSString stringWithFormat:@"%d", totalFriends];
|
||||
[_friendsCollectionView reloadData];
|
||||
});
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) requestUserInfo
|
||||
{
|
||||
NSNumber* userID = [self getUserID];
|
||||
|
||||
// ** Request basic user info **
|
||||
// If no user id is set, load the current user info
|
||||
if( self.userId == nil )
|
||||
{
|
||||
[self fetchMyProfileAndUpdateRobux:NO];
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxData fetchUserProfile:userID avatarSize:PROFILE_IMAGE_SIZE completion:^(RBXUserProfileInfo *profile)
|
||||
{
|
||||
_profile = profile;
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[self showUserInfo:profile];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
// ** Request badges **
|
||||
_playerBadges = [NSMutableArray array];
|
||||
[_badgesCollectionView reloadData];
|
||||
|
||||
[RobloxData fetchUserBadges:RBXUserBadgeTypePlayer forUser:userID badgeSize:BADGE_SIZE completion:^(NSArray *badges)
|
||||
{
|
||||
if(badges)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
_badgesContainerView.hidden = _badgesContainerView.hidden || badges == nil || badges.count == 0;
|
||||
|
||||
[_playerBadges addObjectsFromArray:badges];
|
||||
[_badgesCollectionView reloadData];
|
||||
|
||||
[self layoutCollectionViews];
|
||||
});
|
||||
}
|
||||
}];
|
||||
|
||||
_robloxBadges = [NSMutableArray array];
|
||||
[_badgesCollectionView reloadData];
|
||||
|
||||
[RobloxData fetchUserBadges:RBXUserBadgeTypeRoblox forUser:userID badgeSize:BADGE_SIZE completion:^(NSArray *badges)
|
||||
{
|
||||
if(badges)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
_badgesContainerView.hidden = _badgesContainerView.hidden || badges == nil || badges.count == 0;
|
||||
|
||||
[_robloxBadges addObjectsFromArray:badges];
|
||||
[_badgesCollectionView reloadData];
|
||||
|
||||
[self layoutCollectionViews];
|
||||
});
|
||||
}
|
||||
}];
|
||||
|
||||
[self fetchFriendsForUser:userID];
|
||||
}
|
||||
|
||||
- (void) showUserInfo:(RBXUserProfileInfo*)profile
|
||||
{
|
||||
_userNameLabel.text = profile.username;
|
||||
[_avatarImageView loadAvatarForUserID:profile.userID prefetchedURL:profile.avatarURL urlIsFinal:profile.avatarIsFinal withSize:PROFILE_IMAGE_SIZE completion:nil];
|
||||
|
||||
if( [self isSessionUser] )
|
||||
{
|
||||
_robuxLabel.text = [NSString stringWithFormat:@"%d", profile.robux];
|
||||
_ticketsLabel.text = [NSString stringWithFormat:@"%d", profile.tickets];
|
||||
}
|
||||
else
|
||||
{
|
||||
_sendMessageButton.hidden = NO;
|
||||
_friendshipButton.hidden = NO;
|
||||
[self updateFrienshipButton];
|
||||
|
||||
self.title = [NSString stringWithFormat:NSLocalizedString(@"ProfileTitleFormat", nil), profile.username];
|
||||
_myGamesViewController.sortTitle = [NSString stringWithFormat:NSLocalizedString(@"FriendPlacesFormat", nil), _profile.username];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setUpCollectionViews
|
||||
{
|
||||
__weak RBProfileViewController* weakSelf = self;
|
||||
|
||||
NSNumber* userID = [self getUserID];
|
||||
|
||||
// Recently played games
|
||||
if( [self isSessionUser] )
|
||||
{
|
||||
_recentlyPlayedViewController = [[GameSortHorizontalViewController alloc] initWithNibName:@"GameSortHorizontalViewController" bundle:nil];
|
||||
_recentlyPlayedViewController.gameSelectedHandler = ^(RBXGameData* gameData) { [weakSelf showGameDetails:gameData]; };
|
||||
_recentlyPlayedViewController.seeAllHandler = ^(NSNumber* sortID) { [weakSelf seeAllGamesForSort:sortID]; };
|
||||
[self addChildViewController:_recentlyPlayedViewController];
|
||||
[_scrollView addSubview:_recentlyPlayedViewController.view];
|
||||
|
||||
[RobloxData fetchGameList:[RBXGameSort RecentlyPlayedGamesSort].sortID
|
||||
fromIndex:0
|
||||
numGames:MAX_ELEMENTS_PER_REQUEST
|
||||
thumbSize:GAME_THUMBNAIL_SIZE
|
||||
completion:^(NSArray *games)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
_recentlyPlayedViewController.view.hidden = games == nil || games.count == 0;
|
||||
[_recentlyPlayedViewController setSort:[RBXGameSort RecentlyPlayedGamesSort] withGames:games];
|
||||
[self layoutCollectionViews];
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
// My games
|
||||
_myGamesViewController = [[GameSortHorizontalViewController alloc] initWithNibName:@"GameSortHorizontalViewController" bundle:nil];
|
||||
_myGamesViewController.gameSelectedHandler = ^(RBXGameData* gameData) { [weakSelf showGameDetails:gameData]; };
|
||||
_myGamesViewController.seeAllHandler = ^(NSNumber* sortID) { [weakSelf seeAllGamesForSort:sortID]; };
|
||||
[self addChildViewController:_myGamesViewController];
|
||||
[_scrollView addSubview:_myGamesViewController.view];
|
||||
|
||||
[RobloxData
|
||||
fetchUserPlaces:userID
|
||||
startIndex:0
|
||||
numGames:MAX_ELEMENTS_PER_REQUEST
|
||||
thumbSize:GAME_THUMBNAIL_SIZE
|
||||
completion:^(NSArray *games)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
_myGamesViewController.view.hidden = games == nil || games.count == 0;
|
||||
[_myGamesViewController setSort:[RBXGameSort MyGamesSort] withGames:games];
|
||||
|
||||
if( _profile != nil && ![self isSessionUser] )
|
||||
{
|
||||
_myGamesViewController.sortTitle = [NSString stringWithFormat:NSLocalizedString(@"FriendPlacesFormat", nil), _profile.username];
|
||||
}
|
||||
});
|
||||
}];
|
||||
|
||||
// Favorite games
|
||||
_favoriteGamesViewController = [[GameSortHorizontalViewController alloc] initWithNibName:@"GameSortHorizontalViewController" bundle:nil];
|
||||
_favoriteGamesViewController.gameSelectedHandler = ^(RBXGameData* gameData) { [weakSelf showGameDetails:gameData]; };
|
||||
_favoriteGamesViewController.seeAllHandler = ^(NSNumber* sortID) { [weakSelf seeAllGamesForSort:sortID]; };
|
||||
[self addChildViewController:_favoriteGamesViewController];
|
||||
[_scrollView addSubview:_favoriteGamesViewController.view];
|
||||
|
||||
[RobloxData
|
||||
fetchUserFavoriteGames:userID
|
||||
startIndex:0
|
||||
numGames:MAX_ELEMENTS_PER_REQUEST
|
||||
thumbSize:GAME_THUMBNAIL_SIZE
|
||||
completion:^(NSArray *games)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
_favoriteGamesViewController.view.hidden = games == nil || games.count == 0;
|
||||
[_favoriteGamesViewController setSort:[RBXGameSort FavoriteGamesSort] withGames:games];
|
||||
[self layoutCollectionViews];
|
||||
});
|
||||
}];
|
||||
|
||||
// Badges
|
||||
_badgesCollectionView.dataSource = self;
|
||||
_badgesCollectionView.delegate = self;
|
||||
[_badgesCollectionView setContentInset:UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0)];
|
||||
|
||||
// Friends
|
||||
_friendsCollectionView.dataSource = self;
|
||||
_friendsCollectionView.delegate = self;
|
||||
[_friendsCollectionView setContentInset:UIEdgeInsetsMake(0.0, 0.0, 0.0, 0.0)];
|
||||
|
||||
[self layoutCollectionViews];
|
||||
}
|
||||
|
||||
- (void)layoutCollectionViews
|
||||
{
|
||||
#define OFFSET_Y 20
|
||||
|
||||
CGFloat lastY = CGRectGetMaxY(_friendsContainerView.frame) + OFFSET_Y;
|
||||
|
||||
if(_recentlyPlayedViewController != nil && _recentlyPlayedViewController.view.hidden == NO)
|
||||
{
|
||||
_recentlyPlayedViewController.view.y = lastY;
|
||||
lastY = CGRectGetMaxY(_recentlyPlayedViewController.view.frame) + OFFSET_Y;
|
||||
}
|
||||
|
||||
if(_myGamesViewController != nil && _myGamesViewController.view.hidden == NO)
|
||||
{
|
||||
_myGamesViewController.view.y = lastY;
|
||||
lastY = CGRectGetMaxY(_myGamesViewController.view.frame) + OFFSET_Y;
|
||||
}
|
||||
|
||||
if(_favoriteGamesViewController != nil && _favoriteGamesViewController.view.hidden == NO)
|
||||
{
|
||||
_favoriteGamesViewController.view.y = lastY;
|
||||
lastY = CGRectGetMaxY(_favoriteGamesViewController.view.frame) + OFFSET_Y;
|
||||
}
|
||||
|
||||
if(_badgesContainerView.hidden == NO)
|
||||
{
|
||||
_badgesContainerView.y = lastY;
|
||||
|
||||
_badgesCollectionView.size = _badgesCollectionView.contentSize;
|
||||
|
||||
_badgesContainerView.size = CGRectUnion(_badgesCollectionView.frame, _badgesHeaderTitle.frame).size;
|
||||
}
|
||||
|
||||
[_scrollView setContentSizeForDirection:UIScrollViewDirectionVertical];
|
||||
}
|
||||
|
||||
- (IBAction)didPressSendMessage:(id)sender
|
||||
{
|
||||
[self performSegueWithIdentifier:@"composeMessage" sender:nil];
|
||||
}
|
||||
|
||||
- (IBAction)didPressFriendshipButton:(id)sender
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
- (IBAction)didPressSeeAllFriends:(id)sender
|
||||
{
|
||||
RBFullFriendListScreenController* controller = [[RBFullFriendListScreenController alloc] init];
|
||||
controller.playerID = _profile.userID;
|
||||
controller.playerName = _profile.username;
|
||||
[self.navigationController pushViewController:controller animated:YES];
|
||||
}
|
||||
|
||||
- (void) showGameDetails:(RBXGameData*)gameData
|
||||
{
|
||||
RBWebGamePreviewScreenController* controller = [[RBWebGamePreviewScreenController alloc] init];
|
||||
controller.gameData = gameData;
|
||||
[self.navigationController pushViewController:controller animated:YES];
|
||||
}
|
||||
|
||||
- (void) seeAllGamesForSort:(NSNumber*)sortID
|
||||
{
|
||||
RBSingleSortScreenController* controller = [[RBSingleSortScreenController alloc] init];
|
||||
controller.sortID = sortID;
|
||||
controller.playerID = [self getUserID];
|
||||
[self.navigationController pushViewController:controller animated:YES];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Collection View Delegate Methods
|
||||
|
||||
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
|
||||
{
|
||||
if (collectionView == _badgesCollectionView) {
|
||||
if (_robloxBadges.count && _playerBadges.count) {
|
||||
return 2;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
|
||||
{
|
||||
if(collectionView == _badgesCollectionView)
|
||||
{
|
||||
switch (section) {
|
||||
case 0:
|
||||
return _robloxBadges.count;
|
||||
break;
|
||||
case 1:
|
||||
return _playerBadges.count;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(collectionView == _friendsCollectionView)
|
||||
{
|
||||
return _friends.count;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
- (UICollectionViewCell*)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
if (collectionView == _badgesCollectionView)
|
||||
{
|
||||
switch (indexPath.section)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
RBXRobloxBadgeCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:ROBLOXBADGECELL_ID forIndexPath:indexPath];
|
||||
cell.badgeInfo = _robloxBadges[indexPath.row];
|
||||
|
||||
return cell;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
RBXPlayerBadgeCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:PLAYERBADGECELL_ID forIndexPath:indexPath];
|
||||
cell.badgeInfo = _playerBadges[indexPath.row];
|
||||
|
||||
return cell;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (collectionView == _friendsCollectionView)
|
||||
{
|
||||
RBPlayerThumbnailCell* cell = [collectionView dequeueReusableCellWithReuseIdentifier:FRIENDCELL_ID forIndexPath:indexPath];
|
||||
cell.friendInfo = _friends[indexPath.row];
|
||||
return cell;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
|
||||
{
|
||||
[collectionView deselectItemAtIndexPath:indexPath animated:YES];
|
||||
|
||||
if ([collectionView isEqual:_friendsCollectionView])
|
||||
{
|
||||
RBProfileViewController *viewController = (RBProfileViewController*) [self.storyboard instantiateViewControllerWithIdentifier:@"RBOthersProfileViewController"];
|
||||
|
||||
RBXFriendInfo* friendInfo = _friends[indexPath.row];
|
||||
viewController.userId = friendInfo.userID;
|
||||
|
||||
[self.navigationController pushViewController:viewController animated:YES];
|
||||
}
|
||||
else if ([collectionView isEqual:_badgesCollectionView])
|
||||
{
|
||||
/*RBXBadgeInfo* badge = (indexPath.section == 0) ? _robloxBadges[indexPath.row] : _playerBadges[indexPath.row];
|
||||
if (!badge) return;
|
||||
GameBadgeViewController* badgePopup = [[GameBadgeViewController alloc] initWithBadgeInfo:badge];
|
||||
[self.navigationController presentViewController:badgePopup animated:YES completion:nil];*/
|
||||
}
|
||||
}
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
|
||||
{
|
||||
if( [segue.identifier isEqualToString:@"composeMessage"] )
|
||||
{
|
||||
RBMessageComposeScreenController* controller = segue.destinationViewController;
|
||||
[controller sendMessageToUser:_profile.userID name:_profile.username];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Friendship
|
||||
|
||||
- (void)updateFrienshipButton
|
||||
{
|
||||
switch(_profile.friendshipStatus)
|
||||
{
|
||||
case RBXFriendshipStatusNonFriends:
|
||||
[_friendshipButton setImage:[UIImage imageNamed:@"Friend Request Button"] forState:UIControlStateNormal];
|
||||
break;
|
||||
case RBXFriendshipStatusRequestSent:
|
||||
[_friendshipButton setImage:[UIImage imageNamed:@"Friend Request Sent"] forState:UIControlStateNormal];
|
||||
break;
|
||||
case RBXFriendshipStatusRequestReceived:
|
||||
[_friendshipButton setImage:[UIImage imageNamed:@"Friend Request Confirm"] forState:UIControlStateNormal];
|
||||
break;
|
||||
case RBXFriendshipStatusFriends:
|
||||
[_friendshipButton setImage:[UIImage imageNamed:@"Friend Button"] forState:UIControlStateNormal];
|
||||
break;
|
||||
case RBXFriendshipStatusBestFriends:
|
||||
[_friendshipButton setImage:[UIImage imageNamed:@"Best Friends Button"] forState:UIControlStateNormal];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)friendshipButtonTouchUpInside:(id)sender
|
||||
{
|
||||
switch (_profile.friendshipStatus)
|
||||
{
|
||||
case RBXFriendshipStatusNonFriends:
|
||||
{
|
||||
[RobloxHUD showSpinnerWithLabel:nil dimBackground:YES];
|
||||
[RobloxData sendFriendRequest:_profile.userID completion:^(BOOL success)
|
||||
{
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
if(success)
|
||||
{
|
||||
_profile.friendshipStatus = RBXFriendshipStatusRequestSent;
|
||||
[self updateFrienshipButton];
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxHUD showMessage:@"Error"];
|
||||
}
|
||||
}];
|
||||
break;
|
||||
}
|
||||
case RBXFriendshipStatusRequestSent:
|
||||
{
|
||||
// Nothing to do
|
||||
break;
|
||||
}
|
||||
case RBXFriendshipStatusFriends:
|
||||
{
|
||||
// Show friend options
|
||||
UIActionSheet* actionSheet = [[UIActionSheet alloc]
|
||||
initWithTitle:nil
|
||||
delegate:self
|
||||
cancelButtonTitle:NSLocalizedString(@"CancelWord", nil)
|
||||
destructiveButtonTitle:nil
|
||||
otherButtonTitles:NSLocalizedString(@"MakeBestFriendPhrase", nil),
|
||||
NSLocalizedString(@"RemoveFriendPhrase", nil), nil];
|
||||
[actionSheet showFromRect:_friendshipButton.frame inView:_friendshipButton.superview animated:YES];
|
||||
break;
|
||||
}
|
||||
case RBXFriendshipStatusBestFriends:
|
||||
{
|
||||
// Show friend options
|
||||
UIActionSheet* actionSheet = [[UIActionSheet alloc]
|
||||
initWithTitle:nil
|
||||
delegate:self
|
||||
cancelButtonTitle:NSLocalizedString(@"CancelWord", nil)
|
||||
destructiveButtonTitle:nil
|
||||
otherButtonTitles:NSLocalizedString(@"RemoveBestFriendPhrase", nil),
|
||||
NSLocalizedString(@"RemoveFriendPhrase", nil), nil];
|
||||
[actionSheet showFromRect:_friendshipButton.frame inView:_friendshipButton.superview animated:YES];
|
||||
break;
|
||||
}
|
||||
case RBXFriendshipStatusRequestReceived:
|
||||
{
|
||||
// Nothing to do
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
|
||||
{
|
||||
if(buttonIndex == 0)
|
||||
{
|
||||
[RobloxHUD showSpinnerWithLabel:nil dimBackground:YES];
|
||||
|
||||
if(_profile.friendshipStatus == RBXFriendshipStatusFriends)
|
||||
{
|
||||
// Add best friend
|
||||
[RobloxData makeBestFriend:_profile.userID completion:^(BOOL success)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
if(success)
|
||||
{
|
||||
_profile.friendshipStatus = RBXFriendshipStatusBestFriends;
|
||||
[self updateFrienshipButton];
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxHUD showMessage:@"Error"];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
else // if(_profile.friendshipStatus == RBXFriendshipStatusBestFriends)
|
||||
{
|
||||
// Remove best friend
|
||||
[RobloxData removeBestFriend:_profile.userID completion:^(BOOL success)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
if(success)
|
||||
{
|
||||
_profile.friendshipStatus = RBXFriendshipStatusFriends;
|
||||
[self updateFrienshipButton];
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxHUD showMessage:@"Error"];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
}
|
||||
else if(buttonIndex == 1)
|
||||
{
|
||||
// Remove friend
|
||||
[RobloxHUD showSpinnerWithLabel:nil dimBackground:YES];
|
||||
|
||||
[RobloxData removeFriend:_profile.userID completion:^(BOOL success)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
if(success)
|
||||
{
|
||||
_profile.friendshipStatus = RBXFriendshipStatusNonFriends;
|
||||
[self updateFrienshipButton];
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxHUD showMessage:@"Error"];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// RBPurchaseViewContrlller.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Christian Hresko on 6/16/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
// TODO - make this a subclass of a common Modal UIViewController
|
||||
@interface RBPurchaseViewContrlller : UIViewController<UIWebViewDelegate, UIGestureRecognizerDelegate>
|
||||
|
||||
- (id)initWithURL:(NSURL*)url andTitle:(NSString*)title;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,137 @@
|
||||
//
|
||||
// RBPurchaseViewContrlller.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Christian Hresko on 6/16/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBPurchaseViewContrlller.h"
|
||||
#import "StoreManager.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "RobloxTheme.h"
|
||||
|
||||
@interface RBPurchaseViewContrlller ()
|
||||
@property (nonatomic, strong) UIWebView* purchaseWebView;
|
||||
@property (nonatomic, strong) UITapGestureRecognizer* gestureRecognizer;
|
||||
@property (nonatomic, strong) NSURL* url;
|
||||
|
||||
- (void)didTapOutside:(UIGestureRecognizer*)sender;
|
||||
- (void)didPressClose;
|
||||
@end
|
||||
|
||||
@implementation RBPurchaseViewContrlller
|
||||
|
||||
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
|
||||
{
|
||||
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
|
||||
if (self) {
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithURL:(NSURL*)url andTitle:(NSString*)title {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.url = url;
|
||||
self.title = title;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)loadView {
|
||||
[super loadView];
|
||||
|
||||
self.purchaseWebView = [[UIWebView alloc] init];
|
||||
self.purchaseWebView.delegate = self;
|
||||
self.purchaseWebView.frame = CGRectMake(self.view.frame.origin.x, self.view.frame.origin.y, self.view.frame.size.width, self.view.frame.size.height);
|
||||
self.purchaseWebView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
self.purchaseWebView.scalesPageToFit = NO;
|
||||
self.purchaseWebView.multipleTouchEnabled = NO;
|
||||
self.purchaseWebView.scrollView.scrollEnabled = NO;
|
||||
self.purchaseWebView.scrollView.bounces = NO;
|
||||
|
||||
[RobloxTheme applyToModalPopupNavBar:self.navigationController.navigationBar];
|
||||
[self.view addSubview:self.purchaseWebView];
|
||||
|
||||
NSURLRequest* request = [NSURLRequest requestWithURL:self.url];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.purchaseWebView loadRequest:request];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
UIButton* close = [RobloxTheme applyCloseButtonToUINavigationItem:self.navigationItem];
|
||||
[close addTarget:self action:@selector(didPressClose) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
self.gestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didTapOutside:)];
|
||||
[self.gestureRecognizer setNumberOfTapsRequired:1];
|
||||
[self.gestureRecognizer setCancelsTouchesInView:NO];
|
||||
self.gestureRecognizer.delegate = self;
|
||||
}
|
||||
|
||||
- (void)viewDidAppear:(BOOL)animated {
|
||||
[super viewDidAppear:animated];
|
||||
[self.view.window addGestureRecognizer:self.gestureRecognizer];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated {
|
||||
[super viewWillDisappear:animated];
|
||||
[self.view.window removeGestureRecognizer:self.gestureRecognizer];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning
|
||||
{
|
||||
[super didReceiveMemoryWarning];
|
||||
}
|
||||
|
||||
- (void)webViewDidStartLoad:(UIWebView *)webView {
|
||||
[RobloxHUD showSpinnerForView:self.view withLabel:nil dimBackground:NO];
|
||||
}
|
||||
|
||||
- (void)webViewDidFinishLoad:(UIWebView *)webView {
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
}
|
||||
|
||||
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
}
|
||||
|
||||
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
|
||||
{
|
||||
id storeManager = GetStoreMgr;
|
||||
if ([storeManager isKindOfClass:[StoreManager class]]) {
|
||||
if([storeManager checkForInAppPurchases:request navigationType:navigationType]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
//// so that we can touch inside the uiwebview
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer {
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)didPressClose {
|
||||
[self dismissViewControllerAnimated:YES completion:^{
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)didTapOutside:(UIGestureRecognizer*)sender {
|
||||
if (sender.state == UIGestureRecognizerStateEnded) {
|
||||
CGPoint location = [sender locationInView:nil];
|
||||
if (![self.navigationController.view pointInside:[self.navigationController.view convertPoint:location fromView:self.view.window] withEvent:nil]) {
|
||||
[self dismissViewControllerAnimated:YES completion:^{
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// RBPurchaseViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Christian Hresko on 6/16/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#include "RBModalPopUpViewController.h"
|
||||
|
||||
// TODO - make this a subclass of a common Modal UIViewController
|
||||
@interface RBPurchaseViewController : RBModalPopUpViewController <UIWebViewDelegate>
|
||||
|
||||
- (id)initWithURL:(NSURL*)url andTitle:(NSString*)title;
|
||||
- (id)initWithBCPurchasing;
|
||||
- (id)initWithRobuxPurchasing;
|
||||
@end
|
||||
@@ -0,0 +1,146 @@
|
||||
//
|
||||
// RBPurchaseViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Christian Hresko on 6/16/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBPurchaseViewController.h"
|
||||
#import "StoreManager.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "RBXEventReporter.h"
|
||||
#import "UserInfo.h"
|
||||
|
||||
@interface RBPurchaseViewController ()
|
||||
@property (nonatomic, strong) UIWebView* purchaseWebView;
|
||||
@property (nonatomic, strong) NSURL* url;
|
||||
@end
|
||||
|
||||
@implementation RBPurchaseViewController
|
||||
{
|
||||
RBXAnalyticsContextName purchasingContext;
|
||||
}
|
||||
|
||||
|
||||
- (id)initWithURL:(NSURL*)url andTitle:(NSString*)title {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.url = url;
|
||||
self.title = title;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
- (id)initWithRobuxPurchasing
|
||||
{
|
||||
purchasingContext = RBXAContextPurchasingRobux;
|
||||
NSString* url = [[RobloxInfo getWWWBaseUrl] stringByAppendingString:@"mobile-app-upgrades/native-ios/robux"];
|
||||
NSString* titleString = [NSString stringWithFormat:@"%@ : R$%@", NSLocalizedString(@"CurrentRobuxBalanceWord", nil), [UserInfo CurrentPlayer].Robux];
|
||||
|
||||
self = [self initWithURL:[NSURL URLWithString:url] andTitle:titleString];
|
||||
return self;
|
||||
}
|
||||
- (id)initWithBCPurchasing
|
||||
{
|
||||
purchasingContext = RBXAContextPurchasingBC;
|
||||
NSString* url = [[RobloxInfo getWWWBaseUrl] stringByAppendingString:@"mobile-app-upgrades/native-ios/bc"];
|
||||
//NSString* BCLevelKey = @"NBCWord";
|
||||
//[UserInfo CurrentPlayer].bcMember
|
||||
NSString* titleString = NSLocalizedString(@"Builders Club", nil); //[NSString stringWithFormat:@"%@ : %@", NSLocalizedString(@"CurrentBuildersClubWord", nil), NSLocalizedString(BCLevelKey, nil)];
|
||||
|
||||
self = [self initWithURL:[NSURL URLWithString:url] andTitle:titleString];
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
|
||||
- (void)loadView {
|
||||
[super loadView];
|
||||
|
||||
self.purchaseWebView = [[UIWebView alloc] init];
|
||||
self.purchaseWebView.delegate = self;
|
||||
self.purchaseWebView.frame = self.view.bounds;
|
||||
self.purchaseWebView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
self.purchaseWebView.scalesPageToFit = YES;
|
||||
self.purchaseWebView.multipleTouchEnabled = NO;
|
||||
self.purchaseWebView.scrollView.scrollEnabled = YES;
|
||||
self.purchaseWebView.scrollView.bounces = NO;
|
||||
|
||||
[self.view addSubview:self.purchaseWebView];
|
||||
|
||||
NSURLRequest* request = [NSURLRequest requestWithURL:self.url];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self.purchaseWebView loadRequest:request];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
self.view.backgroundColor = [UIColor whiteColor];
|
||||
|
||||
//pesky phone problem
|
||||
if (![RobloxInfo thisDeviceIsATablet])
|
||||
[self disableTapRecognizer];
|
||||
}
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonClose withContext:purchasingContext];
|
||||
}
|
||||
|
||||
|
||||
- (void)webViewDidStartLoad:(UIWebView *)webView {
|
||||
[RobloxHUD showSpinnerWithLabel:nil dimBackground:NO];
|
||||
|
||||
if (![RobloxInfo thisDeviceIsATablet])
|
||||
webView.hidden = YES;
|
||||
}
|
||||
|
||||
- (void)webViewDidFinishLoad:(UIWebView *)webView {
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
|
||||
//scale the page to fit the view it is shoved into
|
||||
if (![RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
CGSize contentSize = webView.scrollView.contentSize;
|
||||
CGSize viewSize = self.view.bounds.size;
|
||||
|
||||
float rw = viewSize.width / contentSize.width;
|
||||
|
||||
webView.scrollView.minimumZoomScale = rw;
|
||||
webView.scrollView.maximumZoomScale = rw;
|
||||
webView.scrollView.zoomScale = rw;
|
||||
webView.hidden = NO;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error {
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
}
|
||||
|
||||
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
|
||||
{
|
||||
id storeManager = GetStoreMgr;
|
||||
if ([storeManager isKindOfClass:[StoreManager class]]) {
|
||||
if([storeManager checkForInAppPurchases:request navigationType:navigationType]) {
|
||||
NSString* url = [NSString stringWithFormat:@"%@", request.URL];
|
||||
NSArray* urlParts = [url componentsSeparatedByString:@"?id="];
|
||||
if (urlParts.count >= 1)
|
||||
{
|
||||
NSString* purchaseID = [[(NSString*)urlParts[1] componentsSeparatedByString:@"."] lastObject];
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonSubmit withContext:purchasingContext withCustomDataString:purchaseID];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// RBResetPasswordControllerViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Christian Hresko on 6/26/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBPurchaseViewController.h"
|
||||
|
||||
@interface RBResetPasswordViewController : RBPurchaseViewController<UIScrollViewDelegate>
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// RBResetPasswordControllerViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Christian Hresko on 6/26/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBResetPasswordViewController.h"
|
||||
#import "RobloxInfo.h"
|
||||
|
||||
@interface RBResetPasswordViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBResetPasswordViewController
|
||||
|
||||
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch {
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// RBSingleSortScreenController
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/23/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "RobloxData.h"
|
||||
|
||||
@interface RBSingleSortScreenController : UIViewController
|
||||
|
||||
@property (strong, nonatomic) NSNumber* playerID;
|
||||
@property (strong, nonatomic) NSNumber* sortID;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,85 @@
|
||||
//
|
||||
// RBSingleSortScreenController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/23/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBSingleSortScreenController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxData.h"
|
||||
#import "RBPlayerThumbnailCell.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "GamesCollectionView.h"
|
||||
#import "RBWebGamePreviewScreenController.h"
|
||||
#import "RBXEventReporter.h"
|
||||
|
||||
#define FRIENDCELL_ID @"FriendCell"
|
||||
|
||||
#define FRIEND_AVATAR_SIZE CGSizeMake(110, 110)
|
||||
#define ITEM_SIZE CGSizeMake(90, 108)
|
||||
|
||||
#define FRIENDS_PER_REQUEST 40
|
||||
#define START_REQUEST_THRESHOLD 10 // The next request will start when there are 10 elements left
|
||||
|
||||
@interface RBSingleSortScreenController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBSingleSortScreenController
|
||||
{
|
||||
GamesCollectionView* _collectionView;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.view.backgroundColor = [RobloxTheme lightBackground];
|
||||
|
||||
self.edgesForExtendedLayout = UIRectEdgeNone;
|
||||
|
||||
_collectionView = [[GamesCollectionView alloc] init];
|
||||
[self.view addSubview:_collectionView];
|
||||
|
||||
[_collectionView loadGamesForSort:self.sortID playerID:self.playerID];
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews
|
||||
{
|
||||
[super viewDidLayoutSubviews];
|
||||
|
||||
[_collectionView setFrame:self.view.bounds];
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(showGameDetails:) name:RBX_NOTIFY_GAME_SELECTED object:nil];
|
||||
}
|
||||
|
||||
- (void) viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void) showGameDetails:(NSNotification*) notification
|
||||
{
|
||||
RBXGameData* gameData = [notification.userInfo objectForKey:@"gameData"];
|
||||
|
||||
[[RBXEventReporter sharedInstance] reportOpenGameDetailFromSort:[NSNumber numberWithInteger:gameData.placeID.integerValue]
|
||||
fromPage:RBXALocationGamesSeeAll
|
||||
inSort:_sortID
|
||||
atIndex:[notification.userInfo objectForKey:@"gameIndex"]
|
||||
totalItemsInSort:[notification.userInfo objectForKey:@"totalGames"]];
|
||||
|
||||
RBWebGamePreviewScreenController* controller = [[RBWebGamePreviewScreenController alloc] init];
|
||||
controller.gameData = gameData;
|
||||
[self.navigationController pushViewController:controller animated:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// RBWebFriendsViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 12/10/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBMobileWebViewController.h"
|
||||
|
||||
@interface RBWebFriendsViewController : RBMobileWebViewController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// RBWebFriendsViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 12/10/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBWebFriendsViewController.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "UITabBarItem+CustomBadge.h"
|
||||
#import "RBXMessagesPollingService.h"
|
||||
|
||||
#define FS_openRobux @"FRIENDS SCREEN - Open Robux"
|
||||
#define FS_openBuildersClub @"FRIENDS SCREEN - Open Builders Club"
|
||||
|
||||
@interface RBWebFriendsViewController ()
|
||||
@property int badgeNumber;
|
||||
@property NSString* friendsURL;
|
||||
@end
|
||||
|
||||
@implementation RBWebFriendsViewController
|
||||
-(id) initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
_badgeNumber = 0;
|
||||
[self initNotificationPolling];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
_friendsURL = [NSString stringWithFormat:@"%@users/%@/friends", [RobloxInfo getWWWBaseUrl], [UserInfo CurrentPlayer].userId];
|
||||
|
||||
[self setViewTheme:RBXThemeSocial];
|
||||
|
||||
self.navigationItem.title = NSLocalizedString(@"FriendsWord", nil);
|
||||
|
||||
[self addRobuxIconWithFlurryEvent:FS_openRobux
|
||||
andBCIconWithFlurryEvent:FS_openBuildersClub];
|
||||
|
||||
|
||||
}
|
||||
|
||||
- (void) viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[self setViewTheme:RBXThemeSocial];
|
||||
|
||||
//auto-navigate to the friends request section if you have any outstanding requests
|
||||
NSString* currentURL = [_friendsURL stringByAppendingString:(_badgeNumber <= 0 ? @"#!/friends" : @"#!/friend-requests")];
|
||||
[self setUrl:currentURL];
|
||||
[super viewWillAppear:animated];
|
||||
}
|
||||
|
||||
-(void) initNotificationPolling
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateBadge) name:RBX_NOTIFY_NEW_MESSAGES_TOTAL object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateBadge) name:RBX_NOTIFY_NEW_FRIEND_REQUESTS_TOTAL object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateBadge) name:RBX_NOTIFY_FRIEND_REQUESTS_UPDATED object:nil];
|
||||
[self updateBadge];
|
||||
}
|
||||
-(void) updateBadge
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
//dispatch the update command to the main thread for instant update
|
||||
_badgeNumber = [[RBXMessagesPollingService sharedInstance] totalFriendRequests];
|
||||
if ([self navigationController])
|
||||
if ([[self navigationController] tabBarItem])
|
||||
{
|
||||
//this should be cleaned up at some point to use a RobloxTheme constant. Roblox Theme needs to be cleaned up.
|
||||
[self.navigationController.tabBarItem setBadgeValue:(_badgeNumber <= 0 ? nil : [NSString stringWithFormat:@"%i",_badgeNumber])];
|
||||
}
|
||||
});
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
//
|
||||
// RBWebGamePreviewScreenController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Pixeloide on 9/19/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBMobileWebViewController.h"
|
||||
#import "RobloxData.h"
|
||||
|
||||
@interface RBWebGamePreviewScreenController : RBMobileWebViewController
|
||||
|
||||
@property(strong, nonatomic) RBXGameData* gameData;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// RBWebGamePreviewScreenController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Pixeloide on 9/19/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBWebGamePreviewScreenController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "UserInfo.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "RobloxGoogleAnalytics.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "LoginManager.h"
|
||||
#import "RBXFunctions.h"
|
||||
|
||||
@interface RBWebGamePreviewScreenController () <UIWebViewDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBWebGamePreviewScreenController
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
self.showNavButtons = NO;
|
||||
|
||||
[super viewDidLoad];
|
||||
|
||||
self.navigationItem.title = _gameData.title;
|
||||
|
||||
self.navigationItem.hidesBackButton = NO;
|
||||
self.navigationItem.leftItemsSupplementBackButton = NO;
|
||||
|
||||
UIBarButtonItem* backButton = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStyleBordered target:nil action:nil];
|
||||
[self.navigationItem setBackBarButtonItem:backButton];
|
||||
|
||||
NSString* gameURL = [NSString stringWithFormat:@"%@PlaceItem.aspx?id=%@", [RobloxInfo getWWWBaseUrl], _gameData.placeID];
|
||||
self.url = gameURL;
|
||||
|
||||
if ([UserInfo CurrentPlayer].userLoggedIn)
|
||||
[self addNavBarButtons:nil];
|
||||
else
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(addNavBarButtons:) name:RBX_NOTIFY_LOGIN_SUCCEEDED object:nil];
|
||||
|
||||
[self addSearchIconWithSearchType:SearchResultGames andFlurryEvent:nil];
|
||||
}
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
[super viewWillAppear:animated];
|
||||
}
|
||||
|
||||
-(void) addNavBarButtons:(NSNotification*)notification
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[self addRobuxIconWithFlurryEvent:nil
|
||||
andBCIconWithFlurryEvent:nil];
|
||||
|
||||
if (notification)
|
||||
[self reloadWebPage];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBX_NOTIFY_LOGIN_SUCCEEDED object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeIcons:) name:RBX_NOTIFY_LOGGED_OUT object:nil];
|
||||
}];
|
||||
}
|
||||
-(void) removeIcons:(NSNotification*) notification
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[self removeRobuxAndBCIcons];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBX_NOTIFY_LOGGED_OUT object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(addNavBarButtons:) name:RBX_NOTIFY_LOGIN_SUCCEEDED object:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// RBCatalogMasterController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/3/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RBMobileWebViewController.h"
|
||||
|
||||
@interface RBWebGamesViewController : RBMobileWebViewController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// RBCatalogMasterController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/3/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBWebGamesViewController.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "UserInfo.h"
|
||||
#import "LoginManager.h"
|
||||
#import "FastLog.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "RBXFunctions.h"
|
||||
|
||||
DYNAMIC_FASTFLAGVARIABLE(UseNewWebGamesPage, true);
|
||||
|
||||
@interface RBWebGamesViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBWebGamesViewController
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.navigationItem.title = NSLocalizedString(@"GameWord", nil);
|
||||
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
|
||||
if ([UserInfo CurrentPlayer].userLoggedIn)
|
||||
[self addNavBarButtons:nil];
|
||||
else
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(addNavBarButtons:) name:RBX_NOTIFY_LOGIN_SUCCEEDED object:nil];
|
||||
|
||||
|
||||
[self addSearchIconWithSearchType:SearchResultGames andFlurryEvent:nil];
|
||||
|
||||
if (DFFlag::UseNewWebGamesPage)
|
||||
[self setUrl:[[RobloxInfo getWWWBaseUrl] stringByAppendingString:@"games"]];
|
||||
else
|
||||
[self setUrl:[[RobloxInfo getWWWBaseUrl] stringByAppendingString:@"games/list"]];
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
-(void) viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[self setViewTheme:RBXThemeGame];
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
}
|
||||
|
||||
-(void) addNavBarButtons:(NSNotification*)notification
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[self addRobuxIconWithFlurryEvent:nil
|
||||
andBCIconWithFlurryEvent:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBX_NOTIFY_LOGIN_SUCCEEDED object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeIcons:) name:RBX_NOTIFY_LOGGED_OUT object:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
-(void) removeIcons:(NSNotification*) notification
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[self removeRobuxAndBCIcons];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBX_NOTIFY_LOGGED_OUT object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(addNavBarButtons:) name:RBX_NOTIFY_LOGIN_SUCCEEDED object:nil];
|
||||
}];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// RBCatalogMasterController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/3/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RBMobileWebViewController.h"
|
||||
|
||||
@interface RBWebMessagesViewController : RBMobileWebViewController
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// RBCatalogMasterController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/3/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBWebMessagesViewController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "RBXMessagesPollingService.h"
|
||||
#import "RBXFunctions.h"
|
||||
|
||||
#define MS_openRobux @"MESSAGES SCREEN - Open Robux"
|
||||
#define MS_openBuildersClub @"MESSAGES SCREEN - Open Builders Club"
|
||||
|
||||
@interface RBWebMessagesViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBWebMessagesViewController
|
||||
|
||||
- (id) initWithCoder:(NSCoder *)aDecoder
|
||||
{
|
||||
self = [super initWithCoder:aDecoder];
|
||||
if (self)
|
||||
{
|
||||
[self initNotificationPolling];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.navigationItem.title = NSLocalizedString(@"MessagesWord", nil);
|
||||
[self addRobuxIconWithFlurryEvent:MS_openRobux
|
||||
andBCIconWithFlurryEvent:MS_openRobux];
|
||||
|
||||
[self setUrl:[[RobloxInfo getBaseUrl] stringByAppendingString:[RobloxInfo thisDeviceIsATablet] ? @"my/messages/#!/inbox" : @"inbox"]];
|
||||
}
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[self setViewTheme:RBXThemeSocial];
|
||||
[super viewWillAppear:animated];
|
||||
}
|
||||
|
||||
#pragma mark
|
||||
#pragma Notification Polling functions
|
||||
|
||||
-(void) initNotificationPolling
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateBadge) name:RBX_NOTIFY_NEW_MESSAGES_TOTAL object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateBadge) name:RBX_NOTIFY_INBOX_UPDATED object:nil];
|
||||
[self updateBadge];
|
||||
}
|
||||
|
||||
-(void) updateBadge
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
//dispatch the update command to the main thread for instant update
|
||||
int totalM = [[RBXMessagesPollingService sharedInstance] totalMessages];
|
||||
|
||||
//add the badge to the navigational tab bar
|
||||
if ([self navigationController])
|
||||
if ([[self navigationController] tabBarItem])
|
||||
[self.navigationController.tabBarItem setBadgeValue:((totalM <= 0) ? nil : [NSString stringWithFormat:@"%i",totalM])];
|
||||
}];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// RBCatalogMasterController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/3/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "RBMobileWebViewController.h"
|
||||
|
||||
@interface RBWebProfileViewController : RBMobileWebViewController
|
||||
@property (nonatomic, strong) NSNumber* userId;
|
||||
@end
|
||||
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// RBCatalogMasterController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 9/3/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "RBWebProfileViewController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "UserInfo.h"
|
||||
#import "LoginManager.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "RBXFunctions.h"
|
||||
|
||||
@interface RBWebProfileViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation RBWebProfileViewController
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
self.navigationItem.title = NSLocalizedString(@"ProfileWord", nil);
|
||||
|
||||
|
||||
NSString* profileURL = [NSString stringWithFormat:@"%@users/%@/profile", [RobloxInfo getWWWBaseUrl], _userId.stringValue];
|
||||
[self loadURL:profileURL screenURL:NO];
|
||||
|
||||
if ([[UserInfo CurrentPlayer] userLoggedIn])
|
||||
[self addNavBarButtons:nil];
|
||||
else
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(addNavBarButtons:) name:RBX_NOTIFY_LOGIN_SUCCEEDED object:nil];
|
||||
|
||||
[self addSearchIconWithSearchType:SearchResultUsers andFlurryEvent:nil];
|
||||
}
|
||||
|
||||
-(void) addNavBarButtons:(NSNotification*)notification
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[self addRobuxIconWithFlurryEvent:nil
|
||||
andBCIconWithFlurryEvent:nil];
|
||||
|
||||
if (notification)
|
||||
[self reloadWebPage];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBX_NOTIFY_LOGIN_SUCCEEDED object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeIcons:) name:RBX_NOTIFY_LOGGED_OUT object:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
-(void) removeIcons:(NSNotification*) notification
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[self removeRobuxAndBCIcons];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBX_NOTIFY_LOGGED_OUT object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(addNavBarButtons:) name:RBX_NOTIFY_LOGIN_SUCCEEDED object:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// SignUpScreenController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 5/20/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "NonRotatableViewController.h"
|
||||
#import "RBValidTextField.h"
|
||||
#import "RBGenderView.h"
|
||||
#import "RBBirthdayPicker.h"
|
||||
|
||||
@interface SignUpScreenController : NonRotatableViewController <UIGestureRecognizerDelegate>
|
||||
|
||||
@property IBOutlet RBValidTextField* username;
|
||||
@property IBOutlet RBValidTextField* password;
|
||||
@property IBOutlet RBValidTextField* verify;
|
||||
@property IBOutlet RBValidTextField* email;
|
||||
@property IBOutlet RBGenderView* gender;
|
||||
@property IBOutlet RBBirthdayPicker* birthday;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,611 @@
|
||||
//
|
||||
// SignUpScreenController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 5/20/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#import "SignUpScreenController.h"
|
||||
#import "TermsAgreementController.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "RobloxAlert.h"
|
||||
#import "SignupVerifier.h"
|
||||
#import "UserInfo.h"
|
||||
#import "RobloxGoogleAnalytics.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "LoginManager.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "LoginScreenController.h"
|
||||
#import "Flurry.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "UIAlertView+Blocks.h"
|
||||
#import "RobloxData.h"
|
||||
#import "NonRotatableNavigationController.h"
|
||||
#import "RBCaptchaViewController.h"
|
||||
#import "RBXEventReporter.h"
|
||||
#import "SocialSignUpViewController.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "RBXFunctions.h"
|
||||
|
||||
#define DEFAULT_VIEW_SIZE CGRectMake(0, 0, 540, 540)
|
||||
#define MAX_USERNAME_LENGTH 20
|
||||
#ifndef kCFCoreFoundationVersionNumber_iOS_8_0
|
||||
#define kCFCoreFoundationVersionNumber_iOS_8_0 1129.15
|
||||
#endif
|
||||
|
||||
//---METRICS---
|
||||
#define SUSC_didPressLogin @"SIGN UP SCREEN - Log In Pressed While Guest"
|
||||
#define SUSC_userNameSelected @"SIGN UP SCREEN - User Name Selected"
|
||||
#define SUSC_emailSelected @"SIGN UP SCREEN - Email Selected"
|
||||
#define SUSC_passwordSelected @"SIGN UP SCREEN - Password Selected"
|
||||
#define SUSC_birthdaySelected @"SIGN UP SCREEN - Birthday Button Pressed"
|
||||
#define SUSC_maleButtonSelected @"SIGN UP SCREEN - Male Button Pressed"
|
||||
#define SUSC_femaleButtonSelected @"SIGN UP SCREEN - Female Button Pressed"
|
||||
#define SUSC_createAccountSelected @"SIGN UP SCREEN - Sign Up Button Pressed"
|
||||
|
||||
@interface SignUpScreenController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation SignUpScreenController
|
||||
{
|
||||
IBOutlet UILabel* _titleLabel;
|
||||
IBOutlet UIButton* _btnCancel;
|
||||
IBOutlet UIButton* _btnSignup;
|
||||
IBOutlet UIWebView *_finePrint;
|
||||
IBOutlet UIButton *loginButton;
|
||||
IBOutlet UIView* _whiteView;
|
||||
IBOutlet UIButton* _btnSocial;
|
||||
IBOutlet UILabel* _lblOr;
|
||||
|
||||
UITapGestureRecognizer* _touches;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// View delegates
|
||||
////////////////////////////////////////////////////////////////
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
[self.view.layer setMasksToBounds:NO];
|
||||
[self.view.layer setShadowColor:[UIColor blackColor].CGColor];
|
||||
[self.view.layer setShadowOpacity:0.6];
|
||||
[self.view.layer setShadowRadius:1.0];
|
||||
[self.view.layer setShadowOffset:CGSizeMake(0.0, 1.0)];
|
||||
|
||||
_touches = [[UITapGestureRecognizer alloc] init];
|
||||
[_touches setNumberOfTouchesRequired:1];
|
||||
[_touches setNumberOfTapsRequired:1];
|
||||
[_touches setDelegate:self];
|
||||
[_touches setEnabled:YES];
|
||||
[self.view addGestureRecognizer:_touches];
|
||||
|
||||
if (![RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
[_whiteView.layer setShadowColor:[UIColor blackColor].CGColor];
|
||||
[_whiteView.layer setShadowOpacity:0.4];
|
||||
[_whiteView.layer setShadowRadius:2.0];
|
||||
[_whiteView.layer setShadowOffset:CGSizeMake(0.0, 0.5)];
|
||||
}
|
||||
|
||||
// Fine print
|
||||
NSString *htmlFile = [[NSBundle mainBundle] pathForResource:@"SignUpDisclamer" ofType:@"html" inDirectory:nil];
|
||||
if(htmlFile)
|
||||
{
|
||||
NSString* htmlContents = [NSString stringWithContentsOfFile:htmlFile encoding:NSUTF8StringEncoding error:NULL];
|
||||
htmlContents = [htmlContents stringByReplacingOccurrencesOfString:@"textPlaceholder" withString:NSLocalizedString(@"SignupFinePrint", nil)];
|
||||
[_finePrint loadData:[htmlContents dataUsingEncoding:NSUTF8StringEncoding] MIMEType:@"text/html" textEncodingName:@"UTF-8" baseURL:[NSURL URLWithString:@""]];
|
||||
}
|
||||
|
||||
__weak SignUpScreenController* weakSelf = self;
|
||||
|
||||
//Initialize the input views
|
||||
//NSLocalizedString([RobloxInfo thisDeviceIsATablet] ? @"UsernameRequirements" : @"UsernameRequirementsShort", nil)]
|
||||
[_username setTitle:NSLocalizedString(@"UsernameWord", nil)];
|
||||
[_username setHint:NSLocalizedString(@"UsernameRequirements", nil)];
|
||||
[_username setNextResponder:_password];
|
||||
[_username setValidationBlock:^{
|
||||
[[SignupVerifier sharedInstance] checkIfValidUsername:weakSelf.username.text completion:^(BOOL success, NSString *validMessage)
|
||||
{
|
||||
if (success)
|
||||
[weakSelf.username markAsValid];
|
||||
else
|
||||
{
|
||||
[weakSelf.username markAsInvalid];
|
||||
|
||||
//handle bad username
|
||||
if ([validMessage isEqualToString:NSLocalizedString(@"UsernameCommon", nil)])
|
||||
{
|
||||
[[SignupVerifier sharedInstance] getAlternateUsername:weakSelf.username.text
|
||||
completion:^(BOOL success, NSString *alternateMessage)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
[RobloxHUD prompt:[NSString stringWithFormat:NSLocalizedString(@"UsernameTaken", nil), alternateMessage]
|
||||
withTitle:NSLocalizedString(@"UsernameTakenTitle", nil)
|
||||
onOK:^
|
||||
{
|
||||
[weakSelf.username setText:alternateMessage];
|
||||
[weakSelf.username markAsValid];
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonPopUpOkay withContext:RBXAContextSignup];
|
||||
}
|
||||
onCancel:^
|
||||
{
|
||||
[weakSelf.username markAsInvalid];
|
||||
[weakSelf.username showError:validMessage];
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonPopUpCancel withContext:RBXAContextSignup];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[weakSelf.username markAsInvalid];
|
||||
[weakSelf.username showError:validMessage];
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[weakSelf.username markAsInvalid];
|
||||
[weakSelf.username showError:validMessage];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}];
|
||||
|
||||
//NSLocalizedString([RobloxInfo thisDeviceIsATablet] ? @"PasswordRequirements" : @"PasswordRequirementsShort"
|
||||
[_password setTitle:NSLocalizedString(@"PasswordWord", nil)];
|
||||
[_password setHint:NSLocalizedString(@"PasswordRequirements", nil)];
|
||||
[_password setNextResponder:_verify];
|
||||
[_password setProtectedTextEntry:YES];
|
||||
[_password setValidationBlock:^{
|
||||
[[SignupVerifier sharedInstance] checkIfValidPassword:weakSelf.password.text
|
||||
withUsername:weakSelf.username.text
|
||||
completion:^(BOOL success, NSString *validMessage)
|
||||
{
|
||||
if (success)
|
||||
[weakSelf.password markAsValid];
|
||||
else
|
||||
{
|
||||
[weakSelf.password markAsInvalid];
|
||||
[weakSelf.password showError:validMessage];
|
||||
}
|
||||
}];
|
||||
|
||||
if (weakSelf.verify.text.length > 0)
|
||||
{
|
||||
[[SignupVerifier sharedInstance] checkIfPasswordsMatch:weakSelf.password.text
|
||||
withVerification:weakSelf.verify.text
|
||||
completion:^(BOOL passwordsMatch, NSString *matchMessage)
|
||||
{
|
||||
if (passwordsMatch)
|
||||
[weakSelf.verify markAsValid];
|
||||
else
|
||||
{
|
||||
[weakSelf.verify markAsInvalid];
|
||||
[weakSelf.verify showError:matchMessage];
|
||||
}
|
||||
}];
|
||||
}
|
||||
}];
|
||||
|
||||
[_verify setTitle:NSLocalizedString(@"VerifyWord", nil)];
|
||||
[_verify setNextResponder:_email];
|
||||
[_verify setProtectedTextEntry:YES];
|
||||
[_verify setValidationBlock:^{
|
||||
[[SignupVerifier sharedInstance] checkIfPasswordsMatch:weakSelf.password.text
|
||||
withVerification:weakSelf.verify.text
|
||||
completion:^(BOOL passwordsMatch, NSString *message)
|
||||
{
|
||||
if (passwordsMatch)
|
||||
[weakSelf.verify markAsValid];
|
||||
else
|
||||
{
|
||||
[weakSelf.verify markAsInvalid];
|
||||
[weakSelf.verify showError:message];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
|
||||
if (_email)
|
||||
{
|
||||
[_email setTitle:NSLocalizedString(@"EmailWord", nil)];
|
||||
[_email setHint:NSLocalizedString(@"EmailRequirements", nil)];
|
||||
[_email setNextResponder:nil];
|
||||
[_email setKeyboardType:UIKeyboardTypeEmailAddress];
|
||||
[_email setValidationBlock:^{
|
||||
[[SignupVerifier sharedInstance] checkIfValidEmail:weakSelf.email.text
|
||||
completion:^(BOOL success, NSString *message)
|
||||
{
|
||||
if (success)
|
||||
[weakSelf.email markAsValid];
|
||||
else
|
||||
{
|
||||
[weakSelf.email markAsInvalid];
|
||||
[weakSelf.email showError:message];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
[_gender setTouchBlock:^{
|
||||
[weakSelf resignAllResponders];
|
||||
}];
|
||||
|
||||
[_birthday setValidationBlock:^{
|
||||
[weakSelf resignAllResponders];
|
||||
|
||||
//check locally if the user is under 13
|
||||
if ([weakSelf.birthday userUnder13])
|
||||
{
|
||||
//check if the user has already entered anything in the email field
|
||||
if ([weakSelf.email.text length] > 0)
|
||||
{
|
||||
NSString* confirmMessage = [NSString stringWithFormat:NSLocalizedString(@"ConfirmEmailUnder13Message", nil), weakSelf.email.text];
|
||||
UIAlertView* confirm = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"ConfirmEmailUnder13",nil)
|
||||
message:confirmMessage
|
||||
delegate:nil
|
||||
cancelButtonTitle:NSLocalizedString(@"NoWord", nil)
|
||||
otherButtonTitles:NSLocalizedString(@"YesWord", nil), nil];
|
||||
[confirm showWithCompletion:^(UIAlertView *alertView, NSInteger buttonIndex)
|
||||
{
|
||||
if (![alertView isEqual:confirm])
|
||||
return;
|
||||
|
||||
//NO - remove the provided email
|
||||
if (buttonIndex == 0)
|
||||
{
|
||||
[weakSelf.email setText:@""];
|
||||
[weakSelf.birthday resignFirstResponder];
|
||||
[weakSelf.email setTitle:NSLocalizedString(@"EmailUnder13Word", nil)];
|
||||
[weakSelf.email forceUpdate];
|
||||
return;
|
||||
}
|
||||
|
||||
//YES - Do Nothing
|
||||
if (buttonIndex == 1)
|
||||
return;
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[weakSelf.email setTitle:NSLocalizedString(@"EmailUnder13Word", nil)];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
[weakSelf.email setTitle:NSLocalizedString(@"EmailWord", nil)];
|
||||
}
|
||||
}];
|
||||
|
||||
|
||||
//Localize some strings
|
||||
[_titleLabel setText:[[NSLocalizedString(@"SignupWord", nil) uppercaseString] stringByReplacingOccurrencesOfString:@" " withString:@""]];
|
||||
|
||||
[_btnSignup setTitle:NSLocalizedString(@"SignupWord", nil) forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToModalSubmitButton:_btnSignup];
|
||||
|
||||
[_btnCancel setTitle:NSLocalizedString(@"CancelWord", nil) forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToModalCancelButton:_btnCancel];
|
||||
|
||||
if (_lblOr)
|
||||
{
|
||||
[_lblOr setText:NSLocalizedString(@"OrWord", nil)];
|
||||
[_lblOr setFont:[RobloxTheme fontBodySmall]];
|
||||
[_lblOr setTextColor:[RobloxTheme colorGray2]];
|
||||
}
|
||||
|
||||
if (_btnSocial)
|
||||
{
|
||||
[_btnSocial setTitle:NSLocalizedString(@"SignInSocialWord", nil) forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToFacebookButton:_btnSocial];
|
||||
}
|
||||
|
||||
[loginButton setTitle:NSLocalizedString(@"LoginWord", nil) forState:UIControlStateNormal];
|
||||
[loginButton.titleLabel setFont:[RobloxTheme fontBody]];
|
||||
[loginButton setHidden:NO];
|
||||
}
|
||||
|
||||
- (BOOL)disablesAutomaticKeyboardDismissal
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[[RBXEventReporter sharedInstance] reportScreenLoaded:RBXAContextSignup];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[_finePrint stopLoading];
|
||||
}
|
||||
|
||||
- (void) viewWillLayoutSubviews {
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
BOOL isPreiOS8 = NSFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_8_0;
|
||||
if (isPreiOS8 && [RobloxInfo thisDeviceIsATablet])
|
||||
self.view.superview.bounds = DEFAULT_VIEW_SIZE;
|
||||
|
||||
if (_btnSocial)
|
||||
[RobloxTheme applyToFacebookButton:_btnSocial];
|
||||
|
||||
}
|
||||
|
||||
- (IBAction)closeController:(id)sender
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonClose withContext:RBXAContextSignup];
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
|
||||
{
|
||||
if([segue.identifier isEqualToString:@"FinePrintSegue"])
|
||||
{
|
||||
TermsAgreementController *controller = (TermsAgreementController *)segue.destinationViewController;
|
||||
controller.url = sender;
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)didPressLogin:(id)sender
|
||||
{
|
||||
[Flurry logEvent:SUSC_didPressLogin];
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonLogin withContext:RBXAContextSignup];
|
||||
|
||||
UIViewController *presenter = self.presentingViewController;
|
||||
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:[RobloxInfo getStoryboardName] bundle:nil];
|
||||
LoginScreenController* controller = (LoginScreenController*)[storyboard instantiateViewControllerWithIdentifier:@"LoginScreenController"];
|
||||
|
||||
BOOL isPreiOS8 = NSFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_8_0;
|
||||
if (isPreiOS8 && [RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:^{
|
||||
[presenter presentViewController:controller animated:YES completion:nil];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
[presenter presentViewController:controller animated:YES completion:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)didPressGigyaSignup:(id)sender
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonSocialSignIn withContext:RBXAContextSignup];
|
||||
[RobloxHUD showSpinnerWithLabel:NSLocalizedString(@"AuthenticatingWithFacebookWord", nil)
|
||||
dimBackground:YES];
|
||||
[[LoginManager sharedInstance] doSocialLoginFromController:self
|
||||
forProvider:[LoginManager ProviderNameFacebook]
|
||||
withCompletion:^(bool success, NSString *message)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
if ([message isEqualToString:@"newUser"])
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
UIViewController *presenter = self.presentingViewController;
|
||||
[self dismissViewControllerAnimated:YES completion:^{
|
||||
//Show social sign up controller
|
||||
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:[RobloxInfo getStoryboardName] bundle:nil];
|
||||
SocialSignUpViewController* controller = [storyboard instantiateViewControllerWithIdentifier:@"SocialSignUpViewController"];
|
||||
[controller setModalPresentationStyle:UIModalPresentationFormSheet];
|
||||
[presenter presentViewController:controller animated:YES completion:nil];
|
||||
}];
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (message)
|
||||
[RobloxAlert RobloxAlertWithMessage:message];
|
||||
[[LoginManager sharedInstance] doSocialLogout];
|
||||
}
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Input fields
|
||||
////////////////////////////////////////////////////////////////
|
||||
- (void) resignAllResponders
|
||||
{
|
||||
[self.view endEditing:YES];
|
||||
|
||||
//RBValidTextField* activeField;
|
||||
//if ([_username isEditing]) activeField = _username;
|
||||
//else if ([_password isEditing]) activeField = _password;
|
||||
//else if ([_verify isEditing]) activeField = _verify;
|
||||
//else if ([_email isEditing]) activeField = _email;
|
||||
//
|
||||
//if (activeField)
|
||||
// [activeField resignFirstResponder];
|
||||
}
|
||||
|
||||
|
||||
- (IBAction)createAccount:(id)sender
|
||||
{
|
||||
[Flurry logEvent:SUSC_createAccountSelected];
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonSubmit withContext:RBXAContextSignup];
|
||||
//[self resignAllResponders];
|
||||
|
||||
//do signup checks
|
||||
[RobloxHUD showSpinnerWithLabel:NSLocalizedString(@"SigningUp", nil) dimBackground:YES];
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
//check the bools
|
||||
bool canLogin = YES; //Why is this called canLogin and not canSignup?
|
||||
|
||||
if (!_username.isValidated)
|
||||
{
|
||||
canLogin = NO;
|
||||
if (_username.text.length == 0)
|
||||
{
|
||||
[_username showError:NSLocalizedString(@"UsernameMissing", nil)];
|
||||
[_username markAsInvalid];
|
||||
}
|
||||
}
|
||||
|
||||
if (!_password.isValidated)
|
||||
{
|
||||
canLogin = NO;
|
||||
if (_password.text.length == 0)
|
||||
{
|
||||
[_password markAsInvalid];
|
||||
[_password showError:NSLocalizedString(@"PasswordMissing", nil)];
|
||||
}
|
||||
}
|
||||
|
||||
if (!_verify.isValidated)
|
||||
{
|
||||
canLogin = NO;
|
||||
if (_verify.text.length == 0)
|
||||
{
|
||||
[_verify markAsInvalid];
|
||||
[_verify showError:NSLocalizedString(@"VerifyMissing", nil)];
|
||||
}
|
||||
}
|
||||
|
||||
//with API proxy or Social signup, the email field may not exist
|
||||
if (_email && _email.text.length > 0 && !_email.isValidated)
|
||||
{
|
||||
canLogin = NO;
|
||||
}
|
||||
|
||||
if (![_birthday isValid])
|
||||
{
|
||||
canLogin = NO;
|
||||
[_birthday markAsInvalid];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (canLogin)
|
||||
{
|
||||
[self executeSignup];
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) executeSignup
|
||||
{
|
||||
[[SignupVerifier sharedInstance] signUpWithUsername:self.username.text
|
||||
password:self.password.text
|
||||
birthString:self.birthday.playerBirthday
|
||||
gender:self.gender.playerGender
|
||||
email:(self.email ? self.email.text : @"")
|
||||
completionBlock:^(NSError *signUpError) {
|
||||
if ([RBXFunctions isEmpty:signUpError]) {
|
||||
//Successful sign up. Log the user into their new account
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
|
||||
[UserInfo CurrentPlayer].username = _username.text;
|
||||
[UserInfo CurrentPlayer].password = _password.text;
|
||||
|
||||
[[LoginManager sharedInstance] loginWithUsername:_username.text password:_password.text completionBlock:^(NSError *loginError) {
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
|
||||
if ([RBXFunctions isEmpty:loginError]) {
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[RobloxHUD hideSpinner:NO];
|
||||
[self dismissViewControllerAnimated:YES completion:^{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:RBX_NOTIFY_SIGNUP_COMPLETED object:self userInfo:nil];
|
||||
}];
|
||||
}];
|
||||
} else {
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[RobloxHUD hideSpinner:NO];
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
} else {
|
||||
//Something failed when signing up
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[RobloxHUD hideSpinner:YES];
|
||||
NSString *reason = signUpError.domain;
|
||||
|
||||
if ([reason isEqualToString:NSLocalizedString(@"TooManyAttempts", nil)])
|
||||
{
|
||||
//open up a captcha so we can attempt to sign up again
|
||||
NonRotatableNavigationController* navigation = [LoginManager CaptchaForSignupWithUsername:_username.text
|
||||
andV1Completion:^(bool success, NSString *message)
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^
|
||||
{
|
||||
if (success == YES)
|
||||
{
|
||||
[self executeSignup];
|
||||
}
|
||||
}];
|
||||
}
|
||||
andV2Completion:^(NSError *captchaError)
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^
|
||||
{
|
||||
if ([RBXFunctions isEmpty:captchaError])
|
||||
{
|
||||
//captcha was successful, try again
|
||||
[self executeSignup];
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxHUD prompt:captchaError.domain withTitle:NSLocalizedString(@"ErrorWord", nil)];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
[self presentViewController:navigation animated:YES completion:nil];
|
||||
}
|
||||
else
|
||||
[RobloxAlert RobloxAlertWithMessage:reason];
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
//Delegate functions
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
|
||||
{
|
||||
//NSLog(@"GestureRecognizer : %@", touch);
|
||||
UIView* touchedView = touch.view;
|
||||
if (touchedView == self.view || touchedView == _gender || touchedView == _whiteView)
|
||||
{
|
||||
[self resignAllResponders];
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Fine print
|
||||
////////////////////////////////////////////////////////////////
|
||||
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
|
||||
{
|
||||
NSString* urlRequestString = [[request URL] absoluteString];
|
||||
NSRange finePrintInit = [urlRequestString rangeOfString:@"file"];
|
||||
if(finePrintInit.location != NSNotFound)
|
||||
return YES;
|
||||
|
||||
[self performSegueWithIdentifier:@"FinePrintSegue" sender:urlRequestString];
|
||||
//[[UIApplication sharedApplication] openURL:request.URL];
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,30 @@
|
||||
//
|
||||
// SocialSignUpViewController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 9/10/15.
|
||||
// Copyright © 2015 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "NonRotatableViewController.h"
|
||||
#import "RBValidTextField.h"
|
||||
#import "RobloxImageView.h"
|
||||
|
||||
@interface SocialSignUpViewController : NonRotatableViewController <UIGestureRecognizerDelegate>
|
||||
|
||||
@property IBOutlet RobloxImageView* imgIdentity;
|
||||
@property IBOutlet RBValidTextField* txtUsername;
|
||||
@property IBOutlet UILabel* lblSelectUserName;
|
||||
@property IBOutlet UILabel* lblNameDescription;
|
||||
@property IBOutlet UILabel* lblAlreadyHaveAccount;
|
||||
@property IBOutlet UILabel* lblAlmostDone;
|
||||
@property IBOutlet UIButton* btnAccept;
|
||||
@property IBOutlet UIButton* btnCancel;
|
||||
@property IBOutlet UIView* whiteView;
|
||||
@property IBOutlet UIView* shadowContainer;
|
||||
|
||||
-(IBAction) createAccount:(id)sender;
|
||||
-(IBAction) cancelSignUp:(id)sender;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,311 @@
|
||||
//
|
||||
// SocialSignUpViewController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Kyler Mulherin on 9/10/15.
|
||||
// Copyright © 2015 ROBLOX. All rights reserved.
|
||||
//
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
#import "SocialSignUpViewController.h"
|
||||
#import "LoginManager.h"
|
||||
#import "SignupVerifier.h"
|
||||
#import "LoginManager.h"
|
||||
#import "RBXEventReporter.h"
|
||||
#import "RobloxHUD.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "UserInfo.h"
|
||||
#import "RBActivityIndicatorView.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "NSDictionary+Parsing.h"
|
||||
#import "UIView+Position.h"
|
||||
#import "RobloxAlert.h"
|
||||
#import "NonRotatableNavigationController.h"
|
||||
#import "RBCaptchaViewController.h"
|
||||
#import "RBCaptchaV2ViewController.h"
|
||||
#import "RBXFunctions.h"
|
||||
|
||||
#ifndef kCFCoreFoundationVersionNumber_iOS_8_0
|
||||
#define kCFCoreFoundationVersionNumber_iOS_8_0 1129.15
|
||||
#endif
|
||||
|
||||
@interface SocialSignUpViewController ()
|
||||
@property (nonatomic, strong) RBActivityIndicatorView* loadingSpinner;
|
||||
|
||||
@property (nonatomic, strong) NSString* photoURL;
|
||||
@property (nonatomic, strong) NSString* username;
|
||||
@property (nonatomic, strong) NSString* gender;
|
||||
@property (nonatomic, strong) NSString* email;
|
||||
@property (nonatomic, strong) NSString* birthday;
|
||||
@property (nonatomic, strong) NSString* gigyaUID;
|
||||
|
||||
@property (nonatomic, strong) UITapGestureRecognizer* touches;
|
||||
|
||||
@end
|
||||
|
||||
@implementation SocialSignUpViewController
|
||||
|
||||
- (void) viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
_touches = [[UITapGestureRecognizer alloc] init];
|
||||
[_touches setNumberOfTouchesRequired:1];
|
||||
[_touches setNumberOfTapsRequired:1];
|
||||
[_touches setDelegate:self];
|
||||
[_touches setEnabled:YES];
|
||||
[self.view addGestureRecognizer:_touches];
|
||||
|
||||
|
||||
self.username = [[UserInfo CurrentPlayer] GigyaName];
|
||||
self.email = [[UserInfo CurrentPlayer] userEmail];
|
||||
self.gigyaUID = [[UserInfo CurrentPlayer] GigyaUID];
|
||||
self.gender = [[UserInfo CurrentPlayer] GigyaGender];
|
||||
self.birthday = [[UserInfo CurrentPlayer] birthday];
|
||||
self.photoURL = [[UserInfo CurrentPlayer] GigyaPhotoURL];
|
||||
|
||||
if (![RobloxInfo thisDeviceIsATablet])
|
||||
[RobloxTheme applyShadowToView:_whiteView];
|
||||
|
||||
self.loadingSpinner = [[RBActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 30, 30)];
|
||||
[self.loadingSpinner setHidden:YES];
|
||||
[self.view addSubview:_loadingSpinner];
|
||||
[self.loadingSpinner startAnimating];
|
||||
|
||||
//initialize the shadow on the container
|
||||
_shadowContainer.backgroundColor = [UIColor clearColor];
|
||||
[RobloxTheme applyShadowToView:_shadowContainer];
|
||||
[_shadowContainer.layer setShadowOffset:CGSizeMake(0.0, 5)];
|
||||
|
||||
//localize the UI
|
||||
[_lblAlmostDone setText:[NSString stringWithFormat:@"%@, %@", self.username, [NSLocalizedString(@"YouAreAlmostDonePhrase", nil) lowercaseString]]];
|
||||
[_lblAlmostDone setAdjustsFontSizeToFitWidth:YES];
|
||||
[_lblAlmostDone setFont:[RobloxInfo thisDeviceIsATablet] ? [RobloxTheme fontH3] : [RobloxTheme fontBodyLarge]];
|
||||
[_lblAlmostDone setTextColor:[RobloxTheme colorGray1]];
|
||||
|
||||
[_lblSelectUserName setText:NSLocalizedString(@"SelectUserNameWord", nil)];
|
||||
[_lblSelectUserName setFont:[RobloxInfo thisDeviceIsATablet] ? [RobloxTheme fontH3] : [RobloxTheme fontBodyLarge]];
|
||||
[_lblSelectUserName setTextColor:[RobloxTheme colorGray1]];
|
||||
|
||||
[_lblAlreadyHaveAccount setText:NSLocalizedString(@"AlreadyHaveARobloxAccountPhrase", nil)];
|
||||
[_lblAlreadyHaveAccount setFont:[RobloxTheme fontBodySmall]];
|
||||
[_lblAlreadyHaveAccount setTextColor:[RobloxInfo thisDeviceIsATablet] ? [RobloxTheme colorGray2] : [RobloxTheme colorGray1]];
|
||||
|
||||
[_lblNameDescription setText:NSLocalizedString(@"SelectUserNameDescriptionPhrase", nil)];
|
||||
[_lblNameDescription setFont:[RobloxInfo thisDeviceIsATablet] ? [RobloxTheme fontBody] : [RobloxTheme fontBodySmall]];
|
||||
[_lblNameDescription setTextColor:[RobloxTheme colorGray2]];
|
||||
|
||||
[_btnAccept setTitle:NSLocalizedString(@"SignupWord", nil) forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToModalSubmitButton:_btnAccept];
|
||||
|
||||
[_btnCancel setTitle:NSLocalizedString(@"CancelWord", nil) forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToModalCancelButton:_btnCancel];
|
||||
|
||||
|
||||
__weak SocialSignUpViewController* weakSelf = self;
|
||||
[_txtUsername setTitle:NSLocalizedString(@"UsernameWord", nil)];
|
||||
[_txtUsername setHint:NSLocalizedString(@"UsernameRequirements", nil)];
|
||||
[_txtUsername setNextResponder:nil];
|
||||
[_txtUsername setValidationBlock:^{
|
||||
[[SignupVerifier sharedInstance] checkIfValidUsername:weakSelf.txtUsername.text completion:^(BOOL success, NSString *validMessage)
|
||||
{
|
||||
if (success)
|
||||
[weakSelf.txtUsername markAsValid];
|
||||
else
|
||||
{
|
||||
[weakSelf.txtUsername markAsInvalid];
|
||||
|
||||
//handle bad username
|
||||
if ([validMessage isEqualToString:NSLocalizedString(@"UsernameCommon", nil)])
|
||||
{
|
||||
[[SignupVerifier sharedInstance] getAlternateUsername:weakSelf.txtUsername.text
|
||||
completion:^(BOOL success, NSString *alternateMessage)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
[RobloxHUD prompt:[NSString stringWithFormat:NSLocalizedString(@"UsernameTaken", nil), alternateMessage]
|
||||
withTitle:NSLocalizedString(@"UsernameTakenTitle", nil)
|
||||
onOK:^
|
||||
{
|
||||
[weakSelf.txtUsername setText:alternateMessage];
|
||||
[weakSelf.txtUsername markAsValid];
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonPopUpOkay withContext:RBXAContextSocialSignup];
|
||||
}
|
||||
onCancel:^
|
||||
{
|
||||
[weakSelf.txtUsername markAsInvalid];
|
||||
[weakSelf.txtUsername showError:validMessage];
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonPopUpCancel withContext:RBXAContextSocialSignup];
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[weakSelf.txtUsername markAsInvalid];
|
||||
[weakSelf.txtUsername showError:validMessage];
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
[weakSelf.txtUsername markAsInvalid];
|
||||
[weakSelf.txtUsername showError:validMessage];
|
||||
}
|
||||
}
|
||||
}];
|
||||
}];
|
||||
|
||||
|
||||
//load the user's profile picture
|
||||
[_shadowContainer setHidden:YES];
|
||||
[_imgIdentity loadBadgeWithURL:self.photoURL
|
||||
withSize:_imgIdentity.size
|
||||
completion:^
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
//stop animating the loading spinner
|
||||
[_loadingSpinner stopAnimating];
|
||||
[_loadingSpinner setHidden:YES];
|
||||
[_shadowContainer setHidden:NO];
|
||||
});
|
||||
}];
|
||||
|
||||
//pre-load a username
|
||||
_txtUsername.hidden = YES;
|
||||
[[SignupVerifier sharedInstance] getAlternateUsername:self.username
|
||||
completion:^(BOOL success, NSString *message)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
_txtUsername.hidden = NO;
|
||||
if (success)
|
||||
[_txtUsername setText:message];
|
||||
});
|
||||
}];
|
||||
}
|
||||
- (void) viewWillLayoutSubviews {
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
BOOL isPreiOS8 = NSFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_8_0;
|
||||
if (isPreiOS8 && [RobloxInfo thisDeviceIsATablet])
|
||||
{
|
||||
self.view.superview.bounds = CGRectMake(0, 0, 540, 540);
|
||||
}
|
||||
|
||||
|
||||
[self.loadingSpinner centerInFrame:_shadowContainer.frame];
|
||||
|
||||
//make a circular image
|
||||
CGFloat centerX = self.view.frame.size.width * 0.5;
|
||||
CGFloat halfHeight = _shadowContainer.height * 0.5;
|
||||
[_shadowContainer setFrame:CGRectMake(centerX - halfHeight, _shadowContainer.y, _shadowContainer.height, _shadowContainer.height)];
|
||||
UIBezierPath* bpath = [UIBezierPath bezierPathWithRoundedRect:_imgIdentity.bounds cornerRadius:(_imgIdentity.width * 0.5)];
|
||||
CAShapeLayer* circularMask = [CAShapeLayer layer];
|
||||
circularMask.path = bpath.CGPath;
|
||||
[_imgIdentity.layer setMask:circularMask];
|
||||
|
||||
_shadowContainer.layer.shadowPath = bpath.CGPath;
|
||||
}
|
||||
|
||||
- (IBAction) createAccount:(id)sender
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonSignup withContext:RBXAContextSocialSignup];
|
||||
|
||||
[_btnAccept setHidden:YES];
|
||||
[_btnCancel setHidden:YES];
|
||||
[_loadingSpinner setHidden:NO];
|
||||
[_loadingSpinner startAnimating];
|
||||
|
||||
[[LoginManager sharedInstance] doSocialSignupWithUsername:_txtUsername.text
|
||||
gigyaID:self.gigyaUID
|
||||
birthday:self.birthday
|
||||
gender:self.gender
|
||||
email:self.email.length > 0 ? self.email : nil
|
||||
completion:^(bool success, NSString *message)
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^
|
||||
{
|
||||
[_btnAccept setHidden:NO];
|
||||
[_btnCancel setHidden:NO];
|
||||
[_loadingSpinner setHidden:YES];
|
||||
[_loadingSpinner stopAnimating];
|
||||
|
||||
if (success)
|
||||
{
|
||||
//TODO : play welcome animation
|
||||
|
||||
|
||||
//dismiss the view and go into the app
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
if ([message isEqualToString:NSLocalizedString(@"SocialSignupErrorCaptchaNeeded", nil)])
|
||||
{
|
||||
//open up a captcha so we can attempt to sign up
|
||||
NonRotatableNavigationController* navigation = [LoginManager CaptchaForSocialSignupWithUsername:_txtUsername.text
|
||||
andV1Completion:^(bool success, NSString *message)
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
//successfully solved captcha, retry the account creation
|
||||
[self createAccount:nil];
|
||||
}
|
||||
}];
|
||||
}
|
||||
andV2Completion:^(NSError *captchaError)
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^
|
||||
{
|
||||
if ([RBXFunctions isEmpty:captchaError])
|
||||
{
|
||||
//successfully solved captcha, retry the account creation
|
||||
[self createAccount:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
[RobloxHUD prompt:captchaError.domain withTitle:NSLocalizedString(@"ErrorWord", nil)];
|
||||
}
|
||||
}];
|
||||
}];
|
||||
[self presentViewController:navigation animated:YES completion:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
//otherwise print the message out to the user
|
||||
[RobloxAlert RobloxAlertWithMessage:message];
|
||||
}
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
- (IBAction) cancelSignUp:(id)sender
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonClose withContext:RBXAContextSocialSignup];
|
||||
[[LoginManager sharedInstance] doSocialLogout];
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
//Delegate functions
|
||||
- (BOOL)disablesAutomaticKeyboardDismissal
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
- (void) resignAllResponders
|
||||
{
|
||||
//[_txtUsername resignFirstResponder];
|
||||
[self.view endEditing:YES];
|
||||
}
|
||||
- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
|
||||
{
|
||||
//NSLog(@"GestureRecognizer : %@", touch);
|
||||
UIView* touchedView = touch.view;
|
||||
if (touchedView == self.view || touchedView == _whiteView)
|
||||
{
|
||||
[self resignAllResponders];
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,15 @@
|
||||
//
|
||||
// AgreementController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 5/22/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface TermsAgreementController : UIViewController
|
||||
|
||||
@property(strong, nonatomic) NSString* url;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// TermsAgreementController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 5/22/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TermsAgreementController.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "UIView+Position.h"
|
||||
|
||||
@interface TermsAgreementController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation TermsAgreementController
|
||||
{
|
||||
IBOutlet UIWebView *_webView;
|
||||
IBOutlet UINavigationBar *_navBar;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[RobloxTheme applyToModalPopupNavBar:_navBar];
|
||||
|
||||
NSURL *url = [NSURL URLWithString:self.url];
|
||||
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
|
||||
[_webView loadRequest:requestObj];
|
||||
[_webView setScalesPageToFit:YES];
|
||||
}
|
||||
|
||||
- (void)viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
[_webView stopLoading];
|
||||
}
|
||||
|
||||
|
||||
- (IBAction)closeButtonTouched:(id)sender
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// WelcomeScreenController.h
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 5/20/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface WelcomeScreenController : UIViewController<UIPickerViewDataSource, UIPickerViewDelegate, UIWebViewDelegate>
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,433 @@
|
||||
//
|
||||
// WelcomeScreenController.m
|
||||
// RobloxMobile
|
||||
//
|
||||
// Created by Ariel Lichtin on 5/20/14.
|
||||
// Copyright (c) 2014 ROBLOX. All rights reserved.
|
||||
//
|
||||
|
||||
#import "WelcomeScreenController.h"
|
||||
#import "RobloxInfo.h"
|
||||
#import "RobloxGoogleAnalytics.h"
|
||||
#import "RobloxMemoryManager.h"
|
||||
#import "LoginManager.h"
|
||||
#import "RobloxNotifications.h"
|
||||
#import "RobloxTheme.h"
|
||||
#import "RobloxAlert.h"
|
||||
#import "Flurry.h"
|
||||
#import "UserInfo.h"
|
||||
#import "RBActivityIndicatorView.h"
|
||||
#import "TermsAgreementController.h"
|
||||
#import "ABTestManager.h"
|
||||
#import "RBXEventReporter.h"
|
||||
#import "NonRotatableNavigationController.h"
|
||||
#import "NSDictionary+Parsing.h"
|
||||
#import "SocialSignUpViewController.h"
|
||||
#import "SignUpScreenController.h"
|
||||
#import "RBXFunctions.h"
|
||||
#import "LoginScreenController.h"
|
||||
#import "LoginManager.h"
|
||||
|
||||
//---METRICS---
|
||||
#define WSC_playNowPressed @"WELCOME SCREEN - Play Now Pressed"
|
||||
#define WSC_loginFinished @"WELCOME SCREEN - Log In Finished"
|
||||
#define WSC_signupFinished @"WELCOME SCREEN - Sign Up Finished"
|
||||
|
||||
@implementation WelcomeScreenController
|
||||
{
|
||||
IBOutlet UIButton *_loginButton;
|
||||
IBOutlet UIButton *_signUpButton;
|
||||
IBOutlet UIButton *_playNowButton;
|
||||
IBOutlet UIButton *_gigyaLogin;
|
||||
IBOutlet UILabel* _versionLabel;
|
||||
IBOutlet RBActivityIndicatorView* _loadingSpinner;
|
||||
IBOutlet UIPickerView *_environmentPicker;
|
||||
IBOutlet UIWebView *_finePrint;
|
||||
NSMutableArray *envs;
|
||||
|
||||
BOOL _transitionedToGamesPage;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
[self initializeUIElements];
|
||||
|
||||
//by-pass this screen if we can help it
|
||||
if ([[ABTestManager sharedInstance] IsInTestMobileGuestMode])
|
||||
{
|
||||
#ifndef RBX_INTERNAL
|
||||
[self goToGamesPage];
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
//report to the server that the app is loaded and ready to go
|
||||
[[RBXEventReporter sharedInstance] reportScreenLoaded:RBXAContextLanding];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) viewWillDisappear:(BOOL)animated
|
||||
{
|
||||
[super viewWillDisappear:animated];
|
||||
|
||||
[_loadingSpinner stopAnimating];
|
||||
}
|
||||
|
||||
- (void) viewWillAppear:(BOOL)animated
|
||||
{
|
||||
[super viewWillAppear:animated];
|
||||
|
||||
[self hideUIButtons];
|
||||
//[_loadingSpinner startAnimating];
|
||||
|
||||
_transitionedToGamesPage = NO;
|
||||
|
||||
//auto log in will set the CurrentPlayer to be logged in if it worked
|
||||
if ([[UserInfo CurrentPlayer] userLoggedIn])
|
||||
{
|
||||
[self goToGamesPage];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subscribe to notifications
|
||||
[self addListeners:nil];
|
||||
|
||||
[_loadingSpinner stopAnimating];
|
||||
[self revealUIButtons];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) viewWillLayoutSubviews
|
||||
{
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
if (_gigyaLogin)
|
||||
[RobloxTheme applyToFacebookButton:_gigyaLogin];
|
||||
}
|
||||
////////////////////////////////////////////////////////////////
|
||||
// UI Functions
|
||||
////////////////////////////////////////////////////////////////
|
||||
- (void) initializeUIElements
|
||||
{
|
||||
//Environment picker for the internal view
|
||||
#ifndef RBX_INTERNAL
|
||||
_environmentPicker.hidden = true;
|
||||
#else
|
||||
[self populateEnvironmentPicker];
|
||||
_environmentPicker.dataSource = self;
|
||||
_environmentPicker.delegate = self;
|
||||
|
||||
//FOR FACEBOOK SIGNUP DEBUGGING
|
||||
[_environmentPicker selectRow:7 inComponent:0 animated:NO];
|
||||
#endif
|
||||
|
||||
// Format buttons
|
||||
[_loginButton setTitle:NSLocalizedString(@"LoginWord", nil) forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToWelcomeLoginButton:_loginButton];
|
||||
|
||||
[_signUpButton setTitle:NSLocalizedString(@"SignupWord", nil) forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToWelcomeLoginButton:_signUpButton];
|
||||
|
||||
[_playNowButton setTitle:NSLocalizedString(@"PlayNowButtonLabel", nil) forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToWelcomeLoginButton:_playNowButton];
|
||||
|
||||
[_gigyaLogin setTitle:NSLocalizedString(@"SignInSocialWord", nil) forState:UIControlStateNormal];
|
||||
[RobloxTheme applyToFacebookButton:_gigyaLogin];
|
||||
if (![[LoginManager sharedInstance] isFacebookEnabled])
|
||||
[_gigyaLogin setHidden:YES];
|
||||
|
||||
//Set the version number
|
||||
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
|
||||
if ([version length])
|
||||
_versionLabel.text = version;
|
||||
else
|
||||
[_versionLabel setHidden:YES];
|
||||
|
||||
|
||||
// Fine print
|
||||
NSString *htmlFile = [[NSBundle mainBundle] pathForResource:@"SignUpDisclamer" ofType:@"html" inDirectory:nil];
|
||||
if(htmlFile)
|
||||
{
|
||||
NSString* htmlContents = [NSString stringWithContentsOfFile:htmlFile encoding:NSUTF8StringEncoding error:NULL];
|
||||
htmlContents = [htmlContents stringByReplacingOccurrencesOfString:@"textPlaceholder" withString:NSLocalizedString(@"HomeFinePrintWords", nil)];
|
||||
[_finePrint setDelegate:self];
|
||||
[_finePrint loadData:[htmlContents dataUsingEncoding:NSUTF8StringEncoding] MIMEType:@"text/html" textEncodingName:@"UTF-8" baseURL:[NSURL URLWithString:@""]];
|
||||
[_finePrint setBackgroundColor:[UIColor clearColor]];
|
||||
[_finePrint setOpaque:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) hideUIButtons
|
||||
{
|
||||
_loginButton.hidden = YES;
|
||||
_signUpButton.hidden = YES;
|
||||
_playNowButton.hidden = YES;
|
||||
_gigyaLogin.hidden = YES;
|
||||
}
|
||||
|
||||
- (void) revealUIButtons
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^
|
||||
{
|
||||
float transitionTime = 1.0;
|
||||
_loginButton.hidden = NO; _loginButton.alpha = 0.0;
|
||||
_signUpButton.hidden = NO; _signUpButton.alpha = 0.0;
|
||||
_playNowButton.hidden = NO; _playNowButton.alpha = 0.0;
|
||||
|
||||
if ([[LoginManager sharedInstance] isFacebookEnabled])
|
||||
{
|
||||
_gigyaLogin.hidden = NO; _gigyaLogin.alpha = 0.0;
|
||||
}
|
||||
|
||||
[UIView animateWithDuration:transitionTime
|
||||
animations:^
|
||||
{
|
||||
_loginButton.alpha = 1.0;
|
||||
_signUpButton.alpha = 1.0;
|
||||
_playNowButton.alpha = 1.0;
|
||||
_gigyaLogin.alpha = 1.0;
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (IBAction)playNowTouchUpInside:(id)sender
|
||||
{
|
||||
//NSLOG_PRETTY_FUNCTION;
|
||||
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonPlayNow withContext:RBXAContextLanding];
|
||||
|
||||
[RobloxGoogleAnalytics setPageViewTracking:@"Login/GuestMode"];
|
||||
|
||||
[self goToGamesPage];
|
||||
}
|
||||
|
||||
-(IBAction)loginTouchUpInside:(id)sender
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonLogin withContext:RBXAContextLanding];
|
||||
}
|
||||
|
||||
-(IBAction)signupTouchUpInside:(id)sender
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonSignup withContext:RBXAContextLanding];
|
||||
|
||||
|
||||
|
||||
NSString* controllerName;
|
||||
if ([[LoginManager sharedInstance] isFacebookEnabled])
|
||||
controllerName = @"SignUpScreenControllerWithSocial";
|
||||
else if ([LoginManager apiProxyEnabled])
|
||||
controllerName = @"SignUpAPIScreenController";
|
||||
else
|
||||
controllerName = @"SignUpScreenController";
|
||||
|
||||
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:[RobloxInfo getStoryboardName] bundle:nil];
|
||||
SignUpScreenController* controller = (SignUpScreenController*)[storyboard instantiateViewControllerWithIdentifier:controllerName];
|
||||
controller.modalPresentationStyle = UIModalPresentationFormSheet;
|
||||
[self.navigationController presentViewController:controller animated:YES completion:nil];
|
||||
}
|
||||
|
||||
-(IBAction)aboutTouchUpInside:(id)sender
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonAbout withContext:RBXAContextLanding];
|
||||
}
|
||||
|
||||
-(IBAction)gigyaTouchUpInside:(id)sender
|
||||
{
|
||||
[[RBXEventReporter sharedInstance] reportButtonClick:RBXAButtonSocialSignIn withContext:RBXAContextLanding];
|
||||
[self hideUIButtons];
|
||||
[[LoginManager sharedInstance] doSocialLoginFromController:self
|
||||
forProvider:[LoginManager ProviderNameFacebook]
|
||||
withCompletion:^(bool success, NSString *message)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
if ([message isEqualToString:@"newUser"])
|
||||
{
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
//Show social sign up controller
|
||||
UIStoryboard* storyboard = [UIStoryboard storyboardWithName:[RobloxInfo getStoryboardName] bundle:nil];
|
||||
SocialSignUpViewController* controller = [storyboard instantiateViewControllerWithIdentifier:@"SocialSignUpViewController"];
|
||||
[controller setModalPresentationStyle:UIModalPresentationFormSheet];
|
||||
[self.navigationController presentViewController:controller animated:YES completion:nil];
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (message)
|
||||
[RobloxAlert RobloxAlertWithMessage:message];
|
||||
[[LoginManager sharedInstance] doSocialLogout];
|
||||
}
|
||||
dispatch_async(dispatch_get_main_queue(), ^{ [self revealUIButtons]; });
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Login / segue functions
|
||||
////////////////////////////////////////////////////////////////
|
||||
- (void) addListeners:(NSNotification*) notification
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(signUpFinished:) name:RBX_NOTIFY_SIGNUP_COMPLETED object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:RBX_NOTIFY_LOGGED_OUT object:nil];
|
||||
}
|
||||
|
||||
- (void) signUpFinished:(NSNotification*) notification
|
||||
{
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[Flurry logEvent:WSC_signupFinished];
|
||||
[self goToGamesPage];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void) goToGamesPage
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(addListeners:) name:RBX_NOTIFY_LOGGED_OUT object:nil];
|
||||
|
||||
if(!_transitionedToGamesPage)
|
||||
{
|
||||
_transitionedToGamesPage = YES;
|
||||
[RBXFunctions dispatchOnMainThread:^{ [self performSegueWithIdentifier:@"goToGamesPage" sender:nil]; }];
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Internal version's environment picker functions
|
||||
////////////////////////////////////////////////////////////////
|
||||
- (void)populateEnvironmentPicker
|
||||
{
|
||||
envs = [[NSMutableArray alloc] init];
|
||||
BOOL iPad = [RobloxInfo thisDeviceIsATablet];
|
||||
// NSString* m = (iPad) ? @"" : @"m.";
|
||||
NSString* www = (iPad) ? @"www." : @"m.";
|
||||
|
||||
// PROD
|
||||
[envs addObject:[NSString stringWithFormat:@"http://%@watrbx.wtf/", www]];
|
||||
|
||||
// Client Test Env
|
||||
[envs addObject:[NSString stringWithFormat:@"http://%@gametest1.pizzaboxer.fun/", www]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://%@gametest2.pizzaboxer.fun/", www]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://%@gametest3.pizzaboxer.fun/", www]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://%@gametest4.pizzaboxer.fun/", www]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://%@gametest5.pizzaboxer.fun/", www]];
|
||||
|
||||
// Web Test Env
|
||||
[envs addObject:[NSString stringWithFormat:@"http://%@sitetest1.pizzaboxer.fun/", www]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://%@sitetest2.pizzaboxer.fun/", www]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://%@sitetest3.pizzaboxer.fun/", www]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://%@sitetest4.pizzaboxer.fun/", www]];
|
||||
|
||||
// Web Personal Test Env
|
||||
[envs addObject:[NSString stringWithFormat:@"http://akshay.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://alex.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://andrew.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://anthony.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://antoni.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://baker.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://ernie.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://guru.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://isaiah.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://linjun.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://linjunmobile.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://je.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://jeremy.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://manika.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://rosemary.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://shailendra.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://vlad.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://wooldridge.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://yunpeng.sitetest3.pizzaboxer.fun/"]];
|
||||
[envs addObject:[NSString stringWithFormat:@"http://ying.sitetest3.pizzaboxer.fun/"]];
|
||||
}
|
||||
|
||||
- (NSInteger)pickerView:(UIPickerView *)thePickerView numberOfRowsInComponent:(NSInteger)component
|
||||
{
|
||||
return envs.count;
|
||||
}
|
||||
|
||||
// Number of columns
|
||||
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)thePickerView
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
// This is where we link the data to the picker
|
||||
- (NSString *)pickerView:(UIPickerView *)thePickerView titleForRow:(NSInteger)row forComponent:(NSInteger)component
|
||||
{
|
||||
return [envs objectAtIndex:row];
|
||||
}
|
||||
|
||||
// Updates the client settings with the new environment
|
||||
- (void)pickerView:(UIPickerView *)thePickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component
|
||||
{
|
||||
[RobloxInfo setBaseUrl:[envs objectAtIndex:row]];
|
||||
|
||||
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW,0), ^{
|
||||
[[RobloxWebUtility sharedInstance] updateAllClientSettingsWithCompletion:^{
|
||||
// initalize the AB Test - this may take a bit
|
||||
[[ABTestManager sharedInstance] fetchExperimentsForBrowserTracker];
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[self revealUIButtons];
|
||||
}];
|
||||
}];
|
||||
});
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[[RobloxMemoryManager sharedInstance] startMemoryBouncer];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Fine print
|
||||
////////////////////////////////////////////////////////////////
|
||||
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
|
||||
{
|
||||
NSString* urlRequestString = [[request URL] absoluteString];
|
||||
NSRange finePrintInit = [urlRequestString rangeOfString:@"file"];
|
||||
if(finePrintInit.location != NSNotFound)
|
||||
return YES;
|
||||
|
||||
[self performSegueWithIdentifier:@"FinePrintSegue" sender:urlRequestString];
|
||||
//[[UIApplication sharedApplication] openURL:request.URL];
|
||||
|
||||
return NO;
|
||||
}
|
||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
|
||||
{
|
||||
//NSLog(@"Segue Identifier: %@", segue.identifier);
|
||||
|
||||
if([segue.identifier isEqualToString:@"FinePrintSegue"])
|
||||
{
|
||||
TermsAgreementController *controller = (TermsAgreementController *)segue.destinationViewController;
|
||||
//NSLog(@"Controller: %d", controller != nil ? 1 : 0);
|
||||
controller.url = sender;
|
||||
}
|
||||
else if ([segue.identifier isEqualToString:@"LoginScreenSegue"])
|
||||
{
|
||||
//NSLog(@"modally displaying login screen controller");
|
||||
LoginScreenController *vc = (LoginScreenController *)segue.destinationViewController;
|
||||
vc.dismissalCompletionHandler = ^(LoginScreenDismissalType dismissType, NSError *loginError) {
|
||||
|
||||
if (dismissType == LoginScreenDismissalLoginSuccess) {
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[Flurry logEvent:WSC_loginFinished];
|
||||
[self goToGamesPage];
|
||||
}];
|
||||
} else {
|
||||
[RBXFunctions dispatchOnMainThread:^{
|
||||
[self revealUIButtons];
|
||||
[_loadingSpinner stopAnimating];
|
||||
}];
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user