diff --git a/SodaLive/Sources/Content/ContentApi.swift b/SodaLive/Sources/Content/ContentApi.swift index fb831b9..c2e422e 100644 --- a/SodaLive/Sources/Content/ContentApi.swift +++ b/SodaLive/Sources/Content/ContentApi.swift @@ -22,8 +22,11 @@ enum ContentApi { case getAudioContentCommentReplyList(commentId: Int, page: Int, size: Int) case deleteAudioContent(audioContentId: Int) case modifyAudioContent(parameters: [MultipartFormData]) - case getMain + case getNewContentUploadCreatorList + case getMainBannerList + case getMainOrderList case getNewContentOfTheme(theme: String) + case getCurationList(page: Int, size: Int) case donation(request: AudioContentDonationRequest) case modifyComment(request: ModifyCommentRequest) case getNewContentThemeList @@ -79,12 +82,21 @@ extension ContentApi: TargetType { case .modifyAudioContent: return "/audio-content" - case .getMain: - return "/audio-content/main" + case .getNewContentUploadCreatorList: + return "/audio-content/main/new-content-upload-creator" + + case .getMainBannerList: + return "/audio-content/main/banner-list" + + case .getMainOrderList: + return "/audio-content/main/order-list" case .getNewContentOfTheme: return "/audio-content/main/new" + case .getCurationList: + return "/audio-content/main/curation-list" + case .donation: return "/audio-content/donation" @@ -111,11 +123,14 @@ extension ContentApi: TargetType { var method: Moya.Method { switch self { case .getAudioContentList, .getAudioContentDetail, .getOrderList, .getAudioContentThemeList, - .getAudioContentCommentList, .getAudioContentCommentReplyList, .getMain, .getNewContentOfTheme, + .getAudioContentCommentList, .getAudioContentCommentReplyList, .getNewContentOfTheme, .getNewContentThemeList, .getNewContentAllOfTheme, .getAudioContentListByCurationId, .getContentRanking, .getContentRankingSortType: return .get + case .getMainBannerList, .getMainOrderList, .getNewContentUploadCreatorList, .getCurationList: + return .get + case .likeContent, .modifyAudioContent, .modifyComment: return .put @@ -163,7 +178,7 @@ extension ContentApi: TargetType { case .addAllPlaybackTracking(let request): return .requestJSONEncodable(request) - case .getAudioContentThemeList, .getMain: + case .getAudioContentThemeList, .getMainBannerList, .getMainOrderList, .getNewContentUploadCreatorList: return .requestPlain case .uploadAudioContent(let parameters): @@ -236,6 +251,14 @@ extension ContentApi: TargetType { case .getContentRankingSortType: return .requestPlain + + case .getCurationList(let page, let size): + let parameters = [ + "page": page - 1, + "size": size + ] as [String : Any] + + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) } } diff --git a/SodaLive/Sources/Content/ContentRepository.swift b/SodaLive/Sources/Content/ContentRepository.swift index 48349ab..955999c 100644 --- a/SodaLive/Sources/Content/ContentRepository.swift +++ b/SodaLive/Sources/Content/ContentRepository.swift @@ -65,14 +65,26 @@ final class ContentRepository { return api.requestPublisher(.modifyAudioContent(parameters: parameters)) } - func getMain() -> AnyPublisher { - return api.requestPublisher(.getMain) + func getNewContentUploadCreatorList() -> AnyPublisher { + return api.requestPublisher(.getNewContentUploadCreatorList) + } + + func getMainBannerList() -> AnyPublisher { + return api.requestPublisher(.getMainBannerList) + } + + func getMainOrderList() -> AnyPublisher { + return api.requestPublisher(.getMainOrderList) } func getNewContentOfTheme(theme: String) -> AnyPublisher { return api.requestPublisher(.getNewContentOfTheme(theme: theme)) } + func getCurationList(page: Int, size: Int) -> AnyPublisher { + return api.requestPublisher(.getCurationList(page: page, size: size)) + } + func donation(contentId: Int, can: Int, comment: String) -> AnyPublisher { return api.requestPublisher(.donation(request: AudioContentDonationRequest(contentId: contentId, donationCan: can, comment: comment))) } diff --git a/SodaLive/Sources/Content/Main/Banner/ContentMainBannerView.swift b/SodaLive/Sources/Content/Main/Banner/ContentMainBannerView.swift new file mode 100644 index 0000000..eb14935 --- /dev/null +++ b/SodaLive/Sources/Content/Main/Banner/ContentMainBannerView.swift @@ -0,0 +1,122 @@ +// +// ContentMainBannerView.swift +// SodaLive +// +// Created by klaus on 2023/08/11. +// + +import SwiftUI +import Kingfisher + +struct ContentMainBannerView: View { + + @StateObject private var viewModel = ContentMainBannerViewModel() + + var body: some View { + ZStack { + if !viewModel.bannerList.isEmpty { + VStack(spacing: 0) { + TabView(selection: $viewModel.currentIndex) { + ForEach(0.. 0, let url = URL(string: link), UIApplication.shared.canOpenURL(url) { + UIApplication.shared.open(url) + } + } + } + .cornerRadius(4.7) + } else { + KFImage(URL(string: item.thumbnailImageUrl)) + .resizable() + .scaledToFill() + .frame( + width: screenSize().width - 26.7, + height: (screenSize().width - 26.7) * 0.53 + ) + .onTapGesture { + switch item.type { + case .EVENT: + AppState.shared.setAppStep(step: .eventDetail(event: item.eventItem!)) + case .CREATOR: + AppState.shared.setAppStep(step: .creatorDetail(userId: item.creatorId!)) + case .LINK: + if let link = item.link, link.trimmingCharacters(in: .whitespaces).count > 0, let url = URL(string: link), UIApplication.shared.canOpenURL(url) { + UIApplication.shared.open(url) + } + } + } + .cornerRadius(4.7) + } + } + } + .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) + .frame( + width: screenSize().width - 26.7, + height: (screenSize().width - 26.7) * 0.53 + ) + + HStack(spacing: 4) { + ForEach(0..() + + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published var isLoading = false + + @Published var currentIndex = 0 + @Published var timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect() + + @Published var bannerList = [GetAudioContentBannerResponse]() + + func getBannerList() { + isLoading = true + + repository.getMainBannerList() + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + self.isLoading = false + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponse<[GetAudioContentBannerResponse]>.self, from: responseData) + self.isLoading = false + + if let data = decoded.data, decoded.success { + self.bannerList.removeAll() + self.bannerList.append(contentsOf: data) + } 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) + } +} diff --git a/SodaLive/Sources/Content/Main/ContentMainBannerView.swift b/SodaLive/Sources/Content/Main/ContentMainBannerView.swift deleted file mode 100644 index f8d0624..0000000 --- a/SodaLive/Sources/Content/Main/ContentMainBannerView.swift +++ /dev/null @@ -1,114 +0,0 @@ -// -// ContentMainBannerView.swift -// SodaLive -// -// Created by klaus on 2023/08/11. -// - -import SwiftUI -import Kingfisher - -struct ContentMainBannerView: View { - - let items: [GetAudioContentBannerResponse] - @State private var currentIndex = 0 - @State private var timer = Timer.publish(every: 3, on: .main, in: .common).autoconnect() - - var body: some View { - VStack(spacing: 0) { - TabView(selection: $currentIndex) { - ForEach(0.. 0, let url = URL(string: link), UIApplication.shared.canOpenURL(url) { - UIApplication.shared.open(url) - } - } - } - .cornerRadius(4.7) - } else { - KFImage(URL(string: item.thumbnailImageUrl)) - .resizable() - .scaledToFill() - .frame( - width: screenSize().width - 26.7, - height: (screenSize().width - 26.7) * 0.53 - ) - .onTapGesture { - switch item.type { - case .EVENT: - AppState.shared.setAppStep(step: .eventDetail(event: item.eventItem!)) - case .CREATOR: - AppState.shared.setAppStep(step: .creatorDetail(userId: item.creatorId!)) - case .LINK: - if let link = item.link, link.trimmingCharacters(in: .whitespaces).count > 0, let url = URL(string: link), UIApplication.shared.canOpenURL(url) { - UIApplication.shared.open(url) - } - } - } - .cornerRadius(4.7) - } - } - } - .tabViewStyle(PageTabViewStyle(indexDisplayMode: .never)) - .frame( - width: screenSize().width - 26.7, - height: (screenSize().width - 26.7) * 0.53 - ) - - HStack(spacing: 4) { - ForEach(0.. Void - @Binding var selectedTheme: String - - var body: some View { - VStack(alignment: .leading, spacing: 0) { - HStack(spacing: 0) { - Text("새로운 콘텐츠") - .font(.custom(Font.bold.rawValue, size: 18.3)) - .foregroundColor(Color(hex: "eeeeee")) - - Spacer() - - Image("ic_forward") - .resizable() - .frame(width: 20, height: 20) - .onTapGesture { - AppState.shared.setAppStep(step: .newContentAll) - } - } - - ContentMainNewContentThemeView(themes: themes, selectTheme: selectTheme, selectedTheme: $selectedTheme) - .padding(.vertical, 16.7) - - ScrollView(.horizontal, showsIndicators: false) { - HStack(alignment: .top, spacing: 13.3) { - ForEach(0.. 0 { - ContentMainNewContentCreatorView(items: viewModel.newContentUploadCreatorList) + if !viewModel.isLoading { + ContentMainNewContentCreatorView() .padding(.bottom, 26.7) .padding(.horizontal, 13.3) - } - - if viewModel.bannerList.count > 0 { - ContentMainBannerView(items: viewModel.bannerList) + + ContentMainBannerView() .padding(.bottom, 40) .padding(.horizontal, 13.3) - } - - if viewModel.orderList.count > 0 { - ContentMainMyStashView(items: viewModel.orderList) + + ContentMainMyStashView() .padding(.bottom, 40) .padding(.horizontal, 13.3) - } - - ContentMainNewContentView( - themes: viewModel.themeList, - items: viewModel.newContentList, - selectTheme: { viewModel.selectedTheme = $0 }, - selectedTheme: $viewModel.selectedTheme - ) - .padding(.horizontal, 13.3) - - if let contentRanking = viewModel.contentRanking { - ContentMainRankingView( - sorts: viewModel.contentRankingSortList, - item: contentRanking, - selectSort: { viewModel.selectedContentRankingSort = $0 }, - selectedSort: $viewModel.selectedContentRankingSort - ) + + ContentMainNewContentView() + .padding(.horizontal, 13.3) + + ContentMainRankingView() .padding(.top, 40) .padding(.horizontal, 13.3) .animation(nil) - } - - if viewModel.curationList.count > 0 { - ContentMainCurationView(items: viewModel.curationList) + + ContentMainCurationView() .padding(.top, 40) .padding(.bottom, 20) } @@ -100,28 +82,6 @@ struct ContentMainView: View { LoadingView() } } - .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .top, autohideIn: 2) { - GeometryReader { geo in - HStack { - Spacer() - Text(viewModel.errorMessage) - .padding(.vertical, 13.3) - .padding(.horizontal, 6.7) - .frame(width: geo.size.width - 66.7, alignment: .center) - .font(.custom(Font.medium.rawValue, size: 12)) - .background(Color(hex: "9970ff")) - .foregroundColor(Color.white) - .multilineTextAlignment(.leading) - .fixedSize(horizontal: false, vertical: true) - .cornerRadius(20) - .padding(.top, 66.7) - Spacer() - } - } - } - .onAppear { - viewModel.getMain() - } } } diff --git a/SodaLive/Sources/Content/Main/ContentMainViewModel.swift b/SodaLive/Sources/Content/Main/ContentMainViewModel.swift index 54357d0..7b9e6be 100644 --- a/SodaLive/Sources/Content/Main/ContentMainViewModel.swift +++ b/SodaLive/Sources/Content/Main/ContentMainViewModel.swift @@ -10,165 +10,13 @@ import Foundation import Combine final class ContentMainViewModel: ObservableObject { - - private let repository = ContentRepository() - private var subscription = Set() - - @Published var errorMessage = "" - @Published var isShowPopup = false @Published var isLoading = false - @Published var newContentUploadCreatorList = [GetNewContentUploadCreator]() - @Published var newContentList = [GetAudioContentMainItem]() - @Published var bannerList = [GetAudioContentBannerResponse]() - @Published var orderList = [GetAudioContentMainItem]() - @Published var themeList = [String]() - @Published var curationList = [GetAudioContentCurationResponse]() - @Published var contentRankingSortList = [String]() - @Published var contentRanking: GetAudioContentRanking? = nil - - @Published var selectedTheme = "전체" { - didSet { - getNewContentOfTheme() - } - } - - @Published var selectedContentRankingSort = "매출" { - didSet { - getContentRanking() - } - } - - func getMain() { + func refresh() { isLoading = true - repository.getMain() - .sink { result in - switch result { - case .finished: - DEBUG_LOG("finish") - case .failure(let error): - ERROR_LOG(error.localizedDescription) - } - } receiveValue: { [unowned self] response in - self.isLoading = false - let responseData = response.data - - do { - let jsonDecoder = JSONDecoder() - let decoded = try jsonDecoder.decode(ApiResponse.self, from: responseData) - self.isLoading = false - - if let data = decoded.data, decoded.success { - self.newContentUploadCreatorList.removeAll() - self.newContentList.removeAll() - self.bannerList.removeAll() - self.orderList.removeAll() - self.curationList.removeAll() - self.themeList.removeAll() - self.contentRankingSortList.removeAll() - - self.newContentUploadCreatorList.append(contentsOf: data.newContentUploadCreatorList) - self.newContentList.append(contentsOf: data.newContentList) - self.bannerList.append(contentsOf: data.bannerList) - self.orderList.append(contentsOf: data.orderList) - self.curationList.append(contentsOf: data.curationList) - self.contentRanking = data.contentRanking - self.contentRankingSortList.append(contentsOf: data.contentRankingSortTypeList) - - self.themeList.append("전체") - self.themeList.append(contentsOf: data.themeList) - } 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 - self.isLoading = false - } - } - .store(in: &subscription) - } - - func getNewContentOfTheme() { - repository.getNewContentOfTheme(theme: selectedTheme == "전체" ? "" : selectedTheme) - .sink { result in - switch result { - case .finished: - DEBUG_LOG("finish") - case .failure(let error): - ERROR_LOG(error.localizedDescription) - } - } receiveValue: { [unowned self] response in - self.isLoading = false - let responseData = response.data - - do { - let jsonDecoder = JSONDecoder() - let decoded = try jsonDecoder.decode(ApiResponse<[GetAudioContentMainItem]>.self, from: responseData) - - if let data = decoded.data, decoded.success { - self.newContentList.removeAll() - self.newContentList.append(contentsOf: data) - } 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 getContentRanking() { - isLoading = true - - repository.getContentRanking(page: 1, size: 12, sortType: selectedContentRankingSort) - .sink { result in - switch result { - case .finished: - DEBUG_LOG("finish") - case .failure(let error): - ERROR_LOG(error.localizedDescription) - } - } receiveValue: { [unowned self] response in - self.isLoading = false - let responseData = response.data - - do { - let jsonDecoder = JSONDecoder() - let decoded = try jsonDecoder.decode(ApiResponse.self, from: responseData) - - if let data = decoded.data, decoded.success { - self.contentRanking = data - } 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) + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [unowned self] in + self.isLoading = false + } } } diff --git a/SodaLive/Sources/Content/Main/ContentMainCurationItemView.swift b/SodaLive/Sources/Content/Main/Curation/ContentMainCurationItemView.swift similarity index 100% rename from SodaLive/Sources/Content/Main/ContentMainCurationItemView.swift rename to SodaLive/Sources/Content/Main/Curation/ContentMainCurationItemView.swift diff --git a/SodaLive/Sources/Content/Main/Curation/ContentMainCurationView.swift b/SodaLive/Sources/Content/Main/Curation/ContentMainCurationView.swift new file mode 100644 index 0000000..5a4e2ba --- /dev/null +++ b/SodaLive/Sources/Content/Main/Curation/ContentMainCurationView.swift @@ -0,0 +1,42 @@ +// +// ContentMainCurationView.swift +// SodaLive +// +// Created by klaus on 2023/08/11. +// + +import SwiftUI + +struct ContentMainCurationView: View { + + @StateObject private var viewModel = ContentMainCurationViewModel() + + var body: some View { + VStack { + if !viewModel.curationList.isEmpty { + LazyVStack(spacing: 40) { + ForEach(0..() + + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published var isLoading = false + + @Published var curationList = [GetAudioContentCurationResponse]() + + var isLast = false + var page = 1 + private let size = 10 + + func getCurationList() { + if !isLoading && !isLast { + isLoading = true + + repository.getCurationList(page: page, size: size) + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + self.isLoading = false + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponse<[GetAudioContentCurationResponse]>.self, from: responseData) + self.isLoading = false + + if let data = decoded.data, decoded.success { + if page == 1 { + self.curationList.removeAll() + } + + if !data.isEmpty { + page += 1 + self.curationList.append(contentsOf: data) + } 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) + } + } +} diff --git a/SodaLive/Sources/Content/Main/ContentMainNewContentThemeView.swift b/SodaLive/Sources/Content/Main/NewContent/ContentMainNewContentThemeView.swift similarity index 100% rename from SodaLive/Sources/Content/Main/ContentMainNewContentThemeView.swift rename to SodaLive/Sources/Content/Main/NewContent/ContentMainNewContentThemeView.swift diff --git a/SodaLive/Sources/Content/Main/NewContent/ContentMainNewContentView.swift b/SodaLive/Sources/Content/Main/NewContent/ContentMainNewContentView.swift new file mode 100644 index 0000000..77b245b --- /dev/null +++ b/SodaLive/Sources/Content/Main/NewContent/ContentMainNewContentView.swift @@ -0,0 +1,68 @@ +// +// ContentMainNewContentView.swift +// SodaLive +// +// Created by klaus on 2023/08/11. +// + +import SwiftUI + +struct ContentMainNewContentView: View { + + @StateObject private var viewModel = ContentMainNewContentViewModel() + + var body: some View { + VStack(spacing: 16.7) { + HStack(spacing: 0) { + Text("새로운 콘텐츠") + .font(.custom(Font.bold.rawValue, size: 18.3)) + .foregroundColor(Color(hex: "eeeeee")) + + Spacer() + + Image("ic_forward") + .resizable() + .frame(width: 20, height: 20) + .onTapGesture { + AppState.shared.setAppStep(step: .newContentAll) + } + } + + if !viewModel.themeList.isEmpty { + ContentMainNewContentThemeView( + themes: viewModel.themeList, + selectTheme: { theme in + viewModel.selectedTheme = theme + }, + selectedTheme: $viewModel.selectedTheme + ) + } + + if !viewModel.newContentList.isEmpty { + ScrollView(.horizontal, showsIndicators: false) { + HStack(alignment: .top, spacing: 13.3) { + ForEach(0..() + + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published var isLoading = false + + @Published var themeList = [String]() + @Published var newContentList = [GetAudioContentMainItem]() + + @Published var selectedTheme = "전체" { + didSet { + newContentList.removeAll() + getNewContentOfTheme() + } + } + + func getThemeList() { + repository.getNewContentThemeList() + .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<[String]>.self, from: responseData) + + if let data = decoded.data, decoded.success { + self.themeList.append("전체") + self.themeList.append(contentsOf: data) + } 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 getNewContentOfTheme() { + isLoading = true + + repository.getNewContentOfTheme(theme: selectedTheme == "전체" ? "" : selectedTheme) + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + self.isLoading = false + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponse<[GetAudioContentMainItem]>.self, from: responseData) + + if let data = decoded.data, decoded.success { + self.newContentList.removeAll() + self.newContentList.append(contentsOf: data) + } 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) + } +} diff --git a/SodaLive/Sources/Content/Main/ContentMainNewContentCreatorItemView.swift b/SodaLive/Sources/Content/Main/NewContentUploadCreator/ContentMainNewContentCreatorItemView.swift similarity index 100% rename from SodaLive/Sources/Content/Main/ContentMainNewContentCreatorItemView.swift rename to SodaLive/Sources/Content/Main/NewContentUploadCreator/ContentMainNewContentCreatorItemView.swift diff --git a/SodaLive/Sources/Content/Main/NewContentUploadCreator/ContentMainNewContentCreatorView.swift b/SodaLive/Sources/Content/Main/NewContentUploadCreator/ContentMainNewContentCreatorView.swift new file mode 100644 index 0000000..1e860f5 --- /dev/null +++ b/SodaLive/Sources/Content/Main/NewContentUploadCreator/ContentMainNewContentCreatorView.swift @@ -0,0 +1,43 @@ +// +// 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) { + LazyHStack(spacing: 21.3) { + ForEach(0..() + + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published var isLoading = false + + @Published var newContentUploadCreatorList = [GetNewContentUploadCreator]() + + func getNewContentUploadCreatorList() { + isLoading = true + + repository.getNewContentUploadCreatorList() + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + self.isLoading = false + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponse<[GetNewContentUploadCreator]>.self, from: responseData) + self.isLoading = false + + if let data = decoded.data, decoded.success { + self.newContentUploadCreatorList.removeAll() + self.newContentUploadCreatorList.append(contentsOf: data) + } 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) + } +} diff --git a/SodaLive/Sources/Content/Main/Order/ContentMainMyStashView.swift b/SodaLive/Sources/Content/Main/Order/ContentMainMyStashView.swift new file mode 100644 index 0000000..8a949db --- /dev/null +++ b/SodaLive/Sources/Content/Main/Order/ContentMainMyStashView.swift @@ -0,0 +1,60 @@ +// +// ContentMainMyStashView.swift +// SodaLive +// +// Created by klaus on 2023/08/11. +// + +import SwiftUI + +struct ContentMainMyStashView: View { + + @StateObject private var viewModel = ContentMainMyStashViewModel() + + var body: some View { + ZStack { + if !viewModel.orderList.isEmpty { + VStack(alignment: .leading, spacing: 13.3) { + HStack(spacing: 0) { + Text("내 보관함") + .font(.custom(Font.bold.rawValue, size: 18.3)) + .foregroundColor(Color(hex: "eeeeee")) + + Spacer() + + Text("전체보기") + .font(.custom(Font.light.rawValue, size: 11.3)) + .foregroundColor(Color(hex: "bbbbbb")) + .onTapGesture { + AppState.shared.setAppStep(step: .orderListAll) + } + } + + ScrollView(.horizontal, showsIndicators: false) { + LazyHStack(alignment: .top, spacing: 13.3) { + ForEach(0..() + + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published var isLoading = false + + @Published var orderList = [GetAudioContentMainItem]() + + func getOrderList() { + isLoading = true + + repository.getMainOrderList() + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + self.isLoading = false + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponse<[GetAudioContentMainItem]>.self, from: responseData) + self.isLoading = false + + if let data = decoded.data, decoded.success { + self.orderList.removeAll() + self.orderList.append(contentsOf: data) + } 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) + } +} diff --git a/SodaLive/Sources/Content/Main/ContentMainRankingSortView.swift b/SodaLive/Sources/Content/Main/Ranking/ContentMainRankingSortView.swift similarity index 100% rename from SodaLive/Sources/Content/Main/ContentMainRankingSortView.swift rename to SodaLive/Sources/Content/Main/Ranking/ContentMainRankingSortView.swift diff --git a/SodaLive/Sources/Content/Main/ContentMainRankingView.swift b/SodaLive/Sources/Content/Main/Ranking/ContentMainRankingView.swift similarity index 51% rename from SodaLive/Sources/Content/Main/ContentMainRankingView.swift rename to SodaLive/Sources/Content/Main/Ranking/ContentMainRankingView.swift index 3d9fb5f..97d98f9 100644 --- a/SodaLive/Sources/Content/Main/ContentMainRankingView.swift +++ b/SodaLive/Sources/Content/Main/Ranking/ContentMainRankingView.swift @@ -10,11 +10,7 @@ import Kingfisher struct ContentMainRankingView: View { - let sorts: [String] - let item: GetAudioContentRanking - - let selectSort: (String) -> Void - @Binding var selectedSort: String + @StateObject private var viewModel = ContentMainRankingViewModel() let rows = [ GridItem(.fixed(60), alignment: .leading), @@ -23,7 +19,7 @@ struct ContentMainRankingView: View { ] var body: some View { - VStack(spacing: 0) { + VStack(spacing: 16.7) { HStack(spacing: 0) { Text("인기 콘텐츠") .font(.custom(Font.bold.rawValue, size: 18.3)) @@ -38,7 +34,7 @@ struct ContentMainRankingView: View { } VStack(spacing: 8) { - Text("\(item.startDate) ~ \(item.endDate)") + Text("\(viewModel.dateString)") .font(.custom(Font.bold.rawValue, size: 14.7)) .foregroundColor(Color(hex: "eeeeee")) @@ -49,15 +45,19 @@ struct ContentMainRankingView: View { .padding(.vertical, 8) .frame(width: screenSize().width - 26.7) .background(Color(hex: "222222")) - .padding(.top, 13.3) - ContentMainRankingSortView(sorts: sorts, selectSort: selectSort, selectedSort: $selectedSort) - .padding(.vertical, 16.7) + if !viewModel.contentRankingSortList.isEmpty { + ContentMainRankingSortView( + sorts: viewModel.contentRankingSortList, + selectSort: { viewModel.selectedContentRankingSort = $0 }, + selectedSort: $viewModel.selectedContentRankingSort + ) + } ScrollView(.horizontal, showsIndicators: false) { LazyHGrid(rows: rows, spacing: 13.3) { - ForEach(0..() + + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published var isLoading = false + + @Published var dateString = "" + @Published var contentRankingSortList = [String]() + @Published var contentRankingItemList = [GetAudioContentRankingItem]() + + @Published var selectedContentRankingSort = "매출" { + didSet { + getContentRanking() + } + } + + func getContentRankingSortType() { + repository.getContentRankingSortType() + .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<[String]>.self, from: responseData) + + if let data = decoded.data, decoded.success { + self.contentRankingSortList.removeAll() + self.contentRankingSortList.append(contentsOf: data) + } 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 getContentRanking() { + isLoading = true + + repository.getContentRanking(page: 1, size: 12, sortType: selectedContentRankingSort) + .sink { result in + switch result { + case .finished: + DEBUG_LOG("finish") + case .failure(let error): + ERROR_LOG(error.localizedDescription) + } + } receiveValue: { [unowned self] response in + self.isLoading = false + let responseData = response.data + + do { + let jsonDecoder = JSONDecoder() + let decoded = try jsonDecoder.decode(ApiResponse.self, from: responseData) + + if let data = decoded.data, decoded.success { + dateString = "\(data.startDate)~\(data.endDate)" + self.contentRankingItemList.append(contentsOf: data.items) + } 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) + } +}