// // ContentCreateViewModel.swift // SodaLive // // Created by klaus on 2023/08/11. // import UIKit import Moya import Combine final class ContentCreateViewModel: ObservableObject { private let repository = ContentRepository() private var subscription = Set() @Published var isLoading = false @Published var isShowCompletePopup = false @Published var errorMessage = "" @Published var isShowPopup = false @Published var fileName = "" @Published var title = "" @Published var detail = "" @Published var hashtags: String = "" @Published var theme: GetAudioContentThemeResponse? = nil @Published var coverImage: UIImage? = nil @Published var selectedFileUrl: URL? = nil { didSet { if let fileUrl = selectedFileUrl { fileName = fileUrl.lastPathComponent } } } @Published var isAvailableComment = true @Published var isActiveReservation = false @Published var isAdult = false @Published var priceString = "0" { didSet { if priceString.count > 5 { priceString = String(priceString.prefix(5)) } else { if let price = Int(priceString) { self.price = price } else { self.price = 0 } } } } @Published var price = 0 @Published var isFree = true { didSet { if isFree { priceString = "0" isLimited = false isOnlyRental = false isGeneratePreview = true } } } @Published var isOnlyRental = false { didSet { if isOnlyRental { isLimited = false } } } @Published var isGeneratePreview = true @Published var isLimited = false { didSet { if !isLimited { limitedString = "" } } } @Published var limited: Int? = nil { didSet { if let limited = limited, limited <= 0 { limitedString = "" } } } @Published var limitedString: String = "" { didSet { if limitedString == "" { limited = nil } else { limited = Int(limitedString) } } } @Published var previewStartTime: String = "" @Published var previewEndTime: String = "" @Published var releaseDateString: String = Date().convertDateFormat(dateFormat: "yyyy.MM.dd") @Published var releaseTimeString: String = Date().convertDateFormat(dateFormat: "a hh:mm") var releaseDate = Date() { willSet { releaseDateString = newValue.convertDateFormat(dateFormat: "yyyy.MM.dd") } } var releaseTime = Date() { willSet { releaseTimeString = newValue.convertDateFormat(dateFormat: "a hh:mm") } } var placeholder = "내용을 입력하세요" func uploadAudioContent() { if !isLoading && validateData() { isLoading = true let request = CreateAudioContentRequest( title: title, detail: detail, tags: hashtags, price: price, limited: limited, releaseDate: isActiveReservation ? "\(releaseDate.convertDateFormat(dateFormat: "yyyy-MM-dd")) \(releaseTime.convertDateFormat(dateFormat: "HH:mm"))" : nil, timezone: TimeZone.current.identifier, themeId: theme!.id, isAdult: isAdult, isOnlyRental: isOnlyRental, isGeneratePreview: isGeneratePreview, isCommentAvailable: isAvailableComment, previewStartTime: isGeneratePreview && previewStartTime.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 ? previewStartTime : nil, previewEndTime: isGeneratePreview && previewEndTime.trimmingCharacters(in: .whitespacesAndNewlines).count > 0 ? previewEndTime : nil ) var multipartData = [MultipartFormData]() let encoder = JSONEncoder() encoder.outputFormatting = .withoutEscapingSlashes let jsonData = try? encoder.encode(request) if let jsonData = jsonData { if let coverImage = coverImage, let imageData = coverImage.jpegData(compressionQuality: 0.8) { multipartData.append( MultipartFormData( provider: .data(imageData), name: "coverImage", fileName: "\(UUID().uuidString)_\(Date().timeIntervalSince1970 * 1000).jpg", mimeType: "image/*") ) } else { errorMessage = "커버이미지를 업로드 하지 못했습니다.\n다시 선택해 주세요" isShowPopup = true isLoading = false return } if let selectedFileUrl = selectedFileUrl { if selectedFileUrl.startAccessingSecurityScopedResource() { defer { selectedFileUrl.stopAccessingSecurityScopedResource() } if let data = try? Data(contentsOf: selectedFileUrl) { multipartData.append( MultipartFormData( provider: .data(data), name: "contentFile", fileName: selectedFileUrl.lastPathComponent, mimeType: "audio/*" ) ) } else { errorMessage = "콘텐츠 파일을 업로드 하지 못했습니다.\n다시 선택해 주세요" isShowPopup = true isLoading = false return } } else { errorMessage = "콘텐츠 파일을 업로드 하지 못했습니다.\n다시 선택해 주세요" isShowPopup = true isLoading = false return } } else { errorMessage = "콘텐츠 파일을 업로드 하지 못했습니다.\n다시 선택해 주세요" isShowPopup = true isLoading = false return } multipartData.append(MultipartFormData(provider: .data(jsonData), name: "request")) repository .uploadAudioContent(parameters: multipartData) .sink { result in switch result { case .finished: DEBUG_LOG("finish") case .failure(let error): ERROR_LOG(error.localizedDescription) } } receiveValue: { [unowned self] response in self.isLoading = false let responseData = response.data do { let jsonDecoder = JSONDecoder() let decoded = try jsonDecoder.decode(ApiResponseWithoutData.self, from: responseData) if decoded.success { self.isShowCompletePopup = true } else { if let message = decoded.message { self.errorMessage = message } else { self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." } self.isShowPopup = true } } catch { self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." self.isShowPopup = true } } .store(in: &subscription) } else { self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." self.isShowPopup = true self.isLoading = false } } } private func validateData() -> Bool { if title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { errorMessage = "제목을 입력해 주세요." isShowPopup = true return false } if detail.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty || detail.count < 5 { errorMessage = "내용을 5자 이상 입력해 주세요." isShowPopup = true return false } if theme == nil { errorMessage = "테마를 선택해 주세요." isShowPopup = true return false } if coverImage == nil { errorMessage = "커버이미지를 선택해 주세요." isShowPopup = true return false } if selectedFileUrl == nil { errorMessage = "오디오 콘텐츠를 선택해 주세요." isShowPopup = true return false } if !isFree && price < 5 { errorMessage = "콘텐츠의 최소금액은 5캔 입니다." isShowPopup = true return false } if previewStartTime.count > 0 && previewEndTime.count > 0 { let startTimeArray = previewStartTime.split(separator: ":") if startTimeArray.count != 3 { errorMessage = "미리 듣기 시간 형식은 00:30:00 과 같아야 합니다" isShowPopup = true return false } for time in startTimeArray { if time.count != 2 { errorMessage = "미리 듣기 시간 형식은 00:30:00 과 같아야 합니다" isShowPopup = true return false } } let endTimeArray = previewStartTime.split(separator: ":") if endTimeArray.count != 3 { errorMessage = "미리 듣기 시간 형식은 00:30:00 과 같아야 합니다" isShowPopup = true return false } for time in endTimeArray { if time.count != 2 { errorMessage = "미리 듣기 시간 형식은 00:30:00 과 같아야 합니다" isShowPopup = true return false } } let timeDifference = timeDifference(startTime: previewStartTime, endTime: previewEndTime) if timeDifference < 30.0 { errorMessage = "미리 듣기의 최소 시간은 30초 입니다" isShowPopup = true return false } } else { if previewStartTime.count > 0 || previewEndTime.count > 0 { errorMessage = "미리 듣기 시작 시간과 종료 시간 둘 다 입력을 하거나 둘 다 입력 하지 않아야 합니다." isShowPopup = true return false } } return true } private func timeDifference(startTime: String, endTime: String) -> Double { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "HH:mm:ss" if let date1 = dateFormatter.date(from: startTime), let date2 = dateFormatter.date(from: endTime) { return date2.timeIntervalSince(date1) } return 0 } }