829 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
			
		
		
	
	
			829 lines
		
	
	
		
			42 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
//
 | 
						|
//  LiveRoomViewV2.swift
 | 
						|
//  SodaLive
 | 
						|
//
 | 
						|
//  Created by klaus on 2024/01/17.
 | 
						|
//
 | 
						|
 | 
						|
import SwiftUI
 | 
						|
import Kingfisher
 | 
						|
import SDWebImageSwiftUI
 | 
						|
 | 
						|
struct LiveRoomViewV2: View {
 | 
						|
    
 | 
						|
    @StateObject var keyboardHandler = KeyboardHandler()
 | 
						|
    @StateObject var viewModel = LiveRoomViewModel()
 | 
						|
    
 | 
						|
    @State private var textHeight: CGFloat = .zero
 | 
						|
    @State private var menuTextHeight: CGFloat = .zero
 | 
						|
    
 | 
						|
    var body: some View {
 | 
						|
        ZStack {
 | 
						|
            Color.black.edgesIgnoringSafeArea(.all)
 | 
						|
            
 | 
						|
            VStack(spacing: 0) {
 | 
						|
                if let liveRoomInfo = viewModel.liveRoomInfo {
 | 
						|
                    if liveRoomInfo.creatorId == UserDefaults.int(forKey: .userId) {
 | 
						|
                        LiveRoomInfoHostView(
 | 
						|
                            title: liveRoomInfo.title,
 | 
						|
                            totalHeart: viewModel.totalHeartCount,
 | 
						|
                            totalDonationCan: viewModel.totalDonationCan,
 | 
						|
                            participantsCount: liveRoomInfo.participantsCount,
 | 
						|
                            isOnBg: viewModel.isBgOn,
 | 
						|
                            isOnNotice: viewModel.isShowNotice,
 | 
						|
                            isOnMenuPan: viewModel.isShowMenuPan,
 | 
						|
                            isOnSignature: viewModel.isSignatureOn,
 | 
						|
                            isShowMenuPanButton: !liveRoomInfo.menuPan.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
 | 
						|
                            creatorId: liveRoomInfo.creatorId,
 | 
						|
                            creatorNickname: liveRoomInfo.creatorNickname,
 | 
						|
                            creatorProfileUrl: liveRoomInfo.creatorProfileUrl,
 | 
						|
                            speakerList: liveRoomInfo.speakerList,
 | 
						|
                            muteSpeakerList: viewModel.muteSpeakers,
 | 
						|
                            activeSpeakerList: viewModel.activeSpeakers,
 | 
						|
                            isAdult: liveRoomInfo.isAdult,
 | 
						|
                            onClickQuit: {
 | 
						|
                                viewModel.isShowLiveEndPopup = true
 | 
						|
                            },
 | 
						|
                            onClickToggleBg: {
 | 
						|
                                viewModel.isBgOn.toggle()
 | 
						|
                            },
 | 
						|
                            onClickShare: {
 | 
						|
                                viewModel.shareRoom()
 | 
						|
                            },
 | 
						|
                            onClickEdit: {
 | 
						|
                                viewModel.isShowEditRoomInfoDialog = true
 | 
						|
                            },
 | 
						|
                            onClickProfile: {
 | 
						|
                                if $0 != UserDefaults.int(forKey: .userId) {
 | 
						|
                                    viewModel.getUserProfile(userId: $0)
 | 
						|
                                }
 | 
						|
                            },
 | 
						|
                            onClickNotice: {
 | 
						|
                                viewModel.isShowNotice.toggle()
 | 
						|
                            },
 | 
						|
                            onClickMenuPan: {
 | 
						|
                                viewModel.isShowMenuPan.toggle()
 | 
						|
                            },
 | 
						|
                            onClickTotalHeart: {
 | 
						|
                                viewModel.isShowHeartRankingPopup = true
 | 
						|
                            },
 | 
						|
                            onClickTotalDonation: {
 | 
						|
                                viewModel.isShowDonationRankingPopup = true
 | 
						|
                            },
 | 
						|
                            onClickParticipants: {
 | 
						|
                                viewModel.isShowProfileList = true
 | 
						|
                            },
 | 
						|
                            onClickToggleSignature: {
 | 
						|
                                viewModel.isSignatureOn.toggle()
 | 
						|
                            }
 | 
						|
                        )
 | 
						|
                    } else {
 | 
						|
                        LiveRoomInfoGuestView(
 | 
						|
                            title: liveRoomInfo.title,
 | 
						|
                            totalHeart: viewModel.totalHeartCount,
 | 
						|
                            totalDonationCan: viewModel.totalDonationCan,
 | 
						|
                            isOnBg: viewModel.isBgOn,
 | 
						|
                            isOnNotice: viewModel.isShowNotice,
 | 
						|
                            isOnMenuPan: viewModel.isShowMenuPan,
 | 
						|
                            isOnSignature: viewModel.isSignatureOn,
 | 
						|
                            isShowMenuPanButton: !liveRoomInfo.menuPan.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
 | 
						|
                            creatorId: liveRoomInfo.creatorId,
 | 
						|
                            creatorNickname: liveRoomInfo.creatorNickname,
 | 
						|
                            creatorProfileUrl: liveRoomInfo.creatorProfileUrl,
 | 
						|
                            speakerList: liveRoomInfo.speakerList,
 | 
						|
                            muteSpeakerList: viewModel.muteSpeakers,
 | 
						|
                            activeSpeakerList: viewModel.activeSpeakers,
 | 
						|
                            isFollowing: liveRoomInfo.isFollowing,
 | 
						|
                            isAdult: liveRoomInfo.isAdult,
 | 
						|
                            onClickQuit: {
 | 
						|
                                viewModel.isShowQuitPopup = true
 | 
						|
                            },
 | 
						|
                            onClickToggleBg: {
 | 
						|
                                viewModel.isBgOn.toggle()
 | 
						|
                            },
 | 
						|
                            onClickShare: {
 | 
						|
                                viewModel.shareRoom()
 | 
						|
                            },
 | 
						|
                            onClickFollow: {
 | 
						|
                                if $0 {
 | 
						|
                                    viewModel.creatorUnFollow()
 | 
						|
                                } else {
 | 
						|
                                    viewModel.creatorFollow()
 | 
						|
                                }
 | 
						|
                            },
 | 
						|
                            onClickProfile: {
 | 
						|
                                if $0 != UserDefaults.int(forKey: .userId) {
 | 
						|
                                    viewModel.getUserProfile(userId: $0)
 | 
						|
                                }
 | 
						|
                            },
 | 
						|
                            onClickNotice: {
 | 
						|
                                viewModel.isShowNotice.toggle()
 | 
						|
                            },
 | 
						|
                            onClickMenuPan: {
 | 
						|
                                viewModel.isShowMenuPan.toggle()
 | 
						|
                            },
 | 
						|
                            onClickTotalHeart: {
 | 
						|
                                viewModel.isShowHeartRankingPopup = true
 | 
						|
                            },
 | 
						|
                            onClickTotalDonation: {
 | 
						|
                                viewModel.isShowDonationRankingPopup = true
 | 
						|
                            },
 | 
						|
                            onClickChangeListener: {
 | 
						|
                                viewModel.setListener()
 | 
						|
                            },
 | 
						|
                            onClickToggleSignature: {
 | 
						|
                                viewModel.isSignatureOn.toggle()
 | 
						|
                            }
 | 
						|
                        )
 | 
						|
                    }
 | 
						|
                    
 | 
						|
                    ZStack(alignment: .topLeading) {
 | 
						|
                        Rectangle()
 | 
						|
                            .foregroundColor(.gray22)
 | 
						|
                            .frame(height: 16)
 | 
						|
                            .frame(maxWidth: .infinity)
 | 
						|
                        
 | 
						|
                        ScrollViewReader { proxy in
 | 
						|
                            ZStack(alignment: .bottom) {
 | 
						|
                                ZStack {
 | 
						|
                                    if viewModel.isBgOn {
 | 
						|
                                        KFImage(URL(string: liveRoomInfo.coverImageUrl))
 | 
						|
                                            .cancelOnDisappear(true)
 | 
						|
                                            .resizable()
 | 
						|
                                            .scaledToFit()
 | 
						|
                                            .frame(maxWidth: .infinity, maxHeight: .infinity)
 | 
						|
                                    }
 | 
						|
                                    
 | 
						|
                                    Rectangle()
 | 
						|
                                        .foregroundColor(.black.opacity(0.25))
 | 
						|
                                        .frame(maxWidth: .infinity)
 | 
						|
                                    
 | 
						|
                                    ScrollView(.vertical, showsIndicators: false) {
 | 
						|
                                        scrollObservableView
 | 
						|
                                        
 | 
						|
                                        if !viewModel.changeIsAdult || UserDefaults.bool(forKey: .auth) {
 | 
						|
                                            LiveRoomChatView(messages: viewModel.messages) {
 | 
						|
                                                if $0 != UserDefaults.int(forKey: .userId) {
 | 
						|
                                                    viewModel.getUserProfile(userId: $0)
 | 
						|
                                                }
 | 
						|
                                            }
 | 
						|
                                            .frame(width: screenSize().width)
 | 
						|
                                            .rotationEffect(Angle(degrees: 180))
 | 
						|
                                            .valueChanged(value: viewModel.messageChangeFlag) { _ in
 | 
						|
                                                if viewModel.offset - viewModel.originOffset > (56.7 * 2) {
 | 
						|
                                                    viewModel.isShowingNewChat = true
 | 
						|
                                                }
 | 
						|
                                            }
 | 
						|
                                        }
 | 
						|
                                    }
 | 
						|
                                    .rotationEffect(Angle(degrees: 180))
 | 
						|
                                    .onTapGesture { hideKeyboard() }
 | 
						|
                                    .onPreferenceChange(ScrollOffsetKey.self) {
 | 
						|
                                        viewModel.setOffset($0)
 | 
						|
                                    }
 | 
						|
                                    .padding(.bottom, 70)
 | 
						|
                                }
 | 
						|
                                .padding(.top, 16)
 | 
						|
                                
 | 
						|
                                VStack(alignment: .trailing, spacing: 0) {
 | 
						|
                                    Spacer()
 | 
						|
                                    
 | 
						|
                                    LiveRoomRightBottomButton(
 | 
						|
                                        imageName: viewModel.isSpeakerMute ? "ic_speaker_off" : "ic_speaker_on",
 | 
						|
                                        onClick: { viewModel.toggleSpeakerMute() }
 | 
						|
                                    )
 | 
						|
                                    .padding(.bottom, 40)
 | 
						|
                                    .padding(.trailing, 13.3)
 | 
						|
                                    
 | 
						|
                                    ZStack(alignment: .bottom) {
 | 
						|
                                        VStack(spacing: 13.3) {
 | 
						|
                                            if viewModel.role == .SPEAKER {
 | 
						|
                                                LiveRoomRightBottomButton(
 | 
						|
                                                    imageName: viewModel.isMute ? "ic_mic_off" : "ic_mic_on",
 | 
						|
                                                    onClick: { viewModel.toggleMute() }
 | 
						|
                                                )
 | 
						|
                                            }
 | 
						|
                                            
 | 
						|
                                            LiveRoomRightBottomButton(
 | 
						|
                                                imageName: "ic_donation_message_list",
 | 
						|
                                                onClick: {
 | 
						|
                                                    DispatchQueue.main.async {
 | 
						|
                                                        hideKeyboard()
 | 
						|
                                                        viewModel.isShowDonationMessagePopup = true
 | 
						|
                                                    }
 | 
						|
                                                }
 | 
						|
                                            )
 | 
						|
                                            
 | 
						|
                                            if liveRoomInfo.creatorId == UserDefaults.int(forKey: .userId) {
 | 
						|
                                                LiveRoomRightBottomButton(
 | 
						|
                                                    imageName: "ic_roulette_settings",
 | 
						|
                                                    onClick: {
 | 
						|
                                                        DispatchQueue.main.async {
 | 
						|
                                                            hideKeyboard()
 | 
						|
                                                            viewModel.isShowRouletteSettings = true
 | 
						|
                                                        }
 | 
						|
                                                    }
 | 
						|
                                                )
 | 
						|
                                            } else {
 | 
						|
                                                if viewModel.isActiveRoulette {
 | 
						|
                                                    LiveRoomRightBottomButton(
 | 
						|
                                                        imageName: "ic_roulette",
 | 
						|
                                                        onClick: {
 | 
						|
                                                            DispatchQueue.main.async {
 | 
						|
                                                                hideKeyboard()
 | 
						|
                                                                viewModel.showRoulette()
 | 
						|
                                                            }
 | 
						|
                                                        }
 | 
						|
                                                    )
 | 
						|
                                                }
 | 
						|
                                            }
 | 
						|
                                            
 | 
						|
                                            if liveRoomInfo.creatorId != UserDefaults.int(forKey: .userId) {
 | 
						|
                                                LiveRoomRightBottomButton(
 | 
						|
                                                    imageName: "ic_donation",
 | 
						|
                                                    onClick: {
 | 
						|
                                                        DispatchQueue.main.async {
 | 
						|
                                                            hideKeyboard()
 | 
						|
                                                            viewModel.isShowDonationPopup = true
 | 
						|
                                                        }
 | 
						|
                                                    }
 | 
						|
                                                )
 | 
						|
                                            }
 | 
						|
                                            
 | 
						|
                                            if liveRoomInfo.creatorId != UserDefaults.int(forKey: .userId) {
 | 
						|
                                                LiveRoomRightBottomButton(
 | 
						|
                                                    imageName: "ic_heart_pink",
 | 
						|
                                                    onClick: { viewModel.likeHeart() }
 | 
						|
                                                )
 | 
						|
                                            }
 | 
						|
                                        }
 | 
						|
                                        
 | 
						|
                                        ZStack {
 | 
						|
                                            ForEach(viewModel.hearts) { heart in
 | 
						|
                                                LiveRoomHeartView(heart: heart)
 | 
						|
                                                    .offset(x: heart.offsetX, y: heart.offsetY)
 | 
						|
                                                    .opacity(heart.opacity)
 | 
						|
                                            }
 | 
						|
                                        }.padding(.bottom, 40)
 | 
						|
                                    }
 | 
						|
                                    .padding(.trailing, 13.3)
 | 
						|
                                    
 | 
						|
                                    LiveRoomInputChatView {
 | 
						|
                                        viewModel.sendMessage(chatMessage: $0) {
 | 
						|
                                            viewModel.isShowingNewChat = false
 | 
						|
                                            proxy.scrollTo(viewModel.messages.count - 1, anchor: .center)
 | 
						|
                                        }
 | 
						|
                                        
 | 
						|
                                        return true
 | 
						|
                                    }
 | 
						|
                                    .padding(.bottom, 10)
 | 
						|
                                }
 | 
						|
                                
 | 
						|
                                if viewModel.isShowingNewChat {
 | 
						|
                                    LiveRoomNewChatView{
 | 
						|
                                        viewModel.isShowingNewChat = false
 | 
						|
                                        proxy.scrollTo(viewModel.messages.count - 1, anchor: .center)
 | 
						|
                                    }.padding(.bottom, 70)
 | 
						|
                                }
 | 
						|
                                
 | 
						|
                                if viewModel.isSignatureOn && viewModel.signatureImageUrl.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 {
 | 
						|
                                    VStack {
 | 
						|
                                        Spacer()
 | 
						|
                                        
 | 
						|
                                        AnimatedImage(url: URL(string: viewModel.signatureImageUrl))
 | 
						|
                                            .resizable()
 | 
						|
                                            .aspectRatio(contentMode: .fit)
 | 
						|
                                            .frame(width: screenSize().width - 64)
 | 
						|
                                        
 | 
						|
                                        Spacer()
 | 
						|
                                        Spacer()
 | 
						|
                                        Spacer()
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
                                
 | 
						|
                                if let signature = viewModel.signature, viewModel.isSignatureOn {
 | 
						|
                                    VStack {
 | 
						|
                                        Spacer()
 | 
						|
                                        
 | 
						|
                                        AnimatedImage(url: URL(string: signature.imageUrl))
 | 
						|
                                            .resizable()
 | 
						|
                                            .aspectRatio(contentMode: .fit)
 | 
						|
                                            .frame(width: screenSize().width - 64)
 | 
						|
                                        
 | 
						|
                                        Spacer()
 | 
						|
                                        Spacer()
 | 
						|
                                        Spacer()
 | 
						|
                                    }
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        
 | 
						|
                        if let heartNickname = viewModel.heartNickname {
 | 
						|
                            LiveRoomHeartDonationChatItemView(nickname: heartNickname)
 | 
						|
                                .padding(.top, 16)
 | 
						|
                        }
 | 
						|
                        
 | 
						|
                        if viewModel.isShowNotice {
 | 
						|
                            VStack(alignment: .leading, spacing: 0) {
 | 
						|
                                Image("ic_notice_triangle")
 | 
						|
                                    .padding(.leading, 13.3)
 | 
						|
                                
 | 
						|
                                VStack(alignment: .leading, spacing: 8) {
 | 
						|
                                    Text("[방송공지]")
 | 
						|
                                        .font(.custom(Font.bold.rawValue, size: 11.3))
 | 
						|
                                        .foregroundColor(.white)
 | 
						|
                                    
 | 
						|
                                    DetectableTextView(text: liveRoomInfo.notice)
 | 
						|
                                        .frame(
 | 
						|
                                            width: 280,
 | 
						|
                                            height: textHeight > 450 ? 450 : textHeight
 | 
						|
                                        )
 | 
						|
                                        .onAppear {
 | 
						|
                                            self.textHeight = self.estimatedHeight(
 | 
						|
                                                for: liveRoomInfo.notice,
 | 
						|
                                                width: 280
 | 
						|
                                            )
 | 
						|
                                        }
 | 
						|
                                        .onChange(of: liveRoomInfo.notice) { newText in
 | 
						|
                                            self.textHeight = self.estimatedHeight(
 | 
						|
                                                for: newText,
 | 
						|
                                                width: 280
 | 
						|
                                            )
 | 
						|
                                        }
 | 
						|
                                }
 | 
						|
                                .padding(8)
 | 
						|
                                .background(Color.gray33)
 | 
						|
                                .padding(.horizontal, 13.3)
 | 
						|
                                .padding(.bottom, 120)
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                        
 | 
						|
                        if viewModel.isShowMenuPan {
 | 
						|
                            VStack(alignment: .leading, spacing: 0) {
 | 
						|
                                Image("ic_notice_triangle")
 | 
						|
 | 
						|
                                ScrollView(.vertical) {
 | 
						|
                                    VStack(alignment: .leading, spacing: 8) {
 | 
						|
                                        Text("[메뉴판]")
 | 
						|
                                            .font(.custom(Font.bold.rawValue, size: 11.3))
 | 
						|
                                            .foregroundColor(.white)
 | 
						|
                                        
 | 
						|
                                        Text(liveRoomInfo.menuPan)
 | 
						|
                                            .font(.custom(Font.light.rawValue, size: 11.3))
 | 
						|
                                            .foregroundColor(.white)
 | 
						|
                                            .lineSpacing(4)
 | 
						|
                                    }
 | 
						|
                                    .background(GeometryReader { geometry in
 | 
						|
                                        Color.clear.preference(key: TextViewHeightKey.self, value: geometry.size.height)
 | 
						|
                                    })
 | 
						|
                                    .padding(8)
 | 
						|
                                }
 | 
						|
                                .background(Color.gray33)
 | 
						|
                                .frame(height: menuTextHeight > 500 ? 500 : menuTextHeight)
 | 
						|
                                .onPreferenceChange(TextViewHeightKey.self) { value in
 | 
						|
                                    menuTextHeight = value + 15
 | 
						|
                                }
 | 
						|
                            }
 | 
						|
                            .frame(maxWidth: 350, alignment: .leading)
 | 
						|
                            .padding(.leading, 60)
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            .popup(isPresented: $viewModel.isShowErrorPopup, type: .toast, position: .top, autohideIn: 1.3) {
 | 
						|
                GeometryReader { geo in
 | 
						|
                    HStack {
 | 
						|
                        Spacer()
 | 
						|
                        Text(viewModel.errorMessage)
 | 
						|
                            .padding(.vertical, 13.3)
 | 
						|
                            .frame(width: geo.size.width - 66.7, alignment: .center)
 | 
						|
                            .font(.custom(Font.medium.rawValue, size: 12))
 | 
						|
                            .background(Color.button)
 | 
						|
                            .foregroundColor(Color.white)
 | 
						|
                            .multilineTextAlignment(.center)
 | 
						|
                            .cornerRadius(20)
 | 
						|
                            .padding(.top, 66.7)
 | 
						|
                        Spacer()
 | 
						|
                    }
 | 
						|
                    .onDisappear {
 | 
						|
                        if viewModel.liveRoomInfo == nil {
 | 
						|
                            viewModel.quitRoom()
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            .cornerRadius(16.7, corners: [.topLeft, .topRight])
 | 
						|
            .offset(y: -(keyboardHandler.keyboardHeight > 0 ? keyboardHandler.keyboardHeight : 0))
 | 
						|
            .onAppear {
 | 
						|
                UIApplication.shared.isIdleTimerDisabled = true
 | 
						|
                UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
 | 
						|
                
 | 
						|
                viewModel.getMemberCan()
 | 
						|
                viewModel.initAgoraEngine()
 | 
						|
                viewModel.getRoomInfo()
 | 
						|
                viewModel.getBlockedMemberIdList()
 | 
						|
                
 | 
						|
                NotificationCenter.default.addObserver(
 | 
						|
                    forName: UIApplication.willTerminateNotification,
 | 
						|
                    object: nil,
 | 
						|
                    queue: .main) { _ in
 | 
						|
                        viewModel.quitRoom()
 | 
						|
                        sleep(3)
 | 
						|
                    }
 | 
						|
            }
 | 
						|
            .onDisappear {
 | 
						|
                UIApplication.shared.isIdleTimerDisabled = false
 | 
						|
                NotificationCenter.default.removeObserver(self)
 | 
						|
                viewModel.stopPeriodicPlaybackValidation()
 | 
						|
            }
 | 
						|
            
 | 
						|
            ZStack {
 | 
						|
                if viewModel.isShowProfilePopup, let liveRoomInfo = viewModel.liveRoomInfo, let selectedProfile = viewModel.selectedProfile {
 | 
						|
                    LiveRoomProfileDialog(
 | 
						|
                        isShowing: $viewModel.isShowProfilePopup,
 | 
						|
                        profileInfo: selectedProfile,
 | 
						|
                        creatorId: liveRoomInfo.creatorId,
 | 
						|
                        isSpeaker: viewModel.role == .SPEAKER,
 | 
						|
                        onClickInviteSpeaker: { inviteSpeaker(peerId: $0) },
 | 
						|
                        onClickChangeListener: {
 | 
						|
                            if $0 == UserDefaults.int(forKey: .userId) {
 | 
						|
                                viewModel.setListener()
 | 
						|
                                return
 | 
						|
                            }
 | 
						|
                            
 | 
						|
                            viewModel.changeListener(peerId: $0)
 | 
						|
                        }
 | 
						|
                    )
 | 
						|
                }
 | 
						|
                
 | 
						|
                if viewModel.isShowDonationPopup {
 | 
						|
                    LiveRoomDonationDialogView(isShowing: $viewModel.isShowDonationPopup, isAudioContentDonation: false) { can, message, isSecret in
 | 
						|
                        viewModel.donation(can: can, message: message, isSecret: isSecret)
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                
 | 
						|
                if viewModel.changeIsAdult && !UserDefaults.bool(forKey: .auth) {
 | 
						|
                    SodaDialog(
 | 
						|
                        title: "알림",
 | 
						|
                        desc: "지금 참여하던 라이브는 '19세 이상' 연령제한이 설정되어 정보통신망 이용촉진 및 정보 보호 등에 관한 법률 및 청소년 보호법의 규정에 의해 만 19세 미만의 청소년은 이용할 수 없습니다.\n마이페이지에서 본인인증 후 다시 이용하시기 바랍니다.",
 | 
						|
                        confirmButtonTitle: "확인",
 | 
						|
                        confirmButtonAction: {
 | 
						|
                            viewModel.quitRoom()
 | 
						|
                        }
 | 
						|
                    )
 | 
						|
                }
 | 
						|
                
 | 
						|
                if viewModel.isShowNoticeLikeHeart {
 | 
						|
                    SodaDialog(
 | 
						|
                        title: "안내",
 | 
						|
                        desc: "'좋아해요'는 유료 후원입니다.\n" +
 | 
						|
                        "클릭시 1캔이 소진됩니다.",
 | 
						|
                        confirmButtonTitle: "확인"
 | 
						|
                    ) {
 | 
						|
                        viewModel.isShowNoticeLikeHeart = false
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                
 | 
						|
                if viewModel.isShowQuitPopup {
 | 
						|
                    SodaDialog(
 | 
						|
                        title: "라이브 나가기",
 | 
						|
                        desc: "라이브에서 나가시겠습니까?",
 | 
						|
                        confirmButtonTitle: "예",
 | 
						|
                        confirmButtonAction: {
 | 
						|
                            viewModel.isShowQuitPopup = false
 | 
						|
                            viewModel.quitRoom()
 | 
						|
                        },
 | 
						|
                        cancelButtonTitle: "아니오",
 | 
						|
                        cancelButtonAction: {
 | 
						|
                            viewModel.isShowQuitPopup = false
 | 
						|
                        }
 | 
						|
                    )
 | 
						|
                }
 | 
						|
                
 | 
						|
                if viewModel.isShowLiveEndPopup {
 | 
						|
                    SodaDialog(
 | 
						|
                        title: "라이브 종료",
 | 
						|
                        desc: "라이브를 종료하시겠습니까?\n" +
 | 
						|
                        "라이브를 종료하면 대화내용은\n" +
 | 
						|
                        "저장되지 않고 사라집니다.\n" +
 | 
						|
                        "참여자들 또한 라이브가 종료되어\n" +
 | 
						|
                        "강제퇴장 됩니다.",
 | 
						|
                        confirmButtonTitle: "예",
 | 
						|
                        confirmButtonAction: {
 | 
						|
                            viewModel.isShowLiveEndPopup = false
 | 
						|
                            viewModel.quitRoom()
 | 
						|
                        },
 | 
						|
                        cancelButtonTitle: "아니오",
 | 
						|
                        cancelButtonAction: {
 | 
						|
                            viewModel.isShowLiveEndPopup = false
 | 
						|
                        }
 | 
						|
                    )
 | 
						|
                }
 | 
						|
            }
 | 
						|
            
 | 
						|
            ZStack {
 | 
						|
                if viewModel.isShowProfileList, let liveRoomInfo = viewModel.liveRoomInfo {
 | 
						|
                    LiveRoomProfilesDialogView(
 | 
						|
                        isShowing: $viewModel.isShowProfileList,
 | 
						|
                        viewModel: viewModel,
 | 
						|
                        roomInfo: liveRoomInfo,
 | 
						|
                        registerNotification: { viewModel.creatorFollow() },
 | 
						|
                        unRegisterNotification: { viewModel.creatorUnFollow() },
 | 
						|
                        onClickProfile: {
 | 
						|
                            if $0 != UserDefaults.int(forKey: .userId) {
 | 
						|
                                viewModel.getUserProfile(userId: $0)
 | 
						|
                            }
 | 
						|
                        },
 | 
						|
                        onClickNoChatting: { userId, nickname, profileUrl in
 | 
						|
                            viewModel.noChattingUserId = userId
 | 
						|
                            viewModel.noChattingUserNickname = nickname
 | 
						|
                            viewModel.noChattingUserProfileUrl = profileUrl
 | 
						|
                            viewModel.isShowNoChattingConfirm = true
 | 
						|
                        }
 | 
						|
                    )
 | 
						|
                }
 | 
						|
                
 | 
						|
                if viewModel.isShowUserProfilePopup, let userProfile = viewModel.userProfile {
 | 
						|
                    Color.black.opacity(0.7)
 | 
						|
                        .edgesIgnoringSafeArea(.all)
 | 
						|
                    
 | 
						|
                    LiveRoomUserProfileDialogView(
 | 
						|
                        isShowing: $viewModel.isShowUserProfilePopup,
 | 
						|
                        viewModel: viewModel,
 | 
						|
                        userProfile: userProfile,
 | 
						|
                        onClickSetManager: {
 | 
						|
                            viewModel.setManagerMessageToPeer(userId: $0)
 | 
						|
                            viewModel.setManager(userId: $0)
 | 
						|
                        },
 | 
						|
                        onClickReleaseManager: { viewModel.changeListener(peerId: $0, isFromManager: true) },
 | 
						|
                        onClickFollow: { viewModel.creatorFollow(creatorId: $0, isGetUserProfile: true) },
 | 
						|
                        onClickUnFollow: { viewModel.creatorUnFollow(creatorId: $0, isGetUserProfile: true) },
 | 
						|
                        onClickInviteSpeaker: { inviteSpeaker(peerId: $0) },
 | 
						|
                        onClickChangeListener: {
 | 
						|
                            viewModel.changeListener(peerId: $0)
 | 
						|
                        },
 | 
						|
                        onClickMenu: { userId, userNickname, isBlocked in
 | 
						|
                            viewModel.reportUserId = userId
 | 
						|
                            viewModel.reportUserNickname = userNickname
 | 
						|
                            viewModel.reportUserIsBlocked = isBlocked
 | 
						|
                            viewModel.isShowReportMenu = true
 | 
						|
                        },
 | 
						|
                        onClickNoChatting: { userId, nickname, profileUrl in
 | 
						|
                            viewModel.noChattingUserId = userId
 | 
						|
                            viewModel.noChattingUserNickname = nickname
 | 
						|
                            viewModel.noChattingUserProfileUrl = profileUrl
 | 
						|
                            viewModel.isShowNoChattingConfirm = true
 | 
						|
                        }
 | 
						|
                    )
 | 
						|
                    .padding(20)
 | 
						|
                    .popup(isPresented: $viewModel.isShowReportPopup, type: .toast, position: .top, autohideIn: 1.3) {
 | 
						|
                        GeometryReader { geo in
 | 
						|
                            HStack {
 | 
						|
                                Spacer()
 | 
						|
                                Text(viewModel.reportMessage)
 | 
						|
                                    .padding(.vertical, 13.3)
 | 
						|
                                    .frame(width: geo.size.width - 66.7, alignment: .center)
 | 
						|
                                    .font(.custom(Font.medium.rawValue, size: 12))
 | 
						|
                                    .background(Color.button)
 | 
						|
                                    .foregroundColor(Color.white)
 | 
						|
                                    .multilineTextAlignment(.center)
 | 
						|
                                    .cornerRadius(20)
 | 
						|
                                    .padding(.top, 66.7)
 | 
						|
                                Spacer()
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                
 | 
						|
                if viewModel.isShowReportMenu {
 | 
						|
                    VStack(spacing: 0) {
 | 
						|
                        ProfileReportMenuView(
 | 
						|
                            isShowing: $viewModel.isShowReportMenu,
 | 
						|
                            isBlockedUser: viewModel.reportUserIsBlocked,
 | 
						|
                            userBlockAction: { viewModel.isShowUesrBlockConfirm = true },
 | 
						|
                            userUnBlockAction: { viewModel.userUnBlock() },
 | 
						|
                            userReportAction: { viewModel.isShowUesrReportView = true },
 | 
						|
                            profileReportAction: { viewModel.isShowProfileReportConfirm = true }
 | 
						|
                        )
 | 
						|
                        
 | 
						|
                        Rectangle()
 | 
						|
                            .foregroundColor(Color.gray22)
 | 
						|
                            .frame(width: screenSize().width, height: 15.3)
 | 
						|
                    }
 | 
						|
                    .ignoresSafeArea()
 | 
						|
                }
 | 
						|
                
 | 
						|
                if viewModel.isShowUesrBlockConfirm {
 | 
						|
                    UserBlockConfirmDialogView(
 | 
						|
                        isShowing: $viewModel.isShowUesrBlockConfirm,
 | 
						|
                        nickname: viewModel.reportUserNickname,
 | 
						|
                        confirmAction: {
 | 
						|
                            viewModel.userBlock { userId in
 | 
						|
                                viewModel.kickOutId = userId
 | 
						|
                                viewModel.kickOut()
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    )
 | 
						|
                }
 | 
						|
                
 | 
						|
                if viewModel.isShowUesrReportView {
 | 
						|
                    UserReportDialogView(
 | 
						|
                        isShowing: $viewModel.isShowUesrReportView,
 | 
						|
                        confirmAction: { reason in
 | 
						|
                            viewModel.report(type: .USER, reason: reason)
 | 
						|
                        }
 | 
						|
                    )
 | 
						|
                }
 | 
						|
                
 | 
						|
                if viewModel.isShowProfileReportConfirm {
 | 
						|
                    ProfileReportDialogView(
 | 
						|
                        isShowing: $viewModel.isShowProfileReportConfirm,
 | 
						|
                        confirmAction: {
 | 
						|
                            viewModel.report(type: .PROFILE)
 | 
						|
                        }
 | 
						|
                    )
 | 
						|
                }
 | 
						|
                
 | 
						|
                if viewModel.isShowNoChattingConfirm && viewModel.noChattingUserId > 0 {
 | 
						|
                    LiveRoomNoChattingDialogView(
 | 
						|
                        nickname: viewModel.noChattingUserNickname,
 | 
						|
                        profileUrl: viewModel.noChattingUserProfileUrl,
 | 
						|
                        confirmAction: {
 | 
						|
                            viewModel.isShowNoChattingConfirm = false
 | 
						|
                            viewModel.setNoChatting()
 | 
						|
                        },
 | 
						|
                        cancelAction: {
 | 
						|
                            viewModel.noChattingUserId = 0
 | 
						|
                            viewModel.noChattingUserNickname = ""
 | 
						|
                            viewModel.noChattingUserProfileUrl = ""
 | 
						|
                            viewModel.isShowNoChattingConfirm = false
 | 
						|
                        }
 | 
						|
                    )
 | 
						|
                }
 | 
						|
                
 | 
						|
                if viewModel.isShowPopup {
 | 
						|
                    LiveRoomDialogView(
 | 
						|
                        content: viewModel.popupContent,
 | 
						|
                        cancelTitle: viewModel.popupCancelTitle,
 | 
						|
                        cancelAction: viewModel.popupCancelAction,
 | 
						|
                        confirmTitle: viewModel.popupConfirmTitle,
 | 
						|
                        confirmAction: viewModel.popupConfirmAction
 | 
						|
                    ).onAppear {
 | 
						|
                        if viewModel.popupConfirmTitle == nil && viewModel.popupConfirmAction == nil {
 | 
						|
                            DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
 | 
						|
                                viewModel.isShowPopup = false
 | 
						|
                                viewModel.popupCancelTitle = nil
 | 
						|
                                viewModel.popupCancelAction = nil
 | 
						|
                                viewModel.popupConfirmTitle = nil
 | 
						|
                                viewModel.popupConfirmAction = nil
 | 
						|
                            }
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                }
 | 
						|
            }
 | 
						|
            
 | 
						|
            if viewModel.isShowRouletteSettings {
 | 
						|
                RouletteSettingsView(isShowing: $viewModel.isShowRouletteSettings, availableActive: true) { isActiveRoulette, message in
 | 
						|
                    self.viewModel.setActiveRoulette(
 | 
						|
                        isActiveRoulette: isActiveRoulette,
 | 
						|
                        message: message
 | 
						|
                    )
 | 
						|
                }
 | 
						|
            }
 | 
						|
            
 | 
						|
            if !viewModel.roulettePreviewList.isEmpty && viewModel.isShowRoulettePreview {
 | 
						|
                RoulettePreviewDialog(
 | 
						|
                    isShowing: $viewModel.isShowRoulettePreview,
 | 
						|
                    title: nil,
 | 
						|
                    onClickSpin: { viewModel.spinRoulette(rouletteId: $0) },
 | 
						|
                    previewList: viewModel.roulettePreviewList
 | 
						|
                )
 | 
						|
            }
 | 
						|
            
 | 
						|
            if viewModel.isShowRoulette {
 | 
						|
                RouletteViewDialog(isShowing: $viewModel.isShowRoulette, options: viewModel.rouletteItems, selectedOption: viewModel.rouletteSelectedItem) {
 | 
						|
                    viewModel.sendRouletteDonation()
 | 
						|
                }
 | 
						|
            }
 | 
						|
            
 | 
						|
            if viewModel.isLoading && viewModel.liveRoomInfo == nil {
 | 
						|
                LoadingView()
 | 
						|
            }
 | 
						|
        }
 | 
						|
        .ignoresSafeArea(.keyboard)
 | 
						|
        .edgesIgnoringSafeArea(keyboardHandler.keyboardHeight > 0 ? .bottom : .init())
 | 
						|
        .sheet(
 | 
						|
            isPresented: $viewModel.isShowShareView,
 | 
						|
            onDismiss: { viewModel.shareMessage = "" },
 | 
						|
            content: {
 | 
						|
                ActivityViewController(activityItems: [viewModel.shareMessage])
 | 
						|
            }
 | 
						|
        )
 | 
						|
        .sheet(isPresented: $viewModel.isShowPhotoPicker) {
 | 
						|
            ImagePicker(
 | 
						|
                isShowing: $viewModel.isShowPhotoPicker,
 | 
						|
                selectedImage: $viewModel.coverImage,
 | 
						|
                sourceType: .photoLibrary
 | 
						|
            )
 | 
						|
        }
 | 
						|
        .sheet(isPresented: $viewModel.isShowEditRoomInfoDialog) {
 | 
						|
            if let liveRoomInfo = viewModel.liveRoomInfo {
 | 
						|
                LiveRoomInfoEditDialog(
 | 
						|
                    isShowing: $viewModel.isShowEditRoomInfoDialog,
 | 
						|
                    isShowPhotoPicker: $viewModel.isShowPhotoPicker,
 | 
						|
                    viewModel: viewModel,
 | 
						|
                    isAdult: liveRoomInfo.isAdult,
 | 
						|
                    isEntryMessageEnabled: viewModel.isEntryMessageEnabled,
 | 
						|
                    isLoading: viewModel.isLoading,
 | 
						|
                    currentTitle: liveRoomInfo.title,
 | 
						|
                    currentNotice: liveRoomInfo.notice,
 | 
						|
                    coverImageUrl: liveRoomInfo.coverImageUrl,
 | 
						|
                    coverImage: viewModel.coverImage
 | 
						|
                ) { newTitle, newNotice, isAdult, isEntryMessageEnabled in
 | 
						|
                    self.viewModel.editLiveRoomInfo(
 | 
						|
                        title: newTitle,
 | 
						|
                        notice: newNotice,
 | 
						|
                        isAdult: isAdult
 | 
						|
                    )
 | 
						|
                    self.viewModel.isEntryMessageEnabled = isEntryMessageEnabled
 | 
						|
                }
 | 
						|
            } else {
 | 
						|
                EmptyView()
 | 
						|
                    .onAppear {
 | 
						|
                        viewModel.isShowEditRoomInfoDialog = false
 | 
						|
                    }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        .sheet(isPresented: $viewModel.isShowDonationRankingPopup) {
 | 
						|
            LiveRoomDonationRankingDialog(isShowing: $viewModel.isShowDonationRankingPopup)
 | 
						|
        }
 | 
						|
        .sheet(isPresented: $viewModel.isShowHeartRankingPopup) {
 | 
						|
            LiveRoomHeartRankingDialog(
 | 
						|
                isShowing: $viewModel.isShowHeartRankingPopup,
 | 
						|
                isShowPopup: $viewModel.isShowPopup,
 | 
						|
                errorMessage: viewModel.errorMessage,
 | 
						|
                isLoading: viewModel.isLoading,
 | 
						|
                heartStatus: viewModel.heartStatus
 | 
						|
            ) {
 | 
						|
                viewModel.getHeartStatus()
 | 
						|
            }
 | 
						|
        }
 | 
						|
        .sheet(isPresented: $viewModel.isShowDonationMessagePopup) {
 | 
						|
            LiveRoomDonationMessageDialog(viewModel: viewModel, isShowing: $viewModel.isShowDonationMessagePopup)
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    private func estimatedHeight(for text: String, width: CGFloat) -> CGFloat {
 | 
						|
        let textView = UITextView(frame: CGRect(x: 0, y: 0, width: width, height: .greatestFiniteMagnitude))
 | 
						|
        textView.font = UIFont.systemFont(ofSize: 11.3)
 | 
						|
        textView.text = text
 | 
						|
        return textView.sizeThatFits(CGSize(width: width, height: .greatestFiniteMagnitude)).height
 | 
						|
    }
 | 
						|
    
 | 
						|
    private func inviteSpeaker(peerId: Int) {
 | 
						|
        if viewModel.liveRoomInfo!.speakerList.count <= 5 {
 | 
						|
            viewModel.inviteSpeaker(peerId: peerId)
 | 
						|
            self.viewModel.popupContent = "스피커 요청을 보냈습니다.\n잠시만 기다려 주세요."
 | 
						|
            self.viewModel.isShowPopup = true
 | 
						|
        } else {
 | 
						|
            viewModel.popupContent = "스피커 정원을 초과했습니다."
 | 
						|
            viewModel.isShowPopup = true
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    private var scrollObservableView: some View {
 | 
						|
        GeometryReader { proxy in
 | 
						|
            let offsetY = proxy.frame(in: .global).origin.y
 | 
						|
            Color.clear
 | 
						|
                .preference(
 | 
						|
                    key: ScrollOffsetKey.self,
 | 
						|
                    value: offsetY
 | 
						|
                )
 | 
						|
                .onAppear {
 | 
						|
                    viewModel.setOriginOffset(offsetY)
 | 
						|
                }
 | 
						|
        }
 | 
						|
        .frame(height: 0)
 | 
						|
    }
 | 
						|
    
 | 
						|
    struct ScrollOffsetKey: PreferenceKey {
 | 
						|
        static var defaultValue: CGFloat = .zero
 | 
						|
        static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
 | 
						|
            value += nextValue()
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    struct TextViewHeightKey: PreferenceKey {
 | 
						|
        static var defaultValue: CGFloat = .zero
 | 
						|
        static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
 | 
						|
            value = nextValue()
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
struct LiveRoomViewV2_Previews: PreviewProvider {
 | 
						|
    static var previews: some View {
 | 
						|
        LiveRoomViewV2()
 | 
						|
    }
 | 
						|
}
 |