Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 0 additions & 22 deletions DevLog/Presentation/Structure/PinnedTodoItem.swift

This file was deleted.

26 changes: 26 additions & 0 deletions DevLog/Presentation/Structure/RecentTodoItem.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// RecentTodoItem.swift
// DevLog
//
// Created by Codex on 3/6/26.
//

import Foundation

struct RecentTodoItem: Identifiable, Hashable {
let id: String
let title: String
let isPinned: Bool
let updatedAt: Date
let tags: [String]
let kind: TodoKind

init(from todo: Todo) {
self.id = todo.id
self.title = todo.title
self.isPinned = todo.isPinned
self.updatedAt = todo.updatedAt
self.tags = todo.tags
self.kind = todo.kind
}
}
66 changes: 43 additions & 23 deletions DevLog/Presentation/ViewModel/HomeViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Foundation
final class HomeViewModel: Store {
struct State: Equatable {
var todoKindPreferences = TodoKind.allCases.map { TodoKindPreference(kind: $0, isVisible: true) }
var pinnedTodos: [PinnedTodoItem] = []
var recentTodos: [RecentTodoItem] = []
var webPages: [WebPageItem] = []
var showContentPicker: Bool = false
var showTodoEditor: Bool = false
Expand All @@ -21,7 +21,7 @@ final class HomeViewModel: Store {
var searchText: String = ""
var isSearching: Bool = false
var reorderTodo: Bool = false
var isPinnedLoading: Bool = false
var isRecentTodosLoading: Bool = false
var isWebPageLoading: Bool = false
var isWebPageInputLoading: Bool = false
var showAlert: Bool = false
Expand Down Expand Up @@ -51,9 +51,9 @@ final class HomeViewModel: Store {
case undoDeleteWebPage
case confirmDeleteWebPage
case setToast(isPresented: Bool, type: ToastType? = nil)
case fetchPinnedTodos([PinnedTodoItem])
case fetchRecentTodos([RecentTodoItem])
case fetchWebPages([WebPageItem])
case setPinnedLoading(Bool)
case setRecentTodosLoading(Bool)
case setWebPageLoading(Bool)
case setWebPageInputLoading(Bool)
}
Expand All @@ -62,7 +62,7 @@ final class HomeViewModel: Store {
case upsertTodo(Todo)
case addWebPage(String)
case deleteWebPage(String)
case fetchPinnedTodos
case fetchRecentTodos
case fetchWebPages
case showModalAfterDelay(ModalType)
}
Expand Down Expand Up @@ -119,7 +119,7 @@ final class HomeViewModel: Store {
.addWebPage, .confirmDeleteWebPage:
effects = reduceByView(action, state: &state)

case .fetchPinnedTodos, .fetchWebPages, .setPinnedLoading,
case .fetchRecentTodos, .fetchWebPages, .setRecentTodosLoading,
.setWebPageLoading, .setWebPageInputLoading:
effects = reduceByRun(action, state: &state)
}
Expand All @@ -134,6 +134,27 @@ final class HomeViewModel: Store {
Task {
do {
try await upsertTodoUseCase.execute(todo)
let page = try await fetchRecentTodos()
let items = page.items
.filter { $0.createdAt != $0.updatedAt }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

이 필터 조건 ($0.createdAt != $0.updatedAt) 때문에 새로 생성된 Todo 항목이 '최근 수정' 목록에 표시되지 않을 수 있습니다. upsertTodo가 새 항목 생성에도 사용되므로, 생성 직후 목록에 나타나지 않는 것은 사용자에게 혼란을 줄 수 있습니다. '최근 수정'이라는 이름에 맞게, 생성된 항목을 제외하는 것이 의도된 동작일 수 있으나, upsert 후 목록을 갱신하는 현재 로직과는 맞지 않아 보입니다. 이 필터 조건을 제거하여 생성된 항목도 목록에 포함시키는 것을 고려해 보세요.

.prefix(5)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

매직 넘버 5가 사용되었습니다. 이 값을 private static let recentTodosLimit = 5와 같이 명명된 상수로 추출하면 코드의 가독성과 유지보수성이 향상됩니다. 이 상수는 upsertTodofetchRecentTodos 두 곳에서 모두 사용될 수 있습니다.

.map { RecentTodoItem(from: $0) }
send(.fetchRecentTodos(items))
} catch {
send(.setAlert(isPresented: true, type: .error))
}
}
case .fetchRecentTodos:
Task {
do {
defer { send(.setRecentTodosLoading(false)) }
send(.setRecentTodosLoading(true))
let page = try await fetchRecentTodos()
let items = page.items
.filter { $0.createdAt != $0.updatedAt }
.prefix(5)
.map { RecentTodoItem(from: $0) }
send(.fetchRecentTodos(items))
Comment on lines +152 to +157
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

이 로직 블록은 upsertTodo 사이드 이펙트 핸들러(137-142행)에 있는 로직과 정확히 일치합니다. 코드 중복을 피하고 유지보수성을 향상시키기 위해 이 로직을 별도의 private 헬퍼 함수로 추출하여 두 곳에서 모두 호출하도록 리팩토링하는 것을 권장합니다.

} catch {
send(.setAlert(isPresented: true, type: .error))
}
Expand Down Expand Up @@ -164,18 +185,6 @@ final class HomeViewModel: Store {
send(.setAlert(isPresented: true, type: .error))
}
}
case .fetchPinnedTodos:
Task {
do {
defer { send(.setPinnedLoading(false)) }
send(.setPinnedLoading(true))
let page = try await fetchTodosUseCase.execute(TodoQuery(isPinned: true), cursor: nil)
let todos = page.items
send(.fetchPinnedTodos(todos.map { PinnedTodoItem(from: $0) }))
} catch {
send(.setAlert(isPresented: true, type: .error))
}
}
case .fetchWebPages:
Task {
do {
Expand Down Expand Up @@ -249,7 +258,7 @@ private extension HomeViewModel {
func reduceByView(_ action: Action, state: inout State) -> [SideEffect] {
switch action {
case .onAppear:
return [.fetchPinnedTodos, .fetchWebPages]
return [.fetchRecentTodos, .fetchWebPages]
case .updateSearching(let isSearching):
state.isSearching = isSearching
case .updateSearchText(let text):
Expand All @@ -275,8 +284,8 @@ private extension HomeViewModel {

func reduceByRun(_ action: Action, state: inout State) -> [SideEffect] {
switch action {
case .fetchPinnedTodos(let todos):
state.pinnedTodos = todos
case .fetchRecentTodos(let todos):
state.recentTodos = todos
case .fetchWebPages(let pages):
let filteredPages: [WebPageItem]
if let (pendingPage, _) = pendingTask {
Expand All @@ -285,8 +294,8 @@ private extension HomeViewModel {
filteredPages = pages
}
state.webPages = filteredPages
case .setPinnedLoading(let isLoading):
state.isPinnedLoading = isLoading
case .setRecentTodosLoading(let isLoading):
state.isRecentTodosLoading = isLoading
case .setWebPageLoading(let isLoading):
state.isWebPageLoading = isLoading
case .setWebPageInputLoading(let isLoading):
Expand Down Expand Up @@ -350,4 +359,15 @@ private extension HomeViewModel {
}
return "https://" + trimmed
}

func fetchRecentTodos() async throws -> TodoPage {
try await fetchTodosUseCase.execute(
TodoQuery(
sortTarget: .updatedAt,
sortOrder: .latest,
pageSize: 100
),
cursor: nil
)
}
}
5 changes: 4 additions & 1 deletion DevLog/Resource/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,10 @@
"최근 검색" : {

},
"최근에 중요 표시를 한 Todo가 표시됩니다." : {
"최근 수정" : {

},
"최근 수정한 Todo가 없습니다." : {

},
"추가" : {
Expand Down
Loading