88import SwiftUI
99
1010struct HomeView : View {
11- @Environment ( \. diContainer) var container : any DIContainer
1211 @Environment ( NavigationRouter< HomeRoute> . self ) private var router
13- @State var viewModel : HomeViewModel
14- let isCompactLayout : Bool
1512 @ScaledMetric ( relativeTo: . largeTitle) private var labelWidth = CGFloat ( 34 )
13+ let coordinator : HomeViewCoordinator
14+ let isCompactLayout : Bool
1615
1716 var body : some View {
1817 List {
@@ -24,116 +23,107 @@ struct HomeView: View {
2423 . navigationTitle ( String ( localized: " nav_home " ) )
2524 . toolbar { toolbar }
2625 . sheet ( isPresented: Binding (
27- get: { viewModel. state. reorderTodo } ,
28- set: { viewModel. send ( . setPresentation( . reorderTodo, $0) ) }
26+ get: { coordinator . viewModel. state. reorderTodo } ,
27+ set: { coordinator . viewModel. send ( . setPresentation( . reorderTodo, $0) ) }
2928 ) ) {
3029 TodoManageView (
31- viewModel: TodoManageViewModel ( viewModel . state . preferences ) ,
30+ viewModel: coordinator . makeTodoManageViewModel ( ) ,
3231 onDismiss: { array in
33- viewModel. send ( . setPresentation( . reorderTodo, false ) )
32+ coordinator . viewModel. send ( . setPresentation( . reorderTodo, false ) )
3433 withAnimation {
35- viewModel. send ( . orderTodoCategory( array) )
34+ coordinator . viewModel. send ( . orderTodoCategory( array) )
3635 }
3736 }
3837 )
3938 }
4039 . sheet ( isPresented: Binding (
41- get: { viewModel. state. showContentPicker } ,
40+ get: { coordinator . viewModel. state. showContentPicker } ,
4241 set: { _, _ in }
4342 ) ) {
4443 contentPicker
4544 }
4645 . fullScreenCover ( isPresented: Binding (
47- get: { viewModel. state. showTodoEditor } ,
48- set: { viewModel. send ( . setPresentation( . todoEditor, $0) ) }
46+ get: { coordinator . viewModel. state. showTodoEditor } ,
47+ set: { coordinator . viewModel. send ( . setPresentation( . todoEditor, $0) ) }
4948 ) ) {
50- if let selectedCategory = viewModel. state. selectedTodoCategory {
49+ if let selectedCategory = coordinator . viewModel. state. selectedTodoCategory {
5150 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) ) }
51+ viewModel: coordinator. makeTodoEditorViewModel ( category: selectedCategory) ,
52+ onSubmit: { coordinator. viewModel. send ( . addTodo( $0) ) }
5853 )
5954 }
6055 }
6156 . fullScreenCover ( isPresented: Binding (
62- get: { viewModel. state. showSearchView } ,
63- set: { viewModel. send ( . setPresentation( . searchView, $0) ) }
57+ get: { coordinator . viewModel. state. showSearchView } ,
58+ set: { coordinator . viewModel. send ( . setPresentation( . searchView, $0) ) }
6459 ) ) {
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- ) )
60+ SearchView ( viewModel: coordinator. makeSearchViewModel ( ) )
7161 }
7262 . alert (
73- viewModel. state. alertTitle,
63+ coordinator . viewModel. state. alertTitle,
7464 isPresented: Binding (
75- get: { viewModel. state. showAlert } ,
76- set: { viewModel. send ( . setAlert( isPresented: $0) ) }
65+ get: { coordinator . viewModel. state. showAlert } ,
66+ set: { coordinator . viewModel. send ( . setAlert( isPresented: $0) ) }
7767 )
7868 ) {
7969 alertButtons
8070 } message: {
81- Text ( viewModel. state. alertMessage)
71+ Text ( coordinator . viewModel. state. alertMessage)
8272 }
8373 . toast (
8474 isPresented: Binding (
85- get: { viewModel. state. showToast } ,
86- set: { viewModel. send ( . setToast( isPresented: $0) ) }
75+ get: { coordinator . viewModel. state. showToast } ,
76+ set: { coordinator . viewModel. send ( . setToast( isPresented: $0) ) }
8777 ) ,
8878 duration: 5 ,
89- action: { viewModel. send ( . undoDeleteWebPage) }
79+ action: { coordinator . viewModel. send ( . undoDeleteWebPage) }
9080 ) {
91- Label ( viewModel. state. toastMessage, systemImage: " arrow.uturn.left " )
81+ Label ( coordinator . viewModel. state. toastMessage, systemImage: " arrow.uturn.left " )
9282 . font ( . caption)
9383 . multilineTextAlignment ( . center)
9484 }
9585 . onAppear {
96- viewModel. send ( . onAppear)
86+ coordinator . viewModel. send ( . onAppear)
9787 }
9888 . overlay {
99- if viewModel. state. isAppending {
89+ if coordinator . viewModel. state. isAppending {
10090 LoadingView ( )
10191 }
10292 }
10393 }
10494
10595 @ViewBuilder
10696 private var alertButtons : some View {
107- switch viewModel. state. alertType {
97+ switch coordinator . viewModel. state. alertType {
10898 case . webPageInput:
10999 TextField (
110100 " https:// " ,
111101 text: Binding (
112- get: { viewModel. state. webPageURLInput } ,
113- set: { viewModel. send ( . updateWebPageURLInput( $0) ) }
102+ get: { coordinator . viewModel. state. webPageURLInput } ,
103+ set: { coordinator . viewModel. send ( . updateWebPageURLInput( $0) ) }
114104 )
115105 )
116106 . textInputAutocapitalization ( . never)
117107 . keyboardType ( . URL)
118108 Button ( String ( localized: " home_add " ) ) {
119- viewModel. send ( . addWebPage)
109+ coordinator . viewModel. send ( . addWebPage)
120110 }
121111 Button ( String ( localized: " common_cancel " ) , role: . cancel) {
122- viewModel. send ( . setAlert( isPresented: false ) )
112+ coordinator . viewModel. send ( . setAlert( isPresented: false ) )
123113 }
124114 case . invalidURL, . error, . none:
125115 Button ( String ( localized: " common_close " ) , role: . cancel) {
126- viewModel. send ( . setAlert( isPresented: false ) )
116+ coordinator . viewModel. send ( . setAlert( isPresented: false ) )
127117 }
128118 }
129119 }
130120
131121 private var todoSection : some View {
132122 Section ( content: {
133- if viewModel. state. isPreferencesLoading {
123+ if coordinator . viewModel. state. isPreferencesLoading {
134124 LoadingView ( )
135125 } else {
136- let preferences = viewModel. state. preferences
126+ let preferences = coordinator . viewModel. state. preferences
137127 ForEach ( preferences. filter { $0. isVisible } , id: \. id) { item in
138128 todoCategoryRow ( item)
139129 }
@@ -146,7 +136,7 @@ struct HomeView: View {
146136 . bold ( )
147137 Spacer ( )
148138 Button ( action: {
149- viewModel. send ( . setPresentation( . reorderTodo, true ) )
139+ coordinator . viewModel. send ( . setPresentation( . reorderTodo, true ) )
150140 } ) {
151141 Image ( systemName: " ellipsis " )
152142 . font ( . title2)
@@ -159,17 +149,17 @@ struct HomeView: View {
159149
160150 private var recentTodoSection : some View {
161151 Section {
162- if viewModel. state. isRecentTodosLoading {
152+ if coordinator . viewModel. state. isRecentTodosLoading {
163153 LoadingView ( )
164- } else if viewModel. state. recentTodos. isEmpty {
154+ } else if coordinator . viewModel. state. recentTodos. isEmpty {
165155 HStack {
166156 Spacer ( )
167157 Text ( String ( localized: " home_recent_empty " ) )
168158 . font ( . callout)
169159 Spacer ( )
170160 }
171161 } else {
172- ForEach ( viewModel. state. recentTodos, id: \. id) { todo in
162+ ForEach ( coordinator . viewModel. state. recentTodos, id: \. id) { todo in
173163 recentTodoRow ( todo)
174164 }
175165 }
@@ -186,13 +176,13 @@ struct HomeView: View {
186176
187177 private var webPageSection : some View {
188178 Section {
189- let webPages = viewModel. state. webPages. filter { !$0. isHidden }
190- if viewModel. state. isWebPageLoading {
179+ let webPages = coordinator . viewModel. state. webPages. filter { !$0. isHidden }
180+ if coordinator . viewModel. state. isWebPageLoading {
191181 LoadingView ( )
192182 . id ( UUID ( ) ) // id 부여를 통해 렌더링 강제
193- } else if viewModel. state. needsWebPageRefresh {
183+ } else if coordinator . viewModel. state. needsWebPageRefresh {
194184 Button {
195- viewModel. send ( . refreshWebPages)
185+ coordinator . viewModel. send ( . refreshWebPages)
196186 } label: {
197187 HStack {
198188 Spacer ( )
@@ -231,18 +221,18 @@ struct HomeView: View {
231221 private var toolbar : some ToolbarContent {
232222 ToolbarItem ( placement: . topBarTrailing) {
233223 Button {
234- viewModel. send ( . setPresentation( . contentPicker, true ) )
224+ coordinator . viewModel. send ( . setPresentation( . contentPicker, true ) )
235225 } label: {
236226 Image ( systemName: " plus " )
237227 }
238- . disabled ( !viewModel. state. isNetworkConnected)
228+ . disabled ( !coordinator . viewModel. state. isNetworkConnected)
239229 }
240230 if #available( iOS 26 . 0 , * ) {
241231 ToolbarSpacer ( . fixed, placement: . topBarTrailing)
242232 }
243233 ToolbarItemGroup ( placement: . topBarTrailing) {
244234 Button {
245- viewModel. send ( . setPresentation( . searchView, true ) )
235+ coordinator . viewModel. send ( . setPresentation( . searchView, true ) )
246236 } label: {
247237 Image ( systemName: " magnifyingglass " )
248238 }
@@ -309,7 +299,7 @@ struct HomeView: View {
309299 }
310300 . swipeActions ( edge: . trailing, allowsFullSwipe: true ) {
311301 Button ( role: . destructive) {
312- viewModel. send ( . deleteWebPage( item) )
302+ coordinator . viewModel. send ( . deleteWebPage( item) )
313303 } label: {
314304 Label ( String ( localized: " common_delete " ) , systemImage: " trash " )
315305 }
@@ -320,14 +310,14 @@ struct HomeView: View {
320310 NavigationStack {
321311 List {
322312 Section {
323- if viewModel. state. isPreferencesLoading {
313+ if coordinator . viewModel. state. isPreferencesLoading {
324314 LoadingView ( )
325315 } else {
326- let preferences = viewModel. state. preferences. filter ( \. isVisible)
316+ let preferences = coordinator . viewModel. state. preferences. filter ( \. isVisible)
327317 ForEach ( preferences, id: \. id) { item in
328318 Button {
329319 DispatchQueue . main. async {
330- viewModel. send ( . tapTodoCategory( item. category) )
320+ coordinator . viewModel. send ( . tapTodoCategory( item. category) )
331321 }
332322 } label: {
333323 labelImage (
@@ -346,7 +336,7 @@ struct HomeView: View {
346336 Section {
347337 Button {
348338 DispatchQueue . main. async {
349- viewModel. send ( . setAlert( isPresented: true , type: . webPageInput) )
339+ coordinator . viewModel. send ( . setAlert( isPresented: true , type: . webPageInput) )
350340 }
351341 } label: {
352342 labelImage (
@@ -365,7 +355,7 @@ struct HomeView: View {
365355 . toolbar {
366356 ToolbarItem ( placement: . topBarLeading) {
367357 Button {
368- viewModel. send ( . setPresentation( . contentPicker, false ) )
358+ coordinator . viewModel. send ( . setPresentation( . contentPicker, false ) )
369359 } label: {
370360 Image ( systemName: " xmark " )
371361 . bold ( )
0 commit comments