724 lines
36 KiB
Swift
724 lines
36 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
|
|
|
|
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,
|
|
totalDonationCan: viewModel.totalDonationCan,
|
|
participantsCount: liveRoomInfo.participantsCount,
|
|
isOnBg: viewModel.isBgOn,
|
|
isOnNotice: viewModel.isShowNotice,
|
|
isOnMenuPan: viewModel.isShowMenuPan,
|
|
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()
|
|
},
|
|
onClickTotalDonation: {
|
|
viewModel.isShowDonationRankingPopup = true
|
|
},
|
|
onClickParticipants: {
|
|
viewModel.isShowProfileList = true
|
|
}
|
|
)
|
|
} else {
|
|
LiveRoomInfoGuestView(
|
|
title: liveRoomInfo.title,
|
|
totalDonationCan: viewModel.totalDonationCan,
|
|
isOnBg: viewModel.isBgOn,
|
|
isOnNotice: viewModel.isShowNotice,
|
|
isOnMenuPan: viewModel.isShowMenuPan,
|
|
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()
|
|
},
|
|
onClickTotalDonation: {
|
|
viewModel.isShowDonationRankingPopup = true
|
|
},
|
|
onClickChangeListener: {
|
|
viewModel.setListener()
|
|
}
|
|
)
|
|
}
|
|
|
|
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))
|
|
.resizable()
|
|
.scaledToFit()
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
}
|
|
|
|
Rectangle()
|
|
.foregroundColor(.black.opacity(0.25))
|
|
.frame(maxWidth: .infinity)
|
|
|
|
ScrollView(.vertical, showsIndicators: false) {
|
|
scrollObservableView
|
|
|
|
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()
|
|
|
|
VStack(spacing: 13.3) {
|
|
if liveRoomInfo.creatorId == UserDefaults.int(forKey: .userId) {
|
|
Image("ic_roulette_settings")
|
|
.resizable()
|
|
.frame(width: 26.7, height: 26.7)
|
|
.padding(11)
|
|
.background(Color(hex: "525252").opacity(0.6))
|
|
.cornerRadius(10)
|
|
.onTapGesture {
|
|
viewModel.isShowRouletteSettings = true
|
|
}
|
|
} else if liveRoomInfo.creatorId != UserDefaults.int(forKey: .userId) && viewModel.isActiveRoulette {
|
|
Image("ic_roulette")
|
|
.resizable()
|
|
.frame(width: 26.7, height: 26.7)
|
|
.padding(11)
|
|
.background(Color(hex: "525252").opacity(0.6))
|
|
.cornerRadius(10)
|
|
.onTapGesture {
|
|
viewModel.showRoulette()
|
|
}
|
|
}
|
|
|
|
if viewModel.role == .SPEAKER {
|
|
Image(viewModel.isMute ? "ic_mic_off" : "ic_mic_on")
|
|
.resizable()
|
|
.frame(width: 26.7, height: 26.7)
|
|
.padding(11)
|
|
.background(Color(hex: "525252").opacity(0.6))
|
|
.cornerRadius(10)
|
|
.onTapGesture {
|
|
viewModel.toggleMute()
|
|
}
|
|
}
|
|
|
|
Image(viewModel.isSpeakerMute ? "ic_speaker_off" : "ic_speaker_on")
|
|
.resizable()
|
|
.frame(width: 26.7, height: 26.7)
|
|
.padding(11)
|
|
.background(Color(hex: "525252").opacity(0.6))
|
|
.cornerRadius(10)
|
|
.onTapGesture {
|
|
viewModel.toggleSpeakerMute()
|
|
}
|
|
|
|
if liveRoomInfo.creatorId == UserDefaults.int(forKey: .userId) &&
|
|
UserDefaults.string(forKey: .role) == MemberRole.CREATOR.rawValue {
|
|
Image("ic_donation_message_list")
|
|
.resizable()
|
|
.frame(width: 26.7, height: 26.7)
|
|
.padding(11)
|
|
.background(Color(hex: "525252").opacity(0.6))
|
|
.cornerRadius(10)
|
|
.onTapGesture {
|
|
viewModel.isShowDonationMessagePopup = true
|
|
}
|
|
} else {
|
|
Image("ic_donation")
|
|
.resizable()
|
|
.frame(width: 26.7, height: 26.7)
|
|
.padding(11)
|
|
.background(Color(hex: "525252").opacity(0.6))
|
|
.cornerRadius(10)
|
|
.onTapGesture {
|
|
viewModel.isShowDonationPopup = true
|
|
}
|
|
}
|
|
}
|
|
.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.signatureImageUrl.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 {
|
|
VStack(spacing: 0) {
|
|
Spacer()
|
|
|
|
AnimatedImage(url: URL(string: viewModel.signatureImageUrl))
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fit)
|
|
.frame(height: 300)
|
|
.frame(maxWidth: .infinity)
|
|
.padding(.horizontal, 20)
|
|
.padding(.bottom, 65)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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")
|
|
.padding(.leading, 60)
|
|
|
|
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)
|
|
}
|
|
.padding(8)
|
|
.background(Color.gray33)
|
|
.padding(.horizontal, 60)
|
|
.padding(.bottom, 120)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.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()
|
|
|
|
NotificationCenter.default.addObserver(
|
|
forName: UIApplication.willTerminateNotification,
|
|
object: nil,
|
|
queue: .main) { _ in
|
|
viewModel.quitRoom()
|
|
sleep(3)
|
|
}
|
|
}
|
|
.onDisappear {
|
|
UIApplication.shared.isIdleTimerDisabled = false
|
|
NotificationCenter.default.removeObserver(self)
|
|
}
|
|
|
|
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 in
|
|
viewModel.donation(can: can, message: message)
|
|
}
|
|
}
|
|
|
|
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(hex: "222222"))
|
|
.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) { isActiveRoulette, message in
|
|
self.viewModel.setActiveRoulette(
|
|
isActiveRoulette: isActiveRoulette,
|
|
message: message
|
|
)
|
|
}
|
|
}
|
|
|
|
if let preview = viewModel.roulettePreview, viewModel.isShowRoulettePreview {
|
|
RoulettePreviewDialog(
|
|
isShowing: $viewModel.isShowRoulettePreview,
|
|
title: nil,
|
|
onClickSpin: { viewModel.spinRoulette() },
|
|
preview: preview
|
|
)
|
|
}
|
|
|
|
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,
|
|
isLoading: viewModel.isLoading,
|
|
currentTitle: liveRoomInfo.title,
|
|
currentNotice: liveRoomInfo.notice,
|
|
coverImageUrl: liveRoomInfo.coverImageUrl,
|
|
coverImage: viewModel.coverImage
|
|
) { newTitle, newNotice in
|
|
self.viewModel.editLiveRoomInfo(
|
|
title: newTitle,
|
|
notice: newNotice
|
|
)
|
|
}
|
|
} else {
|
|
EmptyView()
|
|
.onAppear {
|
|
viewModel.isShowEditRoomInfoDialog = false
|
|
}
|
|
}
|
|
}
|
|
.sheet(isPresented: $viewModel.isShowDonationRankingPopup) {
|
|
LiveRoomDonationRankingDialog(isShowing: $viewModel.isShowDonationRankingPopup)
|
|
}
|
|
.sheet(isPresented: $viewModel.isShowDonationMessagePopup) {
|
|
LiveRoomDonationMessageDialog(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 <= 4 {
|
|
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 LiveRoomViewV2_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
LiveRoomViewV2()
|
|
}
|
|
}
|