diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index cca6ad61..c418bf6b 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,10 +3,12 @@ - closed #이슈번호 +## 🎯 의도 + ## 📝 작업 내용 ### 📌 요약 ### 🔍 상세 -## 📸 영상 / 이미지 (Optional) \ No newline at end of file +## 📸 영상 / 이미지 (Optional) diff --git a/DevLog/Presentation/ViewModel/TodoDetailViewModel.swift b/DevLog/Presentation/ViewModel/TodoDetailViewModel.swift index 6ceb674f..c3c4e9eb 100644 --- a/DevLog/Presentation/ViewModel/TodoDetailViewModel.swift +++ b/DevLog/Presentation/ViewModel/TodoDetailViewModel.swift @@ -40,11 +40,11 @@ final class TodoDetailViewModel: Store { } private(set) var state: State = .init() + let todoId: String let showEditButton: Bool private let fetchTodoUseCase: FetchTodoByIdUseCase private let fetchReferenceItemsUseCase: FetchReferenceItemsUseCase private let upsertUseCase: UpsertTodoUseCase - private let todoId: String private let loadingState = LoadingState() init( diff --git a/DevLog/Presentation/ViewModel/TodoListViewModel.swift b/DevLog/Presentation/ViewModel/TodoListViewModel.swift index 9700fc49..86643ab6 100644 --- a/DevLog/Presentation/ViewModel/TodoListViewModel.swift +++ b/DevLog/Presentation/ViewModel/TodoListViewModel.swift @@ -13,7 +13,6 @@ final class TodoListViewModel: Store { var todos: [TodoListItem] = [] var searchText: String = "" var searchResults: [TodoListItem] = [] - let category: TodoCategory var showEditor: Bool = false var showAlert: Bool = false var alertTitle: String = "" @@ -81,6 +80,7 @@ final class TodoListViewModel: Store { case request } + let category: TodoCategory private(set) var state: State private let fetchTodosUseCase: FetchTodosUseCase private let fetchTodoByIdUseCase: FetchTodoByIdUseCase @@ -106,8 +106,8 @@ final class TodoListViewModel: Store { self.upsertTodoUseCase = upsertTodoUseCase self.deleteTodoUseCase = deleteTodoUseCase self.undoDeleteTodoUseCase = undoDeleteTodoUseCase + self.category = category self.state = State( - category: category, query: TodoQuery(category: category) ) } @@ -191,7 +191,7 @@ final class TodoListViewModel: Store { self.endLoading(.immediate) } } - let query = TodoQuery(category: state.category, keyword: keyword) + let query = TodoQuery(category: category, keyword: keyword) let page = try await fetchTodosUseCase.execute(query, cursor: nil) if Task.isCancelled { return } send(.fetchSearchResults(page.items.compactMap { TodoListItem(from: $0) })) @@ -307,7 +307,7 @@ private extension TodoListViewModel { self.nextCursor = nil return [.fetch] case .resetFilters: - state.query = TodoQuery(category: state.category) + state.query = TodoQuery(category: category) self.nextCursor = nil return [.fetch] case .setIsSearching(let value): diff --git a/DevLog/UI/Home/TodoListView.swift b/DevLog/UI/Home/TodoListView.swift index 9dfcb9d8..9a99bf51 100644 --- a/DevLog/UI/Home/TodoListView.swift +++ b/DevLog/UI/Home/TodoListView.swift @@ -45,7 +45,7 @@ struct TodoListView: View { prompt: Text( String.localizedStringWithFormat( String(localized: "todo_list_search_prompt_format"), - TodoCategoryItem(from: viewModel.state.category).localizedName + TodoCategoryItem(from: viewModel.category).localizedName ) ) ) @@ -71,14 +71,14 @@ struct TodoListView: View { ) { Label(viewModel.state.toastMessage, systemImage: "arrow.uturn.left") } - .navigationTitle(TodoCategoryItem(from: viewModel.state.category).localizedName) + .navigationTitle(TodoCategoryItem(from: viewModel.category).localizedName) .fullScreenCover(isPresented: Binding( get: { viewModel.state.showEditor }, set: { viewModel.send(.setShowEditor($0)) } )) { TodoEditorView( viewModel: TodoEditorViewModel( - category: viewModel.state.category, + category: viewModel.category, fetchPreferencesUseCase: container.resolve(FetchTodoCategoryPreferencesUseCase.self), fetchReferenceItemsUseCase: container.resolve(FetchReferenceItemsUseCase.self) ), @@ -222,7 +222,7 @@ struct TodoListView: View { prompt: Text( String.localizedStringWithFormat( String(localized: "todo_list_search_prompt_format"), - TodoCategoryItem(from: viewModel.state.category).localizedName + TodoCategoryItem(from: viewModel.category).localizedName ) ) ) @@ -240,7 +240,7 @@ struct TodoListView: View { Text( String.localizedStringWithFormat( String(localized: "todo_list_search_instruction_format"), - TodoCategoryItem(from: viewModel.state.category).localizedName + TodoCategoryItem(from: viewModel.category).localizedName ) ) .foregroundStyle(Color.gray) diff --git a/DevLog/UI/Main/MainView.swift b/DevLog/UI/Main/MainView.swift index fb461ef1..7701fa90 100644 --- a/DevLog/UI/Main/MainView.swift +++ b/DevLog/UI/Main/MainView.swift @@ -13,13 +13,11 @@ struct MainView: View { @State private var homeViewCoordinator: HomeViewCoordinator @State private var todayViewCoordinator: TodayViewCoordinator @Binding var selectedTab: MainTab - private let container: DIContainer init( container: DIContainer, selectedTab: Binding ) { - self.container = container self._coordinator = State(initialValue: MainViewCoordinator(container: container)) self._homeViewCoordinator = State(initialValue: HomeViewCoordinator(container: container)) self._todayViewCoordinator = State(initialValue: TodayViewCoordinator(container: container)) @@ -117,7 +115,7 @@ struct MainView: View { Group { if let todoId = coordinator.todoIdToPresent?.id { TodoDetailView( - viewModel: makeTodoDetailViewModel( + viewModel: coordinator.todoDetailViewModel( todoId: todoId, showEditButton: false ) @@ -235,11 +233,11 @@ struct MainView: View { switch homeRoute { case .category(let item): TodoListView( - viewModel: makeTodoListViewModel(category: item.todoCategory) + viewModel: coordinator.todoListViewModel(category: item.todoCategory) ) .id(item.id) case .todo(let item): - TodoDetailView(viewModel: makeTodoDetailViewModel(todoId: item.id)) + TodoDetailView(viewModel: coordinator.todoDetailViewModel(todoId: item.id)) .id(item.id) case .webPage(let item): WebView(url: item.url) @@ -302,7 +300,7 @@ struct MainView: View { private func todayDestinationView(_ todayRoute: TodayRoute) -> some View { switch todayRoute { case .todo(let item): - TodoDetailView(viewModel: makeTodoDetailViewModel(todoId: item.id)) + TodoDetailView(viewModel: coordinator.todoDetailViewModel(todoId: item.id)) .id(item.id) } } @@ -378,29 +376,6 @@ private extension MainView { ) } - func makeTodoListViewModel(category: TodoCategory) -> TodoListViewModel { - TodoListViewModel( - fetchTodosUseCase: container.resolve(FetchTodosUseCase.self), - fetchTodoByIdUseCase: container.resolve(FetchTodoByIdUseCase.self), - upsertTodoUseCase: container.resolve(UpsertTodoUseCase.self), - deleteTodoUseCase: container.resolve(DeleteTodoUseCase.self), - undoDeleteTodoUseCase: container.resolve(UndoDeleteTodoUseCase.self), - category: category - ) - } - - func makeTodoDetailViewModel( - todoId: String, - showEditButton: Bool = true - ) -> TodoDetailViewModel { - TodoDetailViewModel( - fetchTodoUseCase: container.resolve(FetchTodoByIdUseCase.self), - fetchReferenceItemsUseCase: container.resolve(FetchReferenceItemsUseCase.self), - upsertUseCase: container.resolve(UpsertTodoUseCase.self), - todoId: todoId, - showEditButton: showEditButton - ) - } } private enum MainTabSplitStyle { diff --git a/DevLog/UI/Main/MainViewCoordinator.swift b/DevLog/UI/Main/MainViewCoordinator.swift index 3b2ebb10..8e89fe9e 100644 --- a/DevLog/UI/Main/MainViewCoordinator.swift +++ b/DevLog/UI/Main/MainViewCoordinator.swift @@ -14,8 +14,14 @@ final class MainViewCoordinator { let pushNotificationListViewModel: PushNotificationListViewModel let profileViewModel: ProfileViewModel var todoIdToPresent: TodoIdItem? + private let diContainer: DIContainer + @ObservationIgnored + private var todoListViewModel: TodoListViewModel? + @ObservationIgnored + private var todoDetailViewModel: TodoDetailViewModel? init(container: DIContainer) { + self.diContainer = container self.mainViewModel = MainViewModel( unreadPushCountUseCase: container.resolve(ObserveUnreadPushCountUseCase.self) ) @@ -36,4 +42,43 @@ final class MainViewCoordinator { updateHeatmapActivityTypesUseCase: container.resolve(UpdateHeatmapActivityTypesUseCase.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), + 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 + } }