Use view model for CollectionListView

This commit is contained in:
Angelo Stavrow 2021-09-24 14:45:28 -04:00
parent 28856eb434
commit 3c1e04e3bf
No known key found for this signature in database
GPG Key ID: 1A49C7064E060EEE
7 changed files with 107 additions and 138 deletions

View File

@ -35,22 +35,7 @@ struct ContentView: View {
.help("Create a new local draft.") .help("Create a new local draft.")
} }
#else #else
if #available(iOS 15.0, *) { CollectionListView(selectedCollection: model.selectedCollection)
CollectionListView()
.alert(isPresented: $model.isPresentingNetworkErrorAlert, content: {
Alert(
title: Text("Connection Error"),
message: Text("""
There is no internet connection at the moment. Please reconnect or try again later.
"""),
dismissButton: .default(Text("OK"), action: {
model.isPresentingNetworkErrorAlert = false
})
)
})
} else {
CollectionListView()
}
#endif #endif
#if os(macOS) #if os(macOS)
@ -64,50 +49,13 @@ struct ContentView: View {
} }
} }
#else #else
if #available(iOS 15.0, *) { PostListView(selectedCollection: nil, showAllPosts: false)
PostListView()
.sheet(
isPresented: $model.isPresentingSettingsView,
onDismiss: { model.isPresentingSettingsView = false },
content: {
SettingsView()
.environmentObject(model)
}
)
} else {
PostListView()
}
#endif #endif
Text("Select a post, or create a new local draft.") Text("Select a post, or create a new local draft.")
.foregroundColor(.secondary) .foregroundColor(.secondary)
} }
.environmentObject(model) .environmentObject(model)
// For iOS 14
if #available(iOS 15.0, *) {
// Do nothing.
} else {
EmptyView()
.sheet(
isPresented: $model.isPresentingSettingsView,
onDismiss: { model.isPresentingSettingsView = false },
content: {
SettingsView()
.environmentObject(model)
}
)
.alert(isPresented: $model.isPresentingNetworkErrorAlert, content: {
Alert(
title: Text("Connection Error"),
message: Text("""
There is no internet connection at the moment. Please reconnect or try again later.
"""),
dismissButton: .default(Text("OK"), action: {
model.isPresentingNetworkErrorAlert = false
})
)
})
}
} }
} }

View File

@ -0,0 +1,40 @@
import SwiftUI
import CoreData
class CollectionListModel: NSObject, ObservableObject {
@Published var list: [WFACollection] = []
private let collectionsController: NSFetchedResultsController<WFACollection>
init(managedObjectContext: NSManagedObjectContext) {
collectionsController = NSFetchedResultsController(fetchRequest: WFACollection.collectionsFetchRequest,
managedObjectContext: managedObjectContext,
sectionNameKeyPath: nil,
cacheName: nil)
super.init()
collectionsController.delegate = self
do {
try collectionsController.performFetch()
list = collectionsController.fetchedObjects ?? []
} catch {
print("Failed to fetch collections!")
}
}
}
extension CollectionListModel: NSFetchedResultsControllerDelegate {
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
guard let collections = controller.fetchedObjects as? [WFACollection] else { return }
self.list = collections
}
}
extension WFACollection {
static var collectionsFetchRequest: NSFetchRequest<WFACollection> {
let request: NSFetchRequest<WFACollection> = WFACollection.createFetchRequest()
request.sortDescriptors = [NSSortDescriptor(keyPath: \WFACollection.title, ascending: true)]
return request
}
}

View File

@ -2,71 +2,24 @@ import SwiftUI
struct CollectionListView: View { struct CollectionListView: View {
@EnvironmentObject var model: WriteFreelyModel @EnvironmentObject var model: WriteFreelyModel
@ObservedObject var collections = CollectionListModel(managedObjectContext: LocalStorageManager.persistentContainer.viewContext)
@FetchRequest( @State var selectedCollection: WFACollection?
entity: WFACollection.entity(),
sortDescriptors: [NSSortDescriptor(keyPath: \WFACollection.title, ascending: true)]
) var collections: FetchedResults<WFACollection>
var body: some View { var body: some View {
List(selection: $model.selectedCollection) { List(selection: $selectedCollection) {
if model.account.isLoggedIn { if model.account.isLoggedIn {
NavigationLink( NavigationLink("All Posts", destination: PostListView(selectedCollection: nil, showAllPosts: true))
destination: PostListView(), NavigationLink("Drafts", destination: PostListView(selectedCollection: nil, showAllPosts: false))
isActive: Binding<Bool>(
get: { () -> Bool in
model.selectedCollection == nil && model.showAllPosts
}, set: { newValue in
if newValue {
self.model.showAllPosts = true
self.model.selectedCollection = nil
} else {
// No-op
}
}
),
label: {
Text("All Posts")
})
NavigationLink(
destination: PostListView(),
isActive: Binding<Bool>(
get: { () -> Bool in
model.selectedCollection == nil && !model.showAllPosts
}, set: { newValue in
if newValue {
self.model.showAllPosts = false
self.model.selectedCollection = nil
} else {
// No-op
}
}
),
label: {
Text(model.account.server == "https://write.as" ? "Anonymous" : "Drafts")
})
Section(header: Text("Your Blogs")) { Section(header: Text("Your Blogs")) {
ForEach(collections, id: \.self) { collection in ForEach(collections.list, id: \.self) { collection in
NavigationLink( NavigationLink(destination: PostListView(selectedCollection: collection, showAllPosts: false),
destination: PostListView(), tag: collection,
isActive: Binding<Bool>( selection: $selectedCollection,
get: { () -> Bool in label: { Text("\(collection.title)") })
model.selectedCollection == collection && !model.showAllPosts
}, set: { newValue in
if newValue {
self.model.showAllPosts = false
self.model.selectedCollection = collection
} else {
// No-op
}
}
),
label: { Text(collection.title) }
)
} }
} }
} else { } else {
NavigationLink(destination: PostListView()) { NavigationLink(destination: PostListView(selectedCollection: nil, showAllPosts: false)) {
Text("Drafts") Text("Drafts")
} }
} }

View File

@ -97,7 +97,7 @@ struct PostListFilteredView: View {
Alert( Alert(
title: Text("Delete Post?"), title: Text("Delete Post?"),
message: Text("This action cannot be undone."), message: Text("This action cannot be undone."),
primaryButton: .cancel() { primaryButton: .cancel {
model.postToDelete = nil model.postToDelete = nil
}, },
secondaryButton: .destructive(Text("Delete"), action: { secondaryButton: .destructive(Text("Delete"), action: {

View File

@ -7,6 +7,9 @@ struct PostListView: View {
@State private var postCount: Int = 0 @State private var postCount: Int = 0
var selectedCollection: WFACollection?
var showAllPosts: Bool
#if os(iOS) #if os(iOS)
private var frameHeight: CGFloat { private var frameHeight: CGFloat {
var height: CGFloat = 50 var height: CGFloat = 50
@ -20,12 +23,12 @@ struct PostListView: View {
#if os(iOS) #if os(iOS)
ZStack(alignment: .bottom) { ZStack(alignment: .bottom) {
PostListFilteredView( PostListFilteredView(
collection: model.selectedCollection, collection: selectedCollection,
showAllPosts: model.showAllPosts, showAllPosts: showAllPosts,
postCount: $postCount postCount: $postCount
) )
.navigationTitle( .navigationTitle(
model.showAllPosts ? "All Posts" : model.selectedCollection?.title ?? ( showAllPosts ? "All Posts" : selectedCollection?.title ?? (
model.account.server == "https://write.as" ? "Anonymous" : "Drafts" model.account.server == "https://write.as" ? "Anonymous" : "Drafts"
) )
) )
@ -70,9 +73,28 @@ struct PostListView: View {
}) })
.accessibilityLabel(Text("Settings")) .accessibilityLabel(Text("Settings"))
.accessibilityHint(Text("Open the Settings sheet")) .accessibilityHint(Text("Open the Settings sheet"))
.sheet(
isPresented: $model.isPresentingSettingsView,
onDismiss: { model.isPresentingSettingsView = false },
content: {
SettingsView()
.environmentObject(model)
}
)
Spacer() Spacer()
Text(postCount == 1 ? "\(postCount) post" : "\(postCount) posts") Text(postCount == 1 ? "\(postCount) post" : "\(postCount) posts")
.foregroundColor(.secondary) .foregroundColor(.secondary)
.alert(isPresented: $model.isPresentingNetworkErrorAlert, content: {
Alert(
title: Text("Connection Error"),
message: Text("""
There is no internet connection at the moment. Please reconnect or try again later.
"""),
dismissButton: .default(Text("OK"), action: {
model.isPresentingNetworkErrorAlert = false
})
)
})
Spacer() Spacer()
if model.isProcessingRequest { if model.isProcessingRequest {
ProgressView() ProgressView()
@ -105,8 +127,8 @@ struct PostListView: View {
.ignoresSafeArea() .ignoresSafeArea()
#else #else
PostListFilteredView( PostListFilteredView(
collection: model.selectedCollection, collection: selectedCollection,
showAllPosts: model.showAllPosts, showAllPosts: showAllPosts,
postCount: $postCount postCount: $postCount
) )
.toolbar { .toolbar {
@ -129,7 +151,7 @@ struct PostListView: View {
} }
} }
.navigationTitle( .navigationTitle(
model.showAllPosts ? "All Posts" : model.selectedCollection?.title ?? ( showAllPosts ? "All Posts" : selectedCollection?.title ?? (
model.account.server == "https://write.as" ? "Anonymous" : "Drafts" model.account.server == "https://write.as" ? "Anonymous" : "Drafts"
) )
) )
@ -142,7 +164,7 @@ struct PostListView_Previews: PreviewProvider {
let context = LocalStorageManager.persistentContainer.viewContext let context = LocalStorageManager.persistentContainer.viewContext
let model = WriteFreelyModel() let model = WriteFreelyModel()
return PostListView() return PostListView(showAllPosts: true)
.environment(\.managedObjectContext, context) .environment(\.managedObjectContext, context)
.environmentObject(model) .environmentObject(model)
} }

View File

@ -34,24 +34,24 @@ struct WriteFreely_MultiPlatformApp: App {
WindowGroup { WindowGroup {
ContentView() ContentView()
.onAppear(perform: { .onAppear(perform: {
if model.editor.showAllPostsFlag { // if model.editor.showAllPostsFlag {
DispatchQueue.main.async { // DispatchQueue.main.async {
self.model.selectedCollection = nil // self.model.selectedCollection = nil
self.model.showAllPosts = true // self.model.showAllPosts = true
} // }
} else { // } else {
DispatchQueue.main.async { // DispatchQueue.main.async {
self.model.selectedCollection = model.editor.fetchSelectedCollectionFromAppStorage() // self.model.selectedCollection = model.editor.fetchSelectedCollectionFromAppStorage()
self.model.showAllPosts = false // self.model.showAllPosts = false
} // }
} // }
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { // DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
if model.editor.lastDraftURL != nil { // if model.editor.lastDraftURL != nil {
self.model.selectedPost = model.editor.fetchLastDraftFromAppStorage() // self.model.selectedPost = model.editor.fetchLastDraftFromAppStorage()
} else { // } else {
createNewLocalPost() // createNewLocalPost()
} // }
} // }
}) })
.environmentObject(model) .environmentObject(model)
.environment(\.managedObjectContext, LocalStorageManager.persistentContainer.viewContext) .environment(\.managedObjectContext, LocalStorageManager.persistentContainer.viewContext)

View File

@ -7,6 +7,8 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
170A7EC126F5186A00F1CBD4 /* CollectionListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 170A7EC026F5186A00F1CBD4 /* CollectionListModel.swift */; };
170A7EC226F5186A00F1CBD4 /* CollectionListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 170A7EC026F5186A00F1CBD4 /* CollectionListModel.swift */; };
170DFA34251BBC44001D82A0 /* PostEditorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 170DFA33251BBC44001D82A0 /* PostEditorModel.swift */; }; 170DFA34251BBC44001D82A0 /* PostEditorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 170DFA33251BBC44001D82A0 /* PostEditorModel.swift */; };
170DFA35251BBC44001D82A0 /* PostEditorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 170DFA33251BBC44001D82A0 /* PostEditorModel.swift */; }; 170DFA35251BBC44001D82A0 /* PostEditorModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 170DFA33251BBC44001D82A0 /* PostEditorModel.swift */; };
17120DA124E19839002B9F6C /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5388D24DDEC7400DEFF9A /* AccountView.swift */; }; 17120DA124E19839002B9F6C /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A5388D24DDEC7400DEFF9A /* AccountView.swift */; };
@ -126,6 +128,7 @@
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
1709ADDF251B9A110053AF79 /* EditorLaunchingPolicy.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = EditorLaunchingPolicy.md; sourceTree = "<group>"; }; 1709ADDF251B9A110053AF79 /* EditorLaunchingPolicy.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = EditorLaunchingPolicy.md; sourceTree = "<group>"; };
170A7EC026F5186A00F1CBD4 /* CollectionListModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionListModel.swift; sourceTree = "<group>"; };
170DFA33251BBC44001D82A0 /* PostEditorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostEditorModel.swift; sourceTree = "<group>"; }; 170DFA33251BBC44001D82A0 /* PostEditorModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostEditorModel.swift; sourceTree = "<group>"; };
17120DA424E19CBF002B9F6C /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; }; 17120DA424E19CBF002B9F6C /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
17120DA824E1B2F5002B9F6C /* AccountLogoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountLogoutView.swift; sourceTree = "<group>"; }; 17120DA824E1B2F5002B9F6C /* AccountLogoutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountLogoutView.swift; sourceTree = "<group>"; };
@ -496,6 +499,7 @@
17DF32D224C8B78D00BCE2E3 /* PostCollection */ = { 17DF32D224C8B78D00BCE2E3 /* PostCollection */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
170A7EC026F5186A00F1CBD4 /* CollectionListModel.swift */,
171BFDF924D4AF8300888236 /* CollectionListView.swift */, 171BFDF924D4AF8300888236 /* CollectionListView.swift */,
); );
path = PostCollection; path = PostCollection;
@ -755,6 +759,7 @@
17B996DA2502D23E0017B536 /* WFAPost+CoreDataProperties.swift in Sources */, 17B996DA2502D23E0017B536 /* WFAPost+CoreDataProperties.swift in Sources */,
1756AE7724CB2EDD00FD7257 /* PostEditorView.swift in Sources */, 1756AE7724CB2EDD00FD7257 /* PostEditorView.swift in Sources */,
17DF32D524C8CA3400BCE2E3 /* PostStatusBadgeView.swift in Sources */, 17DF32D524C8CA3400BCE2E3 /* PostStatusBadgeView.swift in Sources */,
170A7EC126F5186A00F1CBD4 /* CollectionListModel.swift in Sources */,
17D435E824E3128F0036B539 /* PreferencesModel.swift in Sources */, 17D435E824E3128F0036B539 /* PreferencesModel.swift in Sources */,
1756AE7A24CB65DF00FD7257 /* PostListView.swift in Sources */, 1756AE7A24CB65DF00FD7257 /* PostListView.swift in Sources */,
17B996D82502D23E0017B536 /* WFAPost+CoreDataClass.swift in Sources */, 17B996D82502D23E0017B536 /* WFAPost+CoreDataClass.swift in Sources */,
@ -791,6 +796,7 @@
17C42E662509237800072984 /* PostListFilteredView.swift in Sources */, 17C42E662509237800072984 /* PostListFilteredView.swift in Sources */,
17120DAD24E1B99F002B9F6C /* AccountLoginView.swift in Sources */, 17120DAD24E1B99F002B9F6C /* AccountLoginView.swift in Sources */,
17466626256C0D0600629997 /* MacEditorTextView.swift in Sources */, 17466626256C0D0600629997 /* MacEditorTextView.swift in Sources */,
170A7EC226F5186A00F1CBD4 /* CollectionListModel.swift in Sources */,
17E5DF8A2543610700DCDC9B /* PostTextEditingView.swift in Sources */, 17E5DF8A2543610700DCDC9B /* PostTextEditingView.swift in Sources */,
17C42E71250AAFD500072984 /* NSManagedObjectContext+ExecuteAndMergeChanges.swift in Sources */, 17C42E71250AAFD500072984 /* NSManagedObjectContext+ExecuteAndMergeChanges.swift in Sources */,
1756AE7B24CB65DF00FD7257 /* PostListView.swift in Sources */, 1756AE7B24CB65DF00FD7257 /* PostListView.swift in Sources */,