BaseMessageCell.swift 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import UIKit
  2. import DcCore
  3. public class BaseMessageCell: UITableViewCell {
  4. static var defaultPadding: CGFloat = 12
  5. static var containerPadding: CGFloat = -6
  6. typealias BMC = BaseMessageCell
  7. lazy var avatarView: InitialsBadge = {
  8. let view = InitialsBadge(size: 28)
  9. view.setColor(UIColor.gray)
  10. view.translatesAutoresizingMaskIntoConstraints = false
  11. return view
  12. }()
  13. lazy var topLabel: UILabel = {
  14. let label = UILabel()
  15. label.translatesAutoresizingMaskIntoConstraints = false
  16. label.text = "title"
  17. label.font = UIFont.preferredFont(for: .caption1, weight: .regular)
  18. return label
  19. }()
  20. lazy var mainContentView: UIStackView = {
  21. let view = UIStackView()
  22. view.translatesAutoresizingMaskIntoConstraints = false
  23. view.axis = .horizontal
  24. return view
  25. }()
  26. lazy var bottomContentView: UIStackView = {
  27. let view = UIStackView(arrangedSubviews: [bottomLabel])
  28. view.translatesAutoresizingMaskIntoConstraints = false
  29. view.axis = .horizontal
  30. return view
  31. }()
  32. lazy var bottomLabel: UILabel = {
  33. let label = UILabel()
  34. label.translatesAutoresizingMaskIntoConstraints = false
  35. label.font = UIFont.preferredFont(for: .caption1, weight: .regular)
  36. return label
  37. }()
  38. private lazy var contentContainer: UIStackView = {
  39. let view = UIStackView(arrangedSubviews: [topLabel, mainContentView, bottomContentView])
  40. view.translatesAutoresizingMaskIntoConstraints = false
  41. view.axis = .vertical
  42. return view
  43. }()
  44. private lazy var messageBackgroundContainer: BackgroundContainer = {
  45. let container = BackgroundContainer()
  46. container.image = UIImage(color: UIColor.blue)
  47. container.contentMode = .scaleToFill
  48. container.clipsToBounds = true
  49. container.translatesAutoresizingMaskIntoConstraints = false
  50. return container
  51. }()
  52. override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
  53. super.init(style: .subtitle, reuseIdentifier: reuseIdentifier)
  54. setupSubviews()
  55. }
  56. required init?(coder: NSCoder) {
  57. fatalError("init(coder:) has not been implemented")
  58. }
  59. func setupSubviews() {
  60. contentView.addSubview(avatarView)
  61. contentView.addSubview(messageBackgroundContainer)
  62. contentView.addSubview(contentContainer)
  63. contentView.addConstraints([
  64. avatarView.constraintAlignTopTo(contentView, paddingTop: BMC.defaultPadding),
  65. avatarView.constraintAlignLeadingTo(contentView),
  66. avatarView.constraintAlignBottomTo(contentView, paddingBottom: BMC.defaultPadding, priority: .defaultLow),
  67. contentContainer.constraintToTrailingOf(avatarView, paddingLeading: BMC.defaultPadding),
  68. contentContainer.constraintAlignTrailingTo(contentView, paddingTrailing: BMC.defaultPadding),
  69. contentContainer.constraintAlignTopTo(contentView, paddingTop: BMC.defaultPadding),
  70. contentContainer.constraintAlignBottomTo(contentView, paddingBottom: BMC.defaultPadding),
  71. messageBackgroundContainer.constraintAlignLeadingTo(contentContainer, paddingLeading: BMC.containerPadding),
  72. messageBackgroundContainer.constraintAlignTopTo(contentContainer, paddingTop: BMC.containerPadding),
  73. messageBackgroundContainer.constraintAlignBottomTo(contentContainer, paddingBottom: BMC.containerPadding),
  74. messageBackgroundContainer.constraintAlignTrailingTo(contentContainer, paddingTrailing: BMC.containerPadding)
  75. ])
  76. }
  77. // update classes inheriting BaseMessageCell first before calling super.update(...)
  78. func update(msg: DcMsg, messageStyle: UIRectCorner) {
  79. topLabel.text = msg.fromContact.displayName
  80. avatarView.setName(msg.fromContact.displayName)
  81. avatarView.setColor(msg.fromContact.color)
  82. messageBackgroundContainer.update(rectCorners: messageStyle,
  83. color: msg.isFromCurrentSender ? DcColors.messagePrimaryColor : DcColors.messageSecondaryColor)
  84. if !msg.isInfo {
  85. bottomLabel.attributedText = getFormattedBottomLine(message: msg)
  86. }
  87. }
  88. func getFormattedBottomLine(message: DcMsg) -> NSAttributedString {
  89. var timestampAttributes: [NSAttributedString.Key: Any] = [
  90. .font: UIFont.systemFont(ofSize: 12),
  91. .foregroundColor: DcColors.grayDateColor,
  92. .paragraphStyle: NSParagraphStyle()
  93. ]
  94. let text = NSMutableAttributedString()
  95. if message.fromContactId == Int(DC_CONTACT_ID_SELF) {
  96. if let style = NSMutableParagraphStyle.default.mutableCopy() as? NSMutableParagraphStyle {
  97. style.alignment = .right
  98. timestampAttributes[.paragraphStyle] = style
  99. }
  100. text.append(NSAttributedString(string: message.formattedSentDate(), attributes: timestampAttributes))
  101. if message.showPadlock() {
  102. attachPadlock(to: text)
  103. }
  104. attachSendingState(message.state, to: text)
  105. return text
  106. }
  107. text.append(NSAttributedString(string: message.formattedSentDate(), attributes: timestampAttributes))
  108. if message.showPadlock() {
  109. attachPadlock(to: text)
  110. }
  111. return text
  112. }
  113. private func attachPadlock(to text: NSMutableAttributedString) {
  114. let imageAttachment = NSTextAttachment()
  115. imageAttachment.image = UIImage(named: "ic_lock")
  116. imageAttachment.image?.accessibilityIdentifier = String.localized("encrypted_message")
  117. let imageString = NSMutableAttributedString(attachment: imageAttachment)
  118. imageString.addAttributes([NSAttributedString.Key.baselineOffset: -1], range: NSRange(location: 0, length: 1))
  119. text.append(NSAttributedString(string: " "))
  120. text.append(imageString)
  121. }
  122. private func attachSendingState(_ state: Int, to text: NSMutableAttributedString) {
  123. let imageAttachment = NSTextAttachment()
  124. var offset = -4
  125. switch Int32(state) {
  126. case DC_STATE_OUT_PENDING, DC_STATE_OUT_PREPARING:
  127. imageAttachment.image = #imageLiteral(resourceName: "ic_hourglass_empty_white_36pt").scaleDownImage(toMax: 16)?.maskWithColor(color: DcColors.grayDateColor)
  128. imageAttachment.image?.accessibilityIdentifier = String.localized("a11y_delivery_status_sending")
  129. offset = -2
  130. case DC_STATE_OUT_DELIVERED:
  131. imageAttachment.image = #imageLiteral(resourceName: "ic_done_36pt").scaleDownImage(toMax: 18)
  132. imageAttachment.image?.accessibilityIdentifier = String.localized("a11y_delivery_status_delivered")
  133. case DC_STATE_OUT_MDN_RCVD:
  134. imageAttachment.image = #imageLiteral(resourceName: "ic_done_all_36pt").scaleDownImage(toMax: 18)
  135. imageAttachment.image?.accessibilityIdentifier = String.localized("a11y_delivery_status_read")
  136. text.append(NSAttributedString(string: " "))
  137. case DC_STATE_OUT_FAILED:
  138. imageAttachment.image = #imageLiteral(resourceName: "ic_error_36pt").scaleDownImage(toMax: 16)
  139. imageAttachment.image?.accessibilityIdentifier = String.localized("a11y_delivery_status_error")
  140. offset = -2
  141. default:
  142. imageAttachment.image = nil
  143. }
  144. let imageString = NSMutableAttributedString(attachment: imageAttachment)
  145. imageString.addAttributes([.baselineOffset: offset],
  146. range: NSRange(location: 0, length: 1))
  147. text.append(imageString)
  148. }
  149. override public func prepareForReuse() {
  150. textLabel?.text = nil
  151. textLabel?.attributedText = nil
  152. topLabel.text = nil
  153. avatarView.reset()
  154. messageBackgroundContainer.prepareForReuse()
  155. }
  156. }