LNUserSearchOverviewListView.swift 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. //
  2. // LNUserSearchOverviewListView.swift
  3. // Lanu
  4. //
  5. // Created by OpenAI Codex on 2026/3/19.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. import MJRefresh
  11. protocol LNUserSearchOverviewListViewDelegate: NSObject {
  12. func onUserSearchOverviewListView(view: LNUserSearchOverviewListView, didClickMore tab: LNUserSearchTab)
  13. }
  14. class LNUserSearchOverviewListView: UIView {
  15. private let emptyView = LNNoMoreDataView()
  16. private let scrollView = UIScrollView()
  17. private let userMaxShowCount = 3
  18. private var userViews: [LNUserSearchUserCardView] = []
  19. private let userSectionView = UIView()
  20. private let roomMaxShowCount = 2
  21. private let roomStackView = LNMultiLineStackView()
  22. private var roomViews: [LNUserSearchRoomCardView] = []
  23. private let roomSectionView = UIView()
  24. private var curKeyword: String?
  25. weak var delegate: LNUserSearchOverviewListViewDelegate?
  26. override init(frame: CGRect) {
  27. super.init(frame: frame)
  28. setupViews()
  29. }
  30. func search(_ keyword: String) {
  31. userSectionView.isHidden = true
  32. roomSectionView.isHidden = true
  33. curKeyword = keyword
  34. reload()
  35. }
  36. required init?(coder: NSCoder) {
  37. fatalError("init(coder:) has not been implemented")
  38. }
  39. }
  40. extension LNUserSearchOverviewListView {
  41. private func reload() {
  42. guard let curKeyword else { return }
  43. LNGameMateManager.shared.mixSearch(keyword: curKeyword) { [weak self] res in
  44. guard let self else { return }
  45. scrollView.mj_header?.endRefreshing()
  46. guard let res else { return }
  47. emptyView.hide()
  48. if res.playmate.isEmpty {
  49. userSectionView.isHidden = true
  50. } else {
  51. userSectionView.isHidden = false
  52. for (index, userView) in userViews.enumerated() {
  53. if index < res.playmate.count {
  54. userView.update(res.playmate[index])
  55. userView.isHidden = false
  56. } else {
  57. userView.isHidden = true
  58. }
  59. }
  60. }
  61. if res.rooms.isEmpty {
  62. roomSectionView.isHidden = true
  63. } else {
  64. roomSectionView.isHidden = false
  65. var views: [UIView] = []
  66. for (index, roomView) in roomViews.enumerated() {
  67. if index < res.rooms.count {
  68. roomView.update(res.rooms[index])
  69. views.append(roomView)
  70. }
  71. }
  72. roomStackView.update(views)
  73. }
  74. if res.playmate.isEmpty, res.rooms.isEmpty {
  75. emptyView.showNoData(tips: .init(key: "A00244"))
  76. }
  77. }
  78. }
  79. }
  80. extension LNUserSearchOverviewListView {
  81. private func setupViews() {
  82. backgroundColor = .white
  83. let header = MJRefreshNormalHeader { [weak self] in
  84. guard let self else { return }
  85. reload()
  86. }
  87. header.lastUpdatedTimeLabel?.isHidden = true
  88. header.stateLabel?.isHidden = true
  89. scrollView.mj_header = header
  90. scrollView.showsVerticalScrollIndicator = false
  91. addSubview(scrollView)
  92. scrollView.snp.makeConstraints { make in
  93. make.edges.equalToSuperview()
  94. }
  95. let stackView = UIStackView()
  96. stackView.axis = .vertical
  97. scrollView.addSubview(stackView)
  98. stackView.snp.makeConstraints { make in
  99. make.edges.equalToSuperview()
  100. make.width.equalTo(scrollView)
  101. }
  102. stackView.addArrangedSubview(buildUserView())
  103. stackView.addArrangedSubview(buildRoomView())
  104. scrollView.addSubview(emptyView)
  105. emptyView.snp.makeConstraints { make in
  106. make.centerX.equalToSuperview()
  107. make.centerY.equalToSuperview().multipliedBy(0.6)
  108. }
  109. }
  110. private func buildHeader(title: String, action: @escaping () -> Void) -> UIView {
  111. let container = UIView()
  112. let titleLabel = UILabel()
  113. titleLabel.text = title
  114. titleLabel.font = .heading_h5
  115. titleLabel.textColor = .text_5
  116. container.addSubview(titleLabel)
  117. titleLabel.snp.makeConstraints { make in
  118. make.verticalEdges.equalToSuperview()
  119. make.leading.equalToSuperview().offset(16)
  120. }
  121. let config = UIImage.SymbolConfiguration(pointSize: 9)
  122. let button = UIButton(type: .system)
  123. button.setTitle(.init(key: "A00048"), for: .normal)
  124. button.setTitleColor(.text_4, for: .normal)
  125. button.titleLabel?.font = .body_xs
  126. button.semanticContentAttribute = .forceRightToLeft
  127. button.setImage(UIImage(systemName: "chevron.backward", withConfiguration: config), for: .normal)
  128. button.tintColor = .text_4
  129. button.imageView?.contentMode = .scaleAspectFit
  130. button.addAction(UIAction(handler: { _ in action() }), for: .touchUpInside)
  131. container.addSubview(button)
  132. button.snp.makeConstraints { make in
  133. make.trailing.equalToSuperview().offset(-16)
  134. make.centerY.equalToSuperview()
  135. }
  136. return container
  137. }
  138. private func buildUserView() -> UIView {
  139. let userHeader = buildHeader(title: .init(key: "A00366")) { [weak self] in
  140. guard let self else { return }
  141. delegate?.onUserSearchOverviewListView(view: self, didClickMore: .user)
  142. }
  143. userSectionView.addSubview(userHeader)
  144. userHeader.snp.makeConstraints { make in
  145. make.horizontalEdges.top.equalToSuperview()
  146. }
  147. let userStackView = UIStackView()
  148. userStackView.axis = .vertical
  149. userSectionView.addSubview(userStackView)
  150. userStackView.snp.makeConstraints { make in
  151. make.horizontalEdges.equalToSuperview()
  152. make.top.equalTo(userHeader.snp.bottom).offset(5)
  153. make.bottom.equalToSuperview()
  154. }
  155. for _ in 0..<userMaxShowCount {
  156. let userView = LNUserSearchUserCardView()
  157. userStackView.addArrangedSubview(userView)
  158. userViews.append(userView)
  159. }
  160. return userSectionView
  161. }
  162. private func buildRoomView() -> UIView {
  163. let roomHeader = buildHeader(title: .init(key: "A00367")) { [weak self] in
  164. guard let self else { return }
  165. delegate?.onUserSearchOverviewListView(view: self, didClickMore: .rooms)
  166. }
  167. roomSectionView.addSubview(roomHeader)
  168. roomHeader.snp.makeConstraints { make in
  169. make.horizontalEdges.top.equalToSuperview()
  170. }
  171. roomStackView.axis = .horizontal
  172. roomStackView.itemSpacing = 13
  173. roomStackView.columns = 2
  174. roomStackView.itemDistribution = .equalSpacing
  175. roomSectionView.addSubview(roomStackView)
  176. roomStackView.snp.makeConstraints { make in
  177. make.horizontalEdges.equalToSuperview().inset(16)
  178. make.bottom.equalToSuperview()
  179. make.top.equalTo(roomHeader.snp.bottom).offset(16)
  180. }
  181. for _ in 0..<roomMaxShowCount {
  182. let roomView = LNUserSearchRoomCardView()
  183. roomViews.append(roomView)
  184. }
  185. roomStackView.update(roomViews)
  186. return roomSectionView
  187. }
  188. }