// // LNUserSearchOverviewListView.swift // Lanu // // Created by OpenAI Codex on 2026/3/19. // import Foundation import UIKit import SnapKit import MJRefresh protocol LNUserSearchOverviewListViewDelegate: NSObject { func onUserSearchOverviewListView(view: LNUserSearchOverviewListView, didClickMore tab: LNUserSearchTab) } class LNUserSearchOverviewListView: UIView { private let emptyView = LNNoMoreDataView() private let scrollView = UIScrollView() private let userMaxShowCount = 3 private var userViews: [LNUserSearchUserCardView] = [] private let userSectionView = UIView() private let roomMaxShowCount = 2 private let roomStackView = LNMultiLineStackView() private var roomViews: [LNUserSearchRoomCardView] = [] private let roomSectionView = UIView() private var curKeyword: String? weak var delegate: LNUserSearchOverviewListViewDelegate? override init(frame: CGRect) { super.init(frame: frame) setupViews() } func search(_ keyword: String) { userSectionView.isHidden = true roomSectionView.isHidden = true curKeyword = keyword reload() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } } extension LNUserSearchOverviewListView { private func reload() { guard let curKeyword else { return } LNGameMateManager.shared.mixSearch(keyword: curKeyword) { [weak self] res in guard let self else { return } scrollView.mj_header?.endRefreshing() guard let res else { return } emptyView.hide() if res.playmate.isEmpty { userSectionView.isHidden = true } else { userSectionView.isHidden = false for (index, userView) in userViews.enumerated() { if index < res.playmate.count { userView.update(res.playmate[index]) userView.isHidden = false } else { userView.isHidden = true } } } if res.rooms.isEmpty { roomSectionView.isHidden = true } else { roomSectionView.isHidden = false var views: [UIView] = [] for (index, roomView) in roomViews.enumerated() { if index < res.rooms.count { roomView.update(res.rooms[index]) views.append(roomView) } } roomStackView.update(views) } if res.playmate.isEmpty, res.rooms.isEmpty { emptyView.showNoData(tips: .init(key: "A00244")) } } } } extension LNUserSearchOverviewListView { private func setupViews() { backgroundColor = .white let header = MJRefreshNormalHeader { [weak self] in guard let self else { return } reload() } header.lastUpdatedTimeLabel?.isHidden = true header.stateLabel?.isHidden = true scrollView.mj_header = header scrollView.showsVerticalScrollIndicator = false addSubview(scrollView) scrollView.snp.makeConstraints { make in make.edges.equalToSuperview() } let stackView = UIStackView() stackView.axis = .vertical scrollView.addSubview(stackView) stackView.snp.makeConstraints { make in make.edges.equalToSuperview() make.width.equalTo(scrollView) } stackView.addArrangedSubview(buildUserView()) stackView.addArrangedSubview(buildRoomView()) scrollView.addSubview(emptyView) emptyView.snp.makeConstraints { make in make.centerX.equalToSuperview() make.centerY.equalToSuperview().multipliedBy(0.6) } } private func buildHeader(title: String, action: @escaping () -> Void) -> UIView { let container = UIView() let titleLabel = UILabel() titleLabel.text = title titleLabel.font = .heading_h5 titleLabel.textColor = .text_5 container.addSubview(titleLabel) titleLabel.snp.makeConstraints { make in make.verticalEdges.equalToSuperview() make.leading.equalToSuperview().offset(16) } let config = UIImage.SymbolConfiguration(pointSize: 9) let button = UIButton(type: .system) button.setTitle(.init(key: "A00048"), for: .normal) button.setTitleColor(.text_4, for: .normal) button.titleLabel?.font = .body_xs button.semanticContentAttribute = .forceRightToLeft button.setImage(UIImage(systemName: "chevron.backward", withConfiguration: config), for: .normal) button.tintColor = .text_4 button.imageView?.contentMode = .scaleAspectFit button.addAction(UIAction(handler: { _ in action() }), for: .touchUpInside) container.addSubview(button) button.snp.makeConstraints { make in make.trailing.equalToSuperview().offset(-16) make.centerY.equalToSuperview() } return container } private func buildUserView() -> UIView { let userHeader = buildHeader(title: .init(key: "A00366")) { [weak self] in guard let self else { return } delegate?.onUserSearchOverviewListView(view: self, didClickMore: .user) } userSectionView.addSubview(userHeader) userHeader.snp.makeConstraints { make in make.horizontalEdges.top.equalToSuperview() } let userStackView = UIStackView() userStackView.axis = .vertical userSectionView.addSubview(userStackView) userStackView.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview() make.top.equalTo(userHeader.snp.bottom).offset(5) make.bottom.equalToSuperview() } for _ in 0.. UIView { let roomHeader = buildHeader(title: .init(key: "A00367")) { [weak self] in guard let self else { return } delegate?.onUserSearchOverviewListView(view: self, didClickMore: .rooms) } roomSectionView.addSubview(roomHeader) roomHeader.snp.makeConstraints { make in make.horizontalEdges.top.equalToSuperview() } roomStackView.axis = .horizontal roomStackView.itemSpacing = 13 roomStackView.columns = 2 roomStackView.itemDistribution = .equalSpacing roomSectionView.addSubview(roomStackView) roomStackView.snp.makeConstraints { make in make.horizontalEdges.equalToSuperview().inset(16) make.bottom.equalToSuperview() make.top.equalTo(roomHeader.snp.bottom).offset(16) } for _ in 0..