From 53266dc91bd9d36613d986eaabf3bf243545a6d1 Mon Sep 17 00:00:00 2001 From: Yu Sung Date: Sat, 22 Feb 2025 04:42:50 +0900 Subject: [PATCH] =?UTF-8?q?=EC=BD=98=ED=85=90=EC=B8=A0=20=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20-=20=EB=AA=A8=EB=8B=9D=EC=BD=9C=20=ED=83=AD=20UI=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SodaLive/Sources/Content/ContentApi.swift | 22 +++- .../Alarm/ContentMainTabAlarmRepository.swift | 24 ++++ .../V2/Alarm/ContentMainTabAlarmView.swift | 70 ++++++++++++ .../Alarm/ContentMainTabAlarmViewModel.swift | 106 ++++++++++++++++++ .../GetContentMainTabAlarmResponse.swift | 17 +++ .../Main/V2/Asmr/ContentMainTabAsmrView.swift | 18 +++ .../V2/Asmr/ContentMainTabAsmrViewModel.swift | 14 +++ .../Content/Main/V2/ContentMainViewV2.swift | 8 +- .../Main/V2/Free/ContentMainTabFreeView.swift | 26 +++++ .../V2/Free/ContentMainTabFreeViewModel.swift | 14 +++ .../V2/Replay/ContentMainTabReplayView.swift | 18 +++ .../ContentMainTabReplayViewModel.swift | 14 +++ 12 files changed, 345 insertions(+), 6 deletions(-) create mode 100644 SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmRepository.swift create mode 100644 SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmView.swift create mode 100644 SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmViewModel.swift create mode 100644 SodaLive/Sources/Content/Main/V2/Alarm/GetContentMainTabAlarmResponse.swift create mode 100644 SodaLive/Sources/Content/Main/V2/Asmr/ContentMainTabAsmrView.swift create mode 100644 SodaLive/Sources/Content/Main/V2/Asmr/ContentMainTabAsmrViewModel.swift create mode 100644 SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeView.swift create mode 100644 SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeViewModel.swift create mode 100644 SodaLive/Sources/Content/Main/V2/Replay/ContentMainTabReplayView.swift create mode 100644 SodaLive/Sources/Content/Main/V2/Replay/ContentMainTabReplayViewModel.swift diff --git a/SodaLive/Sources/Content/ContentApi.swift b/SodaLive/Sources/Content/ContentApi.swift index 5fd5348..d8f7d74 100644 --- a/SodaLive/Sources/Content/ContentApi.swift +++ b/SodaLive/Sources/Content/ContentApi.swift @@ -50,6 +50,9 @@ enum ContentApi { case getContentMainNewContentOfTheme(theme: String, isAdultContentVisible: Bool, contentType: ContentType) case getDailyContentRanking(sortType: String) case getRecommendContentByTag(tag: String) + + case getContentMainAlarm + case getContentMainAlarmAll(theme: String, page: Int, size: Int) } extension ContentApi: TargetType { @@ -172,6 +175,12 @@ extension ContentApi: TargetType { case .getRecommendContentByTag: return "/v2/audio-content/main/content/recommend-content-by-tag" + + case .getContentMainAlarm: + return "/v2/audio-content/main/alarm" + + case .getContentMainAlarmAll: + return "/v2/audio-content/main/alarm/all" } } @@ -187,7 +196,7 @@ extension ContentApi: TargetType { return .get case .getContentMainHome, .getPopularContentByCreator, .getContentMainSeries, .getRecommendSeriesListByGenre, .getRecommendSeriesByCreator, .getContentMainContent, - .getContentMainNewContentOfTheme, .getDailyContentRanking, .getRecommendContentByTag: + .getContentMainNewContentOfTheme, .getDailyContentRanking, .getRecommendContentByTag, .getContentMainAlarm, .getContentMainAlarmAll: return .get case .likeContent, .modifyAudioContent, .modifyComment, .unpinContent: @@ -344,7 +353,7 @@ extension ContentApi: TargetType { return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) - case .getContentMainHome, .getContentMainSeries, .getContentMainContent: + case .getContentMainHome, .getContentMainSeries, .getContentMainContent, .getContentMainAlarm: return .requestPlain case .getRecommendSeriesListByGenre(let genreId): @@ -374,6 +383,15 @@ extension ContentApi: TargetType { case .getRecommendContentByTag(let tag): let parameters = ["tag": tag] return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) + + case .getContentMainAlarmAll(let theme, let page, let size): + let parameters = [ + "theme": theme, + "page": page - 1, + "size": size + ] as [String : Any] + + return .requestParameters(parameters: parameters, encoding: URLEncoding.queryString) } } diff --git a/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmRepository.swift b/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmRepository.swift new file mode 100644 index 0000000..b208ee5 --- /dev/null +++ b/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmRepository.swift @@ -0,0 +1,24 @@ +// +// ContentMainTabAlarmRepository.swift +// SodaLive +// +// Created by klaus on 2/22/25. +// + +import Foundation +import CombineMoya +import Combine +import Moya + +final class ContentMainTabAlarmRepository { + + private let api = MoyaProvider() + + func getContentMainAlarm() -> AnyPublisher { + return api.requestPublisher(.getContentMainAlarm) + } + + func getContentMainAlarmAll(theme: String, page: Int = 1, size: Int = 10) -> AnyPublisher { + return api.requestPublisher(.getContentMainAlarmAll(theme: theme, page: page, size: size)) + } +} diff --git a/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmView.swift b/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmView.swift new file mode 100644 index 0000000..dc30c6a --- /dev/null +++ b/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmView.swift @@ -0,0 +1,70 @@ +// +// ContentMainTabAlarmView.swift +// SodaLive +// +// Created by klaus on 2/22/25. +// + +import SwiftUI + +struct ContentMainTabAlarmView: View { + + @StateObject var viewModel = ContentMainTabAlarmViewModel() + + 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.alarmThemeList.isEmpty { + ContentMainNewContentViewV2( + title: "새로운 알람", + onClickMore: {}, + themeList: viewModel.alarmThemeList, + contentList: viewModel.newAlarmContentList + ) { + viewModel.getContentMainAlarm(theme: $0) + } + .padding(.top, 30) + } + + if !viewModel.eventBannerList.isEmpty { + SectionEventBannerView(items: viewModel.eventBannerList) + .padding(.top, 30) + } + + if !viewModel.curationList.isEmpty { + ContentMainCurationViewV2(curationList: viewModel.curationList) + .padding(.top, 30) + } + } + .onAppear { + viewModel.fetchData() + } + } + } + .popup(isPresented: $viewModel.isShowPopup, type: .toast, position: .bottom, autohideIn: 2) { + HStack { + Spacer() + Text(viewModel.errorMessage) + .padding(.vertical, 13.3) + .frame(width: screenSize().width - 66.7, alignment: .center) + .font(.custom(Font.medium.rawValue, size: 12)) + .background(Color.button) + .foregroundColor(Color.white) + .multilineTextAlignment(.leading) + .cornerRadius(20) + .padding(.bottom, 66.7) + Spacer() + } + } + } +} + +#Preview { + ContentMainTabAlarmView() +} diff --git a/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmViewModel.swift b/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmViewModel.swift new file mode 100644 index 0000000..abb65df --- /dev/null +++ b/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmViewModel.swift @@ -0,0 +1,106 @@ +// +// ContentMainTabAlarmViewModel.swift +// SodaLive +// +// Created by klaus on 2/22/25. +// + +import Foundation +import Combine + +final class ContentMainTabAlarmViewModel: ObservableObject { + private let repository = ContentMainTabAlarmRepository() + private var subscription = Set() + + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published var isLoading = false + + @Published var bannerList: [GetAudioContentBannerResponse] = [] + @Published var alarmThemeList: [String] = [] + @Published var newAlarmContentList: [GetAudioContentMainItem] = [] + @Published var rankAlarmContentList: [GetAudioContentRankingItem] = [] + @Published var eventBannerList: [EventItem] = [] + @Published var curationList: [GetContentCurationResponse] = [] + + func fetchData() { + isLoading = true + repository.getContentMainAlarm() + .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.alarmThemeList = ["전체"] + data.alarmThemeList + self.newAlarmContentList = data.newAlarmContentList + self.rankAlarmContentList = data.rankAlarmContentList + 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 getContentMainAlarm(theme: String) { + isLoading = true + repository.getContentMainAlarmAll(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.self, from: responseData) + + if let data = decoded.data, decoded.success { + self.newAlarmContentList = 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 + } + + self.isLoading = false + } + .store(in: &subscription) + } +} diff --git a/SodaLive/Sources/Content/Main/V2/Alarm/GetContentMainTabAlarmResponse.swift b/SodaLive/Sources/Content/Main/V2/Alarm/GetContentMainTabAlarmResponse.swift new file mode 100644 index 0000000..5673d79 --- /dev/null +++ b/SodaLive/Sources/Content/Main/V2/Alarm/GetContentMainTabAlarmResponse.swift @@ -0,0 +1,17 @@ +// +// GetContentMainTabAlarmResponse.swift +// SodaLive +// +// Created by klaus on 2/22/25. +// + +import Foundation + +struct GetContentMainTabAlarmResponse: Decodable { + let contentBannerList: [GetAudioContentBannerResponse] + let alarmThemeList: [String] + let newAlarmContentList: [GetAudioContentMainItem] + let rankAlarmContentList: [GetAudioContentRankingItem] + let eventBannerList: GetEventResponse + let curationList: [GetContentCurationResponse] +} diff --git a/SodaLive/Sources/Content/Main/V2/Asmr/ContentMainTabAsmrView.swift b/SodaLive/Sources/Content/Main/V2/Asmr/ContentMainTabAsmrView.swift new file mode 100644 index 0000000..ecbca0d --- /dev/null +++ b/SodaLive/Sources/Content/Main/V2/Asmr/ContentMainTabAsmrView.swift @@ -0,0 +1,18 @@ +// +// ContentMainTabAsmrView.swift +// SodaLive +// +// Created by klaus on 2/22/25. +// + +import SwiftUI + +struct ContentMainTabAsmrView: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +#Preview { + ContentMainTabAsmrView() +} diff --git a/SodaLive/Sources/Content/Main/V2/Asmr/ContentMainTabAsmrViewModel.swift b/SodaLive/Sources/Content/Main/V2/Asmr/ContentMainTabAsmrViewModel.swift new file mode 100644 index 0000000..3088df5 --- /dev/null +++ b/SodaLive/Sources/Content/Main/V2/Asmr/ContentMainTabAsmrViewModel.swift @@ -0,0 +1,14 @@ +// +// ContentMainTabAsmrViewModel.swift +// SodaLive +// +// Created by klaus on 2/22/25. +// + +import Foundation + +final class ContentMainTabAsmrViewModel: ObservableObject { + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published var isLoading = false +} diff --git a/SodaLive/Sources/Content/Main/V2/ContentMainViewV2.swift b/SodaLive/Sources/Content/Main/V2/ContentMainViewV2.swift index 554c353..cab7824 100644 --- a/SodaLive/Sources/Content/Main/V2/ContentMainViewV2.swift +++ b/SodaLive/Sources/Content/Main/V2/ContentMainViewV2.swift @@ -118,13 +118,13 @@ struct ContentMainViewV2: View { case .CONTENT: ContentMainTabContentView() case .ALARM: - EmptyView() + ContentMainTabAlarmView() case .ASMR: - EmptyView() + ContentMainTabAsmrView() case .REPLAY: - EmptyView() + ContentMainTabReplayView() case .FREE: - EmptyView() + ContentMainTabFreeView() } } } diff --git a/SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeView.swift b/SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeView.swift new file mode 100644 index 0000000..ab2ac14 --- /dev/null +++ b/SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeView.swift @@ -0,0 +1,26 @@ +// +// ContentMainTabFreeView.swift +// SodaLive +// +// Created by klaus on 2/22/25. +// + +import SwiftUI + +struct ContentMainTabFreeView: View { + + @StateObject var viewModel = ContentMainTabFreeViewModel() + + var body: some View { + BaseView(isLoading: $viewModel.isLoading) { + ScrollView(.vertical, showsIndicators: false) { + VStack(spacing: 0) { + } + } + } + } +} + +#Preview { + ContentMainTabFreeView() +} diff --git a/SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeViewModel.swift b/SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeViewModel.swift new file mode 100644 index 0000000..8cb091f --- /dev/null +++ b/SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeViewModel.swift @@ -0,0 +1,14 @@ +// +// ContentMainTabFreeViewModel.swift +// SodaLive +// +// Created by klaus on 2/22/25. +// + +import Foundation + +final class ContentMainTabFreeViewModel: ObservableObject { + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published var isLoading = false +} diff --git a/SodaLive/Sources/Content/Main/V2/Replay/ContentMainTabReplayView.swift b/SodaLive/Sources/Content/Main/V2/Replay/ContentMainTabReplayView.swift new file mode 100644 index 0000000..c990b9b --- /dev/null +++ b/SodaLive/Sources/Content/Main/V2/Replay/ContentMainTabReplayView.swift @@ -0,0 +1,18 @@ +// +// ContentMainTabReplayView.swift +// SodaLive +// +// Created by klaus on 2/22/25. +// + +import SwiftUI + +struct ContentMainTabReplayView: View { + var body: some View { + Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/) + } +} + +#Preview { + ContentMainTabReplayView() +} diff --git a/SodaLive/Sources/Content/Main/V2/Replay/ContentMainTabReplayViewModel.swift b/SodaLive/Sources/Content/Main/V2/Replay/ContentMainTabReplayViewModel.swift new file mode 100644 index 0000000..8efd16d --- /dev/null +++ b/SodaLive/Sources/Content/Main/V2/Replay/ContentMainTabReplayViewModel.swift @@ -0,0 +1,14 @@ +// +// ContentMainTabReplayViewModel.swift +// SodaLive +// +// Created by klaus on 2/22/25. +// + +import Foundation + +final class ContentMainTabReplayViewModel: ObservableObject { + @Published var errorMessage = "" + @Published var isShowPopup = false + @Published var isLoading = false +}