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 */; };
|
D664673A24BD0B8E00B0B741 /* Fonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D664673924BD0B8E00B0B741 /* Fonts.swift */; };
|
||||||
D69F00AC24BE9DD300E37622 /* GeminiDataTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69F00AB24BE9DD300E37622 /* GeminiDataTask.swift */; };
|
D69F00AC24BE9DD300E37622 /* GeminiDataTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69F00AB24BE9DD300E37622 /* GeminiDataTask.swift */; };
|
||||||
D69F00AE24BEA29100E37622 /* GeminiResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = D69F00AD24BEA29100E37622 /* GeminiResponse.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 */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -190,6 +191,7 @@
|
||||||
D664673924BD0B8E00B0B741 /* Fonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fonts.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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 */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -286,6 +288,7 @@
|
||||||
children = (
|
children = (
|
||||||
D626645E24BBF1C200DF9B88 /* AppDelegate.swift */,
|
D626645E24BBF1C200DF9B88 /* AppDelegate.swift */,
|
||||||
D626646024BBF1C200DF9B88 /* ContentView.swift */,
|
D626646024BBF1C200DF9B88 /* ContentView.swift */,
|
||||||
|
D69F00AF24BEA84D00E37622 /* NavigationManager.swift */,
|
||||||
D626646224BBF1C300DF9B88 /* Assets.xcassets */,
|
D626646224BBF1C300DF9B88 /* Assets.xcassets */,
|
||||||
D626646724BBF1C300DF9B88 /* Main.storyboard */,
|
D626646724BBF1C300DF9B88 /* Main.storyboard */,
|
||||||
D626646A24BBF1C300DF9B88 /* Info.plist */,
|
D626646A24BBF1C300DF9B88 /* Info.plist */,
|
||||||
|
@ -668,6 +671,7 @@
|
||||||
files = (
|
files = (
|
||||||
D626646124BBF1C200DF9B88 /* ContentView.swift in Sources */,
|
D626646124BBF1C200DF9B88 /* ContentView.swift in Sources */,
|
||||||
D626645F24BBF1C200DF9B88 /* AppDelegate.swift in Sources */,
|
D626645F24BBF1C200DF9B88 /* AppDelegate.swift in Sources */,
|
||||||
|
D69F00B024BEA84D00E37622 /* NavigationManager.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,11 +14,12 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
var window: NSWindow!
|
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) {
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
// Create the SwiftUI view that provides the window contents.
|
// 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.
|
// Create the window and set the content view.
|
||||||
window = NSWindow(
|
window = NSWindow(
|
||||||
|
|
|
@ -11,7 +11,7 @@ import GeminiRenderer
|
||||||
import GeminiProtocol
|
import GeminiProtocol
|
||||||
|
|
||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
let url: URL
|
let navigator: NavigationManager
|
||||||
@State var task: GeminiDataTask?
|
@State var task: GeminiDataTask?
|
||||||
@State var document: Document?
|
@State var document: Document?
|
||||||
@State var errorMessage: String?
|
@State var errorMessage: String?
|
||||||
|
@ -19,12 +19,13 @@ struct ContentView: View {
|
||||||
var body: some View {
|
var body: some View {
|
||||||
mainView
|
mainView
|
||||||
.frame(minWidth: 480, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity)
|
.frame(minWidth: 480, maxWidth: .infinity, minHeight: 300, maxHeight: .infinity)
|
||||||
|
.onReceive(navigator.$currentURL, perform: self.urlChanged)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var mainView: some View {
|
private var mainView: some View {
|
||||||
if let document = document {
|
if let document = document {
|
||||||
DocumentView(document: document)
|
DocumentView(document: document, changeURL: navigator.changeURL)
|
||||||
} else if let errorMessage = errorMessage {
|
} else if let errorMessage = errorMessage {
|
||||||
VStack {
|
VStack {
|
||||||
Text("An error occurred")
|
Text("An error occurred")
|
||||||
|
@ -47,6 +48,7 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadDocument() {
|
private func loadDocument() {
|
||||||
|
let url = navigator.currentURL
|
||||||
task = try! GeminiDataTask(url: url, completion: { (response) in
|
task = try! GeminiDataTask(url: url, completion: { (response) in
|
||||||
switch response {
|
switch response {
|
||||||
case let .failure(error):
|
case let .failure(error):
|
||||||
|
@ -58,13 +60,20 @@ struct ContentView: View {
|
||||||
}
|
}
|
||||||
self.document = GeminiParser.parse(text: text, baseURL: url)
|
self.document = GeminiParser.parse(text: text, baseURL: url)
|
||||||
}
|
}
|
||||||
|
self.task = nil
|
||||||
})
|
})
|
||||||
task!.resume()
|
task!.resume()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func urlChanged(_ newValue: URL) {
|
||||||
|
self.task = nil
|
||||||
|
self.document = nil
|
||||||
|
self.errorMessage = nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ContentView_Previews: PreviewProvider {
|
struct ContentView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
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 {
|
public struct DocumentView: View {
|
||||||
private let document: Document
|
private let document: Document
|
||||||
private let blocks: [RenderingBlock]
|
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.document = document
|
||||||
self.blocks = document.renderingBlocks
|
self.blocks = document.renderingBlocks
|
||||||
|
self.changeURL = changeURL
|
||||||
}
|
}
|
||||||
|
|
||||||
public var body: some View {
|
public var body: some View {
|
||||||
ScrollView(.vertical) {
|
ScrollView(.vertical) {
|
||||||
MaybeLazyVStack(alignment: .leading) {
|
MaybeLazyVStack(alignment: .leading) {
|
||||||
ForEach(blocks.indices) { (index) in
|
ForEach(blocks.indices) { (index) in
|
||||||
RenderingBlockView(block: blocks[index])
|
RenderingBlockView(block: blocks[index], changeURL: changeURL)
|
||||||
}
|
}
|
||||||
}.padding()
|
}.padding()
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,12 @@ import GeminiFormat
|
||||||
|
|
||||||
struct RenderingBlockView: View {
|
struct RenderingBlockView: View {
|
||||||
let block: RenderingBlock
|
let block: RenderingBlock
|
||||||
|
let changeURL: ((URL) -> Void)?
|
||||||
|
|
||||||
|
init(block: RenderingBlock, changeURL: ((URL) -> Void)? = nil) {
|
||||||
|
self.block = block
|
||||||
|
self.changeURL = changeURL
|
||||||
|
}
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
@ -20,11 +26,15 @@ struct RenderingBlockView: View {
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
case let .link(url, text: linkText):
|
case let .link(url, text: linkText):
|
||||||
let text = linkText ?? url.absoluteString
|
let text = linkText ?? url.absoluteString
|
||||||
|
Button {
|
||||||
|
self.changeURL?(url)
|
||||||
|
} label: {
|
||||||
Text(verbatim: text)
|
Text(verbatim: text)
|
||||||
.font(.documentBody)
|
.font(.documentBody)
|
||||||
.foregroundColor(.blue)
|
.foregroundColor(.blue)
|
||||||
.underline()
|
.underline()
|
||||||
.frame(maxWidth: .infinity, alignment: .leading)
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
}.buttonStyle(LinkButtonStyle())
|
||||||
case let .preformatted(text, alt: _):
|
case let .preformatted(text, alt: _):
|
||||||
ScrollView(.horizontal) {
|
ScrollView(.horizontal) {
|
||||||
Text(verbatim: text)
|
Text(verbatim: text)
|
||||||
|
|
Loading…
Reference in New Issue