swiftui-multiplatform/Shared/PostList/PostListView.swift

198 lines
8.2 KiB
Swift
Raw Permalink Normal View History

import SwiftUI
import Combine
2020-08-17 17:46:23 +00:00
struct PostListView: View {
@EnvironmentObject var model: WriteFreelyModel
@EnvironmentObject var errorHandling: ErrorHandling
@Environment(\.managedObjectContext) var managedObjectContext
@State private var postCount: Int = 0
@State private var filteredListViewId: Int = 0
2021-09-24 18:45:28 +00:00
var selectedCollection: WFACollection?
var showAllPosts: Bool
#if os(iOS)
private var frameHeight: CGFloat {
var height: CGFloat = 50
let bottom = UIApplication.shared.windows.first?.safeAreaInsets.bottom ?? 0
height += bottom
return height
}
#endif
var body: some View {
#if os(iOS)
ZStack(alignment: .bottom) {
PostListFilteredView(
2021-09-24 18:45:28 +00:00
collection: selectedCollection,
showAllPosts: showAllPosts,
postCount: $postCount
)
.id(self.filteredListViewId)
.navigationTitle(
2021-09-24 18:45:28 +00:00
showAllPosts ? "All Posts" : selectedCollection?.title ?? (
model.account.server == "https://write.as" ? "Anonymous" : "Drafts"
)
)
.toolbar {
ToolbarItem(placement: .primaryAction) {
ZStack {
// We have to add a Spacer as a sibling view to the Button in some kind of Stack so that any
// a11y modifiers are applied as expected: bug report filed as FB8956392.
if #unavailable(iOS 16) {
Spacer()
}
Button(action: {
let managedPost = model.editor.generateNewLocalPost(withFont: model.preferences.font)
withAnimation {
self.model.showAllPosts = false
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
self.model.selectedPost = managedPost
}
}
}, label: {
ZStack {
Image("does.not.exist")
.accessibilityHidden(true)
Image(systemName: "square.and.pencil")
.accessibilityHidden(true)
.imageScale(.large) // These modifiers compensate for the resizing
.padding(.vertical, 12) // done to the Image (and the button tap target)
.padding(.leading, 12) // by the SwiftUI layout system from adding a
.padding(.trailing, 8) // Spacer in this ZStack (FB8956392).
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
})
.accessibilityLabel(Text("Compose"))
.accessibilityHint(Text("Compose a new local draft"))
}
2020-08-10 20:50:24 +00:00
}
}
VStack {
HStack(spacing: 0) {
Button(action: {
model.isPresentingSettingsView = true
}, label: {
Image(systemName: "gear")
2021-01-11 16:58:59 +00:00
.padding(.vertical, 4)
.padding(.horizontal, 8)
})
.accessibilityLabel(Text("Settings"))
.accessibilityHint(Text("Open the Settings sheet"))
2021-09-24 18:45:28 +00:00
.sheet(
isPresented: $model.isPresentingSettingsView,
onDismiss: { model.isPresentingSettingsView = false },
content: {
SettingsView()
.environmentObject(model)
}
)
Spacer()
Text(postCount == 1 ? "\(postCount) post" : "\(postCount) posts")
.foregroundColor(.secondary)
Spacer()
if model.isProcessingRequest {
ProgressView()
2021-01-11 16:58:59 +00:00
.padding(.vertical, 4)
.padding(.horizontal, 8)
} else {
2023-10-23 21:15:41 +00:00
if model.hasNetworkConnection {
Button(action: {
DispatchQueue.main.async {
model.fetchUserCollections()
model.fetchUserPosts()
}
}, label: {
Image(systemName: "arrow.clockwise")
.padding(.vertical, 4)
.padding(.horizontal, 8)
})
.accessibilityLabel(Text("Refresh Posts"))
.accessibilityHint(Text("Fetch changes from the server"))
.disabled(!model.account.isLoggedIn)
} else {
Image(systemName: "wifi.exclamationmark")
2021-01-11 16:58:59 +00:00
.padding(.vertical, 4)
.padding(.horizontal, 8)
2023-10-23 21:15:41 +00:00
.foregroundColor(.secondary)
}
}
}
2021-01-11 16:58:59 +00:00
.padding(.top, 8)
.padding(.horizontal, 8)
Spacer()
}
.frame(height: frameHeight)
.background(Color(UIColor.systemGray5))
.overlay(Divider(), alignment: .top)
}
.ignoresSafeArea(.all, edges: .bottom)
2021-09-24 19:29:20 +00:00
.onAppear {
// Set the selected collection and whether or not we want to show all posts
2021-09-24 19:29:20 +00:00
model.selectedCollection = selectedCollection
model.showAllPosts = showAllPosts
// We use this to invalidate and refresh the view, so that new posts created outside of the app (e.g.,
// in the action extension) show up.
withAnimation {
self.filteredListViewId += 1
}
2021-09-24 19:29:20 +00:00
}
.onChange(of: model.hasError) { value in
if value {
if let error = model.currentError {
self.errorHandling.handle(error: error)
} else {
self.errorHandling.handle(error: AppError.genericError())
}
model.hasError = false
}
}
#else
PostListFilteredView(
2021-09-24 18:45:28 +00:00
collection: selectedCollection,
showAllPosts: showAllPosts,
postCount: $postCount
)
.toolbar {
ToolbarItemGroup(placement: .primaryAction) {
if model.selectedPost != nil {
ActivePostToolbarView(activePost: model.selectedPost!)
}
}
}
.navigationTitle(
2021-09-24 18:45:28 +00:00
showAllPosts ? "All Posts" : selectedCollection?.title ?? (
model.account.server == "https://write.as" ? "Anonymous" : "Drafts"
)
)
2021-10-08 18:29:18 +00:00
.onAppear {
model.selectedCollection = selectedCollection
model.showAllPosts = showAllPosts
}
.onChange(of: model.hasError) { value in
if value {
if let error = model.currentError {
self.errorHandling.handle(error: error)
} else {
self.errorHandling.handle(error: AppError.genericError())
}
model.hasError = false
}
}
#endif
}
}
2020-09-09 16:12:19 +00:00
struct PostListView_Previews: PreviewProvider {
static var previews: some View {
let context = LocalStorageManager.standard.container.viewContext
2020-09-09 16:12:19 +00:00
let model = WriteFreelyModel()
2021-09-24 18:45:28 +00:00
return PostListView(showAllPosts: true)
2020-09-09 16:12:19 +00:00
.environment(\.managedObjectContext, context)
.environmentObject(model)
}
}