diff --git a/Application/DevLogPresentation/DevLogPresentation.xcodeproj/project.pbxproj b/Application/DevLogPresentation/DevLogPresentation.xcodeproj/project.pbxproj index 4c46008a..a4b0f82e 100644 --- a/Application/DevLogPresentation/DevLogPresentation.xcodeproj/project.pbxproj +++ b/Application/DevLogPresentation/DevLogPresentation.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 70; + objectVersion = 71; objects = { /* Begin PBXBuildFile section */ diff --git a/Application/DevLogPresentation/Sources/Home/TodoWindowCoordinator.swift b/Application/DevLogPresentation/Sources/Home/TodoWindowCoordinator.swift new file mode 100644 index 00000000..bea4f7ce --- /dev/null +++ b/Application/DevLogPresentation/Sources/Home/TodoWindowCoordinator.swift @@ -0,0 +1,64 @@ +// +// TodoWindowCoordinator.swift +// DevLogPresentation +// +// Created by opfic on 5/31/26. +// + +import Foundation +import DevLogCore +import DevLogDomain + +@MainActor +@Observable +final class TodoWindowCoordinator { + private let diContainer: DIContainer + @ObservationIgnored + private var listViewModel: TodoListViewModel? + @ObservationIgnored + private var detailViewModel: TodoDetailViewModel? + + init(container: DIContainer) { + self.diContainer = container + } + + func makeListViewModel(category: TodoCategory) -> TodoListViewModel { + if let listViewModel, + listViewModel.category == category { + return listViewModel + } + + let listViewModel = TodoListViewModel( + fetchTodosUseCase: diContainer.resolve(FetchTodosUseCase.self), + fetchTodoByIdUseCase: diContainer.resolve(FetchTodoByIdUseCase.self), + upsertTodoUseCase: diContainer.resolve(UpsertTodoUseCase.self), + deleteTodoUseCase: diContainer.resolve(DeleteTodoUseCase.self), + undoDeleteTodoUseCase: diContainer.resolve(UndoDeleteTodoUseCase.self), + trackAnalyticsEventUseCase: diContainer.resolve(TrackAnalyticsEventUseCase.self), + category: category + ) + self.listViewModel = listViewModel + return listViewModel + } + + func makeDetailViewModel( + todoId: String, + showEditButton: Bool = true + ) -> TodoDetailViewModel { + if let detailViewModel, + detailViewModel.todoId == todoId, + detailViewModel.showEditButton == showEditButton { + return detailViewModel + } + + let detailViewModel = TodoDetailViewModel( + fetchTodoUseCase: diContainer.resolve(FetchTodoByIdUseCase.self), + fetchReferenceItemsUseCase: diContainer.resolve(FetchReferenceItemsUseCase.self), + upsertUseCase: diContainer.resolve(UpsertTodoUseCase.self), + todoId: todoId, + showEditButton: showEditButton + ) + self.detailViewModel = detailViewModel + return detailViewModel + } +} diff --git a/Application/DevLogPresentation/Sources/Main/MainView.swift b/Application/DevLogPresentation/Sources/Main/MainView.swift index 4ba0d939..355708af 100644 --- a/Application/DevLogPresentation/Sources/Main/MainView.swift +++ b/Application/DevLogPresentation/Sources/Main/MainView.swift @@ -12,6 +12,7 @@ import DevLogDomain struct MainView: View { @Environment(\.horizontalSizeClass) private var horizontalSizeClass @State private var coordinator: MainViewCoordinator + @State private var todoWindowCoordinator: TodoWindowCoordinator @State private var homeViewCoordinator: HomeViewCoordinator @State private var todayViewCoordinator: TodayViewCoordinator @State private var pushNotificationListViewCoordinator: PushNotificationListViewCoordinator @@ -23,6 +24,7 @@ struct MainView: View { selectedTab: Binding ) { self._coordinator = State(initialValue: MainViewCoordinator(container: container)) + self._todoWindowCoordinator = State(initialValue: TodoWindowCoordinator(container: container)) self._homeViewCoordinator = State(initialValue: HomeViewCoordinator(container: container)) self._todayViewCoordinator = State(initialValue: TodayViewCoordinator(container: container)) self._pushNotificationListViewCoordinator = State( @@ -43,11 +45,11 @@ struct MainView: View { } } .onAppear { - coordinator.mainViewModel.send(.onAppear) + coordinator.viewModel.send(.onAppear) } .onChange(of: selectedTab, initial: true) { _, newValue in guard let newValue else { return } - coordinator.mainViewModel.send(.selectedTabChanged(newValue)) + coordinator.viewModel.send(.selectedTabChanged(newValue)) if newValue == .home { homeViewCoordinator.fetchData() } else if newValue == .today { @@ -59,12 +61,12 @@ struct MainView: View { } } .alert( - coordinator.mainViewModel.state.alertTitle, + coordinator.viewModel.state.alertTitle, isPresented: mainAlertPresented ) { Button(String(localized: "common_close"), role: .cancel) { } } message: { - Text(coordinator.mainViewModel.state.alertMessage) + Text(coordinator.viewModel.state.alertMessage) } } @@ -86,7 +88,7 @@ struct MainView: View { .tabItem { tabLabel(.notification) } - .badge(coordinator.mainViewModel.state.unreadPushCount) + .badge(coordinator.viewModel.state.unreadPushCount) .tag(MainTab.notification as MainTab?) profileView @@ -189,7 +191,7 @@ struct MainView: View { private func sidebarRow(_ tab: MainTab) -> some View { if tab == .notification { tabLabel(tab) - .badge(coordinator.mainViewModel.state.unreadPushCount) + .badge(coordinator.viewModel.state.unreadPushCount) .tag(tab) } else { tabLabel(tab) @@ -254,11 +256,11 @@ struct MainView: View { switch homeRoute { case .category(let item): TodoListView( - viewModel: coordinator.todoListViewModel(category: item.todoCategory) + viewModel: todoWindowCoordinator.makeListViewModel(category: item.todoCategory) ) .id(item.id) case .todo(let item): - TodoDetailView(viewModel: coordinator.todoDetailViewModel(todoId: item.id)) + TodoDetailView(viewModel: todoWindowCoordinator.makeDetailViewModel(todoId: item.id)) .id(item.id) case .webPage(let item): WebView(url: item.url) @@ -321,7 +323,7 @@ struct MainView: View { private func todayDestinationView(_ todayRoute: TodayRoute) -> some View { switch todayRoute { case .todo(let item): - TodoDetailView(viewModel: coordinator.todoDetailViewModel(todoId: item.id)) + TodoDetailView(viewModel: todoWindowCoordinator.makeDetailViewModel(todoId: item.id)) .id(item.id) } } @@ -345,8 +347,8 @@ private extension MainView { var mainAlertPresented: Binding { Binding( - get: { coordinator.mainViewModel.state.showAlert }, - set: { coordinator.mainViewModel.send(.setAlert($0)) } + get: { coordinator.viewModel.state.showAlert }, + set: { coordinator.viewModel.send(.setAlert($0)) } ) } diff --git a/Application/DevLogPresentation/Sources/Main/MainViewCoordinator.swift b/Application/DevLogPresentation/Sources/Main/MainViewCoordinator.swift index 1546d7d7..825265f8 100644 --- a/Application/DevLogPresentation/Sources/Main/MainViewCoordinator.swift +++ b/Application/DevLogPresentation/Sources/Main/MainViewCoordinator.swift @@ -12,58 +12,12 @@ import DevLogDomain @MainActor @Observable final class MainViewCoordinator { - let mainViewModel: MainViewModel - private let diContainer: DIContainer - @ObservationIgnored - private var todoListViewModel: TodoListViewModel? - @ObservationIgnored - private var todoDetailViewModel: TodoDetailViewModel? + let viewModel: MainViewModel init(container: DIContainer) { - self.diContainer = container - self.mainViewModel = MainViewModel( + self.viewModel = MainViewModel( trackAnalyticsEventUseCase: container.resolve(TrackAnalyticsEventUseCase.self), unreadPushCountUseCase: container.resolve(ObserveUnreadPushCountUseCase.self) ) } - - func todoListViewModel(category: TodoCategory) -> TodoListViewModel { - if let todoListViewModel, - todoListViewModel.category == category { - return todoListViewModel - } - - let todoListViewModel = TodoListViewModel( - fetchTodosUseCase: diContainer.resolve(FetchTodosUseCase.self), - fetchTodoByIdUseCase: diContainer.resolve(FetchTodoByIdUseCase.self), - upsertTodoUseCase: diContainer.resolve(UpsertTodoUseCase.self), - deleteTodoUseCase: diContainer.resolve(DeleteTodoUseCase.self), - undoDeleteTodoUseCase: diContainer.resolve(UndoDeleteTodoUseCase.self), - trackAnalyticsEventUseCase: diContainer.resolve(TrackAnalyticsEventUseCase.self), - category: category - ) - self.todoListViewModel = todoListViewModel - return todoListViewModel - } - - func todoDetailViewModel( - todoId: String, - showEditButton: Bool = true - ) -> TodoDetailViewModel { - if let todoDetailViewModel, - todoDetailViewModel.todoId == todoId, - todoDetailViewModel.showEditButton == showEditButton { - return todoDetailViewModel - } - - let todoDetailViewModel = TodoDetailViewModel( - fetchTodoUseCase: diContainer.resolve(FetchTodoByIdUseCase.self), - fetchReferenceItemsUseCase: diContainer.resolve(FetchReferenceItemsUseCase.self), - upsertUseCase: diContainer.resolve(UpsertTodoUseCase.self), - todoId: todoId, - showEditButton: showEditButton - ) - self.todoDetailViewModel = todoDetailViewModel - return todoDetailViewModel - } }