// // LNIMChatGameMateOrderView.swift // Lanu // // Created by OneeChan on 2025/12/31. // import Foundation import UIKit import SnapKit import Combine class LNIMChatGameMateOrderView: UIView { private let stackView = UIStackView() private let gameIc = UIImageView() private let titleLabel = UILabel() private let titleArrow = UIImageView.arrowImageView(size: 12) private let descLabel = UILabel() private let contentArrow = UIImageView.arrowImageView(size: 12) private let replyView = UIView() private let replyRemainLabel = UILabel() private var countDownTimer: Timer? private let goCallView = UIView() private let actionView = UIView() private let extraView = UIView() private let extraLabel = UILabel() private var curOrder: LNUnfinishedOrderVO? weak var viewModel: LNIMChatViewModel? { didSet { viewModel?.$myOrders.sink { [weak self] list in guard let self else { return } isHidden = list.isEmpty == true if let first = list.first { update(first) } }.store(in: &cancellables) } } override init(frame: CGRect) { super.init(frame: frame) setupViews() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension LNIMChatGameMateOrderView { private func startCountDown() { stopCountDown() updateRemain() let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in guard let self else { return } if updateRemain() { stopCountDown() viewModel?.getUnfinishOrder() } } RunLoop.current.add(timer, forMode: .common) countDownTimer = timer } private func stopCountDown() { countDownTimer?.invalidate() countDownTimer = nil } @discardableResult private func updateRemain() -> Bool { guard let curOrder else { return true } let remain = 3600 - (Int(curTime) - curOrder.createTime / 1_000) replyRemainLabel.text = remain.timeCountDisplay return remain <= 0 } } extension LNIMChatGameMateOrderView { private func update(_ order: LNUnfinishedOrderVO) { curOrder = order let isCreator = order.buyerUserNo.isMyUid gameIc.sd_setImage(with: URL(string: order.categoryIcon)) descLabel.text = "\(order.bizCategoryName)/\(order.unit) x \(order.purchaseQty)" extraLabel.text = .init(key: "A00056", order.customerRemark) actionView.isHidden = true extraView.isHidden = true replyView.isHidden = true goCallView.isHidden = true titleArrow.isHidden = true contentArrow.isHidden = true stopCountDown() switch order.status { case .created, .waitingForAccept: titleLabel.text = .init(key: "A00078") titleArrow.isHidden = false replyView.isHidden = false let remain = 3600 - (Int(curTime) - order.createTime / 1_000) replyRemainLabel.text = remain.timeCountDisplay startCountDown() if !isCreator { actionView.isHidden = false } case .accepted: titleLabel.text = .init(key: "A00079") contentArrow.isHidden = false if !isCreator, !order.customerRemark.isEmpty { extraView.isHidden = false } case .servicing: titleLabel.text = .init(key: "A00080") titleArrow.isHidden = false goCallView.isHidden = false if !isCreator, !order.customerRemark.isEmpty { extraView.isHidden = false } case .completed, .refunded, .rejected, .serviceDone, .cancelled: break } stackView.snp.updateConstraints { make in make.verticalEdges.equalToSuperview().inset((!extraView.isHidden || !actionView.isHidden) ? 6 : 11) } } private func setupViews() { let container = UIView() container.backgroundColor = .fill container.layer.cornerRadius = 12 addSubview(container) container.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(16) make.bottom.equalToSuperview() make.top.equalToSuperview().offset(8) } stackView.axis = .vertical stackView.spacing = 6 container.addSubview(stackView) stackView.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(16) make.verticalEdges.equalToSuperview().inset(11) } let orderView = buildOrderView() stackView.addArrangedSubview(orderView) let actionView = buildActionView() stackView.addArrangedSubview(actionView) let extraView = buildExtraView() stackView.addArrangedSubview(extraView) } private func buildOrderView() -> UIView { let stackView = UIStackView() stackView.spacing = 16 stackView.snp.makeConstraints { make in make.height.equalTo(50) } let infoView = buildInfoView() stackView.addArrangedSubview(infoView) let replyView = buildReplyView() stackView.addArrangedSubview(replyView) let callView = buildCallView() stackView.addArrangedSubview(callView) contentArrow.tintColor = .text_4 stackView.addSubview(contentArrow) contentArrow.snp.makeConstraints { make in make.centerY.equalToSuperview() make.trailing.equalToSuperview() } return stackView } private func buildInfoView() -> UIView { let container = UIView() container.onTap { [weak self] in guard let self else { return } if curOrder?.buyerUserNo.isMyUid != false { pushToOrderList() } else { pushToOrderRecord() } } container.addSubview(gameIc) gameIc.snp.makeConstraints { make in make.leading.equalToSuperview() make.verticalEdges.equalToSuperview() make.width.height.equalTo(50) } let textView = UIView() container.addSubview(textView) textView.snp.makeConstraints { make in make.centerY.equalToSuperview() make.leading.equalTo(gameIc.snp.trailing).offset(1) make.trailing.lessThanOrEqualToSuperview() } titleLabel.font = .heading_h5 titleLabel.textColor = .text_5 textView.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.leading.equalToSuperview() make.top.equalToSuperview() } titleArrow.tintColor = .text_4 textView.addSubview(titleArrow) titleArrow.snp.makeConstraints { make in make.leading.equalTo(titleLabel.snp.trailing).offset(5) make.centerY.equalTo(titleLabel) make.trailing.lessThanOrEqualToSuperview() } descLabel.font = .body_s descLabel.textColor = .text_3 textView.addSubview(descLabel) descLabel.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.bottom.equalToSuperview() make.top.equalTo(titleLabel.snp.bottom).offset(2) } return container } private func buildReplyView() -> UIView { replyView.isHidden = true let replyTitleLabel = UILabel() replyTitleLabel.font = .body_s replyTitleLabel.text = .init(key: "A00081") replyTitleLabel.textColor = .text_5 replyTitleLabel.textAlignment = .center replyView.addSubview(replyTitleLabel) replyTitleLabel.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.top.equalToSuperview() } replyRemainLabel.font = .heading_h5 replyRemainLabel.textColor = .text_5 replyView.addSubview(replyRemainLabel) replyRemainLabel.snp.makeConstraints { make in make.centerX.equalToSuperview() make.bottom.equalToSuperview() make.top.equalTo(replyTitleLabel.snp.bottom).offset(1) make.leading.greaterThanOrEqualToSuperview() } return replyView } private func buildCallView() -> UIView { goCallView.isHidden = true goCallView.onTap { [weak self] in guard let self else { return } guard let uid = viewModel?.userId else { return } LNIMManager.shared.makeVoiceCall(uid: uid) } let callButton = UIButton() callButton.setTitle(.init(key: "C00016"), for: .normal) callButton.contentEdgeInsets = .init(top: 0, left: 17, bottom: 0, right: 17) callButton.titleLabel?.font = .heading_h5 callButton.setTitleColor(.text_1, for: .normal) callButton.setBackgroundImage(.primary_8, for: .normal) callButton.layer.cornerRadius = 12 callButton.clipsToBounds = true callButton.setContentHuggingPriority(.required, for: .horizontal) callButton.setContentCompressionResistancePriority(.required, for: .horizontal) callButton.snp.makeConstraints { make in make.height.equalTo(24) } callButton.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } guard let uid = viewModel?.userId else { return } LNIMManager.shared.makeVoiceCall(uid: uid) }), for: .touchUpInside) goCallView.addSubview(callButton) callButton.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.centerY.equalToSuperview() } return goCallView } private func buildActionView() -> UIView { actionView.snp.makeConstraints { make in make.height.equalTo(40) } let reject = UIButton() reject.setTitle(.init(key: "A00082"), for: .normal) reject.setTitleColor(.text_4, for: .normal) reject.titleLabel?.font = .body_s reject.contentEdgeInsets = .init(top: 7, left: 40, bottom: 7, right: 40) reject.layer.cornerRadius = 15 reject.layer.borderWidth = 1 reject.layer.borderColor = .text_3 reject.setContentHuggingPriority(.defaultHigh, for: .horizontal) reject.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } guard let curOrder else { return } LNOrderManager.shared.rejectOrder(orderId: curOrder.orderId) { success in } }), for: .touchUpInside) actionView.addSubview(reject) reject.snp.makeConstraints { make in make.leading.equalToSuperview() make.centerY.equalToSuperview() make.height.equalTo(30) } let accept = UIButton() accept.setTitle(.init(key: "A00083"), for: .normal) accept.setTitleColor(.text_1, for: .normal) accept.titleLabel?.font = .heading_h5 accept.layer.cornerRadius = 15 accept.clipsToBounds = true accept.setBackgroundImage(.primary_8, for: .normal) accept.addAction(UIAction(handler: { [weak self] _ in guard let self else { return } guard let curOrder else { return } LNOrderManager.shared.acceptOrder(orderId: curOrder.orderId) { success in } }), for: .touchUpInside) actionView.addSubview(accept) accept.snp.makeConstraints { make in make.centerY.equalToSuperview() make.height.equalTo(30) make.leading.equalTo(reject.snp.trailing).offset(12) make.trailing.equalToSuperview() } return actionView } private func buildExtraView() -> UIView { let line = UIView() line.backgroundColor = .fill_2 extraView.addSubview(line) line.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(-4) make.top.equalToSuperview() make.height.equalTo(0.5) } extraLabel.font = .body_xs extraLabel.textColor = .text_4 extraLabel.numberOfLines = 0 extraView.addSubview(extraLabel) extraLabel.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.top.equalTo(line.snp.bottom).offset(5) make.bottom.equalToSuperview().offset(-2) } return extraView } } #if DEBUG import SwiftUI struct LNIMChatGameMateOrderViewPreview: UIViewRepresentable { func makeUIView(context: Context) -> some UIView { let container = UIView() container.backgroundColor = .lightGray let view = LNIMChatGameMateOrderView() container.addSubview(view) view.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.centerY.equalToSuperview() } return container } func updateUIView(_ uiView: UIViewType, context: Context) { } } #Preview(body: { LNIMChatGameMateOrderViewPreview() }) #endif