2019-12-20 09:42:28 +00:00
|
|
|
/*
|
|
|
|
Copyright 2019 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 Foundation
|
|
|
|
|
|
|
|
@objcMembers
|
|
|
|
class KeyVerificationBaseBubbleCell: MXKRoomBubbleTableViewCell {
|
|
|
|
|
|
|
|
// MARK: - Constants
|
|
|
|
|
|
|
|
private enum Sizing {
|
|
|
|
static var sizes = Set<SizingViewHeight>()
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Properties
|
|
|
|
|
|
|
|
// MARK: Public
|
|
|
|
|
|
|
|
weak var keyVerificationCellInnerContentView: KeyVerificationCellInnerContentView?
|
2020-01-14 19:10:13 +00:00
|
|
|
|
|
|
|
weak var bubbleCellContentView: BubbleCellContentView?
|
2019-12-20 09:42:28 +00:00
|
|
|
|
|
|
|
override var bubbleInfoContainer: UIView! {
|
|
|
|
get {
|
2020-01-14 19:10:13 +00:00
|
|
|
guard let infoContainer = self.bubbleCellContentView?.bubbleInfoContainer else {
|
2019-12-20 09:42:28 +00:00
|
|
|
fatalError("[KeyVerificationBaseBubbleCell] bubbleInfoContainer should not be used before set")
|
|
|
|
}
|
|
|
|
return infoContainer
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
super.bubbleInfoContainer = newValue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override var bubbleOverlayContainer: UIView! {
|
|
|
|
get {
|
2020-01-14 19:10:13 +00:00
|
|
|
guard let overlayContainer = self.bubbleCellContentView?.bubbleOverlayContainer else {
|
2019-12-20 09:42:28 +00:00
|
|
|
fatalError("[KeyVerificationBaseBubbleCell] bubbleOverlayContainer should not be used before set")
|
|
|
|
}
|
|
|
|
return overlayContainer
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
super.bubbleInfoContainer = newValue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override var bubbleInfoContainerTopConstraint: NSLayoutConstraint! {
|
|
|
|
get {
|
2020-01-14 19:10:13 +00:00
|
|
|
guard let infoContainerTopConstraint = self.bubbleCellContentView?.bubbleInfoContainerTopConstraint else {
|
2019-12-20 09:42:28 +00:00
|
|
|
fatalError("[KeyVerificationBaseBubbleCell] bubbleInfoContainerTopConstraint should not be used before set")
|
|
|
|
}
|
|
|
|
return infoContainerTopConstraint
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
super.bubbleInfoContainerTopConstraint = newValue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Setup
|
|
|
|
|
|
|
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
|
|
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
|
|
|
self.commonInit()
|
|
|
|
}
|
|
|
|
|
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
}
|
|
|
|
|
|
|
|
private func commonInit() {
|
|
|
|
|
|
|
|
self.selectionStyle = .none
|
|
|
|
self.setupContentView()
|
|
|
|
self.update(theme: ThemeService.shared().theme)
|
|
|
|
|
|
|
|
super.setupViews()
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Public
|
|
|
|
|
|
|
|
func update(theme: Theme) {
|
2020-01-14 19:10:13 +00:00
|
|
|
self.bubbleCellContentView?.update(theme: theme)
|
2019-12-20 09:42:28 +00:00
|
|
|
self.keyVerificationCellInnerContentView?.update(theme: theme)
|
|
|
|
}
|
|
|
|
|
|
|
|
func buildUserInfoText(with userId: String, userDisplayName: String?) -> String {
|
|
|
|
|
|
|
|
let userInfoText: String
|
|
|
|
|
|
|
|
if let userDisplayName = userDisplayName {
|
|
|
|
userInfoText = "\(userId) (\(userDisplayName))"
|
|
|
|
} else {
|
|
|
|
userInfoText = userId
|
|
|
|
}
|
|
|
|
|
|
|
|
return userInfoText
|
|
|
|
}
|
|
|
|
|
|
|
|
func senderId(from bubbleCellData: MXKRoomBubbleCellData) -> String {
|
|
|
|
return bubbleCellData.senderId ?? ""
|
|
|
|
}
|
|
|
|
|
|
|
|
func senderDisplayName(from bubbleCellData: MXKRoomBubbleCellData) -> String? {
|
|
|
|
let senderId = self.senderId(from: bubbleCellData)
|
|
|
|
guard let senderDisplayName = bubbleCellData.senderDisplayName, senderId != senderDisplayName else {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return senderDisplayName
|
|
|
|
}
|
|
|
|
|
2020-01-14 19:10:13 +00:00
|
|
|
class func sizingView() -> KeyVerificationBaseBubbleCell {
|
|
|
|
fatalError("[KeyVerificationBaseBubbleCell] Subclass should implement this method")
|
2019-12-20 09:42:28 +00:00
|
|
|
}
|
|
|
|
|
2020-01-14 19:10:13 +00:00
|
|
|
class func sizingViewHeightHashValue(from bubbleCellData: MXKRoomBubbleCellData) -> Int {
|
|
|
|
|
|
|
|
var hasher = Hasher()
|
|
|
|
|
|
|
|
let sizingView = self.sizingView()
|
|
|
|
sizingView.render(bubbleCellData)
|
|
|
|
|
|
|
|
// Add cell class name
|
|
|
|
hasher.combine(self.defaultReuseIdentifier())
|
|
|
|
|
|
|
|
if let keyVerificationCellInnerContentView = sizingView.keyVerificationCellInnerContentView {
|
|
|
|
|
|
|
|
// Add other user info
|
|
|
|
if let otherUserInfo = keyVerificationCellInnerContentView.otherUserInfo {
|
|
|
|
hasher.combine(otherUserInfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add request status text
|
|
|
|
if keyVerificationCellInnerContentView.isRequestStatusHidden == false,
|
|
|
|
let requestStatusText = sizingView.keyVerificationCellInnerContentView?.requestStatusText {
|
|
|
|
hasher.combine(requestStatusText)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return hasher.finalize()
|
2019-12-20 09:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - Overrides
|
|
|
|
|
|
|
|
override class func defaultReuseIdentifier() -> String! {
|
|
|
|
return String(describing: self)
|
|
|
|
}
|
|
|
|
|
|
|
|
override func didEndDisplay() {
|
|
|
|
super.didEndDisplay()
|
|
|
|
self.removeReadReceiptsView()
|
|
|
|
}
|
|
|
|
|
|
|
|
override class func height(for cellData: MXKCellData!, withMaximumWidth maxWidth: CGFloat) -> CGFloat {
|
|
|
|
guard let cellData = cellData else {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
guard let roomBubbleCellData = cellData as? MXKRoomBubbleCellData else {
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
let height: CGFloat
|
|
|
|
|
|
|
|
let sizingViewHeight = self.findOrCreateSizingViewHeight(from: roomBubbleCellData)
|
|
|
|
|
|
|
|
if let cachedHeight = sizingViewHeight.heights[maxWidth] {
|
|
|
|
height = cachedHeight
|
|
|
|
} else {
|
|
|
|
height = self.contentViewHeight(for: roomBubbleCellData, fitting: maxWidth)
|
|
|
|
sizingViewHeight.heights[maxWidth] = height
|
|
|
|
}
|
|
|
|
|
|
|
|
return height
|
|
|
|
}
|
|
|
|
|
2020-01-14 19:10:13 +00:00
|
|
|
override func render(_ cellData: MXKCellData!) {
|
|
|
|
super.render(cellData)
|
|
|
|
|
|
|
|
if let bubbleData = self.bubbleData,
|
|
|
|
let bubbleCellContentView = self.bubbleCellContentView,
|
|
|
|
let paginationDate = bubbleData.date,
|
|
|
|
bubbleCellContentView.showPaginationTitle {
|
|
|
|
bubbleCellContentView.paginationLabel.text = bubbleData.eventFormatter.dateString(from: paginationDate, withTime: false)?.uppercased()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-20 09:42:28 +00:00
|
|
|
// MARK: - Private
|
|
|
|
|
|
|
|
private func setupContentView() {
|
2020-01-14 19:10:13 +00:00
|
|
|
if self.bubbleCellContentView == nil {
|
2019-12-20 09:42:28 +00:00
|
|
|
|
2020-01-14 19:10:13 +00:00
|
|
|
let bubbleCellContentView = BubbleCellContentView.instantiate()
|
2019-12-20 09:42:28 +00:00
|
|
|
|
|
|
|
let innerContentView = KeyVerificationCellInnerContentView.instantiate()
|
|
|
|
|
2020-01-14 19:10:13 +00:00
|
|
|
bubbleCellContentView.innerContentView.vc_addSubViewMatchingParent(innerContentView)
|
2019-12-20 09:42:28 +00:00
|
|
|
|
2020-01-14 19:10:13 +00:00
|
|
|
self.contentView.vc_addSubViewMatchingParent(bubbleCellContentView)
|
2019-12-20 09:42:28 +00:00
|
|
|
|
2020-01-14 19:10:13 +00:00
|
|
|
self.bubbleCellContentView = bubbleCellContentView
|
2019-12-20 09:42:28 +00:00
|
|
|
self.keyVerificationCellInnerContentView = innerContentView
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static func findOrCreateSizingViewHeight(from bubbleData: MXKRoomBubbleCellData) -> SizingViewHeight {
|
|
|
|
|
|
|
|
let sizingViewHeight: SizingViewHeight
|
|
|
|
let bubbleDataHashValue = bubbleData.hashValue
|
|
|
|
|
|
|
|
if let foundSizingViewHeight = self.Sizing.sizes.first(where: { (sizingViewHeight) -> Bool in
|
|
|
|
return sizingViewHeight.uniqueIdentifier == bubbleDataHashValue
|
|
|
|
}) {
|
|
|
|
sizingViewHeight = foundSizingViewHeight
|
|
|
|
} else {
|
|
|
|
sizingViewHeight = SizingViewHeight(uniqueIdentifier: bubbleDataHashValue)
|
|
|
|
}
|
|
|
|
|
|
|
|
return sizingViewHeight
|
|
|
|
}
|
|
|
|
|
|
|
|
private static func contentViewHeight(for cellData: MXKCellData, fitting width: CGFloat) -> CGFloat {
|
|
|
|
let sizingView = self.sizingView()
|
|
|
|
|
|
|
|
sizingView.render(cellData)
|
|
|
|
sizingView.layoutIfNeeded()
|
|
|
|
|
|
|
|
let fittingSize = CGSize(width: width, height: UIView.layoutFittingCompressedSize.height)
|
2020-01-14 19:10:13 +00:00
|
|
|
var height = sizingView.systemLayoutSizeFitting(fittingSize).height
|
|
|
|
|
|
|
|
if let roomBubbleCellData = cellData as? RoomBubbleCellData, let readReceipts = roomBubbleCellData.readReceipts, readReceipts.count > 0 {
|
|
|
|
height+=RoomBubbleCellLayout.readReceiptsViewHeight
|
|
|
|
}
|
2019-12-20 09:42:28 +00:00
|
|
|
|
|
|
|
return height
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - BubbleCellReadReceiptsDisplayable
|
|
|
|
extension KeyVerificationBaseBubbleCell: BubbleCellReadReceiptsDisplayable {
|
|
|
|
|
|
|
|
func addReadReceiptsView(_ readReceiptsView: UIView) {
|
2020-01-14 19:10:13 +00:00
|
|
|
self.bubbleCellContentView?.addReadReceiptsView(readReceiptsView)
|
2019-12-20 09:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func removeReadReceiptsView() {
|
2020-01-14 19:10:13 +00:00
|
|
|
self.bubbleCellContentView?.removeReadReceiptsView()
|
2019-12-20 09:42:28 +00:00
|
|
|
}
|
|
|
|
}
|