// // SLComposeTableViewController.m // PhotoShareExtension // // Created by Russell on 8/5/15. // // #import "ReceiverTableViewController.h" typedef NS_ENUM(NSUInteger, NixTableViewSection) { kPlaylistHeader, kPlaylist, kFriendHeader, kFriend }; @interface ReceiverTableViewController () @property (nonatomic, strong) NSArray *playlistValues; @property (nonatomic, strong) NSArray *friendValues; @property (nonatomic, strong) NSMutableArray *selectedPlaylists; @property (nonatomic, strong) NSMutableArray *selectedFriends; - (IBAction)send:(id)sender; - (void)preparePlaylistValues; - (void)prepareFriendValues; - (void)reloadData; - (NSString *)headerTitleWithPlaylistCounts:(NSUInteger)playlists andFriendCounts:(NSUInteger)friends; - (void)updateHeaderTitleWithCounts; - (void)loadAllData:(void (^)(NSError *))completionHandler; - (void)loadPlaylistsWithCompletionHandler:(void (^)(NSError *))completionHandler; - (void)loadFriendsWithCompletionHandler:(void (^)(NSError *))completionHandler; @end @implementation ReceiverTableViewController #pragma mark - IB - (IBAction)send:(id)sender { self.selectedPlaylists = [[NSMutableArray alloc] initWithCapacity:self.containerViewController.playlistData.count]; self.selectedFriends = [[NSMutableArray alloc] initWithCapacity:self.containerViewController.friendData.count]; NSArray *selectedIndexPaths = [self.tableView indexPathsForSelectedRows]; [selectedIndexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) { @autoreleasepool { switch (indexPath.section) { case kPlaylist: { NSString *playlistId = self.containerViewController.playlistData[indexPath.row][@"id"]; [self.selectedPlaylists addObject:playlistId]; break; } case kFriend: { NSString *friendUsername = self.containerViewController.friendData[indexPath.row][@"username"]; [self.selectedFriends addObject:friendUsername]; break; } default: break; } } }]; self.containerViewController.playlistIds = [self.selectedPlaylists copy]; self.containerViewController.friendUsernames = [self.selectedFriends copy]; [self showActivityIndicator]; self.navigationItem.rightBarButtonItem.enabled = NO; self.navigationItem.hidesBackButton = YES; self.navigationController.interactivePopGestureRecognizer.enabled = NO; [self.containerViewController send]; } #pragma mark - Navigation - (void)viewDidLoad { [super viewDidLoad]; // Uncomment the following line to preserve selection between presentations. // self.clearsSelectionOnViewWillAppear = NO; // Uncomment the following line to display an Edit button in the navigation bar for this view controller. // self.navigationItem.rightBarButtonItem = self.editButtonItem; self.tableView.editing = YES; self.refreshControl = [[UIRefreshControl alloc] init]; [self.refreshControl addTarget:self action:@selector(reloadData) forControlEvents:UIControlEventValueChanged]; if (!self.containerViewController.playlistData || !self.containerViewController.friendData) { [self showActivityIndicator]; [self loadAllData:^(NSError *error) { [self hideActivityIndicator]; if (error) { UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"signin_again_title", nil) message:NSLocalizedString(@"signin_again_message", nil) preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"close", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [self.containerViewController.extensionContext cancelRequestWithError:error]; }]]; [self presentViewController:alertController animated:YES completion:nil]; } else { if (self.playlistValues.count == 0 && self.friendValues.count == 0) { NSError *err; UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"no_receiver_title", nil) message:NSLocalizedString(@"no_receiver_message", nil) preferredStyle:UIAlertControllerStyleAlert]; [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"close", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { [self.containerViewController.extensionContext cancelRequestWithError:err]; }]]; [self presentViewController:alertController animated:YES completion:nil]; } else { [self.tableView reloadData]; } } }]; } } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; self.navigationItem.rightBarButtonItem.enabled = NO; NSArray *selectedReceiverIndexPaths = self.containerViewController.selectedReceiverIndexPaths; [self.containerViewController verifyServiceAvailableWithCompletionHandler:^(BOOL success) { if ([selectedReceiverIndexPaths count]) { self.navigationItem.rightBarButtonItem.enabled = YES; } }]; if (self.containerViewController.playlistData && self.containerViewController.friendData) { [self preparePlaylistValues]; [self prepareFriendValues]; [self.tableView reloadData]; if ([selectedReceiverIndexPaths count]) { [selectedReceiverIndexPaths enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) { [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; }]; [self updateHeaderTitleWithCounts]; } } } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; self.friendValues = nil; self.playlistValues = nil; self.containerViewController.friendData = nil; self.containerViewController.playlistData = nil; self.containerViewController.selectedReceiverIndexPaths = nil; //[self.tableView indexPathsForSelectedRows]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #pragma mark - Private - (void)showActivityIndicator { UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; self.navigationItem.titleView = activityIndicator; [activityIndicator startAnimating]; } - (void)hideActivityIndicator { UIActivityIndicatorView *activityIndicator = (UIActivityIndicatorView *)self.navigationItem.titleView; if ([activityIndicator isKindOfClass:[UIActivityIndicatorView class]]) { [activityIndicator stopAnimating]; } self.navigationItem.titleView = nil; } - (void)preparePlaylistValues { NSMutableArray *playlistValues = [NSMutableArray arrayWithCapacity:self.containerViewController.playlistData.count]; for (NSDictionary *record in self.containerViewController.playlistData) { @autoreleasepool { NSString *name = record[@"name"]; [playlistValues addObject:name]; } } self.playlistValues = playlistValues; } - (void)prepareFriendValues { NSMutableArray *friendValues = [NSMutableArray arrayWithCapacity:self.containerViewController.friendData.count]; for (NSDictionary *record in self.containerViewController.friendData) { @autoreleasepool { NSString *name = record[@"fullName"]; [friendValues addObject:name]; } } self.friendValues = friendValues; } - (void)reloadData { NSArray *playlistData = [self.containerViewController.playlistData copy]; NSArray *friendData = [self.containerViewController.friendData copy]; NSMutableArray *selectedPlaylistIds = [[NSMutableArray alloc] initWithCapacity:playlistData.count]; NSMutableArray *selectedFriendUsernames = [[NSMutableArray alloc] initWithCapacity:friendData.count]; [[self.tableView indexPathsForSelectedRows] enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) { switch (indexPath.section) { case kPlaylist: [selectedPlaylistIds addObject:playlistData[indexPath.row][@"id"]]; break; case kFriend: [selectedFriendUsernames addObject:friendData[indexPath.row][@"username"]]; break; default: break; } }]; [self loadAllData:^(NSError *error) { [self.refreshControl endRefreshing]; dispatch_async(dispatch_get_main_queue(), ^{ NSArray *freshPlaylistData = [self.containerViewController.playlistData copy]; NSArray *freshFriendData = [self.containerViewController.friendData copy]; [freshPlaylistData enumerateObjectsUsingBlock:^(NSDictionary *playlist, NSUInteger idx, BOOL *stop) { @autoreleasepool { if ([selectedPlaylistIds containsObject:playlist[@"id"]]) { NSIndexPath *indexPath = [NSIndexPath indexPathForRow:idx inSection:kPlaylist]; [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; } } }]; [freshFriendData enumerateObjectsUsingBlock:^(NSDictionary *friend, NSUInteger idx, BOOL *stop) { @autoreleasepool { if ([selectedFriendUsernames containsObject:friend[@"username"]]) { NSIndexPath *indexPath = [NSIndexPath indexPathForRow:idx inSection:kFriend]; [self.tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; } } }]; }); }]; } - (NSString *)headerTitleWithPlaylistCounts:(NSUInteger)playlists andFriendCounts:(NSUInteger)friends { NSString *rawString; if (playlists == 0) { if (friends == 1) { rawString = NSLocalizedString(@"navigation_header_n_selected_friend", nil); } else { rawString = NSLocalizedString(@"navigation_header_n_selected_friends", nil); } } if (friends == 0) { if (playlists == 1) { rawString = NSLocalizedString(@"navigation_header_n_selected_playlist", nil); } else { rawString = NSLocalizedString(@"navigation_header_n_selected_playlists", nil); } } if (0 < playlists && 0 < friends) { rawString = NSLocalizedString(@"navigation_header_n_selected_receivers", nil); } if (playlists + friends == 0) { return NSLocalizedString(@"navigation_header_select_receivers", nil); } return [NSString stringWithFormat:rawString, playlists + friends]; } - (void)updateHeaderTitleWithCounts { __block NSUInteger playlistCount = 0; __block NSUInteger friendCount = 0; [[self.tableView indexPathsForSelectedRows] enumerateObjectsUsingBlock:^(NSIndexPath *indexPath, NSUInteger idx, BOOL *stop) { switch (indexPath.section) { case kPlaylist: playlistCount++; break; case kFriend: friendCount++; break; } }]; NSString *title = [self headerTitleWithPlaylistCounts:playlistCount andFriendCounts:friendCount]; self.navigationItem.title = title; } #pragma mark Network - (void)loadAllData:(void (^)(NSError *))completionHandler { __block NSError *error = nil; dispatch_group_t dispatchGroup = dispatch_group_create(); dispatch_group_enter(dispatchGroup); [self loadPlaylistsWithCompletionHandler:^(NSError *playlistsError) { if (playlistsError) error = playlistsError; dispatch_group_leave(dispatchGroup); }]; dispatch_group_enter(dispatchGroup); [self loadFriendsWithCompletionHandler:^(NSError *friendsError) { if (friendsError) error = friendsError; dispatch_group_leave(dispatchGroup); }]; dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{ completionHandler(error); }); } - (void)loadPlaylistsWithCompletionHandler:(void (^)(NSError *))completionHandler { [self.containerViewController.api playlists:^(id data, NSURLResponse *response, NSError *error) { if (error || !data || [data isKindOfClass:[NSDictionary class]]) { completionHandler(error); return; } if ([data isKindOfClass:[NSArray class]]) { NSPredicate *predicateByTitle = [NSPredicate predicateWithFormat:@"SELF.type == %@", @"normal"]; NSArray *playlists = [(NSArray*)data filteredArrayUsingPredicate:predicateByTitle]; self.containerViewController.playlistData = playlists; [self preparePlaylistValues]; [self.tableView reloadData]; } completionHandler(nil); }]; } - (void)loadFriendsWithCompletionHandler:(void (^)(NSError *))completionHandler { [self.containerViewController.api friends:^(NSDictionary *data, NSURLResponse *response, NSError *error) { if (error || data[@"error"]) { completionHandler(error); return; } NSString *predString = [NSString stringWithFormat:@"(status='%@')", @"success"]; NSPredicate *pred = [NSPredicate predicateWithFormat:predString]; NSArray *friendsArr = [data[@"friends"] filteredArrayUsingPredicate:pred]; self.containerViewController.friendData = friendsArr; [self prepareFriendValues]; [self.tableView reloadData]; completionHandler(nil); }]; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Return the number of sections. return 4; } - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section { switch (section) { case kPlaylistHeader: return self.playlistValues.count ? NSLocalizedString(@"table_header_playlist", nil) : nil; case kFriendHeader: return self.friendValues.count ? NSLocalizedString(@"table_header_friend", nil) : nil; default: return nil; } } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Return the number of rows in the section. switch (section) { case kPlaylist: return self.playlistValues.count; case kFriend: return self.friendValues.count; case kPlaylistHeader: return self.playlistValues.count ? 1 : 0; case kFriendHeader: return self.friendValues.count ? 1 : 0; default: return 0; } } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellId = @"SLComposeTableViewControllerCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId]; } if ([[tableView indexPathsForSelectedRows] containsObject:indexPath]) { [tableView selectRowAtIndexPath:indexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; } switch (indexPath.section) { case kPlaylist: cell.textLabel.text = self.playlistValues[indexPath.row]; break; case kFriend: cell.textLabel.text = self.friendValues[indexPath.row]; break; case kPlaylistHeader: cell.textLabel.text = NSLocalizedString(@"table_select_all_playlists", nil); break; case kFriendHeader: cell.textLabel.text = NSLocalizedString(@"table_select_all_friends", nil); break; default: break; } return cell; } /* // Override to support conditional editing of the table view. - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath { // Return NO if you do not want the specified item to be editable. return YES; } */ /* // Override to support editing the table view. - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { // Delete the row from the data source [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; } else if (editingStyle == UITableViewCellEditingStyleInsert) { // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view } } */ /* // Override to support rearranging the table view. - (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath { } */ #pragma mark - UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { switch (indexPath.section) { case kPlaylistHeader: { for (NSUInteger i = 0; i < [tableView numberOfRowsInSection:kPlaylist]; i++) { NSIndexPath *playlistIndexPath = [NSIndexPath indexPathForRow:i inSection:kPlaylist]; [tableView selectRowAtIndexPath:playlistIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; } break; } case kFriendHeader: { for (NSUInteger i = 0; i < [tableView numberOfRowsInSection:kFriend]; i++) { NSIndexPath *friendIndexPath = [NSIndexPath indexPathForRow:i inSection:kFriend]; [tableView selectRowAtIndexPath:friendIndexPath animated:NO scrollPosition:UITableViewScrollPositionNone]; } break; } default: break; } self.navigationItem.rightBarButtonItem.enabled = YES; [self updateHeaderTitleWithCounts]; } - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath { switch (indexPath.section) { case kPlaylistHeader: { for (NSUInteger i = 0; i < [tableView numberOfRowsInSection:kPlaylist]; i++) { NSIndexPath *playlistIndexPath = [NSIndexPath indexPathForRow:i inSection:kPlaylist]; [tableView deselectRowAtIndexPath:playlistIndexPath animated:NO]; } break; } case kFriendHeader: { for (NSUInteger i = 0; i < [tableView numberOfRowsInSection:kFriend]; i++) { NSIndexPath *friendIndexPath = [NSIndexPath indexPathForRow:i inSection:kFriend]; [tableView deselectRowAtIndexPath:friendIndexPath animated:NO]; } break; } case kPlaylist: { NSIndexPath *headerIndexPath = [NSIndexPath indexPathForRow:0 inSection:kPlaylistHeader]; [tableView deselectRowAtIndexPath:headerIndexPath animated:NO]; break; } case kFriend: { NSIndexPath *headerIndexPath = [NSIndexPath indexPathForRow:0 inSection:kFriendHeader]; [tableView deselectRowAtIndexPath:headerIndexPath animated:NO]; break; } default: break; } if (![tableView indexPathsForSelectedRows]) { self.navigationItem.rightBarButtonItem.enabled = NO; } [self updateHeaderTitleWithCounts]; } /* // Override to support conditional rearranging of the table view. - (BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath { // Return NO if you do not want the item to be re-orderable. return YES; } */ /* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */ @end