// // ContentView.swift // Gemini // // Created by Shadowfacts on 7/12/20. // import SwiftUI import GeminiFormat import GeminiRenderer import GeminiProtocol public struct BrowserView: View { let navigator: NavigationManager let scrollingEnabled: Bool @State var task: GeminiDataTask? @State var state: ViewState = .loading public init(navigator: NavigationManager, scrollingEnabled: Bool = true) { self.navigator = navigator self.scrollingEnabled = scrollingEnabled } public var body: some View { mainView .onReceive(navigator.$currentURL, perform: self.urlChanged) } @ViewBuilder private var mainView: some View { VStack { switch state { case .loading: Spacer() loadingView .onAppear(perform: self.loadDocument) Spacer() case let .error(message): Text("An error occurred") .font(.headline) Text(message) .lineLimit(nil) case let .document(doc): DocumentView(document: doc, scrollingEnabled: scrollingEnabled, changeURL: navigator.changeURL) Spacer() } } } @ViewBuilder private var loadingView: some View { if #available(macOS 10.16, iOS 14.0, *) { ProgressView("Loading...") } else { Text("Loading...") } } private func loadDocument() { let url = navigator.currentURL task = try! GeminiDataTask(url: url, completion: { (response) in self.task = nil switch response { case let .failure(error): state = .error(error.localizedDescription) case let .success(response): if response.status.isRedirect { if let redirect = URL(string: response.meta) { navigator.changeURL(redirect) loadDocument() } else { state = .error("Invalid redirect URL: '\(response.meta)'") } } else if response.status.isSuccess, let text = response.bodyText { state = .document(GeminiParser.parse(text: text, baseURL: url)) } else { state = .error("Unknown error: \(response.header)") } } }) task!.resume() } private func urlChanged(_ newValue: URL) { if case .loading = state { task?.cancel() loadDocument() } else { state = .loading } } } extension BrowserView { enum ViewState { case loading case document(Document) case error(String) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { BrowserView(navigator: NavigationManager(url: URL(string: "gemini://localhost/overview.gmi")!)) } }