2021-01-11 00:06:20 +00:00
|
|
|
// Copyright © 2021 Metabolist. All rights reserved.
|
|
|
|
|
|
|
|
import Combine
|
|
|
|
import UIKit
|
|
|
|
import ViewModels
|
|
|
|
|
|
|
|
final class CompositionPollOptionView: UIView {
|
2021-01-19 19:59:20 +00:00
|
|
|
let textField = UITextField()
|
2021-01-11 00:06:20 +00:00
|
|
|
let option: CompositionViewModel.PollOption
|
|
|
|
let removeButton = UIButton(type: .close)
|
|
|
|
private let viewModel: CompositionViewModel
|
2021-01-14 17:49:53 +00:00
|
|
|
private let parentViewModel: NewStatusViewModel
|
2021-01-11 00:06:20 +00:00
|
|
|
private var cancellables = Set<AnyCancellable>()
|
|
|
|
|
|
|
|
init(viewModel: CompositionViewModel,
|
2021-01-14 17:49:53 +00:00
|
|
|
parentViewModel: NewStatusViewModel,
|
|
|
|
option: CompositionViewModel.PollOption) {
|
2021-01-11 00:06:20 +00:00
|
|
|
self.viewModel = viewModel
|
2021-01-14 17:49:53 +00:00
|
|
|
self.parentViewModel = parentViewModel
|
2021-01-11 00:06:20 +00:00
|
|
|
self.option = option
|
|
|
|
|
|
|
|
super.init(frame: .zero)
|
|
|
|
|
|
|
|
initialSetup()
|
|
|
|
}
|
|
|
|
|
|
|
|
@available(*, unavailable)
|
|
|
|
required init?(coder: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private extension CompositionPollOptionView {
|
|
|
|
// swiftlint:disable:next function_body_length
|
|
|
|
func initialSetup() {
|
|
|
|
let stackView = UIStackView()
|
|
|
|
let remainingCharactersLabel = UILabel()
|
|
|
|
|
|
|
|
addSubview(stackView)
|
|
|
|
stackView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
stackView.spacing = .defaultSpacing
|
|
|
|
|
|
|
|
stackView.addArrangedSubview(textField)
|
|
|
|
textField.borderStyle = .roundedRect
|
|
|
|
textField.adjustsFontForContentSizeCategory = true
|
|
|
|
textField.font = .preferredFont(forTextStyle: .body)
|
2021-01-14 17:49:53 +00:00
|
|
|
let textInputAccessoryView = CompositionInputAccessoryView(
|
|
|
|
viewModel: viewModel,
|
2021-02-14 02:28:30 +00:00
|
|
|
parentViewModel: parentViewModel,
|
|
|
|
autocompleteQueryPublisher: option.$autocompleteQuery.eraseToAnyPublisher())
|
2021-01-14 17:49:53 +00:00
|
|
|
textField.inputAccessoryView = textInputAccessoryView
|
|
|
|
textField.tag = textInputAccessoryView.tagForInputView
|
2021-01-11 00:06:20 +00:00
|
|
|
textField.addAction(
|
2021-02-15 21:52:28 +00:00
|
|
|
UIAction { [weak self] _ in self?.textFieldEditingChanged() },
|
2021-01-11 00:06:20 +00:00
|
|
|
for: .editingChanged)
|
2021-01-11 22:45:30 +00:00
|
|
|
textField.text = option.text
|
2021-01-11 00:06:20 +00:00
|
|
|
|
|
|
|
stackView.addArrangedSubview(remainingCharactersLabel)
|
|
|
|
remainingCharactersLabel.adjustsFontForContentSizeCategory = true
|
|
|
|
remainingCharactersLabel.font = .preferredFont(forTextStyle: .callout)
|
|
|
|
remainingCharactersLabel.setContentHuggingPriority(.required, for: .horizontal)
|
|
|
|
|
|
|
|
stackView.addArrangedSubview(removeButton)
|
|
|
|
removeButton.showsMenuAsPrimaryAction = true
|
|
|
|
removeButton.menu = UIMenu(
|
|
|
|
children: [
|
|
|
|
UIAction(
|
|
|
|
title: NSLocalizedString("remove", comment: ""),
|
|
|
|
image: UIImage(systemName: "trash"),
|
|
|
|
attributes: .destructive) { [weak self] _ in
|
|
|
|
guard let self = self else { return }
|
|
|
|
|
|
|
|
self.viewModel.remove(pollOption: self.option)
|
|
|
|
}])
|
|
|
|
removeButton.setContentHuggingPriority(.required, for: .horizontal)
|
|
|
|
removeButton.setContentHuggingPriority(.required, for: .vertical)
|
|
|
|
|
|
|
|
NSLayoutConstraint.activate([
|
|
|
|
stackView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
|
|
|
stackView.topAnchor.constraint(equalTo: topAnchor),
|
|
|
|
stackView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
|
|
|
stackView.bottomAnchor.constraint(equalTo: bottomAnchor)
|
|
|
|
])
|
|
|
|
|
|
|
|
option.$remainingCharacters
|
|
|
|
.sink {
|
|
|
|
remainingCharactersLabel.text = String($0)
|
|
|
|
remainingCharactersLabel.textColor = $0 < 0 ? .systemRed : .label
|
|
|
|
}
|
|
|
|
.store(in: &cancellables)
|
2021-02-15 21:52:28 +00:00
|
|
|
|
|
|
|
textInputAccessoryView.autocompleteSelections
|
|
|
|
.sink { [weak self] in self?.autocompleteSelected($0) }
|
|
|
|
.store(in: &cancellables)
|
|
|
|
}
|
|
|
|
|
|
|
|
func textFieldEditingChanged() {
|
|
|
|
guard let text = textField.text else { return }
|
|
|
|
|
|
|
|
option.text = text
|
|
|
|
|
|
|
|
if let textToSelectedRange = textField.textToSelectedRange {
|
|
|
|
option.textToSelectedRange = textToSelectedRange
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func autocompleteSelected(_ autocompleteText: String) {
|
|
|
|
guard let autocompleteQuery = option.autocompleteQuery,
|
|
|
|
let queryRange = option.textToSelectedRange.range(of: autocompleteQuery, options: .backwards),
|
|
|
|
let textToSelectedRangeRange = option.text.range(of: option.textToSelectedRange)
|
|
|
|
else { return }
|
|
|
|
|
|
|
|
let replaced = option.textToSelectedRange.replacingOccurrences(
|
|
|
|
of: autocompleteQuery,
|
|
|
|
with: autocompleteText.appending(" "),
|
|
|
|
range: queryRange)
|
|
|
|
|
|
|
|
textField.text = option.text.replacingOccurrences(
|
|
|
|
of: option.textToSelectedRange,
|
|
|
|
with: replaced,
|
|
|
|
range: textToSelectedRangeRange)
|
|
|
|
textFieldEditingChanged()
|
2021-01-11 00:06:20 +00:00
|
|
|
}
|
|
|
|
}
|