Parse links in profiles
Changelog-Added: Parse links in profiles
This commit is contained in:
43
damus/Util/Markdown.swift
Normal file
43
damus/Util/Markdown.swift
Normal file
@@ -0,0 +1,43 @@
|
||||
//
|
||||
// Markdown.swift
|
||||
// damus
|
||||
//
|
||||
// Created by Lionello Lunesu on 2022-12-28.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public struct Markdown {
|
||||
private let 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
|
||||
}
|
||||
|
||||
public static func parse(content: String) -> AttributedString {
|
||||
// Similar to the parsing in NoteContentView
|
||||
let md_opts: AttributedString.MarkdownParsingOptions =
|
||||
.init(interpretedSyntax: .inlineOnlyPreservingWhitespace)
|
||||
|
||||
if let txt = try? AttributedString(markdown: content, 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 {
|
||||
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) else { continue }
|
||||
let url = input[range]
|
||||
output.replaceSubrange(range, with: "[\(url)](\(Markdown.withScheme(url)))")
|
||||
}
|
||||
// TODO: escape unintentional markdown
|
||||
return Markdown.parse(content: output)
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,9 @@ import SwiftUI
|
||||
struct FollowUserView: View {
|
||||
let target: FollowTarget
|
||||
let damus_state: DamusState
|
||||
|
||||
|
||||
static let markdown = Markdown()
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
let pmodel = ProfileModel(pubkey: target.pubkey, damus: damus_state)
|
||||
@@ -23,8 +25,8 @@ struct FollowUserView: View {
|
||||
VStack(alignment: .leading) {
|
||||
let profile = damus_state.profiles.lookup(id: target.pubkey)
|
||||
ProfileName(pubkey: target.pubkey, profile: profile, contacts: damus_state.contacts, show_friend_confirmed: false)
|
||||
if let about = profile.flatMap { $0.about } {
|
||||
Text(about)
|
||||
if let about = profile?.about {
|
||||
Text(FollowUserView.markdown.process(about))
|
||||
.lineLimit(3)
|
||||
.font(.footnote)
|
||||
}
|
||||
|
||||
@@ -70,17 +70,10 @@ struct NoteContentView: View {
|
||||
let size: EventViewKind
|
||||
|
||||
func MainContent() -> some View {
|
||||
let md_opts: AttributedString.MarkdownParsingOptions =
|
||||
.init(interpretedSyntax: .inlineOnlyPreservingWhitespace)
|
||||
|
||||
return VStack(alignment: .leading) {
|
||||
if let txt = try? AttributedString(markdown: artifacts.content, options: md_opts) {
|
||||
Text(txt)
|
||||
.font(eventviewsize_to_font(size))
|
||||
} else {
|
||||
Text(artifacts.content)
|
||||
.font(eventviewsize_to_font(size))
|
||||
}
|
||||
Text(Markdown.parse(content: artifacts.content))
|
||||
.font(eventviewsize_to_font(size))
|
||||
|
||||
if show_images && artifacts.images.count > 0 {
|
||||
ImageCarousel(urls: artifacts.images)
|
||||
} else if !show_images && artifacts.images.count > 0 {
|
||||
|
||||
@@ -152,7 +152,9 @@ struct ProfileView: View {
|
||||
.environmentObject(user_settings)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static let markdown = Markdown()
|
||||
|
||||
var DMButton: some View {
|
||||
let dm_model = damus_state.dms.lookup_or_create(profile.pubkey)
|
||||
let dmview = DMChatView(damus_state: damus_state, pubkey: profile.pubkey)
|
||||
@@ -188,7 +190,6 @@ struct ProfileView: View {
|
||||
|
||||
DMButton
|
||||
|
||||
|
||||
if profile.pubkey != damus_state.pubkey {
|
||||
FollowButtonView(
|
||||
target: profile.get_follow_target(),
|
||||
@@ -205,7 +206,7 @@ struct ProfileView: View {
|
||||
ProfileNameView(pubkey: profile.pubkey, profile: data, contacts: damus_state.contacts)
|
||||
.padding(.bottom)
|
||||
|
||||
Text(data?.about ?? "")
|
||||
Text(ProfileView.markdown.process(data?.about ?? ""))
|
||||
.font(.subheadline)
|
||||
|
||||
Divider()
|
||||
|
||||
Reference in New Issue
Block a user