LNFeedCommentListPanel.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. //
  2. // LNFeedCommentListPanel.swift
  3. // Gami
  4. //
  5. // Created by OneeChan on 2026/3/3.
  6. //
  7. import Foundation
  8. import UIKit
  9. import SnapKit
  10. import MJRefresh
  11. class LNFeedCommentListPanel: LNPopupView {
  12. private let titleLabel = UILabel()
  13. private let tableView = UITableView()
  14. private let tipsLabel = UILabel()
  15. private var feedId: String?
  16. private var comments: [LNFeedCommentVO] = []
  17. private var total = 0
  18. private var nextTag: String?
  19. override init(frame: CGRect) {
  20. super.init(frame: frame)
  21. setupViews()
  22. }
  23. func load(_ id: String) {
  24. feedId = id
  25. loadList()
  26. }
  27. required init?(coder: NSCoder) {
  28. fatalError("init(coder:) has not been implemented")
  29. }
  30. }
  31. extension LNFeedCommentListPanel {
  32. private func loadList() {
  33. guard let feedId else { return }
  34. LNFeedManager.shared.getFeedCommentList(id: feedId, next: nextTag) { [weak self] res in
  35. guard let self else { return }
  36. if let res {
  37. if nextTag?.isEmpty != false {
  38. comments.removeAll()
  39. tableView.mj_header?.endRefreshing()
  40. }
  41. comments.append(contentsOf: res.list)
  42. self.nextTag = nextTag
  43. tableView.reloadData()
  44. titleLabel.text = .init(key: "A00304", res.total)
  45. total = res.total
  46. }
  47. if nextTag?.isEmpty != false {
  48. tableView.mj_footer?.endRefreshingWithNoMoreData()
  49. } else {
  50. tableView.mj_footer?.endRefreshing()
  51. }
  52. }
  53. }
  54. }
  55. extension LNFeedCommentListPanel: UITableViewDataSource {
  56. func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  57. comments.count
  58. }
  59. func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  60. let cell = tableView.dequeueReusableCell(withIdentifier: LNFeedCommentCell.className, for: indexPath) as! LNFeedCommentCell
  61. cell.update(comments[indexPath.row])
  62. return cell
  63. }
  64. }
  65. extension LNFeedCommentListPanel {
  66. private func setupViews() {
  67. containerHeight = .percent(0.7)
  68. let header = buildHeader()
  69. container.addSubview(header)
  70. header.snp.makeConstraints { make in
  71. make.horizontalEdges.equalToSuperview()
  72. make.top.equalToSuperview()
  73. }
  74. let menu = buildInputView()
  75. container.addSubview(menu)
  76. menu.snp.makeConstraints { make in
  77. make.horizontalEdges.equalToSuperview()
  78. make.bottom.equalToSuperview().offset(commonBottomInset)
  79. }
  80. let listView = buildListView()
  81. container.addSubview(listView)
  82. listView.snp.makeConstraints { make in
  83. make.top.equalTo(header.snp.bottom)
  84. make.horizontalEdges.equalToSuperview()
  85. make.bottom.equalTo(menu.snp.top)
  86. }
  87. }
  88. private func buildHeader() -> UIView {
  89. let header = UIView()
  90. header.snp.makeConstraints { make in
  91. make.height.equalTo(52)
  92. }
  93. titleLabel.font = .body_m
  94. titleLabel.textColor = .text_5
  95. header.addSubview(titleLabel)
  96. titleLabel.snp.makeConstraints { make in
  97. make.leading.equalToSuperview().offset(16)
  98. make.centerY.equalToSuperview()
  99. }
  100. let close = UIButton()
  101. close.setImage(.init(systemName: "xmark"), for: .normal)
  102. close.tintColor = .text_4
  103. close.addAction(UIAction(handler: { [weak self] _ in
  104. guard let self else { return }
  105. dismiss()
  106. }), for: .touchUpInside)
  107. header.addSubview(close)
  108. close.snp.makeConstraints { make in
  109. make.centerY.equalToSuperview()
  110. make.trailing.equalToSuperview().offset(-16)
  111. make.width.height.equalTo(24)
  112. }
  113. return header
  114. }
  115. private func buildInputView() -> UIView {
  116. let container = UIView()
  117. container.onTap { [weak self] in
  118. guard let self else { return }
  119. guard let feedId else { return }
  120. let panel = LNCommonInputPanel()
  121. panel.maxInput = LNFeedManager.feedCommentMaxInput
  122. panel.handler = { [weak self] comment in
  123. guard let self else { return }
  124. LNFeedManager.shared.sendFeedComment(id: feedId, content: comment) { [weak self] success in
  125. guard let self else { return }
  126. guard success else { return }
  127. let item = LNFeedCommentVO()
  128. item.avatar = myUserInfo.avatar
  129. item.nickname = myUserInfo.nickname
  130. item.textContent = comment
  131. item.createdAt = Int(curTime * 1_000)
  132. comments.insert(item, at: 0)
  133. tableView.reloadData()
  134. total += 1
  135. titleLabel.text = .init(key: "A00304", total)
  136. LNFeedManager.shared.notifyFeedCommentChanged(id: feedId, count: total)
  137. }
  138. }
  139. panel.popup()
  140. }
  141. container.snp.makeConstraints { make in
  142. make.height.equalTo(58)
  143. }
  144. let inputView = UIView()
  145. inputView.backgroundColor = .fill_2
  146. inputView.layer.cornerRadius = 19
  147. inputView.isUserInteractionEnabled = false
  148. container.addSubview(inputView)
  149. inputView.snp.makeConstraints { make in
  150. make.horizontalEdges.equalToSuperview().inset(12)
  151. make.height.equalTo(38)
  152. make.centerY.equalToSuperview()
  153. }
  154. let editIc = UIImageView()
  155. editIc.image = .icImChatMenuRemark.withTintColor(.text_2)
  156. inputView.addSubview(editIc)
  157. editIc.snp.makeConstraints { make in
  158. make.leading.equalToSuperview().offset(10)
  159. make.centerY.equalToSuperview()
  160. make.width.height.equalTo(24)
  161. }
  162. tipsLabel.font = .body_m
  163. tipsLabel.textColor = .text_2
  164. tipsLabel.text = .init(key: "A00084")
  165. inputView.addSubview(tipsLabel)
  166. tipsLabel.snp.makeConstraints { make in
  167. make.leading.equalTo(editIc.snp.trailing).offset(4)
  168. make.trailing.equalToSuperview().offset(-10)
  169. make.centerY.equalToSuperview()
  170. }
  171. return container
  172. }
  173. private func buildListView() -> UIView {
  174. tableView.register(LNFeedCommentCell.self, forCellReuseIdentifier: LNFeedCommentCell.className)
  175. tableView.showsVerticalScrollIndicator = false
  176. tableView.showsHorizontalScrollIndicator = false
  177. tableView.separatorStyle = .none
  178. tableView.dataSource = self
  179. tableView.allowsSelection = false
  180. let header = MJRefreshNormalHeader { [weak self] in
  181. guard let self else { return }
  182. self.nextTag = nil
  183. self.loadList()
  184. }
  185. header.lastUpdatedTimeLabel?.isHidden = true
  186. header.stateLabel?.isHidden = true
  187. tableView.mj_header = header
  188. let footer = MJRefreshAutoNormalFooter { [weak self] in
  189. guard let self else { return }
  190. self.loadList()
  191. }
  192. footer.setTitle("", for: .noMoreData)
  193. footer.setTitle(.init(key: "A00046"), for: .idle)
  194. footer.endRefreshingCompletionBlock = { [weak self] in
  195. guard let self else { return }
  196. loadList()
  197. }
  198. tableView.mj_footer = footer
  199. return tableView
  200. }
  201. }