mirror of
git://jb55.com/damus
synced 2024-09-30 00:40:45 +00:00
Fix broken markdown renderer
This switches away from the old markdown renderer to the new one at https://github.com/damus-io/swift-markdown-ui Changelog-Fixed: Fix broken markdown renderer
This commit is contained in:
parent
bf1175f22c
commit
bd4c29604f
@ -96,7 +96,6 @@
|
||||
4C363AA228296A7E006E126D /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363AA128296A7E006E126D /* SearchView.swift */; };
|
||||
4C363AA428296DEE006E126D /* SearchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363AA328296DEE006E126D /* SearchModel.swift */; };
|
||||
4C363AA828297703006E126D /* InsertSort.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C363AA728297703006E126D /* InsertSort.swift */; };
|
||||
4C3A1D332960DB0500558C0F /* Markdown.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3A1D322960DB0500558C0F /* Markdown.swift */; };
|
||||
4C3A1D3729637E0500558C0F /* PreviewCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3A1D3629637E0500558C0F /* PreviewCache.swift */; };
|
||||
4C3AC79B28306D7B00E1F516 /* Contacts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3AC79A28306D7B00E1F516 /* Contacts.swift */; };
|
||||
4C3AC79D2833036D00E1F516 /* FollowingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C3AC79C2833036D00E1F516 /* FollowingView.swift */; };
|
||||
@ -540,7 +539,6 @@
|
||||
4C363AA128296A7E006E126D /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; };
|
||||
4C363AA328296DEE006E126D /* SearchModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchModel.swift; sourceTree = "<group>"; };
|
||||
4C363AA728297703006E126D /* InsertSort.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InsertSort.swift; sourceTree = "<group>"; };
|
||||
4C3A1D322960DB0500558C0F /* Markdown.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Markdown.swift; sourceTree = "<group>"; };
|
||||
4C3A1D3629637E0500558C0F /* PreviewCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewCache.swift; sourceTree = "<group>"; };
|
||||
4C3AC79A28306D7B00E1F516 /* Contacts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Contacts.swift; sourceTree = "<group>"; };
|
||||
4C3AC79C2833036D00E1F516 /* FollowingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FollowingView.swift; sourceTree = "<group>"; };
|
||||
@ -1245,7 +1243,6 @@
|
||||
4CE879492995B58700F758CC /* Relays */,
|
||||
4CF0ABEA29844B2F00D66079 /* AnyCodable */,
|
||||
4CC7AAE6297EFA7B00430951 /* Zap.swift */,
|
||||
4C3A1D322960DB0500558C0F /* Markdown.swift */,
|
||||
F7908E96298B1FDF00AB113A /* NIPURLBuilder.swift */,
|
||||
4CEE2AF4280B29E600AB5EEF /* TimeAgo.swift */,
|
||||
4CE4F8CC281352B30009DFBB /* Notifications.swift */,
|
||||
@ -2092,7 +2089,6 @@
|
||||
5C513FBA297F72980072348F /* CustomPicker.swift in Sources */,
|
||||
4CACA9D5280C31E100D9BBE8 /* ReplyView.swift in Sources */,
|
||||
F7908E92298B0F0700AB113A /* RelayDetailView.swift in Sources */,
|
||||
4C3A1D332960DB0500558C0F /* Markdown.swift in Sources */,
|
||||
4C9147002A2A891E00DDEA40 /* error.c in Sources */,
|
||||
4CE879552996BAB900F758CC /* RelayPaidDetail.swift in Sources */,
|
||||
501F8C5529FF5EF6001AFC1D /* PersistedProfile.swift in Sources */,
|
||||
|
@ -1243,7 +1243,7 @@ func render_notification_content_preview(cache: EventCache, ev: NostrEvent, prof
|
||||
}
|
||||
|
||||
switch artifacts {
|
||||
case .parts:
|
||||
case .longform:
|
||||
// we should never hit this until we have more note types built out of parts
|
||||
// since we handle this case above in known_kind == .longform
|
||||
return String(ev.content.prefix(prefix_len))
|
||||
|
@ -1,88 +0,0 @@
|
||||
//
|
||||
// Markdown.swift
|
||||
// damus
|
||||
//
|
||||
// Created by Lionello Lunesu on 2022-12-28.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
func count_leading_hashes(_ str: String) -> Int {
|
||||
var count = 0
|
||||
for c in str {
|
||||
if c == "#" {
|
||||
count += 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
func get_heading_title_size(count: Int) -> SwiftUI.Font {
|
||||
if count >= 3 {
|
||||
return Font.title3
|
||||
} else if count >= 2 {
|
||||
return Font.title2
|
||||
} else if count >= 1 {
|
||||
return Font.title
|
||||
}
|
||||
|
||||
return Font.body
|
||||
}
|
||||
|
||||
public struct Markdown {
|
||||
private var detector = try? NSDataDetector(types: NSTextCheckingResult.CheckingType.link.rawValue)
|
||||
|
||||
/// Ensure the specified URL has a scheme by prepending "https://" if it's absent.
|
||||
static func withScheme(_ url: any StringProtocol) -> any StringProtocol {
|
||||
return url.contains("://") ? url : "https://" + url
|
||||
}
|
||||
|
||||
/// Parse a string with markdown into an `AttributedString`, if possible, or else return it as regular text.
|
||||
public static func parse(content: String) -> AttributedString {
|
||||
let md_opts: AttributedString.MarkdownParsingOptions =
|
||||
.init(interpretedSyntax: .inlineOnlyPreservingWhitespace)
|
||||
|
||||
guard content.utf8.count > 0 else {
|
||||
return AttributedString(stringLiteral: "")
|
||||
}
|
||||
|
||||
let leading_hashes = count_leading_hashes(content)
|
||||
if leading_hashes > 0 {
|
||||
if var str = try? AttributedString(markdown: content) {
|
||||
str.font = get_heading_title_size(count: leading_hashes)
|
||||
return str
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: escape unintentional markdown
|
||||
let escaped = content.replacingOccurrences(of: "\\_", with: "\\\\\\_")
|
||||
if let txt = try? AttributedString(markdown: escaped, options: md_opts) {
|
||||
return txt
|
||||
} else {
|
||||
return AttributedString(stringLiteral: content)
|
||||
}
|
||||
}
|
||||
|
||||
/// Process the input text and add markdown for any embedded URLs.
|
||||
public func process(_ input: String) -> AttributedString {
|
||||
guard let detector else {
|
||||
return AttributedString(stringLiteral: input)
|
||||
}
|
||||
let matches = detector.matches(in: input, options: [], range: NSRange(location: 0, length: input.utf16.count))
|
||||
var output = input
|
||||
// Start with the last match, because replacing the first would invalidate all subsequent indices
|
||||
for match in matches.reversed() {
|
||||
guard let range = Range(match.range, in: input)
|
||||
, let url = match.url else { continue }
|
||||
let text = input[range]
|
||||
// Use the absoluteString from the matched URL, except when it defaults to http (since we default to https)
|
||||
let uri = url.scheme == "http" ? Markdown.withScheme(text) : url.absoluteString
|
||||
output.replaceSubrange(range, with: "[\(text)](\(uri))")
|
||||
}
|
||||
return Markdown.parse(content: output)
|
||||
}
|
||||
}
|
@ -7,6 +7,8 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
import MarkdownUI
|
||||
|
||||
let eula = """
|
||||
**End User License Agreement**
|
||||
|
||||
@ -63,7 +65,7 @@ struct EULAView: View {
|
||||
var body: some View {
|
||||
ZStack {
|
||||
ScrollView {
|
||||
Text(Markdown.parse(content: eula))
|
||||
Markdown(eula)
|
||||
.padding()
|
||||
}
|
||||
.padding(EdgeInsets(top: 20, leading: 10, bottom: 50, trailing: 10))
|
||||
|
@ -58,9 +58,9 @@ struct LongformPreviewBody: View {
|
||||
.foregroundColor(.gray)
|
||||
|
||||
if case .loaded(let arts) = artifacts.state,
|
||||
case .parts(let parts) = arts
|
||||
case .longform(let longform) = arts
|
||||
{
|
||||
Words(parts.words).font(.footnote)
|
||||
Words(longform.words).font(.footnote)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,6 @@ struct FollowUserView: View {
|
||||
let target: FollowTarget
|
||||
let damus_state: DamusState
|
||||
|
||||
static let markdown = Markdown()
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
UserViewRow(damus_state: damus_state, pubkey: target.pubkey)
|
||||
|
@ -8,6 +8,7 @@
|
||||
import SwiftUI
|
||||
import LinkPresentation
|
||||
import NaturalLanguage
|
||||
import MarkdownUI
|
||||
|
||||
struct Blur: UIViewRepresentable {
|
||||
var style: UIBlurEffect.Style = .systemUltraThinMaterial
|
||||
@ -212,8 +213,9 @@ struct NoteContentView: View {
|
||||
var ArtifactContent: some View {
|
||||
Group {
|
||||
switch self.note_artifacts {
|
||||
case .parts(let parts):
|
||||
artifactPartsView(parts.parts)
|
||||
case .longform(let md):
|
||||
Markdown(md.markdown)
|
||||
.padding(.horizontal)
|
||||
case .separated(let separated):
|
||||
MainContent(artifacts: separated)
|
||||
}
|
||||
@ -285,22 +287,27 @@ func mention_str(_ m: Mention, profiles: Profiles) -> CompatibleText {
|
||||
}
|
||||
}
|
||||
|
||||
struct LongformContent {
|
||||
let markdown: MarkdownContent
|
||||
let words: Int
|
||||
|
||||
init(_ markdown: String) {
|
||||
let blocks = [BlockNode].init(markdown: markdown)
|
||||
self.markdown = MarkdownContent(blocks: blocks)
|
||||
self.words = count_markdown_words(blocks: blocks)
|
||||
}
|
||||
}
|
||||
|
||||
enum NoteArtifacts {
|
||||
case separated(NoteArtifactsSeparated)
|
||||
case parts(NoteArtifactsParts)
|
||||
case longform(LongformContent)
|
||||
|
||||
var images: [URL] {
|
||||
switch self {
|
||||
case .separated(let arts):
|
||||
return arts.images
|
||||
case .parts(let parts):
|
||||
return parts.parts.reduce(into: [URL]()) { acc, part in
|
||||
guard case .media(let m) = part,
|
||||
case .image(let url) = m
|
||||
else { return }
|
||||
|
||||
acc.append(url)
|
||||
}
|
||||
case .longform:
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -390,7 +397,7 @@ func render_note_content(ev: NostrEvent, profiles: Profiles, privkey: String?) -
|
||||
let blocks = ev.blocks(privkey)
|
||||
|
||||
if ev.known_kind == .longform {
|
||||
return .parts(render_blocks_parted(blocks: blocks, profiles: profiles))
|
||||
return .longform(LongformContent(ev.content))
|
||||
}
|
||||
|
||||
return .separated(render_blocks(blocks: blocks, profiles: profiles))
|
||||
@ -409,68 +416,6 @@ fileprivate func artifact_part_last_text_ind(parts: [ArtifactPart]) -> (Int, Tex
|
||||
return (ind, txt)
|
||||
}
|
||||
|
||||
func render_blocks_parted(blocks bs: Blocks, profiles: Profiles) -> NoteArtifactsParts {
|
||||
let blocks = bs.blocks
|
||||
|
||||
let new_parts = NoteArtifactsParts(parts: [], words: bs.words)
|
||||
|
||||
return blocks.reduce(into: new_parts) { parts, block in
|
||||
|
||||
switch block {
|
||||
case .mention(let m):
|
||||
guard let (last_ind, txt) = artifact_part_last_text_ind(parts: parts.parts) else {
|
||||
parts.parts.append(.text(mention_str(m, profiles: profiles).text))
|
||||
return
|
||||
}
|
||||
parts.parts[last_ind] = .text(txt + mention_str(m, profiles: profiles).text)
|
||||
|
||||
case .text(let str):
|
||||
guard let (last_ind, txt) = artifact_part_last_text_ind(parts: parts.parts) else {
|
||||
// TODO: (jb55) md is longform specific
|
||||
let md = Markdown.parse(content: str)
|
||||
parts.parts.append(.text(Text(md)))
|
||||
return
|
||||
}
|
||||
|
||||
parts.parts[last_ind] = .text(txt + Text(str))
|
||||
|
||||
case .relay(let relay):
|
||||
guard let (last_ind, txt) = artifact_part_last_text_ind(parts: parts.parts) else {
|
||||
parts.parts.append(.text(Text(relay)))
|
||||
return
|
||||
}
|
||||
|
||||
parts.parts[last_ind] = .text(txt + Text(relay))
|
||||
|
||||
case .hashtag(let htag):
|
||||
guard let (last_ind, txt) = artifact_part_last_text_ind(parts: parts.parts) else {
|
||||
parts.parts.append(.text(hashtag_str(htag).text))
|
||||
return
|
||||
}
|
||||
|
||||
parts.parts[last_ind] = .text(txt + hashtag_str(htag).text)
|
||||
|
||||
case .invoice(let invoice):
|
||||
parts.parts.append(.invoice(invoice))
|
||||
return
|
||||
|
||||
case .url(let url):
|
||||
let url_type = classify_url(url)
|
||||
switch url_type {
|
||||
case .media(let media_url):
|
||||
parts.parts.append(.media(media_url))
|
||||
case .link(let url):
|
||||
guard let (last_ind, txt) = artifact_part_last_text_ind(parts: parts.parts) else {
|
||||
parts.parts.append(.text(url_str(url).text))
|
||||
return
|
||||
}
|
||||
|
||||
parts.parts[last_ind] = .text(txt + url_str(url).text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func reduce_text_block(blocks: [Block], ind: Int, txt: String, one_note_ref: Bool) -> CompatibleText {
|
||||
var trimmed = txt
|
||||
|
||||
|
@ -104,8 +104,6 @@ struct ProfileView: View {
|
||||
let pfp_size: CGFloat = 90.0
|
||||
let bannerHeight: CGFloat = 150.0
|
||||
|
||||
static let markdown = Markdown()
|
||||
|
||||
@State var is_zoomed: Bool = false
|
||||
@State var show_share_sheet: Bool = false
|
||||
@State var show_qr_code: Bool = false
|
||||
|
Loading…
Reference in New Issue
Block a user