GroupDetailsViewController.m 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2013-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. #import "GroupDetailsViewController.h"
  21. #import "Conversation.h"
  22. #import "Contact.h"
  23. #import "GroupMemberCell.h"
  24. #import "ContactPickerViewController.h"
  25. #import "ContactDetailsViewController.h"
  26. #import "ImageData.h"
  27. #import "UIDefines.h"
  28. #import "AvatarMaker.h"
  29. #import "AppDelegate.h"
  30. #import "AddMemberCell.h"
  31. #import "BundleUtil.h"
  32. #import "DeleteConversationAction.h"
  33. #import "FullscreenImageViewController.h"
  34. #import "EditGroupViewController.h"
  35. #import "PickGroupMembersViewController.h"
  36. #import "ModalNavigationController.h"
  37. #import "ModalPresenter.h"
  38. #import "UIImage+ColoredImage.h"
  39. #import "CreateGroupNavigationController.h"
  40. #import "UserSettings.h"
  41. #import "Threema-Swift.h"
  42. #import "MDMSetup.h"
  43. #import "BundleUtil.h"
  44. @interface GroupDetailsViewController () <MFMailComposeViewControllerDelegate>
  45. @property BOOL canExportChat;
  46. @end
  47. @implementation GroupDetailsViewController {
  48. NSArray *members;
  49. Contact *selectedContact;
  50. NSInteger includeMediaIndex;
  51. NSInteger withoutMediaIndex;
  52. }
  53. - (void)viewDidLoad {
  54. [super viewDidLoad];
  55. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updatedGroup:) name:kNotificationUpdatedGroup object:nil];
  56. self.navigationController.interactivePopGestureRecognizer.enabled = YES;
  57. self.navigationController.interactivePopGestureRecognizer.delegate = nil;
  58. _nameLabel.font = [UIFont boldSystemFontOfSize: _nameLabel.font.pointSize];
  59. UITapGestureRecognizer *disclosureTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedHeaderView)];
  60. [_disclosureButton addGestureRecognizer:disclosureTapRecognizer];
  61. UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tappedImage)];
  62. [_imageView addGestureRecognizer:tapRecognizer];
  63. [self setupColors];
  64. }
  65. - (void)setupColors {
  66. [_nameLabel setTextColor:[Colors fontNormal]];
  67. _nameLabel.shadowColor = nil;
  68. [_creatorLabel setTextColor:[Colors fontLight]];
  69. _creatorLabel.shadowColor = nil;
  70. UIImage *disclosureImage = [self.disclosureButton.imageView.image imageWithTint:[Colors main]];
  71. [self.disclosureButton setImage:disclosureImage forState:UIControlStateNormal];
  72. if (@available(iOS 11.0, *)) {
  73. _imageView.accessibilityIgnoresInvertColors = true;
  74. }
  75. }
  76. - (void)viewWillAppear:(BOOL)animated {
  77. [super viewWillAppear:animated];
  78. self.view.alpha = 1.0;
  79. _canExportChat = [self canExportConversation];
  80. [self reloadMembers];
  81. [self.tableView reloadData];
  82. [self updateHeaderView];
  83. }
  84. - (void)viewWillLayoutSubviews {
  85. [super viewWillLayoutSubviews];
  86. UIFontDescriptor *fontDescriptor = [UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleTitle3];
  87. CGFloat size = fontDescriptor.pointSize;
  88. _nameLabel.font = [UIFont boldSystemFontOfSize:size];
  89. }
  90. - (void)setGroup:(GroupProxy *)newGroup {
  91. [_group.conversation removeObserver:self forKeyPath:@"members"];
  92. [_group.conversation removeObserver:self forKeyPath:@"groupName"];
  93. [_group.conversation removeObserver:self forKeyPath:@"groupImage"];
  94. _group = newGroup;
  95. [_group.conversation addObserver:self forKeyPath:@"members" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
  96. [_group.conversation addObserver:self forKeyPath:@"groupName" options:0 context:nil];
  97. [_group.conversation addObserver:self forKeyPath:@"groupImage" options:0 context:nil];
  98. }
  99. - (void)dealloc {
  100. [_group.conversation removeObserver:self forKeyPath:@"members"];
  101. [_group.conversation removeObserver:self forKeyPath:@"groupName"];
  102. [_group.conversation removeObserver:self forKeyPath:@"groupImage"];
  103. [[NSNotificationCenter defaultCenter] removeObserver:self];
  104. }
  105. - (void)updateHeaderView {
  106. UIImage *avatarImage = [[AvatarMaker sharedAvatarMaker] avatarForConversation:_group.conversation size:_imageView.frame.size.width masked:NO];
  107. _imageView.image = avatarImage;
  108. _imageView.contentMode = UIViewContentModeScaleAspectFill;
  109. _imageView.layer.masksToBounds = YES;
  110. _imageView.layer.cornerRadius = _imageView.bounds.size.width / 2;
  111. _nameLabel.text = _group.name;
  112. if ([_group isOwnGroup]) {
  113. _disclosureButton.hidden = NO;
  114. } else {
  115. _disclosureButton.hidden = YES;
  116. }
  117. _creatorLabel.text = [_group creatorString];
  118. _headerView.accessibilityLabel = [NSString stringWithFormat:@"%@, %@", _nameLabel.text, _creatorLabel.text];
  119. }
  120. - (void)reloadMembers {
  121. members = _group.sortedActiveMembers;
  122. }
  123. - (void)tappedHeaderView {
  124. [self showEditGroupVC];
  125. }
  126. - (void)tappedImage {
  127. if (_group.conversation.groupImage) {
  128. UIImage *image = [UIImage imageWithData:_group.conversation.groupImage.data];
  129. if (image != nil) {
  130. FullscreenImageViewController *imageController = [FullscreenImageViewController controllerForImage:image];
  131. if (SYSTEM_IS_IPAD) {
  132. ModalNavigationController *nav = [[ModalNavigationController alloc] initWithRootViewController:imageController];
  133. nav.showDoneButton = YES;
  134. nav.showFullScreenOnIPad = YES;
  135. [self presentViewController:nav animated:YES completion:nil];
  136. } else {
  137. [self.navigationController pushViewController:imageController animated:YES];
  138. }
  139. } else {
  140. [self showEditGroupVC];
  141. }
  142. } else {
  143. [self showEditGroupVC];
  144. }
  145. }
  146. - (void)showEditGroupVC {
  147. if ([_group isOwnGroup] == NO) {
  148. return;
  149. }
  150. UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"CreateGroup" bundle:nil];
  151. EditGroupViewController *editVC = [storyboard instantiateViewControllerWithIdentifier:@"EditGroupViewController"];
  152. editVC.group = _group;
  153. UINavigationController *navigationVC = [[UINavigationController alloc] initWithRootViewController:editVC];
  154. [ModalPresenter present:navigationVC on:self fromRect:_headerView.frame inView:self.view];
  155. }
  156. - (void)showEditGroupMembersVCFrom:(NSIndexPath *)indexPath {
  157. if ([_group isOwnGroup] == NO || [self canAddMember] == NO) {
  158. return;
  159. }
  160. UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"CreateGroup" bundle:nil];
  161. PickGroupMembersViewController *pickMembersVC = [storyboard instantiateViewControllerWithIdentifier:@"PickGroupMembersViewController"];
  162. pickMembersVC.group = _group;
  163. UINavigationController *navigationVC = [[UINavigationController alloc] initWithRootViewController:pickMembersVC];
  164. UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
  165. [ModalPresenter present:navigationVC on:self fromRect:cell.frame inView:self.view];
  166. [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
  167. }
  168. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  169. if ([segue.identifier isEqualToString:@"ShowContact"]) {
  170. ContactDetailsViewController *detailsVc = (ContactDetailsViewController*)segue.destinationViewController;
  171. detailsVc.contact = selectedContact;
  172. }
  173. else if ([segue.identifier isEqualToString:@"ShowPushSetting"]) {
  174. NotificationSettingViewController *notificationSettingViewController = (NotificationSettingViewController *)segue.destinationViewController;
  175. notificationSettingViewController.identity = [NSString stringWithHexData:self.group.groupId];
  176. notificationSettingViewController.isGroup = YES;
  177. notificationSettingViewController.conversation = _group.conversation;
  178. }
  179. }
  180. - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
  181. if ([keyPath isEqualToString:@"members"]) {
  182. dispatch_async(dispatch_get_main_queue(), ^{
  183. [self reloadMembers];
  184. [self updateHeaderView];
  185. [self.tableView reloadData];
  186. });
  187. } else if ([keyPath isEqualToString:@"groupName"] || [keyPath isEqualToString:@"groupImage"]) {
  188. dispatch_async(dispatch_get_main_queue(), ^{
  189. [self updateHeaderView];
  190. });
  191. }
  192. }
  193. - (void)syncMembers {
  194. [_group syncGroupInfoToAll];
  195. [UIAlertTemplate showAlertWithOwner:self title:NSLocalizedString(@"group_sync_title", nil) message:NSLocalizedString(@"group_sync_message", nil) actionOk:nil];
  196. [self.tableView deselectRowAtIndexPath:[self.tableView indexPathForSelectedRow] animated:YES];
  197. }
  198. - (void)exportChatWithMedia:(bool)withMedia {
  199. EntityManager *em = [[EntityManager alloc] init];
  200. ConversationExporter *exporter = [[ConversationExporter alloc] initWithViewController:self conversation: self.group.conversation
  201. entityManager:em withMedia:withMedia];
  202. [exporter exportGroupConversation];
  203. }
  204. - (void)conversationActionFromRect:(CGRect)rect {
  205. UIAlertController *actionSheet = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:NSLocalizedString(@"include_media_title", nil), kExportConversationMediaSizeLimit] message:nil preferredStyle:UIAlertControllerStyleActionSheet];
  206. [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"include_media", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
  207. [self exportChatWithMedia:true];
  208. }]];
  209. [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"without_media", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
  210. [self exportChatWithMedia:false];
  211. }]];
  212. [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"cancel", nil) style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) {
  213. [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:1 inSection:1] animated:YES];
  214. }]];
  215. actionSheet.popoverPresentationController.sourceRect = rect;
  216. actionSheet.popoverPresentationController.sourceView = self.view;
  217. [self presentViewController:actionSheet animated:YES completion:nil];
  218. }
  219. - (void)sendMessageAction {
  220. NSDictionary *info = [NSDictionary dictionaryWithObjectsAndKeys:
  221. _group.conversation, kKeyConversation,
  222. [NSNumber numberWithBool:YES], kKeyForceCompose,
  223. nil];
  224. [[NSNotificationCenter defaultCenter] postNotificationName:kNotificationShowConversation object:nil userInfo:info];
  225. }
  226. - (BOOL)canExportConversation {
  227. MDMSetup *mdmSetup = [[MDMSetup alloc] initWithSetup:NO];
  228. if (_group.conversation == nil || [mdmSetup disableExport]) {
  229. return NO;
  230. }
  231. return _group.conversation.messages.count > 0;
  232. }
  233. - (void)leaveGroup {
  234. NSString *title = [self alertMessage];
  235. [UIAlertTemplate showAlertWithOwner:self title:title message:nil titleOk:[BundleUtil localizedStringForKey:@"leave"] actionOk:^(UIAlertAction * _Nonnull action) {
  236. [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:3] animated:YES];
  237. [self processLeaveGroup];
  238. } titleCancel:[BundleUtil localizedStringForKey:@"cancel"] actionCancel:^(UIAlertAction * _Nonnull action) {
  239. [self.tableView deselectRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:3] animated:YES];
  240. }];
  241. }
  242. - (NSString *)alertMessage {
  243. NSString *message;
  244. if (_group.conversation.isGroup && _group.conversation.contact == nil) {
  245. message = NSLocalizedString(@"group_admin_delete_confirm", nil);
  246. } else {
  247. message = NSLocalizedString(@"group_delete_confirm", nil);
  248. }
  249. return message;
  250. }
  251. - (void)processLeaveGroup {
  252. [_group leaveGroup];
  253. [self reloadMembers];
  254. [self.tableView reloadData];
  255. [UIAlertTemplate showAlertWithOwner:self title:@"" message:NSLocalizedString(@"group_member_self_left", nil) actionOk:nil];
  256. }
  257. - (BOOL)canAddMember {
  258. return (_group.conversation.members.count < [[BundleUtil objectForInfoDictionaryKey:@"ThreemaMaxGroupMembers"] intValue]);
  259. }
  260. - (void)cloneGroup {
  261. [self.tableView deselectRowAtIndexPath:self.tableView.indexPathForSelectedRow animated:YES];
  262. MDMSetup *mdmSetup = [[MDMSetup alloc] initWithSetup:NO];
  263. if ([mdmSetup disableCreateGroup]) {
  264. [UIAlertTemplate showAlertWithOwner:self title:@"" message:NSLocalizedString(@"disabled_by_device_policy", nil) actionOk:nil];
  265. return;
  266. }
  267. NSString *title = NSLocalizedString(@"group_clone_title", nil);
  268. NSString *message = NSLocalizedString(@"group_clone_message", nil);
  269. UIAlertController *alertController = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
  270. [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"no", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
  271. }]];
  272. [alertController addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"yes", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
  273. UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"CreateGroup" bundle:nil];
  274. CreateGroupNavigationController *navVC = (CreateGroupNavigationController *)[storyboard instantiateInitialViewController];
  275. navVC.cloneGroupId = _group.groupId;
  276. [self presentViewController:navVC animated:YES completion:nil];
  277. }]];
  278. [self presentViewController:alertController animated:YES completion:nil];
  279. }
  280. #pragma mark - Table view data source
  281. - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
  282. if (indexPath.section == 0 && (indexPath.row-1) == members.count) {
  283. [Colors updateTableViewCellBackground:cell];
  284. [Colors setTextColor:[Colors main] inView:cell.contentView];
  285. } else if (indexPath.section == 3 ) {
  286. // handle custom table cells
  287. [Colors updateTableViewCellBackground:cell];
  288. [Colors setTextColor:[Colors red] inView:cell.contentView];
  289. } else {
  290. [Colors updateTableViewCell:cell];
  291. }
  292. }
  293. - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
  294. return 4;
  295. }
  296. - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
  297. {
  298. if (section == 0) {
  299. NSInteger n = members.count + 2;
  300. if ([_group isOwnGroup]) {
  301. n += 2; /* me = creator */
  302. }
  303. return n;
  304. } else if (section == 1) {
  305. if (_hideActionButtons) {
  306. return 0;
  307. }
  308. if (_canExportChat) {
  309. return 2;
  310. }
  311. return 1;
  312. } else if (section == 2) {
  313. if (_hideActionButtons || [_group isSelfMember] == false) {
  314. return 0;
  315. }
  316. return 1;
  317. } else if (section == 3) {
  318. if (_hideActionButtons || [_group isSelfMember] == false) {
  319. return 0;
  320. }
  321. return 1;
  322. }
  323. return 0;
  324. }
  325. - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
  326. {
  327. if (indexPath.section == 0) {
  328. if (indexPath.row == 0) {
  329. /* first = me */
  330. GroupMemberCell *cell = [tableView dequeueReusableCellWithIdentifier:@"GroupMemberCell"];
  331. cell.accessoryType = UITableViewCellAccessoryNone;
  332. cell.selectionStyle = UITableViewCellSelectionStyleNone;
  333. cell.isSelfMember = YES;
  334. cell.contact = nil;
  335. if (![_group isSelfMember]) {
  336. cell.nameLabel.text = [BundleUtil localizedStringForKey:@"you are not a member"];
  337. }
  338. return cell;
  339. } else if ((indexPath.row-1) < members.count) {
  340. GroupMemberCell *cell = [tableView dequeueReusableCellWithIdentifier:@"GroupMemberCell"];
  341. cell.isSelfMember = NO;
  342. cell.contact = [members objectAtIndex:indexPath.row-1];
  343. cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
  344. cell.selectionStyle = UITableViewCellSelectionStyleBlue;
  345. return cell;
  346. }
  347. else if ((indexPath.row-1) == members.count && [_group isOwnGroup]) {
  348. BOOL canAddMember = [self canAddMember];
  349. AddMemberCell *cell = [tableView dequeueReusableCellWithIdentifier:@"AddMemberCell"];
  350. cell.userInteractionEnabled = canAddMember;
  351. cell.plusImage.alpha = canAddMember ? 1.0 : 0.4;
  352. cell.addLabel.enabled = canAddMember;
  353. cell.accessibilityTraits = UIAccessibilityTraitButton;
  354. return cell;
  355. }
  356. else if ((indexPath.row-1) == members.count && ![_group isOwnGroup]) {
  357. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CloneCell"];
  358. cell.accessibilityTraits = UIAccessibilityTraitButton;
  359. return cell;
  360. }
  361. else if ((indexPath.row-1) == (members.count+1)) {
  362. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"SyncCell"];
  363. cell.accessibilityTraits = UIAccessibilityTraitButton;
  364. return cell;
  365. }
  366. else if ((indexPath.row-1) == (members.count+2)) {
  367. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CloneCell"];
  368. cell.accessibilityTraits = UIAccessibilityTraitButton;
  369. return cell;
  370. }
  371. } else if (indexPath.section == 1) {
  372. NSString *cellIdentifier;
  373. if (indexPath.row == 0) {
  374. cellIdentifier = @"SendMessageCell";
  375. } else {
  376. cellIdentifier = @"ExportConversationCell";
  377. }
  378. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
  379. cell.accessibilityTraits = UIAccessibilityTraitButton;
  380. return cell;
  381. } else if (indexPath.section == 2) {
  382. UITableViewCell *pushSettingCell = [tableView dequeueReusableCellWithIdentifier:@"PushSettingCell"];
  383. pushSettingCell.textLabel.text = NSLocalizedString(@"pushSetting_title", @"");
  384. return pushSettingCell;
  385. } else if (indexPath.section == 3) {
  386. UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"LeaveGroupCell"];
  387. cell.accessibilityTraits = UIAccessibilityTraitButton;
  388. return cell;
  389. }
  390. return nil;
  391. }
  392. - (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
  393. if (section == 0) {
  394. return 40.0;
  395. }
  396. return UITableViewAutomaticDimension;
  397. }
  398. - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
  399. if (section == 0) {
  400. return NSLocalizedString(@"members", nil);
  401. }
  402. return nil;
  403. }
  404. - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
  405. if (indexPath.section == 0) {
  406. if (indexPath.row != 0 && (indexPath.row-1) < members.count) {
  407. selectedContact = [members objectAtIndex:indexPath.row-1];
  408. [self performSegueWithIdentifier:@"ShowContact" sender:self];
  409. } else if ((indexPath.row-1) == members.count && [_group isOwnGroup]) {
  410. [self showEditGroupMembersVCFrom:indexPath];
  411. }
  412. else if ((indexPath.row-1) == members.count && ![_group isOwnGroup]) {
  413. [self cloneGroup];
  414. }
  415. else if ((indexPath.row-1) == (members.count+1)) {
  416. [self syncMembers];
  417. }
  418. else if ((indexPath.row-1) == (members.count+2)) {
  419. [self cloneGroup];
  420. }
  421. } else if (indexPath.section == 1) {
  422. if (indexPath.row == 0) {
  423. [self sendMessageAction];
  424. } else {
  425. UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
  426. [self conversationActionFromRect:cell.frame];
  427. }
  428. } else if (indexPath.section == 2) {
  429. } else if (indexPath.section == 3) {
  430. [self leaveGroup];
  431. }
  432. }
  433. #pragma mark - Table view delegae
  434. - (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath
  435. {
  436. if (indexPath.section == 0 && _group.isOwnGroup) {
  437. if (indexPath.row == 0) {
  438. /* first = me */
  439. return NO;
  440. } else if ((indexPath.row-1) < members.count) {
  441. return YES;
  442. }
  443. }
  444. return NO;
  445. }
  446. - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
  447. if (editingStyle == UITableViewCellEditingStyleDelete) {
  448. Contact *contact = [members objectAtIndex:indexPath.row-1];
  449. [_group adminRemoveMember:contact];
  450. [self reloadMembers];
  451. [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone];
  452. }
  453. }
  454. #pragma mark - Mail composer delegate
  455. - (void)mailComposeController:(MFMailComposeViewController *)controller didFinishWithResult:(MFMailComposeResult)result error:(NSError *)error {
  456. [self dismissViewControllerAnimated:YES completion:nil];
  457. }
  458. # pragma mark - preview actions
  459. - (NSArray<id<UIPreviewActionItem>> *)previewActionItems {
  460. NSMutableArray *actions = [NSMutableArray array];
  461. NSString *sendMessageTitle = NSLocalizedString(@"send_message", nil);
  462. UIPreviewAction *sendMessageAction = [UIPreviewAction actionWithTitle:sendMessageTitle style:UIPreviewActionStyleDefault handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
  463. [self sendMessageAction];
  464. }];
  465. [actions addObject:sendMessageAction];
  466. if (_delegate) {
  467. NSString *leaveGroupTitle = NSLocalizedString(@"leave_group", nil);
  468. UIPreviewAction *leaveGroupAction = [UIPreviewAction actionWithTitle:leaveGroupTitle style:UIPreviewActionStyleDestructive handler:^(UIPreviewAction * _Nonnull action, UIViewController * _Nonnull previewViewController) {
  469. [_delegate presentGroupDetails:self onCompletion:^(GroupDetailsViewController *groupDetailsViewController) {
  470. [groupDetailsViewController leaveGroup];
  471. }];
  472. }];
  473. [actions addObject:leaveGroupAction];
  474. }
  475. return actions;
  476. }
  477. #pragma mark - Notifications
  478. - (void)updatedGroup:(NSNotification*)notification {
  479. NSString *creatorString = notification.userInfo[@"creatorString"];
  480. NSData *groupId = notification.userInfo[@"groupId"];
  481. if ([_group.creatorString isEqualToString:creatorString] && _group.groupId == groupId) {
  482. dispatch_async(dispatch_get_main_queue(), ^{
  483. [self reloadMembers];
  484. [self updateHeaderView];
  485. [self.tableView reloadData];
  486. });
  487. }
  488. }
  489. @end