Merge pull request #1055 from mastodon/ios-103-post-menu
Post Overflow Menus (IOS-103)
This commit is contained in:
commit
09ff88d0b6
|
@ -101,8 +101,11 @@
|
|||
"title": "Translate from %s",
|
||||
"unknown_language": "Unknown"
|
||||
},
|
||||
"edit_post": "Edit"
|
||||
|
||||
"edit_post": "Edit",
|
||||
"bookmark": "Bookmark",
|
||||
"remove_bookmark": "Remove Bookmark",
|
||||
"follow": "Follow %s",
|
||||
"unfollow": "Unfollow %s"
|
||||
},
|
||||
"tabs": {
|
||||
"home": "Home",
|
||||
|
|
|
@ -384,6 +384,16 @@ extension DataSourceFacade {
|
|||
composeContext: .editStatus(status: status, statusSource: statusSource),
|
||||
destination: .topLevel)
|
||||
_ = dependency.coordinator.present(scene: .editStatus(viewModel: editStatusViewModel), transition: .modal(animated: true))
|
||||
|
||||
case .showOriginal:
|
||||
// do nothing, as the translation is reverted in `StatusTableViewCellDelegate` in `DataSourceProvider+StatusTableViewCellDelegate.swift`.
|
||||
break
|
||||
case .followUser(_):
|
||||
|
||||
guard let author = menuContext.author else { return }
|
||||
|
||||
try await DataSourceFacade.responseToUserFollowAction(dependency: dependency,
|
||||
user: author)
|
||||
}
|
||||
} // end func
|
||||
}
|
||||
|
|
|
@ -496,6 +496,14 @@ extension StatusTableViewCellDelegate where Self: DataSourceProvider & AuthConte
|
|||
cell.invalidateIntrinsicContentSize()
|
||||
}
|
||||
}
|
||||
|
||||
if case .showOriginal = action {
|
||||
DispatchQueue.main.async {
|
||||
if let cell = cell as? StatusTableViewCell {
|
||||
cell.statusView.revertTranslation()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try await DataSourceFacade.responseToMenuAction(
|
||||
dependency: self,
|
||||
|
|
|
@ -428,7 +428,7 @@ extension ProfileViewController {
|
|||
}
|
||||
|
||||
let menu = MastodonMenu.setupMenu(
|
||||
actions: menuActions,
|
||||
actions: [menuActions],
|
||||
delegate: self
|
||||
)
|
||||
return menu
|
||||
|
|
|
@ -187,6 +187,7 @@ extension NotificationView {
|
|||
}
|
||||
.assign(to: \.isBlocking, on: viewModel)
|
||||
.store(in: &disposeBag)
|
||||
|
||||
// isMyself
|
||||
Publishers.CombineLatest(
|
||||
author.publisher(for: \.domain),
|
||||
|
@ -199,12 +200,27 @@ extension NotificationView {
|
|||
}
|
||||
.assign(to: \.isMyself, on: viewModel)
|
||||
.store(in: &disposeBag)
|
||||
|
||||
// follow request state
|
||||
notification.publisher(for: \.followRequestState)
|
||||
.assign(to: \.followRequestState, on: viewModel)
|
||||
.store(in: &disposeBag)
|
||||
|
||||
notification.publisher(for: \.transientFollowRequestState)
|
||||
.assign(to: \.transientFollowRequestState, on: viewModel)
|
||||
.store(in: &disposeBag)
|
||||
|
||||
// Following
|
||||
author.publisher(for: \.followingBy)
|
||||
.map { [weak viewModel] followingBy in
|
||||
guard let viewModel = viewModel else { return false }
|
||||
guard let authContext = viewModel.authContext else { return false }
|
||||
return followingBy.contains(where: {
|
||||
$0.id == authContext.mastodonAuthenticationBox.userID && $0.domain == authContext.mastodonAuthenticationBox.domain
|
||||
})
|
||||
}
|
||||
.assign(to: \.isFollowed, on: viewModel)
|
||||
.store(in: &disposeBag)
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,6 +112,8 @@ public enum L10n {
|
|||
public static func blockDomain(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "Common.Controls.Actions.BlockDomain", String(describing: p1), fallback: "Block %@")
|
||||
}
|
||||
/// Bookmark
|
||||
public static let bookmark = L10n.tr("Localizable", "Common.Controls.Actions.Bookmark", fallback: "Bookmark")
|
||||
/// Cancel
|
||||
public static let cancel = L10n.tr("Localizable", "Common.Controls.Actions.Cancel", fallback: "Cancel")
|
||||
/// Compose
|
||||
|
@ -136,6 +138,10 @@ public enum L10n {
|
|||
public static let editPost = L10n.tr("Localizable", "Common.Controls.Actions.EditPost", fallback: "Edit")
|
||||
/// Find people to follow
|
||||
public static let findPeople = L10n.tr("Localizable", "Common.Controls.Actions.FindPeople", fallback: "Find people to follow")
|
||||
/// Follow %@
|
||||
public static func follow(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "Common.Controls.Actions.Follow", String(describing: p1), fallback: "Follow %@")
|
||||
}
|
||||
/// Manually search instead
|
||||
public static let manuallySearch = L10n.tr("Localizable", "Common.Controls.Actions.ManuallySearch", fallback: "Manually search instead")
|
||||
/// Next
|
||||
|
@ -154,6 +160,8 @@ public enum L10n {
|
|||
public static let previous = L10n.tr("Localizable", "Common.Controls.Actions.Previous", fallback: "Previous")
|
||||
/// Remove
|
||||
public static let remove = L10n.tr("Localizable", "Common.Controls.Actions.Remove", fallback: "Remove")
|
||||
/// Remove Bookmark
|
||||
public static let removeBookmark = L10n.tr("Localizable", "Common.Controls.Actions.RemoveBookmark", fallback: "Remove Bookmark")
|
||||
/// Reply
|
||||
public static let reply = L10n.tr("Localizable", "Common.Controls.Actions.Reply", fallback: "Reply")
|
||||
/// Report %@
|
||||
|
@ -188,6 +196,10 @@ public enum L10n {
|
|||
public static func unblockDomain(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "Common.Controls.Actions.UnblockDomain", String(describing: p1), fallback: "Unblock %@")
|
||||
}
|
||||
/// Unfollow %@
|
||||
public static func unfollow(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "Common.Controls.Actions.Unfollow", String(describing: p1), fallback: "Unfollow %@")
|
||||
}
|
||||
public enum TranslatePost {
|
||||
/// Translate from %@
|
||||
public static func title(_ p1: Any) -> String {
|
||||
|
|
|
@ -40,6 +40,8 @@ Please check your internet connection.";
|
|||
"Common.Controls.Actions.Discard" = "Discard";
|
||||
"Common.Controls.Actions.Done" = "Done";
|
||||
"Common.Controls.Actions.Edit" = "Edit";
|
||||
"Common.Controls.Actions.Bookmark" = "Bookmark";
|
||||
"Common.Controls.Actions.RemoveBookmark" = "Remove Bookmark";
|
||||
"Common.Controls.Actions.EditPost" = "Edit";
|
||||
"Common.Controls.Actions.FindPeople" = "Find people to follow";
|
||||
"Common.Controls.Actions.ManuallySearch" = "Manually search instead";
|
||||
|
@ -67,6 +69,8 @@ Please check your internet connection.";
|
|||
"Common.Controls.Actions.TranslatePost.UnknownLanguage" = "Unknown";
|
||||
"Common.Controls.Actions.TryAgain" = "Try Again";
|
||||
"Common.Controls.Actions.UnblockDomain" = "Unblock %@";
|
||||
"Common.Controls.Actions.Follow" = "Follow %@";
|
||||
"Common.Controls.Actions.Unfollow" = "Unfollow %@";
|
||||
"Common.Controls.Friendship.Block" = "Block";
|
||||
"Common.Controls.Friendship.BlockDomain" = "Block %@";
|
||||
"Common.Controls.Friendship.BlockUser" = "Block %@";
|
||||
|
|
|
@ -39,6 +39,7 @@ extension NotificationView {
|
|||
@Published public var isMuting = false
|
||||
@Published public var isBlocking = false
|
||||
@Published public var isTranslated = false
|
||||
@Published public var isFollowed = false
|
||||
|
||||
@Published public var timestamp: Date?
|
||||
|
||||
|
@ -208,18 +209,19 @@ extension NotificationView.ViewModel {
|
|||
$authorName,
|
||||
$isMuting,
|
||||
$isBlocking,
|
||||
Publishers.CombineLatest(
|
||||
Publishers.CombineLatest3(
|
||||
$isMyself,
|
||||
$isTranslated
|
||||
$isTranslated,
|
||||
$isFollowed
|
||||
)
|
||||
)
|
||||
.sink { [weak self] authorName, isMuting, isBlocking, isMyselfIsTranslated in
|
||||
.sink { [weak self] authorName, isMuting, isBlocking, isMyselfIsTranslatedIsFollowed in
|
||||
guard let name = authorName?.string else {
|
||||
notificationView.menuButton.menu = nil
|
||||
return
|
||||
}
|
||||
|
||||
let (isMyself, isTranslated) = isMyselfIsTranslated
|
||||
let (isMyself, isTranslated, isFollowed) = isMyselfIsTranslatedIsFollowed
|
||||
|
||||
lazy var instanceConfigurationV2: Mastodon.Entity.V2.Instance.Configuration? = {
|
||||
guard
|
||||
|
@ -243,6 +245,7 @@ extension NotificationView.ViewModel {
|
|||
isBlocking: isBlocking,
|
||||
isMyself: isMyself,
|
||||
isBookmarking: false, // no bookmark action display for notification item
|
||||
isFollowed: isFollowed,
|
||||
isTranslationEnabled: instanceConfigurationV2?.translation?.enabled == true,
|
||||
isTranslated: isTranslated,
|
||||
statusLanguage: ""
|
||||
|
|
|
@ -460,9 +460,10 @@ extension NotificationView {
|
|||
public typealias AuthorMenuContext = StatusAuthorView.AuthorMenuContext
|
||||
|
||||
public func setupAuthorMenu(menuContext: AuthorMenuContext) -> (UIMenu, [UIAccessibilityCustomAction]) {
|
||||
var actions: [MastodonMenu.Action] = []
|
||||
|
||||
actions = [
|
||||
var actions: [[MastodonMenu.Action]] = []
|
||||
var upperActions: [MastodonMenu.Action] = []
|
||||
|
||||
upperActions = [
|
||||
.muteUser(.init(
|
||||
name: menuContext.name,
|
||||
isMuting: menuContext.isMuting
|
||||
|
@ -473,11 +474,13 @@ extension NotificationView {
|
|||
)),
|
||||
.reportUser(
|
||||
.init(name: menuContext.name)
|
||||
),
|
||||
)
|
||||
]
|
||||
|
||||
actions.append(upperActions)
|
||||
|
||||
if menuContext.isMyself {
|
||||
actions.append(.deleteStatus)
|
||||
actions.append([.deleteStatus])
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -150,6 +150,7 @@ extension StatusAuthorView {
|
|||
public let isBlocking: Bool
|
||||
public let isMyself: Bool
|
||||
public let isBookmarking: Bool
|
||||
public let isFollowed: Bool
|
||||
|
||||
public let isTranslationEnabled: Bool
|
||||
public let isTranslated: Bool
|
||||
|
@ -157,46 +158,54 @@ extension StatusAuthorView {
|
|||
}
|
||||
|
||||
public func setupAuthorMenu(menuContext: AuthorMenuContext) -> (UIMenu, [UIAccessibilityCustomAction]) {
|
||||
var actions = [MastodonMenu.Action]()
|
||||
var actions: [[MastodonMenu.Action]] = []
|
||||
var postActions: [MastodonMenu.Action] = []
|
||||
var userActions: [MastodonMenu.Action] = []
|
||||
|
||||
if menuContext.isMyself {
|
||||
actions.append(.editStatus)
|
||||
postActions.append(.editStatus)
|
||||
}
|
||||
|
||||
if !menuContext.isMyself {
|
||||
if let statusLanguage = menuContext.statusLanguage, menuContext.isTranslationEnabled, !menuContext.isTranslated {
|
||||
actions.append(
|
||||
.translateStatus(.init(language: statusLanguage))
|
||||
)
|
||||
if let statusLanguage = menuContext.statusLanguage, menuContext.isTranslationEnabled {
|
||||
if menuContext.isTranslated == false {
|
||||
postActions.append(.translateStatus(.init(language: statusLanguage)))
|
||||
} else {
|
||||
postActions.append(.showOriginal)
|
||||
}
|
||||
|
||||
actions.append(contentsOf: [
|
||||
.muteUser(.init(
|
||||
name: menuContext.name,
|
||||
isMuting: menuContext.isMuting
|
||||
)),
|
||||
.blockUser(.init(
|
||||
name: menuContext.name,
|
||||
isBlocking: menuContext.isBlocking
|
||||
)),
|
||||
.reportUser(
|
||||
.init(name: menuContext.name)
|
||||
)
|
||||
])
|
||||
}
|
||||
|
||||
actions.append(contentsOf: [
|
||||
.bookmarkStatus(
|
||||
.init(isBookmarking: menuContext.isBookmarking)
|
||||
),
|
||||
.shareStatus
|
||||
])
|
||||
|
||||
postActions.append(.bookmarkStatus(.init(isBookmarking: menuContext.isBookmarking)))
|
||||
postActions.append(.shareStatus)
|
||||
|
||||
if menuContext.isMyself == false {
|
||||
|
||||
userActions.append(.followUser(.init(
|
||||
name: menuContext.name,
|
||||
isFollowing: menuContext.isFollowed
|
||||
)))
|
||||
|
||||
userActions.append(.muteUser(.init(
|
||||
name: menuContext.name,
|
||||
isMuting: menuContext.isMuting
|
||||
)))
|
||||
|
||||
userActions.append(.blockUser(.init(
|
||||
name: menuContext.name,
|
||||
isBlocking: menuContext.isBlocking
|
||||
)))
|
||||
|
||||
userActions.append(.reportUser(
|
||||
.init(name: menuContext.name)
|
||||
))
|
||||
}
|
||||
|
||||
actions.append(postActions)
|
||||
actions.append(userActions)
|
||||
|
||||
if menuContext.isMyself {
|
||||
actions.append(.deleteStatus)
|
||||
actions.append([.deleteStatus])
|
||||
}
|
||||
|
||||
|
||||
let menu = MastodonMenu.setupMenu(
|
||||
actions: actions,
|
||||
delegate: self.statusView!
|
||||
|
@ -214,14 +223,14 @@ extension StatusAuthorView {
|
|||
|
||||
extension StatusAuthorView {
|
||||
@objc private func authorAvatarButtonDidPressed(_ sender: UIButton) {
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
guard let statusView = statusView else { return }
|
||||
|
||||
statusView.delegate?.statusView(statusView, authorAvatarButtonDidPressed: avatarButton)
|
||||
}
|
||||
|
||||
@objc private func contentSensitiveeToggleButtonDidPressed(_ sender: UIButton) {
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
guard let statusView = statusView else { return }
|
||||
|
||||
statusView.delegate?.statusView(statusView, contentSensitiveeToggleButtonDidPressed: sender)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -258,6 +258,18 @@ extension StatusView {
|
|||
}
|
||||
.assign(to: \.isMyself, on: viewModel)
|
||||
.store(in: &disposeBag)
|
||||
|
||||
// Following
|
||||
author.publisher(for: \.followingBy)
|
||||
.map { [weak viewModel] followingBy in
|
||||
guard let viewModel = viewModel else { return false }
|
||||
guard let authContext = viewModel.authContext else { return false }
|
||||
return followingBy.contains(where: {
|
||||
$0.id == authContext.mastodonAuthenticationBox.userID && $0.domain == authContext.mastodonAuthenticationBox.domain
|
||||
})
|
||||
}
|
||||
.assign(to: \.isFollowed, on: viewModel)
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
|
||||
private func configureTimestamp(timestamp: AnyPublisher<Date, Never>) {
|
||||
|
@ -278,8 +290,9 @@ extension StatusView {
|
|||
viewModel.applicationName = applicationName
|
||||
}
|
||||
|
||||
func revertTranslation() {
|
||||
public func revertTranslation() {
|
||||
guard let originalStatus = viewModel.originalStatus else { return }
|
||||
|
||||
viewModel.translatedFromLanguage = nil
|
||||
viewModel.translatedUsingProvider = nil
|
||||
originalStatus.reblog?.update(translatedContent: nil)
|
||||
|
|
|
@ -45,6 +45,7 @@ extension StatusView {
|
|||
@Published public var isMyself = false
|
||||
@Published public var isMuting = false
|
||||
@Published public var isBlocking = false
|
||||
@Published public var isFollowed = false
|
||||
|
||||
// Translation
|
||||
@Published public var isCurrentlyTranslating = false
|
||||
|
@ -656,10 +657,11 @@ extension StatusView.ViewModel {
|
|||
$authorName,
|
||||
$isMyself
|
||||
)
|
||||
let publishersTwo = Publishers.CombineLatest3(
|
||||
let publishersTwo = Publishers.CombineLatest4(
|
||||
$isMuting,
|
||||
$isBlocking,
|
||||
$isBookmark
|
||||
$isBookmark,
|
||||
$isFollowed
|
||||
)
|
||||
let publishersThree = Publishers.CombineLatest(
|
||||
$translatedFromLanguage,
|
||||
|
@ -673,7 +675,7 @@ extension StatusView.ViewModel {
|
|||
).eraseToAnyPublisher()
|
||||
.sink { tupleOne, tupleTwo, tupleThree in
|
||||
let (authorName, isMyself) = tupleOne
|
||||
let (isMuting, isBlocking, isBookmark) = tupleTwo
|
||||
let (isMuting, isBlocking, isBookmark, isFollowed) = tupleTwo
|
||||
let (translatedFromLanguage, language) = tupleThree
|
||||
|
||||
guard let name = authorName?.string else {
|
||||
|
@ -704,6 +706,7 @@ extension StatusView.ViewModel {
|
|||
isBlocking: isBlocking,
|
||||
isMyself: isMyself,
|
||||
isBookmarking: isBookmark,
|
||||
isFollowed: isFollowed,
|
||||
isTranslationEnabled: instanceConfigurationV2?.translation?.enabled == true,
|
||||
isTranslated: translatedFromLanguage != nil,
|
||||
statusLanguage: language
|
||||
|
|
|
@ -792,7 +792,6 @@ extension StatusView: StatusMetricViewDelegate {
|
|||
// MARK: - MastodonMenuDelegate
|
||||
extension StatusView: MastodonMenuDelegate {
|
||||
public func menuAction(_ action: MastodonMenu.Action) {
|
||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
||||
delegate?.statusView(self, menuButton: authorView.menuButton, didSelectAction: action)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,31 +14,30 @@ public protocol MastodonMenuDelegate: AnyObject {
|
|||
|
||||
public enum MastodonMenu {
|
||||
public static func setupMenu(
|
||||
actions: [Action],
|
||||
actions: [[Action]],
|
||||
delegate: MastodonMenuDelegate
|
||||
) -> UIMenu {
|
||||
var children: [UIMenuElement] = []
|
||||
for action in actions {
|
||||
|
||||
let element: UIMenuElement
|
||||
|
||||
if case let .deleteStatus = action {
|
||||
let deleteAction = action.build(delegate: delegate).menuElement
|
||||
element = UIMenu(options: .displayInline, children: [deleteAction])
|
||||
} else {
|
||||
element = action.build(delegate: delegate).menuElement
|
||||
for actionGroup in actions {
|
||||
var submenuChildren: [UIMenuElement] = []
|
||||
for action in actionGroup {
|
||||
let element = action.build(delegate: delegate).menuElement
|
||||
submenuChildren.append(element)
|
||||
}
|
||||
children.append(element)
|
||||
let submenu = UIMenu(options: .displayInline, children: submenuChildren)
|
||||
children.append(submenu)
|
||||
}
|
||||
|
||||
return UIMenu(children: children)
|
||||
}
|
||||
|
||||
public static func setupAccessibilityActions(
|
||||
actions: [Action],
|
||||
actions: [[Action]],
|
||||
delegate: MastodonMenuDelegate
|
||||
) -> [UIAccessibilityCustomAction] {
|
||||
var accessibilityActions: [UIAccessibilityCustomAction] = []
|
||||
for action in actions {
|
||||
for action in actions.flatMap({ $0 }) {
|
||||
let element = action.build(delegate: delegate)
|
||||
accessibilityActions.append(element.accessibilityCustomAction)
|
||||
}
|
||||
|
@ -49,6 +48,7 @@ public enum MastodonMenu {
|
|||
extension MastodonMenu {
|
||||
public enum Action {
|
||||
case translateStatus(TranslateStatusActionContext)
|
||||
case showOriginal
|
||||
case muteUser(MuteUserActionContext)
|
||||
case blockUser(BlockUserActionContext)
|
||||
case reportUser(ReportUserActionContext)
|
||||
|
@ -58,6 +58,7 @@ extension MastodonMenu {
|
|||
case shareStatus
|
||||
case deleteStatus
|
||||
case editStatus
|
||||
case followUser(FollowUserActionContext)
|
||||
|
||||
func build(delegate: MastodonMenuDelegate) -> LabeledAction {
|
||||
switch self {
|
||||
|
@ -121,10 +122,10 @@ extension MastodonMenu {
|
|||
let title: String
|
||||
let image: UIImage?
|
||||
if context.isBookmarking {
|
||||
title = "Remove Bookmark" // TODO: i18n
|
||||
title = L10n.Common.Controls.Actions.removeBookmark
|
||||
image = UIImage(systemName: "bookmark.slash.fill")
|
||||
} else {
|
||||
title = "Bookmark" // TODO: i18n
|
||||
title = L10n.Common.Controls.Actions.bookmark
|
||||
image = UIImage(systemName: "bookmark")
|
||||
}
|
||||
let action = LabeledAction(title: title, image: image) { [weak delegate] in
|
||||
|
@ -134,7 +135,7 @@ extension MastodonMenu {
|
|||
return action
|
||||
case .shareStatus:
|
||||
let action = LabeledAction(
|
||||
title: "Share", // TODO: i18n
|
||||
title: L10n.Common.Controls.Actions.sharePost,
|
||||
image: UIImage(systemName: "square.and.arrow.up")
|
||||
) { [weak delegate] in
|
||||
guard let delegate = delegate else { return }
|
||||
|
@ -161,6 +162,16 @@ extension MastodonMenu {
|
|||
delegate.menuAction(self)
|
||||
}
|
||||
return translateAction
|
||||
case .showOriginal:
|
||||
let action = LabeledAction(
|
||||
title: L10n.Common.Controls.Status.Translation.showOriginal,
|
||||
image: UIImage(systemName: "character.book.closed")
|
||||
) { [weak delegate] in
|
||||
guard let delegate = delegate else { return }
|
||||
delegate.menuAction(self)
|
||||
}
|
||||
|
||||
return action
|
||||
case .editStatus:
|
||||
let editStatusAction = LabeledAction(
|
||||
title: L10n.Common.Controls.Actions.editPost,
|
||||
|
@ -172,6 +183,22 @@ extension MastodonMenu {
|
|||
}
|
||||
|
||||
return editStatusAction
|
||||
case .followUser(let context):
|
||||
let title: String
|
||||
let image: UIImage?
|
||||
if context.isFollowing {
|
||||
title = L10n.Common.Controls.Actions.unfollow(context.name)
|
||||
image = UIImage(systemName: "person.fill.badge.minus")
|
||||
} else {
|
||||
title = L10n.Common.Controls.Actions.follow(context.name)
|
||||
image = UIImage(systemName: "person.fill.badge.plus")
|
||||
}
|
||||
let action = LabeledAction(title: title, image: image) { [weak delegate] in
|
||||
guard let delegate = delegate else { return }
|
||||
delegate.menuAction(self)
|
||||
}
|
||||
return action
|
||||
|
||||
} // end switch
|
||||
} // end func build
|
||||
} // end enum Action
|
||||
|
@ -237,4 +264,15 @@ extension MastodonMenu {
|
|||
self.language = language
|
||||
}
|
||||
}
|
||||
|
||||
public struct FollowUserActionContext {
|
||||
|
||||
public let name: String
|
||||
public let isFollowing: Bool
|
||||
|
||||
init(name: String, isFollowing: Bool) {
|
||||
self.name = name
|
||||
self.isFollowing = isFollowing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue