mirror of
https://github.com/metabolist/metatext.git
synced 2024-11-22 00:01:00 +00:00
Composition niceties
This commit is contained in:
parent
c104f47ea9
commit
5c179457e5
8 changed files with 60 additions and 25 deletions
|
@ -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";
|
||||||
|
|
|
@ -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 }) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Loading…
Reference in a new issue