시리즈 전체보기 페이지 추가
This commit is contained in:
		| @@ -125,4 +125,6 @@ enum AppStep { | |||||||
|     case seriesDetail(seriesId: Int) |     case seriesDetail(seriesId: Int) | ||||||
|      |      | ||||||
|     case seriesAll(creatorId: Int) |     case seriesAll(creatorId: Int) | ||||||
|  |      | ||||||
|  |     case seriesContentAll(seriesId: Int, seriesTitle: String) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -0,0 +1,52 @@ | |||||||
|  | // | ||||||
|  | //  SeriesContentAllView.swift | ||||||
|  | //  SodaLive | ||||||
|  | // | ||||||
|  | //  Created by klaus on 4/30/24. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | import SwiftUI | ||||||
|  |  | ||||||
|  | struct SeriesContentAllView: View { | ||||||
|  |      | ||||||
|  |     @ObservedObject var viewModel = SeriesContentAllViewModel() | ||||||
|  |      | ||||||
|  |     let seriesId: Int | ||||||
|  |     let seriesTitle: String | ||||||
|  |      | ||||||
|  |     var body: some View { | ||||||
|  |         VStack(spacing: 0) { | ||||||
|  |             BaseView(isLoading: $viewModel.isLoading) { | ||||||
|  |                 VStack(spacing: 0) { | ||||||
|  |                     DetailNavigationBar(title: "\(seriesTitle) - 전체회차 듣기") | ||||||
|  |                      | ||||||
|  |                     ScrollView(.vertical, showsIndicators: false) { | ||||||
|  |                         VStack(spacing: 12) { | ||||||
|  |                             ForEach(0..<viewModel.seriesContentList.count, id: \.self) { index in | ||||||
|  |                                 let item = viewModel.seriesContentList[index] | ||||||
|  |                                  | ||||||
|  |                                 SeriesContentListItemView(item: item) | ||||||
|  |                                     .contentShape(Rectangle()) | ||||||
|  |                                     .onTapGesture { | ||||||
|  |                                         AppState.shared | ||||||
|  |                                             .setAppStep(step: .contentDetail(contentId: item.contentId)) | ||||||
|  |                                     } | ||||||
|  |                                     .onAppear { | ||||||
|  |                                         if index == viewModel.seriesContentList.count - 1 { | ||||||
|  |                                             viewModel.getSeriesContentList() | ||||||
|  |                                         } | ||||||
|  |                                     } | ||||||
|  |                             } | ||||||
|  |                         } | ||||||
|  |                         .padding(13.3) | ||||||
|  |                         .padding(.top, 12) | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         .onAppear { | ||||||
|  |             viewModel.seriesId = seriesId | ||||||
|  |             viewModel.getSeriesContentList() | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,74 @@ | |||||||
|  | // | ||||||
|  | //  SeriesContentAllViewModel.swift | ||||||
|  | //  SodaLive | ||||||
|  | // | ||||||
|  | //  Created by klaus on 4/30/24. | ||||||
|  | // | ||||||
|  |  | ||||||
|  | import Foundation | ||||||
|  | import Combine | ||||||
|  |  | ||||||
|  | final class SeriesContentAllViewModel: ObservableObject { | ||||||
|  |     private let repository = SeriesRepository() | ||||||
|  |     private var subscription = Set<AnyCancellable>() | ||||||
|  |      | ||||||
|  |     var seriesId = 0 | ||||||
|  |      | ||||||
|  |     @Published var isLoading = false | ||||||
|  |     @Published var errorMessage = "" | ||||||
|  |     @Published var isShowPopup = false | ||||||
|  |     @Published var seriesContentList = [GetSeriesContentListItem]() | ||||||
|  |      | ||||||
|  |     var page = 1 | ||||||
|  |     var isLast = false | ||||||
|  |     private let pageSize = 10 | ||||||
|  |      | ||||||
|  |     func getSeriesContentList() { | ||||||
|  |         if !isLoading && !isLast { | ||||||
|  |             repository | ||||||
|  |                 .getSeriesContentList(seriesId: seriesId, 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 | ||||||
|  |                      | ||||||
|  |                     do { | ||||||
|  |                         let jsonDecoder = JSONDecoder() | ||||||
|  |                         let decoded = try jsonDecoder.decode(ApiResponse<GetSeriesContentListResponse>.self, from: responseData) | ||||||
|  |                          | ||||||
|  |                         if let data = decoded.data, decoded.success { | ||||||
|  |                             if page == 1 { | ||||||
|  |                                 self.seriesContentList.removeAll() | ||||||
|  |                             } | ||||||
|  |                              | ||||||
|  |                             if !data.items.isEmpty { | ||||||
|  |                                 page += 1 | ||||||
|  |                                 self.seriesContentList.append(contentsOf: data.items) | ||||||
|  |                             } else { | ||||||
|  |                                 isLast = true | ||||||
|  |                             } | ||||||
|  |                         } else { | ||||||
|  |                             if let message = decoded.message { | ||||||
|  |                                 self.errorMessage = message | ||||||
|  |                             } else { | ||||||
|  |                                 self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." | ||||||
|  |                             } | ||||||
|  |                              | ||||||
|  |                             self.isShowPopup = true | ||||||
|  |                         } | ||||||
|  |                     } catch { | ||||||
|  |                         self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." | ||||||
|  |                         self.isShowPopup = true | ||||||
|  |                     } | ||||||
|  |                      | ||||||
|  |                     self.isLoading = false | ||||||
|  |                 } | ||||||
|  |                 .store(in: &subscription) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -17,7 +17,7 @@ struct SeriesContentListItemView: View { | |||||||
|             HStack(spacing: 11) { |             HStack(spacing: 11) { | ||||||
|                 KFImage(URL(string: item.coverImage)) |                 KFImage(URL(string: item.coverImage)) | ||||||
|                     .resizable() |                     .resizable() | ||||||
|                     .scaledToFit() |                     .scaledToFill() | ||||||
|                     .frame(width: 66.7, height: 66.7) |                     .frame(width: 66.7, height: 66.7) | ||||||
|                     .cornerRadius(5.3) |                     .cornerRadius(5.3) | ||||||
|                  |                  | ||||||
|   | |||||||
| @@ -35,7 +35,10 @@ struct SeriesDetailHomeView: View { | |||||||
|                     .foregroundColor(Color.button) |                     .foregroundColor(Color.button) | ||||||
|             ) |             ) | ||||||
|             .padding(.top, 16) |             .padding(.top, 16) | ||||||
|             .onTapGesture {} |             .onTapGesture { | ||||||
|  |                 AppState.shared | ||||||
|  |                     .setAppStep(step: .seriesContentAll(seriesId: seriesId, seriesTitle: title)) | ||||||
|  |             } | ||||||
|              |              | ||||||
|             VStack(spacing: 8) { |             VStack(spacing: 8) { | ||||||
|                 ForEach(0..<contentList.count, id: \.self) { |                 ForEach(0..<contentList.count, id: \.self) { | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ import Moya | |||||||
| enum SeriesApi { | enum SeriesApi { | ||||||
|     case getSeriesList(creatorId: Int, sortType: SeriesListAllViewModel.SeriesSortType, page: Int, size: Int) |     case getSeriesList(creatorId: Int, sortType: SeriesListAllViewModel.SeriesSortType, page: Int, size: Int) | ||||||
|     case getSeriesDetail(seriesId: Int) |     case getSeriesDetail(seriesId: Int) | ||||||
|  |     case getSeriesContentList(seriesId: Int, page: Int, size: Int) | ||||||
| } | } | ||||||
|  |  | ||||||
| extension SeriesApi: TargetType { | extension SeriesApi: TargetType { | ||||||
| @@ -25,12 +26,15 @@ extension SeriesApi: TargetType { | |||||||
|              |              | ||||||
|         case .getSeriesDetail(let seriesId): |         case .getSeriesDetail(let seriesId): | ||||||
|             return "/audio-content/series/\(seriesId)" |             return "/audio-content/series/\(seriesId)" | ||||||
|  |              | ||||||
|  |         case .getSeriesContentList(let seriesId, _, _): | ||||||
|  |             return "/audio-content/series/\(seriesId)/content" | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     var method: Moya.Method { |     var method: Moya.Method { | ||||||
|         switch self { |         switch self { | ||||||
|         case .getSeriesList, .getSeriesDetail: |         case .getSeriesList, .getSeriesDetail, .getSeriesContentList: | ||||||
|             return .get |             return .get | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -49,6 +53,14 @@ extension SeriesApi: TargetType { | |||||||
|              |              | ||||||
|         case .getSeriesDetail: |         case .getSeriesDetail: | ||||||
|             return .requestPlain |             return .requestPlain | ||||||
|  |              | ||||||
|  |         case .getSeriesContentList(_, let page, let size): | ||||||
|  |             let parameters = [ | ||||||
|  |                 "page": page - 1, | ||||||
|  |                 "size": size | ||||||
|  |             ] as [String : Any] | ||||||
|  |              | ||||||
|  |             return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|      |      | ||||||
|   | |||||||
| @@ -20,4 +20,8 @@ class SeriesRepository { | |||||||
|     func getSeriesDetail(seriesId: Int) -> AnyPublisher<Response, MoyaError> { |     func getSeriesDetail(seriesId: Int) -> AnyPublisher<Response, MoyaError> { | ||||||
|         return api.requestPublisher(.getSeriesDetail(seriesId: seriesId)) |         return api.requestPublisher(.getSeriesDetail(seriesId: seriesId)) | ||||||
|     } |     } | ||||||
|  |      | ||||||
|  |     func getSeriesContentList(seriesId: Int, page: Int, size: Int) -> AnyPublisher<Response, MoyaError> { | ||||||
|  |         return api.requestPublisher(.getSeriesContentList(seriesId: seriesId, page: page, size: size)) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -184,6 +184,9 @@ struct ContentView: View { | |||||||
|             case .seriesDetail(let seriesId): |             case .seriesDetail(let seriesId): | ||||||
|                 SeriesDetailView(seriesId: seriesId) |                 SeriesDetailView(seriesId: seriesId) | ||||||
|                  |                  | ||||||
|  |             case .seriesContentAll(let seriesId, let seriesTitle): | ||||||
|  |                 SeriesContentAllView(seriesId: seriesId, seriesTitle: seriesTitle) | ||||||
|  |                  | ||||||
|             default: |             default: | ||||||
|                 EmptyView() |                 EmptyView() | ||||||
|                     .frame(width: 0, height: 0, alignment: .topLeading) |                     .frame(width: 0, height: 0, alignment: .topLeading) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Yu Sung
					Yu Sung