Make clicking links navigate

This commit is contained in:
Shadowfacts 2020-07-14 23:15:56 -04:00
parent 564c4a5020
commit 968837a6da
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
6 changed files with 60 additions and 12 deletions

View File

@ -45,6 +45,7 @@
D664673A24BD0B8E00B0B741 /* Fonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664673924BD0B8E00B0B741 /* Fonts.swift */; };
D69F00AC24BE9DD300E37622 /* GeminiDataTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69F00AB24BE9DD300E37622 /* GeminiDataTask.swift */; };
D69F00AE24BEA29100E37622 /* GeminiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69F00AD24BEA29100E37622 /* GeminiResponse.swift */; };
D69F00B024BEA84D00E37622 /* NavigationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69F00AF24BEA84D00E37622 /* NavigationManager.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -190,6 +191,7 @@
D664673924BD0B8E00B0B741 /* Fonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fonts.swift; sourceTree = "<group>"; };
D69F00AB24BE9DD300E37622 /* GeminiDataTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeminiDataTask.swift; sourceTree = "<group>"; };
D69F00AD24BEA29100E37622 /* GeminiResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeminiResponse.swift; sourceTree = "<group>"; };
D69F00AF24BEA84D00E37622 /* NavigationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationManager.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -286,6 +288,7 @@
children = (
D626645E24BBF1C200DF9B88 /* AppDelegate.swift */,
D626646024BBF1C200DF9B88 /* ContentView.swift */,
D69F00AF24BEA84D00E37622 /* NavigationManager.swift */,
D626646224BBF1C300DF9B88 /* Assets.xcassets */,
D626646724BBF1C300DF9B88 /* Main.storyboard */,
D626646A24BBF1C300DF9B88 /* Info.plist */,
@ -668,6 +671,7 @@
files = (
D626646124BBF1C200DF9B88 /* ContentView.swift in Sources */,
D626645F24BBF1C200DF9B88 /* AppDelegate.swift in Sources */,
D69F00B024BEA84D00E37622 /* NavigationManager.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -14,11 +14,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
var window: NSWindow!
let url = URL(string: "gemini://gemini.circumlunar.space/")!
let homePage = URL(string: "gemini://gemini.circumlunar.space/")!
private(set) lazy var navigationManager = NavigationManager(url: homePage)
func applicationDidFinishLaunching(_ aNotification: Notification) {
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView(url: url)
let contentView = ContentView(navigator: navigationManager)
// Create the window and set the content view.
window = NSWindow(

View File

@ -11,7 +11,7 @@ import GeminiRenderer
import GeminiProtocol
struct ContentView: View {
let url: URL
let navigator: NavigationManager
@State var task: GeminiDataTask?
@State var document: Document?
@State var errorMessage: String?
@ -19,12 +19,13 @@ struct ContentView: View {
var body: some View {
mainView
.frame(minWidth: 480, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity)
.onReceive(navigator.$currentURL, perform: self.urlChanged)
}
@ViewBuilder
private var mainView: some View {
if let document = document {
DocumentView(document: document)
DocumentView(document: document, changeURL: navigator.changeURL)
} else if let errorMessage = errorMessage {
VStack {
Text("An error occurred")
@ -47,6 +48,7 @@ struct ContentView: View {
}
private func loadDocument() {
let url = navigator.currentURL
task = try! GeminiDataTask(url: url, completion: { (response) in
switch response {
case let .failure(error):
@ -58,13 +60,20 @@ struct ContentView: View {
}
self.document = GeminiParser.parse(text: text, baseURL: url)
}
self.task = nil
})
task!.resume()
}
private func urlChanged(_ newValue: URL) {
self.task = nil
self.document = nil
self.errorMessage = nil
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(url: URL(string: "gemini://gemini.circumlunar.space/")!)
ContentView(navigator: NavigationManager(url: URL(string: "gemini://localhost/overview.gmi")!))
}
}

View File

@ -0,0 +1,22 @@
//
// NavigationManager.swift
// Gemini
//
// Created by Shadowfacts on 7/14/20.
//
import Foundation
class NavigationManager: ObservableObject {
@Published var currentURL: URL
init(url: URL) {
self.currentURL = url
}
func changeURL(_ url: URL) {
self.currentURL = url
}
}

View File

@ -11,17 +11,19 @@ import GeminiFormat
public struct DocumentView: View {
private let document: Document
private let blocks: [RenderingBlock]
private let changeURL: ((URL) -> Void)?
public init(document: Document) {
public init(document: Document, changeURL: ((URL) -> Void)? = nil) {
self.document = document
self.blocks = document.renderingBlocks
self.changeURL = changeURL
}
public var body: some View {
ScrollView(.vertical) {
MaybeLazyVStack(alignment: .leading) {
ForEach(blocks.indices) { (index) in
RenderingBlockView(block: blocks[index])
RenderingBlockView(block: blocks[index], changeURL: changeURL)
}
}.padding()
}

View File

@ -10,6 +10,12 @@ import GeminiFormat
struct RenderingBlockView: View {
let block: RenderingBlock
let changeURL: ((URL) -> Void)?
init(block: RenderingBlock, changeURL: ((URL) -> Void)? = nil) {
self.block = block
self.changeURL = changeURL
}
@ViewBuilder
var body: some View {
@ -20,11 +26,15 @@ struct RenderingBlockView: View {
.frame(maxWidth: .infinity, alignment: .leading)
case let .link(url, text: linkText):
let text = linkText ?? url.absoluteString
Text(verbatim: text)
.font(.documentBody)
.foregroundColor(.blue)
.underline()
.frame(maxWidth: .infinity, alignment: .leading)
Button {
self.changeURL?(url)
} label: {
Text(verbatim: text)
.font(.documentBody)
.foregroundColor(.blue)
.underline()
.frame(maxWidth: .infinity, alignment: .leading)
}.buttonStyle(LinkButtonStyle())
case let .preformatted(text, alt: _):
ScrollView(.horizontal) {
Text(verbatim: text)