From 568a084531e9696943859d6fba520ee9fbfad9ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcel=20M=C3=BCller?= Date: Fri, 20 Sep 2024 11:44:12 +0200 Subject: [PATCH] Add support for archived conversations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcel Müller --- NextcloudTalk/NCAPIControllerExtensions.swift | 34 +++ NextcloudTalk/NCDatabaseManager.h | 1 + NextcloudTalk/NCDatabaseManager.m | 3 +- NextcloudTalk/NCRoom.h | 1 + NextcloudTalk/NCRoom.m | 4 +- NextcloudTalk/RoomInfoTableViewController.m | 233 +++++++++++++++--- NextcloudTalk/RoomsTableViewController.m | 58 ++++- NextcloudTalk/en.lproj/Localizable.strings | 21 ++ 8 files changed, 309 insertions(+), 46 deletions(-) diff --git a/NextcloudTalk/NCAPIControllerExtensions.swift b/NextcloudTalk/NCAPIControllerExtensions.swift index 939a189aa..1858a775c 100644 --- a/NextcloudTalk/NCAPIControllerExtensions.swift +++ b/NextcloudTalk/NCAPIControllerExtensions.swift @@ -343,4 +343,38 @@ import Foundation completionBlock(false) } } + + // MARK: - Archived conversations + + public func archiveRoom(_ token: String, forAccount account: TalkAccount, completionBlock: @escaping (_ success: Bool) -> Void) { + guard let encodedToken = token.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed), + let apiSessionManager = self.apiSessionManagers.object(forKey: account.accountId) as? NCAPISessionManager + else { + completionBlock(false) + return + } + + let apiVersion = self.conversationAPIVersion(for: account) + let urlString = self.getRequestURL(forEndpoint: "room/\(encodedToken)/archive", withAPIVersion: apiVersion, for: account) + + apiSessionManager.postOcs(urlString, account: account) { _, error in + completionBlock(error == nil) + } + } + + public func unarchiveRoom(_ token: String, forAccount account: TalkAccount, completionBlock: @escaping (_ success: Bool) -> Void) { + guard let encodedToken = token.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed), + let apiSessionManager = self.apiSessionManagers.object(forKey: account.accountId) as? NCAPISessionManager + else { + completionBlock(false) + return + } + + let apiVersion = self.conversationAPIVersion(for: account) + let urlString = self.getRequestURL(forEndpoint: "room/\(encodedToken)/archive", withAPIVersion: apiVersion, for: account) + + apiSessionManager.deleteOcs(urlString, account: account) { _, error in + completionBlock(error == nil) + } + } } diff --git a/NextcloudTalk/NCDatabaseManager.h b/NextcloudTalk/NCDatabaseManager.h index 80b0cbed1..19b24ef1b 100644 --- a/NextcloudTalk/NCDatabaseManager.h +++ b/NextcloudTalk/NCDatabaseManager.h @@ -74,6 +74,7 @@ extern NSString * const kCapabilityChatReadLast; extern NSString * const kCapabilityBanV1; extern NSString * const kCapabilityMentionPermissions; extern NSString * const kCapabilityEditMessagesNoteToSelf; +extern NSString * const kCapabilityArchivedConversations; extern NSString * const kNotificationsCapabilityExists; diff --git a/NextcloudTalk/NCDatabaseManager.m b/NextcloudTalk/NCDatabaseManager.m index 867ccebe1..f6abb5355 100644 --- a/NextcloudTalk/NCDatabaseManager.m +++ b/NextcloudTalk/NCDatabaseManager.m @@ -16,7 +16,7 @@ NSString *const kTalkDatabaseFolder = @"Library/Application Support/Talk"; NSString *const kTalkDatabaseFileName = @"talk.realm"; -uint64_t const kTalkDatabaseSchemaVersion = 68; +uint64_t const kTalkDatabaseSchemaVersion = 69; NSString * const kCapabilitySystemMessages = @"system-messages"; NSString * const kCapabilityNotificationLevels = @"notification-levels"; @@ -75,6 +75,7 @@ NSString * const kCapabilityBanV1 = @"ban-v1"; NSString * const kCapabilityMentionPermissions = @"mention-permissions"; NSString * const kCapabilityEditMessagesNoteToSelf = @"edit-messages-note-to-self"; +NSString * const kCapabilityArchivedConversations = @"archived-conversations"; NSString * const kNotificationsCapabilityExists = @"exists"; diff --git a/NextcloudTalk/NCRoom.h b/NextcloudTalk/NCRoom.h index 0e2ac0563..835b7ab16 100644 --- a/NextcloudTalk/NCRoom.h +++ b/NextcloudTalk/NCRoom.h @@ -142,6 +142,7 @@ extern NSString * const NCRoomObjectTypeRoom; @property (nonatomic, copy) NSString *remoteToken; @property (nonatomic, copy) NSString *lastReceivedProxyHash; @property (nonatomic, assign) NSInteger mentionPermissions; +@property (nonatomic, assign) BOOL isArchived; + (instancetype)roomWithDictionary:(NSDictionary *)roomDict; + (instancetype)roomWithDictionary:(NSDictionary *)roomDict andAccountId:(NSString *)accountId; diff --git a/NextcloudTalk/NCRoom.m b/NextcloudTalk/NCRoom.m index 99f0df722..bad035818 100644 --- a/NextcloudTalk/NCRoom.m +++ b/NextcloudTalk/NCRoom.m @@ -61,6 +61,7 @@ + (instancetype)roomWithDictionary:(NSDictionary *)roomDict room.remoteServer = [roomDict objectForKey:@"remoteServer"]; room.remoteToken = [roomDict objectForKey:@"remoteToken"]; room.mentionPermissions = [[roomDict objectForKey:@"mentionPermissions"] integerValue]; + room.isArchived = [[roomDict objectForKey:@"isArchived"] boolValue]; // Local-only field -> update only if there's actually a value if ([roomDict objectForKey:@"pendingMessage"] != nil) { @@ -80,7 +81,7 @@ + (instancetype)roomWithDictionary:(NSDictionary *)roomDict } else { room.displayName = [displayName stringValue]; } - + id participants = [roomDict objectForKey:@"participants"]; if ([participants isKindOfClass:[NSDictionary class]]) { room.participants = (RLMArray *)[participants allKeys]; @@ -191,6 +192,7 @@ + (void)updateRoom:(NCRoom *)managedRoom withRoom:(NCRoom *)room managedRoom.remoteToken = room.remoteToken; managedRoom.remoteServer = room.remoteServer; managedRoom.mentionPermissions = room.mentionPermissions; + managedRoom.isArchived = room.isArchived; } + (NSString *)primaryKey { diff --git a/NextcloudTalk/RoomInfoTableViewController.m b/NextcloudTalk/RoomInfoTableViewController.m index 763faf80e..dd309394e 100644 --- a/NextcloudTalk/RoomInfoTableViewController.m +++ b/NextcloudTalk/RoomInfoTableViewController.m @@ -40,6 +40,7 @@ kRoomInfoSectionWebinar, kRoomInfoSectionSIP, kRoomInfoSectionParticipants, + kRoomInfoSectionNonDestructive, kRoomInfoSectionDestructive } RoomInfoSection; @@ -78,6 +79,11 @@ kSIPActionNumber } SIPAction; +typedef enum NondestructiveAction { + kNondestructiveActionArchive = 0, + kNondestructiveActionUnarchive +} NondestructiveAction; + typedef enum DestructiveAction { kDestructiveActionLeave = 0, kDestructiveActionClearHistory, @@ -105,6 +111,8 @@ kModificationErrorRoomDescription, kModificationErrorBanActor, kModificationErrorMentionPermissions, + kModificationErrorArchive, + kModificationErrorUnarchive, } ModificationError; typedef enum FileAction { @@ -161,7 +169,7 @@ - (instancetype)initForRoom:(NCRoom *)room fromChatViewController:(ChatViewContr - (void)viewDidLoad { [super viewDidLoad]; - + self.navigationItem.title = NSLocalizedString(@"Conversation settings", nil); [self.navigationController.navigationBar setTitleTextAttributes: @{NSForegroundColorAttributeName:[NCAppBranding themeTextColor]}]; @@ -177,15 +185,15 @@ - (void)viewDidLoad self.navigationItem.standardAppearance = appearance; self.navigationItem.compactAppearance = appearance; self.navigationItem.scrollEdgeAppearance = appearance; - + _roomParticipants = [[NSMutableArray alloc] init]; - + _publicSwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; [_publicSwitch addTarget: self action: @selector(publicValueChanged:) forControlEvents:UIControlEventValueChanged]; - + _listableSwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; [_listableSwitch addTarget: self action: @selector(listableValueChanged:) forControlEvents:UIControlEventValueChanged]; - + _listableForEveryoneSwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; [_listableForEveryoneSwitch addTarget: self action: @selector(listableForEveryoneValueChanged:) forControlEvents:UIControlEventValueChanged]; @@ -194,23 +202,23 @@ - (void)viewDidLoad _readOnlySwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; [_readOnlySwitch addTarget: self action: @selector(readOnlyValueChanged:) forControlEvents:UIControlEventValueChanged]; - + _lobbySwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; [_lobbySwitch addTarget: self action: @selector(lobbyValueChanged:) forControlEvents:UIControlEventValueChanged]; - + _sipSwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; [_sipSwitch addTarget: self action: @selector(sipValueChanged:) forControlEvents:UIControlEventValueChanged]; - + _sipNoPINSwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; [_sipNoPINSwitch addTarget: self action: @selector(sipNoPINValueChanged:) forControlEvents:UIControlEventValueChanged]; - + _callNotificationSwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; [_callNotificationSwitch addTarget: self action: @selector(callNotificationValueChanged:) forControlEvents:UIControlEventValueChanged]; - + _lobbyDatePicker = [[UIDatePicker alloc] init]; _lobbyDatePicker.datePickerMode = UIDatePickerModeDateAndTime; _lobbyDatePicker.preferredDatePickerStyle = UIDatePickerStyleWheels; - + _lobbyDateTextField = [[UITextField alloc] initWithFrame:CGRectMake(0, 00, 150, 30)]; _lobbyDateTextField.textAlignment = NSTextAlignmentRight; _lobbyDateTextField.placeholder = NSLocalizedString(@"Manual", @"TRANSLATORS this is used when no meeting start time is set and the meeting will be started manually"); @@ -218,14 +226,14 @@ - (void)viewDidLoad _lobbyDateTextField.minimumFontSize = 9; [_lobbyDateTextField setInputView:_lobbyDatePicker]; [self setupLobbyDatePicker]; - + _modifyingRoomView = [[UIActivityIndicatorView alloc] init]; _modifyingRoomView.color = [NCAppBranding themeTextColor]; - + _headerView = [[HeaderWithButton alloc] init]; [_headerView.button setTitle:NSLocalizedString(@"Add", nil) forState:UIControlStateNormal]; [_headerView.button addTarget:self action:@selector(addParticipantsButtonPressed) forControlEvents:UIControlEventTouchUpInside]; - + [self.tableView registerNib:[UINib nibWithNibName:kContactsTableCellNibName bundle:nil] forCellReuseIdentifier:kContactCellIdentifier]; [self.tableView registerNib:[UINib nibWithNibName:RoomNameTableViewCell.nibName bundle:nil] forCellReuseIdentifier:RoomNameTableViewCell.identifier]; [self.tableView registerNib:[UINib nibWithNibName:RoomDescriptionTableViewCell.nibName bundle:nil] forCellReuseIdentifier:RoomDescriptionTableViewCell.identifier]; @@ -235,14 +243,14 @@ - (void)viewDidLoad target:self action:@selector(cancelButtonPressed)]; self.navigationController.navigationBar.topItem.leftBarButtonItem = cancelButton; } - + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didUpdateRoom:) name:NCRoomsManagerDidUpdateRoomNotification object:nil]; } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; - + [[NCRoomsManager sharedInstance] updateRoom:_room.token withCompletionBlock:nil]; [self getRoomParticipants]; } @@ -279,27 +287,33 @@ - (NSArray *)getRoomInfoSections NSMutableArray *sections = [[NSMutableArray alloc] init]; // Room name section [sections addObject:[NSNumber numberWithInt:kRoomInfoSectionName]]; + // Room description section if ([[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityRoomDescription] && _room.roomDescription && ![_room.roomDescription isEqualToString:@""]) { [sections addObject:[NSNumber numberWithInt:kRoomInfoSectionDescription]]; } + // File actions section if ([_room.objectType isEqualToString:NCRoomObjectTypeFile]) { [sections addObject:[NSNumber numberWithInt:kRoomInfoSectionFile]]; } + // Shared items section if ([[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityRichObjectListMedia] && ![self.room isFederated]) { [sections addObject:[NSNumber numberWithInt:kRoomInfoSectionSharedItems]]; } + // Notifications section if ([self getNotificationsActions].count > 0) { [sections addObject:[NSNumber numberWithInt:kRoomInfoSectionNotifications]]; } + // Conversation section if ([self getConversationActions].count > 0) { [sections addObject:[NSNumber numberWithInt:kRoomInfoSectionConversation]]; } + // Moderator sections if (_room.canModerate) { // Guests section @@ -309,16 +323,24 @@ - (NSArray *)getRoomInfoSections [sections addObject:[NSNumber numberWithInt:kRoomInfoSectionWebinar]]; } } + // SIP section if (_room.sipState > NCRoomSIPStateDisabled) { [sections addObject:[NSNumber numberWithInt:kRoomInfoSectionSIP]]; } + // Participants section [sections addObject:[NSNumber numberWithInt:kRoomInfoSectionParticipants]]; + + if ([[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityArchivedConversations]) { + [sections addObject:[NSNumber numberWithInt:kRoomInfoSectionNonDestructive]]; + } + // Destructive actions section if (!_hideDestructiveActions) { [sections addObject:[NSNumber numberWithInt:kRoomInfoSectionDestructive]]; } + return [NSArray arrayWithArray:sections]; } @@ -344,13 +366,13 @@ - (NSArray *)getNotificationsActions [actions addObject:[NSNumber numberWithInt:kNotificationActionChatNotifications]]; } // Call notifications action - if ([[NCDatabaseManager sharedInstance] roomHasTalkCapability:kCapabilityNotificationCalls forRoom:self.room] && + if ([[NCDatabaseManager sharedInstance] roomHasTalkCapability:kCapabilityNotificationCalls forRoom:self.room] && [[NCDatabaseManager sharedInstance] roomTalkCapabilitiesForRoom:self.room].callEnabled && ![self.room isFederated]) { - + [actions addObject:[NSNumber numberWithInt:kNotificationActionCallNotifications]]; } - + return [NSArray arrayWithArray:actions]; } @@ -372,7 +394,7 @@ - (NSArray *)getFileActions [actions addObject:[NSNumber numberWithInt:kFileActionPreview]]; // Open file in nextcloud app [actions addObject:[NSNumber numberWithInt:kFileActionOpenInFilesApp]]; - + return [NSArray arrayWithArray:actions]; } @@ -422,7 +444,7 @@ - (NSArray *)getConversationActions // Listable room action if ([[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityListableRooms]) { [actions addObject:[NSNumber numberWithInt:kConversationActionListable]]; - + if (_room.listable != NCRoomListableScopeParticipantsOnly && [[NCSettingsController sharedInstance] isGuestsAppEnabled]) { [actions addObject:[NSNumber numberWithInt:kConversationActionListableForEveryone]]; } @@ -442,7 +464,7 @@ - (NSArray *)getConversationActions if (_room.type != kNCRoomTypeChangelog && _room.type != kNCRoomTypeNoteToSelf) { [actions addObject:[NSNumber numberWithInt:kConversationActionShareLink]]; } - + return [NSArray arrayWithArray:actions]; } @@ -476,6 +498,21 @@ - (NSArray *)getWebinarActions return [NSArray arrayWithArray:actions]; } +- (NSArray *)getRoomNondestructiveActions +{ + NSMutableArray *actions = [[NSMutableArray alloc] init]; + + if ([[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityArchivedConversations]) { + if (_room.isArchived) { + [actions addObject:[NSNumber numberWithInt:kNondestructiveActionUnarchive]]; + } else { + [actions addObject:[NSNumber numberWithInt:kNondestructiveActionArchive]]; + } + } + + return actions; +} + - (NSArray *)getRoomDestructiveActions { NSMutableArray *actions = [[NSMutableArray alloc] init]; @@ -542,67 +579,67 @@ - (void)showRoomModificationError:(ModificationError)error withMessage:(NSString case kModificationErrorChatNotifications: errorDescription = NSLocalizedString(@"Could not change notifications setting", nil); break; - + case kModificationErrorCallNotifications: errorDescription = NSLocalizedString(@"Could not change call notifications setting", nil); break; - + case kModificationErrorShare: errorDescription = NSLocalizedString(@"Could not change sharing permissions of the conversation", nil); break; - + case kModificationErrorPassword: errorDescription = NSLocalizedString(@"Could not change password protection settings", nil); break; - + case kModificationErrorResendInvitations: errorDescription = NSLocalizedString(@"Could not resend email invitations", nil); break; - + case kModificationErrorSendCallNotification: errorDescription = NSLocalizedString(@"Could not send call notification", nil); break; - + case kModificationErrorLobby: errorDescription = NSLocalizedString(@"Could not change lobby state of the conversation", nil); break; - + case kModificationErrorSIP: errorDescription = NSLocalizedString(@"Could not change SIP state of the conversation", nil); break; - + case kModificationErrorModeration: errorDescription = NSLocalizedString(@"Could not change moderation permissions of the participant", nil); break; - + case kModificationErrorRemove: errorDescription = NSLocalizedString(@"Could not remove participant", nil); break; - + case kModificationErrorLeave: errorDescription = NSLocalizedString(@"Could not leave conversation", nil); break; - + case kModificationErrorLeaveModeration: errorDescription = NSLocalizedString(@"You need to promote a new moderator before you can leave this conversation", nil); break; - + case kModificationErrorDelete: errorDescription = NSLocalizedString(@"Could not delete conversation", nil); break; - + case kModificationErrorClearHistory: errorDescription = NSLocalizedString(@"Could not clear chat history", nil); break; - + case kModificationErrorListable: errorDescription = NSLocalizedString(@"Could not change listable scope of the conversation", nil); break; - + case kModificationErrorReadOnly: errorDescription = NSLocalizedString(@"Could not change read-only state of the conversation", nil); break; - + case kModificationErrorMessageExpiration: errorDescription = NSLocalizedString(@"Could not set message expiration time", nil); break; @@ -619,10 +656,18 @@ - (void)showRoomModificationError:(ModificationError)error withMessage:(NSString errorDescription = NSLocalizedString(@"Could not change mention permissions of the conversation", nil); break; + case kModificationErrorArchive: + errorDescription = NSLocalizedString(@"Could not archive conversation", nil); + break; + + case kModificationErrorUnarchive: + errorDescription = NSLocalizedString(@"Could not unarchive conversation", nil); + break; + default: break; } - + UIAlertController *renameDialog = [UIAlertController alertControllerWithTitle:errorDescription message:errorMessage @@ -1098,6 +1143,32 @@ - (void)clearHistory }]; } +- (void)archiveRoom +{ + TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount]; + + [[NCAPIController sharedInstance] archiveRoom:_room.token forAccount:activeAccount completionBlock:^(BOOL success) { + if (!success) { + [self showRoomModificationError:kModificationErrorArchive]; + } + + [[NCRoomsManager sharedInstance] updateRoom:self->_room.token withCompletionBlock:nil]; + }]; +} + +- (void)unarchiveRoom +{ + TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount]; + + [[NCAPIController sharedInstance] unarchiveRoom:_room.token forAccount:activeAccount completionBlock:^(BOOL success) { + if (!success) { + [self showRoomModificationError:kModificationErrorUnarchive]; + } + + [[NCRoomsManager sharedInstance] updateRoom:self->_room.token withCompletionBlock:nil]; + }]; +} + - (void)leaveRoom { [[NCAPIController sharedInstance] removeSelfFromRoom:_room.token forAccount:[[NCDatabaseManager sharedInstance] activeAccount] withCompletionBlock:^(NSInteger errorCode, NSError *error) { @@ -1639,7 +1710,11 @@ - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger case kRoomInfoSectionParticipants: return _roomParticipants.count; break; - + + case kRoomInfoSectionNonDestructive: + return [self getRoomNondestructiveActions].count; + break; + case kRoomInfoSectionDestructive: return [self getRoomDestructiveActions].count; break; @@ -1694,6 +1769,26 @@ - (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInte return nil; } +- (NSString *)tableView:(UITableView *)tableView titleForFooterInSection:(NSInteger)section +{ + NSArray *sections = [self getRoomInfoSections]; + RoomInfoSection infoSection = [[sections objectAtIndex:section] intValue]; + switch (infoSection) { + case kRoomInfoSectionNonDestructive: + if (!_room.isArchived) { + return NSLocalizedString(@"Once a conversation is archived, it will be hidden by default. Select the filter 'Archived' to view archived conversations. Direct mentions will still be received.", nil); + } else { + return NSLocalizedString(@"Once a conversation is unarchived, it will be shown by default again.", nil); + } + + break; + default: + break; + } + + return nil; +} + - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { NSArray *sections = [self getRoomInfoSections]; @@ -1767,7 +1862,9 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N static NSString *listableForEveryoneCellIdentifier = @"ListableForEveryoneCellIdentifier"; static NSString *mentionPermissionsCellIdentifier = @"mentionPermissionsCellIdentifier"; static NSString *readOnlyStateCellIdentifier = @"ReadOnlyStateCellIdentifier"; - + static NSString *archiveConversationCellIdentifier = @"ArchiveConversationCellIdentifier"; + static NSString *unarchiveConversationCellIdentifier = @"UnarchiveConversationCellIdentifier"; + NSArray *sections = [self getRoomInfoSections]; RoomInfoSection section = [[sections objectAtIndex:indexPath.section] intValue]; switch (section) { @@ -2326,6 +2423,44 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N return cell; } break; + case kRoomInfoSectionNonDestructive: + { + NSArray *actions = [self getRoomNondestructiveActions]; + NondestructiveAction action = [[actions objectAtIndex:indexPath.row] intValue]; + switch (action) { + case kNondestructiveActionArchive: + { + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:archiveConversationCellIdentifier]; + if (!cell) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:archiveConversationCellIdentifier]; + } + + cell.textLabel.text = NSLocalizedString(@"Archive conversation", nil); + cell.textLabel.numberOfLines = 0; + UIImage *image = [[UIImage systemImageNamed:@"archivebox"] imageWithTintColor:[UIColor labelColor] renderingMode:UIImageRenderingModeAlwaysOriginal]; + [cell.imageView setImage:image]; + + return cell; + } + break; + case kNondestructiveActionUnarchive: + { + UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:unarchiveConversationCellIdentifier]; + if (!cell) { + cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:unarchiveConversationCellIdentifier]; + } + + cell.textLabel.text = NSLocalizedString(@"Unarchive conversation", nil); + cell.textLabel.numberOfLines = 0; + UIImage *image = [[UIImage systemImageNamed:@"eye"] imageWithTintColor:[UIColor labelColor] renderingMode:UIImageRenderingModeAlwaysOriginal]; + [cell.imageView setImage:image]; + + return cell; + } + break; + } + } + break; case kRoomInfoSectionDestructive: { NSArray *actions = [self getRoomDestructiveActions]; @@ -2475,6 +2610,22 @@ - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath [self showOptionsForParticipantAtIndexPath:indexPath]; } break; + case kRoomInfoSectionNonDestructive: + { + NSArray *actions = [self getRoomNondestructiveActions]; + NondestructiveAction action = [[actions objectAtIndex:indexPath.row] intValue]; + switch (action) { + case kNondestructiveActionArchive: + [self archiveRoom]; + break; + case kNondestructiveActionUnarchive: + [self unarchiveRoom]; + break; + default: + break; + } + } + break; case kRoomInfoSectionDestructive: { NSArray *actions = [self getRoomDestructiveActions]; diff --git a/NextcloudTalk/RoomsTableViewController.m b/NextcloudTalk/RoomsTableViewController.m index 9e01d7084..a6cd3d4fe 100644 --- a/NextcloudTalk/RoomsTableViewController.m +++ b/NextcloudTalk/RoomsTableViewController.m @@ -33,7 +33,8 @@ typedef enum RoomsFilter { kRoomsFilterAll = 0, kRoomsFilterUnread, - kRoomsFilterMentioned + kRoomsFilterMentioned, + kRoomsFilterArchived } RoomsFilter; typedef enum RoomsSections { @@ -706,9 +707,11 @@ - (NSArray *)filterRoomsWithFilter:(RoomsFilter)filter case kRoomsFilterUnread: return [_allRooms filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"unreadMessages > 0"]]; case kRoomsFilterMentioned: - return [_allRooms filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"hasUnreadMention == YES"]]; + return [_allRooms filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"hasUnreadMention == YES AND isArchived == NO"]]; + case kRoomsFilterArchived: + return [_allRooms filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"isArchived == YES"]]; default: - return _allRooms; + return [_allRooms filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"isArchived == NO OR hasUnreadMention == YES"]]; } } @@ -748,6 +751,10 @@ - (NSArray *)availableFilters [filters addObject:[NSNumber numberWithInt:kRoomsFilterUnread]]; [filters addObject:[NSNumber numberWithInt:kRoomsFilterMentioned]]; + if ([[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityArchivedConversations]) { + [filters addObject:[NSNumber numberWithInt:kRoomsFilterArchived]]; + } + return [NSArray arrayWithArray:filters]; } @@ -760,6 +767,8 @@ - (NSString *)filterName:(RoomsFilter)filter return NSLocalizedString(@"Unread", @"'Unread' meaning 'Unread conversations'"); case kRoomsFilterMentioned: return NSLocalizedString(@"Mentioned", @"'Mentioned' meaning 'Mentioned conversations'"); + case kRoomsFilterArchived: + return NSLocalizedString(@"Archived", @"'Archived' meaning 'Archived conversations'"); default: return @""; } @@ -1061,6 +1070,32 @@ - (void)shareLinkFromRoom:(NCRoom *)room } } +- (void)archiveRoom:(NCRoom *)room +{ + TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount]; + + [[NCAPIController sharedInstance] archiveRoom:room.token forAccount:activeAccount completionBlock:^(BOOL success) { + if (!success) { + NSLog(@"Error archiving room"); + } + + [[NCRoomsManager sharedInstance] updateRoomsUpdatingUserStatus:YES onlyLastModified:NO]; + }]; +} + +- (void)unarchiveRoom:(NCRoom *)room +{ + TalkAccount *activeAccount = [[NCDatabaseManager sharedInstance] activeAccount]; + + [[NCAPIController sharedInstance] unarchiveRoom:room.token forAccount:activeAccount completionBlock:^(BOOL success) { + if (!success) { + NSLog(@"Error unarchiving room"); + } + + [[NCRoomsManager sharedInstance] updateRoomsUpdatingUserStatus:YES onlyLastModified:NO]; + }]; +} + - (void)markRoomAsRead:(NCRoom *)room { [[NCAPIController sharedInstance] setChatReadMarker:room.lastMessage.messageId inRoom:room.token forAccount:[[NCDatabaseManager sharedInstance] activeAccount] withCompletionBlock:^(NSError *error) { @@ -1630,6 +1665,23 @@ - (UIContextMenuConfiguration *)tableView:(UITableView *)tableView contextMenuCo [actions addObject:notificationActions]; } + // Archive conversation + if ([[NCDatabaseManager sharedInstance] serverHasTalkCapability:kCapabilityArchivedConversations]) { + if (room.isArchived) { + UIAction *unarchiveAction = [UIAction actionWithTitle:NSLocalizedString(@"Unarchive conversation", nil) image:[UIImage systemImageNamed:@"eye"] identifier:nil handler:^(UIAction *action) { + [weakSelf unarchiveRoom:room]; + }]; + + [actions addObject:unarchiveAction]; + } else { + UIAction *archiveAction = [UIAction actionWithTitle:NSLocalizedString(@"Archive conversation", nil) image:[UIImage systemImageNamed:@"archivebox"] identifier:nil handler:^(UIAction *action) { + [weakSelf archiveRoom:room]; + }]; + + [actions addObject:archiveAction]; + } + } + // Room info UIAction *roomInfoAction = [UIAction actionWithTitle:NSLocalizedString(@"Conversation settings", nil) image:[UIImage systemImageNamed:@"gearshape"] identifier:nil handler:^(UIAction *action) { [weakSelf presentRoomInfoForRoom:room]; diff --git a/NextcloudTalk/en.lproj/Localizable.strings b/NextcloudTalk/en.lproj/Localizable.strings index 0c615a4d7..2527ce353 100644 --- a/NextcloudTalk/en.lproj/Localizable.strings +++ b/NextcloudTalk/en.lproj/Localizable.strings @@ -304,6 +304,12 @@ /* No comment provided by engineer. */ "Appear offline" = "Appear offline"; +/* No comment provided by engineer. */ +"Archive conversation" = "Archive conversation"; + +/* 'Archived' meaning 'Archived conversations' */ +"Archived" = "Archived"; + /* Alice and Bob are typing… */ "are typing…" = "are typing…"; @@ -502,6 +508,9 @@ /* No comment provided by engineer. */ "Could not add participant" = "Could not add participant"; +/* No comment provided by engineer. */ +"Could not archive conversation" = "Could not archive conversation"; + /* No comment provided by engineer. */ "Could not ban participant" = "Could not ban participant"; @@ -592,6 +601,9 @@ /* No comment provided by engineer. */ "Could not share file" = "Could not share file"; +/* No comment provided by engineer. */ +"Could not unarchive conversation" = "Could not unarchive conversation"; + /* No comment provided by engineer. */ "Create" = "Create"; @@ -1237,9 +1249,15 @@ /* Will be used as the caller name when a VoIP notification can't be decrypted */ "Old account" = "Old account"; +/* No comment provided by engineer. */ +"Once a conversation is archived, it will be hidden by default. Select the filter 'Archived' to view archived conversations. Direct mentions will still be received." = "Once a conversation is archived, it will be hidden by default. Select the filter 'Archived' to view archived conversations. Direct mentions will still be received."; + /* No comment provided by engineer. */ "Once a conversation is left, to rejoin a closed conversation, an invite is needed. An open conversation can be rejoined at any time." = "Once a conversation is left, to rejoin a closed conversation, an invite is needed. An open conversation can be rejoined at any time."; +/* No comment provided by engineer. */ +"Once a conversation is unarchived, it will be shown by default again." = "Once a conversation is unarchived, it will be shown by default again."; + /* No comment provided by engineer. */ "Online" = "Online"; @@ -1741,6 +1759,9 @@ /* No comment provided by engineer. */ "Unable to open file" = "Unable to open file"; +/* No comment provided by engineer. */ +"Unarchive conversation" = "Unarchive conversation"; + /* No comment provided by engineer. */ "Unavailable" = "Unavailable";