Skip to content

Commit 9dcd3c1

Browse files
committed
refactor: sheet용 State 분리
1 parent e6bc64b commit 9dcd3c1

4 files changed

Lines changed: 109 additions & 27 deletions

File tree

Application/DevLogPresentation/Sources/Home/Editor/TodoEditorFeature.swift

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,14 @@ struct TodoEditorFeature {
1515
@ObservableState
1616
struct State: Equatable {
1717
@Presents var alert: AlertState<Never>?
18+
@Presents var sheet: SheetState?
1819
var isCompleted: Bool = false
1920
var completedAt: Date?
2021
var isPinned: Bool = false
21-
var selectedTodoId: TodoIdItem?
2222
var title: String = ""
2323
var content: String = ""
2424
var referenceItems: [Int: TodoReferenceItem] = [:]
2525
var dueDate: Date?
26-
var showInfo: Bool = false
2726
var loading = LoadingFeature.State()
2827
var tags: OrderedSet<String> = []
2928
var tagText: String = ""
@@ -91,6 +90,13 @@ struct TodoEditorFeature {
9190
}
9291
}
9392

93+
@ObservableState
94+
@CasePathable
95+
enum SheetState: Equatable {
96+
case info
97+
case todo(TodoIdItem)
98+
}
99+
94100
enum EditorTab: Equatable {
95101
case editor
96102
case preview
@@ -103,16 +109,22 @@ struct TodoEditorFeature {
103109

104110
enum Action: BindableAction, Equatable {
105111
case alert(PresentationAction<Never>)
112+
case sheet(PresentationAction<Sheet>)
106113
case binding(BindingAction<State>)
107114
case onAppear
108115
case addTag(String)
109116
case removeTag(String)
110117
case setCompleted(Bool)
118+
case setSheet(SheetState?)
111119
case upsertTodo
112120
case createSucceeded
113121
case saveFailed
114122
case updateSucceeded(Todo)
115123
case loading(LoadingFeature.Action)
124+
125+
enum Sheet: Equatable {
126+
case tapCloseButton
127+
}
116128
}
117129

118130
@Dependency(\.date.now) var now
@@ -130,6 +142,12 @@ struct TodoEditorFeature {
130142
switch action {
131143
case .alert:
132144
break
145+
case .sheet(.dismiss):
146+
state.sheet = nil
147+
case .sheet(.presented(.tapCloseButton)):
148+
state.sheet = nil
149+
case .sheet:
150+
break
133151
case .binding(\.content):
134152
if state.tabViewTag == .preview {
135153
return resolveMarkdownEffect(content: state.content)
@@ -160,6 +178,8 @@ struct TodoEditorFeature {
160178
state.completedAt = isCompleted ? now : nil
161179
}
162180
state.isCompleted = isCompleted
181+
case .setSheet(let sheet):
182+
state.sheet = sheet
163183
case .upsertTodo:
164184
state.saveResult = nil
165185
if state.originalDraft == nil {
@@ -180,6 +200,18 @@ struct TodoEditorFeature {
180200
return .none
181201
}
182202
.ifLet(\.$alert, action: \.alert)
203+
.ifLet(\.$sheet, action: \.sheet) {
204+
TodoEditorSheetFeature()
205+
}
206+
}
207+
}
208+
209+
private struct TodoEditorSheetFeature: Reducer {
210+
typealias State = TodoEditorFeature.SheetState
211+
typealias Action = TodoEditorFeature.Action.Sheet
212+
213+
var body: some ReducerOf<Self> {
214+
EmptyReducer()
183215
}
184216
}
185217

Application/DevLogPresentation/Sources/Home/Editor/TodoEditorView.swift

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -53,37 +53,16 @@ struct TodoEditorView: View {
5353
.navigationTitle(store.navigationTitle)
5454
.navigationBarTitleDisplayMode(.inline)
5555
.toolbarBackground(.background, for: .navigationBar)
56-
.sheet(isPresented: $store.showInfo) {
57-
TodoEditorInfoSheetView(store: store) {
58-
store.send(.binding(.set(\.showInfo, false)))
59-
}
60-
}
61-
.sheet(item: $store.selectedTodoId) { item in
62-
NavigationStack {
63-
TodoDetailView(store: Store(
64-
initialState: TodoDetailFeature.State(todoId: item.id, showEditButton: false)
65-
) {
66-
TodoDetailFeature()
67-
} withDependencies: {
68-
$0.fetchTodoByIdUseCase = container.resolve(FetchTodoByIdUseCase.self)
69-
$0.fetchReferenceItemsUseCase = container.resolve(FetchReferenceItemsUseCase.self)
70-
})
71-
.toolbar {
72-
ToolbarLeadingButton {
73-
store.send(.binding(.set(\.selectedTodoId, nil)))
74-
}
75-
}
76-
}
77-
.background(Color(.systemGroupedBackground))
78-
.presentationDragIndicator(.visible)
56+
.sheet(item: $store.scope(state: \.sheet, action: \.sheet)) { sheetStore in
57+
sheetContent(sheetStore)
7958
}
8059
.toolbar {
8160
if !isiOSAppOnMac {
8261
ToolbarLeadingButton { close() }
8362
}
8463
ToolbarItem(placement: .topBarTrailing) {
8564
Button {
86-
store.send(.binding(.set(\.showInfo, true)))
65+
store.send(.setSheet(.info))
8766
} label: {
8867
Image(systemName: "info.circle")
8968
}
@@ -165,7 +144,7 @@ struct TodoEditorView: View {
165144
TodoMarkdownContentView(
166145
content: store.content,
167146
referenceItems: store.referenceItems,
168-
onOpenTodoID: { store.send(.binding(.set(\.selectedTodoId, TodoIdItem(id: $0)))) }
147+
onOpenTodoID: { store.send(.setSheet(.todo(TodoIdItem(id: $0)))) }
169148
)
170149
}
171150
}
@@ -223,6 +202,36 @@ struct TodoEditorView: View {
223202
}
224203
}
225204

205+
@ViewBuilder
206+
private func sheetContent(
207+
_ sheetStore: Store<TodoEditorFeature.SheetState, TodoEditorFeature.Action.Sheet>
208+
) -> some View {
209+
switch sheetStore.state {
210+
case .info:
211+
TodoEditorInfoSheetView(store: store) {
212+
sheetStore.send(.tapCloseButton)
213+
}
214+
case .todo(let item):
215+
NavigationStack {
216+
TodoDetailView(store: Store(
217+
initialState: TodoDetailFeature.State(todoId: item.id, showEditButton: false)
218+
) {
219+
TodoDetailFeature()
220+
} withDependencies: {
221+
$0.fetchTodoByIdUseCase = container.resolve(FetchTodoByIdUseCase.self)
222+
$0.fetchReferenceItemsUseCase = container.resolve(FetchReferenceItemsUseCase.self)
223+
})
224+
.toolbar {
225+
ToolbarLeadingButton {
226+
sheetStore.send(.tapCloseButton)
227+
}
228+
}
229+
}
230+
.background(Color(.systemGroupedBackground))
231+
.presentationDragIndicator(.visible)
232+
}
233+
}
234+
226235
private enum Field: Hashable {
227236
case title, content
228237
}

Application/DevLogPresentation/Tests/Home/TodoEditorFeatureTestDoubles.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ final class TodoEditorStoreTestAdapter {
2525
var content: String { store.state.content }
2626
var referenceItems: [Int: TodoReferenceItem] { store.state.referenceItems }
2727
var dueDate: Date? { store.state.dueDate }
28+
var sheet: TodoEditorFeature.SheetState? { store.state.sheet }
2829
var isLoading: Bool { store.state.isLoading }
2930
var tags: [String] { Array(store.state.tags) }
3031
var categories: [TodoCategoryItem] { store.state.categories }
@@ -112,6 +113,24 @@ final class TodoEditorStoreTestAdapter {
112113
}
113114
}
114115

116+
func setSheet(_ sheet: TodoEditorFeature.SheetState?) async {
117+
await store.send(.setSheet(sheet)) {
118+
$0.sheet = sheet
119+
}
120+
}
121+
122+
func dismissSheet() async {
123+
await store.send(.sheet(.dismiss)) {
124+
$0.sheet = nil
125+
}
126+
}
127+
128+
func tapSheetCloseButton() async {
129+
await store.send(.sheet(.presented(.tapCloseButton))) {
130+
$0.sheet = nil
131+
}
132+
}
133+
115134
func setDueDate(_ dueDate: Date?) async {
116135
let expectedDueDate = expectedDueDate(for: dueDate)
117136
await store.send(.binding(.set(\.dueDate, dueDate))) {

Application/DevLogPresentation/Tests/Home/TodoEditorFeatureTests.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,28 @@ struct TodoEditorFeatureTests {
143143
#expect(adapter.referenceItems[5] == TodoReferenceItem(from: reference5))
144144
}
145145

146+
@Test("정보와 참조 Todo 시트 상태를 액션에 맞게 변경한다")
147+
func 정보와_참조_Todo_시트_상태를_액션에_맞게_변경한다() async {
148+
let adapter = TodoEditorStoreTestAdapter(category: .system(.doc))
149+
let item = TodoIdItem(id: "todo-2")
150+
151+
await adapter.setSheet(.info)
152+
153+
#expect(adapter.sheet == .info)
154+
155+
await adapter.dismissSheet()
156+
157+
#expect(adapter.sheet == nil)
158+
159+
await adapter.setSheet(.todo(item))
160+
161+
#expect(adapter.sheet == .todo(item))
162+
163+
await adapter.tapSheetCloseButton()
164+
165+
#expect(adapter.sheet == nil)
166+
}
167+
146168
@Test("새 Todo 저장 성공은 draft를 저장하고 생성 완료 상태를 남긴다")
147169
func 새_Todo_저장_성공은_draft를_저장하고_생성_완료_상태를_남긴다() async {
148170
let upsertSpy = TodoEditorUpsertTodoUseCaseSpy()

0 commit comments

Comments
 (0)