Add emoji reactions feature

This commit is contained in:
Benoit Martins 2023-09-05 16:59:40 +02:00 committed by QuentinArguillere
parent 91614236e8
commit 30a350b0dc
5 changed files with 264 additions and 17 deletions

View file

@ -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)

View file

@ -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) {

View file

@ -1,10 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21679"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
@ -17,37 +18,85 @@
<rect key="frame" x="0.0" y="0.0" width="350" height="72"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="p2H-ZL-cwJ">
<rect key="frame" x="310" y="21" width="30" height="30"/>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="p2H-ZL-cwJ">
<rect key="frame" x="300" y="21" width="30" height="30"/>
<constraints>
<constraint firstAttribute="width" constant="30" id="T7j-qm-kur"/>
<constraint firstAttribute="height" constant="30" id="Xxh-BC-eg9"/>
<constraint firstAttribute="width" constant="24" id="T7j-qm-kur"/>
<constraint firstAttribute="height" constant="24" id="Xxh-BC-eg9"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gXQ-5Z-Z7W">
<rect key="frame" x="10" y="4" width="290" height="64"/>
<rect key="frame" x="14" y="6" width="288" height="60"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
<color key="textColor" systemColor="systemPinkColor"/>
<color key="highlightedColor" systemColor="systemPinkColor"/>
</label>
<view contentMode="center" id="TrN-T1-Qnp">
<rect key="frame" x="0.0" y="0.0" width="350" height="72"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" id="zqz-hc-78g" userLabel="emoji1">
<rect key="frame" x="5" y="18" width="47" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" title="❤️"/>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" id="MZ1-PD-4wS" userLabel="emoji2">
<rect key="frame" x="77" y="18" width="47" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" title="👍"/>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" id="4hz-YC-t5L" userLabel="emoji3">
<rect key="frame" x="151" y="18" width="47" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" title="😂"/>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" id="rVf-gM-dFu" userLabel="emoji4">
<rect key="frame" x="225" y="18" width="47" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" title="😮"/>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" id="Jif-0X-Jwc" userLabel="emoji5">
<rect key="frame" x="298" y="18" width="47" height="35"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" title="😢"/>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="cIf-Td-6PX"/>
</view>
</subviews>
<constraints>
<constraint firstItem="gXQ-5Z-Z7W" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="10" id="2VM-KK-mno"/>
<constraint firstItem="gXQ-5Z-Z7W" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="4" id="3W8-Sp-Op6"/>
<constraint firstAttribute="bottom" secondItem="gXQ-5Z-Z7W" secondAttribute="bottom" constant="4" id="58p-Vq-TIF"/>
<constraint firstItem="gXQ-5Z-Z7W" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="4" id="BPM-Vb-6IV"/>
<constraint firstItem="gXQ-5Z-Z7W" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="14" id="2VM-KK-mno"/>
<constraint firstItem="gXQ-5Z-Z7W" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="6" id="3W8-Sp-Op6"/>
<constraint firstAttribute="bottom" secondItem="gXQ-5Z-Z7W" secondAttribute="bottom" constant="6" id="58p-Vq-TIF"/>
<constraint firstItem="gXQ-5Z-Z7W" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="6" id="BPM-Vb-6IV"/>
<constraint firstItem="p2H-ZL-cwJ" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="C1J-DC-iVA"/>
<constraint firstAttribute="bottom" secondItem="gXQ-5Z-Z7W" secondAttribute="bottom" constant="4" id="RGu-L9-5La"/>
<constraint firstAttribute="trailing" secondItem="p2H-ZL-cwJ" secondAttribute="trailing" constant="10" id="T9l-t0-h70"/>
<constraint firstAttribute="bottom" secondItem="gXQ-5Z-Z7W" secondAttribute="bottom" constant="6" id="RGu-L9-5La"/>
<constraint firstAttribute="trailing" secondItem="p2H-ZL-cwJ" secondAttribute="trailing" constant="14" id="T9l-t0-h70"/>
<constraint firstItem="p2H-ZL-cwJ" firstAttribute="leading" secondItem="gXQ-5Z-Z7W" secondAttribute="trailing" constant="10" id="pge-0N-hit"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
<connections>
<outlet property="myEmojiButton1" destination="zqz-hc-78g" id="cFg-0n-hUt"/>
<outlet property="myEmojiButton2" destination="MZ1-PD-4wS" id="Adu-iE-jNq"/>
<outlet property="myEmojiButton3" destination="4hz-YC-t5L" id="cKM-9N-Der"/>
<outlet property="myEmojiButton4" destination="rVf-gM-dFu" id="jlI-tK-CsX"/>
<outlet property="myEmojiButton5" destination="Jif-0X-Jwc" id="WrW-Xb-jeB"/>
<outlet property="myEmojisView" destination="TrN-T1-Qnp" id="DJR-3c-mQ0"/>
<outlet property="myImageView" destination="p2H-ZL-cwJ" id="hwZ-fL-pEC"/>
<outlet property="optionLabel" destination="gXQ-5Z-Z7W" id="rhQ-sW-3Mf"/>
</connections>
<point key="canvasLocation" x="154.19847328244273" y="-22.535211267605636"/>
</tableViewCell>
</objects>
<resources>
<systemColor name="systemPinkColor">
<color red="1" green="0.17647058823529413" blue="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>

View file

@ -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)

View file

@ -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:"")