Gemini/BrowserCore/BrowserView.swift
2020-09-29 16:28:37 -04:00

108 lines
3.0 KiB
Swift

//
// 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")!))
}
}