1+ // Replace existing SourcesManagerView with this
12import SwiftUI
23
34struct SourcesManagerView : View {
45 @EnvironmentObject var sourcesViewModel : SourcesViewModel
56 @Environment ( \. dismiss) var dismiss
6-
7+
78 var body : some View {
8- NavigationView {
9- List {
10- Section {
11- ForEach ( sourcesViewModel. sources) { source in
12- if sourcesViewModel. editingSource? . id == source. id {
13- // Editing view
14- VStack ( alignment: . leading, spacing: 8 ) {
15- TextField ( " Source URL " , text: $sourcesViewModel. newSourceURL)
16- . textInputAutocapitalization ( . never)
17- . disableAutocorrection ( true )
18- . keyboardType ( . URL)
19- . autocapitalization ( . none)
20-
21- HStack {
22- Button ( " Cancel " ) {
23- sourcesViewModel. editingSource = nil
24- sourcesViewModel. newSourceURL = " "
25- }
26- . buttonStyle ( . bordered)
27-
28- Button ( " Save " ) {
29- sourcesViewModel. updateSource (
30- source: source,
31- newURLString: sourcesViewModel. newSourceURL
32- )
33- }
34- . buttonStyle ( . borderedProminent)
35- . disabled ( sourcesViewModel. newSourceURL. trimmingCharacters ( in: . whitespacesAndNewlines) . isEmpty)
36- }
37- }
38- . padding ( . vertical, 4 )
39- } else {
40- // Normal display view
41- SourceRow ( source: source)
42- . swipeActions ( edge: . trailing) {
43- Button ( role: . destructive) {
44- if let index = sourcesViewModel. sources. firstIndex ( where: { $0. id == source. id } ) {
45- sourcesViewModel. sources. remove ( at: index)
46- }
47- } label: {
48- Label ( " Delete " , systemImage: " trash " )
49- }
50-
51- Button {
52- sourcesViewModel. startEditing ( source)
53- } label: {
54- Label ( " Edit " , systemImage: " pencil " )
55- }
56- . tint ( . blue)
57- }
58- }
59- }
60- . onDelete ( perform: sourcesViewModel. deleteSource)
61- . onMove ( perform: sourcesViewModel. moveSource)
62- } header: {
63- Text ( " Sources ( \( sourcesViewModel. sources. count) ) " )
64- } footer: {
65- Text ( " Tap and hold to reorder. Swipe left to edit or delete. " )
66- . font ( . caption)
67- . foregroundColor ( . secondary)
68- }
69-
70- Section {
71- if sourcesViewModel. isAddingNew {
9+ List {
10+ Section {
11+ ForEach ( sourcesViewModel. sources) { source in
12+ if sourcesViewModel. editingSource? . id == source. id {
13+ // Editing view
7214 VStack ( alignment: . leading, spacing: 8 ) {
73- TextField ( " Enter source URL" , text: $sourcesViewModel. newSourceURL)
15+ TextField ( " Source URL" , text: $sourcesViewModel. newSourceURL)
7416 . textInputAutocapitalization ( . never)
7517 . disableAutocorrection ( true )
7618 . keyboardType ( . URL)
7719 . autocapitalization ( . none)
78- . onSubmit {
79- addSource ( )
80- }
81-
20+
8221 HStack {
8322 Button ( " Cancel " ) {
84- sourcesViewModel. isAddingNew = false
23+ sourcesViewModel. editingSource = nil
8524 sourcesViewModel. newSourceURL = " "
8625 }
8726 . buttonStyle ( . bordered)
88-
89- Button ( " Add " ) {
90- addSource ( )
27+
28+ Button ( " Save " ) {
29+ sourcesViewModel. updateSource (
30+ source: source,
31+ newURLString: sourcesViewModel. newSourceURL
32+ )
9133 }
9234 . buttonStyle ( . borderedProminent)
9335 . disabled ( sourcesViewModel. newSourceURL. trimmingCharacters ( in: . whitespacesAndNewlines) . isEmpty)
9436 }
9537 }
9638 . padding ( . vertical, 4 )
9739 } else {
98- Button {
99- sourcesViewModel. isAddingNew = true
100- } label: {
101- Label ( " Add New Source " , systemImage: " plus.circle.fill " )
102- }
40+ // Normal display view
41+ SourceRow ( source: source)
42+ . swipeActions ( edge: . trailing) {
43+ Button ( role: . destructive) {
44+ if let index = sourcesViewModel. sources. firstIndex ( where: { $0. id == source. id } ) {
45+ sourcesViewModel. sources. remove ( at: index)
46+ sourcesViewModel. saveSources ( )
47+ }
48+ } label: {
49+ Label ( " Delete " , systemImage: " trash " )
50+ }
51+
52+ Button {
53+ sourcesViewModel. startEditing ( source)
54+ } label: {
55+ Label ( " Edit " , systemImage: " pencil " )
56+ }
57+ . tint ( . blue)
58+ }
10359 }
104- } header: {
105- Text ( " Add Source " )
10660 }
61+ . onDelete ( perform: sourcesViewModel. deleteSource)
62+ . onMove ( perform: sourcesViewModel. moveSource)
63+ } header: {
64+ Text ( " Sources ( \( sourcesViewModel. sources. count) ) " )
65+ } footer: {
66+ Text ( " Tap and hold to reorder. Swipe left to edit or delete. " )
67+ . font ( . caption)
68+ . foregroundColor ( . secondary)
10769 }
108- . navigationTitle ( " Sources Manager " )
109- . navigationBarTitleDisplayMode ( . inline)
110- . toolbar {
111- ToolbarItem ( placement: . navigationBarLeading) {
112- Button ( " Done " ) {
113- dismiss ( )
70+
71+ Section {
72+ if sourcesViewModel. isAddingNew {
73+ VStack ( alignment: . leading, spacing: 8 ) {
74+ TextField ( " Enter source URL " , text: $sourcesViewModel. newSourceURL)
75+ . textInputAutocapitalization ( . never)
76+ . disableAutocorrection ( true )
77+ . keyboardType ( . URL)
78+ . autocapitalization ( . none)
79+ . onSubmit {
80+ addSource ( )
81+ }
82+
83+ HStack {
84+ Button ( " Cancel " ) {
85+ sourcesViewModel. isAddingNew = false
86+ sourcesViewModel. newSourceURL = " "
87+ }
88+ . buttonStyle ( . bordered)
89+
90+ Button ( " Add " ) {
91+ addSource ( )
92+ }
93+ . buttonStyle ( . borderedProminent)
94+ . disabled ( sourcesViewModel. newSourceURL. trimmingCharacters ( in: . whitespacesAndNewlines) . isEmpty)
95+ }
11496 }
115- }
116-
117- ToolbarItem ( placement: . navigationBarTrailing) {
118- EditButton ( )
119- }
120-
121- ToolbarItem ( placement: . navigationBarTrailing) {
97+ . padding ( . vertical, 4 )
98+ } else {
12299 Button {
123- sourcesViewModel. validateAllSources ( )
100+ sourcesViewModel. isAddingNew = true
124101 } label: {
125- Image ( systemName : " arrow.clockwise " )
102+ Label ( " Add New Source " , systemImage : " plus.circle.fill " )
126103 }
127104 }
105+ } header: {
106+ Text ( " Add Source " )
128107 }
129- . onAppear {
130- sourcesViewModel. validateAllSources ( )
108+ }
109+ . navigationTitle ( " Sources Manager " )
110+ . navigationBarTitleDisplayMode ( . inline)
111+ . toolbar {
112+ ToolbarItem ( placement: . navigationBarLeading) {
113+ Button ( " Done " ) {
114+ dismiss ( )
115+ }
116+ }
117+
118+ ToolbarItem ( placement: . navigationBarTrailing) {
119+ EditButton ( )
131120 }
121+
122+ ToolbarItem ( placement: . navigationBarTrailing) {
123+ Button {
124+ sourcesViewModel. validateAllSources ( )
125+ } label: {
126+ Image ( systemName: " arrow.clockwise " )
127+ }
128+ }
129+ }
130+ . onAppear {
131+ sourcesViewModel. validateAllSources ( )
132132 }
133133 }
134-
134+
135135 private func addSource( ) {
136136 sourcesViewModel. addSource ( urlString: sourcesViewModel. newSourceURL)
137137 sourcesViewModel. isAddingNew = false
138138 sourcesViewModel. newSourceURL = " "
139139 }
140- }
141-
142- struct SourceRow : View {
143- let source : SourcesViewModel . Source
144- @EnvironmentObject var sourcesViewModel : SourcesViewModel
145-
146- var body : some View {
147- HStack {
148- VStack ( alignment: . leading, spacing: 4 ) {
149- Text ( source. urlString)
150- . font ( . system( . body, design: . monospaced) )
151- . foregroundColor ( . primary)
152- . lineLimit ( 2 )
153- . minimumScaleFactor ( 0.8 )
154-
155- if let validationState = sourcesViewModel. validationStates [ source. urlString] {
156- HStack {
157- Image ( systemName: validationState. icon)
158- . font ( . caption)
159- . foregroundColor ( validationState. color)
160- Text ( validationState. description)
161- . font ( . caption)
162- . foregroundColor ( validationState. color)
163- }
164- }
165- }
166-
167- Spacer ( )
168- }
169- . padding ( . vertical, 4 )
170- . contentShape ( Rectangle ( ) )
171- }
172140}
0 commit comments