mirror of
https://github.com/mastodon/mastodon-ios.git
synced 2024-12-23 23:46:56 +01:00
feat: add keyboard shortcuts for compose scene
This commit is contained in:
parent
443c863465
commit
0bcfc14aa6
@ -87,10 +87,9 @@
|
||||
"keyboard": {
|
||||
"common": {
|
||||
"switch_to_tab": "Switch to %s",
|
||||
"compose_new_post": "Compose New Post",
|
||||
"show_favorites": "Show Favorites",
|
||||
"open_settings": "Open Settings",
|
||||
"previous_section": "Previous Section",
|
||||
"next_section": "Next Section"
|
||||
"open_settings": "Open Settings"
|
||||
},
|
||||
"timeline": {
|
||||
"previous_status": "Previous Status",
|
||||
@ -103,6 +102,10 @@
|
||||
"toggle_favorite": "Toggle Status Favorite",
|
||||
"toggle_content_warning": "Toggle Content Warning",
|
||||
"preview_image": "Preview Image"
|
||||
},
|
||||
"segmented_control": {
|
||||
"previous_section": "Previous Section",
|
||||
"next_section": "Next Section"
|
||||
}
|
||||
},
|
||||
"status": {
|
||||
@ -379,6 +382,14 @@
|
||||
"post_visibility_menu": "Post visibility menu",
|
||||
"input_limit_remains_count": "Input limit remains %ld",
|
||||
"input_limit_exceeds_count": "Input limit exceeds %ld"
|
||||
},
|
||||
"keyboard": {
|
||||
"discard_post": "Discard Post",
|
||||
"publish_post": "Publish Post",
|
||||
"toggle_poll": "Toggle Poll",
|
||||
"toggle_content_warning": "Toggle Content Warning",
|
||||
"append_attachment_entry": "Append Attachment - %s",
|
||||
"select_visibility_entry": "Select Visibility - %s"
|
||||
}
|
||||
},
|
||||
"profile": {
|
||||
|
@ -199,12 +199,10 @@ internal enum L10n {
|
||||
}
|
||||
internal enum Keyboard {
|
||||
internal enum Common {
|
||||
/// Next Section
|
||||
internal static let nextSection = L10n.tr("Localizable", "Common.Controls.Keyboard.Common.NextSection")
|
||||
/// Compose New Post
|
||||
internal static let composeNewPost = L10n.tr("Localizable", "Common.Controls.Keyboard.Common.ComposeNewPost")
|
||||
/// Open Settings
|
||||
internal static let openSettings = L10n.tr("Localizable", "Common.Controls.Keyboard.Common.OpenSettings")
|
||||
/// Previous Section
|
||||
internal static let previousSection = L10n.tr("Localizable", "Common.Controls.Keyboard.Common.PreviousSection")
|
||||
/// Show Favorites
|
||||
internal static let showFavorites = L10n.tr("Localizable", "Common.Controls.Keyboard.Common.ShowFavorites")
|
||||
/// Switch to %@
|
||||
@ -212,6 +210,12 @@ internal enum L10n {
|
||||
return L10n.tr("Localizable", "Common.Controls.Keyboard.Common.SwitchToTab", String(describing: p1))
|
||||
}
|
||||
}
|
||||
internal enum SegmentedControl {
|
||||
/// Next Section
|
||||
internal static let nextSection = L10n.tr("Localizable", "Common.Controls.Keyboard.SegmentedControl.NextSection")
|
||||
/// Previous Section
|
||||
internal static let previousSection = L10n.tr("Localizable", "Common.Controls.Keyboard.SegmentedControl.PreviousSection")
|
||||
}
|
||||
internal enum Timeline {
|
||||
/// Next Status
|
||||
internal static let nextStatus = L10n.tr("Localizable", "Common.Controls.Keyboard.Timeline.NextStatus")
|
||||
@ -436,6 +440,24 @@ internal enum L10n {
|
||||
/// Write an accurate warning here...
|
||||
internal static let placeholder = L10n.tr("Localizable", "Scene.Compose.ContentWarning.Placeholder")
|
||||
}
|
||||
internal enum Keyboard {
|
||||
/// Append Attachment - %@
|
||||
internal static func appendAttachmentEntry(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "Scene.Compose.Keyboard.AppendAttachmentEntry", String(describing: p1))
|
||||
}
|
||||
/// Discard Post
|
||||
internal static let discardPost = L10n.tr("Localizable", "Scene.Compose.Keyboard.DiscardPost")
|
||||
/// Publish Post
|
||||
internal static let publishPost = L10n.tr("Localizable", "Scene.Compose.Keyboard.PublishPost")
|
||||
/// Select Visibility - %@
|
||||
internal static func selectVisibilityEntry(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "Scene.Compose.Keyboard.SelectVisibilityEntry", String(describing: p1))
|
||||
}
|
||||
/// Toggle Content Warning
|
||||
internal static let toggleContentWarning = L10n.tr("Localizable", "Scene.Compose.Keyboard.ToggleContentWarning")
|
||||
/// Toggle Poll
|
||||
internal static let togglePoll = L10n.tr("Localizable", "Scene.Compose.Keyboard.TogglePoll")
|
||||
}
|
||||
internal enum MediaSelection {
|
||||
/// Browse
|
||||
internal static let browse = L10n.tr("Localizable", "Scene.Compose.MediaSelection.Browse")
|
||||
|
@ -27,8 +27,8 @@ enum SegmentedControlNavigationDirection: String, CaseIterable {
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .previous: return L10n.Common.Controls.Keyboard.Common.previousSection
|
||||
case .next: return L10n.Common.Controls.Keyboard.Common.nextSection
|
||||
case .previous: return L10n.Common.Controls.Keyboard.SegmentedControl.previousSection
|
||||
case .next: return L10n.Common.Controls.Keyboard.SegmentedControl.nextSection
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,11 +68,12 @@ Please check your internet connection.";
|
||||
"Common.Controls.Firendship.UnblockUser" = "Unblock %@";
|
||||
"Common.Controls.Firendship.Unmute" = "Unmute";
|
||||
"Common.Controls.Firendship.UnmuteUser" = "Unmute %@";
|
||||
"Common.Controls.Keyboard.Common.NextSection" = "Next Section";
|
||||
"Common.Controls.Keyboard.Common.ComposeNewPost" = "Compose New Post";
|
||||
"Common.Controls.Keyboard.Common.OpenSettings" = "Open Settings";
|
||||
"Common.Controls.Keyboard.Common.PreviousSection" = "Previous Section";
|
||||
"Common.Controls.Keyboard.Common.ShowFavorites" = "Show Favorites";
|
||||
"Common.Controls.Keyboard.Common.SwitchToTab" = "Switch to %@";
|
||||
"Common.Controls.Keyboard.SegmentedControl.NextSection" = "Next Section";
|
||||
"Common.Controls.Keyboard.SegmentedControl.PreviousSection" = "Previous Section";
|
||||
"Common.Controls.Keyboard.Timeline.NextStatus" = "Next Status";
|
||||
"Common.Controls.Keyboard.Timeline.OpenAuthorProfile" = "Open Author Profile";
|
||||
"Common.Controls.Keyboard.Timeline.OpenRebloggerProfile" = "Open Reblogger Profile";
|
||||
@ -149,6 +150,12 @@ uploaded to Mastodon.";
|
||||
"Scene.Compose.ComposeAction" = "Publish";
|
||||
"Scene.Compose.ContentInputPlaceholder" = "Type or paste what's on your mind";
|
||||
"Scene.Compose.ContentWarning.Placeholder" = "Write an accurate warning here...";
|
||||
"Scene.Compose.Keyboard.AppendAttachmentEntry" = "Append Attachment - %@";
|
||||
"Scene.Compose.Keyboard.DiscardPost" = "Discard Post";
|
||||
"Scene.Compose.Keyboard.PublishPost" = "Publish Post";
|
||||
"Scene.Compose.Keyboard.SelectVisibilityEntry" = "Select Visibility - %@";
|
||||
"Scene.Compose.Keyboard.ToggleContentWarning" = "Toggle Content Warning";
|
||||
"Scene.Compose.Keyboard.TogglePoll" = "Toggle Poll";
|
||||
"Scene.Compose.MediaSelection.Browse" = "Browse";
|
||||
"Scene.Compose.MediaSelection.Camera" = "Take Photo";
|
||||
"Scene.Compose.MediaSelection.PhotoLibrary" = "Photo Library";
|
||||
|
@ -68,11 +68,12 @@ Please check your internet connection.";
|
||||
"Common.Controls.Firendship.UnblockUser" = "Unblock %@";
|
||||
"Common.Controls.Firendship.Unmute" = "Unmute";
|
||||
"Common.Controls.Firendship.UnmuteUser" = "Unmute %@";
|
||||
"Common.Controls.Keyboard.Common.NextSection" = "Next Section";
|
||||
"Common.Controls.Keyboard.Common.ComposeNewPost" = "Compose New Post";
|
||||
"Common.Controls.Keyboard.Common.OpenSettings" = "Open Settings";
|
||||
"Common.Controls.Keyboard.Common.PreviousSection" = "Previous Section";
|
||||
"Common.Controls.Keyboard.Common.ShowFavorites" = "Show Favorites";
|
||||
"Common.Controls.Keyboard.Common.SwitchToTab" = "Switch to %@";
|
||||
"Common.Controls.Keyboard.SegmentedControl.NextSection" = "Next Section";
|
||||
"Common.Controls.Keyboard.SegmentedControl.PreviousSection" = "Previous Section";
|
||||
"Common.Controls.Keyboard.Timeline.NextStatus" = "Next Status";
|
||||
"Common.Controls.Keyboard.Timeline.OpenAuthorProfile" = "Open Author Profile";
|
||||
"Common.Controls.Keyboard.Timeline.OpenRebloggerProfile" = "Open Reblogger Profile";
|
||||
@ -149,6 +150,12 @@ uploaded to Mastodon.";
|
||||
"Scene.Compose.ComposeAction" = "Publish";
|
||||
"Scene.Compose.ContentInputPlaceholder" = "Type or paste what's on your mind";
|
||||
"Scene.Compose.ContentWarning.Placeholder" = "Write an accurate warning here...";
|
||||
"Scene.Compose.Keyboard.AppendAttachmentEntry" = "Append Attachment - %@";
|
||||
"Scene.Compose.Keyboard.DiscardPost" = "Discard Post";
|
||||
"Scene.Compose.Keyboard.PublishPost" = "Publish Post";
|
||||
"Scene.Compose.Keyboard.SelectVisibilityEntry" = "Select Visibility - %@";
|
||||
"Scene.Compose.Keyboard.ToggleContentWarning" = "Toggle Content Warning";
|
||||
"Scene.Compose.Keyboard.TogglePoll" = "Toggle Poll";
|
||||
"Scene.Compose.MediaSelection.Browse" = "Browse";
|
||||
"Scene.Compose.MediaSelection.Camera" = "Take Photo";
|
||||
"Scene.Compose.MediaSelection.PhotoLibrary" = "Photo Library";
|
||||
|
@ -37,6 +37,8 @@ final class ComposeViewController: UIViewController, NeedsDependency {
|
||||
button.adjustsImageWhenHighlighted = false
|
||||
return button
|
||||
}()
|
||||
|
||||
private(set) lazy var cancelBarButtonItem = UIBarButtonItem(title: L10n.Common.Controls.Actions.cancel, style: .plain, target: self, action: #selector(ComposeViewController.cancelBarButtonItemPressed(_:)))
|
||||
private(set) lazy var publishBarButtonItem: UIBarButtonItem = {
|
||||
let barButtonItem = UIBarButtonItem(customView: publishButton)
|
||||
return barButtonItem
|
||||
@ -138,7 +140,7 @@ extension ComposeViewController {
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
view.backgroundColor = Asset.Scene.Compose.background.color
|
||||
navigationItem.leftBarButtonItem = UIBarButtonItem(title: L10n.Common.Controls.Actions.cancel, style: .plain, target: self, action: #selector(ComposeViewController.cancelBarButtonItemPressed(_:)))
|
||||
navigationItem.leftBarButtonItem = cancelBarButtonItem
|
||||
navigationItem.rightBarButtonItem = publishBarButtonItem
|
||||
publishButton.addTarget(self, action: #selector(ComposeViewController.publishBarButtonItemPressed(_:)), for: .touchUpInside)
|
||||
|
||||
@ -247,7 +249,8 @@ extension ComposeViewController {
|
||||
|
||||
// adjust inset for auto-complete
|
||||
let autoCompleteTableViewBottomInset: CGFloat = {
|
||||
let tableViewFrameInWindow = self.autoCompleteViewController.tableView.superview!.convert(self.autoCompleteViewController.tableView.frame, to: nil)
|
||||
guard let superview = self.autoCompleteViewController.tableView.superview else { return .zero }
|
||||
let tableViewFrameInWindow = superview.convert(self.autoCompleteViewController.tableView.frame, to: nil)
|
||||
let padding = tableViewFrameInWindow.maxY + self.composeToolbarView.frame.height + AutoCompleteViewController.chevronViewHeight - endFrame.minY
|
||||
return max(0, padding)
|
||||
}()
|
||||
@ -1257,3 +1260,130 @@ extension ComposeViewController: AutoCompleteViewControllerDelegate {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension ComposeViewController {
|
||||
override var keyCommands: [UIKeyCommand]? {
|
||||
composeKeyCommands
|
||||
}
|
||||
}
|
||||
|
||||
extension ComposeViewController {
|
||||
|
||||
enum ComposeKeyCommand: String, CaseIterable {
|
||||
case discardPost
|
||||
case publishPost
|
||||
case mediaBrowse
|
||||
case mediaPhotoLibrary
|
||||
case mediaCamera
|
||||
case togglePoll
|
||||
case toggleContentWarning
|
||||
case selectVisibilityPublic
|
||||
case selectVisibilityUnlisted
|
||||
case selectVisibilityPrivate
|
||||
case selectVisibilityDirect
|
||||
|
||||
var title: String {
|
||||
switch self {
|
||||
case .discardPost: return L10n.Scene.Compose.Keyboard.discardPost
|
||||
case .publishPost: return L10n.Scene.Compose.Keyboard.publishPost
|
||||
case .mediaBrowse: return L10n.Scene.Compose.Keyboard.appendAttachmentEntry(L10n.Scene.Compose.MediaSelection.browse)
|
||||
case .mediaPhotoLibrary: return L10n.Scene.Compose.Keyboard.appendAttachmentEntry(L10n.Scene.Compose.MediaSelection.photoLibrary)
|
||||
case .mediaCamera: return L10n.Scene.Compose.Keyboard.appendAttachmentEntry(L10n.Scene.Compose.MediaSelection.camera)
|
||||
case .togglePoll: return L10n.Scene.Compose.Keyboard.togglePoll
|
||||
case .toggleContentWarning: return L10n.Scene.Compose.Keyboard.toggleContentWarning
|
||||
case .selectVisibilityPublic: return L10n.Scene.Compose.Keyboard.selectVisibilityEntry(L10n.Scene.Compose.Visibility.public)
|
||||
case .selectVisibilityUnlisted: return L10n.Scene.Compose.Keyboard.selectVisibilityEntry(L10n.Scene.Compose.Visibility.unlisted)
|
||||
case .selectVisibilityPrivate: return L10n.Scene.Compose.Keyboard.selectVisibilityEntry(L10n.Scene.Compose.Visibility.private)
|
||||
case .selectVisibilityDirect: return L10n.Scene.Compose.Keyboard.selectVisibilityEntry(L10n.Scene.Compose.Visibility.direct)
|
||||
}
|
||||
}
|
||||
|
||||
// UIKeyCommand input
|
||||
var input: String {
|
||||
switch self {
|
||||
case .discardPost: return "w" // + command
|
||||
case .publishPost: return "\r" // (enter) + command
|
||||
case .mediaBrowse: return "b" // + option + command
|
||||
case .mediaPhotoLibrary: return "p" // + option + command
|
||||
case .mediaCamera: return "c" // + option + command
|
||||
case .togglePoll: return "p" // + shift + command
|
||||
case .toggleContentWarning: return "c" // + shift + command
|
||||
case .selectVisibilityPublic: return "1" // + command
|
||||
case .selectVisibilityUnlisted: return "2" // + command
|
||||
case .selectVisibilityPrivate: return "3" // + command
|
||||
case .selectVisibilityDirect: return "4" // + command
|
||||
}
|
||||
}
|
||||
|
||||
var modifierFlags: UIKeyModifierFlags {
|
||||
switch self {
|
||||
case .discardPost: return [.command]
|
||||
case .publishPost: return [.command]
|
||||
case .mediaBrowse: return [.alternate, .command]
|
||||
case .mediaPhotoLibrary: return [.alternate, .command]
|
||||
case .mediaCamera: return [.alternate, .command]
|
||||
case .togglePoll: return [.shift, .command]
|
||||
case .toggleContentWarning: return [.shift, .command]
|
||||
case .selectVisibilityPublic: return [.command]
|
||||
case .selectVisibilityUnlisted: return [.command]
|
||||
case .selectVisibilityPrivate: return [.command]
|
||||
case .selectVisibilityDirect: return [.command]
|
||||
}
|
||||
}
|
||||
|
||||
var propertyList: Any {
|
||||
return rawValue
|
||||
}
|
||||
}
|
||||
|
||||
var composeKeyCommands: [UIKeyCommand]? {
|
||||
ComposeKeyCommand.allCases.map { command in
|
||||
UIKeyCommand(
|
||||
title: command.title,
|
||||
image: nil,
|
||||
action: #selector(Self.composeKeyCommandHandler(_:)),
|
||||
input: command.input,
|
||||
modifierFlags: command.modifierFlags,
|
||||
propertyList: command.propertyList,
|
||||
alternates: [],
|
||||
discoverabilityTitle: nil,
|
||||
attributes: [],
|
||||
state: .off
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func composeKeyCommandHandler(_ sender: UIKeyCommand) {
|
||||
guard let rawValue = sender.propertyList as? String,
|
||||
let command = ComposeKeyCommand(rawValue: rawValue) else { return }
|
||||
|
||||
switch command {
|
||||
case .discardPost:
|
||||
cancelBarButtonItemPressed(cancelBarButtonItem)
|
||||
case .publishPost:
|
||||
publishBarButtonItemPressed(publishBarButtonItem)
|
||||
case .mediaBrowse:
|
||||
present(documentPickerController, animated: true, completion: nil)
|
||||
case .mediaPhotoLibrary:
|
||||
present(imagePicker, animated: true, completion: nil)
|
||||
case .mediaCamera:
|
||||
guard UIImagePickerController.isSourceTypeAvailable(.camera) else {
|
||||
return
|
||||
}
|
||||
present(imagePickerController, animated: true, completion: nil)
|
||||
case .togglePoll:
|
||||
composeToolbarView.pollButton.sendActions(for: .touchUpInside)
|
||||
case .toggleContentWarning:
|
||||
composeToolbarView.contentWarningButton.sendActions(for: .touchUpInside)
|
||||
case .selectVisibilityPublic:
|
||||
viewModel.selectedStatusVisibility.value = .public
|
||||
case .selectVisibilityUnlisted:
|
||||
viewModel.selectedStatusVisibility.value = .unlisted
|
||||
case .selectVisibilityPrivate:
|
||||
viewModel.selectedStatusVisibility.value = .private
|
||||
case .selectVisibilityDirect:
|
||||
viewModel.selectedStatusVisibility.value = .direct
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -246,6 +246,21 @@ extension MainTabBarController {
|
||||
)
|
||||
}
|
||||
|
||||
var composeNewPostKeyCommand: UIKeyCommand {
|
||||
UIKeyCommand(
|
||||
title: L10n.Common.Controls.Keyboard.Common.composeNewPost,
|
||||
image: nil,
|
||||
action: #selector(MainTabBarController.composeNewPostKeyCommandHandler(_:)),
|
||||
input: "n",
|
||||
modifierFlags: .command,
|
||||
propertyList: nil,
|
||||
alternates: [],
|
||||
discoverabilityTitle: nil,
|
||||
attributes: [],
|
||||
state: .off
|
||||
)
|
||||
}
|
||||
|
||||
override var keyCommands: [UIKeyCommand]? {
|
||||
guard let topMost = self.topMost else {
|
||||
return []
|
||||
@ -259,6 +274,11 @@ extension MainTabBarController {
|
||||
// switch tabs
|
||||
commands.append(contentsOf: switchToTabKeyCommands)
|
||||
|
||||
// show compose
|
||||
if !(self.topMost is ComposeViewController) {
|
||||
commands.append(composeNewPostKeyCommand)
|
||||
}
|
||||
|
||||
// show favorites
|
||||
if !(self.topMost is FavoriteViewController) {
|
||||
commands.append(showFavoritesKeyCommand)
|
||||
@ -312,4 +332,10 @@ extension MainTabBarController {
|
||||
coordinator.present(scene: .settings(viewModel: settingsViewModel), from: nil, transition: .modal(animated: true, completion: nil))
|
||||
}
|
||||
|
||||
@objc private func composeNewPostKeyCommandHandler(_ sender: UIKeyCommand) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
let composeViewModel = ComposeViewModel(context: context, composeKind: .post)
|
||||
coordinator.present(scene: .compose(viewModel: composeViewModel), from: nil, transition: .modal(animated: true, completion: nil))
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user