// // ContentDetailView.swift // SodaLive // // Created by klaus on 2023/08/11. // import SwiftUI import Kingfisher import RefreshableScrollView struct ContentDetailView: View { let contentId: Int @Environment(\.presentationMode) var presentationMode: Binding @StateObject private var viewModel = ContentDetailViewModel() @State private var isShowOrderView = false @State private var isShowOrderConfirmView = false @State private var isShowCommentListView = false var body: some View { GeometryReader { proxy in BaseView(isLoading: $viewModel.isLoading) { VStack(spacing: 0) { HStack(spacing: 0) { Button { if presentationMode.wrappedValue.isPresented { presentationMode.wrappedValue.dismiss() } else { AppState.shared.back() } } label: { Image("ic_back") .resizable() .frame(width: 20, height: 20) Text("콘텐츠 상세") .font(.custom(Font.bold.rawValue, size: 18.3)) .foregroundColor(Color(hex: "eeeeee")) } Spacer() Image("ic_seemore_vertical") .onTapGesture { viewModel.isShowReportMenu = true } } .padding(.horizontal, 13.3) .frame(height: 50) .background(Color.black) if let audioContent = viewModel.audioContent { ContentDetailCreatorProfileView( creator: audioContent.creator, onClickFollow: { viewModel.creatorFollow(userId: $0) }, onClickUnFollow: { viewModel.creatorUnFollow(userId: $0) } ) .padding(.horizontal, 13.3) .padding(.top, 5.3) .onTapGesture { AppState.shared .setAppStep(step: .creatorDetail(userId: audioContent.creator.creatorId)) } ZStack { RefreshableScrollView( refreshing: $viewModel.isLoading, action: { viewModel.getAudioContentDetail() }) { VStack(spacing: 0) { ContentDetailPlayView( audioContent: audioContent, isAlertPreview: audioContent.price > 0 && !audioContent.existOrdered && audioContent.orderType == nil && audioContent.creator.creatorId != UserDefaults.int(forKey: .userId), isShowPreviewAlert: $viewModel.isShowPreviewAlert ) ContentDetailInfoView( isExpandDescription: $viewModel.isExpandDescription, isShowPreviewAlert: $viewModel.isShowPreviewAlert, audioContent: audioContent, onClickLike: { viewModel.likeContent() }, onClickShare: { viewModel.shareAudioContent( contentImage: audioContent.coverImageUrl, contentTitle: "\(audioContent.title) - \(audioContent.creator.nickname)" ) }, onClickDonation: { viewModel.isShowDonationPopup = true } ) .padding(.horizontal, 13.3) if let releaseDate = audioContent.releaseDate { Text(releaseDate) .font(.custom(Font.bold.rawValue, size: 13.3)) .foregroundColor(.white) .frame(maxWidth: .infinity) .frame(height: 48.7) .background(Color(hex: "525252")) .cornerRadius(5.3) .padding(.top, 18.3) .padding(.horizontal, 13.3) } else if audioContent.price > 0 && !audioContent.existOrdered && audioContent.orderType == nil && audioContent.creator.creatorId != UserDefaults.int(forKey: .userId) { ContentDetailPurchaseButton(price: audioContent.price, isOnlyRental: audioContent.isOnlyRental) .contentShape(Rectangle()) .padding(.horizontal, 13.3) .onTapGesture { isShowOrderView = true } } if audioContent.isCommentAvailable { ContentDetailCommentView( commentCount: audioContent.commentCount, commentList: audioContent.commentList, registerComment: { comment in self.viewModel.registerComment(comment: comment) } ) .padding(10.3) .background(Color.white.opacity(0.1)) .cornerRadius(5.3) .padding(.top, 13.3) .contentShape(Rectangle()) .padding(.horizontal, 13.3) .onTapGesture { if audioContent.commentCount > 0 { isShowCommentListView = true } } } ContentDetailOtherContentView( title: "크리에이터의 다른 콘텐츠", items: audioContent.creatorOtherContentList, onClickItem: { viewModel.contentId = $0 } ) .padding(.top, 26.7) .padding(.horizontal, 13.3) Rectangle() .foregroundColor(Color(hex: "232323")) .frame(height: 6.7) .padding(.top, 24) ContentDetailOtherContentView( title: "테마의 다른 콘텐츠", items: audioContent.sameThemeOtherContentList, onClickItem: { viewModel.contentId = $0 } ) .padding(.top, 26.7) .padding(.horizontal, 13.3) } } if audioContent.isMosaic { ContentDetailMosaicView() } } .padding(.top, 13.3) } Spacer() } .navigationTitle("") .navigationBarBackButtonHidden() .onAppear { viewModel.contentId = contentId AppState.shared.pushAudioContentId = 0 } if let audioContent = viewModel.audioContent, isShowOrderView { VStack(spacing: 0) { ContentOrderDialogView( isShowing: $isShowOrderView, price: audioContent.price, isOnlyRental: audioContent.isOnlyRental, onTapPurchase: { viewModel.orderType = $0 isShowOrderConfirmView = true } ) if proxy.safeAreaInsets.bottom > 0 { Rectangle() .foregroundColor(Color(hex: "222222")) .frame(width: proxy.size.width, height: 15.3) } } .ignoresSafeArea() } if let orderType = viewModel.orderType, let audioContent = viewModel.audioContent, isShowOrderConfirmView { VStack(spacing: 0) { ContentOrderConfirmDialogView( isShowing: $isShowOrderConfirmView, audioContent: audioContent, orderType: orderType, isOnlyRental: audioContent.isOnlyRental, onClickConfirm: { viewModel.order(orderType: orderType) } ) } .ignoresSafeArea() } ZStack { if viewModel.isShowReportMenu { VStack(spacing: 0) { ContentDetailMenuView( isShowing: $viewModel.isShowReportMenu, isShowCreatorMenu: viewModel.audioContent!.creator.creatorId == UserDefaults.int(forKey: .userId), modifyAction: { if viewModel.audioContent!.creator.creatorId == UserDefaults.int(forKey: .userId) { AppState .shared .setAppStep( step: .modifyContent(contentId: contentId) ) } }, deleteAction: { if viewModel.audioContent!.creator.creatorId == UserDefaults.int(forKey: .userId) { viewModel.isShowDeleteConfirm = true } }, reportAction: { viewModel.isShowReportView = true } ) if proxy.safeAreaInsets.bottom > 0 { Rectangle() .foregroundColor(Color(hex: "222222")) .frame(width: proxy.size.width, height: 15.3) } } .ignoresSafeArea() } if viewModel.isShowReportView { AudioContentReportDialogView( isShowing: $viewModel.isShowReportView, confirmAction: { reason in viewModel.report( type: .AUDIO_CONTENT, audioContentId: contentId, reason: reason ) } ) } if viewModel.isShowDeleteConfirm { AudioContentDeleteDialogView( isShowing: $viewModel.isShowDeleteConfirm, title: viewModel.audioContent!.title, confirmAction: { viewModel.deleteAudioContent { DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { AppState.shared.back() } } }, showToast: { viewModel.errorMessage = "동의하셔야 삭제할 수 있습니다." viewModel.isShowPopup = true } ) } if viewModel.isShowDonationPopup { LiveRoomDonationDialogView(isShowing: $viewModel.isShowDonationPopup, isAudioContentDonation: true) { can, comment in viewModel.donation(can: can, comment: comment) } } } } .sheet( isPresented: $viewModel.isShowShareView, onDismiss: { viewModel.shareMessage = "" }, content: { ActivityViewController(activityItems: [viewModel.shareMessage]) } ) .sheet( isPresented: $isShowCommentListView, content: { AudioContentCommentListView( isPresented: $isShowCommentListView, creatorId: viewModel.audioContent!.creator.creatorId, audioContentId: viewModel.audioContent!.contentId ) } ) .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) { GeometryReader { geo in HStack { Spacer() Text(viewModel.errorMessage) .padding(.vertical, 13.3) .frame(width: screenSize().width - 66.7, alignment: .center) .font(.custom(Font.medium.rawValue, size: 12)) .background(Color(hex: "9970ff")) .foregroundColor(Color.white) .multilineTextAlignment(.center) .cornerRadius(20) .padding(.top, 66.7) Spacer() } } } } } }