FLEXBookmarksViewController.m 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. //
  2. // FLEXBookmarksViewController.m
  3. // FLEX
  4. //
  5. // Created by Tanner on 2/6/20.
  6. // Copyright © 2020 Flipboard. All rights reserved.
  7. //
  8. #import "FLEXBookmarksViewController.h"
  9. #import "FLEXExplorerViewController.h"
  10. #import "FLEXNavigationController.h"
  11. #import "FLEXObjectExplorerFactory.h"
  12. #import "FLEXBookmarkManager.h"
  13. #import "UIBarButtonItem+FLEX.h"
  14. #import "FLEXColor.h"
  15. #import "FLEXUtility.h"
  16. #import "FLEXRuntimeUtility.h"
  17. #import "FLEXTableView.h"
  18. @interface FLEXBookmarksViewController ()
  19. @property (nonatomic, copy) NSArray *bookmarks;
  20. @property (nonatomic, readonly) FLEXExplorerViewController *corePresenter;
  21. @end
  22. @implementation FLEXBookmarksViewController
  23. #pragma mark - Initialization
  24. - (id)init {
  25. return [self initWithStyle:UITableViewStylePlain];
  26. }
  27. - (void)viewDidLoad {
  28. [super viewDidLoad];
  29. self.navigationController.hidesBarsOnSwipe = NO;
  30. self.tableView.allowsMultipleSelectionDuringEditing = YES;
  31. [self reloadData];
  32. }
  33. - (void)viewWillAppear:(BOOL)animated {
  34. [super viewWillAppear:animated];
  35. [self setupDefaultBarItems];
  36. }
  37. #pragma mark - Private
  38. - (void)reloadData {
  39. // We assume the bookmarks aren't going to change out from under us, since
  40. // presenting any other tool via keyboard shortcuts should dismiss us first
  41. self.bookmarks = FLEXBookmarkManager.bookmarks;
  42. self.title = [NSString stringWithFormat:@"Bookmarks (%@)", @(self.bookmarks.count)];
  43. }
  44. - (void)setupDefaultBarItems {
  45. self.navigationItem.rightBarButtonItem = FLEXBarButtonItemSystem(Done, self, @selector(dismissAnimated));
  46. self.toolbarItems = @[
  47. UIBarButtonItem.flex_flexibleSpace,
  48. FLEXBarButtonItemSystem(Edit, self, @selector(toggleEditing)),
  49. ];
  50. // Disable editing if no bookmarks available
  51. self.toolbarItems.lastObject.enabled = self.bookmarks.count > 0;
  52. }
  53. - (void)setupEditingBarItems {
  54. self.navigationItem.rightBarButtonItem = nil;
  55. self.toolbarItems = @[
  56. [UIBarButtonItem itemWithTitle:@"Close All" target:self action:@selector(closeAllButtonPressed:)],
  57. UIBarButtonItem.flex_flexibleSpace,
  58. // We use a non-system done item because we change its title dynamically
  59. [UIBarButtonItem doneStyleitemWithTitle:@"Done" target:self action:@selector(toggleEditing)]
  60. ];
  61. self.toolbarItems.firstObject.tintColor = FLEXColor.destructiveColor;
  62. }
  63. - (FLEXExplorerViewController *)corePresenter {
  64. // We must be presented by a FLEXExplorerViewController, or presented
  65. // by another view controller that was presented by FLEXExplorerViewController
  66. FLEXExplorerViewController *presenter = (id)self.presentingViewController;
  67. presenter = (id)presenter.presentingViewController ?: presenter;
  68. presenter = (id)presenter.presentingViewController ?: presenter;
  69. NSAssert(
  70. [presenter isKindOfClass:[FLEXExplorerViewController class]],
  71. @"The bookmarks view controller expects to be presented by the explorer controller"
  72. );
  73. return presenter;
  74. }
  75. #pragma mark Button Actions
  76. - (void)dismissAnimated {
  77. [self dismissAnimated:nil];
  78. }
  79. - (void)dismissAnimated:(id)selectedObject {
  80. if (selectedObject) {
  81. UIViewController *explorer = [FLEXObjectExplorerFactory
  82. explorerViewControllerForObject:selectedObject
  83. ];
  84. if ([self.presentingViewController isKindOfClass:[FLEXNavigationController class]]) {
  85. // I am presented on an existing navigation stack, so
  86. // dismiss myself and push the bookmark there
  87. UINavigationController *presenter = (id)self.presentingViewController;
  88. [presenter dismissViewControllerAnimated:YES completion:^{
  89. [presenter pushViewController:explorer animated:YES];
  90. }];
  91. } else {
  92. // Dismiss myself and present explorer
  93. UIViewController *presenter = self.corePresenter;
  94. [presenter dismissViewControllerAnimated:YES completion:^{
  95. [presenter presentViewController:[FLEXNavigationController
  96. withRootViewController:explorer
  97. ] animated:YES completion:nil];
  98. }];
  99. }
  100. } else {
  101. // Just dismiss myself
  102. [self dismissViewControllerAnimated:YES completion:nil];
  103. }
  104. }
  105. - (void)toggleEditing {
  106. NSArray<NSIndexPath *> *selected = self.tableView.indexPathsForSelectedRows;
  107. self.editing = !self.editing;
  108. if (self.isEditing) {
  109. [self setupEditingBarItems];
  110. } else {
  111. [self setupDefaultBarItems];
  112. // Get index set of bookmarks to close
  113. NSMutableIndexSet *indexes = [NSMutableIndexSet new];
  114. for (NSIndexPath *ip in selected) {
  115. [indexes addIndex:ip.row];
  116. }
  117. if (selected.count) {
  118. // Close bookmarks and update data source
  119. [FLEXBookmarkManager.bookmarks removeObjectsAtIndexes:indexes];
  120. [self reloadData];
  121. // Remove deleted rows
  122. [self.tableView deleteRowsAtIndexPaths:selected withRowAnimation:UITableViewRowAnimationAutomatic];
  123. }
  124. }
  125. }
  126. - (void)closeAllButtonPressed:(UIBarButtonItem *)sender {
  127. [FLEXAlert makeSheet:^(FLEXAlert *make) {
  128. NSInteger count = self.bookmarks.count;
  129. NSString *title = FLEXPluralFormatString(count, @"Remove %@ bookmarks", @"Remove %@ bookmark");
  130. make.button(title).destructiveStyle().handler(^(NSArray<NSString *> *strings) {
  131. [self closeAll];
  132. [self toggleEditing];
  133. });
  134. make.button(@"Cancel").cancelStyle();
  135. } showFrom:self source:sender];
  136. }
  137. - (void)closeAll {
  138. NSInteger rowCount = self.bookmarks.count;
  139. // Close bookmarks and update data source
  140. [FLEXBookmarkManager.bookmarks removeAllObjects];
  141. [self reloadData];
  142. // Delete rows from table view
  143. NSArray<NSIndexPath *> *allRows = [NSArray flex_forEachUpTo:rowCount map:^id(NSUInteger row) {
  144. return [NSIndexPath indexPathForRow:row inSection:0];
  145. }];
  146. [self.tableView deleteRowsAtIndexPaths:allRows withRowAnimation:UITableViewRowAnimationAutomatic];
  147. }
  148. #pragma mark - Table View Data Source
  149. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
  150. return self.bookmarks.count;
  151. }
  152. - (UITableViewCell *)tableView:(FLEXTableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
  153. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kFLEXDetailCell forIndexPath:indexPath];
  154. id object = self.bookmarks[indexPath.row];
  155. cell.textLabel.text = [FLEXRuntimeUtility safeDescriptionForObject:object];
  156. cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ — %p", [object class], object];
  157. return cell;
  158. }
  159. #pragma mark - Table View Delegate
  160. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  161. if (self.editing) {
  162. // Case: editing with multi-select
  163. self.toolbarItems.lastObject.title = @"Remove Selected";
  164. self.toolbarItems.lastObject.tintColor = FLEXColor.destructiveColor;
  165. } else {
  166. // Case: selected a bookmark
  167. [self dismissAnimated:self.bookmarks[indexPath.row]];
  168. }
  169. }
  170. - (void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath {
  171. NSParameterAssert(self.editing);
  172. if (tableView.indexPathsForSelectedRows.count == 0) {
  173. self.toolbarItems.lastObject.title = @"Done";
  174. self.toolbarItems.lastObject.tintColor = self.view.tintColor;
  175. }
  176. }
  177. - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {
  178. return YES;
  179. }
  180. - (void)tableView:(UITableView *)table
  181. commitEditingStyle:(UITableViewCellEditingStyle)edit
  182. forRowAtIndexPath:(NSIndexPath *)indexPath {
  183. NSParameterAssert(edit == UITableViewCellEditingStyleDelete);
  184. // Remove bookmark and update data source
  185. [FLEXBookmarkManager.bookmarks removeObjectAtIndex:indexPath.row];
  186. [self reloadData];
  187. // Delete row from table view
  188. [table deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
  189. }
  190. @end