DirectoryTableViewController.m 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /**
  2. * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. #import "DirectoryTableViewController.h"
  6. @import NextcloudKit;
  7. #import "UIImageView+AFNetworking.h"
  8. #import "NCAPIController.h"
  9. #import "NCDatabaseManager.h"
  10. #import "NCAppBranding.h"
  11. #import "NCSettingsController.h"
  12. #import "PlaceholderView.h"
  13. #import "NextcloudTalk-Swift.h"
  14. @interface DirectoryTableViewController ()
  15. {
  16. NSString *_path;
  17. NSString *_userHomePath;
  18. NSString *_token;
  19. NSMutableArray *_itemsInDirectory;
  20. NSIndexPath *_selectedItem;
  21. UIBarButtonItem *_sortingButton;
  22. PlaceholderView *_directoryBackgroundView;
  23. UIActivityIndicatorView *_sharingFileView;
  24. }
  25. @end
  26. @implementation DirectoryTableViewController
  27. - (instancetype)initWithPath:(NSString *)path inRoom:(nonnull NSString *)token
  28. {
  29. self = [super init];
  30. if (self) {
  31. _path = path;
  32. _token = token;
  33. }
  34. return self;
  35. }
  36. - (void)viewDidLoad
  37. {
  38. [super viewDidLoad];
  39. TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount];
  40. _userHomePath = [[NCAPIController sharedInstance] filesPathForAccount:activeAccount];
  41. [self configureNavigationBar];
  42. _sharingFileView = [[UIActivityIndicatorView alloc] init];
  43. _sharingFileView.color = [NCAppBranding themeTextColor];
  44. self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
  45. // Directory placeholder view
  46. _directoryBackgroundView = [[PlaceholderView alloc] init];
  47. [_directoryBackgroundView setImage:[UIImage imageNamed:@"folder-placeholder"]];
  48. [_directoryBackgroundView.placeholderTextView setText:NSLocalizedString(@"No files in here", nil)];
  49. [_directoryBackgroundView.placeholderView setHidden:YES];
  50. [_directoryBackgroundView.loadingView startAnimating];
  51. self.tableView.backgroundView = _directoryBackgroundView;
  52. [self.navigationController.navigationBar setTitleTextAttributes:
  53. @{NSForegroundColorAttributeName:[NCAppBranding themeTextColor]}];
  54. self.navigationController.navigationBar.tintColor = [NCAppBranding themeTextColor];
  55. self.navigationController.navigationBar.barTintColor = [NCAppBranding themeColor];
  56. self.tabBarController.tabBar.tintColor = [NCAppBranding themeColor];
  57. self.navigationController.navigationBar.translucent = NO;
  58. UIColor *themeColor = [NCAppBranding themeColor];
  59. UINavigationBarAppearance *appearance = [[UINavigationBarAppearance alloc] init];
  60. [appearance configureWithOpaqueBackground];
  61. appearance.backgroundColor = themeColor;
  62. appearance.titleTextAttributes = @{NSForegroundColorAttributeName:[NCAppBranding themeTextColor]};
  63. self.navigationItem.standardAppearance = appearance;
  64. self.navigationItem.compactAppearance = appearance;
  65. self.navigationItem.scrollEdgeAppearance = appearance;
  66. self.tableView.separatorInset = UIEdgeInsetsMake(0, 64, 0, 0);
  67. [self.tableView registerNib:[UINib nibWithNibName:DirectoryTableViewCell.nibName bundle:nil] forCellReuseIdentifier:DirectoryTableViewCell.identifier];
  68. }
  69. - (void)viewWillAppear:(BOOL)animated
  70. {
  71. [super viewWillAppear:animated];
  72. [self getItemsInDirectory];
  73. }
  74. - (void)cancelButtonPressed
  75. {
  76. [self dismissViewControllerAnimated:YES completion:nil];
  77. }
  78. - (void)shareButtonPressed
  79. {
  80. [self showConfirmationDialogForSharingItemWithPath:_path andName:[_path lastPathComponent]];
  81. }
  82. - (void)addMenuToSortingButton
  83. {
  84. NSMutableArray *items = [[NSMutableArray alloc] init];
  85. UIAction *alphabeticalAction = [UIAction actionWithTitle:NSLocalizedString(@"Alphabetical order", nil)
  86. image:[self imageForSortingOption:NCAlphabeticalSorting]
  87. identifier:nil
  88. handler:^(UIAction *action) {
  89. [[NCSettingsController sharedInstance] setPreferredFileSorting:NCAlphabeticalSorting];
  90. [self sortItemsInDirectory];
  91. }];
  92. UIAction *modificationDateAction = [UIAction actionWithTitle:NSLocalizedString(@"Modification date", nil)
  93. image:[self imageForSortingOption:NCModificationDateSorting]
  94. identifier:nil
  95. handler:^(UIAction *action) {
  96. [[NCSettingsController sharedInstance] setPreferredFileSorting:NCModificationDateSorting];
  97. [self sortItemsInDirectory];
  98. }];
  99. [items addObject:alphabeticalAction];
  100. [items addObject:modificationDateAction];
  101. _sortingButton.menu = [UIMenu menuWithTitle:@"" children:items];
  102. }
  103. - (UIImage *)imageForSortingOption:(NCPreferredFileSorting)option
  104. {
  105. if ([[NCSettingsController sharedInstance] getPreferredFileSorting] == option) {
  106. return [UIImage systemImageNamed:@"checkmark"];
  107. }
  108. return nil;
  109. }
  110. #pragma mark - Files
  111. - (void)getItemsInDirectory
  112. {
  113. [[NCAPIController sharedInstance] readFolderForAccount:[[NCDatabaseManager sharedInstance] activeAccount] atPath:_path depth:@"1" withCompletionBlock:^(NSArray *items, NSError *error) {
  114. if (!error) {
  115. NSMutableArray *itemsInDirectory = [NSMutableArray new];
  116. for (NKFile *item in items) {
  117. NSString *currentDirectory = [self->_path isEqualToString:@""] ? @"/" : [self->_path lastPathComponent];
  118. NSString *itemPath = [item.path stringByReplacingOccurrencesOfString:self->_userHomePath withString:@""];
  119. // When nextcloud is installed in a subdirectory, it's not enough to replace the _userHomePath,
  120. // because the subdirectory would get a part of the itemPath (see https://github.com/nextcloud/talk-ios/issues/996)
  121. NSArray *itemPathParts = [item.path componentsSeparatedByString:self->_userHomePath];
  122. if (itemPathParts.count > 1) {
  123. itemPath = itemPathParts[1];
  124. }
  125. if ([[itemPath lastPathComponent] isEqualToString:currentDirectory] && !item.e2eEncrypted) {
  126. [itemsInDirectory addObject:item];
  127. }
  128. }
  129. self->_itemsInDirectory = itemsInDirectory;
  130. [self sortItemsInDirectory];
  131. [self->_directoryBackgroundView.loadingView stopAnimating];
  132. [self->_directoryBackgroundView.loadingView setHidden:YES];
  133. [self->_directoryBackgroundView.placeholderView setHidden:(itemsInDirectory.count > 0)];
  134. }
  135. }];
  136. }
  137. - (void)sortItemsInDirectory
  138. {
  139. NSSortDescriptor *valueDescriptor = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:NO];
  140. if ([[NCSettingsController sharedInstance] getPreferredFileSorting] == NCAlphabeticalSorting) {
  141. valueDescriptor = [[NSSortDescriptor alloc] initWithKey:@"fileName" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)];
  142. }
  143. NSArray *descriptors = [NSArray arrayWithObjects:valueDescriptor, nil];
  144. [_itemsInDirectory sortUsingDescriptors:descriptors];
  145. [self addMenuToSortingButton];
  146. [self.tableView reloadData];
  147. }
  148. - (void)shareFileWithPath:(NSString *)path
  149. {
  150. [self setSharingFileUI];
  151. [[NCAPIController sharedInstance] shareFileOrFolderForAccount:[[NCDatabaseManager sharedInstance] activeAccount] atPath:path toRoom:_token talkMetaData:nil withCompletionBlock:^(NSError *error) {
  152. if (!error) {
  153. [self dismissViewControllerAnimated:YES completion:nil];
  154. } else {
  155. [self removeSharingFileUI];
  156. [self showErrorSharingItem];
  157. NSLog(@"Error sharing file or folder: %@", [error description]);
  158. }
  159. }];
  160. }
  161. #pragma mark - Utils
  162. - (void)configureNavigationBar
  163. {
  164. // Sorting button
  165. _sortingButton = [[UIBarButtonItem alloc] initWithImage:[UIImage systemImageNamed:@"ellipsis.circle"]
  166. style:UIBarButtonItemStylePlain
  167. target:self
  168. action:nil];
  169. [self addMenuToSortingButton];
  170. // Home folder
  171. if ([_path isEqualToString:@""]) {
  172. UIBarButtonItem *cancelButton = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
  173. target:self action:@selector(cancelButtonPressed)];
  174. self.navigationItem.leftBarButtonItem = cancelButton;
  175. self.navigationItem.rightBarButtonItem = _sortingButton;
  176. UIImage *navigationLogo = [UIImage imageNamed:@"navigation-home"];
  177. UIImageView *navigationImageView = [[UIImageView alloc] initWithImage:navigationLogo];
  178. navigationImageView.image = [navigationImageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
  179. [navigationImageView setTintColor:[NCAppBranding themeTextColor]];
  180. self.navigationItem.titleView = navigationImageView;
  181. UIBarButtonItem *backButton = [[UIBarButtonItem alloc] initWithImage:navigationLogo style:UIBarButtonItemStylePlain
  182. target:nil action:nil];
  183. self.navigationItem.backBarButtonItem = backButton;
  184. // Other directories
  185. } else {
  186. UIBarButtonItem *shareButton = [[UIBarButtonItem alloc] initWithImage:[UIImage imageNamed:@"sharing"]
  187. style:UIBarButtonItemStylePlain
  188. target:self
  189. action:@selector(shareButtonPressed)];
  190. self.navigationItem.rightBarButtonItems = @[_sortingButton, shareButton];
  191. self.navigationItem.title = [_path lastPathComponent];
  192. }
  193. }
  194. - (void)setSharingFileUI
  195. {
  196. [_sharingFileView startAnimating];
  197. UIBarButtonItem *sharingFileButton = [[UIBarButtonItem alloc] initWithCustomView:_sharingFileView];
  198. self.navigationItem.rightBarButtonItems = @[sharingFileButton];
  199. self.navigationController.navigationBar.userInteractionEnabled = NO;
  200. self.tableView.userInteractionEnabled = NO;
  201. }
  202. - (void)removeSharingFileUI
  203. {
  204. [_sharingFileView stopAnimating];
  205. [self configureNavigationBar];
  206. self.navigationController.navigationBar.userInteractionEnabled = YES;
  207. self.tableView.userInteractionEnabled = YES;
  208. }
  209. - (void)showConfirmationDialogForSharingItemWithPath:(NSString *)path andName:(NSString *)name
  210. {
  211. UIAlertController *confirmDialog =
  212. [UIAlertController alertControllerWithTitle:name
  213. message:[NSString stringWithFormat:NSLocalizedString(@"Do you want to share '%@' in the conversation?", nil), name]
  214. preferredStyle:UIAlertControllerStyleAlert];
  215. UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Share", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
  216. [self shareFileWithPath:path];
  217. }];
  218. [confirmDialog addAction:confirmAction];
  219. UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Cancel", nil) style:UIAlertActionStyleCancel handler:nil];
  220. [confirmDialog addAction:cancelAction];
  221. [self presentViewController:confirmDialog animated:YES completion:nil];
  222. }
  223. - (void)showErrorSharingItem
  224. {
  225. UIAlertController *confirmDialog =
  226. [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Could not share file", nil)
  227. message:NSLocalizedString(@"An error occurred while sharing the file", nil)
  228. preferredStyle:UIAlertControllerStyleAlert];
  229. UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:UIAlertActionStyleDefault handler:nil];
  230. [confirmDialog addAction:confirmAction];
  231. [self presentViewController:confirmDialog animated:YES completion:nil];
  232. }
  233. #pragma mark - Table view data source
  234. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  235. {
  236. return 1;
  237. }
  238. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  239. {
  240. return _itemsInDirectory.count;
  241. }
  242. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
  243. {
  244. return DirectoryTableViewCell.cellHeight;
  245. }
  246. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  247. {
  248. NKFile *item = [_itemsInDirectory objectAtIndex:indexPath.row];
  249. DirectoryTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:DirectoryTableViewCell.identifier];
  250. if (!cell) {
  251. cell = [[DirectoryTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:DirectoryTableViewCell.identifier];
  252. }
  253. // Name and modification date
  254. cell.fileNameLabel.text = item.fileName;
  255. cell.fileInfoLabel.text = [NCUtils relativeTimeFromDateWithDate:item.date];
  256. // Icon or preview
  257. NSString *imageName = [NCUtils previewImageForMimeType:item.contentType];
  258. UIImage *filePreviewImage = [UIImage imageNamed:imageName];
  259. if (item.directory) {
  260. cell.fileImageView.image = [UIImage imageNamed:@"folder"];
  261. } else if (item.hasPreview) {
  262. NSString *fileId = [NSString stringWithFormat:@"%@", item.fileId];
  263. [cell.fileImageView setImageWithURLRequest:[[NCAPIController sharedInstance] createPreviewRequestForFile:fileId width:40 height:40 usingAccount:[[NCDatabaseManager sharedInstance] activeAccount]]
  264. placeholderImage:filePreviewImage success:nil failure:nil];
  265. } else {
  266. cell.fileImageView.image = filePreviewImage;
  267. }
  268. // Disclosure indicator
  269. if (item.directory) {
  270. cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  271. } else {
  272. cell.accessoryType = UITableViewCellAccessoryNone;
  273. }
  274. return cell;
  275. }
  276. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
  277. {
  278. NKFile *item = [_itemsInDirectory objectAtIndex:indexPath.row];
  279. NSString *selectedItemPath = [NSString stringWithFormat:@"%@/%@", _path, item.fileName];
  280. if (item.directory) {
  281. DirectoryTableViewController *directoryVC = [[DirectoryTableViewController alloc] initWithPath:selectedItemPath inRoom:_token];
  282. [self.navigationController pushViewController:directoryVC animated:YES];
  283. } else {
  284. [self showConfirmationDialogForSharingItemWithPath:selectedItemPath andName:item.fileName];
  285. }
  286. [self.tableView deselectRowAtIndexPath:indexPath animated:YES];
  287. }
  288. @end