// // ConferenceInvitationViewController.swift // TUIRoomKit // // Created by jeremiawang on 2024/8/6. // import Foundation import RTCRoomEngine import UIKit import Combine import Factory class ConferenceInvitationViewController: UIViewController { private var cancellableSet = Set() var roomInfo: TUIRoomInfo var invitation: TUIInvitation init(roomInfo: TUIRoomInfo, invitation: TUIInvitation) { self.roomInfo = roomInfo self.invitation = invitation super.init(nibName: nil, bundle: nil) self.modalPresentationStyle = .fullScreen } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override public var shouldAutorotate: Bool { return false } override public var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait } private let backgroundImageView: UIImageView = { let view = UIImageView() view.contentMode = .scaleAspectFill view.alpha = 0.2 return view }() private let avatarImageView: UIImageView = { let avatarImageView = UIImageView() avatarImageView.contentMode = .scaleAspectFill avatarImageView.layer.cornerRadius = 25 avatarImageView.clipsToBounds = true return avatarImageView }() private let inviteLabel: UILabel = { let label = UILabel() label.textColor = .white label.font = UIFont(name: "PingFangSC-Regular", size: 14) return label }() private let conferenceNameLabel: UILabel = { let label = UILabel() label.textColor = .white label.font = UIFont.boldSystemFont(ofSize: 24) label.numberOfLines = 1 label.lineBreakMode = .byTruncatingTail return label }() private let detailsLabel: UILabel = { let label = UILabel() label.textColor = .white label.font = UIFont(name: "PingFangSC-Regular", size: 14) return label }() private let joinSliderView: UIView = { let view = UIView() view.backgroundColor = .white.withAlphaComponent(0.1) view.layer.cornerRadius = 39.scale375() return view }() private let joinLabel: UILabel = { let label = UILabel() label.text = .joinNowText label.textColor = .white.withAlphaComponent(0.8) label.font = UIFont(name: "PingFangSC-Medium", size: 16) label.textAlignment = .center return label }() private let sliderThumbView: UIView = { let view = UIView() view.backgroundColor = UIColor.tui_color(withHex: "1C66E5") view.layer.cornerRadius = 32 return view }() private let arrowImageView: UIImageView = { let imageView = UIImageView() imageView.image = UIImage(named: "room_rightlink_arrow", in: tuiRoomKitBundle(), compatibleWith: nil) imageView.tintColor = .white return imageView }() private let declineButton: UIButton = { let button = UIButton(type: .system) button.setTitle(.notEnterText, for: .normal) button.setTitleColor(.white.withAlphaComponent(0.8), for: .normal) button.titleLabel?.font = UIFont(name: "PingFangSC-Medium", size: 16) return button }() override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .black initState() initializeData() constructViewHierarchy() activateConstraints() bindInteraction() operation.select(ViewSelectors.getDismissInvitationFlag) .receive(on: DispatchQueue.main) .removeDuplicates() .sink { [weak self] shouldDismissInvitation in guard let self = self else { return } if shouldDismissInvitation { InvitationObserverService.shared.dismissInvitationWindow() } } .store(in: &cancellableSet) } deinit { debugPrint("deinit \(self)") } private func initState() { self.operation.dispatch(action: InvitationViewActions.resetInvitationFlag()) } private func constructViewHierarchy() { view.addSubview(backgroundImageView) view.addSubview(avatarImageView) view.addSubview(inviteLabel) view.addSubview(conferenceNameLabel) view.addSubview(detailsLabel) view.addSubview(joinSliderView) joinSliderView.addSubview(joinLabel) joinSliderView.addSubview(sliderThumbView) sliderThumbView.addSubview(arrowImageView) view.addSubview(declineButton) } private func activateConstraints() { backgroundImageView.snp.makeConstraints { make in make.edges.equalToSuperview() } avatarImageView.snp.makeConstraints { make in make.centerX.equalToSuperview() make.height.width.equalTo(50.scale375()) make.top.equalToSuperview().offset(150.scale375Height()) } inviteLabel.snp.makeConstraints { make in make.centerX.equalToSuperview() make.top.equalTo(avatarImageView.snp.bottom).offset(16) } conferenceNameLabel.snp.makeConstraints { make in make.centerX.equalToSuperview() make.width.lessThanOrEqualTo(300.scale375()) make.top.equalTo(inviteLabel.snp.bottom).offset(30) } detailsLabel.snp.makeConstraints { make in make.centerX.equalToSuperview() make.top.equalTo(conferenceNameLabel.snp.bottom).offset(10) } declineButton.snp.makeConstraints { make in make.centerX.equalToSuperview() make.bottom.equalToSuperview().offset(-94.scale375Height()) } joinSliderView.snp.makeConstraints { make in make.centerX.equalToSuperview() make.width.equalTo(200.scale375()) make.height.equalTo(78.scale375()) make.bottom.equalTo(declineButton.snp.top).offset(-30) } joinLabel.snp.makeConstraints { make in make.centerY.equalToSuperview() make.centerX.equalToSuperview().offset(32.scale375()) } sliderThumbView.snp.makeConstraints { make in make.left.equalTo(joinSliderView.snp.left).offset(5) make.centerY.equalTo(joinSliderView.snp.centerY) make.width.height.equalTo(64.scale375()) } arrowImageView.snp.makeConstraints { make in make.center.equalTo(sliderThumbView) make.width.height.equalTo(20.scale375()) } } private func bindInteraction() { let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:))) sliderThumbView.addGestureRecognizer(panGesture) declineButton.addTarget(self, action: #selector(rejectAction), for: .touchUpInside) } private func initializeData() { inviteLabel.text = invitation.inviter.userName + .inviteJoinConferenceText conferenceNameLabel.text = roomInfo.name detailsLabel.text = .hostText + roomInfo.ownerName + " | " + .participantText + String(roomInfo.memberCount) let placeholder = UIImage(named: "room_default_user", in: tuiRoomKitBundle(), compatibleWith: nil) if let url = URL(string: invitation.inviter.avatarUrl) { avatarImageView.sd_setImage(with: url, placeholderImage: placeholder) backgroundImageView.sd_setImage(with: url, placeholderImage: placeholder) } else { avatarImageView.image = placeholder backgroundImageView.image = placeholder } } @objc private func handlePanGesture(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: joinSliderView) let maxTranslation = joinSliderView.frame.width - sliderThumbView.frame.width - 10 switch gesture.state { case .changed: if translation.x >= 0 && translation.x <= maxTranslation { sliderThumbView.snp.updateConstraints { make in make.left.equalTo(joinSliderView.snp.left).offset(5 + translation.x) } } case .ended: if translation.x >= maxTranslation { sliderThumbView.snp.updateConstraints { make in make.left.equalTo(joinSliderView.snp.left).offset(5 + maxTranslation) } gesture.isEnabled = false self.acceptAction() } else { sliderThumbView.snp.updateConstraints { make in make.left.equalTo(joinSliderView.snp.left).offset(5) } UIView.animate(withDuration: 0.3) { self.view.layoutIfNeeded() } } default: break } } private func acceptAction() { operation.dispatch(action: ConferenceInvitationActions.accept(payload: roomInfo.roomId)) } @objc func rejectAction() { operation.dispatch(action: InvitationViewActions.dismissInvitationView()) operation.dispatch(action: ConferenceInvitationActions.reject(payload: (roomInfo.roomId, .rejectToEnter))) } @Injected(\.navigation) var route @Injected(\.conferenceStore) var operation } private extension String { static var inviteJoinConferenceText: String { localized(" invite you to join the conference") } static var hostText: String { localized("Conference Host") } static var participantText: String { localized("Participant") } static var peopleText: String { localized("People") } static var joinNowText: String { localized("Join now") } static var notEnterText: String { localized("Do not enter for now") } }