Skip to content

Commit 5d95643

Browse files
authored
[#357] 검색 로직에 Todo 레퍼런스 숫자를 추가한다 (#362)
* fix: 검색 시 인덱스 이슈 해결 * fix: 검색 시 검색어가 없을 때 LoadingView가 간헐적으로 뜨는 현상 해결 * feat: '#숫자' 를 붙이면 Todo의 번호로만 검색되도록 구현
1 parent 471dbf0 commit 5d95643

File tree

5 files changed

+69
-12
lines changed

5 files changed

+69
-12
lines changed

DevLog/Infra/Service/TodoService.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,13 @@ final class TodoService {
146146
let snapshot = try await firestoreQuery.getDocuments()
147147
let todos = snapshot.documents.compactMap { makeResponse(from: $0) }
148148

149+
let todoNumber = searchedTodoNumber(from: trimmedKeyword)
149150
let filtered = todos.filter { todo in
150-
todo.title.localizedCaseInsensitiveContains(trimmedKeyword)
151+
if let todoNumber, todo.number == todoNumber {
152+
return true
153+
}
154+
155+
return todo.title.localizedCaseInsensitiveContains(trimmedKeyword)
151156
|| todo.content.localizedCaseInsensitiveContains(trimmedKeyword)
152157
|| todo.tags.contains { $0.localizedCaseInsensitiveContains(trimmedKeyword) }
153158
}
@@ -483,6 +488,19 @@ private extension TodoService {
483488
)
484489
}
485490

491+
func searchedTodoNumber(from keyword: String) -> Int? {
492+
guard keyword.hasPrefix("#") else {
493+
return nil
494+
}
495+
496+
let numberText = String(keyword.dropFirst())
497+
guard !numberText.isEmpty, numberText.allSatisfy(\.isNumber) else {
498+
return nil
499+
}
500+
501+
return Int(numberText)
502+
}
503+
486504
enum TodoFieldKey: String {
487505
case id
488506
case isPinned

DevLog/Presentation/ViewModel/SearchViewModel.swift

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ final class SearchViewModel: Store {
107107
cancelDebounce()
108108
state.webPages = []
109109
state.todos = []
110+
state.isLoading = false
110111
} else {
111112
state.isLoading = true
112113
scheduleDebouncedQuery(query)
@@ -136,12 +137,16 @@ final class SearchViewModel: Store {
136137
do {
137138
send(.setLoading(true))
138139
defer { send(.setLoading(false)) }
140+
let searchesTodoOnly = searchesTodoOnly(query)
139141
async let todos = fetchTodosUseCase.execute(TodoQuery(keyword: query), cursor: nil)
140-
async let webPages = fetchWebPagesUseCase.execute(query)
142+
async let webPageItems = fetchWebPageItems(
143+
query: query,
144+
searchesTodoOnly: searchesTodoOnly
145+
)
141146
let todoItems = try await todos.items.compactMap { TodoListItem(from: $0) }
142-
let webPageItems = try await webPages.map { WebPageItem(from: $0) }
147+
let resolvedWebPageItems = try await webPageItems
143148
send(.fetchTodos(todoItems))
144-
send(.fetchWebPage(webPageItems))
149+
send(.fetchWebPage(resolvedWebPageItems))
145150
} catch {
146151
send(.setAlert(true))
147152
}
@@ -177,6 +182,22 @@ private extension SearchViewModel {
177182
searchDebounceTask = nil
178183
}
179184

185+
func searchesTodoOnly(_ query: String) -> Bool {
186+
query.trimmingCharacters(in: .whitespacesAndNewlines).hasPrefix("#")
187+
}
188+
189+
func fetchWebPageItems(
190+
query: String,
191+
searchesTodoOnly: Bool
192+
) async throws -> [WebPageItem] {
193+
if searchesTodoOnly {
194+
return []
195+
}
196+
197+
let webPages = try await fetchWebPagesUseCase.execute(query)
198+
return webPages.map { WebPageItem(from: $0) }
199+
}
200+
180201
func saveRecentQueries(_ queries: OrderedSet<String>) {
181202
updateRecentSearchQueriesUseCase.execute(Array(queries))
182203
}

DevLog/Resource/Localizable.xcstrings

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1495,13 +1495,13 @@
14951495
"en" : {
14961496
"stringUnit" : {
14971497
"state" : "translated",
1498-
"value" : "Search"
1498+
"value" : "Search (e.g. #123)"
14991499
}
15001500
},
15011501
"ko" : {
15021502
"stringUnit" : {
15031503
"state" : "translated",
1504-
"value" : "검색"
1504+
"value" : "검색 (예: #123)"
15051505
}
15061506
}
15071507
}
@@ -2950,13 +2950,13 @@
29502950
"en" : {
29512951
"stringUnit" : {
29522952
"state" : "translated",
2953-
"value" : "Search %1$@"
2953+
"value" : "Search %1$@ (e.g. #123)"
29542954
}
29552955
},
29562956
"ko" : {
29572957
"stringUnit" : {
29582958
"state" : "translated",
2959-
"value" : "%1$@ 검색"
2959+
"value" : "%1$@ 검색 (예: #123)"
29602960
}
29612961
}
29622962
}
@@ -3367,4 +3367,4 @@
33673367
}
33683368
},
33693369
"version" : "1.0"
3370-
}
3370+
}

DevLog/UI/Search/SearchView.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,16 @@ struct SearchView: View {
6161
@ViewBuilder
6262
private var searchableContent: some View {
6363
Group {
64-
if viewModel.state.isLoading {
65-
LoadingView()
66-
} else if viewModel.state.searchQuery.isEmpty {
64+
if viewModel.state.searchQuery.isEmpty {
6765
if viewModel.state.recentQueries.isEmpty {
6866
searchInstruction
6967
} else {
7068
ScrollView {
7169
recentQueries
7270
}
7371
}
72+
} else if viewModel.state.isLoading {
73+
LoadingView()
7474
} else if viewModel.state.webPages.isEmpty && viewModel.state.todos.isEmpty {
7575
emptySearchResult
7676
} else {

Firebase/firestore.index.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,24 @@
3232
}
3333
]
3434
},
35+
{
36+
"collectionGroup": "todoLists",
37+
"queryScope": "COLLECTION",
38+
"fields": [
39+
{
40+
"fieldPath": "deletedAt",
41+
"order": "ASCENDING"
42+
},
43+
{
44+
"fieldPath": "createdAt",
45+
"order": "DESCENDING"
46+
},
47+
{
48+
"fieldPath": "__name__",
49+
"order": "ASCENDING"
50+
}
51+
]
52+
},
3553
{
3654
"collectionGroup": "todoLists",
3755
"queryScope": "COLLECTION",

0 commit comments

Comments
 (0)