Use WebURL for more lenient parsing of external URLs

Fixes #136
This commit is contained in:
Shadowfacts 2022-02-03 23:11:29 -05:00
parent 6e964ff601
commit 54c01be7ff
13 changed files with 90 additions and 41 deletions

View File

@ -14,7 +14,6 @@ public class Attachment: Codable {
public let url: URL public let url: URL
public let remoteURL: URL? public let remoteURL: URL?
public let previewURL: URL? public let previewURL: URL?
public let textURL: URL?
public let meta: Metadata? public let meta: Metadata?
public let description: String? public let description: String?
public let blurHash: String? public let blurHash: String?
@ -33,7 +32,6 @@ public class Attachment: Codable {
self.url = try container.decode(URL.self, forKey: .url) self.url = try container.decode(URL.self, forKey: .url)
self.previewURL = try? container.decode(URL?.self, forKey: .previewURL) self.previewURL = try? container.decode(URL?.self, forKey: .previewURL)
self.remoteURL = try? container.decode(URL?.self, forKey: .remoteURL) self.remoteURL = try? container.decode(URL?.self, forKey: .remoteURL)
self.textURL = try? container.decode(URL?.self, forKey: .textURL)
self.meta = try? container.decode(Metadata?.self, forKey: .meta) self.meta = try? container.decode(Metadata?.self, forKey: .meta)
self.description = try? container.decode(String?.self, forKey: .description) self.description = try? container.decode(String?.self, forKey: .description)
self.blurHash = try? container.decode(String?.self, forKey: .blurHash) self.blurHash = try? container.decode(String?.self, forKey: .blurHash)
@ -45,7 +43,6 @@ public class Attachment: Codable {
case url case url
case remoteURL = "remote_url" case remoteURL = "remote_url"
case previewURL = "preview_url" case previewURL = "preview_url"
case textURL = "text_url"
case meta case meta
case description case description
case blurHash = "blurhash" case blurHash = "blurhash"

View File

@ -7,17 +7,18 @@
// //
import Foundation import Foundation
import WebURL
public class Card: Codable { public class Card: Codable {
public let url: URL public let url: WebURL
public let title: String public let title: String
public let description: String public let description: String
public let image: URL? public let image: WebURL?
public let kind: Kind public let kind: Kind
public let authorName: String? public let authorName: String?
public let authorURL: URL? public let authorURL: WebURL?
public let providerName: String? public let providerName: String?
public let providerURL: URL? public let providerURL: WebURL?
public let html: String? public let html: String?
public let width: Int? public let width: Int?
public let height: Int? public let height: Int?
@ -26,15 +27,15 @@ public class Card: Codable {
public required init(from decoder: Decoder) throws { public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
self.url = try container.decode(URL.self, forKey: .url) self.url = try container.decode(WebURL.self, forKey: .url)
self.title = try container.decode(String.self, forKey: .title) self.title = try container.decode(String.self, forKey: .title)
self.description = try container.decode(String.self, forKey: .description) self.description = try container.decode(String.self, forKey: .description)
self.kind = try container.decode(Kind.self, forKey: .kind) self.kind = try container.decode(Kind.self, forKey: .kind)
self.image = try? container.decodeIfPresent(URL.self, forKey: .image) self.image = try? container.decodeIfPresent(WebURL.self, forKey: .image)
self.authorName = try? container.decodeIfPresent(String.self, forKey: .authorName) self.authorName = try? container.decodeIfPresent(String.self, forKey: .authorName)
self.authorURL = try? container.decodeIfPresent(URL.self, forKey: .authorURL) self.authorURL = try? container.decodeIfPresent(WebURL.self, forKey: .authorURL)
self.providerName = try? container.decodeIfPresent(String.self, forKey: .providerName) self.providerName = try? container.decodeIfPresent(String.self, forKey: .providerName)
self.providerURL = try? container.decodeIfPresent(URL.self, forKey: .providerURL) self.providerURL = try? container.decodeIfPresent(WebURL.self, forKey: .providerURL)
self.html = try? container.decodeIfPresent(String.self, forKey: .html) self.html = try? container.decodeIfPresent(String.self, forKey: .html)
self.width = try? container.decodeIfPresent(Int.self, forKey: .width) self.width = try? container.decodeIfPresent(Int.self, forKey: .width)
self.height = try? container.decodeIfPresent(Int.self, forKey: .height) self.height = try? container.decodeIfPresent(Int.self, forKey: .height)

View File

@ -7,29 +7,22 @@
// //
import Foundation import Foundation
import WebURL
public class Emoji: Codable { public class Emoji: Codable {
public let shortcode: String public let shortcode: String
public let url: URL // these shouldn't need to be WebURLs as they're not external resources,
public let staticURL: URL // but some instances (pleroma?) has emoji urls that Foundation considers malformed so we use WebURL to be more lenient
public let url: WebURL
public let staticURL: WebURL
public let visibleInPicker: Bool public let visibleInPicker: Bool
public required init(from decoder: Decoder) throws { public required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
self.shortcode = try container.decode(String.self, forKey: .shortcode) self.shortcode = try container.decode(String.self, forKey: .shortcode)
if let url = try? container.decode(URL.self, forKey: .url) { self.url = try container.decode(WebURL.self, forKey: .url)
self.url = url self.staticURL = try container.decode(WebURL.self, forKey: .staticURL)
} else {
let str = try container.decode(String.self, forKey: .url)
self.url = URL(string: str.replacingOccurrences(of: " ", with: "%20"))!
}
if let url = try? container.decode(URL.self, forKey: .staticURL) {
self.staticURL = url
} else {
let staticStr = try container.decode(String.self, forKey: .staticURL)
self.staticURL = URL(string: staticStr.replacingOccurrences(of: " ", with: "%20"))!
}
self.visibleInPicker = try container.decode(Bool.self, forKey: .visibleInPicker) self.visibleInPicker = try container.decode(Bool.self, forKey: .visibleInPicker)
} }

View File

@ -7,9 +7,10 @@
// //
import Foundation import Foundation
import WebURL
public class Mention: Codable { public class Mention: Codable {
public let url: URL public let url: WebURL
public let username: String public let username: String
public let acct: String public let acct: String
/// The instance-local ID of the user being mentioned. /// The instance-local ID of the user being mentioned.

View File

@ -38,8 +38,7 @@ public final class Status: /*StatusProtocol,*/ Decodable {
public let bookmarked: Bool? public let bookmarked: Bool?
public let card: Card? public let card: Card?
public let poll: Poll? public let poll: Poll?
// Hometown only // Hometown, Glitch only
// TODO: glitch too?
public let localOnly: Bool? public let localOnly: Bool?
public var applicationName: String? { application?.name } public var applicationName: String? { application?.name }

View File

@ -126,6 +126,7 @@
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Helpers.swift */; }; D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Helpers.swift */; };
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */; }; D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */; };
D63569E023908A8D003DD353 /* StatusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60A4FFB238B726A008AC647 /* StatusState.swift */; }; D63569E023908A8D003DD353 /* StatusState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D60A4FFB238B726A008AC647 /* StatusState.swift */; };
D635ED5027ACD9260003635B /* WebURL in Frameworks */ = {isa = PBXBuildFile; productRef = D635ED4F27ACD9260003635B /* WebURL */; };
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */; }; D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */; };
D6370B9C24421FF30092A7FF /* Tusker.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = D6370B9A24421FF30092A7FF /* Tusker.xcdatamodeld */; }; D6370B9C24421FF30092A7FF /* Tusker.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = D6370B9A24421FF30092A7FF /* Tusker.xcdatamodeld */; };
D63A8D0B2561C27F00D9DFFF /* ProfileStatusesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63A8D0A2561C27F00D9DFFF /* ProfileStatusesViewController.swift */; }; D63A8D0B2561C27F00D9DFFF /* ProfileStatusesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63A8D0A2561C27F00D9DFFF /* ProfileStatusesViewController.swift */; };
@ -177,6 +178,8 @@
D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663626B21361C6700C9CBA2 /* Account+Preferences.swift */; }; D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663626B21361C6700C9CBA2 /* Account+Preferences.swift */; };
D66362752137068A00C9CBA2 /* Visibility+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D66362742137068A00C9CBA2 /* Visibility+Helpers.swift */; }; D66362752137068A00C9CBA2 /* Visibility+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D66362742137068A00C9CBA2 /* Visibility+Helpers.swift */; };
D6674AEA23341F7600E8DF94 /* AppShortcutItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6674AE923341F7600E8DF94 /* AppShortcutItems.swift */; }; D6674AEA23341F7600E8DF94 /* AppShortcutItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6674AE923341F7600E8DF94 /* AppShortcutItems.swift */; };
D6676CA327A8D0020052936B /* WebURL in Frameworks */ = {isa = PBXBuildFile; productRef = D6676CA227A8D0020052936B /* WebURL */; };
D6676CA527A8D0020052936B /* WebURLFoundationExtras in Frameworks */ = {isa = PBXBuildFile; productRef = D6676CA427A8D0020052936B /* WebURLFoundationExtras */; };
D667E5E12134937B0057A976 /* TimelineStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D667E5E02134937B0057A976 /* TimelineStatusTableViewCell.xib */; }; D667E5E12134937B0057A976 /* TimelineStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D667E5E02134937B0057A976 /* TimelineStatusTableViewCell.xib */; };
D667E5F12134D5050057A976 /* UIViewController+Delegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */; }; D667E5F12134D5050057A976 /* UIViewController+Delegates.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F02134D5050057A976 /* UIViewController+Delegates.swift */; };
D667E5F52135BCD50057A976 /* ConversationTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F42135BCD50057A976 /* ConversationTableViewController.swift */; }; D667E5F52135BCD50057A976 /* ConversationTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F42135BCD50057A976 /* ConversationTableViewController.swift */; };
@ -766,6 +769,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D635ED5027ACD9260003635B /* WebURL in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -781,10 +785,12 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
D6676CA327A8D0020052936B /* WebURL in Frameworks */,
D61099C02144B0CC00432DC2 /* Pachyderm.framework in Frameworks */, D61099C02144B0CC00432DC2 /* Pachyderm.framework in Frameworks */,
D6B0539F23BD2BA300A066FA /* SheetController in Frameworks */, D6B0539F23BD2BA300A066FA /* SheetController in Frameworks */,
D69CCBBF249E6EFD000AF167 /* CrashReporter in Frameworks */, D69CCBBF249E6EFD000AF167 /* CrashReporter in Frameworks */,
D60CFFDB24A290BA00D00083 /* SwiftSoup in Frameworks */, D60CFFDB24A290BA00D00083 /* SwiftSoup in Frameworks */,
D6676CA527A8D0020052936B /* WebURLFoundationExtras in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -1720,6 +1726,9 @@
dependencies = ( dependencies = (
); );
name = Pachyderm; name = Pachyderm;
packageProductDependencies = (
D635ED4F27ACD9260003635B /* WebURL */,
);
productName = Pachyderm; productName = Pachyderm;
productReference = D61099AB2144B0CC00432DC2 /* Pachyderm.framework */; productReference = D61099AB2144B0CC00432DC2 /* Pachyderm.framework */;
productType = "com.apple.product-type.framework"; productType = "com.apple.product-type.framework";
@ -1765,6 +1774,8 @@
D6B0539E23BD2BA300A066FA /* SheetController */, D6B0539E23BD2BA300A066FA /* SheetController */,
D69CCBBE249E6EFD000AF167 /* CrashReporter */, D69CCBBE249E6EFD000AF167 /* CrashReporter */,
D60CFFDA24A290BA00D00083 /* SwiftSoup */, D60CFFDA24A290BA00D00083 /* SwiftSoup */,
D6676CA227A8D0020052936B /* WebURL */,
D6676CA427A8D0020052936B /* WebURLFoundationExtras */,
); );
productName = Tusker; productName = Tusker;
productReference = D6D4DDCC212518A000E1C4BB /* Tusker.app */; productReference = D6D4DDCC212518A000E1C4BB /* Tusker.app */;
@ -1882,6 +1893,7 @@
D6B0539D23BD2BA300A066FA /* XCRemoteSwiftPackageReference "SheetController" */, D6B0539D23BD2BA300A066FA /* XCRemoteSwiftPackageReference "SheetController" */,
D69CCBBD249E6EFD000AF167 /* XCRemoteSwiftPackageReference "plcrashreporter" */, D69CCBBD249E6EFD000AF167 /* XCRemoteSwiftPackageReference "plcrashreporter" */,
D60CFFD924A290BA00D00083 /* XCRemoteSwiftPackageReference "SwiftSoup" */, D60CFFD924A290BA00D00083 /* XCRemoteSwiftPackageReference "SwiftSoup" */,
D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */,
); );
productRefGroup = D6D4DDCD212518A000E1C4BB /* Products */; productRefGroup = D6D4DDCD212518A000E1C4BB /* Products */;
projectDirPath = ""; projectDirPath = "";
@ -2892,6 +2904,14 @@
minimumVersion = 2.3.2; minimumVersion = 2.3.2;
}; };
}; };
D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/karwa/swift-url";
requirement = {
branch = main;
kind = branch;
};
};
D69CCBBD249E6EFD000AF167 /* XCRemoteSwiftPackageReference "plcrashreporter" */ = { D69CCBBD249E6EFD000AF167 /* XCRemoteSwiftPackageReference "plcrashreporter" */ = {
isa = XCRemoteSwiftPackageReference; isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/microsoft/plcrashreporter"; repositoryURL = "https://github.com/microsoft/plcrashreporter";
@ -2916,6 +2936,21 @@
package = D60CFFD924A290BA00D00083 /* XCRemoteSwiftPackageReference "SwiftSoup" */; package = D60CFFD924A290BA00D00083 /* XCRemoteSwiftPackageReference "SwiftSoup" */;
productName = SwiftSoup; productName = SwiftSoup;
}; };
D635ED4F27ACD9260003635B /* WebURL */ = {
isa = XCSwiftPackageProductDependency;
package = D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */;
productName = WebURL;
};
D6676CA227A8D0020052936B /* WebURL */ = {
isa = XCSwiftPackageProductDependency;
package = D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */;
productName = WebURL;
};
D6676CA427A8D0020052936B /* WebURLFoundationExtras */ = {
isa = XCSwiftPackageProductDependency;
package = D6676CA127A8D0020052936B /* XCRemoteSwiftPackageReference "swift-url" */;
productName = WebURLFoundationExtras;
};
D69CCBBE249E6EFD000AF167 /* CrashReporter */ = { D69CCBBE249E6EFD000AF167 /* CrashReporter */ = {
isa = XCSwiftPackageProductDependency; isa = XCSwiftPackageProductDependency;
package = D69CCBBD249E6EFD000AF167 /* XCRemoteSwiftPackageReference "plcrashreporter" */; package = D69CCBBD249E6EFD000AF167 /* XCRemoteSwiftPackageReference "plcrashreporter" */;

View File

@ -2,7 +2,7 @@
"object": { "object": {
"pins": [ "pins": [
{ {
"package": "plcrashreporter", "package": "PLCrashReporter",
"repositoryURL": "https://github.com/microsoft/plcrashreporter", "repositoryURL": "https://github.com/microsoft/plcrashreporter",
"state": { "state": {
"branch": null, "branch": null,
@ -19,6 +19,24 @@
"version": null "version": null
} }
}, },
{
"package": "swift-system",
"repositoryURL": "https://github.com/apple/swift-system.git",
"state": {
"branch": null,
"revision": "836bc4557b74fe6d2660218d56e3ce96aff76574",
"version": "1.1.1"
}
},
{
"package": "swift-url",
"repositoryURL": "https://github.com/karwa/swift-url",
"state": {
"branch": "main",
"revision": "1519936d9813af86e57c06dc7f727cb281de9f16",
"version": null
}
},
{ {
"package": "SwiftSoup", "package": "SwiftSoup",
"repositoryURL": "https://github.com/scinfu/SwiftSoup.git", "repositoryURL": "https://github.com/scinfu/SwiftSoup.git",

View File

@ -8,6 +8,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
import WebURLFoundationExtras
class EmojiCollectionViewCell: UICollectionViewCell { class EmojiCollectionViewCell: UICollectionViewCell {
@ -45,7 +46,7 @@ class EmojiCollectionViewCell: UICollectionViewCell {
func updateUI(emoji: Emoji) { func updateUI(emoji: Emoji) {
currentEmojiShortcode = emoji.shortcode currentEmojiShortcode = emoji.shortcode
imageRequest = ImageCache.emojis.get(emoji.url) { [weak self] (_, image) in imageRequest = ImageCache.emojis.get(URL(emoji.url)!) { [weak self] (_, image) in
guard let image = image else { return } guard let image = image else { return }
DispatchQueue.main.async { [weak self] in DispatchQueue.main.async { [weak self] in
guard let self = self, self.currentEmojiShortcode == emoji.shortcode else { return } guard let self = self, self.currentEmojiShortcode == emoji.shortcode else { return }

View File

@ -8,6 +8,7 @@
import SwiftUI import SwiftUI
import Pachyderm import Pachyderm
import WebURLFoundationExtras
private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: []) private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: [])
@ -48,7 +49,7 @@ struct AccountDisplayNameLabel<Account: AccountProtocol>: View {
} }
group.enter() group.enter()
let request = ImageCache.emojis.get(emoji.url) { (_, image) in let request = ImageCache.emojis.get(URL(emoji.url)!) { (_, image) in
defer { group.leave() } defer { group.leave() }
guard let image = image else { return } guard let image = image else { return }

View File

@ -8,6 +8,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
import WebURLFoundationExtras
private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: []) private let emojiRegex = try! NSRegularExpression(pattern: ":(\\w+):", options: [])
@ -56,7 +57,7 @@ extension BaseEmojiLabel {
foundEmojis = true foundEmojis = true
if let image = ImageCache.emojis.get(emoji.url)?.image { if let image = ImageCache.emojis.get(URL(emoji.url)!)?.image {
// if the image is cached, add it immediately // if the image is cached, add it immediately
emojiImages[emoji.shortcode] = image emojiImages[emoji.shortcode] = image
} else { } else {
@ -64,9 +65,9 @@ extension BaseEmojiLabel {
group.enter() group.enter()
// todo: ImageCache.emojis.get here will re-check the memory and disk caches, there should be another method to force-refetch // todo: ImageCache.emojis.get here will re-check the memory and disk caches, there should be another method to force-refetch
let request = ImageCache.emojis.get(emoji.url) { (_, image) in let request = ImageCache.emojis.get(URL(emoji.url)!) { (_, image) in
guard let image = image, guard let image = image,
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: emoji.url, image: image) else { let transformedImage = ImageGrayscalifier.convertIfNecessary(url: URL(emoji.url)!, image: image) else {
group.leave() group.leave()
return return
} }

View File

@ -8,6 +8,7 @@
import SwiftUI import SwiftUI
import Pachyderm import Pachyderm
import WebURLFoundationExtras
struct CustomEmojiImageView: View { struct CustomEmojiImageView: View {
let emoji: Emoji let emoji: Emoji
@ -33,7 +34,7 @@ struct CustomEmojiImageView: View {
} }
private func loadImage() { private func loadImage() {
request = ImageCache.emojis.get(emoji.url) { (_, image) in request = ImageCache.emojis.get(URL(emoji.url)!) { (_, image) in
DispatchQueue.main.async { DispatchQueue.main.async {
self.request = nil self.request = nil
if let image = image { if let image = image {

View File

@ -9,6 +9,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
import SafariServices import SafariServices
import WebURLFoundationExtras
class StatusCardView: UIView { class StatusCardView: UIView {
@ -141,9 +142,9 @@ class StatusCardView: UIView {
if let imageURL = card.image { if let imageURL = card.image {
placeholderImageView.isHidden = true placeholderImageView.isHidden = true
imageRequest = ImageCache.attachments.get(imageURL, completion: { (_, image) in imageRequest = ImageCache.attachments.get(URL(imageURL)!, completion: { (_, image) in
guard let image = image, guard let image = image,
let transformedImage = ImageGrayscalifier.convertIfNecessary(url: imageURL, image: image) else { let transformedImage = ImageGrayscalifier.convertIfNecessary(url: URL(imageURL)!, image: image) else {
return return
} }
DispatchQueue.main.async { DispatchQueue.main.async {
@ -200,7 +201,7 @@ class StatusCardView: UIView {
setNeedsDisplay() setNeedsDisplay()
if let card = card, let delegate = navigationDelegate { if let card = card, let delegate = navigationDelegate {
delegate.selected(url: card.url) delegate.selected(url: URL(card.url)!)
} }
} }
@ -219,9 +220,9 @@ extension StatusCardView: UIContextMenuInteractionDelegate {
guard let card = card else { return nil } guard let card = card else { return nil }
return UIContextMenuConfiguration(identifier: nil) { return UIContextMenuConfiguration(identifier: nil) {
return SFSafariViewController(url: card.url) return SFSafariViewController(url: URL(card.url)!)
} actionProvider: { (_) in } actionProvider: { (_) in
let actions = self.actionsForURL(card.url, sourceView: self) let actions = self.actionsForURL(URL(card.url)!, sourceView: self)
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: actions) return UIMenu(title: "", image: nil, identifier: nil, options: [], children: actions)
} }
} }

View File

@ -27,7 +27,7 @@ class StatusContentTextView: ContentTextView {
let status = mastodonController.persistentContainer.status(for: statusID) { let status = mastodonController.persistentContainer.status(for: statusID) {
mention = status.mentions.first { (mention) in mention = status.mentions.first { (mention) in
// Mastodon and Pleroma include the @ in the <a> text, GNU Social does not // Mastodon and Pleroma include the @ in the <a> text, GNU Social does not
(text.dropFirst() == mention.username || text == mention.username) && url.host == mention.url.host (text.dropFirst() == mention.username || text == mention.username) && url.host == mention.url.host!.serialized
} }
} else { } else {
mention = nil mention = nil