From 0a22f87acca53038209c5cc10f4cc51eb05f47d9 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Wed, 18 Mar 2026 19:45:45 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SodaLive/Resources/Localizable.xcstrings | 73 +++--- SodaLive/Sources/App/AppStep.swift | 4 - .../Content/Main/ContentMainView.swift | 188 -------------- .../Content/Main/ContentMainViewModel.swift | 22 -- .../ContentMainCurationItemView.swift | 53 ---- .../Curation/ContentMainCurationView.swift | 42 --- .../ContentMainCurationViewModel.swift | 76 ------ .../Main/GetAudioContentMainResponse.swift | 24 -- .../ContentMainNewContentThemeView.swift | 55 ---- .../ContentMainNewContentView.swift | 68 ----- .../ContentMainNewContentViewModel.swift | 105 -------- .../Main/Order/ContentMainMyStashView.swift | 61 ----- .../Order/ContentMainMyStashViewModel.swift | 62 ----- .../ContentMainCreatorRankingView.swift | 150 ----------- .../ContentMainCreatorRankingViewModel.swift | 54 ---- .../Main/Ranking/ContentMainRankingView.swift | 114 -------- .../Ranking/ContentMainRankingViewModel.swift | 105 -------- .../ContentMainRecommendSeriesView.swift | 63 ----- .../ContentMainRecommendSeriesViewModel.swift | 60 ----- .../ContentMainTabContentRepository.swift | 64 ----- .../Content/ContentMainTabContentView.swift | 93 ------- .../ContentMainTabContentViewModel.swift | 231 ----------------- .../ContentMainTabRankContentView.swift | 165 ------------ .../Content/ContentMainTagCurationView.swift | 245 ------------------ .../GetContentMainTabContentResponse.swift | 20 -- .../Main/V2/ContentByChannelView.swift | 220 ---------------- .../Content/Main/V2/ContentCreatorView.swift | 57 ---- .../Main/V2/ContentMainCurationViewV2.swift | 90 ------- .../Main/V2/ContentMainNewContentViewV2.swift | 103 -------- .../Main/V2/ContentMainNoItemView.swift | 34 --- .../Content/Main/V2/ContentMainViewV2.swift | 235 ----------------- .../Main/V2/GetContentCurationResponse.swift | 11 - .../Completed/CompletedSeriesView.swift | 72 ----- .../Completed/CompletedSeriesViewModel.swift | 73 ------ .../ContentMainCompletedSeriesView.swift | 83 ------ .../ContentMainNewOrRecommendSeriesView.swift | 115 -------- .../Series/ContentMainSeriesByGenreView.swift | 116 --------- .../ContentMainSeriesCurationView.swift | 158 ----------- .../Series/ContentMainSeriesGenreView.swift | 58 ----- .../Series/ContentMainSeriesRankingView.swift | 141 ---------- .../ContentMainTabSeriesRepository.swift | 55 ---- .../V2/Series/ContentMainTabSeriesView.swift | 95 ------- .../ContentMainTabSeriesViewModel.swift | 157 ----------- .../GetContentMainTabSeriesResponse.swift | 28 -- ...ontentMainOriginalAudioDramaItemView.swift | 108 -------- .../ContentMainOriginalAudioDramaView.swift | 82 ------ .../Main/V2/Series/SeriesByChannelView.swift | 123 --------- SodaLive/Sources/ContentView.swift | 6 - 48 files changed, 41 insertions(+), 4376 deletions(-) delete mode 100644 SodaLive/Sources/Content/Main/ContentMainView.swift delete mode 100644 SodaLive/Sources/Content/Main/ContentMainViewModel.swift delete mode 100644 SodaLive/Sources/Content/Main/Curation/ContentMainCurationItemView.swift delete mode 100644 SodaLive/Sources/Content/Main/Curation/ContentMainCurationView.swift delete mode 100644 SodaLive/Sources/Content/Main/Curation/ContentMainCurationViewModel.swift delete mode 100644 SodaLive/Sources/Content/Main/NewContent/ContentMainNewContentThemeView.swift delete mode 100644 SodaLive/Sources/Content/Main/NewContent/ContentMainNewContentView.swift delete mode 100644 SodaLive/Sources/Content/Main/NewContent/ContentMainNewContentViewModel.swift delete mode 100644 SodaLive/Sources/Content/Main/Order/ContentMainMyStashView.swift delete mode 100644 SodaLive/Sources/Content/Main/Order/ContentMainMyStashViewModel.swift delete mode 100644 SodaLive/Sources/Content/Main/Ranking/ContentMainCreatorRankingView.swift delete mode 100644 SodaLive/Sources/Content/Main/Ranking/ContentMainCreatorRankingViewModel.swift delete mode 100644 SodaLive/Sources/Content/Main/Ranking/ContentMainRankingView.swift delete mode 100644 SodaLive/Sources/Content/Main/Ranking/ContentMainRankingViewModel.swift delete mode 100644 SodaLive/Sources/Content/Main/RecommendSeries/ContentMainRecommendSeriesView.swift delete mode 100644 SodaLive/Sources/Content/Main/RecommendSeries/ContentMainRecommendSeriesViewModel.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Content/ContentMainTabContentRepository.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Content/ContentMainTabContentView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Content/ContentMainTabContentViewModel.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Content/ContentMainTabRankContentView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Content/ContentMainTagCurationView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Content/GetContentMainTabContentResponse.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/ContentByChannelView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/ContentCreatorView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/ContentMainCurationViewV2.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/ContentMainNewContentViewV2.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/ContentMainNoItemView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/ContentMainViewV2.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/GetContentCurationResponse.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Series/Completed/CompletedSeriesView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Series/Completed/CompletedSeriesViewModel.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Series/ContentMainCompletedSeriesView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Series/ContentMainNewOrRecommendSeriesView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Series/ContentMainSeriesByGenreView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Series/ContentMainSeriesCurationView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Series/ContentMainSeriesGenreView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Series/ContentMainSeriesRankingView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Series/ContentMainTabSeriesRepository.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Series/ContentMainTabSeriesView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Series/ContentMainTabSeriesViewModel.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Series/OriginalAudioDrama/ContentMainOriginalAudioDramaItemView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Series/OriginalAudioDrama/ContentMainOriginalAudioDramaView.swift delete mode 100644 SodaLive/Sources/Content/Main/V2/Series/SeriesByChannelView.swift diff --git a/SodaLive/Resources/Localizable.xcstrings b/SodaLive/Resources/Localizable.xcstrings index 1c69091..595a5da 100644 --- a/SodaLive/Resources/Localizable.xcstrings +++ b/SodaLive/Resources/Localizable.xcstrings @@ -3942,6 +3942,7 @@ } }, "마이페이지에서 본인인증을 해주세요" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -4133,22 +4134,6 @@ } } }, - "모든 기기에서 로그아웃" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Log out from all devices" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "全端末からログアウト" - } - } - } - }, "목" : { "localizations" : { "en" : { @@ -4165,6 +4150,22 @@ } } }, + "모든 기기에서 로그아웃" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Log out from all devices" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "全端末からログアウト" + } + } + } + }, "모서리 원을 드래그해서 크롭 영역 크기를 조정하세요" : { }, @@ -4601,6 +4602,7 @@ } }, "보이스 모닝콜" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -5177,6 +5179,7 @@ } }, "새로운 콘텐츠" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -5388,6 +5391,7 @@ } }, "숏플" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -6463,6 +6467,7 @@ } }, "오리지널 오디오 드라마" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -7184,6 +7189,7 @@ } }, "일간 랭킹" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -7521,6 +7527,7 @@ } }, "장르별 추천 시리즈" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -8662,22 +8669,6 @@ } } }, - "캔" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cans" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "CAN" - } - } - } - }, "캐릭터 정보" : { "localizations" : { "en" : { @@ -8694,6 +8685,22 @@ } } }, + "캔" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cans" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "CAN" + } + } + } + }, "캔 충전" : { "localizations" : { "en" : { @@ -8807,6 +8814,7 @@ } }, "콘텐츠 마켓" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -9127,6 +9135,7 @@ } }, "태그별 추천 콘텐츠" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { diff --git a/SodaLive/Sources/App/AppStep.swift b/SodaLive/Sources/App/AppStep.swift index 7092378..d8ff766 100644 --- a/SodaLive/Sources/App/AppStep.swift +++ b/SodaLive/Sources/App/AppStep.swift @@ -150,10 +150,6 @@ enum AppStep { case search - case contentMain(startTab: ContentMainTab) - - case completedSeriesAll - case message case notificationList diff --git a/SodaLive/Sources/Content/Main/ContentMainView.swift b/SodaLive/Sources/Content/Main/ContentMainView.swift deleted file mode 100644 index a149ec6..0000000 --- a/SodaLive/Sources/Content/Main/ContentMainView.swift +++ /dev/null @@ -1,188 +0,0 @@ -// -// ContentMainView.swift -// SodaLive -// -// Created by klaus on 2023/08/09. -// - -import SwiftUI - -struct ContentMainView: View { - - @StateObject var viewModel = ContentMainViewModel() - - var body: some View { - ZStack(alignment: .bottomTrailing) { - Color.black.ignoresSafeArea() - - ScrollView(.vertical, showsIndicators: false) { - VStack(alignment: .leading, spacing: 0) { - HStack(spacing: 0) { - Text("콘텐츠 마켓") - .appFont(size: 21.3, weight: .bold) - .foregroundColor(Color.button) - - Spacer() - - Image("ic_content_keep") - .onTapGesture { - AppState.shared.setAppStep(step: .myBox(currentTab: .orderlist)) - } - } - .padding(.bottom, 26.7) - .padding(.horizontal, 13.3) - - if !viewModel.isLoading { - ContentMainBannerView() - .padding(.bottom, 26.7) - .padding(.horizontal, 13.3) - - HStack(spacing: 0) { - Image("ic_title_search_black") - - Text("채널명을 입력해 보세요") - .appFont(size: 13.3, weight: .medium) - .foregroundColor(Color.gray55) - .keyboardType(.default) - .padding(.horizontal, 13.3) - - Spacer() - } - .padding(.horizontal, 21.3) - .frame(height: 50) - .frame(maxWidth: .infinity) - .background(Color.gray22) - .overlay( - RoundedRectangle(cornerRadius: 6.7) - .strokeBorder(lineWidth: 1) - .foregroundColor(Color.graybb) - ) - .padding(.bottom, 40) - .padding(.horizontal, 13.3) - .onTapGesture { - UserDefaults.set("", forKey: .searchChannel) - AppState.shared.setAppStep(step: .search) - } - - ContentMainCreatorRankingView() - .padding(.bottom, 40) - .padding(.horizontal, 13.3) - - ContentMainRecommendSeriesView() - - HStack(spacing: 8) { - ZStack { - Image("img_bg_morning_call") - .resizable() - .frame(height: 53.3) - .frame(maxWidth: .infinity) - .cornerRadius(2.6) - - HStack(spacing: 2.7) { - Image("ic_alarm_clock_blue") - - Text("보이스 모닝콜") - .appFont(size: 16.7, weight: .bold) - .foregroundColor(Color(hex: "0057ff")) - } - .cornerRadius(2.6) - } - .onTapGesture { - AppState.shared.setAppStep( - step: .contentAllByTheme(themeId: 12) - ) - } - - ZStack { - Image("img_bg_short_play") - .resizable() - .frame(height: 53.3) - .frame(maxWidth: .infinity) - .cornerRadius(2.6) - - HStack(spacing: 2.7) { - Image("ic_short_play") - - Text("숏플") - .appFont(size: 16.7, weight: .bold) - .foregroundColor(Color(hex: "dd158d")) - } - .cornerRadius(2.6) - } - .onTapGesture { - AppState.shared.setAppStep( - step: .contentAllByTheme(themeId: 11) - ) - } - } - .padding(.bottom, 40) - .padding(.horizontal, 13.3) - - ContentMainNewContentView() - .padding(.horizontal, 13.3) - - ContentMainRankingView() - .padding(.top, 40) - .padding(.horizontal, 13.3) - .animation(nil) - - ContentMainCurationView() - .padding(.top, 40) - .padding(.bottom, 20) - - Text(""" - - 회사명 : 주식회사 소다라이브 - - - 대표자 : 이재형 - - - 주소 : 경기도 성남시 분당구 황새울로335번길 10, 5층 563A호 - - - 사업자등록번호 : 870-81-03220 - - - 통신판매업신고 : 제2024-성남분당B-1012호 - - - 고객센터 : 02.2055.1477 (이용시간 10:00~19:00) - - - 대표 이메일 : sodalive.official@gmail.com - """) - .appFont(size: 11, weight: .medium) - .foregroundColor(Color.gray77) - .padding(.top, 13.3) - .padding(.horizontal, 13.3) - } - } - .padding(.vertical, 13.3) - } - - if UserDefaults.string(forKey: .role) == MemberRole.CREATOR.rawValue { - HStack(spacing: 5) { - Image("ic_thumb_play") - .resizable() - .frame(width: 20, height: 20) - - Text("콘텐츠 업로드") - .appFont(size: 13.3, weight: .bold) - .foregroundColor(.white) - } - .padding(13.3) - .background(Color(hex: "3bb9f1")) - .cornerRadius(44) - .padding(.trailing, 16.7) - .padding(.bottom, 16.7) - .onTapGesture { - AppState.shared.setAppStep(step: .createContent) - } - } - } - - if viewModel.isLoading { - LoadingView() - } - } -} - -struct ContentMainView_Previews: PreviewProvider { - static var previews: some View { - ContentMainView() - } -} diff --git a/SodaLive/Sources/Content/Main/ContentMainViewModel.swift b/SodaLive/Sources/Content/Main/ContentMainViewModel.swift deleted file mode 100644 index 7b9e6be..0000000 --- a/SodaLive/Sources/Content/Main/ContentMainViewModel.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// ContentMainViewModel.swift -// SodaLive -// -// Created by klaus on 2023/08/11. -// - -import Foundation - -import Combine - -final class ContentMainViewModel: ObservableObject { - @Published var isLoading = false - - func refresh() { - isLoading = true - - DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [unowned self] in - self.isLoading = false - } - } -} diff --git a/SodaLive/Sources/Content/Main/Curation/ContentMainCurationItemView.swift b/SodaLive/Sources/Content/Main/Curation/ContentMainCurationItemView.swift deleted file mode 100644 index 117cfec..0000000 --- a/SodaLive/Sources/Content/Main/Curation/ContentMainCurationItemView.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// ContentMainCurationItemView.swift -// SodaLive -// -// Created by klaus on 2023/08/11. -// - -import SwiftUI - -struct ContentMainCurationItemView: View { - - let item: GetAudioContentCurationResponse - - var body: some View { - VStack(alignment: .leading, spacing: 0) { - HStack(spacing: 0) { - Text(item.title) - .appFont(size: 18.3, weight: .bold) - .foregroundColor(Color(hex: "eeeeee")) - - Spacer() - - Image("ic_forward") - .resizable() - .frame(width: 20, height: 20) - .onTapGesture { - AppState.shared - .setAppStep( - step: .curationAll( - title: item.title, - curationId: item.curationId - ) - ) - } - } - - Text(item.description) - .appFont(size: 13, weight: .medium) - .foregroundColor(Color(hex: "777777")) - .padding(.top, 4) - - 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 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/GetAudioContentMainResponse.swift b/SodaLive/Sources/Content/Main/GetAudioContentMainResponse.swift index 4d6b45d..8915577 100644 --- a/SodaLive/Sources/Content/Main/GetAudioContentMainResponse.swift +++ b/SodaLive/Sources/Content/Main/GetAudioContentMainResponse.swift @@ -7,17 +7,6 @@ import Foundation -struct GetAudioContentMainResponse: Decodable { - let newContentUploadCreatorList: [ContentCreatorResponse] - let bannerList: [GetAudioContentBannerResponse] - let orderList: [GetAudioContentMainItem] - let themeList: [String] - let newContentList: [GetAudioContentMainItem] - let curationList: [GetAudioContentCurationResponse] - let contentRankingSortTypeList: [String] - let contentRanking: GetAudioContentRanking -} - struct GetAudioContentRanking: Decodable { let startDate: String let endDate: String @@ -37,12 +26,6 @@ struct GetAudioContentRankingItem: Decodable { let creatorProfileImageUrl: String } -struct ContentCreatorResponse: Decodable { - let creatorId: Int - let creatorNickname: String - let creatorProfileImageUrl: String -} - struct GetAudioContentMainItem: Decodable { let contentId: Int let coverImageUrl: String @@ -55,13 +38,6 @@ struct GetAudioContentMainItem: Decodable { let isPointAvailable: Bool } -struct GetAudioContentCurationResponse: Decodable { - let curationId: Int - let title: String - let description: String - let contents: [GetAudioContentMainItem] -} - struct GetAudioContentBannerResponse: Decodable { let type: AudioContentBannerType let thumbnailImageUrl: String diff --git a/SodaLive/Sources/Content/Main/NewContent/ContentMainNewContentThemeView.swift b/SodaLive/Sources/Content/Main/NewContent/ContentMainNewContentThemeView.swift deleted file mode 100644 index 370e305..0000000 --- a/SodaLive/Sources/Content/Main/NewContent/ContentMainNewContentThemeView.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// ContentMainNewContentThemeView.swift -// SodaLive -// -// Created by klaus on 2023/08/11. -// - -import SwiftUI - -struct ContentMainNewContentThemeView: View { - let themes: [String] - let selectTheme: (String) -> Void - - @Binding var selectedTheme: String - - var body: some View { - ScrollView(.horizontal, showsIndicators: false) { - HStack(spacing: 8) { - 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.removeAll() - 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/Order/ContentMainMyStashView.swift b/SodaLive/Sources/Content/Main/Order/ContentMainMyStashView.swift deleted file mode 100644 index 5d62e7f..0000000 --- a/SodaLive/Sources/Content/Main/Order/ContentMainMyStashView.swift +++ /dev/null @@ -1,61 +0,0 @@ -// -// 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("내 보관함") - .appFont(size: 18.3, weight: .bold) - .foregroundColor(Color(hex: "eeeeee")) - - Spacer() - - Text("전체보기") - .appFont(size: 11.3, weight: .light) - .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/Ranking/ContentMainCreatorRankingView.swift b/SodaLive/Sources/Content/Main/Ranking/ContentMainCreatorRankingView.swift deleted file mode 100644 index 844e7f3..0000000 --- a/SodaLive/Sources/Content/Main/Ranking/ContentMainCreatorRankingView.swift +++ /dev/null @@ -1,150 +0,0 @@ -// -// ContentMainCreatorRankingView.swift -// SodaLive -// -// Created by klaus on 1/5/25. -// - -import SwiftUI -import Kingfisher - -struct ContentMainCreatorRankingView: View { - - @StateObject var viewModel = ContentMainCreatorRankingViewModel() - - let rankingCrawns = ["ic_crown_1", "ic_crown_2", "ic_crown_3"] - let rankingColors = [ - [Color(hex: "ffdc00"), Color(hex: "ffb600")], - [Color(hex: "ffffff"), Color(hex: "9f9f9f")], - [Color(hex: "e6a77a"), Color(hex: "c67e4a")], - [Color(hex: "ffffff").opacity(0), Color(hex: "ffffff").opacity(0)] - ] - - var body: some View { - ZStack { - if let response = viewModel.creatorRankingResponse { - VStack(alignment: .leading, spacing: 13.3) { - VStack(alignment: .leading, spacing: 4) { - if let coloredTitle = response.coloredTitle, let color = response.color { - let titleArray = response.title.components(separatedBy: coloredTitle) - HStack(spacing: 0) { - Text(titleArray[0]) - .appFont(size: 18.3, weight: .bold) - .foregroundColor(Color.grayee) - - Text(coloredTitle) - .appFont(size: 18.3, weight: .bold) - .foregroundColor(Color(hex: color)) - - if titleArray.count > 1 { - Text(titleArray[1]) - .appFont(size: 18.3, weight: .bold) - .foregroundColor(Color.grayee) - } - } - } else { - Text(response.title) - .appFont(size: 18.3, weight: .bold) - .foregroundColor(Color.grayee) - } - - if let desc = response.desc { - VStack(spacing: 8) { - Text("\(desc)") - .appFont(size: 14.7, weight: .bold) - .foregroundColor(Color.grayee) - - Text("※ 인기 크리에이터의 순위는 매주 업데이트됩니다.") - .appFont(size: 13.3, weight: .light) - .foregroundColor(Color.graybb) - } - .padding(.vertical, 8) - .frame(maxWidth: .infinity) - .background(Color.gray22) - .padding(.top, 13.3) - } - } - .frame(maxWidth: .infinity) - .frame(alignment: .leading) - - ScrollView(.horizontal, showsIndicators: false) { - HStack(spacing: 13.3) { - ForEach(0..() - - @Published var errorMessage = "" - @Published var isShowPopup = false - @Published var creatorRankingResponse: GetExplorerSectionResponse? = nil - - func getCreatorRanking() { - repository.getCreatorRanking() - .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.self, from: responseData) - - if let data = decoded.data, decoded.success { - self.creatorRankingResponse = 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/Ranking/ContentMainRankingView.swift b/SodaLive/Sources/Content/Main/Ranking/ContentMainRankingView.swift deleted file mode 100644 index 4cd2710..0000000 --- a/SodaLive/Sources/Content/Main/Ranking/ContentMainRankingView.swift +++ /dev/null @@ -1,114 +0,0 @@ -// -// ContentMainRankingView.swift -// SodaLive -// -// Created by klaus on 2023/10/15. -// - -import SwiftUI -import Kingfisher - -struct ContentMainRankingView: View { - - @StateObject private var viewModel = ContentMainRankingViewModel() - - let rows = [ - GridItem(.fixed(60), alignment: .leading), - GridItem(.fixed(60), alignment: .leading), - GridItem(.fixed(60), alignment: .leading) - ] - - var body: some View { - LazyVStack(spacing: 16.7) { - HStack(spacing: 0) { - Text("인기 콘텐츠") - .appFont(size: 18.3, weight: .bold) - .foregroundColor(Color(hex: "eeeeee")) - - Spacer() - - Image("ic_forward") - .onTapGesture { - AppState.shared.setAppStep(step: .contentRankingAll) - } - } - - VStack(spacing: 8) { - Text("\(viewModel.dateString)") - .appFont(size: 14.7, weight: .bold) - .foregroundColor(Color(hex: "eeeeee")) - - Text("※ 인기 콘텐츠의 순위는 매주 업데이트됩니다.") - .appFont(size: 13.3, weight: .light) - .foregroundColor(Color(hex: "bbbbbb")) - } - .padding(.vertical, 8) - .frame(width: screenSize().width - 26.7) - .background(Color(hex: "222222")) - - 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.removeAll() - 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) - } -} diff --git a/SodaLive/Sources/Content/Main/RecommendSeries/ContentMainRecommendSeriesView.swift b/SodaLive/Sources/Content/Main/RecommendSeries/ContentMainRecommendSeriesView.swift deleted file mode 100644 index 3266984..0000000 --- a/SodaLive/Sources/Content/Main/RecommendSeries/ContentMainRecommendSeriesView.swift +++ /dev/null @@ -1,63 +0,0 @@ -// -// 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("추천 시리즈") - .appFont(size: 18.3, weight: .bold) - .foregroundColor(Color.grayee) - - 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 seriesList = [SeriesListItem]() - - func getRecommendSeriesList() { - repository.getRecommendSeriesList() - .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<[SeriesListItem]>.self, from: responseData) - self.isLoading = false - - if let data = decoded.data, decoded.success { - self.seriesList.removeAll() - self.seriesList.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/V2/Content/ContentMainTabContentRepository.swift b/SodaLive/Sources/Content/Main/V2/Content/ContentMainTabContentRepository.swift deleted file mode 100644 index f50f8f9..0000000 --- a/SodaLive/Sources/Content/Main/V2/Content/ContentMainTabContentRepository.swift +++ /dev/null @@ -1,64 +0,0 @@ -// -// ContentMainTabContentRepository.swift -// SodaLive -// -// Created by klaus on 2/21/25. -// - -import Foundation -import CombineMoya -import Combine -import Moya - -final class ContentMainTabContentRepository { - - private let api = MoyaProvider() - - func getContentMainContent() -> AnyPublisher { - return api.requestPublisher( - .getContentMainContent( - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } - - func getNewContentOfTheme(theme: String) -> AnyPublisher { - return api.requestPublisher( - .getContentMainNewContentOfTheme( - theme: theme, - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } - - func getContentRanking(sortType: String) -> AnyPublisher { - return api.requestPublisher( - .getDailyContentRanking( - sortType: sortType, - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } - - func getRecommendContentByTag(tag: String) -> AnyPublisher { - return api.requestPublisher( - .getRecommendContentByTag( - tag: tag, - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } - - func getPopularContentByCreator(creatorId: Int) -> AnyPublisher { - return api.requestPublisher( - .getContentMainContentPopularContentByCreator( - creatorId: creatorId, - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } -} diff --git a/SodaLive/Sources/Content/Main/V2/Content/ContentMainTabContentView.swift b/SodaLive/Sources/Content/Main/V2/Content/ContentMainTabContentView.swift deleted file mode 100644 index 6b7969e..0000000 --- a/SodaLive/Sources/Content/Main/V2/Content/ContentMainTabContentView.swift +++ /dev/null @@ -1,93 +0,0 @@ -// -// ContentMainTabContentView.swift -// SodaLive -// -// Created by klaus on 2/21/25. -// - -import SwiftUI - -struct ContentMainTabContentView: View { - - @StateObject var viewModel = ContentMainTabContentViewModel() - - var body: some View { - BaseView(isLoading: $viewModel.isLoading) { - ScrollView(.vertical, showsIndicators: false) { - VStack(spacing: 0) { - if !viewModel.bannerList.isEmpty { - ContentMainBannerViewV2(bannerList: viewModel.bannerList) - .padding(.horizontal, 13.3) - } - - if !viewModel.contentThemeList.isEmpty { - ContentMainNewContentViewV2( - title: "새로운 단편", - onClickMore: { - AppState.shared - .setAppStep(step: .newContentAll(isFree: false)) - }, - themeList: viewModel.contentThemeList, - contentList: viewModel.newContentList - ) { - viewModel.getNewContentOfTheme(theme: $0) - } - .padding(.top, 30) - } - - if !viewModel.rankSortTypeList.isEmpty { - ContentMainTabRankContentView( - title: "일간 랭킹", - isMore: false, - onClickMore: {}, - sortList: viewModel.rankSortTypeList, - onClickSort: { viewModel.getContentRanking(sort: $0) }, - contentList: viewModel.rankContentList - ) - .padding(.top, 30) - } - - if !viewModel.contentRankCreatorList.isEmpty { - ContentByChannelView( - title: "채널별 추천 단편", - creatorList: viewModel.contentRankCreatorList, - contentList: viewModel.likeCountRankContentList, - onClickCreator: { - viewModel.getPopularContentByCreator(creatorId: $0) - } - ) - .padding(.top, 30) - } - - if !viewModel.eventBannerList.isEmpty { - SectionEventBannerView(items: viewModel.eventBannerList) - .padding(.top, 30) - } - - if !viewModel.tagList.isEmpty { - ContentMainTagCurationView( - tagList: viewModel.tagList, - contentList: viewModel.tagCurationContentList - ) { - viewModel.getRecommendContentByTag(tag: $0) - } - .padding(.top, 30) - } - - if !viewModel.curationList.isEmpty { - ContentMainCurationViewV2(curationList: viewModel.curationList) - .padding(.top, 30) - } - } - .onAppear { - viewModel.fetchData() - } - } - } - .sodaToast(isPresented: $viewModel.isShowPopup, message: viewModel.errorMessage, autohideIn: 2) - } -} - -#Preview { - ContentMainTabContentView() -} diff --git a/SodaLive/Sources/Content/Main/V2/Content/ContentMainTabContentViewModel.swift b/SodaLive/Sources/Content/Main/V2/Content/ContentMainTabContentViewModel.swift deleted file mode 100644 index fe02a24..0000000 --- a/SodaLive/Sources/Content/Main/V2/Content/ContentMainTabContentViewModel.swift +++ /dev/null @@ -1,231 +0,0 @@ -// -// ContentMainTabContentViewModel.swift -// SodaLive -// -// Created by klaus on 2/21/25. -// - -import Foundation -import Combine - -final class ContentMainTabContentViewModel: ObservableObject { - private let repository = ContentMainTabContentRepository() - private var subscription = Set() - - @Published var errorMessage = "" - @Published var isShowPopup = false - @Published var isLoading = false - - @Published var bannerList: [GetAudioContentBannerResponse] = [] - @Published var contentThemeList: [String] = [] - @Published var newContentList: [GetAudioContentMainItem] = [] - @Published var rankSortTypeList: [String] = [] - @Published var rankContentList: [GetAudioContentRankingItem] = [] - @Published var contentRankCreatorList: [ContentCreatorResponse] = [] - @Published var likeCountRankContentList: [GetAudioContentRankingItem] = [] - @Published var eventBannerList: [EventItem] = [] - @Published var tagList: [String] = [] - @Published var tagCurationContentList: [GetAudioContentMainItem] = [] - @Published var curationList: [GetContentCurationResponse] = [] - - func fetchData() { - isLoading = true - repository.getContentMainContent() - .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.self, from: responseData) - - if let data = decoded.data, decoded.success { - self.bannerList = data.bannerList - self.contentThemeList = ["전체"] + data.contentThemeList - self.newContentList = data.newContentList - self.rankSortTypeList = data.rankSortTypeList - self.rankContentList = data.rankContentList - self.contentRankCreatorList = data.contentRankCreatorList - self.likeCountRankContentList = data.likeCountRankContentList - self.eventBannerList = data.eventBannerList.eventList - self.tagList = data.tagList - self.tagCurationContentList = data.tagCurationContentList - self.curationList = data.curationList - } 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) - } - - func getNewContentOfTheme(theme: String) { - isLoading = true - - repository.getNewContentOfTheme(theme: theme == "전체" ? "" : theme) - .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<[GetAudioContentMainItem]>.self, from: responseData) - - if let data = decoded.data, decoded.success { - self.newContentList = 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) - } - - func getContentRanking(sort: String = "매출") { - isLoading = true - repository.getContentRanking(sortType: sort) - .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<[GetAudioContentRankingItem]>.self, from: responseData) - - if let data = decoded.data, decoded.success { - self.rankContentList = 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) - } - - func getRecommendContentByTag(tag: String) { - isLoading = true - repository.getRecommendContentByTag(tag: tag) - .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<[GetAudioContentMainItem]>.self, from: responseData) - - if let data = decoded.data, decoded.success { - self.tagCurationContentList = 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) - } - - func getPopularContentByCreator(creatorId: Int) { - isLoading = true - repository.getPopularContentByCreator(creatorId: creatorId) - .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<[GetAudioContentRankingItem]>.self, from: responseData) - - if let data = decoded.data, decoded.success { - self.likeCountRankContentList = 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/V2/Content/ContentMainTabRankContentView.swift b/SodaLive/Sources/Content/Main/V2/Content/ContentMainTabRankContentView.swift deleted file mode 100644 index af4dc82..0000000 --- a/SodaLive/Sources/Content/Main/V2/Content/ContentMainTabRankContentView.swift +++ /dev/null @@ -1,165 +0,0 @@ -// -// ContentMainTabRankContentView.swift -// SodaLive -// -// Created by klaus on 2/20/25. -// - -import SwiftUI - -import Kingfisher - -struct ContentMainTabRankContentView: View { - - @AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token) - - let rows = [ - GridItem(.fixed(60), alignment: .leading), - GridItem(.fixed(60), alignment: .leading), - GridItem(.fixed(60), alignment: .leading) - ] - - let title: String - - let isMore: Bool - let onClickMore: () -> Void - - let sortList: [String] - let onClickSort: (String) -> Void - - let contentList: [GetAudioContentRankingItem] - - @State private var selectedSort = "" - - var body: some View { - VStack(spacing: 13.3) { - HStack(spacing: 0) { - Text(title) - .appFont(size: 18.3, weight: .bold) - .foregroundColor(Color(hex: "eeeeee")) - - Spacer() - - if isMore { - Image("ic_forward") - .onTapGesture { onClickMore() } - } - } - .padding(.horizontal, 13.3) - - if !sortList.isEmpty { - ContentMainRankingSortView( - sorts: sortList, - selectSort: { - selectedSort = $0 - onClickSort($0) - }, - selectedSort: $selectedSort - ) - } - - ScrollView(.horizontal, showsIndicators: false) { - LazyHGrid(rows: rows, spacing: 13.3) { - ForEach(0.. Void - - let tagColumns = [ - GridItem(.flexible()), - GridItem(.flexible()), - GridItem(.flexible()), - GridItem(.flexible()) - ] - - let contentColumns = [ - GridItem(.flexible()), - GridItem(.flexible()), - GridItem(.flexible()) - ] - - @State private var selectedTag = "" - - var body: some View { - VStack(alignment: .leading, spacing: 13.3) { - Text("태그별 추천 콘텐츠") - .appFont(size: 18.3, weight: .bold) - .foregroundColor(.grayee) - .padding(.horizontal, 13.3) - - LazyVGrid(columns: tagColumns, spacing: 6) { - ForEach(0.. 0 { - Image("ic_card_can_gray") - - Text("\(item.price)") - .appFont(size: 8.5, weight: .medium) - .foregroundColor(Color.white) - } else { - Text("무료") - .appFont(size: 8.5, weight: .medium) - .foregroundColor(Color.white) - } - } - .padding(3) - .background(Color.gray33.opacity(0.7)) - .cornerRadius(10) - .padding(.leading, 2.7) - .padding(.bottom, 2.7) - - Spacer() - - HStack(spacing: 2) { - Text(item.duration) - .appFont(size: 8.5, weight: .medium) - .foregroundColor(Color.white) - } - .padding(3) - .background(Color.gray33.opacity(0.7)) - .cornerRadius(10) - .padding(.trailing, 2.7) - .padding(.bottom, 2.7) - } - } - } - .frame(width: itemWidth, height: itemWidth) - - Text(item.title) - .appFont(size: 13.3, weight: .medium) - .foregroundColor(Color.grayd2) - .frame(width: itemWidth, alignment: .leading) - .multilineTextAlignment(.leading) - .fixedSize(horizontal: false, vertical: true) - .lineLimit(2) - - HStack(spacing: 5.3) { - KFImage(URL(string: item.creatorProfileImageUrl)) - .cancelOnDisappear(true) - .downsampling( - size: CGSize( - width: 21.3, - height: 21.3 - ) - ) - .resizable() - .scaledToFill() - .frame(width: 21.3, height: 21.3) - .clipShape(Circle()) - .onTapGesture { AppState.shared.setAppStep(step: .creatorDetail(userId: item.creatorId)) } - - Text(item.creatorNickname) - .appFont(size: 12, weight: .medium) - .foregroundColor(.gray77) - .lineLimit(1) - } - .padding(.bottom, 10) - } - .onTapGesture { - AppState.shared - .setAppStep(step: .contentDetail(contentId: item.contentId)) - } - } -} - -#Preview { - ContentMainTagCurationView( - tagList: ["test", "test2", "test3", "test4", "test5", "test6", "test7"], - contentList: [ - GetAudioContentMainItem( - contentId: 1, - coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", - title: "ㅓ처랴햐햫햐햐", - creatorId: 8, - creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", - creatorNickname: "유저1", - price: 100, - duration: "00:00:30", - isPointAvailable: true - ), - GetAudioContentMainItem( - contentId: 2, - coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", - title: "ㅓ처랴햐햫햐햐", - creatorId: 8, - creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", - creatorNickname: "유저2", - price: 0, - duration: "00:00:30", - isPointAvailable: false - ), - GetAudioContentMainItem( - contentId: 3, - coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", - title: "ㅓ처랴햐햫햐햐", - creatorId: 8, - creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", - creatorNickname: "유저3", - price: 1000, - duration: "00:00:30", - isPointAvailable: false - ), - GetAudioContentMainItem( - contentId: 4, - coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", - title: "ㅓ처랴햐햫햐햐", - creatorId: 8, - creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", - creatorNickname: "유저3", - price: 50000, - duration: "00:00:30", - isPointAvailable: false - ) - ], - selectTag: { _ in } - ) -} diff --git a/SodaLive/Sources/Content/Main/V2/Content/GetContentMainTabContentResponse.swift b/SodaLive/Sources/Content/Main/V2/Content/GetContentMainTabContentResponse.swift deleted file mode 100644 index e75602f..0000000 --- a/SodaLive/Sources/Content/Main/V2/Content/GetContentMainTabContentResponse.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// GetContentMainTabContentResponse.swift -// SodaLive -// -// Created by klaus on 2/21/25. -// - -struct GetContentMainTabContentResponse: Decodable { - let bannerList: [GetAudioContentBannerResponse] - let contentThemeList: [String] - let newContentList: [GetAudioContentMainItem] - let rankSortTypeList: [String] - let rankContentList: [GetAudioContentRankingItem] - let contentRankCreatorList: [ContentCreatorResponse] - let likeCountRankContentList: [GetAudioContentRankingItem] - let eventBannerList: GetEventResponse - let tagList: [String] - let tagCurationContentList: [GetAudioContentMainItem] - let curationList: [GetContentCurationResponse] -} diff --git a/SodaLive/Sources/Content/Main/V2/ContentByChannelView.swift b/SodaLive/Sources/Content/Main/V2/ContentByChannelView.swift deleted file mode 100644 index 6ffa6be..0000000 --- a/SodaLive/Sources/Content/Main/V2/ContentByChannelView.swift +++ /dev/null @@ -1,220 +0,0 @@ -// -// ContentByChannelView.swift -// SodaLive -// -// Created by klaus on 2/20/25. -// - -import SwiftUI -import Kingfisher - -struct ContentByChannelView: View { - - @AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token) - - let title: String - let creatorList: [ContentCreatorResponse] - let contentList: [GetAudioContentRankingItem] - let onClickCreator: (Int) -> Void - - @State private var selectedCreatorId = 0 - - let columns = [ - GridItem(.flexible(), spacing: 13.3), - GridItem(.flexible(), spacing: 13.3) - ] - - var body: some View { - VStack(alignment: .leading, spacing: 20) { - Text(title) - .appFont(size: 18.3, weight: .bold) - .foregroundColor(.grayee) - .padding(.horizontal, 13.3) - - ScrollView(.horizontal, showsIndicators: false) { - HStack(spacing: 22) { - ForEach(0.. 0 { - Image("ic_card_can_gray_32") - } - - Text(content.price > 0 ? "\(content.price)" : "무료") - .appFont(size: 12, weight: .medium) - .foregroundColor(Color.white) - } - .padding(4) - .background(Color.gray33.opacity(0.7)) - .cornerRadius(10) - - Spacer() - - Text(content.duration) - .appFont(size: 12, weight: .medium) - .foregroundColor(Color.white) - .padding(4) - .background(Color.gray33.opacity(0.7)) - .cornerRadius(10) - } - .padding(.horizontal, 2.7) - .padding(.bottom, 2.7) - } - - Text(content.title) - .appFont(size: 13.3, weight: .medium) - .foregroundColor(.grayd2) - .lineLimit(1) - - HStack(spacing: 5.3) { - KFImage(URL(string: content.creatorProfileImageUrl)) - .cancelOnDisappear(true) - .downsampling(size: CGSize(width: 21, height: 21)) - .resizable() - .frame(width: 21, height: 21) - .clipShape(Circle()) - .clipped() - - Text(content.creatorNickname) - .appFont(size: 10, weight: .medium) - .foregroundColor(.gray77) - } - .onTapGesture { - if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - AppState.shared - .setAppStep(step: .creatorDetail(userId: content.creatorId)) - } else { - AppState.shared - .setAppStep(step: .login) - } - } - } - .onTapGesture { - if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - AppState.shared - .setAppStep(step: .contentDetail(contentId: content.contentId)) - } else { - AppState.shared - .setAppStep(step: .login) - } - } - } - } - .padding(.horizontal, 13.3) - } - .onAppear { - if !self.creatorList.isEmpty { - selectedCreatorId = creatorList[0].creatorId - } - } - } -} - -#Preview { - ContentByChannelView( - title: "채널별 인기 콘텐츠", - creatorList: [ - ContentCreatorResponse( - creatorId: 1, - creatorNickname: "유저1", - creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png" - ), - ContentCreatorResponse( - creatorId: 2, - creatorNickname: "유저2", - creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png" - ) - ], - contentList: [ - GetAudioContentRankingItem( - contentId: 1, - title: "안녕하세요 오늘은 커버곡을 들려드릴께요....안녕하세요 오늘은 커버곡을 들려드릴께요....", - coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", - themeStr: "커버곡", - price: 100, - duration: "00:30:20", - creatorId: 1, - creatorNickname: "유저1", - isPointAvailable: true, - creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png" - ), - GetAudioContentRankingItem( - contentId: 2, - title: "안녕하세요 오늘은 커버곡을 들려드릴께요....", - coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", - themeStr: "커버곡", - price: 0, - duration: "00:30:20", - creatorId: 1, - creatorNickname: "유저1", - isPointAvailable: false, - creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png" - ), - GetAudioContentRankingItem( - contentId: 3, - title: "안녕하세요 오늘은 커버곡을 들려드릴께요....안녕하세요 오늘은 커버곡을 들려드릴께요....", - coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", - themeStr: "커버곡", - price: 50, - duration: "00:30:20", - creatorId: 1, - creatorNickname: "유저1", - isPointAvailable: false, - creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png" - ), - GetAudioContentRankingItem( - contentId: 4, - title: "안녕하세요 오늘은 커버곡을 들려드릴께요....안녕하세요 오늘은 커버곡을 들려드릴께요....", - coverImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png", - themeStr: "커버곡", - price: 50, - duration: "00:30:20", - creatorId: 1, - creatorNickname: "유저1", - isPointAvailable: false, - creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png" - ) - ] - ) { _ in } -} diff --git a/SodaLive/Sources/Content/Main/V2/ContentCreatorView.swift b/SodaLive/Sources/Content/Main/V2/ContentCreatorView.swift deleted file mode 100644 index d2c9691..0000000 --- a/SodaLive/Sources/Content/Main/V2/ContentCreatorView.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// ContentCreatorView.swift -// SodaLive -// -// Created by klaus on 2/20/25. -// - -import SwiftUI -import Kingfisher - -struct ContentCreatorView: View { - - let isSelected: Bool - let item: ContentCreatorResponse - - var body: some View { - VStack(spacing: 13.3) { - KFImage(URL(string: item.creatorProfileImageUrl)) - .cancelOnDisappear(true) - .downsampling(size: CGSize(width: 60, height: 60)) - .resizable() - .frame(width: 60, height: 60) - .clipShape(Circle()) - .overlay( - Circle() - .strokeBorder(lineWidth: 3) - .foregroundColor( - .button - .opacity(isSelected ? 1 : 0) - ) - ) - - Text(item.creatorNickname) - .appFont(size: 11.3, weight: .medium) - .foregroundColor( - isSelected ? - Color.button : - Color.graybb - - ) - .lineLimit(1) - .truncationMode(.tail) - } - .frame(width: 60) - } -} - -#Preview { - ContentCreatorView( - isSelected: true, - item: ContentCreatorResponse( - creatorId: 1, - creatorNickname: "유저1", - creatorProfileImageUrl: "https://test-cf.sodalive.net/profile/default-profile.png" - ) - ) -} diff --git a/SodaLive/Sources/Content/Main/V2/ContentMainCurationViewV2.swift b/SodaLive/Sources/Content/Main/V2/ContentMainCurationViewV2.swift deleted file mode 100644 index 785704b..0000000 --- a/SodaLive/Sources/Content/Main/V2/ContentMainCurationViewV2.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// ContentMainCurationViewV2.swift -// SodaLive -// -// Created by klaus on 2/21/25. -// - -import SwiftUI - -struct ContentMainCurationViewV2: View { - - let curationList: [GetContentCurationResponse] - - var body: some View { - LazyVStack(spacing: 30) { - ForEach(0.. Void - let themeList: [String] - let contentList: [GetAudioContentMainItem] - - let selectTheme: (String) -> Void - @State private var selectedTheme = "전체" - - var body: some View { - LazyVStack(spacing: 13.3) { - HStack(spacing: 0) { - Text(title) - .appFont(size: 18.3, weight: .bold) - .foregroundColor(.grayee) - - Spacer() - - Image("ic_forward") - .resizable() - .frame(width: 20, height: 20) - .onTapGesture { onClickMore() } - } - .padding(.horizontal, 13.3) - - if !themeList.isEmpty { - ContentMainContentThemeView( - themeList: themeList, - selectTheme: selectTheme, - selectedTheme: $selectedTheme - ) - } - - ScrollView(.horizontal, showsIndicators: false) { - HStack(spacing: 13.3) { - ForEach(0..() - - @Published var errorMessage = "" - @Published var isShowPopup = false - @Published var isLoading = false - - @Published var rankCompleteSeriesList: [SeriesListItem] = [] - @Published var totalCount = 0 - - var isLast = false - var page = 1 - private let size = 20 - - func getCompletedSeries() { - if !isLast && !isLoading { - isLoading = true - - repository.getCompletedSeries(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 - let responseData = response.data - - do { - let jsonDecoder = JSONDecoder() - let decoded = try jsonDecoder.decode(ApiResponse.self, from: responseData) - - if let data = decoded.data, decoded.success { - page += 1 - - if (data.items.count > 0) { - self.totalCount = data.totalCount - self.rankCompleteSeriesList = 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) - } - } -} diff --git a/SodaLive/Sources/Content/Main/V2/Series/ContentMainCompletedSeriesView.swift b/SodaLive/Sources/Content/Main/V2/Series/ContentMainCompletedSeriesView.swift deleted file mode 100644 index 9e32940..0000000 --- a/SodaLive/Sources/Content/Main/V2/Series/ContentMainCompletedSeriesView.swift +++ /dev/null @@ -1,83 +0,0 @@ -// -// ContentMainCompletedSeriesView.swift -// SodaLive -// -// Created by klaus on 2/21/25. -// - -import SwiftUI - -struct ContentMainCompletedSeriesView: View { - - let itemList: [SeriesListItem] - let onClickMore: () -> Void - - var body: some View { - VStack(spacing: 13.3) { - HStack(spacing: 0) { - Text("완결 시리즈") - .appFont(size: 18.3, weight: .bold) - .foregroundColor(.grayee) - - Spacer() - - Image("ic_forward") - .onTapGesture { - onClickMore() - } - } - .padding(.horizontal, 13.3) - - ScrollView(.horizontal, showsIndicators: false) { - HStack(spacing: 13.3) { - ForEach(0.. Void - - @State private var selectedGenreId = 0 - - var body: some View { - VStack(alignment: .leading, spacing: 13.3) { - Text("장르별 추천 시리즈") - .appFont(size: 18.3, weight: .bold) - .foregroundColor(Color.grayee) - .padding(.horizontal, 13.3) - - ContentMainSeriesGenreView( - genreList: genreList, - selectGenre: { - selectedGenreId = $0 - onClickGenre($0) - }, - selectedGenreId: $selectedGenreId - ) - - if !itemList.isEmpty { - ScrollView(.horizontal, showsIndicators: false) { - HStack(spacing: 13.3) { - ForEach(0.. Void - - @Binding var selectedGenreId: Int - - var body: some View { - ScrollView(.horizontal, showsIndicators: false) { - HStack(alignment: .top, spacing: 8) { - ForEach(0..() - - func getContentMainSeries() -> AnyPublisher { - return api.requestPublisher( - .getContentMainSeries( - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } - - func getRecommendSeriesListByGenre(genreId: Int) -> AnyPublisher { - return api.requestPublisher( - .getRecommendSeriesListByGenre( - genreId: genreId, - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } - - func getRecommendSeriesByCreator(creatorId: Int) -> AnyPublisher { - return api.requestPublisher( - .getRecommendSeriesByCreator( - creatorId: creatorId, - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } - - func getCompletedSeries(page: Int, size: Int) -> AnyPublisher { - return api.requestPublisher( - .getCompletedSeries( - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL, - page: page, - size: size - ) - ) - } -} diff --git a/SodaLive/Sources/Content/Main/V2/Series/ContentMainTabSeriesView.swift b/SodaLive/Sources/Content/Main/V2/Series/ContentMainTabSeriesView.swift deleted file mode 100644 index 0ff92f5..0000000 --- a/SodaLive/Sources/Content/Main/V2/Series/ContentMainTabSeriesView.swift +++ /dev/null @@ -1,95 +0,0 @@ -// -// ContentMainTabSeriesView.swift -// SodaLive -// -// Created by klaus on 2/20/25. -// - -import SwiftUI - -struct ContentMainTabSeriesView: View { - - @StateObject var viewModel = ContentMainTabSeriesViewModel() - - var body: some View { - BaseView(isLoading: $viewModel.isLoading) { - ScrollView(.vertical, showsIndicators: false) { - VStack(spacing: 0) { - if !viewModel.bannerList.isEmpty { - ContentMainBannerViewV2(bannerList: viewModel.bannerList) - .padding(.horizontal, 13.3) - } - - if !viewModel.originalAudioDramaList.isEmpty { - ContentMainOriginalAudioDramaView(itemList: viewModel.originalAudioDramaList) { - } - .padding(.top, 30) - } - - if !viewModel.rankSeriesList.isEmpty { - ContentMainSeriesRankingView(seriesList: viewModel.rankSeriesList) - .padding(.top, 30) - } - - if !viewModel.genreList.isEmpty { - ContentMainSeriesByGenreView( - genreList: viewModel.genreList, - itemList: viewModel.recommendSeriesList - ) { - viewModel.getRecommendSeriesListByGenre(genreId: $0) - } - .padding(.top, 30) - } - - if !viewModel.newSeriesList.isEmpty { - ContentMainNewOrRecommendSeriesView( - title: "새로운 시리즈", - recommendSeriesList: viewModel.newSeriesList - ) - .padding(.top, 30) - } - - if !viewModel.rankCompleteSeriesList.isEmpty { - ContentMainCompletedSeriesView( - itemList: viewModel.rankCompleteSeriesList, - onClickMore: { - AppState.shared - .setAppStep(step: .completedSeriesAll) - } - ) - .padding(.top, 30) - } - - if !viewModel.seriesRankCreatorList.isEmpty { - SeriesByChannelView( - title: "채널별 추천 시리즈", - creatorList: viewModel.seriesRankCreatorList, - seriesList: viewModel.recommendSeriesByChannel - ) { - viewModel.getRecommendSeriesByCreator(creatorId: $0) - } - .padding(.top, 30) - } - - if !viewModel.eventBannerList.isEmpty { - SectionEventBannerView(items: viewModel.eventBannerList) - .padding(.top, 30) - } - - if !viewModel.curationList.isEmpty { - ContentMainSeriesCurationView(curationList: viewModel.curationList) - .padding(.top, 30) - } - } - .onAppear { - viewModel.fetchData() - } - } - } - .sodaToast(isPresented: $viewModel.isShowPopup, message: viewModel.errorMessage, autohideIn: 2) - } -} - -#Preview { - ContentMainTabSeriesView() -} diff --git a/SodaLive/Sources/Content/Main/V2/Series/ContentMainTabSeriesViewModel.swift b/SodaLive/Sources/Content/Main/V2/Series/ContentMainTabSeriesViewModel.swift deleted file mode 100644 index c3b9d73..0000000 --- a/SodaLive/Sources/Content/Main/V2/Series/ContentMainTabSeriesViewModel.swift +++ /dev/null @@ -1,157 +0,0 @@ -// -// ContentMainTabSeriesViewModel.swift -// SodaLive -// -// Created by klaus on 2/20/25. -// - -import Foundation -import Combine - -final class ContentMainTabSeriesViewModel: ObservableObject { - private let repository = ContentMainTabSeriesRepository() - private let contentRepository = ContentRepository() - private var subscription = Set() - - @Published var errorMessage = "" - @Published var isShowPopup = false - @Published var isLoading = false - - @Published var bannerList: [GetAudioContentBannerResponse] = [] - @Published var originalAudioDramaList: [SeriesListItem] = [] - @Published var rankSeriesList: [SeriesListItem] = [] - @Published var genreList: [GetSeriesGenreListResponse] = [] - @Published var recommendSeriesList: [SeriesListItem] = [] - @Published var newSeriesList: [GetRecommendSeriesListResponse] = [] - @Published var rankCompleteSeriesList: [SeriesListItem] = [] - @Published var seriesRankCreatorList: [ContentCreatorResponse] = [] - @Published var recommendSeriesByChannel: [SeriesListItem] = [] - @Published var eventBannerList: [EventItem] = [] - @Published var curationList: [GetSeriesCurationResponse] = [] - - func fetchData() { - isLoading = true - - repository.getContentMainSeries() - .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.self, from: responseData) - - if let data = decoded.data, decoded.success { - self.bannerList = data.contentBannerList - self.originalAudioDramaList = data.originalAudioDrama - self.rankSeriesList = data.rankSeriesList - self.genreList = data.genreList - self.recommendSeriesList = data.recommendSeriesList - self.newSeriesList = data.newSeriesList - self.rankCompleteSeriesList = data.rankCompleteSeriesList - self.seriesRankCreatorList = data.seriesRankCreatorList - self.recommendSeriesByChannel = data.recommendSeriesByChannel - self.eventBannerList = data.eventBannerList.eventList - self.curationList = data.curationList - } 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) - } - - func getRecommendSeriesListByGenre(genreId: Int) { - isLoading = true - repository.getRecommendSeriesListByGenre(genreId: genreId) - .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<[SeriesListItem]>.self, from: responseData) - - if let data = decoded.data, decoded.success { - self.recommendSeriesList = 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) - } - - func getRecommendSeriesByCreator(creatorId: Int) { - recommendSeriesByChannel = [] - isLoading = true - repository.getRecommendSeriesByCreator(creatorId: creatorId) - .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<[SeriesListItem]>.self, from: responseData) - - if let data = decoded.data, decoded.success { - self.recommendSeriesByChannel = 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/V2/Series/GetContentMainTabSeriesResponse.swift b/SodaLive/Sources/Content/Main/V2/Series/GetContentMainTabSeriesResponse.swift index 85e3874..15bc85d 100644 --- a/SodaLive/Sources/Content/Main/V2/Series/GetContentMainTabSeriesResponse.swift +++ b/SodaLive/Sources/Content/Main/V2/Series/GetContentMainTabSeriesResponse.swift @@ -5,35 +5,7 @@ // Created by klaus on 2/20/25. // -struct GetContentMainTabSeriesResponse: Decodable { - let contentBannerList: [GetAudioContentBannerResponse] - let originalAudioDrama: [SeriesListItem] - let rankSeriesList: [SeriesListItem] - let genreList: [GetSeriesGenreListResponse] - let recommendSeriesList: [SeriesListItem] - let newSeriesList: [GetRecommendSeriesListResponse] - let rankCompleteSeriesList: [SeriesListItem] - let seriesRankCreatorList: [ContentCreatorResponse] - let recommendSeriesByChannel: [SeriesListItem] - let eventBannerList: GetEventResponse - let curationList: [GetSeriesCurationResponse] -} - struct GetSeriesGenreListResponse: Decodable { let id: Int let genre: String } - -struct GetRecommendSeriesListResponse: Decodable { - let seriesId: Int - let title: String - let imageUrl: String - let creatorId: Int - let creatorNickname: String - let creatorProfileImageUrl: String -} - -struct GetSeriesCurationResponse: Decodable { - let title: String - let items: [SeriesListItem] -} diff --git a/SodaLive/Sources/Content/Main/V2/Series/OriginalAudioDrama/ContentMainOriginalAudioDramaItemView.swift b/SodaLive/Sources/Content/Main/V2/Series/OriginalAudioDrama/ContentMainOriginalAudioDramaItemView.swift deleted file mode 100644 index cf19c7c..0000000 --- a/SodaLive/Sources/Content/Main/V2/Series/OriginalAudioDrama/ContentMainOriginalAudioDramaItemView.swift +++ /dev/null @@ -1,108 +0,0 @@ -// -// ContentMainOriginalAudioDramaItemView.swift -// SodaLive -// -// Created by klaus on 2/21/25. -// - -import SwiftUI -import Kingfisher - -struct ContentMainOriginalAudioDramaItemView: View { - - let itemWidth: CGFloat - let item: SeriesListItem - let isAll: Bool - - var body: some View { - VStack(alignment: .leading, spacing: 8) { - ZStack { - KFImage(URL(string: item.coverImage)) - .cancelOnDisappear(true) - .downsampling( - size: CGSize( - width: itemWidth, - height: (itemWidth * 636) / 450 - ) - ) - .resizable() - .scaledToFill() - .frame(width: itemWidth, height: (itemWidth * 636) / 450, alignment: .center) - .cornerRadius(5) - .clipped() - .onTapGesture { - AppState.shared - .setAppStep(step: .seriesDetail(seriesId: item.seriesId)) - } - - VStack(alignment: .leading, spacing: 0) { - HStack(spacing: 3.3) { - if !item.isComplete && item.isNew { - SeriesItemBadgeView(title: "신작", backgroundColor: .button) - } - - if item.isComplete { - SeriesItemBadgeView(title: "완결", backgroundColor: Color(hex: "002abd")) - } - - if item.isPopular { - SeriesItemBadgeView(title: "인기", backgroundColor: Color(hex: "ec6033")) - } - - Spacer() - - if !isAll { - SeriesItemBadgeView(title: "총 \(item.numberOfContent)화", backgroundColor: Color.gray33.opacity(0.7)) - } - } - - Spacer() - - HStack { - Spacer() - - if isAll { - SeriesItemBadgeView(title: "총 \(item.numberOfContent)화", backgroundColor: Color.gray33.opacity(0.7)) - } - } - } - .padding(3.3) - } - .frame(width: itemWidth, height: (itemWidth * 636) / 450, alignment: .center) - - Text(item.title) - .appFont(size: 12, weight: .medium) - .foregroundColor(Color.grayee) - .lineLimit(1) - - if isAll { - Text(item.publishedDaysOfWeek) - .appFont(size: 11, weight: .medium) - .foregroundColor(Color.gray77) - } - } - .frame(width: itemWidth) - } -} - -#Preview { - ContentMainOriginalAudioDramaItemView( - itemWidth: 150, - item: SeriesListItem( - seriesId: 1, - title: "제목, 관심사,프로필+방장, 참여인원(어딘가..)", - coverImage: "https://test-cf.sodalive.net/profile/default-profile.png", - publishedDaysOfWeek: "매주 수, 토요일", - isComplete: true, - creator: SeriesListItemCreator( - creatorId: 1, - nickname: "creator", - profileImage: "https://test-cf.sodalive.net/profile/default-profile.png" - ), - numberOfContent: 10, - isNew: false, - isPopular: true - ), - isAll: false - ) -} diff --git a/SodaLive/Sources/Content/Main/V2/Series/OriginalAudioDrama/ContentMainOriginalAudioDramaView.swift b/SodaLive/Sources/Content/Main/V2/Series/OriginalAudioDrama/ContentMainOriginalAudioDramaView.swift deleted file mode 100644 index 9590ec8..0000000 --- a/SodaLive/Sources/Content/Main/V2/Series/OriginalAudioDrama/ContentMainOriginalAudioDramaView.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// ContentMainOriginalAudioDramaView.swift -// SodaLive -// -// Created by klaus on 2/21/25. -// - -import SwiftUI - -struct ContentMainOriginalAudioDramaView: View { - - let itemList: [SeriesListItem] - let onClickMore: () -> Void - - var body: some View { - VStack(spacing: 13.3) { - HStack(spacing: 0) { - Text("오리지널 오디오 드라마") - .appFont(size: 18.3, weight: .bold) - .foregroundColor(.grayee) - - Spacer() - - Image("ic_forward") - .onTapGesture { onClickMore() } - } - .padding(.horizontal, 13.3) - - ScrollView(.horizontal, showsIndicators: false) { - HStack(spacing: 13.3) { - ForEach(0.. Void - - @State private var selectedCreatorId = 0 - - var body: some View { - VStack(alignment: .leading, spacing: 13.3) { - Text(title) - .appFont(size: 18.3, weight: .bold) - .foregroundColor(Color(hex: "eeeeee")) - .padding(.horizontal, 13.3) - - ScrollView(.horizontal) { - HStack(spacing: 22) { - ForEach(0..