diff --git a/Session/Conversations/ConversationViewController.m b/Session/Conversations/ConversationViewController.m index 0bbb517eb..b7f2b3a53 100644 --- a/Session/Conversations/ConversationViewController.m +++ b/Session/Conversations/ConversationViewController.m @@ -1528,15 +1528,17 @@ typedef enum : NSUInteger { [self showDetailViewForViewItem:conversationViewItem]; } -- (void)report:(id)conversationViewItem +- (void)banUser:(id)conversationViewItem { - UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Report?" message:@"If the message is found to violate the Session Public Chat code of conduct it will be removed." preferredStyle:UIAlertControllerStyleAlert]; - [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) { - uint64_t messageID = 0; - if ([conversationViewItem.interaction isKindOfClass:TSMessage.class]) { - messageID = ((TSMessage *)conversationViewItem.interaction).openGroupServerMessageID; + UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Ban This User?" message:nil preferredStyle:UIAlertControllerStyleAlert]; + [alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) { + NSString* publicKey; + if ([conversationViewItem.interaction isKindOfClass:TSIncomingMessage.class]) { + publicKey = ((TSIncomingMessage *)conversationViewItem.interaction).authorId; } - [SNOpenGroupAPI reportMessageWithID:messageID inChannel:1 onServer:@"https://chat.getsession.org"]; + SNOpenGroup *openGroup = [LKStorage.shared getOpenGroupForThreadID:self.thread.uniqueId]; + if (openGroup == nil) return; + [[SNOpenGroupAPI banPublicKey:publicKey fromServer:openGroup.server] retainUntilComplete]; }]]; [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleDefault handler:nil]]; [self presentViewController:alert animated:YES completion:nil]; diff --git a/Session/Conversations/ConversationViewItem.h b/Session/Conversations/ConversationViewItem.h index 2d45d5eea..45f91a697 100644 --- a/Session/Conversations/ConversationViewItem.h +++ b/Session/Conversations/ConversationViewItem.h @@ -67,6 +67,7 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType); @property (nonatomic, readonly) BOOL isGroupThread; @property (nonatomic, readonly) BOOL userCanDeleteGroupMessage; +@property (nonatomic, readonly) BOOL userHasModerationPermission; @property (nonatomic, readonly) BOOL hasBodyText; diff --git a/Session/Conversations/ConversationViewItem.m b/Session/Conversations/ConversationViewItem.m index aff2f43a8..75e2b9ceb 100644 --- a/Session/Conversations/ConversationViewItem.m +++ b/Session/Conversations/ConversationViewItem.m @@ -1184,6 +1184,23 @@ NSString *NSStringForOWSMessageCellType(OWSMessageCellType cellType) } } +- (BOOL)userHasModerationPermission +{ + if (!self.isGroupThread) return false; + TSGroupThread *groupThread = (TSGroupThread *)self.interaction.thread; + + // Make sure it's an open group message + TSMessage *message = (TSMessage *)self.interaction; + if (!message.isOpenGroupMessage) return false; + + // Ensure we have the details needed to contact the server + SNOpenGroup *openGroup = [LKStorage.shared getOpenGroupForThreadID:groupThread.uniqueId]; + if (openGroup == nil) return false; + + // Check that we're a moderator + return [SNOpenGroupAPI isUserModerator:[SNGeneralUtilities getUserPublicKey] forChannel:openGroup.channel onServer:openGroup.server]; +} + @end NS_ASSUME_NONNULL_END diff --git a/Session/Conversations/MessageActions.swift b/Session/Conversations/MessageActions.swift index d049b7c20..96fe41dc5 100644 --- a/Session/Conversations/MessageActions.swift +++ b/Session/Conversations/MessageActions.swift @@ -6,7 +6,7 @@ import Foundation @objc protocol MessageActionsDelegate: class { - func report(_ conversationViewItem: ConversationViewItem) + func banUser(_ conversationViewItem: ConversationViewItem) func messageActionsShowDetailsForItem(_ conversationViewItem: ConversationViewItem) func messageActionsReplyToItem(_ conversationViewItem: ConversationViewItem) func copyPublicKey(for conversationViewItem: ConversationViewItem) @@ -45,14 +45,6 @@ struct MessageActionBuilder { block: { [weak delegate] _ in delegate?.messageActionsShowDetailsForItem(conversationViewItem) } ) } - - static func report(_ conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction { - return MenuAction(image: #imageLiteral(resourceName: "Flag"), - title: NSLocalizedString("Report", comment: ""), - subtitle: nil, - block: { [weak delegate] _ in delegate?.report(conversationViewItem) } - ) - } static func deleteMessage(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction { return MenuAction(image: #imageLiteral(resourceName: "ic_trash"), @@ -61,6 +53,14 @@ struct MessageActionBuilder { block: { _ in conversationViewItem.deleteAction() } ) } + + static func banUser(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction { + return MenuAction(image: #imageLiteral(resourceName: "ic_block"), + title: "Ban User", + subtitle: nil, + block: { [weak delegate] _ in delegate?.banUser(conversationViewItem) } + ) + } static func copyMedia(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction { return MenuAction(image: #imageLiteral(resourceName: "ic_copy"), @@ -108,10 +108,9 @@ class ConversationViewItemActions: NSObject { actions.append(deleteAction) } - if isGroup && conversationViewItem.interaction.thread.name() == "Loki Public Chat" - || conversationViewItem.interaction.thread.name() == "Session Public Chat" { - let reportAction = MessageActionBuilder.report(conversationViewItem, delegate: delegate) - actions.append(reportAction) + if isGroup && conversationViewItem.interaction is TSIncomingMessage && conversationViewItem.userHasModerationPermission { + let banAction = MessageActionBuilder.banUser(conversationViewItem: conversationViewItem, delegate: delegate) + actions.append(banAction) } let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: conversationViewItem, delegate: delegate) @@ -152,10 +151,9 @@ class ConversationViewItemActions: NSObject { actions.append(deleteAction) } - if isGroup && conversationViewItem.interaction.thread.name() == "Loki Public Chat" - || conversationViewItem.interaction.thread.name() == "Session Public Chat" { - let reportAction = MessageActionBuilder.report(conversationViewItem, delegate: delegate) - actions.append(reportAction) + if isGroup && conversationViewItem.interaction is TSIncomingMessage && conversationViewItem.userHasModerationPermission { + let banAction = MessageActionBuilder.banUser(conversationViewItem: conversationViewItem, delegate: delegate) + actions.append(banAction) } let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: conversationViewItem, delegate: delegate) @@ -185,10 +183,9 @@ class ConversationViewItemActions: NSObject { actions.append(deleteAction) } - if isGroup && conversationViewItem.interaction.thread.name() == "Loki Public Chat" - || conversationViewItem.interaction.thread.name() == "Session Public Chat" { - let reportAction = MessageActionBuilder.report(conversationViewItem, delegate: delegate) - actions.append(reportAction) + if isGroup && conversationViewItem.interaction is TSIncomingMessage && conversationViewItem.userHasModerationPermission { + let banAction = MessageActionBuilder.banUser(conversationViewItem: conversationViewItem, delegate: delegate) + actions.append(banAction) } let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: conversationViewItem, delegate: delegate) diff --git a/SessionMessagingKit/Open Groups/OpenGroupAPI.swift b/SessionMessagingKit/Open Groups/OpenGroupAPI.swift index e6ca2bbd1..d6d39e854 100644 --- a/SessionMessagingKit/Open Groups/OpenGroupAPI.swift +++ b/SessionMessagingKit/Open Groups/OpenGroupAPI.swift @@ -205,7 +205,7 @@ public final class OpenGroupAPI : DotNetAPI { } let lastDeletionServerID = storage.getLastDeletionServerID(for: channel, on: server) if serverID > (lastDeletionServerID ?? 0) { - storage.write { transaction in + storage.writeSync { transaction in storage.setLastDeletionServerID(for: channel, on: server, to: serverID, using: transaction) } } @@ -238,6 +238,33 @@ public final class OpenGroupAPI : DotNetAPI { }.handlingInvalidAuthTokenIfNeeded(for: server) } } + + // MARK: Banning + @objc(banPublicKey:fromServer:) + public static func objc_ban(_ publicKey: String, from server: String) -> AnyPromise { + return AnyPromise.from(ban(publicKey, from: server)) + } + + public static func ban(_ publicKey: String, from server: String) -> Promise { + SNLog("Banning user with ID: \(publicKey) from server: \(server).") + return attempt(maxRetryCount: maxRetryCount, recoveringOn: DispatchQueue.global(qos: .default)) { + getOpenGroupServerPublicKey(for: server).then(on: DispatchQueue.global(qos: .default)) { serverPublicKey in + getAuthToken(for: server).then(on: DispatchQueue.global(qos: .default)) { token -> Promise in + let url = URL(string: "\(server)/loki/v1/moderation/blacklist/@\(publicKey)")! + let request = TSRequest(url: url, method: "POST", parameters: [:]) + request.allHTTPHeaderFields = [ "Content-Type" : "application/json", "Authorization" : "Bearer \(token)" ] + let promise = OnionRequestAPI.sendOnionRequest(request, to: server, using: serverPublicKey, isJSONRequired: false) + promise.done(on: DispatchQueue.global(qos: .default)) { _ -> Void in + SNLog("Banned user with ID: \(publicKey) from server: \(server).") + } + promise.catch(on: DispatchQueue.main) { error in + print(error) + } + return promise.map { _ in } + } + }.handlingInvalidAuthTokenIfNeeded(for: server) + } + } // MARK: Display Name & Profile Picture public static func getDisplayNames(for channel: UInt64, on server: String) -> Promise { diff --git a/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPoller.swift b/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPoller.swift index 4f72e7d51..bd8f7d243 100644 --- a/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPoller.swift +++ b/SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPoller.swift @@ -19,7 +19,7 @@ public final class OpenGroupPoller : NSObject { // MARK: Settings private let pollForNewMessagesInterval: TimeInterval = 4 - private let pollForDeletedMessagesInterval: TimeInterval = 60 + private let pollForDeletedMessagesInterval: TimeInterval = 30 private let pollForModeratorsInterval: TimeInterval = 10 * 60 // MARK: Lifecycle