Show link cards on statuses
This commit is contained in:
parent
80b3585b71
commit
39b244384b
|
@ -8,7 +8,7 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
public class Card: Decodable {
|
||||
public class Card: Codable {
|
||||
public let url: URL
|
||||
public let title: String
|
||||
public let description: String
|
||||
|
@ -21,6 +21,7 @@ public class Card: Decodable {
|
|||
public let html: String?
|
||||
public let width: Int?
|
||||
public let height: Int?
|
||||
public let blurhash: String?
|
||||
|
||||
public required init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
@ -29,14 +30,26 @@ public class Card: Decodable {
|
|||
self.title = try container.decode(String.self, forKey: .title)
|
||||
self.description = try container.decode(String.self, forKey: .description)
|
||||
self.kind = try container.decode(Kind.self, forKey: .kind)
|
||||
self.image = try? container.decode(URL.self, forKey: .image)
|
||||
self.authorName = try? container.decode(String.self, forKey: .authorName)
|
||||
self.authorURL = try? container.decode(URL.self, forKey: .authorURL)
|
||||
self.providerName = try? container.decode(String.self, forKey: .providerName)
|
||||
self.providerURL = try? container.decode(URL.self, forKey: .providerURL)
|
||||
self.html = try? container.decode(String.self, forKey: .html)
|
||||
self.width = try? container.decode(Int.self, forKey: .width)
|
||||
self.height = try? container.decode(Int.self, forKey: .height)
|
||||
self.image = try? container.decodeIfPresent(URL.self, forKey: .image)
|
||||
self.authorName = try? container.decodeIfPresent(String.self, forKey: .authorName)
|
||||
self.authorURL = try? container.decodeIfPresent(URL.self, forKey: .authorURL)
|
||||
self.providerName = try? container.decodeIfPresent(String.self, forKey: .providerName)
|
||||
self.providerURL = try? container.decodeIfPresent(URL.self, forKey: .providerURL)
|
||||
self.html = try? container.decodeIfPresent(String.self, forKey: .html)
|
||||
self.width = try? container.decodeIfPresent(Int.self, forKey: .width)
|
||||
self.height = try? container.decodeIfPresent(Int.self, forKey: .height)
|
||||
self.blurhash = try? container.decodeIfPresent(String.self, forKey: .blurhash)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
try container.encode(url, forKey: .url)
|
||||
try container.encode(title, forKey: .title)
|
||||
try container.encode(description, forKey: .description)
|
||||
try container.encode(kind, forKey: .kind)
|
||||
try container.encode(image, forKey: .image)
|
||||
try container.encode(blurhash, forKey: .blurhash)
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
|
@ -52,14 +65,14 @@ public class Card: Decodable {
|
|||
case html
|
||||
case width
|
||||
case height
|
||||
case blurhash
|
||||
}
|
||||
}
|
||||
|
||||
extension Card {
|
||||
public enum Kind: String, Decodable {
|
||||
public enum Kind: String, Codable {
|
||||
case link
|
||||
case photo
|
||||
case video
|
||||
case rich
|
||||
}
|
||||
}
|
||||
|
|
|
@ -244,6 +244,7 @@
|
|||
D6C143DA253510F4007DC240 /* ComposeContentWarningTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C143D9253510F4007DC240 /* ComposeContentWarningTextField.swift */; };
|
||||
D6C143E025354E34007DC240 /* EmojiPickerCollectionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C143DF25354E34007DC240 /* EmojiPickerCollectionViewController.swift */; };
|
||||
D6C143FD25354FD0007DC240 /* EmojiCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C143FB25354FD0007DC240 /* EmojiCollectionViewCell.swift */; };
|
||||
D6C1B2082545D1EC00DAAA66 /* StatusCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C1B2072545D1EC00DAAA66 /* StatusCardView.swift */; };
|
||||
D6C693EF216192C2007D6A6D /* TuskerNavigationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */; };
|
||||
D6C693FC2162FE6F007D6A6D /* LoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C693FB2162FE6F007D6A6D /* LoadingViewController.swift */; };
|
||||
D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */; };
|
||||
|
@ -579,6 +580,7 @@
|
|||
D6C143D9253510F4007DC240 /* ComposeContentWarningTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeContentWarningTextField.swift; sourceTree = "<group>"; };
|
||||
D6C143DF25354E34007DC240 /* EmojiPickerCollectionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerCollectionViewController.swift; sourceTree = "<group>"; };
|
||||
D6C143FB25354FD0007DC240 /* EmojiCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
D6C1B2072545D1EC00DAAA66 /* StatusCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusCardView.swift; sourceTree = "<group>"; };
|
||||
D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TuskerNavigationDelegate.swift; sourceTree = "<group>"; };
|
||||
D6C693FB2162FE6F007D6A6D /* LoadingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingViewController.swift; sourceTree = "<group>"; };
|
||||
D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Children.swift"; sourceTree = "<group>"; };
|
||||
|
@ -1061,6 +1063,7 @@
|
|||
D6BED173212667E900F02DA0 /* TimelineStatusTableViewCell.swift */,
|
||||
D663625C2135C74800C9CBA2 /* ConversationMainStatusTableViewCell.xib */,
|
||||
D663625E2135C75500C9CBA2 /* ConversationMainStatusTableViewCell.swift */,
|
||||
D6C1B2072545D1EC00DAAA66 /* StatusCardView.swift */,
|
||||
);
|
||||
path = Status;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1945,6 +1948,7 @@
|
|||
D67895BC24671E6D00D4CD9E /* PKDrawing+Render.swift in Sources */,
|
||||
04D14BB022B34A2800642648 /* GalleryViewController.swift in Sources */,
|
||||
D641C773213CAA25004B4513 /* NotificationsTableViewController.swift in Sources */,
|
||||
D6C1B2082545D1EC00DAAA66 /* StatusCardView.swift in Sources */,
|
||||
D6412B0524B0227D00F5412E /* ProfileViewController.swift in Sources */,
|
||||
D64BC18A23C16487000D0238 /* UnpinStatusActivity.swift in Sources */,
|
||||
D64D8CA92463B494006B0BAA /* MultiThreadDictionary.swift in Sources */,
|
||||
|
|
|
@ -21,6 +21,7 @@ public final class StatusMO: NSManagedObject, StatusProtocol {
|
|||
@NSManaged public var applicationName: String?
|
||||
@NSManaged private var attachmentsData: Data?
|
||||
@NSManaged private var bookmarkedInternal: Bool
|
||||
@NSManaged public var cardData: Data?
|
||||
@NSManaged public var content: String
|
||||
@NSManaged public var createdAt: Date
|
||||
@NSManaged private var emojisData: Data?
|
||||
|
@ -56,6 +57,9 @@ public final class StatusMO: NSManagedObject, StatusProtocol {
|
|||
@LazilyDecoding(arrayFrom: \StatusMO.mentionsData)
|
||||
public var mentions: [Mention]
|
||||
|
||||
@LazilyDecoding(from: \StatusMO.cardData, fallback: nil)
|
||||
public var card: Card?
|
||||
|
||||
public var pinned: Bool? { pinnedInternal }
|
||||
public var bookmarked: Bool? { bookmarkedInternal }
|
||||
|
||||
|
@ -105,6 +109,7 @@ extension StatusMO {
|
|||
self.applicationName = status.application?.name
|
||||
self.attachments = status.attachments
|
||||
self.bookmarkedInternal = status.bookmarked ?? false
|
||||
self.card = status.card
|
||||
self.content = status.content
|
||||
self.createdAt = status.createdAt
|
||||
self.emojis = status.emojis
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17507" systemVersion="19G2021" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17510.1" systemVersion="19G2021" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="Account" representedClassName="AccountMO" syncable="YES">
|
||||
<attribute name="acct" attributeType="String"/>
|
||||
<attribute name="avatar" attributeType="URI"/>
|
||||
|
@ -44,6 +44,7 @@
|
|||
<attribute name="applicationName" optional="YES" attributeType="String"/>
|
||||
<attribute name="attachmentsData" attributeType="Binary"/>
|
||||
<attribute name="bookmarkedInternal" optional="YES" attributeType="Boolean" usesScalarValueType="YES"/>
|
||||
<attribute name="cardData" optional="YES" attributeType="Binary"/>
|
||||
<attribute name="content" attributeType="String"/>
|
||||
<attribute name="createdAt" attributeType="Date" usesScalarValueType="NO"/>
|
||||
<attribute name="emojisData" attributeType="Binary" customClassName="[Data]"/>
|
||||
|
@ -74,7 +75,7 @@
|
|||
</entity>
|
||||
<elements>
|
||||
<element name="Account" positionX="169.21875" positionY="78.9609375" width="128" height="343"/>
|
||||
<element name="Status" positionX="-63" positionY="-18" width="128" height="418"/>
|
||||
<element name="Relationship" positionX="63" positionY="135" width="128" height="208"/>
|
||||
<element name="Status" positionX="-63" positionY="-18" width="128" height="433"/>
|
||||
</elements>
|
||||
</model>
|
|
@ -32,6 +32,7 @@ class BaseStatusTableViewCell: UITableViewCell {
|
|||
@IBOutlet weak var contentWarningLabel: EmojiLabel!
|
||||
@IBOutlet weak var collapseButton: UIButton!
|
||||
@IBOutlet weak var contentTextView: StatusContentTextView!
|
||||
@IBOutlet weak var cardView: StatusCardView!
|
||||
@IBOutlet weak var attachmentsView: AttachmentsContainerView!
|
||||
@IBOutlet weak var replyButton: UIButton!
|
||||
@IBOutlet weak var favoriteButton: UIButton!
|
||||
|
@ -143,6 +144,10 @@ class BaseStatusTableViewCell: UITableViewCell {
|
|||
updateUI(account: account)
|
||||
updateUIForPreferences(account: account, status: status)
|
||||
|
||||
cardView.card = status.card
|
||||
cardView.isHidden = status.card == nil
|
||||
cardView.navigationDelegate = navigationDelegate
|
||||
|
||||
attachmentsView.updateUI(status: status)
|
||||
attachmentsView.isAccessibilityElement = status.attachments.count > 0
|
||||
attachmentsView.accessibilityLabel = String(format: NSLocalizedString("%d attachments", comment: "status attachments count accessibility label"), status.attachments.count)
|
||||
|
@ -265,6 +270,7 @@ class BaseStatusTableViewCell: UITableViewCell {
|
|||
self.collapsed = collapsed
|
||||
|
||||
contentTextView.isHidden = collapsed
|
||||
cardView.isHidden = cardView.card == nil || collapsed
|
||||
attachmentsView.isHidden = attachmentsView.attachments.count == 0 || collapsed
|
||||
|
||||
let buttonImage = UIImage(systemName: collapsed ? "chevron.down" : "chevron.up")!
|
||||
|
|
|
@ -96,6 +96,13 @@
|
|||
<fontDescription key="fontDescription" type="system" pointSize="20"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QqC-GR-TLC" customClass="StatusCardView" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="176" width="343" height="0.0"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" priority="999" constant="65" id="Tdo-Hv-ITE"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IF9-9U-Gk0" customClass="AttachmentsContainerView" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="176" width="343" height="0.0"/>
|
||||
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
|
||||
|
@ -205,6 +212,7 @@
|
|||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="QqC-GR-TLC" firstAttribute="width" secondItem="GuG-Qd-B8I" secondAttribute="width" id="2WL-jD-I09"/>
|
||||
<constraint firstItem="Cnd-Fj-B7l" firstAttribute="width" secondItem="GuG-Qd-B8I" secondAttribute="width" id="2hS-RG-81T"/>
|
||||
<constraint firstItem="z0g-HN-gS0" firstAttribute="width" secondItem="GuG-Qd-B8I" secondAttribute="width" id="4TF-2Z-mdf"/>
|
||||
<constraint firstItem="IF9-9U-Gk0" firstAttribute="width" secondItem="GuG-Qd-B8I" secondAttribute="width" id="8A8-wi-7sg"/>
|
||||
|
@ -227,6 +235,7 @@
|
|||
<connections>
|
||||
<outlet property="attachmentsView" destination="IF9-9U-Gk0" id="Oxw-sJ-MJE"/>
|
||||
<outlet property="avatarImageView" destination="mB9-HO-1vf" id="0R0-rt-Osh"/>
|
||||
<outlet property="cardView" destination="QqC-GR-TLC" id="CWR-fH-IfE"/>
|
||||
<outlet property="collapseButton" destination="8r8-O8-Agh" id="0es-Hi-bpt"/>
|
||||
<outlet property="contentTextView" destination="z0g-HN-gS0" id="atk-1f-83e"/>
|
||||
<outlet property="contentWarningLabel" destination="cwQ-mR-L1b" id="5sm-PC-FIN"/>
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
//
|
||||
// StatusCardView.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 10/25/20.
|
||||
// Copyright © 2020 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
import SafariServices
|
||||
|
||||
class StatusCardView: UIView {
|
||||
|
||||
weak var navigationDelegate: TuskerNavigationDelegate?
|
||||
|
||||
var card: Card? {
|
||||
didSet {
|
||||
if let card = card {
|
||||
self.updateUI(card: card)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private let activeBackgroundColor = UIColor.secondarySystemFill
|
||||
private let inactiveBackgroundColor = UIColor.secondarySystemBackground
|
||||
|
||||
private var imageRequest: ImageCache.Request?
|
||||
|
||||
private var titleLabel: UILabel!
|
||||
private var descriptionLabel: UILabel!
|
||||
private var imageView: UIImageView!
|
||||
private var placeholderImageView: UIImageView!
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
commonInit()
|
||||
}
|
||||
|
||||
private func commonInit() {
|
||||
self.clipsToBounds = true
|
||||
self.layer.cornerRadius = 6.5
|
||||
self.layer.borderWidth = 1
|
||||
self.layer.borderColor = UIColor.lightGray.cgColor
|
||||
self.backgroundColor = inactiveBackgroundColor
|
||||
|
||||
self.addInteraction(UIContextMenuInteraction(delegate: self))
|
||||
|
||||
titleLabel = UILabel()
|
||||
titleLabel.font = UIFont(descriptor: UIFontDescriptor.preferredFontDescriptor(withTextStyle: .subheadline).withSymbolicTraits(.traitBold)!, size: 0)
|
||||
titleLabel.numberOfLines = 2
|
||||
|
||||
descriptionLabel = UILabel()
|
||||
descriptionLabel.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .caption1), size: 0)
|
||||
descriptionLabel.numberOfLines = 2
|
||||
descriptionLabel.setContentCompressionResistancePriority(.defaultLow, for: .vertical)
|
||||
|
||||
let vStack = UIStackView(arrangedSubviews: [
|
||||
titleLabel,
|
||||
descriptionLabel
|
||||
])
|
||||
vStack.axis = .vertical
|
||||
vStack.alignment = .leading
|
||||
vStack.distribution = .fill
|
||||
vStack.spacing = 0
|
||||
|
||||
imageView = UIImageView()
|
||||
imageView.contentMode = .scaleAspectFill
|
||||
imageView.clipsToBounds = true
|
||||
|
||||
let hStack = UIStackView(arrangedSubviews: [
|
||||
imageView,
|
||||
vStack
|
||||
])
|
||||
hStack.translatesAutoresizingMaskIntoConstraints = false
|
||||
hStack.axis = .horizontal
|
||||
hStack.alignment = .center
|
||||
hStack.distribution = .fill
|
||||
hStack.spacing = 4
|
||||
|
||||
addSubview(hStack)
|
||||
|
||||
placeholderImageView = UIImageView(image: UIImage(systemName: "doc.text"))
|
||||
placeholderImageView.translatesAutoresizingMaskIntoConstraints = false
|
||||
placeholderImageView.contentMode = .scaleAspectFit
|
||||
placeholderImageView.tintColor = .gray
|
||||
|
||||
addSubview(placeholderImageView)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
imageView.heightAnchor.constraint(equalTo: heightAnchor),
|
||||
imageView.widthAnchor.constraint(equalTo: imageView.heightAnchor),
|
||||
|
||||
vStack.heightAnchor.constraint(equalTo: heightAnchor, constant: -8),
|
||||
|
||||
hStack.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
hStack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -4),
|
||||
hStack.topAnchor.constraint(equalTo: topAnchor),
|
||||
hStack.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
|
||||
placeholderImageView.widthAnchor.constraint(equalToConstant: 30),
|
||||
placeholderImageView.heightAnchor.constraint(equalToConstant: 30),
|
||||
placeholderImageView.centerXAnchor.constraint(equalTo: imageView.centerXAnchor),
|
||||
placeholderImageView.centerYAnchor.constraint(equalTo: imageView.centerYAnchor),
|
||||
])
|
||||
}
|
||||
|
||||
private func updateUI(card: Card) {
|
||||
self.imageView.image = nil
|
||||
|
||||
if let image = card.image {
|
||||
placeholderImageView.isHidden = true
|
||||
|
||||
imageRequest = ImageCache.attachments.get(image, completion: { (data) in
|
||||
guard let data = data, let image = UIImage(data: data) else { return }
|
||||
DispatchQueue.main.async {
|
||||
self.imageView.image = image
|
||||
}
|
||||
})
|
||||
if imageRequest != nil {
|
||||
loadBlurHash()
|
||||
}
|
||||
} else {
|
||||
placeholderImageView.isHidden = false
|
||||
}
|
||||
|
||||
let title = card.title.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
titleLabel.text = title
|
||||
titleLabel.isHidden = title.isEmpty
|
||||
|
||||
let description = card.description.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
descriptionLabel.text = description
|
||||
descriptionLabel.isHidden = description.isEmpty
|
||||
}
|
||||
|
||||
private func loadBlurHash() {
|
||||
guard let card = card, let hash = card.blurhash else { return }
|
||||
|
||||
let imageViewSize = self.imageView.bounds.size
|
||||
|
||||
// todo: merge this code with AttachmentView, use a single DispatchQueue
|
||||
DispatchQueue.global(qos: .default).async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
|
||||
let size: CGSize
|
||||
if let width = card.width, let height = card.height {
|
||||
size = CGSize(width: width, height: height)
|
||||
} else {
|
||||
size = imageViewSize
|
||||
}
|
||||
|
||||
guard let preview = UIImage(blurHash: hash, size: size) else {
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async { [weak self] in
|
||||
guard let self = self,
|
||||
self.card?.url == card.url,
|
||||
self.imageView.image == nil else { return }
|
||||
|
||||
self.imageView.image = preview
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
backgroundColor = activeBackgroundColor
|
||||
setNeedsDisplay()
|
||||
}
|
||||
|
||||
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
}
|
||||
|
||||
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
backgroundColor = inactiveBackgroundColor
|
||||
setNeedsDisplay()
|
||||
|
||||
if let card = card, let delegate = navigationDelegate {
|
||||
delegate.selected(url: card.url)
|
||||
}
|
||||
}
|
||||
|
||||
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
backgroundColor = inactiveBackgroundColor
|
||||
setNeedsDisplay()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension StatusCardView: MenuPreviewProvider {
|
||||
}
|
||||
|
||||
extension StatusCardView: UIContextMenuInteractionDelegate {
|
||||
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
|
||||
guard let card = card else { return nil }
|
||||
|
||||
return UIContextMenuConfiguration(identifier: nil) {
|
||||
return SFSafariViewController(url: card.url)
|
||||
} actionProvider: { (_) in
|
||||
let actions = self.actionsForURL(card.url, sourceView: self)
|
||||
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: actions)
|
||||
}
|
||||
}
|
||||
|
||||
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
|
||||
if let viewController = animator.previewViewController,
|
||||
let delegate = navigationDelegate {
|
||||
animator.preferredCommitStyle = .pop
|
||||
animator.addCompletion {
|
||||
if let customPresenting = viewController as? CustomPreviewPresenting {
|
||||
customPresenting.presentFromPreview(presenter: delegate)
|
||||
} else {
|
||||
delegate.show(viewController)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -114,6 +114,13 @@
|
|||
<fontDescription key="fontDescription" type="system" pointSize="16"/>
|
||||
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
|
||||
</textView>
|
||||
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="LKo-VB-XWl" customClass="StatusCardView" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="169.5" width="277" height="0.0"/>
|
||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" priority="999" constant="65" id="khY-jm-CPn"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view hidden="YES" contentMode="scaleToFill" verticalCompressionResistancePriority="1" translatesAutoresizingMaskIntoConstraints="NO" id="nbq-yr-2mA" customClass="AttachmentsContainerView" customModule="Tusker" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="169.5" width="277" height="0.0"/>
|
||||
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor"/>
|
||||
|
@ -238,6 +245,7 @@
|
|||
<connections>
|
||||
<outlet property="attachmentsView" destination="nbq-yr-2mA" id="SVm-zl-mPb"/>
|
||||
<outlet property="avatarImageView" destination="QMP-j2-HLn" id="xfS-v8-Gzu"/>
|
||||
<outlet property="cardView" destination="LKo-VB-XWl" id="6X5-8P-Ata"/>
|
||||
<outlet property="collapseButton" destination="O0E-Vf-XYR" id="nWd-gg-st8"/>
|
||||
<outlet property="contentTextView" destination="waJ-f5-LKv" id="hrR-Zg-gLY"/>
|
||||
<outlet property="contentWarningLabel" destination="inI-Og-YiU" id="C7a-eK-qcx"/>
|
||||
|
|
Loading…
Reference in New Issue