parent
0c37b99a68
commit
9b3cc61dcb
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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://見.香港/热狗/🌭"))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)!
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue