166 lines
6.7 KiB
Swift
166 lines
6.7 KiB
Swift
//
|
|
// StatusEditCollectionViewCell.swift
|
|
// Tusker
|
|
//
|
|
// Created by Shadowfacts on 5/11/23.
|
|
// Copyright © 2023 Shadowfacts. All rights reserved.
|
|
//
|
|
|
|
import UIKit
|
|
import Pachyderm
|
|
|
|
@MainActor
|
|
protocol StatusEditCollectionViewCellDelegate: AnyObject, TuskerNavigationDelegate {
|
|
func statusEditCellNeedsReconfigure(_ cell: StatusEditCollectionViewCell, animated: Bool, completion: (() -> Void)?)
|
|
}
|
|
|
|
class StatusEditCollectionViewCell: UICollectionViewListCell {
|
|
|
|
private lazy var contentVStack = UIStackView(arrangedSubviews: [
|
|
timestampLabel,
|
|
contentWarningLabel,
|
|
collapseButton,
|
|
contentContainer,
|
|
]).configure {
|
|
$0.axis = .vertical
|
|
$0.spacing = 4
|
|
$0.alignment = .fill
|
|
}
|
|
|
|
private lazy var timestampLabel = UILabel().configure {
|
|
$0.textColor = .secondaryLabel
|
|
$0.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([
|
|
.traits: [
|
|
UIFontDescriptor.TraitKey.weight: UIFont.Weight.light.rawValue,
|
|
]
|
|
]), size: 0)
|
|
$0.adjustsFontForContentSizeCategory = true
|
|
}
|
|
|
|
private lazy var contentWarningLabel = EmojiLabel().configure {
|
|
$0.numberOfLines = 0
|
|
$0.textColor = .secondaryLabel
|
|
$0.font = UIFont(descriptor: .preferredFontDescriptor(withTextStyle: .body).addingAttributes([
|
|
.traits: [
|
|
UIFontDescriptor.TraitKey.weight: UIFont.Weight.bold.rawValue
|
|
]
|
|
]), size: 0)
|
|
$0.adjustsFontForContentSizeCategory = true
|
|
$0.setContentHuggingPriority(.defaultHigh, for: .vertical)
|
|
$0.isUserInteractionEnabled = true
|
|
$0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(collapseButtonPressed)))
|
|
}
|
|
|
|
private lazy var collapseButton = StatusCollapseButton(configuration: {
|
|
var config = UIButton.Configuration.filled()
|
|
config.image = UIImage(systemName: "chevron.down")
|
|
return config
|
|
}()).configure {
|
|
$0.tintAdjustmentMode = .normal
|
|
$0.setContentHuggingPriority(.defaultHigh, for: .vertical)
|
|
$0.addTarget(self, action: #selector(collapseButtonPressed), for: .touchUpInside)
|
|
}
|
|
|
|
private let contentContainer = StatusContentContainer<StatusEditContentTextView, StatusEditPollView>(useTopSpacer: false).configure {
|
|
$0.contentTextView.defaultFont = TimelineStatusCollectionViewCell.contentFont
|
|
$0.contentTextView.monospaceFont = TimelineStatusCollectionViewCell.monospaceFont
|
|
$0.contentTextView.paragraphStyle = TimelineStatusCollectionViewCell.contentParagraphStyle
|
|
$0.setContentHuggingPriority(.defaultLow, for: .vertical)
|
|
}
|
|
|
|
weak var delegate: StatusEditCollectionViewCellDelegate?
|
|
private var mastodonController: MastodonController! { delegate?.apiController }
|
|
|
|
private var edit: StatusEdit!
|
|
private var statusState: CollapseState!
|
|
|
|
override init(frame: CGRect) {
|
|
super.init(frame: frame)
|
|
|
|
contentVStack.translatesAutoresizingMaskIntoConstraints = false
|
|
contentView.addSubview(contentVStack)
|
|
NSLayoutConstraint.activate([
|
|
contentVStack.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
|
|
contentVStack.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -6),
|
|
contentVStack.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
|
|
contentVStack.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
|
|
])
|
|
}
|
|
|
|
required init?(coder: NSCoder) {
|
|
fatalError("init(coder:) has not been implemented")
|
|
}
|
|
|
|
// todo: accessibility
|
|
|
|
|
|
// MARK: Configure UI
|
|
|
|
func updateUI(edit: StatusEdit, state: CollapseState, index: Int) {
|
|
self.edit = edit
|
|
self.statusState = state
|
|
|
|
timestampLabel.text = ConversationMainStatusCollectionViewCell.dateFormatter.string(from: edit.createdAt)
|
|
|
|
contentContainer.contentTextView.setTextFrom(edit: edit, index: index)
|
|
contentContainer.contentTextView.navigationDelegate = delegate
|
|
contentContainer.attachmentsView.delegate = self
|
|
contentContainer.attachmentsView.updateUI(attachments: edit.attachments)
|
|
contentContainer.pollView.isHidden = edit.poll == nil
|
|
contentContainer.pollView.updateUI(poll: edit.poll, emojis: edit.emojis)
|
|
contentContainer.cardView.isHidden = true
|
|
|
|
contentWarningLabel.text = edit.spoilerText
|
|
contentWarningLabel.isHidden = edit.spoilerText.isEmpty
|
|
if !contentWarningLabel.isHidden {
|
|
contentWarningLabel.setEmojis(edit.emojis, identifier: index)
|
|
}
|
|
|
|
_ = state.resolveFor(status: edit, height: {
|
|
let width = self.bounds.width - 2*16
|
|
return contentContainer.estimateVisibleSubviewHeight(effectiveWidth: width)
|
|
})
|
|
collapseButton.isHidden = !state.collapsible!
|
|
contentContainer.setCollapsed(state.collapsed!)
|
|
if state.collapsed! {
|
|
contentContainer.alpha = 0
|
|
// TODO: is this accessing the image view before the button's been laid out?
|
|
collapseButton.imageView!.transform = CGAffineTransform(rotationAngle: 0)
|
|
collapseButton.accessibilityLabel = NSLocalizedString("Expand Status", comment: "expand status button accessibility label")
|
|
} else {
|
|
contentContainer.alpha = 1
|
|
collapseButton.imageView!.transform = CGAffineTransform(rotationAngle: .pi)
|
|
collapseButton.accessibilityLabel = NSLocalizedString("Collapse Status", comment: "collapse status button accessibility label")
|
|
}
|
|
}
|
|
|
|
// MARK: Interaction
|
|
|
|
@objc private func collapseButtonPressed() {
|
|
statusState.collapsed!.toggle()
|
|
contentContainer.layer.masksToBounds = true
|
|
delegate?.statusEditCellNeedsReconfigure(self, animated: true) {
|
|
self.contentContainer.layer.masksToBounds = false
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
extension StatusEditCollectionViewCell: AttachmentViewDelegate {
|
|
func attachmentViewGallery(startingAt index: Int) -> GalleryViewController? {
|
|
guard let delegate else {
|
|
return nil
|
|
}
|
|
let attachments = contentContainer.attachmentsView.attachments!
|
|
let sourceViews = attachments.map {
|
|
contentContainer.attachmentsView.getAttachmentView(for: $0)
|
|
}
|
|
let gallery = delegate.gallery(attachments: attachments, sourceViews: sourceViews, startIndex: index)
|
|
return gallery
|
|
}
|
|
|
|
func attachmentViewPresent(_ vc: UIViewController, animated: Bool) {
|
|
delegate?.present(vc, animated: animated)
|
|
}
|
|
}
|