Make clicking links navigate
This commit is contained in:
parent
564c4a5020
commit
968837a6da
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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")!))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue