diff --git a/SodaLive/Resources/Localizable.xcstrings b/SodaLive/Resources/Localizable.xcstrings index f0bde19..1c69091 100644 --- a/SodaLive/Resources/Localizable.xcstrings +++ b/SodaLive/Resources/Localizable.xcstrings @@ -1276,6 +1276,7 @@ } }, "※ 인기 순위는 매주 업데이트됩니다." : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -1324,6 +1325,7 @@ } }, "※ 최근 2주간 등록된 새로운 ASMR 입니다." : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -1340,6 +1342,7 @@ } }, "※ 최근 2주간 등록된 새로운 라이브 다시듣기 입니다." : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -1356,6 +1359,7 @@ } }, "※ 최근 2주간 등록된 새로운 알람 입니다." : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -4129,18 +4133,18 @@ } } }, - "모집완료" : { + "모든 기기에서 로그아웃" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Recruitment closed" + "value" : "Log out from all devices" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "募集終了" + "value" : "全端末からログアウト" } } } @@ -4161,24 +4165,24 @@ } } }, - "모든 기기에서 로그아웃" : { + "모서리 원을 드래그해서 크롭 영역 크기를 조정하세요" : { + + }, + "모집완료" : { "localizations" : { "en" : { "stringUnit" : { "state" : "translated", - "value" : "Log out from all devices" + "value" : "Recruitment closed" } }, "ja" : { "stringUnit" : { "state" : "translated", - "value" : "全端末からログアウト" + "value" : "募集終了" } } } - }, - "모서리 원을 드래그해서 크롭 영역 크기를 조정하세요" : { - }, "모집중" : { "localizations" : { @@ -7051,6 +7055,7 @@ } }, "인기 시리즈" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -7339,6 +7344,7 @@ } }, "자세히 >" : { + "extractionState" : "stale", "localizations" : { "en" : { "stringUnit" : { @@ -8656,22 +8662,6 @@ } } }, - "캐릭터 정보" : { - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Character info" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "キャラクター情報" - } - } - } - }, "캔" : { "localizations" : { "en" : { @@ -8688,6 +8678,22 @@ } } }, + "캐릭터 정보" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Character info" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "キャラクター情報" + } + } + } + }, "캔 충전" : { "localizations" : { "en" : { diff --git a/SodaLive/Sources/App/AppStep.swift b/SodaLive/Sources/App/AppStep.swift index a1a19fd..7092378 100644 --- a/SodaLive/Sources/App/AppStep.swift +++ b/SodaLive/Sources/App/AppStep.swift @@ -154,14 +154,6 @@ enum AppStep { case completedSeriesAll - case newAlarmContentAll - - case newAsmrContentAll - - case newReplayContentAll - - case introduceCreatorAll - case message case notificationList diff --git a/SodaLive/Sources/Content/Main/V2/Alarm/All/ContentMainAlarmAllView.swift b/SodaLive/Sources/Content/Main/V2/Alarm/All/ContentMainAlarmAllView.swift deleted file mode 100644 index c3dfca1..0000000 --- a/SodaLive/Sources/Content/Main/V2/Alarm/All/ContentMainAlarmAllView.swift +++ /dev/null @@ -1,98 +0,0 @@ -// -// ContentMainAlarmAllView.swift -// SodaLive -// -// Created by klaus on 2/22/25. -// - -import SwiftUI - -struct ContentMainAlarmAllView: View { - - @StateObject var viewModel = ContentMainAlarmAllViewModel() - @State private var isInitialized = false - - var body: some View { - Group { - BaseView(isLoading: $viewModel.isLoading) { - VStack(alignment: .leading, spacing: 13.3) { - DetailNavigationBar(title: "새로운 알람") - - Text("※ 최근 2주간 등록된 새로운 알람 입니다.") - .appFont(size: 14.7, weight: .medium) - .foregroundColor(.graybb) - .padding(.horizontal, 13.3) - .padding(.vertical, 8) - .frame(width: screenSize().width, alignment: .leading) - .background(Color.gray22) - - ContentMainNewContentThemeView( - themes: viewModel.themeList, - selectTheme: { - viewModel.selectedTheme = $0 - }, - selectedTheme: $viewModel.selectedTheme - ) - .padding(.horizontal, 20) - - HStack(spacing: 0) { - Text("전체") - .appFont(size: 13.3, weight: .medium) - .foregroundColor(Color(hex: "e2e2e2")) - - Text("\(viewModel.totalCount)") - .appFont(size: 13.3, weight: .medium) - .foregroundColor(Color(hex: "ff5c49")) - .padding(.leading, 8) - - Text("개") - .appFont(size: 13.3, weight: .medium) - .foregroundColor(Color(hex: "e2e2e2")) - .padding(.leading, 2) - } - .padding(.horizontal, 13.3) - - ScrollView(.vertical, showsIndicators: false) { - let horizontalPadding: CGFloat = 16 - let gridSpacing: CGFloat = 16 - let itemSize = (screenSize().width - (horizontalPadding * 2) - gridSpacing) / 2 - - LazyVGrid( - columns: Array( - repeating: GridItem( - .flexible(), - spacing: gridSpacing, - alignment: .topLeading - ), - count: 2 - ), - alignment: .leading, - spacing: gridSpacing - ) { - ForEach(0..() - - @Published var errorMessage = "" - @Published var isShowPopup = false - @Published var isLoading = false - - @Published var themeList = ["전체", "모닝콜", "슬립콜", "알람"] - @Published var newContentList = [GetAudioContentMainItem]() - - @Published var selectedTheme = "전체" { - didSet { - page = 1 - isLast = false - getContentMainAlarmAll() - } - } - @Published var totalCount = 0 - - var page = 1 - var isLast = false - private let pageSize = 20 - - func getContentMainAlarmAll() { - if (!isLast && !isLoading) { - isLoading = true - - repository.getContentMainAlarmAll( - theme: selectedTheme == "전체" ? "" : selectedTheme, - page: page, - size: pageSize - ) - .sink { result in - switch result { - case .finished: - DEBUG_LOG("finish") - case .failure(let error): - ERROR_LOG(error.localizedDescription) - } - } receiveValue: { [unowned self] response in - 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 { - if page == 1 { - newContentList.removeAll() - } - - self.totalCount = data.totalCount - - if !data.items.isEmpty { - page += 1 - self.newContentList.append(contentsOf: data.items) - } else { - isLast = true - } - } else { - if let message = decoded.message { - self.errorMessage = message - } else { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." - } - - self.isShowPopup = true - } - } catch { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." - self.isShowPopup = true - self.isLoading = false - } - } - .store(in: &subscription) - } else { - isLoading = false - } - } -} diff --git a/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmRepository.swift b/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmRepository.swift deleted file mode 100644 index 67aa001..0000000 --- a/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmRepository.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// 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( - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } - - func getContentMainAlarmAll(theme: String, page: Int = 1, size: Int = 10) -> AnyPublisher { - return api.requestPublisher( - .getContentMainAlarmAll( - theme: theme, - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL, - page: page, - size: size - ) - ) - } -} diff --git a/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmView.swift b/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmView.swift deleted file mode 100644 index 2909542..0000000 --- a/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmView.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// 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: { - AppState.shared - .setAppStep(step: .newAlarmContentAll) - }, - 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() - } - } - } - .sodaToast(isPresented: $viewModel.isShowPopup, message: viewModel.errorMessage, autohideIn: 2) - } -} - -#Preview { - ContentMainTabAlarmView() -} diff --git a/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmViewModel.swift b/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmViewModel.swift deleted file mode 100644 index abb65df..0000000 --- a/SodaLive/Sources/Content/Main/V2/Alarm/ContentMainTabAlarmViewModel.swift +++ /dev/null @@ -1,106 +0,0 @@ -// -// 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 deleted file mode 100644 index 5673d79..0000000 --- a/SodaLive/Sources/Content/Main/V2/Alarm/GetContentMainTabAlarmResponse.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// 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/All/ContentMainAsmrAllView.swift b/SodaLive/Sources/Content/Main/V2/Asmr/All/ContentMainAsmrAllView.swift deleted file mode 100644 index 167300b..0000000 --- a/SodaLive/Sources/Content/Main/V2/Asmr/All/ContentMainAsmrAllView.swift +++ /dev/null @@ -1,93 +0,0 @@ -// -// ContentMainAsmrAllView.swift -// SodaLive -// -// Created by klaus on 2/22/25. -// - -import SwiftUI - -struct ContentMainAsmrAllView: View { - - @StateObject var viewModel = ContentNewAllViewModel() - @State private var isInitialized = false - - var body: some View { - Group { - BaseView(isLoading: $viewModel.isLoading) { - VStack(alignment: .leading, spacing: 13.3) { - DetailNavigationBar(title: "새로운 ASMR") - - Text("※ 최근 2주간 등록된 새로운 ASMR 입니다.") - .appFont(size: 14.7, weight: .medium) - .foregroundColor(.graybb) - .padding(.horizontal, 13.3) - .padding(.vertical, 8) - .frame(width: screenSize().width, alignment: .leading) - .background(Color.gray22) - - HStack(spacing: 0) { - Text("전체") - .appFont(size: 13.3, weight: .medium) - .foregroundColor(Color(hex: "e2e2e2")) - - Text("\(viewModel.totalCount)") - .appFont(size: 13.3, weight: .medium) - .foregroundColor(Color(hex: "ff5c49")) - .padding(.leading, 8) - - Text("개") - .appFont(size: 13.3, weight: .medium) - .foregroundColor(Color(hex: "e2e2e2")) - .padding(.leading, 2) - } - .padding(.horizontal, 13.3) - - ScrollView(.vertical, showsIndicators: false) { - let horizontalPadding: CGFloat = 16 - let gridSpacing: CGFloat = 16 - let itemSize = (screenSize().width - (horizontalPadding * 2) - gridSpacing) / 2 - - LazyVGrid( - columns: Array( - repeating: GridItem( - .flexible(), - spacing: gridSpacing, - alignment: .topLeading - ), - count: 2 - ), - alignment: .leading, - spacing: gridSpacing - ) { - ForEach(0..() - - func getContentMainAsmr() -> AnyPublisher { - return api.requestPublisher( - .getContentMainAsmr( - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } - - func getPopularContentByCreator(creatorId: Int) -> AnyPublisher { - return api.requestPublisher( - .getPopularAsmrContentByCreator( - creatorId: creatorId, - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } -} diff --git a/SodaLive/Sources/Content/Main/V2/Asmr/ContentMainTabAsmrView.swift b/SodaLive/Sources/Content/Main/V2/Asmr/ContentMainTabAsmrView.swift deleted file mode 100644 index 68cbb3e..0000000 --- a/SodaLive/Sources/Content/Main/V2/Asmr/ContentMainTabAsmrView.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// ContentMainTabAsmrView.swift -// SodaLive -// -// Created by klaus on 2/22/25. -// - -import SwiftUI - -struct ContentMainTabAsmrView: View { - - @StateObject var viewModel = ContentMainTabAsmrViewModel() - - 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.newAsmrContentList.isEmpty { - ContentMainNewContentViewV2( - title: "새로운 ASMR", - onClickMore: { - AppState.shared - .setAppStep(step: .newAsmrContentAll) - }, - themeList: [], - contentList: viewModel.newAsmrContentList - ) { _ in } - .padding(.top, 30) - } - - if !viewModel.creatorList.isEmpty { - ContentByChannelView( - title: "채널별 추천 ASMR", - creatorList: viewModel.creatorList, - contentList: viewModel.salesCountRankContentList, - onClickCreator: { - viewModel.getPopularContentByCreator(creatorId: $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() - } - } - } - .sodaToast(isPresented: $viewModel.isShowPopup, message: viewModel.errorMessage, autohideIn: 2) - } -} - -#Preview { - ContentMainTabAsmrView() -} diff --git a/SodaLive/Sources/Content/Main/V2/Asmr/ContentMainTabAsmrViewModel.swift b/SodaLive/Sources/Content/Main/V2/Asmr/ContentMainTabAsmrViewModel.swift deleted file mode 100644 index d5ec319..0000000 --- a/SodaLive/Sources/Content/Main/V2/Asmr/ContentMainTabAsmrViewModel.swift +++ /dev/null @@ -1,106 +0,0 @@ -// -// ContentMainTabAsmrViewModel.swift -// SodaLive -// -// Created by klaus on 2/22/25. -// - -import Foundation -import Combine - -final class ContentMainTabAsmrViewModel: ObservableObject { - private let repository = ContentMainTabAsmrRepository() - private var subscription = Set() - - @Published var errorMessage = "" - @Published var isShowPopup = false - @Published var isLoading = false - - @Published var bannerList: [GetAudioContentBannerResponse] = [] - @Published var newAsmrContentList: [GetAudioContentMainItem] = [] - @Published var creatorList: [ContentCreatorResponse] = [] - @Published var salesCountRankContentList: [GetAudioContentRankingItem] = [] - @Published var eventBannerList: [EventItem] = [] - @Published var curationList: [GetContentCurationResponse] = [] - - func fetchData() { - isLoading = true - repository.getContentMainAsmr() - .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.newAsmrContentList = data.newAsmrContentList - self.creatorList = data.creatorList - self.salesCountRankContentList = data.salesCountRankContentList - 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 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.salesCountRankContentList = 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/Asmr/GetContentMainTabAsmrResponse.swift b/SodaLive/Sources/Content/Main/V2/Asmr/GetContentMainTabAsmrResponse.swift deleted file mode 100644 index 5b54bc7..0000000 --- a/SodaLive/Sources/Content/Main/V2/Asmr/GetContentMainTabAsmrResponse.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// GetContentMainTabAsmrResponse.swift -// SodaLive -// -// Created by klaus on 2/22/25. -// - -struct GetContentMainTabAsmrResponse: Decodable { - let contentBannerList: [GetAudioContentBannerResponse] - let newAsmrContentList: [GetAudioContentMainItem] - let creatorList: [ContentCreatorResponse] - let salesCountRankContentList: [GetAudioContentRankingItem] - let eventBannerList: GetEventResponse - let curationList: [GetContentCurationResponse] -} diff --git a/SodaLive/Sources/Content/Main/V2/ContentMainViewV2.swift b/SodaLive/Sources/Content/Main/V2/ContentMainViewV2.swift index bdaf987..cfc3e9b 100644 --- a/SodaLive/Sources/Content/Main/V2/ContentMainViewV2.swift +++ b/SodaLive/Sources/Content/Main/V2/ContentMainViewV2.swift @@ -12,10 +12,6 @@ enum ContentMainTab { case HOME case SERIES case CONTENT - case ALARM - case ASMR - case REPLAY - case FREE } struct TabItem { @@ -35,10 +31,6 @@ struct ContentMainViewV2: View { TabItem(title: "홈", tab: .HOME), TabItem(title: "시리즈", tab: .SERIES), TabItem(title: "단편", tab: .CONTENT), - TabItem(title: "모닝콜", tab: .ALARM), - TabItem(title: "ASMR", tab: .ASMR), - TabItem(title: "다시듣기", tab: .REPLAY), - TabItem(title: "무료", tab: .FREE) ] init(selectedTab: ContentMainTab = .SERIES) { @@ -115,14 +107,6 @@ struct ContentMainViewV2: View { ContentMainTabSeriesView() case .CONTENT: ContentMainTabContentView() - case .ALARM: - ContentMainTabAlarmView() - case .ASMR: - ContentMainTabAsmrView() - case .REPLAY: - ContentMainTabReplayView() - case .FREE: - ContentMainTabFreeView() } } diff --git a/SodaLive/Sources/Content/Main/V2/Free/All/ContentMainIntroduceCreatorAllView.swift b/SodaLive/Sources/Content/Main/V2/Free/All/ContentMainIntroduceCreatorAllView.swift deleted file mode 100644 index e6efaea..0000000 --- a/SodaLive/Sources/Content/Main/V2/Free/All/ContentMainIntroduceCreatorAllView.swift +++ /dev/null @@ -1,66 +0,0 @@ -// -// ContentMainIntroduceCreatorAllView.swift -// SodaLive -// -// Created by klaus on 2/22/25. -// - -import SwiftUI - -struct ContentMainIntroduceCreatorAllView: View { - - @StateObject var viewModel = ContentMainIntroduceCreatorAllViewModel() - @State private var isInitialized = false - - var body: some View { - Group { - BaseView(isLoading: $viewModel.isLoading) { - VStack(spacing: 13.3) { - DetailNavigationBar(title: "크리에이터 소개") - - ScrollView(.vertical, showsIndicators: false) { - let horizontalPadding: CGFloat = 16 - let gridSpacing: CGFloat = 16 - let itemSize = (screenSize().width - (horizontalPadding * 2) - gridSpacing) / 2 - - LazyVGrid( - columns: Array( - repeating: GridItem( - .flexible(), - spacing: gridSpacing, - alignment: .topLeading - ), - count: 2 - ), - alignment: .leading, - spacing: gridSpacing - ) { - ForEach(0..() - - @Published var errorMessage = "" - @Published var isShowPopup = false - @Published var isLoading = false - - @Published var introduceCreatorList: [GetAudioContentMainItem] = [] - - var page = 1 - var isLast = false - private let size = 20 - - func getIntroduceCreatorList() { - if (!isLast && !isLoading) { - isLoading = true - repository.getIntroduceCreatorList(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<[GetAudioContentMainItem]>.self, from: responseData) - self.isLoading = false - - if let data = decoded.data, decoded.success { - if page == 1 { - introduceCreatorList.removeAll() - } - - if !data.isEmpty { - page += 1 - self.introduceCreatorList.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/V2/Free/ContentMainTabFreeRepository.swift b/SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeRepository.swift deleted file mode 100644 index 54015c5..0000000 --- a/SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeRepository.swift +++ /dev/null @@ -1,58 +0,0 @@ -// -// ContentMainTabFreeRepository.swift -// SodaLive -// -// Created by klaus on 2/22/25. -// - -import Foundation -import CombineMoya -import Combine -import Moya - -final class ContentMainTabFreeRepository { - - private let api = MoyaProvider() - - func getContentMainFree() -> AnyPublisher { - return api.requestPublisher( - .getContentMainFree( - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } - - func getIntroduceCreatorList(page: Int, size: Int) -> AnyPublisher { - return api.requestPublisher( - .getIntroduceCreatorList( - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL, - page: page, - size: size - ) - ) - } - - func getNewContentOfTheme(theme: String, page: Int = 1, size: Int = 20) -> AnyPublisher { - return api.requestPublisher( - .getNewFreeContentOfTheme( - theme: theme, - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL, - page: page, - size: size - ) - ) - } - - func getPopularContentByCreator(creatorId: Int) -> AnyPublisher { - return api.requestPublisher( - .getPopularFreeContentByCreator( - creatorId: creatorId, - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } -} diff --git a/SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeView.swift b/SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeView.swift deleted file mode 100644 index c718dbe..0000000 --- a/SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeView.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// 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) { - if !viewModel.bannerList.isEmpty { - ContentMainBannerViewV2(bannerList: viewModel.bannerList) - .padding(.horizontal, 13.3) - } - - if let introduceCreator = viewModel.introduceCreator { - ContentMainNewContentViewV2( - title: introduceCreator.title, - onClickMore: { - AppState.shared - .setAppStep(step: .introduceCreatorAll) - }, - themeList: [], - contentList: introduceCreator.items - ) { _ in } - .padding(.top, 30) - } - - if !viewModel.recommendSeriesList.isEmpty { - ContentMainNewOrRecommendSeriesView( - title: "추천 무료 시리즈", - recommendSeriesList: viewModel.recommendSeriesList - ) - .padding(.top, 30) - } - - if !viewModel.themeList.isEmpty { - ContentMainNewContentViewV2( - title: "새로운 무료 콘텐츠", - onClickMore: { - AppState.shared - .setAppStep(step: .newContentAll(isFree: true)) - }, - themeList: viewModel.themeList, - contentList: viewModel.newFreeContentList - ) { - viewModel.getNewContentOfTheme(theme: $0) - } - .padding(.top, 30) - } - - if !viewModel.creatorList.isEmpty { - ContentByChannelView( - title: "채널별 추천 무료 콘텐츠", - creatorList: viewModel.creatorList, - contentList: viewModel.playCountRankContentList, - onClickCreator: { - viewModel.getPopularContentByCreator(creatorId: $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 { - ContentMainTabFreeView() -} diff --git a/SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeViewModel.swift b/SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeViewModel.swift deleted file mode 100644 index 9b940db..0000000 --- a/SodaLive/Sources/Content/Main/V2/Free/ContentMainTabFreeViewModel.swift +++ /dev/null @@ -1,151 +0,0 @@ -// -// ContentMainTabFreeViewModel.swift -// SodaLive -// -// Created by klaus on 2/22/25. -// - -import Foundation -import Combine - -final class ContentMainTabFreeViewModel: ObservableObject { - private let repository = ContentMainTabFreeRepository() - private var subscription = Set() - - @Published var errorMessage = "" - @Published var isShowPopup = false - @Published var isLoading = false - - @Published var bannerList: [GetAudioContentBannerResponse] = [] - @Published var introduceCreator: GetContentCurationResponse? = nil - @Published var recommendSeriesList: [GetRecommendSeriesListResponse] = [] - @Published var themeList: [String] = [] - @Published var newFreeContentList: [GetAudioContentMainItem] = [] - @Published var creatorList: [ContentCreatorResponse] = [] - @Published var playCountRankContentList: [GetAudioContentRankingItem] = [] - @Published var curationList: [GetContentCurationResponse] = [] - - func fetchData() { - isLoading = true - repository.getContentMainFree() - .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.introduceCreator = data.introduceCreator - self.recommendSeriesList = data.recommendSeriesList - self.newFreeContentList = data.newFreeContentList - self.creatorList = data.creatorList - self.playCountRankContentList = data.playCountRankContentList - self.curationList = data.curationList - - self.themeList.removeAll() - 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 { - self.errorMessage = "다시 시도해 주세요.\n계속 같은 문제가 발생할 경우 고객센터로 문의 주시기 바랍니다." - self.isShowPopup = true - } - - self.isLoading = false - } - .store(in: &subscription) - } - - func getNewContentOfTheme(theme: String) { - isLoading = true - repository.getNewContentOfTheme(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.newFreeContentList = 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.playCountRankContentList = 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/Free/GetContentMainTabFreeResponse.swift b/SodaLive/Sources/Content/Main/V2/Free/GetContentMainTabFreeResponse.swift deleted file mode 100644 index 052e440..0000000 --- a/SodaLive/Sources/Content/Main/V2/Free/GetContentMainTabFreeResponse.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// GetContentMainTabFreeResponse.swift -// SodaLive -// -// Created by klaus on 2/22/25. -// - -struct GetContentMainTabFreeResponse: Decodable { - let contentBannerList: [GetAudioContentBannerResponse] - let introduceCreator: GetContentCurationResponse? - let recommendSeriesList: [GetRecommendSeriesListResponse] - let themeList: [String] - let newFreeContentList: [GetAudioContentMainItem] - let creatorList: [ContentCreatorResponse] - let playCountRankContentList: [GetAudioContentRankingItem] - let curationList: [GetContentCurationResponse] -} diff --git a/SodaLive/Sources/Content/Main/V2/Home/Category/ContentMainTabCategoryView.swift b/SodaLive/Sources/Content/Main/V2/Home/Category/ContentMainTabCategoryView.swift deleted file mode 100644 index e71c36e..0000000 --- a/SodaLive/Sources/Content/Main/V2/Home/Category/ContentMainTabCategoryView.swift +++ /dev/null @@ -1,38 +0,0 @@ -// -// ContentMainTabCategoryView.swift -// SodaLive -// -// Created by klaus on 2/20/25. -// - -import SwiftUI - -struct ContentMainTabCategoryView: View { - - let imageName: String - let title: String - let onClick: () -> Void - - var body: some View { - VStack(spacing: 5.3) { - Image(imageName) - .resizable() - .frame(width: 43, height: 43) - - Text(title) - .appFont(size: 12, weight: .medium) - .foregroundColor(.gray77) - } - .onTapGesture { - onClick() - } - } -} - -#Preview { - ContentMainTabCategoryView( - imageName: "ic_category_series", - title: "시리즈", - onClick: {} - ) -} diff --git a/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeNoticeView.swift b/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeNoticeView.swift deleted file mode 100644 index 437845a..0000000 --- a/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeNoticeView.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// ContentMainTabHomeNoticeView.swift -// SodaLive -// -// Created by klaus on 2/20/25. -// - -import SwiftUI - -struct ContentMainTabHomeNoticeView: View { - - let notice: NoticeItem - let onClick: (NoticeItem) -> Void - - var body: some View { - HStack(spacing: 0) { - Text(notice.title) - .appFont(size: 13.3, weight: .medium) - .foregroundColor(.white) - - Spacer() - - Text("자세히 >") - .appFont(size: 13.3, weight: .medium) - .foregroundColor(.white) - .onTapGesture { - onClick(notice) - } - } - .padding(.horizontal, 13.3) - .padding(.vertical, 10) - .background(Color.gray22) - .cornerRadius(5.3) - } -} - -#Preview { - ContentMainTabHomeNoticeView( - notice: NoticeItem( - title: "[업데이트] 1.28.0 버전 업데이트", - content: "test", - date: "2025-02-07" - ) - ) { - AppState.shared.setAppStep(step: .noticeDetail(notice: $0)) - } -} diff --git a/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeRepository.swift b/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeRepository.swift deleted file mode 100644 index 112c564..0000000 --- a/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeRepository.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// ContentMainTabHomeRepository.swift -// SodaLive -// -// Created by klaus on 2/20/25. -// - -import Foundation -import CombineMoya -import Combine -import Moya - -class ContentMainTabHomeRepository { - private let api = MoyaProvider() - - func getContentMainHome() -> AnyPublisher { - return api.requestPublisher( - .getContentMainHome( - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } - - func getPopularContentByCreator(creatorId: Int) -> AnyPublisher { - return api.requestPublisher( - .getPopularContentByCreator( - creatorId: creatorId, - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } - - func getContentRanking(sortType: String = "매출") -> AnyPublisher { - return api.requestPublisher( - .getContentMainHomeContentRanking( - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL, - sortType: sortType - ) - ) - } -} diff --git a/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeView.swift b/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeView.swift deleted file mode 100644 index dc3b529..0000000 --- a/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeView.swift +++ /dev/null @@ -1,301 +0,0 @@ -// -// ContentMainTabHomeView.swift -// SodaLive -// -// Created by klaus on 2/20/25. -// - -import SwiftUI - -struct ContentMainTabHomeView: View { - - @StateObject var viewModel = ContentMainTabHomeViewModel() - @AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token) - @AppStorage("role") private var role: String = UserDefaults.string(forKey: UserDefaultsKey.role) - - var body: some View { - BaseView(isLoading: $viewModel.isLoading) { - ZStack(alignment: .bottomTrailing) { - ScrollView(.vertical, showsIndicators: false) { - VStack(alignment: .leading, spacing: 0) { - HStack(spacing: 0) { - Text("보이스온") - .appFont(size: 21.3, weight: .bold) - .foregroundColor(Color.white) - .padding(.leading, 8) - - Spacer() - - if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - Image("ic_can") - .onTapGesture { - AppState - .shared - .setAppStep(step: .canCharge(refresh: {})) - } - } - } - .padding(.horizontal, 13.3) - - if let notice = viewModel.noticeItem { - ContentMainTabHomeNoticeView(notice: notice) { - AppState.shared - .setAppStep(step: .noticeDetail(notice: $0)) - } - .padding(.top, 15) - .padding(.horizontal, 13.3) - } - - if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && - viewModel.bannerList.count > 0 { - ContentMainBannerViewV2(bannerList: viewModel.bannerList) - .padding(.top, 30) - .padding(.horizontal, 13.3) - } - - if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - HStack(spacing: 0) { - Image("ic_title_search_black") - - Text("검색어를 2글자 이상 입력하세요") - .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(.top, 30) - .padding(.horizontal, 13.3) - .onTapGesture { - UserDefaults.set("", forKey: .searchChannel) - AppState.shared.setAppStep(step: .search) - } - - VStack(spacing: 13.3) { - HStack(spacing: 0) { - ContentMainTabCategoryView( - imageName: "ic_category_series", - title: "시리즈", - onClick: { - AppState.shared - .setAppStep( - step: .contentMain( - startTab: .SERIES - ) - ) - } - ) - .frame(maxWidth: .infinity) - - ContentMainTabCategoryView( - imageName: "ic_category_content", - title: "단편", - onClick: { - AppState.shared - .setAppStep( - step: .contentMain( - startTab: .CONTENT - ) - ) - } - ) - .frame(maxWidth: .infinity) - - ContentMainTabCategoryView( - imageName: "ic_category_alarm", - title: "모닝콜", - onClick: { - AppState.shared - .setAppStep( - step: .contentMain( - startTab: .ALARM - ) - ) - } - ) - .frame(maxWidth: .infinity) - - ContentMainTabCategoryView( - imageName: "ic_category_asmr", - title: "ASMR", - onClick: { - AppState.shared - .setAppStep( - step: .contentMain( - startTab: .ASMR - ) - ) - } - ) - .frame(maxWidth: .infinity) - } - - HStack(spacing: 0) { - ContentMainTabCategoryView( - imageName: "ic_category_replay", - title: "다시듣기", - onClick: { - AppState.shared - .setAppStep( - step: .contentMain( - startTab: .REPLAY - ) - ) - } - ) - .frame(maxWidth: .infinity) - - ContentMainTabCategoryView( - imageName: "ic_category_free", - title: "무료", - onClick: { - AppState.shared - .setAppStep( - step: .contentMain( - startTab: .FREE - ) - ) - } - ) - .frame(maxWidth: .infinity) - - ContentMainTabCategoryView( - imageName: "ic_category_audio_book", - title: "오디오북", - onClick: { - viewModel.errorMessage = "준비중입니다." - viewModel.isShowPopup = true - } - ) - .frame(maxWidth: .infinity) - - ContentMainTabCategoryView( - imageName: "ic_category_audio_toon", - title: "오디오툰", - onClick: { - viewModel.errorMessage = "준비중입니다." - viewModel.isShowPopup = true - } - ) - .frame(maxWidth: .infinity) - } - } - .padding(.vertical, 13.3) - .background(Color.gray22) - .cornerRadius(5.3) - .padding(.top, 30) - .padding(.horizontal, 13.3) - } - - if let response = viewModel.rankCreatorResponse { - ContentMainTabHomeRankCreatorView(response: response) - .padding(.top, 30) - .padding(.horizontal, 13.3) - } - - if !viewModel.rankSeriesList.isEmpty { - ContentMainTabHomeRankSeriesView(seriesList: viewModel.rankSeriesList) - .padding(.top, 30) - .padding(.horizontal, 13.3) - } - - if !viewModel.rankSortTypeList.isEmpty { - ContentMainTabRankContentView( - title: "인기 단편", - isMore: true, - onClickMore: { - if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - AppState.shared.setAppStep(step: .contentRankingAll) - } else { - AppState.shared.setAppStep(step: .login) - } - }, - sortList: !viewModel.rankSortTypeList.isEmpty ? - viewModel.rankSortTypeList : - [], - onClickSort: { viewModel.getContentRanking(sort: $0) }, - contentList: viewModel.rankContentList - ) - .padding(.top, 30) - } - - if !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && - viewModel.eventBannerList.count > 0 { - SectionEventBannerView(items: viewModel.eventBannerList) - .padding(.top, 30) - } - - if !viewModel.contentRankCreatorList.isEmpty { - ContentByChannelView( - title: "채널별 인기 콘텐츠", - creatorList: viewModel.contentRankCreatorList, - contentList: viewModel.salesCountRankContentList, - onClickCreator: { - viewModel.getPopularContentByCreator(creatorId: $0) - } - ) - .padding(.top, 30) - } - - 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, 30) - } - .onAppear { - viewModel.fetchData() - } - } - - if 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) - } - } - } - .sodaToast(isPresented: $viewModel.isShowPopup, message: viewModel.errorMessage, autohideIn: 2) - } - } -} - -#Preview { - ContentMainTabHomeView() -} diff --git a/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeViewModel.swift b/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeViewModel.swift deleted file mode 100644 index fd222e4..0000000 --- a/SodaLive/Sources/Content/Main/V2/Home/ContentMainTabHomeViewModel.swift +++ /dev/null @@ -1,152 +0,0 @@ -// -// ContentMainTabHomeViewModel.swift -// SodaLive -// -// Created by klaus on 2/20/25. -// - -import Foundation -import Combine - -final class ContentMainTabHomeViewModel: ObservableObject { - - private let repository = ContentMainTabHomeRepository() - private var subscription = Set() - - @Published var errorMessage = "" - @Published var isShowPopup = false - @Published var isLoading = false - - @Published var noticeItem: NoticeItem? = nil - @Published var bannerList = [GetAudioContentBannerResponse]() - @Published var rankCreatorResponse: GetExplorerSectionResponse? = nil - @Published var rankSeriesList = [SeriesListItem]() - @Published var rankSortTypeList: [String] = [] - @Published var rankContentList: [GetAudioContentRankingItem] = [] - @Published var eventBannerList: [EventItem] = [] - @Published var contentRankCreatorList: [ContentCreatorResponse] = [] - @Published var salesCountRankContentList: [GetAudioContentRankingItem] = [] - - func fetchData() { - isLoading = true - - repository.getContentMainHome() - .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.noticeItem = data.latestNotice - self.bannerList = data.bannerList - self.rankCreatorResponse = data.rankCreatorList - self.rankSeriesList = data.rankSeriesList - self.rankSortTypeList = data.rankSortTypeList - self.rankContentList = data.rankContentList - self.eventBannerList = data.eventBannerList.eventList - self.contentRankCreatorList = data.contentRankCreatorList - self.salesCountRankContentList = data.salesCountRankContentList - } 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 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.salesCountRankContentList = 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/Home/GetContentMainTabHomeResponse.swift b/SodaLive/Sources/Content/Main/V2/Home/GetContentMainTabHomeResponse.swift deleted file mode 100644 index e79b04f..0000000 --- a/SodaLive/Sources/Content/Main/V2/Home/GetContentMainTabHomeResponse.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// GetContentMainTabHomeResponse.swift -// SodaLive -// -// Created by klaus on 2/20/25. -// - -struct GetContentMainTabHomeResponse: Decodable { - let latestNotice: NoticeItem? - let bannerList: [GetAudioContentBannerResponse] - let rankCreatorList: GetExplorerSectionResponse - let rankSeriesList: [SeriesListItem] - let rankSortTypeList: [String] - let rankContentList: [GetAudioContentRankingItem] - let eventBannerList: GetEventResponse - let contentRankCreatorList: [ContentCreatorResponse] - let salesCountRankContentList: [GetAudioContentRankingItem] -} diff --git a/SodaLive/Sources/Content/Main/V2/Home/Rank/ContentMainTabHomeRankCreatorView.swift b/SodaLive/Sources/Content/Main/V2/Home/Rank/ContentMainTabHomeRankCreatorView.swift deleted file mode 100644 index 14c6234..0000000 --- a/SodaLive/Sources/Content/Main/V2/Home/Rank/ContentMainTabHomeRankCreatorView.swift +++ /dev/null @@ -1,180 +0,0 @@ -// -// ContentMainTabHomeRankCreatorView.swift -// SodaLive -// -// Created by klaus on 2/20/25. -// - -import SwiftUI -import Kingfisher - -struct ContentMainTabHomeRankCreatorView: View { - - @AppStorage("token") private var token: String = UserDefaults.string(forKey: UserDefaultsKey.token) - - let response: GetExplorerSectionResponse - - 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 { - VStack(alignment: .leading, spacing: 0) { - if let desc = response.desc, !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty { - 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) - } - - 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) - } - } - .padding(.top, token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? 0 : 30) - } else { - Text(response.title) - .appFont(size: 18.3, weight: .bold) - .foregroundColor(Color.grayee) - .padding(.top, 30) - } - - ScrollView(.horizontal, showsIndicators: false) { - HStack(spacing: 13.3) { - ForEach(0..() - - func getContentMainReplay() -> AnyPublisher { - return api.requestPublisher( - .getContentMainReplay( - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } - - func getPopularContentByCreator(creatorId: Int) -> AnyPublisher { - return api.requestPublisher( - .getPopularReplayContentByCreator( - creatorId: creatorId, - isAdultContentVisible: UserDefaults.isAdultContentVisible(), - contentType: ContentType(rawValue: UserDefaults.string(forKey: .contentPreference)) ?? ContentType.ALL - ) - ) - } -} diff --git a/SodaLive/Sources/Content/Main/V2/Replay/ContentMainTabReplayView.swift b/SodaLive/Sources/Content/Main/V2/Replay/ContentMainTabReplayView.swift deleted file mode 100644 index 78801a4..0000000 --- a/SodaLive/Sources/Content/Main/V2/Replay/ContentMainTabReplayView.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// ContentMainTabReplayView.swift -// SodaLive -// -// Created by klaus on 2/22/25. -// - -import SwiftUI - -struct ContentMainTabReplayView: View { - - @StateObject var viewModel = ContentMainTabReplayViewModel() - - 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.newReplayContentList.isEmpty { - ContentMainNewContentViewV2( - title: "새로운 라이브 다시듣기", - onClickMore: { - AppState.shared - .setAppStep(step: .newReplayContentAll) - }, - themeList: [], - contentList: viewModel.newReplayContentList - ) { _ in } - .padding(.top, 30) - } - - if !viewModel.creatorList.isEmpty { - ContentByChannelView( - title: "채널별 라이브 다시듣기", - creatorList: viewModel.creatorList, - contentList: viewModel.salesCountRankContentList, - onClickCreator: { - viewModel.getPopularContentByCreator(creatorId: $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() - } - } - } - .sodaToast(isPresented: $viewModel.isShowPopup, message: viewModel.errorMessage, autohideIn: 2) - } -} - -#Preview { - ContentMainTabReplayView() -} diff --git a/SodaLive/Sources/Content/Main/V2/Replay/ContentMainTabReplayViewModel.swift b/SodaLive/Sources/Content/Main/V2/Replay/ContentMainTabReplayViewModel.swift deleted file mode 100644 index df78626..0000000 --- a/SodaLive/Sources/Content/Main/V2/Replay/ContentMainTabReplayViewModel.swift +++ /dev/null @@ -1,106 +0,0 @@ -// -// ContentMainTabReplayViewModel.swift -// SodaLive -// -// Created by klaus on 2/22/25. -// - -import Foundation -import Combine - -final class ContentMainTabReplayViewModel: ObservableObject { - private let repository = ContentMainTabReplayRepository() - private var subscription = Set() - - @Published var errorMessage = "" - @Published var isShowPopup = false - @Published var isLoading = false - - @Published var bannerList: [GetAudioContentBannerResponse] = [] - @Published var newReplayContentList: [GetAudioContentMainItem] = [] - @Published var creatorList: [ContentCreatorResponse] = [] - @Published var salesCountRankContentList: [GetAudioContentRankingItem] = [] - @Published var eventBannerList: [EventItem] = [] - @Published var curationList: [GetContentCurationResponse] = [] - - func fetchData() { - isLoading = true - repository.getContentMainReplay() - .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.newReplayContentList = data.newLiveReplayContentList - self.creatorList = data.creatorList - self.salesCountRankContentList = data.salesCountRankContentList - 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 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.salesCountRankContentList = 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/Replay/GetContentMainTabReplayResponse.swift b/SodaLive/Sources/Content/Main/V2/Replay/GetContentMainTabReplayResponse.swift deleted file mode 100644 index 3e61883..0000000 --- a/SodaLive/Sources/Content/Main/V2/Replay/GetContentMainTabReplayResponse.swift +++ /dev/null @@ -1,15 +0,0 @@ -// -// GetContentMainTabReplayResponse.swift -// SodaLive -// -// Created by klaus on 2/22/25. -// - -struct GetContentMainTabReplayResponse: Decodable { - let contentBannerList: [GetAudioContentBannerResponse] - let newLiveReplayContentList: [GetAudioContentMainItem] - let creatorList: [ContentCreatorResponse] - let salesCountRankContentList: [GetAudioContentRankingItem] - let eventBannerList: GetEventResponse - let curationList: [GetContentCurationResponse] -} diff --git a/SodaLive/Sources/ContentView.swift b/SodaLive/Sources/ContentView.swift index 1b1b15e..7242300 100644 --- a/SodaLive/Sources/ContentView.swift +++ b/SodaLive/Sources/ContentView.swift @@ -296,18 +296,6 @@ struct AppStepLayerView: View { case .completedSeriesAll: CompletedSeriesView() - case .newAlarmContentAll: - ContentMainAlarmAllView() - - case .newAsmrContentAll: - ContentMainAsmrAllView() - - case .newReplayContentAll: - ContentMainReplayAllView() - - case .introduceCreatorAll: - ContentMainIntroduceCreatorAllView() - case .message: MessageView()