2
2
mirror of https://github.com/mastodon/mastodon-ios synced 2025-04-11 22:58:02 +02:00

Update Post Menu (IOS-229) (#1292)

* Add missing actions, hack way to inject options into menu (IOS-229)

* Implement new menu structure (IOS-229)

This doesn't work-work yet, as functionality isn't implemented yet. Also: Special options and traits.

* Add preferredElementSize to submenus (IOS-229)

* Copy Link (IOS-229)

* Open In Browser (IOS-229)

* Boost status from menu (IOS-229)

* Favorite status from menu (IOS-229)

* Fix following-status in menu and don't cache menu (IOS-229)

* Add some destruction, add localization and use "Boost" instead of "Reblog" (IOS-229)

* Use struct instead of tuple for those menu-parts (IOS-229)
This commit is contained in:
Nathan Mattes 2024-05-18 14:30:51 +02:00 committed by GitHub
parent 14393a26df
commit 8d3cb2beb3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 304 additions and 203 deletions

View File

@ -143,9 +143,9 @@
"next_status": "Next Post",
"open_status": "Open Post",
"open_author_profile": "Open Author's Profile",
"open_reblogger_profile": "Open Reblogger's Profile",
"open_reblogger_profile": "Open Booster's Profile",
"reply_status": "Reply to Post",
"toggle_reblog": "Toggle Reblog on Post",
"toggle_reblog": "Toggle Boost on Post",
"toggle_favorite": "Toggle Favorite on Post",
"toggle_content_warning": "Toggle Content Warning",
"preview_image": "Preview Image"
@ -156,7 +156,7 @@
}
},
"status": {
"user_reblogged": "%s reblogged",
"user_reblogged": "%s boosted",
"user_replied_to": "Replied to %s",
"show_post": "Show Post",
"show_user_profile": "Show user profile",
@ -178,8 +178,8 @@
},
"actions": {
"reply": "Reply",
"reblog": "Reblog",
"unreblog": "Undo reblog",
"reblog": "Boost",
"unreblog": "Undo boost",
"favorite": "Favorite",
"unfavorite": "Unfavorite",
"menu": "Menu",
@ -190,9 +190,10 @@
"share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Tap then hold to show menu",
"a11y_labels": {
"reblog": "Re-blog",
"unreblog": "Undo re-blog"
}
"reblog": "Boost",
"unreblog": "Undo boost"
},
"copy_link": "Copy Link"
},
"tag": {
"url": "URL",
@ -222,7 +223,7 @@
},
"posted_via_application": "%s via %s",
"buttons": {
"reblogs_title": "Reblogs",
"reblogs_title": "Boosts",
"favorites_title": "Favorites",
"edit_history_title": "Edit History",
"edit_history_detail": "Last edit %s"
@ -251,8 +252,8 @@
"unmute_user": "Unmute %s",
"muted": "Muted",
"edit_info": "Edit Info",
"show_reblogs": "Show Reblogs",
"hide_reblogs": "Hide Reblogs"
"show_reblogs": "Show Boosts",
"hide_reblogs": "Hide Boosts"
},
"timeline": {
"filtered": "Filtered",
@ -616,12 +617,12 @@
"message": "Confirm to unblock %s"
},
"confirm_show_reblogs": {
"title": "Show Reblogs",
"message": "Confirm to show reblogs"
"title": "Show Boosts",
"message": "Confirm to show boosts"
},
"confirm_hide_reblogs": {
"title": "Hide Reblogs",
"message": "Confirm to hide reblogs"
"title": "Hide Boosts",
"message": "Confirm to hide boosts"
},
"confirm_block_domain": {
"title": "Block domain",
@ -655,7 +656,7 @@
"title": "Favorited By"
},
"reblogged_by": {
"title": "Reblogged By"
"title": "Boosted By"
},
"search": {
"title": "Search",
@ -715,7 +716,7 @@
"notification_description": {
"followed_you": "followed you",
"favorited_your_post": "favorited your post",
"reblogged_your_post": "reblogged your post",
"reblogged_your_post": "boosted your post",
"mentioned_you": "mentioned you",
"request_to_follow_you": "request to follow you",
"poll_has_ended": "poll has ended"
@ -871,7 +872,7 @@
"unfollowed": "Unfollowed",
"unfollow_user": "Unfollow %s",
"mute_user": "Mute %s",
"you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "You wont see their posts or reblogs in your home feed. They wont know theyve been muted.",
"you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "You wont see their posts or boosts in your home feed. They wont know theyve been muted.",
"block_user": "Block %s",
"they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "They will no longer be able to follow or see your posts, but they can see if theyve been blocked.",
"while_we_review_this_you_can_take_action_against_user": "While we review this, you can take action against %s"

View File

@ -143,9 +143,9 @@
"next_status": "Next Post",
"open_status": "Open Post",
"open_author_profile": "Open Author's Profile",
"open_reblogger_profile": "Open Reblogger's Profile",
"open_reblogger_profile": "Open Booster's Profile",
"reply_status": "Reply to Post",
"toggle_reblog": "Toggle Reblog on Post",
"toggle_reblog": "Toggle Boost on Post",
"toggle_favorite": "Toggle Favorite on Post",
"toggle_content_warning": "Toggle Content Warning",
"preview_image": "Preview Image"
@ -156,7 +156,7 @@
}
},
"status": {
"user_reblogged": "%s reblogged",
"user_reblogged": "%s boosted",
"user_replied_to": "Replied to %s",
"show_post": "Show Post",
"show_user_profile": "Show user profile",
@ -178,8 +178,8 @@
},
"actions": {
"reply": "Reply",
"reblog": "Reblog",
"unreblog": "Undo reblog",
"reblog": "Boost",
"unreblog": "Undo boost",
"favorite": "Favorite",
"unfavorite": "Unfavorite",
"menu": "Menu",
@ -190,9 +190,10 @@
"share_link_in_post": "Share Link in Post",
"tap_then_hold_to_show_menu": "Tap then hold to show menu",
"a11y_labels": {
"reblog": "Re-blog",
"unreblog": "Undo re-blog"
}
"reblog": "Boost",
"unreblog": "Undo boost"
},
"copy_link": "Copy Link"
},
"tag": {
"url": "URL",
@ -222,7 +223,7 @@
},
"posted_via_application": "%s via %s",
"buttons": {
"reblogs_title": "Reblogs",
"reblogs_title": "Boosts",
"favorites_title": "Favorites",
"edit_history_title": "Edit History",
"edit_history_detail": "Last edit %s"
@ -251,8 +252,8 @@
"unmute_user": "Unmute %s",
"muted": "Muted",
"edit_info": "Edit Info",
"show_reblogs": "Show Reblogs",
"hide_reblogs": "Hide Reblogs"
"show_reblogs": "Show Boosts",
"hide_reblogs": "Hide Boosts"
},
"timeline": {
"filtered": "Filtered",
@ -616,12 +617,12 @@
"message": "Confirm to unblock %s"
},
"confirm_show_reblogs": {
"title": "Show Reblogs",
"message": "Confirm to show reblogs"
"title": "Show Boosts",
"message": "Confirm to show boosts"
},
"confirm_hide_reblogs": {
"title": "Hide Reblogs",
"message": "Confirm to hide reblogs"
"title": "Hide Boosts",
"message": "Confirm to hide boosts"
},
"confirm_block_domain": {
"title": "Block domain",
@ -655,7 +656,7 @@
"title": "Favorited By"
},
"reblogged_by": {
"title": "Reblogged By"
"title": "Boosted By"
},
"search": {
"title": "Search",
@ -715,7 +716,7 @@
"notification_description": {
"followed_you": "followed you",
"favorited_your_post": "favorited your post",
"reblogged_your_post": "reblogged your post",
"reblogged_your_post": "boosted your post",
"mentioned_you": "mentioned you",
"request_to_follow_you": "request to follow you",
"poll_has_ended": "poll has ended"
@ -871,7 +872,7 @@
"unfollowed": "Unfollowed",
"unfollow_user": "Unfollow %s",
"mute_user": "Mute %s",
"you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "You wont see their posts or reblogs in your home feed. They wont know theyve been muted.",
"you_wont_see_their_posts_or_reblogs_in_your_home_feed_they_wont_know_they_ve_been_muted": "You wont see their posts or boosts in your home feed. They wont know theyve been muted.",
"block_user": "Block %s",
"they_will_no_longer_be_able_to_follow_or_see_your_posts_but_they_can_see_if_theyve_been_blocked": "They will no longer be able to follow or see your posts, but they can see if theyve been blocked.",
"while_we_review_this_you_can_take_action_against_user": "While we review this, you can take action against %s"

View File

@ -384,6 +384,38 @@ extension DataSourceFacade {
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel)
alertController.addAction(cancelAction)
dependency.present(alertController, animated: true)
case .boostStatus(_):
guard let status: MastodonStatus = menuContext.statusViewModel?.originalStatus?.reblog ?? menuContext.statusViewModel?.originalStatus else {
assertionFailure()
return
}
try await responseToStatusReblogAction(provider: dependency, status: status)
case .favoriteStatus(_):
guard let status: MastodonStatus = menuContext.statusViewModel?.originalStatus?.reblog ?? menuContext.statusViewModel?.originalStatus else {
assertionFailure()
return
}
try await responseToStatusFavoriteAction(provider: dependency, status: status)
case .copyLink:
guard let status: MastodonStatus = menuContext.statusViewModel?.originalStatus?.reblog ?? menuContext.statusViewModel?.originalStatus else {
assertionFailure()
return
}
UIPasteboard.general.string = status.entity.url
case .openInBrowser:
guard
let status: MastodonStatus = menuContext.statusViewModel?.originalStatus?.reblog ?? menuContext.statusViewModel?.originalStatus,
let urlString = status.entity.url,
let url = URL(string: urlString)
else {
assertionFailure()
return
}
dependency.coordinator.present(scene: .safari(url: url), transition: .safariPresent(animated: true))
}
}
}

View File

@ -61,9 +61,10 @@ extension NotificationTableViewCellDelegate where Self: DataSourceProvider & Aut
barButtonItem: nil
)
)
case .translateStatus(_), .showOriginal, .shareUser(_), .blockDomain(_), .bookmarkStatus(_), .hideReblogs(_), .shareStatus, .deleteStatus, .editStatus, .followUser(_):
case .translateStatus(_), .showOriginal, .shareUser(_), .blockDomain(_), .bookmarkStatus(_), .hideReblogs(_), .shareStatus, .deleteStatus, .editStatus, .followUser(_), .boostStatus(_), .favoriteStatus(_), .copyLink, .openInBrowser:
// Do Nothing
break
}
}
}

View File

@ -461,37 +461,26 @@ extension NotificationView {
}
public func setupAuthorMenu(menuContext: AuthorMenuContext) -> (UIMenu, [UIAccessibilityCustomAction]) {
var actions: [[MastodonMenu.Action]] = []
var upperActions: [MastodonMenu.Action] = []
upperActions = [
.muteUser(.init(
name: menuContext.name,
isMuting: menuContext.isMuting
)),
.blockUser(.init(
name: menuContext.name,
isBlocking: menuContext.isBlocking
)),
.reportUser(
.init(name: menuContext.name)
var items = [
MastodonMenu.Submenu(actions: [
.muteUser(.init(name: menuContext.name,isMuting: menuContext.isMuting)),
.blockUser(.init(name: menuContext.name,isBlocking: menuContext.isBlocking)),
.reportUser(.init(name: menuContext.name))]
)
]
actions.append(upperActions)
if menuContext.isMyself {
actions.append([.deleteStatus])
items.append(MastodonMenu.Submenu(actions: [.deleteStatus]))
}
let menu = MastodonMenu.setupMenu(
actions: actions,
submenus: items,
delegate: self
)
let accessibilityActions = MastodonMenu.setupAccessibilityActions(
actions: actions,
actions: items.compactMap { $0.actions } ,
delegate: self
)

View File

@ -451,7 +451,7 @@ extension ProfileViewController {
}
let menu = MastodonMenu.setupMenu(
actions: [menuActions],
submenus: [MastodonMenu.Submenu(actions: menuActions)],
delegate: self
)
return menu
@ -929,42 +929,36 @@ extension ProfileViewController: ProfileAboutViewControllerDelegate {
extension ProfileViewController: MastodonMenuDelegate {
func menuAction(_ action: MastodonMenu.Action) {
switch action {
case .muteUser(_),
.blockUser(_),
.blockDomain(_),
.hideReblogs(_):
Task {
try await DataSourceFacade.responseToMenuAction(
dependency: self,
action: action,
menuContext: DataSourceFacade.MenuContext(
author: viewModel.account,
statusViewModel: nil,
button: nil,
barButtonItem: self.moreMenuBarButtonItem
))
}
case .reportUser(_), .shareUser(_):
Task {
try await DataSourceFacade.responseToMenuAction(
dependency: self,
action: action,
menuContext: DataSourceFacade.MenuContext(
author: viewModel.account,
statusViewModel: nil,
button: nil,
barButtonItem: self.moreMenuBarButtonItem
))
}
case .muteUser(_),
.blockUser(_),
.blockDomain(_),
.hideReblogs(_):
Task {
try await DataSourceFacade.responseToMenuAction(
dependency: self,
action: action,
menuContext: DataSourceFacade.MenuContext(
author: viewModel.account,
statusViewModel: nil,
button: nil,
barButtonItem: self.moreMenuBarButtonItem
))
}
case .reportUser(_), .shareUser(_):
Task {
try await DataSourceFacade.responseToMenuAction(
dependency: self,
action: action,
menuContext: DataSourceFacade.MenuContext(
author: viewModel.account,
statusViewModel: nil,
button: nil,
barButtonItem: self.moreMenuBarButtonItem
))
}
case .translateStatus(_),
.showOriginal,
.bookmarkStatus(_),
.shareStatus,
.deleteStatus,
.editStatus,
.followUser(_):
break
case .translateStatus(_), .showOriginal, .bookmarkStatus(_), .shareStatus, .deleteStatus, .editStatus, .followUser(_), .boostStatus(_), .favoriteStatus(_), .copyLink, .openInBrowser:
break
}
}
}

View File

@ -259,8 +259,8 @@ public enum L10n {
public static let follow = L10n.tr("Localizable", "Common.Controls.Friendship.Follow", fallback: "Follow")
/// Following
public static let following = L10n.tr("Localizable", "Common.Controls.Friendship.Following", fallback: "Following")
/// Hide Reblogs
public static let hideReblogs = L10n.tr("Localizable", "Common.Controls.Friendship.HideReblogs", fallback: "Hide Reblogs")
/// Hide Boosts
public static let hideReblogs = L10n.tr("Localizable", "Common.Controls.Friendship.HideReblogs", fallback: "Hide Boosts")
/// Mute
public static let mute = L10n.tr("Localizable", "Common.Controls.Friendship.Mute", fallback: "Mute")
/// Muted
@ -273,8 +273,8 @@ public enum L10n {
public static let pending = L10n.tr("Localizable", "Common.Controls.Friendship.Pending", fallback: "Pending")
/// Request
public static let request = L10n.tr("Localizable", "Common.Controls.Friendship.Request", fallback: "Request")
/// Show Reblogs
public static let showReblogs = L10n.tr("Localizable", "Common.Controls.Friendship.ShowReblogs", fallback: "Show Reblogs")
/// Show Boosts
public static let showReblogs = L10n.tr("Localizable", "Common.Controls.Friendship.ShowReblogs", fallback: "Show Boosts")
/// Unblock
public static let unblock = L10n.tr("Localizable", "Common.Controls.Friendship.Unblock", fallback: "Unblock")
/// Unblock %@
@ -312,8 +312,8 @@ public enum L10n {
public static let nextStatus = L10n.tr("Localizable", "Common.Controls.Keyboard.Timeline.NextStatus", fallback: "Next Post")
/// Open Author's Profile
public static let openAuthorProfile = L10n.tr("Localizable", "Common.Controls.Keyboard.Timeline.OpenAuthorProfile", fallback: "Open Author's Profile")
/// Open Reblogger's Profile
public static let openRebloggerProfile = L10n.tr("Localizable", "Common.Controls.Keyboard.Timeline.OpenRebloggerProfile", fallback: "Open Reblogger's Profile")
/// Open Booster's Profile
public static let openRebloggerProfile = L10n.tr("Localizable", "Common.Controls.Keyboard.Timeline.OpenRebloggerProfile", fallback: "Open Booster's Profile")
/// Open Post
public static let openStatus = L10n.tr("Localizable", "Common.Controls.Keyboard.Timeline.OpenStatus", fallback: "Open Post")
/// Preview Image
@ -326,8 +326,8 @@ public enum L10n {
public static let toggleContentWarning = L10n.tr("Localizable", "Common.Controls.Keyboard.Timeline.ToggleContentWarning", fallback: "Toggle Content Warning")
/// Toggle Favorite on Post
public static let toggleFavorite = L10n.tr("Localizable", "Common.Controls.Keyboard.Timeline.ToggleFavorite", fallback: "Toggle Favorite on Post")
/// Toggle Reblog on Post
public static let toggleReblog = L10n.tr("Localizable", "Common.Controls.Keyboard.Timeline.ToggleReblog", fallback: "Toggle Reblog on Post")
/// Toggle Boost on Post
public static let toggleReblog = L10n.tr("Localizable", "Common.Controls.Keyboard.Timeline.ToggleReblog", fallback: "Toggle Boost on Post")
}
}
public enum Status {
@ -357,23 +357,25 @@ public enum L10n {
public static let showUserProfile = L10n.tr("Localizable", "Common.Controls.Status.ShowUserProfile", fallback: "Show user profile")
/// Tap to reveal
public static let tapToReveal = L10n.tr("Localizable", "Common.Controls.Status.TapToReveal", fallback: "Tap to reveal")
/// %@ reblogged
/// %@ boosted
public static func userReblogged(_ p1: Any) -> String {
return L10n.tr("Localizable", "Common.Controls.Status.UserReblogged", String(describing: p1), fallback: "%@ reblogged")
return L10n.tr("Localizable", "Common.Controls.Status.UserReblogged", String(describing: p1), fallback: "%@ boosted")
}
/// Replied to %@
public static func userRepliedTo(_ p1: Any) -> String {
return L10n.tr("Localizable", "Common.Controls.Status.UserRepliedTo", String(describing: p1), fallback: "Replied to %@")
}
public enum Actions {
/// Copy Link
public static let copyLink = L10n.tr("Localizable", "Common.Controls.Status.Actions.CopyLink", fallback: "Copy Link")
/// Favorite
public static let favorite = L10n.tr("Localizable", "Common.Controls.Status.Actions.Favorite", fallback: "Favorite")
/// Hide
public static let hide = L10n.tr("Localizable", "Common.Controls.Status.Actions.Hide", fallback: "Hide")
/// Menu
public static let menu = L10n.tr("Localizable", "Common.Controls.Status.Actions.Menu", fallback: "Menu")
/// Reblog
public static let reblog = L10n.tr("Localizable", "Common.Controls.Status.Actions.Reblog", fallback: "Reblog")
/// Boost
public static let reblog = L10n.tr("Localizable", "Common.Controls.Status.Actions.Reblog", fallback: "Boost")
/// Reply
public static let reply = L10n.tr("Localizable", "Common.Controls.Status.Actions.Reply", fallback: "Reply")
/// Share Link in Post
@ -388,13 +390,13 @@ public enum L10n {
public static let tapThenHoldToShowMenu = L10n.tr("Localizable", "Common.Controls.Status.Actions.TapThenHoldToShowMenu", fallback: "Tap then hold to show menu")
/// Unfavorite
public static let unfavorite = L10n.tr("Localizable", "Common.Controls.Status.Actions.Unfavorite", fallback: "Unfavorite")
/// Undo reblog
public static let unreblog = L10n.tr("Localizable", "Common.Controls.Status.Actions.Unreblog", fallback: "Undo reblog")
/// Undo boost
public static let unreblog = L10n.tr("Localizable", "Common.Controls.Status.Actions.Unreblog", fallback: "Undo boost")
public enum A11YLabels {
/// Re-blog
public static let reblog = L10n.tr("Localizable", "Common.Controls.Status.Actions.A11YLabels.Reblog", fallback: "Re-blog")
/// Undo re-blog
public static let unreblog = L10n.tr("Localizable", "Common.Controls.Status.Actions.A11YLabels.Unreblog", fallback: "Undo re-blog")
/// Boost
public static let reblog = L10n.tr("Localizable", "Common.Controls.Status.Actions.A11YLabels.Reblog", fallback: "Boost")
/// Undo boost
public static let unreblog = L10n.tr("Localizable", "Common.Controls.Status.Actions.A11YLabels.Unreblog", fallback: "Undo boost")
}
}
public enum Buttons {
@ -406,8 +408,8 @@ public enum L10n {
public static let editHistoryTitle = L10n.tr("Localizable", "Common.Controls.Status.Buttons.EditHistoryTitle", fallback: "Edit History")
/// Favorites
public static let favoritesTitle = L10n.tr("Localizable", "Common.Controls.Status.Buttons.FavoritesTitle", fallback: "Favorites")
/// Reblogs
public static let reblogsTitle = L10n.tr("Localizable", "Common.Controls.Status.Buttons.ReblogsTitle", fallback: "Reblogs")
/// Boosts
public static let reblogsTitle = L10n.tr("Localizable", "Common.Controls.Status.Buttons.ReblogsTitle", fallback: "Boosts")
}
public enum EditHistory {
/// Original Post · %@
@ -898,8 +900,8 @@ public enum L10n {
public static let mentionedYou = L10n.tr("Localizable", "Scene.Notification.NotificationDescription.MentionedYou", fallback: "mentioned you")
/// poll has ended
public static let pollHasEnded = L10n.tr("Localizable", "Scene.Notification.NotificationDescription.PollHasEnded", fallback: "poll has ended")
/// reblogged your post
public static let rebloggedYourPost = L10n.tr("Localizable", "Scene.Notification.NotificationDescription.RebloggedYourPost", fallback: "reblogged your post")
/// boosted your post
public static let rebloggedYourPost = L10n.tr("Localizable", "Scene.Notification.NotificationDescription.RebloggedYourPost", fallback: "boosted your post")
/// request to follow you
public static let requestToFollowYou = L10n.tr("Localizable", "Scene.Notification.NotificationDescription.RequestToFollowYou", fallback: "request to follow you")
}
@ -1027,10 +1029,10 @@ public enum L10n {
public static let title = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmBlockUser.Title", fallback: "Block Account")
}
public enum ConfirmHideReblogs {
/// Confirm to hide reblogs
public static let message = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.Message", fallback: "Confirm to hide reblogs")
/// Hide Reblogs
public static let title = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.Title", fallback: "Hide Reblogs")
/// Confirm to hide boosts
public static let message = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.Message", fallback: "Confirm to hide boosts")
/// Hide Boosts
public static let title = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.Title", fallback: "Hide Boosts")
}
public enum ConfirmMuteUser {
/// Confirm to mute %@
@ -1041,10 +1043,10 @@ public enum L10n {
public static let title = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmMuteUser.Title", fallback: "Mute Account")
}
public enum ConfirmShowReblogs {
/// Confirm to show reblogs
public static let message = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmShowReblogs.Message", fallback: "Confirm to show reblogs")
/// Show Reblogs
public static let title = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmShowReblogs.Title", fallback: "Show Reblogs")
/// Confirm to show boosts
public static let message = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmShowReblogs.Message", fallback: "Confirm to show boosts")
/// Show Boosts
public static let title = L10n.tr("Localizable", "Scene.Profile.RelationshipActionAlert.ConfirmShowReblogs.Title", fallback: "Show Boosts")
}
public enum ConfirmUnblockDomain {
/// Confirm to unblock domain %@
@ -1085,8 +1087,8 @@ public enum L10n {
}
}
public enum RebloggedBy {
/// Reblogged By
public static let title = L10n.tr("Localizable", "Scene.RebloggedBy.Title", fallback: "Reblogged By")
/// Boosted By
public static let title = L10n.tr("Localizable", "Scene.RebloggedBy.Title", fallback: "Boosted By")
}
public enum Register {
/// Create Account
@ -1258,8 +1260,8 @@ public enum L10n {
public static func whileWeReviewThisYouCanTakeActionAgainstUser(_ p1: Any) -> String {
return L10n.tr("Localizable", "Scene.Report.StepFinal.WhileWeReviewThisYouCanTakeActionAgainstUser", String(describing: p1), fallback: "While we review this, you can take action against %@")
}
/// You wont see their posts or reblogs in your home feed. They wont know theyve been muted.
public static let youWontSeeTheirPostsOrReblogsInYourHomeFeedTheyWontKnowTheyVeBeenMuted = L10n.tr("Localizable", "Scene.Report.StepFinal.YouWontSeeTheirPostsOrReblogsInYourHomeFeedTheyWontKnowTheyVeBeenMuted", fallback: "You wont see their posts or reblogs in your home feed. They wont know theyve been muted.")
/// You wont see their posts or boosts in your home feed. They wont know theyve been muted.
public static let youWontSeeTheirPostsOrReblogsInYourHomeFeedTheyWontKnowTheyVeBeenMuted = L10n.tr("Localizable", "Scene.Report.StepFinal.YouWontSeeTheirPostsOrReblogsInYourHomeFeedTheyWontKnowTheyVeBeenMuted", fallback: "You wont see their posts or boosts in your home feed. They wont know theyve been muted.")
}
public enum StepFour {
/// Is there anything else we should know?

View File

@ -90,13 +90,13 @@ Please check your internet connection.";
"Common.Controls.Friendship.EditInfo" = "Edit Info";
"Common.Controls.Friendship.Follow" = "Follow";
"Common.Controls.Friendship.Following" = "Following";
"Common.Controls.Friendship.HideReblogs" = "Hide Reblogs";
"Common.Controls.Friendship.HideReblogs" = "Hide Boosts";
"Common.Controls.Friendship.Mute" = "Mute";
"Common.Controls.Friendship.MuteUser" = "Mute %@";
"Common.Controls.Friendship.Muted" = "Muted";
"Common.Controls.Friendship.Pending" = "Pending";
"Common.Controls.Friendship.Request" = "Request";
"Common.Controls.Friendship.ShowReblogs" = "Show Reblogs";
"Common.Controls.Friendship.ShowReblogs" = "Show Boosts";
"Common.Controls.Friendship.Unblock" = "Unblock";
"Common.Controls.Friendship.UnblockUser" = "Unblock %@";
"Common.Controls.Friendship.Unmute" = "Unmute";
@ -109,20 +109,21 @@ Please check your internet connection.";
"Common.Controls.Keyboard.SegmentedControl.PreviousSection" = "Previous Section";
"Common.Controls.Keyboard.Timeline.NextStatus" = "Next Post";
"Common.Controls.Keyboard.Timeline.OpenAuthorProfile" = "Open Author's Profile";
"Common.Controls.Keyboard.Timeline.OpenRebloggerProfile" = "Open Reblogger's Profile";
"Common.Controls.Keyboard.Timeline.OpenRebloggerProfile" = "Open Booster's Profile";
"Common.Controls.Keyboard.Timeline.OpenStatus" = "Open Post";
"Common.Controls.Keyboard.Timeline.PreviewImage" = "Preview Image";
"Common.Controls.Keyboard.Timeline.PreviousStatus" = "Previous Post";
"Common.Controls.Keyboard.Timeline.ReplyStatus" = "Reply to Post";
"Common.Controls.Keyboard.Timeline.ToggleContentWarning" = "Toggle Content Warning";
"Common.Controls.Keyboard.Timeline.ToggleFavorite" = "Toggle Favorite on Post";
"Common.Controls.Keyboard.Timeline.ToggleReblog" = "Toggle Reblog on Post";
"Common.Controls.Status.Actions.A11YLabels.Reblog" = "Re-blog";
"Common.Controls.Status.Actions.A11YLabels.Unreblog" = "Undo re-blog";
"Common.Controls.Keyboard.Timeline.ToggleReblog" = "Toggle Boost on Post";
"Common.Controls.Status.Actions.A11YLabels.Reblog" = "Boost";
"Common.Controls.Status.Actions.A11YLabels.Unreblog" = "Undo boost";
"Common.Controls.Status.Actions.CopyLink" = "Copy Link";
"Common.Controls.Status.Actions.Favorite" = "Favorite";
"Common.Controls.Status.Actions.Hide" = "Hide";
"Common.Controls.Status.Actions.Menu" = "Menu";
"Common.Controls.Status.Actions.Reblog" = "Reblog";
"Common.Controls.Status.Actions.Reblog" = "Boost";
"Common.Controls.Status.Actions.Reply" = "Reply";
"Common.Controls.Status.Actions.ShareLinkInPost" = "Share Link in Post";
"Common.Controls.Status.Actions.ShowGif" = "Show GIF";
@ -130,11 +131,11 @@ Please check your internet connection.";
"Common.Controls.Status.Actions.ShowVideoPlayer" = "Show video player";
"Common.Controls.Status.Actions.TapThenHoldToShowMenu" = "Tap then hold to show menu";
"Common.Controls.Status.Actions.Unfavorite" = "Unfavorite";
"Common.Controls.Status.Actions.Unreblog" = "Undo reblog";
"Common.Controls.Status.Actions.Unreblog" = "Undo boost";
"Common.Controls.Status.Buttons.EditHistoryDetail" = "Last edit %@";
"Common.Controls.Status.Buttons.EditHistoryTitle" = "Edit History";
"Common.Controls.Status.Buttons.FavoritesTitle" = "Favorites";
"Common.Controls.Status.Buttons.ReblogsTitle" = "Reblogs";
"Common.Controls.Status.Buttons.ReblogsTitle" = "Boosts";
"Common.Controls.Status.ContentWarning" = "Content Warning";
"Common.Controls.Status.EditHistory.OriginalPost" = "Original Post · %@";
"Common.Controls.Status.EditHistory.Title" = "Edit History";
@ -167,7 +168,7 @@ Please check your internet connection.";
"Common.Controls.Status.Translation.TranslatedFrom" = "Translated from %@ using %@";
"Common.Controls.Status.Translation.UnknownLanguage" = "Unknown";
"Common.Controls.Status.Translation.UnknownProvider" = "Unknown";
"Common.Controls.Status.UserReblogged" = "%@ reblogged";
"Common.Controls.Status.UserReblogged" = "%@ boosted";
"Common.Controls.Status.UserRepliedTo" = "Replied to %@";
"Common.Controls.Status.Visibility.Direct" = "Only mentioned user can see this post.";
"Common.Controls.Status.Visibility.Private" = "Only their followers can see this post.";
@ -319,7 +320,7 @@ uploaded to Mastodon.";
"Scene.Notification.NotificationDescription.FollowedYou" = "followed you";
"Scene.Notification.NotificationDescription.MentionedYou" = "mentioned you";
"Scene.Notification.NotificationDescription.PollHasEnded" = "poll has ended";
"Scene.Notification.NotificationDescription.RebloggedYourPost" = "reblogged your post";
"Scene.Notification.NotificationDescription.RebloggedYourPost" = "boosted your post";
"Scene.Notification.NotificationDescription.RequestToFollowYou" = "request to follow you";
"Scene.Notification.Title.Everything" = "Everything";
"Scene.Notification.Title.Mentions" = "Mentions";
@ -361,12 +362,12 @@ uploaded to Mastodon.";
"Scene.Profile.RelationshipActionAlert.ConfirmBlockDomain.Title" = "Block domain";
"Scene.Profile.RelationshipActionAlert.ConfirmBlockUser.Message" = "Confirm to block %@";
"Scene.Profile.RelationshipActionAlert.ConfirmBlockUser.Title" = "Block Account";
"Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.Message" = "Confirm to hide reblogs";
"Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.Title" = "Hide Reblogs";
"Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.Message" = "Confirm to hide boosts";
"Scene.Profile.RelationshipActionAlert.ConfirmHideReblogs.Title" = "Hide Boosts";
"Scene.Profile.RelationshipActionAlert.ConfirmMuteUser.Message" = "Confirm to mute %@";
"Scene.Profile.RelationshipActionAlert.ConfirmMuteUser.Title" = "Mute Account";
"Scene.Profile.RelationshipActionAlert.ConfirmShowReblogs.Message" = "Confirm to show reblogs";
"Scene.Profile.RelationshipActionAlert.ConfirmShowReblogs.Title" = "Show Reblogs";
"Scene.Profile.RelationshipActionAlert.ConfirmShowReblogs.Message" = "Confirm to show boosts";
"Scene.Profile.RelationshipActionAlert.ConfirmShowReblogs.Title" = "Show Boosts";
"Scene.Profile.RelationshipActionAlert.ConfirmUnblockDomain.Message" = "Confirm to unblock domain %@";
"Scene.Profile.RelationshipActionAlert.ConfirmUnblockDomain.Title" = "Unblock domain";
"Scene.Profile.RelationshipActionAlert.ConfirmUnblockUser.Message" = "Confirm to unblock %@";
@ -378,7 +379,7 @@ uploaded to Mastodon.";
"Scene.Profile.SegmentedControl.Posts" = "Posts";
"Scene.Profile.SegmentedControl.PostsAndReplies" = "Posts and Replies";
"Scene.Profile.SegmentedControl.Replies" = "Replies";
"Scene.RebloggedBy.Title" = "Reblogged By";
"Scene.RebloggedBy.Title" = "Boosted By";
"Scene.Register.Error.Item.Agreement" = "Agreement";
"Scene.Register.Error.Item.Email" = "Email";
"Scene.Register.Error.Item.Locale" = "Locale";
@ -431,7 +432,7 @@ uploaded to Mastodon.";
"Scene.Report.StepFinal.Unfollowed" = "Unfollowed";
"Scene.Report.StepFinal.WhenYouSeeSomethingYouDontLikeOnMastodonYouCanRemoveThePersonFromYourExperience." = "When you see something you dont like on Mastodon, you can remove the person from your experience.";
"Scene.Report.StepFinal.WhileWeReviewThisYouCanTakeActionAgainstUser" = "While we review this, you can take action against %@";
"Scene.Report.StepFinal.YouWontSeeTheirPostsOrReblogsInYourHomeFeedTheyWontKnowTheyVeBeenMuted" = "You wont see their posts or reblogs in your home feed. They wont know theyve been muted.";
"Scene.Report.StepFinal.YouWontSeeTheirPostsOrReblogsInYourHomeFeedTheyWontKnowTheyVeBeenMuted" = "You wont see their posts or boosts in your home feed. They wont know theyve been muted.";
"Scene.Report.StepFour.IsThereAnythingElseWeShouldKnow" = "Is there anything else we should know?";
"Scene.Report.StepFour.Step4Of4" = "Step 4 of 4";
"Scene.Report.StepOne.IDontLikeIt" = "I dont like it";

View File

@ -147,73 +147,68 @@ extension StatusAuthorView {
public let isMuting: Bool
public let isBlocking: Bool
public let isMyself: Bool
public let isBookmarking: Bool
public let isBookmarked: Bool
public let isFollowed: Bool
public let isTranslationEnabled: Bool
public let isTranslated: Bool
public let statusLanguage: String?
public let isFavorited: Bool
public let isBoosted: Bool
}
public func setupAuthorMenu(menuContext: AuthorMenuContext) -> (UIMenu, [UIAccessibilityCustomAction]) {
var actions: [[MastodonMenu.Action]] = []
var postActions: [MastodonMenu.Action] = []
var userActions: [MastodonMenu.Action] = []
var items: [MastodonMenu.Submenu] = []
items.append(MastodonMenu.Submenu(
actions: [
.boostStatus(.init(isBoosted: menuContext.isBoosted)),
.favoriteStatus(.init(isFavorited: menuContext.isFavorited)),
.bookmarkStatus(.init(isBookmarked: menuContext.isBookmarked)),
],
preferredElementSize: .medium
))
if menuContext.isMyself {
postActions.append(.editStatus)
}
items.append(MastodonMenu.Submenu(actions: [.editStatus]))
} else if menuContext.isTranslationEnabled,
let statusLanguage = menuContext.statusLanguage,
let deviceLanguage = Bundle.main.preferredLocalizations.first,
deviceLanguage != statusLanguage {
let action: MastodonMenu.Action
if menuContext.isTranslationEnabled,
let statusLanguage = menuContext.statusLanguage,
let deviceLanguage = Bundle.main.preferredLocalizations.first,
deviceLanguage != statusLanguage {
if menuContext.isTranslated == false {
postActions.append(.translateStatus(.init(language: statusLanguage)))
action = .translateStatus(.init(language: statusLanguage))
} else {
postActions.append(.showOriginal)
action = .showOriginal
}
items.append(MastodonMenu.Submenu(actions: [action]))
}
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)
items.append(MastodonMenu.Submenu(actions: [.shareStatus, .openInBrowser, .copyLink]))
if menuContext.isMyself {
actions.append([.deleteStatus])
items.append(MastodonMenu.Submenu(actions: [.deleteStatus]))
} else {
items.append(MastodonMenu.Submenu(actions: [
.followUser(.init(name: menuContext.name, isFollowing: menuContext.isFollowed)),
.muteUser(.init( name: menuContext.name, isMuting: menuContext.isMuting))
]))
items.append(MastodonMenu.Submenu(actions: [
.blockUser(.init(name: menuContext.name, isBlocking: menuContext.isBlocking)),
.reportUser(.init(name: menuContext.name))
]))
}
let menu = MastodonMenu.setupMenu(
actions: actions,
submenus: items,
delegate: self.statusView!
)
let accessibilityActions = MastodonMenu.setupAccessibilityActions(
actions: actions,
actions: items.compactMap { $0.actions } ,
delegate: self.statusView!
)

View File

@ -686,13 +686,18 @@ extension StatusView.ViewModel {
$language
)
let publisherTwo = Publishers.CombineLatest3(
$isBookmark, $isFavorite, $isReblog
)
Publishers.CombineLatest3(
publisherOne.eraseToAnyPublisher(),
$isBookmark,
publisherTwo.eraseToAnyPublisher(),
publishersThree.eraseToAnyPublisher()
).eraseToAnyPublisher()
.sink { tupleOne, isBookmark, tupleThree in
.sink { tupleOne, tupleTwo, tupleThree in
let (authorName, authorId, isMyself) = tupleOne
let (isBookmark, isFavorite, isBoosted) = tupleTwo
let (translatedFromLanguage, language) = tupleThree
guard let name = authorName?.string, let authorId = authorId, let context = self.context, let authContext = self.authContext else {
@ -705,8 +710,8 @@ extension StatusView.ViewModel {
let isTranslationEnabled = instance?.isTranslationEnabled ?? false
authorView.menuButton.menu = UIMenu(children: [
UIDeferredMenuElement({ menuElement in
UIDeferredMenuElement.uncached({ menuElement in
let domain = authContext.mastodonAuthenticationBox.domain
Task { @MainActor in
@ -724,11 +729,13 @@ extension StatusView.ViewModel {
isMuting: rel.muting,
isBlocking: rel.blocking,
isMyself: isMyself,
isBookmarking: isBookmark,
isFollowed: rel.followedBy,
isBookmarked: isBookmark,
isFollowed: rel.following,
isTranslationEnabled: isTranslationEnabled,
isTranslated: translatedFromLanguage != nil,
statusLanguage: language
statusLanguage: language,
isFavorited: isFavorite,
isBoosted: isBoosted
)
let (menu, actions) = authorView.setupAuthorMenu(menuContext: menuContext)
@ -737,7 +744,11 @@ extension StatusView.ViewModel {
menuElement(menu.children)
}
} else {
menuElement(MastodonMenu.setupMenu(actions: [[.shareStatus]], delegate: statusView).children)
menuElement(
MastodonMenu.setupMenu(
submenus: [MastodonMenu.Submenu(actions: [.shareStatus])],
delegate: statusView).children
)
}
}
})

View File

@ -13,19 +13,33 @@ public protocol MastodonMenuDelegate: AnyObject {
}
public enum MastodonMenu {
public struct Submenu {
public let actions: [Action]
public let options: UIMenu.Options
public let preferredElementSize: UIMenu.ElementSize
public init(actions: [Action], options: UIMenu.Options = .displayInline, preferredElementSize: UIMenu.ElementSize = .large) {
self.actions = actions
self.options = options
self.preferredElementSize = preferredElementSize
}
}
public static func setupMenu(
actions: [[Action]],
submenus: [Submenu],
delegate: MastodonMenuDelegate
) -> UIMenu {
var children: [UIMenuElement] = []
for actionGroup in actions {
for item in submenus {
var submenuChildren: [UIMenuElement] = []
for action in actionGroup {
for action in item.actions {
let element = action.build(delegate: delegate).menuElement
submenuChildren.append(element)
}
let submenu = UIMenu(options: .displayInline, children: submenuChildren)
let submenu = UIMenu(options: item.options, children: submenuChildren)
submenu.preferredElementSize = item.preferredElementSize
children.append(submenu)
}
@ -60,6 +74,10 @@ extension MastodonMenu {
case editStatus
case followUser(FollowUserActionContext)
case blockDomain(BlockDomainActionContext)
case boostStatus(BoostStatusActionContext)
case favoriteStatus(FavoriteStatusActionContext)
case copyLink
case openInBrowser
func build(delegate: MastodonMenuDelegate) -> LabeledAction {
switch self {
@ -96,7 +114,7 @@ extension MastodonMenu {
title = L10n.Common.Controls.Friendship.blockUser(context.name)
image = UIImage(systemName: "hand.raised")
}
let blockAction = LabeledAction(title: title, image: image) { [weak delegate] in
let blockAction = LabeledAction(title: title, image: image, attributes: .destructive) { [weak delegate] in
guard let delegate = delegate else { return }
delegate.menuAction(self)
}
@ -104,7 +122,8 @@ extension MastodonMenu {
case .reportUser(let context):
let reportAction = LabeledAction(
title: L10n.Common.Controls.Actions.reportUser(context.name),
image: UIImage(systemName: "flag")
image: UIImage(systemName: "flag"),
attributes: .destructive
) { [weak delegate] in
guard let delegate = delegate else { return }
delegate.menuAction(self)
@ -122,7 +141,7 @@ extension MastodonMenu {
case .bookmarkStatus(let context):
let title: String
let image: UIImage?
if context.isBookmarking {
if context.isBookmarked {
title = L10n.Common.Controls.Actions.removeBookmark
image = UIImage(systemName: "bookmark.slash.fill")
} else {
@ -215,6 +234,44 @@ extension MastodonMenu {
delegate.menuAction(self)
}
return action
case .boostStatus(let context):
let title: String
if context.isBoosted {
title = L10n.Common.Controls.Status.Actions.unreblog
} else {
title = L10n.Common.Controls.Status.Actions.reblog
}
return LabeledAction(title: title, image: UIImage(systemName: "arrow.2.squarepath")) { [weak delegate] in
delegate?.menuAction(self)
}
case .favoriteStatus(let context):
let title: String
let image: UIImage?
if context.isFavorited {
title = L10n.Common.Controls.Status.Actions.unfavorite
image = UIImage(systemName: "star.slash")
} else {
title = L10n.Common.Controls.Status.Actions.favorite
image = UIImage(systemName: "star")
}
return LabeledAction(title: title, image: image) { [weak delegate] in
delegate?.menuAction(self)
}
case .copyLink:
return LabeledAction(title: L10n.Common.Controls.Status.Actions.copyLink, image: UIImage(systemName: "doc.on.doc")) { [weak delegate] in
delegate?.menuAction(self)
}
case .openInBrowser:
return LabeledAction(title: L10n.Common.Controls.Actions.openInBrowser, image: UIImage(systemName: "safari")) { [weak delegate] in
delegate?.menuAction(self)
}
}
}
}
@ -242,10 +299,10 @@ extension MastodonMenu {
}
public struct BookmarkStatusActionContext {
public let isBookmarking: Bool
public let isBookmarked: Bool
public init(isBookmarking: Bool) {
self.isBookmarking = isBookmarking
public init(isBookmarked: Bool) {
self.isBookmarked = isBookmarked
}
}
@ -301,4 +358,21 @@ extension MastodonMenu {
self.isBlocking = isBlocking
}
}
public struct BoostStatusActionContext {
public let isBoosted: Bool
public init(isBoosted: Bool) {
self.isBoosted = isBoosted
}
}
public struct FavoriteStatusActionContext {
public let isFavorited: Bool
public init(isFavorited: Bool) {
self.isFavorited = isFavorited
}
}
}