2021-06-28 13:41:41 +02:00
|
|
|
//
|
|
|
|
// ComposeStatusContentTableViewCell.swift
|
|
|
|
// Mastodon
|
|
|
|
//
|
|
|
|
// Created by MainasuK Cirno on 2021-6-28.
|
|
|
|
//
|
|
|
|
|
|
|
|
import os.log
|
|
|
|
import UIKit
|
|
|
|
import Combine
|
2021-07-22 13:34:24 +02:00
|
|
|
import MetaTextKit
|
2021-07-19 11:12:45 +02:00
|
|
|
import UITextView_Placeholder
|
2022-01-27 14:23:39 +01:00
|
|
|
import MastodonAsset
|
|
|
|
import MastodonLocalization
|
|
|
|
import MastodonUI
|
2021-06-28 13:41:41 +02:00
|
|
|
|
2021-09-29 10:27:35 +02:00
|
|
|
protocol ComposeStatusContentTableViewCellDelegate: AnyObject {
|
|
|
|
func composeStatusContentTableViewCell(_ cell: ComposeStatusContentTableViewCell, textViewShouldBeginEditing textView: UITextView) -> Bool
|
|
|
|
}
|
|
|
|
|
2021-06-28 13:41:41 +02:00
|
|
|
final class ComposeStatusContentTableViewCell: UITableViewCell {
|
|
|
|
|
2022-01-27 14:23:39 +01:00
|
|
|
let logger = Logger(subsystem: "ComposeStatusContentTableViewCell", category: "View")
|
2021-07-19 11:12:45 +02:00
|
|
|
|
2021-06-28 13:41:41 +02:00
|
|
|
var disposeBag = Set<AnyCancellable>()
|
2021-09-29 10:27:35 +02:00
|
|
|
weak var delegate: ComposeStatusContentTableViewCellDelegate?
|
|
|
|
|
2022-01-27 14:23:39 +01:00
|
|
|
let statusView = StatusView()
|
2021-06-28 13:41:41 +02:00
|
|
|
|
|
|
|
let statusContentWarningEditorView = StatusContentWarningEditorView()
|
|
|
|
|
|
|
|
let textEditorViewContainerView = UIView()
|
|
|
|
|
|
|
|
static let metaTextViewTag: Int = 333
|
|
|
|
let metaText: MetaText = {
|
|
|
|
let metaText = MetaText()
|
|
|
|
metaText.textView.backgroundColor = .clear
|
|
|
|
metaText.textView.isScrollEnabled = false
|
|
|
|
metaText.textView.keyboardType = .twitter
|
2021-06-29 13:27:08 +02:00
|
|
|
metaText.textView.textDragInteraction?.isEnabled = false // disable drag for link and attachment
|
2021-06-29 10:41:58 +02:00
|
|
|
metaText.textView.textContainer.lineFragmentPadding = 10 // leading inset
|
2021-06-28 13:41:41 +02:00
|
|
|
metaText.textView.font = UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular))
|
|
|
|
metaText.textView.attributedPlaceholder = {
|
|
|
|
var attributes = metaText.textAttributes
|
|
|
|
attributes[.foregroundColor] = Asset.Colors.Label.secondary.color
|
|
|
|
return NSAttributedString(
|
|
|
|
string: L10n.Scene.Compose.contentInputPlaceholder,
|
|
|
|
attributes: attributes
|
|
|
|
)
|
|
|
|
}()
|
2021-07-23 13:10:27 +02:00
|
|
|
metaText.paragraphStyle = {
|
2021-06-30 09:43:33 +02:00
|
|
|
let style = NSMutableParagraphStyle()
|
|
|
|
style.lineSpacing = 5
|
2021-08-04 09:32:11 +02:00
|
|
|
style.paragraphSpacing = 0
|
2021-06-30 09:43:33 +02:00
|
|
|
return style
|
|
|
|
}()
|
|
|
|
metaText.textAttributes = [
|
|
|
|
.font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .regular)),
|
|
|
|
.foregroundColor: Asset.Colors.Label.primary.color,
|
|
|
|
]
|
|
|
|
metaText.linkAttributes = [
|
|
|
|
.font: UIFontMetrics(forTextStyle: .body).scaledFont(for: .systemFont(ofSize: 17, weight: .semibold)),
|
|
|
|
.foregroundColor: Asset.Colors.brandBlue.color,
|
|
|
|
]
|
2021-06-28 13:41:41 +02:00
|
|
|
return metaText
|
|
|
|
}()
|
|
|
|
|
|
|
|
// output
|
|
|
|
let contentWarningContent = PassthroughSubject<String, Never>()
|
|
|
|
|
|
|
|
override func prepareForReuse() {
|
|
|
|
super.prepareForReuse()
|
|
|
|
|
|
|
|
metaText.delegate = nil
|
|
|
|
metaText.textView.delegate = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
|
|
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
|
|
|
_init()
|
|
|
|
}
|
|
|
|
|
|
|
|
required init?(coder: NSCoder) {
|
|
|
|
super.init(coder: coder)
|
|
|
|
_init()
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
extension ComposeStatusContentTableViewCell {
|
|
|
|
|
|
|
|
private func _init() {
|
2021-06-29 10:41:58 +02:00
|
|
|
selectionStyle = .none
|
2021-06-28 13:41:41 +02:00
|
|
|
layer.zPosition = 999
|
2021-06-30 10:15:15 +02:00
|
|
|
backgroundColor = .clear
|
2021-06-28 13:41:41 +02:00
|
|
|
preservesSuperviewLayoutMargins = true
|
|
|
|
|
|
|
|
let containerStackView = UIStackView()
|
|
|
|
containerStackView.axis = .vertical
|
|
|
|
containerStackView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
contentView.addSubview(containerStackView)
|
|
|
|
NSLayoutConstraint.activate([
|
|
|
|
containerStackView.topAnchor.constraint(equalTo: contentView.topAnchor),
|
|
|
|
containerStackView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor),
|
|
|
|
containerStackView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor),
|
|
|
|
containerStackView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor),
|
|
|
|
])
|
|
|
|
containerStackView.preservesSuperviewLayoutMargins = true
|
|
|
|
|
|
|
|
containerStackView.addArrangedSubview(statusContentWarningEditorView)
|
|
|
|
statusContentWarningEditorView.setContentHuggingPriority(.required - 1, for: .vertical)
|
|
|
|
|
|
|
|
let statusContainerView = UIView()
|
|
|
|
statusContainerView.preservesSuperviewLayoutMargins = true
|
|
|
|
containerStackView.addArrangedSubview(statusContainerView)
|
|
|
|
statusView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
statusContainerView.addSubview(statusView)
|
|
|
|
NSLayoutConstraint.activate([
|
|
|
|
statusView.topAnchor.constraint(equalTo: statusContainerView.topAnchor, constant: 20),
|
2022-01-27 14:23:39 +01:00
|
|
|
statusView.leadingAnchor.constraint(equalTo: statusContainerView.leadingAnchor),
|
|
|
|
statusView.trailingAnchor.constraint(equalTo: statusContainerView.trailingAnchor),
|
2021-06-28 13:41:41 +02:00
|
|
|
statusView.bottomAnchor.constraint(equalTo: statusContainerView.bottomAnchor),
|
|
|
|
])
|
2022-01-27 14:23:39 +01:00
|
|
|
statusView.setup(style: .composeStatusAuthor)
|
2021-06-28 13:41:41 +02:00
|
|
|
|
|
|
|
containerStackView.addArrangedSubview(textEditorViewContainerView)
|
|
|
|
metaText.textView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
textEditorViewContainerView.addSubview(metaText.textView)
|
|
|
|
NSLayoutConstraint.activate([
|
|
|
|
metaText.textView.topAnchor.constraint(equalTo: textEditorViewContainerView.topAnchor),
|
|
|
|
metaText.textView.leadingAnchor.constraint(equalTo: textEditorViewContainerView.layoutMarginsGuide.leadingAnchor),
|
|
|
|
metaText.textView.trailingAnchor.constraint(equalTo: textEditorViewContainerView.layoutMarginsGuide.trailingAnchor),
|
|
|
|
metaText.textView.bottomAnchor.constraint(equalTo: textEditorViewContainerView.bottomAnchor),
|
2022-02-17 09:35:59 +01:00
|
|
|
metaText.textView.heightAnchor.constraint(greaterThanOrEqualToConstant: 64).priority(.defaultHigh),
|
2021-06-28 13:41:41 +02:00
|
|
|
])
|
|
|
|
statusContentWarningEditorView.textView.delegate = self
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// MARK: - UITextViewDelegate
|
|
|
|
extension ComposeStatusContentTableViewCell: UITextViewDelegate {
|
2021-09-29 10:27:35 +02:00
|
|
|
|
|
|
|
func textViewShouldBeginEditing(_ textView: UITextView) -> Bool {
|
|
|
|
return delegate?.composeStatusContentTableViewCell(self, textViewShouldBeginEditing: textView) ?? true
|
|
|
|
}
|
2021-06-28 13:41:41 +02:00
|
|
|
|
|
|
|
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
|
|
|
|
switch textView {
|
|
|
|
case statusContentWarningEditorView.textView:
|
|
|
|
// disable input line break
|
|
|
|
guard text != "\n" else { return false }
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
assertionFailure()
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func textViewDidChange(_ textView: UITextView) {
|
2021-07-19 11:12:45 +02:00
|
|
|
logger.debug("\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): text: \(textView.text ?? "<nil>")")
|
2021-06-28 13:41:41 +02:00
|
|
|
guard textView === statusContentWarningEditorView.textView else { return }
|
|
|
|
// replace line break with space
|
2022-03-18 19:51:24 +01:00
|
|
|
// needs check input state to prevent break the IME
|
|
|
|
if textView.markedTextRange == nil {
|
|
|
|
textView.text = textView.text.replacingOccurrences(of: "\n", with: " ")
|
|
|
|
}
|
2021-06-28 13:41:41 +02:00
|
|
|
contentWarningContent.send(textView.text)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|