From 30a350b0dc6363115704e27d03bc94bf9f6bca37 Mon Sep 17 00:00:00 2001 From: Benoit Martins Date: Tue, 5 Sep 2023 16:59:40 +0200 Subject: [PATCH] Add emoji reactions feature --- .../ChatConversationTableViewSwift.swift | 69 +++++++++- Classes/Swift/Chat/Views/DropDownCell.swift | 7 + Classes/Swift/Chat/Views/DropDownCell.xib | 79 ++++++++--- .../Chat/Views/MultilineMessageCell.swift | 125 ++++++++++++++++++ Classes/Swift/Voip/Theme/VoipTexts.swift | 1 + 5 files changed, 264 insertions(+), 17 deletions(-) diff --git a/Classes/Swift/Chat/Views/ChatConversationTableViewSwift.swift b/Classes/Swift/Chat/Views/ChatConversationTableViewSwift.swift index 6011178be..423c7bbdb 100644 --- a/Classes/Swift/Chat/Views/ChatConversationTableViewSwift.swift +++ b/Classes/Swift/Chat/Views/ChatConversationTableViewSwift.swift @@ -314,10 +314,10 @@ class ChatConversationTableViewSwift: UIViewController, UICollectionViewDataSour func tapChooseMenuItemMessage(contentViewBubble: UIView, event: EventLog, preContentSize: CGFloat) { menu!.anchorView = view - menu!.width = 200 + menu!.width = 240 let coordinateMin = contentViewBubble.convert(contentViewBubble.frame.origin, to: view) - let coordinateMax = contentViewBubble.convert(CGPoint(x: contentViewBubble.frame.maxX, y: contentViewBubble.frame.maxY), to: view) + let coordinateMax = contentViewBubble.convert(CGPoint(x: contentViewBubble.frame.maxX - 40, y: contentViewBubble.frame.maxY), to: view) if (coordinateMax.y + CGFloat(menu!.dataSource.count * 44) - preContentSize < view.frame.maxY) { menu!.bottomOffset = CGPoint(x: event.chatMessage!.isOutgoing ? coordinateMax.x - 200 : coordinateMin.x, y: coordinateMax.y - preContentSize) @@ -334,6 +334,8 @@ class ChatConversationTableViewSwift: UIViewController, UICollectionViewDataSour menu!.selectionAction = { [weak self] (index: Int, item: String) in guard let _ = self else { return } switch item { + case VoipTexts.bubble_chat_dropDown_emojis: + self!.copyMessage(message: event.chatMessage!) case VoipTexts.bubble_chat_dropDown_resend: self!.resendMessage(message: event.chatMessage!) case VoipTexts.bubble_chat_dropDown_copy_text: @@ -376,6 +378,66 @@ class ChatConversationTableViewSwift: UIViewController, UICollectionViewDataSour } if(index < images.count){ switch menu.dataSource[index] { + case VoipTexts.bubble_chat_dropDown_emojis: + cell.myImageView.image = UIImage(named: images[7]) + cell.myEmojisView.isHidden = false + cell.myImageView.isHidden = true + cell.optionLabel.isHidden = true + cell.myEmojiButton1.onClick { + do { + let messageReaction = try message.createReaction(utf8Reaction: "❤️") + messageReaction.send() + self.menu!.clearSelection() + self.menu?.removeFromSuperview() + self.collectionView.reloadData() + } catch { + Log.e(error.localizedDescription) + } + } + cell.myEmojiButton2.onClick { + do { + let messageReaction = try message.createReaction(utf8Reaction: "👍") + messageReaction.send() + self.menu!.clearSelection() + self.menu?.removeFromSuperview() + self.collectionView.reloadData() + } catch { + Log.e(error.localizedDescription) + } + } + cell.myEmojiButton3.onClick { + do { + let messageReaction = try message.createReaction(utf8Reaction: "😂") + messageReaction.send() + self.menu!.clearSelection() + self.menu?.removeFromSuperview() + self.collectionView.reloadData() + } catch { + Log.e(error.localizedDescription) + } + } + cell.myEmojiButton4.onClick { + do { + let messageReaction = try message.createReaction(utf8Reaction: "😮") + messageReaction.send() + self.menu!.clearSelection() + self.menu?.removeFromSuperview() + self.collectionView.reloadData() + } catch { + Log.e(error.localizedDescription) + } + } + cell.myEmojiButton5.onClick { + do { + let messageReaction = try message.createReaction(utf8Reaction: "😢") + messageReaction.send() + self.menu!.clearSelection() + self.menu?.removeFromSuperview() + self.collectionView.reloadData() + } catch { + Log.e(error.localizedDescription) + } + } case VoipTexts.bubble_chat_dropDown_resend: if #available(iOS 13.0, *) { cell.myImageView.image = UIImage(named: images[0])!.withTintColor(.darkGray) @@ -399,11 +461,14 @@ class ChatConversationTableViewSwift: UIViewController, UICollectionViewDataSour } } } + return menu }() menu!.dataSource.removeAll() let state = message.state + + menu!.dataSource.append(VoipTexts.bubble_chat_dropDown_emojis) if (state.rawValue == LinphoneChatMessageStateNotDelivered.rawValue || state.rawValue == LinphoneChatMessageStateFileTransferError.rawValue) { menu!.dataSource.append(VoipTexts.bubble_chat_dropDown_resend) diff --git a/Classes/Swift/Chat/Views/DropDownCell.swift b/Classes/Swift/Chat/Views/DropDownCell.swift index 883b38d38..535313f78 100644 --- a/Classes/Swift/Chat/Views/DropDownCell.swift +++ b/Classes/Swift/Chat/Views/DropDownCell.swift @@ -23,10 +23,17 @@ import DropDown class MyCell: DropDownCell { @IBOutlet var myImageView: UIImageView! + @IBOutlet var myEmojisView: UIView! + @IBOutlet var myEmojiButton1: UIButton! + @IBOutlet var myEmojiButton2: UIButton! + @IBOutlet var myEmojiButton3: UIButton! + @IBOutlet var myEmojiButton4: UIButton! + @IBOutlet var myEmojiButton5: UIButton! override func awakeFromNib() { super.awakeFromNib() myImageView.contentMode = .scaleAspectFit + myEmojisView.isHidden = true } override func setSelected(_ selected: Bool, animated: Bool) { diff --git a/Classes/Swift/Chat/Views/DropDownCell.xib b/Classes/Swift/Chat/Views/DropDownCell.xib index 3fb09edee..219172ebc 100644 --- a/Classes/Swift/Chat/Views/DropDownCell.xib +++ b/Classes/Swift/Chat/Views/DropDownCell.xib @@ -1,10 +1,11 @@ - + - + + @@ -17,37 +18,85 @@ - - + + - - + + + + + + + + + + + + + + - - - - + + + + - - + + + + + + + + + + + + + diff --git a/Classes/Swift/Chat/Views/MultilineMessageCell.swift b/Classes/Swift/Chat/Views/MultilineMessageCell.swift index 12ed08d48..9e83333db 100644 --- a/Classes/Swift/Chat/Views/MultilineMessageCell.swift +++ b/Classes/Swift/Chat/Views/MultilineMessageCell.swift @@ -32,6 +32,7 @@ class MultilineMessageCell: SwipeCollectionViewCell, UICollectionViewDataSource, var contentMediaViewBubble: UIView = UIView(frame: .zero) var contentBubble: UIView = UIView(frame: .zero) var bubble: UIView = UIView(frame: .zero) + var bubbleReaction: UIView = UIView(frame: .zero) var imageUser = UIImageView() var contactDateLabel = StyledLabel(VoipTheme.chat_conversation_forward_label) var chatRead = UIImageView(image: UIImage(named: "chat_delivered.png")) @@ -165,6 +166,14 @@ class MultilineMessageCell: SwipeCollectionViewCell, UICollectionViewDataSource, var messageWithRecording = false + var stackViewReactions = UIStackView() + var stackViewReactionsItem1 = UILabel() + var stackViewReactionsItem2 = UILabel() + var stackViewReactionsItem3 = UILabel() + var stackViewReactionsItem4 = UILabel() + var stackViewReactionsItem5 = UILabel() + var stackViewReactionsCounter = UILabel() + override init(frame: CGRect) { super.init(frame: frame) initCell() @@ -240,6 +249,61 @@ class MultilineMessageCell: SwipeCollectionViewCell, UICollectionViewDataSource, bubble.trailingAnchor.constraint(equalTo: contentBubble.trailingAnchor).isActive = true bubble.layer.cornerRadius = 10.0 + + + + + contentBubble.addSubview(bubbleReaction) + bubbleReaction.translatesAutoresizingMaskIntoConstraints = false + bubbleReaction.topAnchor.constraint(equalTo: bubble.bottomAnchor, constant: -10).isActive = true + bubbleReaction.layer.cornerRadius = 8.0 + bubbleReaction.layer.borderWidth = 0.5 + bubbleReaction.layer.borderColor = VoipTheme.backgroundWhiteBlack.get().cgColor + bubbleReaction.isHidden = true + + bubbleReaction.addSubview(stackViewReactions) + stackViewReactions.axis = .horizontal + stackViewReactions.distribution = .fill + stackViewReactions.alignment = .center + stackViewReactions.height(16).done() + + stackViewReactions.topAnchor.constraint(equalTo: bubbleReaction.topAnchor, constant: 4).isActive = true + stackViewReactions.bottomAnchor.constraint(equalTo: bubbleReaction.bottomAnchor, constant: -4).isActive = true + stackViewReactions.leadingAnchor.constraint(equalTo: bubbleReaction.leadingAnchor, constant: 4).isActive = true + stackViewReactions.trailingAnchor.constraint(equalTo: bubbleReaction.trailingAnchor, constant: -4).isActive = true + + stackViewReactionsItem1.text = "❤️" + stackViewReactionsItem1.font = UIFont.systemFont(ofSize: 12.0) + stackViewReactionsItem1.isHidden = true + stackViewReactionsItem2.text = "👍" + stackViewReactionsItem2.font = UIFont.systemFont(ofSize: 12.0) + stackViewReactionsItem2.isHidden = true + stackViewReactionsItem3.text = "😂" + stackViewReactionsItem3.font = UIFont.systemFont(ofSize: 12.0) + stackViewReactionsItem3.isHidden = true + stackViewReactionsItem4.text = "😮" + stackViewReactionsItem4.font = UIFont.systemFont(ofSize: 12.0) + stackViewReactionsItem4.isHidden = true + stackViewReactionsItem5.text = "😢" + stackViewReactionsItem5.font = UIFont.systemFont(ofSize: 12.0) + stackViewReactionsItem5.isHidden = true + stackViewReactionsCounter.text = "0" + stackViewReactionsCounter.font = UIFont.systemFont(ofSize: 12.0) + stackViewReactionsCounter.textColor = .black + stackViewReactionsCounter.isHidden = true + + stackViewReactions.addArrangedSubview(stackViewReactionsItem1) + stackViewReactions.addArrangedSubview(stackViewReactionsItem2) + stackViewReactions.addArrangedSubview(stackViewReactionsItem3) + stackViewReactions.addArrangedSubview(stackViewReactionsItem4) + stackViewReactions.addArrangedSubview(stackViewReactionsItem5) + stackViewReactions.addArrangedSubview(stackViewReactionsCounter) + + + + + + contentBubble.addSubview(chatRead) chatRead.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -2).isActive = true chatRead.trailingAnchor.constraint(equalTo: deleteItemCheckBox.leadingAnchor, constant: -8).isActive = true @@ -1380,6 +1444,67 @@ class MultilineMessageCell: SwipeCollectionViewCell, UICollectionViewDataSource, imageVideoViewBubble.isHidden = true } } + + if event.chatMessage!.reactions.count > 0 { + bubbleReaction.isHidden = false + bubbleReaction.backgroundColor = bubble.backgroundColor + if event.chatMessage?.isOutgoing == true { + bubbleReaction.trailingAnchor.constraint(equalTo: bubble.trailingAnchor, constant: -6).isActive = true + } else { + + bubbleReaction.leadingAnchor.constraint(equalTo: bubble.leadingAnchor, constant: 6).isActive = true + } + + bubble.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -14).isActive = true + + contentViewBubble.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -14).isActive = true + + chatRead.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -16).isActive = true + + var reactionCount = 0 + event.chatMessage!.reactions.forEach { chatMessageReaction in + reactionCount += 1 + switch chatMessageReaction.body { + case "❤️": + if stackViewReactionsItem1.isHidden == false { + stackViewReactionsCounter.text = String(reactionCount) + stackViewReactionsCounter.isHidden = false + } else { + stackViewReactionsItem1.isHidden = false + } + case "👍": + if stackViewReactionsItem2.isHidden == false { + stackViewReactionsCounter.text = String(reactionCount) + stackViewReactionsCounter.isHidden = false + } else { + stackViewReactionsItem2.isHidden = false + } + case "😂": + if stackViewReactionsItem3.isHidden == false { + stackViewReactionsCounter.text = String(reactionCount) + stackViewReactionsCounter.isHidden = false + } else { + stackViewReactionsItem3.isHidden = false + } + case "😮": + if stackViewReactionsItem4.isHidden == false { + stackViewReactionsCounter.text = String(reactionCount) + stackViewReactionsCounter.isHidden = false + } else { + stackViewReactionsItem4.isHidden = false + } + case "😢": + if stackViewReactionsItem5.isHidden == false { + stackViewReactionsCounter.text = String(reactionCount) + stackViewReactionsCounter.isHidden = false + } else { + stackViewReactionsItem5.isHidden = false + } + default: + stackViewReactionsItem1.isHidden = false + } + } + } }else{ contentBubble.isHidden = true NSLayoutConstraint.deactivate(constraintBubble) diff --git a/Classes/Swift/Voip/Theme/VoipTexts.swift b/Classes/Swift/Voip/Theme/VoipTexts.swift index fb11b99e0..99446b6f9 100644 --- a/Classes/Swift/Voip/Theme/VoipTexts.swift +++ b/Classes/Swift/Voip/Theme/VoipTexts.swift @@ -190,6 +190,7 @@ import UIKit @objc static let bubble_chat_reply = NSLocalizedString("Answer",comment:"") @objc static let bubble_chat_reply_message_does_not_exist = NSLocalizedString("Original message does not exist anymore.",comment:"") + @objc static let bubble_chat_dropDown_emojis = NSLocalizedString("Emojis",comment:"") @objc static let bubble_chat_dropDown_resend = NSLocalizedString("Resend",comment:"") @objc static let bubble_chat_dropDown_copy_text = NSLocalizedString("Copy text",comment:"") @objc static let bubble_chat_dropDown_forward = NSLocalizedString("Forward",comment:"")