355 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
			
		
		
	
	
			355 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Swift
		
	
	
	
	
	
//
 | 
						|
//  AuditionRoleDetailViewModel.swift
 | 
						|
//  SodaLive
 | 
						|
//
 | 
						|
//  Created by klaus on 1/6/25.
 | 
						|
//
 | 
						|
 | 
						|
import Foundation
 | 
						|
import Moya
 | 
						|
import Combine
 | 
						|
 | 
						|
final class AuditionRoleDetailViewModel: ObservableObject {
 | 
						|
    
 | 
						|
    private let repository = AuditionRepository()
 | 
						|
    private var subscription = Set<AnyCancellable>()
 | 
						|
    
 | 
						|
    @Published var errorMessage = ""
 | 
						|
    @Published var isShowPopup = false
 | 
						|
    @Published var isLoading = false
 | 
						|
    
 | 
						|
    @Published var totalCount = 0
 | 
						|
    @Published var applicantList = [GetAuditionRoleApplicantItem]()
 | 
						|
    
 | 
						|
    @Published var name = "보이스온"
 | 
						|
    @Published var auditionRoleDetail: GetAuditionRoleDetailResponse? = nil
 | 
						|
    
 | 
						|
    @Published private(set) var sortType = AuditionApplicantSortType.NEWEST {
 | 
						|
        didSet {
 | 
						|
            refreshApplicantList()
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    @Published var fileName = ""
 | 
						|
    @Published var soundData: Data? = nil
 | 
						|
    @Published var phoneNumber = ""
 | 
						|
    
 | 
						|
    @Published var isShowNotifyVote = true
 | 
						|
    @Published var isShowVoteCompleteView = false
 | 
						|
    @Published var isShowNoticeReapply = false
 | 
						|
    @Published var dialogTitle = ""
 | 
						|
    @Published var dialogDesc = ""
 | 
						|
    
 | 
						|
    var page = 1
 | 
						|
    var isLast = false
 | 
						|
    private var pageSize = 10
 | 
						|
    
 | 
						|
    var auditionRoleId = -1 {
 | 
						|
        didSet {
 | 
						|
            if auditionRoleId > 0 {
 | 
						|
                getAuditionRoleDetail()
 | 
						|
            } else {
 | 
						|
                onFailure()
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    var onFailure: () -> Void = {}
 | 
						|
    
 | 
						|
    func setSortType(sortType: AuditionApplicantSortType) {
 | 
						|
        if self.sortType != sortType {
 | 
						|
            self.sortType = sortType
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    func getAuditionRoleDetail() {
 | 
						|
        isLoading = true
 | 
						|
        
 | 
						|
        let auditionRoleDetail = repository.getAuditionRoleDetail(auditionRoleId: auditionRoleId)
 | 
						|
        let auditionApplicantList = repository.getAuditionApplicantList(auditionRoleId: auditionRoleId, sortType: sortType, page: page, size: pageSize)
 | 
						|
        
 | 
						|
        Publishers
 | 
						|
            .CombineLatest(auditionRoleDetail, auditionApplicantList)
 | 
						|
            .sink { result in
 | 
						|
                switch result {
 | 
						|
                case .finished:
 | 
						|
                    DEBUG_LOG("finish")
 | 
						|
                case .failure(let error):
 | 
						|
                    ERROR_LOG(error.localizedDescription)
 | 
						|
                }
 | 
						|
            } receiveValue: { [unowned self] (roleDetailResponse, applicantListResponse) in
 | 
						|
                let roleDetail = roleDetailResponse.data
 | 
						|
                let applicantList = applicantListResponse.data
 | 
						|
                
 | 
						|
                let jsonDecoder = JSONDecoder()
 | 
						|
                
 | 
						|
                do {
 | 
						|
                    let roleDetailDecoded = try jsonDecoder.decode(ApiResponse<GetAuditionRoleDetailResponse>.self, from: roleDetail)
 | 
						|
                    if let data = roleDetailDecoded.data, roleDetailDecoded.success {
 | 
						|
                        self.name = data.name
 | 
						|
                        self.isShowNoticeReapply = data.isAlreadyApplicant
 | 
						|
                        self.auditionRoleDetail = data
 | 
						|
                    } else {
 | 
						|
                        if let message = roleDetailDecoded.message {
 | 
						|
                            self.errorMessage = message
 | 
						|
                        } else {
 | 
						|
                            self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
 | 
						|
                        }
 | 
						|
                        
 | 
						|
                        self.isShowPopup = true
 | 
						|
                    }
 | 
						|
                } catch {
 | 
						|
                    self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
 | 
						|
                    self.isShowPopup = true
 | 
						|
                }
 | 
						|
                
 | 
						|
                do {
 | 
						|
                    let applicantListDecoded = try jsonDecoder.decode(ApiResponse<GetAuditionApplicantListResponse>.self, from: applicantList)
 | 
						|
                    if let data = applicantListDecoded.data, applicantListDecoded.success {
 | 
						|
                        self.totalCount = data.totalCount
 | 
						|
                        self.applicantList.append(contentsOf: data.items)
 | 
						|
                        
 | 
						|
                        if data.items.isEmpty {
 | 
						|
                            isLast = true
 | 
						|
                        } else {
 | 
						|
                            page += 1
 | 
						|
                        }
 | 
						|
                    } else {
 | 
						|
                        if let message = applicantListDecoded.message {
 | 
						|
                            self.errorMessage = message
 | 
						|
                        } else {
 | 
						|
                            self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
 | 
						|
                        }
 | 
						|
                        
 | 
						|
                        self.isShowPopup = true
 | 
						|
                        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
 | 
						|
                            self.onFailure()
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                } catch {
 | 
						|
                    self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
 | 
						|
                    self.isShowPopup = true
 | 
						|
                    DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
 | 
						|
                        self.onFailure()
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                
 | 
						|
                self.isLoading = false
 | 
						|
            }
 | 
						|
            .store(in: &subscription)
 | 
						|
    }
 | 
						|
    
 | 
						|
    func getAuditionApplicantList() {
 | 
						|
        if !isLoading && !isLast {
 | 
						|
            isLoading = true
 | 
						|
            
 | 
						|
            repository.getAuditionApplicantList(auditionRoleId: auditionRoleId, sortType: sortType, page: page, size: pageSize)
 | 
						|
                .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
 | 
						|
                    self.isLoading = false
 | 
						|
                    
 | 
						|
                    do {
 | 
						|
                        let jsonDecoder = JSONDecoder()
 | 
						|
                        let decoded = try jsonDecoder.decode(ApiResponse<GetAuditionApplicantListResponse>.self, from: responseData)
 | 
						|
                        
 | 
						|
                        if let data = decoded.data, decoded.success {
 | 
						|
                            self.totalCount = data.totalCount
 | 
						|
                            self.applicantList.append(contentsOf: data.items)
 | 
						|
                            
 | 
						|
                            if data.items.isEmpty {
 | 
						|
                                isLast = true
 | 
						|
                            } else {
 | 
						|
                                page += 1
 | 
						|
                            }
 | 
						|
                        } 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)
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    func applyAudition(onSuccess: @escaping () -> Void) {
 | 
						|
        if phoneNumber.count != 11 {
 | 
						|
            errorMessage = "잘못된 연락처 입니다.\n다시 입력해 주세요."
 | 
						|
            isShowPopup = true
 | 
						|
            return
 | 
						|
        }
 | 
						|
        
 | 
						|
        guard let soundData = soundData else {
 | 
						|
            errorMessage = "잘못된 녹음 파일 입니다.\n다시 선택해 주세요."
 | 
						|
            isShowPopup = true
 | 
						|
            return
 | 
						|
        }
 | 
						|
        
 | 
						|
        isLoading = true
 | 
						|
        
 | 
						|
        let request = ApplyAuditionRoleRequest(roleId: auditionRoleId, phoneNumber: phoneNumber)
 | 
						|
        var multipartData = [MultipartFormData]()
 | 
						|
        
 | 
						|
        let encoder = JSONEncoder()
 | 
						|
        encoder.outputFormatting = .withoutEscapingSlashes
 | 
						|
        let jsonData = try? encoder.encode(request)
 | 
						|
        
 | 
						|
        if let jsonData = jsonData {
 | 
						|
            multipartData.append(MultipartFormData(provider: .data(jsonData), name: "request"))
 | 
						|
            multipartData.append(
 | 
						|
                MultipartFormData(
 | 
						|
                    provider: .data(soundData),
 | 
						|
                    name: "contentFile",
 | 
						|
                    fileName: fileName,
 | 
						|
                    mimeType: "audio/*"
 | 
						|
                )
 | 
						|
            )
 | 
						|
            
 | 
						|
            repository.applyAudition(parameters: multipartData)
 | 
						|
                .sink { result in
 | 
						|
                    switch result {
 | 
						|
                    case .finished:
 | 
						|
                        DEBUG_LOG("finish")
 | 
						|
                    case .failure(let error):
 | 
						|
                        ERROR_LOG(error.localizedDescription)
 | 
						|
                    }
 | 
						|
                } receiveValue: { 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.deleteAllRecordingFilesWithNamePrefix("voiceon_now_voice")
 | 
						|
                            
 | 
						|
                            self.phoneNumber = ""
 | 
						|
                            self.fileName = ""
 | 
						|
                            self.soundData = nil
 | 
						|
                            
 | 
						|
                            self.refreshApplicantList()
 | 
						|
                            onSuccess()
 | 
						|
                        } else {
 | 
						|
                            if let message = decoded.message {
 | 
						|
                                self.errorMessage = message
 | 
						|
                            } else {
 | 
						|
                                self.errorMessage = "오디션 지원을 완료하지 못했습니다.\n다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
 | 
						|
                            }
 | 
						|
                            
 | 
						|
                            self.isShowPopup = true
 | 
						|
                        }
 | 
						|
                    } catch {
 | 
						|
                        self.errorMessage = "오디션 지원을 완료하지 못했습니다.\n다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
 | 
						|
                        self.isShowPopup = true
 | 
						|
                    }
 | 
						|
                }
 | 
						|
                .store(in: &subscription)
 | 
						|
        } else {
 | 
						|
            self.errorMessage = "오디션 지원을 완료하지 못했습니다.\n다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다."
 | 
						|
            self.isShowPopup = true
 | 
						|
            self.isLoading = false
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    func voteApplicant(applicantId: Int) {
 | 
						|
        isLoading = true
 | 
						|
        
 | 
						|
        repository.voteApplicant(applicantId: applicantId)
 | 
						|
            .sink { result in
 | 
						|
                switch result {
 | 
						|
                case .finished:
 | 
						|
                    DEBUG_LOG("finish")
 | 
						|
                case .failure(let error):
 | 
						|
                    ERROR_LOG(error.localizedDescription)
 | 
						|
                }
 | 
						|
            } receiveValue: { 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 {
 | 
						|
                        if self.isShowNotifyVote {
 | 
						|
                            self.dialogTitle = "[오디션 응원]"
 | 
						|
                            self.dialogDesc = "오디션을 응원하셨습니다\n(무료응원 : 1계정당 1일 1회)\n1캔으로 추가 응원을 해보세요."
 | 
						|
                            self.isShowVoteCompleteView = true
 | 
						|
                        }
 | 
						|
                        
 | 
						|
                        if let index = self.applicantList.firstIndex(where: { $0.applicantId == applicantId }) {
 | 
						|
                            var applicant = self.applicantList[index]
 | 
						|
                            applicant.voteCount += 1
 | 
						|
                            
 | 
						|
                            self.applicantList.remove(at: index)
 | 
						|
                            self.applicantList.insert(applicant, at: index)
 | 
						|
                        }
 | 
						|
                    } else {
 | 
						|
                        if let message = decoded.message {
 | 
						|
                            if message.contains("오늘 응원은 여기까지") {
 | 
						|
                                self.dialogTitle = "[오늘 응원 제한]"
 | 
						|
                                self.dialogDesc = "오늘 응원은 여기까지!\n하루 최대 10회까지 이용이 가능합니다.\n내일 다시 이용해주세요."
 | 
						|
                                self.isShowVoteCompleteView = true
 | 
						|
                            } else {
 | 
						|
                                self.errorMessage = message
 | 
						|
                                self.isShowPopup = true
 | 
						|
                            }
 | 
						|
                        } else {
 | 
						|
                            self.errorMessage = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
 | 
						|
                            self.isShowPopup = true
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                } catch {
 | 
						|
                    self.errorMessage = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
 | 
						|
                    self.isShowPopup = true
 | 
						|
                }
 | 
						|
            }
 | 
						|
            .store(in: &subscription)
 | 
						|
    }
 | 
						|
    
 | 
						|
    private func refreshApplicantList() {
 | 
						|
        self.page = 1
 | 
						|
        self.isLast = false
 | 
						|
        self.totalCount = 0
 | 
						|
        self.applicantList = []
 | 
						|
        self.getAuditionApplicantList()
 | 
						|
    }
 | 
						|
    
 | 
						|
    func deleteAllRecordingFilesWithNamePrefix(_ prefix: String) {
 | 
						|
        let fileManager = FileManager.default
 | 
						|
        let documentsURL = getDocumentsDirectory()
 | 
						|
 | 
						|
        do {
 | 
						|
            let fileURLs = try fileManager.contentsOfDirectory(at: documentsURL, includingPropertiesForKeys: nil, options: [])
 | 
						|
            for fileURL in fileURLs {
 | 
						|
                if fileURL.lastPathComponent.hasPrefix(prefix) {
 | 
						|
                    try fileManager.removeItem(at: fileURL)
 | 
						|
                    DEBUG_LOG("녹음 파일 삭제 성공: \(fileURL)")
 | 
						|
                }
 | 
						|
            }
 | 
						|
        } catch {
 | 
						|
            DEBUG_LOG("녹음 파일 삭제 실패: \(error.localizedDescription)")
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    private func getDocumentsDirectory() -> URL {
 | 
						|
        let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
 | 
						|
        return paths[0]
 | 
						|
    }
 | 
						|
}
 |