Files
2025-08-06 13:49:11 +08:00

557 lines
21 KiB
Objective-C

//
// 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