From 97811ab0184f329dfb6fd28e39b03238325ed2d2 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sun, 10 May 2026 18:28:05 +0900 Subject: [PATCH 1/4] =?UTF-8?q?refactor:=20TodayView=20=EC=A0=84=EC=9A=A9?= =?UTF-8?q?=20Coordinator=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=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 --- DevLog/UI/Main/MainView.swift | 18 ++++---- DevLog/UI/Main/MainViewCoordinator.swift | 9 ---- DevLog/UI/Today/TodayView.swift | 49 +++++++++++----------- DevLog/UI/Today/TodayViewCoordinator.swift | 25 +++++++++++ 4 files changed, 59 insertions(+), 42 deletions(-) create mode 100644 DevLog/UI/Today/TodayViewCoordinator.swift diff --git a/DevLog/UI/Main/MainView.swift b/DevLog/UI/Main/MainView.swift index 4ffcbe88..87bdaf5f 100644 --- a/DevLog/UI/Main/MainView.swift +++ b/DevLog/UI/Main/MainView.swift @@ -11,6 +11,7 @@ struct MainView: View { @Environment(\.horizontalSizeClass) private var horizontalSizeClass @State private var coordinator: MainViewCoordinator @State private var homeViewCoordinator: HomeViewCoordinator + @State private var todayViewCoordinator: TodayViewCoordinator @Binding var selectedTab: MainTab private let container: DIContainer @@ -21,6 +22,7 @@ struct MainView: View { self.container = container self._coordinator = State(initialValue: MainViewCoordinator(container: container)) self._homeViewCoordinator = State(initialValue: HomeViewCoordinator(container: container)) + self._todayViewCoordinator = State(initialValue: TodayViewCoordinator(container: container)) self._selectedTab = selectedTab } @@ -102,7 +104,7 @@ struct MainView: View { } detail: { todayRegularDetailView } - .environment(coordinator.todayNavigationRouter) + .environment(todayViewCoordinator.router) case .notification: NavigationSplitView { mainSidebar @@ -268,12 +270,12 @@ struct MainView: View { todayContentView } } - .environment(coordinator.todayNavigationRouter) + .environment(todayViewCoordinator.router) } private var todayContentView: some View { TodayView( - viewModel: coordinator.todayViewModel, + coordinator: todayViewCoordinator, isCompactLayout: isCompactLayout ) } @@ -282,7 +284,7 @@ struct MainView: View { private var todayRegularDetailView: some View { NavigationStack(path: todayDetailPath) { Group { - if let todayRoute = coordinator.todayNavigationRouter.root { + if let todayRoute = todayViewCoordinator.router.root { todayDestinationView(todayRoute) } else { ContentUnavailableView( @@ -366,15 +368,15 @@ private extension MainView { var todayNavigationPath: Binding<[TodayRoute]> { Binding( - get: { coordinator.todayNavigationRouter.path }, - set: { coordinator.todayNavigationRouter.path = $0 } + get: { todayViewCoordinator.router.path }, + set: { todayViewCoordinator.router.path = $0 } ) } var todayDetailPath: Binding<[TodayRoute]> { Binding( - get: { coordinator.todayNavigationRouter.detailPath }, - set: { coordinator.todayNavigationRouter.detailPath = $0 } + get: { todayViewCoordinator.router.detailPath }, + set: { todayViewCoordinator.router.detailPath = $0 } ) } diff --git a/DevLog/UI/Main/MainViewCoordinator.swift b/DevLog/UI/Main/MainViewCoordinator.swift index efcdd9f3..3b2ebb10 100644 --- a/DevLog/UI/Main/MainViewCoordinator.swift +++ b/DevLog/UI/Main/MainViewCoordinator.swift @@ -11,23 +11,14 @@ import Foundation @Observable final class MainViewCoordinator { let mainViewModel: MainViewModel - let todayViewModel: TodayViewModel let pushNotificationListViewModel: PushNotificationListViewModel let profileViewModel: ProfileViewModel - let todayNavigationRouter = NavigationRouter() var todoIdToPresent: TodoIdItem? init(container: DIContainer) { self.mainViewModel = MainViewModel( unreadPushCountUseCase: container.resolve(ObserveUnreadPushCountUseCase.self) ) - self.todayViewModel = TodayViewModel( - fetchTodosUseCase: container.resolve(FetchTodosUseCase.self), - fetchTodoByIdUseCase: container.resolve(FetchTodoByIdUseCase.self), - upsertTodoUseCase: container.resolve(UpsertTodoUseCase.self), - fetchTodayDisplayOptionsUseCase: container.resolve(FetchTodayDisplayOptionsUseCase.self), - updateTodayDisplayOptionsUseCase: container.resolve(UpdateTodayDisplayOptionsUseCase.self) - ) self.pushNotificationListViewModel = PushNotificationListViewModel( fetchUseCase: container.resolve(FetchPushNotificationsUseCase.self), deleteUseCase: container.resolve(DeletePushNotificationUseCase.self), diff --git a/DevLog/UI/Today/TodayView.swift b/DevLog/UI/Today/TodayView.swift index 027d1d63..03b31b6d 100644 --- a/DevLog/UI/Today/TodayView.swift +++ b/DevLog/UI/Today/TodayView.swift @@ -8,17 +8,16 @@ import SwiftUI struct TodayView: View { - @Environment(NavigationRouter.self) private var router - @State var viewModel: TodayViewModel + let coordinator: TodayViewCoordinator let isCompactLayout: Bool var body: some View { List { summarySection - if viewModel.sections.isEmpty, !viewModel.state.isLoading { + if coordinator.viewModel.sections.isEmpty, !coordinator.viewModel.state.isLoading { emptySection } else { - ForEach(viewModel.sections) { section in + ForEach(coordinator.viewModel.sections) { section in todoSection(section.title, items: section.items) } } @@ -27,21 +26,21 @@ struct TodayView: View { .navigationTitle(String(localized: "nav_today")) .toolbar { toolbarContent } .background(NavigationBarConfigurator()) - .refreshable { viewModel.send(.refresh) } - .onAppear { viewModel.send(.onAppear) } + .refreshable { coordinator.viewModel.send(.refresh) } + .onAppear { coordinator.viewModel.send(.onAppear) } .alert( - viewModel.state.alertTitle, + coordinator.viewModel.state.alertTitle, isPresented: Binding( - get: { viewModel.state.showAlert }, - set: { viewModel.send(.setAlert($0)) } + get: { coordinator.viewModel.state.showAlert }, + set: { coordinator.viewModel.send(.setAlert($0)) } ) ) { Button(String(localized: "common_close"), role: .cancel) { } } message: { - Text(viewModel.state.alertMessage) + Text(coordinator.viewModel.state.alertMessage) } .overlay { - if viewModel.state.isLoading { + if coordinator.viewModel.state.isLoading { LoadingView() } } @@ -54,14 +53,14 @@ struct TodayView: View { ForEach(TodayViewModel.SectionScope.allCases, id: \.self) { scope in Button { withAnimation(.easeInOut) { - viewModel.send(.setSectionScope(scope)) + coordinator.viewModel.send(.setSectionScope(scope)) } } label: { SummaryCard( title: scope.title, - value: viewModel.summaryValue(for: scope), + value: coordinator.viewModel.summaryValue(for: scope), accentColor: scope.accentColor, - isSelected: viewModel.state.selectedSectionScope == scope + isSelected: coordinator.viewModel.state.selectedSectionScope == scope ) } .buttonStyle(.plain) @@ -81,8 +80,8 @@ struct TodayView: View { Picker( String(localized: "today_due_visibility_label"), selection: Binding( - get: { viewModel.state.displayOptions.dueDateVisibility }, - set: { viewModel.send(.setDueDateVisibility($0)) } + get: { coordinator.viewModel.state.displayOptions.dueDateVisibility }, + set: { coordinator.viewModel.send(.setDueDateVisibility($0)) } ) ) { ForEach(TodayDisplayOptions.DueDateVisibility.allCases, id: \.self) { option in @@ -93,20 +92,20 @@ struct TodayView: View { Toggle( String(localized: "today_pinned_only"), isOn: Binding( - get: { viewModel.state.displayOptions.focusVisibility == .focusedOnly }, + get: { coordinator.viewModel.state.displayOptions.focusVisibility == .focusedOnly }, set: { - viewModel.send(.setFocusVisibility($0 ? .focusedOnly : .all)) + coordinator.viewModel.send(.setFocusVisibility($0 ? .focusedOnly : .all)) } ) ) .tint(.orange) - if viewModel.state.displayOptions.focusVisibility == .focusedOnly { + if coordinator.viewModel.state.displayOptions.focusVisibility == .focusedOnly { Text(String(localized: "today_pinned_only_description")) .font(.caption) } } label: { - let options = viewModel.state.displayOptions + let options = coordinator.viewModel.state.displayOptions Image(systemName: "line.3.horizontal.decrease.circle\(options == .default ? "" : ".fill")") } } @@ -135,7 +134,7 @@ struct TodayView: View { todoRow(item) .swipeActions(edge: .leading, allowsFullSwipe: false) { Button { - viewModel.send(.togglePinned(item)) + coordinator.viewModel.send(.togglePinned(item)) } label: { Image(systemName: item.isPinned ? "star.slash" : "star.fill") } @@ -143,7 +142,7 @@ struct TodayView: View { } .swipeActions(edge: .trailing, allowsFullSwipe: false) { Button { - viewModel.send(.completeTodo(item)) + coordinator.viewModel.send(.completeTodo(item)) } label: { Label(String(localized: "today_complete_action"), systemImage: "checkmark") } @@ -166,7 +165,7 @@ struct TodayView: View { } } else { Button { - router.replace(with: .todo(TodoIdItem(id: item.id))) + coordinator.router.replace(with: .todo(TodoIdItem(id: item.id))) } label: { TodayTodoRow(item: item) .frame(maxWidth: .infinity, alignment: .leading) @@ -177,9 +176,9 @@ struct TodayView: View { } private var emptyStateContent: EmptyStateContent { - switch viewModel.state.selectedSectionScope { + switch coordinator.viewModel.state.selectedSectionScope { case .all: - if viewModel.state.todos.isEmpty { + if coordinator.viewModel.state.todos.isEmpty { return EmptyStateContent( title: String(localized: "today_empty_all_title"), message: String(localized: "today_empty_all_message") diff --git a/DevLog/UI/Today/TodayViewCoordinator.swift b/DevLog/UI/Today/TodayViewCoordinator.swift new file mode 100644 index 00000000..abcf992a --- /dev/null +++ b/DevLog/UI/Today/TodayViewCoordinator.swift @@ -0,0 +1,25 @@ +// +// TodayViewCoordinator.swift +// DevLog +// +// Created by opfic on 5/10/26. +// + +import Foundation + +@MainActor +@Observable +final class TodayViewCoordinator { + let viewModel: TodayViewModel + let router = NavigationRouter() + + init(container: DIContainer) { + self.viewModel = TodayViewModel( + fetchTodosUseCase: container.resolve(FetchTodosUseCase.self), + fetchTodoByIdUseCase: container.resolve(FetchTodoByIdUseCase.self), + upsertTodoUseCase: container.resolve(UpsertTodoUseCase.self), + fetchTodayDisplayOptionsUseCase: container.resolve(FetchTodayDisplayOptionsUseCase.self), + updateTodayDisplayOptionsUseCase: container.resolve(UpdateTodayDisplayOptionsUseCase.self) + ) + } +} From ebfcb762bda036c61addebd2fb1df55580cf6f75 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sun, 10 May 2026 18:31:37 +0900 Subject: [PATCH 2/4] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EB=9D=BC=EC=9A=B0=ED=84=B0=20=EC=A3=BC=EC=9E=85=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/UI/Main/MainView.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/DevLog/UI/Main/MainView.swift b/DevLog/UI/Main/MainView.swift index 87bdaf5f..fb461ef1 100644 --- a/DevLog/UI/Main/MainView.swift +++ b/DevLog/UI/Main/MainView.swift @@ -104,7 +104,6 @@ struct MainView: View { } detail: { todayRegularDetailView } - .environment(todayViewCoordinator.router) case .notification: NavigationSplitView { mainSidebar @@ -270,7 +269,6 @@ struct MainView: View { todayContentView } } - .environment(todayViewCoordinator.router) } private var todayContentView: some View { From b85b5e73749a1e0147290e8e54ba62c9ff276791 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sun, 10 May 2026 18:37:04 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20row=EC=9D=98=20=EC=BB=A8=ED=85=90?= =?UTF-8?q?=EC=B8=A0=EB=A5=BC=20=EC=A0=95=ED=99=95=ED=95=98=EA=B2=8C=20?= =?UTF-8?q?=ED=83=AD=20=ED=95=B4=EC=95=BC=20=EB=B2=84=ED=8A=BC=EC=9D=B4=20?= =?UTF-8?q?=EC=9E=91=EB=8F=99=ED=95=98=EB=8A=94=20=ED=98=84=EC=83=81=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/UI/Today/TodayView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/DevLog/UI/Today/TodayView.swift b/DevLog/UI/Today/TodayView.swift index 03b31b6d..74478982 100644 --- a/DevLog/UI/Today/TodayView.swift +++ b/DevLog/UI/Today/TodayView.swift @@ -169,6 +169,7 @@ struct TodayView: View { } label: { TodayTodoRow(item: item) .frame(maxWidth: .infinity, alignment: .leading) + .contentShape(.rect) .listRowInsets(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) } .buttonStyle(.plain) From 85d325b29f853774fdbf366d42206dad33c984f8 Mon Sep 17 00:00:00 2001 From: opficdev Date: Sun, 10 May 2026 19:34:42 +0900 Subject: [PATCH 4/4] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EB=AA=A8=EB=94=94=ED=8C=8C=EC=9D=B4=EC=96=B4=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DevLog/UI/Today/TodayView.swift | 2 -- 1 file changed, 2 deletions(-) diff --git a/DevLog/UI/Today/TodayView.swift b/DevLog/UI/Today/TodayView.swift index 74478982..1cc027c7 100644 --- a/DevLog/UI/Today/TodayView.swift +++ b/DevLog/UI/Today/TodayView.swift @@ -161,7 +161,6 @@ struct TodayView: View { if isCompactLayout { NavigationLink(value: TodayRoute.todo(TodoIdItem(id: item.id))) { TodayTodoRow(item: item) - .listRowInsets(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) } } else { Button { @@ -170,7 +169,6 @@ struct TodayView: View { TodayTodoRow(item: item) .frame(maxWidth: .infinity, alignment: .leading) .contentShape(.rect) - .listRowInsets(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) } .buttonStyle(.plain) }