Skip to content

Commit cb33de8

Browse files
authored
[#54] HomeVIew를 개선한다 (#77)
* feat: Seachable 제거 * style: 뷰 분리 * feat: plus 버튼을 탭하면 TODO를 선택하고 작성할 수 있는 뷰를 띄움 * ui: 버튼 스타일 제거 * style: content, sheet 변수명 수정 * feat: HomeView에서 SearchView 제거 * feat: 돋보기 버튼을 탭하면 서치뷰가 풀스크린으로 뜨도록 구현 * refactor: Action 통합화 * feat: SearchView 전면 개정 * style: 주석 수정 * style: 주석 수정
1 parent 6b6e953 commit cb33de8

8 files changed

Lines changed: 339 additions & 391 deletions

File tree

DevLog/Presentation/ViewModel/HomeViewModel.swift

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ final class HomeViewModel: Store {
1212
// UI
1313
var todoKindPreferences = TodoKind.allCases.map { TodoKindPreference(kind: $0, isVisible: true) }
1414
var pinnedTodos: [Todo] = []
15+
var showTodoKindPicker: Bool = false
16+
var showTodoEditor: Bool = false
17+
var showSearchView: Bool = false
18+
var selectedTodoKind: TodoKind?
1519

1620
// User Input
1721
var searchText: String = ""
@@ -29,15 +33,18 @@ final class HomeViewModel: Store {
2933
case onAppear
3034

3135
// User
32-
case tapEllipsisButton
36+
case tapTodoKind(TodoKind)
3337
case upsertTodo(Todo)
3438
case orderTodoKindPreferences([TodoKindPreference])
3539

3640
// Binding
3741
case updateSearching(Bool)
3842
case updateSearchText(String)
39-
case closeOrderingSheet
40-
case closeToast
43+
case setReorderTodo(Bool)
44+
case setShowTodoEditor(Bool)
45+
case setShowTodoKindPicker(Bool)
46+
case setShowSearchView(Bool)
47+
case setShowToast(Bool)
4148

4249
// Call from run
4350
case didFetchPinnedTodos([Todo])
@@ -61,29 +68,40 @@ final class HomeViewModel: Store {
6168
}
6269

6370
func reduce(with action: Action) -> [SideEffect] {
71+
var state = self.state
6472
switch action {
6573
case .onAppear:
6674
return [.fetchPinnedTodos]
67-
case.tapEllipsisButton:
68-
state.reorderTodo = true
69-
case .updateSearching(let isSearching):
70-
state.isSearching = isSearching
71-
case .updateSearchText(let text):
72-
state.searchText = text
73-
case .upsertTodo(let todo):
74-
return [.upsertTodo(todo)]
75-
case .orderTodoKindPreferences(let preferences):
76-
state.todoKindPreferences = preferences
77-
78-
case .closeOrderingSheet:
79-
state.reorderTodo = false
80-
case .closeToast:
81-
state.showToast = false
82-
75+
case .tapTodoKind(let kind):
76+
state.selectedTodoKind = kind
77+
state.showTodoKindPicker = false
78+
state.showTodoEditor = true
79+
case .updateSearching(let value):
80+
state.isSearching = value
81+
case .updateSearchText(let value):
82+
state.searchText = value
83+
case .setReorderTodo(let value):
84+
state.reorderTodo = value
85+
case .setShowTodoEditor(let value):
86+
state.showTodoEditor = value
87+
if !value {
88+
state.selectedTodoKind = nil
89+
}
90+
case .setShowTodoKindPicker(let value):
91+
state.showTodoKindPicker = value
92+
case .setShowSearchView(let value):
93+
state.showSearchView = value
94+
case .setShowToast(let value):
95+
state.showToast = value
96+
case .upsertTodo(let value):
97+
return [.upsertTodo(value)]
98+
case .orderTodoKindPreferences(let value):
99+
state.todoKindPreferences = value
83100
case .didFetchPinnedTodos(let todos):
84101
state.pinnedTodos = todos
85102
}
86-
103+
104+
self.state = state
87105
return []
88106
}
89107

DevLog/Presentation/ViewModel/PushNotificationViewModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// NotificationViewModel.swift
2+
// PushNotificationViewModel.swift
33
// DevLog
44
//
55
// Created by 최윤진 on 11/22/25.

DevLog/Presentation/ViewModel/SearchViewModel.swift

Lines changed: 24 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -10,93 +10,61 @@ import OrderedCollections
1010

1111
final class SearchViewModel: Store {
1212
struct State {
13-
var alertMsg: String = ""
1413
var isLoading: Bool = false
1514
var isSearching: Bool = false
16-
var newURL: String = "https://"
1715
var searchQuery: String = ""
1816
var selectedWebPage: WebPageItem?
19-
var showAlert: Bool = false
20-
var alertTitle: String = ""
21-
var alertType: AlertType?
22-
var alertMessage: String = ""
2317
var webPages: OrderedSet<WebPageItem> = []
2418
var filteredWebPages: [WebPageItem] {
25-
if searchQuery.isEmpty {
26-
// TODO: 자주 방문한 것을 보여주는 기능으로 변경 필요
27-
return []
28-
} else {
29-
return webPages.filter {
30-
$0.title.localizedCaseInsensitiveContains(searchQuery) ||
31-
$0.displayURL.localizedCaseInsensitiveContains(searchQuery)
32-
}
19+
webPages.filter {
20+
$0.title.localizedCaseInsensitiveContains(searchQuery) ||
21+
$0.displayURL.localizedCaseInsensitiveContains(searchQuery)
3322
}
3423
}
3524
}
3625

3726
enum Action {
38-
case addWebPage(WebPageItem? = nil)
39-
case deleteWebPage(item: WebPageItem, fromEffect: Bool = false)
27+
case onAppear
4028
case fetchWebPage([WebPageItem]? = nil)
4129
case selectWebPage(WebPageItem)
42-
case setAlert(isPresented: Bool, type: AlertType? = nil)
4330
case setLoading(Bool)
44-
case setNewURL(String = "https://")
4531
case setSearching(Bool)
4632
case setSearchQuery(String)
4733
}
4834

4935
enum SideEffect {
5036
case fetch
51-
case add(String)
52-
case delete(WebPageItem)
53-
}
54-
55-
enum AlertType {
56-
case addWebPage, error
5737
}
5838

5939
@Published private(set) var state: State = .init()
6040
private let fetchWebPagesUseCase: FetchWebPagesUseCase
61-
private let addWebPageUseCase: AddWebPageUseCase
62-
private let deleteWebPageUseCase: DeleteWebPageUseCase
6341

64-
init(
65-
fetchWebPagesUseCase: FetchWebPagesUseCase,
66-
addWebPageUseCase: AddWebPageUseCase,
67-
deleteWebPageUseCase: DeleteWebPageUseCase
68-
) {
42+
init(fetchWebPagesUseCase: FetchWebPagesUseCase) {
6943
self.fetchWebPagesUseCase = fetchWebPagesUseCase
70-
self.addWebPageUseCase = addWebPageUseCase
71-
self.deleteWebPageUseCase = deleteWebPageUseCase
7244
}
7345

7446
func reduce(with action: Action) -> [SideEffect] {
7547
var state = self.state
7648

7749
switch action {
78-
case .addWebPage(let item):
79-
guard let item else { return [.add(state.newURL)] }
80-
state.webPages.append(item)
81-
case .deleteWebPage(let info, let fromEffect):
82-
if !fromEffect { return [.delete(info)] }
83-
state.webPages.removeAll { $0.url == info.url }
50+
case .onAppear:
51+
state.isSearching = true
52+
self.state = state
53+
return [.fetch]
8454
case .fetchWebPage(let items):
85-
guard let items else { return [.fetch] }
55+
guard let items else {
56+
self.state = state
57+
return [.fetch]
58+
}
8659
state.webPages = OrderedSet(items)
87-
case .selectWebPage(let newValue):
88-
state.selectedWebPage = newValue
89-
case .setAlert(let isPresented, let type):
90-
setAlert(isPresented: isPresented, for: type)
91-
return []
92-
case .setLoading(let newValue):
93-
state.isLoading = newValue
94-
case .setNewURL(let newValue):
95-
state.newURL = newValue
96-
case .setSearching(let newValue):
97-
state.isSearching = newValue
98-
case .setSearchQuery(let newValue):
99-
state.searchQuery = newValue
60+
case .selectWebPage(let item):
61+
state.selectedWebPage = item
62+
case .setLoading(let isLoading):
63+
state.isLoading = isLoading
64+
case .setSearching(let isSearching):
65+
state.isSearching = isSearching
66+
case .setSearchQuery(let query):
67+
state.searchQuery = query
10068
}
10169

10270
self.state = state
@@ -108,56 +76,14 @@ final class SearchViewModel: Store {
10876
case .fetch:
10977
Task {
11078
do {
111-
defer { send(.setLoading(false)) }
11279
send(.setLoading(true))
113-
let items = try await fetchWebPagesUseCase.execute().map { WebPageItem(from: $0) }
114-
send(.fetchWebPage(items))
115-
} catch {
116-
send(.setAlert(isPresented: true, type: .error))
117-
}
118-
}
119-
case .add(let urlString):
120-
Task {
121-
do {
12280
defer { send(.setLoading(false)) }
123-
send(.setLoading(true))
124-
let metadata = try await addWebPageUseCase.execute(urlString)
125-
let item = WebPageItem(from: metadata)
126-
send(.addWebPage(item))
127-
send(.setNewURL())
128-
} catch {
129-
send(.setAlert(isPresented: true, type: .error))
130-
}
131-
}
132-
case .delete(let item):
133-
Task {
134-
do {
135-
defer { send(.setLoading(false)) }
136-
send(.setLoading(true))
137-
try await deleteWebPageUseCase.execute(item.url.absoluteString)
138-
send(.deleteWebPage(item: item, fromEffect: true))
81+
let items = try await self.fetchWebPagesUseCase.execute().map { WebPageItem(from: $0) }
82+
send(.fetchWebPage(items))
13983
} catch {
140-
send(.setAlert(isPresented: true, type: .error))
84+
14185
}
14286
}
14387
}
14488
}
14589
}
146-
147-
private extension SearchViewModel {
148-
func setAlert(isPresented: Bool, for type: AlertType?) {
149-
switch type {
150-
case .addWebPage:
151-
state.alertTitle = "웹 페이지 추가"
152-
state.alertMessage = ""
153-
case .error:
154-
state.alertTitle = "오류"
155-
state.alertMessage = "문제가 발생했습니다. 잠시 다시 시도해주세요."
156-
case .none:
157-
state.alertTitle = ""
158-
state.alertMessage = ""
159-
}
160-
state.alertType = type
161-
state.showAlert = isPresented
162-
}
163-
}

DevLog/Resource/Localizable.xcstrings

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@
66
},
77
"%@ 검색" : {
88

9-
},
10-
"DevLog 검색" : {
11-
129
},
1310
"lime_green" : {
1411
"extractionState" : "manual",
@@ -221,6 +218,9 @@
221218
},
222219
"TODO" : {
223220

221+
},
222+
"TODO 종류" : {
223+
224224
},
225225
"TODO 편집" : {
226226

@@ -245,14 +245,20 @@
245245
}
246246
}
247247
},
248-
"URL" : {
248+
"Web Pages" : {
249249

250250
},
251251
"개인정보 처리방침" : {
252252

253253
},
254254
"검색" : {
255255

256+
},
257+
"검색 결과가 없습니다." : {
258+
259+
},
260+
"검색어를 입력해 저장한 앱 컨텐츠를 찾아보세요." : {
261+
256262
},
257263
"계정 삭제" : {
258264

@@ -316,9 +322,6 @@
316322
},
317323
"알림" : {
318324

319-
},
320-
"앱 내 컨텐츠를 검색할 수 있어요." : {
321-
322325
},
323326
"어제" : {
324327

@@ -337,9 +340,6 @@
337340
},
338341
"작성된 알림이 없습니다." : {
339342

340-
},
341-
"저장된 웹페이지가 없습니다.\n우측 '+' 버튼을 눌러 웹페이지를 추가해보세요." : {
342-
343343
},
344344
"정렬 옵션" : {
345345

DevLog/UI/Common/MainView.swift

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,6 @@ struct MainView: View {
2828
Image(systemName: "bell.fill")
2929
Text("알림")
3030
}
31-
SearchView(viewModel: SearchViewModel(
32-
fetchWebPagesUseCase: container.resolve(FetchWebPagesUseCase.self),
33-
addWebPageUseCase: container.resolve(AddWebPageUseCase.self),
34-
deleteWebPageUseCase: container.resolve(DeleteWebPageUseCase.self)
35-
))
36-
.tabItem {
37-
Image(systemName: "magnifyingglass")
38-
Text("검색")
39-
}
4031
ProfileView(viewModel: ProfileViewModel(
4132
fetchUserDataUseCase: container.resolve(FetchUserDataUseCase.self),
4233
upsertStatusMessageUseCase: container.resolve(UpsertStatusMessageUseCase.self)

0 commit comments

Comments
 (0)