Files
omnivore/apple/OmnivoreKit/Sources/App/Views/RootView/RootView.swift
2022-05-17 14:48:44 -07:00

151 lines
4.5 KiB
Swift

import Models
import Services
import SwiftUI
import Utils
import Views
struct LinkRequest: Identifiable {
let id: UUID
let serverID: String
}
public struct RootView: View {
let pdfViewerProvider: ((URL, PDFViewerViewModel) -> AnyView)?
@StateObject private var viewModel = RootViewModel()
public init(
pdfViewerProvider: ((URL, PDFViewerViewModel) -> AnyView)?,
intercomProvider: IntercomProvider?
) {
self.pdfViewerProvider = pdfViewerProvider
if let intercomProvider = intercomProvider {
DataService.showIntercomMessenger = intercomProvider.showIntercomMessenger
DataService.registerIntercomUser = intercomProvider.registerIntercomUser
Authenticator.unregisterIntercomUser = intercomProvider.unregisterIntercomUser
}
}
public var body: some View {
InnerRootView(viewModel: viewModel)
.environmentObject(viewModel.services.authenticator)
.environmentObject(viewModel.services.dataService)
.environment(\.managedObjectContext, viewModel.services.dataService.viewContext)
.onAppear {
if let pdfViewerProvider = pdfViewerProvider {
viewModel.configurePDFProvider(pdfViewerProvider: pdfViewerProvider)
}
}
}
}
struct InnerRootView: View {
@EnvironmentObject var dataService: DataService
@EnvironmentObject var authenticator: Authenticator
@ObservedObject var viewModel: RootViewModel
@ViewBuilder private var innerBody: some View {
if authenticator.isLoggedIn {
PrimaryContentView()
.onAppear {
viewModel.triggerPushNotificationRequestIfNeeded()
}
#if os(iOS)
.fullScreenCover(item: $viewModel.linkRequest) { _ in
NavigationView {
WebReaderLoadingContainer(
requestID: viewModel.linkRequest?.serverID ?? "",
handleClose: { viewModel.linkRequest = nil }
)
}
}
#endif
.snackBar(isShowing: $viewModel.showSnackbar, message: viewModel.snackbarMessage)
// Schedule the dismissal every time we present the snackbar.
.onChange(of: viewModel.showSnackbar) { newValue in
if newValue {
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
withAnimation {
viewModel.showSnackbar = false
}
}
}
}
#if os(iOS)
.customAlert(isPresented: $viewModel.showPushNotificationPrimer) {
pushNotificationPrimerView
}
#endif
} else {
WelcomeView()
.accessibilityElement()
.accessibilityIdentifier("welcomeView")
}
}
var body: some View {
Group {
#if os(iOS)
innerBody
#elseif os(macOS)
innerBody
.frame(minWidth: 400, idealWidth: 1200, minHeight: 400, idealHeight: 1200)
#endif
}
#if os(iOS)
.onOpenURL { url in
withoutAnimation {
if viewModel.linkRequest != nil {
viewModel.linkRequest = nil
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) {
Task { await viewModel.onOpenURL(url: url) }
}
} else {
Task { await viewModel.onOpenURL(url: url) }
}
}
}
.onReceive(NSNotification.operationSuccessPublisher) { notification in
if let message = notification.userInfo?["message"] as? String {
viewModel.showSnackbar = true
viewModel.snackbarMessage = message
}
}
.onReceive(NSNotification.operationFailedPublisher) { notification in
if let message = notification.userInfo?["message"] as? String {
viewModel.showSnackbar = true
viewModel.snackbarMessage = message
}
}
#endif
}
#if os(iOS)
private var pushNotificationPrimerView: PushNotificationPrimer {
PushNotificationPrimer(
acceptAction: { viewModel.handlePushNotificationPrimerAcceptance() },
denyAction: {
UserDefaults.standard.set(true, forKey: UserDefaultKey.userHasDeniedPushPrimer.rawValue)
viewModel.showPushNotificationPrimer = false
}
)
}
#endif
}
#if os(iOS)
// Allows us to present a sheet without animation
// Used to configure full screen modal view coming from share extension read now button action
private extension View {
func withoutAnimation(_ completion: @escaping () -> Void) {
UIView.setAnimationsEnabled(false)
completion()
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(200)) {
UIView.setAnimationsEnabled(true)
}
}
}
#endif