Add Markdown file previews
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
950C584D58E80106DF350A22 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C0921C76676A14A024BA417 /* AppDelegate.swift */; };
|
||||
96C4AFC206A1DBAE3D3E00CE /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8F145F13B8ED83A5AB0009A4 /* SettingsViewController.swift */; };
|
||||
D59D3ED36ABCC1D8690A9088 /* gotcha_core.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE12480725C13293FAFDDA52 /* gotcha_core.swift */; };
|
||||
D78A121E9ADA0B72E2CF94DC /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = 8AD463F3AC817A44306C2297 /* MarkdownUI */; };
|
||||
E7AC0B140F5CFC5EF226D174 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DDAABE6B13ADC6D08D9438AF /* Assets.xcassets */; };
|
||||
EC7F8B4703DDE37A0B10CD9B /* AppContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = F61515849F6AACD721FE915C /* AppContext.swift */; };
|
||||
F55A89489B2758D694F3B27D /* Support.swift in Sources */ = {isa = PBXBuildFile; fileRef = F75B3E4FFB9C9992517C4D69 /* Support.swift */; };
|
||||
@@ -38,6 +39,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7DD332583B169B3CA1CA46E0 /* Highlighter in Frameworks */,
|
||||
D78A121E9ADA0B72E2CF94DC /* MarkdownUI in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -103,6 +105,7 @@
|
||||
name = Gotcha;
|
||||
packageProductDependencies = (
|
||||
EC5F999F50905E3801E8A71A /* Highlighter */,
|
||||
8AD463F3AC817A44306C2297 /* MarkdownUI */,
|
||||
);
|
||||
productName = Gotcha;
|
||||
productReference = 5FB3250A93766966A60A685E /* Gotcha.app */;
|
||||
@@ -134,6 +137,7 @@
|
||||
minimizedProjectReferenceProxies = 1;
|
||||
packageReferences = (
|
||||
85EFB86C5B2FDE8DEAD98A24 /* XCRemoteSwiftPackageReference "HighlighterSwift" */,
|
||||
0B96240AE4FB1E82EE0D6B17 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */,
|
||||
);
|
||||
preferredProjectObjectVersion = 77;
|
||||
productRefGroup = F059299C038F3CAFCE470831 /* Products */;
|
||||
@@ -389,6 +393,14 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
0B96240AE4FB1E82EE0D6B17 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/gonzalezreal/swift-markdown-ui";
|
||||
requirement = {
|
||||
kind = upToNextMajorVersion;
|
||||
minimumVersion = 2.1.0;
|
||||
};
|
||||
};
|
||||
85EFB86C5B2FDE8DEAD98A24 /* XCRemoteSwiftPackageReference "HighlighterSwift" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/smittytone/HighlighterSwift";
|
||||
@@ -400,6 +412,11 @@
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
8AD463F3AC817A44306C2297 /* MarkdownUI */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 0B96240AE4FB1E82EE0D6B17 /* XCRemoteSwiftPackageReference "swift-markdown-ui" */;
|
||||
productName = MarkdownUI;
|
||||
};
|
||||
EC5F999F50905E3801E8A71A /* Highlighter */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 85EFB86C5B2FDE8DEAD98A24 /* XCRemoteSwiftPackageReference "HighlighterSwift" */;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"originHash" : "00a1c96095892a05bfd089593813422ede18735e452666e3e7a642ebd9048a45",
|
||||
"originHash" : "7a8d1ff34841e518cc21434c2148b0c9b3d7c0d8b436c01ab8711755963dcc94",
|
||||
"pins" : [
|
||||
{
|
||||
"identity" : "highlighterswift",
|
||||
@@ -9,6 +9,33 @@
|
||||
"revision" : "fe7aae9c9b31d3b296fd3d2dd575e1a207bb29e0",
|
||||
"version" : "3.1.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "networkimage",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/gonzalezreal/NetworkImage",
|
||||
"state" : {
|
||||
"revision" : "2849f5323265386e200484b0d0f896e73c3411b9",
|
||||
"version" : "6.0.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-cmark",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/swiftlang/swift-cmark",
|
||||
"state" : {
|
||||
"revision" : "924936d0427cb25a61169739a7660230bffa6ea6",
|
||||
"version" : "0.8.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"identity" : "swift-markdown-ui",
|
||||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/gonzalezreal/swift-markdown-ui",
|
||||
"state" : {
|
||||
"revision" : "5f613358148239d0292c0cef674a3c2314737f9e",
|
||||
"version" : "2.4.1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"version" : 3
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import Highlighter
|
||||
import MarkdownUI
|
||||
import QuickLook
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
@@ -336,6 +338,8 @@ final class RepositoryDirectoryViewController: RefreshingTableViewController {
|
||||
|
||||
@MainActor
|
||||
final class RepositoryFileViewController: UIViewController, QLPreviewControllerDataSource {
|
||||
private enum MarkdownMode: Int { case preview, source }
|
||||
|
||||
private let context: AppContext
|
||||
private let owner: String
|
||||
private let repository: String
|
||||
@@ -343,6 +347,9 @@ final class RepositoryFileViewController: UIViewController, QLPreviewControllerD
|
||||
private let spinner = UIActivityIndicatorView(style: .medium)
|
||||
private var loadingTask: Task<Void, Never>?
|
||||
private var previewURL: URL?
|
||||
private var markdownSource: String?
|
||||
private var displayedController: UIViewController?
|
||||
private var displayedView: UIView?
|
||||
private weak var sourceScrollView: UIScrollView?
|
||||
private weak var sourceTextView: UITextView?
|
||||
|
||||
@@ -370,7 +377,11 @@ final class RepositoryFileViewController: UIViewController, QLPreviewControllerD
|
||||
path: path
|
||||
)
|
||||
if !isMediaFile(path), let source = String(data: data, encoding: .utf8) {
|
||||
showSource(source)
|
||||
if isMarkdownFile {
|
||||
showMarkdown(source)
|
||||
} else {
|
||||
showSource(source)
|
||||
}
|
||||
} else {
|
||||
try showQuickLook(data)
|
||||
}
|
||||
@@ -396,6 +407,10 @@ final class RepositoryFileViewController: UIViewController, QLPreviewControllerD
|
||||
|| type.conforms(to: .pdf)
|
||||
}
|
||||
|
||||
private var isMarkdownFile: Bool {
|
||||
["md", "markdown", "mdown", "mkd"].contains((path as NSString).pathExtension.lowercased())
|
||||
}
|
||||
|
||||
deinit {
|
||||
loadingTask?.cancel()
|
||||
if let previewURL { try? FileManager.default.removeItem(at: previewURL) }
|
||||
@@ -409,7 +424,40 @@ final class RepositoryFileViewController: UIViewController, QLPreviewControllerD
|
||||
previewURL! as NSURL
|
||||
}
|
||||
|
||||
private func showMarkdown(_ source: String) {
|
||||
markdownSource = source
|
||||
navigationItem.prompt = title
|
||||
let modeControl = UISegmentedControl(items: ["Preview", "Source"])
|
||||
modeControl.selectedSegmentIndex = MarkdownMode.preview.rawValue
|
||||
modeControl.addTarget(self, action: #selector(markdownModeChanged(_:)), for: .valueChanged)
|
||||
modeControl.accessibilityLabel = "Markdown view"
|
||||
navigationItem.titleView = modeControl
|
||||
showMarkdownPreview(source)
|
||||
}
|
||||
|
||||
@objc private func markdownModeChanged(_ sender: UISegmentedControl) {
|
||||
guard let mode = MarkdownMode(rawValue: sender.selectedSegmentIndex),
|
||||
let source = markdownSource else { return }
|
||||
switch mode {
|
||||
case .preview: showMarkdownPreview(source)
|
||||
case .source: showSource(source)
|
||||
}
|
||||
}
|
||||
|
||||
private func showMarkdownPreview(_ source: String) {
|
||||
removeDisplayedView()
|
||||
let preview = UIHostingController(rootView: RepositoryMarkdownPreview(source: source))
|
||||
addChild(preview)
|
||||
preview.view.frame = view.bounds
|
||||
preview.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
displayedController = preview
|
||||
displayedView = preview.view
|
||||
view.addSubview(preview.view)
|
||||
preview.didMove(toParent: self)
|
||||
}
|
||||
|
||||
private func showSource(_ source: String) {
|
||||
removeDisplayedView()
|
||||
let highlighter = Highlighter()
|
||||
highlighter?.setTheme(
|
||||
traitCollection.userInterfaceStyle == .dark ? "atom-one-dark" : "atom-one-light"
|
||||
@@ -445,10 +493,21 @@ final class RepositoryFileViewController: UIViewController, QLPreviewControllerD
|
||||
sourceScrollView = scrollView
|
||||
sourceTextView = textView
|
||||
scrollView.addSubview(textView)
|
||||
displayedView = scrollView
|
||||
view.addSubview(scrollView)
|
||||
updateSourceLayout()
|
||||
}
|
||||
|
||||
private func removeDisplayedView() {
|
||||
displayedController?.willMove(toParent: nil)
|
||||
displayedView?.removeFromSuperview()
|
||||
displayedController?.removeFromParent()
|
||||
displayedController = nil
|
||||
displayedView = nil
|
||||
sourceScrollView = nil
|
||||
sourceTextView = nil
|
||||
}
|
||||
|
||||
private func updateSourceLayout() {
|
||||
guard let scrollView = sourceScrollView, let textView = sourceTextView else { return }
|
||||
textView.textContainer.widthTracksTextView = false
|
||||
@@ -488,6 +547,20 @@ final class RepositoryFileViewController: UIViewController, QLPreviewControllerD
|
||||
}
|
||||
}
|
||||
|
||||
private struct RepositoryMarkdownPreview: View {
|
||||
let source: String
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
Markdown(source)
|
||||
.markdownTheme(.gitHub)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding()
|
||||
}
|
||||
.background(Color(uiColor: .systemBackground))
|
||||
}
|
||||
}
|
||||
|
||||
enum DiffSource {
|
||||
case commit(owner: String, repository: String, sha: String, path: String)
|
||||
case pull(owner: String, repository: String, number: Int64, path: String)
|
||||
|
||||
@@ -7,6 +7,9 @@ packages:
|
||||
Highlighter:
|
||||
url: https://github.com/smittytone/HighlighterSwift
|
||||
from: 3.1.0
|
||||
MarkdownUI:
|
||||
url: https://github.com/gonzalezreal/swift-markdown-ui
|
||||
from: 2.1.0
|
||||
targets:
|
||||
Gotcha:
|
||||
type: application
|
||||
@@ -35,6 +38,7 @@ targets:
|
||||
- Generated/gotcha_core.swift
|
||||
dependencies:
|
||||
- package: Highlighter
|
||||
- package: MarkdownUI
|
||||
preBuildScripts:
|
||||
- name: Build Rust core
|
||||
basedOnDependencyAnalysis: false
|
||||
|
||||
Reference in New Issue
Block a user