콘텐츠 메인
- 추천 시리즈 UI 추가
This commit is contained in:
		| @@ -6,7 +6,6 @@ | ||||
| // | ||||
|  | ||||
| import SwiftUI | ||||
| import RefreshableScrollView | ||||
|  | ||||
| struct ContentMainView: View { | ||||
|      | ||||
| @@ -16,12 +15,7 @@ struct ContentMainView: View { | ||||
|         ZStack(alignment: .bottomTrailing) { | ||||
|             Color.black.ignoresSafeArea() | ||||
|              | ||||
|             RefreshableScrollView( | ||||
|                 refreshing: $viewModel.isLoading, | ||||
|                 action: { | ||||
|                     viewModel.refresh() | ||||
|                 } | ||||
|             ) { | ||||
|             ScrollView(.vertical, showsIndicators: false) { | ||||
|                 VStack(alignment: .leading, spacing: 0) { | ||||
|                     Text("콘텐츠 마켓") | ||||
|                         .font(.custom(Font.bold.rawValue, size: 21.3)) | ||||
| @@ -30,9 +24,7 @@ struct ContentMainView: View { | ||||
|                         .padding(.horizontal, 13.3) | ||||
|                      | ||||
|                     if !viewModel.isLoading { | ||||
|                         ContentMainNewContentCreatorView() | ||||
|                             .padding(.bottom, 26.7) | ||||
|                             .padding(.horizontal, 13.3) | ||||
|                         ContentMainRecommendSeriesView() | ||||
|                          | ||||
|                         ContentMainBannerView() | ||||
|                             .padding(.bottom, 26.7) | ||||
| @@ -124,10 +116,10 @@ struct ContentMainView: View { | ||||
|                     AppState.shared.setAppStep(step: .createContent) | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|             if viewModel.isLoading { | ||||
|                 LoadingView() | ||||
|             } | ||||
|         } | ||||
|          | ||||
|         if viewModel.isLoading { | ||||
|             LoadingView() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,45 +0,0 @@ | ||||
| // | ||||
| //  ContentMainNewContentCreatorItemView.swift | ||||
| //  SodaLive | ||||
| // | ||||
| //  Created by klaus on 2023/08/11. | ||||
| // | ||||
|  | ||||
| import SwiftUI | ||||
| import Kingfisher | ||||
|  | ||||
| struct ContentMainNewContentCreatorItemView: View { | ||||
|      | ||||
|     let item: GetNewContentUploadCreator | ||||
|      | ||||
|     var body: some View { | ||||
|         VStack(spacing: 8) { | ||||
|             KFImage(URL(string: item.creatorProfileImageUrl)) | ||||
|                 .resizable() | ||||
|                 .scaledToFill() | ||||
|                 .frame(width: screenSize().width * 0.18, height: screenSize().width * 0.18, alignment: .top) | ||||
|                 .clipShape(Circle()) | ||||
|              | ||||
|             Text(item.creatorNickname) | ||||
|                 .font(.custom(Font.medium.rawValue, size: 11.3)) | ||||
|                 .foregroundColor(Color(hex: "bbbbbb")) | ||||
|                 .frame(width: screenSize().width * 0.18) | ||||
|                 .lineLimit(1) | ||||
|         } | ||||
|         .onTapGesture { | ||||
|             AppState.shared.setAppStep(step: .creatorDetail(userId: item.creatorId)) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct ContentMainNewContentCreatorItemView_Previews: PreviewProvider { | ||||
|     static var previews: some View { | ||||
|         ContentMainNewContentCreatorItemView( | ||||
|             item: GetNewContentUploadCreator( | ||||
|                 creatorId: 2, | ||||
|                 creatorNickname: "수다친구112", | ||||
|                 creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png" | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| @@ -1,43 +0,0 @@ | ||||
| // | ||||
| //  ContentMainNewContentCreatorView.swift | ||||
| //  SodaLive | ||||
| // | ||||
| //  Created by klaus on 2023/08/11. | ||||
| // | ||||
|  | ||||
| import SwiftUI | ||||
|  | ||||
| struct ContentMainNewContentCreatorView: View { | ||||
|      | ||||
|     @StateObject private var viewModel = ContentMainNewContentCreatorViewModel() | ||||
|      | ||||
|     var body: some View { | ||||
|         ZStack { | ||||
|             if !viewModel.newContentUploadCreatorList.isEmpty { | ||||
|                 ScrollView(.horizontal, showsIndicators: false) { | ||||
|                     HStack(spacing: 21.3) { | ||||
|                         ForEach(0..<viewModel.newContentUploadCreatorList.count, id: \.self) { index in | ||||
|                             let item = viewModel.newContentUploadCreatorList[index] | ||||
|                             ContentMainNewContentCreatorItemView(item: item) | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|             if viewModel.isLoading { | ||||
|                 ActivityIndicatorView() | ||||
|                     .frame(width: 100, height: 100) | ||||
|             } | ||||
|         } | ||||
|         .frame(maxWidth: .infinity) | ||||
|         .onAppear { | ||||
|             viewModel.getNewContentUploadCreatorList() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| struct ContentMainNewContentCreatorView_Previews: PreviewProvider { | ||||
|     static var previews: some View { | ||||
|         ContentMainNewContentCreatorView() | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,45 @@ | ||||
| // | ||||
| //  ContentMainRecommendSeriesView.swift | ||||
| //  SodaLive | ||||
| // | ||||
| //  Created by klaus on 5/7/24. | ||||
| // | ||||
|  | ||||
| import SwiftUI | ||||
|  | ||||
| struct ContentMainRecommendSeriesView: View { | ||||
|      | ||||
|     @StateObject private var viewModel = ContentMainRecommendSeriesViewModel() | ||||
|      | ||||
|     var body: some View { | ||||
|         ZStack { | ||||
|             if !viewModel.seriesList.isEmpty { | ||||
|                 VStack(alignment: .leading, spacing: 13.3) { | ||||
|                     Text("추천 시리즈") | ||||
|                         .font(.custom(Font.bold.rawValue, size: 18.3)) | ||||
|                         .foregroundColor(Color.grayee) | ||||
|                      | ||||
|                     ScrollView(.horizontal, showsIndicators: false) { | ||||
|                         HStack(alignment: .top, spacing: 13.3) { | ||||
|                             ForEach(0..<viewModel.seriesList.count, id: \.self) { | ||||
|                                 let item = viewModel.seriesList[$0] | ||||
|                                 SeriesListBigItemView(item: item, isVisibleCreator: true) | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|                 .padding(.bottom, 26.7) | ||||
|                 .padding(.horizontal, 13.3) | ||||
|             } else { | ||||
|                 EmptyView() | ||||
|             } | ||||
|         } | ||||
|         .onAppear { | ||||
|             viewModel.getRecommendSeriesList() | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #Preview { | ||||
|     ContentMainRecommendSeriesView() | ||||
| } | ||||
| @@ -1,28 +1,26 @@ | ||||
| // | ||||
| //  ContentMainNewContentCreatorViewModel.swift | ||||
| //  ContentMainRecommendSeriesViewModel.swift | ||||
| //  SodaLive | ||||
| // | ||||
| //  Created by klaus on 2023/12/11. | ||||
| //  Created by klaus on 5/7/24. | ||||
| // | ||||
| 
 | ||||
| import Foundation | ||||
| import Combine | ||||
| 
 | ||||
| final class ContentMainNewContentCreatorViewModel: ObservableObject { | ||||
| final class ContentMainRecommendSeriesViewModel: ObservableObject { | ||||
|      | ||||
|     private let repository = ContentRepository() | ||||
|     private let repository = SeriesRepository() | ||||
|     private var subscription = Set<AnyCancellable>() | ||||
|      | ||||
|     @Published var errorMessage = "" | ||||
|     @Published var isShowPopup = false | ||||
|     @Published var isLoading = false | ||||
|      | ||||
|     @Published var newContentUploadCreatorList = [GetNewContentUploadCreator]() | ||||
|     @Published var seriesList = [SeriesListItem]() | ||||
|      | ||||
|     func getNewContentUploadCreatorList() { | ||||
|         isLoading = true | ||||
|          | ||||
|         repository.getNewContentUploadCreatorList() | ||||
|     func getRecommendSeriesList() { | ||||
|         repository.getRecommendSeriesList() | ||||
|             .sink { result in | ||||
|                 switch result { | ||||
|                 case .finished: | ||||
| @@ -36,23 +34,23 @@ final class ContentMainNewContentCreatorViewModel: ObservableObject { | ||||
|                  | ||||
|                 do { | ||||
|                     let jsonDecoder = JSONDecoder() | ||||
|                     let decoded = try jsonDecoder.decode(ApiResponse<[GetNewContentUploadCreator]>.self, from: responseData) | ||||
|                     let decoded = try jsonDecoder.decode(ApiResponse<[SeriesListItem]>.self, from: responseData) | ||||
|                     self.isLoading = false | ||||
|                      | ||||
|                     if let data = decoded.data, decoded.success { | ||||
|                         self.newContentUploadCreatorList.removeAll() | ||||
|                         self.newContentUploadCreatorList.append(contentsOf: data) | ||||
|                         self.seriesList.removeAll() | ||||
|                         self.seriesList.append(contentsOf: data) | ||||
|                     } else { | ||||
|                         if let message = decoded.message { | ||||
|                             self.errorMessage = message | ||||
|                         } else { | ||||
|                             self.errorMessage = "크리에이터 리스트를 불러오지 못했습니다. 다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." | ||||
|                             self.errorMessage = "추천 시리즈를 불러오지 못했습니다. 다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." | ||||
|                         } | ||||
|                          | ||||
|                         self.isShowPopup = true | ||||
|                     } | ||||
|                 } catch { | ||||
|                     self.errorMessage = "크리에이터 리스트를 불러오지 못했습니다. 다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." | ||||
|                     self.errorMessage = "추천 시리즈를 불러오지 못했습니다. 다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." | ||||
|                     self.isShowPopup = true | ||||
|                     self.isLoading = false | ||||
|                 } | ||||
| @@ -12,6 +12,7 @@ enum SeriesApi { | ||||
|     case getSeriesList(creatorId: Int, sortType: SeriesListAllViewModel.SeriesSortType, page: Int, size: Int) | ||||
|     case getSeriesDetail(seriesId: Int) | ||||
|     case getSeriesContentList(seriesId: Int, page: Int, size: Int) | ||||
|     case getRecommendSeriesList | ||||
| } | ||||
|  | ||||
| extension SeriesApi: TargetType { | ||||
| @@ -29,12 +30,15 @@ extension SeriesApi: TargetType { | ||||
|              | ||||
|         case .getSeriesContentList(let seriesId, _, _): | ||||
|             return "/audio-content/series/\(seriesId)/content" | ||||
|              | ||||
|         case .getRecommendSeriesList: | ||||
|             return "/audio-content/series/recommend" | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     var method: Moya.Method { | ||||
|         switch self { | ||||
|         case .getSeriesList, .getSeriesDetail, .getSeriesContentList: | ||||
|         case .getSeriesList, .getSeriesDetail, .getSeriesContentList, .getRecommendSeriesList: | ||||
|             return .get | ||||
|         } | ||||
|     } | ||||
| @@ -51,7 +55,7 @@ extension SeriesApi: TargetType { | ||||
|              | ||||
|             return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) | ||||
|              | ||||
|         case .getSeriesDetail: | ||||
|         case .getSeriesDetail, .getRecommendSeriesList: | ||||
|             return .requestPlain | ||||
|              | ||||
|         case .getSeriesContentList(_, let page, let size): | ||||
|   | ||||
| @@ -24,4 +24,8 @@ class SeriesRepository { | ||||
|     func getSeriesContentList(seriesId: Int, page: Int, size: Int) -> AnyPublisher<Response, MoyaError> { | ||||
|         return api.requestPublisher(.getSeriesContentList(seriesId: seriesId, page: page, size: size)) | ||||
|     } | ||||
|      | ||||
|     func getRecommendSeriesList() -> AnyPublisher<Response, MoyaError> { | ||||
|         return api.requestPublisher(.getRecommendSeriesList) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -33,12 +33,7 @@ struct UserProfileSeriesView: View { | ||||
|                 HStack(alignment: .top, spacing: 13.3) { | ||||
|                     ForEach(0..<items.count, id: \.self) { | ||||
|                         let item = items[$0] | ||||
|                         SeriesListBigItemView(item: item) | ||||
|                             .contentShape(Rectangle()) | ||||
|                             .onTapGesture { | ||||
|                                 AppState.shared | ||||
|                                     .setAppStep(step: .seriesDetail(seriesId: item.seriesId)) | ||||
|                             } | ||||
|                         SeriesListBigItemView(item: item, isVisibleCreator: false) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|   | ||||
| @@ -12,6 +12,7 @@ import Kingfisher | ||||
| struct SeriesListBigItemView: View { | ||||
|      | ||||
|     let item: SeriesListItem | ||||
|     let isVisibleCreator: Bool | ||||
|      | ||||
|     var body: some View { | ||||
|         VStack(alignment: .leading, spacing: 8) { | ||||
| @@ -22,6 +23,10 @@ struct SeriesListBigItemView: View { | ||||
|                     .frame(width: 116.7, height: 165, alignment: .center) | ||||
|                     .cornerRadius(5) | ||||
|                     .clipped() | ||||
|                     .onTapGesture { | ||||
|                         AppState.shared | ||||
|                             .setAppStep(step: .seriesDetail(seriesId: item.seriesId)) | ||||
|                     } | ||||
|                  | ||||
|                 VStack(alignment: .leading, spacing: 0) { | ||||
|                     HStack(spacing: 3.3) { | ||||
| @@ -57,6 +62,25 @@ struct SeriesListBigItemView: View { | ||||
|                 .foregroundColor(Color.grayee) | ||||
|                 .lineLimit(2) | ||||
|              | ||||
|             if isVisibleCreator { | ||||
|                 HStack(spacing: 3) { | ||||
|                     KFImage(URL(string: item.creator.profileImage)) | ||||
|                         .resizable() | ||||
|                         .scaledToFill() | ||||
|                         .frame(width: 16, height: 16, alignment: .center) | ||||
|                         .clipShape(Circle()) | ||||
|                      | ||||
|                     Text(item.creator.nickname) | ||||
|                         .font(.custom(Font.medium.rawValue, size: 10)) | ||||
|                         .foregroundColor(Color.gray77) | ||||
|                         .lineLimit(1) | ||||
|                 } | ||||
|                 .onTapGesture { | ||||
|                     AppState.shared | ||||
|                         .setAppStep(step: .creatorDetail(userId: item.creator.creatorId)) | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|             Text(item.publishedDaysOfWeek) | ||||
|                 .font(.custom(Font.medium.rawValue, size: 11)) | ||||
|                 .foregroundColor(Color.gray77) | ||||
| @@ -81,6 +105,7 @@ struct SeriesListBigItemView: View { | ||||
|             numberOfContent: 10, | ||||
|             isNew: true, | ||||
|             isPopular: true | ||||
|         ) | ||||
|         ), | ||||
|         isVisibleCreator: true | ||||
|     ) | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Yu Sung
					Yu Sung