diff --git a/Targets/D3N/Sources/Domain/Auth/AuthClient.swift b/Targets/D3N/Sources/Domain/Auth/AuthClient.swift index e591b61..de6c83e 100644 --- a/Targets/D3N/Sources/Domain/Auth/AuthClient.swift +++ b/Targets/D3N/Sources/Domain/Auth/AuthClient.swift @@ -44,7 +44,7 @@ extension AuthClient: DependencyKey { static let liveValue = AuthClient( appleLogin: { code, idToken in let target: TargetType = AuthService.appleLogin(code: code, idToken: idToken) - let response: Result = await D3NAPIkProvider.reqeust(target: target) + let response: Result = await D3NAPIProvider.reqeust(target: target) return response.map { dto in let entity = dto.toEntity() @@ -55,7 +55,7 @@ extension AuthClient: DependencyKey { }, appleUnlink: { let target: TargetType = AuthService.appleUnlink - let response: Result = await D3NAPIkProvider.justRequest(target: target) + let response: Result = await D3NAPIProvider.justRequest(target: target) return response.map { dto in LocalStorageManager.deleteAll() @@ -64,7 +64,7 @@ extension AuthClient: DependencyKey { }, refresh: { let target: TargetType = AuthService.refresh - let response: Result = await D3NAPIkProvider.reqeust(target: target) + let response: Result = await D3NAPIProvider.reqeust(target: target) return response.map { dto in LocalStorageManager.save(.accessToken, value: dto.accessToken) diff --git a/Targets/D3N/Sources/Domain/D3NAPIProvider.swift b/Targets/D3N/Sources/Domain/D3NAPIProvider.swift index c5cd75c..cdd9825 100644 --- a/Targets/D3N/Sources/Domain/D3NAPIProvider.swift +++ b/Targets/D3N/Sources/Domain/D3NAPIProvider.swift @@ -16,7 +16,7 @@ public enum D3NAPIError: Error { case none } -final class D3NAPIkProvider { +final class D3NAPIProvider { private static let provider = MoyaProvider( session: Session(interceptor: AuthInterceptor()), plugins: [D3NNetworkLoggerPlugin()] diff --git a/Targets/D3N/Sources/Domain/News/DTO/Response/FetchNewsListResponseDTO.swift b/Targets/D3N/Sources/Domain/News/DTO/Response/FetchNewsListResponseDTO.swift index 8565245..08b499c 100644 --- a/Targets/D3N/Sources/Domain/News/DTO/Response/FetchNewsListResponseDTO.swift +++ b/Targets/D3N/Sources/Domain/News/DTO/Response/FetchNewsListResponseDTO.swift @@ -21,6 +21,7 @@ struct FetchNewsListResponseElement: Codable { let mediaCompanyLogo: String let mediaCompanyName: String let secondTime: Int + let level: Int } extension FetchNewsListResponseElement { @@ -35,7 +36,8 @@ extension FetchNewsListResponseElement { mediaCompanyId: self.mediaCompanyId, mediaCompanyLogo: self.mediaCompanyLogo, mediaCompanyName: self.mediaCompanyName, - secondTime: self.secondTime + secondTime: self.secondTime, + level: self.level ) } } diff --git a/Targets/D3N/Sources/Domain/News/DTO/Response/FetchTodayNewsListResponseDTO.swift b/Targets/D3N/Sources/Domain/News/DTO/Response/FetchTodayNewsListResponseDTO.swift new file mode 100644 index 0000000..9a83509 --- /dev/null +++ b/Targets/D3N/Sources/Domain/News/DTO/Response/FetchTodayNewsListResponseDTO.swift @@ -0,0 +1,27 @@ +// +// FetchTodayNewsListResponseDTO.swift +// D3N +// +// Created by 송영모 on 11/30/23. +// Copyright © 2023 sju. All rights reserved. +// + +import Foundation + +typealias FetchTodayNewsListResponseDTO = [FetchTodayNewsListResponseElement] + +struct FetchTodayNewsListResponseElement: Codable { + let type, title, subtitle: String + let newsList: [FetchNewsListResponseElement] +} + +extension FetchTodayNewsListResponseElement { + func toEntity() -> TodayNewsEntity { + return .init( + type: self.type, + title: self.title, + subtitle: self.subtitle, + newses: self.newsList.map { $0.toEntity() } + ) + } +} diff --git a/Targets/D3N/Sources/Domain/News/Entity/NewsEntity.swift b/Targets/D3N/Sources/Domain/News/Entity/NewsEntity.swift index 150d8bd..8949256 100644 --- a/Targets/D3N/Sources/Domain/News/Entity/NewsEntity.swift +++ b/Targets/D3N/Sources/Domain/News/Entity/NewsEntity.swift @@ -8,7 +8,7 @@ import Foundation -public struct NewsEntity: Equatable { +public struct NewsEntity: Equatable, Hashable { let id: Int let field: NewsField let type: NewsType @@ -19,6 +19,7 @@ public struct NewsEntity: Equatable { let mediaCompanyLogo: String let mediaCompanyName: String var secondTime: Int + let level: Int init( id: Int, @@ -30,7 +31,8 @@ public struct NewsEntity: Equatable { mediaCompanyId: String, mediaCompanyLogo: String, mediaCompanyName: String, - secondTime: Int + secondTime: Int, + level: Int ) { self.id = id self.field = field @@ -42,6 +44,7 @@ public struct NewsEntity: Equatable { self.mediaCompanyLogo = mediaCompanyLogo self.mediaCompanyName = mediaCompanyName self.secondTime = secondTime + self.level = level } var timeString: String { @@ -65,7 +68,8 @@ public extension NewsEntity { mediaCompanyId: "056", mediaCompanyLogo: "https://mimgnews.pstatic.net/image/upload/office_logo/056/2021/07/15/logo_056_57_20210715101841.png", mediaCompanyName: "KBS", - secondTime: 0 + secondTime: 0, + level: 0 ) static let mocks = [ @@ -79,7 +83,8 @@ public extension NewsEntity { mediaCompanyId: "018", mediaCompanyLogo: "https://mimgnews.pstatic.net/image/upload/office_logo/018/2018/08/08/logo_018_57_20180808174308.png", mediaCompanyName: "이데일리", - secondTime: 0 + secondTime: 0, + level: 0 ), NewsEntity( id: 11605391, @@ -91,7 +96,8 @@ public extension NewsEntity { mediaCompanyId: "056", mediaCompanyLogo: "https://mimgnews.pstatic.net/image/upload/office_logo/056/2021/07/15/logo_056_57_20210715101841.png", mediaCompanyName: "KBS", - secondTime: 0 + secondTime: 0, + level: 0 ) ] } diff --git a/Targets/D3N/Sources/Domain/News/Entity/TodayNewsEntity.swift b/Targets/D3N/Sources/Domain/News/Entity/TodayNewsEntity.swift new file mode 100644 index 0000000..8a39c0b --- /dev/null +++ b/Targets/D3N/Sources/Domain/News/Entity/TodayNewsEntity.swift @@ -0,0 +1,16 @@ +// +// TodayNewsEntity.swift +// D3N +// +// Created by 송영모 on 11/30/23. +// Copyright © 2023 sju. All rights reserved. +// + +import Foundation + +public struct TodayNewsEntity: Equatable { + public let type: String + public let title: String + public let subtitle: String + public let newses: [NewsEntity] +} diff --git a/Targets/D3N/Sources/Domain/News/NewsClient.swift b/Targets/D3N/Sources/Domain/News/NewsClient.swift index 6427cd3..660d6cb 100644 --- a/Targets/D3N/Sources/Domain/News/NewsClient.swift +++ b/Targets/D3N/Sources/Domain/News/NewsClient.swift @@ -13,17 +13,20 @@ import Moya struct NewsClient { var fetch: (Int, Int) async -> Result<[NewsEntity], D3NAPIError> + var fetchToday: () async -> Result<[TodayNewsEntity], D3NAPIError> var updateTime: (Int, Int) async -> Result } extension NewsClient: TestDependencyKey { static let previewValue = Self( fetch: { _, _ in .failure(.none) }, + fetchToday: { .failure(.none) }, updateTime: { _, _ in .failure(.none) } ) static let testValue = Self( fetch: unimplemented("\(Self.self).fetch"), + fetchToday: unimplemented("\(Self.self).fetchToday"), updateTime: unimplemented("\(Self.self).updateTime") ) } @@ -40,13 +43,19 @@ extension NewsClient: DependencyKey { static let liveValue = NewsClient( fetch: { pageIndex, pageSize in let target: TargetType = NewsService.fetchNewsList(pageIndex: pageIndex, pageSize: pageSize) - let response: Result = await D3NAPIkProvider.reqeust(target: target) + let response: Result = await D3NAPIProvider.reqeust(target: target) return response.map(\.content).map { $0.map { $0.toEntity() } } }, + fetchToday: { + let target: TargetType = NewsService.fetchTodayNewsList + let response: Result = await D3NAPIProvider.reqeust(target: target) + + return response.map { $0.map { $0.toEntity() } } + }, updateTime: { newsId, secondTime in let target: TargetType = NewsService.updateNewsTime(newsId: newsId, secondTime: secondTime) - let response: Result = await D3NAPIkProvider.reqeust(target: target) + let response: Result = await D3NAPIProvider.reqeust(target: target) return response.map { _ in return true diff --git a/Targets/D3N/Sources/Domain/News/NewsService.swift b/Targets/D3N/Sources/Domain/News/NewsService.swift index 14eb04f..e6de2c1 100644 --- a/Targets/D3N/Sources/Domain/News/NewsService.swift +++ b/Targets/D3N/Sources/Domain/News/NewsService.swift @@ -11,6 +11,7 @@ import Moya public enum NewsService { case fetchNewsList(pageIndex: Int,pageSize: Int) + case fetchTodayNewsList case updateNewsTime(newsId: Int, secondTime: Int) } @@ -19,16 +20,16 @@ extension NewsService: TargetType { public var path: String { switch self { - case .fetchNewsList: - return "news/list" - case .updateNewsTime: - return "news/time" + case .fetchNewsList: return "news/list" + case .fetchTodayNewsList: return "news/today/list" + case .updateNewsTime: return "news/time" } } public var method: Moya.Method { switch self { case .fetchNewsList: return .get + case .fetchTodayNewsList: return .get case .updateNewsTime: return .patch } } @@ -37,6 +38,8 @@ extension NewsService: TargetType { switch self { case let .fetchNewsList(pageIndex: page, pageSize: size): return .requestParameters(parameters: ["pageIndex": page, "pageSize": size], encoding: URLEncoding.queryString) + case .fetchTodayNewsList: + return .requestPlain case let .updateNewsTime(newsId: id, secondTime: time): let dto = UpdateNewsTimeRequestDTO(newsId: id, secondTime: time) return .requestJSONEncodable(dto) diff --git a/Targets/D3N/Sources/Domain/Quiz/DTO/Response/FetchQuizListResponseDTO.swift b/Targets/D3N/Sources/Domain/Quiz/DTO/Response/FetchQuizListResponseDTO.swift index 061480b..227e044 100644 --- a/Targets/D3N/Sources/Domain/Quiz/DTO/Response/FetchQuizListResponseDTO.swift +++ b/Targets/D3N/Sources/Domain/Quiz/DTO/Response/FetchQuizListResponseDTO.swift @@ -18,6 +18,7 @@ struct FetchQuizListResponseElement: Codable { let reason: String let secondTime: Int let selectedAnswer: Int? + let level: Int } extension FetchQuizListResponseElement { @@ -29,7 +30,8 @@ extension FetchQuizListResponseElement { answer: self.answer, reason: self.reason, secondTime: self.secondTime, - selectedAnswer: self.selectedAnswer + selectedAnswer: self.selectedAnswer, + level: self.level ) } } diff --git a/Targets/D3N/Sources/Domain/Quiz/Entity/QuizEntity.swift b/Targets/D3N/Sources/Domain/Quiz/Entity/QuizEntity.swift index a26cc2c..d3d73e4 100644 --- a/Targets/D3N/Sources/Domain/Quiz/Entity/QuizEntity.swift +++ b/Targets/D3N/Sources/Domain/Quiz/Entity/QuizEntity.swift @@ -16,6 +16,7 @@ public struct QuizEntity: Equatable { let reason: String var secondTime: Int var selectedAnswer: Int? + var level: Int var isSolved: Bool @@ -35,7 +36,8 @@ public struct QuizEntity: Equatable { answer: Int, reason: String, secondTime: Int, - selectedAnswer: Int? + selectedAnswer: Int?, + level: Int ) { self.id = id self.question = question @@ -44,6 +46,7 @@ public struct QuizEntity: Equatable { self.reason = reason self.secondTime = secondTime self.selectedAnswer = selectedAnswer + self.level = level self.isSolved = selectedAnswer != nil } diff --git a/Targets/D3N/Sources/Domain/Quiz/QuizClient.swift b/Targets/D3N/Sources/Domain/Quiz/QuizClient.swift index 6e13b4c..ecb7964 100644 --- a/Targets/D3N/Sources/Domain/Quiz/QuizClient.swift +++ b/Targets/D3N/Sources/Domain/Quiz/QuizClient.swift @@ -42,13 +42,13 @@ extension QuizClient: DependencyKey { static let liveValue = QuizClient( fetch: { newsId in let target: TargetType = QuizService.fetch(newsId: newsId) - let response: Result = await D3NAPIkProvider.reqeust(target: target) + let response: Result = await D3NAPIProvider.reqeust(target: target) return response.map { $0.map { $0.toEntity() } } }, submit: { quizs in let target: TargetType = QuizService.submit(quizs: quizs) - let response: Result = await D3NAPIkProvider.reqeust(target: target) + let response: Result = await D3NAPIProvider.reqeust(target: target) return response.map { $0.map { $0.quizId } @@ -56,7 +56,7 @@ extension QuizClient: DependencyKey { }, updateTime: { quizId, secondTime in let target: TargetType = QuizService.updateTime(quizId: quizId, secondTime: secondTime) - let response: Result = await D3NAPIkProvider.reqeust(target: target) + let response: Result = await D3NAPIProvider.reqeust(target: target) return response.map { _ in return true diff --git a/Targets/D3N/Sources/Domain/User/UserClient.swift b/Targets/D3N/Sources/Domain/User/UserClient.swift index f7b9c93..1c52746 100644 --- a/Targets/D3N/Sources/Domain/User/UserClient.swift +++ b/Targets/D3N/Sources/Domain/User/UserClient.swift @@ -44,7 +44,7 @@ extension UserClient: DependencyKey { static let liveValue = UserClient( onboardNeeded: { let target: TargetType = UserService.onboardNeeded - let response: Result = await D3NAPIkProvider.reqeust(target: target) + let response: Result = await D3NAPIProvider.reqeust(target: target) return response.map { dto in let entity = dto.isOnBoardingNeeded LocalStorageManager.save(.isOnBoardingNeeded, value: entity) @@ -53,7 +53,7 @@ extension UserClient: DependencyKey { }, onboard: { nickname, gender, birthDay, newsFields in let target: TargetType = UserService.onboard(nickname: nickname, gender: gender, birthDay: birthDay, newsFields: newsFields) - let response: Result = await D3NAPIkProvider.reqeust(target: target) + let response: Result = await D3NAPIProvider.reqeust(target: target) return response.map { dto in let entity = dto.toEntity() diff --git a/Targets/D3N/Sources/Feature/AllNews/AllNewsNavigationStackView.swift b/Targets/D3N/Sources/Feature/AllNews/AllNewsNavigationStackView.swift index 8df6209..95dd691 100644 --- a/Targets/D3N/Sources/Feature/AllNews/AllNewsNavigationStackView.swift +++ b/Targets/D3N/Sources/Feature/AllNews/AllNewsNavigationStackView.swift @@ -47,7 +47,7 @@ public struct AllNewsNavigationStackView: View { case .allNews: CaseLet( /AllNewsNavigationStackStore.Path.State.allNews, - action: TodayNavigationStackStore.Path.Action.allNews, + action: TodayNewsNavigationStackStore.Path.Action.allNews, then: AllNewsView.init(store:) ) } diff --git a/Targets/D3N/Sources/Feature/MainTab/MainTabStore.swift b/Targets/D3N/Sources/Feature/MainTab/MainTabStore.swift index b35537a..df8c68d 100644 --- a/Targets/D3N/Sources/Feature/MainTab/MainTabStore.swift +++ b/Targets/D3N/Sources/Feature/MainTab/MainTabStore.swift @@ -17,7 +17,7 @@ struct MainTabStore: Reducer { struct State: Equatable { var currentScene: MainScene = .question - var today: TodayNavigationStackStore.State? = .init() + var today: TodayNewsNavigationStackStore.State? = .init() var allNews: AllNewsNavigationStackStore.State? = .init() var myPage: MyPageNavigationStackStore.State? = .init() } @@ -31,7 +31,7 @@ struct MainTabStore: Reducer { case authRefreshRequest case authRefreshResponse(Result) - case today(TodayNavigationStackStore.Action) + case today(TodayNewsNavigationStackStore.Action) case allNews(AllNewsNavigationStackStore.Action) case myPage(MyPageNavigationStackStore.Action) case delegate(Delegate) @@ -69,7 +69,7 @@ struct MainTabStore: Reducer { } } .ifLet(\.today, action: /Action.today) { - TodayNavigationStackStore() + TodayNewsNavigationStackStore() } .ifLet(\.allNews, action: /Action.allNews) { AllNewsNavigationStackStore() diff --git a/Targets/D3N/Sources/Feature/MainTab/MainTabView.swift b/Targets/D3N/Sources/Feature/MainTab/MainTabView.swift index b0040c1..602023c 100644 --- a/Targets/D3N/Sources/Feature/MainTab/MainTabView.swift +++ b/Targets/D3N/Sources/Feature/MainTab/MainTabView.swift @@ -16,7 +16,7 @@ struct MainTabView: View { WithViewStore(self.store, observe: { $0 }) { viewStore in TabView { IfLetStore(self.store.scope(state: \.today, action: MainTabStore.Action.today)) { - TodayNavigationStackView(store: $0) + TodayNewsNavigationStackView(store: $0) .tabItem { Image(systemName: "doc.text.image") Text("투데이") diff --git a/Targets/D3N/Sources/Feature/News/List/NewsListStore.swift b/Targets/D3N/Sources/Feature/News/List/NewsListStore.swift deleted file mode 100644 index 121a9ae..0000000 --- a/Targets/D3N/Sources/Feature/News/List/NewsListStore.swift +++ /dev/null @@ -1,88 +0,0 @@ -//// -//// NewsListStore.swift -//// D3N -//// -//// Created by 송영모 on 10/17/23. -//// Copyright © 2023 sju. All rights reserved. -//// -// -//import Foundation -// -//import ComposableArchitecture -// -//public struct NewsListStore: Reducer { -// let FIXED_PAGE_SIZE = 10 -// -// public struct State: Equatable, Identifiable { -// public let id: UUID -// -// var newsEntityList: [NewsEntity] = [] -// var pageIndex: Int = 0 -// -// var newsListItems: IdentifiedArrayOf = [] -// -// public init( -// id: UUID = .init() -// ) { -// self.id = id -// } -// } -// -// @Dependency(\.newsClient) var newsClient -// -// public enum Action: Equatable { -// case onAppear -// -// case fetchNewsListRequest -// case fetchNewsListResponse(Result<[NewsEntity], D3NAPIError>) -// -// case newsListItems(id: NewsListItemCellStore.State.ID, action: NewsListItemCellStore.Action) -// case delegate(Delegate) -// -// public enum Delegate: Equatable { -// case select(NewsEntity) -// } -// } -// -// public var body: some ReducerOf { -// Reduce { state, action in -// switch action { -// case .onAppear: -// return .concatenate([ -// .send(.fetchNewsListRequest) -// ]) -// -// case .fetchNewsListRequest: -// return .run { [pageIndex = state.pageIndex] send in -// let response = await newsClient.fetchNewsList(pageIndex, FIXED_PAGE_SIZE) -// await send(.fetchNewsListResponse(response)) -// } -// -// case let .fetchNewsListResponse(.success(newsEntityList)): -// let newsListItems = state.makeNewsListItems(from: newsEntityList) -// state.newsListItems.append(contentsOf: newsListItems) -// state.pageIndex += 1 -// return .none -// -// case let .newsListItems(id: id, action: .delegate(action)): -// switch action { -// case .onAppear: -// if id == state.newsListItems.ids.last { -// return .send(.fetchNewsListRequest) -// } -// case .tapped: -// if let newsEntity = state.newsListItems[id: id]?.newsEntity { -// return .send(.delegate(.select(newsEntity))) -// } -// } -// return .none -// -// default: -// return .none -// } -// } -// .forEach(\.newsListItems, action: /Action.newsListItems(id:action:)) { -// NewsListItemCellStore() -// } -// } -//} diff --git a/Targets/D3N/Sources/Feature/News/List/NewsListView.swift b/Targets/D3N/Sources/Feature/News/List/NewsListView.swift deleted file mode 100644 index cf47a08..0000000 --- a/Targets/D3N/Sources/Feature/News/List/NewsListView.swift +++ /dev/null @@ -1,45 +0,0 @@ -//// -//// NewsListView.swift -//// D3N -//// -//// Created by 송영모 on 10/17/23. -//// Copyright © 2023 sju. All rights reserved. -//// -// -//import Foundation -//import SwiftUI -// -//import ComposableArchitecture -// -//public struct NewsListView: View { -// let store: StoreOf -// -// public init(store: StoreOf) { -// self.store = store -// } -// -// public var body: some View { -// WithViewStore(self.store, observe: { $0 }) { viewStore in -// ScrollView { -// VStack { -// newsListItemsView -// .padding(.horizontal) -// -// Spacer() -// } -// } -// .navigationTitle("전체 뉴스") -// .onAppear { -// viewStore.send(.onAppear) -// } -// } -// } -// -// private var newsListItemsView: some View { -// LazyVStack { -// ForEachStore(self.store.scope(state: \.newsListItems, action: NewsListStore.Action.newsListItems(id:action:))) { -// NewsListItemCellView(store: $0) -// } -// } -// } -//} diff --git a/Targets/D3N/Sources/Feature/Quiz/List/Cell/ChoiceListItemCellStore.swift b/Targets/D3N/Sources/Feature/Quiz/List/Cell/ChoiceListItemCellStore.swift deleted file mode 100644 index cffd54b..0000000 --- a/Targets/D3N/Sources/Feature/Quiz/List/Cell/ChoiceListItemCellStore.swift +++ /dev/null @@ -1,56 +0,0 @@ -// -// ChoiceListItemCellStore.swift -// D3N -// -// Created by 송영모 on 10/14/23. -// Copyright © 2023 sju. All rights reserved. -// - -import Foundation - -import ComposableArchitecture - -public struct ChoiceListItemCellStore: Reducer { - public struct State: Equatable, Identifiable { - public var id: UUID - public let choice: String - public var isSelected: Bool - - public init( - id: UUID = .init(), - choice: String, - isSelected: Bool = false - ) { - self.id = id - self.choice = choice - self.isSelected = isSelected - } - } - - public enum Action: Equatable { - case onAppear - - case tapped - - case delegate(Delegate) - - public enum Delegate: Equatable { - case tapped - } - } - - public var body: some ReducerOf { - Reduce { state, action in - switch action { - case .onAppear: - return .none - - case .tapped: - return .send(.delegate(.tapped)) - - default: - return .none - } - } - } -} diff --git a/Targets/D3N/Sources/Feature/Quiz/List/Cell/ChoiceListItemCellView.swift b/Targets/D3N/Sources/Feature/Quiz/List/Cell/ChoiceListItemCellView.swift deleted file mode 100644 index fffbdfa..0000000 --- a/Targets/D3N/Sources/Feature/Quiz/List/Cell/ChoiceListItemCellView.swift +++ /dev/null @@ -1,39 +0,0 @@ -// -// ChoiceListItemCellView.swift -// D3N -// -// Created by 송영모 on 10/14/23. -// Copyright © 2023 sju. All rights reserved. -// - -import Foundation -import SwiftUI - -import ComposableArchitecture - -public struct ChoiceListItemCellView: View { - let store: StoreOf - - public init(store: StoreOf) { - self.store = store - } - - public var body: some View { - WithViewStore(self.store, observe: { $0 }) { viewStore in - HStack { - Button(action: { - viewStore.send(.tapped) - }, label: { - Label( - LocalizedStringKey(viewStore.state.choice), - systemImage: viewStore.state.isSelected ? "circle.fill" : "circle" - ) - }) - .font(.subheadline) - .foregroundStyle(.foreground) - - Spacer() - } - } - } -} diff --git a/Targets/D3N/Sources/Feature/Quiz/List/Cell/QuizListItemCellStore.swift b/Targets/D3N/Sources/Feature/Quiz/List/Cell/QuizListItemCellStore.swift index ad9706d..a00895a 100644 --- a/Targets/D3N/Sources/Feature/Quiz/List/Cell/QuizListItemCellStore.swift +++ b/Targets/D3N/Sources/Feature/Quiz/List/Cell/QuizListItemCellStore.swift @@ -21,6 +21,7 @@ public struct QuizListItemCellStore: Reducer { var reason: String var secondTime: Int var selectedAnswer: Int? + var level: Int init( id: Int = .init(), @@ -30,16 +31,19 @@ public struct QuizListItemCellStore: Reducer { answer: Int, reason: String, secondTime: Int, - selectedAnswer: Int? = nil + selectedAnswer: Int? = nil, + level: Int ) { self.id = id self.isSolved = isSolved + self.question = question self.choices = choices self.answer = answer self.reason = reason self.secondTime = secondTime self.selectedAnswer = selectedAnswer + self.level = level } } diff --git a/Targets/D3N/Sources/Feature/Quiz/List/Cell/QuizListItemCellView.swift b/Targets/D3N/Sources/Feature/Quiz/List/Cell/QuizListItemCellView.swift index 9380c88..ba64cc0 100644 --- a/Targets/D3N/Sources/Feature/Quiz/List/Cell/QuizListItemCellView.swift +++ b/Targets/D3N/Sources/Feature/Quiz/List/Cell/QuizListItemCellView.swift @@ -26,9 +26,10 @@ public struct QuizListItemCellView: View { reason: viewStore.state.reason, answer: viewStore.state.answer, selectedAnswer: viewStore.state.selectedAnswer, + level: viewStore.state.level, isSolved: viewStore.state.isSolved ) - .padding(.top, 40) + .padding(.top, 10) Spacer() @@ -60,9 +61,13 @@ public struct QuizListItemCellView: View { reason: String, answer: Int, selectedAnswer: Int?, + level: Int, isSolved: Bool ) -> some View { VStack { + D3NIconTextBox.resolved(number: level) + .padding(.bottom, 10) + Text(question) Spacer() diff --git a/Targets/D3N/Sources/Feature/Quiz/List/QuizListStore.swift b/Targets/D3N/Sources/Feature/Quiz/List/QuizListStore.swift index c8917fa..787435a 100644 --- a/Targets/D3N/Sources/Feature/Quiz/List/QuizListStore.swift +++ b/Targets/D3N/Sources/Feature/Quiz/List/QuizListStore.swift @@ -33,7 +33,8 @@ public struct QuizListStore: Reducer { answer: quiz.answer, reason: quiz.reason, secondTime: quiz.secondTime, - selectedAnswer: quiz.selectedAnswer + selectedAnswer: quiz.selectedAnswer, + level: quiz.level ) } ) diff --git a/Targets/D3N/Sources/Feature/Today/Detail/TodayDetailStore.swift b/Targets/D3N/Sources/Feature/Today/Detail/TodayDetailStore.swift deleted file mode 100644 index 8d5893f..0000000 --- a/Targets/D3N/Sources/Feature/Today/Detail/TodayDetailStore.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// TodayDetailStore.swift -// D3N -// -// Created by 송영모 on 10/12/23. -// Copyright © 2023 sju. All rights reserved. -// - -import ComposableArchitecture - -public struct TodayDetailStore: Reducer { - public struct State: Equatable { } - - public enum Action: Equatable { - case onAppear - } - - public var body: some ReducerOf { - Reduce { state, action in - switch action { - case .onAppear: - return .none - - default: - return .none - } - } - } -} - diff --git a/Targets/D3N/Sources/Feature/Today/Detail/TodayDetailView.swift b/Targets/D3N/Sources/Feature/Today/Detail/TodayDetailView.swift deleted file mode 100644 index 6a56f0a..0000000 --- a/Targets/D3N/Sources/Feature/Today/Detail/TodayDetailView.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// TodayDetailView.swift -// D3N -// -// Created by 송영모 on 10/12/23. -// Copyright © 2023 sju. All rights reserved. -// - -import Foundation -import SwiftUI - -import ComposableArchitecture - -public struct TodayDetailView: View { - let store: StoreOf - - public init(store: StoreOf) { - self.store = store - } - - public var body: some View { - WithViewStore(self.store, observe: { $0 }) { viewStore in - Text("Today Detail") - } - } -} diff --git a/Targets/D3N/Sources/Feature/Today/Main/Cell/TodayListItemCellView.swift b/Targets/D3N/Sources/Feature/Today/Main/Cell/TodayListItemCellView.swift deleted file mode 100644 index f60e8e0..0000000 --- a/Targets/D3N/Sources/Feature/Today/Main/Cell/TodayListItemCellView.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// TodayListItemCellView.swift -// D3N -// -// Created by 송영모 on 11/6/23. -// Copyright © 2023 sju. All rights reserved. -// - -import Foundation -import SwiftUI - -import ComposableArchitecture - -public struct TodayListItemCellView: View { - let store: StoreOf - - @State var isPressed: Bool = false - - public init(store: StoreOf) { - self.store = store - } - - public var body: some View { - WithViewStore(self.store, observe: { $0 }) { viewStore in - Button(action: { - viewStore.send(.tapped) - }, label: { - HStack { - viewStore.state.newsEntity.field.icon - - VStack(alignment: .leading, spacing: 5) { - Text(viewStore.state.newsEntity.title) - .fontWeight(.semibold) - .lineLimit(1) - - Text(viewStore.state.newsEntity.summary) - .font(.footnote) - .foregroundStyle(.gray) - .lineLimit(1) - } - } - }) - .buttonStyle( - ScrollViewGestureButtonStyle( - pressAction: { - withAnimation { - isPressed = true - } - }, - doubleTapTimeoutout: 1, - doubleTapAction: { }, - longPressTime: 0, - longPressAction: { }, - endAction: { - withAnimation { - isPressed = false - } - } - ) - ) - .padding(10) - .background(isPressed ? Color.systemGray6 : Color.background) - .cornerRadius(20) - .clipped() - .scaleEffect(isPressed ? 0.95 : 1) - .onAppear { - viewStore.send(.onAppear) - } - } - } -} diff --git a/Targets/D3N/Sources/Feature/Today/Main/TodayMainStore.swift b/Targets/D3N/Sources/Feature/Today/Main/TodayMainStore.swift deleted file mode 100644 index f688c46..0000000 --- a/Targets/D3N/Sources/Feature/Today/Main/TodayMainStore.swift +++ /dev/null @@ -1,94 +0,0 @@ -// -// TodayMainStore.swift -// D3N -// -// Created by 송영모 on 10/12/23. -// Copyright © 2023 sju. All rights reserved. -// - -import Foundation - -import ComposableArchitecture - -public struct TodayMainStore: Reducer { - let TODAY_NEWS_PAGE_INDEX = 0 - let TODAY_NEWS_PAGE_SIZE = 3 - - public struct State: Equatable { - var newses: [NewsEntity] = [] { - didSet { - self.todayListItems = makeTodayListItems(from: newses) - } - } - - var todayListItems: IdentifiedArrayOf = [] - - public init() { } - } - - public enum Action: Equatable { - case onAppear - - case allNewsButtonTapped - - case fetchNewsListRequest - case fetchNewsListResponse(Result<[NewsEntity], D3NAPIError>) - - case todayListItems(id: TodayListItemCellStore.State.ID, action: TodayListItemCellStore.Action) - - case delegate(Delegate) - - public enum Delegate: Equatable { - case select(NewsEntity) - case allNewsButtonTapped - } - } - - @Dependency(\.newsClient) var newsClient - - public var body: some ReducerOf { - Reduce { state, action in - switch action { - case .onAppear: - return .concatenate([ - .send(.fetchNewsListRequest) - ]) - - case .allNewsButtonTapped: - return .send(.delegate(.allNewsButtonTapped)) - - case .fetchNewsListRequest: - return .run { send in - let response = await newsClient.fetch(TODAY_NEWS_PAGE_INDEX, TODAY_NEWS_PAGE_SIZE) - await send(.fetchNewsListResponse(response)) - } - - case let .fetchNewsListResponse(.success(newses)): - state.newses = newses - return .none - - case let .todayListItems(id: id, action: .delegate(.tapped)): - if let newsEntity = state.todayListItems[id: id]?.newsEntity { - return .send(.delegate(.select(newsEntity))) - } - return .none - - default: - return .none - } - } - .forEach(\.todayListItems, action: /Action.todayListItems(id:action:)) { - TodayListItemCellStore() - } - } -} - -public extension TodayMainStore.State { - func makeTodayListItems(from newses: [NewsEntity]) -> IdentifiedArrayOf { - return .init( - uniqueElements: newses.map { newsEntity in - return .init(newsEntity: newsEntity) - } - ) - } -} diff --git a/Targets/D3N/Sources/Feature/Today/Main/TodayMainView.swift b/Targets/D3N/Sources/Feature/Today/Main/TodayMainView.swift deleted file mode 100644 index 6349af8..0000000 --- a/Targets/D3N/Sources/Feature/Today/Main/TodayMainView.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// TodayMainView.swift -// D3N -// -// Created by 송영모 on 10/12/23. -// Copyright © 2023 sju. All rights reserved. -// - -import Foundation -import SwiftUI - -import ComposableArchitecture - -public struct TodayMainView: View { - let store: StoreOf - - public init(store: StoreOf) { - self.store = store - } - - public var body: some View { - WithViewStore(self.store, observe: { $0 }) { viewStore in - ScrollView { - VStack { - todayNewsView(viewStore: viewStore) - .padding() - } - } - .onAppear { - viewStore.send(.onAppear, animation: .default) - } - .navigationTitle("Today") - } - } - - private func todayNewsView(viewStore: ViewStoreOf) -> some View { - VStack(alignment: .leading, spacing: .zero) { - VStack(alignment: .leading){ - Text("최신 뉴스를 가져왔어요") - .font(.subheadline) - .foregroundStyle(.gray) - .fontWeight(.semibold) - - HStack { - Text("오늘의 뉴스 3 문제") - .font(.title) - .fontWeight(.semibold) - - Spacer() - - Button("전체보기", action: { - viewStore.send(.allNewsButtonTapped) - } - ) - } - } - .padding([.horizontal, .top]) - .onTapGesture { - viewStore.send(.allNewsButtonTapped) - } - - VStack(alignment: .leading, spacing: .zero) { - ForEachStore(self.store.scope(state: \.todayListItems, action: TodayMainStore.Action.todayListItems(id:action:))) { - TodayListItemCellView(store: $0) - } - } - .padding(.bottom, 10) - } - .background(Color.background) - .cornerRadius(20) - .clipped() - .shadow(color: .systemGray5, radius: 20) - } -} diff --git a/Targets/D3N/Sources/Feature/Today/Main/Cell/TodayListItemCellStore.swift b/Targets/D3N/Sources/Feature/TodayNews/Main/Cell/TodayNewsListItemCellStore.swift similarity index 59% rename from Targets/D3N/Sources/Feature/Today/Main/Cell/TodayListItemCellStore.swift rename to Targets/D3N/Sources/Feature/TodayNews/Main/Cell/TodayNewsListItemCellStore.swift index 448bbf8..09fab61 100644 --- a/Targets/D3N/Sources/Feature/Today/Main/Cell/TodayListItemCellStore.swift +++ b/Targets/D3N/Sources/Feature/TodayNews/Main/Cell/TodayNewsListItemCellStore.swift @@ -1,8 +1,8 @@ // -// TodayListItemCellStore.swift +// TodayNewsListItemCellStore.swift // D3N // -// Created by 송영모 on 11/6/23. +// Created by 송영모 on 11/30/23. // Copyright © 2023 sju. All rights reserved. // @@ -10,41 +10,37 @@ import Foundation import ComposableArchitecture -public struct TodayListItemCellStore: Reducer { +public struct TodayNewsListItemCellStore: Reducer { public struct State: Equatable, Identifiable { public var id: UUID - public var newsEntity: NewsEntity + public var todayNews: TodayNewsEntity public init( id: UUID = .init(), - newsEntity: NewsEntity + todayNews: TodayNewsEntity ) { self.id = id - self.newsEntity = newsEntity + self.todayNews = todayNews } } public enum Action: Equatable { case onAppear - case tapped + case select(NewsEntity) case delegate(Delegate) public enum Delegate: Equatable { - case onAppear - case tapped + case select(NewsEntity) } } public var body: some ReducerOf { Reduce { state, action in switch action { - case .onAppear: - return .send(.delegate(.onAppear)) - - case .tapped: - return .send(.delegate(.tapped)) + case let .select(news): + return .send(.delegate(.select(news))) default: return .none diff --git a/Targets/D3N/Sources/Feature/TodayNews/Main/Cell/TodayNewsListItemCellView.swift b/Targets/D3N/Sources/Feature/TodayNews/Main/Cell/TodayNewsListItemCellView.swift new file mode 100644 index 0000000..cb55852 --- /dev/null +++ b/Targets/D3N/Sources/Feature/TodayNews/Main/Cell/TodayNewsListItemCellView.swift @@ -0,0 +1,61 @@ +// +// TodayNewsListItemCellView.swift +// D3N +// +// Created by 송영모 on 11/30/23. +// Copyright © 2023 sju. All rights reserved. +// + +import Foundation +import SwiftUI + +import ComposableArchitecture + +public struct TodayNewsListItemCellView: View { + let store: StoreOf + + public init(store: StoreOf) { + self.store = store + } + + public var body: some View { + WithViewStore(self.store, observe: { $0 }) { viewStore in + VStack(alignment: .leading, spacing: .zero) { + VStack(alignment: .leading) { + Text(viewStore.state.todayNews.title) + .font(.subheadline) + .foregroundStyle(.gray) + .fontWeight(.semibold) + + HStack { + Text(viewStore.state.todayNews.subtitle) + .font(.title) + .fontWeight(.semibold) + + Spacer() + } + } + .padding([.horizontal, .top]) + + VStack(alignment: .leading, spacing: .zero) { + ForEach(viewStore.state.todayNews.newses, id: \.self) { news in + D3NIconAnimationButton( + icon: news.field.icon, + title: news.title, + content: news.summary, + isSelected: false, + action: { + viewStore.send(.select(news)) + } + ) + } + } + .padding(.bottom, 10) + } + .background(Color.background) + .cornerRadius(20) + .clipped() + .shadow(color: .systemGray5, radius: 20) + } + } +} diff --git a/Targets/D3N/Sources/Feature/TodayNews/Main/TodayNewsMainStore.swift b/Targets/D3N/Sources/Feature/TodayNews/Main/TodayNewsMainStore.swift new file mode 100644 index 0000000..72cd41f --- /dev/null +++ b/Targets/D3N/Sources/Feature/TodayNews/Main/TodayNewsMainStore.swift @@ -0,0 +1,89 @@ +// +// TodayMainStore.swift +// D3N +// +// Created by 송영모 on 10/12/23. +// Copyright © 2023 sju. All rights reserved. +// + +import Foundation + +import ComposableArchitecture + +public struct TodayNewsMainStore: Reducer { + public struct State: Equatable { + var todayNewses: [TodayNewsEntity] = [] { + didSet { + self.todayNewsListItems = makeTodayNewsListItems(from: todayNewses) + } + } + + var todayNewsListItems: IdentifiedArrayOf = [] + + public init() { } + } + + public enum Action: Equatable { + case onAppear + + case allNewsButtonTapped + + case fetchTodayNewsListRequest + case fetchTodayNewsListResponse(Result<[TodayNewsEntity], D3NAPIError>) + + case todayNewsListItems(id: TodayNewsListItemCellStore.State.ID, action: TodayNewsListItemCellStore.Action) + + case delegate(Delegate) + + public enum Delegate: Equatable { + case select(NewsEntity) + case allNewsButtonTapped + } + } + + @Dependency(\.newsClient) var newsClient + + public var body: some ReducerOf { + Reduce { state, action in + switch action { + case .onAppear: + return .send(.fetchTodayNewsListRequest) + + case .allNewsButtonTapped: + return .send(.delegate(.allNewsButtonTapped)) + + case .fetchTodayNewsListRequest: + return .run { send in + let response = await newsClient.fetchToday() + await send(.fetchTodayNewsListResponse(response)) + } + + case let .fetchTodayNewsListResponse(.success(todayNewses)): + state.todayNewses = todayNewses + return .none + + case let .todayNewsListItems(id: _, action: .delegate(action)): + switch action { + case let .select(news): + return .send(.delegate(.select(news))) + } + + default: + return .none + } + } + .forEach(\.todayNewsListItems, action: /Action.todayNewsListItems(id: action:)) { + TodayNewsListItemCellStore() + } + } +} + +public extension TodayNewsMainStore.State { + func makeTodayNewsListItems(from todayNewses: [TodayNewsEntity]) -> IdentifiedArrayOf { + return .init( + uniqueElements: todayNewses.map { todayNews in + .init(todayNews: todayNews) + } + ) + } +} diff --git a/Targets/D3N/Sources/Feature/TodayNews/Main/TodayNewsMainView.swift b/Targets/D3N/Sources/Feature/TodayNews/Main/TodayNewsMainView.swift new file mode 100644 index 0000000..922b02e --- /dev/null +++ b/Targets/D3N/Sources/Feature/TodayNews/Main/TodayNewsMainView.swift @@ -0,0 +1,37 @@ +// +// TodayMainView.swift +// D3N +// +// Created by 송영모 on 10/12/23. +// Copyright © 2023 sju. All rights reserved. +// + +import Foundation +import SwiftUI + +import ComposableArchitecture + +public struct TodayNewsMainView: View { + let store: StoreOf + + public init(store: StoreOf) { + self.store = store + } + + public var body: some View { + WithViewStore(self.store, observe: { $0 }) { viewStore in + ScrollView { + VStack { + ForEachStore(self.store.scope(state: \.todayNewsListItems, action: TodayNewsMainStore.Action.todayNewsListItems(id:action:))) { + TodayNewsListItemCellView(store: $0) + .padding() + } + } + } + .onAppear { + viewStore.send(.onAppear, animation: .default) + } + .navigationTitle("Today") + } + } +} diff --git a/Targets/D3N/Sources/Feature/Today/TodayNavigationStackStore.swift b/Targets/D3N/Sources/Feature/TodayNews/TodayNewsNavigationStackStore.swift similarity index 89% rename from Targets/D3N/Sources/Feature/Today/TodayNavigationStackStore.swift rename to Targets/D3N/Sources/Feature/TodayNews/TodayNewsNavigationStackStore.swift index 20c4831..5a90f3b 100644 --- a/Targets/D3N/Sources/Feature/Today/TodayNavigationStackStore.swift +++ b/Targets/D3N/Sources/Feature/TodayNews/TodayNewsNavigationStackStore.swift @@ -8,9 +8,9 @@ import ComposableArchitecture -public struct TodayNavigationStackStore: Reducer { +public struct TodayNewsNavigationStackStore: Reducer { public struct State: Equatable { - var main: TodayMainStore.State = .init() + var main: TodayNewsMainStore.State = .init() var path: StackState = .init() } @@ -20,7 +20,7 @@ public struct TodayNavigationStackStore: Reducer { case onAppear - case main(TodayMainStore.Action) + case main(TodayNewsMainStore.Action) case path(StackAction) case popToRoot @@ -33,23 +33,18 @@ public struct TodayNavigationStackStore: Reducer { public struct Path: Reducer { public enum State: Equatable { - case detail(TodayDetailStore.State) case quizMain(QuizMainStore.State) case quizResult(QuizResultStore.State) case allNews(AllNewsStore.State) } public enum Action: Equatable { - case detail(TodayDetailStore.Action) case quizMain(QuizMainStore.Action) case quizResult(QuizResultStore.Action) case allNews(AllNewsStore.Action) } public var body: some Reducer { - Scope(state: /State.detail, action: /Action.detail) { - TodayDetailStore() - } Scope(state: /State.quizMain, action: /Action.quizMain) { QuizMainStore() } @@ -109,7 +104,7 @@ public struct TodayNavigationStackStore: Reducer { } } Scope(state: \.main, action: /Action.main) { - TodayMainStore() + TodayNewsMainStore() } .forEach(\.path, action: /Action.path) { Path() diff --git a/Targets/D3N/Sources/Feature/Today/TodayNavigationStackView.swift b/Targets/D3N/Sources/Feature/TodayNews/TodayNewsNavigationStackView.swift similarity index 50% rename from Targets/D3N/Sources/Feature/Today/TodayNavigationStackView.swift rename to Targets/D3N/Sources/Feature/TodayNews/TodayNewsNavigationStackView.swift index 7c93525..520f557 100644 --- a/Targets/D3N/Sources/Feature/Today/TodayNavigationStackView.swift +++ b/Targets/D3N/Sources/Feature/TodayNews/TodayNewsNavigationStackView.swift @@ -11,50 +11,43 @@ import SwiftUI import ComposableArchitecture -public struct TodayNavigationStackView: View { - let store: StoreOf +public struct TodayNewsNavigationStackView: View { + let store: StoreOf - public init(store: StoreOf) { + public init(store: StoreOf) { self.store = store } public var body: some View { NavigationStackStore(self.store.scope( state: \.path, - action: TodayNavigationStackStore.Action.path)) { + action: TodayNewsNavigationStackStore.Action.path)) { WithViewStore(self.store, observe: { $0 }) { viewStore in - TodayMainView(store: self.store.scope(state: \.main, action: TodayNavigationStackStore.Action.main)) + TodayNewsMainView(store: self.store.scope(state: \.main, action: TodayNewsNavigationStackStore.Action.main)) .task { viewStore.send(.onAppear) } } } destination: { switch $0 { - case .detail: - CaseLet( - /TodayNavigationStackStore.Path.State.detail, - action: TodayNavigationStackStore.Path.Action.detail, - then: TodayDetailView.init(store:) - ) - case .quizMain: CaseLet( - /TodayNavigationStackStore.Path.State.quizMain, - action: TodayNavigationStackStore.Path.Action.quizMain, + /TodayNewsNavigationStackStore.Path.State.quizMain, + action: TodayNewsNavigationStackStore.Path.Action.quizMain, then: QuizMainView.init(store:) ) case .quizResult: CaseLet( - /TodayNavigationStackStore.Path.State.quizResult, - action: TodayNavigationStackStore.Path.Action.quizResult, + /TodayNewsNavigationStackStore.Path.State.quizResult, + action: TodayNewsNavigationStackStore.Path.Action.quizResult, then: QuizResultView.init(store:) ) case .allNews: CaseLet( - /TodayNavigationStackStore.Path.State.allNews, - action: TodayNavigationStackStore.Path.Action.allNews, + /TodayNewsNavigationStackStore.Path.State.allNews, + action: TodayNewsNavigationStackStore.Path.Action.allNews, then: AllNewsView.init(store:) ) } diff --git a/Targets/D3N/Sources/Shared/DesignSystem/D3NIcon.swift b/Targets/D3N/Sources/Shared/DesignSystem/D3NIcon.swift index d659e51..d1cd383 100644 --- a/Targets/D3N/Sources/Shared/DesignSystem/D3NIcon.swift +++ b/Targets/D3N/Sources/Shared/DesignSystem/D3NIcon.swift @@ -54,19 +54,35 @@ public extension D3NIcon { } } - static func one(isActive: Bool) -> D3NIcon { - return .init(systemImageName: "1.circle.fill", isActive: isActive) + static func one(color: Color? = nil, isActive: Bool) -> D3NIcon { + if let color = color { + return .init(systemImageName: "1.circle.fill", inactiveColor: color, isActive: isActive) + } else { + return .init(systemImageName: "1.circle.fill", isActive: isActive) + } } - static func two(isActive: Bool) -> D3NIcon { - return .init(systemImageName: "2.circle.fill", isActive: isActive) + static func two(color: Color? = nil, isActive: Bool) -> D3NIcon { + if let color = color { + return .init(systemImageName: "2.circle.fill", inactiveColor: color, isActive: isActive) + } else { + return .init(systemImageName: "2.circle.fill", isActive: isActive) + } } - static func three(isActive: Bool) -> D3NIcon { - return .init(systemImageName: "3.circle.fill", isActive: isActive) + static func three(color: Color? = nil, isActive: Bool) -> D3NIcon { + if let color = color { + return .init(systemImageName: "3.circle.fill", inactiveColor: color, isActive: isActive) + } else { + return .init(systemImageName: "3.circle.fill", isActive: isActive) + } } - static func four(isActive: Bool) -> D3NIcon { - return .init(systemImageName: "4.circle.fill", isActive: isActive) + static func four(color: Color? = nil, isActive: Bool) -> D3NIcon { + if let color = color { + return .init(systemImageName: "4.circle.fill", inactiveColor: color, isActive: isActive) + } else { + return .init(systemImageName: "4.circle.fill", isActive: isActive) + } } } diff --git a/Targets/D3N/Sources/Shared/DesignSystem/D3NIconTextBox.swift b/Targets/D3N/Sources/Shared/DesignSystem/D3NIconTextBox.swift new file mode 100644 index 0000000..3e8e92b --- /dev/null +++ b/Targets/D3N/Sources/Shared/DesignSystem/D3NIconTextBox.swift @@ -0,0 +1,51 @@ +// +// D3NIconTextBox.swift +// D3N +// +// Created by 송영모 on 11/30/23. +// Copyright © 2023 sju. All rights reserved. +// + +import Foundation +import SwiftUI + +public struct D3NIconTextBox: View { + let icon: D3NIcon? + let key: LocalizedStringKey + + init( + icon: D3NIcon? = nil, + key: LocalizedStringKey + ) { + self.icon = icon + self.key = key + } + + public var body: some View { + HStack { + icon + + Text(key) + .fontWeight(.semibold) + } + .padding(10) + .background(.ultraThickMaterial) + .cornerRadius(20) + .clipped() + } +} + +public extension D3NIconTextBox { + static func resolved(number: Int) -> D3NIconTextBox { + switch number { + case 1: return .one + case 2: return .two + case 3: return .three + default: return .one + } + } + + static let one = D3NIconTextBox(icon: .one(color: .yellow, isActive: false), key: "LEVEL 1") + static let two = D3NIconTextBox(icon: .two(color: .green, isActive: false), key: "LEVEL 2") + static let three = D3NIconTextBox(icon: .three(color: .pink, isActive: false), key: "LEVEL 3") +}