Files
featherVoice/TUIKit/TUIRoomKit/Source/View/ConferenceOptions/ConferenceInvitation/ConferenceInvitationViewController.swift
2025-08-08 10:49:36 +08:00

287 lines
9.9 KiB
Swift

//
// 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<AnyCancellable>()
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")
}
}