Composition niceties

This commit is contained in:
Justin Mazzocchi 2021-01-19 11:59:20 -08:00
parent c104f47ea9
commit 5c179457e5
No known key found for this signature in database
GPG key ID: E223E6937AAFB01C
8 changed files with 60 additions and 25 deletions

View file

@ -51,6 +51,7 @@
"compose.photo-library" = "Photo Library"; "compose.photo-library" = "Photo Library";
"compose.poll.add-choice" = "Add a choice"; "compose.poll.add-choice" = "Add a choice";
"compose.poll.allow-multiple-choices" = "Allow multiple choices"; "compose.poll.allow-multiple-choices" = "Allow multiple choices";
"compose.poll.option-%ld" = "Option %ld";
"compose.prompt" = "What's on your mind?"; "compose.prompt" = "What's on your mind?";
"compose.take-photo-or-video" = "Take Photo or Video"; "compose.take-photo-or-video" = "Take Photo or Video";
"emoji.custom" = "Custom"; "emoji.custom" = "Custom";

View file

@ -193,9 +193,19 @@ private extension NewStatusViewController {
} }
for removal in diff.removals { for removal in diff.removals {
guard case let .remove(_, id, _) = removal else { continue } guard case let .remove(_, id, _) = removal,
let index = stackView.arrangedSubviews.firstIndex(where: { ($0 as? CompositionView)?.id == id })
else { continue }
stackView.arrangedSubviews.first { ($0 as? CompositionView)?.id == id }?.removeFromSuperview() if (stackView.arrangedSubviews[index] as? CompositionView)?.textView.isFirstResponder ?? false {
if index > 0 {
(stackView.arrangedSubviews[index - 1] as? CompositionView)?.textView.becomeFirstResponder()
} else if stackView.arrangedSubviews.count > index {
(stackView.arrangedSubviews[index + 1] as? CompositionView)?.textView.becomeFirstResponder()
}
}
stackView.arrangedSubviews[index].removeFromSuperview()
} }
for compositionView in stackView.arrangedSubviews.compactMap({ $0 as? CompositionView }) { for compositionView in stackView.arrangedSubviews.compactMap({ $0 as? CompositionView }) {

View file

@ -50,16 +50,18 @@ final class AttachmentUploadView: UIView {
progressView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor) progressView.bottomAnchor.constraint(equalTo: layoutMarginsGuide.bottomAnchor)
]) ])
viewModel.$attachmentUpload.sink { [weak self] in viewModel.$attachmentUpload.sink { [weak self] attachmentUpload in
guard let self = self else { return } guard let self = self else { return }
if let attachmentUpload = $0 { UIView.animate(withDuration: .zeroIfReduceMotion(.shortAnimationDuration)) {
self.progressCancellable = attachmentUpload.progress.publisher(for: \.fractionCompleted) if let attachmentUpload = attachmentUpload {
.receive(on: DispatchQueue.main) self.progressCancellable = attachmentUpload.progress.publisher(for: \.fractionCompleted)
.sink { self.progressView.progress = Float($0) } .receive(on: DispatchQueue.main)
self.isHidden = false .sink { self.progressView.progress = Float($0) }
} else { self.isHidden = false
self.isHidden = true } else {
self.isHidden = true
}
} }
} }
.store(in: &cancellables) .store(in: &cancellables)

View file

@ -65,7 +65,6 @@ private extension CompositionInputAccessoryView {
#endif #endif
let attachmentButton = UIBarButtonItem( let attachmentButton = UIBarButtonItem(
title: "hm",
image: UIImage(systemName: "paperclip"), image: UIImage(systemName: "paperclip"),
menu: UIMenu(children: attachmentActions)) menu: UIMenu(children: attachmentActions))
let pollButton = UIBarButtonItem( let pollButton = UIBarButtonItem(

View file

@ -5,6 +5,7 @@ import UIKit
import ViewModels import ViewModels
final class CompositionPollOptionView: UIView { final class CompositionPollOptionView: UIView {
let textField = UITextField()
let option: CompositionViewModel.PollOption let option: CompositionViewModel.PollOption
let removeButton = UIButton(type: .close) let removeButton = UIButton(type: .close)
private let viewModel: CompositionViewModel private let viewModel: CompositionViewModel
@ -33,7 +34,6 @@ private extension CompositionPollOptionView {
// swiftlint:disable:next function_body_length // swiftlint:disable:next function_body_length
func initialSetup() { func initialSetup() {
let stackView = UIStackView() let stackView = UIStackView()
let textField = UITextField()
let remainingCharactersLabel = UILabel() let remainingCharactersLabel = UILabel()
addSubview(stackView) addSubview(stackView)
@ -51,7 +51,7 @@ private extension CompositionPollOptionView {
textField.tag = textInputAccessoryView.tagForInputView textField.tag = textInputAccessoryView.tagForInputView
textField.addAction( textField.addAction(
UIAction { [weak self] _ in UIAction { [weak self] _ in
self?.option.text = textField.text ?? "" }, self?.option.text = self?.textField.text ?? "" },
for: .editingChanged) for: .editingChanged)
textField.text = option.text textField.text = option.text

View file

@ -62,6 +62,7 @@ private extension CompositionPollView {
withConfiguration: UIImage.SymbolConfiguration(scale: .medium)), withConfiguration: UIImage.SymbolConfiguration(scale: .medium)),
for: .normal) for: .normal)
addChoiceButton.setTitle(NSLocalizedString("compose.poll.add-choice", comment: ""), for: .normal) addChoiceButton.setTitle(NSLocalizedString("compose.poll.add-choice", comment: ""), for: .normal)
addChoiceButton.imageEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: .defaultSpacing)
let expiresInButton = UIButton(type: .system) let expiresInButton = UIButton(type: .system)
@ -76,6 +77,7 @@ private extension CompositionPollView {
self?.viewModel.pollExpiresIn = expiry self?.viewModel.pollExpiresIn = expiry
} }
}) })
expiresInButton.imageEdgeInsets = .init(top: 0, left: 0, bottom: 0, right: .defaultSpacing)
let switchStackView = UIStackView() let switchStackView = UIStackView()
@ -121,6 +123,9 @@ private extension CompositionPollView {
parentViewModel: self.parentViewModel, parentViewModel: self.parentViewModel,
option: option) option: option)
optionView.textField.placeholder = String.localizedStringWithFormat(
NSLocalizedString("compose.poll.option-%ld", comment: ""),
index + 1)
self.stackView.insertArrangedSubview(optionView, at: index) self.stackView.insertArrangedSubview(optionView, at: index)
} }
} }
@ -129,6 +134,14 @@ private extension CompositionPollView {
optionView.removeButton.isHidden = index < CompositionViewModel.minPollOptionCount optionView.removeButton.isHidden = index < CompositionViewModel.minPollOptionCount
if !$0.contains(where: { $0 === optionView.option }) { if !$0.contains(where: { $0 === optionView.option }) {
if optionView.textField.isFirstResponder {
if index > 0 {
self.pollOptionViews[index - 1].textField.becomeFirstResponder()
} else if self.pollOptionViews.count > index {
self.pollOptionViews[index + 1].textField.becomeFirstResponder()
}
}
optionView.removeFromSuperview() optionView.removeFromSuperview()
} }
} }

View file

@ -157,17 +157,21 @@ private extension CompositionView {
.store(in: &cancellables) .store(in: &cancellables)
viewModel.$displayContentWarning viewModel.$displayContentWarning
.sink { [weak self] in .sink { [weak self] displayContentWarning in
guard let self = self else { return } guard let self = self else { return }
if self.spoilerTextField.isHidden && self.textView.isFirstResponder && $0 { if self.spoilerTextField.isHidden && self.textView.isFirstResponder && displayContentWarning {
self.spoilerTextField.becomeFirstResponder() self.spoilerTextField.becomeFirstResponder()
} else if !self.spoilerTextField.isHidden && self.spoilerTextField.isFirstResponder && !$0 { } else if !self.spoilerTextField.isHidden
&& self.spoilerTextField.isFirstResponder
&& !displayContentWarning {
self.textView.becomeFirstResponder() self.textView.becomeFirstResponder()
} }
self.spoilerTextField.isHidden = !$0 UIView.animate(withDuration: .zeroIfReduceMotion(.shortAnimationDuration)) {
textViewBaselineConstraint.isActive = !$0 self.spoilerTextField.isHidden = !displayContentWarning
textViewBaselineConstraint.isActive = !displayContentWarning
}
} }
.store(in: &cancellables) .store(in: &cancellables)
@ -177,20 +181,24 @@ private extension CompositionView {
viewModel.$attachmentViewModels viewModel.$attachmentViewModels
.receive(on: RunLoop.main) .receive(on: RunLoop.main)
.sink { [weak self] in .sink { [weak self] attachmentViewModels in
self?.attachmentsView.viewModel = self?.viewModel UIView.animate(withDuration: .zeroIfReduceMotion(.shortAnimationDuration)) {
self?.attachmentsView.isHidden = $0.isEmpty self?.attachmentsView.viewModel = self?.viewModel
self?.markAttachmentsSensitiveView.isHidden = $0.isEmpty self?.attachmentsView.isHidden = attachmentViewModels.isEmpty
self?.markAttachmentsSensitiveView.isHidden = attachmentViewModels.isEmpty
}
} }
.store(in: &cancellables) .store(in: &cancellables)
viewModel.$displayPoll viewModel.$displayPoll
.sink { [weak self] in .sink { [weak self] displayPoll in
if !$0 { if !displayPoll {
self?.textView.becomeFirstResponder() self?.textView.becomeFirstResponder()
} }
self?.pollView.isHidden = !$0 UIView.animate(withDuration: .zeroIfReduceMotion(.shortAnimationDuration)) {
self?.pollView.isHidden = !displayPoll
}
} }
.store(in: &cancellables) .store(in: &cancellables)

View file

@ -22,6 +22,8 @@ extension CGRect {
extension TimeInterval { extension TimeInterval {
static let defaultAnimationDuration: Self = 0.5 static let defaultAnimationDuration: Self = 0.5
static let shortAnimationDuration = defaultAnimationDuration / 2 static let shortAnimationDuration = defaultAnimationDuration / 2
static func zeroIfReduceMotion(_ duration: Self) -> Self { UIAccessibility.isReduceMotionEnabled ? 0 : duration }
} }
extension UIImage { extension UIImage {