提交
This commit is contained in:
@@ -0,0 +1,322 @@
|
||||
//
|
||||
// UserListCell.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2024/5/7.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RTCRoomEngine
|
||||
import Factory
|
||||
import Combine
|
||||
|
||||
class UserListCell: UITableViewCell {
|
||||
var attendeeModel: UserEntity
|
||||
var viewModel: UserListViewModel
|
||||
|
||||
let avatarImageView: UIImageView = {
|
||||
let img = UIImageView()
|
||||
img.layer.cornerRadius = 20
|
||||
img.layer.masksToBounds = true
|
||||
return img
|
||||
}()
|
||||
|
||||
let userLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = UIColor(0xD5E0F2)
|
||||
label.backgroundColor = UIColor.clear
|
||||
label.textAlignment = isRTL ? .right : .left
|
||||
label.textAlignment = .left
|
||||
label.font = UIFont(name: "PingFangSC-Regular", size: 16)
|
||||
label.numberOfLines = 1
|
||||
return label
|
||||
}()
|
||||
|
||||
let roleImageView: UIImageView = {
|
||||
let imageView = UIImageView()
|
||||
return imageView
|
||||
}()
|
||||
|
||||
let roleLabel: UILabel = {
|
||||
let label = UILabel ()
|
||||
label.font = UIFont(name: "PingFangSC-Regular", size: 12)
|
||||
label.backgroundColor = UIColor.clear
|
||||
label.textColor = UIColor(0x4791FF)
|
||||
return label
|
||||
}()
|
||||
|
||||
let muteAudioButton: UIButton = {
|
||||
let button = UIButton()
|
||||
button.setImage(UIImage(named: "room_unMute_audio", in: tuiRoomKitBundle(), compatibleWith: nil)?.checkOverturn(), for: .normal)
|
||||
button.setImage(UIImage(named: "room_mute_audio_red", in: tuiRoomKitBundle(), compatibleWith: nil)?.checkOverturn(), for: .selected)
|
||||
return button
|
||||
}()
|
||||
|
||||
let muteVideoButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.setImage(UIImage(named: "room_unMute_video", in: tuiRoomKitBundle(), compatibleWith: nil)?.checkOverturn(), for: .normal)
|
||||
button.setImage(UIImage(named: "room_mute_video_red", in: tuiRoomKitBundle(), compatibleWith: nil)?.checkOverturn(), for: .selected)
|
||||
return button
|
||||
}()
|
||||
|
||||
let inviteStageButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.backgroundColor = UIColor(0x0565FA)
|
||||
button.layer.cornerRadius = 6
|
||||
button.setTitle(.inviteSeatText, for: .normal)
|
||||
button.setTitleColor(UIColor(0xFFFFFF), for: .normal)
|
||||
button.titleLabel?.font = UIFont.systemFont(ofSize: 12, weight: .regular)
|
||||
button.isHidden = true
|
||||
return button
|
||||
}()
|
||||
|
||||
let callButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.backgroundColor = UIColor(0x6B758A)
|
||||
button.layer.cornerRadius = 6
|
||||
button.setTitle(.callText, for: .normal)
|
||||
button.setTitleColor(UIColor(0xB2BBD1), for: .normal)
|
||||
button.titleLabel?.font = UIFont(name: "PingFangSC-Regular", size: 12)
|
||||
button.isHidden = true
|
||||
return button
|
||||
}()
|
||||
|
||||
let callingLabel: UILabel = {
|
||||
let label = UILabel ()
|
||||
label.font = UIFont(name: "PingFangSC-Regular", size: 14)
|
||||
label.backgroundColor = UIColor.clear
|
||||
label.textColor = UIColor(0xD5E0F2)
|
||||
label.text = .callingText
|
||||
label.isHidden = true
|
||||
return label
|
||||
}()
|
||||
|
||||
let notJoiningLabel: UILabel = {
|
||||
let label = UILabel ()
|
||||
label.font = UIFont(name: "PingFangSC-Regular", size: 14)
|
||||
label.backgroundColor = UIColor.clear
|
||||
label.textAlignment = .right
|
||||
label.textColor = UIColor(0xD5E0F2)
|
||||
label.text = .notJoinNowText
|
||||
label.isHidden = true
|
||||
return label
|
||||
}()
|
||||
|
||||
let downLineView : UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = UIColor(0x4F586B,alpha: 0.3)
|
||||
return view
|
||||
}()
|
||||
|
||||
init(attendeeModel: UserEntity ,viewModel: UserListViewModel) {
|
||||
self.attendeeModel = attendeeModel
|
||||
self.viewModel = viewModel
|
||||
super.init(style: .default, reuseIdentifier: "UserListCell")
|
||||
}
|
||||
|
||||
private var isViewReady: Bool = false
|
||||
override func didMoveToWindow() {
|
||||
super.didMoveToWindow()
|
||||
guard !isViewReady else { return }
|
||||
isViewReady = true
|
||||
constructViewHierarchy()
|
||||
activateConstraints()
|
||||
bindInteraction()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func constructViewHierarchy() {
|
||||
contentView.addSubview(avatarImageView)
|
||||
contentView.addSubview(userLabel)
|
||||
contentView.addSubview(roleImageView)
|
||||
contentView.addSubview(roleLabel)
|
||||
contentView.addSubview(muteAudioButton)
|
||||
contentView.addSubview(muteVideoButton)
|
||||
contentView.addSubview(inviteStageButton)
|
||||
contentView.addSubview(downLineView)
|
||||
contentView.addSubview(callButton)
|
||||
contentView.addSubview(callingLabel)
|
||||
contentView.addSubview(notJoiningLabel)
|
||||
}
|
||||
|
||||
func activateConstraints() {
|
||||
avatarImageView.snp.makeConstraints { make in
|
||||
make.width.height.equalTo(40)
|
||||
make.leading.equalToSuperview()
|
||||
make.centerY.equalToSuperview()
|
||||
}
|
||||
muteVideoButton.snp.makeConstraints { make in
|
||||
make.width.height.equalTo(20.scale375())
|
||||
make.trailing.equalToSuperview()
|
||||
make.centerY.equalTo(self.avatarImageView)
|
||||
}
|
||||
muteAudioButton.snp.makeConstraints { make in
|
||||
make.width.height.equalTo(20.scale375())
|
||||
make.trailing.equalTo(self.muteVideoButton.snp.leading).offset(-20.scale375())
|
||||
make.centerY.equalTo(self.avatarImageView)
|
||||
}
|
||||
inviteStageButton.snp.makeConstraints { make in
|
||||
make.trailing.equalToSuperview()
|
||||
make.centerY.equalTo(self.avatarImageView)
|
||||
make.width.equalTo(80.scale375())
|
||||
make.height.equalTo(30.scale375Height())
|
||||
}
|
||||
userLabel.snp.makeConstraints { make in
|
||||
if attendeeModel.userRole == .generalUser {
|
||||
make.centerY.equalToSuperview()
|
||||
} else {
|
||||
make.top.equalToSuperview().offset(10.scale375Height())
|
||||
}
|
||||
make.leading.equalTo(avatarImageView.snp.trailing).offset(12.scale375())
|
||||
make.width.equalTo(150.scale375())
|
||||
make.height.equalTo(22.scale375())
|
||||
}
|
||||
roleImageView.snp.makeConstraints { make in
|
||||
make.top.equalTo(userLabel.snp.bottom).offset(2.scale375Height())
|
||||
make.leading.equalTo(avatarImageView.snp.trailing).offset(12.scale375())
|
||||
make.width.height.equalTo(14.scale375())
|
||||
}
|
||||
roleLabel.snp.makeConstraints { make in
|
||||
make.centerY.equalTo(roleImageView)
|
||||
make.leading.equalTo(roleImageView.snp.trailing).offset(2.scale375())
|
||||
make.trailing.equalTo(81.scale375())
|
||||
make.height.equalTo(16.scale375())
|
||||
}
|
||||
callButton.snp.makeConstraints { make in
|
||||
make.trailing.equalToSuperview()
|
||||
make.centerY.equalTo(self.avatarImageView)
|
||||
make.width.equalTo(48.scale375())
|
||||
make.height.equalTo(28.scale375Height())
|
||||
}
|
||||
callingLabel.snp.makeConstraints { make in
|
||||
make.trailing.equalToSuperview()
|
||||
make.centerY.equalTo(self.avatarImageView)
|
||||
make.width.equalTo(60.scale375())
|
||||
make.height.equalTo(28.scale375Height())
|
||||
}
|
||||
notJoiningLabel.snp.makeConstraints{ make in
|
||||
make.trailing.equalTo(callButton.snp.leading).offset(-12.scale375())
|
||||
make.centerY.equalTo(self.avatarImageView)
|
||||
make.width.equalTo(120.scale375())
|
||||
make.height.equalTo(28.scale375Height())
|
||||
}
|
||||
downLineView.snp.makeConstraints { make in
|
||||
make.leading.equalTo(userLabel)
|
||||
make.trailing.equalToSuperview()
|
||||
make.bottom.equalToSuperview()
|
||||
make.height.equalTo(1.scale375())
|
||||
}
|
||||
}
|
||||
|
||||
func bindInteraction() {
|
||||
backgroundColor = UIColor(0x17181F)
|
||||
setupViewState(item: attendeeModel)
|
||||
inviteStageButton.addTarget(self, action: #selector(inviteStageAction(sender:)), for: .touchUpInside)
|
||||
muteAudioButton.addTarget(self, action: #selector(showUserManageAction(sender:)), for: .touchUpInside)
|
||||
muteVideoButton.addTarget(self, action: #selector(showUserManageAction(sender:)), for: .touchUpInside)
|
||||
callButton.addTarget(self, action: #selector(inviteEnterAction(sender:)), for: .touchUpInside)
|
||||
}
|
||||
|
||||
func setupViewState(item: UserEntity) {
|
||||
let placeholder = UIImage(named: "room_default_user", in: tuiRoomKitBundle(), compatibleWith: nil)
|
||||
if let url = URL(string: item.avatarUrl) {
|
||||
avatarImageView.sd_setImage(with: url, placeholderImage: placeholder)
|
||||
} else {
|
||||
avatarImageView.image = placeholder
|
||||
}
|
||||
if item.userId == viewModel.currentUser.userId {
|
||||
userLabel.text = item.userName + "(" + .meText + ")"
|
||||
} else {
|
||||
userLabel.text = item.userName
|
||||
}
|
||||
switch item.userRole {
|
||||
case .roomOwner:
|
||||
roleImageView.image = UIImage(named: "room_role_owner", in: tuiRoomKitBundle(), compatibleWith: nil)
|
||||
roleLabel.text = .ownerText
|
||||
case .administrator:
|
||||
roleImageView.image = UIImage(named: "room_role_administrator", in: tuiRoomKitBundle(), compatibleWith: nil)
|
||||
roleLabel.text = .administratorText
|
||||
default: break
|
||||
}
|
||||
roleImageView.isHidden = item.userRole == .generalUser
|
||||
roleLabel.isHidden = item.userRole == .generalUser
|
||||
muteAudioButton.isSelected = !item.hasAudioStream
|
||||
muteVideoButton.isSelected = !item.hasVideoStream
|
||||
if viewModel.roomInfo.isSeatEnabled {
|
||||
muteAudioButton.isHidden = !attendeeModel.isOnSeat
|
||||
muteVideoButton.isHidden = !attendeeModel.isOnSeat
|
||||
if viewModel.checkSelfInviteAbility(invitee: attendeeModel) {
|
||||
inviteStageButton.isHidden = attendeeModel.isOnSeat
|
||||
} else {
|
||||
inviteStageButton.isHidden = true
|
||||
}
|
||||
}
|
||||
setupCallingViewState(item: item)
|
||||
}
|
||||
|
||||
private func setupCallingViewState(item: UserEntity) {
|
||||
if let index = viewModel.invitationList.firstIndex(where: { $0.invitee.userId == item.userId }) {
|
||||
let invitation = viewModel.invitationList[index]
|
||||
muteAudioButton.isHidden = true
|
||||
muteVideoButton.isHidden = true
|
||||
inviteStageButton.isHidden = true
|
||||
let isPending = invitation.status == .pending
|
||||
callButton.isHidden = isPending
|
||||
callingLabel.isHidden = !isPending
|
||||
notJoiningLabel.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
func showNotJoiningLabel() {
|
||||
self.notJoiningLabel.isHidden = false
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
|
||||
self.notJoiningLabel.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
@objc func inviteStageAction(sender: UIButton) {
|
||||
viewModel.userId = attendeeModel.userId
|
||||
viewModel.inviteSeatAction(sender: sender)
|
||||
}
|
||||
|
||||
@objc func showUserManageAction(sender: UIButton) {
|
||||
viewModel.showUserManageViewAction(userId: attendeeModel.userId, userName: attendeeModel.userName)
|
||||
}
|
||||
|
||||
@objc func inviteEnterAction(sender: UIButton) {
|
||||
self.conferenceStore.dispatch(action: ConferenceInvitationActions.inviteUsers(payload: (viewModel.roomInfo.roomId, [attendeeModel.userId])))
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
|
||||
@Injected(\.conferenceStore) private var conferenceStore
|
||||
}
|
||||
|
||||
private extension String {
|
||||
static var inviteSeatText: String {
|
||||
localized("Invite to stage")
|
||||
}
|
||||
static var meText: String {
|
||||
localized("Me")
|
||||
}
|
||||
static var ownerText: String {
|
||||
localized("Host")
|
||||
}
|
||||
static var administratorText: String {
|
||||
localized("Administrator")
|
||||
}
|
||||
static var callText: String {
|
||||
localized("Call")
|
||||
}
|
||||
static var callingText: String {
|
||||
localized("Calling...")
|
||||
}
|
||||
static var notJoinNowText: String {
|
||||
localized("Not joining for now")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
//
|
||||
// UserListManagerView.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/1/5.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class UserListManagerView: UIView {
|
||||
var viewModel: UserListManagerViewModel
|
||||
private var isViewReady: Bool = false
|
||||
private var viewArray: [ButtonItemView] = []
|
||||
private var currentLandscape: Bool = isLandscape
|
||||
|
||||
let contentView: UIView = {
|
||||
let view = UIView(frame: .zero)
|
||||
view.backgroundColor = UIColor(0x22262E)
|
||||
view.layer.cornerRadius = 12
|
||||
return view
|
||||
}()
|
||||
|
||||
let dropArrowButton: UIButton = {
|
||||
let button = UIButton()
|
||||
button.setImage(UIImage(named: "room_drop_arrow", in: tuiRoomKitBundle(), compatibleWith: nil), for: .normal)
|
||||
button.contentEdgeInsets = UIEdgeInsets(top: 12.scale375Height(), left: 20.scale375(), bottom: 12.scale375Height(), right: 20.scale375())
|
||||
return button
|
||||
}()
|
||||
|
||||
let rightArrowButton: UIButton = {
|
||||
let button = UIButton()
|
||||
button.setImage(UIImage(named: "room_right_arrow", in: tuiRoomKitBundle(), compatibleWith: nil), for: .normal)
|
||||
button.contentEdgeInsets = UIEdgeInsets(top: 20.scale375Height(), left: 12.scale375(), bottom: 20.scale375Height(), right: 12.scale375())
|
||||
return button
|
||||
}()
|
||||
|
||||
let scrollView: UIScrollView = {
|
||||
let view = UIScrollView()
|
||||
return view
|
||||
}()
|
||||
|
||||
let avatarImageView: UIImageView = {
|
||||
let img = UIImageView()
|
||||
img.layer.cornerRadius = 20
|
||||
img.layer.masksToBounds = true
|
||||
return img
|
||||
}()
|
||||
|
||||
let userLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = UIColor(0xD5E0F2)
|
||||
label.textAlignment = isRTL ? .right : .left
|
||||
label.font = UIFont(name: "PingFangSC-Regular", size: 16)
|
||||
label.numberOfLines = 1
|
||||
return label
|
||||
}()
|
||||
|
||||
let headView: UIView = {
|
||||
let view = UIView()
|
||||
return view
|
||||
}()
|
||||
|
||||
let stackView: UIStackView = {
|
||||
let view = UIStackView()
|
||||
view.axis = .vertical
|
||||
view.alignment = .center
|
||||
view.distribution = .equalSpacing
|
||||
view.spacing = 0
|
||||
return view
|
||||
}()
|
||||
|
||||
let backBlockView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = UIColor(0x17181F)
|
||||
view.alpha = 0.9
|
||||
return view
|
||||
}()
|
||||
|
||||
init(viewModel: UserListManagerViewModel) {
|
||||
self.viewModel = viewModel
|
||||
super.init(frame: .zero)
|
||||
contentView.transform = CGAffineTransform(translationX: 0, y: kScreenHeight)
|
||||
alpha = 0
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func didMoveToWindow() {
|
||||
super.didMoveToWindow()
|
||||
guard !isViewReady else { return }
|
||||
constructViewHierarchy()
|
||||
activateConstraints()
|
||||
bindInteraction()
|
||||
isViewReady = true
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
guard currentLandscape != isLandscape else { return }
|
||||
setupViewOrientation(isLandscape: isLandscape)
|
||||
currentLandscape = isLandscape
|
||||
}
|
||||
|
||||
private func constructViewHierarchy() {
|
||||
addSubview(backBlockView)
|
||||
addSubview(contentView)
|
||||
contentView.addSubview(dropArrowButton)
|
||||
contentView.addSubview(rightArrowButton)
|
||||
contentView.addSubview(scrollView)
|
||||
scrollView.addSubview(headView)
|
||||
scrollView.addSubview(stackView)
|
||||
headView.addSubview(avatarImageView)
|
||||
headView.addSubview(userLabel)
|
||||
setupStackView()
|
||||
}
|
||||
|
||||
private func activateConstraints() {
|
||||
setupViewOrientation(isLandscape: isLandscape)
|
||||
headView.snp.makeConstraints { make in
|
||||
make.top.leading.equalToSuperview()
|
||||
make.trailing.equalToSuperview().offset(-16.scale375())
|
||||
make.height.equalTo(40.scale375())
|
||||
}
|
||||
avatarImageView.snp.makeConstraints { make in
|
||||
make.centerY.equalToSuperview()
|
||||
make.leading.equalToSuperview()
|
||||
make.width.height.equalTo(40.scale375())
|
||||
}
|
||||
userLabel.snp.makeConstraints { make in
|
||||
make.centerY.equalToSuperview()
|
||||
make.leading.equalTo(avatarImageView.snp.trailing).offset(10.scale375())
|
||||
make.trailing.equalToSuperview()
|
||||
make.height.equalTo(22.scale375())
|
||||
}
|
||||
stackView.snp.makeConstraints { make in
|
||||
make.top.equalTo(headView.snp.bottom).offset(20.scale375())
|
||||
make.leading.trailing.equalToSuperview()
|
||||
make.bottom.equalToSuperview()
|
||||
make.width.equalToSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
private func setupViewOrientation(isLandscape: Bool) {
|
||||
backBlockView.snp.remakeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
contentView.snp.remakeConstraints { make in
|
||||
if isLandscape {
|
||||
make.height.equalToSuperview()
|
||||
} else {
|
||||
make.height.equalTo(500.scale375())
|
||||
}
|
||||
make.bottom.leading.trailing.equalToSuperview()
|
||||
}
|
||||
dropArrowButton.snp.remakeConstraints { make in
|
||||
make.height.equalTo(isLandscape ? 0 : 43.scale375())
|
||||
make.top.centerX.equalToSuperview()
|
||||
}
|
||||
rightArrowButton.snp.remakeConstraints { make in
|
||||
make.width.equalTo(isLandscape ? 27.scale375() : 0)
|
||||
make.leading.centerY.equalToSuperview()
|
||||
}
|
||||
scrollView.snp.remakeConstraints { make in
|
||||
make.top.equalTo(dropArrowButton.snp.bottom).offset(10.scale375())
|
||||
if isLandscape {
|
||||
make.leading.equalTo(rightArrowButton.snp.trailing).offset(5.scale375())
|
||||
} else {
|
||||
make.leading.equalToSuperview().offset(16.scale375())
|
||||
}
|
||||
make.bottom.equalToSuperview()
|
||||
make.trailing.equalToSuperview().offset(-16.scale375())
|
||||
}
|
||||
}
|
||||
|
||||
func bindInteraction() {
|
||||
viewModel.viewResponder = self
|
||||
setupViewState()
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(dismiss))
|
||||
backBlockView.addGestureRecognizer(tap)
|
||||
dropArrowButton.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
|
||||
rightArrowButton.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
|
||||
}
|
||||
|
||||
private func setupStackView() {
|
||||
for item in viewModel.userListManagerItems {
|
||||
let view = ButtonItemView(itemData: item)
|
||||
viewArray.append(view)
|
||||
stackView.addArrangedSubview(view)
|
||||
view.snp.makeConstraints { make in
|
||||
make.height.equalTo(53.scale375())
|
||||
make.width.equalToSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func setupViewState() {
|
||||
let placeholder = UIImage(named: "room_default_user", in: tuiRoomKitBundle(), compatibleWith: nil)
|
||||
guard let attendeeModel = viewModel.attendeeList.first(where: { $0.userId == viewModel.selectUserId }) else { return }
|
||||
if let url = URL(string: attendeeModel.avatarUrl) {
|
||||
avatarImageView.sd_setImage(with: url, placeholderImage: placeholder)
|
||||
} else {
|
||||
avatarImageView.image = placeholder
|
||||
}
|
||||
if attendeeModel.userId == viewModel.currentUser.userId {
|
||||
userLabel.text = attendeeModel.userName + "(" + .meText + ")"
|
||||
} else {
|
||||
userLabel.text = attendeeModel.userName
|
||||
}
|
||||
}
|
||||
|
||||
func updateStackView(items:[ButtonItemData]) {
|
||||
for view in viewArray {
|
||||
view.removeFromSuperview()
|
||||
}
|
||||
viewArray.removeAll()
|
||||
for item in items {
|
||||
let view = ButtonItemView(itemData: item)
|
||||
viewArray.append(view)
|
||||
stackView.addArrangedSubview(view)
|
||||
view.snp.makeConstraints { make in
|
||||
make.height.equalTo(53.scale375())
|
||||
make.width.equalToSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func show(rootView: UIView) {
|
||||
rootView.addSubview(self)
|
||||
self.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
setupViewOrientation(isLandscape: isLandscape)
|
||||
UIView.animate(withDuration: 0.3) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.alpha = 1
|
||||
self.contentView.transform = .identity
|
||||
}
|
||||
}
|
||||
|
||||
@objc func dismiss() {
|
||||
UIView.animate(withDuration: 0.3) { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.alpha = 0
|
||||
self.contentView.transform = CGAffineTransform(translationX: 0, y: kScreenHeight)
|
||||
} completion: { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func backBlockAction(sender: RoomInfoView) {
|
||||
viewModel.backBlockAction(sender: self)
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
}
|
||||
|
||||
extension UserListManagerView: UserListManagerViewEventResponder {
|
||||
func showAlert(title: String?, message: String?, sureTitle: String?, declineTitle: String?, sureBlock: (() -> ())?, declineBlock: (() -> ())?) {
|
||||
RoomRouter.presentAlert(title: title, message: message, sureTitle: sureTitle, declineTitle: declineTitle, sureBlock: sureBlock, declineBlock: declineBlock)
|
||||
}
|
||||
|
||||
func updateUI(item: ButtonItemData) {
|
||||
guard let view = viewArray.first(where: { $0.itemData.buttonType == item.buttonType }) else { return }
|
||||
view.setupViewState(item: item)
|
||||
}
|
||||
|
||||
func addStackView(item: ButtonItemData, index: Int?) {
|
||||
let view = ButtonItemView(itemData: item)
|
||||
if let index = index, viewArray.count > index + 1 {
|
||||
viewArray.insert(view, at: index)
|
||||
stackView.insertArrangedSubview(view, at: index)
|
||||
} else {
|
||||
viewArray.append(view)
|
||||
stackView.addArrangedSubview(view)
|
||||
}
|
||||
view.snp.makeConstraints { make in
|
||||
make.height.equalTo(53.scale375())
|
||||
make.width.equalToSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
func removeStackView(itemType: ButtonItemData.ButtonType) {
|
||||
let views = viewArray.filter({ view in
|
||||
view.itemData.buttonType == itemType
|
||||
})
|
||||
views.forEach { view in
|
||||
view.removeFromSuperview()
|
||||
}
|
||||
viewArray.removeAll(where: { $0.itemData.buttonType == itemType })
|
||||
}
|
||||
|
||||
func dismissView() {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
func makeToast(text : String) {
|
||||
RoomRouter.makeToastInCenter(toast: text, duration: 0.5)
|
||||
}
|
||||
|
||||
func setUserListManagerViewHidden(isHidden: Bool) {
|
||||
self.isHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
private extension String {
|
||||
static var meText: String {
|
||||
localized("Me")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,631 @@
|
||||
//
|
||||
// UserListView.swift
|
||||
// TUIRoomKit
|
||||
//
|
||||
// Created by janejntang on 2023/1/4.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Factory
|
||||
import Combine
|
||||
import RTCRoomEngine
|
||||
|
||||
class UserListView: UIView {
|
||||
let viewModel: UserListViewModel
|
||||
|
||||
let memberLabel: UILabel = {
|
||||
let label = UILabel()
|
||||
label.textColor = UIColor(0xD5E0F2)
|
||||
label.font = UIFont(name: "PingFangSC-Regular", size: 16)
|
||||
return label
|
||||
}()
|
||||
|
||||
let searchBar: UISearchBar = {
|
||||
let searchBar = UISearchBar()
|
||||
searchBar.placeholder = .searchMemberText
|
||||
searchBar.setBackgroundImage(UIColor(0x17181F).trans2Image(), for: .top, barMetrics: .default)
|
||||
if #available(iOS 13, *) {
|
||||
searchBar.searchTextField.textColor = UIColor(0xB2BBD1)
|
||||
searchBar.searchTextField.tintColor = UIColor(0xB2BBD1).withAlphaComponent(0.3)
|
||||
searchBar.searchTextField.layer.cornerRadius = 6
|
||||
searchBar.searchTextField.font = UIFont.systemFont(ofSize: 14, weight: .medium)
|
||||
} else {
|
||||
if let textField = searchBar.value(forKey: "searchField") as? UITextField {
|
||||
textField.textColor = UIColor(0xB2BBD1)
|
||||
textField.tintColor = UIColor(0xB2BBD1).withAlphaComponent(0.3)
|
||||
textField.layer.cornerRadius = 6
|
||||
textField.font = UIFont.systemFont(ofSize: 14, weight: .medium)
|
||||
}
|
||||
}
|
||||
|
||||
return searchBar
|
||||
}()
|
||||
|
||||
let searchControl: UIControl = {
|
||||
let view = UIControl()
|
||||
view.backgroundColor = .clear
|
||||
view.isHidden = true
|
||||
return view
|
||||
}()
|
||||
|
||||
let listStateView: UIView = {
|
||||
let view = UIView(frame: .zero)
|
||||
view.backgroundColor = UIColor(0x4F586B).withAlphaComponent(0.3)
|
||||
view.layer.cornerRadius = 6
|
||||
return view
|
||||
}()
|
||||
|
||||
let haveEnteredButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.setTitleColor(UIColor(0xD5E0F2), for: .normal)
|
||||
button.titleLabel?.font = UIFont(name: "PingFangSC-Regular", size: 14)
|
||||
button.setBackgroundImage(UIColor(0xD5E0F2).withAlphaComponent(0.3).trans2Image(), for: .selected)
|
||||
button.setBackgroundImage(UIColor.clear.trans2Image(), for: .normal)
|
||||
button.layer.cornerRadius = 6
|
||||
button.clipsToBounds = true
|
||||
button.isSelected = true
|
||||
return button
|
||||
}()
|
||||
|
||||
let onStageButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.setTitleColor(UIColor(0xD5E0F2), for: .normal)
|
||||
button.titleLabel?.font = UIFont(name: "PingFangSC-Regular", size: 14)
|
||||
button.setBackgroundImage(UIColor(0xD5E0F2).withAlphaComponent(0.3).trans2Image(), for: .selected)
|
||||
button.setBackgroundImage(UIColor.clear.trans2Image(), for: .normal)
|
||||
button.layer.cornerRadius = 6
|
||||
button.clipsToBounds = true
|
||||
button.isSelected = true
|
||||
return button
|
||||
}()
|
||||
|
||||
let offStageButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.setTitleColor(UIColor(0xD5E0F2), for: .normal)
|
||||
button.titleLabel?.font = UIFont(name: "PingFangSC-Regular", size: 14)
|
||||
button.setBackgroundImage(UIColor(0xD5E0F2).withAlphaComponent(0.3).trans2Image(), for: .selected)
|
||||
button.setBackgroundImage(UIColor.clear.trans2Image(), for: .normal)
|
||||
button.layer.cornerRadius = 6
|
||||
button.clipsToBounds = true
|
||||
button.isSelected = false
|
||||
return button
|
||||
}()
|
||||
|
||||
let invatationListButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.setTitleColor(UIColor(0xD5E0F2), for: .normal)
|
||||
button.titleLabel?.font = UIFont(name: "PingFangSC-Regular", size: 14)
|
||||
button.setBackgroundImage(UIColor(0xD5E0F2).withAlphaComponent(0.3).trans2Image(), for: .selected)
|
||||
button.setBackgroundImage(UIColor.clear.trans2Image(), for: .normal)
|
||||
button.layer.cornerRadius = 6
|
||||
button.clipsToBounds = true
|
||||
button.isSelected = false
|
||||
return button
|
||||
}()
|
||||
|
||||
let muteAllAudioButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
|
||||
button.setTitle(.allMuteAudioText, for: .normal)
|
||||
button.setTitleColor(UIColor(0xB2BBD1), for: .normal)
|
||||
button.setTitle(.allUnMuteAudioText, for: .selected)
|
||||
button.setTitleColor(UIColor(0xF2504B), for: .selected)
|
||||
button.setTitle(.allUnMuteAudioText, for: [.selected, .highlighted])
|
||||
button.setTitleColor(UIColor(0xF2504B), for: [.selected, .highlighted])
|
||||
button.backgroundColor = UIColor(0x4F586B, alpha: 0.3)
|
||||
button.layer.cornerRadius = 6
|
||||
button.clipsToBounds = true
|
||||
button.titleLabel?.adjustsFontSizeToFitWidth = true
|
||||
return button
|
||||
}()
|
||||
|
||||
let muteAllVideoButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
|
||||
button.setTitle(.allMuteVideoText, for: .normal)
|
||||
button.setTitleColor(UIColor(0xB2BBD1), for: .normal)
|
||||
button.setTitle(.allUnMuteVideoText, for: .selected)
|
||||
button.setTitleColor(UIColor(0xF2504B), for: .selected)
|
||||
button.setTitle(.allUnMuteVideoText, for: [.selected, .highlighted])
|
||||
button.setTitleColor(UIColor(0xF2504B), for: [.selected, .highlighted])
|
||||
button.backgroundColor = UIColor(0x4F586B, alpha: 0.3)
|
||||
button.layer.cornerRadius = 6
|
||||
button.clipsToBounds = true
|
||||
button.titleLabel?.adjustsFontSizeToFitWidth = true
|
||||
return button
|
||||
}()
|
||||
|
||||
let moreFunctionButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
|
||||
button.setTitle(.moreText, for: .normal)
|
||||
button.setTitleColor(UIColor(0xB2BBD1), for: .normal)
|
||||
button.backgroundColor = UIColor(0x4F586B, alpha: 0.3)
|
||||
button.layer.cornerRadius = 6
|
||||
button.clipsToBounds = true
|
||||
button.titleLabel?.adjustsFontSizeToFitWidth = true
|
||||
return button
|
||||
}()
|
||||
|
||||
let callEveryoneButton: UIButton = {
|
||||
let button = UIButton(type: .custom)
|
||||
button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
|
||||
button.setTitle(.callEveryoneText, for: .normal)
|
||||
button.setTitleColor(UIColor(0xB2BBD1), for: .normal)
|
||||
button.backgroundColor = UIColor(0x4F586B)
|
||||
button.layer.cornerRadius = 6
|
||||
button.clipsToBounds = true
|
||||
button.isHidden = true
|
||||
return button
|
||||
}()
|
||||
|
||||
let bottomControlView: UIView = {
|
||||
let view = UIView()
|
||||
view.backgroundColor = UIColor(0x17181F)
|
||||
return view
|
||||
}()
|
||||
|
||||
lazy var userListTableView: UITableView = {
|
||||
let tableView = UITableView(frame: .zero, style: .plain)
|
||||
tableView.separatorStyle = .none
|
||||
tableView.delegate = self
|
||||
tableView.dataSource = self
|
||||
tableView.backgroundColor = UIColor(0x17181F)
|
||||
tableView.register(UserListCell.self, forCellReuseIdentifier: "UserListCell")
|
||||
return tableView
|
||||
}()
|
||||
|
||||
lazy var raiseHandNotificationView: RaiseHandApplicationNotificationView = {
|
||||
let viewModel = RaiseHandApplicationNotificationViewModel()
|
||||
viewModel.delayDisappearanceTime = 0
|
||||
let applicationNotificationView = RaiseHandApplicationNotificationView(viewModel: viewModel)
|
||||
applicationNotificationView.delegate = self.viewModel
|
||||
return applicationNotificationView
|
||||
}()
|
||||
|
||||
private lazy var invitationListPublisher = {
|
||||
conferenceStore.select(ConferenceInvitationSelectors.getInvitationList)
|
||||
}()
|
||||
|
||||
var cancellableSet = Set<AnyCancellable>()
|
||||
|
||||
init(viewModel: UserListViewModel) {
|
||||
self.viewModel = viewModel
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private var isViewReady: Bool = false
|
||||
override func didMoveToWindow() {
|
||||
super.didMoveToWindow()
|
||||
guard !isViewReady else { return }
|
||||
isViewReady = true
|
||||
backgroundColor = UIColor(0x17181F)
|
||||
constructViewHierarchy()
|
||||
activateConstraints()
|
||||
bindInteraction()
|
||||
}
|
||||
|
||||
func constructViewHierarchy() {
|
||||
addSubview(memberLabel)
|
||||
addSubview(searchBar)
|
||||
addSubview(listStateView)
|
||||
listStateView.addSubview(haveEnteredButton)
|
||||
listStateView.addSubview(onStageButton)
|
||||
listStateView.addSubview(offStageButton)
|
||||
listStateView.addSubview(invatationListButton)
|
||||
addSubview(raiseHandNotificationView)
|
||||
addSubview(userListTableView)
|
||||
addSubview(bottomControlView)
|
||||
bottomControlView.addSubview(muteAllAudioButton)
|
||||
bottomControlView.addSubview(muteAllVideoButton)
|
||||
bottomControlView.addSubview(moreFunctionButton)
|
||||
addSubview(callEveryoneButton)
|
||||
addSubview(searchControl)
|
||||
}
|
||||
|
||||
func activateConstraints() {
|
||||
memberLabel.snp.makeConstraints { make in
|
||||
make.top.equalToSuperview().offset(10.scale375Height())
|
||||
make.leading.equalToSuperview().offset(16.scale375())
|
||||
make.trailing.equalToSuperview().offset(-16.scale375())
|
||||
make.height.equalTo(24.scale375Height())
|
||||
}
|
||||
searchBar.snp.makeConstraints { make in
|
||||
make.top.equalTo(memberLabel.snp.bottom).offset(18.scale375Height())
|
||||
make.leading.equalToSuperview().offset(16.scale375())
|
||||
make.trailing.equalToSuperview().offset(-16.scale375())
|
||||
make.height.equalTo(36.scale375Height())
|
||||
}
|
||||
if #available(iOS 13.0, *) {
|
||||
searchBar.searchTextField.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
} else {
|
||||
if let searchField = searchBar.value(forKey: "searchField") as? UITextField {
|
||||
searchField.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
listStateView.snp.makeConstraints { make in
|
||||
make.top.equalTo(searchBar.snp.bottom).offset(15.scale375Height())
|
||||
make.leading.trailing.equalTo(searchBar)
|
||||
make.height.equalTo(36.scale375Height())
|
||||
}
|
||||
setupListStateView()
|
||||
bottomControlView.snp.makeConstraints { make in
|
||||
make.leading.trailing.bottom.equalToSuperview()
|
||||
make.height.equalTo(84.scale375Height())
|
||||
}
|
||||
callEveryoneButton.snp.makeConstraints { make in
|
||||
make.leading.equalToSuperview().offset(16.scale375())
|
||||
make.trailing.equalToSuperview().offset(-16.scale375())
|
||||
make.bottom.equalToSuperview().offset(-34.scale375())
|
||||
make.height.equalTo(40.scale375Height())
|
||||
}
|
||||
raiseHandNotificationView.snp.makeConstraints { make in
|
||||
make.top.equalTo(listStateView.snp.bottom).offset(10.scale375Height())
|
||||
make.leading.equalToSuperview().offset(8.scale375())
|
||||
make.trailing.equalToSuperview().offset(-8.scale375())
|
||||
make.height.equalTo(40.scale375Height())
|
||||
}
|
||||
setupUserListTableView()
|
||||
muteAllAudioButton.snp.makeConstraints { make in
|
||||
make.top.equalToSuperview().offset(10.scale375Height())
|
||||
make.leading.equalToSuperview().offset(16.scale375())
|
||||
make.width.equalTo(108.scale375())
|
||||
make.height.equalTo(40.scale375())
|
||||
}
|
||||
muteAllVideoButton.snp.makeConstraints { make in
|
||||
make.top.equalToSuperview().offset(10.scale375Height())
|
||||
make.leading.equalToSuperview().offset(133.scale375())
|
||||
make.width.equalTo(108.scale375())
|
||||
make.height.equalTo(40.scale375())
|
||||
}
|
||||
moreFunctionButton.snp.makeConstraints { make in
|
||||
make.top.equalToSuperview().offset(10.scale375Height())
|
||||
make.leading.equalToSuperview().offset(250.scale375())
|
||||
make.width.equalTo(108.scale375())
|
||||
make.height.equalTo(40.scale375())
|
||||
}
|
||||
searchControl.snp.makeConstraints { make in
|
||||
make.edges.equalToSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
private func setupUserListTableView() {
|
||||
guard userListTableView.superview != nil else { return }
|
||||
userListTableView.snp.remakeConstraints { make in
|
||||
let aboveView = viewModel.isShownNotificationView ? raiseHandNotificationView : listStateView
|
||||
let bottomView = viewModel.userListType == .notInRoomUsers ? callEveryoneButton : bottomControlView
|
||||
make.top.equalTo(aboveView.snp.bottom).offset(15.scale375Height())
|
||||
make.leading.equalToSuperview().offset(16.scale375())
|
||||
make.trailing.equalToSuperview().offset(-16.scale375())
|
||||
make.bottom.equalTo(bottomView.snp.top)
|
||||
}
|
||||
}
|
||||
|
||||
private func setupListStateView() {
|
||||
if viewModel.isSeatEnabled {
|
||||
onStageButton.snp.remakeConstraints { make in
|
||||
make.left.centerY.equalToSuperview()
|
||||
make.height.equalTo(32.scale375Height())
|
||||
make.width.equalToSuperview().multipliedBy(1.0 / 3.0)
|
||||
}
|
||||
offStageButton.snp.remakeConstraints { make in
|
||||
make.centerY.equalToSuperview()
|
||||
make.leading.equalTo(onStageButton.snp.trailing)
|
||||
make.height.equalTo(32.scale375Height())
|
||||
make.width.equalToSuperview().multipliedBy(1.0 / 3.0)
|
||||
}
|
||||
invatationListButton.snp.remakeConstraints { make in
|
||||
make.centerY.equalToSuperview()
|
||||
make.leading.equalTo(offStageButton.snp.trailing)
|
||||
make.height.equalTo(32.scale375Height())
|
||||
make.width.equalToSuperview().multipliedBy(1.0 / 3.0)
|
||||
}
|
||||
} else {
|
||||
haveEnteredButton.snp.remakeConstraints { make in
|
||||
make.left.centerY.equalToSuperview()
|
||||
make.height.equalTo(32.scale375Height())
|
||||
make.width.equalToSuperview().multipliedBy(0.5)
|
||||
}
|
||||
invatationListButton.snp.remakeConstraints { make in
|
||||
make.centerY.equalToSuperview()
|
||||
make.leading.equalTo(haveEnteredButton.snp.trailing)
|
||||
make.height.equalTo(32.scale375Height())
|
||||
make.width.equalToSuperview().multipliedBy(0.5)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func bindInteraction() {
|
||||
viewModel.viewResponder = self
|
||||
setupViewState()
|
||||
searchBar.delegate = self
|
||||
muteAllAudioButton.addTarget(self, action: #selector(muteAllAudioAction), for: .touchUpInside)
|
||||
muteAllVideoButton.addTarget(self, action: #selector(muteAllVideoAction), for: .touchUpInside)
|
||||
moreFunctionButton.addTarget(self, action: #selector(moreFunctionAction), for: .touchUpInside)
|
||||
let tap = UITapGestureRecognizer(target: self, action: #selector(hideSearchControl(sender:)))
|
||||
searchControl.addGestureRecognizer(tap)
|
||||
haveEnteredButton.addTarget(self, action: #selector(selectAllUserAction(sender:)), for: .touchUpInside)
|
||||
onStageButton.addTarget(self, action: #selector(selectOnStageAction(sender:)), for: .touchUpInside)
|
||||
offStageButton.addTarget(self, action: #selector(selectOffStageAction(sender:)), for: .touchUpInside)
|
||||
invatationListButton.addTarget(self, action: #selector(selectInvitationListAction(sender:)), for: .touchUpInside)
|
||||
callEveryoneButton.addTarget(self, action: #selector(callEveryoneAction(sender:)), for: .touchUpInside)
|
||||
invitationListPublisher
|
||||
.receive(on: DispatchQueue.mainQueue)
|
||||
.sink { [weak self] invitationList in
|
||||
guard let self = self else { return }
|
||||
let oldList = viewModel.invitationList
|
||||
viewModel.invitationList = invitationList
|
||||
if viewModel.userListType == .notInRoomUsers {
|
||||
self.updateInvitationTableView(oldList: oldList, newList: invitationList)
|
||||
self.updateBottomControlView()
|
||||
}
|
||||
self.updateListStateView()
|
||||
}
|
||||
.store(in: &cancellableSet)
|
||||
}
|
||||
|
||||
func setupViewState() {
|
||||
memberLabel.text = String(format: .memberText, viewModel.allUserCount)
|
||||
let roomInfo = viewModel.roomInfo
|
||||
muteAllAudioButton.isSelected = roomInfo.isMicrophoneDisableForAllUser
|
||||
muteAllVideoButton.isSelected = roomInfo.isCameraDisableForAllUser
|
||||
bottomControlView.isHidden = !viewModel.isShownBottomControlView
|
||||
setupListStateViewText()
|
||||
}
|
||||
|
||||
private func setupListStateViewText() {
|
||||
if viewModel.isSeatEnabled {
|
||||
let seatedListText: String = localizedReplace(.onStageNumberText, replace: String(viewModel.onStageCount))
|
||||
onStageButton.setTitle(seatedListText, for: .normal)
|
||||
let offSeatListText: String = localizedReplace(.notOnStageNumberText, replace: String(viewModel.offStageCount))
|
||||
offStageButton.setTitle(offSeatListText, for: .normal)
|
||||
} else {
|
||||
let haveEnteredListText: String = localizedReplace(.haveEnterenRoomText, replace: String(viewModel.allUserCount))
|
||||
haveEnteredButton.setTitle(haveEnteredListText, for: .normal)
|
||||
}
|
||||
let invitationListText: String = localizedReplace(.notEnteredRoomText, replace: String(viewModel.invitationUserList.count))
|
||||
invatationListButton.setTitle(invitationListText, for: .normal)
|
||||
}
|
||||
|
||||
private func updateBottomControlView() {
|
||||
if viewModel.userListType == .notInRoomUsers {
|
||||
bottomControlView.isHidden = true
|
||||
callEveryoneButton.isHidden = viewModel.invitationList.isEmpty
|
||||
} else {
|
||||
callEveryoneButton.isHidden = true
|
||||
bottomControlView.isHidden = !viewModel.isShownBottomControlView
|
||||
}
|
||||
setupUserListTableView()
|
||||
}
|
||||
|
||||
@objc func muteAllAudioAction(sender: UIButton) {
|
||||
viewModel.muteAllAudioAction(sender: sender, view: self)
|
||||
}
|
||||
|
||||
@objc func muteAllVideoAction(sender: UIButton) {
|
||||
viewModel.muteAllVideoAction(sender: sender, view: self)
|
||||
}
|
||||
|
||||
@objc func moreFunctionAction(sender: UIButton) {
|
||||
RoomRouter.shared.presentPopUpViewController(viewType: .inviteViewType, height: 158.scale375Height())
|
||||
}
|
||||
|
||||
@objc func hideSearchControl(sender: UIView) {
|
||||
if #available(iOS 13, *) {
|
||||
searchBar.searchTextField.resignFirstResponder()
|
||||
} else {
|
||||
searchBar.resignFirstResponder()
|
||||
}
|
||||
searchControl.isHidden = true
|
||||
}
|
||||
|
||||
@objc func selectAllUserAction(sender: UIButton) {
|
||||
guard sender.isSelected != true else { return }
|
||||
sender.isSelected = true
|
||||
invatationListButton.isSelected = false
|
||||
viewModel.changeListState(type: .allUsers)
|
||||
updateBottomControlView()
|
||||
}
|
||||
|
||||
@objc func selectOnStageAction(sender: UIButton) {
|
||||
guard sender.isSelected != true else { return }
|
||||
sender.isSelected = true
|
||||
offStageButton.isSelected = false
|
||||
invatationListButton.isSelected = false
|
||||
viewModel.changeListState(type: .onStageUsers)
|
||||
updateBottomControlView()
|
||||
}
|
||||
|
||||
@objc func selectOffStageAction(sender: UIButton) {
|
||||
guard sender.isSelected != true else { return }
|
||||
sender.isSelected = true
|
||||
onStageButton.isSelected = false
|
||||
invatationListButton.isSelected = false
|
||||
viewModel.changeListState(type: .offStageUsers)
|
||||
updateBottomControlView()
|
||||
}
|
||||
|
||||
@objc func selectInvitationListAction(sender: UIButton) {
|
||||
guard sender.isSelected != true else { return }
|
||||
sender.isSelected = true
|
||||
haveEnteredButton.isSelected = false
|
||||
onStageButton.isSelected = false
|
||||
offStageButton.isSelected = false
|
||||
viewModel.changeListState(type: .notInRoomUsers)
|
||||
updateBottomControlView()
|
||||
}
|
||||
|
||||
@objc func callEveryoneAction(sender: UIButton) {
|
||||
let userIdsNeedtoCall = viewModel.invitationList
|
||||
.filter { $0.status != .pending }
|
||||
.map { $0.invitee.userId }
|
||||
conferenceStore.dispatch(action: ConferenceInvitationActions.inviteUsers(payload: (viewModel.roomInfo.roomId, userIdsNeedtoCall)))
|
||||
}
|
||||
|
||||
func updateInvitationTableView(oldList: [TUIInvitation], newList: [TUIInvitation]) {
|
||||
let result = viewModel.compareLists(oldList: oldList, newList: newList)
|
||||
|
||||
userListTableView.beginUpdates()
|
||||
for invitation in result.removed {
|
||||
if let index = oldList.firstIndex(where: { $0.invitee.userId == invitation.invitee.userId }) {
|
||||
userListTableView.deleteRows(at: [IndexPath(row: index, section: 0)], with: .automatic)
|
||||
viewModel.attendeeList.remove(at: index)
|
||||
}
|
||||
}
|
||||
for invitation in result.added {
|
||||
viewModel.attendeeList.insert(UserEntity(invitation: invitation), at: 0)
|
||||
userListTableView.insertRows(at: [IndexPath(row: 0, section: 0)], with: .automatic)
|
||||
}
|
||||
for invitation in result.changed {
|
||||
if let index = oldList.firstIndex(where: { $0.invitee.userId == invitation.invitee.userId }) {
|
||||
if invitation.status == .rejected {
|
||||
DispatchQueue.main.async {
|
||||
if let cell = self.userListTableView.cellForRow(at: IndexPath(row: index, section: 0)) as? UserListCell {
|
||||
cell.showNotJoiningLabel()
|
||||
}
|
||||
}
|
||||
}
|
||||
userListTableView.reloadRows(at: [IndexPath(row: index, section: 0)], with: .none)
|
||||
}
|
||||
}
|
||||
userListTableView.endUpdates()
|
||||
}
|
||||
|
||||
deinit {
|
||||
debugPrint("deinit \(self)")
|
||||
}
|
||||
|
||||
@Injected(\.conferenceStore) private var conferenceStore
|
||||
}
|
||||
|
||||
extension UserListView: UISearchBarDelegate {
|
||||
func searchBar(_ searchBar:UISearchBar,textDidChange searchText:String){
|
||||
let searchContentText = searchText.trimmingCharacters(in: .whitespaces)
|
||||
viewModel.searchText = searchContentText
|
||||
viewModel.isSearching = searchContentText.count != 0
|
||||
viewModel.updateAttendeeList()
|
||||
userListTableView.reloadData()
|
||||
}
|
||||
|
||||
func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
|
||||
searchControl.isHidden = false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
extension UserListView: UITableViewDataSource {
|
||||
internal func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
return viewModel.attendeeList.count
|
||||
}
|
||||
}
|
||||
|
||||
extension UserListView: UITableViewDelegate {
|
||||
internal func tableView(_ tableView: UITableView,
|
||||
cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
let attendeeModel = viewModel.attendeeList[indexPath.row]
|
||||
let cell = UserListCell(attendeeModel: attendeeModel, viewModel: viewModel)
|
||||
cell.selectionStyle = .none
|
||||
return cell
|
||||
}
|
||||
|
||||
internal func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
|
||||
let attendeeModel = viewModel.attendeeList[indexPath.row]
|
||||
viewModel.showUserManageViewAction(userId: attendeeModel.userId, userName: attendeeModel.userName)
|
||||
}
|
||||
|
||||
internal func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||
return 60.scale375Height()
|
||||
}
|
||||
}
|
||||
|
||||
extension UserListView: UserListViewResponder {
|
||||
func updateUserListTableView() {
|
||||
setupUserListTableView()
|
||||
}
|
||||
|
||||
func updateMemberLabel(count: Int) {
|
||||
memberLabel.text = String(format: .memberText, viewModel.allUserCount)
|
||||
}
|
||||
|
||||
func updateListStateView() {
|
||||
setupListStateViewText()
|
||||
}
|
||||
|
||||
func updateMuteAllAudioButtonState(isSelect: Bool) {
|
||||
muteAllAudioButton.isSelected = isSelect
|
||||
}
|
||||
|
||||
func updateMuteAllVideoButtonState(isSelect: Bool) {
|
||||
muteAllVideoButton.isSelected = isSelect
|
||||
}
|
||||
|
||||
func updateBottomControlView(isHidden: Bool) {
|
||||
bottomControlView.isHidden = isHidden
|
||||
}
|
||||
|
||||
func updateUserManagerViewDisplayStatus(isHidden: Bool) {
|
||||
let model = UserListManagerViewModel(selectUserId: viewModel.userId)
|
||||
let view = UserListManagerView(viewModel: model)
|
||||
view.show(rootView: self)
|
||||
}
|
||||
|
||||
func makeToast(text: String) {
|
||||
RoomRouter.makeToastInCenter(toast: text, duration: 0.5)
|
||||
}
|
||||
|
||||
func reloadUserListView() {
|
||||
userListTableView.reloadData()
|
||||
}
|
||||
|
||||
func showAlert(title: String?, message: String?, sureTitle: String?, declineTitle: String?, sureBlock: (() -> ())?, declineBlock: (() -> ())?) {
|
||||
RoomRouter.presentAlert(title: title, message: message, sureTitle: sureTitle, declineTitle: declineTitle, sureBlock: sureBlock, declineBlock: declineBlock)
|
||||
}
|
||||
}
|
||||
|
||||
private extension String {
|
||||
static var allMuteAudioText: String {
|
||||
localized("Mute All")
|
||||
}
|
||||
static var allMuteVideoText: String {
|
||||
localized("Stop all video")
|
||||
}
|
||||
static var allUnMuteAudioText: String {
|
||||
localized("Unmute all")
|
||||
}
|
||||
static var allUnMuteVideoText: String {
|
||||
localized("Enable all video")
|
||||
}
|
||||
static var moreText: String {
|
||||
localized("More")
|
||||
}
|
||||
static var memberText: String {
|
||||
localized("Users(%lld)")
|
||||
}
|
||||
static var searchMemberText: String {
|
||||
localized("Search for participants")
|
||||
}
|
||||
static var onStageNumberText: String {
|
||||
localized("On stage(xx)")
|
||||
}
|
||||
static var notOnStageNumberText: String {
|
||||
localized("Not on stage(xx)")
|
||||
}
|
||||
static var notEnteredRoomText: String {
|
||||
localized("Not Entered(xx)")
|
||||
}
|
||||
static var haveEnterenRoomText: String {
|
||||
localized("Entered(xx)")
|
||||
}
|
||||
static var callEveryoneText: String {
|
||||
localized("Call all")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user