Skip to content

Commit f04b479

Browse files
authored
[#98] 웹페이지 제거 기능을 적용하는 뷰를 구성한다 (#108)
* feat: 웹페이지를 HomeView에서 볼수 있도록 ui 구현 * feat: 유즈케이스 연결 및 ui 일부 수정 * refactor: 불필요 리턴 코드 제거 * feat: LoadingView 추가 및 스와이프 액션으로 웹사이트 제거 * ui: push -> navigationlink, title 추가 * refactor: 불필요 상태 변화 제거
1 parent 2215030 commit f04b479

11 files changed

Lines changed: 165 additions & 44 deletions

File tree

.swiftlint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ disabled_rules:
22
- nesting
33
- multiple_closures_with_trailing_closure
44
- trailing_whitespace
5+
- type_body_length

DevLog/Data/Repository/WebPageRepositoryImpl.swift

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ final class WebPageRepositoryImpl: WebPageRepository {
2323
.compactMap { try? $0.toDomain() }
2424
}
2525

26-
func upsert(_ urlString: String) async throws -> WebPage {
26+
func upsert(_ urlString: String) async throws {
2727
let metadata = try await metadataService.fetchMetadata(from: urlString)
2828
let request = WebPageRequest(
2929
title: metadata.title,
@@ -32,13 +32,6 @@ final class WebPageRepositoryImpl: WebPageRepository {
3232
imageURL: metadata.imageURL
3333
)
3434
try await webPageService.upsertWebPage(request)
35-
let response = WebPageResponse(
36-
title: request.title,
37-
url: request.url,
38-
displayURL: request.displayURL,
39-
imageURL: request.imageURL
40-
)
41-
return try response.toDomain()
4235
}
4336

4437
func delete(_ urlString: String) async throws {

DevLog/Domain/Protocol/WebPageRepository.swift

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

88
protocol WebPageRepository {
99
func fetch(_ query: String) async throws -> [WebPage]
10-
func upsert(_ urlString: String) async throws -> WebPage
10+
func upsert(_ urlString: String) async throws
1111
func delete(_ urlString: String) async throws
1212
}

DevLog/Domain/UseCase/WebPage/Upsert/AddWebPageUseCase.swift

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

88
protocol AddWebPageUseCase {
9-
func execute(_ urlString: String) async throws -> WebPage
9+
func execute(_ urlString: String) async throws
1010
}

DevLog/Domain/UseCase/WebPage/Upsert/AddWebPageUseCaseImpl.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ final class AddWebPageUseCaseImpl: AddWebPageUseCase {
1212
self.repository = repository
1313
}
1414

15-
func execute(_ urlString: String) async throws -> WebPage {
15+
func execute(_ urlString: String) async throws {
1616
try await repository.upsert(urlString)
1717
}
1818
}

DevLog/Presentation/ViewModel/HomeViewModel.swift

Lines changed: 63 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ final class HomeViewModel: Store {
1111
struct State {
1212
var todoKindPreferences = TodoKind.allCases.map { TodoKindPreference(kind: $0, isVisible: true) }
1313
var pinnedTodos: [PinnedTodoItem] = []
14+
var webPages: [WebPageItem] = []
1415
var showTodoKindPicker: Bool = false
1516
var showTodoEditor: Bool = false
1617
var showSearchView: Bool = false
@@ -19,7 +20,9 @@ final class HomeViewModel: Store {
1920
var searchText: String = ""
2021
var isSearching: Bool = false
2122
var reorderTodo: Bool = false
22-
var isLoading: Bool = false
23+
var isPinnedLoading: Bool = false
24+
var isWebPageLoading: Bool = false
25+
var isWebPageInputLoading: Bool = false
2326
var showAlert: Bool = false
2427
var alertTitle: String = ""
2528
var alertType: AlertType?
@@ -40,14 +43,20 @@ final class HomeViewModel: Store {
4043
case updateSearchText(String)
4144
case upsertTodo(Todo)
4245
case addWebPage
46+
case deleteWebPage(WebPageItem)
4347
case fetchPinnedTodos([PinnedTodoItem])
44-
case setLoading(Bool)
48+
case fetchWebPages([WebPageItem])
49+
case setPinnedLoading(Bool)
50+
case setWebPageLoading(Bool)
51+
case setWebPageInputLoading(Bool)
4552
}
4653

4754
enum SideEffect {
4855
case upsertTodo(Todo)
4956
case addWebPage(String)
57+
case deleteWebPage(String)
5058
case fetchPinnedTodos
59+
case fetchWebPages
5160
}
5261

5362
enum AlertType {
@@ -58,17 +67,23 @@ final class HomeViewModel: Store {
5867

5968
private let upsertTodoUseCase: UpsertTodoUseCase
6069
private let addWebPageUseCase: AddWebPageUseCase
70+
private let deleteWebPageUseCase: DeleteWebPageUseCase
6171
private let fetchPinnedTodosUseCase: FetchPinnedTodosUseCase
72+
private let fetchWebPagesUseCase: FetchWebPagesUseCase
6273
@Published private(set) var state = State()
6374

6475
init(
6576
addWebPageUseCase: AddWebPageUseCase,
77+
deleteWebPageUseCase: DeleteWebPageUseCase,
6678
upsertTodoUseCase: UpsertTodoUseCase,
67-
fetchPinnedTodosUseCase: FetchPinnedTodosUseCase
79+
fetchPinnedTodosUseCase: FetchPinnedTodosUseCase,
80+
fetchWebPagesUseCase: FetchWebPagesUseCase
6881
) {
6982
self.addWebPageUseCase = addWebPageUseCase
83+
self.deleteWebPageUseCase = deleteWebPageUseCase
7084
self.upsertTodoUseCase = upsertTodoUseCase
7185
self.fetchPinnedTodosUseCase = fetchPinnedTodosUseCase
86+
self.fetchWebPagesUseCase = fetchWebPagesUseCase
7287
}
7388

7489
func reduce(with action: Action) -> [SideEffect] {
@@ -81,10 +96,10 @@ final class HomeViewModel: Store {
8196
.updateWebPageURLInput, .setAlert:
8297
effects = reduceByUser(action, state: &state)
8398

84-
case .onAppear, .updateSearching, .updateSearchText, .upsertTodo, .addWebPage:
99+
case .onAppear, .updateSearching, .updateSearchText, .upsertTodo, .addWebPage, .deleteWebPage:
85100
effects = reduceByView(action, state: &state)
86101

87-
case .fetchPinnedTodos, .setLoading:
102+
case .fetchPinnedTodos, .fetchWebPages, .setPinnedLoading, .setWebPageLoading, .setWebPageInputLoading:
88103
effects = reduceByRun(action, state: &state)
89104
}
90105

@@ -97,8 +112,6 @@ final class HomeViewModel: Store {
97112
case .upsertTodo(let todo):
98113
Task {
99114
do {
100-
defer { send(.setLoading(false)) }
101-
send(.setLoading(true))
102115
try await upsertTodoUseCase.execute(todo)
103116
} catch {
104117
send(.setAlert(isPresented: true, type: .error))
@@ -107,24 +120,51 @@ final class HomeViewModel: Store {
107120
case .addWebPage(let urlString):
108121
Task {
109122
do {
110-
defer { send(.setLoading(false)) }
111-
send(.setLoading(true))
112-
_ = try await addWebPageUseCase.execute(urlString)
123+
defer { send(.setWebPageInputLoading(false)) }
124+
send(.setWebPageInputLoading(true))
125+
try await addWebPageUseCase.execute(urlString)
126+
let pages = try await fetchWebPagesUseCase.execute("")
127+
send(.fetchWebPages(pages.map { WebPageItem(from: $0) }))
113128
} catch {
129+
send(.setWebPageInputLoading(false))
130+
send(.setAlert(isPresented: true, type: .error))
131+
}
132+
}
133+
case .deleteWebPage(let urlString):
134+
Task {
135+
do {
136+
defer { send(.setWebPageLoading(false)) }
137+
send(.setWebPageLoading(true))
138+
try await deleteWebPageUseCase.execute(urlString)
139+
let pages = try await fetchWebPagesUseCase.execute("")
140+
send(.fetchWebPages(pages.map { WebPageItem(from: $0) }))
141+
} catch {
142+
send(.setWebPageLoading(false))
114143
send(.setAlert(isPresented: true, type: .error))
115144
}
116145
}
117146
case .fetchPinnedTodos:
118147
Task {
119148
do {
120-
defer { send(.setLoading(false)) }
121-
send(.setLoading(true))
149+
defer { send(.setPinnedLoading(false)) }
150+
send(.setPinnedLoading(true))
122151
let todos = try await fetchPinnedTodosUseCase.execute()
123152
send(.fetchPinnedTodos(todos.map { PinnedTodoItem(from: $0) }))
124153
} catch {
125154
send(.setAlert(isPresented: true, type: .error))
126155
}
127156
}
157+
case .fetchWebPages:
158+
Task {
159+
do {
160+
defer { send(.setWebPageLoading(false)) }
161+
send(.setWebPageLoading(true))
162+
let pages = try await fetchWebPagesUseCase.execute("")
163+
send(.fetchWebPages(pages.map { WebPageItem(from: $0) }))
164+
} catch {
165+
send(.setAlert(isPresented: true, type: .error))
166+
}
167+
}
128168
}
129169
}
130170
}
@@ -161,7 +201,7 @@ private extension HomeViewModel {
161201
func reduceByView(_ action: Action, state: inout State) -> [SideEffect] {
162202
switch action {
163203
case .onAppear:
164-
return [.fetchPinnedTodos]
204+
return [.fetchPinnedTodos, .fetchWebPages]
165205
case .updateSearching(let isSearching):
166206
state.isSearching = isSearching
167207
case .updateSearchText(let text):
@@ -175,6 +215,8 @@ private extension HomeViewModel {
175215
}
176216
setAlert(&state, isPresented: false, type: nil)
177217
return [.addWebPage(normalizedURL)]
218+
case .deleteWebPage(let page):
219+
return [.deleteWebPage(page.url.absoluteString)]
178220
default:
179221
break
180222
}
@@ -185,8 +227,14 @@ private extension HomeViewModel {
185227
switch action {
186228
case .fetchPinnedTodos(let todos):
187229
state.pinnedTodos = todos
188-
case .setLoading(let isLoading):
189-
state.isLoading = isLoading
230+
case .fetchWebPages(let pages):
231+
state.webPages = pages
232+
case .setPinnedLoading(let isLoading):
233+
state.isPinnedLoading = isLoading
234+
case .setWebPageLoading(let isLoading):
235+
state.isWebPageLoading = isLoading
236+
case .setWebPageInputLoading(let isLoading):
237+
state.isWebPageInputLoading = isLoading
190238
default:
191239
break
192240
}

DevLog/Presentation/ViewModel/SearchViewModel.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ final class SearchViewModel: Store {
1313
var isLoading: Bool = false
1414
var isSearching: Bool = false
1515
var searchQuery: String = ""
16-
var selectedWebPage: WebPageItem?
1716
var webPages: [WebPageItem] = []
1817
var todos: [TodoListItem] = []
1918
var recentQueries: OrderedSet<String> = []
@@ -27,7 +26,6 @@ final class SearchViewModel: Store {
2726
enum Action {
2827
case fetchWebPage([WebPageItem])
2928
case fetchTodos([TodoListItem])
30-
case selectWebPage(WebPageItem)
3129
case addRecentQuery(String)
3230
case removeRecentQuery(String)
3331
case clearRecentQueries
@@ -78,8 +76,6 @@ final class SearchViewModel: Store {
7876
state.webPages = items
7977
case .fetchTodos(let items):
8078
state.todos = items
81-
case .selectWebPage(let item):
82-
state.selectedWebPage = item
8379
case .addRecentQuery(let query):
8480
let trimmed = query.trimmingCharacters(in: .whitespacesAndNewlines)
8581
guard !trimmed.isEmpty else { break }

DevLog/Resource/Localizable.xcstrings

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@
313313
},
314314
"사용자 설정" : {
315315

316+
},
317+
"삭제" : {
318+
316319
},
317320
"상태 설정" : {
318321

@@ -361,6 +364,9 @@
361364
},
362365
"작성된 내용이 없습니다." : {
363366

367+
},
368+
"저장한 Web Page가 표시됩니다." : {
369+
364370
},
365371
"전체 삭제" : {
366372

@@ -386,7 +392,7 @@
386392
"최근 검색" : {
387393

388394
},
389-
"최근에 중요 표시를 한 Todo가 여기 표시됩니다." : {
395+
"최근에 중요 표시를 한 Todo가 표시됩니다." : {
390396

391397
},
392398
"추가" : {

DevLog/UI/Common/MainView.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ struct MainView: View {
1414
TabView {
1515
HomeView(viewModel: HomeViewModel(
1616
addWebPageUseCase: container.resolve(AddWebPageUseCase.self),
17+
deleteWebPageUseCase: container.resolve(DeleteWebPageUseCase.self),
1718
upsertTodoUseCase: container.resolve(UpsertTodoUseCase.self),
18-
fetchPinnedTodosUseCase: container.resolve(FetchPinnedTodosUseCase.self)
19+
fetchPinnedTodosUseCase: container.resolve(FetchPinnedTodosUseCase.self),
20+
fetchWebPagesUseCase: container.resolve(FetchWebPagesUseCase.self)
1921
))
2022
.tabItem {
2123
Image(systemName: "house.fill")

0 commit comments

Comments
 (0)