Add cell layout updater implementation for bubbles.

This commit is contained in:
SBiOSoftWhare 2022-01-14 09:51:08 +01:00
parent b916968139
commit f90f2fba0e

View file

@ -0,0 +1,293 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import UIKit
@objcMembers
class BubbleRoomCellLayoutUpdater: RoomCellLayoutUpdaterProtocol {
// MARK: - Constants
private enum Constants {
static let bubbleBackgroundViewCornerRadius: CGFloat = 12.0
}
// MARK: - Properties
private var incomingColor: UIColor {
return ThemeService.shared().theme.colors.system
}
private var outgoingColor: UIColor {
return ThemeService.shared().theme.colors.accent.withAlphaComponent(0.10)
}
// MARK: - Public
func updateLayoutIfNeeded(for cell: MXKRoomBubbleTableViewCell, andCellData cellData: MXKRoomBubbleCellData) {
var isMessageSenderCurrentUser: Bool = false
if let senderId = cellData.senderId, let currentUserId = cellData.mxSession.myUserId, senderId == currentUserId {
isMessageSenderCurrentUser = true
}
if isMessageSenderCurrentUser {
self.updateLayout(forOutgoingTextMessageCell: cell, andCellData: cellData)
} else {
self.updateLayout(forIncomingTextMessageCell: cell, andCellData: cellData)
}
}
func updateLayout(forIncomingTextMessageCell cell: MXKRoomBubbleTableViewCell, andCellData cellData: MXKRoomBubbleCellData) {
if let messageBubbleBackgroundView = self.getMessageBubbleBackgroundView(from: cell) {
if self.canUseBubbleBackground(forCell: cell, withCellData: cellData) {
messageBubbleBackgroundView.isHidden = false
self.updateMessageBubbleBackgroundView(messageBubbleBackgroundView, withCell: cell, andCellData: cellData)
} else {
messageBubbleBackgroundView.isHidden = true
}
}
}
func updateLayout(forOutgoingTextMessageCell cell: MXKRoomBubbleTableViewCell, andCellData cellData: MXKRoomBubbleCellData) {
if let messageBubbleBackgroundView = self.getMessageBubbleBackgroundView(from: cell) {
if self.canUseBubbleBackground(forCell: cell, withCellData: cellData) {
messageBubbleBackgroundView.isHidden = false
self.updateMessageBubbleBackgroundView(messageBubbleBackgroundView, withCell: cell, andCellData: cellData)
} else {
messageBubbleBackgroundView.isHidden = true
}
}
}
func setupLayout(forIncomingTextMessageCell cell: MXKRoomBubbleTableViewCell) {
self.setupIncomingMessageTextViewMargins(for: cell)
self.addBubbleBackgroundViewToCell(cell, backgroundColor: self.incomingColor)
cell.setNeedsUpdateConstraints()
}
func setupLayout(forOutgoingTextMessageCell cell: MXKRoomBubbleTableViewCell) {
self.setupOutgoingMessageTextViewMargins(for: cell)
// Hide avatar view
cell.pictureView?.isHidden = true
self.addBubbleBackgroundViewToCell(cell, backgroundColor: self.outgoingColor)
cell.setNeedsUpdateConstraints()
}
// MARK: - Private
// MARK: Bubble background view
private func createBubbleBackgroundView(with backgroundColor: UIColor) -> RoomMessageBubbleBackgroundView {
let bubbleBackgroundView = RoomMessageBubbleBackgroundView()
bubbleBackgroundView.backgroundColor = backgroundColor
return bubbleBackgroundView
}
func getMessageBubbleBackgroundView(from cell: MXKRoomBubbleTableViewCell) -> RoomMessageBubbleBackgroundView? {
let foundView = cell.contentView.subviews.first { view in
return view is RoomMessageBubbleBackgroundView
}
return foundView as? RoomMessageBubbleBackgroundView
}
private func addBubbleBackgroundViewToCell(_ bubbleCell: MXKRoomBubbleTableViewCell, backgroundColor: UIColor) {
guard let messageTextView = bubbleCell.messageTextView else {
return
}
let topMargin: CGFloat = 0.0
let leftMargin: CGFloat = -5.0
let rightMargin: CGFloat = 5.0
let bubbleBackgroundView = self.createBubbleBackgroundView(with: backgroundColor)
bubbleCell.contentView.insertSubview(bubbleBackgroundView, at: 0)
let topAnchor = messageTextView.topAnchor
let leadingAnchor = messageTextView.leadingAnchor
let trailingAnchor = messageTextView.trailingAnchor
bubbleBackgroundView.updateHeight(messageTextView.frame.height)
NSLayoutConstraint.activate([
bubbleBackgroundView.topAnchor.constraint(equalTo: topAnchor, constant: topMargin),
bubbleBackgroundView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: leftMargin),
bubbleBackgroundView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: rightMargin)
])
}
private func canUseBubbleBackground(forCell cell: MXKRoomBubbleTableViewCell, withCellData cellData: MXKRoomBubbleCellData) -> Bool {
guard let firstComponent = cellData.getFirstBubbleComponentWithDisplay(), let firstEvent = firstComponent.event else {
return false
}
switch firstEvent.eventType {
case .roomMessage:
if let messageTypeString = firstEvent.content["msgtype"] as? String {
let messageType = MXMessageType(identifier: messageTypeString)
switch messageType {
case .text :
return true
default:
break
}
}
default:
break
}
return false
}
private func getTextMessageHeight(for cell: MXKRoomBubbleTableViewCell, andCellData cellData: MXKRoomBubbleCellData) -> CGFloat? {
guard let roomBubbleCellData = cellData as? RoomBubbleCellData,
let lastBubbleComponent = cellData.getLastBubbleComponentWithDisplay(),
let firstComponent = roomBubbleCellData.getFirstBubbleComponentWithDisplay() else {
return nil
}
let bubbleHeight: CGFloat
let bottomMargin: CGFloat = 4.0
let lastEventId = lastBubbleComponent.event.eventId
let lastMessageBottomPosition = cell.bottomPosition(ofEvent: lastEventId)
let firstEventId = firstComponent.event.eventId
let firstMessageTopPosition = cell.topPosition(ofEvent: firstEventId)
let additionalContentHeight = roomBubbleCellData.additionalHeight(forEvent: lastEventId)
bubbleHeight = lastMessageBottomPosition - firstMessageTopPosition - additionalContentHeight + bottomMargin
guard bubbleHeight >= 0 else {
return nil
}
return bubbleHeight
}
@discardableResult
private func updateMessageBubbleBackgroundView(_ roomMessageBubbleBackgroundView: RoomMessageBubbleBackgroundView, withCell cell: MXKRoomBubbleTableViewCell, andCellData cellData: MXKRoomBubbleCellData) -> Bool {
var finalBubbleHeight: CGFloat?
if let bubbleHeight = self.getTextMessageHeight(for: cell, andCellData: cellData) {
finalBubbleHeight = bubbleHeight
} else if let messageTextViewHeight = cell.messageTextView?.frame.height {
finalBubbleHeight = messageTextViewHeight
}
if let finalBubbleHeight = finalBubbleHeight {
return roomMessageBubbleBackgroundView.updateHeight(finalBubbleHeight)
} else {
return false
}
}
private func getIncomingMessageTextViewInsets(from bubbleCell: MXKRoomBubbleTableViewCell) -> UIEdgeInsets {
let messageViewMarginTop: CGFloat
let messageViewMarginBottom: CGFloat = -2.0
let messageViewMarginLeft: CGFloat = 3.0
let messageViewMarginRight: CGFloat = 0.0
if bubbleCell.userNameLabel != nil {
messageViewMarginTop = 10.0
} else {
messageViewMarginTop = 0.0
}
let messageViewInsets = UIEdgeInsets(top: messageViewMarginTop, left: messageViewMarginLeft, bottom: messageViewMarginBottom, right: messageViewMarginRight)
return messageViewInsets
}
// MARK: Text message
private func setupIncomingMessageTextViewMargins(for cell: MXKRoomBubbleTableViewCell) {
guard cell.messageTextView != nil else {
return
}
let messageViewInsets = self.getIncomingMessageTextViewInsets(from: cell)
cell.msgTextViewBottomConstraint.constant += messageViewInsets.bottom
cell.msgTextViewTopConstraint.constant += messageViewInsets.top
cell.msgTextViewLeadingConstraint.constant += messageViewInsets.left
cell.msgTextViewTrailingConstraint.constant += messageViewInsets.right
}
private func setupOutgoingMessageTextViewMargins(for cell: MXKRoomBubbleTableViewCell) {
guard let messageTextView = cell.messageTextView else {
return
}
let contentView = cell.contentView
let leftMargin: CGFloat = 80.0
let rightMargin: CGFloat = 38.0
let bottomMargin: CGFloat = -2.0
cell.msgTextViewLeadingConstraint.isActive = false
cell.msgTextViewTrailingConstraint.isActive = false
let leftConstraint = messageTextView.leadingAnchor.constraint(greaterThanOrEqualTo: contentView.leadingAnchor, constant: leftMargin)
let rightConstraint = messageTextView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -rightMargin)
NSLayoutConstraint.activate([
leftConstraint,
rightConstraint
])
cell.msgTextViewLeadingConstraint = leftConstraint
cell.msgTextViewTrailingConstraint = rightConstraint
cell.msgTextViewBottomConstraint.constant += bottomMargin
}
}