Compare commits
No commits in common. "9b3cc61dcbcedd83d0c9debb1457b1adecac4048" and "5b70c713b2829a39332c7e33f01f55566e6f32fb" have entirely different histories.
9b3cc61dcb
...
5b70c713b2
|
@ -16,7 +16,7 @@ let package = Package(
|
|||
],
|
||||
dependencies: [
|
||||
// Dependencies declare other packages that this package depends on.
|
||||
.package(url: "https://github.com/karwa/swift-url.git", branch: "main"),
|
||||
.package(url: "https://github.com/karwa/swift-url.git", from: "0.3.1"),
|
||||
],
|
||||
targets: [
|
||||
// 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 let name: String
|
||||
public let url: WebURL
|
||||
public let url: URL
|
||||
/// Only present when returned from the trending hashtags endpoint
|
||||
public let history: [History]?
|
||||
|
||||
public init(name: String, url: URL) {
|
||||
self.name = name
|
||||
self.url = WebURL(url)!
|
||||
self.url = url
|
||||
self.history = nil
|
||||
}
|
||||
|
||||
|
@ -26,14 +26,24 @@ public class Hashtag: Codable {
|
|||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.name = try container.decode(String.self, forKey: .name)
|
||||
// pixelfed (possibly others) don't fully escape special characters in the hashtag url
|
||||
self.url = try container.decode(WebURL.self, forKey: .url)
|
||||
do {
|
||||
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)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(name, forKey: .name)
|
||||
try container.encode(url, forKey: .url)
|
||||
try container.encode(url.absoluteString, forKey: .url)
|
||||
try container.encodeIfPresent(history, forKey: .history)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,10 @@
|
|||
|
||||
import Foundation
|
||||
import WebURL
|
||||
import WebURLFoundationExtras
|
||||
|
||||
public class Mention: Codable {
|
||||
public let url: WebURL
|
||||
public let url: URL
|
||||
public let username: String
|
||||
public let acct: String
|
||||
/// The instance-local ID of the user being mentioned.
|
||||
|
@ -21,7 +22,17 @@ public class Mention: Codable {
|
|||
self.username = try container.decode(String.self, forKey: .username)
|
||||
self.acct = try container.decode(String.self, forKey: .acct)
|
||||
self.id = try container.decode(String.self, forKey: .id)
|
||||
self.url = try container.decode(WebURL.self, forKey: .url)
|
||||
do {
|
||||
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 {
|
||||
|
|
|
@ -12,14 +12,9 @@ import WebURLFoundationExtras
|
|||
class URLTests: XCTestCase {
|
||||
|
||||
func testDecodeURL() {
|
||||
XCTAssertNotNil(WebURL(URL(string: "https://xn--baw-joa.social/@unituebingen")!))
|
||||
XCTAssertNotNil(WebURL("https://xn--baw-joa.social/@unituebingen"))
|
||||
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://見.香港/热狗/🌭"))
|
||||
}
|
||||
print(WebURL(URL(string: "https://xn--baw-joa.social/@unituebingen")!))
|
||||
let url = WebURL("https://xn--baw-joa.social/@unituebingen")
|
||||
print(url)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
import Foundation
|
||||
import CoreData
|
||||
import Pachyderm
|
||||
import WebURLFoundationExtras
|
||||
|
||||
@objc(SavedHashtag)
|
||||
public final class SavedHashtag: NSManagedObject {
|
||||
|
@ -33,6 +32,6 @@ extension SavedHashtag {
|
|||
convenience init(hashtag: Hashtag, context: NSManagedObjectContext) {
|
||||
self.init(context: context)
|
||||
self.name = hashtag.name
|
||||
self.url = URL(hashtag.url)!
|
||||
self.url = hashtag.url
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,9 +70,7 @@ struct ComposeAttachmentsList: View {
|
|||
.listRowInsets(EdgeInsets(top: cellPadding / 2, leading: cellPadding / 2, bottom: cellPadding / 2, trailing: cellPadding / 2))
|
||||
}
|
||||
.listStyle(PlainListStyle())
|
||||
// todo: scrollDisabled doesn't remove the need for manually calculating the frame height
|
||||
.frame(height: totalListHeight)
|
||||
.scrollDisabledIfAvailable(totalHeight: totalListHeight)
|
||||
.onAppear(perform: self.didAppear)
|
||||
.onReceive(draft.$attachments, perform: self.attachmentsChanged)
|
||||
}
|
||||
|
@ -214,16 +212,6 @@ fileprivate extension View {
|
|||
self
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS, obsoleted: 16.0)
|
||||
@ViewBuilder
|
||||
func scrollDisabledIfAvailable(totalHeight: CGFloat) -> some View {
|
||||
if #available(iOS 16.0, *) {
|
||||
self.scrollDisabled(true)
|
||||
} else {
|
||||
self.frame(height: totalHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 16.0, *)
|
||||
|
|
|
@ -10,7 +10,6 @@ import UIKit
|
|||
import Combine
|
||||
import Pachyderm
|
||||
import CoreData
|
||||
import WebURLFoundationExtras
|
||||
|
||||
class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
||||
|
||||
|
@ -583,10 +582,7 @@ extension ExploreViewController: UICollectionViewDragDelegate {
|
|||
activity.displaysAuxiliaryScene = true
|
||||
provider = NSItemProvider(object: activity)
|
||||
case let .savedHashtag(hashtag):
|
||||
guard let url = URL(hashtag.url) else {
|
||||
return []
|
||||
}
|
||||
provider = NSItemProvider(object: url as NSURL)
|
||||
provider = NSItemProvider(object: hashtag.url as NSURL)
|
||||
if let activity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtag.name), accountID: accountID) {
|
||||
activity.displaysAuxiliaryScene = true
|
||||
provider.registerObject(activity, visibility: .all)
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
import WebURLFoundationExtras
|
||||
|
||||
class TrendingHashtagsViewController: UIViewController {
|
||||
|
||||
|
@ -106,11 +105,10 @@ extension TrendingHashtagsViewController: UICollectionViewDelegate {
|
|||
extension TrendingHashtagsViewController: UICollectionViewDragDelegate {
|
||||
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
|
||||
guard let item = dataSource.itemIdentifier(for: indexPath),
|
||||
case let .tag(hashtag) = item,
|
||||
let url = URL(hashtag.url) else {
|
||||
case let .tag(hashtag) = item else {
|
||||
return []
|
||||
}
|
||||
let provider = NSItemProvider(object: url as NSURL)
|
||||
let provider = NSItemProvider(object: hashtag.url as NSURL)
|
||||
if let activity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtag.name), accountID: mastodonController.accountInfo!.id) {
|
||||
activity.displaysAuxiliaryScene = true
|
||||
provider.registerObject(activity, visibility: .all)
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
import UIKit
|
||||
import Pachyderm
|
||||
import SafariServices
|
||||
import WebURLFoundationExtras
|
||||
|
||||
class SearchViewController: UIViewController {
|
||||
|
||||
|
@ -296,10 +295,7 @@ extension SearchViewController: UICollectionViewDragDelegate {
|
|||
}
|
||||
switch item {
|
||||
case let .tag(hashtag):
|
||||
guard let url = URL(hashtag.url) else {
|
||||
return []
|
||||
}
|
||||
let provider = NSItemProvider(object: url as NSURL)
|
||||
let provider = NSItemProvider(object: hashtag.url as NSURL)
|
||||
if let activity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtag.name), accountID: mastodonController.accountInfo!.id) {
|
||||
activity.displaysAuxiliaryScene = true
|
||||
provider.registerObject(activity, visibility: .all)
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
import UIKit
|
||||
import SafariServices
|
||||
import Pachyderm
|
||||
import WebURLFoundationExtras
|
||||
|
||||
protocol MenuActionProvider: AnyObject {
|
||||
var navigationDelegate: TuskerNavigationDelegate? { get }
|
||||
|
@ -117,12 +116,7 @@ extension MenuActionProvider {
|
|||
actionsSection = []
|
||||
}
|
||||
|
||||
let shareSection: [UIMenuElement]
|
||||
if let url = URL(hashtag.url) {
|
||||
shareSection = actionsForURL(url, sourceView: sourceView)
|
||||
} else {
|
||||
shareSection = []
|
||||
}
|
||||
let shareSection = actionsForURL(hashtag.url, sourceView: sourceView)
|
||||
|
||||
return [
|
||||
UIMenu(title: "", image: nil, identifier: nil, options: [.displayInline], children: shareSection),
|
||||
|
|
|
@ -270,9 +270,8 @@ class ContentTextView: LinkTextView, BaseEmojiLabel {
|
|||
|
||||
extension ContentTextView: UITextViewDelegate {
|
||||
func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
|
||||
// generally disable the text view's link interactions, we handle tapping links ourself with a gesture recognizer
|
||||
// the builtin data detectors use the x-apple-data-detectors scheme, and we allow the text view to handle those itself
|
||||
return URL.scheme == "x-apple-data-detectors"
|
||||
// disable the text view's link interactions, we handle tapping links ourself with a gesture recognizer
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -50,10 +50,6 @@ class ConversationMainStatusTableViewCell: BaseStatusTableViewCell {
|
|||
]
|
||||
|
||||
contentTextView.defaultFont = .systemFont(ofSize: 18)
|
||||
contentTextView.dataDetectorTypes = [.flightNumber, .address, .shipmentTrackingNumber, .phoneNumber]
|
||||
if #available(iOS 16.0, *) {
|
||||
contentTextView.dataDetectorTypes.formUnion([.money, .physicalValue])
|
||||
}
|
||||
|
||||
profileDetailContainerView.addInteraction(UIContextMenuInteraction(delegate: self))
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="21179.7" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21169.4"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
|
@ -108,7 +108,7 @@
|
|||
<action selector="collapseButtonPressed" destination="iN0-l3-epB" eventType="touchUpInside" id="JaH-xX-UOD"/>
|
||||
</connections>
|
||||
</button>
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" delaysContentTouches="NO" editable="NO" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="waJ-f5-LKv" customClass="StatusContentTextView" customModule="Tusker" customModuleProvider="target">
|
||||
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" delaysContentTouches="NO" editable="NO" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="waJ-f5-LKv" customClass="StatusContentTextView" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="83" width="277" height="82.5"/>
|
||||
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
|
||||
<color key="textColor" systemColor="labelColor"/>
|
||||
|
@ -272,14 +272,14 @@
|
|||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="104"/>
|
||||
<image name="chevron.down" catalog="system" width="128" height="70"/>
|
||||
<image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="106"/>
|
||||
<image name="chevron.down" catalog="system" width="128" height="72"/>
|
||||
<image name="ellipsis" catalog="system" width="128" height="37"/>
|
||||
<image name="pin.fill" catalog="system" width="116" height="128"/>
|
||||
<image name="pin.fill" catalog="system" width="119" height="128"/>
|
||||
<image name="repeat" catalog="system" width="128" height="98"/>
|
||||
<image name="star.fill" catalog="system" width="128" height="116"/>
|
||||
<systemColor name="labelColor">
|
||||
<color red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</systemColor>
|
||||
<systemColor name="secondaryLabelColor">
|
||||
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
import WebURLFoundationExtras
|
||||
|
||||
class StatusContentTextView: ContentTextView {
|
||||
|
||||
|
@ -28,7 +27,7 @@ class StatusContentTextView: ContentTextView {
|
|||
let status = mastodonController.persistentContainer.status(for: statusID) {
|
||||
mention = status.mentions.first { (mention) in
|
||||
// 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!.serialized
|
||||
(text.dropFirst() == mention.username || text == mention.username) && url.host == mention.url.host!
|
||||
}
|
||||
} else {
|
||||
mention = nil
|
||||
|
@ -42,7 +41,7 @@ class StatusContentTextView: ContentTextView {
|
|||
let mastodonController = mastodonController,
|
||||
let status = mastodonController.persistentContainer.status(for: statusID) {
|
||||
hashtag = status.hashtags.first { (hashtag) in
|
||||
URL(hashtag.url) == url
|
||||
hashtag.url == url
|
||||
}
|
||||
} else {
|
||||
hashtag = nil
|
||||
|
|
Loading…
Reference in New Issue