콘텐츠 전체보기
- 카테고리 추가
This commit is contained in:
		
							
								
								
									
										45
									
								
								SodaLive/Sources/Content/Category/CategoryApi.swift
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								SodaLive/Sources/Content/Category/CategoryApi.swift
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,45 @@ | ||||
| // | ||||
| //  CategoryApi.swift | ||||
| //  SodaLive | ||||
| // | ||||
| //  Created by klaus on 2/7/24. | ||||
| // | ||||
|  | ||||
| import Foundation | ||||
| import Moya | ||||
|  | ||||
| enum CategoryApi { | ||||
|     case getCategoryList(creatorId: Int) | ||||
| } | ||||
|  | ||||
| extension CategoryApi: TargetType { | ||||
|     var baseURL: URL { | ||||
|         return URL(string: BASE_URL)! | ||||
|     } | ||||
|      | ||||
|     var path: String { | ||||
|         switch self { | ||||
|         case .getCategoryList: | ||||
|             return "/category" | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     var method: Moya.Method { | ||||
|         switch self { | ||||
|         case .getCategoryList: | ||||
|             return .get | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     var task: Moya.Task { | ||||
|         switch self { | ||||
|         case .getCategoryList(let creatorId): | ||||
|             let parameters = ["creatorId": creatorId] as [String : Any] | ||||
|             return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     var headers: [String : String]? { | ||||
|         return ["Authorization": "Bearer \(UserDefaults.string(forKey: UserDefaultsKey.token))"] | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,59 @@ | ||||
| // | ||||
| //  ContentListCategoryView.swift | ||||
| //  SodaLive | ||||
| // | ||||
| //  Created by klaus on 2/7/24. | ||||
| // | ||||
|  | ||||
| import SwiftUI | ||||
|  | ||||
| struct ContentListCategoryView: View { | ||||
|     let categoryList: [GetCategoryListResponse] | ||||
|     let selectCategory: (Int) -> Void | ||||
|      | ||||
|     @Binding var selectedCategory: String | ||||
|      | ||||
|     var body: some View { | ||||
|         ScrollView(.horizontal, showsIndicators: false) { | ||||
|             HStack(spacing: 8) { | ||||
|                 ForEach(0..<categoryList.count, id: \.self) { index in | ||||
|                     let category = categoryList[index] | ||||
|                     Text(category.category) | ||||
|                         .font(.custom(Font.medium.rawValue, size: 14.7)) | ||||
|                         .foregroundColor(Color(hex: selectedCategory == category.category ? "3bb9f1" : "777777")) | ||||
|                         .padding(.horizontal, 13.3) | ||||
|                         .padding(.vertical, 9.3) | ||||
|                         .border( | ||||
|                             Color(hex: selectedCategory == category.category ? "3bb9f1" : "eeeeee"), | ||||
|                             width: 0.5 | ||||
|                         ) | ||||
|                         .cornerRadius(16.7) | ||||
|                         .overlay( | ||||
|                             RoundedRectangle(cornerRadius: CGFloat(16.7)) | ||||
|                                 .stroke(lineWidth: 0.5) | ||||
|                                 .foregroundColor(Color(hex: selectedCategory == category.category ? "3bb9f1" : "eeeeee")) | ||||
|                         ) | ||||
|                         .onTapGesture { | ||||
|                             if selectedCategory != category.category { | ||||
|                                 selectedCategory = category.category | ||||
|                                 selectCategory(category.categoryId) | ||||
|                             } | ||||
|                         } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #Preview { | ||||
|      | ||||
|     ContentListCategoryView( | ||||
|         categoryList: [ | ||||
|             GetCategoryListResponse(categoryId: 0, category: "전체"), | ||||
|             GetCategoryListResponse(categoryId: 1, category: "test"), | ||||
|             GetCategoryListResponse(categoryId: 0, category: "test2") | ||||
|         ], | ||||
|         selectCategory: { _ in }, | ||||
|         selectedCategory: .constant("전체") | ||||
|     ) | ||||
| } | ||||
| @@ -0,0 +1,13 @@ | ||||
| // | ||||
| //  GetCategoryListResponse.swift | ||||
| //  SodaLive | ||||
| // | ||||
| //  Created by klaus on 2/7/24. | ||||
| // | ||||
|  | ||||
| import Foundation | ||||
|  | ||||
| struct GetCategoryListResponse: Decodable { | ||||
|     let categoryId: Int | ||||
|     let category: String | ||||
| } | ||||
| @@ -9,7 +9,7 @@ import Foundation | ||||
| import Moya | ||||
|  | ||||
| enum ContentApi { | ||||
|     case getAudioContentList(userId: Int, page: Int, size: Int, sort: ContentListViewModel.Sort) | ||||
|     case getAudioContentList(userId: Int, categoryId: Int, page: Int, size: Int, sort: ContentListViewModel.Sort) | ||||
|     case getAudioContentDetail(audioContentId: Int) | ||||
|     case likeContent(request: PutAudioContentLikeRequest) | ||||
|     case registerComment(request: RegisterAudioContentCommentRequest) | ||||
| @@ -152,9 +152,10 @@ extension ContentApi: TargetType { | ||||
|      | ||||
|     var task: Moya.Task { | ||||
|         switch self { | ||||
|         case .getAudioContentList(let userId, let page, let size, let sort): | ||||
|         case .getAudioContentList(let userId, let categoryId, let page, let size, let sort): | ||||
|             let parameters = [ | ||||
|                 "creator-id": userId, | ||||
|                 "category-id": categoryId, | ||||
|                 "page": page - 1, | ||||
|                 "size": size, | ||||
|                 "sort-type": sort | ||||
|   | ||||
| @@ -35,6 +35,15 @@ struct ContentListView: View { | ||||
|                     .frame(height: 50) | ||||
|                     .background(Color.black) | ||||
|                      | ||||
|                     if !viewModel.categoryList.isEmpty { | ||||
|                         ContentListCategoryView( | ||||
|                             categoryList: viewModel.categoryList, | ||||
|                             selectCategory: { viewModel.selectCategory(categoryId: $0) }, | ||||
|                             selectedCategory: $viewModel.selectedCategory | ||||
|                         ) | ||||
|                         .padding(.horizontal, 13.3) | ||||
|                     } | ||||
|                      | ||||
|                     if userId == UserDefaults.int(forKey: .userId) { | ||||
|                         Text("새로운 콘텐츠 등록하기") | ||||
|                             .font(.custom(Font.bold.rawValue, size: 15)) | ||||
| @@ -132,12 +141,14 @@ struct ContentListView: View { | ||||
|                                     } | ||||
|                                 } | ||||
|                             } | ||||
|                             .padding(.horizontal, 13.3) | ||||
|                         } | ||||
|                     } | ||||
|                     .padding(.top, 13.3) | ||||
|                 } | ||||
|                 .onAppear { | ||||
|                     viewModel.userId = userId | ||||
|                     viewModel.getCategoryList() | ||||
|                     viewModel.getAudioContentList() | ||||
|                 } | ||||
|                 .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .bottom, autohideIn: 2) { | ||||
|   | ||||
| @@ -21,6 +21,7 @@ final class ContentListViewModel: ObservableObject { | ||||
|     @Published var isShowPopup = false | ||||
|     @Published var isLoading = false | ||||
|      | ||||
|     @Published var categoryList: [GetCategoryListResponse] = [] | ||||
|     @Published var audioContentList: [GetAudioContentListItem] = [] | ||||
|     @Published var totalCount = 0 | ||||
|     @Published var sort = Sort.NEWEST { | ||||
| @@ -32,18 +33,20 @@ final class ContentListViewModel: ObservableObject { | ||||
|     } | ||||
|      | ||||
|     @Published var scrollToTop = false | ||||
|     @Published var selectedCategory: String = "전체" | ||||
|      | ||||
|     var userId = 0 | ||||
|     var page = 1 | ||||
|     var isLast = false | ||||
|     private let pageSize = 10 | ||||
|     private var categoryId = 0 | ||||
|      | ||||
|     func getAudioContentList() { | ||||
|         if (!isLast && !isLoading) { | ||||
|             isLoading = true | ||||
|              | ||||
|             repository | ||||
|                 .getAudioContentList(userId: userId, page: page, size: pageSize, sort: sort) | ||||
|                 .getAudioContentList(userId: userId, categoryId: categoryId, page: page, size: pageSize, sort: sort) | ||||
|                 .sink { result in | ||||
|                     switch result { | ||||
|                     case .finished: | ||||
| @@ -64,11 +67,11 @@ final class ContentListViewModel: ObservableObject { | ||||
|                                 self.scrollToTop.toggle() | ||||
|                             } | ||||
|                              | ||||
|                             if !data.items.isEmpty { | ||||
|                                 page += 1 | ||||
|                                 self.totalCount = data.totalCount | ||||
|                                 self.audioContentList.append(contentsOf: data.items) | ||||
|                             } else { | ||||
|                             page += 1 | ||||
|                             self.totalCount = data.totalCount | ||||
|                             self.audioContentList.append(contentsOf: data.items) | ||||
|                              | ||||
|                             if data.items.isEmpty { | ||||
|                                 isLast = true | ||||
|                             } | ||||
|                         } else { | ||||
| @@ -90,4 +93,53 @@ final class ContentListViewModel: ObservableObject { | ||||
|                 .store(in: &subscription) | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     func getCategoryList() { | ||||
|         repository.getCategoryList(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 | ||||
|                 let responseData = response.data | ||||
|                  | ||||
|                 do { | ||||
|                     let jsonDecoder = JSONDecoder() | ||||
|                     let decoded = try jsonDecoder.decode(ApiResponse<[GetCategoryListResponse]>.self, from: responseData) | ||||
|                      | ||||
|                     if let data = decoded.data, decoded.success { | ||||
|                         categoryList.removeAll() | ||||
|                          | ||||
|                         if !data.isEmpty { | ||||
|                             categoryList.append(GetCategoryListResponse(categoryId: 0, category: "전체")) | ||||
|                             categoryList.append(contentsOf: data) | ||||
|                         } | ||||
|                     } else { | ||||
|                         if let message = decoded.message { | ||||
|                             self.errorMessage = message | ||||
|                         } else { | ||||
|                             self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." | ||||
|                         } | ||||
|                          | ||||
|                         self.isShowPopup = true | ||||
|                     } | ||||
|                 }  catch { | ||||
|                     print(error) | ||||
|                     self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." | ||||
|                     self.isShowPopup = true | ||||
|                 } | ||||
|             } | ||||
|             .store(in: &subscription) | ||||
|     } | ||||
|      | ||||
|     func selectCategory(categoryId: Int) { | ||||
|         self.categoryId = categoryId | ||||
|          | ||||
|         page = 1 | ||||
|         isLast = false | ||||
|         getAudioContentList() | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -12,9 +12,10 @@ import Moya | ||||
|  | ||||
| final class ContentRepository { | ||||
|     private let api = MoyaProvider<ContentApi>() | ||||
|     private let categoryApi = MoyaProvider<CategoryApi>() | ||||
|      | ||||
|     func getAudioContentList(userId: Int, page: Int, size: Int, sort: ContentListViewModel.Sort) -> AnyPublisher<Response, MoyaError> { | ||||
|         return api.requestPublisher(.getAudioContentList(userId: userId, page: page, size: size, sort: sort)) | ||||
|     func getAudioContentList(userId: Int, categoryId: Int,  page: Int, size: Int, sort: ContentListViewModel.Sort) -> AnyPublisher<Response, MoyaError> { | ||||
|         return api.requestPublisher(.getAudioContentList(userId: userId, categoryId: categoryId, page: page, size: size, sort: sort)) | ||||
|     } | ||||
|      | ||||
|     func getAudioContentDetail(audioContentId: Int) -> AnyPublisher<Response, MoyaError> { | ||||
| @@ -120,4 +121,8 @@ final class ContentRepository { | ||||
|     func unpinContent(contentId: Int) -> AnyPublisher<Response, MoyaError> { | ||||
|         return api.requestPublisher(.unpinContent(contentId: contentId)) | ||||
|     } | ||||
|      | ||||
|     func getCategoryList(creatorId: Int) -> AnyPublisher<Response, MoyaError> { | ||||
|         return categoryApi.requestPublisher(.getCategoryList(creatorId: creatorId)) | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Yu Sung
					Yu Sung