Update WebURL to version with IDNA support

Closes #163
This commit is contained in:
Shadowfacts 2022-07-09 11:45:27 -04:00
parent 0c37b99a68
commit 9b3cc61dcb
10 changed files with 41 additions and 39 deletions

View File

@ -16,7 +16,7 @@ let package = Package(
], ],
dependencies: [ dependencies: [
// Dependencies declare other packages that this package depends on. // Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/karwa/swift-url.git", from: "0.3.1"), .package(url: "https://github.com/karwa/swift-url.git", branch: "main"),
], ],
targets: [ targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite. // Targets are the basic building blocks of a package. A target can define a module or a test suite.

View File

@ -12,13 +12,13 @@ import WebURLFoundationExtras
public class Hashtag: Codable { public class Hashtag: Codable {
public let name: String public let name: String
public let url: URL public let url: WebURL
/// Only present when returned from the trending hashtags endpoint /// Only present when returned from the trending hashtags endpoint
public let history: [History]? public let history: [History]?
public init(name: String, url: URL) { public init(name: String, url: URL) {
self.name = name self.name = name
self.url = url self.url = WebURL(url)!
self.history = nil self.history = nil
} }
@ -26,24 +26,14 @@ public class Hashtag: Codable {
let container = try decoder.container(keyedBy: CodingKeys.self) let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name) self.name = try container.decode(String.self, forKey: .name)
// pixelfed (possibly others) don't fully escape special characters in the hashtag url // pixelfed (possibly others) don't fully escape special characters in the hashtag url
do { self.url = try container.decode(WebURL.self, forKey: .url)
let webURL = try container.decode(WebURL.self, forKey: .url)
if let url = URL(webURL) {
self.url = url
} else {
let s = try? container.decode(String.self, forKey: .url)
throw DecodingError.dataCorruptedError(forKey: .url, in: container, debugDescription: "unable to convert WebURL \(s?.debugDescription ?? "nil") to URL")
}
} catch {
self.url = try container.decode(URL.self, forKey: .url)
}
self.history = try container.decodeIfPresent([History].self, forKey: .history) self.history = try container.decodeIfPresent([History].self, forKey: .history)
} }
public func encode(to encoder: Encoder) throws { public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self) var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name) try container.encode(name, forKey: .name)
try container.encode(url.absoluteString, forKey: .url) try container.encode(url, forKey: .url)
try container.encodeIfPresent(history, forKey: .history) try container.encodeIfPresent(history, forKey: .history)
} }

View File

@ -8,10 +8,9 @@
import Foundation import Foundation
import WebURL import WebURL
import WebURLFoundationExtras
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.
@ -22,17 +21,7 @@ public class Mention: Codable {
self.username = try container.decode(String.self, forKey: .username) self.username = try container.decode(String.self, forKey: .username)
self.acct = try container.decode(String.self, forKey: .acct) self.acct = try container.decode(String.self, forKey: .acct)
self.id = try container.decode(String.self, forKey: .id) self.id = try container.decode(String.self, forKey: .id)
do { self.url = try container.decode(WebURL.self, forKey: .url)
let webURL = try container.decode(WebURL.self, forKey: .url)
if let url = URL(webURL) {
self.url = url
} else {
let s = try? container.decode(String.self, forKey: .url)
throw DecodingError.dataCorruptedError(forKey: .url, in: container, debugDescription: "unable to convert WebURL \(s?.debugDescription ?? "nil") to URL")
}
} catch {
self.url = try container.decode(URL.self, forKey: .url)
}
} }
private enum CodingKeys: String, CodingKey { private enum CodingKeys: String, CodingKey {

View File

@ -12,9 +12,14 @@ import WebURLFoundationExtras
class URLTests: XCTestCase { class URLTests: XCTestCase {
func testDecodeURL() { func testDecodeURL() {
print(WebURL(URL(string: "https://xn--baw-joa.social/@unituebingen")!)) XCTAssertNotNil(WebURL(URL(string: "https://xn--baw-joa.social/@unituebingen")!))
let url = WebURL("https://xn--baw-joa.social/@unituebingen") XCTAssertNotNil(WebURL("https://xn--baw-joa.social/@unituebingen"))
print(url) XCTAssertNotNil(URLComponents(string: "https://xn--baw-joa.social/test/é"))
XCTAssertNotNil(WebURL("https://xn--baw-joa.social/test/é"))
if #available(iOS 16.0, *) {
XCTAssertNotNil(try? URL.ParseStrategy().parse("https://xn--baw-joa.social/test/é"))
XCTAssertNotNil(try? URL.ParseStrategy().parse("http://見.香港/热狗/🌭"))
}
} }
} }

View File

@ -9,6 +9,7 @@
import Foundation import Foundation
import CoreData import CoreData
import Pachyderm import Pachyderm
import WebURLFoundationExtras
@objc(SavedHashtag) @objc(SavedHashtag)
public final class SavedHashtag: NSManagedObject { public final class SavedHashtag: NSManagedObject {
@ -32,6 +33,6 @@ extension SavedHashtag {
convenience init(hashtag: Hashtag, context: NSManagedObjectContext) { convenience init(hashtag: Hashtag, context: NSManagedObjectContext) {
self.init(context: context) self.init(context: context)
self.name = hashtag.name self.name = hashtag.name
self.url = hashtag.url self.url = URL(hashtag.url)!
} }
} }

View File

@ -10,6 +10,7 @@ import UIKit
import Combine import Combine
import Pachyderm import Pachyderm
import CoreData import CoreData
import WebURLFoundationExtras
class ExploreViewController: UIViewController, UICollectionViewDelegate { class ExploreViewController: UIViewController, UICollectionViewDelegate {
@ -582,7 +583,10 @@ extension ExploreViewController: UICollectionViewDragDelegate {
activity.displaysAuxiliaryScene = true activity.displaysAuxiliaryScene = true
provider = NSItemProvider(object: activity) provider = NSItemProvider(object: activity)
case let .savedHashtag(hashtag): case let .savedHashtag(hashtag):
provider = NSItemProvider(object: hashtag.url as NSURL) guard let url = URL(hashtag.url) else {
return []
}
provider = NSItemProvider(object: url as NSURL)
if let activity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtag.name), accountID: accountID) { if let activity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtag.name), accountID: accountID) {
activity.displaysAuxiliaryScene = true activity.displaysAuxiliaryScene = true
provider.registerObject(activity, visibility: .all) provider.registerObject(activity, visibility: .all)

View File

@ -8,6 +8,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
import WebURLFoundationExtras
class TrendingHashtagsViewController: UIViewController { class TrendingHashtagsViewController: UIViewController {
@ -105,10 +106,11 @@ extension TrendingHashtagsViewController: UICollectionViewDelegate {
extension TrendingHashtagsViewController: UICollectionViewDragDelegate { extension TrendingHashtagsViewController: UICollectionViewDragDelegate {
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] { func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
guard let item = dataSource.itemIdentifier(for: indexPath), guard let item = dataSource.itemIdentifier(for: indexPath),
case let .tag(hashtag) = item else { case let .tag(hashtag) = item,
let url = URL(hashtag.url) else {
return [] return []
} }
let provider = NSItemProvider(object: hashtag.url as NSURL) let provider = NSItemProvider(object: url as NSURL)
if let activity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtag.name), accountID: mastodonController.accountInfo!.id) { if let activity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtag.name), accountID: mastodonController.accountInfo!.id) {
activity.displaysAuxiliaryScene = true activity.displaysAuxiliaryScene = true
provider.registerObject(activity, visibility: .all) provider.registerObject(activity, visibility: .all)

View File

@ -9,6 +9,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
import SafariServices import SafariServices
import WebURLFoundationExtras
class SearchViewController: UIViewController { class SearchViewController: UIViewController {
@ -295,7 +296,10 @@ extension SearchViewController: UICollectionViewDragDelegate {
} }
switch item { switch item {
case let .tag(hashtag): case let .tag(hashtag):
let provider = NSItemProvider(object: hashtag.url as NSURL) guard let url = URL(hashtag.url) else {
return []
}
let provider = NSItemProvider(object: url as NSURL)
if let activity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtag.name), accountID: mastodonController.accountInfo!.id) { if let activity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtag.name), accountID: mastodonController.accountInfo!.id) {
activity.displaysAuxiliaryScene = true activity.displaysAuxiliaryScene = true
provider.registerObject(activity, visibility: .all) provider.registerObject(activity, visibility: .all)

View File

@ -9,6 +9,7 @@
import UIKit import UIKit
import SafariServices import SafariServices
import Pachyderm import Pachyderm
import WebURLFoundationExtras
protocol MenuActionProvider: AnyObject { protocol MenuActionProvider: AnyObject {
var navigationDelegate: TuskerNavigationDelegate? { get } var navigationDelegate: TuskerNavigationDelegate? { get }
@ -116,7 +117,12 @@ extension MenuActionProvider {
actionsSection = [] actionsSection = []
} }
let shareSection = actionsForURL(hashtag.url, sourceView: sourceView) let shareSection: [UIMenuElement]
if let url = URL(hashtag.url) {
shareSection = actionsForURL(url, sourceView: sourceView)
} else {
shareSection = []
}
return [ return [
UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: shareSection), UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: shareSection),

View File

@ -8,6 +8,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
import WebURLFoundationExtras
class StatusContentTextView: ContentTextView { class StatusContentTextView: ContentTextView {
@ -27,7 +28,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
@ -41,7 +42,7 @@ class StatusContentTextView: ContentTextView {
let mastodonController = mastodonController, let mastodonController = mastodonController,
let status = mastodonController.persistentContainer.status(for: statusID) { let status = mastodonController.persistentContainer.status(for: statusID) {
hashtag = status.hashtags.first { (hashtag) in hashtag = status.hashtags.first { (hashtag) in
hashtag.url == url URL(hashtag.url) == url
} }
} else { } else {
hashtag = nil hashtag = nil