diff --git a/Session/Conversations/Context Menu/ContextMenuVC+Action.swift b/Session/Conversations/Context Menu/ContextMenuVC+Action.swift index 90fce90e0..d3765b7c3 100644 --- a/Session/Conversations/Context Menu/ContextMenuVC+Action.swift +++ b/Session/Conversations/Context Menu/ContextMenuVC+Action.swift @@ -105,6 +105,7 @@ extension ContextMenuVC { for cellViewModel: MessageViewModel, recentEmojis: [EmojiWithSkinTones], currentUserIsOpenGroupModerator: Bool, + currentThreadIsMessageRequest: Bool, delegate: ContextMenuActionDelegate? ) -> [Action]? { // No context items for info messages @@ -156,6 +157,13 @@ extension ContextMenuVC { currentUserIsOpenGroupModerator ) + let shouldShowEmojiActions: Bool = { + if cellViewModel.threadVariant == .openGroup { + return OpenGroupManager.isOpenGroupSupport(.reactions, on: cellViewModel.threadOpenGroupServer) + } + return currentThreadIsMessageRequest + }() + let generatedActions: [Action] = [ (canReply ? Action.reply(cellViewModel, delegate) : nil), (canCopy ? Action.copy(cellViewModel, delegate) : nil), @@ -165,7 +173,7 @@ extension ContextMenuVC { (canBan ? Action.ban(cellViewModel, delegate) : nil), (canBan ? Action.banAndDeleteAllMessages(cellViewModel, delegate) : nil), ] - .appending(contentsOf: recentEmojis.map { Action.react(cellViewModel, $0, delegate) }) + .appending(contentsOf: (shouldShowEmojiActions ? recentEmojis : []).map { Action.react(cellViewModel, $0, delegate) }) .appending(Action.emojiPlusButton(cellViewModel, delegate)) .compactMap { $0 } diff --git a/Session/Conversations/ConversationVC+Interaction.swift b/Session/Conversations/ConversationVC+Interaction.swift index 5f3e9b1ba..ff2ecc991 100644 --- a/Session/Conversations/ConversationVC+Interaction.swift +++ b/Session/Conversations/ConversationVC+Interaction.swift @@ -671,14 +671,13 @@ extension ConversationVC: contextMenuWindow == nil, let actions: [ContextMenuVC.Action] = ContextMenuVC.actions( for: cellViewModel, - recentEmojis: ((self.viewModel.threadData.threadIsMessageRequest == true) ? [] : - (self.viewModel.threadData.recentReactionEmoji ?? []) - ).compactMap { EmojiWithSkinTones(rawValue: $0) }, + recentEmojis: (self.viewModel.threadData.recentReactionEmoji ?? []).compactMap { EmojiWithSkinTones(rawValue: $0) }, currentUserIsOpenGroupModerator: OpenGroupManager.isUserModeratorOrAdmin( self.viewModel.threadData.currentUserPublicKey, for: self.viewModel.threadData.openGroupRoomToken, on: self.viewModel.threadData.openGroupServer ), + currentThreadIsMessageRequest: (self.viewModel.threadData.threadIsMessageRequest == true), delegate: self ) else { return } @@ -1125,7 +1124,9 @@ extension ConversationVC: Emoji.addRecent(db, emoji: emoji) } - if let openGroup: OpenGroup = try? OpenGroup.fetchOne(db, id: cellViewModel.threadId) { + if let openGroup: OpenGroup = try? OpenGroup.fetchOne(db, id: cellViewModel.threadId), + OpenGroupManager.isOpenGroupSupport(.reactions, on: openGroup.server) + { // Send reaction to open groups guard let openGroupServerMessageId: Int64 = try? Interaction diff --git a/SessionMessagingKit/Database/Models/Capability.swift b/SessionMessagingKit/Database/Models/Capability.swift index 9feda3eb1..304bc8813 100644 --- a/SessionMessagingKit/Database/Models/Capability.swift +++ b/SessionMessagingKit/Database/Models/Capability.swift @@ -16,11 +16,12 @@ public struct Capability: Codable, FetchableRecord, PersistableRecord, TableReco public enum Variant: Equatable, Hashable, CaseIterable, Codable, DatabaseValueConvertible { public static var allCases: [Variant] { - [.sogs, .blind] + [.sogs, .blind, .reactions] } case sogs case blind + case reactions /// Fallback case if the capability isn't supported by this version of the app case unsupported(String) diff --git a/SessionMessagingKit/Open Groups/OpenGroupManager.swift b/SessionMessagingKit/Open Groups/OpenGroupManager.swift index d291af196..7b6521d27 100644 --- a/SessionMessagingKit/Open Groups/OpenGroupManager.swift +++ b/SessionMessagingKit/Open Groups/OpenGroupManager.swift @@ -740,6 +740,29 @@ public final class OpenGroupManager: NSObject { // MARK: - Convenience + /// This method specifies if the given capability is supported on a specified Open Group + public static func isOpenGroupSupport( + _ capability: Capability.Variant, + on server: String?, + using dependencies: OGMDependencies = OGMDependencies() + ) -> Bool { + guard let server: String = server else { return false } + + return dependencies.storage + .read { db in + let capabilities: [Capability.Variant] = (try? Capability + .select(.variant) + .filter(Capability.Columns.openGroupServer == server) + .filter(Capability.Columns.isMissing == false) + .asRequest(of: Capability.Variant.self) + .fetchAll(db)) + .defaulting(to: []) + + return capabilities.contains(capability) + } + .defaulting(to: false) + } + /// This method specifies if the given publicKey is a moderator or an admin within a specified Open Group public static func isUserModeratorOrAdmin( _ publicKey: String,