Skip to content

Commit 698ab4e

Browse files
committed
feat: TODO에서 다른 TODO를 참조 시 커스텀 카테고리로 되어 있어도 참조가 가능하도록 구현
1 parent b29a8b5 commit 698ab4e

11 files changed

Lines changed: 95 additions & 32 deletions
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//
2+
// TodoReferenceResponse.swift
3+
// DevLog
4+
//
5+
// Created by opfic on 3/30/26.
6+
//
7+
8+
import Foundation
9+
10+
struct TodoReferenceResponse {
11+
let id: String
12+
let number: Int
13+
let title: String
14+
let category: TodoCategoryResponse
15+
}

DevLog/Data/Repository/TodoRepositoryImpl.swift

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,35 @@ final class TodoRepositoryImpl: TodoRepository {
5959
return try resolve(todoResponse, userTodoCategories: userTodoCategories).toDomain()
6060
}
6161

62-
func fetchReferenceItems(_ numbers: [Int]) async throws -> [Int: TodoReferenceItem] {
63-
try await todoService.fetchReferenceItems(numbers)
62+
func fetchReferences(_ numbers: [Int]) async throws -> [Int: TodoReference] {
63+
async let responseTask = todoService.fetchReferences(numbers)
64+
async let preferencesTask = todoCategoryService.fetchPreferences()
65+
66+
let (responses, preferences) = try await (responseTask, preferencesTask)
67+
let userTodoCategories: [UserTodoCategory] = preferences.compactMap { preference in
68+
guard case .user(let category) = preference.category else {
69+
return nil
70+
}
71+
72+
return category
73+
}
74+
75+
return try responses.reduce(into: [Int: TodoReference]()) { partialResult, pair in
76+
let response = try resolve(pair.value, userTodoCategories: userTodoCategories)
77+
let category: TodoCategory
78+
switch response.category {
79+
case .decoded(let decodedCategory):
80+
category = decodedCategory
81+
case .raw(let value):
82+
throw DataError.invalidData("TodoReferenceResponse.category is invalid: \(value)")
83+
}
84+
85+
partialResult[pair.key] = TodoReference(
86+
id: response.id,
87+
title: response.title,
88+
category: category
89+
)
90+
}
6491
}
6592

6693
func upsertTodo(_ todo: Todo) async throws {
@@ -117,4 +144,35 @@ private extension TodoRepositoryImpl {
117144
category: .decoded(category)
118145
)
119146
}
147+
148+
func resolve(
149+
_ response: TodoReferenceResponse,
150+
userTodoCategories: [UserTodoCategory]
151+
) throws -> TodoReferenceResponse {
152+
let categoryID: String
153+
switch response.category {
154+
case .raw(let value):
155+
categoryID = value
156+
case .decoded:
157+
return response
158+
}
159+
160+
let category: TodoCategory
161+
if let systemTodoCategory = SystemTodoCategory(rawValue: categoryID) {
162+
category = .system(systemTodoCategory)
163+
} else if let userTodoCategory = userTodoCategories.first(where: {
164+
$0.id == categoryID
165+
}) {
166+
category = .user(userTodoCategory)
167+
} else {
168+
throw DataError.invalidData("TodoReferenceResponse.category is invalid: \(categoryID)")
169+
}
170+
171+
return TodoReferenceResponse(
172+
id: response.id,
173+
number: response.number,
174+
title: response.title,
175+
category: .decoded(category)
176+
)
177+
}
120178
}

DevLog/Domain/Entity/TodoReferenceItem.swift renamed to DevLog/Domain/Entity/TodoReference.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
//
2-
// TodoReferenceItem.swift
2+
// TodoReference.swift
33
// DevLog
44
//
55
// Created by opfic on 3/25/26.
66
//
77

88
import Foundation
99

10-
struct TodoReferenceItem: Equatable {
10+
struct TodoReference: Equatable {
1111
let id: String
1212
let title: String
1313
let category: TodoCategory

DevLog/Domain/Protocol/TodoRepository.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import Foundation
1010
protocol TodoRepository {
1111
func fetchTodos(_ query: TodoQuery, cursor: TodoCursor?) async throws -> TodoPage
1212
func fetchTodo(_ todoId: String) async throws -> Todo
13-
func fetchReferenceItems(_ numbers: [Int]) async throws -> [Int: TodoReferenceItem]
13+
func fetchReferences(_ numbers: [Int]) async throws -> [Int: TodoReference]
1414
func upsertTodo(_ todo: Todo) async throws
1515
func deleteTodo(_ todoId: String) async throws
1616
func undoDeleteTodo(_ todoId: String) async throws

DevLog/Domain/UseCase/Todo/Fetch/FetchReferenceItemsUseCase.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
//
77

88
protocol FetchReferenceItemsUseCase {
9-
func execute(_ numbers: [Int]) async throws -> [Int: TodoReferenceItem]
9+
func execute(_ numbers: [Int]) async throws -> [Int: TodoReference]
1010
}

DevLog/Domain/UseCase/Todo/Fetch/FetchReferenceItemsUseCaseImpl.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ final class FetchReferenceItemsUseCaseImpl: FetchReferenceItemsUseCase {
1212
self.repository = repository
1313
}
1414

15-
func execute(_ numbers: [Int]) async throws -> [Int: TodoReferenceItem] {
16-
try await repository.fetchReferenceItems(numbers)
15+
func execute(_ numbers: [Int]) async throws -> [Int: TodoReference] {
16+
try await repository.fetchReferences(numbers)
1717
}
1818
}

DevLog/Infra/Service/TodoService.swift

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ final class TodoService {
241241
}
242242
}
243243

244-
func fetchReferenceItems(_ numbers: [Int]) async throws -> [Int: TodoReferenceItem] {
244+
func fetchReferences(_ numbers: [Int]) async throws -> [Int: TodoReferenceResponse] {
245245
guard let uid = Auth.auth().currentUser?.uid else { throw AuthError.notAuthenticated }
246246

247247
let uniqueNumbers = Array(Set(numbers)).sorted()
@@ -265,7 +265,7 @@ final class TodoService {
265265
return documents
266266
}
267267

268-
return snapshots.reduce(into: [Int: TodoReferenceItem]()) { partialResult, document in
268+
return snapshots.reduce(into: [Int: TodoReferenceResponse]()) { partialResult, document in
269269
let data = document.data()
270270
guard
271271
!(data[TodoFieldKey.deletingAt.rawValue] is Timestamp),
@@ -274,21 +274,11 @@ final class TodoService {
274274
return
275275
}
276276

277-
let todoCategory: TodoCategory
278-
switch response.category {
279-
case .raw(let category):
280-
guard let systemTodoCategory = SystemTodoCategory(rawValue: category) else {
281-
return
282-
}
283-
todoCategory = .system(systemTodoCategory)
284-
case .decoded(let category):
285-
todoCategory = category
286-
}
287-
288-
partialResult[response.number] = TodoReferenceItem(
277+
partialResult[response.number] = TodoReferenceResponse(
289278
id: response.id,
279+
number: response.number,
290280
title: response.title,
291-
category: todoCategory
281+
category: response.category
292282
)
293283
}
294284
}

DevLog/Presentation/ViewModel/TodoDetailViewModel.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ final class TodoDetailViewModel: Store {
1212
struct State: Equatable {
1313
var todo: Todo?
1414
var selectedTodoId: TodoIdItem?
15-
var referenceItems: [Int: TodoReferenceItem] = [:]
15+
var referenceItems: [Int: TodoReference] = [:]
1616
var isLoading: Bool = false
1717
var showAlert: Bool = false
1818
var showEditor: Bool = false
@@ -28,7 +28,7 @@ final class TodoDetailViewModel: Store {
2828
case setShowInfo(Bool)
2929
case setSelectedTodoId(TodoIdItem?)
3030
case setTodo(Todo)
31-
case setReferenceItems([Int: TodoReferenceItem])
31+
case setReferenceItems([Int: TodoReference])
3232
case setLoading(Bool)
3333
case upsertTodo(Todo)
3434
}
@@ -108,7 +108,7 @@ final class TodoDetailViewModel: Store {
108108
case .resolveMarkdown(let content):
109109
Task {
110110
let numbers = content.todoReferenceNumbers
111-
var referenceItems = [Int: TodoReferenceItem]()
111+
var referenceItems = [Int: TodoReference]()
112112

113113
if !numbers.isEmpty {
114114
do {

DevLog/Presentation/ViewModel/TodoEditorViewModel.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ final class TodoEditorViewModel: Store {
5050
var selectedTodoId: TodoIdItem?
5151
var title: String = ""
5252
var content: String = ""
53-
var referenceItems: [Int: TodoReferenceItem] = [:]
53+
var referenceItems: [Int: TodoReference] = [:]
5454
var dueDate: Date?
5555
var showInfo: Bool = false
5656
var tags: OrderedSet<String> = []
@@ -83,7 +83,7 @@ final class TodoEditorViewModel: Store {
8383
case setTagText(String)
8484
case setTitle(String)
8585
case setCategories([TodoCategory])
86-
case setReferenceItems([Int: TodoReferenceItem])
86+
case setReferenceItems([Int: TodoReference])
8787
}
8888

8989
enum SideEffect {
@@ -227,7 +227,7 @@ final class TodoEditorViewModel: Store {
227227
case .resolveMarkdown(let content):
228228
Task {
229229
let numbers = content.todoReferenceNumbers
230-
var referenceItems = [Int: TodoReferenceItem]()
230+
var referenceItems = [Int: TodoReference]()
231231

232232
if !numbers.isEmpty {
233233
do {

DevLog/UI/Common/TodoDetailContentView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import MarkdownUI
1111
struct TodoDetailContentView: View {
1212
let title: String
1313
let content: String
14-
let referenceItems: [Int: TodoReferenceItem]
14+
let referenceItems: [Int: TodoReference]
1515
var number: Int?
1616
var activityLabel: String?
1717
var onOpenTodoID: ((String) -> Void)?

0 commit comments

Comments
 (0)