Files
sodalive-ios/SodaLive/Sources/Content/Detail/ContentDetailViewModel.swift

544 lines
21 KiB
Swift

//
// ContentDetailViewModel.swift
// SodaLive
//
// Created by klaus on 2023/08/11.
//
import Foundation
import Combine
final class ContentDetailViewModel: ObservableObject {
private let repository = ContentRepository()
private let reportRepository = ReportRepository()
private var userRepository = UserRepository()
private var subscription = Set<AnyCancellable>()
@Published var errorMessage = ""
@Published var isShowPopup = false
@Published var isLoading = false
@Published var isShowPreviewAlert = false
@Published var isExpandDescription = false
@Published var isShowDonationPopup = false
@Published var isShowShareView = false
@Published var shareMessage = ""
@Published private(set) var audioContent: GetAudioContentDetailResponse?
@Published var orderType: OrderType?
@Published var isShowReportMenu = false
@Published var isShowReportView = false
@Published var isShowDeleteConfirm = false
@Published var isShowNoticePinContentPopup = false
var contentId: Int = 0 {
didSet {
getAudioContentDetail()
}
}
func getAudioContentDetail() {
audioContent = nil
isLoading = true
repository.getAudioContentDetail(audioContentId: contentId)
.sink { result in
switch result {
case .finished:
DEBUG_LOG("finish")
case .failure(let error):
ERROR_LOG(error.localizedDescription)
}
} receiveValue: { [unowned self] response in
let responseData = response.data
do {
let jsonDecoder = JSONDecoder()
let decoded = try jsonDecoder.decode(ApiResponse<GetAudioContentDetailResponse>.self, from: responseData)
if let data = decoded.data, decoded.success {
if AppState.shared.purchasedContentId > 0 && AppState.shared.purchasedContentId == data.contentId {
self.order(orderType: AppState.shared.purchasedContentOrderType)
} else {
self.audioContent = data
}
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = I18n.Common.commonError
}
self.isShowPopup = true
}
} catch {
print(error)
self.errorMessage = I18n.Common.commonError
self.isShowPopup = true
}
self.isLoading = false
}
.store(in: &subscription)
}
func creatorFollow(creatorId: Int, follow: Bool = true, notify: Bool = true) {
isLoading = true
userRepository.creatorFollow(creatorId: creatorId, follow: follow, notify: notify)
.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.getAudioContentDetail()
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = I18n.Common.commonError
}
self.isShowPopup = true
}
} catch {
self.errorMessage = I18n.Common.commonError
self.isShowPopup = true
}
}
.store(in: &subscription)
}
func creatorUnFollow(userId: Int) {
isLoading = true
userRepository.creatorUnFollow(creatorId: userId)
.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.getAudioContentDetail()
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = I18n.Common.commonError
}
self.isShowPopup = true
}
} catch {
self.errorMessage = I18n.Common.commonError
self.isShowPopup = true
}
}
.store(in: &subscription)
}
func likeContent() {
isLoading = true
repository.likeContent(audioContentId: contentId)
.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(ApiResponse<PutAudioContentLikeResponse>.self, from: responseData)
if decoded.success {
self.getAudioContentDetail()
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = I18n.Common.commonError
}
self.isShowPopup = true
}
} catch {
self.errorMessage = I18n.Common.commonError
self.isShowPopup = true
}
}
.store(in: &subscription)
}
func shareAudioContent(contentImage: String, contentTitle: String) {
isLoading = true
let params = [
"af_dp": "voiceon://",
"deep_link_value": "content",
"deep_link_sub5": "\(contentId)"
]
if let shareUrl = createOneLinkUrlWithURLComponents(params: params) {
self.shareMessage = shareUrl
self.isShowShareView = true
} else {
self.errorMessage = I18n.ContentDetail.shareLinkCreateFailed
self.isShowPopup = true
}
self.isLoading = false
}
func registerComment(comment: String, isSecret: Bool) {
if comment.trimmingCharacters(in: .whitespaces).isEmpty {
return
}
isLoading = true
repository.registerComment(audioContentId: contentId, comment: comment, isSecret: isSecret)
.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.getAudioContentDetail()
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = I18n.Common.commonError
}
self.isShowPopup = true
}
} catch {
self.errorMessage = I18n.Common.commonError
self.isShowPopup = true
}
}
.store(in: &subscription)
}
func order(orderType: OrderType) {
isShowPreviewAlert = false
isLoading = true
repository.orderAudioContent(contentId: contentId, orderType: orderType)
.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 {
AppState.shared.purchasedContentId = 0
AppState.shared.purchasedContentOrderType = .KEEP
self.orderType = nil
self.errorMessage = orderType == .RENTAL ? I18n.ContentDetail.rentalCompleted : I18n.ContentDetail.purchaseCompleted
self.isShowPopup = true
self.getAudioContentDetail()
ContentPlayManager.shared.conditionalStopAudio(contentId: contentId)
} else {
if let message = decoded.message {
self.errorMessage = message
if isInsufficientCanError(message: message, errorProperty: decoded.errorProperty) {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
AppState.shared.setAppStep(step: .canCharge(refresh: {}, afterCompletionToGoBack: true))
}
}
} else {
self.errorMessage = I18n.Common.commonError
}
self.isShowPopup = true
}
} catch {
self.errorMessage = I18n.Common.commonError
self.isShowPopup = true
}
}
.store(in: &subscription)
}
func report(type: ReportType, audioContentId: Int? = nil, reason: String = "프로필 신고") {
isLoading = true
let request = ReportRequest(type: type, reason: reason, reportedMemberId: nil, cheersId: nil, audioContentId: audioContentId)
reportRepository.report(request: request)
.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 let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = I18n.Common.commonError
}
self.isShowPopup = true
} catch {
self.errorMessage = I18n.Common.commonError
self.isShowPopup = true
}
}
.store(in: &subscription)
}
func deleteAudioContent(onSuccess: @escaping () -> Void) {
isLoading = true
repository.deleteAudioContent(audioContentId: contentId)
.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.orderType = nil
self.errorMessage = I18n.ContentDetail.deleteCompleted
self.isShowPopup = true
onSuccess()
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = I18n.Common.commonError
}
self.isShowPopup = true
}
} catch {
self.errorMessage = I18n.Common.commonError
self.isShowPopup = true
}
}
.store(in: &subscription)
}
func donation(can: Int, comment: String) {
if can <= 0 {
self.errorMessage = I18n.LiveRoom.atLeastOneCanDonationMessage
self.isShowPopup = true
} else if comment.trimmingCharacters(in: .whitespaces).isEmpty {
self.errorMessage = I18n.ContentDetail.donationMessageRequired
self.isShowPopup = true
} else {
isLoading = true
repository.donation(contentId: contentId, can: can, comment: comment)
.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 {
UserDefaults.set(UserDefaults.int(forKey: .can) - can, forKey: .can)
self.errorMessage = I18n.ContentDetail.donationCompleted(can)
self.isShowPopup = true
self.getAudioContentDetail()
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = I18n.Common.commonError
}
self.isShowPopup = true
}
} catch {
self.errorMessage = I18n.Common.commonError
self.isShowPopup = true
}
}
.store(in: &subscription)
}
}
func pinContent(contentId: Int) {
isLoading = true
repository.pinContent(contentId: contentId)
.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.errorMessage = I18n.ContentDetail.pinCompleted
self.isShowPopup = true
self.getAudioContentDetail()
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = I18n.Common.commonError
}
self.isShowPopup = true
}
} catch {
self.errorMessage = I18n.Common.commonError
self.isShowPopup = true
}
}
.store(in: &subscription)
}
func unpinContent(contentId: Int) {
isLoading = true
repository.unpinContent(contentId: contentId)
.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.errorMessage = I18n.ContentDetail.unpinCompleted
self.isShowPopup = true
self.getAudioContentDetail()
} else {
if let message = decoded.message {
self.errorMessage = message
} else {
self.errorMessage = I18n.Common.commonError
}
self.isShowPopup = true
}
} catch {
self.errorMessage = I18n.Common.commonError
self.isShowPopup = true
}
}
.store(in: &subscription)
}
private func isInsufficientCanError(message: String, errorProperty: String?) -> Bool {
let normalizedErrorProperty = errorProperty?.lowercased() ?? ""
if normalizedErrorProperty.contains("not_enough_can") ||
normalizedErrorProperty.contains("insufficient_can") ||
normalizedErrorProperty.contains("lack_of_can")
{
return true
}
let normalizedMessage = message.lowercased()
let insufficientCanPatterns = [
"캔이 부족",
"캔 부족",
"not enough can",
"insufficient can",
"not enough cans",
"insufficient cans",
"canが不足"
]
return insufficientCanPatterns.contains { normalizedMessage.contains($0) }
}
}