metatext/Views/UIKit/CompositionInputAccessoryView.swift

204 lines
7.8 KiB
Swift
Raw Normal View History

2020-12-16 01:39:38 +00:00
// Copyright © 2020 Metabolist. All rights reserved.
2021-01-01 23:31:39 +00:00
import AVFoundation
2020-12-16 01:39:38 +00:00
import Combine
2021-01-01 00:49:59 +00:00
import Mastodon
2020-12-16 01:39:38 +00:00
import UIKit
import ViewModels
2021-01-18 08:24:11 +00:00
final class CompositionInputAccessoryView: UIToolbar {
2021-01-14 17:49:53 +00:00
let tagForInputView = UUID().hashValue
2021-01-01 00:49:59 +00:00
2020-12-16 01:39:38 +00:00
private let viewModel: CompositionViewModel
2021-01-01 00:49:59 +00:00
private let parentViewModel: NewStatusViewModel
2020-12-16 01:39:38 +00:00
private var cancellables = Set<AnyCancellable>()
2021-01-01 00:49:59 +00:00
init(viewModel: CompositionViewModel, parentViewModel: NewStatusViewModel) {
2020-12-16 01:39:38 +00:00
self.viewModel = viewModel
2021-01-01 00:49:59 +00:00
self.parentViewModel = parentViewModel
2020-12-16 01:39:38 +00:00
2021-01-19 05:16:21 +00:00
super.init(
frame: .init(
origin: .zero,
size: .init(width: UIScreen.main.bounds.width, height: .minimumButtonDimension)))
2020-12-16 01:39:38 +00:00
initialSetup()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
private extension CompositionInputAccessoryView {
2021-01-01 00:49:59 +00:00
// swiftlint:disable:next function_body_length
2020-12-16 01:39:38 +00:00
func initialSetup() {
2021-01-19 05:16:21 +00:00
autoresizingMask = .flexibleHeight
2021-01-27 01:45:25 +00:00
heightAnchor.constraint(equalToConstant: .minimumButtonDimension).isActive = true
2021-01-10 07:08:45 +00:00
var attachmentActions = [
UIAction(
title: NSLocalizedString("compose.browse", comment: ""),
image: UIImage(systemName: "ellipsis")) { [weak self] _ in
guard let self = self else { return }
2021-01-01 00:49:59 +00:00
2021-01-10 07:08:45 +00:00
self.parentViewModel.presentDocumentPicker(viewModel: self.viewModel)
},
UIAction(
title: NSLocalizedString("compose.photo-library", comment: ""),
image: UIImage(systemName: "rectangle.on.rectangle")) { [weak self] _ in
guard let self = self else { return }
2020-12-16 01:39:38 +00:00
2021-01-10 07:08:45 +00:00
self.parentViewModel.presentMediaPicker(viewModel: self.viewModel)
}
]
2021-01-03 22:37:06 +00:00
2021-01-01 23:31:39 +00:00
#if !IS_SHARE_EXTENSION
2021-01-10 07:08:45 +00:00
attachmentActions.insert(UIAction(
title: NSLocalizedString("compose.take-photo-or-video", comment: ""),
image: UIImage(systemName: "camera.fill")) { [weak self] _ in
guard let self = self else { return }
2021-01-01 23:31:39 +00:00
2021-01-10 07:08:45 +00:00
self.parentViewModel.presentCamera(viewModel: self.viewModel)
},
at: 1)
2021-01-01 23:31:39 +00:00
#endif
2021-01-18 08:24:11 +00:00
let attachmentButton = UIBarButtonItem(
image: UIImage(systemName: "paperclip"),
menu: UIMenu(children: attachmentActions))
2021-02-01 19:53:47 +00:00
attachmentButton.accessibilityLabel =
NSLocalizedString("compose.attachments-button.accessibility-label", comment: "")
2021-01-18 08:24:11 +00:00
let pollButton = UIBarButtonItem(
image: UIImage(systemName: "chart.bar.xaxis"),
primaryAction: UIAction { [weak self] _ in self?.viewModel.displayPoll.toggle() })
2021-02-01 19:53:47 +00:00
pollButton.accessibilityLabel = NSLocalizedString("compose.poll-button.accessibility-label", comment: "")
2021-01-18 08:24:11 +00:00
let visibilityButton = UIBarButtonItem(
image: UIImage(systemName: parentViewModel.visibility.systemImageName),
2021-01-28 23:32:08 +00:00
menu: visibilityMenu(selectedVisibility: parentViewModel.visibility))
2021-02-01 19:53:47 +00:00
switch parentViewModel.identityContext.appPreferences.statusWord {
case .toot:
visibilityButton.accessibilityLabel =
NSLocalizedString("compose.visibility-button.accessibility-label.toot", comment: "")
case .post:
visibilityButton.accessibilityLabel =
NSLocalizedString("compose.visibility-button.accessibility-label.post", comment: "")
}
2021-01-18 08:24:11 +00:00
let contentWarningButton = UIBarButtonItem(
title: NSLocalizedString("status.content-warning-abbreviation", comment: ""),
primaryAction: UIAction { [weak self] _ in self?.viewModel.displayContentWarning.toggle() })
2021-02-01 19:53:47 +00:00
viewModel.$displayContentWarning.sink {
if $0 {
contentWarningButton.accessibilityHint =
NSLocalizedString("compose.content-warning-button.remove", comment: "")
} else {
contentWarningButton.accessibilityHint =
NSLocalizedString("compose.content-warning-button.add", comment: "")
}
}
.store(in: &cancellables)
2021-01-18 08:24:11 +00:00
let emojiButton = UIBarButtonItem(
image: UIImage(systemName: "face.smiling"),
primaryAction: UIAction { [weak self] _ in
guard let self = self else { return }
2021-01-14 17:49:53 +00:00
2021-01-18 08:24:11 +00:00
self.parentViewModel.presentEmojiPicker(tag: self.tagForInputView)
})
2021-02-01 19:53:47 +00:00
emojiButton.accessibilityLabel = NSLocalizedString("compose.emoji-button", comment: "")
2021-01-18 08:24:11 +00:00
let addButton = UIBarButtonItem(
image: UIImage(systemName: "plus.circle.fill"),
primaryAction: UIAction { [weak self] _ in
guard let self = self else { return }
2021-01-14 17:49:53 +00:00
2021-01-18 08:24:11 +00:00
self.parentViewModel.insert(after: self.viewModel)
})
2020-12-16 01:39:38 +00:00
2021-02-01 19:53:47 +00:00
switch parentViewModel.identityContext.appPreferences.statusWord {
case .toot:
addButton.accessibilityLabel =
NSLocalizedString("compose.add-button-accessibility-label.toot", comment: "")
case .post:
addButton.accessibilityLabel =
NSLocalizedString("compose.add-button-accessibility-label.post", comment: "")
}
2021-01-04 01:15:50 +00:00
let charactersLabel = UILabel()
charactersLabel.font = .preferredFont(forTextStyle: .callout)
2021-01-18 08:24:11 +00:00
charactersLabel.adjustsFontForContentSizeCategory = true
charactersLabel.adjustsFontSizeToFitWidth = true
let charactersBarItem = UIBarButtonItem(customView: charactersLabel)
items = [
attachmentButton,
UIBarButtonItem.fixedSpace(.defaultSpacing),
pollButton,
UIBarButtonItem.fixedSpace(.defaultSpacing),
visibilityButton,
UIBarButtonItem.fixedSpace(.defaultSpacing),
contentWarningButton,
UIBarButtonItem.fixedSpace(.defaultSpacing),
emojiButton,
UIBarButtonItem.flexibleSpace(),
charactersBarItem,
UIBarButtonItem.fixedSpace(.defaultSpacing),
addButton]
2021-01-01 00:49:59 +00:00
2021-01-10 07:08:45 +00:00
viewModel.$canAddAttachment
.sink { attachmentButton.isEnabled = $0 }
.store(in: &cancellables)
2021-01-04 01:15:50 +00:00
2021-01-11 00:06:20 +00:00
viewModel.$attachmentViewModels
.combineLatest(viewModel.$attachmentUpload)
.sink { pollButton.isEnabled = $0.isEmpty && $1 == nil }
.store(in: &cancellables)
2021-01-04 01:15:50 +00:00
viewModel.$remainingCharacters.sink {
charactersLabel.text = String($0)
charactersLabel.textColor = $0 < 0 ? .systemRed : .label
2021-02-01 19:53:47 +00:00
charactersLabel.accessibilityLabel = String.localizedStringWithFormat(
NSLocalizedString("compose.characters-remaining-accessibility-label-%ld", comment: ""),
$0)
2021-01-04 01:15:50 +00:00
}
.store(in: &cancellables)
2021-01-03 22:37:06 +00:00
2021-01-01 00:49:59 +00:00
viewModel.$isPostable
2021-01-18 08:24:11 +00:00
.sink { addButton.isEnabled = $0 }
2021-01-01 00:49:59 +00:00
.store(in: &cancellables)
2020-12-16 01:39:38 +00:00
2021-01-01 00:49:59 +00:00
parentViewModel.$visibility
2021-01-28 23:32:08 +00:00
.sink { [weak self] in
visibilityButton.image = UIImage(systemName: $0.systemImageName)
visibilityButton.menu = self?.visibilityMenu(selectedVisibility: $0)
}
2021-01-01 00:49:59 +00:00
.store(in: &cancellables)
2020-12-16 01:39:38 +00:00
}
}
2021-01-28 23:32:08 +00:00
private extension CompositionInputAccessoryView {
func visibilityMenu(selectedVisibility: Status.Visibility) -> UIMenu {
UIMenu(children: Status.Visibility.allCasesExceptUnknown.reversed().map { visibility in
UIAction(
title: visibility.title ?? "",
image: UIImage(systemName: visibility.systemImageName),
discoverabilityTitle: visibility.description,
state: visibility == selectedVisibility ? .on : .off) { [weak self] _ in
self?.parentViewModel.visibility = visibility
}
})
}
}