NetNewsWire/Multiplatform/Shared/SceneNavigationView.swift
Stuart Breckenridge 673f0ce718
Handles single and multiple sync failures
If a single sync failure is encountered a sheet is presented which allows the user to update their credentials.

If multiple sync failures are encountered an alert is shown listing the accounts which encountered errors. On iOS, this alert can take the user into Settings, but there is no obvious way to programatically pesent macOS preferences.
2020-07-25 16:40:04 +08:00

220 lines
5.6 KiB
Swift

//
// SceneNavigationView.swift
// NetNewsWire
//
// Created by Maurice Parker on 6/28/20.
// Copyright © 2020 Ranchero Software. All rights reserved.
//
import SwiftUI
import Account
struct SceneNavigationView: View {
@StateObject private var sceneModel = SceneModel()
@State private var showSheet = false
@State private var showShareSheet = false
@State private var sheetToShow: SidebarSheets = .none
@State private var showAccountSyncErrorAlert = false // multiple sync errors
#if os(iOS)
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
#endif
var body: some View {
NavigationView {
#if os(macOS)
SidebarContainerView()
.frame(minWidth: 100, idealWidth: 150, maxHeight: .infinity)
#else
SidebarContainerView()
#endif
#if os(iOS)
if horizontalSizeClass != .compact {
TimelineContainerView()
}
#else
TimelineContainerView()
#endif
ArticleContainerView()
}
.environmentObject(sceneModel)
.onAppear {
sceneModel.startup()
}
.onChange(of: sheetToShow) { value in
value != .none ? (showSheet = true) : (showSheet = false)
}
.onReceive(sceneModel.$accountSyncErrors) { errors in
if errors.count == 0 {
showAccountSyncErrorAlert = false
} else {
if errors.count > 1 {
showAccountSyncErrorAlert = true
} else {
sheetToShow = .fixCredentials
}
}
}
.sheet(isPresented: $showSheet,
onDismiss: {
sheetToShow = .none
sceneModel.accountSyncErrors = []
}) {
if sheetToShow == .web {
AddWebFeedView()
}
if sheetToShow == .folder {
AddFolderView()
}
#if os(iOS)
if sheetToShow == .settings {
SettingsView()
}
#endif
if sheetToShow == .fixCredentials {
FixAccountCredentialView(accountSyncError: sceneModel.accountSyncErrors[0])
}
}
.alert(isPresented: $showAccountSyncErrorAlert, content: {
#if os(macOS)
return Alert(title: Text("Account Sync Error"),
message: Text("The following accounts failed to sync: ") + Text(sceneModel.accountSyncErrors.map({ $0.account.nameForDisplay }).joined(separator: ", ")) + Text(". You can update credentials in Preferences"),
dismissButton: .default(Text("Dismiss")))
#else
return Alert(title: Text("Account Sync Error"),
message: Text("The following accounts failed to sync: ") + Text(sceneModel.accountSyncErrors.map({ $0.account.nameForDisplay }).joined(separator: ", ")) + Text(". You can update credentials in Settings"),
primaryButton: .default(Text("Show Settings"), action: {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
sheetToShow = .settings
})
}),
secondaryButton: .cancel(Text("Dismiss")))
#endif
})
.toolbar {
#if os(macOS)
ToolbarItem() {
Menu {
Button("Add Web Feed", action: { sheetToShow = .web })
Button("Add Reddit Feed", action: { })
Button("Add Twitter Feed", action: { })
Button("Add Folder", action: { sheetToShow = .folder})
} label : {
AppAssets.addMenuImage
}
}
ToolbarItem {
Button {
// AccountManager.shared.refreshAll(errorHandler: handleRefreshError)
AccountManager.shared.refreshAll(completion: nil)
} label: {
AppAssets.refreshImage
}
.help("Refresh").padding(.trailing, 40)
}
ToolbarItem {
Button {
sceneModel.markAllAsRead()
} label: {
AppAssets.markAllAsReadImagePNG
.offset(y: 7)
}
.disabled(sceneModel.markAllAsReadButtonState == nil)
.help("Mark All as Read")
}
ToolbarItem {
MacSearchField()
.frame(width: 200)
}
ToolbarItem {
Button {
sceneModel.goToNextUnread()
} label: {
AppAssets.nextUnreadArticleImage
}
.disabled(sceneModel.nextUnreadButtonState == nil)
.help("Go to Next Unread").padding(.trailing, 40)
}
ToolbarItem {
Button {
sceneModel.toggleReadStatusForSelectedArticles()
} label: {
if sceneModel.readButtonState ?? false {
AppAssets.readClosedImage
} else {
AppAssets.readOpenImage
}
}
.disabled(sceneModel.readButtonState == nil)
.help(sceneModel.readButtonState ?? false ? "Mark as Unread" : "Mark as Read")
}
ToolbarItem {
Button {
sceneModel.toggleStarredStatusForSelectedArticles()
} label: {
if sceneModel.starButtonState ?? false {
AppAssets.starClosedImage
} else {
AppAssets.starOpenImage
}
}
.disabled(sceneModel.starButtonState == nil)
.help(sceneModel.starButtonState ?? false ? "Mark as Unstarred" : "Mark as Starred")
}
ToolbarItem {
Button {
} label: {
AppAssets.articleExtractorOff
}
.disabled(sceneModel.extractorButtonState == nil)
.help("Show Reader View")
}
ToolbarItem {
Button {
sceneModel.openSelectedArticleInBrowser()
} label: {
AppAssets.openInBrowserImage
}
.disabled(sceneModel.openInBrowserButtonState == nil)
.help("Open in Browser")
}
ToolbarItem {
ZStack {
if showShareSheet {
SharingServiceView(articles: sceneModel.selectedArticles, showing: $showShareSheet)
.frame(width: 20, height: 20)
}
Button {
showShareSheet = true
} label: {
AppAssets.shareImage
}
}
.disabled(sceneModel.shareButtonState == nil)
.help("Share")
}
#endif
}
}
func handleRefreshError(_ error: Error) {
sceneModel.accountErrorMessage = error.localizedDescription
}
}
struct NavigationView_Previews: PreviewProvider {
static var previews: some View {
SceneNavigationView()
.environmentObject(SceneModel())
}
}