|
@@ -6,39 +6,38 @@
|
|
// Copyright © 2017 Jonas Reinsch. All rights reserved.
|
|
// Copyright © 2017 Jonas Reinsch. All rights reserved.
|
|
//
|
|
//
|
|
|
|
|
|
-import UIKit
|
|
|
|
-import MessageKit
|
|
|
|
import MapKit
|
|
import MapKit
|
|
import MessageInputBar
|
|
import MessageInputBar
|
|
|
|
+import MessageKit
|
|
|
|
+import UIKit
|
|
|
|
|
|
class ChatViewController: MessagesViewController {
|
|
class ChatViewController: MessagesViewController {
|
|
let outgoingAvatarOverlap: CGFloat = 17.5
|
|
let outgoingAvatarOverlap: CGFloat = 17.5
|
|
|
|
|
|
let chatId: Int
|
|
let chatId: Int
|
|
let refreshControl = UIRefreshControl()
|
|
let refreshControl = UIRefreshControl()
|
|
- var messageIds:[Int] = []
|
|
|
|
|
|
+ var messageIds: [Int] = []
|
|
var messageList: [Message] = []
|
|
var messageList: [Message] = []
|
|
|
|
|
|
var msgChangedObserver: Any?
|
|
var msgChangedObserver: Any?
|
|
var incomingMsgObserver: Any?
|
|
var incomingMsgObserver: Any?
|
|
-
|
|
|
|
|
|
+
|
|
var disableWriting = false
|
|
var disableWriting = false
|
|
-
|
|
|
|
|
|
+
|
|
init(chatId: Int) {
|
|
init(chatId: Int) {
|
|
self.chatId = chatId
|
|
self.chatId = chatId
|
|
super.init(nibName: nil, bundle: nil)
|
|
super.init(nibName: nil, bundle: nil)
|
|
// self.getMessageIds()
|
|
// self.getMessageIds()
|
|
-
|
|
|
|
|
|
+
|
|
/*
|
|
/*
|
|
- let chat = MRChat(id: chatId)
|
|
|
|
|
|
+ let chat = MRChat(id: chatId)
|
|
let subtitle = dc_chat_get_subtitle(chat.chatPointer)!
|
|
let subtitle = dc_chat_get_subtitle(chat.chatPointer)!
|
|
|
|
|
|
- let s = String(validatingUTF8: subtitle)
|
|
|
|
- logger.info( s)
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
|
|
+ let s = String(validatingUTF8: subtitle)
|
|
|
|
+ logger.info( s)
|
|
|
|
+ */
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
@objc
|
|
@objc
|
|
func loadMoreMessages() {
|
|
func loadMoreMessages() {
|
|
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 1) {
|
|
DispatchQueue.global(qos: .userInitiated).asyncAfter(deadline: .now() + 1) {
|
|
@@ -50,7 +49,7 @@ class ChatViewController: MessagesViewController {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func loadFirstMessages() {
|
|
func loadFirstMessages() {
|
|
DispatchQueue.global(qos: .userInitiated).async {
|
|
DispatchQueue.global(qos: .userInitiated).async {
|
|
self.getMessageIds()
|
|
self.getMessageIds()
|
|
@@ -62,14 +61,14 @@ class ChatViewController: MessagesViewController {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
private func idToMessage(messageId: Int) -> Message {
|
|
private func idToMessage(messageId: Int) -> Message {
|
|
let message = MRMessage(id: messageId)
|
|
let message = MRMessage(id: messageId)
|
|
let contact = MRContact(id: message.fromContactId)
|
|
let contact = MRContact(id: message.fromContactId)
|
|
let messageId = "\(messageId)"
|
|
let messageId = "\(messageId)"
|
|
let date = Date(timeIntervalSince1970: Double(message.timestamp))
|
|
let date = Date(timeIntervalSince1970: Double(message.timestamp))
|
|
let sender = Sender(id: "\(contact.id)", displayName: contact.name)
|
|
let sender = Sender(id: "\(contact.id)", displayName: contact.name)
|
|
-
|
|
|
|
|
|
+
|
|
if message.isInfo {
|
|
if message.isInfo {
|
|
let text = NSAttributedString(string: message.text ?? "", attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 12), NSAttributedString.Key.foregroundColor: UIColor.darkGray])
|
|
let text = NSAttributedString(string: message.text ?? "", attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 12), NSAttributedString.Key.foregroundColor: UIColor.darkGray])
|
|
return Message(attributedText: text, sender: sender, messageId: messageId, date: date)
|
|
return Message(attributedText: text, sender: sender, messageId: messageId, date: date)
|
|
@@ -79,16 +78,16 @@ class ChatViewController: MessagesViewController {
|
|
return Message(text: message.text ?? "- empty -", sender: sender, messageId: messageId, date: date)
|
|
return Message(text: message.text ?? "- empty -", sender: sender, messageId: messageId, date: date)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
private func messageToMRMessage(message: Message) -> MRMessage? {
|
|
private func messageToMRMessage(message: Message) -> MRMessage? {
|
|
if let id = Int(message.messageId) {
|
|
if let id = Int(message.messageId) {
|
|
return MRMessage(id: id)
|
|
return MRMessage(id: id)
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
-
|
|
|
|
- var textDraft:String? {
|
|
|
|
|
|
+
|
|
|
|
+ var textDraft: String? {
|
|
// FIXME: need to free pointer
|
|
// FIXME: need to free pointer
|
|
if let draft = dc_get_draft(mailboxPointer, UInt32(chatId)) {
|
|
if let draft = dc_get_draft(mailboxPointer, UInt32(chatId)) {
|
|
if let text = dc_msg_get_text(draft) {
|
|
if let text = dc_msg_get_text(draft) {
|
|
@@ -99,62 +98,62 @@ class ChatViewController: MessagesViewController {
|
|
}
|
|
}
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func getMessageIds() {
|
|
func getMessageIds() {
|
|
- let c_messageIds = dc_get_chat_msgs(mailboxPointer, UInt32(self.chatId), 0, 0)
|
|
|
|
|
|
+ let c_messageIds = dc_get_chat_msgs(mailboxPointer, UInt32(chatId), 0, 0)
|
|
messageIds = Utils.copyAndFreeArray(inputArray: c_messageIds)
|
|
messageIds = Utils.copyAndFreeArray(inputArray: c_messageIds)
|
|
-
|
|
|
|
|
|
+
|
|
let ids: UnsafePointer = UnsafePointer(messageIds.map { id in
|
|
let ids: UnsafePointer = UnsafePointer(messageIds.map { id in
|
|
- return UInt32(id)
|
|
|
|
|
|
+ UInt32(id)
|
|
})
|
|
})
|
|
|
|
|
|
dc_markseen_msgs(mailboxPointer, ids, Int32(messageIds.count))
|
|
dc_markseen_msgs(mailboxPointer, ids, Int32(messageIds.count))
|
|
}
|
|
}
|
|
-
|
|
|
|
- required init?(coder aDecoder: NSCoder) {
|
|
|
|
|
|
+
|
|
|
|
+ required init?(coder _: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
super.viewWillAppear(animated)
|
|
super.viewWillAppear(animated)
|
|
-
|
|
|
|
|
|
+
|
|
let nc = NotificationCenter.default
|
|
let nc = NotificationCenter.default
|
|
- msgChangedObserver = nc.addObserver(forName:dc_notificationChanged,
|
|
|
|
- object:nil, queue: OperationQueue.main) {
|
|
|
|
- notification in
|
|
|
|
- if let ui = notification.userInfo {
|
|
|
|
- if self.chatId == ui["chat_id"] as! Int {
|
|
|
|
- self.updateMessage(ui["message_id"] as! Int)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- incomingMsgObserver = nc.addObserver(forName:dc_notificationIncoming,
|
|
|
|
- object:nil, queue: OperationQueue.main) {
|
|
|
|
- notification in
|
|
|
|
- if let ui = notification.userInfo {
|
|
|
|
- if self.chatId == ui["chat_id"] as! Int {
|
|
|
|
- let id = ui["message_id"] as! Int
|
|
|
|
- self.insertMessage(self.idToMessage(messageId: id))
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ msgChangedObserver = nc.addObserver(forName: dc_notificationChanged,
|
|
|
|
+ object: nil, queue: OperationQueue.main) {
|
|
|
|
+ notification in
|
|
|
|
+ if let ui = notification.userInfo {
|
|
|
|
+ if self.chatId == ui["chat_id"] as! Int {
|
|
|
|
+ self.updateMessage(ui["message_id"] as! Int)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ incomingMsgObserver = nc.addObserver(forName: dc_notificationIncoming,
|
|
|
|
+ object: nil, queue: OperationQueue.main) {
|
|
|
|
+ notification in
|
|
|
|
+ if let ui = notification.userInfo {
|
|
|
|
+ if self.chatId == ui["chat_id"] as! Int {
|
|
|
|
+ let id = ui["message_id"] as! Int
|
|
|
|
+ self.insertMessage(self.idToMessage(messageId: id))
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
func setTextDraft() {
|
|
func setTextDraft() {
|
|
if let text = self.messageInputBar.inputTextView.text {
|
|
if let text = self.messageInputBar.inputTextView.text {
|
|
let draft = dc_msg_new(mailboxPointer, DC_MSG_TEXT)
|
|
let draft = dc_msg_new(mailboxPointer, DC_MSG_TEXT)
|
|
dc_msg_set_text(draft, text.cString(using: .utf8))
|
|
dc_msg_set_text(draft, text.cString(using: .utf8))
|
|
dc_set_draft(mailboxPointer, UInt32(chatId), draft)
|
|
dc_set_draft(mailboxPointer, UInt32(chatId), draft)
|
|
-
|
|
|
|
|
|
+
|
|
// cleanup
|
|
// cleanup
|
|
dc_msg_unref(draft)
|
|
dc_msg_unref(draft)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
override func viewDidDisappear(_ animated: Bool) {
|
|
override func viewDidDisappear(_ animated: Bool) {
|
|
super.viewDidDisappear(animated)
|
|
super.viewDidDisappear(animated)
|
|
-
|
|
|
|
|
|
+
|
|
setTextDraft()
|
|
setTextDraft()
|
|
let nc = NotificationCenter.default
|
|
let nc = NotificationCenter.default
|
|
if let msgChangedObserver = self.msgChangedObserver {
|
|
if let msgChangedObserver = self.msgChangedObserver {
|
|
@@ -164,93 +163,91 @@ class ChatViewController: MessagesViewController {
|
|
nc.removeObserver(incomingMsgObserver)
|
|
nc.removeObserver(incomingMsgObserver)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
override var inputAccessoryView: UIView? {
|
|
override var inputAccessoryView: UIView? {
|
|
if disableWriting {
|
|
if disableWriting {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
return messageInputBar
|
|
return messageInputBar
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
override func viewDidLoad() {
|
|
override func viewDidLoad() {
|
|
super.viewDidLoad()
|
|
super.viewDidLoad()
|
|
|
|
|
|
- let chat = MRChat(id: self.chatId)
|
|
|
|
|
|
+ let chat = MRChat(id: chatId)
|
|
updateTitleView(title: chat.name, subtitle: nil)
|
|
updateTitleView(title: chat.name, subtitle: nil)
|
|
-
|
|
|
|
|
|
+
|
|
configureMessageCollectionView()
|
|
configureMessageCollectionView()
|
|
if !disableWriting {
|
|
if !disableWriting {
|
|
configureMessageInputBar()
|
|
configureMessageInputBar()
|
|
messageInputBar.inputTextView.text = textDraft
|
|
messageInputBar.inputTextView.text = textDraft
|
|
messageInputBar.inputTextView.becomeFirstResponder()
|
|
messageInputBar.inputTextView.becomeFirstResponder()
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
loadFirstMessages()
|
|
loadFirstMessages()
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func configureMessageCollectionView() {
|
|
func configureMessageCollectionView() {
|
|
messagesCollectionView.messagesDataSource = self
|
|
messagesCollectionView.messagesDataSource = self
|
|
messagesCollectionView.messageCellDelegate = self
|
|
messagesCollectionView.messageCellDelegate = self
|
|
-
|
|
|
|
|
|
+
|
|
scrollsToBottomOnKeyboardBeginsEditing = true // default false
|
|
scrollsToBottomOnKeyboardBeginsEditing = true // default false
|
|
maintainPositionOnKeyboardFrameChanged = true // default false
|
|
maintainPositionOnKeyboardFrameChanged = true // default false
|
|
-
|
|
|
|
|
|
+
|
|
messagesCollectionView.addSubview(refreshControl)
|
|
messagesCollectionView.addSubview(refreshControl)
|
|
refreshControl.addTarget(self, action: #selector(loadMoreMessages), for: .valueChanged)
|
|
refreshControl.addTarget(self, action: #selector(loadMoreMessages), for: .valueChanged)
|
|
-
|
|
|
|
-
|
|
|
|
|
|
+
|
|
let layout = messagesCollectionView.collectionViewLayout as? MessagesCollectionViewFlowLayout
|
|
let layout = messagesCollectionView.collectionViewLayout as? MessagesCollectionViewFlowLayout
|
|
layout?.sectionInset = UIEdgeInsets(top: 1, left: 8, bottom: 1, right: 8)
|
|
layout?.sectionInset = UIEdgeInsets(top: 1, left: 8, bottom: 1, right: 8)
|
|
-
|
|
|
|
|
|
+
|
|
// Hide the outgoing avatar and adjust the label alignment to line up with the messages
|
|
// Hide the outgoing avatar and adjust the label alignment to line up with the messages
|
|
layout?.setMessageOutgoingAvatarSize(.zero)
|
|
layout?.setMessageOutgoingAvatarSize(.zero)
|
|
layout?.setMessageOutgoingMessageTopLabelAlignment(LabelAlignment(textAlignment: .right, textInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 8)))
|
|
layout?.setMessageOutgoingMessageTopLabelAlignment(LabelAlignment(textAlignment: .right, textInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 8)))
|
|
layout?.setMessageOutgoingMessageBottomLabelAlignment(LabelAlignment(textAlignment: .right, textInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 8)))
|
|
layout?.setMessageOutgoingMessageBottomLabelAlignment(LabelAlignment(textAlignment: .right, textInsets: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 8)))
|
|
-
|
|
|
|
|
|
+
|
|
// Set outgoing avatar to overlap with the message bubble
|
|
// Set outgoing avatar to overlap with the message bubble
|
|
layout?.setMessageIncomingMessageTopLabelAlignment(LabelAlignment(textAlignment: .left, textInsets: UIEdgeInsets(top: 0, left: 18, bottom: outgoingAvatarOverlap, right: 0)))
|
|
layout?.setMessageIncomingMessageTopLabelAlignment(LabelAlignment(textAlignment: .left, textInsets: UIEdgeInsets(top: 0, left: 18, bottom: outgoingAvatarOverlap, right: 0)))
|
|
layout?.setMessageIncomingAvatarSize(CGSize(width: 30, height: 30))
|
|
layout?.setMessageIncomingAvatarSize(CGSize(width: 30, height: 30))
|
|
layout?.setMessageIncomingMessagePadding(UIEdgeInsets(top: -outgoingAvatarOverlap, left: -18, bottom: outgoingAvatarOverlap, right: 18))
|
|
layout?.setMessageIncomingMessagePadding(UIEdgeInsets(top: -outgoingAvatarOverlap, left: -18, bottom: outgoingAvatarOverlap, right: 18))
|
|
-
|
|
|
|
|
|
+
|
|
layout?.setMessageIncomingAccessoryViewSize(CGSize(width: 30, height: 30))
|
|
layout?.setMessageIncomingAccessoryViewSize(CGSize(width: 30, height: 30))
|
|
layout?.setMessageIncomingAccessoryViewPadding(HorizontalEdgeInsets(left: 8, right: 0))
|
|
layout?.setMessageIncomingAccessoryViewPadding(HorizontalEdgeInsets(left: 8, right: 0))
|
|
layout?.setMessageOutgoingAccessoryViewSize(CGSize(width: 30, height: 30))
|
|
layout?.setMessageOutgoingAccessoryViewSize(CGSize(width: 30, height: 30))
|
|
layout?.setMessageOutgoingAccessoryViewPadding(HorizontalEdgeInsets(left: 0, right: 8))
|
|
layout?.setMessageOutgoingAccessoryViewPadding(HorizontalEdgeInsets(left: 0, right: 8))
|
|
-
|
|
|
|
|
|
+
|
|
messagesCollectionView.messagesLayoutDelegate = self
|
|
messagesCollectionView.messagesLayoutDelegate = self
|
|
messagesCollectionView.messagesDisplayDelegate = self
|
|
messagesCollectionView.messagesDisplayDelegate = self
|
|
-
|
|
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func configureMessageInputBar() {
|
|
func configureMessageInputBar() {
|
|
messageInputBar.delegate = self
|
|
messageInputBar.delegate = self
|
|
messageInputBar.inputTextView.tintColor = Constants.primaryColor
|
|
messageInputBar.inputTextView.tintColor = Constants.primaryColor
|
|
messageInputBar.sendButton.tintColor = Constants.primaryColor
|
|
messageInputBar.sendButton.tintColor = Constants.primaryColor
|
|
-
|
|
|
|
|
|
+
|
|
messageInputBar.isTranslucent = true
|
|
messageInputBar.isTranslucent = true
|
|
messageInputBar.separatorLine.isHidden = true
|
|
messageInputBar.separatorLine.isHidden = true
|
|
messageInputBar.inputTextView.tintColor = Constants.primaryColor
|
|
messageInputBar.inputTextView.tintColor = Constants.primaryColor
|
|
-
|
|
|
|
|
|
+
|
|
messageInputBar.delegate = self
|
|
messageInputBar.delegate = self
|
|
scrollsToBottomOnKeyboardBeginsEditing = true
|
|
scrollsToBottomOnKeyboardBeginsEditing = true
|
|
-
|
|
|
|
- messageInputBar.inputTextView.backgroundColor = UIColor(red: 245/255, green: 245/255, blue: 245/255, alpha: 1)
|
|
|
|
|
|
+
|
|
|
|
+ messageInputBar.inputTextView.backgroundColor = UIColor(red: 245 / 255, green: 245 / 255, blue: 245 / 255, alpha: 1)
|
|
messageInputBar.inputTextView.placeholderTextColor = UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1)
|
|
messageInputBar.inputTextView.placeholderTextColor = UIColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1)
|
|
messageInputBar.inputTextView.textContainerInset = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 38)
|
|
messageInputBar.inputTextView.textContainerInset = UIEdgeInsets(top: 8, left: 16, bottom: 8, right: 38)
|
|
messageInputBar.inputTextView.placeholderLabelInsets = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 38)
|
|
messageInputBar.inputTextView.placeholderLabelInsets = UIEdgeInsets(top: 8, left: 20, bottom: 8, right: 38)
|
|
- messageInputBar.inputTextView.layer.borderColor = UIColor(red: 200/255, green: 200/255, blue: 200/255, alpha: 1).cgColor
|
|
|
|
|
|
+ messageInputBar.inputTextView.layer.borderColor = UIColor(red: 200 / 255, green: 200 / 255, blue: 200 / 255, alpha: 1).cgColor
|
|
messageInputBar.inputTextView.layer.borderWidth = 1.0
|
|
messageInputBar.inputTextView.layer.borderWidth = 1.0
|
|
messageInputBar.inputTextView.layer.cornerRadius = 16.0
|
|
messageInputBar.inputTextView.layer.cornerRadius = 16.0
|
|
messageInputBar.inputTextView.layer.masksToBounds = true
|
|
messageInputBar.inputTextView.layer.masksToBounds = true
|
|
messageInputBar.inputTextView.scrollIndicatorInsets = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)
|
|
messageInputBar.inputTextView.scrollIndicatorInsets = UIEdgeInsets(top: 8, left: 0, bottom: 8, right: 0)
|
|
configureInputBarItems()
|
|
configureInputBarItems()
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
private func configureInputBarItems() {
|
|
private func configureInputBarItems() {
|
|
messageInputBar.setLeftStackViewWidthConstant(to: 44, animated: false)
|
|
messageInputBar.setLeftStackViewWidthConstant(to: 44, animated: false)
|
|
messageInputBar.setRightStackViewWidthConstant(to: 36, animated: false)
|
|
messageInputBar.setRightStackViewWidthConstant(to: 36, animated: false)
|
|
-
|
|
|
|
|
|
+
|
|
let sendButtonImage = UIImage(named: "paper_plane")?.withRenderingMode(.alwaysTemplate)
|
|
let sendButtonImage = UIImage(named: "paper_plane")?.withRenderingMode(.alwaysTemplate)
|
|
messageInputBar.sendButton.image = sendButtonImage
|
|
messageInputBar.sendButton.image = sendButtonImage
|
|
messageInputBar.sendButton.tintColor = UIColor(white: 1, alpha: 1)
|
|
messageInputBar.sendButton.tintColor = UIColor(white: 1, alpha: 1)
|
|
@@ -260,9 +257,9 @@ class ChatViewController: MessagesViewController {
|
|
|
|
|
|
messageInputBar.sendButton.title = nil
|
|
messageInputBar.sendButton.title = nil
|
|
messageInputBar.sendButton.layer.cornerRadius = 18
|
|
messageInputBar.sendButton.layer.cornerRadius = 18
|
|
-
|
|
|
|
|
|
+
|
|
messageInputBar.textViewPadding.right = -40
|
|
messageInputBar.textViewPadding.right = -40
|
|
-
|
|
|
|
|
|
+
|
|
let leftItems = [
|
|
let leftItems = [
|
|
InputBarButtonItem()
|
|
InputBarButtonItem()
|
|
.configure {
|
|
.configure {
|
|
@@ -274,12 +271,12 @@ class ChatViewController: MessagesViewController {
|
|
$0.tintColor = Constants.primaryColor
|
|
$0.tintColor = Constants.primaryColor
|
|
}.onDeselected {
|
|
}.onDeselected {
|
|
$0.tintColor = UIColor(white: 0.8, alpha: 1)
|
|
$0.tintColor = UIColor(white: 0.8, alpha: 1)
|
|
- }.onTouchUpInside { _ in
|
|
|
|
|
|
+ }.onTouchUpInside { _ in
|
|
self.didPressPhotoButton()
|
|
self.didPressPhotoButton()
|
|
- }
|
|
|
|
|
|
+ },
|
|
]
|
|
]
|
|
messageInputBar.setStackViewItems(leftItems, forStack: .left, animated: false)
|
|
messageInputBar.setStackViewItems(leftItems, forStack: .left, animated: false)
|
|
-
|
|
|
|
|
|
+
|
|
// This just adds some more flare
|
|
// This just adds some more flare
|
|
messageInputBar.sendButton
|
|
messageInputBar.sendButton
|
|
.onEnabled { item in
|
|
.onEnabled { item in
|
|
@@ -290,21 +287,20 @@ class ChatViewController: MessagesViewController {
|
|
UIView.animate(withDuration: 0.3, animations: {
|
|
UIView.animate(withDuration: 0.3, animations: {
|
|
item.backgroundColor = UIColor(white: 0.9, alpha: 1)
|
|
item.backgroundColor = UIColor(white: 0.9, alpha: 1)
|
|
})
|
|
})
|
|
- }
|
|
|
|
|
|
+ }
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
// MARK: - UICollectionViewDataSource
|
|
// MARK: - UICollectionViewDataSource
|
|
-
|
|
|
|
|
|
+
|
|
public override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
|
public override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
|
|
-
|
|
|
|
guard let messagesDataSource = messagesCollectionView.messagesDataSource else {
|
|
guard let messagesDataSource = messagesCollectionView.messagesDataSource else {
|
|
fatalError("Ouch. nil data source for messages")
|
|
fatalError("Ouch. nil data source for messages")
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
// guard !isSectionReservedForTypingBubble(indexPath.section) else {
|
|
// guard !isSectionReservedForTypingBubble(indexPath.section) else {
|
|
// return super.collectionView(collectionView, cellForItemAt: indexPath)
|
|
// return super.collectionView(collectionView, cellForItemAt: indexPath)
|
|
// }
|
|
// }
|
|
-
|
|
|
|
|
|
+
|
|
let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
|
|
let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
|
|
if case .custom = message.kind {
|
|
if case .custom = message.kind {
|
|
let cell = messagesCollectionView.dequeueReusableCell(CustomCell.self, for: indexPath)
|
|
let cell = messagesCollectionView.dequeueReusableCell(CustomCell.self, for: indexPath)
|
|
@@ -318,38 +314,37 @@ class ChatViewController: MessagesViewController {
|
|
// MARK: - MessagesDataSource
|
|
// MARK: - MessagesDataSource
|
|
|
|
|
|
extension ChatViewController: MessagesDataSource {
|
|
extension ChatViewController: MessagesDataSource {
|
|
- func numberOfSections(in messagesCollectionView: MessagesCollectionView) -> Int {
|
|
|
|
|
|
+ func numberOfSections(in _: MessagesCollectionView) -> Int {
|
|
return messageList.count
|
|
return messageList.count
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func currentSender() -> Sender {
|
|
func currentSender() -> Sender {
|
|
-
|
|
|
|
let currentSender = Sender(id: "1", displayName: "Alice")
|
|
let currentSender = Sender(id: "1", displayName: "Alice")
|
|
return currentSender
|
|
return currentSender
|
|
}
|
|
}
|
|
-
|
|
|
|
- func messageForItem(at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageType {
|
|
|
|
|
|
+
|
|
|
|
+ func messageForItem(at indexPath: IndexPath, in _: MessagesCollectionView) -> MessageType {
|
|
return messageList[indexPath.section]
|
|
return messageList[indexPath.section]
|
|
}
|
|
}
|
|
-
|
|
|
|
- func avatar(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> Avatar {
|
|
|
|
|
|
+
|
|
|
|
+ func avatar(for message: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) -> Avatar {
|
|
if let id = Int(messageList[indexPath.section].messageId) {
|
|
if let id = Int(messageList[indexPath.section].messageId) {
|
|
let message = MRMessage(id: id)
|
|
let message = MRMessage(id: id)
|
|
let contact = message.fromContact
|
|
let contact = message.fromContact
|
|
return Avatar(image: contact.profileImage, initials: Utils.getInitials(inputName: contact.name))
|
|
return Avatar(image: contact.profileImage, initials: Utils.getInitials(inputName: contact.name))
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
return Avatar(image: nil, initials: "?")
|
|
return Avatar(image: nil, initials: "?")
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func cellTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
|
|
func cellTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
|
|
if isTimeLabelVisible(at: indexPath) {
|
|
if isTimeLabelVisible(at: indexPath) {
|
|
return NSAttributedString(string: MessageKitDateFormatter.shared.string(from: message.sentDate), attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 10), NSAttributedString.Key.foregroundColor: UIColor.darkGray])
|
|
return NSAttributedString(string: MessageKitDateFormatter.shared.string(from: message.sentDate), attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 10), NSAttributedString.Key.foregroundColor: UIColor.darkGray])
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func messageTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
|
|
func messageTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
|
|
if !isPreviousMessageSameSender(at: indexPath) {
|
|
if !isPreviousMessageSameSender(at: indexPath) {
|
|
let name = message.sender.displayName
|
|
let name = message.sender.displayName
|
|
@@ -358,45 +353,42 @@ extension ChatViewController: MessagesDataSource {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
func isTimeLabelVisible(at indexPath: IndexPath) -> Bool {
|
|
func isTimeLabelVisible(at indexPath: IndexPath) -> Bool {
|
|
// TODO: better heuristic when to show the time label
|
|
// TODO: better heuristic when to show the time label
|
|
return indexPath.section % 3 == 0 && !isPreviousMessageSameSender(at: indexPath)
|
|
return indexPath.section % 3 == 0 && !isPreviousMessageSameSender(at: indexPath)
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func isPreviousMessageSameSender(at indexPath: IndexPath) -> Bool {
|
|
func isPreviousMessageSameSender(at indexPath: IndexPath) -> Bool {
|
|
guard indexPath.section - 1 >= 0 else { return false }
|
|
guard indexPath.section - 1 >= 0 else { return false }
|
|
return messageList[indexPath.section].sender == messageList[indexPath.section - 1].sender
|
|
return messageList[indexPath.section].sender == messageList[indexPath.section - 1].sender
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func isInfoMessage(at indexPath: IndexPath) -> Bool {
|
|
func isInfoMessage(at indexPath: IndexPath) -> Bool {
|
|
if let id = Int(messageList[indexPath.section].messageId) {
|
|
if let id = Int(messageList[indexPath.section].messageId) {
|
|
return MRMessage(id: id).isInfo
|
|
return MRMessage(id: id).isInfo
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
return false
|
|
return false
|
|
}
|
|
}
|
|
-
|
|
|
|
-
|
|
|
|
|
|
+
|
|
func isNextMessageSameSender(at indexPath: IndexPath) -> Bool {
|
|
func isNextMessageSameSender(at indexPath: IndexPath) -> Bool {
|
|
guard indexPath.section + 1 < messageList.count else { return false }
|
|
guard indexPath.section + 1 < messageList.count else { return false }
|
|
return messageList[indexPath.section].sender == messageList[indexPath.section + 1].sender
|
|
return messageList[indexPath.section].sender == messageList[indexPath.section + 1].sender
|
|
}
|
|
}
|
|
-
|
|
|
|
- func messageBottomLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
|
|
|
|
|
|
|
|
|
|
+ func messageBottomLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
|
|
guard indexPath.section < messageList.count else { return nil }
|
|
guard indexPath.section < messageList.count else { return nil }
|
|
if let m = messageToMRMessage(message: messageList[indexPath.section]) {
|
|
if let m = messageToMRMessage(message: messageList[indexPath.section]) {
|
|
- if !isNextMessageSameSender(at: indexPath) && isFromCurrentSender(message: message) {
|
|
|
|
|
|
+ if !isNextMessageSameSender(at: indexPath), isFromCurrentSender(message: message) {
|
|
return NSAttributedString(string: m.stateOutDescription(), attributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .caption1)])
|
|
return NSAttributedString(string: m.stateOutDescription(), attributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .caption1)])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func updateMessage(_ messageId: Int) {
|
|
func updateMessage(_ messageId: Int) {
|
|
let messageIdStr = String(messageId)
|
|
let messageIdStr = String(messageId)
|
|
- if let index = messageList.firstIndex(where:{$0.messageId == messageIdStr}) {
|
|
|
|
|
|
+ if let index = messageList.firstIndex(where: { $0.messageId == messageIdStr }) {
|
|
messageList[index] = idToMessage(messageId: messageId)
|
|
messageList[index] = idToMessage(messageId: messageId)
|
|
// Reload section to update header/footer labels
|
|
// Reload section to update header/footer labels
|
|
messagesCollectionView.performBatchUpdates({
|
|
messagesCollectionView.performBatchUpdates({
|
|
@@ -416,7 +408,7 @@ extension ChatViewController: MessagesDataSource {
|
|
insertMessage(idToMessage(messageId: messageId))
|
|
insertMessage(idToMessage(messageId: messageId))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func insertMessage(_ message: Message) {
|
|
func insertMessage(_ message: Message) {
|
|
messageList.append(message)
|
|
messageList.append(message)
|
|
// Reload last section to update header/footer labels and insert a new one
|
|
// Reload last section to update header/footer labels and insert a new one
|
|
@@ -431,10 +423,10 @@ extension ChatViewController: MessagesDataSource {
|
|
}
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func isLastSectionVisible() -> Bool {
|
|
func isLastSectionVisible() -> Bool {
|
|
guard !messageList.isEmpty else { return false }
|
|
guard !messageList.isEmpty else { return false }
|
|
-
|
|
|
|
|
|
+
|
|
let lastIndexPath = IndexPath(item: 0, section: messageList.count - 1)
|
|
let lastIndexPath = IndexPath(item: 0, section: messageList.count - 1)
|
|
return messagesCollectionView.indexPathsForVisibleItems.contains(lastIndexPath)
|
|
return messagesCollectionView.indexPathsForVisibleItems.contains(lastIndexPath)
|
|
}
|
|
}
|
|
@@ -443,32 +435,29 @@ extension ChatViewController: MessagesDataSource {
|
|
// MARK: - MessagesDisplayDelegate
|
|
// MARK: - MessagesDisplayDelegate
|
|
|
|
|
|
extension ChatViewController: MessagesDisplayDelegate {
|
|
extension ChatViewController: MessagesDisplayDelegate {
|
|
-
|
|
|
|
// MARK: - Text Messages
|
|
// MARK: - Text Messages
|
|
-
|
|
|
|
- func textColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor {
|
|
|
|
|
|
+
|
|
|
|
+ func textColor(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
|
|
return .darkText
|
|
return .darkText
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
// MARK: - All Messages
|
|
// MARK: - All Messages
|
|
-
|
|
|
|
- func backgroundColor(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> UIColor {
|
|
|
|
|
|
+
|
|
|
|
+ func backgroundColor(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
|
|
return isFromCurrentSender(message: message) ? Constants.messagePrimaryColor : Constants.messageSecondaryColor
|
|
return isFromCurrentSender(message: message) ? Constants.messagePrimaryColor : Constants.messageSecondaryColor
|
|
}
|
|
}
|
|
-
|
|
|
|
- func messageStyle(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> MessageStyle {
|
|
|
|
|
|
|
|
|
|
+ func messageStyle(for message: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) -> MessageStyle {
|
|
if isInfoMessage(at: indexPath) {
|
|
if isInfoMessage(at: indexPath) {
|
|
return .custom { view in
|
|
return .custom { view in
|
|
view.style = .none
|
|
view.style = .none
|
|
view.backgroundColor = UIColor(alpha: 0, red: 0, green: 0, blue: 0)
|
|
view.backgroundColor = UIColor(alpha: 0, red: 0, green: 0, blue: 0)
|
|
view.center.x = self.view.center.x
|
|
view.center.x = self.view.center.x
|
|
-
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
var corners: UIRectCorner = []
|
|
var corners: UIRectCorner = []
|
|
-
|
|
|
|
|
|
+
|
|
if isFromCurrentSender(message: message) {
|
|
if isFromCurrentSender(message: message) {
|
|
corners.formUnion(.topLeft)
|
|
corners.formUnion(.topLeft)
|
|
corners.formUnion(.bottomLeft)
|
|
corners.formUnion(.bottomLeft)
|
|
@@ -488,7 +477,7 @@ extension ChatViewController: MessagesDisplayDelegate {
|
|
corners.formUnion(.bottomLeft)
|
|
corners.formUnion(.bottomLeft)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
return .custom { view in
|
|
return .custom { view in
|
|
let radius: CGFloat = 16
|
|
let radius: CGFloat = 16
|
|
let path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
|
|
let path = UIBezierPath(roundedRect: view.bounds, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
|
|
@@ -497,9 +486,8 @@ extension ChatViewController: MessagesDisplayDelegate {
|
|
view.layer.mask = mask
|
|
view.layer.mask = mask
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
- func configureAvatarView(_ avatarView: AvatarView, for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) {
|
|
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ func configureAvatarView(_ avatarView: AvatarView, for message: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) {
|
|
if let id = Int(messageList[indexPath.section].messageId) {
|
|
if let id = Int(messageList[indexPath.section].messageId) {
|
|
let message = MRMessage(id: id)
|
|
let message = MRMessage(id: id)
|
|
let contact = message.fromContact
|
|
let contact = message.fromContact
|
|
@@ -508,8 +496,8 @@ extension ChatViewController: MessagesDisplayDelegate {
|
|
avatarView.isHidden = isNextMessageSameSender(at: indexPath) || message.isInfo
|
|
avatarView.isHidden = isNextMessageSameSender(at: indexPath) || message.isInfo
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
- func enabledDetectors(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> [DetectorType] {
|
|
|
|
|
|
+
|
|
|
|
+ func enabledDetectors(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> [DetectorType] {
|
|
return [.url, .date, .phoneNumber, .address]
|
|
return [.url, .date, .phoneNumber, .address]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -517,35 +505,33 @@ extension ChatViewController: MessagesDisplayDelegate {
|
|
// MARK: - MessagesLayoutDelegate
|
|
// MARK: - MessagesLayoutDelegate
|
|
|
|
|
|
extension ChatViewController: MessagesLayoutDelegate {
|
|
extension ChatViewController: MessagesLayoutDelegate {
|
|
-
|
|
|
|
- func cellTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
|
|
|
|
|
|
+ func cellTopLabelHeight(for _: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) -> CGFloat {
|
|
if isTimeLabelVisible(at: indexPath) {
|
|
if isTimeLabelVisible(at: indexPath) {
|
|
return 18
|
|
return 18
|
|
}
|
|
}
|
|
return 0
|
|
return 0
|
|
}
|
|
}
|
|
-
|
|
|
|
- func messageTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
|
|
|
|
|
|
+
|
|
|
|
+ func messageTopLabelHeight(for message: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) -> CGFloat {
|
|
if isFromCurrentSender(message: message) {
|
|
if isFromCurrentSender(message: message) {
|
|
return !isPreviousMessageSameSender(at: indexPath) ? 20 : 0
|
|
return !isPreviousMessageSameSender(at: indexPath) ? 20 : 0
|
|
} else {
|
|
} else {
|
|
return !isPreviousMessageSameSender(at: indexPath) ? (20 + outgoingAvatarOverlap) : 0
|
|
return !isPreviousMessageSameSender(at: indexPath) ? (20 + outgoingAvatarOverlap) : 0
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
- func messageBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
|
|
|
|
|
|
+
|
|
|
|
+ func messageBottomLabelHeight(for message: MessageType, at indexPath: IndexPath, in _: MessagesCollectionView) -> CGFloat {
|
|
return (!isNextMessageSameSender(at: indexPath) && isFromCurrentSender(message: message)) && !isInfoMessage(at: indexPath) ? 16 : 0
|
|
return (!isNextMessageSameSender(at: indexPath) && isFromCurrentSender(message: message)) && !isInfoMessage(at: indexPath) ? 16 : 0
|
|
}
|
|
}
|
|
|
|
|
|
- func heightForLocation(message: MessageType, at indexPath: IndexPath, with maxWidth: CGFloat, in messagesCollectionView: MessagesCollectionView) -> CGFloat {
|
|
|
|
|
|
+ func heightForLocation(message _: MessageType, at _: IndexPath, with _: CGFloat, in _: MessagesCollectionView) -> CGFloat {
|
|
return 40
|
|
return 40
|
|
}
|
|
}
|
|
-
|
|
|
|
- func footerViewSize(for message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize {
|
|
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ func footerViewSize(for _: MessageType, at _: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CGSize {
|
|
return CGSize(width: messagesCollectionView.bounds.width, height: 10)
|
|
return CGSize(width: messagesCollectionView.bounds.width, height: 10)
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
@objc func didPressPhotoButton() {
|
|
@objc func didPressPhotoButton() {
|
|
if UIImagePickerController.isSourceTypeAvailable(.camera) {
|
|
if UIImagePickerController.isSourceTypeAvailable(.camera) {
|
|
let imagePicker = UIImagePickerController()
|
|
let imagePicker = UIImagePickerController()
|
|
@@ -553,49 +539,49 @@ extension ChatViewController: MessagesLayoutDelegate {
|
|
imagePicker.cameraDevice = .rear
|
|
imagePicker.cameraDevice = .rear
|
|
imagePicker.delegate = self
|
|
imagePicker.delegate = self
|
|
imagePicker.allowsEditing = true
|
|
imagePicker.allowsEditing = true
|
|
- self.present(imagePicker, animated: true, completion: nil)
|
|
|
|
|
|
+ present(imagePicker, animated: true, completion: nil)
|
|
} else {
|
|
} else {
|
|
- logger.info( "no camera available")
|
|
|
|
|
|
+ logger.info("no camera available")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
fileprivate func saveImage(image: UIImage) -> String? {
|
|
fileprivate func saveImage(image: UIImage) -> String? {
|
|
guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) as NSURL else {
|
|
guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) as NSURL else {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
let size = image.size.applying(CGAffineTransform(scaleX: 0.2, y: 0.2))
|
|
let size = image.size.applying(CGAffineTransform(scaleX: 0.2, y: 0.2))
|
|
let hasAlpha = false
|
|
let hasAlpha = false
|
|
let scale: CGFloat = 0.0
|
|
let scale: CGFloat = 0.0
|
|
-
|
|
|
|
|
|
+
|
|
UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale)
|
|
UIGraphicsBeginImageContextWithOptions(size, !hasAlpha, scale)
|
|
image.draw(in: CGRect(origin: CGPoint.zero, size: size))
|
|
image.draw(in: CGRect(origin: CGPoint.zero, size: size))
|
|
-
|
|
|
|
|
|
+
|
|
let _scaledImage = UIGraphicsGetImageFromCurrentImageContext()
|
|
let _scaledImage = UIGraphicsGetImageFromCurrentImageContext()
|
|
UIGraphicsEndImageContext()
|
|
UIGraphicsEndImageContext()
|
|
-
|
|
|
|
|
|
+
|
|
guard let scaledImage = _scaledImage else {
|
|
guard let scaledImage = _scaledImage else {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
guard let data = scaledImage.jpegData(compressionQuality: 0.9) else {
|
|
guard let data = scaledImage.jpegData(compressionQuality: 0.9) else {
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
do {
|
|
do {
|
|
- let timestamp = Int(Date().timeIntervalSince1970)
|
|
|
|
|
|
+ let timestamp = Int(Date().timeIntervalSince1970)
|
|
let path = directory.appendingPathComponent("\(chatId)_\(timestamp).jpg")
|
|
let path = directory.appendingPathComponent("\(chatId)_\(timestamp).jpg")
|
|
try data.write(to: path!)
|
|
try data.write(to: path!)
|
|
return path?.relativePath
|
|
return path?.relativePath
|
|
} catch {
|
|
} catch {
|
|
- logger.info( error.localizedDescription)
|
|
|
|
|
|
+ logger.info(error.localizedDescription)
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
extension ChatViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
|
|
extension ChatViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
|
|
- func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
|
|
|
|
|
|
+ func imagePickerController(_: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
|
|
DispatchQueue.global().async {
|
|
DispatchQueue.global().async {
|
|
if let pickedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage,
|
|
if let pickedImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage,
|
|
let width = Int32(exactly: pickedImage.size.width),
|
|
let width = Int32(exactly: pickedImage.size.width),
|
|
@@ -605,43 +591,40 @@ extension ChatViewController: UIImagePickerControllerDelegate, UINavigationContr
|
|
dc_msg_set_file(msg, path, "image/jpeg")
|
|
dc_msg_set_file(msg, path, "image/jpeg")
|
|
dc_msg_set_dimension(msg, width, height)
|
|
dc_msg_set_dimension(msg, width, height)
|
|
dc_send_msg(mailboxPointer, UInt32(self.chatId), msg)
|
|
dc_send_msg(mailboxPointer, UInt32(self.chatId), msg)
|
|
-
|
|
|
|
|
|
+
|
|
// cleanup
|
|
// cleanup
|
|
dc_msg_unref(msg)
|
|
dc_msg_unref(msg)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
dismiss(animated: true, completion: nil)
|
|
dismiss(animated: true, completion: nil)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
// MARK: - MessageCellDelegate
|
|
// MARK: - MessageCellDelegate
|
|
|
|
|
|
extension ChatViewController: MessageCellDelegate {
|
|
extension ChatViewController: MessageCellDelegate {
|
|
- func didTapMessage(in cell: MessageCollectionViewCell) {
|
|
|
|
- logger.info( "Message tapped")
|
|
|
|
|
|
+ func didTapMessage(in _: MessageCollectionViewCell) {
|
|
|
|
+ logger.info("Message tapped")
|
|
}
|
|
}
|
|
-
|
|
|
|
- func didTapAvatar(in cell: MessageCollectionViewCell) {
|
|
|
|
- logger.info( "Avatar tapped")
|
|
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ func didTapAvatar(in _: MessageCollectionViewCell) {
|
|
|
|
+ logger.info("Avatar tapped")
|
|
}
|
|
}
|
|
-
|
|
|
|
- @objc(didTapCellTopLabelIn:) func didTapCellTopLabel(in cell: MessageCollectionViewCell) {
|
|
|
|
- logger.info( "Top label tapped")
|
|
|
|
|
|
+
|
|
|
|
+ @objc(didTapCellTopLabelIn:) func didTapCellTopLabel(in _: MessageCollectionViewCell) {
|
|
|
|
+ logger.info("Top label tapped")
|
|
}
|
|
}
|
|
|
|
|
|
- func didTapBottomLabel(in cell: MessageCollectionViewCell) {
|
|
|
|
- print ("Bottom label tapped")
|
|
|
|
|
|
+ func didTapBottomLabel(in _: MessageCollectionViewCell) {
|
|
|
|
+ print("Bottom label tapped")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// MARK: - MessageLabelDelegate
|
|
// MARK: - MessageLabelDelegate
|
|
|
|
|
|
extension ChatViewController: MessageLabelDelegate {
|
|
extension ChatViewController: MessageLabelDelegate {
|
|
-
|
|
|
|
- func didSelectAddress(_ addressComponents: [String : String]) {
|
|
|
|
|
|
+ func didSelectAddress(_ addressComponents: [String: String]) {
|
|
let mapAddress = Utils.formatAddressForQuery(address: addressComponents)
|
|
let mapAddress = Utils.formatAddressForQuery(address: addressComponents)
|
|
if let escapedMapAddress = mapAddress.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
|
|
if let escapedMapAddress = mapAddress.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
|
|
// Use query, to handle malformed addresses
|
|
// Use query, to handle malformed addresses
|
|
@@ -650,58 +633,56 @@ extension ChatViewController: MessageLabelDelegate {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func didSelectDate(_ date: Date) {
|
|
func didSelectDate(_ date: Date) {
|
|
let interval = date.timeIntervalSinceReferenceDate
|
|
let interval = date.timeIntervalSinceReferenceDate
|
|
if let url = NSURL(string: "calshow:\(interval)") {
|
|
if let url = NSURL(string: "calshow:\(interval)") {
|
|
UIApplication.shared.open(url as URL)
|
|
UIApplication.shared.open(url as URL)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func didSelectPhoneNumber(_ phoneNumber: String) {
|
|
func didSelectPhoneNumber(_ phoneNumber: String) {
|
|
- logger.info( "phone open", phoneNumber)
|
|
|
|
|
|
+ logger.info("phone open", phoneNumber)
|
|
if let escapedPhoneNumber = phoneNumber.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
|
|
if let escapedPhoneNumber = phoneNumber.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
|
|
if let url = NSURL(string: "tel:\(escapedPhoneNumber)") {
|
|
if let url = NSURL(string: "tel:\(escapedPhoneNumber)") {
|
|
UIApplication.shared.open(url as URL)
|
|
UIApplication.shared.open(url as URL)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
func didSelectURL(_ url: URL) {
|
|
func didSelectURL(_ url: URL) {
|
|
UIApplication.shared.open(url)
|
|
UIApplication.shared.open(url)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// MARK: - LocationMessageDisplayDelegate
|
|
// MARK: - LocationMessageDisplayDelegate
|
|
|
|
+
|
|
/*
|
|
/*
|
|
-extension ChatViewController: LocationMessageDisplayDelegate {
|
|
|
|
- func annotationViewForLocation(message: MessageType, at indexPath: IndexPath, in messageCollectionView: MessagesCollectionView) -> MKAnnotationView? {
|
|
|
|
- let annotationView = MKAnnotationView(annotation: nil, reuseIdentifier: nil)
|
|
|
|
- let pinImage = #imageLiteral(resourceName: "ic_block_36pt").withRenderingMode(.alwaysTemplate)
|
|
|
|
- annotationView.image = pinImage
|
|
|
|
- annotationView.centerOffset = CGPoint(x: 0, y: -pinImage.size.height / 2)
|
|
|
|
- return annotationView
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- func animationBlockForLocation(message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> ((UIImageView) -> Void)? {
|
|
|
|
- return { view in
|
|
|
|
- view.layer.transform = CATransform3DMakeScale(0, 0, 0)
|
|
|
|
- view.alpha = 0.0
|
|
|
|
- UIView.animate(withDuration: 0.6, delay: 0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0, options: [], animations: {
|
|
|
|
- view.layer.transform = CATransform3DIdentity
|
|
|
|
- view.alpha = 1.0
|
|
|
|
- }, completion: nil)
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-*/
|
|
|
|
|
|
+ extension ChatViewController: LocationMessageDisplayDelegate {
|
|
|
|
+ func annotationViewForLocation(message: MessageType, at indexPath: IndexPath, in messageCollectionView: MessagesCollectionView) -> MKAnnotationView? {
|
|
|
|
+ let annotationView = MKAnnotationView(annotation: nil, reuseIdentifier: nil)
|
|
|
|
+ let pinImage = #imageLiteral(resourceName: "ic_block_36pt").withRenderingMode(.alwaysTemplate)
|
|
|
|
+ annotationView.image = pinImage
|
|
|
|
+ annotationView.centerOffset = CGPoint(x: 0, y: -pinImage.size.height / 2)
|
|
|
|
+ return annotationView
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ func animationBlockForLocation(message: MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> ((UIImageView) -> Void)? {
|
|
|
|
+ return { view in
|
|
|
|
+ view.layer.transform = CATransform3DMakeScale(0, 0, 0)
|
|
|
|
+ view.alpha = 0.0
|
|
|
|
+ UIView.animate(withDuration: 0.6, delay: 0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0, options: [], animations: {
|
|
|
|
+ view.layer.transform = CATransform3DIdentity
|
|
|
|
+ view.alpha = 1.0
|
|
|
|
+ }, completion: nil)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ */
|
|
|
|
|
|
// MARK: - MessageInputBarDelegate
|
|
// MARK: - MessageInputBarDelegate
|
|
-
|
|
|
|
|
|
|
|
extension ChatViewController: MessageInputBarDelegate {
|
|
extension ChatViewController: MessageInputBarDelegate {
|
|
-
|
|
|
|
func messageInputBar(_ inputBar: MessageInputBar, didPressSendButtonWith text: String) {
|
|
func messageInputBar(_ inputBar: MessageInputBar, didPressSendButtonWith text: String) {
|
|
DispatchQueue.global().async {
|
|
DispatchQueue.global().async {
|
|
dc_send_text_msg(mailboxPointer, UInt32(self.chatId), text)
|
|
dc_send_text_msg(mailboxPointer, UInt32(self.chatId), text)
|