투표기능 추가
This commit is contained in:
		| @@ -11,6 +11,7 @@ import Kingfisher | ||||
| struct AuditionApplicantItemView: View { | ||||
|      | ||||
|     let item: GetAuditionRoleApplicantItem | ||||
|     let onClickVote: (Int) -> Void | ||||
|      | ||||
|     var body: some View { | ||||
|         VStack(spacing: 5.3) { | ||||
| @@ -47,6 +48,9 @@ struct AuditionApplicantItemView: View { | ||||
|                         .font(.custom(Font.medium.rawValue, size: 12)) | ||||
|                         .foregroundColor(Color.gray77) | ||||
|                 } | ||||
|                 .onTapGesture { | ||||
|                     onClickVote(item.applicantId) | ||||
|                 } | ||||
|             } | ||||
|             .padding(.vertical, 18.7) | ||||
|              | ||||
| @@ -67,6 +71,7 @@ struct AuditionApplicantItemView: View { | ||||
|             profileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", | ||||
|             voiceUrl: "", | ||||
|             voteCount: 777 | ||||
|         ) | ||||
|         ), | ||||
|         onClickVote: { _ in } | ||||
|     ) | ||||
| } | ||||
|   | ||||
| @@ -16,5 +16,5 @@ struct GetAuditionRoleApplicantItem: Decodable { | ||||
|     let nickname: String | ||||
|     let profileImageUrl: String | ||||
|     let voiceUrl: String | ||||
|     let voteCount: Int | ||||
|     var voteCount: Int | ||||
| } | ||||
|   | ||||
| @@ -14,6 +14,7 @@ enum AuditionApi { | ||||
|     case getAuditionRoleDetail(auditionRoleId: Int) | ||||
|     case getAuditionApplicantList(auditionRoleId: Int, sortType: AuditionApplicantSortType, page: Int, size: Int) | ||||
|     case applyAudition(parameters: [MultipartFormData]) | ||||
|     case voteApplicant(applicantId: Int) | ||||
| } | ||||
|  | ||||
| extension AuditionApi: TargetType { | ||||
| @@ -37,6 +38,9 @@ extension AuditionApi: TargetType { | ||||
|              | ||||
|         case .applyAudition: | ||||
|             return "/audition/applicant" | ||||
|              | ||||
|         case .voteApplicant: | ||||
|             return "/audition/vote" | ||||
|         } | ||||
|     } | ||||
|      | ||||
| @@ -46,7 +50,7 @@ extension AuditionApi: TargetType { | ||||
|         case .getAuditionList, .getAuditionDetail, . getAuditionRoleDetail, .getAuditionApplicantList: | ||||
|             return .get | ||||
|              | ||||
|         case .applyAudition: | ||||
|         case .applyAudition, .voteApplicant: | ||||
|             return .post | ||||
|         } | ||||
|     } | ||||
| @@ -77,6 +81,10 @@ extension AuditionApi: TargetType { | ||||
|              | ||||
|         case .applyAudition(let parameters): | ||||
|             return .uploadMultipart(parameters) | ||||
|              | ||||
|         case .voteApplicant(let applicantId): | ||||
|             let request = VoteAuditionApplicantRequest(applicantId: applicantId) | ||||
|             return .requestJSONEncodable(request) | ||||
|         } | ||||
|     } | ||||
|      | ||||
|   | ||||
| @@ -39,4 +39,8 @@ final class AuditionRepository { | ||||
|     func applyAudition(parameters: [MultipartFormData]) -> AnyPublisher<Response, MoyaError> { | ||||
|         return api.requestPublisher(.applyAudition(parameters: parameters)) | ||||
|     } | ||||
|      | ||||
|     func voteApplicant(applicantId: Int) -> AnyPublisher<Response, MoyaError> { | ||||
|         return api.requestPublisher(.voteApplicant(applicantId: applicantId)) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -87,8 +87,12 @@ struct AuditionRoleDetailView: View { | ||||
|                                     ForEach(0..<viewModel.applicantList.count, id: \.self) { | ||||
|                                         let applicant = viewModel.applicantList[$0] | ||||
|                                          | ||||
|                                         AuditionApplicantItemView(item: applicant) | ||||
|                                             .padding(.bottom, $0 == viewModel.applicantList.count - 1 ? 33 : 0) | ||||
|                                         AuditionApplicantItemView( | ||||
|                                             item: applicant, | ||||
|                                             onClickVote: { | ||||
|                                                 viewModel.voteApplicant(applicantId: $0) | ||||
|                                             } | ||||
|                                         ).padding(.bottom, $0 == viewModel.applicantList.count - 1 ? 33 : 0) | ||||
|                                          | ||||
|                                         if $0 == viewModel.applicantList.count - 1 { | ||||
|                                             Color.clear | ||||
| @@ -193,6 +197,17 @@ struct AuditionRoleDetailView: View { | ||||
|                     viewModel.deleteAllRecordingFilesWithNamePrefix("voiceon_now_voice") | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|             if viewModel.isShowSodaDialog { | ||||
|                 SodaDialog( | ||||
|                     title: viewModel.dialogTitle, | ||||
|                     desc: viewModel.dialogDesc, | ||||
|                     confirmButtonTitle: "확인" | ||||
|                 ) { | ||||
|                     viewModel.isShowSodaDialog = false | ||||
|                     viewModel.isShowNotifyVote = false | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|   | ||||
| @@ -36,6 +36,11 @@ final class AuditionRoleDetailViewModel: ObservableObject { | ||||
|     @Published var soundData: Data? = nil | ||||
|     @Published var phoneNumber = "" | ||||
|      | ||||
|     @Published var isShowNotifyVote = true | ||||
|     @Published var isShowSodaDialog = false | ||||
|     @Published var dialogTitle = "" | ||||
|     @Published var dialogDesc = "" | ||||
|      | ||||
|     var page = 1 | ||||
|     var isLast = false | ||||
|     private var pageSize = 10 | ||||
| @@ -262,6 +267,62 @@ final class AuditionRoleDetailViewModel: ObservableObject { | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     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.isShowSodaDialog = 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.isShowSodaDialog = true | ||||
|                             } else { | ||||
|                                 self.errorMessage = message | ||||
|                                 self.isShowPopup = true | ||||
|                             } | ||||
|                         } else { | ||||
|                             self.errorMessage = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." | ||||
|                             self.isShowPopup = true | ||||
|                         } | ||||
|                     } | ||||
|                 } catch { | ||||
|                     self.errorMessage = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요." | ||||
|                     self.isShowPopup = true | ||||
|                 } | ||||
|             } | ||||
|             .store(in: &subscription) | ||||
|     } | ||||
|      | ||||
|     func deleteAllRecordingFilesWithNamePrefix(_ prefix: String) { | ||||
|         let fileManager = FileManager.default | ||||
|         let documentsURL = getDocumentsDirectory() | ||||
|   | ||||
| @@ -0,0 +1,14 @@ | ||||
| // | ||||
| //  VoteAuditionApplicantRequest.swift | ||||
| //  SodaLive | ||||
| // | ||||
| //  Created by klaus on 1/7/25. | ||||
| // | ||||
|  | ||||
| import Foundation | ||||
|  | ||||
| struct VoteAuditionApplicantRequest: Encodable { | ||||
|     let applicantId: Int | ||||
|     let container: String = "ios" | ||||
|     let timezone: String = TimeZone.current.identifier | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Yu Sung
					Yu Sung