metatext/Views/CompositionView.swift

219 lines
9.3 KiB
Swift
Raw Normal View History

2020-12-10 02:44:06 +00:00
// Copyright © 2020 Metabolist. All rights reserved.
import Combine
import Kingfisher
import UIKit
2021-01-01 00:49:59 +00:00
import ViewModels
2020-12-10 02:44:06 +00:00
2020-12-19 06:30:19 +00:00
final class CompositionView: UIView {
2020-12-10 02:44:06 +00:00
let avatarImageView = UIImageView()
2021-01-01 00:49:59 +00:00
let spoilerTextField = UITextField()
2020-12-10 02:44:06 +00:00
let textView = UITextView()
2021-01-03 21:55:04 +00:00
let textViewPlaceholder = UILabel()
2021-01-10 06:32:41 +00:00
let removeButton = UIButton(type: .close)
let inReplyToView = UIView()
let hasReplyFollowingView = UIView()
2021-01-11 00:06:20 +00:00
let compositionInputAccessoryView: CompositionInputAccessoryView
2021-01-08 06:11:33 +00:00
let attachmentsView = AttachmentsView()
2021-01-03 23:57:40 +00:00
let attachmentUploadView: AttachmentUploadView
2021-01-11 00:06:20 +00:00
let pollView: CompositionPollView
2021-01-10 02:25:19 +00:00
let markAttachmentsSensitiveView: MarkAttachmentsSensitiveView
2020-12-10 02:44:06 +00:00
2021-01-01 00:49:59 +00:00
private let viewModel: CompositionViewModel
private let parentViewModel: NewStatusViewModel
2020-12-10 02:44:06 +00:00
private var cancellables = Set<AnyCancellable>()
2021-01-01 00:49:59 +00:00
init(viewModel: CompositionViewModel, parentViewModel: NewStatusViewModel) {
self.viewModel = viewModel
self.parentViewModel = parentViewModel
2020-12-10 02:44:06 +00:00
2021-01-11 00:06:20 +00:00
compositionInputAccessoryView = CompositionInputAccessoryView(
viewModel: viewModel,
parentViewModel: parentViewModel)
2021-01-03 23:57:40 +00:00
attachmentUploadView = AttachmentUploadView(viewModel: viewModel)
2021-01-10 02:25:19 +00:00
markAttachmentsSensitiveView = MarkAttachmentsSensitiveView(viewModel: viewModel)
2021-01-11 00:06:20 +00:00
pollView = CompositionPollView(viewModel: viewModel, inputAccessoryView: compositionInputAccessoryView)
2020-12-19 06:30:19 +00:00
2020-12-10 02:44:06 +00:00
super.init(frame: .zero)
initialSetup()
}
@available(*, unavailable)
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
2021-01-01 00:49:59 +00:00
extension CompositionView {
var id: CompositionViewModel.Id { viewModel.id }
2020-12-10 02:44:06 +00:00
}
2020-12-16 01:39:38 +00:00
extension CompositionView: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
2021-01-01 00:49:59 +00:00
viewModel.text = textView.text
2020-12-16 01:39:38 +00:00
}
}
2020-12-10 02:44:06 +00:00
private extension CompositionView {
2021-01-03 01:22:17 +00:00
static let attachmentCollectionViewHeight: CGFloat = 200
2020-12-17 06:48:06 +00:00
2021-01-01 00:49:59 +00:00
// swiftlint:disable:next function_body_length
2020-12-10 02:44:06 +00:00
func initialSetup() {
2021-01-01 00:49:59 +00:00
tag = viewModel.id.hashValue
2020-12-10 02:44:06 +00:00
addSubview(avatarImageView)
avatarImageView.translatesAutoresizingMaskIntoConstraints = false
avatarImageView.layer.cornerRadius = .avatarDimension / 2
avatarImageView.clipsToBounds = true
2021-01-10 06:32:41 +00:00
avatarImageView.setContentHuggingPriority(.required, for: .horizontal)
2020-12-10 02:44:06 +00:00
let stackView = UIStackView()
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
2021-01-01 00:49:59 +00:00
stackView.spacing = .defaultSpacing
stackView.addArrangedSubview(spoilerTextField)
2021-01-03 21:55:04 +00:00
spoilerTextField.borderStyle = .roundedRect
2021-01-01 00:49:59 +00:00
spoilerTextField.adjustsFontForContentSizeCategory = true
spoilerTextField.font = .preferredFont(forTextStyle: .body)
spoilerTextField.placeholder = NSLocalizedString("status.spoiler-text-placeholder", comment: "")
2021-01-11 00:06:20 +00:00
spoilerTextField.inputAccessoryView = compositionInputAccessoryView
2021-01-01 00:49:59 +00:00
spoilerTextField.addAction(
UIAction { [weak self] _ in
guard let self = self, let text = self.spoilerTextField.text else { return }
self.viewModel.contentWarning = text
},
for: .editingChanged)
2020-12-10 02:44:06 +00:00
2021-01-03 21:55:04 +00:00
let textViewFont = UIFont.preferredFont(forTextStyle: .body)
2020-12-10 02:44:06 +00:00
stackView.addArrangedSubview(textView)
textView.isScrollEnabled = false
textView.adjustsFontForContentSizeCategory = true
2021-01-03 21:55:04 +00:00
textView.font = textViewFont
textView.textContainerInset = .zero
textView.textContainer.lineFragmentPadding = 0
2021-01-11 00:06:20 +00:00
textView.inputAccessoryView = compositionInputAccessoryView
2020-12-16 01:39:38 +00:00
textView.inputAccessoryView?.sizeToFit()
textView.delegate = self
2021-01-03 21:55:04 +00:00
textView.addSubview(textViewPlaceholder)
textViewPlaceholder.translatesAutoresizingMaskIntoConstraints = false
textViewPlaceholder.adjustsFontForContentSizeCategory = true
textViewPlaceholder.font = .preferredFont(forTextStyle: .body)
textViewPlaceholder.textColor = .secondaryLabel
textViewPlaceholder.text = NSLocalizedString("compose.prompt", comment: "")
2020-12-10 02:44:06 +00:00
2021-01-08 06:11:33 +00:00
stackView.addArrangedSubview(attachmentsView)
2021-01-10 06:32:41 +00:00
attachmentsView.isHidden = true
2020-12-17 06:48:06 +00:00
stackView.addArrangedSubview(attachmentUploadView)
2021-01-10 06:32:41 +00:00
attachmentUploadView.isHidden = true
2021-01-10 02:25:19 +00:00
stackView.addArrangedSubview(markAttachmentsSensitiveView)
2021-01-10 06:32:41 +00:00
markAttachmentsSensitiveView.isHidden = true
2021-01-11 00:06:20 +00:00
stackView.addArrangedSubview(pollView)
pollView.isHidden = true
2021-01-10 06:32:41 +00:00
addSubview(removeButton)
removeButton.translatesAutoresizingMaskIntoConstraints = false
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.parentViewModel.remove(viewModel: self.viewModel)
}])
removeButton.setContentHuggingPriority(.required, for: .horizontal)
removeButton.setContentCompressionResistancePriority(.required, for: .horizontal)
for view in [inReplyToView, hasReplyFollowingView] {
addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .opaqueSeparator
view.widthAnchor.constraint(equalToConstant: .hairline).isActive = true
}
2020-12-17 06:48:06 +00:00
2021-01-01 00:49:59 +00:00
textView.text = viewModel.text
spoilerTextField.text = viewModel.contentWarning
2020-12-12 00:41:37 +00:00
2021-01-03 21:55:04 +00:00
let textViewBaselineConstraint = textView.topAnchor.constraint(
lessThanOrEqualTo: avatarImageView.centerYAnchor,
constant: -textViewFont.lineHeight / 2)
viewModel.$text.map(\.isEmpty)
.sink { [weak self] in self?.textViewPlaceholder.isHidden = !$0 }
.store(in: &cancellables)
2021-01-01 00:49:59 +00:00
viewModel.$displayContentWarning
.sink { [weak self] in
guard let self = self else { return }
2020-12-12 00:41:37 +00:00
2021-01-01 00:49:59 +00:00
if self.spoilerTextField.isHidden && self.textView.isFirstResponder && $0 {
self.spoilerTextField.becomeFirstResponder()
} else if !self.spoilerTextField.isHidden && self.spoilerTextField.isFirstResponder && !$0 {
self.textView.becomeFirstResponder()
}
2020-12-19 23:05:14 +00:00
2021-01-01 00:49:59 +00:00
self.spoilerTextField.isHidden = !$0
2021-01-03 21:55:04 +00:00
textViewBaselineConstraint.isActive = !$0
2021-01-01 00:49:59 +00:00
}
.store(in: &cancellables)
2020-12-10 02:44:06 +00:00
2021-01-01 00:49:59 +00:00
parentViewModel.$identification.map(\.identity.image)
2020-12-10 02:44:06 +00:00
.sink { [weak self] in self?.avatarImageView.kf.setImage(with: $0) }
.store(in: &cancellables)
2020-12-17 06:48:06 +00:00
2021-01-01 00:49:59 +00:00
viewModel.$attachmentViewModels
2021-01-08 06:11:33 +00:00
.receive(on: RunLoop.main)
2020-12-19 06:30:19 +00:00
.sink { [weak self] in
2021-01-08 06:11:33 +00:00
self?.attachmentsView.viewModel = self?.viewModel
self?.attachmentsView.isHidden = $0.isEmpty
2021-01-10 02:25:19 +00:00
self?.markAttachmentsSensitiveView.isHidden = $0.isEmpty
2020-12-19 06:30:19 +00:00
}
.store(in: &cancellables)
2021-01-11 00:06:20 +00:00
viewModel.$displayPoll
.sink { [weak self] in
if !$0 {
self?.textView.becomeFirstResponder()
}
self?.pollView.isHidden = !$0
}
.store(in: &cancellables)
2021-01-01 00:49:59 +00:00
let guide = UIDevice.current.userInterfaceIdiom == .pad ? readableContentGuide : layoutMarginsGuide
let constraints = [
avatarImageView.heightAnchor.constraint(equalToConstant: .avatarDimension),
avatarImageView.widthAnchor.constraint(equalToConstant: .avatarDimension),
avatarImageView.topAnchor.constraint(equalTo: guide.topAnchor),
avatarImageView.leadingAnchor.constraint(equalTo: guide.leadingAnchor),
avatarImageView.bottomAnchor.constraint(lessThanOrEqualTo: guide.bottomAnchor),
stackView.leadingAnchor.constraint(equalTo: avatarImageView.trailingAnchor, constant: .defaultSpacing),
2021-01-03 21:55:04 +00:00
stackView.topAnchor.constraint(greaterThanOrEqualTo: guide.topAnchor),
2021-01-01 00:49:59 +00:00
stackView.bottomAnchor.constraint(lessThanOrEqualTo: guide.bottomAnchor),
2021-01-03 21:55:04 +00:00
textViewPlaceholder.leadingAnchor.constraint(equalTo: textView.leadingAnchor),
textViewPlaceholder.topAnchor.constraint(equalTo: textView.topAnchor),
2021-01-10 06:32:41 +00:00
textViewPlaceholder.trailingAnchor.constraint(equalTo: textView.trailingAnchor),
removeButton.leadingAnchor.constraint(equalTo: stackView.trailingAnchor, constant: .defaultSpacing),
removeButton.topAnchor.constraint(equalTo: guide.topAnchor),
removeButton.trailingAnchor.constraint(equalTo: guide.trailingAnchor),
inReplyToView.centerXAnchor.constraint(equalTo: avatarImageView.centerXAnchor),
inReplyToView.topAnchor.constraint(equalTo: topAnchor),
inReplyToView.bottomAnchor.constraint(equalTo: avatarImageView.topAnchor),
hasReplyFollowingView.centerXAnchor.constraint(equalTo: avatarImageView.centerXAnchor),
hasReplyFollowingView.topAnchor.constraint(equalTo: avatarImageView.bottomAnchor),
hasReplyFollowingView.bottomAnchor.constraint(equalTo: bottomAnchor)
2021-01-01 00:49:59 +00:00
]
NSLayoutConstraint.activate(constraints)
2020-12-10 02:44:06 +00:00
}
}