// // 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 @State var task: GeminiDataTask? @State var state: ViewState = .loading public init(navigator: NavigationManager) { self.navigator = navigator } public var body: some View { mainView .onReceive(navigator.$currentURL, perform: self.urlChanged) } @ViewBuilder private var mainView: some View { switch state { case .loading: loadingView .onAppear(perform: self.loadDocument) case let .error(message): VStack { Text("An error occurred") .font(.headline) Text(message) } case let .document(doc): DocumentView(document: doc, changeURL: navigator.changeURL) } } @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) { 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")!)) } }