// // LNIMChatBaseMessageCell.swift // Lanu // // Created by OneeChan on 2025/12/5. // import Foundation import UIKit import SnapKit class LNIMChatBaseMessageCell: UITableViewCell { let container = UIView() private let bubbleView = UIView() private let peerArrow = UIImageView() private let myArrow = UIImageView() private let holder = UIView() private let stackView = UIStackView() private let timeLabel = UILabel() private let readStateIc = UIImageView() private let sendStateIc = UIImageView() private var curItem: LNIMMessageData? private weak var viewModel: LNIMChatViewModel? override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) setupViews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } func update(_ data: LNIMMessageData, viewModel: LNIMChatViewModel) { if data.isSelf == true { bubbleView.snp.remakeConstraints { make in make.trailing.equalToSuperview() make.top.bottom.equalToSuperview() } peerArrow.isHidden = true myArrow.isHidden = false holder.layer.maskedCorners = [.layerMinXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner] holder.backgroundColor = .init(hex: "#E6FFFA") stackView.addArrangedSubview(readStateIc) readStateIc.image = data.readReceipt?.isPeerRead == true ? .icImChatRead : .icImChatUnread } else { bubbleView.snp.remakeConstraints { make in make.leading.equalToSuperview() make.top.bottom.equalToSuperview() } peerArrow.isHidden = false myArrow.isHidden = true holder.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMaxYCorner, .layerMaxXMaxYCorner] holder.backgroundColor = .fill stackView.removeArrangedSubview(readStateIc) readStateIc.removeFromSuperview() } sendStateIc.isHidden = data.imMessage.isSelf != true || data.imMessage.status != .MSG_STATUS_SEND_FAIL if let time = data.imMessage.timestamp { timeLabel.text = time.tencentIMTimeDesc } curItem = data self.viewModel = viewModel } func setupViews() { backgroundColor = .clear contentView.addSubview(bubbleView) bubbleView.snp.makeConstraints { make in make.trailing.equalToSuperview() make.top.bottom.equalToSuperview() } peerArrow.image = .icImChatBubblePeer bubbleView.addSubview(peerArrow) peerArrow.snp.makeConstraints { make in make.top.equalToSuperview() make.leading.equalToSuperview().offset(16) } let holder = buildHolder() bubbleView.addSubview(holder) holder.snp.makeConstraints { make in make.top.equalToSuperview() make.bottom.equalToSuperview().offset(-8) make.leading.equalTo(peerArrow.snp.trailing) make.width.lessThanOrEqualTo(282) } myArrow.image = .icImChatBubbleMe bubbleView.addSubview(myArrow) myArrow.snp.makeConstraints { make in make.leading.equalTo(holder.snp.trailing) make.top.equalToSuperview() make.trailing.equalToSuperview().offset(-16) } sendStateIc.image = .icImChatSendFailed bubbleView.addSubview(sendStateIc) sendStateIc.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalTo(holder.snp.leading).offset(-8) } sendStateIc.onTap { [weak self] in guard let self else { return } guard let curItem else { return } viewModel?.resendMessage(message: curItem) } } } extension LNIMChatBaseMessageCell { private func buildHolder() -> UIView { stackView.axis = .horizontal stackView.spacing = 4 holder.addSubview(stackView) stackView.snp.makeConstraints { make in make.trailing.bottom.equalToSuperview().offset(-8) make.leading.greaterThanOrEqualToSuperview().offset(8) } timeLabel.font = .body_xs timeLabel.textColor = .text_3 stackView.addArrangedSubview(timeLabel) readStateIc.image = .icImChatUnread stackView.addArrangedSubview(readStateIc) holder.layer.cornerRadius = 12 holder.addSubview(container) container.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(8) make.top.equalToSuperview().offset(8) make.bottom.equalTo(stackView.snp.top).offset(-4) } return holder } }