forked from shadowfacts/Tusker
Status cell interaction
This commit is contained in:
parent
780e8b09b7
commit
24e90de672
|
@ -149,6 +149,10 @@ class TimelineViewController: UIViewController, TimelineLikeCollectionViewContro
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
super.viewWillAppear(animated)
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
collectionView.indexPathsForSelectedItems?.forEach {
|
||||||
|
collectionView.deselectItem(at: $0, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
await controller.loadInitial()
|
await controller.loadInitial()
|
||||||
}
|
}
|
||||||
|
@ -220,6 +224,15 @@ extension TimelineViewController {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isSelectable: Bool {
|
||||||
|
switch self {
|
||||||
|
case .publicTimelineDescription, .status(id: _, state: _):
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,6 +323,10 @@ extension TimelineViewController: UICollectionViewDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
|
||||||
|
return dataSource.itemIdentifier(for: indexPath)?.isSelectable ?? false
|
||||||
|
}
|
||||||
|
|
||||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||||
guard let item = dataSource.itemIdentifier(for: indexPath) else {
|
guard let item = dataSource.itemIdentifier(for: indexPath) else {
|
||||||
return
|
return
|
||||||
|
@ -317,10 +334,10 @@ extension TimelineViewController: UICollectionViewDelegate {
|
||||||
switch item {
|
switch item {
|
||||||
case .publicTimelineDescription:
|
case .publicTimelineDescription:
|
||||||
removeTimelineDescriptionCell()
|
removeTimelineDescriptionCell()
|
||||||
|
case .status(id: let id, state: let state):
|
||||||
default:
|
selected(status: id, state: state.copy())
|
||||||
// TODO: cell selection
|
case .loadingIndicator, .confirmLoadMore:
|
||||||
break
|
fatalError("unreachable")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,11 +356,11 @@ extension TimelineViewController: MenuActionProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TimelineViewController: TimelineStatusCollectionViewCellDelegate {
|
extension TimelineViewController: TimelineStatusCollectionViewCellDelegate {
|
||||||
func statusCellCollapsedStateChanged(_ cell: TimelineStatusCollectionViewCell) {
|
func statusCellNeedsReconfigure(_ cell: TimelineStatusCollectionViewCell, animated: Bool) {
|
||||||
if let indexPath = collectionView.indexPath(for: cell) {
|
if let indexPath = collectionView.indexPath(for: cell) {
|
||||||
var snapshot = dataSource.snapshot()
|
var snapshot = dataSource.snapshot()
|
||||||
snapshot.reconfigureItems([dataSource.itemIdentifier(for: indexPath)!])
|
snapshot.reconfigureItems([dataSource.itemIdentifier(for: indexPath)!])
|
||||||
dataSource.apply(snapshot, animatingDifferences: true)
|
dataSource.apply(snapshot, animatingDifferences: animated)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,19 +11,18 @@ import Pachyderm
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
protocol TimelineStatusCollectionViewCellDelegate: AnyObject, TuskerNavigationDelegate, MenuActionProvider {
|
protocol TimelineStatusCollectionViewCellDelegate: AnyObject, TuskerNavigationDelegate, MenuActionProvider {
|
||||||
func statusCellCollapsedStateChanged(_ cell: TimelineStatusCollectionViewCell)
|
func statusCellNeedsReconfigure(_ cell: TimelineStatusCollectionViewCell, animated: Bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
class TimelineStatusCollectionViewCell: UICollectionViewListCell {
|
class TimelineStatusCollectionViewCell: UICollectionViewListCell {
|
||||||
|
|
||||||
// MARK: Subviews
|
// MARK: Subviews
|
||||||
|
|
||||||
private let reblogLabel = EmojiLabel().configure {
|
private lazy var reblogLabel = EmojiLabel().configure {
|
||||||
$0.textColor = .secondaryLabel
|
$0.textColor = .secondaryLabel
|
||||||
// this needs to have a higher priorty than the content container's zero height constraint
|
// this needs to have a higher priorty than the content container's zero height constraint
|
||||||
$0.setContentHuggingPriority(.defaultHigh, for: .vertical)
|
$0.setContentHuggingPriority(.defaultHigh, for: .vertical)
|
||||||
// TODO: tap gesture
|
$0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(reblogLabelPressed)))
|
||||||
// $0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(reblogLabelPressed)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private lazy var mainContainer = UIView().configure {
|
private lazy var mainContainer = UIView().configure {
|
||||||
|
@ -47,18 +46,16 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static let avatarImageViewSize: CGFloat = 50
|
private static let avatarImageViewSize: CGFloat = 50
|
||||||
private let avatarImageView = CachedImageView(cache: .avatars).configure {
|
private lazy var avatarImageView = CachedImageView(cache: .avatars).configure {
|
||||||
$0.layer.masksToBounds = true
|
$0.layer.masksToBounds = true
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
$0.heightAnchor.constraint(equalToConstant: TimelineStatusCollectionViewCell.avatarImageViewSize),
|
$0.heightAnchor.constraint(equalToConstant: TimelineStatusCollectionViewCell.avatarImageViewSize),
|
||||||
$0.widthAnchor.constraint(equalToConstant: TimelineStatusCollectionViewCell.avatarImageViewSize),
|
$0.widthAnchor.constraint(equalToConstant: TimelineStatusCollectionViewCell.avatarImageViewSize),
|
||||||
])
|
])
|
||||||
// TODO: context menu
|
$0.isUserInteractionEnabled = true
|
||||||
// $0.addInteraction(UIContextMenuInteraction(delegate: self))
|
$0.addInteraction(UIContextMenuInteraction(delegate: self))
|
||||||
// TODO: drag gesture
|
$0.addInteraction(UIDragInteraction(delegate: self))
|
||||||
// $0.addInteraction(UIDragInteraction(delegate: self))
|
$0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
||||||
// TODO: tap gesture
|
|
||||||
// $0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private let metaIndicatorsView = StatusMetaIndicatorsView()
|
private let metaIndicatorsView = StatusMetaIndicatorsView()
|
||||||
|
@ -82,8 +79,7 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell {
|
||||||
]).configure {
|
]).configure {
|
||||||
$0.axis = .horizontal
|
$0.axis = .horizontal
|
||||||
$0.spacing = 4
|
$0.spacing = 4
|
||||||
// TODO: tap gesture
|
$0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
||||||
// $0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private let displayNameLabel = EmojiLabel().configure {
|
private let displayNameLabel = EmojiLabel().configure {
|
||||||
|
@ -194,16 +190,19 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
private let replyButton = UIButton().configure {
|
private lazy var replyButton = UIButton().configure {
|
||||||
$0.setImage(UIImage(systemName: "arrowshape.turn.up.left.fill"), for: .normal)
|
$0.setImage(UIImage(systemName: "arrowshape.turn.up.left.fill"), for: .normal)
|
||||||
|
$0.addTarget(self, action: #selector(replyPressed), for: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
private let favoriteButton = UIButton().configure {
|
private lazy var favoriteButton = UIButton().configure {
|
||||||
$0.setImage(UIImage(systemName: "star.fill"), for: .normal)
|
$0.setImage(UIImage(systemName: "star.fill"), for: .normal)
|
||||||
|
$0.addTarget(self, action: #selector(favoritePressed), for: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
private let reblogButton = UIButton().configure {
|
private lazy var reblogButton = UIButton().configure {
|
||||||
$0.setImage(UIImage(systemName: "repeat"), for: .normal)
|
$0.setImage(UIImage(systemName: "repeat"), for: .normal)
|
||||||
|
$0.addTarget(self, action: #selector(reblogPressed), for: .touchUpInside)
|
||||||
}
|
}
|
||||||
|
|
||||||
private let moreButton = UIButton().configure {
|
private let moreButton = UIButton().configure {
|
||||||
|
@ -241,6 +240,8 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell {
|
||||||
|
|
||||||
private var firstLayout = true
|
private var firstLayout = true
|
||||||
private var isGrayscale = false
|
private var isGrayscale = false
|
||||||
|
|
||||||
|
private var updateTimestampWorkItem: DispatchWorkItem?
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
@ -316,6 +317,39 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Accessibility
|
||||||
|
|
||||||
|
override var accessibilityLabel: String? {
|
||||||
|
get {
|
||||||
|
guard let status = mastodonController.persistentContainer.status(for: statusID) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var str = "\(status.account.displayOrUserName), \(contentTextView.text ?? "")"
|
||||||
|
|
||||||
|
if status.attachments.count > 0 {
|
||||||
|
// TODO: localize me
|
||||||
|
str += ", \(status.attachments.count) attachment\(status.attachments.count > 1 ? "s" : "")"
|
||||||
|
}
|
||||||
|
if status.poll != nil {
|
||||||
|
str += ", poll"
|
||||||
|
}
|
||||||
|
str += ", \(status.createdAt.formatted(.relative(presentation: .numeric)))"
|
||||||
|
if let rebloggerID,
|
||||||
|
let reblogger = mastodonController.persistentContainer.account(for: rebloggerID) {
|
||||||
|
str += ", reblogged by \(reblogger.displayOrUserName)"
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
set {}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func accessibilityActivate() -> Bool {
|
||||||
|
delegate?.selected(status: statusID, state: statusState.copy())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Configure UI
|
||||||
|
|
||||||
func updateUI(statusID: String, state: StatusState) {
|
func updateUI(statusID: String, state: StatusState) {
|
||||||
guard var status = mastodonController.persistentContainer.status(for: statusID) else {
|
guard var status = mastodonController.persistentContainer.status(for: statusID) else {
|
||||||
fatalError()
|
fatalError()
|
||||||
|
@ -353,6 +387,11 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell {
|
||||||
updateGrayscaleableUI(account: account, status: status)
|
updateGrayscaleableUI(account: account, status: status)
|
||||||
updateUIForPreferences(account: account, status: status)
|
updateUIForPreferences(account: account, status: status)
|
||||||
|
|
||||||
|
doUpdateTimestamp(status: status)
|
||||||
|
|
||||||
|
timestampLabel.isHidden = showPinned
|
||||||
|
pinImageView.isHidden = !showPinned
|
||||||
|
|
||||||
cardView.card = status.card
|
cardView.card = status.card
|
||||||
cardView.isHidden = status.card == nil
|
cardView.isHidden = status.card == nil
|
||||||
cardView.navigationDelegate = delegate
|
cardView.navigationDelegate = delegate
|
||||||
|
@ -395,7 +434,7 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUIForPreferences(account: AccountMO, status: StatusMO) {
|
private func updateUIForPreferences(account: AccountMO, status: StatusMO) {
|
||||||
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * TimelineStatusCollectionViewCell.avatarImageViewSize
|
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadiusFraction * TimelineStatusCollectionViewCell.avatarImageViewSize
|
||||||
attachmentsView.contentHidden = Preferences.shared.blurAllMedia || status.sensitive
|
attachmentsView.contentHidden = Preferences.shared.blurAllMedia || status.sensitive
|
||||||
|
|
||||||
|
@ -415,6 +454,38 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updateTimestamp() {
|
||||||
|
guard let mastodonController,
|
||||||
|
let status = mastodonController.persistentContainer.status(for: statusID) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
doUpdateTimestamp(status: status)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func doUpdateTimestamp(status: StatusMO) {
|
||||||
|
timestampLabel.text = status.createdAt.timeAgoString()
|
||||||
|
|
||||||
|
let delay: DispatchTimeInterval?
|
||||||
|
switch status.createdAt.timeAgo().1 {
|
||||||
|
case .second:
|
||||||
|
delay = .seconds(10)
|
||||||
|
case .minute:
|
||||||
|
delay = .seconds(60)
|
||||||
|
default:
|
||||||
|
delay = nil
|
||||||
|
}
|
||||||
|
if let delay {
|
||||||
|
if updateTimestampWorkItem == nil {
|
||||||
|
updateTimestampWorkItem = DispatchWorkItem { [weak self] in
|
||||||
|
self?.updateTimestamp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: updateTimestampWorkItem!)
|
||||||
|
} else {
|
||||||
|
updateTimestampWorkItem = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private func updateRebloggerLabel(reblogger: AccountMO) {
|
private func updateRebloggerLabel(reblogger: AccountMO) {
|
||||||
if Preferences.shared.hideCustomEmojiInUsernames {
|
if Preferences.shared.hideCustomEmojiInUsernames {
|
||||||
reblogLabel.text = "Reblogged by \(reblogger.displayNameWithoutCustomEmoji)"
|
reblogLabel.text = "Reblogged by \(reblogger.displayNameWithoutCustomEmoji)"
|
||||||
|
@ -484,16 +555,157 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell {
|
||||||
let oldState = actionsContainer.isHidden
|
let oldState = actionsContainer.isHidden
|
||||||
if oldState != Preferences.shared.hideActionsInTimeline {
|
if oldState != Preferences.shared.hideActionsInTimeline {
|
||||||
updateActionsVisibility()
|
updateActionsVisibility()
|
||||||
delegate?.statusCellCollapsedStateChanged(self)
|
delegate?.statusCellNeedsReconfigure(self, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Interaction
|
// MARK: Interaction
|
||||||
|
|
||||||
@objc func collapseButtonPressed() {
|
@objc private func reblogLabelPressed() {
|
||||||
|
guard let rebloggerID else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
delegate?.selected(account: rebloggerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func accountPressed() {
|
||||||
|
delegate?.selected(account: accountID)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func collapseButtonPressed() {
|
||||||
statusState.collapsed!.toggle()
|
statusState.collapsed!.toggle()
|
||||||
// this delegate call causes the collection view to reconfigure this cell, at which point (and inside of the collection view's animation handling) we'll update the contentContainer
|
// this delegate call causes the collection view to reconfigure this cell, at which point (and inside of the collection view's animation handling) we'll update the contentContainer
|
||||||
delegate?.statusCellCollapsedStateChanged(self)
|
delegate?.statusCellNeedsReconfigure(self, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func replyPressed() {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func favoritePressed() {
|
||||||
|
guard let status = mastodonController.persistentContainer.status(for: statusID) else {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
let oldValue = status.favourited
|
||||||
|
status.favourited.toggle()
|
||||||
|
// update ui before network request to make things appear speedy
|
||||||
|
updateStatusState(status: status)
|
||||||
|
|
||||||
|
let request = (status.favourited ? Status.favourite : Status.unfavourite)(statusID)
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
let (newStatus, _) = try await mastodonController.run(request)
|
||||||
|
mastodonController.persistentContainer.addOrUpdate(status: newStatus)
|
||||||
|
// TODO: should this before the network request
|
||||||
|
UIImpactFeedbackGenerator(style: .light).impactOccurred()
|
||||||
|
} catch {
|
||||||
|
status.favourited = oldValue
|
||||||
|
// TODO: display error message
|
||||||
|
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func reblogPressed() {
|
||||||
|
guard let status = mastodonController.persistentContainer.status(for: statusID) else {
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !status.reblogged,
|
||||||
|
Preferences.shared.confirmBeforeReblog {
|
||||||
|
let image: UIImage?
|
||||||
|
let reblogVisibilityActions: [CustomAlertController.MenuAction]?
|
||||||
|
if mastodonController.instanceFeatures.reblogVisibility {
|
||||||
|
image = UIImage(systemName: Status.Visibility.public.unfilledImageName)
|
||||||
|
reblogVisibilityActions = [Status.Visibility.unlisted, .private].map { visibility in
|
||||||
|
CustomAlertController.MenuAction(title: "Reblog as \(visibility.displayName)", subtitle: visibility.subtitle, image: UIImage(systemName: visibility.unfilledImageName)) { [unowned self] in
|
||||||
|
self.doReblog(status: status, visibility: visibility)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
image = nil
|
||||||
|
reblogVisibilityActions = []
|
||||||
|
}
|
||||||
|
|
||||||
|
let preview = ConfirmReblogStatusPreviewView(status: status)
|
||||||
|
var config = CustomAlertController.Configuration(title: "Are you sure you want to reblog this post?", content: preview, actions: [
|
||||||
|
CustomAlertController.Action(title: "Cancel", style: .cancel, handler: nil),
|
||||||
|
CustomAlertController.Action(title: "Reblog", image: image, style: .default, handler: { [unowned self] in
|
||||||
|
self.doReblog(status: status, visibility: nil)
|
||||||
|
})
|
||||||
|
])
|
||||||
|
if let reblogVisibilityActions {
|
||||||
|
var menuAction = CustomAlertController.Action(title: nil, image: UIImage(systemName: "chevron.down"), style: .menu(reblogVisibilityActions), handler: nil)
|
||||||
|
menuAction.isSecondaryMenu = true
|
||||||
|
config.actions.append(menuAction)
|
||||||
|
}
|
||||||
|
let alert = CustomAlertController(config: config)
|
||||||
|
delegate?.present(alert, animated: true)
|
||||||
|
} else {
|
||||||
|
doReblog(status: status, visibility: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func doReblog(status: StatusMO, visibility: Status.Visibility?) {
|
||||||
|
let oldValue = status.reblogged
|
||||||
|
status.reblogged.toggle()
|
||||||
|
updateStatusState(status: status)
|
||||||
|
|
||||||
|
let request: Request<Status>
|
||||||
|
if status.reblogged {
|
||||||
|
request = Status.reblog(statusID, visibility: visibility)
|
||||||
|
} else {
|
||||||
|
request = Status.unreblog(statusID)
|
||||||
|
}
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
let (newStatus, _) = try await mastodonController.run(request)
|
||||||
|
mastodonController.persistentContainer.addOrUpdate(status: newStatus)
|
||||||
|
UIImpactFeedbackGenerator(style: .light).impactOccurred()
|
||||||
|
} catch {
|
||||||
|
status.reblogged = oldValue
|
||||||
|
// TODO: display error message
|
||||||
|
UINotificationFeedbackGenerator().notificationOccurred(.error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension TimelineStatusCollectionViewCell: UIContextMenuInteractionDelegate {
|
||||||
|
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
|
||||||
|
return UIContextMenuConfiguration() {
|
||||||
|
ProfileViewController(accountID: self.accountID, mastodonController: self.mastodonController)
|
||||||
|
} actionProvider: { _ in
|
||||||
|
return UIMenu(children: self.delegate?.actionsForProfile(accountID: self.accountID, sourceView: interaction.view!) ?? [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
|
||||||
|
if let viewController = animator.previewViewController,
|
||||||
|
let delegate {
|
||||||
|
animator.preferredCommitStyle = .pop
|
||||||
|
animator.addCompletion {
|
||||||
|
if let customPresenting = viewController as? CustomPreviewPresenting {
|
||||||
|
customPresenting.presentFromPreview(presenter: delegate)
|
||||||
|
} else {
|
||||||
|
delegate.show(viewController)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TimelineStatusCollectionViewCell: UIDragInteractionDelegate {
|
||||||
|
func dragInteraction(_ interaction: UIDragInteraction, itemsForBeginning session: UIDragSession) -> [UIDragItem] {
|
||||||
|
guard let currentAccountID = mastodonController.accountInfo?.id,
|
||||||
|
let account = mastodonController.persistentContainer.account(for: accountID) else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
let provider = NSItemProvider(object: account.url as NSURL)
|
||||||
|
let activity = UserActivityManager.showProfileActivity(id: accountID, accountID: currentAccountID)
|
||||||
|
activity.displaysAuxiliaryScene = true
|
||||||
|
provider.registerObject(activity, visibility: .all)
|
||||||
|
return [UIDragItem(itemProvider: provider)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue