88import SwiftUI
99
1010struct HomeView : View {
11- @Environment ( \. diContainer) var container : any DIContainer
12- @Environment ( NavigationRouter< HomeRoute> . self ) private var router
13- @State var viewModel : HomeViewModel
14- let isCompactLayout : Bool
1511 @ScaledMetric ( relativeTo: . largeTitle) private var labelWidth = CGFloat ( 34 )
12+ let coordinator : HomeViewCoordinator
13+ let isCompactLayout : Bool
1614
1715 var body : some View {
1816 List {
@@ -24,116 +22,107 @@ struct HomeView: View {
2422 . navigationTitle ( String ( localized: " nav_home " ) )
2523 . toolbar { toolbar }
2624 . sheet ( isPresented: Binding (
27- get: { viewModel. state. reorderTodo } ,
28- set: { viewModel. send ( . setPresentation( . reorderTodo, $0) ) }
25+ get: { coordinator . viewModel. state. reorderTodo } ,
26+ set: { coordinator . viewModel. send ( . setPresentation( . reorderTodo, $0) ) }
2927 ) ) {
3028 TodoManageView (
31- viewModel: TodoManageViewModel ( viewModel . state . preferences ) ,
29+ viewModel: coordinator . makeTodoManageViewModel ( ) ,
3230 onDismiss: { array in
33- viewModel. send ( . setPresentation( . reorderTodo, false ) )
31+ coordinator . viewModel. send ( . setPresentation( . reorderTodo, false ) )
3432 withAnimation {
35- viewModel. send ( . orderTodoCategory( array) )
33+ coordinator . viewModel. send ( . orderTodoCategory( array) )
3634 }
3735 }
3836 )
3937 }
4038 . sheet ( isPresented: Binding (
41- get: { viewModel. state. showContentPicker } ,
39+ get: { coordinator . viewModel. state. showContentPicker } ,
4240 set: { _, _ in }
4341 ) ) {
4442 contentPicker
4543 }
4644 . fullScreenCover ( isPresented: Binding (
47- get: { viewModel. state. showTodoEditor } ,
48- set: { viewModel. send ( . setPresentation( . todoEditor, $0) ) }
45+ get: { coordinator . viewModel. state. showTodoEditor } ,
46+ set: { coordinator . viewModel. send ( . setPresentation( . todoEditor, $0) ) }
4947 ) ) {
50- if let selectedCategory = viewModel. state. selectedTodoCategory {
48+ if let selectedCategory = coordinator . viewModel. state. selectedTodoCategory {
5149 TodoEditorView (
52- viewModel: TodoEditorViewModel (
53- category: selectedCategory,
54- fetchPreferencesUseCase: container. resolve ( FetchTodoCategoryPreferencesUseCase . self) ,
55- fetchReferenceItemsUseCase: container. resolve ( FetchReferenceItemsUseCase . self)
56- ) ,
57- onSubmit: { viewModel. send ( . addTodo( $0) ) }
50+ viewModel: coordinator. makeTodoEditorViewModel ( category: selectedCategory) ,
51+ onSubmit: { coordinator. viewModel. send ( . addTodo( $0) ) }
5852 )
5953 }
6054 }
6155 . fullScreenCover ( isPresented: Binding (
62- get: { viewModel. state. showSearchView } ,
63- set: { viewModel. send ( . setPresentation( . searchView, $0) ) }
56+ get: { coordinator . viewModel. state. showSearchView } ,
57+ set: { coordinator . viewModel. send ( . setPresentation( . searchView, $0) ) }
6458 ) ) {
65- SearchView ( viewModel: SearchViewModel (
66- fetchWebPagesUseCase: container. resolve ( FetchWebPagesUseCase . self) ,
67- fetchTodosUseCase: container. resolve ( FetchTodosUseCase . self) ,
68- fetchRecentSearchQueriesUseCase: container. resolve ( FetchRecentSearchQueriesUseCase . self) ,
69- updateRecentSearchQueriesUseCase: container. resolve ( UpdateRecentSearchQueriesUseCase . self)
70- ) )
59+ SearchView ( viewModel: coordinator. makeSearchViewModel ( ) )
7160 }
7261 . alert (
73- viewModel. state. alertTitle,
62+ coordinator . viewModel. state. alertTitle,
7463 isPresented: Binding (
75- get: { viewModel. state. showAlert } ,
76- set: { viewModel. send ( . setAlert( isPresented: $0) ) }
64+ get: { coordinator . viewModel. state. showAlert } ,
65+ set: { coordinator . viewModel. send ( . setAlert( isPresented: $0) ) }
7766 )
7867 ) {
7968 alertButtons
8069 } message: {
81- Text ( viewModel. state. alertMessage)
70+ Text ( coordinator . viewModel. state. alertMessage)
8271 }
8372 . toast (
8473 isPresented: Binding (
85- get: { viewModel. state. showToast } ,
86- set: { viewModel. send ( . setToast( isPresented: $0) ) }
74+ get: { coordinator . viewModel. state. showToast } ,
75+ set: { coordinator . viewModel. send ( . setToast( isPresented: $0) ) }
8776 ) ,
8877 duration: 5 ,
89- action: { viewModel. send ( . undoDeleteWebPage) }
78+ action: { coordinator . viewModel. send ( . undoDeleteWebPage) }
9079 ) {
91- Label ( viewModel. state. toastMessage, systemImage: " arrow.uturn.left " )
80+ Label ( coordinator . viewModel. state. toastMessage, systemImage: " arrow.uturn.left " )
9281 . font ( . caption)
9382 . multilineTextAlignment ( . center)
9483 }
9584 . onAppear {
96- viewModel. send ( . onAppear)
85+ coordinator . viewModel. send ( . onAppear)
9786 }
9887 . overlay {
99- if viewModel. state. isAppending {
88+ if coordinator . viewModel. state. isAppending {
10089 LoadingView ( )
10190 }
10291 }
10392 }
10493
10594 @ViewBuilder
10695 private var alertButtons : some View {
107- switch viewModel. state. alertType {
96+ switch coordinator . viewModel. state. alertType {
10897 case . webPageInput:
10998 TextField (
11099 " https:// " ,
111100 text: Binding (
112- get: { viewModel. state. webPageURLInput } ,
113- set: { viewModel. send ( . updateWebPageURLInput( $0) ) }
101+ get: { coordinator . viewModel. state. webPageURLInput } ,
102+ set: { coordinator . viewModel. send ( . updateWebPageURLInput( $0) ) }
114103 )
115104 )
116105 . textInputAutocapitalization ( . never)
117106 . keyboardType ( . URL)
118107 Button ( String ( localized: " home_add " ) ) {
119- viewModel. send ( . addWebPage)
108+ coordinator . viewModel. send ( . addWebPage)
120109 }
121110 Button ( String ( localized: " common_cancel " ) , role: . cancel) {
122- viewModel. send ( . setAlert( isPresented: false ) )
111+ coordinator . viewModel. send ( . setAlert( isPresented: false ) )
123112 }
124113 case . invalidURL, . error, . none:
125114 Button ( String ( localized: " common_close " ) , role: . cancel) {
126- viewModel. send ( . setAlert( isPresented: false ) )
115+ coordinator . viewModel. send ( . setAlert( isPresented: false ) )
127116 }
128117 }
129118 }
130119
131120 private var todoSection : some View {
132121 Section ( content: {
133- if viewModel. state. isPreferencesLoading {
122+ if coordinator . viewModel. state. isPreferencesLoading {
134123 LoadingView ( )
135124 } else {
136- let preferences = viewModel. state. preferences
125+ let preferences = coordinator . viewModel. state. preferences
137126 ForEach ( preferences. filter { $0. isVisible } , id: \. id) { item in
138127 todoCategoryRow ( item)
139128 }
@@ -146,7 +135,7 @@ struct HomeView: View {
146135 . bold ( )
147136 Spacer ( )
148137 Button ( action: {
149- viewModel. send ( . setPresentation( . reorderTodo, true ) )
138+ coordinator . viewModel. send ( . setPresentation( . reorderTodo, true ) )
150139 } ) {
151140 Image ( systemName: " ellipsis " )
152141 . font ( . title2)
@@ -159,17 +148,17 @@ struct HomeView: View {
159148
160149 private var recentTodoSection : some View {
161150 Section {
162- if viewModel. state. isRecentTodosLoading {
151+ if coordinator . viewModel. state. isRecentTodosLoading {
163152 LoadingView ( )
164- } else if viewModel. state. recentTodos. isEmpty {
153+ } else if coordinator . viewModel. state. recentTodos. isEmpty {
165154 HStack {
166155 Spacer ( )
167156 Text ( String ( localized: " home_recent_empty " ) )
168157 . font ( . callout)
169158 Spacer ( )
170159 }
171160 } else {
172- ForEach ( viewModel. state. recentTodos, id: \. id) { todo in
161+ ForEach ( coordinator . viewModel. state. recentTodos, id: \. id) { todo in
173162 recentTodoRow ( todo)
174163 }
175164 }
@@ -186,13 +175,13 @@ struct HomeView: View {
186175
187176 private var webPageSection : some View {
188177 Section {
189- let webPages = viewModel. state. webPages. filter { !$0. isHidden }
190- if viewModel. state. isWebPageLoading {
178+ let webPages = coordinator . viewModel. state. webPages. filter { !$0. isHidden }
179+ if coordinator . viewModel. state. isWebPageLoading {
191180 LoadingView ( )
192181 . id ( UUID ( ) ) // id 부여를 통해 렌더링 강제
193- } else if viewModel. state. needsWebPageRefresh {
182+ } else if coordinator . viewModel. state. needsWebPageRefresh {
194183 Button {
195- viewModel. send ( . refreshWebPages)
184+ coordinator . viewModel. send ( . refreshWebPages)
196185 } label: {
197186 HStack {
198187 Spacer ( )
@@ -231,18 +220,18 @@ struct HomeView: View {
231220 private var toolbar : some ToolbarContent {
232221 ToolbarItem ( placement: . topBarTrailing) {
233222 Button {
234- viewModel. send ( . setPresentation( . contentPicker, true ) )
223+ coordinator . viewModel. send ( . setPresentation( . contentPicker, true ) )
235224 } label: {
236225 Image ( systemName: " plus " )
237226 }
238- . disabled ( !viewModel. state. isNetworkConnected)
227+ . disabled ( !coordinator . viewModel. state. isNetworkConnected)
239228 }
240229 if #available( iOS 26 . 0 , * ) {
241230 ToolbarSpacer ( . fixed, placement: . topBarTrailing)
242231 }
243232 ToolbarItemGroup ( placement: . topBarTrailing) {
244233 Button {
245- viewModel. send ( . setPresentation( . searchView, true ) )
234+ coordinator . viewModel. send ( . setPresentation( . searchView, true ) )
246235 } label: {
247236 Image ( systemName: " magnifyingglass " )
248237 }
@@ -261,7 +250,7 @@ struct HomeView: View {
261250 }
262251 } else {
263252 Button {
264- router. show ( . category( item) )
253+ coordinator . router. replace ( with : . category( item) )
265254 } label: {
266255 labelImage (
267256 text: item. localizedName,
@@ -281,7 +270,7 @@ struct HomeView: View {
281270 }
282271 } else {
283272 Button {
284- router. show ( . todo( TodoIdItem ( id: item. id) ) )
273+ coordinator . router. replace ( with : . todo( TodoIdItem ( id: item. id) ) )
285274 } label: {
286275 RecentTodoRow ( todo: item)
287276 . frame ( maxWidth: . infinity, alignment: . leading)
@@ -299,7 +288,7 @@ struct HomeView: View {
299288 }
300289 } else {
301290 Button {
302- router. show ( . webPage( item) )
291+ coordinator . router. replace ( with : . webPage( item) )
303292 } label: {
304293 WebItemRow ( item: item, showsChevron: false )
305294 . frame ( maxWidth: . infinity, alignment: . leading)
@@ -309,7 +298,7 @@ struct HomeView: View {
309298 }
310299 . swipeActions ( edge: . trailing, allowsFullSwipe: true ) {
311300 Button ( role: . destructive) {
312- viewModel. send ( . deleteWebPage( item) )
301+ coordinator . viewModel. send ( . deleteWebPage( item) )
313302 } label: {
314303 Label ( String ( localized: " common_delete " ) , systemImage: " trash " )
315304 }
@@ -320,14 +309,14 @@ struct HomeView: View {
320309 NavigationStack {
321310 List {
322311 Section {
323- if viewModel. state. isPreferencesLoading {
312+ if coordinator . viewModel. state. isPreferencesLoading {
324313 LoadingView ( )
325314 } else {
326- let preferences = viewModel. state. preferences. filter ( \. isVisible)
315+ let preferences = coordinator . viewModel. state. preferences. filter ( \. isVisible)
327316 ForEach ( preferences, id: \. id) { item in
328317 Button {
329318 DispatchQueue . main. async {
330- viewModel. send ( . tapTodoCategory( item. category) )
319+ coordinator . viewModel. send ( . tapTodoCategory( item. category) )
331320 }
332321 } label: {
333322 labelImage (
@@ -346,7 +335,7 @@ struct HomeView: View {
346335 Section {
347336 Button {
348337 DispatchQueue . main. async {
349- viewModel. send ( . setAlert( isPresented: true , type: . webPageInput) )
338+ coordinator . viewModel. send ( . setAlert( isPresented: true , type: . webPageInput) )
350339 }
351340 } label: {
352341 labelImage (
@@ -365,7 +354,7 @@ struct HomeView: View {
365354 . toolbar {
366355 ToolbarItem ( placement: . topBarLeading) {
367356 Button {
368- viewModel. send ( . setPresentation( . contentPicker, false ) )
357+ coordinator . viewModel. send ( . setPresentation( . contentPicker, false ) )
369358 } label: {
370359 Image ( systemName: " xmark " )
371360 . bold ( )
0 commit comments