From d481e2527d60eab7cc7a86449836a3b3b09c1eed Mon Sep 17 00:00:00 2001 From: junyng Date: Sun, 9 Apr 2023 22:29:29 +0900 Subject: [PATCH 1/7] =?UTF-8?q?[Pl-29]=20ReducerProtocol=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../HomeContainerCore.swift | 12 +- .../MakePromise/MakePromiseAction.swift | 22 -- .../MakePromise/MakePromiseState.swift | 197 ---------- .../MakePromise/MakePromiseStore.swift | 349 ++++++++++++++---- .../Sources/MakePromise/MakePromiseView.swift | 26 +- .../SubViews/MakePromiseBottomButton.swift | 6 +- .../SubViews/SelectThemeStore.swift | 33 +- .../SubViews/SelectThemeView.swift | 4 +- .../SubViews/SetNameAndPlaceStore.swift | 84 +++-- .../SubViews/SetNameAndPlaceView.swift | 16 +- .../SubViews/TopInformationView.swift | 4 +- .../TimeTableFeature/Day+Extensions.swift | 4 +- .../TimeTableFeature/TimeTableView.swift | 179 ++++----- 13 files changed, 454 insertions(+), 482 deletions(-) delete mode 100644 AppPackage/Sources/MakePromise/MakePromiseAction.swift delete mode 100644 AppPackage/Sources/MakePromise/MakePromiseState.swift diff --git a/AppPackage/Sources/HomeContainerFeature/HomeContainerCore.swift b/AppPackage/Sources/HomeContainerFeature/HomeContainerCore.swift index fede91f..5cfc90b 100644 --- a/AppPackage/Sources/HomeContainerFeature/HomeContainerCore.swift +++ b/AppPackage/Sources/HomeContainerFeature/HomeContainerCore.swift @@ -39,12 +39,12 @@ public struct HomeContainerCore: ReducerProtocol { } public enum DestinationState: Equatable { - case makePromise(MakePromiseState) + case makePromise(MakePromise.State) case promiseList(PromiseListCore.State) } public enum DestinationAction: Equatable { - case makePromise(MakePromiseAction) + case makePromise(MakePromise.Action) case promiseList(PromiseListCore.Action) } @@ -135,14 +135,8 @@ public struct HomeContainerCore: ReducerProtocol { Scope( state: /DestinationState.makePromise, action: /DestinationAction.makePromise, - child: { - Reduce( - makePromiseReducer, - environment: .init() - ) - } + child: MakePromise.init ) - Scope( state: /DestinationState.promiseList, action: /DestinationAction.promiseList, diff --git a/AppPackage/Sources/MakePromise/MakePromiseAction.swift b/AppPackage/Sources/MakePromise/MakePromiseAction.swift deleted file mode 100644 index 0756787..0000000 --- a/AppPackage/Sources/MakePromise/MakePromiseAction.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// File.swift -// -// -// Created by 한상준 on 2023/02/25. -// - -import CalendarFeature -import Foundation -import TimeTableFeature - -public enum MakePromiseAction: Equatable { - case dismiss - case nextButtonTapped - case backButtonTapped - - case selectTheme(SelectThemeAction) - case setNameAndPlace(SetNameAndPlaceAction) - case calendar(CalendarCore.Action) - case timeSelection(TimeSelection.Action) - case timeTable(TimeTableAction) -} diff --git a/AppPackage/Sources/MakePromise/MakePromiseState.swift b/AppPackage/Sources/MakePromise/MakePromiseState.swift deleted file mode 100644 index a754090..0000000 --- a/AppPackage/Sources/MakePromise/MakePromiseState.swift +++ /dev/null @@ -1,197 +0,0 @@ -// -// File.swift -// -// -// Created by 한상준 on 2023/02/25. -// - -import CalendarFeature -import ComposableArchitecture -import TimeTableFeature - -public struct MakePromiseState: Equatable { - var shouldShowBackButton: Bool - var steps: [Step] - var currentStep: Step? { - if index < steps.count { - return steps[index] - } - return nil - } - - var index: Int = 0 - - var selectTheme: SelectThemeState? { - get { - getSelectThemeState() - } - set { - setSelectThemeState(newValue) - } - } - - var setNameAndPlace: SetNameAndPlaceState? { - get { - steps.compactMap { step in - guard case let .setNameAndPlace(state) = step else { - return nil - } - return state - }.first - } - set { - guard let newState = newValue else { - return - } - guard let index = steps.firstIndex(where: { - guard case .setNameAndPlace = $0 else { - return false - } - return true - }) else { return } - steps[index] = .setNameAndPlace(newState) - } - } - - var timeSelection: TimeSelection.State? { - get { - steps.compactMap { step in - guard case let .timeSelection(state) = step else { - return nil - } - return state - }.first - } - set { - guard let newState = newValue else { - return - } - guard let index = steps.firstIndex(where: { - guard case .timeSelection = $0 else { - return false - } - return true - }) else { return } - steps[index] = .timeSelection(newState) - } - } - - var calendar: CalendarCore.State? { - get { - steps.compactMap { step in - guard case let .calendar(state) = step else { - return nil - } - return state - }.first - } - set { - guard let newState = newValue else { - return - } - guard let index = steps.firstIndex(where: { - guard case .calendar = $0 else { - return false - } - return true - }) else { return } - steps[index] = .calendar(newState) - } - } - - var timeTable: TimeTableState? { - get { - steps.compactMap { step in - guard case let .timeTable(state) = step else { - return nil - } - return state - }.first - } - set { - guard let newState = newValue else { - return - } - guard let index = steps.firstIndex(where: { - guard case .timeTable = $0 else { - return false - } - return true - }) else { return } - steps[index] = .timeTable(newState) - } - } - - public init( - shouldShowBackButton: Bool = false, - steps: [Step] = [ - .selectTheme(.init()), - .setNameAndPlace(.init()), - .timeSelection(.init(timeRange: .init(start: 9, end: 23))), - .calendar(.init()), - .timeTable(.mock) - ] - ) { - self.shouldShowBackButton = shouldShowBackButton - self.steps = steps - } - - public enum Step: Equatable { - case selectTheme(SelectThemeState) - case setNameAndPlace(SetNameAndPlaceState) - case calendar(CalendarCore.State) - case timeSelection(TimeSelection.State) - case timeTable(TimeTableState) - } - - var isNextButtonEnable: Bool { - guard let currentStep else { return false } - switch currentStep { - case let .selectTheme(selectTheme): - return selectTheme.selectedType != nil - case .setNameAndPlace: - return true - case let .calendar(calendar): - return calendar.selectedDates.count > 0 - case let .timeSelection(timeSelection): - return timeSelection.isTimeRangeValid - case let .timeTable(timeTable): - return timeTable.isTimeSelected - } - } - - mutating func moveNextStep() { - guard index + 1 < steps.count else { return } - index += 1 - } - - mutating func movePastStep() { - let newIndex = index - 1 - guard newIndex >= 0, newIndex < steps.count else { return } - index = newIndex - } - - mutating func updateBackButtonVisibleState() { - shouldShowBackButton = index > 0 - } - - func getSelectThemeState() -> SelectThemeState? { - steps - .compactMap { - guard case let .selectTheme(state) = $0 else { - return nil - } - return state - } - .first - } - - mutating func setSelectThemeState(_ newState: SelectThemeState?) { - guard let newState else { - return - } - steps[index] = Step.selectTheme(newState) - } - - mutating func fetchCurrentStep() {} -} diff --git a/AppPackage/Sources/MakePromise/MakePromiseStore.swift b/AppPackage/Sources/MakePromise/MakePromiseStore.swift index 0af0469..23d112b 100644 --- a/AppPackage/Sources/MakePromise/MakePromiseStore.swift +++ b/AppPackage/Sources/MakePromise/MakePromiseStore.swift @@ -11,92 +11,283 @@ import ComposableArchitecture import Foundation import TimeTableFeature -public struct MakePromiseEnvironment { - public init() {} -} +public struct MakePromise: ReducerProtocol { + public struct State: Equatable { + var shouldShowBackButton: Bool + var steps: [Step] + var currentStep: Step? { + if index < steps.count { + return steps[index] + } + return nil + } -public let makePromiseReducer = AnyReducer.combine( - makePromiseSelectThemeReducer - .optional() - .pullback( - state: \.selectTheme, - action: /MakePromiseAction.selectTheme, - environment: { _ in SelectThemeEnvironment() } - ), - makePromiseSetNameAndPlaceReducer - .optional() - .pullback( - state: \.setNameAndPlace, - action: /MakePromiseAction.setNameAndPlace, - environment: { _ in SetNameAndPlaceEnvironment() } - ), - AnyReducer { _ in - CalendarCore() - } - .optional() - .pullback( - state: \.calendar, - action: /MakePromiseAction.calendar, - environment: { _ in } - ), - AnyReducer { _ in - TimeSelection() - } - .optional() - .pullback( - state: \.timeSelection, - action: /MakePromiseAction.timeSelection, - environment: { _ in } - ), - timeTableReducer - .optional() - .pullback( - state: \.timeTable, - action: /MakePromiseAction.timeTable, - environment: { _ in () } - ), - AnyReducer { state, action, _ in - switch action { - case .dismiss: - return .none - - case .nextButtonTapped: - if case let .timeSelection(timeSelection) = state.currentStep, - let startTime = timeSelection.startTime, - let endTime = timeSelection.endTime { - state.timeTable?.startTime = TimeInterval(startTime * 3600) - state.timeTable?.endTime = TimeInterval(endTime * 3600) - state.timeTable?.reload() + var index: Int = 0 + + var selectTheme: SelectTheme.State? { + get { + getSelectThemeState() + } + set { + setSelectThemeState(newValue) + } + } + + var setNameAndPlace: SetNameAndPlace.State? { + get { + steps.compactMap { step in + guard case let .setNameAndPlace(state) = step else { + return nil + } + return state + }.first + } + set { + guard let newState = newValue else { + return + } + guard let index = steps.firstIndex(where: { + guard case .setNameAndPlace = $0 else { + return false + } + return true + }) else { return } + steps[index] = .setNameAndPlace(newState) + } + } + + var timeSelection: TimeSelection.State? { + get { + steps.compactMap { step in + guard case let .timeSelection(state) = step else { + return nil + } + return state + }.first + } + set { + guard let newState = newValue else { + return + } + guard let index = steps.firstIndex(where: { + guard case .timeSelection = $0 else { + return false + } + return true + }) else { return } + steps[index] = .timeSelection(newState) + } + } + + var calendar: CalendarCore.State? { + get { + steps.compactMap { step in + guard case let .calendar(state) = step else { + return nil + } + return state + }.first + } + set { + guard let newState = newValue else { + return + } + guard let index = steps.firstIndex(where: { + guard case .calendar = $0 else { + return false + } + return true + }) else { return } + steps[index] = .calendar(newState) + } + } + + var timeTable: TimeTable.State? { + get { + steps.compactMap { step in + guard case let .timeTable(state) = step else { + return nil + } + return state + }.first + } + set { + guard let newState = newValue else { + return + } + guard let index = steps.firstIndex(where: { + guard case .timeTable = $0 else { + return false + } + return true + }) else { return } + steps[index] = .timeTable(newState) + } + } + + public init( + shouldShowBackButton: Bool = false, + steps: [Step] = [ + .selectTheme(.init()), + .setNameAndPlace(.init()), + .timeSelection(.init(timeRange: .init(start: 9, end: 23))), + .calendar(.init()), + .timeTable(.mock) + ] + ) { + self.shouldShowBackButton = shouldShowBackButton + self.steps = steps + } + + public enum Step: Equatable { + case selectTheme(SelectTheme.State) + case setNameAndPlace(SetNameAndPlace.State) + case calendar(CalendarCore.State) + case timeSelection(TimeSelection.State) + case timeTable(TimeTable.State) + } + + var isNextButtonEnable: Bool { + guard let currentStep else { return false } + switch currentStep { + case let .selectTheme(selectTheme): + return selectTheme.selectedType != nil + case .setNameAndPlace: + return true + case let .calendar(calendar): + return calendar.selectedDates.count > 0 + case let .timeSelection(timeSelection): + return timeSelection.isTimeRangeValid + case let .timeTable(timeTable): + return timeTable.isTimeSelected } + } + + mutating func moveNextStep() { + guard index + 1 < steps.count else { return } + index += 1 + } + + mutating func movePastStep() { + let newIndex = index - 1 + guard newIndex >= 0, newIndex < steps.count else { return } + index = newIndex + } - if case let .calendar(calendarState) = state.currentStep { - let days: [TimeTableState.Day] = calendarState.selectedDates.map { - .init(date: $0) + mutating func updateBackButtonVisibleState() { + shouldShowBackButton = index > 0 + } + + func getSelectThemeState() -> SelectTheme.State? { + steps + .compactMap { + guard case let .selectTheme(state) = $0 else { + return nil + } + return state } - state.timeTable?.days = days + .first + } + + mutating func setSelectThemeState(_ newState: SelectTheme.State?) { + guard let newState else { + return } + steps[index] = Step.selectTheme(newState) + } + + mutating func fetchCurrentStep() {} + } + + public enum Action: Equatable { + case dismiss + case nextButtonTapped + case backButtonTapped - if state.isNextButtonEnable { - state.moveNextStep() + case selectTheme(SelectTheme.Action) + case setNameAndPlace(SetNameAndPlace.Action) + case calendar(CalendarCore.Action) + case timeSelection(TimeSelection.Action) + case timeTable(TimeTable.Action) + } + + public var body: some ReducerProtocolOf { + Reduce { state, action in + switch action { + case .dismiss: + return .none + + case .nextButtonTapped: + if case let .timeSelection(timeSelection) = state.currentStep, + let startTime = timeSelection.startTime, + let endTime = timeSelection.endTime + { + state.timeTable?.startTime = TimeInterval(startTime * 3600) + state.timeTable?.endTime = TimeInterval(endTime * 3600) + state.timeTable?.reload() + } + + if case let .calendar(calendarState) = state.currentStep { + let days: [TimeTable.State.Day] = calendarState.selectedDates.map { + .init(date: $0) + } + state.timeTable?.days = days + } + + if state.isNextButtonEnable { + state.moveNextStep() + state.updateBackButtonVisibleState() + } + return .none + case .backButtonTapped: + state.movePastStep() state.updateBackButtonVisibleState() + return .none + case .selectTheme(.promiseTypeListItemTapped): + return .none + case .setNameAndPlace(.filledPromiseName): + return .none + case .setNameAndPlace(.filledPromisePlace): + return .none + case .calendar: + return .none + case .timeSelection: + return .none + case .timeTable: + return .none } - return .none - case .backButtonTapped: - state.movePastStep() - state.updateBackButtonVisibleState() - return .none - case let .selectTheme(.promiseTypeListItemTapped(promiseType)): - return .none - case let .setNameAndPlace(.filledPromiseName(name)): - return .none - case let .setNameAndPlace(.filledPromisePlace(place)): - return .none - case .calendar: - return .none - case .timeSelection: - return .none - case .timeTable: - return .none + } + .ifLet( + \.selectTheme, + action: /MakePromise.Action.selectTheme + ) { + SelectTheme() + } + .ifLet( + \.setNameAndPlace, + action: /MakePromise.Action.setNameAndPlace + ) { + SetNameAndPlace() + } + .ifLet( + \.calendar, + action: /MakePromise.Action.calendar + ) { + CalendarCore() + } + .ifLet( + \.timeSelection, + action: /MakePromise.Action.timeSelection + ) { + TimeSelection() + } + .ifLet( + \.timeTable, + action: /MakePromise.Action.timeTable + ) { + TimeTable() } } -) + + public init() {} +} diff --git a/AppPackage/Sources/MakePromise/MakePromiseView.swift b/AppPackage/Sources/MakePromise/MakePromiseView.swift index f9d921d..78ef033 100644 --- a/AppPackage/Sources/MakePromise/MakePromiseView.swift +++ b/AppPackage/Sources/MakePromise/MakePromiseView.swift @@ -13,7 +13,7 @@ import SwiftUI import TimeTableFeature public struct MakePromiseView: View { - let store: Store + let store: StoreOf public var body: some View { VStack { @@ -25,34 +25,34 @@ public struct MakePromiseView: View { } } - public init(store: Store) { + public init(store: StoreOf) { self.store = store } } struct PromiseContentView: View { - var store: Store + var store: StoreOf public var body: some View { IfLetStore(store.scope(state: \.currentStep)) { store in SwitchStore(store) { CaseLet( - state: /MakePromiseState.Step.selectTheme, - action: MakePromiseAction.selectTheme, + state: /MakePromise.State.Step.selectTheme, + action: MakePromise.Action.selectTheme, then: SelectThemeView.init ) CaseLet( - state: /MakePromiseState.Step.setNameAndPlace, - action: MakePromiseAction.setNameAndPlace, + state: /MakePromise.State.Step.setNameAndPlace, + action: MakePromise.Action.setNameAndPlace, then: NameAndPlaceView.init ) CaseLet( - state: /MakePromiseState.Step.timeSelection, - action: MakePromiseAction.timeSelection, + state: /MakePromise.State.Step.timeSelection, + action: MakePromise.Action.timeSelection, then: TimeSelectionView.init ) CaseLet( - state: /MakePromiseState.Step.calendar, - action: MakePromiseAction.calendar, + state: /MakePromise.State.Step.calendar, + action: MakePromise.Action.calendar, then: { CalendarView( type: .promise, @@ -61,8 +61,8 @@ struct PromiseContentView: View { } ) CaseLet( - state: /MakePromiseState.Step.timeTable, - action: MakePromiseAction.timeTable, + state: /MakePromise.State.Step.timeTable, + action: MakePromise.Action.timeTable, then: TimeTableView.init ) } diff --git a/AppPackage/Sources/MakePromise/SubViews/MakePromiseBottomButton.swift b/AppPackage/Sources/MakePromise/SubViews/MakePromiseBottomButton.swift index 3c196de..09c6c25 100644 --- a/AppPackage/Sources/MakePromise/SubViews/MakePromiseBottomButton.swift +++ b/AppPackage/Sources/MakePromise/SubViews/MakePromiseBottomButton.swift @@ -11,7 +11,7 @@ import DesignSystem import SwiftUI public struct MakePromiseBottomButton: View { - var store: Store + var store: StoreOf public var body: some View { WithViewStore(self.store) { viewStore in HStack { @@ -34,7 +34,7 @@ public struct MakePromiseBottomButton: View { } public struct PromiseNextButton: View { - var store: Store + var store: StoreOf public var body: some View { WithViewStore(self.store) { viewStore in @@ -56,7 +56,7 @@ public struct PromiseNextButton: View { } public struct PromiseBackButton: View { - var store: Store + var store: StoreOf public var body: some View { WithViewStore(self.store) { viewStore in Button { diff --git a/AppPackage/Sources/MakePromise/SubViews/SelectThemeStore.swift b/AppPackage/Sources/MakePromise/SubViews/SelectThemeStore.swift index ec75ae7..6550671 100644 --- a/AppPackage/Sources/MakePromise/SubViews/SelectThemeStore.swift +++ b/AppPackage/Sources/MakePromise/SubViews/SelectThemeStore.swift @@ -26,23 +26,26 @@ public enum PromiseType: String, CaseIterable, Equatable { } } -public struct SelectThemeState: Equatable { - var selectedType: PromiseType? - public init(selectedType: PromiseType? = nil) { - self.selectedType = selectedType - } -} +public struct SelectTheme: ReducerProtocol { + public struct State: Equatable { + var selectedType: PromiseType? -public enum SelectThemeAction: Equatable { - case promiseTypeListItemTapped(PromiseType) -} + public init(selectedType: PromiseType? = nil) { + self.selectedType = selectedType + } + } -public struct SelectThemeEnvironment {} + public enum Action: Equatable { + case promiseTypeListItemTapped(PromiseType) + } -public let makePromiseSelectThemeReducer = AnyReducer { state, action, _ in - switch action { - case let .promiseTypeListItemTapped(type): - state.selectedType = (state.selectedType == type) ? nil : type - return .none + public var body: some ReducerProtocolOf { + Reduce { state, action in + switch action { + case let .promiseTypeListItemTapped(type): + state.selectedType = (state.selectedType == type) ? nil : type + return .none + } + } } } diff --git a/AppPackage/Sources/MakePromise/SubViews/SelectThemeView.swift b/AppPackage/Sources/MakePromise/SubViews/SelectThemeView.swift index b89184b..9467978 100644 --- a/AppPackage/Sources/MakePromise/SubViews/SelectThemeView.swift +++ b/AppPackage/Sources/MakePromise/SubViews/SelectThemeView.swift @@ -11,7 +11,7 @@ import DesignSystem import SwiftUI public struct SelectThemeView: View { - var store: Store + var store: StoreOf let listItemEdgePadding = EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 20) public var body: some View { VStack { @@ -28,7 +28,7 @@ public struct SelectThemeView: View { public struct SelectThemeItemView: View { var promiseType: PromiseType - var store: Store + var store: StoreOf let itemCornerRadius: CGFloat = 16 let checkMarkCircle = "checkmark.circle" let checkmarkCircleFill = "checkmark.circle.fill" diff --git a/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceStore.swift b/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceStore.swift index 78c7680..a28063b 100644 --- a/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceStore.swift +++ b/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceStore.swift @@ -9,56 +9,58 @@ import ComposableArchitecture import Foundation -public struct SetNameAndPlaceState: Equatable { - var maxCharacter = 10 - var promiseName: String = "" - var promisePlace: String = "" +public struct SetNameAndPlace: ReducerProtocol { + public struct State: Equatable { + var maxCharacter = 10 + var promiseName: String = "" + var promisePlace: String = "" - var numberOfCharacterInNameText: Int { - if promiseName.count <= maxCharacter { - return promiseName.count - } else { - return maxCharacter + var numberOfCharacterInNameText: Int { + if promiseName.count <= maxCharacter { + return promiseName.count + } else { + return maxCharacter + } } - } - var numberOfCharacterInPlaceText: Int { - if promisePlace.count <= maxCharacter { - return promisePlace.count - } else { - return maxCharacter + var numberOfCharacterInPlaceText: Int { + if promisePlace.count <= maxCharacter { + return promisePlace.count + } else { + return maxCharacter + } } - } - - var shouldShowNameTextCountWarning: Bool { - promiseName.count > maxCharacter - } - var shouldShowPlaceTextCountWarning: Bool { - promisePlace.count > maxCharacter - } - - var isNextButtonEnable: Bool { - (numberOfCharacterInNameText > 0 && !shouldShowNameTextCountWarning) && (numberOfCharacterInPlaceText > 0 && !shouldShowPlaceTextCountWarning) - } + var shouldShowNameTextCountWarning: Bool { + promiseName.count > maxCharacter + } - public init() {} -} + var shouldShowPlaceTextCountWarning: Bool { + promisePlace.count > maxCharacter + } -public enum SetNameAndPlaceAction: Equatable { - case filledPromiseName(String) - case filledPromisePlace(String) -} + var isNextButtonEnable: Bool { + (numberOfCharacterInNameText > 0 && !shouldShowNameTextCountWarning) && (numberOfCharacterInPlaceText > 0 && !shouldShowPlaceTextCountWarning) + } -public struct SetNameAndPlaceEnvironment {} + public init() {} + } -public let makePromiseSetNameAndPlaceReducer = AnyReducer { state, action, _ in - switch action { - case let .filledPromiseName(name): - state.promiseName = name - case let .filledPromisePlace(place): - state.promisePlace = place + public enum Action: Equatable { + case filledPromiseName(String) + case filledPromisePlace(String) } - return .none + public var body: some ReducerProtocolOf { + Reduce { state, action in + switch action { + case let .filledPromiseName(name): + state.promiseName = name + return .none + case let .filledPromisePlace(place): + state.promisePlace = place + return .none + } + } + } } diff --git a/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceView.swift b/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceView.swift index 088b4c0..76a8c96 100644 --- a/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceView.swift +++ b/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceView.swift @@ -13,7 +13,7 @@ import SwiftUI typealias NameAndPlaceView = SetNameAndPlaceView public struct SetNameAndPlaceView: View { - var store: Store + var store: StoreOf public enum TextFieldType { case name @@ -52,10 +52,10 @@ public struct SetNameAndPlaceView: View { public struct TextFieldWithTitleView: View { var type: SetNameAndPlaceView.TextFieldType - var store: Store + var store: StoreOf - @ObservedObject var viewStore: ViewStore - init(type: SetNameAndPlaceView.TextFieldType, store: Store) { + @ObservedObject var viewStore: ViewStore + init(type: SetNameAndPlaceView.TextFieldType, store: StoreOf) { self.type = type self.store = store viewStore = ViewStore( @@ -71,7 +71,7 @@ public struct TextFieldWithTitleView: View { let numberOfCharacter: Int let maxNumberOfCharacter: Int - init(_ type: SetNameAndPlaceView.TextFieldType, state: SetNameAndPlaceState) { + init(_ type: SetNameAndPlaceView.TextFieldType, state: SetNameAndPlace.State) { switch type { case .name: showWarningMessage = state.shouldShowNameTextCountWarning @@ -87,7 +87,7 @@ public struct TextFieldWithTitleView: View { } } - typealias SetNameAndPlaceStore = ViewStore + typealias SetNameAndPlaceStore = ViewStoreOf public var body: some View { HStack { Spacer(minLength: 20) @@ -125,12 +125,12 @@ public struct TextFieldWithTitleView: View { } } - func getBorderColor(_ viewStore: ViewStore, type _: SetNameAndPlaceView.TextFieldType) -> Color { + func getBorderColor(_ viewStore: ViewStore, type _: SetNameAndPlaceView.TextFieldType) -> Color { return viewStore.showWarningMessage ? PDS.COLOR.scarlet1.scale : PDS.COLOR.purple9.scale } - func getTextCountColor(_ viewStore: ViewStore, type _: SetNameAndPlaceView.TextFieldType) -> Color { + func getTextCountColor(_ viewStore: ViewStore, type _: SetNameAndPlaceView.TextFieldType) -> Color { return viewStore.showWarningMessage ? PDS.COLOR.scarlet1.scale : PDS.COLOR.gray4.scale } diff --git a/AppPackage/Sources/MakePromise/SubViews/TopInformationView.swift b/AppPackage/Sources/MakePromise/SubViews/TopInformationView.swift index 90a8683..e606843 100644 --- a/AppPackage/Sources/MakePromise/SubViews/TopInformationView.swift +++ b/AppPackage/Sources/MakePromise/SubViews/TopInformationView.swift @@ -11,7 +11,7 @@ import DesignSystem import SwiftUI public struct TopInformationView: View { - var store: Store + var store: StoreOf public var body: some View { WithViewStore(self.store) { viewStore in VStack(spacing: 4) { @@ -52,7 +52,7 @@ public struct TopInformationView: View { } } -private extension MakePromiseState.Step { +private extension MakePromise.State.Step { var title: String { switch self { case .selectTheme: diff --git a/AppPackage/Sources/TimeTableFeature/Day+Extensions.swift b/AppPackage/Sources/TimeTableFeature/Day+Extensions.swift index b762aff..14a428d 100644 --- a/AppPackage/Sources/TimeTableFeature/Day+Extensions.swift +++ b/AppPackage/Sources/TimeTableFeature/Day+Extensions.swift @@ -8,7 +8,7 @@ import Foundation -extension TimeTableState.Day { +extension TimeTable.State.Day { func formatted(with formatter: DateFormatter) -> String { formatter.string(from: date) } @@ -30,7 +30,7 @@ extension DateFormatter { }() } -extension Array where Element == TimeTableState.Day { +extension Array where Element == TimeTable.State.Day { static var weekend: Self { return (0 ..< 7).map { index -> Element in .init(date: .init(timeIntervalSinceNow: .day * TimeInterval(index))) diff --git a/AppPackage/Sources/TimeTableFeature/TimeTableView.swift b/AppPackage/Sources/TimeTableFeature/TimeTableView.swift index 67eafe7..f87009f 100644 --- a/AppPackage/Sources/TimeTableFeature/TimeTableView.swift +++ b/AppPackage/Sources/TimeTableFeature/TimeTableView.swift @@ -9,113 +9,115 @@ import ComposableArchitecture import SwiftUI -public struct TimeTableState: Equatable { - public struct Day: Hashable, Equatable, Identifiable { - public let id: Int - let date: Date +public struct TimeTable: ReducerProtocol { + public struct State: Equatable { + public var days: [Day] + public var startTime: TimeInterval + public var endTime: TimeInterval + public var timeInterval: TimeInterval + public var timeMarkerInterval: TimeInterval + public var timeRanges: [TimeRange] = [] + public var timeCells: [[TimeCell]] = [] - public init(date: Date) { - id = date.hashValue - self.date = date + public struct Day: Hashable, Equatable, Identifiable { + public let id: Int + let date: Date + + public init(date: Date) { + id = date.hashValue + self.date = date + } } - } - public enum TimeCell { - case selected - case deselected + public enum TimeCell { + case selected + case deselected - mutating func toggle() { - switch self { - case .deselected: - self = .selected - case .selected: - self = .deselected + mutating func toggle() { + switch self { + case .deselected: + self = .selected + case .selected: + self = .deselected + } } } - } - - public struct TimeRange: Equatable, Hashable { - let startTime: TimeInterval - let endTime: TimeInterval - let isStartTimeVisible: Bool - } - public var days: [Day] - public var startTime: TimeInterval - public var endTime: TimeInterval - public var timeInterval: TimeInterval - public var timeMarkerInterval: TimeInterval - public var timeRanges: [TimeRange] = [] - public var timeCells: [[TimeCell]] = [] + public struct TimeRange: Equatable, Hashable { + let startTime: TimeInterval + let endTime: TimeInterval + let isStartTimeVisible: Bool + } - public init( - days: [Day] = [], - startTime: TimeInterval = .init(), - endTime: TimeInterval = .init(), - timeInterval: TimeInterval = .init(), - timeMarkerInterval: TimeInterval = .init() - ) { - self.days = days - self.startTime = startTime - self.endTime = endTime - self.timeInterval = timeInterval - self.timeMarkerInterval = timeMarkerInterval - reload() - } + public init( + days: [Day] = [], + startTime: TimeInterval = .init(), + endTime: TimeInterval = .init(), + timeInterval: TimeInterval = .init(), + timeMarkerInterval: TimeInterval = .init() + ) { + self.days = days + self.startTime = startTime + self.endTime = endTime + self.timeInterval = timeInterval + self.timeMarkerInterval = timeMarkerInterval + reload() + } - public var isTimeSelected: Bool { - timeCells - .flatMap { $0 } - .contains { $0 == .selected } - } + public var isTimeSelected: Bool { + timeCells + .flatMap { $0 } + .contains { $0 == .selected } + } - public var isGridLoadable: Bool { - timeRanges.count > 0 && days.count > 0 && timeCells.count > 0 - } + public var isGridLoadable: Bool { + timeRanges.count > 0 && days.count > 0 && timeCells.count > 0 + } - public mutating func reload() { - timeRanges = stride( - from: startTime, - to: endTime, - by: timeInterval - ) - .map { - .init( - startTime: $0, - endTime: $0 + timeInterval, - isStartTimeVisible: $0.truncatingRemainder(dividingBy: timeMarkerInterval) == 0 + public mutating func reload() { + timeRanges = stride( + from: startTime, + to: endTime, + by: timeInterval + ) + .map { + .init( + startTime: $0, + endTime: $0 + timeInterval, + isStartTimeVisible: $0.truncatingRemainder(dividingBy: timeMarkerInterval) == 0 + ) + } + timeCells = .init( + repeating: .init(repeating: .deselected, count: timeRanges.count), + count: days.count ) } - timeCells = .init( - repeating: .init(repeating: .deselected, count: timeRanges.count), - count: days.count - ) } -} -public enum TimeTableAction: Equatable { - case timeCellTapped(row: Int, column: Int) -} + public enum Action: Equatable { + case timeCellTapped(row: Int, column: Int) + } -public let timeTableReducer = Reducer< - TimeTableState, - TimeTableAction, - Void -> { state, action, _ in - switch action { - case let .timeCellTapped(row, column): - guard row >= 0, row < state.timeRanges.count else { return .none } - guard column >= 0, column < state.days.count else { return .none } - state.timeCells[column][row].toggle() - return .none + public var body: some ReducerProtocolOf { + Reduce { state, action in + switch action { + case let .timeCellTapped(row, column): + guard row >= 0, row < state.timeRanges.count else { return .none } + guard column >= 0, column < state.days.count else { return .none } + state.timeCells[column][row].toggle() + return .none + } + } } + + public init() {} } public struct TimeTableView: View { - let store: Store - @ObservedObject var viewStore: ViewStore + let store: StoreOf + @ObservedObject var viewStore: ViewStoreOf - public init(store: Store) { + public init(store: StoreOf) { self.store = store viewStore = ViewStore(store) } @@ -308,7 +310,7 @@ private enum Resource { } } -public extension TimeTableState { +public extension TimeTable.State { static var mock: Self { let startTime: TimeInterval = 9 * .hour let endTime: TimeInterval = 24 * .hour @@ -328,8 +330,7 @@ struct TimeTableView_Previews: PreviewProvider { TimeTableView( store: .init( initialState: .mock, - reducer: timeTableReducer, - environment: () + reducer: TimeTable() ) ) } From 26b9d697325c81dbc95d9fa1c6217ce39bcb47d0 Mon Sep 17 00:00:00 2001 From: junyng Date: Tue, 11 Apr 2023 01:37:06 +0900 Subject: [PATCH 2/7] =?UTF-8?q?[Pl-29]=20=ED=85=8C=EB=A7=88=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20=ED=99=94=EB=A9=B4=20Category=20API=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MakePromise/MakePromiseStore.swift | 4 +- .../SubViews/SelectThemeStore.swift | 72 +++++++++++++------ .../SubViews/SelectThemeView.swift | 72 ++++++++++++++----- 3 files changed, 108 insertions(+), 40 deletions(-) diff --git a/AppPackage/Sources/MakePromise/MakePromiseStore.swift b/AppPackage/Sources/MakePromise/MakePromiseStore.swift index 23d112b..2fc470c 100644 --- a/AppPackage/Sources/MakePromise/MakePromiseStore.swift +++ b/AppPackage/Sources/MakePromise/MakePromiseStore.swift @@ -151,7 +151,7 @@ public struct MakePromise: ReducerProtocol { guard let currentStep else { return false } switch currentStep { case let .selectTheme(selectTheme): - return selectTheme.selectedType != nil + return selectTheme.selectThemeItems.contains(where: { $0.isSelected }) case .setNameAndPlace: return true case let .calendar(calendar): @@ -243,7 +243,7 @@ public struct MakePromise: ReducerProtocol { state.movePastStep() state.updateBackButtonVisibleState() return .none - case .selectTheme(.promiseTypeListItemTapped): + case .selectTheme: return .none case .setNameAndPlace(.filledPromiseName): return .none diff --git a/AppPackage/Sources/MakePromise/SubViews/SelectThemeStore.swift b/AppPackage/Sources/MakePromise/SubViews/SelectThemeStore.swift index 6550671..b15bab8 100644 --- a/AppPackage/Sources/MakePromise/SubViews/SelectThemeStore.swift +++ b/AppPackage/Sources/MakePromise/SubViews/SelectThemeStore.swift @@ -6,46 +6,74 @@ // Copyright © 2022 Team-Planz. All rights reserved. // +import APIClient +import APIClientLive import ComposableArchitecture import Foundation import SwiftUI -public enum PromiseType: String, CaseIterable, Equatable { - case meal = "식사 약속" - case meeting = "미팅 약속" - case travel = "여행 약속" - case etc = "기타 약속" - - var withEmoji: String { - switch self { - case .meal: return rawValue + " 🍚" - case .meeting: return rawValue + " ☕️" - case .travel: return rawValue + " ✈️" - case .etc: return rawValue + " ☺️" - } - } -} - public struct SelectTheme: ReducerProtocol { public struct State: Equatable { - var selectedType: PromiseType? + var selectThemeItems: IdentifiedArrayOf - public init(selectedType: PromiseType? = nil) { - self.selectedType = selectedType + public init( + selectThemeItems: IdentifiedArrayOf = [] + ) { + self.selectThemeItems = selectThemeItems } } public enum Action: Equatable { - case promiseTypeListItemTapped(PromiseType) + case task + case categoriesResponse(TaskResult<[SharedModels.Category]>) + case selectThemeItem(id: Int, action: SelectThemeItem.Action) } + @Dependency(\.apiClient) var apiClient + public var body: some ReducerProtocolOf { Reduce { state, action in switch action { - case let .promiseTypeListItemTapped(type): - state.selectedType = (state.selectedType == type) ? nil : type + case .task: + return .task { + await .categoriesResponse( + TaskResult { + try await apiClient.request( + route: .promising(.fetchCategories), + as: [SharedModels.Category].self + ) + } + ) + } + case let .categoriesResponse(.success(categories)): + state.selectThemeItems = .init( + uniqueElements: categories.map { + SelectThemeItem.State( + id: $0.id, + title: $0.keyword + ) + } + ) + return .none + case .categoriesResponse(.failure): + return .none + case let .selectThemeItem(id: id, action: .tapped): + state.selectThemeItems.map(\.id).forEach { + state.selectThemeItems[id: $0]?.isSelected = ($0 == id) + } return .none } } + .forEach(\.selectThemeItems, action: /Action.selectThemeItem(id:action:)) { + SelectThemeItem() + } + } +} + +extension SharedModels.Category: Equatable { + public static func == (lhs: SharedModels.Category, rhs: SharedModels.Category) -> Bool { + lhs.id == rhs.id + && lhs.keyword == rhs.keyword + && lhs.type == rhs.type } } diff --git a/AppPackage/Sources/MakePromise/SubViews/SelectThemeView.swift b/AppPackage/Sources/MakePromise/SubViews/SelectThemeView.swift index 9467978..6afd751 100644 --- a/AppPackage/Sources/MakePromise/SubViews/SelectThemeView.swift +++ b/AppPackage/Sources/MakePromise/SubViews/SelectThemeView.swift @@ -14,21 +14,61 @@ public struct SelectThemeView: View { var store: StoreOf let listItemEdgePadding = EdgeInsets(top: 6, leading: 20, bottom: 6, trailing: 20) public var body: some View { - VStack { - Spacer() - ForEach(PromiseType.allCases, id: \.self) { - SelectThemeItemView(promiseType: $0, store: store) - .padding(listItemEdgePadding) + WithViewStore(store) { viewStore in + VStack { + Spacer() + ForEachStore( + store.scope( + state: \.selectThemeItems, + action: SelectTheme.Action.selectThemeItem(id:action:) + ) + ) { + SelectThemeItemView(store: $0) + .padding(listItemEdgePadding) + } + Spacer() + } + .background(Color.white) + .task { + viewStore.send(.task) + } + } + } +} + +public struct SelectThemeItem: ReducerProtocol { + public struct State: Equatable, Identifiable { + public let id: Int + let title: String + public var isSelected: Bool + + init( + id: Int, + title: String, + isSelected: Bool = false + ) { + self.id = id + self.title = title + self.isSelected = isSelected + } + } + + public enum Action { + case tapped + } + + public var body: some ReducerProtocolOf { + Reduce { _, action in + switch action { + case .tapped: + return .none } - Spacer() } - .background(Color.white) } } public struct SelectThemeItemView: View { - var promiseType: PromiseType - var store: StoreOf + var store: StoreOf let itemCornerRadius: CGFloat = 16 let checkMarkCircle = "checkmark.circle" let checkmarkCircleFill = "checkmark.circle.fill" @@ -36,27 +76,27 @@ public struct SelectThemeItemView: View { public var body: some View { WithViewStore(self.store) { viewStore in HStack { - Text(promiseType.withEmoji) + Text(viewStore.title) .foregroundColor( - viewStore.selectedType == promiseType ? + viewStore.isSelected ? PDS.COLOR.purple9.scale : PDS.COLOR.gray5.scale ) Spacer() Image(systemName: - viewStore.selectedType == promiseType ? + viewStore.isSelected ? checkmarkCircleFill : checkMarkCircle ) .foregroundColor( - viewStore.selectedType == promiseType ? + viewStore.isSelected ? PDS.COLOR.purple9.scale : PDS.COLOR.gray5.scale ) } .padding(EdgeInsets(top: 16, leading: 20, bottom: 16, trailing: 20)) .background( - viewStore.selectedType == promiseType ? + viewStore.isSelected ? PDS.COLOR.purple9.scale.opacity(0.15) : PDS.COLOR.gray1.scale ) @@ -65,11 +105,11 @@ public struct SelectThemeItemView: View { RoundedRectangle(cornerRadius: itemCornerRadius) .stroke( PDS.COLOR.purple9.scale, - lineWidth: viewStore.selectedType == promiseType ? 0.7 : 0 + lineWidth: viewStore.isSelected ? 0.7 : 0 ) ) .onTapGesture { - viewStore.send(.promiseTypeListItemTapped(promiseType)) + viewStore.send(.tapped) } } } From dfeef40550258054403efa7f53a6e5d840f7dfc5 Mon Sep 17 00:00:00 2001 From: junyng Date: Wed, 12 Apr 2023 01:33:50 +0900 Subject: [PATCH 3/7] =?UTF-8?q?[Pl-29]=20=EC=95=BD=EC=86=8D=20=EC=9E=A5?= =?UTF-8?q?=EC=86=8C=20=EB=AA=85=20=EA=B0=80=EC=A0=B8=EC=98=A4=EA=B8=B0=20?= =?UTF-8?q?API=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MakePromise/MakePromiseStore.swift | 11 ++-- .../SubViews/SetNameAndPlaceStore.swift | 52 +++++++++++++++++-- .../SubViews/SetNameAndPlaceView.swift | 31 ++++++----- 3 files changed, 74 insertions(+), 20 deletions(-) diff --git a/AppPackage/Sources/MakePromise/MakePromiseStore.swift b/AppPackage/Sources/MakePromise/MakePromiseStore.swift index 2fc470c..f536386 100644 --- a/AppPackage/Sources/MakePromise/MakePromiseStore.swift +++ b/AppPackage/Sources/MakePromise/MakePromiseStore.swift @@ -6,6 +6,7 @@ // Copyright © 2022 Team-Planz. All rights reserved. // +import APIClient import CalendarFeature import ComposableArchitecture import Foundation @@ -218,6 +219,12 @@ public struct MakePromise: ReducerProtocol { return .none case .nextButtonTapped: + if case let .selectTheme(selectTheme) = state.currentStep { + state.setNameAndPlace?.id = selectTheme.selectThemeItems + .filter { $0.isSelected } + .first?.id ?? 0 + } + if case let .timeSelection(timeSelection) = state.currentStep, let startTime = timeSelection.startTime, let endTime = timeSelection.endTime @@ -245,9 +252,7 @@ public struct MakePromise: ReducerProtocol { return .none case .selectTheme: return .none - case .setNameAndPlace(.filledPromiseName): - return .none - case .setNameAndPlace(.filledPromisePlace): + case .setNameAndPlace: return .none case .calendar: return .none diff --git a/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceStore.swift b/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceStore.swift index a28063b..5dd291e 100644 --- a/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceStore.swift +++ b/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceStore.swift @@ -6,14 +6,32 @@ // Copyright © 2022 Team-Planz. All rights reserved. // +import APIClient +import APIClientLive import ComposableArchitecture import Foundation public struct SetNameAndPlace: ReducerProtocol { public struct State: Equatable { - var maxCharacter = 10 - var promiseName: String = "" - var promisePlace: String = "" + public var id: Int + var maxCharacter: Int + var promiseNamePlaceholder: String + var promiseName: String + var promisePlace: String + + public init( + id: Int = .init(), + maxCharacter: Int = 10, + promiseNamePlaceholder: String = .init(), + promiseName: String = .init(), + promisePlace: String = .init() + ) { + self.id = id + self.maxCharacter = maxCharacter + self.promiseNamePlaceholder = promiseNamePlaceholder + self.promiseName = promiseName + self.promisePlace = promisePlace + } var numberOfCharacterInNameText: Int { if promiseName.count <= maxCharacter { @@ -42,18 +60,36 @@ public struct SetNameAndPlace: ReducerProtocol { var isNextButtonEnable: Bool { (numberOfCharacterInNameText > 0 && !shouldShowNameTextCountWarning) && (numberOfCharacterInPlaceText > 0 && !shouldShowPlaceTextCountWarning) } - - public init() {} } public enum Action: Equatable { + case task + case placeHintResponse(TaskResult) case filledPromiseName(String) case filledPromisePlace(String) } + @Dependency(\.apiClient) var apiClient + public var body: some ReducerProtocolOf { Reduce { state, action in switch action { + case .task: + return .task { [id = state.id] in + await .placeHintResponse( + TaskResult { + try await apiClient.request( + route: .promising(.randomName(id)), + as: SharedModels.CategoryName.self + ) + } + ) + } + case let .placeHintResponse(.success(placeHint)): + state.promiseNamePlaceholder = placeHint.name + return .none + case .placeHintResponse(.failure): + return .none case let .filledPromiseName(name): state.promiseName = name return .none @@ -64,3 +100,9 @@ public struct SetNameAndPlace: ReducerProtocol { } } } + +extension SharedModels.CategoryName: Equatable { + public static func == (lhs: SharedModels.CategoryName, rhs: SharedModels.CategoryName) -> Bool { + lhs.name == rhs.name + } +} diff --git a/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceView.swift b/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceView.swift index 76a8c96..d27fcd1 100644 --- a/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceView.swift +++ b/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceView.swift @@ -35,17 +35,22 @@ public struct SetNameAndPlaceView: View { } public var body: some View { - VStack { - Spacer() - VStack(spacing: 24) { - TextFieldWithTitleView( - type: .name, store: self.store - ) - TextFieldWithTitleView( - type: .place, store: self.store - ) + WithViewStore(store) { viewStore in + VStack { + Spacer() + VStack(spacing: 24) { + TextFieldWithTitleView( + type: .name, store: self.store + ) + TextFieldWithTitleView( + type: .place, store: self.store + ) + } + Spacer() + } + .task { + viewStore.send(.task) } - Spacer() } } } @@ -67,6 +72,7 @@ public struct TextFieldWithTitleView: View { struct ViewState: Equatable { let showWarningMessage: Bool + let placeholder: String let textFieldText: String let numberOfCharacter: Int let maxNumberOfCharacter: Int @@ -77,12 +83,13 @@ public struct TextFieldWithTitleView: View { showWarningMessage = state.shouldShowNameTextCountWarning textFieldText = state.promiseName numberOfCharacter = state.numberOfCharacterInNameText + placeholder = state.promiseNamePlaceholder case .place: showWarningMessage = state.shouldShowPlaceTextCountWarning textFieldText = state.promisePlace numberOfCharacter = state.numberOfCharacterInPlaceText + placeholder = .init() } - maxNumberOfCharacter = state.maxCharacter } } @@ -97,7 +104,7 @@ public struct TextFieldWithTitleView: View { Spacer() } TextField( - type.placeHolder, + viewStore.placeholder, text: viewStore.binding( get: { $0.textFieldText }, send: { type == .name ? .filledPromiseName($0) : .filledPromisePlace($0) } From eeabbcf9c422fd7ca5de5dd5b4c67bf38bd7afa8 Mon Sep 17 00:00:00 2001 From: junyng Date: Sat, 15 Apr 2023 16:36:51 +0900 Subject: [PATCH 4/7] =?UTF-8?q?[Pl-29]=20=EC=95=BD=EC=86=8D=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=EB=8C=80=20=EC=84=A4=EC=A0=95=20API=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MakePromise/MakePromiseStore.swift | 138 +++++++++++++++--- .../Sources/MakePromise/MakePromiseView.swift | 6 + 2 files changed, 123 insertions(+), 21 deletions(-) diff --git a/AppPackage/Sources/MakePromise/MakePromiseStore.swift b/AppPackage/Sources/MakePromise/MakePromiseStore.swift index f536386..40d7762 100644 --- a/AppPackage/Sources/MakePromise/MakePromiseStore.swift +++ b/AppPackage/Sources/MakePromise/MakePromiseStore.swift @@ -14,6 +14,7 @@ import TimeTableFeature public struct MakePromise: ReducerProtocol { public struct State: Equatable { + @PresentationState var alert: AlertState? var shouldShowBackButton: Bool var steps: [Step] var currentStep: Step? { @@ -127,15 +128,17 @@ public struct MakePromise: ReducerProtocol { } public init( + alert: AlertState? = nil, shouldShowBackButton: Bool = false, steps: [Step] = [ .selectTheme(.init()), .setNameAndPlace(.init()), - .timeSelection(.init(timeRange: .init(start: 9, end: 23))), .calendar(.init()), + .timeSelection(.init(timeRange: .init(start: 9, end: 23))), .timeTable(.mock) ] ) { + self.alert = alert self.shouldShowBackButton = shouldShowBackButton self.steps = steps } @@ -200,16 +203,26 @@ public struct MakePromise: ReducerProtocol { mutating func fetchCurrentStep() {} } + @Dependency(\.apiClient) var apiClient + public enum Action: Equatable { case dismiss case nextButtonTapped case backButtonTapped + case temporaryPromisingResponse(TaskResult) + case selectTheme(SelectTheme.Action) case setNameAndPlace(SetNameAndPlace.Action) case calendar(CalendarCore.Action) case timeSelection(TimeSelection.Action) case timeTable(TimeTable.Action) + case alert(PresentationAction) + } + + public enum AlertAction: Equatable { + case confirmButtonTapped + case dismiss } public var body: some ReducerProtocolOf { @@ -241,12 +254,25 @@ public struct MakePromise: ReducerProtocol { state.timeTable?.days = days } + if case .timeSelection = state.currentStep { + state.alert = .init( + title: .init(Resource.string.alert), + message: .init(Resource.string.warning), + primaryButton: .cancel(.init(Resource.string.cancel), action: .send(.dismiss)), + secondaryButton: .default(.init(Resource.string.confirm), action: .send(.confirmButtonTapped)) + ) + return .none + } + if state.isNextButtonEnable { state.moveNextStep() state.updateBackButtonVisibleState() } return .none case .backButtonTapped: + if case .timeTable = state.currentStep { + return .send(.dismiss) + } state.movePastStep() state.updateBackButtonVisibleState() return .none @@ -260,39 +286,109 @@ public struct MakePromise: ReducerProtocol { return .none case .timeTable: return .none + case .alert(.presented(.confirmButtonTapped)): + guard case let .timeSelection(timeSelection) = state.currentStep else { + return .none + } + guard let categoryID = state.selectTheme?.selectThemeItems + .filter({ $0.isSelected }).first?.id + else { + return .none + } + guard let startTime: Date = .today(hour: timeSelection.startTime), + let endTime: Date = .today(hour: timeSelection.endTime) + else { + return .none + } + state.alert = nil + return .task { [state = state] in + await .temporaryPromisingResponse( + TaskResult { + try await apiClient.request( + route: .promising( + .create( + .init( + name: state.setNameAndPlace?.promiseName ?? "", + minTime: dateFormatter.string(from: startTime), + maxTime: dateFormatter.string(from: endTime), + categoryID: categoryID, + availableDates: state.calendar?.selectedDates.map { dateFormatter.string(from: $0) } ?? [], + place: state.setNameAndPlace?.promisePlace ?? "" + ) + ) + ), + as: SharedModels.CreatePromisingResponse.self + ) + } + ) + } + case let .temporaryPromisingResponse(.success(response)): + state.moveNextStep() + state.updateBackButtonVisibleState() + return .none + case .temporaryPromisingResponse(.failure): + return .none + case .alert: + return .none } } - .ifLet( - \.selectTheme, - action: /MakePromise.Action.selectTheme - ) { + .ifLet(\.selectTheme, action: /MakePromise.Action.selectTheme) { SelectTheme() } - .ifLet( - \.setNameAndPlace, - action: /MakePromise.Action.setNameAndPlace - ) { + .ifLet(\.setNameAndPlace, action: /MakePromise.Action.setNameAndPlace) { SetNameAndPlace() } - .ifLet( - \.calendar, - action: /MakePromise.Action.calendar - ) { + .ifLet(\.calendar, action: /MakePromise.Action.calendar) { CalendarCore() } - .ifLet( - \.timeSelection, - action: /MakePromise.Action.timeSelection - ) { + .ifLet(\.timeSelection, action: /MakePromise.Action.timeSelection) { TimeSelection() } - .ifLet( - \.timeTable, - action: /MakePromise.Action.timeTable - ) { + .ifLet(\.timeTable, action: /MakePromise.Action.timeTable) { TimeTable() } } public init() {} } + +private enum Resource { + enum string { + static let alert = "알림" + static let warning = "다음을 누르시면 이전 단계로 돌아갈 수 없습니다. 진행하시겠습니까?" + static let cancel = "취소" + static let confirm = "확인" + } +} + +extension SharedModels.CreatePromisingResponse: Equatable { + public static func == (lhs: SharedModels.CreatePromisingResponse, rhs: SharedModels.CreatePromisingResponse) -> Bool { + lhs.id == rhs.id + } +} + +private var dateFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" + return dateFormatter +}() + +private extension Date { + static func today( + hour: Int? = nil, + minute: Int? = nil, + second: Int? = nil + ) -> Self? { + let today: Date = .now + let calendar = Calendar.current + let dateComponents = DateComponents( + year: calendar.component(.year, from: today), + month: calendar.component(.month, from: today), + day: calendar.component(.day, from: today), + hour: hour, + minute: minute, + second: second + ) + return calendar.date(from: dateComponents) + } +} diff --git a/AppPackage/Sources/MakePromise/MakePromiseView.swift b/AppPackage/Sources/MakePromise/MakePromiseView.swift index 78ef033..e373877 100644 --- a/AppPackage/Sources/MakePromise/MakePromiseView.swift +++ b/AppPackage/Sources/MakePromise/MakePromiseView.swift @@ -69,5 +69,11 @@ struct PromiseContentView: View { } .frame(alignment: .top) .navigationBarBackButtonHidden() + .alert( + store: self.store.scope( + state: \.$alert, + action: MakePromise.Action.alert + ) + ) } } From 43fd31a1028520d966344e81563fa32ede370e50 Mon Sep 17 00:00:00 2001 From: junyng Date: Sun, 16 Apr 2023 17:57:35 +0900 Subject: [PATCH 5/7] =?UTF-8?q?[Pl-29]=20=EC=95=BD=EC=86=8D=20=EC=8B=9C?= =?UTF-8?q?=EA=B0=84=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20API=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AppPackage/Package.swift | 3 +- .../MakePromise/MakePromiseStore.swift | 76 ++++++++++++++----- .../TimeTableFeature/TimeTableView.swift | 64 +++++++++++++++- 3 files changed, 119 insertions(+), 24 deletions(-) diff --git a/AppPackage/Package.swift b/AppPackage/Package.swift index db555f0..4e89945 100644 --- a/AppPackage/Package.swift +++ b/AppPackage/Package.swift @@ -78,7 +78,8 @@ let package = Package( .target( name: "TimeTableFeature", dependencies: [ - .product(name: "ComposableArchitecture", package: "swift-composable-architecture") + .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), + .product(name: "APIClient", package: "Planz-iOS-APIClient") ] ), .target( diff --git a/AppPackage/Sources/MakePromise/MakePromiseStore.swift b/AppPackage/Sources/MakePromise/MakePromiseStore.swift index 40d7762..6fd4dc2 100644 --- a/AppPackage/Sources/MakePromise/MakePromiseStore.swift +++ b/AppPackage/Sources/MakePromise/MakePromiseStore.swift @@ -135,7 +135,7 @@ public struct MakePromise: ReducerProtocol { .setNameAndPlace(.init()), .calendar(.init()), .timeSelection(.init(timeRange: .init(start: 9, end: 23))), - .timeTable(.mock) + .timeTable(.init()) ] ) { self.alert = alert @@ -209,9 +209,8 @@ public struct MakePromise: ReducerProtocol { case dismiss case nextButtonTapped case backButtonTapped - case temporaryPromisingResponse(TaskResult) - + case updatePromiseTimeRespose(TaskResult) case selectTheme(SelectTheme.Action) case setNameAndPlace(SetNameAndPlace.Action) case calendar(CalendarCore.Action) @@ -237,23 +236,6 @@ public struct MakePromise: ReducerProtocol { .filter { $0.isSelected } .first?.id ?? 0 } - - if case let .timeSelection(timeSelection) = state.currentStep, - let startTime = timeSelection.startTime, - let endTime = timeSelection.endTime - { - state.timeTable?.startTime = TimeInterval(startTime * 3600) - state.timeTable?.endTime = TimeInterval(endTime * 3600) - state.timeTable?.reload() - } - - if case let .calendar(calendarState) = state.currentStep { - let days: [TimeTable.State.Day] = calendarState.selectedDates.map { - .init(date: $0) - } - state.timeTable?.days = days - } - if case .timeSelection = state.currentStep { state.alert = .init( title: .init(Resource.string.alert), @@ -264,6 +246,35 @@ public struct MakePromise: ReducerProtocol { return .none } + if case let .timeTable(timeTable) = state.currentStep { + guard let sessionID = timeTable.sessionID else { + return .none + } + return .task { [state = state] in + await .updatePromiseTimeRespose( + TaskResult { + try await apiClient.request( + route: .promising( + .respondTimeByHost( + sessionID, + .init( + unit: 0.5, + timeTable: state.timeTable?.days.enumerated().map { index, day in + .init( + date: dateFormatter.string(from: day.date), + times: state.timeTable?.timeCells[index].map { $0 == .selected } ?? [] + ) + } ?? [] + ) + ) + ), + as: SharedModels.UpdatePromiseTimeResponse.self + ) + } + ) + } + } + if state.isNextButtonEnable { state.moveNextStep() state.updateBackButtonVisibleState() @@ -312,7 +323,8 @@ public struct MakePromise: ReducerProtocol { minTime: dateFormatter.string(from: startTime), maxTime: dateFormatter.string(from: endTime), categoryID: categoryID, - availableDates: state.calendar?.selectedDates.map { dateFormatter.string(from: $0) } ?? [], + availableDates: state.calendar?.selectedDates + .map { dateFormatter.string(from: $0) } ?? [], place: state.setNameAndPlace?.promisePlace ?? "" ) ) @@ -323,11 +335,14 @@ public struct MakePromise: ReducerProtocol { ) } case let .temporaryPromisingResponse(.success(response)): + state.timeTable?.sessionID = response.id state.moveNextStep() state.updateBackButtonVisibleState() return .none case .temporaryPromisingResponse(.failure): return .none + case .updatePromiseTimeRespose: + return .none case .alert: return .none } @@ -367,6 +382,25 @@ extension SharedModels.CreatePromisingResponse: Equatable { } } +extension SharedModels.PromisingTime: Equatable { + public static func == ( + lhs: SharedModels.PromisingTime, + rhs: SharedModels.PromisingTime + ) -> Bool { + lhs.unit == rhs.unit + && lhs.timeTable.count == rhs.timeTable.count + } +} + +extension SharedModels.UpdatePromiseTimeResponse: Equatable { + public static func == ( + lhs: SharedModels.UpdatePromiseTimeResponse, + rhs: SharedModels.UpdatePromiseTimeResponse + ) -> Bool { + lhs.promiseID == rhs.promiseID + } +} + private var dateFormatter: DateFormatter = { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" diff --git a/AppPackage/Sources/TimeTableFeature/TimeTableView.swift b/AppPackage/Sources/TimeTableFeature/TimeTableView.swift index f87009f..46237b7 100644 --- a/AppPackage/Sources/TimeTableFeature/TimeTableView.swift +++ b/AppPackage/Sources/TimeTableFeature/TimeTableView.swift @@ -6,11 +6,14 @@ // Copyright © 2022 Team-Planz. All rights reserved. // +import APIClient +import APIClientLive import ComposableArchitecture import SwiftUI public struct TimeTable: ReducerProtocol { public struct State: Equatable { + public var sessionID: String? public var days: [Day] public var startTime: TimeInterval public var endTime: TimeInterval @@ -21,7 +24,7 @@ public struct TimeTable: ReducerProtocol { public struct Day: Hashable, Equatable, Identifiable { public let id: Int - let date: Date + public let date: Date public init(date: Date) { id = date.hashValue @@ -61,7 +64,6 @@ public struct TimeTable: ReducerProtocol { self.endTime = endTime self.timeInterval = timeInterval self.timeMarkerInterval = timeMarkerInterval - reload() } public var isTimeSelected: Bool { @@ -95,12 +97,48 @@ public struct TimeTable: ReducerProtocol { } public enum Action: Equatable { + case task + case fetchSessionResponse(TaskResult) case timeCellTapped(row: Int, column: Int) } + @Dependency(\.apiClient) var apiClient + public var body: some ReducerProtocolOf { Reduce { state, action in switch action { + case .task: + guard let id = state.sessionID else { + return .none + } + return .task { + await .fetchSessionResponse( + TaskResult { + try await apiClient.request( + route: .promising(.fetchSession(id)), + as: SharedModels.PromisingSessionResponse.self + ) + } + ) + } + case let .fetchSessionResponse(.success(response)): + guard let startTime: Date = dateFormatter.date(from: response.minTime), + let endTime: Date = dateFormatter.date(from: response.maxTime) + else { + return .none + } + state.startTime = TimeInterval(Calendar.current.component(.hour, from: startTime) * 3600) + state.endTime = TimeInterval(Calendar.current.component(.hour, from: endTime) * 3600) + state.days = response.availableDates + .compactMap { + dateFormatter.date(from: $0) + } + .map { .init(date: $0) } + state.timeInterval = response.unit * .hour + state.reload() + return .none + case .fetchSessionResponse(.failure): + return .none case let .timeCellTapped(row, column): guard row >= 0, row < state.timeRanges.count else { return .none } guard column >= 0, column < state.days.count else { return .none } @@ -158,6 +196,9 @@ public struct TimeTableView: View { gradient, alignment: .topLeading ) } + .task { + viewStore.send(.task) + } } } @@ -335,3 +376,22 @@ struct TimeTableView_Previews: PreviewProvider { ) } } + +private var dateFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" + return dateFormatter +}() + +extension SharedModels.PromisingSessionResponse: Equatable { + public static func == ( + lhs: SharedModels.PromisingSessionResponse, + rhs: SharedModels.PromisingSessionResponse + ) -> Bool { + lhs.minTime == rhs.minTime + && lhs.maxTime == rhs.maxTime + && lhs.totalCount == rhs.totalCount + && lhs.unit == rhs.unit + && lhs.availableDates == rhs.availableDates + } +} From a1882663e5f2ca9379ae2dea1d06dd8a4adcaeb5 Mon Sep 17 00:00:00 2001 From: junyng Date: Mon, 17 Apr 2023 00:15:52 +0900 Subject: [PATCH 6/7] =?UTF-8?q?[Pl-29]=20Entity=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9,=20=EB=AA=A8=EB=8D=B8=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=8D=BC=ED=8B=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AppPackage/Package.swift | 3 +- AppPackage/Sources/Entity/Model.swift | 70 +++++++++++-------- .../MakePromise/MakePromiseStore.swift | 40 +++-------- .../SubViews/SelectThemeStore.swift | 13 +--- .../SubViews/SetNameAndPlaceStore.swift | 11 +-- .../TimeTableFeature/TimeTableView.swift | 18 +---- 6 files changed, 58 insertions(+), 97 deletions(-) diff --git a/AppPackage/Package.swift b/AppPackage/Package.swift index 4e89945..89b16d9 100644 --- a/AppPackage/Package.swift +++ b/AppPackage/Package.swift @@ -79,7 +79,8 @@ let package = Package( name: "TimeTableFeature", dependencies: [ .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), - .product(name: "APIClient", package: "Planz-iOS-APIClient") + "APIClient", + "APIClientLive" ] ), .target( diff --git a/AppPackage/Sources/Entity/Model.swift b/AppPackage/Sources/Entity/Model.swift index 1b6279a..e6fa12b 100644 --- a/AppPackage/Sources/Entity/Model.swift +++ b/AppPackage/Sources/Entity/Model.swift @@ -30,31 +30,31 @@ public struct UpdateUsernameRequest: Encodable, Equatable { public struct CreatePromisingRequest: Encodable, Equatable { private enum CodingKeys: String, CodingKey { case name = "promisingName" - case startDate = "minTime" - case endDate = "maxTime" + case minTime + case maxTime case categoryID = "categoryId" case availableDates case place = "placeName" } public let name: String - public let startDate: Date - public let endDate: Date + public let minTime: String + public let maxTime: String public let categoryID: Int - public let availableDates: [Date] + public let availableDates: [String] public let place: String public init( name: String, - startDate: Date, - endDate: Date, + minTime: String, + maxTime: String, categoryID: Int, - availableDates: [Date], + availableDates: [String], place: String ) { self.name = name - self.startDate = startDate - self.endDate = endDate + self.minTime = minTime + self.maxTime = maxTime self.categoryID = categoryID self.availableDates = availableDates self.place = place @@ -74,29 +74,21 @@ public struct CreatePromisingResponse: Decodable, Equatable { } public struct PromisingSessionResponse: Decodable, Equatable { - private enum CodingKeys: String, CodingKey { - case startDate = "minTime" - case endDate = "maxTime" - case totalCount - case unit - case availableDates - } - - public let startDate: Date - public let endDate: Date + public let minTime: String + public let maxTime: String public let totalCount: Int - public let unit: Int - public let availableDates: [Date] + public let unit: Double + public let availableDates: [String] public init( - startDate: Date, - endDate: Date, + minTime: String, + maxTime: String, totalCount: Int, - unit: Int, - availableDates: [Date] + unit: Double, + availableDates: [String] ) { - self.startDate = startDate - self.endDate = endDate + self.minTime = minTime + self.maxTime = maxTime self.totalCount = totalCount self.unit = unit self.availableDates = availableDates @@ -172,12 +164,28 @@ public struct PromisingSession: Codable, Equatable { } public struct PromisingTime: Codable, Equatable { - public let unit: Int - public let timeTable: TimeTable + public let unit: Double + public let timeTable: [TimeTable] public struct TimeTable: Codable, Equatable { - public let date: Date + public let date: String public let times: [Bool] + + public init( + date: String, + times: [Bool] + ) { + self.date = date + self.times = times + } + } + + public init( + unit: Double, + timeTable: [TimeTable] + ) { + self.unit = unit + self.timeTable = timeTable } } diff --git a/AppPackage/Sources/MakePromise/MakePromiseStore.swift b/AppPackage/Sources/MakePromise/MakePromiseStore.swift index 6fd4dc2..6ef0124 100644 --- a/AppPackage/Sources/MakePromise/MakePromiseStore.swift +++ b/AppPackage/Sources/MakePromise/MakePromiseStore.swift @@ -9,6 +9,7 @@ import APIClient import CalendarFeature import ComposableArchitecture +import Entity import Foundation import TimeTableFeature @@ -104,7 +105,7 @@ public struct MakePromise: ReducerProtocol { } } - var timeTable: TimeTable.State? { + var timeTable: TimeTableFeature.TimeTable.State? { get { steps.compactMap { step in guard case let .timeTable(state) = step else { @@ -148,7 +149,7 @@ public struct MakePromise: ReducerProtocol { case setNameAndPlace(SetNameAndPlace.State) case calendar(CalendarCore.State) case timeSelection(TimeSelection.State) - case timeTable(TimeTable.State) + case timeTable(TimeTableFeature.TimeTable.State) } var isNextButtonEnable: Bool { @@ -209,13 +210,13 @@ public struct MakePromise: ReducerProtocol { case dismiss case nextButtonTapped case backButtonTapped - case temporaryPromisingResponse(TaskResult) - case updatePromiseTimeRespose(TaskResult) + case temporaryPromisingResponse(TaskResult) + case updatePromiseTimeRespose(TaskResult) case selectTheme(SelectTheme.Action) case setNameAndPlace(SetNameAndPlace.Action) case calendar(CalendarCore.Action) case timeSelection(TimeSelection.Action) - case timeTable(TimeTable.Action) + case timeTable(TimeTableFeature.TimeTable.Action) case alert(PresentationAction) } @@ -268,7 +269,7 @@ public struct MakePromise: ReducerProtocol { ) ) ), - as: SharedModels.UpdatePromiseTimeResponse.self + as: UpdatePromiseTimeResponse.self ) } ) @@ -329,7 +330,7 @@ public struct MakePromise: ReducerProtocol { ) ) ), - as: SharedModels.CreatePromisingResponse.self + as: CreatePromisingResponse.self ) } ) @@ -376,31 +377,6 @@ private enum Resource { } } -extension SharedModels.CreatePromisingResponse: Equatable { - public static func == (lhs: SharedModels.CreatePromisingResponse, rhs: SharedModels.CreatePromisingResponse) -> Bool { - lhs.id == rhs.id - } -} - -extension SharedModels.PromisingTime: Equatable { - public static func == ( - lhs: SharedModels.PromisingTime, - rhs: SharedModels.PromisingTime - ) -> Bool { - lhs.unit == rhs.unit - && lhs.timeTable.count == rhs.timeTable.count - } -} - -extension SharedModels.UpdatePromiseTimeResponse: Equatable { - public static func == ( - lhs: SharedModels.UpdatePromiseTimeResponse, - rhs: SharedModels.UpdatePromiseTimeResponse - ) -> Bool { - lhs.promiseID == rhs.promiseID - } -} - private var dateFormatter: DateFormatter = { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" diff --git a/AppPackage/Sources/MakePromise/SubViews/SelectThemeStore.swift b/AppPackage/Sources/MakePromise/SubViews/SelectThemeStore.swift index b15bab8..a7b82e3 100644 --- a/AppPackage/Sources/MakePromise/SubViews/SelectThemeStore.swift +++ b/AppPackage/Sources/MakePromise/SubViews/SelectThemeStore.swift @@ -9,6 +9,7 @@ import APIClient import APIClientLive import ComposableArchitecture +import Entity import Foundation import SwiftUI @@ -25,7 +26,7 @@ public struct SelectTheme: ReducerProtocol { public enum Action: Equatable { case task - case categoriesResponse(TaskResult<[SharedModels.Category]>) + case categoriesResponse(TaskResult<[Entity.Category]>) case selectThemeItem(id: Int, action: SelectThemeItem.Action) } @@ -40,7 +41,7 @@ public struct SelectTheme: ReducerProtocol { TaskResult { try await apiClient.request( route: .promising(.fetchCategories), - as: [SharedModels.Category].self + as: [Entity.Category].self ) } ) @@ -69,11 +70,3 @@ public struct SelectTheme: ReducerProtocol { } } } - -extension SharedModels.Category: Equatable { - public static func == (lhs: SharedModels.Category, rhs: SharedModels.Category) -> Bool { - lhs.id == rhs.id - && lhs.keyword == rhs.keyword - && lhs.type == rhs.type - } -} diff --git a/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceStore.swift b/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceStore.swift index 5dd291e..69c4c86 100644 --- a/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceStore.swift +++ b/AppPackage/Sources/MakePromise/SubViews/SetNameAndPlaceStore.swift @@ -9,6 +9,7 @@ import APIClient import APIClientLive import ComposableArchitecture +import Entity import Foundation public struct SetNameAndPlace: ReducerProtocol { @@ -64,7 +65,7 @@ public struct SetNameAndPlace: ReducerProtocol { public enum Action: Equatable { case task - case placeHintResponse(TaskResult) + case placeHintResponse(TaskResult) case filledPromiseName(String) case filledPromisePlace(String) } @@ -80,7 +81,7 @@ public struct SetNameAndPlace: ReducerProtocol { TaskResult { try await apiClient.request( route: .promising(.randomName(id)), - as: SharedModels.CategoryName.self + as: CategoryName.self ) } ) @@ -100,9 +101,3 @@ public struct SetNameAndPlace: ReducerProtocol { } } } - -extension SharedModels.CategoryName: Equatable { - public static func == (lhs: SharedModels.CategoryName, rhs: SharedModels.CategoryName) -> Bool { - lhs.name == rhs.name - } -} diff --git a/AppPackage/Sources/TimeTableFeature/TimeTableView.swift b/AppPackage/Sources/TimeTableFeature/TimeTableView.swift index 46237b7..d09049c 100644 --- a/AppPackage/Sources/TimeTableFeature/TimeTableView.swift +++ b/AppPackage/Sources/TimeTableFeature/TimeTableView.swift @@ -9,6 +9,7 @@ import APIClient import APIClientLive import ComposableArchitecture +import Entity import SwiftUI public struct TimeTable: ReducerProtocol { @@ -98,7 +99,7 @@ public struct TimeTable: ReducerProtocol { public enum Action: Equatable { case task - case fetchSessionResponse(TaskResult) + case fetchSessionResponse(TaskResult) case timeCellTapped(row: Int, column: Int) } @@ -116,7 +117,7 @@ public struct TimeTable: ReducerProtocol { TaskResult { try await apiClient.request( route: .promising(.fetchSession(id)), - as: SharedModels.PromisingSessionResponse.self + as: PromisingSessionResponse.self ) } ) @@ -382,16 +383,3 @@ private var dateFormatter: DateFormatter = { dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" return dateFormatter }() - -extension SharedModels.PromisingSessionResponse: Equatable { - public static func == ( - lhs: SharedModels.PromisingSessionResponse, - rhs: SharedModels.PromisingSessionResponse - ) -> Bool { - lhs.minTime == rhs.minTime - && lhs.maxTime == rhs.maxTime - && lhs.totalCount == rhs.totalCount - && lhs.unit == rhs.unit - && lhs.availableDates == rhs.availableDates - } -} From 15f0a7721689a1d09ec81dbf5233eddb74933654 Mon Sep 17 00:00:00 2001 From: junyng Date: Mon, 17 Apr 2023 01:17:59 +0900 Subject: [PATCH 7/7] =?UTF-8?q?[Pl-29]=20=ED=83=80=EC=9E=84=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=8B=9C?= =?UTF-8?q?=EC=8A=A4=ED=85=9C=20=EC=BB=AC=EB=9F=AC=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AppPackage/Package.swift | 3 +- .../TimeTableFeature/TimeTableView.swift | 36 ++++++++----------- 2 files changed, 16 insertions(+), 23 deletions(-) diff --git a/AppPackage/Package.swift b/AppPackage/Package.swift index 89b16d9..9fd1a9c 100644 --- a/AppPackage/Package.swift +++ b/AppPackage/Package.swift @@ -80,7 +80,8 @@ let package = Package( dependencies: [ .product(name: "ComposableArchitecture", package: "swift-composable-architecture"), "APIClient", - "APIClientLive" + "APIClientLive", + "DesignSystem" ] ), .target( diff --git a/AppPackage/Sources/TimeTableFeature/TimeTableView.swift b/AppPackage/Sources/TimeTableFeature/TimeTableView.swift index d09049c..42e6536 100644 --- a/AppPackage/Sources/TimeTableFeature/TimeTableView.swift +++ b/AppPackage/Sources/TimeTableFeature/TimeTableView.swift @@ -11,6 +11,7 @@ import APIClientLive import ComposableArchitecture import Entity import SwiftUI +import DesignSystem public struct TimeTable: ReducerProtocol { public struct State: Equatable { @@ -176,11 +177,11 @@ public struct TimeTableView: View { width: dayCellWidth * CGFloat(viewStore.days.count), height: LayoutConstant.headerHeight ) - .background(Resource.PlanzColor.white200) + .background(PDS.COLOR.white2.scale) Divider() .frame(height: LayoutConstant.lineWidth) - .overlay(Resource.PlanzColor.gray200) + .overlay(PDS.COLOR.gray4.scale) if viewStore.isGridLoadable { grid .frame( @@ -190,7 +191,7 @@ public struct TimeTableView: View { } Divider() .frame(height: LayoutConstant.lineWidth) - .overlay(Resource.PlanzColor.gray200) + .overlay(PDS.COLOR.gray4.scale) } } .overlay( @@ -210,11 +211,11 @@ public struct TimeTableView: View { VStack(alignment: .center) { Text(day.formatted(with: .dayOnly)) .font(.system(size: 12)) - .foregroundColor(Resource.PlanzColor.gray800) + .foregroundColor(PDS.COLOR.gray8.scale) Text(day.formatted(with: .monthAndDay)) .font(.system(size: 14)) - .foregroundColor(Resource.PlanzColor.purple900) + .foregroundColor(PDS.COLOR.purple9.scale) } .frame( width: proxy.size.width / CGFloat(viewStore.days.count), @@ -225,7 +226,7 @@ public struct TimeTableView: View { .stroke(Resource.PlanzColor.dayCellBorder) .background( RoundedRectangle(cornerRadius: LayoutConstant.dayCellCornerRadius) - .fill(Resource.PlanzColor.dayCellBackground) + .fill(PDS.COLOR.purple1.scale) ) .frame( width: LayoutConstant.dayCellSize.width, @@ -245,7 +246,7 @@ public struct TimeTableView: View { if timeRange.isStartTimeVisible { Text(timeRange.startTime.formatted(with: .hhmm)) .font(.system(size: 12)) - .foregroundColor(Resource.PlanzColor.gray900) + .foregroundColor(PDS.COLOR.cGray2.scale) .minimumScaleFactor(0.5) } else { Spacer() @@ -255,12 +256,12 @@ public struct TimeTableView: View { } } .frame(width: LayoutConstant.timelineWidth) - .background(Resource.PlanzColor.white200) + .background(PDS.COLOR.white2.scale) .overlay( HStack { Divider() .frame(width: LayoutConstant.lineWidth) - .overlay(Resource.PlanzColor.gray200) + .overlay(PDS.COLOR.gray4.scale) }, alignment: .trailing ) @@ -287,17 +288,17 @@ public struct TimeTableView: View { Rectangle() .frame(width: (proxy.size.width - LayoutConstant.timelineWidth) / CGFloat(columns)) .foregroundColor(viewStore.timeCells[column][row] == .selected - ? Resource.PlanzColor.purple900 : Resource.PlanzColor.white200) + ? PDS.COLOR.purple9.scale : PDS.COLOR.white2.scale) .clipShape(Rectangle()) .overlay( VerticalLine() - .stroke(Resource.PlanzColor.timeCellBorder) + .stroke(PDS.COLOR.gray3.scale) .frame(width: LayoutConstant.lineWidth), alignment: .trailing ) .overlay( HorizontalLine() - .stroke(Resource.PlanzColor.timeCellBorder, + .stroke(PDS.COLOR.gray3.scale, style: row % 2 == 0 ? .init(dash: [2]) : .init()) .frame(height: LayoutConstant.lineWidth), alignment: .bottom @@ -315,7 +316,7 @@ public struct TimeTableView: View { var gradient: some View { LinearGradient( - colors: [Resource.PlanzColor.white200.opacity(0.1), Resource.PlanzColor.white200], + colors: [PDS.COLOR.white2.scale.opacity(0.1), PDS.COLOR.white2.scale], startPoint: .trailing, endPoint: .leading ) @@ -339,16 +340,7 @@ private enum LayoutConstant { private enum Resource { enum PlanzColor { - static let gray200: Color = .init(red: 205 / 255, green: 210 / 255, blue: 217 / 255) - static let gray500: Color = .init(red: 156 / 255, green: 163 / 255, blue: 173 / 255) - static let gray800: Color = .init(red: 2 / 255, green: 2 / 255, blue: 2 / 255) - static let gray900: Color = .init(red: 91 / 255, green: 104 / 255, blue: 122 / 255) - static let white200: Color = .init(red: 251 / 255, green: 251 / 255, blue: 251 / 255) - static let purple100: Color = .init(red: 251 / 255, green: 251 / 255, blue: 251 / 255) - static let purple900: Color = .init(red: 102 / 255, green: 113 / 255, blue: 246 / 255) - static let dayCellBackground: Color = .init(red: 232 / 255, green: 234 / 255, blue: 254 / 255) static let dayCellBorder: Color = .init(red: 206 / 255, green: 210 / 255, blue: 252 / 255) - static let timeCellBorder: Color = .init(red: 232 / 255, green: 234 / 255, blue: 237 / 255) } }