UserListView.swift 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642
  1. //
  2. // UserListView.swift
  3. // TUIRoomKit
  4. //
  5. // Created by janejntang on 2023/1/4.
  6. // Copyright © 2023 Tencent. All rights reserved.
  7. //
  8. import Foundation
  9. import Factory
  10. import Combine
  11. import RTCRoomEngine
  12. class UserListView: UIView {
  13. let viewModel: UserListViewModel
  14. let memberLabel: UILabel = {
  15. let label = UILabel()
  16. label.textColor = UIColor(0xD5E0F2)
  17. label.font = UIFont(name: "PingFangSC-Regular", size: 16)
  18. return label
  19. }()
  20. let searchBar: UISearchBar = {
  21. let searchBar = UISearchBar()
  22. searchBar.placeholder = .searchMemberText
  23. searchBar.setBackgroundImage(UIColor(0x17181F).trans2Image(), for: .top, barMetrics: .default)
  24. if #available(iOS 13, *) {
  25. searchBar.searchTextField.textColor = UIColor(0xB2BBD1)
  26. searchBar.searchTextField.tintColor = UIColor(0xB2BBD1).withAlphaComponent(0.3)
  27. searchBar.searchTextField.layer.cornerRadius = 6
  28. searchBar.searchTextField.font = UIFont.systemFont(ofSize: 14, weight: .medium)
  29. } else {
  30. if let textField = searchBar.value(forKey: "searchField") as? UITextField {
  31. textField.textColor = UIColor(0xB2BBD1)
  32. textField.tintColor = UIColor(0xB2BBD1).withAlphaComponent(0.3)
  33. textField.layer.cornerRadius = 6
  34. textField.font = UIFont.systemFont(ofSize: 14, weight: .medium)
  35. }
  36. }
  37. return searchBar
  38. }()
  39. let searchControl: UIControl = {
  40. let view = UIControl()
  41. view.backgroundColor = .clear
  42. view.isHidden = true
  43. return view
  44. }()
  45. let listStateView: UIView = {
  46. let view = UIView(frame: .zero)
  47. view.backgroundColor = UIColor(0x4F586B).withAlphaComponent(0.3)
  48. view.layer.cornerRadius = 6
  49. return view
  50. }()
  51. let haveEnteredButton: UIButton = {
  52. let button = UIButton(type: .custom)
  53. button.setTitleColor(UIColor(0xD5E0F2), for: .normal)
  54. button.titleLabel?.font = UIFont(name: "PingFangSC-Regular", size: 14)
  55. button.setBackgroundImage(UIColor(0xD5E0F2).withAlphaComponent(0.3).trans2Image(), for: .selected)
  56. button.setBackgroundImage(UIColor.clear.trans2Image(), for: .normal)
  57. button.layer.cornerRadius = 6
  58. button.clipsToBounds = true
  59. button.isSelected = true
  60. return button
  61. }()
  62. let onStageButton: UIButton = {
  63. let button = UIButton(type: .custom)
  64. button.setTitleColor(UIColor(0xD5E0F2), for: .normal)
  65. button.titleLabel?.font = UIFont(name: "PingFangSC-Regular", size: 14)
  66. button.setBackgroundImage(UIColor(0xD5E0F2).withAlphaComponent(0.3).trans2Image(), for: .selected)
  67. button.setBackgroundImage(UIColor.clear.trans2Image(), for: .normal)
  68. button.layer.cornerRadius = 6
  69. button.clipsToBounds = true
  70. button.isSelected = true
  71. return button
  72. }()
  73. let offStageButton: UIButton = {
  74. let button = UIButton(type: .custom)
  75. button.setTitleColor(UIColor(0xD5E0F2), for: .normal)
  76. button.titleLabel?.font = UIFont(name: "PingFangSC-Regular", size: 14)
  77. button.setBackgroundImage(UIColor(0xD5E0F2).withAlphaComponent(0.3).trans2Image(), for: .selected)
  78. button.setBackgroundImage(UIColor.clear.trans2Image(), for: .normal)
  79. button.layer.cornerRadius = 6
  80. button.clipsToBounds = true
  81. button.isSelected = false
  82. return button
  83. }()
  84. let invatationListButton: UIButton = {
  85. let button = UIButton(type: .custom)
  86. button.setTitleColor(UIColor(0xD5E0F2), for: .normal)
  87. button.titleLabel?.font = UIFont(name: "PingFangSC-Regular", size: 14)
  88. button.setBackgroundImage(UIColor(0xD5E0F2).withAlphaComponent(0.3).trans2Image(), for: .selected)
  89. button.setBackgroundImage(UIColor.clear.trans2Image(), for: .normal)
  90. button.layer.cornerRadius = 6
  91. button.clipsToBounds = true
  92. button.isSelected = false
  93. return button
  94. }()
  95. let muteAllAudioButton: UIButton = {
  96. let button = UIButton(type: .custom)
  97. button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
  98. button.setTitle(.allMuteAudioText, for: .normal)
  99. button.setTitleColor(UIColor(0xB2BBD1), for: .normal)
  100. button.setTitle(.allUnMuteAudioText, for: .selected)
  101. button.setTitleColor(UIColor(0xF2504B), for: .selected)
  102. button.setTitle(.allUnMuteAudioText, for: [.selected, .highlighted])
  103. button.setTitleColor(UIColor(0xF2504B), for: [.selected, .highlighted])
  104. button.backgroundColor = UIColor(0x4F586B, alpha: 0.3)
  105. button.layer.cornerRadius = 6
  106. button.clipsToBounds = true
  107. button.titleLabel?.adjustsFontSizeToFitWidth = true
  108. return button
  109. }()
  110. let muteAllVideoButton: UIButton = {
  111. let button = UIButton(type: .custom)
  112. button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
  113. button.setTitle(.allMuteVideoText, for: .normal)
  114. button.setTitleColor(UIColor(0xB2BBD1), for: .normal)
  115. button.setTitle(.allUnMuteVideoText, for: .selected)
  116. button.setTitleColor(UIColor(0xF2504B), for: .selected)
  117. button.setTitle(.allUnMuteVideoText, for: [.selected, .highlighted])
  118. button.setTitleColor(UIColor(0xF2504B), for: [.selected, .highlighted])
  119. button.backgroundColor = UIColor(0x4F586B, alpha: 0.3)
  120. button.layer.cornerRadius = 6
  121. button.clipsToBounds = true
  122. button.titleLabel?.adjustsFontSizeToFitWidth = true
  123. return button
  124. }()
  125. let moreFunctionButton: UIButton = {
  126. let button = UIButton(type: .custom)
  127. button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
  128. button.setTitle(.moreText, for: .normal)
  129. button.setTitleColor(UIColor(0xB2BBD1), for: .normal)
  130. button.backgroundColor = UIColor(0x4F586B, alpha: 0.3)
  131. button.layer.cornerRadius = 6
  132. button.clipsToBounds = true
  133. button.titleLabel?.adjustsFontSizeToFitWidth = true
  134. return button
  135. }()
  136. let callEveryoneButton: UIButton = {
  137. let button = UIButton(type: .custom)
  138. button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
  139. button.setTitle(.callEveryoneText, for: .normal)
  140. button.setTitleColor(UIColor(0xB2BBD1), for: .normal)
  141. button.backgroundColor = UIColor(0x4F586B)
  142. button.layer.cornerRadius = 6
  143. button.clipsToBounds = true
  144. button.isHidden = true
  145. return button
  146. }()
  147. let bottomControlView: UIView = {
  148. let view = UIView()
  149. view.backgroundColor = UIColor(0x17181F)
  150. return view
  151. }()
  152. lazy var userListTableView: UITableView = {
  153. let tableView = UITableView(frame: .zero, style: .plain)
  154. tableView.separatorStyle = .none
  155. tableView.delegate = self
  156. tableView.dataSource = self
  157. tableView.backgroundColor = UIColor(0x17181F)
  158. tableView.register(UserListCell.self, forCellReuseIdentifier: "UserListCell")
  159. return tableView
  160. }()
  161. lazy var raiseHandNotificationView: RaiseHandApplicationNotificationView = {
  162. let viewModel = RaiseHandApplicationNotificationViewModel()
  163. viewModel.delayDisappearanceTime = 0
  164. let applicationNotificationView = RaiseHandApplicationNotificationView(viewModel: viewModel)
  165. applicationNotificationView.delegate = self.viewModel
  166. return applicationNotificationView
  167. }()
  168. private lazy var invitationListPublisher = {
  169. conferenceStore.select(ConferenceInvitationSelectors.getInvitationList)
  170. }()
  171. var cancellableSet = Set<AnyCancellable>()
  172. init(viewModel: UserListViewModel) {
  173. self.viewModel = viewModel
  174. super.init(frame: .zero)
  175. }
  176. required init?(coder: NSCoder) {
  177. fatalError("init(coder:) has not been implemented")
  178. }
  179. private var isViewReady: Bool = false
  180. override func didMoveToWindow() {
  181. super.didMoveToWindow()
  182. guard !isViewReady else { return }
  183. isViewReady = true
  184. backgroundColor = UIColor(0x17181F)
  185. constructViewHierarchy()
  186. activateConstraints()
  187. bindInteraction()
  188. reportViewShow()
  189. }
  190. func constructViewHierarchy() {
  191. addSubview(memberLabel)
  192. addSubview(searchBar)
  193. addSubview(listStateView)
  194. listStateView.addSubview(haveEnteredButton)
  195. listStateView.addSubview(onStageButton)
  196. listStateView.addSubview(offStageButton)
  197. listStateView.addSubview(invatationListButton)
  198. addSubview(raiseHandNotificationView)
  199. addSubview(userListTableView)
  200. addSubview(bottomControlView)
  201. bottomControlView.addSubview(muteAllAudioButton)
  202. bottomControlView.addSubview(muteAllVideoButton)
  203. bottomControlView.addSubview(moreFunctionButton)
  204. addSubview(callEveryoneButton)
  205. addSubview(searchControl)
  206. }
  207. func activateConstraints() {
  208. memberLabel.snp.makeConstraints { make in
  209. make.top.equalToSuperview().offset(10.scale375Height())
  210. make.leading.equalToSuperview().offset(16.scale375())
  211. make.trailing.equalToSuperview().offset(-16.scale375())
  212. make.height.equalTo(24.scale375Height())
  213. }
  214. searchBar.snp.makeConstraints { make in
  215. make.top.equalTo(memberLabel.snp.bottom).offset(18.scale375Height())
  216. make.leading.equalToSuperview().offset(16.scale375())
  217. make.trailing.equalToSuperview().offset(-16.scale375())
  218. make.height.equalTo(36.scale375Height())
  219. }
  220. if #available(iOS 13.0, *) {
  221. searchBar.searchTextField.snp.makeConstraints { make in
  222. make.edges.equalToSuperview()
  223. }
  224. } else {
  225. if let searchField = searchBar.value(forKey: "searchField") as? UITextField {
  226. searchField.snp.makeConstraints { make in
  227. make.edges.equalToSuperview()
  228. }
  229. }
  230. }
  231. listStateView.snp.makeConstraints { make in
  232. make.top.equalTo(searchBar.snp.bottom).offset(15.scale375Height())
  233. make.leading.trailing.equalTo(searchBar)
  234. make.height.equalTo(36.scale375Height())
  235. }
  236. setupListStateView()
  237. bottomControlView.snp.makeConstraints { make in
  238. make.leading.trailing.bottom.equalToSuperview()
  239. make.height.equalTo(84.scale375Height())
  240. }
  241. callEveryoneButton.snp.makeConstraints { make in
  242. make.leading.equalToSuperview().offset(16.scale375())
  243. make.trailing.equalToSuperview().offset(-16.scale375())
  244. make.bottom.equalToSuperview().offset(-34.scale375())
  245. make.height.equalTo(40.scale375Height())
  246. }
  247. raiseHandNotificationView.snp.makeConstraints { make in
  248. make.top.equalTo(listStateView.snp.bottom).offset(10.scale375Height())
  249. make.leading.equalToSuperview().offset(8.scale375())
  250. make.trailing.equalToSuperview().offset(-8.scale375())
  251. make.height.equalTo(40.scale375Height())
  252. }
  253. setupUserListTableView()
  254. muteAllAudioButton.snp.makeConstraints { make in
  255. make.top.equalToSuperview().offset(10.scale375Height())
  256. make.leading.equalToSuperview().offset(16.scale375())
  257. make.width.equalTo(108.scale375())
  258. make.height.equalTo(40.scale375())
  259. }
  260. muteAllVideoButton.snp.makeConstraints { make in
  261. make.top.equalToSuperview().offset(10.scale375Height())
  262. make.leading.equalToSuperview().offset(133.scale375())
  263. make.width.equalTo(108.scale375())
  264. make.height.equalTo(40.scale375())
  265. }
  266. moreFunctionButton.snp.makeConstraints { make in
  267. make.top.equalToSuperview().offset(10.scale375Height())
  268. make.leading.equalToSuperview().offset(250.scale375())
  269. make.width.equalTo(108.scale375())
  270. make.height.equalTo(40.scale375())
  271. }
  272. searchControl.snp.makeConstraints { make in
  273. make.edges.equalToSuperview()
  274. }
  275. }
  276. private func reportViewShow() {
  277. viewModel.reportUserListPanelShow()
  278. }
  279. private func setupUserListTableView() {
  280. guard userListTableView.superview != nil else { return }
  281. userListTableView.snp.remakeConstraints { make in
  282. let aboveView = viewModel.isShownNotificationView ? raiseHandNotificationView : listStateView
  283. let bottomView = viewModel.userListType == .notInRoomUsers ? callEveryoneButton : bottomControlView
  284. make.top.equalTo(aboveView.snp.bottom).offset(15.scale375Height())
  285. make.leading.equalToSuperview().offset(16.scale375())
  286. make.trailing.equalToSuperview().offset(-16.scale375())
  287. make.bottom.equalTo(bottomView.snp.top)
  288. }
  289. }
  290. private func setupListStateView() {
  291. if viewModel.isSeatEnabled {
  292. onStageButton.snp.remakeConstraints { make in
  293. make.left.centerY.equalToSuperview()
  294. make.height.equalTo(32.scale375Height())
  295. make.width.equalToSuperview().multipliedBy(1.0 / 3.0)
  296. }
  297. offStageButton.snp.remakeConstraints { make in
  298. make.centerY.equalToSuperview()
  299. make.leading.equalTo(onStageButton.snp.trailing)
  300. make.height.equalTo(32.scale375Height())
  301. make.width.equalToSuperview().multipliedBy(1.0 / 3.0)
  302. }
  303. invatationListButton.snp.remakeConstraints { make in
  304. make.centerY.equalToSuperview()
  305. make.leading.equalTo(offStageButton.snp.trailing)
  306. make.height.equalTo(32.scale375Height())
  307. make.width.equalToSuperview().multipliedBy(1.0 / 3.0)
  308. }
  309. } else {
  310. haveEnteredButton.snp.remakeConstraints { make in
  311. make.left.centerY.equalToSuperview()
  312. make.height.equalTo(32.scale375Height())
  313. make.width.equalToSuperview().multipliedBy(0.5)
  314. }
  315. invatationListButton.snp.remakeConstraints { make in
  316. make.centerY.equalToSuperview()
  317. make.leading.equalTo(haveEnteredButton.snp.trailing)
  318. make.height.equalTo(32.scale375Height())
  319. make.width.equalToSuperview().multipliedBy(0.5)
  320. }
  321. }
  322. }
  323. func bindInteraction() {
  324. viewModel.viewResponder = self
  325. setupViewState()
  326. searchBar.delegate = self
  327. muteAllAudioButton.addTarget(self, action: #selector(muteAllAudioAction), for: .touchUpInside)
  328. muteAllVideoButton.addTarget(self, action: #selector(muteAllVideoAction), for: .touchUpInside)
  329. moreFunctionButton.addTarget(self, action: #selector(moreFunctionAction), for: .touchUpInside)
  330. let tap = UITapGestureRecognizer(target: self, action: #selector(hideSearchControl(sender:)))
  331. searchControl.addGestureRecognizer(tap)
  332. haveEnteredButton.addTarget(self, action: #selector(selectAllUserAction(sender:)), for: .touchUpInside)
  333. onStageButton.addTarget(self, action: #selector(selectOnStageAction(sender:)), for: .touchUpInside)
  334. offStageButton.addTarget(self, action: #selector(selectOffStageAction(sender:)), for: .touchUpInside)
  335. invatationListButton.addTarget(self, action: #selector(selectInvitationListAction(sender:)), for: .touchUpInside)
  336. callEveryoneButton.addTarget(self, action: #selector(callEveryoneAction(sender:)), for: .touchUpInside)
  337. invitationListPublisher
  338. .receive(on: DispatchQueue.mainQueue)
  339. .sink { [weak self] invitationList in
  340. guard let self = self else { return }
  341. let oldList = viewModel.invitationList
  342. viewModel.invitationList = invitationList
  343. if viewModel.userListType == .notInRoomUsers {
  344. self.updateInvitationTableView(oldList: oldList, newList: invitationList)
  345. self.updateBottomControlView()
  346. }
  347. self.updateListStateView()
  348. }
  349. .store(in: &cancellableSet)
  350. }
  351. func setupViewState() {
  352. memberLabel.text = String(format: .memberText, viewModel.allUserCount)
  353. let roomInfo = viewModel.roomInfo
  354. muteAllAudioButton.isSelected = roomInfo.isMicrophoneDisableForAllUser
  355. muteAllVideoButton.isSelected = roomInfo.isCameraDisableForAllUser
  356. bottomControlView.isHidden = !viewModel.isShownBottomControlView
  357. setupListStateViewText()
  358. }
  359. private func setupListStateViewText() {
  360. if viewModel.isSeatEnabled {
  361. let seatedListText: String = localizedReplace(.onStageNumberText, replace: String(viewModel.onStageCount))
  362. onStageButton.setTitle(seatedListText, for: .normal)
  363. let offSeatListText: String = localizedReplace(.notOnStageNumberText, replace: String(viewModel.offStageCount))
  364. offStageButton.setTitle(offSeatListText, for: .normal)
  365. } else {
  366. let haveEnteredListText: String = localizedReplace(.haveEnterenRoomText, replace: String(viewModel.allUserCount))
  367. haveEnteredButton.setTitle(haveEnteredListText, for: .normal)
  368. }
  369. let invitationListText: String = localizedReplace(.notEnteredRoomText, replace: String(viewModel.invitationUserList.count))
  370. invatationListButton.setTitle(invitationListText, for: .normal)
  371. }
  372. private func updateBottomControlView() {
  373. if viewModel.userListType == .notInRoomUsers {
  374. bottomControlView.isHidden = true
  375. callEveryoneButton.isHidden = viewModel.invitationList.isEmpty
  376. } else {
  377. callEveryoneButton.isHidden = true
  378. bottomControlView.isHidden = !viewModel.isShownBottomControlView
  379. }
  380. setupUserListTableView()
  381. }
  382. @objc func muteAllAudioAction(sender: UIButton) {
  383. viewModel.muteAllAudioAction(sender: sender, view: self)
  384. }
  385. @objc func muteAllVideoAction(sender: UIButton) {
  386. viewModel.muteAllVideoAction(sender: sender, view: self)
  387. }
  388. @objc func moreFunctionAction(sender: UIButton) {
  389. let view = MoreUserManagerView()
  390. view.show(rootView: self)
  391. }
  392. @objc func hideSearchControl(sender: UIView) {
  393. if #available(iOS 13, *) {
  394. searchBar.searchTextField.resignFirstResponder()
  395. } else {
  396. searchBar.resignFirstResponder()
  397. }
  398. searchControl.isHidden = true
  399. }
  400. @objc func selectAllUserAction(sender: UIButton) {
  401. guard sender.isSelected != true else { return }
  402. sender.isSelected = true
  403. invatationListButton.isSelected = false
  404. viewModel.changeListState(type: .allUsers)
  405. updateBottomControlView()
  406. }
  407. @objc func selectOnStageAction(sender: UIButton) {
  408. guard sender.isSelected != true else { return }
  409. sender.isSelected = true
  410. offStageButton.isSelected = false
  411. invatationListButton.isSelected = false
  412. viewModel.changeListState(type: .onStageUsers)
  413. updateBottomControlView()
  414. }
  415. @objc func selectOffStageAction(sender: UIButton) {
  416. guard sender.isSelected != true else { return }
  417. sender.isSelected = true
  418. onStageButton.isSelected = false
  419. invatationListButton.isSelected = false
  420. viewModel.changeListState(type: .offStageUsers)
  421. updateBottomControlView()
  422. }
  423. @objc func selectInvitationListAction(sender: UIButton) {
  424. guard sender.isSelected != true else { return }
  425. sender.isSelected = true
  426. haveEnteredButton.isSelected = false
  427. onStageButton.isSelected = false
  428. offStageButton.isSelected = false
  429. viewModel.changeListState(type: .notInRoomUsers)
  430. updateBottomControlView()
  431. }
  432. @objc func callEveryoneAction(sender: UIButton) {
  433. let userIdsNeedtoCall = viewModel.invitationList
  434. .filter { $0.status != .pending }
  435. .map { $0.invitee.userId }
  436. conferenceStore.dispatch(action: ConferenceInvitationActions.inviteUsers(payload: (viewModel.roomInfo.roomId, userIdsNeedtoCall)))
  437. }
  438. func updateInvitationTableView(oldList: [TUIInvitation], newList: [TUIInvitation]) {
  439. let result = viewModel.compareLists(oldList: oldList, newList: newList)
  440. userListTableView.beginUpdates()
  441. for invitation in result.removed {
  442. if let index = oldList.firstIndex(where: { $0.invitee.userId == invitation.invitee.userId }) {
  443. userListTableView.deleteRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
  444. viewModel.attendeeList.remove(at: index)
  445. }
  446. }
  447. for invitation in result.added {
  448. viewModel.attendeeList.insert(UserEntity(invitation: invitation), at: 0)
  449. userListTableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
  450. }
  451. for invitation in result.changed {
  452. if let index = oldList.firstIndex(where: { $0.invitee.userId == invitation.invitee.userId }) {
  453. if invitation.status == .rejected {
  454. DispatchQueue.main.async {
  455. if let cell = self.userListTableView.cellForRow(at: IndexPath(row: index, section: 0)) as? UserListCell {
  456. cell.showNotJoiningLabel()
  457. }
  458. }
  459. }
  460. userListTableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .none)
  461. }
  462. }
  463. userListTableView.endUpdates()
  464. }
  465. deinit {
  466. debugPrint("deinit \(self)")
  467. }
  468. @Injected(\.conferenceStore) private var conferenceStore
  469. }
  470. extension UserListView: UISearchBarDelegate {
  471. func searchBar(_ searchBar:UISearchBar,textDidChange searchText:String){
  472. let searchContentText = searchText.trimmingCharacters(in: .whitespaces)
  473. viewModel.searchText = searchContentText
  474. viewModel.isSearching = searchContentText.count != 0
  475. viewModel.updateAttendeeList()
  476. userListTableView.reloadData()
  477. }
  478. func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
  479. searchControl.isHidden = false
  480. return true
  481. }
  482. func searchBarShouldEndEditing(_ searchBar: UISearchBar) -> Bool {
  483. viewModel.reportUserListSearch()
  484. return true
  485. }
  486. }
  487. extension UserListView: UITableViewDataSource {
  488. internal func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
  489. return viewModel.attendeeList.count
  490. }
  491. }
  492. extension UserListView: UITableViewDelegate {
  493. internal func tableView(_ tableView: UITableView,
  494. cellForRowAt indexPath: IndexPath) -> UITableViewCell {
  495. let attendeeModel = viewModel.attendeeList[indexPath.row]
  496. let cell = UserListCell(attendeeModel: attendeeModel, viewModel: viewModel)
  497. cell.selectionStyle = .none
  498. return cell
  499. }
  500. internal func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
  501. let attendeeModel = viewModel.attendeeList[indexPath.row]
  502. viewModel.showUserManageViewAction(userId: attendeeModel.userId, userName: attendeeModel.userName)
  503. }
  504. internal func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
  505. return 60.scale375Height()
  506. }
  507. }
  508. extension UserListView: UserListViewResponder {
  509. func updateUserListTableView() {
  510. setupUserListTableView()
  511. }
  512. func updateMemberLabel(count: Int) {
  513. memberLabel.text = String(format: .memberText, viewModel.allUserCount)
  514. }
  515. func updateListStateView() {
  516. setupListStateViewText()
  517. }
  518. func updateMuteAllAudioButtonState(isSelect: Bool) {
  519. muteAllAudioButton.isSelected = isSelect
  520. }
  521. func updateMuteAllVideoButtonState(isSelect: Bool) {
  522. muteAllVideoButton.isSelected = isSelect
  523. }
  524. func updateBottomControlView(isHidden: Bool) {
  525. bottomControlView.isHidden = isHidden
  526. }
  527. func updateUserManagerViewDisplayStatus(isHidden: Bool) {
  528. let model = UserListManagerViewModel(selectUserId: viewModel.userId)
  529. let view = UserListManagerView(viewModel: model)
  530. view.show(rootView: self)
  531. }
  532. func makeToast(text: String) {
  533. RoomRouter.makeToastInCenter(toast: text, duration: 0.5)
  534. }
  535. func reloadUserListView() {
  536. userListTableView.reloadData()
  537. }
  538. func showAlert(title: String?, message: String?, sureTitle: String?, declineTitle: String?, sureBlock: (() -> ())?, declineBlock: (() -> ())?) {
  539. RoomRouter.presentAlert(title: title, message: message, sureTitle: sureTitle, declineTitle: declineTitle, sureBlock: sureBlock, declineBlock: declineBlock)
  540. }
  541. }
  542. private extension String {
  543. static var allMuteAudioText: String {
  544. localized("Mute All")
  545. }
  546. static var allMuteVideoText: String {
  547. localized("Stop all video")
  548. }
  549. static var allUnMuteAudioText: String {
  550. localized("Unmute all")
  551. }
  552. static var allUnMuteVideoText: String {
  553. localized("Enable all video")
  554. }
  555. static var moreText: String {
  556. localized("More")
  557. }
  558. static var memberText: String {
  559. localized("Users(%lld)")
  560. }
  561. static var searchMemberText: String {
  562. localized("Search for participants")
  563. }
  564. static var onStageNumberText: String {
  565. localized("On stage(xx)")
  566. }
  567. static var notOnStageNumberText: String {
  568. localized("Not on stage(xx)")
  569. }
  570. static var notEnteredRoomText: String {
  571. localized("Not Entered(xx)")
  572. }
  573. static var haveEnterenRoomText: String {
  574. localized("Entered(xx)")
  575. }
  576. static var callEveryoneText: String {
  577. localized("Call all")
  578. }
  579. }