RoomSearchTableViewController.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. /**
  2. * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. #import "RoomSearchTableViewController.h"
  6. @import NextcloudKit;
  7. #import "NCAPIController.h"
  8. #import "NCAppBranding.h"
  9. #import "NCDatabaseManager.h"
  10. #import "NCRoom.h"
  11. #import "NCSettingsController.h"
  12. #import "PlaceholderView.h"
  13. #import "NextcloudTalk-Swift.h"
  14. typedef enum RoomSearchSection {
  15. RoomSearchSectionFiltered = 0,
  16. RoomSearchSectionUsers,
  17. RoomSearchSectionListable,
  18. RoomSearchSectionMessages
  19. } RoomSearchSection;
  20. @interface RoomSearchTableViewController ()
  21. {
  22. PlaceholderView *_roomSearchBackgroundView;
  23. }
  24. @end
  25. @implementation RoomSearchTableViewController
  26. - (void)viewDidLoad
  27. {
  28. [super viewDidLoad];
  29. [self.tableView registerNib:[UINib nibWithNibName:RoomTableViewCell.nibName bundle:nil] forCellReuseIdentifier:RoomTableViewCell.identifier];
  30. self.tableView.tableFooterView = [[UIView alloc] initWithFrame:CGRectZero];
  31. // Align header's title to ContactsTableViewCell's label
  32. self.tableView.separatorInset = UIEdgeInsetsMake(0, 52, 0, 0);
  33. self.tableView.separatorInsetReference = UITableViewSeparatorInsetFromAutomaticInsets;
  34. // Contacts placeholder view
  35. _roomSearchBackgroundView = [[PlaceholderView alloc] initForTableViewStyle:UITableViewStyleInsetGrouped];
  36. [_roomSearchBackgroundView setImage:[UIImage imageNamed:@"conversations-placeholder"]];
  37. [_roomSearchBackgroundView.placeholderTextView setText:NSLocalizedString(@"No results found", nil)];
  38. [_roomSearchBackgroundView.placeholderView setHidden:YES];
  39. [_roomSearchBackgroundView.loadingView startAnimating];
  40. self.tableView.backgroundView = _roomSearchBackgroundView;
  41. }
  42. - (void)didReceiveMemoryWarning
  43. {
  44. [super didReceiveMemoryWarning];
  45. }
  46. - (void)setRooms:(NSArray *)rooms
  47. {
  48. _rooms = rooms;
  49. [self reloadAndCheckSearchingIndicator];
  50. }
  51. - (void)setUsers:(NSArray *)users
  52. {
  53. _users = users;
  54. [self reloadAndCheckSearchingIndicator];
  55. }
  56. - (void)setListableRooms:(NSArray *)listableRooms
  57. {
  58. _listableRooms = listableRooms;
  59. [self reloadAndCheckSearchingIndicator];
  60. }
  61. - (void)setMessages:(NSArray *)messages
  62. {
  63. _messages = messages;
  64. [self reloadAndCheckSearchingIndicator];
  65. }
  66. - (void)setSearchingMessages:(BOOL)searchingMessages
  67. {
  68. _searchingMessages = searchingMessages;
  69. [self reloadAndCheckSearchingIndicator];
  70. }
  71. #pragma mark - User Interface
  72. - (void)reloadAndCheckSearchingIndicator
  73. {
  74. [self.tableView reloadData];
  75. if (_searchingMessages) {
  76. if ([self searchSections].count > 0) {
  77. [_roomSearchBackgroundView.loadingView stopAnimating];
  78. [_roomSearchBackgroundView.loadingView setHidden:YES];
  79. [self showSearchingFooterView];
  80. } else {
  81. [_roomSearchBackgroundView.loadingView startAnimating];
  82. [_roomSearchBackgroundView.loadingView setHidden:NO];
  83. [self hideSearchingFooterView];
  84. }
  85. [_roomSearchBackgroundView.placeholderView setHidden:YES];
  86. } else {
  87. [_roomSearchBackgroundView.loadingView stopAnimating];
  88. [_roomSearchBackgroundView.loadingView setHidden:YES];
  89. [_roomSearchBackgroundView.placeholderView setHidden:[self searchSections].count > 0];
  90. }
  91. }
  92. - (void)showSearchingFooterView
  93. {
  94. UIActivityIndicatorView *loadingMoreView = [[UIActivityIndicatorView alloc] initWithFrame:CGRectMake(0, 0, 44, 44)];
  95. loadingMoreView.color = [UIColor darkGrayColor];
  96. [loadingMoreView startAnimating];
  97. self.tableView.tableFooterView = loadingMoreView;
  98. }
  99. - (void)hideSearchingFooterView
  100. {
  101. self.tableView.tableFooterView = nil;
  102. }
  103. - (void)clearSearchedResults
  104. {
  105. _rooms = @[];
  106. _users = @[];
  107. _listableRooms = @[];
  108. _messages = @[];
  109. [self reloadAndCheckSearchingIndicator];
  110. }
  111. #pragma mark - Utils
  112. - (NSArray *)searchSections
  113. {
  114. NSMutableArray *sections = [NSMutableArray new];
  115. if (_rooms.count > 0) {
  116. [sections addObject:@(RoomSearchSectionFiltered)];
  117. }
  118. if (_users.count > 0) {
  119. [sections addObject:@(RoomSearchSectionUsers)];
  120. }
  121. if (_listableRooms.count > 0) {
  122. [sections addObject:@(RoomSearchSectionListable)];
  123. }
  124. if (_messages.count > 0) {
  125. [sections addObject:@(RoomSearchSectionMessages)];
  126. }
  127. return [NSArray arrayWithArray:sections];
  128. }
  129. - (NCRoom *)roomForIndexPath:(NSIndexPath *)indexPath
  130. {
  131. NSInteger searchSection = [[[self searchSections] objectAtIndex:indexPath.section] integerValue];
  132. if (searchSection == RoomSearchSectionFiltered && indexPath.row < _rooms.count) {
  133. return [_rooms objectAtIndex:indexPath.row];
  134. } else if (searchSection == RoomSearchSectionListable && indexPath.row < _listableRooms.count) {
  135. return [_listableRooms objectAtIndex:indexPath.row];
  136. }
  137. return nil;
  138. }
  139. - (NKSearchEntry *)messageForIndexPath:(NSIndexPath *)indexPath
  140. {
  141. NSInteger searchSection = [[[self searchSections] objectAtIndex:indexPath.section] integerValue];
  142. if (searchSection == RoomSearchSectionMessages && indexPath.row < _messages.count) {
  143. return [_messages objectAtIndex:indexPath.row];;
  144. }
  145. return nil;
  146. }
  147. - (UITableViewCell *)tableView:(UITableView *)tableView cellForMessageAtIndexPath:(NSIndexPath *)indexPath
  148. {
  149. NKSearchEntry *messageEntry = [_messages objectAtIndex:indexPath.row];
  150. RoomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:RoomTableViewCell.identifier];
  151. if (!cell) {
  152. cell = [[RoomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:RoomTableViewCell.identifier];
  153. }
  154. cell.titleLabel.text = messageEntry.title;
  155. cell.subtitleLabel.text = messageEntry.subline;
  156. // Thumbnail image
  157. NSURL *thumbnailURL = [[NSURL alloc] initWithString:messageEntry.thumbnailURL];
  158. NSString *actorId = [messageEntry.attributes objectForKey:@"actorId"];
  159. NSString *actorType = [messageEntry.attributes objectForKey:@"actorType"];
  160. if (thumbnailURL && thumbnailURL.absoluteString.length > 0) {
  161. [cell.roomImage setImageWithURL:thumbnailURL placeholderImage:nil];
  162. cell.roomImage.contentMode = UIViewContentModeScaleToFill;
  163. } else {
  164. [cell.roomImage setActorAvatarForId:actorId withType:actorType withDisplayName:@"" withRoomToken:nil];
  165. }
  166. // Clear possible content not removed by cell reuse
  167. cell.dateLabel.text = @"";
  168. [cell setUnreadWithMessages:0 mentioned:NO groupMentioned:NO];
  169. // Add message date (if it is included in attributes)
  170. NSInteger timestamp = [[messageEntry.attributes objectForKey:@"timestamp"] integerValue];
  171. if (timestamp > 0) {
  172. NSDate *date = [[NSDate alloc] initWithTimeIntervalSince1970:timestamp];
  173. cell.dateLabel.text = [NCUtils readableTimeOrDateFromDate:date];
  174. }
  175. return cell;
  176. }
  177. - (NCUser *)userForIndexPath:(NSIndexPath *)indexPath
  178. {
  179. NSInteger searchSection = [[[self searchSections] objectAtIndex:indexPath.section] integerValue];
  180. if (searchSection == RoomSearchSectionUsers && indexPath.row < _users.count) {
  181. return [_users objectAtIndex:indexPath.row];
  182. }
  183. return nil;
  184. }
  185. - (UITableViewCell *)tableView:(UITableView *)tableView cellForUserAtIndexPath:(NSIndexPath *)indexPath
  186. {
  187. NCUser *user = [_users objectAtIndex:indexPath.row];
  188. RoomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:RoomTableViewCell.identifier];
  189. if (!cell) {
  190. cell = [[RoomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:RoomTableViewCell.identifier];
  191. }
  192. // Clear possible content not removed by cell reuse
  193. cell.dateLabel.text = @"";
  194. [cell setUnreadWithMessages:0 mentioned:NO groupMentioned:NO];
  195. cell.titleLabel.text = user.name;
  196. cell.titleOnly = YES;
  197. [cell.roomImage setActorAvatarForId:user.userId withType:user.source withDisplayName:user.name withRoomToken:nil];
  198. return cell;
  199. }
  200. #pragma mark - Table view data source
  201. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
  202. {
  203. return [self searchSections].count;
  204. }
  205. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  206. {
  207. NSInteger searchSection = [[[self searchSections] objectAtIndex:section] integerValue];
  208. switch (searchSection) {
  209. case RoomSearchSectionFiltered:
  210. return _rooms.count;
  211. case RoomSearchSectionUsers:
  212. return _users.count;
  213. case RoomSearchSectionListable:
  214. return _listableRooms.count;
  215. case RoomSearchSectionMessages:
  216. return _messages.count;
  217. default:
  218. return 0;
  219. }
  220. }
  221. - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
  222. {
  223. return RoomTableViewCell.cellHeight;
  224. }
  225. - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
  226. {
  227. NSInteger searchSection = [[[self searchSections] objectAtIndex:section] integerValue];
  228. switch (searchSection) {
  229. case RoomSearchSectionFiltered:
  230. return NSLocalizedString(@"Conversations", @"");
  231. case RoomSearchSectionUsers:
  232. return NSLocalizedString(@"Users", @"");
  233. case RoomSearchSectionListable:
  234. return NSLocalizedString(@"Open conversations", @"TRANSLATORS 'Open conversations' as a type of conversation. 'Open conversations' are conversations that can be found by other users");
  235. case RoomSearchSectionMessages:
  236. return NSLocalizedString(@"Messages", @"");
  237. default:
  238. return nil;
  239. }
  240. }
  241. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  242. {
  243. NSInteger searchSection = [[[self searchSections] objectAtIndex:indexPath.section] integerValue];
  244. // Messages
  245. if (searchSection == RoomSearchSectionMessages) {
  246. return [self tableView:tableView cellForMessageAtIndexPath:indexPath];
  247. }
  248. // Contacts
  249. if (searchSection == RoomSearchSectionUsers) {
  250. return [self tableView:tableView cellForUserAtIndexPath:indexPath];
  251. }
  252. NCRoom *room = [self roomForIndexPath:indexPath];
  253. RoomTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:RoomTableViewCell.identifier];
  254. if (!cell) {
  255. cell = [[RoomTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:RoomTableViewCell.identifier];
  256. }
  257. // Set room name
  258. cell.titleLabel.text = room.displayName;
  259. // Set last activity
  260. if (room.lastMessageId || room.lastMessageProxiedJSONString) {
  261. cell.titleOnly = NO;
  262. cell.subtitleLabel.attributedText = room.lastMessageString;
  263. } else {
  264. cell.titleOnly = YES;
  265. }
  266. NSDate *date = [[NSDate alloc] initWithTimeIntervalSince1970:room.lastActivity];
  267. cell.dateLabel.text = [NCUtils readableTimeOrDateFromDate:date];
  268. // Set unread messages
  269. if ([[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityDirectMentionFlag]) {
  270. BOOL mentioned = room.unreadMentionDirect || room.type == kNCRoomTypeOneToOne || room.type == kNCRoomTypeFormerOneToOne;
  271. BOOL groupMentioned = room.unreadMention && !room.unreadMentionDirect;
  272. [cell setUnreadWithMessages:room.unreadMessages mentioned:mentioned groupMentioned:groupMentioned];
  273. } else {
  274. BOOL mentioned = room.unreadMention || room.type == kNCRoomTypeOneToOne || room.type == kNCRoomTypeFormerOneToOne;
  275. [cell setUnreadWithMessages:room.unreadMessages mentioned:mentioned groupMentioned:NO];
  276. }
  277. [cell.roomImage setAvatarFor:room];
  278. // Set favorite or call image
  279. if (room.hasCall) {
  280. [cell.favoriteImage setTintColor:[UIColor systemRedColor]];
  281. [cell.favoriteImage setImage:[UIImage systemImageNamed:@"video.fill"]];
  282. } else if (room.isFavorite) {
  283. [cell.favoriteImage setTintColor:[UIColor systemYellowColor]];
  284. [cell.favoriteImage setImage:[UIImage systemImageNamed:@"star.fill"]];
  285. }
  286. return cell;
  287. }
  288. @end