Split MenuActionProvider from MenuPreviewProvider

This commit is contained in:
Shadowfacts 2022-05-01 23:04:56 -04:00
parent c069712c22
commit bf8a294676
24 changed files with 118 additions and 73 deletions

View File

@ -67,3 +67,9 @@ class AccountListTableViewController: EnhancedTableViewController {
extension AccountListTableViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController }
}
extension AccountListTableViewController: ToastableViewController {
}
extension AccountListTableViewController: MenuActionProvider {
}

View File

@ -153,6 +153,15 @@ class BookmarksTableViewController: EnhancedTableViewController {
}
extension BookmarksTableViewController: TuskerNavigationDelegate {
}
extension BookmarksTableViewController: ToastableViewController {
}
extension BookmarksTableViewController: MenuActionProvider {
}
extension BookmarksTableViewController: StatusTableViewCellDelegate {
var apiController: MastodonController { mastodonController }

View File

@ -444,6 +444,9 @@ extension ConversationTableViewController: TuskerNavigationDelegate {
}
}
extension ConversationTableViewController: MenuActionProvider {
}
extension ConversationTableViewController: StatusTableViewCellDelegate {
var apiController: MastodonController { mastodonController }
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {

View File

@ -134,8 +134,10 @@ extension ProfileDirectoryViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController }
}
extension ProfileDirectoryViewController: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { self }
extension ProfileDirectoryViewController: ToastableViewController {
}
extension ProfileDirectoryViewController: MenuActionProvider {
}
extension ProfileDirectoryViewController: UICollectionViewDelegate {

View File

@ -110,6 +110,8 @@ extension TrendingHashtagsViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController }
}
extension TrendingHashtagsViewController: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { self }
extension TrendingHashtagsViewController: ToastableViewController {
}
extension TrendingHashtagsViewController: MenuActionProvider {
}

View File

@ -104,6 +104,8 @@ extension TrendingLinksViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController }
}
extension TrendingLinksViewController: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { self }
extension TrendingLinksViewController: ToastableViewController {
}
extension TrendingLinksViewController: MenuActionProvider {
}

View File

@ -85,6 +85,12 @@ extension TrendingStatusesViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController }
}
extension TrendingStatusesViewController: ToastableViewController {
}
extension TrendingStatusesViewController: MenuActionProvider {
}
extension TrendingStatusesViewController: StatusTableViewCellDelegate {
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
tableView.beginUpdates()

View File

@ -176,3 +176,9 @@ extension EditListAccountsViewController: SearchResultsViewControllerDelegate {
extension EditListAccountsViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController }
}
extension EditListAccountsViewController: ToastableViewController {
}
extension EditListAccountsViewController: MenuActionProvider {
}

View File

@ -250,6 +250,9 @@ extension NotificationsTableViewController: TuskerNavigationDelegate {
var apiController: MastodonController { mastodonController }
}
extension NotificationsTableViewController: MenuActionProvider {
}
extension NotificationsTableViewController: StatusTableViewCellDelegate {
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {
cellHeightChanged()

View File

@ -288,6 +288,15 @@ extension SearchResultsViewController: UISearchBarDelegate {
}
}
extension SearchResultsViewController: TuskerNavigationDelegate {
}
extension SearchResultsViewController: ToastableViewController {
}
extension SearchResultsViewController: MenuActionProvider {
}
extension SearchResultsViewController: StatusTableViewCellDelegate {
var apiController: MastodonController { mastodonController }
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {

View File

@ -144,6 +144,15 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
}
extension StatusActionAccountListTableViewController: TuskerNavigationDelegate {
}
extension StatusActionAccountListTableViewController: ToastableViewController {
}
extension StatusActionAccountListTableViewController: MenuActionProvider {
}
extension StatusActionAccountListTableViewController: StatusTableViewCellDelegate {
var apiController: MastodonController { mastodonController }
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell) {

View File

@ -294,6 +294,9 @@ extension TimelineTableViewController: StatusTableViewCellDelegate {
}
}
extension TimelineTableViewController: MenuActionProvider {
}
extension TimelineTableViewController: UITableViewDataSourcePrefetching, StatusTablePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
let ids: [String] = indexPaths.compactMap {

View File

@ -10,29 +10,33 @@ import UIKit
import SafariServices
import Pachyderm
protocol MenuActionProvider: AnyObject {
var navigationDelegate: TuskerNavigationDelegate? { get }
var toastableViewController: ToastableViewController? { get }
}
protocol MenuPreviewProvider: AnyObject {
typealias PreviewProviders = (content: UIContextMenuContentPreviewProvider, actions: () -> [UIMenuElement])
var navigationDelegate: TuskerNavigationDelegate? { get }
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders?
}
protocol CustomPreviewPresenting {
func presentFromPreview(presenter: UIViewController)
}
extension MenuPreviewProvider {
extension MenuActionProvider where Self: TuskerNavigationDelegate {
var navigationDelegate: TuskerNavigationDelegate? { self }
}
extension MenuActionProvider where Self: ToastableViewController {
var toastableViewController: ToastableViewController? { self }
}
extension MenuActionProvider {
private var mastodonController: MastodonController? { navigationDelegate?.apiController }
// Default no-op implementation
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
return nil
}
func actionsForProfile(accountID: String, sourceView: UIView?) -> [UIMenuElement] {
guard let mastodonController = mastodonController,
let account = mastodonController.persistentContainer.account(for: accountID) else { return [] }
@ -84,7 +88,7 @@ extension MenuPreviewProvider {
func actionsForURL(_ url: URL, sourceView: UIView?) -> [UIAction] {
return [
openInSafariAction(url: url),
openInSafariAction(url: url),
createAction(identifier: "share", title: "Share", systemImageName: "square.and.arrow.up", handler: { [weak self, weak sourceView] (_) in
guard let self = self else { return }
self.navigationDelegate?.showMoreOptions(forURL: url, sourceView: sourceView)

View File

@ -11,7 +11,7 @@ import SwiftSoup
class AccountTableViewCell: UITableViewCell {
weak var delegate: TuskerNavigationDelegate?
weak var delegate: (TuskerNavigationDelegate & MenuActionProvider)?
var mastodonController: MastodonController! { delegate?.apiController }
@IBOutlet weak var avatarImageView: UIImageView!
@ -98,13 +98,11 @@ extension AccountTableViewCell: SelectableTableViewCell {
}
extension AccountTableViewCell: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { return delegate }
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
guard let mastodonController = mastodonController else { return nil }
return (
content: { ProfileViewController(accountID: self.accountID, mastodonController: mastodonController) },
actions: { self.actionsForProfile(accountID: self.accountID, sourceView: self.avatarImageView) }
actions: { self.delegate?.actionsForProfile(accountID: self.accountID, sourceView: self.avatarImageView) ?? [] }
)
}
}

View File

@ -243,9 +243,10 @@ extension ContentTextView: UITextViewDelegate {
}
}
extension ContentTextView: MenuPreviewProvider {
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
fatalError("unimplemented")
extension ContentTextView: MenuActionProvider {
var toastableViewController: ToastableViewController? {
// todo: pass this down through the text view
nil
}
}

View File

@ -12,7 +12,7 @@ import SwiftSoup
class ActionNotificationGroupTableViewCell: UITableViewCell {
weak var delegate: TuskerNavigationDelegate?
weak var delegate: (TuskerNavigationDelegate & MenuActionProvider)?
var mastodonController: MastodonController! { delegate?.apiController }
@IBOutlet weak var actionImageView: UIImageView!
@ -244,8 +244,6 @@ extension ActionNotificationGroupTableViewCell: SelectableTableViewCell {
}
extension ActionNotificationGroupTableViewCell: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { return delegate }
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
return (content: {
let notifications = self.group.notifications

View File

@ -11,7 +11,7 @@ import Pachyderm
class FollowNotificationGroupTableViewCell: UITableViewCell {
weak var delegate: TuskerNavigationDelegate?
weak var delegate: (TuskerNavigationDelegate & MenuActionProvider)?
var mastodonController: MastodonController! { delegate?.apiController }
@IBOutlet weak var avatarStackView: UIStackView!
@ -196,8 +196,6 @@ extension FollowNotificationGroupTableViewCell: SelectableTableViewCell {
}
extension FollowNotificationGroupTableViewCell: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { return delegate }
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
guard let mastodonController = mastodonController else { return nil }
let accountIDs = self.group.notifications.map { $0.account.id }
@ -209,7 +207,7 @@ extension FollowNotificationGroupTableViewCell: MenuPreviewProvider {
}
}, actions: {
if accountIDs.count == 1 {
return self.actionsForProfile(accountID: accountIDs.first!, sourceView: self)
return self.delegate?.actionsForProfile(accountID: accountIDs.first!, sourceView: self) ?? []
} else {
return []
}

View File

@ -11,7 +11,7 @@ import Pachyderm
class FollowRequestNotificationTableViewCell: UITableViewCell {
weak var delegate: TuskerNavigationDelegate?
weak var delegate: (TuskerNavigationDelegate & MenuActionProvider)?
var mastodonController: MastodonController! { delegate?.apiController }
@IBOutlet weak var stackView: UIStackView!
@ -169,8 +169,6 @@ extension FollowRequestNotificationTableViewCell: SelectableTableViewCell {
}
extension FollowRequestNotificationTableViewCell: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { return delegate }
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
guard let mastodonController = mastodonController else { return nil }
return (content: {

View File

@ -12,7 +12,7 @@ import SwiftSoup
class PollFinishedTableViewCell: UITableViewCell {
weak var delegate: TuskerNavigationDelegate?
weak var delegate: (TuskerNavigationDelegate & MenuActionProvider)?
var mastodonController: MastodonController? { delegate?.apiController }
@IBOutlet weak var displayNameLabel: EmojiLabel!
@ -91,8 +91,6 @@ extension PollFinishedTableViewCell: SelectableTableViewCell {
}
extension PollFinishedTableViewCell: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { delegate }
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
guard let delegate = delegate,
let statusID = notification?.status?.id,
@ -102,7 +100,7 @@ extension PollFinishedTableViewCell: MenuPreviewProvider {
return (content: {
delegate.conversation(mainStatusID: statusID, state: .unknown)
}, actions: {
self.actionsForStatus(status, sourceView: self)
delegate.actionsForStatus(status, sourceView: self)
})
}
}

View File

@ -10,7 +10,7 @@ import UIKit
import Pachyderm
import Combine
protocol ProfileHeaderViewDelegate: TuskerNavigationDelegate {
protocol ProfileHeaderViewDelegate: TuskerNavigationDelegate, MenuActionProvider {
func profileHeader(_ headerView: ProfileHeaderView, selectedPostsIndexChangedTo newIndex: Int)
}
@ -106,7 +106,7 @@ class ProfileHeaderView: UIView {
updateImages(account: account)
moreButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: [], children: actionsForProfile(accountID: accountID, sourceView: moreButton))
moreButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: [], children: delegate?.actionsForProfile(accountID: accountID, sourceView: moreButton) ?? [])
noteTextView.navigationDelegate = delegate
noteTextView.setTextFromHtml(account.note)
@ -272,7 +272,3 @@ extension ProfileHeaderView: UIPointerInteractionDelegate {
return UIPointerStyle(effect: .lift(preview), shape: .none)
}
}
extension ProfileHeaderView: MenuPreviewProvider {
var navigationDelegate: TuskerNavigationDelegate? { delegate }
}

View File

@ -11,11 +11,11 @@ import Pachyderm
import Combine
import AVKit
protocol StatusTableViewCellDelegate: TuskerNavigationDelegate {
protocol StatusTableViewCellDelegate: TuskerNavigationDelegate, MenuActionProvider {
func statusCellCollapsedStateChanged(_ cell: BaseStatusTableViewCell)
}
class BaseStatusTableViewCell: UITableViewCell, MenuPreviewProvider {
class BaseStatusTableViewCell: UITableViewCell {
weak var delegate: StatusTableViewCellDelegate? {
didSet {
@ -153,7 +153,8 @@ class BaseStatusTableViewCell: UITableViewCell, MenuPreviewProvider {
cardView.card = status.card
cardView.isHidden = status.card == nil
cardView.navigationDelegate = navigationDelegate
cardView.navigationDelegate = delegate
cardView.actionProvider = delegate
attachmentsView.updateUI(status: status)
@ -206,7 +207,7 @@ class BaseStatusTableViewCell: UITableViewCell, MenuPreviewProvider {
// keep menu in sync with changed states e.g. bookmarked, muted
// do not include reply action here, because the cell already contains a button for it
moreButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: [], children: actionsForStatus(status, sourceView: moreButton, includeReply: false))
moreButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: [], children: delegate?.actionsForStatus(status, sourceView: moreButton, includeReply: false) ?? [])
pollView.isHidden = status.poll == nil
pollView.mastodonController = mastodonController
@ -326,13 +327,6 @@ class BaseStatusTableViewCell: UITableViewCell, MenuPreviewProvider {
showStatusAutomatically = false
}
// MARK: - MenuPreviewProvider
var navigationDelegate: TuskerNavigationDelegate? { return delegate }
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
return nil
}
// MARK: - Interaction
@IBAction func collapseButtonPressed() {

View File

@ -112,7 +112,7 @@ extension ConversationMainStatusTableViewCell: UIContextMenuInteractionDelegate
return UIContextMenuConfiguration(identifier: nil) {
ProfileViewController(accountID: self.accountID, mastodonController: self.mastodonController)
} actionProvider: { (_) in
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: self.actionsForProfile(accountID: self.accountID, sourceView: self.avatarImageView))
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: self.delegate?.actionsForProfile(accountID: self.accountID, sourceView: self.avatarImageView) ?? [])
}
}
}

View File

@ -14,6 +14,7 @@ import WebURLFoundationExtras
class StatusCardView: UIView {
weak var navigationDelegate: TuskerNavigationDelegate?
weak var actionProvider: MenuActionProvider?
var card: Card? {
didSet {
@ -212,9 +213,6 @@ class StatusCardView: UIView {
}
extension StatusCardView: MenuPreviewProvider {
}
extension StatusCardView: UIContextMenuInteractionDelegate {
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, configurationForMenuAtLocation location: CGPoint) -> UIContextMenuConfiguration? {
guard let card = card else { return nil }
@ -222,7 +220,7 @@ extension StatusCardView: UIContextMenuInteractionDelegate {
return UIContextMenuConfiguration(identifier: nil) {
return SFSafariViewController(url: URL(card.url)!)
} actionProvider: { (_) in
let actions = self.actionsForURL(URL(card.url)!, sourceView: self)
let actions = self.actionProvider?.actionsForURL(URL(card.url)!, sourceView: self) ?? []
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: actions)
}
}

View File

@ -214,17 +214,6 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
reply()
}
override func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> BaseStatusTableViewCell.PreviewProviders? {
guard let mastodonController = mastodonController,
let status = mastodonController.persistentContainer.status(for: statusID) else {
return nil
}
return (
content: { ConversationTableViewController(for: self.statusID, state: self.statusState.copy(), mastodonController: mastodonController) },
actions: { self.actionsForStatus(status, sourceView: self) }
)
}
// MARK: - Accessibility
override var accessibilityLabel: String? {
@ -377,13 +366,13 @@ extension TimelineStatusTableViewCell: UIContextMenuInteractionDelegate {
return UIContextMenuConfiguration(identifier: nil) {
ProfileViewController(accountID: self.accountID, mastodonController: self.mastodonController)
} actionProvider: { (_) in
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: self.actionsForProfile(accountID: self.accountID, sourceView: self.avatarImageView))
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: self.delegate?.actionsForProfile(accountID: self.accountID, sourceView: self.avatarImageView) ?? [])
}
}
func contextMenuInteraction(_ interaction: UIContextMenuInteraction, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
if let viewController = animator.previewViewController,
let delegate = navigationDelegate {
let delegate = delegate {
animator.preferredCommitStyle = .pop
animator.addCompletion {
if let customPresenting = viewController as? CustomPreviewPresenting {
@ -396,3 +385,16 @@ extension TimelineStatusTableViewCell: UIContextMenuInteractionDelegate {
}
}
extension TimelineStatusTableViewCell: MenuPreviewProvider {
func getPreviewProviders(for location: CGPoint, sourceViewController: UIViewController) -> PreviewProviders? {
guard let mastodonController = mastodonController,
let status = mastodonController.persistentContainer.status(for: statusID) else {
return nil
}
return (
content: { ConversationTableViewController(for: self.statusID, state: self.statusState.copy(), mastodonController: mastodonController) },
actions: { self.delegate?.actionsForStatus(status, sourceView: self) ?? [] }
)
}
}